diff --git a/.editorconfig b/.editorconfig index f2a3aa4e..10c5ad9b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,4 +9,31 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:error dotnet_style_predefined_type_for_member_access = true:error csharp_style_var_for_built_in_types = false:error csharp_style_var_when_type_is_apparent = true:error -csharp_style_var_elsewhere = true:error +csharp_style_var_elsewhere = false:error +csharp_using_directive_placement = inside_namespace:error +csharp_style_namespace_declarations = file_scoped:error +dotnet_diagnostic.IDE0041.severity = error + +dotnet_style_qualification_for_event = true:error +dotnet_style_qualification_for_field = true:error +dotnet_style_qualification_for_method = true:error +dotnet_style_qualification_for_property = true:error +dotnet_diagnostic.SA1633.severity = none +dotnet_diagnostic.SA1634.severity = none +dotnet_diagnostic.SA1635.severity = none +dotnet_diagnostic.SA1636.severity = none +dotnet_diagnostic.SA1637.severity = none +dotnet_diagnostic.SA1638.severity = none + +[{test/ProjNet.Tests/*.cs,test/ProjNet.Tests/**/*.cs}] +dotnet_diagnostic.SA1611.severity = none +dotnet_diagnostic.CA1707.severity = none +dotnet_diagnostic.CS1591.severity = none + +[src/ProjNet/CoordinateSystems/Projections/AiroceanProjection.cs] +dotnet_diagnostic.SA1117.severity = none +dotnet_diagnostic.SA1201.severity = none + +[{src/ProjNet.Benchmark/*.cs,src/ProjNet.Benchmark/**/*.cs}] +dotnet_diagnostic.CA1822.severity = none +dotnet_diagnostic.CS1591.severity = none diff --git a/.gitattributes b/.gitattributes index 0231ec3c..47d2e71d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,27 @@ +# Auto-detect text files and normalize line endings * text=auto -*.cs text=auto diff=csharp + +# Source code +*.cs text diff=csharp +*.csproj text diff=xml +*.props text diff=xml +*.targets text diff=xml +*.sln text eol=crlf + +# Generated catalog — collapse in GitHub PRs +*.g.cs linguist-generated=true + +# Scripts and config +*.py text diff=python +*.ps1 text +*.json text +*.xml text diff=xml +*.md text diff=markdown +*.txt text +*.yml text +*.yaml text +*.editorconfig text + +# Archives +*.zip binary +*.nupkg binary diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..9dbe4600 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,14 @@ +# Copilot instructions + +## Repository context + +- Build and test from the repository root. +- Build with `dotnet build .\ProjNet4GeoAPI.sln -c Release --tl:off -v minimal`. +- Test with `dotnet test --project .\test\ProjNet.Tests\ProjNET.Tests.csproj`. +- The main CI workflow is `.github/workflows/full-ci.yml`; benchmark monitoring lives in `.github/workflows/benchmarks.yml`; CodeQL uses `.github/workflows/codeql.yml`; mutation testing uses `.github/workflows/mutation-tests.yml`. +- Coverage generation mirrors CI after a Release build: run `dotnet tool restore`, then `dotnet build .\ProjNet4GeoAPI.sln -c Release --tl:off -v minimal`, then `dotnet dotnet-coverage collect --output .\coverage-out\coverage.cobertura.xml --output-format cobertura -- dotnet test --project .\test\ProjNet.Tests\ProjNET.Tests.csproj -c Release --no-build`. +- Curated benchmark runs mirror CI after a Release build: use `.\.github\scripts\Invoke-CuratedBenchmarks.ps1 -Mode Smoke -NoBuild` or `.\.github\scripts\Invoke-CuratedBenchmarks.ps1 -Mode Full -NoBuild`; keep `Get-CuratedBenchmarkConfiguration.ps1` as the single source of truth and validate converted datasets with `Convert-BenchmarkReports.ps1` plus `Assert-CuratedBenchmarkDataset.ps1`. +- The library ships `netstandard2.0`, `netstandard2.1`, and `net8.0`. +- Public API changes must update `src/ProjNet/PublicAPI.Shipped.txt` intentionally. +- In touched C# code, prefer `is null` / `is not null` checks and avoid broad warning suppressions. + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..f90678c1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 + +updates: + - package-ecosystem: nuget + directory: / + schedule: + interval: weekly + labels: + - dependencies + - nuget + commit-message: + prefix: 'deps(nuget):' + include: scope + ignore: + - dependency-name: StyleCop.Analyzers + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + labels: + - dependencies + - github-actions + commit-message: + prefix: 'deps(actions):' + include: scope diff --git a/.github/instructions/csharp.instructions.md b/.github/instructions/csharp.instructions.md new file mode 100644 index 00000000..16f04d02 --- /dev/null +++ b/.github/instructions/csharp.instructions.md @@ -0,0 +1,14 @@ +# C# specific copilot instructions + +## Mandatory quality gates + +### Code analysis and code style + +- Null checks must use pattern syntax: use `is null` and `is not null` instead of `== null` and `!= null`. + +### Code Warnings and StyleCop warnings + +- StyleCop warnings are blocking: fix them before task completion. +- Keep exceptions narrow and only for clearly generated code (for example `*.g.cs` files or files with a standard `` header). +- Do not add broad suppressions (global, project-wide, or blanket pragmas). Any suppression change must be explicitly approved and scoped to a specific diagnostic. +- Any suppression must be accompanied by a justification comment that explains why the suppression is necessary and what the intended fix is (if applicable). The justification should be clear and concise, providing enough context for reviewers to understand the reasoning behind the suppression. \ No newline at end of file diff --git a/.github/problem-matchers/dotnet.json b/.github/problem-matchers/dotnet.json new file mode 100644 index 00000000..55bdcba7 --- /dev/null +++ b/.github/problem-matchers/dotnet.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "dotnet-msbuild", + "pattern": [ + { + "regexp": "^(.*)\\((\\d+),(\\d+)\\):\\s+(warning|error)(?:\\s+([A-Za-z]+\\d+))?:\\s+(.*?)(?:\\s+\\[(.*)\\])?$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "code": 5, + "message": 6 + } + ] + } + ] +} diff --git a/.github/scripts/Assert-CuratedBenchmarkDataset.ps1 b/.github/scripts/Assert-CuratedBenchmarkDataset.ps1 new file mode 100644 index 00000000..acc275ef --- /dev/null +++ b/.github/scripts/Assert-CuratedBenchmarkDataset.ps1 @@ -0,0 +1,72 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$DatasetPath +) + +. "$PSScriptRoot/Get-CuratedBenchmarkConfiguration.ps1" + +$doubleStyles = [System.Globalization.NumberStyles]::Float -bor [System.Globalization.NumberStyles]::AllowThousands +$culture = [System.Globalization.CultureInfo]::InvariantCulture + +[object[]]$entries = Get-Content -Path $DatasetPath -Raw | ConvertFrom-Json +$entryNames = @($entries | ForEach-Object { $_.name } | Sort-Object) +$configuration = Get-CuratedBenchmarkConfiguration +$expectedNames = @($configuration.RequiredDatasetPatterns | Sort-Object) + +if ($entryNames.Count -ne $expectedNames.Count) +{ + throw "Curated benchmark dataset contains $($entryNames.Count) entries but expected $($expectedNames.Count)." +} + +$missingEntries = $expectedNames | Where-Object { $_ -notin $entryNames } +if ($missingEntries.Count -gt 0) +{ + throw "Curated benchmark dataset is missing expected entries: $($missingEntries -join ', ')." +} + +$unexpectedEntries = $entryNames | Where-Object { $_ -notin $expectedNames } +if ($unexpectedEntries.Count -gt 0) +{ + throw "Curated benchmark dataset contains unexpected entries: $($unexpectedEntries -join ', ')." +} + +foreach ($entry in $entries) +{ + if ([string]::IsNullOrWhiteSpace($entry.name)) + { + throw 'Curated benchmark dataset contains an entry without a valid name.' + } + + if ($entry.unit -ne 'ns') + { + throw "Curated benchmark dataset entry '$($entry.name)' does not use the expected 'ns' unit." + } + + $value = 0d + if ($null -eq $entry.value -or -not [double]::TryParse([string]$entry.value, $doubleStyles, $culture, [ref]$value)) + { + throw "Curated benchmark dataset entry '$($entry.name)' does not contain a valid numeric value." + } + + if ([double]::IsNaN($value) -or [double]::IsInfinity($value) -or $value -lt 0) + { + throw "Curated benchmark dataset entry '$($entry.name)' does not contain a finite non-negative numeric value." + } + + $range = 0d + if ($null -eq $entry.range -or -not [double]::TryParse([string]$entry.range, $doubleStyles, $culture, [ref]$range)) + { + throw "Curated benchmark dataset entry '$($entry.name)' does not contain a valid numeric range." + } + + if ([double]::IsNaN($range) -or [double]::IsInfinity($range) -or $range -lt 0) + { + throw "Curated benchmark dataset entry '$($entry.name)' does not contain a finite non-negative numeric range." + } + + if ($entry.extra -isnot [string] -or [string]::IsNullOrWhiteSpace($entry.extra)) + { + throw "Curated benchmark dataset entry '$($entry.name)' does not contain explanatory extra metadata." + } +} diff --git a/.github/scripts/Convert-BenchmarkReports.ps1 b/.github/scripts/Convert-BenchmarkReports.ps1 new file mode 100644 index 00000000..e3a6382b --- /dev/null +++ b/.github/scripts/Convert-BenchmarkReports.ps1 @@ -0,0 +1,63 @@ +param( + [Parameter(Mandatory = $true)] + [string]$InputDirectory, + + [Parameter(Mandatory = $true)] + [string]$OutputFile +) + +$reportFiles = Get-ChildItem -Path $InputDirectory -Filter '*-report-full-compressed.json' -File | Sort-Object Name +if ($reportFiles.Count -eq 0) +{ + throw "No BenchmarkDotNet JSON reports were found in '$InputDirectory'." +} + +$entries = foreach ($reportFile in $reportFiles) +{ + $report = Get-Content -Path $reportFile.FullName -Raw | ConvertFrom-Json + foreach ($benchmark in @($report.Benchmarks)) + { + if ($null -eq $benchmark.Statistics) + { + throw "Benchmark '$($benchmark.FullName)' did not produce statistics." + } + + $allocatedMetric = @($benchmark.Metrics) | + Where-Object { $_.Descriptor.Id -eq 'Allocated Memory' } | + Select-Object -First 1 + + $extraLines = @( + "Type: $($benchmark.Type)", + "Runtime: $($report.HostEnvironmentInfo.RuntimeVersion)", + "Mean: $([double]$benchmark.Statistics.Mean) ns", + "StdDev: $([double]$benchmark.Statistics.StandardDeviation) ns" + ) + + if ($null -ne $allocatedMetric) + { + $extraLines += "Allocated: $([double]$allocatedMetric.Value) B/op" + } + + [pscustomobject]@{ + name = $benchmark.FullName + unit = 'ns' + value = [double]$benchmark.Statistics.Mean + range = [string]([math]::Round([double]$benchmark.Statistics.StandardDeviation, 4)) + extra = $extraLines -join [Environment]::NewLine + } + } +} + +$outputDirectory = Split-Path -Parent $OutputFile +if (-not [string]::IsNullOrWhiteSpace($outputDirectory) -and -not (Test-Path $outputDirectory)) +{ + New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null +} + +$sortedEntries = @($entries | Sort-Object name) + +$sortedEntries | + ConvertTo-Json -Depth 5 -AsArray | + Set-Content -Path $OutputFile -Encoding utf8NoBOM + +Write-Host "Wrote $($sortedEntries.Count) benchmark entries to '$OutputFile'." diff --git a/.github/scripts/Get-CuratedBenchmarkConfiguration.ps1 b/.github/scripts/Get-CuratedBenchmarkConfiguration.ps1 new file mode 100644 index 00000000..f34f413b --- /dev/null +++ b/.github/scripts/Get-CuratedBenchmarkConfiguration.ps1 @@ -0,0 +1,74 @@ +function Get-CuratedBenchmarkConfiguration +{ + $catalogFirstBenchmarks = @( + 'ProjNet.Benchmark.CatalogFirstTransformationLookupBenchmarks.FirstCreateTransformation4326To3857' + ) + + $wktParsingBenchmarks = @( + 'ProjNet.Benchmark.WktParsingBenchmarks.ParseSimpleGeographicCs', + 'ProjNet.Benchmark.WktParsingBenchmarks.ParseProjectedCs', + 'ProjNet.Benchmark.WktParsingBenchmarks.ParseCompoundCs', + 'ProjNet.Benchmark.WktParsingBenchmarks.ParseGeodeticWkt2', + 'ProjNet.Benchmark.WktParsingBenchmarks.ParseProjectedWkt2', + 'ProjNet.Benchmark.WktParsingBenchmarks.ParseBoundWkt2' + ) + + $projectionTransformBenchmarks = @( + 'ProjNet.Benchmark.ProjectionTransformBenchmarks.TransformBatchMercator', + 'ProjNet.Benchmark.ProjectionTransformBenchmarks.TransformBatchUtm32N', + 'ProjNet.Benchmark.ProjectionTransformBenchmarks.TransformBatchLambert93' + ) + + $projParityBenchmarks = @( + 'ProjNet.Benchmark.ProjParityBenchmarks.Wgs84ToWebMercatorBatched(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.Wgs84ToWebMercatorOneByOne(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.Wgs84ToUtm32NBatched(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.Wgs84ToUtm31NBatched(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.Utm31NToWgs84Batched(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.Wgs84ToLambert93Batched(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.Lambert93ToWgs84Batched(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.WebMercatorToWgs84Batched(PointCount: 10000)', + 'ProjNet.Benchmark.ProjParityBenchmarks.Wgs84ToWebMercatorBatchedWithNoise(PointCount: 10000)' + ) + + $transformationFactoryBenchmarks = @( + 'ProjNet.Benchmark.TransformationFactoryBenchmarks.CreateTransformWgs84ToMercator', + 'ProjNet.Benchmark.TransformationFactoryBenchmarks.CreateTransformWgs84ToUtm32N', + 'ProjNet.Benchmark.TransformationFactoryBenchmarks.CreateTransformUtm32NToLambert93' + ) + + $allCuratedBenchmarks = + $catalogFirstBenchmarks + + $wktParsingBenchmarks + + $projectionTransformBenchmarks + + $projParityBenchmarks + + $transformationFactoryBenchmarks + + return @{ + FullRuns = @( + @{ + Filters = $catalogFirstBenchmarks + $wktParsingBenchmarks + Overrides = @() + }, + @{ + Filters = $projectionTransformBenchmarks + $projParityBenchmarks + $transformationFactoryBenchmarks + Overrides = @('--launchCount', '1', '--warmupCount', '1', '--iterationCount', '3') + } + ) + SmokeRuns = @( + @{ + Filters = $catalogFirstBenchmarks + Overrides = @('--launchCount', '1', '--warmupCount', '0', '--iterationCount', '1') + }, + @{ + Filters = $wktParsingBenchmarks + Overrides = @('--launchCount', '1', '--warmupCount', '1', '--iterationCount', '1') + }, + @{ + Filters = $projectionTransformBenchmarks + $projParityBenchmarks + $transformationFactoryBenchmarks + Overrides = @('--launchCount', '1', '--warmupCount', '1', '--iterationCount', '1') + } + ) + RequiredDatasetPatterns = $allCuratedBenchmarks + } +} diff --git a/.github/scripts/Invoke-CuratedBenchmarks.ps1 b/.github/scripts/Invoke-CuratedBenchmarks.ps1 new file mode 100644 index 00000000..cf6d22ca --- /dev/null +++ b/.github/scripts/Invoke-CuratedBenchmarks.ps1 @@ -0,0 +1,66 @@ +[CmdletBinding()] +param( + [ValidateSet('Full', 'Smoke')] + [string]$Mode = 'Full', + + [string]$ArtifactsPath = 'BenchmarkDotNet.Artifacts', + + [switch]$NoBuild +) + +. "$PSScriptRoot/Get-CuratedBenchmarkConfiguration.ps1" + +$projectPath = 'src/ProjNet.Benchmark/ProjNet.Benchmark.csproj' +$configuration = Get-CuratedBenchmarkConfiguration + +function Invoke-BenchmarkRun +{ + param( + [string[]]$Filters, + [string[]]$Overrides + ) + + $arguments = @('run', '-c', 'Release') + if ($NoBuild) + { + $arguments += '--no-build' + } + + $arguments += '--project', $projectPath, '--', '--artifacts', $ArtifactsPath, '--exporters', 'json' + $arguments += '--curated' + + if ($Overrides.Count -gt 0) + { + $arguments += $Overrides + } + + $arguments += '--filter' + $arguments += $Filters + + & dotnet @arguments + if ($LASTEXITCODE -ne 0) + { + throw "BenchmarkDotNet run failed for filters '$($Filters -join ', ')'." + } +} + +Remove-Item -Recurse -Force $ArtifactsPath -ErrorAction SilentlyContinue + +switch ($Mode) +{ + 'Full' + { + foreach ($run in $configuration.FullRuns) + { + Invoke-BenchmarkRun -Filters $run.Filters -Overrides $run.Overrides + } + } + + 'Smoke' + { + foreach ($run in $configuration.SmokeRuns) + { + Invoke-BenchmarkRun -Filters $run.Filters -Overrides $run.Overrides + } + } +} diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml new file mode 100644 index 00000000..3401b6f5 --- /dev/null +++ b/.github/workflows/benchmarks.yml @@ -0,0 +1,138 @@ +name: Benchmark Monitoring + +on: + workflow_dispatch: + schedule: + - cron: '0 6 * * 1' + +permissions: + contents: write + +concurrency: + group: benchmark-data + cancel-in-progress: false + +jobs: + benchmark: + name: Run curated benchmarks + runs-on: ubuntu-24.04 + + steps: + - name: Get source + uses: actions/checkout@v6.0.2 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v5.2.0 + with: + global-json-file: global.json + + - name: Cache NuGet packages + uses: actions/cache@v5.0.5 + with: + path: ~/.nuget/packages + key: nuget-${{ hashFiles('Directory.Packages.props', 'dotnet-tools.json') }} + restore-keys: | + nuget- + + - name: Register .NET problem matcher + run: echo "::add-matcher::.github/problem-matchers/dotnet.json" + + - name: Build + run: dotnet build ProjNet4GeoAPI.sln -c Release --tl:off -v minimal + + - name: Run curated benchmarks + shell: pwsh + run: ./.github/scripts/Invoke-CuratedBenchmarks.ps1 -Mode Full -NoBuild + + - name: Build benchmark regression dataset + shell: pwsh + run: > + ./.github/scripts/Convert-BenchmarkReports.ps1 + -InputDirectory BenchmarkDotNet.Artifacts/results + -OutputFile BenchmarkDotNet.Artifacts/benchmark-action-data.json + + - name: Validate curated benchmark dataset + shell: pwsh + run: ./.github/scripts/Assert-CuratedBenchmarkDataset.ps1 -DatasetPath BenchmarkDotNet.Artifacts/benchmark-action-data.json + + - name: Ensure benchmark data branch exists + if: ${{ github.ref_type == 'branch' && github.ref_name == github.event.repository.default_branch }} + shell: bash + run: | + if git ls-remote --exit-code --heads origin benchmark-data > /dev/null 2>&1; then + echo "benchmark-data branch already exists." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + worktree_dir="$(mktemp -d)" + git worktree add --detach "$worktree_dir" + pushd "$worktree_dir" > /dev/null + git checkout --orphan benchmark-data + git rm -rf . > /dev/null 2>&1 || true + echo "# Benchmark data" > README.md + git add README.md + git commit -m "Initialize benchmark data branch" + git push origin HEAD:benchmark-data + popd > /dev/null + git worktree remove "$worktree_dir" --force + + - name: Require existing benchmark data branch for compare-only runs + if: ${{ github.ref_type != 'branch' || github.ref_name != github.event.repository.default_branch }} + shell: bash + run: | + if ! git ls-remote --exit-code --heads origin benchmark-data > /dev/null 2>&1; then + echo "benchmark-data branch does not exist yet. Run the workflow on the default branch first to establish the baseline." >&2 + exit 1 + fi + + git fetch --depth=1 origin benchmark-data + + if git cat-file -e FETCH_HEAD:dev/bench/data.js 2>/dev/null; then + echo "benchmark-data branch contains persisted benchmark history." + exit 0 + fi + + echo "benchmark-data branch does not contain persisted benchmark history yet. Run the workflow on the default branch first to establish the baseline." >&2 + exit 1 + + - name: Store benchmark result + if: ${{ github.ref_type == 'branch' && github.ref_name == github.event.repository.default_branch }} + uses: benchmark-action/github-action-benchmark@v1.22.0 + with: + name: ProjNET Curated Benchmarks + tool: customSmallerIsBetter + output-file-path: BenchmarkDotNet.Artifacts/benchmark-action-data.json + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true + gh-pages-branch: benchmark-data + alert-threshold: '150%' + fail-on-alert: false + comment-on-alert: true + summary-always: true + + - name: Compare benchmark result against stored baseline + if: ${{ github.ref_type != 'branch' || github.ref_name != github.event.repository.default_branch }} + uses: benchmark-action/github-action-benchmark@v1.22.0 + with: + name: ProjNET Curated Benchmarks + tool: customSmallerIsBetter + output-file-path: BenchmarkDotNet.Artifacts/benchmark-action-data.json + github-token: ${{ secrets.GITHUB_TOKEN }} + gh-pages-branch: benchmark-data + auto-push: false + save-data-file: false + alert-threshold: '150%' + fail-on-alert: false + summary-always: true + + - name: Upload benchmark artifacts + uses: actions/upload-artifact@v7.0.1 + with: + name: benchmark-results + path: BenchmarkDotNet.Artifacts + retention-days: 90 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..7d420455 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,54 @@ +name: CodeQL + +on: + push: + pull_request: + schedule: + - cron: '0 8 * * 3' + +permissions: + actions: read + contents: read + security-events: write + +concurrency: + group: codeql-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze C# + runs-on: ubuntu-latest + + steps: + - name: Get source + uses: actions/checkout@v6.0.2 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v5.2.0 + with: + global-json-file: global.json + + - name: Cache NuGet packages + uses: actions/cache@v5.0.5 + with: + path: ~/.nuget/packages + key: nuget-${{ hashFiles('Directory.Packages.props', 'dotnet-tools.json') }} + restore-keys: | + nuget- + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4.35.2 + with: + languages: csharp + build-mode: manual + + - name: Build + run: dotnet build ProjNet4GeoAPI.sln -c Release --tl:off -v minimal + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4.35.2 + with: + category: /language:csharp diff --git a/.github/workflows/full-ci.yml b/.github/workflows/full-ci.yml index b7b681a8..4dcb428b 100644 --- a/.github/workflows/full-ci.yml +++ b/.github/workflows/full-ci.yml @@ -1,41 +1,179 @@ name: Full Continuous Integration -on: [push, pull_request] +on: + push: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true jobs: pack: - name: Build (${{ matrix.os }}) - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ ubuntu-latest, windows-latest, macOS-latest ] + name: Build, test, and pack + runs-on: ubuntu-latest steps: - name: Get source - uses: actions/checkout@v2 + uses: actions/checkout@v6.0.2 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v5.2.0 + with: + global-json-file: global.json - - name: Setup .NET - uses: actions/setup-dotnet@v4 + - name: Cache NuGet packages + uses: actions/cache@v5.0.5 with: - dotnet-version: 8 + path: ~/.nuget/packages + key: nuget-${{ hashFiles('Directory.Packages.props', 'dotnet-tools.json') }} + restore-keys: | + nuget- + + - name: Restore tools + run: dotnet tool restore + + - name: Register .NET problem matcher + run: echo "::add-matcher::.github/problem-matchers/dotnet.json" - name: Build - run: dotnet build -c Release -v minimal -p:WarningLevel=3 + run: dotnet build ProjNet4GeoAPI.sln -c Release --tl:off -v minimal + + - name: Dependency review + if: ${{ github.event_name == 'pull_request' }} + uses: actions/dependency-review-action@v4.9.0 + + - name: Test with coverage + run: | + mkdir -p coverage-out + dotnet dotnet-coverage collect \ + --output coverage-out/coverage.cobertura.xml \ + --output-format cobertura \ + -- dotnet test --project test/ProjNet.Tests/ProjNET.Tests.csproj -c Release --no-build + + - name: Record coverage artifact status + if: ${{ always() }} + id: coverage-status + shell: pwsh + run: | + if (Test-Path 'coverage-out/coverage.cobertura.xml') { + 'present=true' >> $env:GITHUB_OUTPUT + } + else { + 'present=false' >> $env:GITHUB_OUTPUT + } + + - name: Upload coverage artifact + if: ${{ always() && steps.coverage-status.outputs.present == 'true' }} + uses: actions/upload-artifact@v7.0.1 + with: + name: code-coverage + path: coverage-out/coverage.cobertura.xml + retention-days: 30 + + - name: Generate coverage summary + if: ${{ always() && steps.coverage-status.outputs.present == 'true' }} + uses: irongut/CodeCoverageSummary@v1.3.0 + with: + filename: coverage-out/coverage.cobertura.xml + badge: true + format: markdown + output: both + thresholds: '60 80' + + - name: Record coverage summary status + if: ${{ always() && steps.coverage-status.outputs.present == 'true' }} + id: coverage-summary-status + shell: pwsh + run: | + if (Test-Path 'code-coverage-results.md') { + 'present=true' >> $env:GITHUB_OUTPUT + } + else { + 'present=false' >> $env:GITHUB_OUTPUT + } + + - name: Add coverage to job summary + if: ${{ always() && steps.coverage-summary-status.outputs.present == 'true' }} + run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY + + - name: Upload coverage summary artifact + if: ${{ always() && steps.coverage-summary-status.outputs.present == 'true' }} + uses: actions/upload-artifact@v7.0.1 + with: + name: coverage-summary + path: code-coverage-results.md + retention-days: 30 + + - name: API compatibility gate + run: dotnet test --project test/ProjNet.Tests/ProjNET.Tests.csproj -c Release --no-build --framework net8.0 --filter-class ProjNet.Tests.PublicApiBaselineTests - - name: Test - run: dotnet test -c Release --no-build - shell: bash # defaults disagree on how to quote the filter string + - name: GIE/GIGS parity smoke gate + run: dotnet test --project test/ProjNet.Tests/ProjNET.Tests.csproj -c Release --no-build --framework net8.0 --filter-class ProjNet.Tests.GieBuiltinsTheoryTests --filter-class ProjNet.Tests.Gigs5101TheoryTests + + - name: Benchmark smoke gate + shell: pwsh + run: | + ./.github/scripts/Invoke-CuratedBenchmarks.ps1 -Mode Smoke -NoBuild + + ./.github/scripts/Convert-BenchmarkReports.ps1 ` + -InputDirectory BenchmarkDotNet.Artifacts/results ` + -OutputFile BenchmarkDotNet.Artifacts/benchmark-action-data.json + + ./.github/scripts/Assert-CuratedBenchmarkDataset.ps1 ` + -DatasetPath BenchmarkDotNet.Artifacts/benchmark-action-data.json - name: Pack - run: dotnet pack -c Release --no-build -o artifacts -p:NoWarn=NU5105 + run: dotnet pack ProjNet4GeoAPI.sln -c Release --no-build -o artifacts -p:NoWarn=NU5105 - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7.0.1 with: - name: NuGet Package Files (${{ matrix.os }}) + name: NuGet Package Files path: artifacts + coverageComment: + name: Coverage PR comment + runs-on: ubuntu-latest + needs: pack + if: ${{ always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && !endsWith(github.actor, '[bot]') }} + permissions: + contents: read + pull-requests: write + + steps: + - name: Download coverage summary artifact + uses: actions/download-artifact@v8.0.1 + continue-on-error: true + with: + name: coverage-summary + path: . + + - name: Update coverage PR comment with fallback + if: ${{ hashFiles('code-coverage-results.md') == '' }} + uses: marocchino/sticky-pull-request-comment@v3.0.4 + with: + header: code-coverage + recreate: true + message: | + Coverage summary unavailable for this run. + + See the workflow logs for the coverage generation or artifact upload step that failed earlier in the pipeline. + + - name: Add coverage PR comment + if: ${{ hashFiles('code-coverage-results.md') != '' }} + uses: marocchino/sticky-pull-request-comment@v3.0.4 + with: + header: code-coverage + recreate: true + path: code-coverage-results.md + deployToMyGet: name: Deploy to MyGet runs-on: ubuntu-latest @@ -44,15 +182,15 @@ jobs: if: github.event_name == 'push' && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master') steps: - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v5.2.0 with: - dotnet-version: 8 + dotnet-version: 10.0.201 - name: Download Package Files - uses: actions/download-artifact@v4.1.7 + uses: actions/download-artifact@v8.0.1 with: - name: NuGet Package Files (ubuntu-latest) + name: NuGet Package Files path: artifacts - name: Publish Package Files to MyGet @@ -69,15 +207,15 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/master' steps: - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v5.2.0 with: - dotnet-version: 8 + dotnet-version: 10.0.201 - name: Download Package Files - uses: actions/download-artifact@v4.1.7 + uses: actions/download-artifact@v8.0.1 with: - name: NuGet Package Files (ubuntu-latest) + name: NuGet Package Files path: artifacts - name: Publish Package Files to NuGet diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml new file mode 100644 index 00000000..64e31a5c --- /dev/null +++ b/.github/workflows/mutation-tests.yml @@ -0,0 +1,63 @@ +name: Mutation Tests + +on: + workflow_dispatch: + push: + branches: + - develop + paths: + - 'src/**' + - 'test/**' + - 'Directory.Build.props' + - 'Directory.Packages.props' + - 'dotnet-tools.json' + - 'global.json' + - 'stryker-config.json' + - '.github/workflows/mutation-tests.yml' + +permissions: + contents: read + +concurrency: + group: mutation-${{ github.ref }} + cancel-in-progress: true + +jobs: + mutation: + name: Run Stryker mutation tests + runs-on: ubuntu-latest + + steps: + - name: Get source + uses: actions/checkout@v6.0.2 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v5.2.0 + with: + global-json-file: global.json + + - name: Cache NuGet packages + uses: actions/cache@v5.0.5 + with: + path: ~/.nuget/packages + key: nuget-${{ hashFiles('Directory.Packages.props', 'dotnet-tools.json') }} + restore-keys: | + nuget- + + - name: Restore tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore ProjNet4GeoAPI.sln + + - name: Run mutation tests + run: dotnet dotnet-stryker --config-file stryker-config.json + + - name: Upload mutation artifacts + uses: actions/upload-artifact@v7.0.1 + with: + name: mutation-artifacts + path: | + **/StrykerOutput/** diff --git a/.gitignore b/.gitignore index 10873bc1..4f04afba 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ +artifacts/ +/coverage-out/ +**/StrykerOutput/ x64/ x86/ build/ @@ -84,11 +87,6 @@ $tf/ # Guidance Automation Toolkit *.gpState -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - # JustCode is a .NET coding addin-in .JustCode @@ -155,6 +153,7 @@ sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* +!stylecop.json ~$* *~ *.dbmdl diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 862645ce..00000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: csharp -mono: none -sudo: false -dotnet: 3.1 -dist: bionic - - -script: - - dotnet build -c Release - - dotnet test -c Release --no-build - - dotnet pack -c Release --no-build -p:NoWarn=NU5105 - - -deploy: - - - - on: - branch: master - provider: script - script: - - dotnet nuget push **/*.nupkg -s https://api.nuget.org/v3/index.json -k $NUGET_API_KEY - skip_cleanup: true - - - - on: - branch: develop - provider: script - script: - - dotnet nuget push **/*.nupkg -s https://www.myget.org/F/nettopologysuite/api/v2/package -k $MYGET_API_KEY - skip_cleanup: true - - -env: - global: - - secure: dUgjW1far6YaEOrqKiWinq3c/y3REQ0HrKyv7QYHcqGYZ/R9E+rs8hB11TEtXAdrwMxaYu/eb5k/dVN+u7LTfl3o6egcRFDNh3q+MJR7MPkeUhoDxreTajcgDzBsUIXzOBBveWAjGE9F/aTgrJx6AlrFw9oPgxzA4/FRm+C/MBA= - - secure: IqKYQqumcEtK+X2WCOZgzHN+DAiRtR4I+oWi9vquSdWplyXviQejh02QY+gxagrkZdufJ5K+scbw7nNH/tkej2Ogf2ce1YRglhLwTFiEpF7Bd8u3wHvVwHgmugd4pryshso4XBXr4e38xQt2LYHNGBx/b7TYqGwyQ24Ij+KvuDA= diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..8f8a00af --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,99 @@ +# Changelog + +All notable changes to this project are documented in this file. + +The format is based on Keep a Changelog and this project follows Semantic Versioning. + +## [3.0.0-alpha] - Unreleased + +### Added + +- Added managed EPSG catalog runtime and supporting generated data access layers for coordinate reference and operation resolution. +- Added EPSG WKT ZIP-based generator pipeline as the primary managed data source. +- Added `net8.0` target for `ProjNET` in addition to `netstandard2.0` and `netstandard2.1`. +- Added source-generated regex paths (conditional on .NET 8) for selected hot regex call sites. +- Added `LICENSES/` folder and `NOTICE.md` for consolidated attribution and licensing context. +- Added broad projection and transformation runtime verification coverage, including direct proj2proj parity fixtures. +- Added public grid resource API (`IGridResourceFetchClient`, `GridResourceResolutionMode`, `NoOpGridResourceFetchClient`) with async `TryFetchAsync` support and programmatic configuration via `CoordinateTransformationFactory.ConfigureGridResolution`. +- Added `IXunitSerializable` implementation on `GieCase` and `Proj2ProjCase` for proper xUnit v3 TheoryData serialization. +- Added strongly-typed WKT object model (`ProjNet.IO.Wkt` namespace) with `WktNode`, `WktKeywordNode`, `WktQuotedString`, `WktNumber`, `WktInteger`, `WktIdentifier` types, supporting compact and pretty-print formatting. All 17 coordinate system types expose `ToWktNode()`. +- Added strongly-typed XML serialization via `XElement ToXml()` on all coordinate system types, providing structured XML output alongside the existing `string XML` property. +- Added additive span-based public API overloads for key transformation and WKT parsing workflows: + - `MathTransform.Transform(ReadOnlySpan, Span)` + - `MathTransform.GetCodomainConvexHull(ReadOnlySpan)` + - `MathTransform.GetDomainFlags(ReadOnlySpan)` + - `CoordinateSystemWktReader.Parse(ReadOnlySpan)` + - `Wgs84ConversionInfo.WriteAffineTransform(Span)` +- Added benchmark scenarios aligned to relevant PROJ `bench_proj_trans` patterns, including deterministic noise-based runs and additional CRS pair coverage. +- Added WKT parsing, per-projection transform throughput, and transformation factory benchmarks for comprehensive performance coverage. +- Added `+towgs84` datum shift pipeline support to GIE test harness via `Wgs84ConversionInfo` integration with `CoordinateTransformationFactory`. +- Added `TryResolveDatum` with 8 named datum definitions (potsdam, NAD27, NAD83, nzgd49, ire65, GGRS87, OSGB36, WGS84) for GIE test harness. +- Added 9 additional ellipsoid definitions (everest, evrst48, evrst56, clrk58, engelis, CPM, delmbr, fschr68m) to GIE test harness. +- Added 4D `+proj=axisswap` runtime support (including sign-aware time ordinate handling) in pipeline execution paths. +- Added focused axisswap coverage with new `AxisSwapMathTransformTests` and `AxisOrderHelperTests`, plus expanded pipeline validation scenarios for `+axis` / `+order` combinations. + +### Changed + +- Reworked EPSG generator output to reduce eager runtime initialization and lookup overhead: + - removed generator dependency on `proj.db`, + - replaced large eager arrays with on-demand switch-based lookup paths where applicable, + - split generated catalog into focused partial files. +- Migrated the test stack fully to xUnit v3 and removed NUnit compatibility usage. +- Migrated NuGet package management to Central Package Management (`Directory.Packages.props`), with GitVersioning and StyleCop as global package references. +- Replaced `Newtonsoft.Json` dependency in tests with `System.Text.Json`. +- Replaced string concatenation and `string.Format` calls with string interpolation across the codebase. +- Replaced 242 placeholder XML documentation comments with meaningful summaries across 43 source and test files. +- Renamed phase-prefixed test files/classes to descriptive names that reflect tested behavior. +- Performed structural cleanup: + - one top-level type per file in targeted areas, + - Roman numeral class-name suffixes replaced with numeric suffixes, + - shared projection constants consolidated. +- Modernized coding style for C# 12 consistency: + - expanded expression-bodied members where appropriate, + - converted applicable `using (...)` scopes to `using var`, + - expanded target-typed `new` and collection-expression usage where safe. +- Updated XML documentation across public API surfaces (projections, transformations, coordinate systems, and services/IO) and removed stale external URL references in targeted doc blocks. +- Updated README to current project status, feature scope, and compatibility/build guidance. +- Replaced legacy Java-style stream tokenization for WKT parsing with a buffered span-based tokenizer (`WktTokenizer`) and integrated it across WKT readers. +- Unified versioning with Nerdbank.GitVersioning (`version.json`) and removed CI-specific legacy `Nts*` version computation paths. +- Moved `InternalsVisibleTo` declaration from source-level assembly attributes to MSBuild project configuration. +- Normalized historical block comments in handwritten source/test files to consistent line comments. +- Renamed opaque `SpecialtyProjectionBatch*` tests into descriptive projection-family-focused test classes. +- Optimized selected hot internal paths using `stackalloc`, `ReadOnlySpan/Span`, and `ArrayPool` to reduce transient allocations. +- Improved GIE builtins conversion fallback handling by normalizing cs2cs-style operation tokens for runtime conversion attempts and prioritizing detailed transform skip reasons. +- Unblocked `+gamma` and `+czech` parameters in GIE test harness, enabling omerc and Krovak projection test cases. +- Completed the remaining late-stage PROJ parity work across runtime projection dispatch and parameter bridging for `omerc`, `tpeqd`, `ocea`, stereographic variants, exact `tmerc`/`gauss_kruger`/`utm`, `krovak`, `nzmg`, `loxim`, `ortho`, `s2`, `healpix`, `rhealpix`, `isea`, `lagrng`, and `vandg`. +- Removed legacy SQLCLR self-assignment workaround in `GeocentricTransform`, replacing anonymous delegates with lambdas. +- Moved CS1591 (missing XML docs) suppression from `.csproj` `` to `.editorconfig` for consistent suppression management. +- Enabled full nullable context across the codebase (`enable` in library and tests, `#nullable enable` directives in source files). +- Enabled `EnforceCodeStyleInBuild` and resolved all SA1413 trailing comma warnings. +- Replaced ambiguous coordinate-definition `KeyValuePair` contracts with explicit typed records in public/provider APIs: + - `CoordinateSystemDefinition` (`Srid`, `Wkt`) + - `CoordinateSystemEntry` (`Srid`, `CoordinateSystem`) + - `ICoordinateSystemDefinitionProvider.GetDefinitions()` now returns `IEnumerable`. + - `CoordinateSystemServices` constructor overloads and enumerator surface now use typed definition/entry models. + +### Fixed + +- Corrected outdated and inconsistent file attribution headers by adopting SPDX-style per-file headers based on provenance categories. +- Corrected `VerticalDatum.WKT` to use `VERT_DATUM` keyword per OGC WKT specification (was incorrectly using `DATUM`). +- Removed dead/commented legacy code found during structural cleanup. +- Removed stale TODO comments, bare AAA test markers, and commented-out code blocks. +- Fixed multiple legacy naming inconsistencies in projection class families and their registry references. +- Fixed pooled-buffer lifecycle coverage by adding explicit success/failure-path tests for `GeoTiffGridLoader` pool rental/return behavior. +- Fixed 3 benchmark methods marked as `static` that prevented BenchmarkDotNet discovery (CatalogFirstCoordinateLookup, CatalogFirstTransformationLookup, CatalogRetainedMemory). +- Fixed several projection/runtime correctness gaps caused by missing projection-specific pipeline parameters and flags such as `+lat_1`, `+W`, `+over`, `+orient`, `+mode`, `+azi`, `+aperture`, and `+resolution`, bringing runtime behavior into closer alignment with current PROJ expectations. +- Fixed the remaining GIE builtins skips; the builtins parity suite now runs without skipped cases. + +### Removed + +- Removed `[Serializable]` attribute from all types; WKT is the supported serialization mechanism. BinaryFormatter infrastructure and serialization tests have been deleted. + +### Deprecated + +- Legacy uppercase and snake_case `MapProjection` aliases remain as compatibility members but should be replaced with PascalCase names in new code. + +### Notes + +- Package version line is aligned to the Nerdbank.GitVersioning configuration in `version.json` (`3.0.0-alpha.{height}`). +- `PackageValidationBaselineVersion` remains `2.1.0` until `3.0.0` is published. diff --git a/Directory.Build.props b/Directory.Build.props index 80908608..3af6a13f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,16 +1,25 @@ - $(MSBuildThisFileDirectory) - $(MSBuildThisFileDirectory)scskey.snk - icon.png - + + true + latest + AllEnabledByDefault + true + 9999 + + + true + true + + + + - - + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..7a542799 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,32 @@ + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt new file mode 100644 index 00000000..8000a6fa --- /dev/null +++ b/LICENSES/LGPL-2.1-or-later.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/PROJ-MIT.txt b/LICENSES/PROJ-MIT.txt new file mode 100644 index 00000000..d46f95cd --- /dev/null +++ b/LICENSES/PROJ-MIT.txt @@ -0,0 +1,34 @@ + +All source, data files and other contents of the PROJ package are +available under the following terms. Note that the PROJ 4.3 and earlier +was "public domain" as is common with US government work, but apparently +this is not a well defined legal term in many countries. Frank Warmerdam placed +everything under the following MIT style license because he believed it is +effectively the same as public domain, allowing anyone to use the code as +they wish, including making proprietary derivatives. + +Initial PROJ 4.3 public domain code was put as Frank Warmerdam as copyright +holder, but he didn't mean to imply he did the work. Essentially all work was +done by Gerald Evenden. + +Copyright information can be found in source files. + + -------------- + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. diff --git a/MIGRATION-v3.md b/MIGRATION-v3.md new file mode 100644 index 00000000..102616ce --- /dev/null +++ b/MIGRATION-v3.md @@ -0,0 +1,321 @@ +# Migrating to ProjNET v3 + +ProjNET v3 makes the coordinate-system object model **immutable**. This is the main intentional breaking change in the v3 line and the foundation for safer sharing, simpler reasoning, and follow-up runtime simplifications. + +## Who needs to change code + +You need source changes if your code previously **mutated** objects returned by: + +- `CoordinateSystemServices` +- `CoordinateSystemFactory` +- WKT/WKT2 parsing +- static convenience accessors such as `GeographicCoordinateSystem.WGS84`, `HorizontalDatum.WGS84`, or `ProjectedCoordinateSystem.WebMercator` + +If your code only reads properties, serializes WKT/XML, or creates transformations from existing coordinate systems, you usually do **not** need changes. + +## Important note about runtime fidelity fixes + +The later v3 modernization work also corrected a number of projection/runtime fidelity gaps +(`omerc`, `tpeqd`, `ocea`, stereographic variants, exact `tmerc`/`gauss_kruger`/`utm`, +`krovak`, `nzmg`, `loxim`, `ortho`, `s2`/`healpix`/`rhealpix`, `isea`, `lagrng`, `vandg`). + +These are **behavioral correctness fixes**, not additional source-breaking API changes. In most +cases you do **not** need to rewrite calling code when upgrading, but you may need to update: + +- expected coordinate values in regression tests, +- stored numeric baselines or snapshots, +- tolerance assertions that were written against older incorrect results. + +## Breaking changes at a glance + +| Area | v2-style usage | v3 behavior | Migration path | +| --- | --- | --- | --- | +| `Info` metadata | `Name`, `Authority`, `AuthorityCode`, `Alias`, `Abbreviation`, `Remarks` were mutable | read-only after construction | `WithName(...)`, `WithAuthority(...)`, or rebuild via constructor/factory | +| Datum metadata | `Ensemble` was mutable | read-only after construction | `WithEnsemble(...)` | +| Horizontal datum conversion | `Wgs84Parameters` was mutable | read-only after construction | `WithWgs84Parameters(...)` | +| Temporal datum | `TimeOrigin` was mutable | read-only after construction | create a new `TemporalDatum` | +| CRS composition | datum/unit/prime-meridian/projection references were mutable | read-only after construction | build the desired object up front or clone through `With...` APIs where available | +| Projection/parameter metadata | several public value holders exposed setters | read-only after construction | replace in-place edits with new instances | + +The exact public signature changes are tracked in `src/ProjNet/PublicAPI.Shipped.txt`. + +## Why v3 made this break + +- Coordinate-system graphs can now be treated as **stable values** instead of partially mutable bags of state. +- Shared catalog/static instances are safer to reuse because callers can no longer mutate them after retrieval. +- Immutability gives a clear basis for the v3 thread-safety story. +- Follow-up internal work can remove more defensive cloning and mutation-oriented plumbing without changing the public programming model again. + +## Common migrations + +### 1. Authority + authority code + +**Before** + +```csharp +var projected = (ProjectedCoordinateSystem)factory.CreateFromWkt(wkt); +projected.Authority = "EPSG"; +projected.AuthorityCode = 28992; +``` + +**After** + +```csharp +var projected = (ProjectedCoordinateSystem)factory.CreateFromWkt(wkt); +projected = (ProjectedCoordinateSystem)projected.WithAuthority("EPSG", 28992); +``` + +### 2. Renaming an existing object + +**Before** + +```csharp +var unit = LinearUnit.Metre; +unit.Name = "Meter"; +``` + +**After** + +```csharp +var unit = (LinearUnit)LinearUnit.Metre.WithName("Meter"); +``` + +### 3. Replacing Bursa-Wolf parameters + +**Before** + +```csharp +var datum = HorizontalDatum.ED50; +datum.Wgs84Parameters = new Wgs84ConversionInfo(-87, -98, -121, 0, 0, 0, 0); +``` + +**After** + +```csharp +var datum = HorizontalDatum.ED50.WithWgs84Parameters( + new Wgs84ConversionInfo(-87, -98, -121, 0, 0, 0, 0)); +``` + +### 4. Updating retained ensemble metadata + +**Before** + +```csharp +var datum = HorizontalDatum.WGS84; +datum.Ensemble = ensemble; +``` + +**After** + +```csharp +var datum = (HorizontalDatum)HorizontalDatum.WGS84.WithEnsemble(ensemble); +``` + +### 5. Creating a renamed + re-identified copy + +**Before** + +```csharp +var geographic = GeographicCoordinateSystem.WGS84; +geographic.Name = "Custom WGS 84"; +geographic.Authority = "TEST"; +geographic.AuthorityCode = 1001; +``` + +**After** + +```csharp +var geographic = (GeographicCoordinateSystem)GeographicCoordinateSystem.WGS84 + .WithName("Custom WGS 84") + .WithAuthority("TEST", 1001); +``` + +### 6. Changing a temporal datum time origin + +There is intentionally **no** `WithTimeOrigin(...)` helper in v3. Create a new temporal datum with the desired origin. + +**Before** + +```csharp +var datum = new TemporalDatum("1970-01-01T00:00:00Z", "Unix epoch", "EPSG", 1040, "", "", ""); +datum.TimeOrigin = "2000-01-01T00:00:00Z"; +``` + +**After** + +```csharp +var original = new TemporalDatum("1970-01-01T00:00:00Z", "Unix epoch", "EPSG", 1040, "", "", ""); +var updated = new TemporalDatum( + "2000-01-01T00:00:00Z", + original.Name, + original.Authority, + original.AuthorityCode, + original.Alias, + original.Remarks, + original.Abbreviation); +``` + +### 7. Replacing subclasses of newly sealed types + +The following public types are now sealed in v3: + +- `AffineTransform` +- `CoordinateTransformation` +- `GeographicTransform` +- `ProjectionParameterSet` + +If you previously inherited from them, switch to composition instead: + +| Sealed type | Typical reason for subclassing | Migration path | +| --- | --- | --- | +| `AffineTransform` | custom affine runtime behavior | derive from `MathTransform` for a custom transform, or wrap an `AffineTransform` instance and delegate to it | +| `CoordinateTransformation` | attach custom metadata or behavior to a resolved transformation | create your own wrapper around `ICoordinateTransformation` / `ICoordinateTransformationCore` instead of inheriting | +| `GeographicTransform` | specialize datum-shift runtime behavior | implement a custom `MathTransform` and plug it into your own transformation pipeline | +| `ProjectionParameterSet` | attach helper methods or validation to the parameter dictionary | keep a separate helper/wrapper type and construct or copy a `ProjectionParameterSet` where the ProjNET APIs require one | + +The practical v3 rule is: **treat these runtime/container types as finished building blocks, not inheritance extension points**. + +### 8. Replacing removed `MapProjection` protected helpers + +Several legacy `protected static` helpers that older custom projections sometimes called directly are no longer part of the v3 surface. + +| Removed helper | v3 replacement | +| --- | --- | +| `phi2z(...)` | use `Phi2z(...)` | +| `sign(...)` | use `Sign(...)` | +| `msfnz(...)` | use `Msfnz(...)` | +| `e0fn(...)` / `e1fn(...)` / `e2fn(...)` / `e3fn(...)` / `e4fn(...)` | use the precomputed meridional-series fields already maintained by `MapProjection` (`en0` … `en4`) together with `Mlfn(...)`, or copy the coefficient math locally if you were computing them outside a projection instance | +| `CUBE(x)` | replace with the direct expression `x * x * x` or a local helper in your own derived type | + +If you own custom projections, the safest migration is usually to rename direct PascalCase replacements first (`Phi2z`, `Sign`, `Msfnz`), then do a small manual rewrite for the removed coefficient/cube helpers. + +### 9. Updating manual `CoordinateSystemServices` enumeration + +`CoordinateSystemServices.GetEnumerator()` now returns `IEnumerator` instead of +`IEnumerator>`. + +This only affects code that explicitly stores or types the enumerator/current item. Plain `foreach` +usage continues to work, but the item type is now `CoordinateSystemEntry` with named `Srid` and +`CoordinateSystem` properties. + +**Before** + +```csharp +IEnumerator> enumerator = services.GetEnumerator(); +while (enumerator.MoveNext()) +{ + KeyValuePair current = enumerator.Current; + Console.WriteLine($"{current.Key}: {current.Value.Name}"); +} +``` + +**After** + +```csharp +IEnumerator enumerator = services.GetEnumerator(); +while (enumerator.MoveNext()) +{ + CoordinateSystemEntry current = enumerator.Current; + Console.WriteLine($"{current.Srid}: {current.CoordinateSystem.Name}"); +} +``` + +### 10. Updating constructor, parsing, and serialization assumptions + +Several smaller API and output changes can require targeted source updates: + +| Area | v2-style assumption | v3 behavior | Migration path | +| --- | --- | --- | --- | +| `CoordinateSystemServices` seeded definitions | constructors accepted `IEnumerable>` | constructors now accept `IEnumerable` | wrap each SRID/WKT pair in `new CoordinateSystemDefinition(srid, wkt)` | +| `CoordinateSystemFactory.CreateFromWkt(...)` | return value was treated as always non-null | return type is `CoordinateSystem?` | null-check or use `?? throw` when your input must be a coordinate system | +| `[Serializable]` on model/runtime types | legacy binary serialization attributes were available | `[Serializable]` was removed from the public surface | switch persistence/integration code to WKT/WKT2/XML or your own DTOs | +| `VerticalDatum.WKT` | emitted `DATUM[...]` in vertical coordinate system output | emits `VERT_DATUM[...]` | update string comparisons, snapshots, and custom parsers to the vertical-specific keyword | + +**Before** + +```csharp +var definitions = new[] +{ + new KeyValuePair(4326, GeographicCoordinateSystem.WGS84.WKT), +}; + +var services = new CoordinateSystemServices(definitions); +CoordinateSystem parsed = factory.CreateFromWkt(wkt); +``` + +**After** + +```csharp +var definitions = new[] +{ + new CoordinateSystemDefinition(4326, GeographicCoordinateSystem.WGS84.WKT), +}; + +var services = new CoordinateSystemServices(definitions); +CoordinateSystem parsed = factory.CreateFromWkt(wkt) + ?? throw new InvalidOperationException("Expected a coordinate system WKT."); +``` + +If you previously depended on `[Serializable]`, treat that as a required migration off legacy binary +serialization rather than a drop-in attribute rename. + +## Important note about return types + +`WithAuthority(...)` and `WithName(...)` are declared on `Info`, and `WithEnsemble(...)` is declared on `Datum`. They preserve the **concrete runtime type**, but their declared return types are the base types: + +- `Info.WithAuthority(...)` -> `Info` +- `Info.WithName(...)` -> `Info` +- `Datum.WithEnsemble(...)` -> `Datum` + +That means callers commonly cast back to the expected subtype: + +```csharp +var projected = (ProjectedCoordinateSystem)parsed.WithAuthority("EPSG", 28992); +var renamed = (PrimeMeridian)PrimeMeridian.Greenwich.WithName("Custom Greenwich"); +var datum = (HorizontalDatum)HorizontalDatum.WGS84.WithEnsemble(ensemble); +``` + +If you prefer an assertion-style guard in tests, `Assert.IsType(...)` is a good fit. + +## Practical search-and-replace checklist + +These searches find the vast majority of v2 mutation sites: + +```powershell +rg '\.Authority\s*=' -g '*.cs' +rg '\.AuthorityCode\s*=' -g '*.cs' +rg '\.Name\s*=' -g '*.cs' +rg '\.Wgs84Parameters\s*=' -g '*.cs' +rg '\.Ensemble\s*=' -g '*.cs' +rg '\.TimeOrigin\s*=' -g '*.cs' +``` + +Recommended replacements: + +| Search hit | Typical replacement | +| --- | --- | +| `.Authority = ...` + `.AuthorityCode = ...` | replace the pair with `value = (T)value.WithAuthority(authority, code);` | +| `.Name = ...` | replace with `value = (T)value.WithName(name);` | +| `.Wgs84Parameters = ...` | replace with `value = value.WithWgs84Parameters(...);` | +| `.Ensemble = ...` | replace with `value = (T)value.WithEnsemble(...);` | +| `.TimeOrigin = ...` | replace with a new `TemporalDatum(...)` instance | + +Because the old setter patterns often span multiple lines and variable names differ from file to file, a **guided manual pass** is safer than trying to force a single bulk regex replacement across the whole codebase. + +## What does not change + +- `CoordinateSystemServices` remains the main entry point for EPSG lookup and transformation creation. +- WKT/WKT2 parsing and serialization stay available. +- `EqualParams(...)` semantics remain metadata-insensitive unless the changed value is part of the actual coordinate-system definition. +- The `With...` helpers preserve the concrete runtime type of the cloned object. + +## Recommended migration strategy + +1. First replace obvious setter pairs (`Authority` + `AuthorityCode`, `Name`, `Wgs84Parameters`, `Ensemble`, `TimeOrigin`). +2. Then compile and fix any remaining setter-based call sites one by one. +3. Prefer replacing post-construction mutation with constructor/factory composition when the target object is built locally anyway. +4. Use the `With...` helpers when adapting parsed/catalog objects that should keep the rest of their definition unchanged. + +## v3 takeaway + +The v3 model treats coordinate-system objects as **values**: build them once, clone intentionally when metadata must differ, and then share them safely. diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 00000000..579a4be8 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,32 @@ +# NOTICE + +This project is distributed under `LGPL-2.1-or-later`. + +## Attribution chain + +- **ProjNet4GeoAPI original implementation** + - Copyright 2005-2009 Morten Nielsen and contributors. + - Distributed under LGPL-2.1-or-later. + +- **GeoTools.NET / Urban Science derived portions** + - Includes code historically attributed to Urban Science Applications, Inc. + - Distributed under LGPL-compatible terms in the ProjNET lineage. + +- **PROJ-derived implementation work** + - Portions of the current projection/transformation implementation are derived from PROJ. + - Upstream PROJ material is provided under the PROJ-specific MIT license text in + `LICENSES/PROJ-MIT.txt`. + +- **Vendored grid data from the OSGeo PROJ data CDN** + - `NKG`, `eur_nkg_nkgrf03vel_realigned.tif`, and `eur_nkg_nkgrf17vel.tif` originate from the Nordic Geodetic Commission / NordicTransformations `eur_nkg` data family distributed via `https://cdn.proj.org/`. + - `no_kv_NKGETRF14_EPSG7922_2000.tif` originates from the Kartverket `no_kv` data family distributed via `https://cdn.proj.org/`. + - These CDN-distributed data files are provided under the CC BY 4.0 license. + - The vendored `no_kv_NKGETRF14_EPSG7922_2000.tif` repository copy is a lossless strip-based rewrite of the official CDN GeoTIFF because the current ProjNET GeoTIFF reader requires strip-based rather than tile-based internal layout; sample values and GeoTIFF/GDAL metadata were preserved. + +- **Current maintenance** + - Copyright 2026 Martin Karing / TKI mbH, Chemnitz, Germany. + +## Included license texts + +- `LICENSES/LGPL-2.1-or-later.txt` +- `LICENSES/PROJ-MIT.txt` diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 3eae5a96..00000000 --- a/NuGet.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/ProjNet4GeoAPI.sln.DotSettings b/ProjNet4GeoAPI.sln.DotSettings deleted file mode 100644 index dc33d670..00000000 --- a/ProjNet4GeoAPI.sln.DotSettings +++ /dev/null @@ -1,6 +0,0 @@ - - True - True - True - True - True \ No newline at end of file diff --git a/README.md b/README.md index 99393015..bc1e60bc 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,177 @@ -# ProjNet (for GeoAPI) -This library is an extended port of [ProjNet](http://projnet.codeplex.com) - -## Important notice -The current team unfortunatly doesn't have the resources to dedicate to supporting this project at this moment. -If you see yourself in the position to help out please [reach out](https://github.com/NetTopologySuite/ProjNet4GeoAPI/issues/99). - -Alternatives: -* [SharpProj](https://www.nuget.org/packages/SharpProj.NetTopologySuite/) -* [DotSpatial.Projections](https://www.nuget.org/packages/DotSpatial.Projections/) -* [DotSpatial.Projections (NetStandard)](https://www.nuget.org/packages/DotSpatial.Projections.NetStandard/) -* [GDAL/OGR](https://www.nuget.org/packages/GDAL/) - -## .NET Spatial Reference and Projection Engine -Proj.NET performs point-to-point coordinate conversions between geodetic coordinate systems for use in fx. Geographic Information Systems (GIS) or GPS applications. The spatial reference model used adheres to the Simple Features specification. -* Read the [Frequently Asked Questions](https://github.com/NetTopologySuite/ProjNet4GeoAPI/wiki/Frequently-Asked-Questions) for common questions. -* Popular [Well-Known Text](https://github.com/NetTopologySuite/ProjNet4GeoAPI/wiki/Popular-Well-Known-Text-representations-of-Spatial-Reference-Systems) representations for Spatial Reference Systems - -### Build status -| Branch | Status | -| --- | --- | -| develop | [![Build Status](https://travis-ci.org/NetTopologySuite/ProjNet4GeoAPI.svg?branch=develop)](https://travis-ci.org/NetTopologySuite/ProjNet4GeoAPI) | -| master | [![Build Status](https://travis-ci.org/NetTopologySuite/ProjNet4GeoAPI.svg?branch=master)](https://travis-ci.org/NetTopologySuite/ProjNet4GeoAPI) | - - -### Get it from NuGet -* For version 1.* - `PM> Install-Package ProjNet4GeoAPI` - - More information on [NuGet](https://www.nuget.org/packages/ProjNet4GeoAPI) -* For version 2.* - `PM> Install-Package ProjNet` - - -### Talk... -Join the [![Gitter](https://img.shields.io/gitter/room/TechnologyAdvice/Stardust.svg)](https://gitter.im/NetTopologySuite/ProjNet4GeoAPI) on ProjNet (for GeoAPI). - - -### Projects using ProjNet(4GeoAPI) -* [SharpMap](https://github.com/SharpMap/SharpMap) - -(If your project is missing, there is an edit button up-right) - -### Supports: -* Datum transformations -* Geographic, Geocentric, and Projected coordinate systems -* Compatible with Microsoft .NetStandard 2.0 -* Converts coordinate systems to/from Well-Known Text (WKT) and to XML - -### Projection types currently supported: -* Albers -* Cassini Soldner -* Hotine Oblique Mercator -* Krovak -* Lambert Azimuthal Equal Area -* Lambert Conformal -* Lambert Tangential Conformal Conic -* Mercator -* Mercator Auxiliary Sphere -* Oblique Mercator -* Oblique Stereographic -* Orthographic -* Polar Stereographic -* Polyconic -* Pseudo Mercator -* Transverse Mercator +# ProjNET 3.0 + +ProjNET is a managed .NET library for coordinate reference system (CRS) modeling, projection methods, and coordinate transformation workflows. + +This repository contains the current ProjNET codebase, aligned with contemporary PROJ behavior and expanded runtime coverage while preserving compatibility-focused API surfaces. + +## What is included + +- Managed coordinate reference system (CRS) definitions and EPSG-backed lookup/catalog support. +- Projection registration with broad alias coverage (`321` aliases). +- Coordinate operation and transformation runtime (including affine, Helmert, Molodensky, deformation, grid-shift, topocentric, and pipeline-based paths). +- WKT parsing/writing and coordinate-system serialization support. + +## Scope and non-goals + +In scope: + +- Coordinate reference system (CRS) modeling and EPSG-backed lookup. +- Coordinate transformation pipelines, including grid-backed and metadata-backed paths. +- WKT and PROJJSON parsing, writing, and serialization support. + +Not in scope: + +- Raster reprojection, image resampling, or map rendering. +- General-purpose vector geometry I/O or GIS data source handling. +- Runtime dependence on `proj.db`, GDAL, or NetTopologySuite. + +## Target frameworks + +`ProjNET` currently targets: + +- `netstandard2.0` (required shipping target) +- `netstandard2.1` +- `net8.0` + +The project is built with C# 12 and includes .NET 8-specific runtime optimizations where applicable (for example conditional source-generated regex paths). + +## Installation + +```powershell +dotnet add package ProjNET +``` + +## Quick usage + +### Use the default EPSG catalog (recommended) + +```csharp +using System; +using ProjNet; + +var services = new CoordinateSystemServices(); +var transformation = services.CreateTransformation(4326, 3857); + +if (transformation is null) +{ + throw new InvalidOperationException("EPSG:4326 to EPSG:3857 transformation is not available."); +} + +double[] result = transformation.MathTransform.Transform(new[] { 10d, 10d }); +``` + +### Use custom WKT definitions when you need to seed your own catalog + +```csharp +using System; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.Data; + +var services = new CoordinateSystemServices(new[] +{ + new CoordinateSystemDefinition(4326, GeographicCoordinateSystem.WGS84.WKT), + new CoordinateSystemDefinition(3857, ProjectedCoordinateSystem.WebMercator.WKT), +}); + +var transformation = services.CreateTransformation(4326, 3857); + +if (transformation is null) +{ + throw new InvalidOperationException("The custom CRS transformation is not available."); +} + +double[] result = transformation.MathTransform.Transform(new[] { 10d, 10d }); +``` + +Expected reference point for `10°,10°` in EPSG:3857 is approximately: + +- `X = 1113194.90793274` +- `Y = 1118889.97485796` + +(Validated by `test/ProjNet.Tests/Integration/VerificationSuiteTests.cs`.) + +### Default EPSG catalog + +`new CoordinateSystemServices()` uses the built-in managed EPSG catalog. +The default catalog exposes `7,217` coordinate reference system (CRS) definitions, so common SRID-based lookups such as `4326` and `3857` work out of the box. + +### Supported formats + +ProjNET supports WKT1, WKT2:2019, and PROJJSON parsing for the CRS types covered by the library. +It can also serialize supported CRS definitions back to WKT and PROJJSON. +See [`docs/concepts.md`](docs/concepts.md) for format terminology and [`docs/README.md`](docs/README.md) for the user-documentation index. + +### AOT and trimming + +The `net8.0` target is marked trimmable and built with trim analysis enabled. +ProjNET is intended to stay compatible with native AOT and trimmed deployments. + +### Thread safety + +`CoordinateSystemServices` synchronizes its one-time initialization and can be reused across threads after construction. +Core immutable CRS model types can also be shared across threads; see the XML docs on the main public types for details. + +## Build and test + +From repository root: + +```powershell +dotnet build .\ProjNet4GeoAPI.sln --tl:off -v minimal +dotnet test --project .\test\ProjNet.Tests\ProjNET.Tests.csproj +``` + +## What's new in v3 + +- Added `net8.0` as a library target while preserving `netstandard` targets. +- Generator now uses EPSG WKT ZIP as primary source (no runtime `proj.db` dependency). +- Large generated eager arrays were replaced by on-demand switch-based lookup paths in the managed EPSG catalog. +- Test infrastructure uses xUnit v3 and Microsoft.Testing.Platform. +- Historical `SpecialtyProjectionBatch*` test naming was removed in favor of behavior-oriented class names. +- SPDX-based file attribution and `LICENSES/` + `NOTICE.md` consolidation completed. +- API XML documentation overhauled across projection, transformation, coordinate-system, and IO/service surfaces. +- Build/versioning was unified with Nerdbank.GitVersioning (`version.json` + shared build props). + +## API compatibility and validation + +- Public API drift is guarded by `PublicApiBaselineTests` against `src/ProjNet/PublicAPI.Shipped.txt`. +- Baseline regeneration (intentional API change only) is controlled by `PROJNET_UPDATE_PUBLIC_API_BASELINE=1`. +- Intentional baseline updates can be performed with: + +```powershell +$env:PROJNET_UPDATE_PUBLIC_API_BASELINE='1' +dotnet test --project .\test\ProjNet.Tests\ProjNET.Tests.csproj --filter-class ProjNet.Tests.PublicApiBaselineTests +``` + +## Transformation coverage summary + +Implemented and validated transformation families include: + +- Affine transforms (`AffineTransform`) +- Geocentric/geographic bridge transforms +- Axis swap and unit conversion +- Helmert and Molodensky families +- Deformation and deformation model transforms +- Horner and TIN shift transforms +- Horizontal/vertical/XYZ grid shifts (NTv2, GTX, GeoTIFF) +- Prime-meridian and topocentric transforms +- Pipeline composition and concatenation paths + +## Projection coverage summary + +ProjNET currently registers **152** projection classes and **321** aliases in `ProjectionsRegistry`. +For the audited projection-family breakdown, PROJ alias coverage, and remaining parity notes, see [`docs/projection-coverage.md`](docs/projection-coverage.md). + +## Documentation and governance + +- User documentation index: `docs/README.md` +- Projection parity matrix: `docs/projection-coverage.md` +- Engineering governance and API baseline policy: `src/ProjNet/ENGINEERING_GOVERNANCE.md` + +## License and attribution + +This project ships under **LGPL-2.1-or-later**. + +- License texts: `LICENSES/` +- Attribution and provenance summary: `NOTICE.md` +- Per-file SPDX attribution is used across source and tests. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..fd45aa44 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,22 @@ +# ProjNET documentation index + +This folder collects the user-facing and project-level reference material that +complements the top-level `README.md`. + +## Start here + +| Path | Purpose | +| --- | --- | +| [`concepts.md`](concepts.md) | Core terminology for CRS, datum, projections, transformation pipelines, formats, and the managed EPSG catalog. | +| [`cookbook.md`](cookbook.md) | Practical recipes for the most common ProjNET workflows. | +| [`grids.md`](grids.md) | Detailed guidance for NTv2, GTX, and GeoTIFF grid configuration. | +| [`projection-coverage.md`](projection-coverage.md) | Audited projection and PROJ alias coverage matrix. | +| [`benchmarks/`](benchmarks/) | Benchmark history and performance-focused documentation. | +| [`grid-fixture-notes/`](grid-fixture-notes/) | Provenance notes for vendored grid fixtures and related artifacts. | + +## Suggested reading order + +1. Start with [`concepts.md`](concepts.md) if you are new to CRS terminology. +2. Move to [`cookbook.md`](cookbook.md) for concrete API examples. +3. Read [`grids.md`](grids.md) before enabling grid-backed transformations in production. +4. Use [`projection-coverage.md`](projection-coverage.md) when you need detailed parity or alias information. diff --git a/docs/benchmarks/wkt-parsing-history.md b/docs/benchmarks/wkt-parsing-history.md new file mode 100644 index 00000000..a8a72614 --- /dev/null +++ b/docs/benchmarks/wkt-parsing-history.md @@ -0,0 +1,46 @@ +# WKT parsing benchmark history + +This snapshot records the main WKT parser performance checkpoints that were used +to drive the M92-M99 optimization work and the later Iteration 11 verification. + +- **Benchmark command**: `dotnet run -c Release --project src\ProjNet.Benchmark -- --filter "*WktParsingBenchmarks*" "*WktBulkParsingBenchmarks*"` +- **Primary scenarios tracked here**: + - `ParseAllCatalogWkt1` + - `ParseAllCatalogWkt2` +- **Interpretation**: + - Lower runtime is better. + - Lower allocation is better. + - The tree-parser migration temporarily caused a severe regression, which was + then recovered over several optimization milestones. + +## Snapshot table + +| Snapshot | Context | WKT1 runtime | WKT1 alloc | WKT2 runtime | WKT2 alloc | +|---|---|---:|---:|---:|---:| +| `c33f984` | Pre-migration baseline | 71.73 ms | 82.47 MB | 95.11 ms | 120.05 MB | +| `e5ac2ea` | Initial post-migration regression | 315.20 ms | 514.41 MB | 142.80 ms | 187.18 MB | +| M92 | First major recovery pass | 84.63 ms | 109.47 MB | 104.79 ms | 128.59 MB | +| M93 | Source-backed node recovery | 73.55 ms | 66.61 MB | 90.99 ms | 84.32 MB | +| M94 | Post-cleanup verification | 74.15 ms | 66.61 MB | 98.08 ms | 80.76 MB | +| M98 | Later parser verification | 87.72 ms | 62.26 MB | 110.61 ms | 67.11 MB | +| M99 | Guard-cleanup benchmark snapshot | 51.20 ms | 62.26 MB | 62.51 ms | 67.11 MB | +| 2026-04-15 | Iteration 11 reference | 61.79 ms | 62.26 MB | 74.53 ms | 66.74 MB | + +## Summary + +- Relative to the original baseline, the current parser remains faster in bulk: + - `ParseAllCatalogWkt1`: `71.73 ms -> 61.79 ms` (`-13.9%`) + - `ParseAllCatalogWkt2`: `95.11 ms -> 74.53 ms` (`-21.6%`) +- Relative to the worst post-migration state, the current parser recovered most + of the lost runtime and nearly all excess allocation pressure. +- `M99` remains the fastest recorded runtime snapshot so far, but the later + Iteration 11 state retains the same low-allocation profile while staying + comfortably ahead of the original baseline. + +## Notes + +- The current reference was captured after the full `CODE_REVIEW_5` remediation + work reached a green build and full test run. +- More detailed ad-hoc benchmark notes and raw logs were used during development, + but this file is the committed repository snapshot for the main WKT parser + checkpoints referenced by the review and follow-up discussions. diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 00000000..25b4449f --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,92 @@ +# ProjNET concepts and terminology + +This guide defines the core terms used throughout ProjNET. The short version is: +ProjNET models coordinate reference systems (CRSs), parses and writes common CRS +formats, and creates coordinate transformations between compatible CRS pairs. + +## Coordinate Reference System (CRS) + +A coordinate reference system describes how numeric ordinates map to real-world +locations. A CRS combines a reference frame, axis order, axis units, and, when +needed, a map projection. In ProjNET, the most common CRS categories are: + +- **Geographic CRS**: longitude and latitude on an ellipsoid, usually in degrees. +- **Projected CRS**: planar easting/northing coordinates derived from a geographic + CRS through a projection, usually in metres or feet. +- **Vertical CRS**: height or depth relative to a vertical datum. +- **Compound CRS**: a horizontal CRS plus a vertical CRS handled together. + +ProjNET also supports geocentric and bound CRS cases where the metadata requires +an explicit earth-centred frame or a mandated transformation to a hub CRS. + +## Datum and datum ensemble + +A datum anchors coordinates to the earth. For horizontal work, the datum defines +the ellipsoid and the realization used to position that ellipsoid relative to the +planet. For vertical work, the datum defines the zero-height surface. Modern +registries also use **datum ensembles** when a CRS intentionally refers to a +family of closely related realizations instead of one exact member. + +## Ellipsoid + +An ellipsoid is the mathematical earth model used by a datum. It is usually +described by a semi-major axis and an inverse flattening value. Geographic and +projected CRSs depend on the ellipsoid because projection formulae and datum +transformations operate on that geometric model. + +## Projection + +A projection converts angular geographic coordinates into planar coordinates. +Every projection introduces trade-offs: some preserve area, some preserve local +shape, some preserve distance or direction along limited paths, and none preserve +everything everywhere. ProjNET contains the projection implementation and the +metadata that binds projection parameters to a projected CRS. + +## Transformation and pipeline + +A transformation converts coordinates from one CRS into another. Simple cases may +only need axis normalization, unit conversion, and a projection forward or inverse +step. More complex cases may also require datum shifts, Helmert operations, +vertical adjustments, or grid-backed corrections. ProjNET composes these steps +into runtime pipelines so the resulting `MathTransform` matches the CRS metadata +as closely as the available catalog and grid data allow. + +## WKT1, WKT2, and PROJJSON + +ProjNET works with three important CRS interchange formats: + +- **WKT1**: the older OGC/ESRI-era Well-Known Text family that is still common in + existing databases and files. +- **WKT2:2019**: the newer ISO 19111-aligned form with richer CRS metadata, + including bound, compound, and modern usage metadata. +- **PROJJSON**: the JSON representation used by the PROJ ecosystem for CRS and + operation metadata exchange. + +ProjNET reads WKT1, WKT2, and PROJJSON for the supported CRS shapes in the +library, and it can serialize supported coordinate-system models back to WKT and +PROJJSON. + +## Grid shifts + +Some transformations depend on sampled correction grids instead of a few numeric +parameters. ProjNET supports the main formats used by the current library: + +- **NTv2 (`.gsb`)** for horizontal grid shifts. +- **GTX (`.gtx`)** for vertical grid shifts. +- **GeoTIFF (`.tif`)** for horizontal, vertical, and xyz grid-backed operations. + +These grids are resolved from local search paths or an optional cache/network +workflow. If a transformation requires a grid and no substitute operation exists, +grid availability can determine whether the transformation can be created. + +## EPSG catalog + +The default `CoordinateSystemServices` constructor uses ProjNET's managed EPSG +catalog. That catalog is generated into the library, keyed by SRID, and available +without a runtime `proj.db` dependency. In practice, that means common lookups +such as EPSG:4326 or EPSG:3857 work out of the box in the default configuration. + +## Further reading + +- EPSG registry browser: +- OGC standards overview: diff --git a/docs/cookbook.md b/docs/cookbook.md new file mode 100644 index 00000000..f4839385 --- /dev/null +++ b/docs/cookbook.md @@ -0,0 +1,214 @@ +# ProjNET cookbook + +This cookbook collects short, copyable examples for the most common ProjNET +workflows. The snippets use the current public APIs and are intended to be +adapted into your application code. + +## 1. Create the simplest EPSG transformation + +Use the built-in managed EPSG catalog when you already know the SRIDs you want. + +```csharp +using System; +using ProjNet; + +var services = new CoordinateSystemServices(); +var transformation = services.CreateTransformation(4326, 3857); + +if (transformation is null) +{ + throw new InvalidOperationException("EPSG:4326 to EPSG:3857 is not available."); +} + +double[] projected = transformation.MathTransform.Transform(new[] { 10d, 10d }); +Console.WriteLine($"X={projected[0]}, Y={projected[1]}"); +``` + +## 2. Transform many points with a reusable output buffer + +For batch work, reuse buffers instead of allocating a fresh array for every point. + +```csharp +using System; +using ProjNet; + +var services = new CoordinateSystemServices(); +var transformation = services.CreateTransformation(4326, 3857) + ?? throw new InvalidOperationException("Transformation is not available."); + +double[][] sourcePoints = +[ + [10d, 10d], + [10.5d, 10.25d], + [11d, 10.5d], +]; + +double[] buffer = new double[2]; + +foreach (double[] point in sourcePoints) +{ + transformation.MathTransform.Transform(point, buffer); + Console.WriteLine($"{point[0]}, {point[1]} -> {buffer[0]}, {buffer[1]}"); +} +``` + +## 3. Inspect a CRS from the catalog + +Look up a CRS once and inspect its authority metadata or serialized form. + +```csharp +using System; +using ProjNet; +using ProjNet.CoordinateSystems; + +var services = new CoordinateSystemServices(); + +if (!services.TryGetCoordinateSystem(4326, out CoordinateSystem? wgs84)) +{ + throw new InvalidOperationException("EPSG:4326 is missing from the catalog."); +} + +Console.WriteLine($"{wgs84.Authority}:{wgs84.AuthorityCode} - {wgs84.Name}"); +Console.WriteLine(wgs84.WKT); +Console.WriteLine(wgs84.ToProjJson()); +``` + +## 4. Seed your own CRS definitions with WKT + +Use `CoordinateSystemDefinition` when you need a private catalog entry instead of +the built-in managed EPSG set. + +```csharp +using System; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.Data; + +var services = new CoordinateSystemServices(new[] +{ + new CoordinateSystemDefinition(4326, GeographicCoordinateSystem.WGS84.WKT), + new CoordinateSystemDefinition(3857, ProjectedCoordinateSystem.WebMercator.WKT), +}); + +var transformation = services.CreateTransformation(4326, 3857) + ?? throw new InvalidOperationException("Custom CRS transformation is not available."); + +double[] projected = transformation.MathTransform.Transform(new[] { 10d, 10d }); +``` + +## 5. Parse WKT2, emit PROJJSON, and roundtrip back to a CRS + +You can parse a modern WKT2 definition, serialize it to PROJJSON, and then read +the JSON form back into a coordinate-system object. + +```csharp +using System; +using ProjNet.CoordinateSystems; +using ProjNet.IO.CoordinateSystems; + +string wkt2 = """ + GEOGCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]], + CS[ellipsoidal,2], + AXIS["latitude",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]], + AXIS["longitude",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4326]] + """; + +CoordinateSystem fromWkt = CoordinateSystemWktReader.Parse(wkt2) as CoordinateSystem + ?? throw new InvalidOperationException("WKT2 did not describe a supported CRS."); + +string projJson = fromWkt.ToProjJson(); + +CoordinateSystem fromProjJson = ProjJsonReader.Parse(projJson) as CoordinateSystem + ?? throw new InvalidOperationException("PROJJSON did not roundtrip to a CRS."); + +Console.WriteLine(fromProjJson.Name); +``` + +## 6. Configure local and network-backed grid resolution + +Grid-backed transformations can be pointed at local folders, a cache directory, +and an optional HTTP source before you create the transformation. + +```csharp +using System; +using ProjNet; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Resources; + +CoordinateTransformationFactory.ConfigureGridResolution( + new HttpGridResourceFetchClient("https://cdn.proj.org/"), + new[] { @"C:\projnet\grids" }, + @"C:\projnet\grid-cache", + GridResourceResolutionMode.LocalThenNetwork); + +var services = new CoordinateSystemServices(); +var transformation = services.CreateTransformation(31467, 25832) + ?? throw new InvalidOperationException("Grid-backed transformation is not available."); +``` + +If you want grid-backed operations to fail fast when the required file is missing, +set `PROJNET_GRID_REQUIRED=true` before creating the transformation. See +`docs/grids.md` for the full environment-variable matrix. + +## 7. Add a custom projection implementation + +This recipe is for contributors or advanced hosts that register additional +projection implementations. A projection type must derive from `MapProjection`, +provide a public constructor that accepts `IEnumerable`, and +be registered in `ProjectionsRegistry`. + +```csharp +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; + +public sealed class DemoProjection : MapProjection +{ + public DemoProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + private DemoProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Demo"; + } + + public override MathTransform Inverse() + { + this.inverse ??= new DemoProjection(this.Parameters.ToProjectionParameter(), this); + return this.inverse; + } + + protected override void RadiansToMeters(ref double lon, ref double lat) + { + lon = this.SphericalRadius * lon; + lat = this.SphericalRadius * lat; + } + + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.InverseSphericalRadius; + y *= this.InverseSphericalRadius; + } +} + +public static class ProjectionBootstrap +{ + public static void RegisterDemoProjection() + { + ProjectionsRegistry.Register("demo_projection", typeof(DemoProjection)); + ProjectionsRegistry.RegisterAlias("demo", "demo_projection"); + } +} +``` + +In production code you should also document the projection, validate its +parameters, and add runtime tests that compare the forward and inverse paths +against an external reference implementation. diff --git a/docs/grid-fixture-notes/NKG_ARTIFACT_SOURCES.txt b/docs/grid-fixture-notes/NKG_ARTIFACT_SOURCES.txt new file mode 100644 index 00000000..df2e0693 --- /dev/null +++ b/docs/grid-fixture-notes/NKG_ARTIFACT_SOURCES.txt @@ -0,0 +1,58 @@ +NKG builtins test artifacts +=========================== + +All repository paths in this note are relative to this repository root. + +These files are vendored locally so the NKG builtins regression coverage in +this repository does not depend on live network access: + +1. `test\ProjNet.Tests\Fixtures\grids\NKG` +2. `test\ProjNet.Tests\Fixtures\grids\eur_nkg_nkgrf03vel_realigned.tif` +3. `test\ProjNet.Tests\Fixtures\grids\eur_nkg_nkgrf17vel.tif` +4. `test\ProjNet.Tests\Fixtures\grids\no_kv_NKGETRF14_EPSG7922_2000.tif` + +Source and license +------------------ + +- `NKG`, `eur_nkg_nkgrf03vel_realigned.tif`, `eur_nkg_nkgrf17vel.tif` + - Distribution source: OSGeo PROJ data CDN, https://cdn.proj.org/ + - Upstream data family: Nordic Geodetic Commission / NordicTransformations + `eur_nkg` + - Representative upstream files: + - https://cdn.proj.org/NKG + - https://cdn.proj.org/eur_nkg_nkgrf03vel_realigned.tif + - https://cdn.proj.org/eur_nkg_nkgrf17vel.tif + - Upstream metadata reference: `eur_nkg_README.txt` in the PROJ-data package + - License: CC BY 4.0 + +- `no_kv_NKGETRF14_EPSG7922_2000.tif` + - Distribution source: OSGeo PROJ data CDN, https://cdn.proj.org/ + - Upstream data family: Kartverket `no_kv` + - Representative upstream file: + - https://cdn.proj.org/no_kv_NKGETRF14_EPSG7922_2000.tif + - Upstream metadata reference: `no_kv_README.txt` in the PROJ-data package + - License: CC BY 4.0 + - Repository artifact note: the vendored repository copy keeps the official + file name but is a lossless strip-based rewrite of the upstream tiled + GeoTIFF. It was generated locally from the official CDN file so the current + ProjNet xyz-grid test/runtime path can read the Norway grid. Sample values + and GeoTIFF/GDAL metadata were preserved during the rewrite. + +Repository use +-------------- + +These fixtures are used by the NKG builtins regression coverage under +`test\ProjNet.Tests\Fixtures\gie\nkg.gie` together with the +vendored grid definitions in +`test\ProjNet.Tests\Fixtures\grids\NKG`. + +The Norway-specific GeoTIFF grid is required for the official pipeline behind +`urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_NO`. The vendored Norway copy +is stored in strip-based layout because the current ProjNet GeoTIFF xyz-grid +loader does not accept tiled scanline reads for this path. + +Project-local attribution +------------------------- + +Additional repository-local attribution for these vendored files is maintained +in `NOTICE.md`. diff --git a/docs/grids.md b/docs/grids.md new file mode 100644 index 00000000..41758fed --- /dev/null +++ b/docs/grids.md @@ -0,0 +1,124 @@ +# Grid resource configuration + +Some ProjNET transformations require external grid files instead of only numeric +parameters. This guide explains which formats are supported, how the resolver +searches for grid files, and how to configure local and network-backed setups. + +## Supported grid formats + +| Format | Typical extension | Primary use | +| --- | --- | --- | +| NTv2 | `.gsb` | Horizontal grid shifts | +| GTX | `.gtx` | Vertical grid shifts | +| GeoTIFF | `.tif` | Horizontal, vertical, and xyz grid-backed shifts | + +## Resolution order + +When ProjNET needs a grid file, it resolves it in this order: + +1. In-memory cache of previously resolved grid names. +2. Direct rooted path if the requested grid name is already an absolute file path. +3. Configured local directories, matched by file name. +4. Optional network fetch into the configured cache directory when + `GridResourceResolutionMode.LocalThenNetwork` is active. + +Successful resolutions are cached for later reuse. Network-fetched files are +written into the cache directory together with a manifest so stale or partial +downloads can be detected and discarded. + +## Environment variables + +`CoordinateTransformationFactory.ConfigureGridResolution()` uses these variables +when you call it with no arguments, and the default process-wide resolver also +reads them on startup: + +| Variable | Meaning | +| --- | --- | +| `PROJNET_GRID_PATHS` | List of local search directories. Empty means no configured local directories. | +| `PROJNET_GRID_CACHE` | Cache directory for downloaded grid files. Required for network-backed resolution to succeed. | +| `PROJNET_GRID_MODE` | Resolution mode. `LocalThenNetwork` and `network` enable network fallback; any other value behaves as `LocalOnly`. | +| `PROJNET_GRID_REQUIRED` | Fail-fast mode for required grids. `1`, `true`, and `yes` force a `DataUnavailable:` exception when a required grid cannot be resolved. | +| `PROJNET_GRID_BASE_URL` | Absolute base URL used by `HttpGridResourceFetchClient` when network mode is enabled. | + +### Local-only PowerShell setup + +```powershell +$env:PROJNET_GRID_PATHS = 'C:\projnet\grids;D:\shared\projnet-grids' +$env:PROJNET_GRID_MODE = 'LocalOnly' +$env:PROJNET_GRID_CACHE = $null +$env:PROJNET_GRID_BASE_URL = $null +``` + +### Local-then-network PowerShell setup + +```powershell +$env:PROJNET_GRID_PATHS = 'C:\projnet\grids' +$env:PROJNET_GRID_CACHE = 'C:\projnet\grid-cache' +$env:PROJNET_GRID_MODE = 'network' +$env:PROJNET_GRID_BASE_URL = 'https://cdn.proj.org/' +``` + +## Programmatic configuration + +Use the public static configuration API when you want to control the resolver in +application startup code instead of depending on process environment state. + +```csharp +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Resources; + +CoordinateTransformationFactory.ConfigureGridResolution( + new HttpGridResourceFetchClient("https://cdn.proj.org/"), + new[] { @"C:\projnet\grids" }, + @"C:\projnet\grid-cache", + GridResourceResolutionMode.LocalThenNetwork); +``` + +To rebuild the resolver from the current environment variables, call: + +```csharp +using ProjNet.CoordinateSystems.Transformations; + +CoordinateTransformationFactory.ConfigureGridResolution(); +``` + +## Local and network deployment patterns + +### Prefer local files for deterministic production deployments + +Ship the grid files alongside your application or mount them into a known +directory. Then set `PROJNET_GRID_PATHS` (or pass `localDirectories`) and keep +`GridResourceResolutionMode.LocalOnly`. This is the most predictable setup for +CI, containers, and offline services. + +### Use cache + network for developer convenience or managed refresh + +When you want a PROJ-style fetch-on-demand experience, configure: + +- one or more local directories for pre-seeded files, +- a writable cache directory, +- `LocalThenNetwork` / `network` mode, and +- an absolute base URL such as `https://cdn.proj.org/`. + +ProjNET downloads only by file name into the cache, validates the cached manifest, +and reuses the cached copy on subsequent resolutions. + +## Required-grid behavior + +By default, ProjNET can still create a transformation when a non-grid fallback +operation exists for the same CRS pair. If the only available path depends on a +missing grid, the transformation cannot be created. + +Set `PROJNET_GRID_REQUIRED=true` when your application must not silently accept a +lower-fidelity fallback. In that mode, missing required grids raise an +`InvalidOperationException` whose message starts with `DataUnavailable:`. + +## Practical notes + +- A rooted grid path such as `C:\projnet\grids\BETA2007.gsb` is used directly. +- Relative grid names are resolved by file name against the configured local + directories or cache directory. +- Network mode without `PROJNET_GRID_CACHE` (or `cacheDirectory`) does not have a + writable destination, so downloads will not succeed. +- The built-in default fetch client is a no-op; network fetching only happens when + an HTTP fetch client is configured explicitly or created from the environment. diff --git a/docs/projection-coverage.md b/docs/projection-coverage.md new file mode 100644 index 00000000..97c437dd --- /dev/null +++ b/docs/projection-coverage.md @@ -0,0 +1,141 @@ +# Projection Coverage Matrix (PROJ parity vs ProjNET) + +This document summarizes the audited projection feature-parity status between upstream +PROJ identifiers and the ProjNET implementation contained in this project. + +## Scope + +- PROJ reference surface: the audited set of upstream `PROJ_HEAD(...)` projection identifiers + captured by this matrix. +- .NET implementation source: `src\ProjNet\CoordinateSystems\Projections\*.cs`, + `src\ProjNet\CoordinateSystems\Projections\ProjectionsRegistry.cs`, and the runtime + dispatch paths in `src\ProjNet\CoordinateSystems\Transformations\ProjPipelineMathTransformFactory*.cs`. +- Status categories: + - **Implemented**: projection class exists and aliases are registered in `ProjectionsRegistry`. + - **Missing**: no registered .NET projection mapping yet. + +## Current summary + +- C++ `PROJ_HEAD` identifiers discovered: **186**. +- Projection aliases registered in `ProjectionsRegistry`: **321**. +- Runtime pipeline conversion dispatches (`ProjPipelineMathTransformFactory`): **23**. +- Classified as projection-registry backed: **160**. +- Classified as runtime-pipeline backed: **18**. +- Initially unresolved after direct registry+pipeline lookup: **8** (`affine`, `cart`, `geoc`, `geocent`, `geogoffset`, `molobadekas`, `pop`, `push`). +- Refined resolution: + - implemented via non-dispatch runtime/factory paths: `affine`, `cart`, `geocent` + - direct `+proj` dispatcher gaps: `push`, `pop`, `geogoffset`, `molobadekas`, `geoc` + +## Implemented projection families in ProjNET + +| Projection family | Registered PROJ/alias codes | +| --- | --- | +| Mercator | `mercator`, `mercator_1sp`, `mercator_2sp`, `mercator_(variant_a)`, `mercator_(variant_b)` | +| Mercator Auxiliary Sphere | `mercator_auxiliary_sphere` | +| Pseudo Mercator | `pseudo_mercator`, `popular_visualisation_pseudo_mercator`, `google_mercator`, `web_mercator` | +| Miller Cylindrical | `miller_cylindrical`, `miller`, `mill` | +| Equidistant Cylindrical | `equidistant_cylindrical`, `equirectangular`, `plate_carree`, `eqc` | +| Lat/Long identity | `latlong`, `longlat` | +| Transverse Cylindrical Equal Area | `transverse_cylindrical_equal_area`, `tcea` | +| Cylindrical Equal Area | `cylindrical_equal_area`, `lambert_cylindrical_equal_area`, `equal_area_cylindrical`, `cea` | +| Loximuthal | `loximuthal`, `loxim` | +| Patterson | `patterson` | +| Transverse Mercator | `transverse_mercator`, `transverse_mercator_south_oriented`, `gauss_kruger`, `utm`, `etmerc`, `extended_transverse_mercator` | +| Swiss Oblique Mercator | `swiss_oblique_mercator`, `somerc` | +| Albers Equal Area | `albers`, `albers_conic_equal_area` | +| Krovak | `krovak` | +| Polyconic | `polyconic` | +| Lambert Conformal Conic | `lambert_conformal_conic`, `lambert_conformal_conic_1sp`, `lambert_conformal_conic_2sp`, `lambert_conformal_conic_2sp_belgium`, `lambert_conic_conformal_(1sp)`, `lambert_conic_conformal_(2sp)`, `lambert_tangential_conformal_conic_projection` | +| Equidistant Conic | `equidistant_conic`, `equidistant_conic_(spherical)`, `eqdc` | +| Bonne | `bonne` | +| Perspective Conic | `perspective_conic`, `pconic` | +| Lambert Azimuthal Equal Area | `lambert_azimuthal_equal_area` | +| Cassini-Soldner | `cassini_soldner` | +| Hotine Oblique Mercator | `hotine_oblique_mercator`, `hotine_oblique_mercator_azimuth_center` | +| Oblique Mercator | `oblique_mercator` | +| Oblique Stereographic | `oblique_stereographic` | +| Orthographic | `orthographic` | +| Near-sided Perspective / Tilted Perspective | `near_sided_perspective`, `nsper`, `tilted_perspective`, `tpers` | +| Laborde | `laborde`, `labrd` | +| Gauss-Schreiber Transverse Mercator | `gauss_schreiber_transverse_mercator`, `gauss_laborde_reunion`, `gstmerc` | +| Geostationary Satellite | `geostationary_satellite`, `geos` | +| New Zealand Map Grid | `new_zealand_map_grid`, `nzmg` | +| Polar Stereographic | `polar_stereographic` | +| Equal Earth | `equal_earth`, `eqearth` | +| Aitoff | `aitoff` | +| van der Grinten | `vandg`, `vandergrinten`, `van_der_grinten`, `van_der_grinten_i` | +| Winkel I | `wink1`, `winkel_i` | +| Winkel II | `wink2`, `winkel_ii` | +| Winkel Tripel | `wintri`, `winkel_tripel` | +| Hammer | `hammer` | +| Sinusoidal | `sinu`, `sinusoidal` | +| Goode Homolosine | `goode`, `goode_homolosine` | +| Interrupted Goode Homolosine | `igh`, `interrupted_goode_homolosine` | +| HEALPix | `healpix` | +| Natural Earth | `natural_earth`, `natearth` | +| Natural Earth 2 | `natural_earth_2`, `natural_earth2`, `natearth2` | +| Robinson | `robinson`, `robin` | +| Mollweide | `mollweide`, `moll` | +| Azimuthal Equidistant | `azimuthal_equidistant`, `aeqd` | +| Gnomonic | `gnomonic`, `gnom` | +| Spherical Cross-Track Height (runtime 3D) | `sch`, `spherical_cross_track_height` | + +## Coverage classification (latest audit) + +### Projection registry backed (class mapping) + +These are mapped through `Register("...")` aliases in `ProjectionsRegistry` and instantiate through projection classes. + +- Count: **160 `PROJ_HEAD` identifiers**. + +### Runtime pipeline backed (conversion/transform dispatch) + +These are mapped through `projCode.Equals("...")` dispatch in `ProjPipelineMathTransformFactory.TryCreateStepTransform`. + +- Count: **18 `PROJ_HEAD` identifiers**. +- Includes: `axisswap`, `gridshift` family, `defmodel`, `deformation`, `tinshift`, `topocentric`, `vertoffset`, `helmert`, `molodensky`, `ob_tran`, `sch`, `set`, `unitconvert`, `pipeline`. + +### Factory/runtime-only support (not direct `+proj` dispatch) + +- `affine` is implemented by `AffineTransform` and WKT transform parsing paths. +- `cart`/`geocent` are implemented through geocentric conversion composition in `CoordinateTransformationFactory` and `GeocentricTransform`. + +### Direct `+proj` dispatch gaps + +The following `PROJ_HEAD` identifiers are not currently dispatched as direct `+proj` tokens in `ProjPipelineMathTransformFactory`: + +- `push` +- `pop` +- `geogoffset` +- `molobadekas` +- `geoc` + +Additional notable direct-dispatch gaps with existing runtime/factory support: + +- `affine` (runtime class + WKT path exists) +- `cart` / `geocent` (geocentric conversion path exists via factory composition) + +These are runtime operation-dispatch parity items, not projection-class registration items. + +## Remaining gap table (M1 consolidated) + +| Identifier | C++ reference surface | .NET status | Classification | Notes | +| --- | --- | --- | --- | --- | +| `push` | pipeline stack op (`PROJ_HEAD`) | no direct pipeline dispatch | gap | Missing `+proj=push` branch in `TryCreateStepTransform`. | +| `pop` | pipeline stack op (`PROJ_HEAD`) | no direct pipeline dispatch | gap | Missing `+proj=pop` branch in `TryCreateStepTransform`. | +| `geogoffset` | transform op (`affine.cpp`) | no direct pipeline dispatch | gap | No `+proj=geogoffset` dispatch branch found. | +| `molobadekas` | transform op (`helmert.cpp`) | no direct pipeline dispatch | partial | Helmert runtime exists; explicit `+proj=molobadekas` token dispatch is absent. | +| `geoc` | geocentric latitude op (`PROJ_HEAD`) | no direct pipeline dispatch | gap | No explicit `+proj=geoc` token branch found. | +| `affine` | transform op (`affine.cpp`) | runtime class exists | partial | `AffineTransform` + WKT path available, but no direct `+proj=affine` dispatch. | +| `cart` | geodetic/cartesian conversion op (`PROJ_HEAD`) | runtime/factory path exists | partial | `GeocentricTransform` + factory composition available, but no direct `+proj=cart` dispatch. | +| `geocent` | geocentric conversion op (`PROJ_HEAD`) | runtime/factory path exists | partial | Geocentric runtime exists, but no direct `+proj=geocent` dispatch. | + +## Validation linkage + +Recent parity-related validation evidence: + +- Build baseline: `dotnet build ProjNet4GeoAPI.sln -c Release` +- Test baseline: `dotnet test --project test/ProjNet.Tests/ProjNET.Tests.csproj -c Release --no-build` +- Runtime GIE coverage harness: `GieBuiltinsTheoryTests` +- Dedicated runtime transform support checks for `ob_tran` and conversion pipeline operations. + diff --git a/dotnet-tools.json b/dotnet-tools.json new file mode 100644 index 00000000..7bbd778a --- /dev/null +++ b/dotnet-tools.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-stryker": { + "version": "4.14.1", + "commands": [ + "dotnet-stryker" + ], + "rollForward": false + }, + "nbgv": { + "version": "3.9.50", + "commands": [ + "nbgv" + ], + "rollForward": false + }, + "dotnet-coverage": { + "version": "18.6.2", + "commands": [ + "dotnet-coverage" + ], + "rollForward": false + } + } +} diff --git a/global.json b/global.json new file mode 100644 index 00000000..6b551f16 --- /dev/null +++ b/global.json @@ -0,0 +1,9 @@ +{ + "sdk": { + "version": "10.0.201", + "rollForward": "latestFeature" + }, + "test": { + "runner": "Microsoft.Testing.Platform" + } +} diff --git a/src/Directory.Build.props b/src/Directory.Build.props index aaad2cc1..9c7af68f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,86 +7,7 @@ true - - - true - true - ci.travis.$(TRAVIS_BUILD_NUMBER) - - true - - - - - true - true - ci.teamcity.$(BUILD_NUMBER) - - - - - true - true - ci.appveyor.$(APPVEYOR_BUILD_NUMBER) - - - - - true - true - ci.github.$(GITHUB_RUN_ID) - - true - - - - - - local - - - - - 2 - 2 - 0 - - $([System.DateTime]::UtcNow.Ticks) - $([System.DateTime]::op_Subtraction($([System.DateTime]::new($(NtsBuildTimestamp)).Date),$([System.DateTime]::new(621355968000000000))).TotalDays.ToString("00000")) - - - $([System.DateTime]::new($(NtsBuildTimestamp)).TimeOfDay.TotalMinutes.ToString("0000")) - - $(NtsMajorVersion).$(NtsMinorVersion).$(NtsPatchVersion) - pre.$(NtsDaysSinceEpoch)$(NtsMinutesSinceStartOfUtcDay)+$(NtsBuildMetadata) - - - - - - $(NtsMajorVersion).0.0.0 - $(NtsMajorVersion).$(NtsMinorVersion).$(NtsPatchVersion).$(NtsBuildNumber) - NetTopologySuite - Team $(Company) diff --git a/src/ProjNet.Benchmark/BenchmarkFixtureResolver.cs b/src/ProjNet.Benchmark/BenchmarkFixtureResolver.cs new file mode 100644 index 00000000..22956705 --- /dev/null +++ b/src/ProjNet.Benchmark/BenchmarkFixtureResolver.cs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.IO; + +/// +/// Resolves repository-local benchmark fixtures from BenchmarkDotNet child-process output directories. +/// +internal static class BenchmarkFixtureResolver +{ + /// + /// Resolves a test grid fixture path under test\ProjNet.Tests\Fixtures\grids. + /// + /// Fixture file name. + /// The absolute grid fixture path. + internal static string ResolveGridPath(string fileName) + { + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "grids", fileName); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate local test grid fixture under test\\ProjNet.Tests\\Fixtures\\grids.", fileName); + } +} diff --git a/src/ProjNet.Benchmark/BenchmarkPipelineTransformFactory.cs b/src/ProjNet.Benchmark/BenchmarkPipelineTransformFactory.cs new file mode 100644 index 00000000..029ba8be --- /dev/null +++ b/src/ProjNet.Benchmark/BenchmarkPipelineTransformFactory.cs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Reflection; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Creates pipeline transforms for benchmark scenarios by calling the internal runtime pipeline factory. +/// +internal static class BenchmarkPipelineTransformFactory +{ + private static readonly MethodInfo CreatePipelineTransformMethod = ResolveCreatePipelineTransformMethod(); + + /// + /// Creates a runtime pipeline transform for the supplied PROJ operation string. + /// + /// The PROJ pipeline or single-step operation string to resolve. + /// The created math transform. + public static MathTransform Create(string operation) + { + ArgumentNullException.ThrowIfNull(operation); + + object?[] arguments = [operation, null, null]; + bool ok = (bool)(CreatePipelineTransformMethod.Invoke(null, arguments) ?? false); + if (!ok) + { + throw new InvalidOperationException(arguments[2] as string ?? "Pipeline transform creation failed."); + } + + return arguments[1] as MathTransform + ?? throw new InvalidOperationException("Pipeline transform factory returned null transform."); + } + + private static MethodInfo ResolveCreatePipelineTransformMethod() + { + Type pipelineFactoryType = typeof(MathTransform).Assembly.GetType( + "ProjNet.CoordinateSystems.Transformations.ProjPipelineMathTransformFactory", + throwOnError: true) + ?? throw new InvalidOperationException("Unable to resolve ProjPipelineMathTransformFactory type."); + + return pipelineFactoryType.GetMethod( + "TryCreateMathTransform", + BindingFlags.Static | BindingFlags.NonPublic, + binder: null, + types: + [ + typeof(string), + typeof(MathTransform).MakeByRefType(), + typeof(string).MakeByRefType(), + ], + modifiers: null) + ?? throw new InvalidOperationException("Unable to resolve ProjPipelineMathTransformFactory.TryCreateMathTransform."); + } +} diff --git a/src/ProjNet.Benchmark/CatalogFirstCoordinateLookupBenchmarks.cs b/src/ProjNet.Benchmark/CatalogFirstCoordinateLookupBenchmarks.cs new file mode 100644 index 00000000..454bf810 --- /dev/null +++ b/src/ProjNet.Benchmark/CatalogFirstCoordinateLookupBenchmarks.cs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using ProjNet; +using ProjNet.CoordinateSystems; + +/// +/// Measures cold-start latency for the first EPSG coordinate system lookup from the managed catalog. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for explicit invocation from Program and benchmark tooling stability.")] +[MemoryDiagnoser] +[SimpleJob(RunStrategy.ColdStart, launchCount: 12, warmupCount: 0, iterationCount: 1)] +public class CatalogFirstCoordinateLookupBenchmarks +{ + /// + /// Resolves EPSG:4326 from a fresh instance. + /// + /// The resolved coordinate system. + [Benchmark(Baseline = true)] + public CoordinateSystem FirstGetCoordinateSystem4326() + { + var services = new CoordinateSystemServices(); + CoordinateSystem? coordinateSystem = services.GetCoordinateSystem(4326); + return coordinateSystem is null ? throw new InvalidOperationException("EPSG:4326 lookup returned null.") : coordinateSystem; + } +} diff --git a/src/ProjNet.Benchmark/CatalogFirstTransformationLookupBenchmarks.cs b/src/ProjNet.Benchmark/CatalogFirstTransformationLookupBenchmarks.cs new file mode 100644 index 00000000..af3f62cd --- /dev/null +++ b/src/ProjNet.Benchmark/CatalogFirstTransformationLookupBenchmarks.cs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using ProjNet; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Measures cold-start latency for first-time EPSG operation-resolution paths that can touch operation catalogs. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for explicit invocation from Program and benchmark tooling stability.")] +[MemoryDiagnoser] +[SimpleJob(RunStrategy.ColdStart, launchCount: 12, warmupCount: 0, iterationCount: 1)] +public class CatalogFirstTransformationLookupBenchmarks +{ + /// + /// Executes the curated cold-start benchmark path once and verifies that it returns a transformation. + /// + public static void Validate() + { + _ = new CatalogFirstTransformationLookupBenchmarks().FirstCreateTransformation4326To3857(); + } + + /// + /// Creates an EPSG:4326 to EPSG:3857 transformation from a fresh instance. + /// + /// The resolved transformation. + [Benchmark(Baseline = true)] + public ICoordinateTransformation FirstCreateTransformation4326To3857() + { + var services = new CoordinateSystemServices(); + ICoordinateTransformation? transformation = services.CreateTransformation(4326, 3857); + return transformation is null + ? throw new InvalidOperationException("EPSG:4326->3857 transformation lookup returned null.") + : transformation; + } +} diff --git a/src/ProjNet.Benchmark/CatalogRetainedMemoryBenchmarks.cs b/src/ProjNet.Benchmark/CatalogRetainedMemoryBenchmarks.cs new file mode 100644 index 00000000..eafe49ed --- /dev/null +++ b/src/ProjNet.Benchmark/CatalogRetainedMemoryBenchmarks.cs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using ProjNet; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Estimates retained managed heap growth after first-time transformation lookup and catalog initialization. +/// +/// +/// This benchmark complements MemoryDiagnoser allocation metrics with a coarse retained-heap delta. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for explicit invocation from Program and benchmark tooling stability.")] +[MemoryDiagnoser] +[SimpleJob(RunStrategy.ColdStart, launchCount: 12, warmupCount: 0, iterationCount: 1)] +public class CatalogRetainedMemoryBenchmarks +{ + /// + /// Computes managed heap growth after creating an EPSG:4326 to EPSG:3857 transformation. + /// + /// Estimated retained managed bytes after first lookup. + [Benchmark(Baseline = true)] + public long ManagedHeapIncreaseAfterFirstTransformationLookup() + { + ForceFullCollection(); + long before = GC.GetTotalMemory(forceFullCollection: true); + + var services = new CoordinateSystemServices(); + ICoordinateTransformation? transformationCandidate = services.CreateTransformation(4326, 3857); + if (transformationCandidate is null) + { + throw new InvalidOperationException("EPSG:4326->3857 transformation lookup returned null."); + } + + ICoordinateTransformation transformation = transformationCandidate; + + ForceFullCollection(); + long after = GC.GetTotalMemory(forceFullCollection: true); + + GC.KeepAlive(services); + GC.KeepAlive(transformation); + return Math.Max(0L, after - before); + } + + private static void ForceFullCollection() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } +} diff --git a/src/ProjNet.Benchmark/GeoTiffLoaderBenchmarks.cs b/src/ProjNet.Benchmark/GeoTiffLoaderBenchmarks.cs new file mode 100644 index 00000000..2770dcfd --- /dev/null +++ b/src/ProjNet.Benchmark/GeoTiffLoaderBenchmarks.cs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Measures GeoTIFF grid-loader throughput for representative fixtures that exercise GDAL metadata parsing. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +[SimpleJob] +public class GeoTiffLoaderBenchmarks +{ + private string horizontalGridPath = string.Empty; + private string verticalGridPath = string.Empty; + + /// + /// Resolves representative GeoTIFF fixtures once per benchmark run. + /// + [GlobalSetup] + public void GlobalSetup() + { + this.horizontalGridPath = BenchmarkFixtureResolver.ResolveGridPath("test_hgrid.tif"); + this.verticalGridPath = BenchmarkFixtureResolver.ResolveGridPath("test_vgrid_uint16_with_scale_offset.tif"); + } + + /// + /// Measures loading a representative horizontal GeoTIFF grid with metadata-defined interpolation settings. + /// + /// The number of loaded grid pages. + [Benchmark(Baseline = true)] + public int LoadHorizontalGrid() + { + return GeoTiffGridLoader.LoadHorizontal(this.horizontalGridPath).Count; + } + + /// + /// Measures loading a representative vertical GeoTIFF grid with scale/offset metadata. + /// + /// The number of loaded grid pages. + [Benchmark] + public int LoadVerticalGridWithScaleOffset() + { + return GeoTiffGridLoader.LoadVertical(this.verticalGridPath).Count; + } +} diff --git a/src/ProjNet.Benchmark/InfoCloneBenchmarks.cs b/src/ProjNet.Benchmark/InfoCloneBenchmarks.cs new file mode 100644 index 00000000..83e3d41b --- /dev/null +++ b/src/ProjNet.Benchmark/InfoCloneBenchmarks.cs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems; + +/// +/// Measures base-typed metadata cloning for representative model types. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +[SimpleJob] +public class InfoCloneBenchmarks +{ + private Info genericUnit = null!; + private Info projectedCoordinateSystem = null!; + + /// + /// Captures representative info-backed model objects once per run. + /// + [GlobalSetup] + public void GlobalSetup() + { + this.projectedCoordinateSystem = ProjectedCoordinateSystem.WebMercator; + this.genericUnit = new Unit("unity", 1d); + } + + /// + /// Measures authority cloning for a base-typed projected coordinate system. + /// + /// A cloned projected coordinate system. + [Benchmark(Baseline = true)] + public Info CloneProjectedCoordinateSystemAuthority() + { + return this.projectedCoordinateSystem.WithAuthority("TEST", 5001); + } + + /// + /// Measures name cloning for a base-typed projected coordinate system. + /// + /// A cloned projected coordinate system. + [Benchmark] + public Info CloneProjectedCoordinateSystemName() + { + return this.projectedCoordinateSystem.WithName("Projected clone"); + } + + /// + /// Measures authority cloning for a base-typed generic unit. + /// + /// A cloned generic unit. + [Benchmark] + public Info CloneGenericUnitAuthority() + { + return this.genericUnit.WithAuthority("TEST", 6001); + } + + /// + /// Measures name cloning for a base-typed generic unit. + /// + /// A cloned generic unit. + [Benchmark] + public Info CloneGenericUnitName() + { + return this.genericUnit.WithName("custom unity"); + } +} diff --git a/src/ProjNet.Benchmark/PerformanceTests.cs b/src/ProjNet.Benchmark/PerformanceTests.cs index ad0e2586..621fdc96 100644 --- a/src/ProjNet.Benchmark/PerformanceTests.cs +++ b/src/ProjNet.Benchmark/PerformanceTests.cs @@ -1,4 +1,11 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; using System.Linq; @@ -10,157 +17,197 @@ using ProjNet.CoordinateSystems.Transformations; using ProjNet.Geometries; -namespace ProjNet.Benchmark +/// +/// Benchmarks in-place coordinate transformation throughput across structure-of-arrays and array-of-struct layouts. +/// +/// +/// These scenarios focus on memory-layout effects and transform invocation styles. They complement +/// which focuses on EPSG-pipeline parity with PROJ's benchmark scenarios. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for explicit invocation from Program and benchmark tooling stability.")] +public class PerformanceTests { - public class PerformanceTests - { - private static readonly MathTransform WGS84ToWebMercator = new CoordinateTransformationFactory().CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, ProjectedCoordinateSystem.WebMercator).MathTransform; + private static readonly MathTransform WGS84ToWebMercator = CreateWgs84ToWebMercator(); - private int _cnt; + private int cnt; - private double[] _xs; + private double[] xs = []; - private double[] _ys; + private double[] ys = []; - private XY[] _xys; + private XY[] xys = []; - private XYZ[] _xyzs; + private XYZ[] xyzs = []; - private double[] _xsCopy; + private double[] xsCopy = []; - private double[] _ysCopy; + private double[] ysCopy = []; - private XY[] _xysCopy; + private XY[] xysCopy = []; - private XYZ[] _xyzsCopy; + private XYZ[] xyzsCopy = []; - public static void Validate() - { - var instance = new PerformanceTests(); - instance.GlobalSetup(); + /// + /// Executes all benchmark entry points once and verifies numerical consistency across variants. + /// + public static void Validate() + { + var instance = new PerformanceTests(); + instance.GlobalSetup(); - instance.SoAOneByOne(); - var firstOutput = instance._xsCopy.Zip(instance._ysCopy, (x, y) => (x, y)).ToArray(); + instance.SoAOneByOne(); + (double X, double Y)[] firstOutput = [.. instance.xsCopy.Zip(instance.ysCopy, (x, y) => (X: x, Y: y))]; - for (int i = 0; i < firstOutput.Length; i++) + for (int i = 0; i < firstOutput.Length; i++) + { + if (firstOutput[i].Equals((instance.xys[i].X, instance.xys[i].Y))) { - if (firstOutput[i].Equals((instance._xys[i].X, instance._xys[i].Y))) - { - throw new Exception("Validation failure: transformer isn't actually transforming."); - } + throw new InvalidOperationException("Validation failure: transformer isn't actually transforming."); } + } - instance.SoABatched(); - Validate(instance._xsCopy.Zip(instance._ysCopy, (x, y) => (x, y)).ToArray()); - - instance.TightAoSOneByOne(); - Validate(Array.ConvertAll(instance._xysCopy, xy => (xy.X, xy.Y))); + instance.SoABatched(); + Validate(instance.xsCopy.Zip(instance.ysCopy, (x, y) => (X: x, Y: y)).ToArray()); - instance.TightAoSBatched(); - Validate(Array.ConvertAll(instance._xysCopy, xy => (xy.X, xy.Y))); + instance.TightAoSOneByOne(); + Validate(Array.ConvertAll(instance.xysCopy, xy => (xy.X, xy.Y))); - instance.LooserAoSOneByOne(); - Validate(Array.ConvertAll(instance._xyzsCopy, xyz => (xyz.X, xyz.Y))); + instance.TightAoSBatched(); + Validate(Array.ConvertAll(instance.xysCopy, xy => (xy.X, xy.Y))); - instance.LooserAoSBatched(); - Validate(Array.ConvertAll(instance._xyzsCopy, xyz => (xyz.X, xyz.Y))); + instance.LooserAoSOneByOne(); + Validate(Array.ConvertAll(instance.xyzsCopy, xyz => (xyz.X, xyz.Y))); - void Validate(ReadOnlySpan<(double x, double y)> nextOutput) - { - if (!nextOutput.SequenceEqual(firstOutput)) - { - throw new Exception("Validation failure: some transform method is giving different results than another."); - } - } - } + instance.LooserAoSBatched(); + Validate(Array.ConvertAll(instance.xyzsCopy, xyz => (xyz.X, xyz.Y))); - [GlobalSetup] - public void GlobalSetup() + void Validate(ReadOnlySpan<(double X, double Y)> nextOutput) { - string currentFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - string fullPathToData = Path.Combine(currentFolderPath, "coords.dat.gz"); - using (var reader = new BinaryReader(new GZipStream(File.OpenRead(fullPathToData), CompressionMode.Decompress))) + if (!nextOutput.SequenceEqual(firstOutput)) { - _cnt = reader.ReadInt32(); - - _xs = new double[_cnt]; - _ys = new double[_cnt]; - _xys = new XY[_cnt]; - _xyzs = new XYZ[_cnt]; - - for (int i = 0; i < _cnt; i++) - { - _xs[i] = _xys[i].X = _xyzs[i].X = reader.ReadDouble(); - } - - for (int i = 0; i < _cnt; i++) - { - _ys[i] = _xys[i].Y = _xyzs[i].Y = reader.ReadDouble(); - } + throw new InvalidOperationException("Validation failure: some transform method is giving different results than another."); } - - // transforms happen in-place, so at the start of every iteration, we copy the source - // coordinate data to these throwaway arrays in order to be able to repeat the test - // without allocating anything. this slightly hurts accuracy, but the effect appears to - // be less than 5% of the total test's time, and [IterationSetup] / [IterationCleanup] - // aren't designed for the kinds of benchmarks we're running here. - _xsCopy = new double[_cnt]; - _ysCopy = new double[_cnt]; - _xysCopy = new XY[_cnt]; - _xyzsCopy = new XYZ[_cnt]; } + } - [Benchmark] - public void SoAOneByOne() + /// + /// Loads benchmark coordinate data and prepares mutable working buffers. + /// + [GlobalSetup] + public void GlobalSetup() + { + string? currentFolderPathCandidate = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + if (currentFolderPathCandidate is null) { - _xs.CopyTo(_xsCopy.AsSpan()); - _ys.CopyTo(_ysCopy.AsSpan()); - for (int i = 0; i < _cnt; i++) - { - WGS84ToWebMercator.Transform(ref _xsCopy[i], ref _ysCopy[i]); - } + throw new InvalidOperationException("Unable to resolve benchmark assembly directory."); } - [Benchmark] - public void SoABatched() + string currentFolderPath = currentFolderPathCandidate; + string fullPathToData = Path.Combine(currentFolderPath, "coords.dat.gz"); + using var reader = new BinaryReader(new GZipStream(File.OpenRead(fullPathToData), CompressionMode.Decompress)); + this.cnt = reader.ReadInt32(); + + this.xs = new double[this.cnt]; + this.ys = new double[this.cnt]; + this.xys = new XY[this.cnt]; + this.xyzs = new XYZ[this.cnt]; + + for (int i = 0; i < this.cnt; i++) { - _xs.CopyTo(_xsCopy.AsSpan()); - _ys.CopyTo(_ysCopy.AsSpan()); - WGS84ToWebMercator.Transform(_xsCopy, _ysCopy); + this.xs[i] = this.xys[i].X = this.xyzs[i].X = reader.ReadDouble(); } - [Benchmark] - public void TightAoSOneByOne() + for (int i = 0; i < this.cnt; i++) { - _xys.CopyTo(_xysCopy.AsSpan()); - for (int i = 0; i < _cnt; i++) - { - WGS84ToWebMercator.Transform(ref _xysCopy[i].X, ref _xysCopy[i].Y); - } + this.ys[i] = this.xys[i].Y = this.xyzs[i].Y = reader.ReadDouble(); } - [Benchmark] - public void TightAoSBatched() + // transforms happen in-place, so at the start of every iteration, we copy the source + // coordinate data to these throwaway arrays in order to be able to repeat the test + // without allocating anything. this slightly hurts accuracy, but the effect appears to + // be less than 5% of the total test's time, and [IterationSetup] / [IterationCleanup] + // aren't designed for the kinds of benchmarks we're running here. + this.xsCopy = new double[this.cnt]; + this.ysCopy = new double[this.cnt]; + this.xysCopy = new XY[this.cnt]; + this.xyzsCopy = new XYZ[this.cnt]; + } + + /// + /// Measures one-by-one transforms for separate X/Y arrays (structure-of-arrays layout). + /// + [Benchmark] + public void SoAOneByOne() + { + this.xs.CopyTo(this.xsCopy.AsSpan()); + this.ys.CopyTo(this.ysCopy.AsSpan()); + for (int i = 0; i < this.cnt; i++) { - _xys.CopyTo(_xysCopy.AsSpan()); - WGS84ToWebMercator.Transform(_xysCopy); + WGS84ToWebMercator.Transform(ref this.xsCopy[i], ref this.ysCopy[i]); } + } + + /// + /// Measures batched transforms for separate X/Y arrays (structure-of-arrays layout). + /// + [Benchmark] + public void SoABatched() + { + this.xs.CopyTo(this.xsCopy.AsSpan()); + this.ys.CopyTo(this.ysCopy.AsSpan()); + WGS84ToWebMercator.Transform(this.xsCopy, this.ysCopy); + } - [Benchmark] - public void LooserAoSOneByOne() + /// + /// Measures one-by-one transforms for tightly packed XY structs (array-of-struct layout). + /// + [Benchmark] + public void TightAoSOneByOne() + { + this.xys.CopyTo(this.xysCopy.AsSpan()); + for (int i = 0; i < this.cnt; i++) { - _xyzs.CopyTo(_xyzsCopy.AsSpan()); - for (int i = 0; i < _cnt; i++) - { - WGS84ToWebMercator.Transform(ref _xyzsCopy[i].X, ref _xyzsCopy[i].Y); - } + WGS84ToWebMercator.Transform(ref this.xysCopy[i].X, ref this.xysCopy[i].Y); } + } - [Benchmark] - public void LooserAoSBatched() + /// + /// Measures batched transforms for tightly packed XY structs (array-of-struct layout). + /// + [Benchmark] + public void TightAoSBatched() + { + this.xys.CopyTo(this.xysCopy.AsSpan()); + WGS84ToWebMercator.Transform(this.xysCopy); + } + + /// + /// Measures one-by-one transforms for looser XYZ structs when only X/Y are transformed. + /// + [Benchmark] + public void LooserAoSOneByOne() + { + this.xyzs.CopyTo(this.xyzsCopy.AsSpan()); + for (int i = 0; i < this.cnt; i++) { - _xyzs.CopyTo(_xyzsCopy.AsSpan()); - WGS84ToWebMercator.Transform(_xyzsCopy); + WGS84ToWebMercator.Transform(ref this.xyzsCopy[i].X, ref this.xyzsCopy[i].Y); } } + + /// + /// Measures batched transforms for looser XYZ structs when only X/Y are transformed. + /// + [Benchmark] + public void LooserAoSBatched() + { + this.xyzs.CopyTo(this.xyzsCopy.AsSpan()); + WGS84ToWebMercator.Transform(this.xyzsCopy); + } + + private static MathTransform CreateWgs84ToWebMercator() + { + ICoordinateTransformation transformation = new CoordinateTransformationFactory() + .CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, ProjectedCoordinateSystem.WebMercator); + return transformation.MathTransform; + } } diff --git a/src/ProjNet.Benchmark/Program.cs b/src/ProjNet.Benchmark/Program.cs index 0c14886c..1c9964b8 100644 --- a/src/ProjNet.Benchmark/Program.cs +++ b/src/ProjNet.Benchmark/Program.cs @@ -1,41 +1,139 @@ -using BenchmarkDotNet.Running; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -namespace ProjNet.Benchmark +namespace ProjNet.Benchmark; + +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Running; + +/// +/// Entry point for benchmark validation and BenchmarkDotNet execution. +/// +/// +/// The startup sequence first runs deterministic sanity validation, then delegates to BenchmarkDotNet +/// for full benchmark execution and reporting. +/// +internal static class Program { - class Program + private static void Main(string[] args) { - static void Main() + bool curatedValidation = ContainsArgument(args, "--curated"); + string[] benchmarkArguments = RemoveArgument(args, "--curated"); + + if (ContainsArgument(args, "--validate")) { - PerformanceTests.Validate(); - BenchmarkRunner.Run(); + if (curatedValidation) + { + ValidateCuratedBenchmarks(); + } + else + { + ValidateBenchmarks(); + } + + return; } - // here's how I generated coords.dat.gz (set TestDataPath and add references + usings, of course): -#if false - static void GenerateTestData() + if (!IsBenchmarkChildProcess(args)) { - const string TestDataPath = @"C:\Path\To\TestData"; - var lst = new List(); - foreach (var fl in new[] { "africa.wkt", "europe.wkt", "world.wkt" }) + if (curatedValidation) + { + ValidateCuratedBenchmarks(); + } + else { - var wkt = new WKTFileReader(Path.Combine(TestDataPath, fl), new WKTReader()); - lst.AddRange(wkt.Read().SelectMany(g => g.Coordinates)); + ValidateBenchmarks(); } + } + + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(benchmarkArguments); + } - using (var writer = new BinaryWriter(new GZipStream(File.Create(Path.Combine(TestDataPath, "coords.dat.gz")), CompressionLevel.Optimal))) + private static void ValidateCuratedBenchmarks() + { + CatalogFirstTransformationLookupBenchmarks.Validate(); + WktParsingBenchmarks.Validate(); + ProjectionTransformBenchmarks.Validate(); + ProjParityBenchmarks.Validate(); + TransformationFactoryBenchmarks.Validate(); + } + + private static void ValidateBenchmarks() + { + PerformanceTests.Validate(); + CatalogFirstTransformationLookupBenchmarks.Validate(); + WktParsingBenchmarks.Validate(); + ProjectionTransformBenchmarks.Validate(); + ProjParityBenchmarks.Validate(); + TransformationFactoryBenchmarks.Validate(); + ProjectionSinglePointBenchmarks.Validate(); + } + + private static bool IsBenchmarkChildProcess(string[] args) + { + for (int i = 0; i < args.Length; i++) + { + if (string.Equals(args[i], "--benchmarkName", StringComparison.Ordinal)) { - writer.Write(lst.Count); - foreach (var coord in lst) - { - writer.Write(coord.X); - } - - foreach (var coord in lst) - { - writer.Write(coord.Y); - } + return true; } } -#endif + + return false; } + + private static string[] RemoveArgument(string[] args, string argument) + { + var filteredArguments = new List(args.Length); + for (int i = 0; i < args.Length; i++) + { + if (!string.Equals(args[i], argument, StringComparison.Ordinal)) + { + filteredArguments.Add(args[i]); + } + } + + return filteredArguments.ToArray(); + } + + private static bool ContainsArgument(string[] args, string argument) + { + for (int i = 0; i < args.Length; i++) + { + if (string.Equals(args[i], argument, StringComparison.Ordinal)) + { + return true; + } + } + + return false; + } + + // here's how I generated coords.dat.gz (set TestDataPath and add references + usings, of course): +#if false + private static void GenerateTestData() + { + const string TestDataPath = @"C:\Path\To\TestData"; + var lst = new List(); + foreach (var fl in new[] { "africa.wkt", "europe.wkt", "world.wkt" }) + { + var wkt = new WKTFileReader(Path.Combine(TestDataPath, fl), new WKTReader()); + lst.AddRange(wkt.Read().SelectMany(g => g.Coordinates)); + } + + using var writer = new BinaryWriter(new GZipStream(File.Create(Path.Combine(TestDataPath, "coords.dat.gz")), CompressionLevel.Optimal)); + writer.Write(lst.Count); + foreach (var coord in lst) + { + writer.Write(coord.X); + } + + foreach (var coord in lst) + { + writer.Write(coord.Y); + } + } +#endif } diff --git a/src/ProjNet.Benchmark/ProjNet.Benchmark.csproj b/src/ProjNet.Benchmark/ProjNet.Benchmark.csproj index 8ff4fe39..cb713e1c 100644 --- a/src/ProjNet.Benchmark/ProjNet.Benchmark.csproj +++ b/src/ProjNet.Benchmark/ProjNet.Benchmark.csproj @@ -3,18 +3,20 @@ Exe - net8 - 1701;1702;1591 + net8.0 + enable + true + $(NoWarn);1701;1702 false - + - + diff --git a/src/ProjNet.Benchmark/ProjParityBenchmarks.cs b/src/ProjNet.Benchmark/ProjParityBenchmarks.cs new file mode 100644 index 00000000..a7ef1c0c --- /dev/null +++ b/src/ProjNet.Benchmark/ProjParityBenchmarks.cs @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Benchmarks CRS-to-CRS transform throughput using scenarios aligned with PROJ's bench_proj_trans utility. +/// +/// +/// The benchmark suite focuses on forward and inverse EPSG pipeline throughput and includes a deterministic +/// noise variant analogous to PROJ's --noise-x/--noise-y options. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for explicit invocation from Program and benchmark tooling stability.")] +[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "Benchmark input generation uses deterministic pseudo-random data for repeatability and is not security-sensitive.")] +[MemoryDiagnoser] +public class ProjParityBenchmarks +{ + private const double NoiseXDegrees = 1e-4; + private const double NoiseYDegrees = 1e-4; + + private static readonly CoordinateSystemServices CoordinateSystemServices = new(); + + private static readonly ICoordinateTransformation Wgs84ToWebMercator = + CoordinateSystemServices.CreateTransformation(4326, 3857) + ?? throw new InvalidOperationException("EPSG:4326->3857 transformation lookup returned null."); + + private static readonly ICoordinateTransformation Wgs84ToUtm32N = + CoordinateSystemServices.CreateTransformation(4326, 32632) + ?? throw new InvalidOperationException("EPSG:4326->32632 transformation lookup returned null."); + + private static readonly ICoordinateTransformation Wgs84ToUtm31N = + CoordinateSystemServices.CreateTransformation(4326, 32631) + ?? throw new InvalidOperationException("EPSG:4326->32631 transformation lookup returned null."); + + private static readonly ICoordinateTransformation Utm31NToWgs84 = + CoordinateSystemServices.CreateTransformation(32631, 4326) + ?? throw new InvalidOperationException("EPSG:32631->4326 transformation lookup returned null."); + + private static readonly ICoordinateTransformation Wgs84ToLambert93 = + CoordinateSystemServices.CreateTransformation(4326, 2154) + ?? throw new InvalidOperationException("EPSG:4326->2154 transformation lookup returned null."); + + private static readonly ICoordinateTransformation Lambert93ToWgs84 = + CoordinateSystemServices.CreateTransformation(2154, 4326) + ?? throw new InvalidOperationException("EPSG:2154->4326 transformation lookup returned null."); + + private static readonly ICoordinateTransformation WebMercatorToWgs84 = + CoordinateSystemServices.CreateTransformation(3857, 4326) + ?? throw new InvalidOperationException("EPSG:3857->4326 transformation lookup returned null."); + + private double[] longitudes = []; + private double[] latitudes = []; + private double[] xBuffer = []; + private double[] yBuffer = []; + private double[] noiseX = []; + private double[] noiseY = []; + private double[] utm31ProjectedX = []; + private double[] utm31ProjectedY = []; + private double[] lambert93ProjectedX = []; + private double[] lambert93ProjectedY = []; + + /// + /// Gets or sets the number of coordinates processed per benchmark invocation. + /// + [Params(10000)] + public int PointCount { get; set; } + + /// + /// Executes one pass of every benchmark scenario and validates that all outputs are finite. + /// + public static void Validate() + { + static void EnsureFinite(double[] xs, double[] ys) + { + for (int i = 0; i < xs.Length; i++) + { + if (double.IsNaN(xs[i]) || double.IsInfinity(xs[i]) || + double.IsNaN(ys[i]) || double.IsInfinity(ys[i])) + { + throw new InvalidOperationException("Benchmark validation failed: transform produced non-finite values."); + } + } + } + + var benchmark = new ProjParityBenchmarks { PointCount = 4 }; + benchmark.GlobalSetup(); + + benchmark.Wgs84ToWebMercatorBatched(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.Wgs84ToWebMercatorOneByOne(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.Wgs84ToUtm32NBatched(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.Wgs84ToUtm31NBatched(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.Utm31NToWgs84Batched(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.Wgs84ToLambert93Batched(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.Lambert93ToWgs84Batched(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.WebMercatorToWgs84Batched(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.Wgs84ToWebMercatorBatchedWithNoise(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + } + + /// + /// Allocates and seeds deterministic coordinate buffers used by all throughput benchmarks. + /// + [GlobalSetup] + public void GlobalSetup() + { + this.longitudes = new double[this.PointCount]; + this.latitudes = new double[this.PointCount]; + this.xBuffer = new double[this.PointCount]; + this.yBuffer = new double[this.PointCount]; + this.noiseX = new double[this.PointCount]; + this.noiseY = new double[this.PointCount]; + this.utm31ProjectedX = new double[this.PointCount]; + this.utm31ProjectedY = new double[this.PointCount]; + this.lambert93ProjectedX = new double[this.PointCount]; + this.lambert93ProjectedY = new double[this.PointCount]; + + var random = new Random(20260317); + for (int i = 0; i < this.PointCount; i++) + { + this.longitudes[i] = -179d + (random.NextDouble() * 358d); + this.latitudes[i] = -85d + (random.NextDouble() * 170d); + this.noiseX[i] = (2d * random.NextDouble()) - 1d; + this.noiseY[i] = (2d * random.NextDouble()) - 1d; + } + + this.PrecomputeProjectedInput(this.utm31ProjectedX, this.utm31ProjectedY, Wgs84ToUtm31N); + this.PrecomputeProjectedInput(this.lambert93ProjectedX, this.lambert93ProjectedY, Wgs84ToLambert93); + } + + /// + /// Measures batched forward throughput for EPSG:4326 to EPSG:3857. + /// + [Benchmark(Baseline = true)] + public void Wgs84ToWebMercatorBatched() + { + this.PrepareInput(); + Wgs84ToWebMercator.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures per-point forward throughput for EPSG:4326 to EPSG:3857. + /// + [Benchmark] + public void Wgs84ToWebMercatorOneByOne() + { + this.PrepareInput(); + for (int i = 0; i < this.PointCount; i++) + { + Wgs84ToWebMercator.MathTransform.Transform(ref this.xBuffer[i], ref this.yBuffer[i]); + } + } + + /// + /// Measures batched forward throughput for EPSG:4326 to EPSG:32632. + /// + [Benchmark] + public void Wgs84ToUtm32NBatched() + { + this.PrepareInput(); + Wgs84ToUtm32N.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures batched forward throughput for EPSG:4326 to EPSG:32631. + /// + [Benchmark] + public void Wgs84ToUtm31NBatched() + { + this.PrepareInput(); + Wgs84ToUtm31N.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures batched inverse throughput for EPSG:32631 to EPSG:4326. + /// + [Benchmark] + public void Utm31NToWgs84Batched() + { + this.PrepareProjectedInput(this.utm31ProjectedX, this.utm31ProjectedY); + Utm31NToWgs84.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures batched forward throughput for EPSG:4326 to EPSG:2154. + /// + [Benchmark] + public void Wgs84ToLambert93Batched() + { + this.PrepareInput(); + Wgs84ToLambert93.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures batched inverse throughput for EPSG:2154 to EPSG:4326. + /// + [Benchmark] + public void Lambert93ToWgs84Batched() + { + this.PrepareProjectedInput(this.lambert93ProjectedX, this.lambert93ProjectedY); + Lambert93ToWgs84.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures round-trip batched throughput via EPSG:3857 and back to EPSG:4326. + /// + [Benchmark] + public void WebMercatorToWgs84Batched() + { + this.PrepareInput(); + Wgs84ToWebMercator.MathTransform.Transform(this.xBuffer, this.yBuffer); + WebMercatorToWgs84.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures EPSG:4326 to EPSG:3857 throughput with deterministic coordinate perturbation. + /// + /// + /// The perturbation model follows the PROJ benchmark pattern: + /// value + noise * uniform(-1, 1) for each axis. + /// + [Benchmark] + public void Wgs84ToWebMercatorBatchedWithNoise() + { + this.PrepareInput(); + this.ApplyNoise(this.xBuffer.AsSpan(), this.yBuffer.AsSpan(), NoiseXDegrees, NoiseYDegrees); + Wgs84ToWebMercator.MathTransform.Transform(this.xBuffer, this.yBuffer); + } + + private void PrepareInput() + { + this.longitudes.CopyTo(this.xBuffer.AsSpan()); + this.latitudes.CopyTo(this.yBuffer.AsSpan()); + } + + private void PrecomputeProjectedInput(double[] xs, double[] ys, ICoordinateTransformation forwardTransform) + { + this.longitudes.CopyTo(xs.AsSpan()); + this.latitudes.CopyTo(ys.AsSpan()); + forwardTransform.MathTransform.Transform(xs, ys); + } + + private void PrepareProjectedInput(double[] xs, double[] ys) + { + xs.CopyTo(this.xBuffer.AsSpan()); + ys.CopyTo(this.yBuffer.AsSpan()); + } + + private void ApplyNoise(Span xs, Span ys, double noiseX, double noiseY) + { + for (int i = 0; i < this.PointCount; i++) + { + if (noiseX != 0d) + { + xs[i] += noiseX * this.noiseX[i]; + } + + if (noiseY != 0d) + { + ys[i] += noiseY * this.noiseY[i]; + } + } + } +} diff --git a/src/ProjNet.Benchmark/ProjectionFactoryBenchmarks.cs b/src/ProjNet.Benchmark/ProjectionFactoryBenchmarks.cs new file mode 100644 index 00000000..1280c2cb --- /dev/null +++ b/src/ProjNet.Benchmark/ProjectionFactoryBenchmarks.cs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Measures projection creation overhead through . +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +public class ProjectionFactoryBenchmarks +{ + private ProjectionParameter[] mercatorParameters = null!; + private ProjectionParameter[] transverseMercatorParameters = null!; + private ProjectionParameter[] schParameters = null!; + + /// + /// Prepares the projection parameter sets used by the factory benchmarks. + /// + [GlobalSetup] + public void GlobalSetup() + { + this.mercatorParameters = + [ + new ProjectionParameter("semi_major", 6378137d), + new ProjectionParameter("semi_minor", 6356752.314245179d), + new ProjectionParameter("central_meridian", 0d), + new ProjectionParameter("latitude_of_origin", 0d), + new ProjectionParameter("unit", 1d), + ]; + + this.transverseMercatorParameters = + [ + new ProjectionParameter("semi_major", 6378137d), + new ProjectionParameter("semi_minor", 6356752.314245179d), + new ProjectionParameter("central_meridian", 9d), + new ProjectionParameter("latitude_of_origin", 0d), + new ProjectionParameter("scale_factor", 0.9996d), + new ProjectionParameter("false_easting", 500000d), + new ProjectionParameter("false_northing", 0d), + new ProjectionParameter("unit", 1d), + ]; + + this.schParameters = + [ + new ProjectionParameter("semi_major", 6378137d), + new ProjectionParameter("semi_minor", 6356752.314245179d), + new ProjectionParameter("plat_0", 30d), + new ProjectionParameter("plon_0", 45d), + new ProjectionParameter("phdg_0", -12d), + ]; + } + + /// + /// Creates a Mercator projection via the registry. + /// + /// The created projection transform. + [Benchmark(Baseline = true)] + public MathTransform CreateMercatorProjection() + { + return ProjectionsRegistry.CreateProjection("mercator", this.mercatorParameters); + } + + /// + /// Creates a Transverse Mercator projection via the registry. + /// + /// The created projection transform. + [Benchmark] + public MathTransform CreateTransverseMercatorProjection() + { + return ProjectionsRegistry.CreateProjection("Transverse_Mercator", this.transverseMercatorParameters); + } + + /// + /// Creates an SCH transform via the registry, exercising the list-backed special-case constructor. + /// + /// The created SCH transform. + [Benchmark] + public MathTransform CreateSchProjection() + { + return ProjectionsRegistry.CreateProjection("sch", this.schParameters); + } +} diff --git a/src/ProjNet.Benchmark/ProjectionSinglePointBenchmarks.cs b/src/ProjNet.Benchmark/ProjectionSinglePointBenchmarks.cs new file mode 100644 index 00000000..d68a070f --- /dev/null +++ b/src/ProjNet.Benchmark/ProjectionSinglePointBenchmarks.cs @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Measures single-point array-transform throughput for projection paths touched by the GIE failure-coverage fixes. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +public class ProjectionSinglePointBenchmarks +{ + private MathTransform guyouForward = null!; + private MathTransform peirceForward = null!; + private MathTransform adamsHemisphereForward = null!; + private MathTransform obliqueMercatorForward = null!; + private MathTransform obliqueMercatorNoRotationForward = null!; + private MathTransform krovakForward = null!; + private MathTransform iseaPolarForward = null!; + private MathTransform lagrangeForward = null!; + private MathTransform loximuthalForward = null!; + private MathTransform mercatorForward = null!; + private MathTransform nzmgForward = null!; + private MathTransform orthographicLocalForward = null!; + private MathTransform s2Forward = null!; + private MathTransform healpixRotatedForward = null!; + private MathTransform rhealpixForward = null!; + private MathTransform transverseMercatorExactForward = null!; + private MathTransform transverseMercatorApproxForward = null!; + private MathTransform stereographicPolarForward = null!; + private MathTransform stereographicObliqueForward = null!; + private MathTransform stereographicEquatorialForward = null!; + private MathTransform vanDerGrintenOverForward = null!; + private MathTransform laeaInverse = null!; + private MathTransform iseaPolarInverse = null!; + private MathTransform orthographicInverse = null!; + private MathTransform robinsonInverse = null!; + private MathTransform krovakInverse = null!; + private MathTransform lagrangeInverse = null!; + private MathTransform loximuthalInverse = null!; + private MathTransform mercatorInverse = null!; + private MathTransform nzmgInverse = null!; + private MathTransform s2Inverse = null!; + private MathTransform healpixRotatedInverse = null!; + private MathTransform rhealpixInverse = null!; + private MathTransform transverseMercatorExactInverse = null!; + private MathTransform transverseMercatorApproxInverse = null!; + private MathTransform stereographicPolarInverse = null!; + private MathTransform stereographicObliqueInverse = null!; + private MathTransform stereographicEquatorialInverse = null!; + private MathTransform vanDerGrintenOverInverse = null!; + + private double[] guyouInput = null!; + private double[] peirceInput = null!; + private double[] adamsHemisphereInput = null!; + private double[] obliqueMercatorInput = null!; + private double[] krovakInput = null!; + private double[] iseaPolarInput = null!; + private double[] lagrangeInput = null!; + private double[] loximuthalInput = null!; + private double[] mercatorInput = null!; + private double[] nzmgInput = null!; + private double[] orthographicLocalInput = null!; + private double[] s2Input = null!; + private double[] healpixRotatedInput = null!; + private double[] rhealpixInput = null!; + private double[] transverseMercatorInput = null!; + private double[] stereographicPolarInput = null!; + private double[] stereographicObliqueInput = null!; + private double[] stereographicEquatorialInput = null!; + private double[] vanDerGrintenOverInput = null!; + private double[] iseaPolarInverseInput = null!; + private double[] krovakInverseInput = null!; + private double[] lagrangeInverseInput = null!; + private double[] laeaInput = null!; + private double[] loximuthalInverseInput = null!; + private double[] orthographicInput = null!; + private double[] robinsonInput = null!; + private double[] mercatorInverseInput = null!; + private double[] nzmgInverseInput = null!; + private double[] s2InverseInput = null!; + private double[] healpixRotatedInverseInput = null!; + private double[] rhealpixInverseInput = null!; + private double[] transverseMercatorExactInverseInput = null!; + private double[] transverseMercatorApproxInverseInput = null!; + private double[] stereographicPolarInverseInput = null!; + private double[] stereographicObliqueInverseInput = null!; + private double[] stereographicEquatorialInverseInput = null!; + private double[] vanDerGrintenOverInverseInput = null!; + + /// + /// Executes one pass of every single-point projection benchmark and validates that the results stay finite. + /// + public static void Validate() + { + var benchmark = new ProjectionSinglePointBenchmarks(); + benchmark.GlobalSetup(); + + EnsureFinite(benchmark.TransformGuyouSinglePoint()); + EnsureFinite(benchmark.TransformPeirceQuincuncialSinglePoint()); + EnsureFinite(benchmark.TransformAdamsHemisphereSinglePoint()); + EnsureFinite(benchmark.TransformObliqueMercatorSinglePoint()); + EnsureFinite(benchmark.TransformObliqueMercatorNoRotationSinglePoint()); + EnsureFinite(benchmark.TransformKrovakSinglePoint()); + EnsureFinite(benchmark.TransformIseaPolarSinglePoint()); + EnsureFinite(benchmark.TransformLagrangeSinglePoint()); + EnsureFinite(benchmark.TransformLoximuthalSinglePoint()); + EnsureFinite(benchmark.TransformMercatorSinglePoint()); + EnsureFinite(benchmark.TransformNzmgSinglePoint()); + EnsureFinite(benchmark.TransformOrthographicLocalSinglePoint()); + EnsureFinite(benchmark.TransformS2SinglePoint()); + EnsureFinite(benchmark.TransformHealpixRotatedSinglePoint()); + EnsureFinite(benchmark.TransformRhealpixSinglePoint()); + EnsureFinite(benchmark.TransformTransverseMercatorExactSinglePoint()); + EnsureFinite(benchmark.TransformTransverseMercatorApproxSinglePoint()); + EnsureFinite(benchmark.TransformStereographicPolarSinglePoint()); + EnsureFinite(benchmark.TransformStereographicObliqueSinglePoint()); + EnsureFinite(benchmark.TransformStereographicEquatorialSinglePoint()); + EnsureFinite(benchmark.TransformVanDerGrintenOverSinglePoint()); + EnsureFinite(benchmark.TransformLambertAzimuthalEqualAreaInverseSinglePoint()); + EnsureFinite(benchmark.TransformIseaPolarInverseSinglePoint()); + EnsureFinite(benchmark.TransformKrovakInverseSinglePoint()); + EnsureFinite(benchmark.TransformLagrangeInverseSinglePoint()); + EnsureFinite(benchmark.TransformLoximuthalInverseSinglePoint()); + EnsureFinite(benchmark.TransformOrthographicInverseSinglePoint()); + EnsureFinite(benchmark.TransformRobinsonInverseSinglePoint()); + EnsureFinite(benchmark.TransformMercatorInverseSinglePoint()); + EnsureFinite(benchmark.TransformNzmgInverseSinglePoint()); + EnsureFinite(benchmark.TransformS2InverseSinglePoint()); + EnsureFinite(benchmark.TransformHealpixRotatedInverseSinglePoint()); + EnsureFinite(benchmark.TransformRhealpixInverseSinglePoint()); + EnsureFinite(benchmark.TransformTransverseMercatorExactInverseSinglePoint()); + EnsureFinite(benchmark.TransformTransverseMercatorApproxInverseSinglePoint()); + EnsureFinite(benchmark.TransformStereographicPolarInverseSinglePoint()); + EnsureFinite(benchmark.TransformStereographicObliqueInverseSinglePoint()); + EnsureFinite(benchmark.TransformStereographicEquatorialInverseSinglePoint()); + EnsureFinite(benchmark.TransformVanDerGrintenOverInverseSinglePoint()); + } + + /// + /// Creates the projection transforms and representative valid source coordinates used by the benchmarks. + /// + [GlobalSetup] + public void GlobalSetup() + { + this.guyouForward = BenchmarkPipelineTransformFactory.Create("+proj=guyou"); + this.peirceForward = BenchmarkPipelineTransformFactory.Create("+proj=peirce_q +shape=square"); + this.adamsHemisphereForward = BenchmarkPipelineTransformFactory.Create("+proj=adams_hemi"); + this.obliqueMercatorForward = BenchmarkPipelineTransformFactory.Create("+proj=omerc +ellps=GRS80 +lat_1=0.5 +lat_2=2"); + this.obliqueMercatorNoRotationForward = BenchmarkPipelineTransformFactory.Create("+proj=omerc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +no_rot"); + this.krovakForward = BenchmarkPipelineTransformFactory.Create("+proj=krovak +ellps=GRS80"); + this.iseaPolarForward = BenchmarkPipelineTransformFactory.Create("+proj=isea +R=6371007.18091875 +orient=pole"); + this.lagrangeForward = BenchmarkPipelineTransformFactory.Create("+proj=lagrng +a=6400000 +W=2 +lat_1=0.5"); + this.loximuthalForward = BenchmarkPipelineTransformFactory.Create("+proj=loxim +a=6400000 +lat_1=0.5 +lat_2=2"); + this.mercatorForward = BenchmarkPipelineTransformFactory.Create("+proj=merc +ellps=GRS80"); + this.nzmgForward = BenchmarkPipelineTransformFactory.Create("+proj=nzmg +ellps=GRS80"); + this.orthographicLocalForward = BenchmarkPipelineTransformFactory.Create("+proj=ortho +lat_0=37.628969166666664 +lon_0=-122.39394166666668 +k_0=0.9999968 +alpha=27.7927777777777 +ellps=GRS80"); + this.s2Forward = BenchmarkPipelineTransformFactory.Create("+proj=s2 +ellps=WGS84 +lat_0=90 +UVtoST=tangent"); + this.healpixRotatedForward = BenchmarkPipelineTransformFactory.Create("+proj=healpix +R=6400000 +rot_xy=42"); + this.rhealpixForward = BenchmarkPipelineTransformFactory.Create("+proj=rhealpix +south_square=2 +north_square=3 +ellps=WGS84"); + this.transverseMercatorExactForward = BenchmarkPipelineTransformFactory.Create("+proj=tmerc +ellps=GRS80"); + this.transverseMercatorApproxForward = BenchmarkPipelineTransformFactory.Create("+proj=tmerc +ellps=GRS80 +approx"); + this.stereographicPolarForward = BenchmarkPipelineTransformFactory.Create("+proj=stere +ellps=GRS80 +lat_0=90 +lat_ts=70"); + this.stereographicObliqueForward = BenchmarkPipelineTransformFactory.Create("+proj=stere +ellps=GRS80 +lat_0=45"); + this.stereographicEquatorialForward = BenchmarkPipelineTransformFactory.Create("+proj=stere +ellps=GRS80 +lat_0=0"); + this.vanDerGrintenOverForward = BenchmarkPipelineTransformFactory.Create("+proj=vandg +a=6400000 +over"); + + MathTransform laeaForward = BenchmarkPipelineTransformFactory.Create("+proj=laea +R=6371000 +lat_0=45"); + MathTransform orthographicForward = BenchmarkPipelineTransformFactory.Create("+proj=ortho +ellps=WGS84 +lat_0=30"); + MathTransform robinsonForward = BenchmarkPipelineTransformFactory.Create("+proj=robin +a=6400000"); + + this.laeaInverse = laeaForward.Inverse(); + this.iseaPolarInverse = this.iseaPolarForward.Inverse(); + this.krovakInverse = this.krovakForward.Inverse(); + this.lagrangeInverse = this.lagrangeForward.Inverse(); + this.loximuthalInverse = this.loximuthalForward.Inverse(); + this.orthographicInverse = orthographicForward.Inverse(); + this.robinsonInverse = robinsonForward.Inverse(); + this.mercatorInverse = this.mercatorForward.Inverse(); + this.nzmgInverse = this.nzmgForward.Inverse(); + this.s2Inverse = this.s2Forward.Inverse(); + this.healpixRotatedInverse = this.healpixRotatedForward.Inverse(); + this.rhealpixInverse = this.rhealpixForward.Inverse(); + this.transverseMercatorExactInverse = this.transverseMercatorExactForward.Inverse(); + this.transverseMercatorApproxInverse = this.transverseMercatorApproxForward.Inverse(); + this.stereographicPolarInverse = this.stereographicPolarForward.Inverse(); + this.stereographicObliqueInverse = this.stereographicObliqueForward.Inverse(); + this.stereographicEquatorialInverse = this.stereographicEquatorialForward.Inverse(); + this.vanDerGrintenOverInverse = this.vanDerGrintenOverForward.Inverse(); + + this.guyouInput = [12d, 25d]; + this.peirceInput = [-15d, 35d]; + this.adamsHemisphereInput = [40d, 30d]; + this.obliqueMercatorInput = [2d, 1d]; + this.krovakInput = [2d, 1d]; + this.iseaPolarInput = [0d, 45d]; + this.lagrangeInput = [2d, 1d]; + this.loximuthalInput = [2d, 1d]; + this.mercatorInput = [18d, -85d]; + this.nzmgInput = [2d, 1d]; + this.orthographicLocalInput = [-122.3846388888889d, 37.62607694444444d]; + this.s2Input = [20d, 70.12337013762532d]; + this.healpixRotatedInput = [2d, 1d]; + this.rhealpixInput = [45d, 50d]; + this.transverseMercatorInput = [44.69d, 35.37d]; + this.stereographicPolarInput = [15d, 80d]; + this.stereographicObliqueInput = [12d, 50d]; + this.stereographicEquatorialInput = [18d, 25d]; + this.vanDerGrintenOverInput = [180.1d, 50d]; + this.iseaPolarInverseInput = this.iseaPolarForward.Transform(this.iseaPolarInput); + this.krovakInverseInput = [200d, 100d]; + this.lagrangeInverseInput = this.lagrangeForward.Transform(this.lagrangeInput); + this.laeaInput = laeaForward.Transform([15d, 20d]); + this.loximuthalInverseInput = [200d, 100d]; + this.orthographicInput = orthographicForward.Transform([20d, 40d]); + this.robinsonInput = robinsonForward.Transform([30d, 12d]); + this.mercatorInverseInput = this.mercatorForward.Transform(this.mercatorInput); + this.nzmgInverseInput = [200000d, 100000d]; + this.s2InverseInput = [0.29020309743436806d, 0.4211558922141421d]; + this.healpixRotatedInverseInput = this.healpixRotatedForward.Transform(this.healpixRotatedInput); + this.rhealpixInverseInput = this.rhealpixForward.Transform(this.rhealpixInput); + this.transverseMercatorExactInverseInput = this.transverseMercatorExactForward.Transform(this.transverseMercatorInput); + this.transverseMercatorApproxInverseInput = this.transverseMercatorApproxForward.Transform(this.transverseMercatorInput); + this.stereographicPolarInverseInput = this.stereographicPolarForward.Transform(this.stereographicPolarInput); + this.stereographicObliqueInverseInput = this.stereographicObliqueForward.Transform(this.stereographicObliqueInput); + this.stereographicEquatorialInverseInput = this.stereographicEquatorialForward.Transform(this.stereographicEquatorialInput); + this.vanDerGrintenOverInverseInput = this.vanDerGrintenOverForward.Transform(this.vanDerGrintenOverInput); + } + + /// + /// Measures single-point forward throughput for the Guyou projection. + /// + /// The projected coordinate pair. + [Benchmark(Baseline = true)] + public double[] TransformGuyouSinglePoint() => this.guyouForward.Transform(this.guyouInput); + + /// + /// Measures single-point forward throughput for the Peirce quincuncial projection. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformPeirceQuincuncialSinglePoint() => this.peirceForward.Transform(this.peirceInput); + + /// + /// Measures single-point forward throughput for the Adams hemisphere-in-a-square projection. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformAdamsHemisphereSinglePoint() => this.adamsHemisphereForward.Transform(this.adamsHemisphereInput); + + /// + /// Measures single-point forward throughput for Hotine oblique Mercator in the default rotated-grid mode. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformObliqueMercatorSinglePoint() => this.obliqueMercatorForward.Transform(this.obliqueMercatorInput); + + /// + /// Measures single-point forward throughput for Hotine oblique Mercator with +no_rot. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformObliqueMercatorNoRotationSinglePoint() => this.obliqueMercatorNoRotationForward.Transform(this.obliqueMercatorInput); + + /// + /// Measures single-point forward throughput for default-parameter Krovak. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformKrovakSinglePoint() => this.krovakForward.Transform(this.krovakInput); + + /// + /// Measures single-point forward throughput for polar-oriented ISEA. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformIseaPolarSinglePoint() => this.iseaPolarForward.Transform(this.iseaPolarInput); + + /// + /// Measures single-point forward throughput for spherical Lagrange using PROJ lat_1 and W. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformLagrangeSinglePoint() => this.lagrangeForward.Transform(this.lagrangeInput); + + /// + /// Measures single-point forward throughput for Loximuthal using the PROJ lat_1 binding. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformLoximuthalSinglePoint() => this.loximuthalForward.Transform(this.loximuthalInput); + + /// + /// Measures single-point forward throughput for ellipsoidal Mercator on a high-latitude input. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformMercatorSinglePoint() => this.mercatorForward.Transform(this.mercatorInput); + + /// + /// Measures single-point forward throughput for New Zealand Map Grid with PROJ default origin and offsets. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformNzmgSinglePoint() => this.nzmgForward.Transform(this.nzmgInput); + + /// + /// Measures single-point forward throughput for local ellipsoidal Orthographic with alpha. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformOrthographicLocalSinglePoint() => this.orthographicLocalForward.Transform(this.orthographicLocalInput); + + /// + /// Measures single-point forward throughput for s2 using tangent UV-to-ST mapping. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformS2SinglePoint() => this.s2Forward.Transform(this.s2Input); + + /// + /// Measures single-point forward throughput for rotated spherical HEALPix. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformHealpixRotatedSinglePoint() => this.healpixRotatedForward.Transform(this.healpixRotatedInput); + + /// + /// Measures single-point forward throughput for ellipsoidal rHEALPix with explicit polar-square placement. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformRhealpixSinglePoint() => this.rhealpixForward.Transform(this.rhealpixInput); + + /// + /// Measures single-point forward throughput for exact ellipsoidal transverse Mercator. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformTransverseMercatorExactSinglePoint() => this.transverseMercatorExactForward.Transform(this.transverseMercatorInput); + + /// + /// Measures single-point forward throughput for the Snyder approximate transverse Mercator path. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformTransverseMercatorApproxSinglePoint() => this.transverseMercatorApproxForward.Transform(this.transverseMercatorInput); + + /// + /// Measures single-point forward throughput for ellipsoidal polar stereographic. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformStereographicPolarSinglePoint() => this.stereographicPolarForward.Transform(this.stereographicPolarInput); + + /// + /// Measures single-point forward throughput for ellipsoidal oblique stereographic. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformStereographicObliqueSinglePoint() => this.stereographicObliqueForward.Transform(this.stereographicObliqueInput); + + /// + /// Measures single-point forward throughput for ellipsoidal equatorial stereographic. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformStereographicEquatorialSinglePoint() => this.stereographicEquatorialForward.Transform(this.stereographicEquatorialInput); + + /// + /// Measures single-point forward throughput for van der Grinten with +over. + /// + /// The projected coordinate pair. + [Benchmark] + public double[] TransformVanDerGrintenOverSinglePoint() => this.vanDerGrintenOverForward.Transform(this.vanDerGrintenOverInput); + + /// + /// Measures single-point inverse throughput for spherical Lambert azimuthal equal area. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformLambertAzimuthalEqualAreaInverseSinglePoint() => this.laeaInverse.Transform(this.laeaInput); + + /// + /// Measures single-point inverse throughput for polar-oriented ISEA. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformIseaPolarInverseSinglePoint() => this.iseaPolarInverse.Transform(this.iseaPolarInverseInput); + + /// + /// Measures single-point inverse throughput for default-parameter Krovak. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformKrovakInverseSinglePoint() => this.krovakInverse.Transform(this.krovakInverseInput); + + /// + /// Measures single-point inverse throughput for spherical Lagrange using PROJ lat_1 and W. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformLagrangeInverseSinglePoint() => this.lagrangeInverse.Transform(this.lagrangeInverseInput); + + /// + /// Measures single-point inverse throughput for Loximuthal using the PROJ lat_1 binding. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformLoximuthalInverseSinglePoint() => this.loximuthalInverse.Transform(this.loximuthalInverseInput); + + /// + /// Measures single-point inverse throughput for oblique ellipsoidal Orthographic. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformOrthographicInverseSinglePoint() => this.orthographicInverse.Transform(this.orthographicInput); + + /// + /// Measures single-point inverse throughput for Robinson. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformRobinsonInverseSinglePoint() => this.robinsonInverse.Transform(this.robinsonInput); + + /// + /// Measures single-point inverse throughput for ellipsoidal Mercator on a high-latitude input. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformMercatorInverseSinglePoint() => this.mercatorInverse.Transform(this.mercatorInverseInput); + + /// + /// Measures single-point inverse throughput for New Zealand Map Grid with PROJ default origin and offsets. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformNzmgInverseSinglePoint() => this.nzmgInverse.Transform(this.nzmgInverseInput); + + /// + /// Measures single-point inverse throughput for s2 using tangent UV-to-ST mapping. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformS2InverseSinglePoint() => this.s2Inverse.Transform(this.s2InverseInput); + + /// + /// Measures single-point inverse throughput for rotated spherical HEALPix. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformHealpixRotatedInverseSinglePoint() => this.healpixRotatedInverse.Transform(this.healpixRotatedInverseInput); + + /// + /// Measures single-point inverse throughput for ellipsoidal rHEALPix with explicit polar-square placement. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformRhealpixInverseSinglePoint() => this.rhealpixInverse.Transform(this.rhealpixInverseInput); + + /// + /// Measures single-point inverse throughput for exact ellipsoidal transverse Mercator. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformTransverseMercatorExactInverseSinglePoint() => this.transverseMercatorExactInverse.Transform(this.transverseMercatorExactInverseInput); + + /// + /// Measures single-point inverse throughput for the Snyder approximate transverse Mercator path. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformTransverseMercatorApproxInverseSinglePoint() => this.transverseMercatorApproxInverse.Transform(this.transverseMercatorApproxInverseInput); + + /// + /// Measures single-point inverse throughput for ellipsoidal polar stereographic. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformStereographicPolarInverseSinglePoint() => this.stereographicPolarInverse.Transform(this.stereographicPolarInverseInput); + + /// + /// Measures single-point inverse throughput for ellipsoidal oblique stereographic. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformStereographicObliqueInverseSinglePoint() => this.stereographicObliqueInverse.Transform(this.stereographicObliqueInverseInput); + + /// + /// Measures single-point inverse throughput for ellipsoidal equatorial stereographic. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformStereographicEquatorialInverseSinglePoint() => this.stereographicEquatorialInverse.Transform(this.stereographicEquatorialInverseInput); + + /// + /// Measures single-point inverse throughput for van der Grinten with +over. + /// + /// The reconstructed geographic coordinate pair. + [Benchmark] + public double[] TransformVanDerGrintenOverInverseSinglePoint() => this.vanDerGrintenOverInverse.Transform(this.vanDerGrintenOverInverseInput); + + private static void EnsureFinite(double[] coordinates) + { + for (int i = 0; i < coordinates.Length; i++) + { + if (double.IsNaN(coordinates[i]) || double.IsInfinity(coordinates[i])) + { + throw new InvalidOperationException("Benchmark validation failed: transform produced non-finite values."); + } + } + } +} diff --git a/src/ProjNet.Benchmark/ProjectionTransformBenchmarks.cs b/src/ProjNet.Benchmark/ProjectionTransformBenchmarks.cs new file mode 100644 index 00000000..048ef765 --- /dev/null +++ b/src/ProjNet.Benchmark/ProjectionTransformBenchmarks.cs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Measures forward transform throughput for common projection types across 10,000 coordinate pairs. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "Benchmark input generation uses deterministic pseudo-random data for repeatability and is not security-sensitive.")] +[MemoryDiagnoser] +public class ProjectionTransformBenchmarks +{ + private const int PointCount = 10_000; + + private CoordinateSystemServices services = null!; + private MathTransform? mercatorTransform; + private MathTransform? utm32NTransform; + private MathTransform? lambert93Transform; + private MathTransform? krovakTransform; + + private double[] longitudes = []; + private double[] latitudes = []; + private double[] xBuffer = []; + private double[] yBuffer = []; + + /// + /// Executes the curated projection transform benchmarks once and verifies that all outputs stay finite. + /// + public static void Validate() + { + static void EnsureFinite(double[] xs, double[] ys) + { + for (int i = 0; i < xs.Length; i++) + { + if (double.IsNaN(xs[i]) || double.IsInfinity(xs[i]) || + double.IsNaN(ys[i]) || double.IsInfinity(ys[i])) + { + throw new InvalidOperationException("Projection transform benchmark validation failed: transform produced non-finite values."); + } + } + } + + var benchmark = new ProjectionTransformBenchmarks(); + benchmark.GlobalSetup(); + + benchmark.TransformBatchMercator(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.TransformBatchUtm32N(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + + benchmark.TransformBatchLambert93(); + EnsureFinite(benchmark.xBuffer, benchmark.yBuffer); + } + + /// + /// Creates coordinate system services, pre-builds transforms, and generates deterministic test coordinates. + /// + [GlobalSetup] + public void GlobalSetup() + { + this.services = new CoordinateSystemServices(); + + this.longitudes = new double[PointCount]; + this.latitudes = new double[PointCount]; + this.xBuffer = new double[PointCount]; + this.yBuffer = new double[PointCount]; + + var random = new Random(42); + for (int i = 0; i < PointCount; i++) + { + this.longitudes[i] = 2d + (random.NextDouble() * 18d); + this.latitudes[i] = 43d + (random.NextDouble() * 12d); + } + } + + /// + /// Measures batched forward transform throughput for EPSG:4326 to EPSG:3857 (Web Mercator). + /// + [Benchmark(Baseline = true)] + public void TransformBatchMercator() + { + this.PrepareInput(); + this.GetOrCreateTransform(ref this.mercatorTransform, 4326, 3857).Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures batched forward transform throughput for EPSG:4326 to EPSG:32632 (UTM Zone 32N). + /// + [Benchmark] + public void TransformBatchUtm32N() + { + this.PrepareInput(); + this.GetOrCreateTransform(ref this.utm32NTransform, 4326, 32632).Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures batched forward transform throughput for EPSG:4326 to EPSG:2154 (Lambert 93). + /// + [Benchmark] + public void TransformBatchLambert93() + { + this.PrepareInput(); + this.GetOrCreateTransform(ref this.lambert93Transform, 4326, 2154).Transform(this.xBuffer, this.yBuffer); + } + + /// + /// Measures batched forward transform throughput for EPSG:4326 to EPSG:5514 (Krovak). + /// + [Benchmark] + public void TransformBatchKrovak() + { + this.PrepareInput(); + this.GetOrCreateTransform(ref this.krovakTransform, 4326, 5514).Transform(this.xBuffer, this.yBuffer); + } + + private void PrepareInput() + { + this.longitudes.CopyTo(this.xBuffer.AsSpan()); + this.latitudes.CopyTo(this.yBuffer.AsSpan()); + } + + private MathTransform GetOrCreateTransform(ref MathTransform? transform, int sourceSrid, int targetSrid) + { + if (transform is not null) + { + return transform; + } + + ICoordinateTransformation projection = this.services.CreateTransformation(sourceSrid, targetSrid) + ?? throw new InvalidOperationException(FormattableString.Invariant($"EPSG:{sourceSrid}->{targetSrid} transformation lookup returned null.")); + transform = projection.MathTransform; + return transform; + } +} diff --git a/src/ProjNet.Benchmark/TransformationFactoryBenchmarks.cs b/src/ProjNet.Benchmark/TransformationFactoryBenchmarks.cs new file mode 100644 index 00000000..2a1ac816 --- /dev/null +++ b/src/ProjNet.Benchmark/TransformationFactoryBenchmarks.cs @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Measures the overhead of . +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +public class TransformationFactoryBenchmarks +{ + private CoordinateSystem wgs84Cs = null!; + private CoordinateSystem mercatorCs = null!; + private CoordinateSystem utm32NCs = null!; + private CoordinateSystem lambert93Cs = null!; + + /// + /// Executes the curated transformation-factory benchmarks once and verifies that they all return transformations. + /// + public static void Validate() + { + var benchmark = new TransformationFactoryBenchmarks(); + benchmark.GlobalSetup(); + + _ = benchmark.CreateTransformWgs84ToMercator(); + _ = benchmark.CreateTransformWgs84ToUtm32N(); + _ = benchmark.CreateTransformUtm32NToLambert93(); + } + + /// + /// Resolves and caches coordinate systems for subsequent factory benchmarks. + /// + [GlobalSetup] + public void GlobalSetup() + { + var services = new CoordinateSystemServices(); + + this.wgs84Cs = services.GetCoordinateSystem(4326) + ?? throw new InvalidOperationException("EPSG:4326 lookup returned null."); + this.mercatorCs = services.GetCoordinateSystem(3857) + ?? throw new InvalidOperationException("EPSG:3857 lookup returned null."); + this.utm32NCs = services.GetCoordinateSystem(32632) + ?? throw new InvalidOperationException("EPSG:32632 lookup returned null."); + this.lambert93Cs = services.GetCoordinateSystem(2154) + ?? throw new InvalidOperationException("EPSG:2154 lookup returned null."); + } + + /// + /// Creates a WGS84 to Web Mercator transformation via . + /// + /// The created coordinate transformation. + [Benchmark(Baseline = true)] + public ICoordinateTransformation CreateTransformWgs84ToMercator() + { + return new CoordinateTransformationFactory().CreateFromCoordinateSystems(this.wgs84Cs, this.mercatorCs); + } + + /// + /// Creates a WGS84 to UTM Zone 32N transformation via . + /// + /// The created coordinate transformation. + [Benchmark] + public ICoordinateTransformation CreateTransformWgs84ToUtm32N() + { + return new CoordinateTransformationFactory().CreateFromCoordinateSystems(this.wgs84Cs, this.utm32NCs); + } + + /// + /// Creates a UTM Zone 32N to Lambert 93 transformation via . + /// + /// The created coordinate transformation. + [Benchmark] + public ICoordinateTransformation CreateTransformUtm32NToLambert93() + { + return new CoordinateTransformationFactory().CreateFromCoordinateSystems(this.utm32NCs, this.lambert93Cs); + } +} diff --git a/src/ProjNet.Benchmark/TransformationRuntimeBenchmarks.cs b/src/ProjNet.Benchmark/TransformationRuntimeBenchmarks.cs new file mode 100644 index 00000000..628c029a --- /dev/null +++ b/src/ProjNet.Benchmark/TransformationRuntimeBenchmarks.cs @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Measures runtime throughput for representative non-projection transformation implementations touched by the M104 exception audit. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +public class TransformationRuntimeBenchmarks +{ + private const int PointCount = 10_000; + private const string MolodenskyOperation = "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149 +abridged"; + private const string HornerOperation = "+proj=horner +ellps=intl +range=10000000 +fwd_origin=4.94690026817276e+05,6.13342113183056e+06 +deg=3 +fwd_c=6.13258562111350e+06,6.19480105709997e+05,9.99378966275206e-01,-2.82153291753490e-02,-2.27089979140026e-10,-1.77019590701470e-09,1.08522286274070e-14,2.11430298751604e-15"; + + private MathTransform molodenskyTransform = null!; + private MathTransform hornerTransform = null!; + private MathTransform horizontalGridShiftTransform = null!; + private MathTransform verticalGridShiftTransform = null!; + + private double[] molodenskyXs = []; + private double[] molodenskyYs = []; + private double[] molodenskyZs = []; + private double[] molodenskyWorkXs = []; + private double[] molodenskyWorkYs = []; + private double[] molodenskyWorkZs = []; + + private double[] hornerXs = []; + private double[] hornerYs = []; + private double[] hornerZs = []; + private double[] hornerWorkXs = []; + private double[] hornerWorkYs = []; + private double[] hornerWorkZs = []; + + private double[] horizontalGridShiftXs = []; + private double[] horizontalGridShiftYs = []; + private double[] horizontalGridShiftZs = []; + private double[] horizontalGridShiftWorkXs = []; + private double[] horizontalGridShiftWorkYs = []; + private double[] horizontalGridShiftWorkZs = []; + + private double[] verticalGridShiftXs = []; + private double[] verticalGridShiftYs = []; + private double[] verticalGridShiftZs = []; + private double[] verticalGridShiftWorkXs = []; + private double[] verticalGridShiftWorkYs = []; + private double[] verticalGridShiftWorkZs = []; + + /// + /// Creates representative transforms and deterministic benchmark inputs. + /// + [GlobalSetup] + public void GlobalSetup() + { + this.molodenskyTransform = BenchmarkPipelineTransformFactory.Create(MolodenskyOperation); + this.hornerTransform = BenchmarkPipelineTransformFactory.Create(HornerOperation); + this.horizontalGridShiftTransform = BenchmarkPipelineTransformFactory.Create(FormattableString.Invariant($"+proj=hgridshift +grids={BenchmarkFixtureResolver.ResolveGridPath("test_hgrid_little_endian.gsb")}")); + this.verticalGridShiftTransform = BenchmarkPipelineTransformFactory.Create(FormattableString.Invariant($"+proj=vgridshift +grids={BenchmarkFixtureResolver.ResolveGridPath("egm96_15.gtx")}")); + + (this.molodenskyXs, this.molodenskyYs, this.molodenskyZs) = CreateMolodenskySource(); + (this.hornerXs, this.hornerYs, this.hornerZs) = CreateHornerSource(); + (this.horizontalGridShiftXs, this.horizontalGridShiftYs, this.horizontalGridShiftZs) = CreateConstantSource(4.5d, 52.5d, 0d); + (this.verticalGridShiftXs, this.verticalGridShiftYs, this.verticalGridShiftZs) = CreateConstantSource(12d, 56d, 0d); + + this.molodenskyWorkXs = new double[PointCount]; + this.molodenskyWorkYs = new double[PointCount]; + this.molodenskyWorkZs = new double[PointCount]; + + this.hornerWorkXs = new double[PointCount]; + this.hornerWorkYs = new double[PointCount]; + this.hornerWorkZs = new double[PointCount]; + + this.horizontalGridShiftWorkXs = new double[PointCount]; + this.horizontalGridShiftWorkYs = new double[PointCount]; + this.horizontalGridShiftWorkZs = new double[PointCount]; + + this.verticalGridShiftWorkXs = new double[PointCount]; + this.verticalGridShiftWorkYs = new double[PointCount]; + this.verticalGridShiftWorkZs = new double[PointCount]; + } + + /// + /// Measures batched Molodensky runtime throughput. + /// + [Benchmark] + public void TransformBatchMolodensky() + { + PrepareInput(this.molodenskyXs, this.molodenskyYs, this.molodenskyZs, this.molodenskyWorkXs, this.molodenskyWorkYs, this.molodenskyWorkZs); + this.molodenskyTransform.Transform(this.molodenskyWorkXs, this.molodenskyWorkYs, this.molodenskyWorkZs); + } + + /// + /// Measures batched Horner runtime throughput. + /// + [Benchmark] + public void TransformBatchHorner() + { + PrepareInput(this.hornerXs, this.hornerYs, this.hornerZs, this.hornerWorkXs, this.hornerWorkYs, this.hornerWorkZs); + this.hornerTransform.Transform(this.hornerWorkXs, this.hornerWorkYs, this.hornerWorkZs); + } + + /// + /// Measures batched NTv2 horizontal grid-shift throughput. + /// + [Benchmark] + public void TransformBatchHorizontalGridShift() + { + PrepareInput( + this.horizontalGridShiftXs, + this.horizontalGridShiftYs, + this.horizontalGridShiftZs, + this.horizontalGridShiftWorkXs, + this.horizontalGridShiftWorkYs, + this.horizontalGridShiftWorkZs); + this.horizontalGridShiftTransform.Transform( + this.horizontalGridShiftWorkXs, + this.horizontalGridShiftWorkYs, + this.horizontalGridShiftWorkZs); + } + + /// + /// Measures batched GTX vertical grid-shift throughput. + /// + [Benchmark] + public void TransformBatchVerticalGridShift() + { + PrepareInput( + this.verticalGridShiftXs, + this.verticalGridShiftYs, + this.verticalGridShiftZs, + this.verticalGridShiftWorkXs, + this.verticalGridShiftWorkYs, + this.verticalGridShiftWorkZs); + this.verticalGridShiftTransform.Transform( + this.verticalGridShiftWorkXs, + this.verticalGridShiftWorkYs, + this.verticalGridShiftWorkZs); + } + + private static void PrepareInput( + double[] sourceXs, + double[] sourceYs, + double[] sourceZs, + double[] workXs, + double[] workYs, + double[] workZs) + { + sourceXs.CopyTo(workXs.AsSpan()); + sourceYs.CopyTo(workYs.AsSpan()); + sourceZs.CopyTo(workZs.AsSpan()); + } + + private static (double[] Xs, double[] Ys, double[] Zs) CreateMolodenskySource() + { + double[] xs = new double[PointCount]; + double[] ys = new double[PointCount]; + double[] zs = new double[PointCount]; + for (int i = 0; i < PointCount; i++) + { + xs[i] = 144.75d + ((i % 128) * 1e-3d); + ys[i] = -37.95d + ((i % 96) * 1e-3d); + zs[i] = 25d + (i % 32); + } + + return (xs, ys, zs); + } + + private static (double[] Xs, double[] Ys, double[] Zs) CreateHornerSource() + { + double[] xs = new double[PointCount]; + double[] ys = new double[PointCount]; + double[] zs = new double[PointCount]; + for (int i = 0; i < PointCount; i++) + { + xs[i] = 495000d + (i % 512); + ys[i] = 6130500d + ((i % 512) * 0.5d); + zs[i] = 0d; + } + + return (xs, ys, zs); + } + + private static (double[] Xs, double[] Ys, double[] Zs) CreateConstantSource(double x, double y, double z) + { + double[] xs = new double[PointCount]; + double[] ys = new double[PointCount]; + double[] zs = new double[PointCount]; + Array.Fill(xs, x); + Array.Fill(ys, y); + Array.Fill(zs, z); + return (xs, ys, zs); + } +} diff --git a/src/ProjNet.Benchmark/WktBulkParsingBenchmarks.cs b/src/ProjNet.Benchmark/WktBulkParsingBenchmarks.cs new file mode 100644 index 00000000..314fed9e --- /dev/null +++ b/src/ProjNet.Benchmark/WktBulkParsingBenchmarks.cs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using BenchmarkDotNet.Attributes; +using ProjNet.Data; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; + +/// +/// Measures bulk WKT parsing throughput across the full managed EPSG catalog. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +[SimpleJob] +public class WktBulkParsingBenchmarks +{ + private string[] catalogWkt1 = Array.Empty(); + private string[] catalogWkt2 = Array.Empty(); + + /// + /// Materializes the managed EPSG catalog as WKT1 and WKT2 string arrays once per benchmark run. + /// + [GlobalSetup] + public void Setup() + { + CoordinateSystemEntry[] entries = new ManagedCoordinateSystemDefinitionProvider() + .GetCoordinateSystems() + .OrderBy(static entry => entry.Srid) + .ToArray(); + + this.catalogWkt1 = entries + .Select(static entry => entry.CoordinateSystem.WKT) + .ToArray(); + + this.catalogWkt2 = entries + .Select(static entry => entry.CoordinateSystem.ToWktNode(WktVersion.Wkt22019).ToString()) + .ToArray(); + } + + /// + /// Parses all managed EPSG catalog entries in WKT1 form. + /// + /// A checksum derived from the parsed entries. + [Benchmark(Baseline = true)] + public int ParseAllCatalogWkt1() + { + return ParseAll(this.catalogWkt1); + } + + /// + /// Parses all managed EPSG catalog entries in WKT2:2019 form. + /// + /// A checksum derived from the parsed entries. + [Benchmark] + public int ParseAllCatalogWkt2() + { + return ParseAll(this.catalogWkt2); + } + + private static int ParseAll(string[] wkts) + { + int checksum = 0; + foreach (string wkt in wkts) + { + checksum += CoordinateSystemWktReader.Parse(wkt).Name.Length; + } + + return checksum; + } +} diff --git a/src/ProjNet.Benchmark/WktKeywordNodeBenchmarks.cs b/src/ProjNet.Benchmark/WktKeywordNodeBenchmarks.cs new file mode 100644 index 00000000..344e2c20 --- /dev/null +++ b/src/ProjNet.Benchmark/WktKeywordNodeBenchmarks.cs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System; +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; + +/// +/// Measures direct keyword-child lookup throughput on representative WKT2 nodes. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +[SimpleJob] +public class WktKeywordNodeBenchmarks +{ + private const int IterationCount = 1_000; + + private readonly (WktKeywordNode Node, string Keyword)[] lookups = new (WktKeywordNode Node, string Keyword)[9]; + + /// + /// Parses a representative projected WKT2 sample and captures hot-path child lookups once per run. + /// + [GlobalSetup] + public void GlobalSetup() + { + const string projectedWkt2 = + """PROJCRS["WGS 84 / UTM zone 32N",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)",ID["EPSG",1166]],MEMBER["World Geodetic System 1984 (G730)",ID["EPSG",1152]],MEMBER["World Geodetic System 1984 (G873)",ID["EPSG",1153]],MEMBER["World Geodetic System 1984 (G1150)",ID["EPSG",1154]],MEMBER["World Geodetic System 1984 (G1674)",ID["EPSG",1155]],MEMBER["World Geodetic System 1984 (G1762)",ID["EPSG",1156]],MEMBER["World Geodetic System 1984 (G2139)",ID["EPSG",1309]],MEMBER["World Geodetic System 1984 (G2296)",ID["EPSG",1383]],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",7030]],ENSEMBLEACCURACY[2],ID["EPSG",6326]],ID["EPSG",4326]],CONVERSION["UTM zone 32N",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",9,ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996,SCALEUNIT["unity",1,ID["EPSG",9201]],ID["EPSG",8805]],PARAMETER["False easting",500000,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8807]],ID["EPSG",16032]],CS[Cartesian,2,ID["EPSG",4400]],AXIS["Easting (E)",east],AXIS["Northing (N)",north],LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",32632]]"""; + + var root = WktKeywordNode.ParseTree(new WktTokenizer(projectedWkt2)); + WktKeywordNode baseGeogCrs = root.FindChild("BASEGEOGCRS") + ?? throw new InvalidOperationException("Projected WKT2 sample is missing BASEGEOGCRS."); + WktKeywordNode conversion = root.FindChild("CONVERSION") + ?? throw new InvalidOperationException("Projected WKT2 sample is missing CONVERSION."); + WktKeywordNode cs = root.FindChild("CS") + ?? throw new InvalidOperationException("Projected WKT2 sample is missing CS."); + WktKeywordNode parameter = conversion.FindChild("PARAMETER") + ?? throw new InvalidOperationException("Projected WKT2 sample is missing PARAMETER."); + + this.lookups[0] = (root, "BASEGEOGCRS"); + this.lookups[1] = (root, "CONVERSION"); + this.lookups[2] = (root, "CS"); + this.lookups[3] = (root, "ID"); + this.lookups[4] = (baseGeogCrs, "ENSEMBLE"); + this.lookups[5] = (baseGeogCrs, "ID"); + this.lookups[6] = (conversion, "METHOD"); + this.lookups[7] = (conversion, "ID"); + this.lookups[8] = (parameter, "ANGLEUNIT"); + } + + /// + /// Measures single-keyword child lookups without the params-array overload. + /// + /// A checksum derived from the resolved keyword nodes. + [Benchmark(Baseline = true)] + public int FindSingleKeywordChild() + { + int checksum = 0; + for (int iteration = 0; iteration < IterationCount; iteration++) + { + for (int i = 0; i < this.lookups.Length; i++) + { + WktKeywordNode? child = this.lookups[i].Node.FindChild(this.lookups[i].Keyword); + checksum += child?.Keyword.Length ?? 0; + } + } + + return checksum; + } +} diff --git a/src/ProjNet.Benchmark/WktParsingBenchmarks.cs b/src/ProjNet.Benchmark/WktParsingBenchmarks.cs new file mode 100644 index 00000000..79de3772 --- /dev/null +++ b/src/ProjNet.Benchmark/WktParsingBenchmarks.cs @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Benchmark; + +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using ProjNet.CoordinateSystems; +using ProjNet.IO.CoordinateSystems; + +/// +/// Measures throughput for WKT strings of varying complexity. +/// +[SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Benchmark entry types are intentionally public for BenchmarkDotNet discovery.")] +[MemoryDiagnoser] +[SimpleJob] +public class WktParsingBenchmarks +{ + private readonly string simpleGeographicWkt = + "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]]"; + + private readonly string projectedWkt = + "PROJCS[\"WGS 84 / UTM zone 32N\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + + private readonly string compoundWkt = + "COMPD_CS[\"WGS 84 + EGM96 height\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],VERT_CS[\"EGM96 height\",VERT_DATUM[\"EGM96 geoid\",2005],UNIT[\"metre\",1]]]"; + + private readonly string geodeticWkt2 = + """GEOGCRS["ED50",DATUM["European Datum 1950",ELLIPSOID["International 1924",6378388,297,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",7022]],ID["EPSG",6230]],CS[ellipsoidal,2,ID["EPSG",6422]],AXIS["Geodetic latitude (Lat)",north],AXIS["Geodetic longitude (Lon)",east],ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",4230]]"""; + + private readonly string projectedWkt2 = + """PROJCRS["WGS 84 / UTM zone 32N",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)",ID["EPSG",1166]],MEMBER["World Geodetic System 1984 (G730)",ID["EPSG",1152]],MEMBER["World Geodetic System 1984 (G873)",ID["EPSG",1153]],MEMBER["World Geodetic System 1984 (G1150)",ID["EPSG",1154]],MEMBER["World Geodetic System 1984 (G1674)",ID["EPSG",1155]],MEMBER["World Geodetic System 1984 (G1762)",ID["EPSG",1156]],MEMBER["World Geodetic System 1984 (G2139)",ID["EPSG",1309]],MEMBER["World Geodetic System 1984 (G2296)",ID["EPSG",1383]],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",7030]],ENSEMBLEACCURACY[2],ID["EPSG",6326]],ID["EPSG",4326]],CONVERSION["UTM zone 32N",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",9,ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996,SCALEUNIT["unity",1,ID["EPSG",9201]],ID["EPSG",8805]],PARAMETER["False easting",500000,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8807]],ID["EPSG",16032]],CS[Cartesian,2,ID["EPSG",4400]],AXIS["Easting (E)",east],AXIS["Northing (N)",north],LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",32632]]"""; + + private readonly string boundWkt2 = + """ + BOUNDCRS[ + SOURCECRS[ + PROJCRS["NAD83 / California zone 3 (ftUS)", + BASEGEODCRS["NAD83", + DATUM["North American Datum 1983", + ELLIPSOID["GRS 1980",6378137,298.257222101, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]]], + CONVERSION["SPCS83 California zone 3 (US Survey feet)", + METHOD["Lambert Conic Conformal (2SP)", + ID["EPSG",9802]], + PARAMETER["Latitude of false origin",36.5, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8821]], + PARAMETER["Longitude of false origin",-120.5, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8822]], + PARAMETER["Latitude of 1st standard parallel",38.4333333333333, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8823]], + PARAMETER["Latitude of 2nd standard parallel",37.0666666666667, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8824]], + PARAMETER["Easting at false origin",6561666.667, + LENGTHUNIT["US survey foot",0.304800609601219], + ID["EPSG",8826]], + PARAMETER["Northing at false origin",1640416.667, + LENGTHUNIT["US survey foot",0.304800609601219], + ID["EPSG",8827]]], + CS[Cartesian,2], + AXIS["easting (X)",east, + ORDER[1], + LENGTHUNIT["US survey foot",0.304800609601219]], + AXIS["northing (Y)",north, + ORDER[2], + LENGTHUNIT["US survey foot",0.304800609601219]], + SCOPE["unknown"], + AREA["USA - California - SPCS - 3"], + BBOX[36.73,-123.02,38.71,-117.83], + ID["EPSG",2227]]], + TARGETCRS[ + GEODCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]], + CS[ellipsoidal,2], + AXIS["latitude",north, + ORDER[1], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["longitude",east, + ORDER[2], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4326]]], + ABRIDGEDTRANSFORMATION["NAD83 to WGS 84 (1)", + METHOD["Geocentric translations (geog2D domain)", + ID["EPSG",9603]], + PARAMETER["X-axis translation",0, + ID["EPSG",8605]], + PARAMETER["Y-axis translation",0, + ID["EPSG",8606]], + PARAMETER["Z-axis translation",0, + ID["EPSG",8607]], + SCOPE["unknown"], + AREA["North America - Canada and USA (CONUS, Alaska mainland)"], + BBOX[23.81,-172.54,86.46,-47.74], + ID["EPSG",1188]]] + """; + + /// + /// Executes every curated WKT parsing benchmark once and verifies that parsing produces non-null results. + /// + public static void Validate() + { + var benchmarks = new WktParsingBenchmarks(); + + _ = benchmarks.ParseSimpleGeographicCs(); + _ = benchmarks.ParseProjectedCs(); + _ = benchmarks.ParseCompoundCs(); + _ = benchmarks.ParseGeodeticWkt2(); + _ = benchmarks.ParseProjectedWkt2(); + _ = benchmarks.ParseBoundWkt2(); + } + + /// + /// Parses a simple WGS84 geographic coordinate system WKT string. + /// + /// The parsed coordinate system info. + [Benchmark(Baseline = true)] + public IInfo ParseSimpleGeographicCs() + { + return CoordinateSystemWktReader.Parse(this.simpleGeographicWkt); + } + + /// + /// Parses a UTM Zone 32N projected coordinate system WKT string with Transverse Mercator projection. + /// + /// The parsed coordinate system info. + [Benchmark] + public IInfo ParseProjectedCs() + { + return CoordinateSystemWktReader.Parse(this.projectedWkt); + } + + /// + /// Parses a compound coordinate system WKT string with both horizontal and vertical components. + /// + /// The parsed coordinate system info. + [Benchmark] + public IInfo ParseCompoundCs() + { + return CoordinateSystemWktReader.Parse(this.compoundWkt); + } + + /// + /// Parses a simple WKT2 geographic coordinate system string. + /// + /// The parsed coordinate system info. + [Benchmark] + public IInfo ParseGeodeticWkt2() + { + return CoordinateSystemWktReader.Parse(this.geodeticWkt2); + } + + /// + /// Parses a projected WKT2 coordinate system string. + /// + /// The parsed coordinate system info. + [Benchmark] + public IInfo ParseProjectedWkt2() + { + return CoordinateSystemWktReader.Parse(this.projectedWkt2); + } + + /// + /// Parses a bound WKT2 coordinate system string with an abridged transformation. + /// + /// The parsed coordinate system info. + [Benchmark] + public IInfo ParseBoundWkt2() + { + return CoordinateSystemWktReader.Parse(this.boundWkt2); + } +} diff --git a/src/ProjNet/ArgumentGuard.cs b/src/ProjNet/ArgumentGuard.cs new file mode 100644 index 00000000..c6dd0c8d --- /dev/null +++ b/src/ProjNet/ArgumentGuard.cs @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +/// +/// Provides lightweight argument validation helpers shared across targets. +/// +internal static class ArgumentGuard +{ + /// + /// Throws an when is . + /// Returns the non-null value for inline assignment scenarios. + /// + /// Reference type of the value being validated. + /// Value to validate. + /// Parameter name for exception reporting. + /// The validated non-null . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NotNull] + internal static T ThrowIfNull( + [NotNull] T? value, +#if NET8_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] string? paramName = null) + where T : class + { + ArgumentNullException.ThrowIfNull(value, paramName); + return value; + } +#else + string paramName) + where T : class + { + return value is null ? throw new ArgumentNullException(paramName) : value; + } +#endif + + /// + /// Throws an when is . + /// + /// Value to validate. + /// Parameter name for exception reporting. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfNull( + object? value, +#if NET8_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] string? paramName = null) + { + ArgumentNullException.ThrowIfNull(value, paramName); + } +#else + string paramName) + { + if (value is null) + { + throw new ArgumentNullException(paramName); + } + } +#endif + + /// + /// Throws when is or empty. + /// + /// Value to validate. + /// Parameter name for exception reporting. + /// The validated non-null, non-empty . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string ThrowIfNullOrEmpty( + string? value, +#if NET8_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] string? paramName = null) + { + ArgumentException.ThrowIfNullOrEmpty(value, paramName); + return value; + } +#else + string paramName) + { + if (value is null) + { + throw new ArgumentNullException(paramName); + } + + return value.Length == 0 ? throw new ArgumentException("Value cannot be empty.", paramName) : value; + } +#endif + + /// + /// Throws when is , empty, or whitespace. + /// + /// Value to validate. + /// Parameter name for exception reporting. + /// The validated non-null, non-empty, non-whitespace . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string ThrowIfNullOrWhiteSpace( + string? value, +#if NET8_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] string? paramName = null) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value, paramName); + return value; + } +#else + string paramName) + { + if (value is null) + { + throw new ArgumentNullException(paramName); + } + + return string.IsNullOrWhiteSpace(value) ? throw new ArgumentException("Value cannot be empty or whitespace.", paramName) : value; + } +#endif + + /// + /// Throws when is negative. + /// + /// Value to validate. + /// Parameter name for exception reporting. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfNegative( + double value, +#if NET8_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] string? paramName = null) +#else + string paramName) +#endif + { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfNegative(value, paramName ?? nameof(value)); +#else + if (value < 0d) + { + throw new ArgumentOutOfRangeException(paramName, value, "Value cannot be negative."); + } +#endif + } + + /// + /// Throws when is not a finite number. + /// + /// Value to validate. + /// Parameter name for exception reporting. + /// Exception message when the value is not finite. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowIfNotFinite( + double value, + string paramName, + string message = "Value must be finite.") + { + if (double.IsNaN(value) || double.IsInfinity(value)) + { + throw new ArgumentOutOfRangeException(paramName, value, message); + } + } + + /// + /// Throws an . + /// + /// Parameter name for exception reporting. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + internal static void ThrowArgumentNull(string paramName) + { + throw new ArgumentNullException(paramName); + } + + /// + /// Throws an with parameter context. + /// + /// Exception message. + /// Parameter name for exception reporting. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + internal static void ThrowArgument(string message, string paramName) + { + throw new ArgumentException(message, paramName); + } + + /// + /// Throws an with parameter context and satisfies expression contexts. + /// + /// Return type used by the caller expression. + /// Exception message. + /// Parameter name for exception reporting. + /// This method always throws; no value is returned. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + internal static T ThrowArgument(string message, string paramName) + { + throw new ArgumentException(message, paramName); + } + + /// + /// Throws an with actual value context. + /// + /// Parameter name for exception reporting. + /// Actual out-of-range value. + /// Exception message. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + internal static void ThrowArgumentOutOfRange(string paramName, object actualValue, string message) + { + throw new ArgumentOutOfRangeException(paramName, actualValue, message); + } + + /// + /// Throws an . + /// + /// Parameter name for exception reporting. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + internal static void ThrowArgumentOutOfRange(string paramName) + { + throw new ArgumentOutOfRangeException(paramName); + } + + /// + /// Throws an . + /// + /// Parameter name for exception reporting. + /// Exception message. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + internal static void ThrowArgumentOutOfRange(string paramName, string message) + { + throw new ArgumentOutOfRangeException(paramName, message); + } +} diff --git a/src/ProjNet/AssemblyInfo.cs b/src/ProjNet/AssemblyInfo.cs deleted file mode 100644 index bc24a8d1..00000000 --- a/src/ProjNet/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly:InternalsVisibleTo("ProjNET.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db8d9f6d2769a39624730f1c8cc970ab47820735cb9ce9c0f1b60d4e9ecfd7b203a329d00000e06c706d90a62c2dcdbd19404e4eaad21e0bf1a18ba6aaddf3d9e8f4a435580d0330cd27173e0bd39aaf24cc0ee021bcc969c3dbe7b96a9d0b04e0946fdf876f173f840ddc55c8ad7ea581e5323c93a97f503804d5373a1c69d9")] diff --git a/src/ProjNet/Compatibility/HashCode/HashCode.cs b/src/ProjNet/Compatibility/HashCode/HashCode.cs new file mode 100644 index 00000000..a1581b07 --- /dev/null +++ b/src/ProjNet/Compatibility/HashCode/HashCode.cs @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System; + +using System.Collections.Generic; + +/// +/// Provides a System.HashCode polyfill for netstandard2.0. +/// +/// +/// +/// The implementation mirrors the .NET runtime hashing strategy (xxHash32-inspired mixing) and supports +/// both the static Combine methods and the mutable Add/ToHashCode pattern. +/// +/// +internal struct HashCode +{ + private const uint Prime1 = 2654435761U; + private const uint Prime2 = 2246822519U; + private const uint Prime3 = 3266489917U; + private const uint Prime4 = 668265263U; + private const uint Prime5 = 374761393U; + private const uint Seed = 0xA5A5A5A5U; + + private uint length; + private uint v1; + private uint v2; + private uint v3; + private uint v4; + private uint queue1; + private uint queue2; + private uint queue3; + + /// + /// Combines one value into a hash code. + /// + /// First value type. + /// First value. + /// The combined hash code. + public static int Combine(T1 value1) + { + uint hc1 = GetHashCode(value1); + uint hash = MixEmptyState(); + hash += 4; + hash = QueueRound(hash, hc1); + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines two values into a hash code. + /// + /// First value type. + /// Second value type. + /// First value. + /// Second value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2) + { + uint hc1 = GetHashCode(value1); + uint hc2 = GetHashCode(value2); + uint hash = MixEmptyState(); + hash += 8; + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines three values into a hash code. + /// + /// First value type. + /// Second value type. + /// Third value type. + /// First value. + /// Second value. + /// Third value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3) + { + uint hc1 = GetHashCode(value1); + uint hc2 = GetHashCode(value2); + uint hc3 = GetHashCode(value3); + uint hash = MixEmptyState(); + hash += 12; + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = QueueRound(hash, hc3); + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines four values into a hash code. + /// + /// First value type. + /// Second value type. + /// Third value type. + /// Fourth value type. + /// First value. + /// Second value. + /// Third value. + /// Fourth value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + uint hc1 = GetHashCode(value1); + uint hc2 = GetHashCode(value2); + uint hc3 = GetHashCode(value3); + uint hc4 = GetHashCode(value4); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 16; + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines five values into a hash code. + /// + /// First value type. + /// Second value type. + /// Third value type. + /// Fourth value type. + /// Fifth value type. + /// First value. + /// Second value. + /// Third value. + /// Fourth value. + /// Fifth value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + uint hc1 = GetHashCode(value1); + uint hc2 = GetHashCode(value2); + uint hc3 = GetHashCode(value3); + uint hc4 = GetHashCode(value4); + uint hc5 = GetHashCode(value5); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 20; + hash = QueueRound(hash, hc5); + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines six values into a hash code. + /// + /// First value type. + /// Second value type. + /// Third value type. + /// Fourth value type. + /// Fifth value type. + /// Sixth value type. + /// First value. + /// Second value. + /// Third value. + /// Fourth value. + /// Fifth value. + /// Sixth value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + uint hc1 = GetHashCode(value1); + uint hc2 = GetHashCode(value2); + uint hc3 = GetHashCode(value3); + uint hc4 = GetHashCode(value4); + uint hc5 = GetHashCode(value5); + uint hc6 = GetHashCode(value6); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 24; + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines seven values into a hash code. + /// + /// First value type. + /// Second value type. + /// Third value type. + /// Fourth value type. + /// Fifth value type. + /// Sixth value type. + /// Seventh value type. + /// First value. + /// Second value. + /// Third value. + /// Fourth value. + /// Fifth value. + /// Sixth value. + /// Seventh value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + { + uint hc1 = GetHashCode(value1); + uint hc2 = GetHashCode(value2); + uint hc3 = GetHashCode(value3); + uint hc4 = GetHashCode(value4); + uint hc5 = GetHashCode(value5); + uint hc6 = GetHashCode(value6); + uint hc7 = GetHashCode(value7); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 28; + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = QueueRound(hash, hc7); + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Combines eight values into a hash code. + /// + /// First value type. + /// Second value type. + /// Third value type. + /// Fourth value type. + /// Fifth value type. + /// Sixth value type. + /// Seventh value type. + /// Eighth value type. + /// First value. + /// Second value. + /// Third value. + /// Fourth value. + /// Fifth value. + /// Sixth value. + /// Seventh value. + /// Eighth value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + uint hc1 = GetHashCode(value1); + uint hc2 = GetHashCode(value2); + uint hc3 = GetHashCode(value3); + uint hc4 = GetHashCode(value4); + uint hc5 = GetHashCode(value5); + uint hc6 = GetHashCode(value6); + uint hc7 = GetHashCode(value7); + uint hc8 = GetHashCode(value8); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + v1 = Round(v1, hc5); + v2 = Round(v2, hc6); + v3 = Round(v3, hc7); + v4 = Round(v4, hc8); + + uint hash = MixState(v1, v2, v3, v4); + hash += 32; + hash = MixFinal(hash); + return (int)hash; + } + + /// + /// Adds a value into the hash. + /// + /// Value type. + /// Value to add. + public void Add(T value) + { + this.Add(value, comparer: null); + } + + /// + /// Adds a value into the hash using a comparer. + /// + /// Value type. + /// Value to add. + /// Comparer to obtain value hash code. + public void Add(T value, IEqualityComparer? comparer) + { + int valueHashCode; + if (comparer is null) + { + valueHashCode = value?.GetHashCode() ?? 0; + } + else + { + valueHashCode = value is null ? 0 : comparer.GetHashCode(value); + } + + this.AddHash((uint)valueHashCode); + } + + /// + /// Converts the accumulated state into a final hash code. + /// + /// The final hash code. + public int ToHashCode() + { + uint hash = this.length < 4 + ? MixEmptyState() + : MixState(this.v1, this.v2, this.v3, this.v4); + + hash += this.length * 4; + + uint position = this.length % 4; + if (position > 0) + { + hash = QueueRound(hash, this.queue1); + if (position > 1) + { + hash = QueueRound(hash, this.queue2); + if (position > 2) + { + hash = QueueRound(hash, this.queue3); + } + } + } + + hash = MixFinal(hash); + return (int)hash; + } + + private static uint GetHashCode(T value) + { + return (uint)(value?.GetHashCode() ?? 0); + } + + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = unchecked(Seed + Prime1 + Prime2); + v2 = unchecked(Seed + Prime2); + v3 = Seed; + v4 = unchecked(Seed - Prime1); + } + + private static uint Round(uint hash, uint input) + { + return RotateLeft(hash + (input * Prime2), 13) * Prime1; + } + + private static uint QueueRound(uint hash, uint queuedValue) + { + return RotateLeft(hash + (queuedValue * Prime3), 17) * Prime4; + } + + private static uint MixState(uint v1, uint v2, uint v3, uint v4) + { + return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18); + } + + private static uint MixEmptyState() + { + return Seed + Prime5; + } + + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + private static uint RotateLeft(uint value, int offset) + { + return (value << offset) | (value >> (32 - offset)); + } + + private void AddHash(uint value) + { + uint previousLength = this.length++; + uint position = previousLength % 4; + + if (position == 0) + { + this.queue1 = value; + return; + } + + if (position == 1) + { + this.queue2 = value; + return; + } + + if (position == 2) + { + this.queue3 = value; + return; + } + + if (previousLength == 3) + { + Initialize(out this.v1, out this.v2, out this.v3, out this.v4); + } + + this.v1 = Round(this.v1, this.queue1); + this.v2 = Round(this.v2, this.queue2); + this.v3 = Round(this.v3, this.queue3); + this.v4 = Round(this.v4, value); + } +} +#endif diff --git a/src/ProjNet/Compatibility/IsExternalInit/IsExternalInit.cs b/src/ProjNet/Compatibility/IsExternalInit/IsExternalInit.cs new file mode 100644 index 00000000..f469cc5e --- /dev/null +++ b/src/ProjNet/Compatibility/IsExternalInit/IsExternalInit.cs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if !NET5_0_OR_GREATER +namespace System.Runtime.CompilerServices; + +/// +/// Reserved to be used by the compiler for tracking metadata. +/// +internal static class IsExternalInit +{ +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/AllowNullAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/AllowNullAttribute.cs new file mode 100644 index 00000000..dc34bfcf --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/AllowNullAttribute.cs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that is allowed as an input value even when the corresponding type disallows it. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class AllowNullAttribute : Attribute +{ +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/DisallowNullAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/DisallowNullAttribute.cs new file mode 100644 index 00000000..2e163c7e --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/DisallowNullAttribute.cs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that is disallowed as an input value even when the corresponding type allows it. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class DisallowNullAttribute : Attribute +{ +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/DoesNotReturnAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/DoesNotReturnAttribute.cs new file mode 100644 index 00000000..4f971c79 --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/DoesNotReturnAttribute.cs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that the method will never return under any circumstance. +/// +[AttributeUsage(AttributeTargets.Method, Inherited = false)] +internal sealed class DoesNotReturnAttribute : Attribute +{ +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/DoesNotReturnIfAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/DoesNotReturnIfAttribute.cs new file mode 100644 index 00000000..3b482442 --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/DoesNotReturnIfAttribute.cs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that the method will not return if the associated parameter has the specified value. +/// +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class DoesNotReturnIfAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Parameter value condition that causes the method not to return. + internal DoesNotReturnIfAttribute(bool parameterValue) + { + this.ParameterValue = parameterValue; + } + + /// + /// Gets a value indicating whether the method does not return when the associated parameter equals this value. + /// + internal bool ParameterValue { get; } +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/MaybeNullAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/MaybeNullAttribute.cs new file mode 100644 index 00000000..c387a1de --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/MaybeNullAttribute.cs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that an output may be even when the corresponding type disallows it. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class MaybeNullAttribute : Attribute +{ +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/MaybeNullWhenAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/MaybeNullWhenAttribute.cs new file mode 100644 index 00000000..a5049ad6 --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/MaybeNullWhenAttribute.cs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that a parameter may be when the associated method returns the specified value. +/// +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class MaybeNullWhenAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Return value condition that allows . + internal MaybeNullWhenAttribute(bool returnValue) + { + this.ReturnValue = returnValue; + } + + /// + /// Gets a value indicating whether is allowed when the method returns this value. + /// + internal bool ReturnValue { get; } +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/MemberNotNullAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/MemberNotNullAttribute.cs new file mode 100644 index 00000000..3f75ce2b --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/MemberNotNullAttribute.cs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that the listed fields and properties are non-null when the attributed method returns successfully. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = false)] +internal sealed class MemberNotNullAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Member guaranteed to be non-null. + internal MemberNotNullAttribute(string member) + { + this.Members = [member]; + } + + /// + /// Initializes a new instance of the class. + /// + /// Members guaranteed to be non-null. + internal MemberNotNullAttribute(params string[] members) + { + this.Members = members; + } + + /// + /// Gets members that are guaranteed to be non-null. + /// + internal string[] Members { get; } +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/MemberNotNullWhenAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/MemberNotNullWhenAttribute.cs new file mode 100644 index 00000000..918427c1 --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/MemberNotNullWhenAttribute.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that the listed fields and properties are non-null when the attributed method returns the specified value. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = false)] +internal sealed class MemberNotNullWhenAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Return value that guarantees non-null members. + /// Member guaranteed to be non-null. + internal MemberNotNullWhenAttribute(bool returnValue, string member) + { + this.ReturnValue = returnValue; + this.Members = [member]; + } + + /// + /// Initializes a new instance of the class. + /// + /// Return value that guarantees non-null members. + /// Members guaranteed to be non-null. + internal MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + this.ReturnValue = returnValue; + this.Members = members; + } + + /// + /// Gets a value indicating whether members are guaranteed non-null for this return value. + /// + internal bool ReturnValue { get; } + + /// + /// Gets members that are guaranteed to be non-null. + /// + internal string[] Members { get; } +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/NotNullAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/NotNullAttribute.cs new file mode 100644 index 00000000..0340ed57 --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/NotNullAttribute.cs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that an output is not even when the corresponding type allows it. +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class NotNullAttribute : Attribute +{ +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/NotNullIfNotNullAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/NotNullIfNotNullAttribute.cs new file mode 100644 index 00000000..7c1263e8 --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/NotNullIfNotNullAttribute.cs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that an output is non-null if the named parameter is non-null. +/// +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] +internal sealed class NotNullIfNotNullAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter whose null-state governs the target value. + internal NotNullIfNotNullAttribute(string parameterName) + { + this.ParameterName = parameterName; + } + + /// + /// Gets the parameter name that controls the target null-state. + /// + internal string ParameterName { get; } +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/NotNullWhenAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/NotNullWhenAttribute.cs new file mode 100644 index 00000000..4b8baf0f --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/NotNullWhenAttribute.cs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Specifies that a parameter is not when the associated method returns the specified value. +/// +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class NotNullWhenAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Return value condition that guarantees non-null. + internal NotNullWhenAttribute(bool returnValue) + { + this.ReturnValue = returnValue; + } + + /// + /// Gets a value indicating whether the value is guaranteed non-null when the method returns this value. + /// + internal bool ReturnValue { get; } +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/RequiresDynamicCodeAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/RequiresDynamicCodeAttribute.cs new file mode 100644 index 00000000..b55e2a07 --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/RequiresDynamicCodeAttribute.cs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Indicates that the specified member requires runtime code generation and may not be compatible with AOT. +/// +[AttributeUsage( + AttributeTargets.Constructor + | AttributeTargets.Method + | AttributeTargets.Class, + Inherited = false)] +internal sealed class RequiresDynamicCodeAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Message that describes why the member is not AOT-safe. + internal RequiresDynamicCodeAttribute(string message) + { + this.Message = message; + } + + /// + /// Gets the AOT warning message. + /// + internal string Message { get; } + + /// + /// Gets or sets the URL with additional guidance. + /// + internal string? Url { get; set; } +} +#endif diff --git a/src/ProjNet/Compatibility/NullableAttributes/RequiresUnreferencedCodeAttribute.cs b/src/ProjNet/Compatibility/NullableAttributes/RequiresUnreferencedCodeAttribute.cs new file mode 100644 index 00000000..78767e5e --- /dev/null +++ b/src/ProjNet/Compatibility/NullableAttributes/RequiresUnreferencedCodeAttribute.cs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +using System; + +/// +/// Indicates that the specified method requires dynamic access to code that may be removed by trimming. +/// +[AttributeUsage( + AttributeTargets.Constructor + | AttributeTargets.Method + | AttributeTargets.Class, + Inherited = false)] +internal sealed class RequiresUnreferencedCodeAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// Message that describes why the member is not trimming-safe. + internal RequiresUnreferencedCodeAttribute(string message) + { + this.Message = message; + } + + /// + /// Gets the trimming warning message. + /// + internal string Message { get; } + + /// + /// Gets or sets the URL with additional guidance. + /// + internal string? Url { get; set; } +} +#endif diff --git a/src/ProjNet/CompatibilitySuppressions.xml b/src/ProjNet/CompatibilitySuppressions.xml new file mode 100644 index 00000000..6c25a8ae --- /dev/null +++ b/src/ProjNet/CompatibilitySuppressions.xml @@ -0,0 +1,1404 @@ + + + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._e + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._es + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._inverse + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._metersPerUnit + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._Parameters + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._reciprocalMetersPerUnit + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._semiMajor + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._semiMinor + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection.S2R + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.AngularUnit.set_RadiansPerUnit(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.AxisInfo.set_Name(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.AxisInfo.set_Orientation(ProjNet.CoordinateSystems.AxisOrientationEnum) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.CompoundCoordinateSystem.set_HeadCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.CompoundCoordinateSystem.set_TailCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.CoordinateSystem.set_DefaultEnvelope(System.Double[]) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Datum.set_DatumType(ProjNet.CoordinateSystems.DatumType) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_AxisUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_InverseFlattening(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_IsIvfDefinitive(System.Boolean) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_SemiMajorAxis(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_SemiMinorAxis(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.FittedCoordinateSystem.#ctor(ProjNet.CoordinateSystems.CoordinateSystem,ProjNet.CoordinateSystems.Transformations.MathTransform,System.String,System.String,System.Int64,System.String,System.String,System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeocentricCoordinateSystem.set_HorizontalDatum(ProjNet.CoordinateSystems.HorizontalDatum) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeocentricCoordinateSystem.set_LinearUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeocentricCoordinateSystem.set_PrimeMeridian(ProjNet.CoordinateSystems.PrimeMeridian) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeographicCoordinateSystem.set_AngularUnit(ProjNet.CoordinateSystems.AngularUnit) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeographicCoordinateSystem.set_PrimeMeridian(ProjNet.CoordinateSystems.PrimeMeridian) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.HorizontalCoordinateSystem.set_HorizontalDatum(ProjNet.CoordinateSystems.HorizontalDatum) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.HorizontalDatum.set_Ellipsoid(ProjNet.CoordinateSystems.Ellipsoid) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.HorizontalDatum.set_Wgs84Parameters(ProjNet.CoordinateSystems.Wgs84ConversionInfo) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Abbreviation(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Alias(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Authority(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_AuthorityCode(System.Int64) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Name(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Remarks(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.LinearUnit.set_MetersPerUnit(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Parameter.set_Name(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Parameter.set_Value(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.PrimeMeridian.set_AngularUnit(ProjNet.CoordinateSystems.AngularUnit) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.PrimeMeridian.set_Longitude(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectedCoordinateSystem.set_GeographicCoordinateSystem(ProjNet.CoordinateSystems.GeographicCoordinateSystem) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectedCoordinateSystem.set_LinearUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectedCoordinateSystem.set_Projection(ProjNet.CoordinateSystems.IProjection) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectionParameter.set_Name(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectionParameter.set_Value(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.adjust_lon(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.asinz(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.authlat(System.Double,System.Double[]) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.authset(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.CUBE(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e0fn(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e1fn(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e2fn(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e3fn(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e4fn(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.get_central_parallel + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.get_lon_origin + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.get_phi0 + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.GMAX(System.Double@,System.Double@) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.GMIN(System.Double@,System.Double@) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.hypot(System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.IMOD(System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.inv_mlfn(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.mlfn(System.Double,System.Double,System.Double,System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.mlfn(System.Double,System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.msfnz(System.Double,System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.phi1z(System.Double,System.Double,System.Int64@) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.phi2z(System.Double,System.Double,System.Int64@) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.qsfn(System.Double,System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.qsfnz(System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.QUAD(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Abbreviation(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Alias(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Authority(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_AuthorityCode(System.Int64) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_lon_origin(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Name(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Remarks(System.String) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.sign(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.sincos(System.Double,System.Double@,System.Double@) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.tsfnz(System.Double,System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.ProjectionParameterSet.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.GeographicTransform.set_SourceGCS(ProjNet.CoordinateSystems.GeographicCoordinateSystem) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.GeographicTransform.set_TargetGCS(ProjNet.CoordinateSystems.GeographicCoordinateSystem) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.MathTransform.Transform(System.Double,System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.MathTransform.Transform(System.Double,System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Unit.set_ConversionFactor(System.Double) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.VerticalCoordinateSystem.set_LinearUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.VerticalCoordinateSystem.set_VerticalDatum(ProjNet.CoordinateSystems.VerticalDatum) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystemServices.#ctor(ProjNet.CoordinateSystems.CoordinateSystemFactory,ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory,System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{System.Int32,System.String}}) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystemServices.#ctor(System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{System.Int32,System.String}}) + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystemServices.GetEnumerator + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._e + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._es + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._inverse + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._metersPerUnit + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._Parameters + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._reciprocalMetersPerUnit + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._semiMajor + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection._semiMinor + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + F:ProjNet.CoordinateSystems.Projections.MapProjection.S2R + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.AngularUnit.set_RadiansPerUnit(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.AxisInfo.set_Name(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.AxisInfo.set_Orientation(ProjNet.CoordinateSystems.AxisOrientationEnum) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.CompoundCoordinateSystem.set_HeadCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.CompoundCoordinateSystem.set_TailCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.CoordinateSystem.set_DefaultEnvelope(System.Double[]) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Datum.set_DatumType(ProjNet.CoordinateSystems.DatumType) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_AxisUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_InverseFlattening(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_IsIvfDefinitive(System.Boolean) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_SemiMajorAxis(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Ellipsoid.set_SemiMinorAxis(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.FittedCoordinateSystem.#ctor(ProjNet.CoordinateSystems.CoordinateSystem,ProjNet.CoordinateSystems.Transformations.MathTransform,System.String,System.String,System.Int64,System.String,System.String,System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeocentricCoordinateSystem.set_HorizontalDatum(ProjNet.CoordinateSystems.HorizontalDatum) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeocentricCoordinateSystem.set_LinearUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeocentricCoordinateSystem.set_PrimeMeridian(ProjNet.CoordinateSystems.PrimeMeridian) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeographicCoordinateSystem.set_AngularUnit(ProjNet.CoordinateSystems.AngularUnit) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.GeographicCoordinateSystem.set_PrimeMeridian(ProjNet.CoordinateSystems.PrimeMeridian) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.HorizontalCoordinateSystem.set_HorizontalDatum(ProjNet.CoordinateSystems.HorizontalDatum) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.HorizontalDatum.set_Ellipsoid(ProjNet.CoordinateSystems.Ellipsoid) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.HorizontalDatum.set_Wgs84Parameters(ProjNet.CoordinateSystems.Wgs84ConversionInfo) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Abbreviation(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Alias(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Authority(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_AuthorityCode(System.Int64) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Name(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Info.set_Remarks(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.LinearUnit.set_MetersPerUnit(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Parameter.set_Name(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Parameter.set_Value(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.PrimeMeridian.set_AngularUnit(ProjNet.CoordinateSystems.AngularUnit) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.PrimeMeridian.set_Longitude(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectedCoordinateSystem.set_GeographicCoordinateSystem(ProjNet.CoordinateSystems.GeographicCoordinateSystem) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectedCoordinateSystem.set_LinearUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectedCoordinateSystem.set_Projection(ProjNet.CoordinateSystems.IProjection) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectionParameter.set_Name(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.ProjectionParameter.set_Value(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.adjust_lon(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.asinz(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.authlat(System.Double,System.Double[]) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.authset(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.CUBE(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e0fn(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e1fn(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e2fn(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e3fn(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.e4fn(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.get_central_parallel + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.get_lon_origin + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.get_phi0 + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.GMAX(System.Double@,System.Double@) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.GMIN(System.Double@,System.Double@) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.hypot(System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.IMOD(System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.inv_mlfn(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.mlfn(System.Double,System.Double,System.Double,System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.mlfn(System.Double,System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.msfnz(System.Double,System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.phi1z(System.Double,System.Double,System.Int64@) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.phi2z(System.Double,System.Double,System.Int64@) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.qsfn(System.Double,System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.qsfnz(System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.QUAD(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Abbreviation(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Alias(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Authority(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_AuthorityCode(System.Int64) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_lon_origin(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Name(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.set_Remarks(System.String) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.sign(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.sincos(System.Double,System.Double@,System.Double@) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.MapProjection.tsfnz(System.Double,System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Projections.ProjectionParameterSet.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.GeographicTransform.set_SourceGCS(ProjNet.CoordinateSystems.GeographicCoordinateSystem) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.GeographicTransform.set_TargetGCS(ProjNet.CoordinateSystems.GeographicCoordinateSystem) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.MathTransform.Transform(System.Double,System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Transformations.MathTransform.Transform(System.Double,System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.Unit.set_ConversionFactor(System.Double) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.VerticalCoordinateSystem.set_LinearUnit(ProjNet.CoordinateSystems.LinearUnit) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystems.VerticalCoordinateSystem.set_VerticalDatum(ProjNet.CoordinateSystems.VerticalDatum) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystemServices.#ctor(ProjNet.CoordinateSystems.CoordinateSystemFactory,ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory,System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{System.Int32,System.String}}) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystemServices.#ctor(System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{System.Int32,System.String}}) + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0002 + M:ProjNet.CoordinateSystemServices.GetEnumerator + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.AxisInfo + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Parameter + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.ProjectionParameter + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Projections.LambertAzimuthalEqualAreaProjection + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Projections.ProjectionParameterSet + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Transformations.AffineTransform + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Wgs84ConversionInfo + lib/netstandard2.0/ProjNET.dll + lib/netstandard2.0/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.AxisInfo + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Parameter + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.ProjectionParameter + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Projections.LambertAzimuthalEqualAreaProjection + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Projections.ProjectionParameterSet + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Transformations.AffineTransform + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + + CP0009 + T:ProjNet.CoordinateSystems.Wgs84ConversionInfo + lib/netstandard2.1/ProjNET.dll + lib/netstandard2.1/ProjNET.dll + true + + \ No newline at end of file diff --git a/src/ProjNet/CoordinateSystemServices.cs b/src/ProjNet/CoordinateSystemServices.cs index 94fd8e3f..2fa9ec08 100644 --- a/src/ProjNet/CoordinateSystemServices.cs +++ b/src/ProjNet/CoordinateSystemServices.cs @@ -1,394 +1,568 @@ -// Copyright 2015 - Spartaco Giubbolini, Felix Obermaier (www.ivv-aachen.de) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet; + using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.Threading; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using ProjNet.CoordinateSystems; using ProjNet.CoordinateSystems.Transformations; - -namespace ProjNet +using ProjNet.Data; +using ProjNet.IO.Wkt; + +/// +/// Provides coordinate system lookup and transformation creation backed by a registry of SRID-keyed systems. +/// +/// +/// +/// Thread safety: Public lookup and transformation-creation methods wait for the one-time initialization +/// load to finish and are safe to call concurrently after the registry has become stable. The protected +/// registration path serializes AddCoordinateSystem updates with internal locks, but derived types +/// should complete further registry mutation before exposing an instance for concurrent reads or enumeration +/// because the backing dictionaries are not concurrent collections. +/// +/// +public class CoordinateSystemServices // : ICoordinateSystemServices { + private readonly Dictionary csBySrid; + private readonly Dictionary sridByCs; + private readonly ConcurrentDictionary<(int SourceSrid, int TargetSrid), ICoordinateTransformation> transformationCache; + + private readonly CoordinateSystemFactory coordinateSystemFactory; + private readonly CoordinateTransformationFactory ctFactory; + private readonly ICoordinateSystemDefinitionProvider definitionProvider; + + private readonly System.Threading.Tasks.Task initializationTask; + /// - /// A coordinate system services class + /// Initializes a new instance of the class + /// using the specified factories and the default definition provider. /// - public class CoordinateSystemServices // : ICoordinateSystemServices + /// The coordinate system factory to use. + /// The coordinate transformation factory to use. + public CoordinateSystemServices( + CoordinateSystemFactory coordinateSystemFactory, + CoordinateTransformationFactory coordinateTransformationFactory) + : this(coordinateSystemFactory, coordinateTransformationFactory, null, null) { - //private static ICoordinateSequenceFactory _coordinateSequenceFactory; + } - ///// - ///// Gets or sets a default coordinate sequence factory - ///// - //public static ICoordinateSequenceFactory CoordinateSequenceFactory - //{ - // get { return _coordinateSequenceFactory ?? new CoordinateArraySequenceFactory(); } - // set { _coordinateSequenceFactory = value; } - //} + /// + /// Initializes a new instance of the class + /// pre-populated from the supplied SRID-to-WKT definition pairs. + /// + /// An enumeration of SRID-to-WKT coordinate system definitions. + public CoordinateSystemServices(IEnumerable definitions) + : this(new CoordinateSystemFactory(), new CoordinateTransformationFactory(), definitions, null) + { + } - private readonly Dictionary _csBySrid; - private readonly Dictionary _sridByCs; + /// + /// Initializes a new instance of the class + /// using default factories and the default definition provider. + /// + public CoordinateSystemServices() + : this(new CoordinateSystemFactory(), new CoordinateTransformationFactory(), null, null) + { + } - private readonly CoordinateSystemFactory _coordinateSystemFactory; - private readonly CoordinateTransformationFactory _ctFactory; - - private readonly ManualResetEvent _initialization = new ManualResetEvent(false); + /// + /// Initializes a new instance of the class + /// using the supplied definition provider and default factories. + /// + /// Coordinate system definition provider that supplies SRID definitions. + public CoordinateSystemServices(ICoordinateSystemDefinitionProvider definitionProvider) + : this(new CoordinateSystemFactory(), new CoordinateTransformationFactory(), null, definitionProvider) + { + } - #region CsEqualityComparer class - private class CsEqualityComparer : EqualityComparer - { - public override bool Equals(IInfo x, IInfo y) - { - return x.AuthorityCode == y.AuthorityCode && - string.Compare(x.Authority, y.Authority, StringComparison.OrdinalIgnoreCase) == 0; - } + /// + /// Initializes a new instance of the class + /// pre-populated from the supplied SRID-to-WKT definition pairs. + /// + /// The coordinate system factory to use. + /// The coordinate transformation factory to use. + /// An enumeration of SRID-to-WKT coordinate system definitions. + public CoordinateSystemServices( + CoordinateSystemFactory coordinateSystemFactory, + CoordinateTransformationFactory coordinateTransformationFactory, + IEnumerable? enumeration) + : this(coordinateSystemFactory, coordinateTransformationFactory, enumeration, null) + { + } - public override int GetHashCode(IInfo obj) - { - if (obj == null) return 0; - return Convert.ToInt32(obj.AuthorityCode) + (obj.Authority != null ? obj.Authority.GetHashCode() : 0); - } + /// + /// Initializes a new instance of the class + /// with explicit control over all dependencies. + /// + /// The coordinate system factory to use. + /// The coordinate transformation factory to use. + /// An enumeration of SRID-to-WKT coordinate system definitions; when , is used instead. + /// Definition provider used when is ; defaults to when . + public CoordinateSystemServices( + CoordinateSystemFactory coordinateSystemFactory, + CoordinateTransformationFactory coordinateTransformationFactory, + IEnumerable? enumeration, + ICoordinateSystemDefinitionProvider? definitionProvider) + { + this.coordinateSystemFactory = ArgumentGuard.ThrowIfNull(coordinateSystemFactory, nameof(coordinateSystemFactory)); + this.ctFactory = ArgumentGuard.ThrowIfNull(coordinateTransformationFactory, nameof(coordinateTransformationFactory)); + this.definitionProvider = definitionProvider ?? new ManagedCoordinateSystemDefinitionProvider(); + + this.csBySrid = []; + this.sridByCs = new(new CsEqualityComparer()); + this.transformationCache = []; + + object enumObj; + if (enumeration is not null) + { + enumObj = enumeration; + } + else if (this.definitionProvider is IManagedCoordinateSystemProvider managedCoordinateSystemProvider) + { + enumObj = managedCoordinateSystemProvider.GetCoordinateSystems(); + } + else + { + enumObj = this.definitionProvider; } - #endregion - #region CoordinateSystemKey class + this.initializationTask = System.Threading.Tasks.Task.Run(() => this.InitializeFromEnumeration(enumObj)); + } - private class CoordinateSystemKey : IInfo + /// + /// Gets the number of coordinate systems registered in this instance. + /// + protected int Count + { + get { - public CoordinateSystemKey(string authority, long authorityCode) - { - Authority = authority; - AuthorityCode = authorityCode; - } + this.WaitForInitialization(); + return this.sridByCs.Count; + } + } - public bool EqualParams(object obj) - { - throw new NotSupportedException(); - } + /// + /// Returns the coordinate system registered under the specified SRID. + /// + /// The SRID of the coordinate system. + /// The coordinate system, or if not found. + public CoordinateSystem? GetCoordinateSystem(int srid) + { + this.WaitForInitialization(); + return this.csBySrid.TryGetValue(srid, out CoordinateSystem? cs) ? cs : null; + } + + /// + /// Tries to get a coordinate system by SRID. + /// + /// The SRID of the coordinate system. + /// The coordinate system if found; otherwise . + /// if a coordinate system was found; otherwise . + public bool TryGetCoordinateSystem(int srid, [NotNullWhen(true)] out CoordinateSystem? coordinateSystem) + { + this.WaitForInitialization(); + return this.csBySrid.TryGetValue(srid, out coordinateSystem); + } + + /// + /// Returns the coordinate system by and . + /// + /// The authority for the coordinate system. + /// The code assigned to the coordinate system by . + /// The coordinate system, or when no entry is registered. + public CoordinateSystem? GetCoordinateSystem(string authority, long code) + { + int? srid = this.GetSRID(authority, code); + return srid.HasValue ? this.GetCoordinateSystem(srid.Value) : null; + } - public string Name { get { return null; } } - public string Authority { get; private set; } - public long AuthorityCode { get; private set; } - public string Alias { get { return null; } } - public string Abbreviation { get { return null; } } - public string Remarks { get { return null; } } - public string WKT { get { return null; } } - public string XML { get { return null; } } + /// + /// Tries to get a coordinate system by authority and code. + /// + /// The authority name. + /// The authority code. + /// The coordinate system if found; otherwise . + /// if a coordinate system was found; otherwise . + public bool TryGetCoordinateSystem(string authority, long code, [NotNullWhen(true)] out CoordinateSystem? coordinateSystem) + { + coordinateSystem = null; + int? srid = this.GetSRID(authority, code); + if (!srid.HasValue) + { + return false; } - #endregion + coordinateSystem = this.GetCoordinateSystem(srid.Value); + return coordinateSystem is not null; + } + + /// + /// Attempts to replace a parsed coordinate system with the canonical catalog instance identified by its authority metadata. + /// + /// The parsed coordinate system to resolve. + /// The canonical catalog instance when the authority metadata matches a registered entry; otherwise the original instance. + public CoordinateSystem ResolveFromCatalog(CoordinateSystem parsed) + { + parsed = ArgumentGuard.ThrowIfNull(parsed, nameof(parsed)); + return this.TryResolveFromCatalog(parsed, out CoordinateSystem? coordinateSystem) ? coordinateSystem : parsed; + } - /// - /// Creates an instance of this class - /// - /// The coordinate sequence factory to use. - /// The coordinate transformation factory to use - public CoordinateSystemServices(CoordinateSystemFactory coordinateSystemFactory, - CoordinateTransformationFactory coordinateTransformationFactory) - : this(coordinateSystemFactory, coordinateTransformationFactory, null) + /// + /// Attempts to replace a parsed coordinate system with the canonical catalog instance identified by its authority metadata. + /// + /// The parsed coordinate system to resolve. + /// The canonical catalog instance when resolution succeeds; otherwise . + /// when the parsed coordinate system resolved to a registered catalog entry; otherwise . + public bool TryResolveFromCatalog(CoordinateSystem parsed, [NotNullWhen(true)] out CoordinateSystem? coordinateSystem) + { + parsed = ArgumentGuard.ThrowIfNull(parsed, nameof(parsed)); + coordinateSystem = null; + + if (string.IsNullOrWhiteSpace(parsed.Authority) || parsed.AuthorityCode < 0) { + return false; } - /// - /// Creates an instance of this class. - /// - /// An enumeration of coordinate system definitions (WKT) - public CoordinateSystemServices(IEnumerable> definitions) - : this(new CoordinateSystemFactory(), new CoordinateTransformationFactory(), definitions) + return this.TryGetCoordinateSystem(parsed.Authority, parsed.AuthorityCode, out coordinateSystem); + } + + /// + /// Gets all available SRID values currently loaded in the registry. + /// + /// Sorted SRID values. + public int[] GetAvailableSridValues() + { + this.WaitForInitialization(); + return [.. this.csBySrid.Keys.OrderBy(v => v)]; + } + + /// + /// Returns the SRID under which the coordinate system identified by and is registered. + /// + /// The authority name. + /// The code assigned by . + /// The SRID, or if no matching coordinate system is registered. + public int? GetSRID(string authority, long authorityCode) + { + var key = new CoordinateSystemKey(authority, authorityCode); + int srid; + this.WaitForInitialization(); + return this.sridByCs.TryGetValue(key, out srid) ? srid : null; + } + + /// + /// Creates a coordinate transformation between two spatial reference systems identified by their SRIDs. + /// + /// + /// This is a convenience overload for . + /// Transformation instances created through this overload are cached by SRID pair until the registry changes or is cleared. + /// + /// The SRID of the source spatial reference system. + /// The SRID of the target spatial reference system. + /// A coordinate transformation, or if no transformation could be created. + /// + /// Thrown when both SRIDs resolve to coordinate systems but no transformation path can be found between them. + /// + public ICoordinateTransformation? CreateTransformation(int sourceSrid, int targetSrid) + { + this.WaitForInitialization(); + + (int SourceSrid, int TargetSrid) key = (sourceSrid, targetSrid); + if (this.transformationCache.TryGetValue(key, out ICoordinateTransformation? transformation)) { + return transformation; } - /// - /// Creates an instance of this class - /// - public CoordinateSystemServices() - : this(new CoordinateSystemFactory(), new CoordinateTransformationFactory(), null) + if (!this.csBySrid.TryGetValue(sourceSrid, out CoordinateSystem? source) || + !this.csBySrid.TryGetValue(targetSrid, out CoordinateSystem? target)) { + return null; } - //public Func GetDefinition { get; set; } - /* - public static string GetFromSpatialReferenceOrg(string authority, long code) + transformation = this.ctFactory.CreateFromCoordinateSystems(source, target); + return this.transformationCache.GetOrAdd(key, transformation); + } + + /// + /// Creates a coordinate transformation between two spatial reference systems. + /// + /// The source spatial reference system. + /// The target spatial reference system. + /// A coordinate transformation, or if no transformation could be created. + /// + /// Thrown when both coordinate systems are provided but no transformation path can be found between them. + /// + public ICoordinateTransformation? CreateTransformation(CoordinateSystem? source, CoordinateSystem? target) + { + return source is null || target is null ? null : this.ctFactory.CreateFromCoordinateSystems(source, target); + } + + /// + /// This operation is not supported. + /// + /// The SRID of the coordinate system to remove. + /// This method never returns normally. + /// Always thrown; removing coordinate systems is not supported. + public bool RemoveCoordinateSystem(int srid) + { + throw new NotSupportedException(); + } + + /// + /// Returns an enumerator that iterates over all registered coordinate system entries. + /// + /// An enumerator over the registered SRID-to-coordinate-system entries. + public IEnumerator GetEnumerator() + { + this.WaitForInitialization(); + return this.csBySrid + .Select(static pair => new CoordinateSystemEntry(pair.Key, pair.Value)) + .GetEnumerator(); + } + + /// + /// Registers a coordinate system under the specified SRID, replacing any existing entry for that SRID. + /// + /// The SRID key. + /// The coordinate system to register. + protected void AddCoordinateSystem(int srid, CoordinateSystem coordinateSystem) + { + lock (((IDictionary)this.csBySrid).SyncRoot) { - var url = string.Format("http://spatialreference.org/ref/{0}/{1}/ogcwkt/", - authority.ToLowerInvariant(), - code); - var req = (HttpWebRequest) WebRequest.Create(url); - using (var resp = req.GetResponse()) + lock (((IDictionary)this.sridByCs).SyncRoot) { - using (var resps = resp.GetResponseStream()) + if (this.sridByCs.ContainsKey(coordinateSystem)) + { + return; + } + + if (this.csBySrid.TryGetValue(srid, out CoordinateSystem? existingCoordinateSystem)) { - if (resps != null) + if (ReferenceEquals(coordinateSystem, existingCoordinateSystem)) { - using (var sr = new StreamReader(resps)) - return sr.ReadToEnd(); + return; } + + this.sridByCs.Remove(existingCoordinateSystem); + this.csBySrid[srid] = coordinateSystem; + this.sridByCs.Add(coordinateSystem, srid); + } + else + { + this.csBySrid.Add(srid, coordinateSystem); + this.sridByCs.Add(coordinateSystem, srid); } + + this.InvalidateTransformationCache(srid); } - return null; } - */ - - /// - /// Creates an instance of this class - /// - /// The coordinate sequence factory to use. - /// The coordinate transformation factory to use - /// An enumeration of coordinate system definitions (WKT) - public CoordinateSystemServices(CoordinateSystemFactory coordinateSystemFactory, - CoordinateTransformationFactory coordinateTransformationFactory, - IEnumerable> enumeration) - { - if (coordinateSystemFactory == null) - throw new ArgumentNullException(nameof(coordinateSystemFactory)); - _coordinateSystemFactory = coordinateSystemFactory; - - if (coordinateTransformationFactory == null) - throw new ArgumentNullException(nameof(coordinateTransformationFactory)); - _ctFactory = coordinateTransformationFactory; - - _csBySrid = new Dictionary(); - _sridByCs = new Dictionary(new CsEqualityComparer()); + } - object enumObj = (object)enumeration ?? DefaultInitialization(); - _initialization = new ManualResetEvent(false); - System.Threading.Tasks.Task.Run(() => FromEnumeration((new[] { this, enumObj }))); - } + /// + /// Registers a coordinate system using its own as the SRID. + /// + /// The coordinate system to register. + /// The SRID under which the coordinate system was registered. + protected virtual int AddCoordinateSystem(CoordinateSystem coordinateSystem) + { + coordinateSystem = ArgumentGuard.ThrowIfNull(coordinateSystem, nameof(coordinateSystem)); + int srid = (int)coordinateSystem.AuthorityCode; + this.AddCoordinateSystem(srid, coordinateSystem); - //private CoordinateSystemServices(ICoordinateSystemFactory coordinateSystemFactory, - // ICoordinateTransformationFactory coordinateTransformationFactory, - // IEnumerable> enumeration) - // : this(coordinateSystemFactory, coordinateTransformationFactory) - //{ - // var enumObj = (object)enumeration ?? DefaultInitialization(); - // _initialization = new ManualResetEvent(false); - // ThreadPool.QueueUserWorkItem(FromEnumeration, new[] { this, enumObj }); - //} + return srid; + } - private static CoordinateSystem CreateCoordinateSystem(CoordinateSystemFactory coordinateSystemFactory, string wkt) + /// + /// Removes all registered coordinate systems. + /// + protected void Clear() + { + lock (((IDictionary)this.csBySrid).SyncRoot) { - try + lock (((IDictionary)this.sridByCs).SyncRoot) { - return coordinateSystemFactory.CreateFromWkt(wkt.Replace("ELLIPSOID", "SPHEROID")); - } - catch (Exception) - { - // as a fallback we ignore projections not supported - return null; + this.csBySrid.Clear(); + this.sridByCs.Clear(); + this.transformationCache.Clear(); } } + } - private static IEnumerable> DefaultInitialization() + private static CoordinateSystem? CreateCoordinateSystem(CoordinateSystemFactory coordinateSystemFactory, string wkt) + { + try + { + return coordinateSystemFactory.CreateFromWkt(StringCompatibility.ReplaceOrdinal(wkt, "ELLIPSOID", "SPHEROID")); + } + catch (WktParseException) { - yield return new KeyValuePair(4326, GeographicCoordinateSystem.WGS84); - yield return new KeyValuePair(3857, ProjectedCoordinateSystem.WebMercator); + // Skip malformed definitions so registry initialization can continue with the remaining rows. + return null; + } + catch (NotSupportedException) + { + // Skip definitions that describe constructs the current reader cannot materialize yet. + return null; } + catch (FormatException) + { + // Skip definitions with invalid numeric/text formatting while loading bulk catalogs. + return null; + } + } - private static void FromEnumeration(CoordinateSystemServices css, - IEnumerable> enumeration) + private static void FromEnumeration( + CoordinateSystemServices css, + IEnumerable enumeration) + { + foreach (CoordinateSystemEntry entry in enumeration) { - foreach (var sridCs in enumeration) - { - css.AddCoordinateSystem(sridCs.Key, sridCs.Value); - } + css.AddCoordinateSystem(entry.Srid, entry.CoordinateSystem); } + } - private static IEnumerable> CreateCoordinateSystems( - CoordinateSystemFactory factory, - IEnumerable> enumeration) + private static IEnumerable CreateCoordinateSystems( + CoordinateSystemFactory factory, + IEnumerable enumeration) + { + foreach (CoordinateSystemDefinition definition in enumeration) { - foreach (var sridWkt in enumeration) + CoordinateSystem? cs = CreateCoordinateSystem(factory, definition.Wkt); + if (cs is not null) { - var cs = CreateCoordinateSystem(factory, sridWkt.Value); - if (cs != null) - yield return new KeyValuePair(sridWkt.Key, cs); + yield return new CoordinateSystemEntry(definition.Srid, cs); } } + } + + private static void FromEnumeration( + CoordinateSystemServices css, + IEnumerable enumeration) + { + FromEnumeration(css, CreateCoordinateSystems(css.coordinateSystemFactory, enumeration)); + } - private static void FromEnumeration(CoordinateSystemServices css, - IEnumerable> enumeration) + private void InitializeFromEnumeration(object enumeration) + { + if (enumeration is ICoordinateSystemDefinitionProvider provider) { - FromEnumeration(css, CreateCoordinateSystems(css._coordinateSystemFactory, enumeration)); + FromEnumeration(this, provider.GetDefinitions()); + return; } - private static void FromEnumeration(object parameter) + if (enumeration is IEnumerable definitionEnumeration) { - object[] paras = (object[]) parameter; - var css = (CoordinateSystemServices) paras[0]; - - if (paras[1] is IEnumerable>) - FromEnumeration(css, (IEnumerable>) paras[1]); - else - FromEnumeration(css, (IEnumerable>)paras[1]); + FromEnumeration(this, definitionEnumeration); + return; + } - css._initialization.Set(); + if (enumeration is IEnumerable coordinateSystemEnumeration) + { + FromEnumeration(this, coordinateSystemEnumeration); + return; } + throw new InvalidOperationException("Unsupported coordinate system initialization payload."); + } - /// - /// Returns the coordinate system by identifier - /// - /// The initialization for the coordinate system - /// The coordinate system. - public CoordinateSystem GetCoordinateSystem(int srid) + private void WaitForInitialization() + { + try + { + this.initializationTask.GetAwaiter().GetResult(); + } + catch (Exception exception) { - _initialization.WaitOne(); - return _csBySrid.TryGetValue(srid, out var cs) ? cs : null; + throw new InvalidOperationException("Coordinate system initialization failed.", exception); } + } - /// - /// Returns the coordinate system by and . - /// - /// The authority for the coordinate system - /// The code assigned to the coordinate system by . - /// The coordinate system. - public CoordinateSystem GetCoordinateSystem(string authority, long code) + private void InvalidateTransformationCache(int srid) + { + foreach (KeyValuePair<(int SourceSrid, int TargetSrid), ICoordinateTransformation> entry in this.transformationCache) { - int? srid = GetSRID(authority, code); - if (srid.HasValue) - return GetCoordinateSystem(srid.Value); - return null; + (int SourceSrid, int TargetSrid) key = entry.Key; + if (key.SourceSrid == srid || key.TargetSrid == srid) + { + this.transformationCache.TryRemove(key, out _); + } } + } - /// - /// Method to get the identifier, by which this coordinate system can be accessed. - /// - /// The authority name - /// The code assigned by - /// The identifier or null - public int? GetSRID(string authority, long authorityCode) + private sealed class CsEqualityComparer : EqualityComparer + { + /// + public override bool Equals(IInfo? x, IInfo? y) { - var key = new CoordinateSystemKey(authority, authorityCode); - int srid; - _initialization.WaitOne(); - if (_sridByCs.TryGetValue(key, out srid)) - return srid; + if (ReferenceEquals(x, y)) + { + return true; + } - return null; + return x is not null && y is not null && x.AuthorityCode == y.AuthorityCode && + string.Equals(x.Authority, y.Authority, StringComparison.OrdinalIgnoreCase); } - /// - /// Method to create a coordinate transformation between two spatial reference systems, defined by their identifiers - /// - /// This is a convenience function for . - /// The identifier for the source spatial reference system. - /// The identifier for the target spatial reference system. - /// A coordinate transformation, null if no transformation could be created. - public ICoordinateTransformation CreateTransformation(int sourceSrid, int targetSrid) + /// + public override int GetHashCode(IInfo obj) { - return CreateTransformation(GetCoordinateSystem(sourceSrid), - GetCoordinateSystem(targetSrid)); + return obj is null + ? 0 + : obj.AuthorityCode.GetHashCode() + (obj.Authority is not null ? StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Authority) : 0); } + } - /// - /// Method to create a coordinate transformation between two spatial reference systems - /// - /// The source spatial reference system. - /// The target spatial reference system. - /// A coordinate transformation, null if no transformation could be created. - public ICoordinateTransformation CreateTransformation(CoordinateSystem source, CoordinateSystem target) + private sealed class CoordinateSystemKey : IInfo + { + public CoordinateSystemKey(string authority, long authorityCode) { - return _ctFactory.CreateFromCoordinateSystems(source, target); + this.Authority = authority ?? string.Empty; + this.AuthorityCode = authorityCode; } - /// - /// AddCoordinateSystem - /// - /// - /// - protected void AddCoordinateSystem(int srid, CoordinateSystem coordinateSystem) - { - lock (((IDictionary) _csBySrid).SyncRoot) - { - lock (((IDictionary) _sridByCs).SyncRoot) - { - if (_sridByCs.ContainsKey(coordinateSystem)) - return; + public string Authority { get; private set; } - if (_csBySrid.ContainsKey(srid)) - { - if (ReferenceEquals(coordinateSystem, _csBySrid[srid])) - return; + public long AuthorityCode { get; private set; } - _sridByCs.Remove(_csBySrid[srid]); - _csBySrid[srid] = coordinateSystem; - _sridByCs.Add(coordinateSystem, srid); - } - else - { - _csBySrid.Add(srid, coordinateSystem); - _sridByCs.Add(coordinateSystem, srid); - } - } - } + public string Name + { + get => string.Empty; } - /// - /// AddCoordinateSystem - /// - /// - /// - protected virtual int AddCoordinateSystem(CoordinateSystem coordinateSystem) + public string Alias { - int srid = (int) coordinateSystem.AuthorityCode; - AddCoordinateSystem(srid, coordinateSystem); + get => string.Empty; + } - return srid; + public string Abbreviation + { + get => string.Empty; } - /// - /// Clear - /// - protected void Clear() + public string Remarks { - _csBySrid.Clear(); + get => string.Empty; } - /// - /// Count - /// - protected int Count + public string WKT { - get - { - _initialization.WaitOne(); - return _sridByCs.Count; - } + get => string.Empty; } - /// - /// RemoveCoordinateSystem - /// - /// - /// - /// - public bool RemoveCoordinateSystem(int srid) + public string XML { - throw new NotSupportedException(); + get => string.Empty; } - /// - /// GetEnumerator - /// - /// - public IEnumerator> GetEnumerator() + public bool EqualParams(object obj) { - _initialization.WaitOne(); - return _csBySrid.GetEnumerator(); + throw new NotSupportedException(); } } } diff --git a/src/ProjNet/CoordinateSystems/AngularUnit.cs b/src/ProjNet/CoordinateSystems/AngularUnit.cs index 93efcc39..c1dfdeae 100644 --- a/src/ProjNet/CoordinateSystems/AngularUnit.cs +++ b/src/ProjNet/CoordinateSystems/AngularUnit.cs @@ -1,154 +1,181 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; +using System.Collections.Generic; using System.Globalization; -using System.Text; - -namespace ProjNet.CoordinateSystems +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// Definition of angular units. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// The predefined angular-unit accessors are thread-safe because they only expose immutable value objects. +/// +/// +public class AngularUnit : Info, IUnit { /// - /// Definition of angular units. + /// Equality tolerance value. Values with a difference less than this are considered equal. + /// + private const double EqualityTolerance = 2.0e-17d; + + /// + /// Initializes a new instance of the class. + /// + /// Radians per unit. + public AngularUnit(double radiansPerUnit) + : this( + radiansPerUnit, string.Empty, string.Empty, -1, string.Empty, string.Empty, string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Radians per unit. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + internal AngularUnit(double radiansPerUnit, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + this.RadiansPerUnit = radiansPerUnit; + } + + /// + /// Gets the degree unit of angle (1° = π/180 radians). + /// + public static AngularUnit Degrees => new(0.017453292519943295769236907684886, "degree", "EPSG", 9102, "deg", string.Empty, "=pi/180 radians"); + + /// + /// Gets the radian angular unit, the SI standard unit of angle. + /// + public static AngularUnit Radian => new(1, "radian", "EPSG", 9101, "rad", string.Empty, "SI standard unit."); + + /// + /// Gets the grad unit of angle (1 grad = π/200 radians). + /// + public static AngularUnit Grad => new(0.015707963267948966192313216916398, "grad", "EPSG", 9105, "gr", string.Empty, "=pi/200 radians."); + + /// + /// Gets the gon unit of angle (1 gon = π/200 radians; equivalent to a grad). + /// + public static AngularUnit Gon => new(0.015707963267948966192313216916398, "gon", "EPSG", 9106, "g", string.Empty, "=pi/200 radians."); + + /// + /// Gets the number of radians per . + /// + public double RadiansPerUnit { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this unit with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new AngularUnit WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this unit with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new AngularUnit WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this angular unit as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_AngularUnit", + new XAttribute("RadiansPerUnit", this.RadiansPerUnit.ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this angular unit to a WKT syntax tree node. + /// + /// A representing this angular unit. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.RadiansPerUnit), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("UNIT", children); + } + + /// + /// Converts this angular unit to a WKT syntax tree node for the requested WKT version. /// - [Serializable] - public class AngularUnit : Info, IUnit - { - /// - /// Equality tolerance value. Values with a difference less than this are considered equal. - /// - private const double EqualityTolerance = 2.0e-17; - - /// - /// Initializes a new instance of a angular unit - /// - /// Radians per unit - public AngularUnit(double radiansPerUnit) - : this( - radiansPerUnit,string.Empty,string.Empty,-1,string.Empty,string.Empty,string.Empty) - { - } - - /// - /// Initializes a new instance of a angular unit - /// - /// Radians per unit - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal AngularUnit(double radiansPerUnit, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) - : - base(name, authority, authorityCode, alias, abbreviation, remarks) - { - _RadiansPerUnit = radiansPerUnit; - } - - #region Predifined units - - /// - /// The angular degrees are PI/180 = 0.017453292519943295769236907684886 radians - /// - public static AngularUnit Degrees - { - get { return new AngularUnit(0.017453292519943295769236907684886, "degree", "EPSG", 9102, "deg", string.Empty, "=pi/180 radians"); } - } - - /// - /// SI standard unit - /// - public static AngularUnit Radian - { - get { return new AngularUnit(1, "radian", "EPSG", 9101, "rad", string.Empty, "SI standard unit."); } - } - - /// - /// Pi / 200 = 0.015707963267948966192313216916398 radians - /// - public static AngularUnit Grad - { - get { return new AngularUnit(0.015707963267948966192313216916398, "grad", "EPSG", 9105, "gr", string.Empty, "=pi/200 radians."); } - } - - /// - /// Pi / 200 = 0.015707963267948966192313216916398 radians - /// - public static AngularUnit Gon - { - get { return new AngularUnit(0.015707963267948966192313216916398, "gon", "EPSG", 9106, "g", string.Empty, "=pi/200 radians."); } - } - #endregion - - #region IAngularUnit Members - - private double _RadiansPerUnit; - - /// - /// Gets or sets the number of radians per . - /// - public double RadiansPerUnit - { - get { return _RadiansPerUnit; } - set { _RadiansPerUnit = value; } - } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat,"UNIT[\"{0}\", {1}", Name, RadiansPerUnit); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object. - /// - public override string XML - { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, "{1}", RadiansPerUnit, InfoXml); - } - } - - #endregion - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is AngularUnit)) - return false; - return Math.Abs(((AngularUnit)obj).RadiansPerUnit - this.RadiansPerUnit) < EqualityTolerance; - } - } + /// The WKT dialect to emit. + /// A representing this angular unit in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.RadiansPerUnit), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("ANGLEUNIT", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is AngularUnit angularUnit && Math.Abs(angularUnit.RadiansPerUnit - this.RadiansPerUnit) < EqualityTolerance; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); } diff --git a/src/ProjNet/CoordinateSystems/AxisInfo.cs b/src/ProjNet/CoordinateSystems/AxisInfo.cs index da457f49..cd8b0e96 100644 --- a/src/ProjNet/CoordinateSystems/AxisInfo.cs +++ b/src/ProjNet/CoordinateSystems/AxisInfo.cs @@ -1,87 +1,123 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// Details of axis. This is used to label axes, and indicate the orientation. +/// +public sealed class AxisInfo { /// - /// Details of axis. This is used to label axes, and indicate the orientation. + /// Initializes a new instance of the class. /// - [Serializable] - public class AxisInfo + /// Name of axis. + /// Axis orientation. + public AxisInfo(string name, AxisOrientationEnum orientation) { - /// - /// Initializes a new instance of an AxisInfo. - /// - /// Name of axis - /// Axis orientation - public AxisInfo(string name, AxisOrientationEnum orientation) - { - _Name = name; - _Orientation = orientation; - } + this.Name = name; + this.Orientation = orientation; + } - private string _Name; + /// + /// Initializes a new instance of the class by copying an existing axis definition. + /// + /// The axis definition to copy. + public AxisInfo(AxisInfo axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + this.Name = axisInfo.Name; + this.Orientation = axisInfo.Orientation; + } - /// - /// Human readable name for axis. Possible values are X, Y, Long, Lat or any other short string. - /// - public string Name - { - get { return _Name; } - set { _Name = value; } - } + /// + /// Gets human readable name for axis. Possible values are X, Y, Long, Lat or any other short string. + /// + public string Name { get; } - private AxisOrientationEnum _Orientation; + /// + /// Gets enumerated value for orientation. + /// + public AxisOrientationEnum Orientation { get; } - /// - /// Gets enumerated value for orientation. - /// - public AxisOrientationEnum Orientation + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public string WKT + { + get { - get { return _Orientation; } - set { _Orientation = value; } + return $"AXIS[\"{this.Name}\", {this.Orientation.ToString().ToUpperInvariant()}]"; } + } - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public string WKT + /// + /// Gets an XML representation of this object. + /// + public string XML + { + get { - get - { - return $"AXIS[\"{Name}\", {Orientation.ToString().ToUpperInvariant()}]"; - } + return FormattableString.Invariant($""); } + } - /// - /// Gets an XML representation of this object - /// - public string XML + /// + /// Returns an XML representation of this axis info as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + return new XElement( + "CS_AxisInfo", + new XAttribute("Name", this.Name), + new XAttribute("Orientation", this.Orientation.ToString().ToUpperInvariant())); + } + + /// + /// Converts this axis info to a WKT syntax tree node. + /// + /// A representing this axis info. + public WktNode ToWktNode() + { + return new WktKeywordNode( + "AXIS", + new WktQuotedString(this.Name), + new WktIdentifier(this.Orientation.ToString().ToUpperInvariant())); + } + + /// + /// Converts this axis info to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this axis info in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + return version switch { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, - "", Name, Orientation.ToString() - .ToUpperInvariant()); - } - } + WktVersion.Wkt1 => this.ToWktNode(), + WktVersion.Wkt22019 => new WktKeywordNode( + "AXIS", + new WktQuotedString(this.Name), + new WktIdentifier(this.Orientation switch + { + AxisOrientationEnum.North => "north", + AxisOrientationEnum.South => "south", + AxisOrientationEnum.East => "east", + AxisOrientationEnum.West => "west", + AxisOrientationEnum.Up => "up", + AxisOrientationEnum.Down => "down", + _ => "other", + })), + _ => throw WktVersionSupport.CreateNotSupportedException(nameof(AxisInfo), version), + }; } } diff --git a/src/ProjNet/CoordinateSystems/AxisOrientationEnum.cs b/src/ProjNet/CoordinateSystems/AxisOrientationEnum.cs index 796bf1ad..8ac732da 100644 --- a/src/ProjNet/CoordinateSystems/AxisOrientationEnum.cs +++ b/src/ProjNet/CoordinateSystems/AxisOrientationEnum.cs @@ -1,63 +1,49 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -namespace ProjNet.CoordinateSystems +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +/// +/// Orientation of axis. Some coordinate systems use non-standard orientations. +/// For example, the first axis in South African grids usually points West, +/// instead of East. This information is obviously relevant for algorithms +/// converting South African grid coordinates into Lat/Long. +/// +public enum AxisOrientationEnum : short { /// - /// Orientation of axis. Some coordinate systems use non-standard orientations. - /// For example, the first axis in South African grids usually points West, - /// instead of East. This information is obviously relevant for algorithms - /// converting South African grid coordinates into Lat/Long. + /// Unknown or unspecified axis orientation. This can be used for local or fitted coordinate systems. /// - public enum AxisOrientationEnum : short - { - /// - /// Unknown or unspecified axis orientation. This can be used for local or fitted coordinate systems. - /// - Other = 0, - - /// - /// Increasing ordinates values go North. This is usually used for Grid Y coordinates and Latitude. - /// - North = 1, - - /// - /// Increasing ordinates values go South. This is rarely used. - /// - South = 2, - - /// - /// Increasing ordinates values go East. This is rarely used. - /// - East = 3, - - /// - /// Increasing ordinates values go West. This is usually used for Grid X coordinates and Longitude. - /// - West = 4, - - /// - /// Increasing ordinates values go up. This is used for vertical coordinate systems. - /// - Up = 5, - - /// - /// Increasing ordinates values go down. This is used for vertical coordinate systems. - /// - Down = 6 - } + Other = 0, + + /// + /// Increasing ordinates values go North. This is usually used for Grid Y coordinates and Latitude. + /// + North = 1, + + /// + /// Increasing ordinates values go South. This is rarely used. + /// + South = 2, + + /// + /// Increasing ordinates values go East. This is rarely used. + /// + East = 3, + + /// + /// Increasing ordinates values go West. This is usually used for Grid X coordinates and Longitude. + /// + West = 4, + + /// + /// Increasing ordinates values go up. This is used for vertical coordinate systems. + /// + Up = 5, + + /// + /// Increasing ordinates values go down. This is used for vertical coordinate systems. + /// + Down = 6, } diff --git a/src/ProjNet/CoordinateSystems/BoundCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/BoundCoordinateSystem.cs new file mode 100644 index 00000000..0b9177e2 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/BoundCoordinateSystem.cs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A coordinate system that retains explicit BoundCRS metadata alongside its source coordinate system. +/// +/// +/// +/// The bound coordinate system keeps the source coordinate system, the target or hub coordinate +/// system, and the bound transformation definition together as a first-class model instead of +/// scattering that metadata across datum and vertical-coordinate-system implementation details. +/// +/// +/// Until dedicated BoundCRS serializers are implemented, legacy WKT1 and XML output intentionally +/// fall back to the source coordinate system representation. +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// +/// +public class BoundCoordinateSystem : CoordinateSystem +{ + /// + /// Initializes a new instance of the class. + /// + /// Source coordinate system described by the bound CRS. + /// Target or hub coordinate system used by the bound transformation. + /// Bound transformation metadata. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + protected internal BoundCoordinateSystem( + CoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + BoundTransformation transformation, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks) + : base( + name, + authority, + authorityCode, + alias, + abbreviation, + remarks, + CreateAxisInfo(sourceCoordinateSystem), + sourceCoordinateSystem?.DefaultEnvelope) + { + this.SourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + this.TargetCoordinateSystem = ArgumentGuard.ThrowIfNull(targetCoordinateSystem, nameof(targetCoordinateSystem)); + this.Transformation = ArgumentGuard.ThrowIfNull(transformation, nameof(transformation)); + } + + /// + /// Gets the source coordinate system described by the bound CRS. + /// + public CoordinateSystem SourceCoordinateSystem { get; } + + /// + /// Gets the target or hub coordinate system used by the bound transformation. + /// + public CoordinateSystem TargetCoordinateSystem { get; } + + /// + /// Gets the bound transformation metadata. + /// + public BoundTransformation Transformation { get; } + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new BoundCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new BoundCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + public override XElement ToXml() => this.SourceCoordinateSystem.ToXml(); + + /// + public override WktNode ToWktNode() => this.SourceCoordinateSystem.ToWktNode(); + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + return version == WktVersion.Wkt1 + ? this.SourceCoordinateSystem.ToWktNode(version) + : BoundCoordinateSystemSupport.CreateWkt2BoundCoordinateSystemNode(this); + } + + /// + public override bool EqualParams(object obj) + { + return obj is BoundCoordinateSystem boundCoordinateSystem + && this.SourceCoordinateSystem.EqualParams(boundCoordinateSystem.SourceCoordinateSystem) + && this.TargetCoordinateSystem.EqualParams(boundCoordinateSystem.TargetCoordinateSystem) + && this.Transformation.Equals(boundCoordinateSystem.Transformation); + } + + /// + public override IUnit GetUnits(int dimension) => this.SourceCoordinateSystem.GetUnits(dimension); + + private static List CreateAxisInfo(CoordinateSystem sourceCoordinateSystem) + { + sourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + var axisInfo = new List(sourceCoordinateSystem.Dimension); + for (int dimension = 0; dimension < sourceCoordinateSystem.Dimension; dimension++) + { + axisInfo.Add(new AxisInfo(sourceCoordinateSystem.GetAxis(dimension))); + } + + return axisInfo; + } +} diff --git a/src/ProjNet/CoordinateSystems/BoundCoordinateSystemSupport.cs b/src/ProjNet/CoordinateSystems/BoundCoordinateSystemSupport.cs new file mode 100644 index 00000000..832a4efe --- /dev/null +++ b/src/ProjNet/CoordinateSystems/BoundCoordinateSystemSupport.cs @@ -0,0 +1,1068 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.IO; +using ProjNet.IO.Wkt; + +/// +/// Shared BoundCRS parsing and runtime-normalization helpers. +/// +internal static class BoundCoordinateSystemSupport +{ + private static readonly AngularUnit ArcSecondUnit = new(4.84813681109535993589914102357e-6d, "arc-second", "EPSG", 9104, "arcsec", string.Empty, "=pi/648000 radians."); + + /// + /// Creates normalized bound-transformation metadata for the currently supported BoundCRS subset. + /// + /// Transformation method name. + /// Bursa-Wolf parameters when the method is parameter based. + /// Parameter file reference when the method is grid based. + /// The normalized bound transformation. + internal static BoundTransformation CreateBoundTransformation(string methodName, Wgs84ConversionInfo? wgs84Parameters, string? parameterFileName) + { + if (IsCoordinateFrameRotationMethod(methodName)) + { + if (wgs84Parameters is null) + { + throw new NotSupportedException("BOUNDCRS coordinate-frame rotations require Bursa-Wolf parameters."); + } + + if (!string.IsNullOrWhiteSpace(parameterFileName)) + { + throw new NotSupportedException("BOUNDCRS Bursa-Wolf-style abridged transformations do not support PARAMETERFILE."); + } + + return new BoundTransformation(methodName, new Wgs84ConversionInfo( + wgs84Parameters.Dx, + wgs84Parameters.Dy, + wgs84Parameters.Dz, + -wgs84Parameters.Ex, + -wgs84Parameters.Ey, + -wgs84Parameters.Ez, + wgs84Parameters.Ppm, + wgs84Parameters.AreaOfUse)); + } + + if (IsGeocentricTranslationsMethod(methodName) || IsPositionVectorMethod(methodName)) + { + if (wgs84Parameters is null) + { + throw new NotSupportedException("BOUNDCRS Bursa-Wolf-style abridged transformations require numeric parameters."); + } + + if (!string.IsNullOrWhiteSpace(parameterFileName)) + { + throw new NotSupportedException("BOUNDCRS Bursa-Wolf-style abridged transformations do not support PARAMETERFILE."); + } + + return new BoundTransformation(methodName, CloneWgs84Parameters(wgs84Parameters)); + } + + if (IsGeographic3DToGravityRelatedHeightMethod(methodName)) + { + if (string.IsNullOrWhiteSpace(parameterFileName)) + { + throw new NotSupportedException("BOUNDCRS vertical abridged transformations require a PARAMETERFILE."); + } + + return new BoundTransformation(methodName, ArgumentGuard.ThrowIfNull(parameterFileName, nameof(parameterFileName))); + } + + throw new NotSupportedException($"BOUNDCRS abridged transformation method '{methodName}' is not supported."); + } + + /// + /// Assigns a normalized BoundCRS transformation parameter to a Bursa-Wolf container. + /// + /// Parameter name or normalized token. + /// Parameter value. + /// Target parameter container. + internal static void AssignTransformationParameter(string parameterName, double value, Wgs84ConversionInfo parameters) + { + parameters = ArgumentGuard.ThrowIfNull(parameters, nameof(parameters)); + + string normalizedParameterName = parameterName.Trim().ToUpperInvariant(); + switch (normalizedParameterName) + { + case "X-AXIS TRANSLATION": + case "DX": + parameters.Dx = value; + break; + case "Y-AXIS TRANSLATION": + case "DY": + parameters.Dy = value; + break; + case "Z-AXIS TRANSLATION": + case "DZ": + parameters.Dz = value; + break; + case "X-AXIS ROTATION": + case "EX": + parameters.Ex = value; + break; + case "Y-AXIS ROTATION": + case "EY": + parameters.Ey = value; + break; + case "Z-AXIS ROTATION": + case "EZ": + parameters.Ez = value; + break; + case "SCALE DIFFERENCE": + case "PPM": + parameters.Ppm = value; + break; + default: + throw new NotSupportedException($"BOUNDCRS transformation parameter '{parameterName}' is not supported."); + } + } + + /// + /// Determines whether the coordinate system tree contains a BoundCRS wrapper. + /// + /// Coordinate system to inspect. + /// when a bound wrapper is present; otherwise . + internal static bool ContainsBoundCoordinateSystem(CoordinateSystem coordinateSystem) + { + return coordinateSystem switch + { + BoundCoordinateSystem => true, + CompoundCoordinateSystem compoundCoordinateSystem => ContainsBoundCoordinateSystem(compoundCoordinateSystem.HeadCoordinateSystem) + || ContainsBoundCoordinateSystem(compoundCoordinateSystem.TailCoordinateSystem), + _ => false, + }; + } + + /// + /// Rewrites BoundCRS wrappers into the legacy runtime-compatible coordinate-system shapes. + /// + /// Coordinate system to normalize. + /// A runtime-compatible coordinate system. + internal static CoordinateSystem NormalizeCoordinateSystemForRuntime(CoordinateSystem coordinateSystem) + { + coordinateSystem = ArgumentGuard.ThrowIfNull(coordinateSystem, nameof(coordinateSystem)); + + return coordinateSystem switch + { + BoundCoordinateSystem boundCoordinateSystem => NormalizeBoundCoordinateSystemForRuntime(boundCoordinateSystem), + CompoundCoordinateSystem compoundCoordinateSystem => NormalizeCompoundCoordinateSystemForRuntime(compoundCoordinateSystem), + _ => coordinateSystem, + }; + } + + /// + /// Attempts to discover the horizontal datum represented by the coordinate system tree. + /// + /// Coordinate system to inspect. + /// Resolved horizontal datum when available. + /// when a horizontal datum was found; otherwise . + internal static bool TryGetHorizontalDatum(CoordinateSystem coordinateSystem, out HorizontalDatum? horizontalDatum) + { + switch (coordinateSystem) + { + case BoundCoordinateSystem boundCoordinateSystem: + return TryGetHorizontalDatum(boundCoordinateSystem.SourceCoordinateSystem, out horizontalDatum); + case GeographicCoordinateSystem geographicCoordinateSystem: + horizontalDatum = geographicCoordinateSystem.HorizontalDatum; + return true; + case ProjectedCoordinateSystem projectedCoordinateSystem: + horizontalDatum = projectedCoordinateSystem.HorizontalDatum; + return true; + case GeocentricCoordinateSystem geocentricCoordinateSystem: + horizontalDatum = geocentricCoordinateSystem.HorizontalDatum; + return true; + case CompoundCoordinateSystem compoundCoordinateSystem when compoundCoordinateSystem.TailCoordinateSystem is VerticalCoordinateSystem: + return TryGetHorizontalDatum(compoundCoordinateSystem.HeadCoordinateSystem, out horizontalDatum); + default: + horizontalDatum = null; + return false; + } + } + + /// + /// Returns the canonical WKT keyword label for diagnostic messages. + /// + /// Coordinate system to classify. + /// The best matching keyword label. + internal static string GetCoordinateSystemKeyword(CoordinateSystem coordinateSystem) + { + return coordinateSystem switch + { + BoundCoordinateSystem => "BOUNDCRS", + GeographicCoordinateSystem => "GEOGCRS", + ProjectedCoordinateSystem => "PROJCRS", + GeocentricCoordinateSystem => "GEODCRS", + VerticalCoordinateSystem => "VERTCRS", + CompoundCoordinateSystem => "COMPOUNDCRS", + FittedCoordinateSystem => "FITTED_CS", + _ => coordinateSystem.GetType().Name, + }; + } + + /// + /// Normalizes a parsed vertical BoundCRS hub to the canonical WGS84 lon/lat/up runtime form. + /// + /// Parsed hub coordinate system. + /// The runtime-compatible hub compound coordinate system. + internal static CompoundCoordinateSystem CreateRuntimeCompatibleVerticalBoundHubCoordinateSystem(CompoundCoordinateSystem parsedHubCoordinateSystem) + { + if (parsedHubCoordinateSystem.TailCoordinateSystem is not VerticalCoordinateSystem parsedVerticalCoordinateSystem) + { + throw new NotSupportedException("BOUNDCRS vertical targets must be ellipsoidal 3D geographic CRS definitions."); + } + + var runtimeVerticalCoordinateSystem = new VerticalCoordinateSystem( + CloneLinearUnit(parsedVerticalCoordinateSystem.LinearUnit), + new VerticalDatum( + DatumType.VD_Ellipsoidal, + parsedVerticalCoordinateSystem.VerticalDatum.Name, + parsedVerticalCoordinateSystem.Authority, + parsedVerticalCoordinateSystem.AuthorityCode, + parsedVerticalCoordinateSystem.Alias, + parsedVerticalCoordinateSystem.Remarks, + parsedVerticalCoordinateSystem.Abbreviation), + [new AxisInfo(parsedVerticalCoordinateSystem.GetAxis(0))], + parsedVerticalCoordinateSystem.Name, + parsedVerticalCoordinateSystem.Authority, + parsedVerticalCoordinateSystem.AuthorityCode, + parsedVerticalCoordinateSystem.Alias, + parsedVerticalCoordinateSystem.Abbreviation, + parsedVerticalCoordinateSystem.Remarks, + parsedVerticalCoordinateSystem.DefaultEnvelope); + + var runtimeHubCoordinateSystem = new CompoundCoordinateSystem( + GeographicCoordinateSystem.WGS84, + runtimeVerticalCoordinateSystem, + parsedHubCoordinateSystem.Name, + parsedHubCoordinateSystem.Authority, + parsedHubCoordinateSystem.AuthorityCode, + parsedHubCoordinateSystem.Alias, + parsedHubCoordinateSystem.Abbreviation, + parsedHubCoordinateSystem.Remarks, + parsedHubCoordinateSystem.DefaultEnvelope); + return runtimeHubCoordinateSystem; + } + + /// + /// Compares parameter-file references using the repository runtime's path-normalization rules. + /// + /// First parameter-file reference. + /// Second parameter-file reference. + /// when both references resolve to the same normalized token; otherwise . + internal static bool AreEquivalentParameterFileReferences(string left, string right) + { + StringComparison comparison = Path.DirectorySeparatorChar == '\\' + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + return NormalizeParameterFileReference(left).Equals(NormalizeParameterFileReference(right), comparison); + } + + /// + /// Normalizes a parameter-file reference for runtime comparison. + /// + /// Parameter-file reference to normalize. + /// The normalized reference. + internal static string NormalizeParameterFileReference(string parameterFileName) + { + string normalized = parameterFileName.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + if (!Path.IsPathRooted(normalized)) + { + return normalized; + } + + try + { + return Path.GetFullPath(normalized); + } + catch (ArgumentException) + { + return normalized; + } + catch (NotSupportedException) + { + return normalized; + } + } + + /// + /// Creates a synthetic first-class BoundCRS wrapper for legacy CRS metadata when BoundCRS serialization requires it. + /// + /// The coordinate system to inspect. + /// The synthetic bound coordinate system when legacy metadata is present; otherwise . + internal static BoundCoordinateSystem? CreateLegacyBoundCoordinateSystemForSerialization(CoordinateSystem coordinateSystem) + { + coordinateSystem = ArgumentGuard.ThrowIfNull(coordinateSystem, nameof(coordinateSystem)); + + if (coordinateSystem is GeographicCoordinateSystem geographicCoordinateSystem + && TryGetLegacyHorizontalBoundTransformation(geographicCoordinateSystem, out BoundTransformation? geographicTransformation)) + { + return new BoundCoordinateSystem( + CreateCoordinateSystemWithoutLegacyBoundMetadata(geographicCoordinateSystem), + GeographicCoordinateSystem.WGS84, + geographicTransformation!, + geographicCoordinateSystem.Name, + geographicCoordinateSystem.Authority, + geographicCoordinateSystem.AuthorityCode, + geographicCoordinateSystem.Alias, + geographicCoordinateSystem.Abbreviation, + geographicCoordinateSystem.Remarks); + } + + if (coordinateSystem is ProjectedCoordinateSystem projectedCoordinateSystem + && TryGetLegacyHorizontalBoundTransformation(projectedCoordinateSystem, out BoundTransformation? projectedTransformation)) + { + return new BoundCoordinateSystem( + CreateCoordinateSystemWithoutLegacyBoundMetadata(projectedCoordinateSystem), + GeographicCoordinateSystem.WGS84, + projectedTransformation!, + projectedCoordinateSystem.Name, + projectedCoordinateSystem.Authority, + projectedCoordinateSystem.AuthorityCode, + projectedCoordinateSystem.Alias, + projectedCoordinateSystem.Abbreviation, + projectedCoordinateSystem.Remarks); + } + + if (coordinateSystem is GeocentricCoordinateSystem geocentricCoordinateSystem + && TryGetLegacyHorizontalBoundTransformation(geocentricCoordinateSystem, out BoundTransformation? geocentricTransformation)) + { + return new BoundCoordinateSystem( + CreateCoordinateSystemWithoutLegacyBoundMetadata(geocentricCoordinateSystem), + GeocentricCoordinateSystem.WGS84, + geocentricTransformation!, + geocentricCoordinateSystem.Name, + geocentricCoordinateSystem.Authority, + geocentricCoordinateSystem.AuthorityCode, + geocentricCoordinateSystem.Alias, + geocentricCoordinateSystem.Abbreviation, + geocentricCoordinateSystem.Remarks); + } + + if (coordinateSystem is VerticalCoordinateSystem verticalCoordinateSystem + && verticalCoordinateSystem.BoundGridTransformation is not null) + { + VerticalBoundGridTransformation boundGridTransformation = verticalCoordinateSystem.BoundGridTransformation; + return new BoundCoordinateSystem( + CreateCoordinateSystemWithoutLegacyBoundMetadata(verticalCoordinateSystem), + CreateCoordinateSystemWithoutLegacyBoundMetadata(boundGridTransformation.HubCoordinateSystem), + new BoundTransformation(boundGridTransformation.MethodName, boundGridTransformation.ParameterFileName), + verticalCoordinateSystem.Name, + verticalCoordinateSystem.Authority, + verticalCoordinateSystem.AuthorityCode, + verticalCoordinateSystem.Alias, + verticalCoordinateSystem.Abbreviation, + verticalCoordinateSystem.Remarks); + } + + return null; + } + + /// + /// Creates a WKT2 BOUNDCRS node for the provided first-class bound coordinate system. + /// + /// The bound coordinate system to serialize. + /// A WKT2 BOUNDCRS node. + internal static WktKeywordNode CreateWkt2BoundCoordinateSystemNode(BoundCoordinateSystem boundCoordinateSystem) + { + boundCoordinateSystem = ArgumentGuard.ThrowIfNull(boundCoordinateSystem, nameof(boundCoordinateSystem)); + + var children = new List + { + new WktKeywordNode("SOURCECRS", CreateWkt2BoundCoordinateSystemComponentNode(boundCoordinateSystem.SourceCoordinateSystem)), + new WktKeywordNode("TARGETCRS", CreateWkt2BoundCoordinateSystemComponentNode(boundCoordinateSystem.TargetCoordinateSystem)), + CreateWkt2AbridgedTransformationNode(boundCoordinateSystem.SourceCoordinateSystem, boundCoordinateSystem.TargetCoordinateSystem, boundCoordinateSystem.Transformation), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(boundCoordinateSystem.Authority, boundCoordinateSystem.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("BOUNDCRS", children); + } + + /// + /// Creates a clone that removes legacy bound metadata so the underlying CRS can be emitted as a WKT2 source or target component. + /// + /// The coordinate system to sanitize. + /// A clone without legacy bound metadata. + internal static CoordinateSystem CreateCoordinateSystemWithoutLegacyBoundMetadata(CoordinateSystem coordinateSystem) + { + coordinateSystem = ArgumentGuard.ThrowIfNull(coordinateSystem, nameof(coordinateSystem)); + + return coordinateSystem switch + { + GeographicCoordinateSystem geographicCoordinateSystem => CloneGeographicCoordinateSystemWithoutLegacyBoundMetadata(geographicCoordinateSystem), + ProjectedCoordinateSystem projectedCoordinateSystem => CloneProjectedCoordinateSystemWithoutLegacyBoundMetadata(projectedCoordinateSystem), + GeocentricCoordinateSystem geocentricCoordinateSystem => CloneGeocentricCoordinateSystemWithoutLegacyBoundMetadata(geocentricCoordinateSystem), + VerticalCoordinateSystem verticalCoordinateSystem => CloneVerticalCoordinateSystemWithoutLegacyBoundMetadata(verticalCoordinateSystem), + CompoundCoordinateSystem compoundCoordinateSystem => CloneCompoundCoordinateSystemWithoutLegacyBoundMetadata(compoundCoordinateSystem), + _ => CloneCoordinateSystem(coordinateSystem), + }; + } + + private static CoordinateSystem NormalizeBoundCoordinateSystemForRuntime(BoundCoordinateSystem boundCoordinateSystem) + { + CoordinateSystem runtimeSource = CloneCoordinateSystem(NormalizeCoordinateSystemForRuntime(boundCoordinateSystem.SourceCoordinateSystem)); + CoordinateSystem runtimeTarget = NormalizeCoordinateSystemForRuntime(boundCoordinateSystem.TargetCoordinateSystem); + return ApplyBoundTransformationToSource(runtimeSource, runtimeTarget, boundCoordinateSystem.Transformation); + } + + private static CompoundCoordinateSystem NormalizeCompoundCoordinateSystemForRuntime(CompoundCoordinateSystem compoundCoordinateSystem) + { + CoordinateSystem normalizedHead = NormalizeCoordinateSystemForRuntime(compoundCoordinateSystem.HeadCoordinateSystem); + CoordinateSystem normalizedTail = NormalizeCoordinateSystemForRuntime(compoundCoordinateSystem.TailCoordinateSystem); + + if (ReferenceEquals(normalizedHead, compoundCoordinateSystem.HeadCoordinateSystem) + && ReferenceEquals(normalizedTail, compoundCoordinateSystem.TailCoordinateSystem)) + { + return compoundCoordinateSystem; + } + + var normalizedCompound = new CompoundCoordinateSystem( + normalizedHead, + normalizedTail, + compoundCoordinateSystem.Name, + compoundCoordinateSystem.Authority, + compoundCoordinateSystem.AuthorityCode, + compoundCoordinateSystem.Alias, + compoundCoordinateSystem.Abbreviation, + compoundCoordinateSystem.Remarks, + compoundCoordinateSystem.DefaultEnvelope); + return normalizedCompound; + } + + private static CoordinateSystem ApplyBoundTransformationToSource( + CoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + BoundTransformation transformation) + { + if (transformation.UsesWgs84Parameters) + { + return ApplyHorizontalBoundCoordinateSystemToSource( + sourceCoordinateSystem, + targetCoordinateSystem, + ArgumentGuard.ThrowIfNull(transformation.Wgs84Parameters, nameof(transformation.Wgs84Parameters))); + } + + if (sourceCoordinateSystem is VerticalCoordinateSystem verticalCoordinateSystem) + { + return ApplyVerticalBoundCoordinateSystemToSource( + verticalCoordinateSystem, + targetCoordinateSystem, + transformation.MethodName, + ArgumentGuard.ThrowIfNull(transformation.ParameterFileName, nameof(transformation.ParameterFileName))); + } + + throw new NotSupportedException( + $"BOUNDCRS source coordinate system type '{GetCoordinateSystemKeyword(sourceCoordinateSystem)}' is not supported."); + } + + private static CoordinateSystem ApplyHorizontalBoundCoordinateSystemToSource( + CoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + Wgs84ConversionInfo wgs84Parameters) + { + if (!TryGetHorizontalDatum(sourceCoordinateSystem, out HorizontalDatum? sourceDatum)) + { + throw new NotSupportedException( + $"BOUNDCRS source coordinate system type '{GetCoordinateSystemKeyword(sourceCoordinateSystem)}' is not supported."); + } + + if (!TryGetHorizontalDatum(targetCoordinateSystem, out HorizontalDatum? targetDatum)) + { + throw new NotSupportedException("BOUNDCRS targets other than WGS 84 are not supported."); + } + + sourceDatum = ArgumentGuard.ThrowIfNull(sourceDatum, nameof(sourceDatum)); + targetDatum = ArgumentGuard.ThrowIfNull(targetDatum, nameof(targetDatum)); + + if (!targetDatum.EqualParams(HorizontalDatum.WGS84)) + { + throw new NotSupportedException("BOUNDCRS targets other than WGS 84 are not supported."); + } + + if (sourceDatum.Wgs84Parameters is not null && !sourceDatum.Wgs84Parameters.Equals(wgs84Parameters)) + { + throw new NotSupportedException("BOUNDCRS source CRS already defines a conflicting WGS 84 transformation."); + } + + if (sourceDatum.Wgs84Parameters is not null) + { + return sourceCoordinateSystem; + } + + HorizontalDatum updatedSourceDatum = CloneHorizontalDatum(sourceDatum, CloneWgs84Parameters(wgs84Parameters)); + return CloneCoordinateSystemWithHorizontalDatum(sourceCoordinateSystem, updatedSourceDatum); + } + + private static VerticalCoordinateSystem ApplyVerticalBoundCoordinateSystemToSource( + VerticalCoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + string methodName, + string parameterFileName) + { + if (targetCoordinateSystem is not CompoundCoordinateSystem hubCoordinateSystem + || hubCoordinateSystem.HeadCoordinateSystem is not GeographicCoordinateSystem hubHorizontal + || hubCoordinateSystem.TailCoordinateSystem is not VerticalCoordinateSystem hubVertical) + { + throw new NotSupportedException("BOUNDCRS vertical targets must be ellipsoidal 3D geographic CRS definitions."); + } + + if (!TryGetHorizontalDatum(hubHorizontal, out HorizontalDatum? targetDatum)) + { + throw new NotSupportedException("BOUNDCRS vertical targets other than WGS 84 are not supported."); + } + + targetDatum = ArgumentGuard.ThrowIfNull(targetDatum, nameof(targetDatum)); + if (!targetDatum.EqualParams(HorizontalDatum.WGS84)) + { + throw new NotSupportedException("BOUNDCRS vertical targets other than WGS 84 are not supported."); + } + + if (hubVertical.VerticalDatum.DatumType != DatumType.VD_Ellipsoidal) + { + throw new NotSupportedException("BOUNDCRS vertical targets must expose an ellipsoidal height axis."); + } + + if (!IsGeographic3DToGravityRelatedHeightMethod(methodName)) + { + throw new NotSupportedException($"BOUNDCRS abridged transformation method '{methodName}' is not supported."); + } + + CompoundCoordinateSystem runtimeHubCoordinateSystem = CreateRuntimeCompatibleVerticalBoundHubCoordinateSystem(hubCoordinateSystem); + + if (sourceCoordinateSystem.BoundGridTransformation is not null + && (!AreEquivalentParameterFileReferences(sourceCoordinateSystem.BoundGridTransformation.ParameterFileName, parameterFileName) + || !sourceCoordinateSystem.BoundGridTransformation.HubCoordinateSystem.EqualParams(runtimeHubCoordinateSystem))) + { + throw new NotSupportedException("BOUNDCRS source CRS already defines a conflicting grid transformation."); + } + + return sourceCoordinateSystem.BoundGridTransformation is null + ? sourceCoordinateSystem.WithBoundGridTransformation(new VerticalBoundGridTransformation(methodName, parameterFileName, runtimeHubCoordinateSystem)) + : sourceCoordinateSystem; + } + + private static bool TryGetLegacyHorizontalBoundTransformation(CoordinateSystem coordinateSystem, out BoundTransformation? transformation) + { + Wgs84ConversionInfo? parameters = coordinateSystem switch + { + GeographicCoordinateSystem geographicCoordinateSystem => TryGetLegacyHorizontalBoundParameters(geographicCoordinateSystem), + ProjectedCoordinateSystem projectedCoordinateSystem => TryGetLegacyHorizontalBoundParameters(projectedCoordinateSystem.GeographicCoordinateSystem), + GeocentricCoordinateSystem geocentricCoordinateSystem => TryGetLegacyHorizontalBoundParameters(geocentricCoordinateSystem.HorizontalDatum), + _ => null, + }; + + transformation = parameters is null + ? null + : new BoundTransformation(GetLegacyHorizontalBoundMethodName(coordinateSystem, parameters), parameters); + return transformation is not null; + } + + private static Wgs84ConversionInfo? TryGetLegacyHorizontalBoundParameters(HorizontalDatum horizontalDatum) + { + return horizontalDatum.Wgs84Parameters is null + ? null + : CloneWgs84Parameters(horizontalDatum.Wgs84Parameters); + } + + private static Wgs84ConversionInfo? TryGetLegacyHorizontalBoundParameters(GeographicCoordinateSystem geographicCoordinateSystem) + { + Wgs84ConversionInfo? datumParameters = TryGetLegacyHorizontalBoundParameters(geographicCoordinateSystem.HorizontalDatum); + + if (geographicCoordinateSystem.WGS84ConversionInfo.Count == 0) + { + return datumParameters; + } + + if (geographicCoordinateSystem.WGS84ConversionInfo.Count > 1) + { + throw new NotSupportedException("BoundCRS output currently supports only a single WGS84 conversion definition."); + } + + Wgs84ConversionInfo conversionParameters = CloneWgs84Parameters(geographicCoordinateSystem.WGS84ConversionInfo[0]); + if (datumParameters is not null && !datumParameters.Equals(conversionParameters)) + { + throw new NotSupportedException("BoundCRS output does not support conflicting legacy WGS84 conversion definitions on the same geographic coordinate system."); + } + + return datumParameters ?? conversionParameters; + } + + private static string GetLegacyHorizontalBoundMethodName(CoordinateSystem coordinateSystem, Wgs84ConversionInfo parameters) + { + bool usesGeographic2dDomain = coordinateSystem is GeographicCoordinateSystem or ProjectedCoordinateSystem; + if (parameters.Ex == 0d && parameters.Ey == 0d && parameters.Ez == 0d && parameters.Ppm == 0d) + { + return usesGeographic2dDomain + ? "Geocentric translations (geog2D domain)" + : "Geocentric translations"; + } + + return usesGeographic2dDomain + ? "Position Vector transformation (geog2D domain)" + : "Position Vector transformation"; + } + + private static WktNode CreateWkt2BoundCoordinateSystemComponentNode(CoordinateSystem coordinateSystem) + { + if (coordinateSystem is BoundCoordinateSystem boundCoordinateSystem) + { + return CreateWkt2BoundCoordinateSystemNode(boundCoordinateSystem); + } + + return CreateCoordinateSystemWithoutLegacyBoundMetadata(coordinateSystem).ToWktNode(WktVersion.Wkt22019); + } + + private static WktKeywordNode CreateWkt2AbridgedTransformationNode( + CoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + BoundTransformation transformation) + { + var children = new List + { + new WktQuotedString($"{sourceCoordinateSystem.Name} to {targetCoordinateSystem.Name}"), + new WktKeywordNode( + "METHOD", + new WktQuotedString(transformation.MethodName)), + }; + + if (transformation.UsesParameterFile) + { + children.Add(new WktKeywordNode( + "PARAMETERFILE", + new WktQuotedString("Geoid (height correction) model file"), + new WktQuotedString(ArgumentGuard.ThrowIfNull(transformation.ParameterFileName, nameof(transformation.ParameterFileName))))); + } + else if (transformation.Wgs84Parameters is not null) + { + AppendWkt2AbridgedTransformationParameters(children, transformation.MethodName, transformation.Wgs84Parameters); + } + else + { + throw new NotSupportedException("BOUNDCRS transformations must define either numeric parameters or a parameter file."); + } + + return new WktKeywordNode("ABRIDGEDTRANSFORMATION", children); + } + + private static void AppendWkt2AbridgedTransformationParameters(List children, string methodName, Wgs84ConversionInfo parameters) + { + if (IsGeocentricTranslationsMethod(methodName)) + { + children.Add(CreateWkt2BoundParameterNode("X-axis translation", parameters.Dx, LinearUnit.Metre.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode("Y-axis translation", parameters.Dy, LinearUnit.Metre.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode("Z-axis translation", parameters.Dz, LinearUnit.Metre.ToWktNode(WktVersion.Wkt22019))); + return; + } + + if (IsPositionVectorMethod(methodName) || IsCoordinateFrameRotationMethod(methodName)) + { + children.Add(CreateWkt2BoundParameterNode("X-axis translation", parameters.Dx, LinearUnit.Metre.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode("Y-axis translation", parameters.Dy, LinearUnit.Metre.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode("Z-axis translation", parameters.Dz, LinearUnit.Metre.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode("X-axis rotation", parameters.Ex, ArcSecondUnit.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode("Y-axis rotation", parameters.Ey, ArcSecondUnit.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode("Z-axis rotation", parameters.Ez, ArcSecondUnit.ToWktNode(WktVersion.Wkt22019))); + children.Add(CreateWkt2BoundParameterNode( + "Scale difference", + parameters.Ppm, + new WktKeywordNode( + "SCALEUNIT", + new WktQuotedString("parts per million"), + new WktNumber(1e-6d)))); + return; + } + + throw new NotSupportedException($"BOUNDCRS abridged transformation method '{methodName}' is not supported."); + } + + private static WktKeywordNode CreateWkt2BoundParameterNode(string parameterName, double value, WktNode unitNode) + { + return new WktKeywordNode( + "PARAMETER", + new WktQuotedString(parameterName), + new WktNumber(value), + unitNode); + } + + private static CoordinateSystem CloneCoordinateSystem(CoordinateSystem coordinateSystem) + { + return coordinateSystem switch + { + GeographicCoordinateSystem geographicCoordinateSystem => CloneGeographicCoordinateSystem(geographicCoordinateSystem), + ProjectedCoordinateSystem projectedCoordinateSystem => CloneProjectedCoordinateSystem(projectedCoordinateSystem), + GeocentricCoordinateSystem geocentricCoordinateSystem => CloneGeocentricCoordinateSystem(geocentricCoordinateSystem), + VerticalCoordinateSystem verticalCoordinateSystem => CloneVerticalCoordinateSystem(verticalCoordinateSystem), + CompoundCoordinateSystem compoundCoordinateSystem => CloneCompoundCoordinateSystem(compoundCoordinateSystem), + _ => throw new NotSupportedException( + $"BOUNDCRS source coordinate system type '{GetCoordinateSystemKeyword(coordinateSystem)}' is not supported."), + }; + } + + private static CoordinateSystem CloneCoordinateSystemWithHorizontalDatum(CoordinateSystem coordinateSystem, HorizontalDatum horizontalDatum) + { + return coordinateSystem switch + { + GeographicCoordinateSystem geographicCoordinateSystem => CloneGeographicCoordinateSystem(geographicCoordinateSystem, horizontalDatum), + ProjectedCoordinateSystem projectedCoordinateSystem => CloneProjectedCoordinateSystem(projectedCoordinateSystem, horizontalDatum), + GeocentricCoordinateSystem geocentricCoordinateSystem => CloneGeocentricCoordinateSystem(geocentricCoordinateSystem, horizontalDatum), + CompoundCoordinateSystem compoundCoordinateSystem when compoundCoordinateSystem.TailCoordinateSystem is VerticalCoordinateSystem + => CloneCompoundCoordinateSystem( + compoundCoordinateSystem, + CloneCoordinateSystemWithHorizontalDatum(compoundCoordinateSystem.HeadCoordinateSystem, horizontalDatum), + CloneCoordinateSystem(compoundCoordinateSystem.TailCoordinateSystem)), + _ => throw new NotSupportedException( + $"BOUNDCRS source coordinate system type '{GetCoordinateSystemKeyword(coordinateSystem)}' is not supported."), + }; + } + + private static GeographicCoordinateSystem CloneGeographicCoordinateSystemWithoutLegacyBoundMetadata(GeographicCoordinateSystem geographicCoordinateSystem) + { + return CloneGeographicCoordinateSystem( + geographicCoordinateSystem, + CloneHorizontalDatum(geographicCoordinateSystem.HorizontalDatum, includeWgs84Parameters: false)); + } + + private static ProjectedCoordinateSystem CloneProjectedCoordinateSystemWithoutLegacyBoundMetadata(ProjectedCoordinateSystem projectedCoordinateSystem) + { + return CloneProjectedCoordinateSystem(projectedCoordinateSystem, includeWgs84Parameters: false); + } + + private static GeocentricCoordinateSystem CloneGeocentricCoordinateSystemWithoutLegacyBoundMetadata(GeocentricCoordinateSystem geocentricCoordinateSystem) + { + return CloneGeocentricCoordinateSystem(geocentricCoordinateSystem, includeWgs84Parameters: false); + } + + private static VerticalCoordinateSystem CloneVerticalCoordinateSystemWithoutLegacyBoundMetadata(VerticalCoordinateSystem verticalCoordinateSystem) + { + return CloneVerticalCoordinateSystem(verticalCoordinateSystem, boundGridTransformation: null); + } + + private static CompoundCoordinateSystem CloneCompoundCoordinateSystemWithoutLegacyBoundMetadata(CompoundCoordinateSystem compoundCoordinateSystem) + { + CoordinateSystem headCoordinateSystem = compoundCoordinateSystem.HeadCoordinateSystem is BoundCoordinateSystem headBound + ? CreateBoundCoordinateSystemWithoutLegacyBoundMetadata(headBound) + : CreateCoordinateSystemWithoutLegacyBoundMetadata(compoundCoordinateSystem.HeadCoordinateSystem); + + CoordinateSystem tailCoordinateSystem = compoundCoordinateSystem.TailCoordinateSystem is BoundCoordinateSystem tailBound + ? CreateBoundCoordinateSystemWithoutLegacyBoundMetadata(tailBound) + : CreateCoordinateSystemWithoutLegacyBoundMetadata(compoundCoordinateSystem.TailCoordinateSystem); + + var clone = new CompoundCoordinateSystem( + headCoordinateSystem, + tailCoordinateSystem, + compoundCoordinateSystem.Name, + compoundCoordinateSystem.Authority, + compoundCoordinateSystem.AuthorityCode, + compoundCoordinateSystem.Alias, + compoundCoordinateSystem.Abbreviation, + compoundCoordinateSystem.Remarks, + compoundCoordinateSystem.DefaultEnvelope); + return clone; + } + + private static BoundCoordinateSystem CreateBoundCoordinateSystemWithoutLegacyBoundMetadata(BoundCoordinateSystem boundCoordinateSystem) + { + var clone = new BoundCoordinateSystem( + CreateCoordinateSystemWithoutLegacyBoundMetadata(boundCoordinateSystem.SourceCoordinateSystem), + CreateCoordinateSystemWithoutLegacyBoundMetadata(boundCoordinateSystem.TargetCoordinateSystem), + boundCoordinateSystem.Transformation, + boundCoordinateSystem.Name, + boundCoordinateSystem.Authority, + boundCoordinateSystem.AuthorityCode, + boundCoordinateSystem.Alias, + boundCoordinateSystem.Abbreviation, + boundCoordinateSystem.Remarks); + return clone; + } + + private static GeographicCoordinateSystem CloneGeographicCoordinateSystem(GeographicCoordinateSystem geographicCoordinateSystem) + { + HorizontalDatum horizontalDatum = CloneHorizontalDatum(geographicCoordinateSystem.HorizontalDatum); + return CloneGeographicCoordinateSystem(geographicCoordinateSystem, horizontalDatum); + } + + private static GeographicCoordinateSystem CloneGeographicCoordinateSystem(GeographicCoordinateSystem geographicCoordinateSystem, HorizontalDatum horizontalDatum) + { + var clone = new GeographicCoordinateSystem( + CloneAngularUnit(geographicCoordinateSystem.AngularUnit), + horizontalDatum, + ClonePrimeMeridian(geographicCoordinateSystem.PrimeMeridian), + CloneAxisInfo(geographicCoordinateSystem), + geographicCoordinateSystem.Name, + geographicCoordinateSystem.Authority, + geographicCoordinateSystem.AuthorityCode, + geographicCoordinateSystem.Alias, + geographicCoordinateSystem.Abbreviation, + geographicCoordinateSystem.Remarks, + geographicCoordinateSystem.DefaultEnvelope, + CloneWgs84ConversionInfoList(geographicCoordinateSystem.WGS84ConversionInfo)); + return clone; + } + + private static ProjectedCoordinateSystem CloneProjectedCoordinateSystem(ProjectedCoordinateSystem projectedCoordinateSystem) + => CloneProjectedCoordinateSystem(projectedCoordinateSystem, includeWgs84Parameters: true); + + private static ProjectedCoordinateSystem CloneProjectedCoordinateSystem(ProjectedCoordinateSystem projectedCoordinateSystem, HorizontalDatum horizontalDatum) + { + GeographicCoordinateSystem geographicCoordinateSystem = CloneGeographicCoordinateSystem(projectedCoordinateSystem.GeographicCoordinateSystem, horizontalDatum); + + var clone = new ProjectedCoordinateSystem( + horizontalDatum, + geographicCoordinateSystem, + CloneLinearUnit(projectedCoordinateSystem.LinearUnit), + CloneProjection(projectedCoordinateSystem.Projection), + CloneAxisInfo(projectedCoordinateSystem), + projectedCoordinateSystem.Name, + projectedCoordinateSystem.Authority, + projectedCoordinateSystem.AuthorityCode, + projectedCoordinateSystem.Alias, + projectedCoordinateSystem.Remarks, + projectedCoordinateSystem.Abbreviation, + projectedCoordinateSystem.DefaultEnvelope); + return clone; + } + + private static ProjectedCoordinateSystem CloneProjectedCoordinateSystem(ProjectedCoordinateSystem projectedCoordinateSystem, bool includeWgs84Parameters) + { + HorizontalDatum horizontalDatum = CloneHorizontalDatum(projectedCoordinateSystem.HorizontalDatum, includeWgs84Parameters); + return CloneProjectedCoordinateSystem(projectedCoordinateSystem, horizontalDatum); + } + + private static GeocentricCoordinateSystem CloneGeocentricCoordinateSystem(GeocentricCoordinateSystem geocentricCoordinateSystem) + => CloneGeocentricCoordinateSystem(geocentricCoordinateSystem, includeWgs84Parameters: true); + + private static GeocentricCoordinateSystem CloneGeocentricCoordinateSystem(GeocentricCoordinateSystem geocentricCoordinateSystem, HorizontalDatum horizontalDatum) + { + var clone = new GeocentricCoordinateSystem( + horizontalDatum, + CloneLinearUnit(geocentricCoordinateSystem.LinearUnit), + ClonePrimeMeridian(geocentricCoordinateSystem.PrimeMeridian), + CloneAxisInfo(geocentricCoordinateSystem), + geocentricCoordinateSystem.Name, + geocentricCoordinateSystem.Authority, + geocentricCoordinateSystem.AuthorityCode, + geocentricCoordinateSystem.Alias, + geocentricCoordinateSystem.Remarks, + geocentricCoordinateSystem.Abbreviation, + geocentricCoordinateSystem.DefaultEnvelope); + return clone; + } + + private static GeocentricCoordinateSystem CloneGeocentricCoordinateSystem(GeocentricCoordinateSystem geocentricCoordinateSystem, bool includeWgs84Parameters) + { + HorizontalDatum horizontalDatum = CloneHorizontalDatum(geocentricCoordinateSystem.HorizontalDatum, includeWgs84Parameters); + return CloneGeocentricCoordinateSystem(geocentricCoordinateSystem, horizontalDatum); + } + + private static VerticalCoordinateSystem CloneVerticalCoordinateSystem(VerticalCoordinateSystem verticalCoordinateSystem) + { + VerticalBoundGridTransformation? boundGridTransformation = verticalCoordinateSystem.BoundGridTransformation is null + ? null + : new VerticalBoundGridTransformation( + verticalCoordinateSystem.BoundGridTransformation.MethodName, + verticalCoordinateSystem.BoundGridTransformation.ParameterFileName, + CloneCompoundCoordinateSystem(verticalCoordinateSystem.BoundGridTransformation.HubCoordinateSystem)); + return CloneVerticalCoordinateSystem(verticalCoordinateSystem, boundGridTransformation); + } + + private static VerticalCoordinateSystem CloneVerticalCoordinateSystem( + VerticalCoordinateSystem verticalCoordinateSystem, + VerticalBoundGridTransformation? boundGridTransformation) + { + return new VerticalCoordinateSystem( + CloneLinearUnit(verticalCoordinateSystem.LinearUnit), + CloneVerticalDatum(verticalCoordinateSystem.VerticalDatum), + [new AxisInfo(verticalCoordinateSystem.GetAxis(0))], + verticalCoordinateSystem.Name, + verticalCoordinateSystem.Authority, + verticalCoordinateSystem.AuthorityCode, + verticalCoordinateSystem.Alias, + verticalCoordinateSystem.Abbreviation, + verticalCoordinateSystem.Remarks, + verticalCoordinateSystem.DefaultEnvelope, + boundGridTransformation); + } + + private static CompoundCoordinateSystem CloneCompoundCoordinateSystem(CompoundCoordinateSystem compoundCoordinateSystem) + => CloneCompoundCoordinateSystem( + compoundCoordinateSystem, + CloneCoordinateSystem(compoundCoordinateSystem.HeadCoordinateSystem), + CloneCoordinateSystem(compoundCoordinateSystem.TailCoordinateSystem)); + + private static CompoundCoordinateSystem CloneCompoundCoordinateSystem( + CompoundCoordinateSystem compoundCoordinateSystem, + CoordinateSystem headCoordinateSystem, + CoordinateSystem tailCoordinateSystem) + { + var clone = new CompoundCoordinateSystem( + headCoordinateSystem, + tailCoordinateSystem, + compoundCoordinateSystem.Name, + compoundCoordinateSystem.Authority, + compoundCoordinateSystem.AuthorityCode, + compoundCoordinateSystem.Alias, + compoundCoordinateSystem.Abbreviation, + compoundCoordinateSystem.Remarks, + compoundCoordinateSystem.DefaultEnvelope); + return clone; + } + + private static HorizontalDatum CloneHorizontalDatum(HorizontalDatum horizontalDatum) + => CloneHorizontalDatum(horizontalDatum, includeWgs84Parameters: true); + + private static HorizontalDatum CloneHorizontalDatum(HorizontalDatum horizontalDatum, Wgs84ConversionInfo? wgs84Parameters) + { + return new HorizontalDatum( + CloneEllipsoid(horizontalDatum.Ellipsoid), + wgs84Parameters, + horizontalDatum.DatumType, + horizontalDatum.Name, + horizontalDatum.Authority, + horizontalDatum.AuthorityCode, + horizontalDatum.Alias, + horizontalDatum.Remarks, + horizontalDatum.Abbreviation, + horizontalDatum.Ensemble); + } + + private static HorizontalDatum CloneHorizontalDatum(HorizontalDatum horizontalDatum, bool includeWgs84Parameters) + { + Wgs84ConversionInfo? wgs84Parameters = includeWgs84Parameters && horizontalDatum.Wgs84Parameters is not null + ? CloneWgs84Parameters(horizontalDatum.Wgs84Parameters) + : null; + + return CloneHorizontalDatum(horizontalDatum, wgs84Parameters); + } + + private static VerticalDatum CloneVerticalDatum(VerticalDatum verticalDatum) + { + return new VerticalDatum( + verticalDatum.DatumType, + verticalDatum.Name, + verticalDatum.Authority, + verticalDatum.AuthorityCode, + verticalDatum.Alias, + verticalDatum.Remarks, + verticalDatum.Abbreviation, + verticalDatum.Ensemble); + } + + private static Ellipsoid CloneEllipsoid(Ellipsoid ellipsoid) + { + return new Ellipsoid( + ellipsoid.SemiMajorAxis, + ellipsoid.SemiMinorAxis, + ellipsoid.InverseFlattening, + ellipsoid.IsIvfDefinitive, + CloneLinearUnit(ellipsoid.AxisUnit), + ellipsoid.Name, + ellipsoid.Authority, + ellipsoid.AuthorityCode, + ellipsoid.Alias, + ellipsoid.Abbreviation, + ellipsoid.Remarks); + } + + private static PrimeMeridian ClonePrimeMeridian(PrimeMeridian primeMeridian) + { + return new PrimeMeridian( + primeMeridian.Longitude, + CloneAngularUnit(primeMeridian.AngularUnit), + primeMeridian.Name, + primeMeridian.Authority, + primeMeridian.AuthorityCode, + primeMeridian.Alias, + primeMeridian.Abbreviation, + primeMeridian.Remarks); + } + + private static AngularUnit CloneAngularUnit(AngularUnit angularUnit) + { + return new AngularUnit( + angularUnit.RadiansPerUnit, + angularUnit.Name, + angularUnit.Authority, + angularUnit.AuthorityCode, + angularUnit.Alias, + angularUnit.Abbreviation, + angularUnit.Remarks); + } + + private static LinearUnit CloneLinearUnit(LinearUnit linearUnit) + { + return new LinearUnit( + linearUnit.MetersPerUnit, + linearUnit.Name, + linearUnit.Authority, + linearUnit.AuthorityCode, + linearUnit.Alias, + linearUnit.Abbreviation, + linearUnit.Remarks); + } + + private static Projection CloneProjection(IProjection projection) + { + var parameters = new List(projection.NumParameters); + for (int i = 0; i < projection.NumParameters; i++) + { + ProjectionParameter parameter = projection.GetParameter(i); + parameters.Add(new ProjectionParameter(parameter.Name, parameter.Value)); + } + + return new Projection( + projection.ClassName, + parameters, + projection.Name, + projection.Authority, + projection.AuthorityCode, + projection.Alias, + projection.Remarks, + projection.Abbreviation); + } + + private static List CloneAxisInfo(CoordinateSystem coordinateSystem) + { + var axisInfo = new List(coordinateSystem.Dimension); + for (int i = 0; i < coordinateSystem.Dimension; i++) + { + axisInfo.Add(new AxisInfo(coordinateSystem.GetAxis(i))); + } + + return axisInfo; + } + + private static Wgs84ConversionInfo CloneWgs84Parameters(Wgs84ConversionInfo parameters) + => new(parameters.Dx, parameters.Dy, parameters.Dz, parameters.Ex, parameters.Ey, parameters.Ez, parameters.Ppm, parameters.AreaOfUse); + + private static List CloneWgs84ConversionInfoList(List conversions) + { + var clone = new List(conversions.Count); + for (int i = 0; i < conversions.Count; i++) + { + clone.Add(CloneWgs84Parameters(conversions[i])); + } + + return clone; + } + + private static bool IsGeocentricTranslationsMethod(string methodName) + => methodName.StartsWith("Geocentric translations", StringComparison.OrdinalIgnoreCase); + + private static bool IsPositionVectorMethod(string methodName) + => methodName.StartsWith("Position Vector transformation", StringComparison.OrdinalIgnoreCase); + + private static bool IsGeographic3DToGravityRelatedHeightMethod(string methodName) + => methodName.Equals("Geographic3D to GravityRelatedHeight (EGM)", StringComparison.OrdinalIgnoreCase); + + private static bool IsCoordinateFrameRotationMethod(string methodName) + => methodName.StartsWith("Coordinate Frame rotation", StringComparison.OrdinalIgnoreCase); +} diff --git a/src/ProjNet/CoordinateSystems/BoundTransformation.cs b/src/ProjNet/CoordinateSystems/BoundTransformation.cs new file mode 100644 index 00000000..db79a867 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/BoundTransformation.cs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; + +/// +/// Describes the transformation metadata carried by a bound coordinate reference system. +/// +/// +/// +/// A bound coordinate reference system links a source coordinate system to a target or hub +/// coordinate system through either Bursa-Wolf-style WGS84 parameters or a parameter-file-based +/// grid transformation. +/// +/// +/// Exactly one of or is populated +/// for any given instance. +/// +/// +public sealed class BoundTransformation : IEquatable +{ + /// + /// Initializes a new instance of the class for a Bursa-Wolf-style transformation. + /// + /// Transformation method name. + /// WGS84 conversion parameters carried by the bound CRS. + public BoundTransformation(string methodName, Wgs84ConversionInfo wgs84Parameters) + { + this.MethodName = ValidateMethodName(methodName, nameof(methodName)); + this.Wgs84Parameters = ArgumentGuard.ThrowIfNull(wgs84Parameters, nameof(wgs84Parameters)); + } + + /// + /// Initializes a new instance of the class for a parameter-file-based transformation. + /// + /// Transformation method name. + /// Grid or parameter file referenced by the bound CRS. + public BoundTransformation(string methodName, string parameterFileName) + { + this.MethodName = ValidateMethodName(methodName, nameof(methodName)); + if (string.IsNullOrWhiteSpace(parameterFileName)) + { + ArgumentGuard.ThrowArgument("Invalid parameter file name", nameof(parameterFileName)); + } + + this.ParameterFileName = parameterFileName; + } + + /// + /// Gets the transformation method name. + /// + public string MethodName { get; } + + /// + /// Gets the Bursa-Wolf-style WGS84 conversion parameters when the bound transformation is parameter based. + /// + public Wgs84ConversionInfo? Wgs84Parameters { get; } + + /// + /// Gets the referenced grid or parameter file when the bound transformation is file based. + /// + public string? ParameterFileName { get; } + + /// + /// Gets a value indicating whether this bound transformation uses WGS84 parameters. + /// + public bool UsesWgs84Parameters => this.Wgs84Parameters is not null; + + /// + /// Gets a value indicating whether this bound transformation uses a parameter file. + /// + public bool UsesParameterFile => !string.IsNullOrWhiteSpace(this.ParameterFileName); + + /// + /// Determines whether this instance equals another bound transformation. + /// + /// The other bound transformation. + /// when both instances describe the same transformation; otherwise . + public bool Equals(BoundTransformation? other) => this.EqualsCore(other); + + /// + /// Determines whether this instance equals another object. + /// + /// The object to compare. + /// when both instances describe the same transformation; otherwise . + public override bool Equals(object? obj) => this.EqualsCore(obj as BoundTransformation); + + /// + /// Returns a hash code for this bound transformation. + /// + /// A hash code for this instance. + public override int GetHashCode() => HashCode.Combine(this.MethodName, this.ParameterFileName, this.Wgs84Parameters); + + private static string ValidateMethodName(string methodName, string paramName) + { + if (string.IsNullOrWhiteSpace(methodName)) + { + ArgumentGuard.ThrowArgument("Invalid method name", paramName); + } + + return methodName; + } + + private bool EqualsCore(BoundTransformation? other) + { + return other is not null + && string.Equals(this.MethodName, other.MethodName, StringComparison.Ordinal) + && string.Equals(this.ParameterFileName, other.ParameterFileName, StringComparison.Ordinal) + && Equals(this.Wgs84Parameters, other.Wgs84Parameters); + } +} diff --git a/src/ProjNet/CoordinateSystems/CompoundCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/CompoundCoordinateSystem.cs index 089ea3a8..c698daa1 100644 --- a/src/ProjNet/CoordinateSystems/CompoundCoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/CompoundCoordinateSystem.cs @@ -1,109 +1,225 @@ -using System; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// This is a compound coordinate system, which combines the coordinate of two other coordinate systems. +/// For example, a compound 3D coordinate system could be made up of a +/// horizontal coordinate system and a vertical coordinate system. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// +/// +public class CompoundCoordinateSystem : CoordinateSystem { /// - /// This is a compound coordinate system, which combines the coordinate of two other coordinate systems. - /// For example, a compound 3D coordinate system could be made up of a - /// horizontal coordinate system and a vertical coordinate system. + /// Initializes a new instance of the class. + /// A compound coordinate system. + /// + /// The head (first) coordinate system. + /// The tail (second) coordinate system. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Optional information. + public CompoundCoordinateSystem(CoordinateSystem headcs, CoordinateSystem tailcs, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : this(headcs, tailcs, name, authority, authorityCode, alias, abbreviation, remarks, CreateAxisInfo(headcs, tailcs), null) + { + } + + /// + /// Initializes a new instance of the class with an explicit default envelope. /// - public class CompoundCoordinateSystem : CoordinateSystem + /// The head (first) coordinate system. + /// The tail (second) coordinate system. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Optional information. + /// Default envelope for the compound domain. + internal CompoundCoordinateSystem( + CoordinateSystem headcs, + CoordinateSystem tailcs, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks, + double[]? defaultEnvelope) + : this(headcs, tailcs, name, authority, authorityCode, alias, abbreviation, remarks, CreateAxisInfo(headcs, tailcs), defaultEnvelope) { - private CoordinateSystem _headCoordinateSystem; - private CoordinateSystem _tailCoordinateSystem; + } + + /// + /// Initializes a new instance of the class with explicit axis metadata. + /// + /// The head (first) coordinate system. + /// The tail (second) coordinate system. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Optional information. + /// Axis definitions. + /// Default envelope for the compound domain. + internal CompoundCoordinateSystem( + CoordinateSystem headcs, + CoordinateSystem tailcs, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks, + List axisInfo, + double[]? defaultEnvelope = null) + : base(name, authority, authorityCode, alias, abbreviation, remarks, axisInfo, defaultEnvelope) + { + this.HeadCoordinateSystem = headcs; + this.TailCoordinateSystem = tailcs; + } - /// - public override string WKT + /// + /// Gets the head coordinate system. + /// + public CoordinateSystem HeadCoordinateSystem { get; } + + /// + /// Gets the tail coordinate system. + /// + public CoordinateSystem TailCoordinateSystem { get; } + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new CompoundCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new CompoundCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this compound coordinate system as an . + /// + /// An containing the XML representation. + public override XElement ToXml() + { + var innerElement = new XElement("CS_CompoundCoordinateSystem"); + innerElement.Add(this.InfoXmlElement); + foreach (AxisInfo ai in this.AxisInfo) { - get - { - var sb = new StringBuilder(); - sb.Append($"COMPD_CS[\"{Name}\",{HeadCoordinateSystem.WKT},{TailCoordinateSystem.WKT}"); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - { - sb.Append($",AUTHORITY[\"{Authority}\",\"{AuthorityCode}\"]"); - } - sb.Append("]"); - return sb.ToString(); - } + innerElement.Add(ai.ToXml()); } - /// - public override string XML + innerElement.Add(this.HeadCoordinateSystem.ToXml()); + innerElement.Add(this.TailCoordinateSystem.ToXml()); + + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + this.HeadCoordinateSystem.ToWktNode(), + this.TailCoordinateSystem.ToWktNode(), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, - "{1}", - this.Dimension, InfoXml); - foreach (var ai in AxisInfo) - sb.Append(ai.XML); - sb.Append(HeadCoordinateSystem.XML); - sb.Append(TailCoordinateSystem.XML); - sb.AppendFormat(""); - return sb.ToString(); - } + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); } - /// - /// The head coordinate system - /// - public CoordinateSystem HeadCoordinateSystem { get => _headCoordinateSystem; set { _headCoordinateSystem = value; } } - - /// - /// The tail coordinate system - /// - public CoordinateSystem TailCoordinateSystem { get => _tailCoordinateSystem; set { _tailCoordinateSystem = value; } } - /// - /// A compound coordinate system - /// - /// The head (first) coordinate system - /// The tail (second) coordinate system - /// Name - /// Authority name - /// Authority-specific identification code - /// Alias - /// Abbreviation - /// Optional information - public CompoundCoordinateSystem(CoordinateSystem headcs, CoordinateSystem tailcs, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) - : base(name, authority, authorityCode, alias, abbreviation, remarks) + return new WktKeywordNode("COMPD_CS", children); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) { - _headCoordinateSystem = headcs; - _tailCoordinateSystem = tailcs; - AxisInfo = new List(); - AxisInfo.AddRange(HeadCoordinateSystem.AxisInfo); - AxisInfo.AddRange(TailCoordinateSystem.AxisInfo); + return this.ToWktNode(); } - /// - public override bool EqualParams(object obj) + var children = new List { - if( obj is CompoundCoordinateSystem compdCs ) - { - return HeadCoordinateSystem.EqualParams(compdCs.HeadCoordinateSystem) && TailCoordinateSystem.EqualParams(compdCs.TailCoordinateSystem); - } + new WktQuotedString(this.Name), + this.HeadCoordinateSystem.ToWktNode(version), + this.TailCoordinateSystem.ToWktNode(version), + }; - return false; + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); } - /// - public override IUnit GetUnits(int dimension) - { - if( dimension < 0 || dimension >= Dimension ) - { - throw new ArgumentException("Dimension not valid", nameof(dimension)); - } + return new WktKeywordNode("COMPOUNDCRS", children); + } - if( dimension < HeadCoordinateSystem.Dimension) - { - return HeadCoordinateSystem.GetUnits(dimension); - } + /// + public override bool EqualParams(object obj) + { + return obj is CompoundCoordinateSystem compdCs && this.HeadCoordinateSystem.EqualParams(compdCs.HeadCoordinateSystem) && this.TailCoordinateSystem.EqualParams(compdCs.TailCoordinateSystem); + } - return TailCoordinateSystem.GetUnits(dimension - HeadCoordinateSystem.Dimension); + /// + public override IUnit GetUnits(int dimension) + { + if (dimension < 0 || dimension >= this.Dimension) + { + ArgumentGuard.ThrowArgument("Dimension not valid", nameof(dimension)); } + + return dimension < this.HeadCoordinateSystem.Dimension + ? this.HeadCoordinateSystem.GetUnits(dimension) + : this.TailCoordinateSystem.GetUnits(dimension - this.HeadCoordinateSystem.Dimension); + } + + private static List CreateAxisInfo(CoordinateSystem headcs, CoordinateSystem tailcs) + { + headcs = ArgumentGuard.ThrowIfNull(headcs, nameof(headcs)); + tailcs = ArgumentGuard.ThrowIfNull(tailcs, nameof(tailcs)); + + var axisInfo = new List(headcs.Dimension + tailcs.Dimension); + axisInfo.AddRange(headcs.AxisInfo); + axisInfo.AddRange(tailcs.AxisInfo); + return axisInfo; } } diff --git a/src/ProjNet/CoordinateSystems/ConcatenatedOperation.cs b/src/ProjNet/CoordinateSystems/ConcatenatedOperation.cs new file mode 100644 index 00000000..56469568 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/ConcatenatedOperation.cs @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A WKT2 concatenated operation consisting of one or more coordinate-operation steps. +/// +public sealed class ConcatenatedOperation : Info +{ + private readonly List steps; + + /// + /// Initializes a new instance of the class. + /// + /// Operation steps. + /// Source coordinate system. + /// Target coordinate system. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public ConcatenatedOperation( + IReadOnlyList steps, + CoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + steps = ArgumentGuard.ThrowIfNull(steps, nameof(steps)); + if (steps.Count == 0) + { + ArgumentGuard.ThrowArgument("Concatenated operations require at least one step.", nameof(steps)); + } + + this.SourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + this.TargetCoordinateSystem = ArgumentGuard.ThrowIfNull(targetCoordinateSystem, nameof(targetCoordinateSystem)); + this.steps = steps.Select(step => ArgumentGuard.ThrowIfNull(step, nameof(steps))).ToList(); + } + + /// + /// Gets the source coordinate system. + /// + public CoordinateSystem SourceCoordinateSystem { get; } + + /// + /// Gets the target coordinate system. + /// + public CoordinateSystem TargetCoordinateSystem { get; } + + /// + /// Gets the concatenated operation steps. + /// + public IReadOnlyList Steps => this.steps; + + /// + public override string WKT => this.ToWktNode(WktVersion.Wkt22019).ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this concatenated operation with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new ConcatenatedOperation WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this concatenated operation with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new ConcatenatedOperation WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Converts this concatenated operation to a WKT syntax tree node. + /// + /// A representing this concatenated operation. + public WktNode ToWktNode() => this.ToWktNode(WktVersion.Wkt22019); + + /// + /// Converts this concatenated operation to a WKT syntax tree node for the requested version. + /// + /// The WKT dialect to emit. + /// A representing this concatenated operation. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + throw WktVersionSupport.CreateNotSupportedException(nameof(ConcatenatedOperation), version); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktKeywordNode("SOURCECRS", this.SourceCoordinateSystem.ToWktNode(version)), + new WktKeywordNode("TARGETCRS", this.TargetCoordinateSystem.ToWktNode(version)), + }; + + foreach (CoordinateOperation step in this.steps) + { + children.Add(new WktKeywordNode("STEP", step.ToWktNode(version))); + } + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("CONCATENATEDOPERATION", children); + } + + /// + /// Returns an XML representation of this concatenated operation as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement("CS_ConcatenatedOperation"); + element.Add(this.InfoXmlElement); + element.Add(new XElement("SourceCoordinateSystem", this.SourceCoordinateSystem.ToXml())); + element.Add(new XElement("TargetCoordinateSystem", this.TargetCoordinateSystem.ToXml())); + foreach (CoordinateOperation step in this.steps) + { + element.Add(new XElement("Step", step.ToXml())); + } + + return element; + } + + /// + public override bool EqualParams(object obj) + { + if (obj is not ConcatenatedOperation concatenatedOperation + || !this.SourceCoordinateSystem.EqualParams(concatenatedOperation.SourceCoordinateSystem) + || !this.TargetCoordinateSystem.EqualParams(concatenatedOperation.TargetCoordinateSystem) + || this.steps.Count != concatenatedOperation.steps.Count) + { + return false; + } + + for (int i = 0; i < this.steps.Count; i++) + { + if (!this.steps[i].EqualParams(concatenatedOperation.steps[i])) + { + return false; + } + } + + return true; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); +} diff --git a/src/ProjNet/CoordinateSystems/CoordinateOperation.cs b/src/ProjNet/CoordinateSystems/CoordinateOperation.cs new file mode 100644 index 00000000..50817359 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/CoordinateOperation.cs @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A WKT2 coordinate operation model containing method, parameter, and endpoint metadata. +/// +public sealed class CoordinateOperation : Info +{ + private readonly List parameters; + + /// + /// Initializes a new instance of the class. + /// + /// Method name. + /// Operation parameters. + /// Source coordinate system. + /// Target coordinate system. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public CoordinateOperation( + string methodName, + IReadOnlyList parameters, + CoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + this.MethodName = string.IsNullOrWhiteSpace(methodName) + ? ArgumentGuard.ThrowArgument("Coordinate operations require a method name.", nameof(methodName)) + : methodName; + this.SourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + this.TargetCoordinateSystem = ArgumentGuard.ThrowIfNull(targetCoordinateSystem, nameof(targetCoordinateSystem)); + parameters = ArgumentGuard.ThrowIfNull(parameters, nameof(parameters)); + this.parameters = parameters.Select(parameter => ArgumentGuard.ThrowIfNull(parameter, nameof(parameters))).ToList(); + } + + /// + /// Gets the source coordinate system. + /// + public CoordinateSystem SourceCoordinateSystem { get; } + + /// + /// Gets the target coordinate system. + /// + public CoordinateSystem TargetCoordinateSystem { get; } + + /// + /// Gets the operation method name. + /// + public string MethodName { get; } + + /// + /// Gets the operation parameters. + /// + public IReadOnlyList Parameters => this.parameters; + + /// + public override string WKT => this.ToWktNode(WktVersion.Wkt22019).ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate operation with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new CoordinateOperation WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate operation with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new CoordinateOperation WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Converts this coordinate operation to a WKT syntax tree node. + /// + /// A representing this coordinate operation. + public WktNode ToWktNode() => this.ToWktNode(WktVersion.Wkt22019); + + /// + /// Converts this coordinate operation to a WKT syntax tree node for the requested version. + /// + /// The WKT dialect to emit. + /// A representing this coordinate operation. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + throw WktVersionSupport.CreateNotSupportedException(nameof(CoordinateOperation), version); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktKeywordNode("SOURCECRS", this.SourceCoordinateSystem.ToWktNode(version)), + new WktKeywordNode("TARGETCRS", this.TargetCoordinateSystem.ToWktNode(version)), + new WktKeywordNode("METHOD", new WktQuotedString(this.MethodName)), + }; + + foreach (Parameter parameter in this.parameters) + { + children.Add(new WktKeywordNode( + "PARAMETER", + new WktQuotedString(parameter.Name), + new WktNumber(parameter.Value))); + } + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("COORDINATEOPERATION", children); + } + + /// + /// Returns an XML representation of this coordinate operation as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement("CS_CoordinateOperation", new XAttribute("MethodName", this.MethodName)); + element.Add(this.InfoXmlElement); + element.Add(new XElement("SourceCoordinateSystem", this.SourceCoordinateSystem.ToXml())); + element.Add(new XElement("TargetCoordinateSystem", this.TargetCoordinateSystem.ToXml())); + foreach (Parameter parameter in this.parameters) + { + element.Add(new XElement( + "Parameter", + new XAttribute("Name", parameter.Name), + new XAttribute("Value", parameter.Value.ToString(CultureInfo.InvariantCulture)))); + } + + return element; + } + + /// + public override bool EqualParams(object obj) + { + if (obj is not CoordinateOperation coordinateOperation + || !string.Equals(this.MethodName, coordinateOperation.MethodName, StringComparison.OrdinalIgnoreCase) + || !this.SourceCoordinateSystem.EqualParams(coordinateOperation.SourceCoordinateSystem) + || !this.TargetCoordinateSystem.EqualParams(coordinateOperation.TargetCoordinateSystem) + || this.parameters.Count != coordinateOperation.parameters.Count) + { + return false; + } + + for (int i = 0; i < this.parameters.Count; i++) + { + if (!string.Equals(this.parameters[i].Name, coordinateOperation.parameters[i].Name, StringComparison.OrdinalIgnoreCase) + || this.parameters[i].Value != coordinateOperation.parameters[i].Value) + { + return false; + } + } + + return true; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); +} diff --git a/src/ProjNet/CoordinateSystems/CoordinateSystem.cs b/src/ProjNet/CoordinateSystems/CoordinateSystem.cs index 2d93025e..b0a2f051 100644 --- a/src/ProjNet/CoordinateSystems/CoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/CoordinateSystem.cs @@ -1,113 +1,207 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; -using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// Base interface for all coordinate systems. +/// +/// +/// A coordinate system is a mathematical space, where the elements of the space +/// are called positions. Each position is described by a list of numbers. The length +/// of the list corresponds to the dimension of the coordinate system. So in a 2D +/// coordinate system each position is described by a list containing 2 numbers. +/// However, in a coordinate system, not all lists of numbers correspond to a +/// position - some lists may be outside the domain of the coordinate system. For +/// example, in a 2D Lat/Lon coordinate system, the list (91,91) does not correspond +/// to a position. +/// Some coordinate systems also have a mapping from the mathematical space into +/// locations in the real world. So in a Lat/Lon coordinate system, the mathematical +/// position (lat, long) corresponds to a location on the surface of the Earth. This +/// mapping from the mathematical space into real-world locations is called a Datum. +/// +public abstract class CoordinateSystem : Info { - /// - /// Base interface for all coordinate systems. - /// - /// - /// A coordinate system is a mathematical space, where the elements of the space - /// are called positions. Each position is described by a list of numbers. The length - /// of the list corresponds to the dimension of the coordinate system. So in a 2D - /// coordinate system each position is described by a list containing 2 numbers. - /// However, in a coordinate system, not all lists of numbers correspond to a - /// position - some lists may be outside the domain of the coordinate system. For - /// example, in a 2D Lat/Lon coordinate system, the list (91,91) does not correspond - /// to a position. - /// Some coordinate systems also have a mapping from the mathematical space into - /// locations in the real world. So in a Lat/Lon coordinate system, the mathematical - /// position (lat, long) corresponds to a location on the surface of the Earth. This - /// mapping from the mathematical space into real-world locations is called a Datum. + private readonly List axisInfo; + private readonly double[] defaultEnvelope; + + /// + /// Initializes a new instance of the class. + /// + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + internal CoordinateSystem(string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : this(name, authority, authorityCode, alias, abbreviation, remarks, [], null) + { + } + + /// + /// Initializes a new instance of the class with axis metadata. + /// + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + /// Axis definitions. + /// Default envelope. + internal CoordinateSystem( + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks, + List axisInfo, + double[]? defaultEnvelope) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + this.axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + this.defaultEnvelope = CloneDefaultEnvelope(defaultEnvelope); + } + + /// + /// Gets dimension of the coordinate system. + /// + public int Dimension + { + get { return this.AxisInfo.Count; } + } + + /// + /// Gets the axis definitions for this coordinate system. + /// + internal List AxisInfo => this.axisInfo; + + /// + /// Gets default envelope of coordinate system. + /// + /// + /// Coordinate systems which are bounded should return the minimum bounding box of their domain. + /// Unbounded coordinate systems should return a box which is as large as is likely to be used. + /// For example, a (lon,lat) geographic coordinate system in degrees should return a box from + /// (-180,-90) to (180,90), and a geocentric coordinate system could return a box from (-r,-r,-r) + /// to (+r,+r,+r) where r is the approximate radius of the Earth. /// - [Serializable] - public abstract class CoordinateSystem : Info - { - /// - /// Initializes a new instance of a coordinate system. - /// - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal CoordinateSystem(string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) - : base (name, authority, authorityCode, alias,abbreviation, remarks) { } - - #region ICoordinateSystem Members - - /// - /// Dimension of the coordinate system. - /// - public int Dimension - { - get { return _AxisInfo.Count; } - } - - /// - /// Gets the units for the dimension within coordinate system. - /// Each dimension in the coordinate system has corresponding units. - /// - public abstract IUnit GetUnits(int dimension); - - private List _AxisInfo; - internal List AxisInfo - { - get { return _AxisInfo; } - set { _AxisInfo = value; } - } - - - /// - /// Gets axis details for dimension within coordinate system. - /// - /// Dimension - /// Axis info - public AxisInfo GetAxis(int dimension) - { - if (dimension >= _AxisInfo.Count || dimension < 0) - throw new ArgumentException("AxisInfo not available for dimension " + dimension.ToString(CultureInfo.InvariantCulture)); - return _AxisInfo[dimension]; - } - - - private double[] _DefaultEnvelope; - - /// - /// Gets default envelope of coordinate system. - /// - /// - /// Coordinate systems which are bounded should return the minimum bounding box of their domain. - /// Unbounded coordinate systems should return a box which is as large as is likely to be used. - /// For example, a (lon,lat) geographic coordinate system in degrees should return a box from - /// (-180,-90) to (180,90), and a geocentric coordinate system could return a box from (-r,-r,-r) - /// to (+r,+r,+r) where r is the approximate radius of the Earth. - /// - public double[] DefaultEnvelope - { - get { return _DefaultEnvelope; } - set { _DefaultEnvelope = value; } - } - - #endregion - } + public double[] DefaultEnvelope => this.defaultEnvelope.Length == 0 ? Array.Empty() : (double[])this.defaultEnvelope.Clone(); + + /// + /// Gets the units for the dimension within coordinate system. + /// Each dimension in the coordinate system has corresponding units. + /// + /// Zero-based index of the dimension. + /// The unit for the specified dimension. + public abstract IUnit GetUnits(int dimension); + + /// + /// Converts this coordinate system to a WKT syntax tree node. + /// + /// A representing this coordinate system. + public virtual WktNode ToWktNode() => new WktIdentifier(this.WKT); + + /// + /// Converts this coordinate system to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this coordinate system in the requested WKT version. + public virtual WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + return version == WktVersion.Wkt1 + ? this.ToWktNode() + : throw WktVersionSupport.CreateNotSupportedException(this.GetType().Name, version); + } + + /// + /// Returns an XML representation of this coordinate system as an . + /// + /// An containing the XML representation. + public virtual XElement ToXml() => throw new NotSupportedException("XML serialization is not supported for this coordinate system type."); + + /// + /// Serializes this coordinate system to PROJJSON. + /// + /// The serialized PROJJSON text. + /// Thrown when PROJJSON serialization is not supported for this coordinate system type. + public string ToProjJson() => ProjJsonWriter.ToJson(this); + + /// + /// Gets axis details for dimension within coordinate system. + /// + /// Zero-based index of the axis. + /// The for the specified dimension. + public AxisInfo GetAxis(int dimension) + { + if (dimension >= this.AxisInfo.Count || dimension < 0) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(dimension), "AxisInfo not available for the requested dimension."); + } + + return this.AxisInfo[dimension]; + } + + /// + /// Clones a coordinate-system default envelope for constructor-time storage. + /// + /// Envelope values to clone. + /// A cloned envelope array, or when no envelope is provided. + internal static double[] CloneDefaultEnvelope(double[]? envelope) + { + if (envelope is null || envelope.Length == 0) + { + return Array.Empty(); + } + + return (double[])envelope.Clone(); + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) + { + return this switch + { + GeographicCoordinateSystem geographicCoordinateSystem => geographicCoordinateSystem.WithAuthority(authority, code), + ProjectedCoordinateSystem projectedCoordinateSystem => projectedCoordinateSystem.WithAuthority(authority, code), + GeocentricCoordinateSystem geocentricCoordinateSystem => geocentricCoordinateSystem.WithAuthority(authority, code), + VerticalCoordinateSystem verticalCoordinateSystem => verticalCoordinateSystem.WithAuthority(authority, code), + CompoundCoordinateSystem compoundCoordinateSystem => compoundCoordinateSystem.WithAuthority(authority, code), + BoundCoordinateSystem boundCoordinateSystem => boundCoordinateSystem.WithAuthority(authority, code), + FittedCoordinateSystem fittedCoordinateSystem => fittedCoordinateSystem.WithAuthority(authority, code), + EngineeringCoordinateSystem engineeringCoordinateSystem => engineeringCoordinateSystem.WithAuthority(authority, code), + ParametricCoordinateSystem parametricCoordinateSystem => parametricCoordinateSystem.WithAuthority(authority, code), + TemporalCoordinateSystem temporalCoordinateSystem => temporalCoordinateSystem.WithAuthority(authority, code), + _ => throw new NotSupportedException($"WithAuthority is not supported for coordinate system type '{this.GetType().FullName}'."), + }; + } + + /// + private protected override Info CloneWithNameCore(string name) + { + return this switch + { + GeographicCoordinateSystem geographicCoordinateSystem => geographicCoordinateSystem.WithName(name), + ProjectedCoordinateSystem projectedCoordinateSystem => projectedCoordinateSystem.WithName(name), + GeocentricCoordinateSystem geocentricCoordinateSystem => geocentricCoordinateSystem.WithName(name), + VerticalCoordinateSystem verticalCoordinateSystem => verticalCoordinateSystem.WithName(name), + CompoundCoordinateSystem compoundCoordinateSystem => compoundCoordinateSystem.WithName(name), + BoundCoordinateSystem boundCoordinateSystem => boundCoordinateSystem.WithName(name), + FittedCoordinateSystem fittedCoordinateSystem => fittedCoordinateSystem.WithName(name), + EngineeringCoordinateSystem engineeringCoordinateSystem => engineeringCoordinateSystem.WithName(name), + ParametricCoordinateSystem parametricCoordinateSystem => parametricCoordinateSystem.WithName(name), + TemporalCoordinateSystem temporalCoordinateSystem => temporalCoordinateSystem.WithName(name), + _ => throw new NotSupportedException($"WithName is not supported for coordinate system type '{this.GetType().FullName}'."), + }; + } } diff --git a/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs b/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs index 28d831d2..213bf918 100644 --- a/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs +++ b/src/ProjNet/CoordinateSystems/CoordinateSystemFactory.cs @@ -1,345 +1,397 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; -using System.Text; +using ProjNet.CoordinateSystems.Transformations; using ProjNet.IO.CoordinateSystems; -namespace ProjNet.CoordinateSystems +/// +/// Builds up complex objects from simpler objects or values. +/// +/// +/// CoordinateSystemFactory allows applications to make coordinate systems that +/// is very flexible, whereas the other factories are easier to use. +/// So this Factory can be used to make 'special' coordinate systems. +/// For example, the EPSG authority has codes for USA state plane coordinate systems +/// using the NAD83 datum, but these coordinate systems always use meters. EPSG does not +/// have codes for NAD83 state plane coordinate systems that use feet units. This factory +/// lets an application create such a hybrid coordinate system. +/// +/// Thread safety: Instances are stateless and may be reused across threads. Factory methods +/// create new coordinate-system model objects and do not mutate shared process-wide state. +/// +/// +public class CoordinateSystemFactory { /// - /// Builds up complex objects from simpler objects or values. + /// Initializes a new instance of the class. /// - /// - /// CoordinateSystemFactory allows applications to make coordinate systems that - /// is very flexible, whereas the other factories are easier to use. - /// So this Factory can be used to make 'special' coordinate systems. - /// For example, the EPSG authority has codes for USA state plane coordinate systems - /// using the NAD83 datum, but these coordinate systems always use meters. EPSG does not - /// have codes for NAD83 state plane coordinate systems that use feet units. This factory - /// lets an application create such a hybrid coordinate system. - /// - public class CoordinateSystemFactory + public CoordinateSystemFactory() + { + } + + /// + /// This method is not implemented and always throws. + /// + /// XML representation for the spatial reference. + /// The resulting spatial reference object. + /// Always thrown because XML-based coordinate system creation is not supported. + public CoordinateSystem CreateFromXml(string xml) + { + throw new NotImplementedException(); + } + + /// + /// Creates a spatial reference object given its Well-known text representation. + /// The output object may be either a or + /// a . + /// + /// The Well-known text representation for the spatial reference. + /// + /// The resulting spatial reference object, or when the WKT + /// does not describe a coordinate system. + /// + public CoordinateSystem? CreateFromWkt(string wkt) { - /// - /// Creates an instance of this class - /// - public CoordinateSystemFactory() { } - - /// - /// Creates a coordinate system object from an XML string. - /// - /// XML representation for the spatial reference - /// The resulting spatial reference object - public CoordinateSystem CreateFromXml(string xml) + IInfo info = CoordinateSystemWktReader.Parse(wkt); + return info as CoordinateSystem; + } + + /// + /// Creates a from the specified head and tail coordinate systems. + /// + /// Name of compound coordinate system. + /// Head coordinate system. + /// Tail coordinate system. + /// Compound coordinate system. + public CompoundCoordinateSystem CreateCompoundCoordinateSystem(string name, CoordinateSystem head, CoordinateSystem tail) + { + if (string.IsNullOrWhiteSpace(name)) { - throw new NotImplementedException(); + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Creates a spatial reference object given its Well-known text representation. - /// The output object may be either a or - /// a . - /// - /// The Well-known text representation for the spatial reference - /// The resulting spatial reference object - public CoordinateSystem CreateFromWkt(string WKT) + return new CompoundCoordinateSystem(head, tail, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + + /// + /// Creates a . + /// + /// Name of bound coordinate system. + /// Source coordinate system. + /// Target or hub coordinate system. + /// Bound transformation metadata. + /// A new . + public BoundCoordinateSystem CreateBoundCoordinateSystem(string name, CoordinateSystem sourceCoordinateSystem, CoordinateSystem targetCoordinateSystem, BoundTransformation transformation) + { + if (string.IsNullOrWhiteSpace(name)) { - var info = CoordinateSystemWktReader.Parse(WKT); - return info as CoordinateSystem; + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } + sourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + targetCoordinateSystem = ArgumentGuard.ThrowIfNull(targetCoordinateSystem, nameof(targetCoordinateSystem)); + transformation = ArgumentGuard.ThrowIfNull(transformation, nameof(transformation)); + + return new BoundCoordinateSystem( + sourceCoordinateSystem, + targetCoordinateSystem, + transformation, + name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } - /// - /// Creates a [NOT IMPLEMENTED]. - /// - /// Name of compound coordinate system. - /// Head coordinate system - /// Tail coordinate system - /// Compound coordinate system - public CompoundCoordinateSystem CreateCompoundCoordinateSystem(string name, CoordinateSystem head, CoordinateSystem tail) + /// + /// Creates a . + /// + /// The units of the axes in the fitted coordinate system will be + /// inferred from the units of the base coordinate system. If the affine map + /// performs a rotation, then any mixed axes must have identical units. For + /// example, a (lat_deg,lon_deg,height_feet) system can be rotated in the + /// (lat,lon) plane, since both affected axes are in degrees. But you + /// should not rotate this coordinate system in any other plane. + /// Name of coordinate system. + /// Base coordinate system. + /// WKT of the math transform to the base coordinate system. + /// Axes of the fitted coordinate system. + /// A new . + public FittedCoordinateSystem CreateFittedCoordinateSystem(string name, CoordinateSystem baseCoordinateSystem, string toBaseWkt, List arAxes) + { + if (string.IsNullOrWhiteSpace(name)) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); - - return new CompoundCoordinateSystem(head, tail, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Creates a . - /// - /// The units of the axes in the fitted coordinate system will be - /// inferred from the units of the base coordinate system. If the affine map - /// performs a rotation, then any mixed axes must have identical units. For - /// example, a (lat_deg,lon_deg,height_feet) system can be rotated in the - /// (lat,lon) plane, since both affected axes are in degrees. But you - /// should not rotate this coordinate system in any other plane. - /// Name of coordinate system - /// Base coordinate system - /// WKT of the math transform to the base coordinate system - /// Axiis of the fitted coordinate system - /// Fitted coordinate system - public FittedCoordinateSystem CreateFittedCoordinateSystem(string name, CoordinateSystem baseCoordinateSystem, string toBaseWkt, List arAxes) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); + MathTransform toBaseTransform = MathTransformWktReader.Parse(toBaseWkt); + return new FittedCoordinateSystem(baseCoordinateSystem, toBaseTransform, name, string.Empty, -1, string.Empty, string.Empty, string.Empty, PrepareFittedAxisInfo(arAxes)); + } - var toBaseTransform = MathTransformWktReader.Parse(toBaseWkt); - return new FittedCoordinateSystem(baseCoordinateSystem, toBaseTransform, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + /// + /// Creates a . + /// + /// The units of the axes in the fitted coordinate system will be + /// inferred from the units of the base coordinate system. If the affine map + /// performs a rotation, then any mixed axes must have identical units. + /// Name of coordinate system. + /// Base coordinate system. + /// Math transform to the base coordinate system. + /// Axes of the fitted coordinate system. + /// A new . + public FittedCoordinateSystem CreateFittedCoordinateSystem(string name, CoordinateSystem baseCoordinateSystem, MathTransform toBase, List arAxes) + { + if (string.IsNullOrWhiteSpace(name)) + { + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Name of coordinate system - /// Base coordinate system - /// the math transform to the base coordinate system - /// Axiis of the fitted coordinate system - /// - public FittedCoordinateSystem CreateFittedCoordinateSystem(string name, CoordinateSystem baseCoordinateSystem, Transformations.MathTransform toBase, List arAxes) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); + return new FittedCoordinateSystem(baseCoordinateSystem, toBase, name, string.Empty, -1, string.Empty, string.Empty, string.Empty, PrepareFittedAxisInfo(arAxes)); + } - return new FittedCoordinateSystem(baseCoordinateSystem, toBase, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + /// + /// Creates an from radius values. + /// + /// + /// Name of ellipsoid. + /// Semi-major axis length in the units of . + /// Semi-minor axis length in the units of . + /// Unit of measure for both axes. + /// Ellipsoid. + public Ellipsoid CreateEllipsoid(string name, double semiMajorAxis, double semiMinorAxis, LinearUnit linearUnit) + { + double ivf = 0; + if (semiMajorAxis != semiMinorAxis) + { + ivf = semiMajorAxis / (semiMajorAxis - semiMinorAxis); } - /* - /// - /// Creates a local coordinate system. - /// - /// - /// The dimension of the local coordinate system is determined by the size of - /// the axis array. All the axes will have the same units. If you want to make - /// a coordinate system with mixed units, then you can make a compound - /// coordinate system from different local coordinate systems. - /// - /// Name of local coordinate system - /// Local datum - /// Units - /// Axis info - /// Local coordinate system - public ILocalCoordinateSystem CreateLocalCoordinateSystem(string name, ILocalDatum datum, IUnit unit, List axes) + return new Ellipsoid(semiMajorAxis, semiMinorAxis, ivf, false, linearUnit, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + + /// + /// Creates an from an major radius, and inverse flattening. + /// + /// + /// Name of ellipsoid. + /// Semi major-axis. + /// Inverse flattening. + /// Linear unit. + /// Ellipsoid. + public Ellipsoid CreateFlattenedSphere(string name, double semiMajorAxis, double inverseFlattening, LinearUnit linearUnit) + { + if (string.IsNullOrWhiteSpace(name)) { - throw new NotImplementedException(); + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - */ - /// - /// Creates an from radius values. - /// - /// - /// Name of ellipsoid - /// - /// - /// - /// Ellipsoid - public Ellipsoid CreateEllipsoid(string name, double semiMajorAxis, double semiMinorAxis, LinearUnit linearUnit) + + return new Ellipsoid(semiMajorAxis, -1, inverseFlattening, true, linearUnit, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + + /// + /// Creates a using a projection object. + /// + /// Name of projected coordinate system. + /// Geographic coordinate system. + /// Projection. + /// Linear unit. + /// Primary axis. + /// Secondary axis. + /// Projected coordinate system. + public ProjectedCoordinateSystem CreateProjectedCoordinateSystem(string name, GeographicCoordinateSystem gcs, IProjection projection, LinearUnit linearUnit, AxisInfo axis0, AxisInfo axis1) + { + if (string.IsNullOrWhiteSpace(name)) { - double ivf = 0; - if (semiMajorAxis != semiMinorAxis) - ivf = semiMajorAxis / (semiMajorAxis - semiMinorAxis); - return new Ellipsoid(semiMajorAxis, semiMinorAxis, ivf, false, linearUnit, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Creates an from an major radius, and inverse flattening. - /// - /// - /// Name of ellipsoid - /// Semi major-axis - /// Inverse flattening - /// Linear unit - /// Ellipsoid - public Ellipsoid CreateFlattenedSphere(string name, double semiMajorAxis, double inverseFlattening, LinearUnit linearUnit) + gcs = ArgumentGuard.ThrowIfNull(gcs, nameof(gcs)); + projection = ArgumentGuard.ThrowIfNull(projection, nameof(projection)); + linearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + + var info = new List(2) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); + axis0, + axis1, + }; + return new ProjectedCoordinateSystem( + gcs.HorizontalDatum, + gcs, + linearUnit, + projection, + info, + name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } - return new Ellipsoid(semiMajorAxis, -1, inverseFlattening, true, linearUnit, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + /// + /// Creates a . + /// + /// Name of projection. + /// Projection class. + /// Projection parameters. + /// Projection. + public IProjection CreateProjection(string name, string wktProjectionClass, List parameters) + { + if (string.IsNullOrWhiteSpace(name)) + { + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Creates a using a projection object. - /// - /// Name of projected coordinate system - /// Geographic coordinate system - /// Projection - /// Linear unit - /// Primary axis - /// Secondary axis - /// Projected coordinate system - public ProjectedCoordinateSystem CreateProjectedCoordinateSystem(string name, GeographicCoordinateSystem gcs, IProjection projection, LinearUnit linearUnit, AxisInfo axis0, AxisInfo axis1) + parameters = ArgumentGuard.ThrowIfNull(parameters, nameof(parameters)); + if (parameters.Count == 0) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name", nameof(name)); - if (gcs == null) - throw new ArgumentException("Geographic coordinate system was null", nameof(gcs)); - if (projection == null) - throw new ArgumentException("Projection was null", nameof(projection)); - if (linearUnit == null) - throw new ArgumentException("Linear unit was null"); - - var info = new List(2); - info.Add(axis0); - info.Add(axis1); - return new ProjectedCoordinateSystem(null, gcs, linearUnit, projection, info, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + ArgumentGuard.ThrowArgument("Invalid projection parameters", nameof(parameters)); } - /// - /// Creates a . - /// - /// Name of projection - /// Projection class - /// Projection parameters - /// Projection - public IProjection CreateProjection(string name, string wktProjectionClass, List parameters) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); - if (parameters == null || parameters.Count == 0) - throw new ArgumentException("Invalid projection parameters"); + return new Projection(wktProjectionClass, parameters, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } - return new Projection(wktProjectionClass, parameters, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + /// + /// Creates from ellipsoid and Bursa-World parameters. + /// + /// + /// Since this method contains a set of Bursa-Wolf parameters, the created + /// datum will always have a relationship to WGS84. If you wish to create a + /// horizontal datum that has no relationship with WGS84, then you can + /// either specify a horizontalDatumType of , or create it via WKT. + /// + /// Name of ellipsoid. + /// Type of datum. + /// Ellipsoid. + /// Optional Wgs84 conversion parameters. + /// Horizontal datum. + public HorizontalDatum CreateHorizontalDatum(string name, DatumType datumType, Ellipsoid ellipsoid, Wgs84ConversionInfo? toWgs84) + { + if (string.IsNullOrWhiteSpace(name)) + { + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Creates from ellipsoid and Bursa-World parameters. - /// - /// - /// Since this method contains a set of Bursa-Wolf parameters, the created - /// datum will always have a relationship to WGS84. If you wish to create a - /// horizontal datum that has no relationship with WGS84, then you can - /// either specify a horizontalDatumType of , or create it via WKT. - /// - /// Name of ellipsoid - /// Type of datum - /// Ellipsoid - /// Wgs84 conversion parameters - /// Horizontal datum - public HorizontalDatum CreateHorizontalDatum(string name, DatumType datumType, Ellipsoid ellipsoid, Wgs84ConversionInfo toWgs84) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); - if (ellipsoid == null) - throw new ArgumentException("Ellipsoid was null"); + ellipsoid = ArgumentGuard.ThrowIfNull(ellipsoid, nameof(ellipsoid)); - return new HorizontalDatum(ellipsoid, toWgs84, datumType, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); - } + return new HorizontalDatum(ellipsoid, toWgs84, datumType, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } - /// - /// Creates a , relative to Greenwich. - /// - /// Name of prime meridian - /// Angular unit - /// Longitude - /// Prime meridian - public PrimeMeridian CreatePrimeMeridian(string name, AngularUnit angularUnit, double longitude) + /// + /// Creates a , relative to Greenwich. + /// + /// Name of prime meridian. + /// Angular unit. + /// Longitude. + /// Prime meridian. + public PrimeMeridian CreatePrimeMeridian(string name, AngularUnit angularUnit, double longitude) + { + if (string.IsNullOrWhiteSpace(name)) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); - - return new PrimeMeridian(longitude, angularUnit, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Creates a , which could be Lat/Lon or Lon/Lat. - /// - /// Name of geographical coordinate system - /// Angular units - /// Horizontal datum - /// Prime meridian - /// First axis - /// Second axis - /// Geographic coordinate system - public GeographicCoordinateSystem CreateGeographicCoordinateSystem(string name, AngularUnit angularUnit, HorizontalDatum datum, PrimeMeridian primeMeridian, AxisInfo axis0, AxisInfo axis1) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); + return new PrimeMeridian(longitude, angularUnit, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } - var info = new List(2); - info.Add(axis0); - info.Add(axis1); - return new GeographicCoordinateSystem(angularUnit, datum, primeMeridian, info, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + /// + /// Creates a , which could be Lat/Lon or Lon/Lat. + /// + /// Name of geographical coordinate system. + /// Angular units. + /// Horizontal datum. + /// Prime meridian. + /// First axis. + /// Second axis. + /// Geographic coordinate system. + public GeographicCoordinateSystem CreateGeographicCoordinateSystem(string name, AngularUnit angularUnit, HorizontalDatum datum, PrimeMeridian primeMeridian, AxisInfo axis0, AxisInfo axis1) + { + if (string.IsNullOrWhiteSpace(name)) + { + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /* - /// - /// Creates a . - /// - /// Name of datum - /// Datum type - /// - public ILocalDatum CreateLocalDatum(string name, DatumType datumType) + var info = new List(2) { - throw new NotImplementedException(); + axis0, + axis1, + }; + return new GeographicCoordinateSystem(angularUnit, datum, primeMeridian, info, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + + /// + /// Creates a from an enumerated type value. + /// + /// Name of datum. + /// Type of datum. + /// Vertical datum. + public VerticalDatum CreateVerticalDatum(string name, DatumType datumType) + { + if (string.IsNullOrWhiteSpace(name)) + { + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - */ + return new VerticalDatum(datumType, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } - /// - /// Creates a from an enumerated type value. - /// - /// Name of datum - /// Type of datum - /// Vertical datum - public VerticalDatum CreateVerticalDatum(string name, DatumType datumType) + /// + /// Creates a from a datum and linear units. + /// + /// Name of vertical coordinate system. + /// Vertical datum. + /// Unit. + /// Axis info. + /// Vertical coordinate system. + public VerticalCoordinateSystem CreateVerticalCoordinateSystem(string name, VerticalDatum datum, LinearUnit verticalUnit, AxisInfo axis) + { + if (string.IsNullOrWhiteSpace(name)) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); - - return new VerticalDatum(datumType, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } + return new VerticalCoordinateSystem(verticalUnit, datum, axis, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } - - /// - /// Creates a from a datum and linear units. - /// - /// Name of vertical coordinate system - /// Vertical datum - /// Unit - /// Axis info - /// Vertical coordinate system - public VerticalCoordinateSystem CreateVerticalCoordinateSystem(string name, VerticalDatum datum, LinearUnit verticalUnit, AxisInfo axis) + /// + /// Creates a from a datum, + /// linear unit and . + /// + /// Name of geocentric coordinate system. + /// Horizontal datum. + /// Linear unit. + /// Prime meridian. + /// Geocentric Coordinate System. + public GeocentricCoordinateSystem CreateGeocentricCoordinateSystem(string name, HorizontalDatum datum, LinearUnit linearUnit, PrimeMeridian primeMeridian) + { + if (string.IsNullOrWhiteSpace(name)) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); - - return new VerticalCoordinateSystem(verticalUnit, datum, axis, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + ArgumentGuard.ThrowArgument("Invalid name", nameof(name)); } - /// - /// Creates a from a datum, - /// linear unit and . - /// - /// Name of geocentric coordinate system - /// Horizontal datum - /// Linear unit - /// Prime meridian - /// Geocentric Coordinate System - public GeocentricCoordinateSystem CreateGeocentricCoordinateSystem(string name, HorizontalDatum datum, LinearUnit linearUnit, PrimeMeridian primeMeridian) + var info = new List(3) + { + new("X", AxisOrientationEnum.Other), + new("Y", AxisOrientationEnum.Other), + new("Z", AxisOrientationEnum.Other), + }; + return new GeocentricCoordinateSystem(datum, linearUnit, primeMeridian, info, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + + private static List? PrepareFittedAxisInfo(List axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + if (axisInfo.Count == 0) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Invalid name"); - - var info = new List(3); - info.Add(new AxisInfo("X", AxisOrientationEnum.Other)); - info.Add(new AxisInfo("Y", AxisOrientationEnum.Other)); - info.Add(new AxisInfo("Z", AxisOrientationEnum.Other)); - return new GeocentricCoordinateSystem(datum, linearUnit, primeMeridian, info, name, string.Empty, -1, string.Empty, string.Empty, string.Empty); + return null; } + + return new List(axisInfo); } } diff --git a/src/ProjNet/CoordinateSystems/CoordinateSystemUtilities.cs b/src/ProjNet/CoordinateSystems/CoordinateSystemUtilities.cs new file mode 100644 index 00000000..7e112f85 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/CoordinateSystemUtilities.cs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Globalization; + +/// +/// Provides shared coordinate-system utility helpers that are independent from specific +/// projection implementations. +/// +public static class CoordinateSystemUtilities +{ + /// + /// Calculates the UTM zone number for the given longitude. + /// + /// The longitude in decimal degrees. + /// The UTM zone number (1-60). + public static long CalcUtmZone(double lon) + { + return lon >= 180d + ? 60L + : (long)(((lon + 180.0) / 6.0) + 1.0); + } + + /// + /// Converts a longitude value in degrees to radians. + /// + /// The value in degrees to convert to radians. + /// If true, -180 and +180 are valid, otherwise they are considered out of range. + /// The longitude converted to radians. + public static double LongitudeToRadians(double x, bool edge) + { + if (edge ? (x >= -180 && x <= 180) : (x > -180 && x < 180)) + { + return DegreesToRadians(x); + } + + string longitudeMessage = $"{x.ToString(CultureInfo.InvariantCulture)} not a valid longitude in degrees."; + ArgumentGuard.ThrowArgumentOutOfRange(nameof(x), longitudeMessage); + return 0d; + } + + /// + /// Converts a latitude value in degrees to radians. + /// + /// The value in degrees to convert to radians. + /// If true, -90 and +90 are valid, otherwise they are considered out of range. + /// The latitude converted to radians. + public static double LatitudeToRadians(double y, bool edge) + { + if (edge ? (y >= -90 && y <= 90) : (y > -90 && y < 90)) + { + return DegreesToRadians(y); + } + + string latitudeMessage = $"{y.ToString(CultureInfo.InvariantCulture)} not a valid latitude in degrees."; + ArgumentGuard.ThrowArgumentOutOfRange(nameof(y), latitudeMessage); + return 0d; + } + + private static double DegreesToRadians(double degrees) => Math.PI * degrees / 180.0; +} diff --git a/src/ProjNet/CoordinateSystems/Datum.cs b/src/ProjNet/CoordinateSystems/Datum.cs index 41cab49a..d93af092 100644 --- a/src/ProjNet/CoordinateSystems/Datum.cs +++ b/src/ProjNet/CoordinateSystems/Datum.cs @@ -1,79 +1,102 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; -namespace ProjNet.CoordinateSystems +/// +/// A set of quantities from which other quantities are calculated. +/// +/// +/// In the OGC abstract model, a datum can be described as a set of real points on the +/// earth that have coordinates. More practically, it is the set of parameters that +/// defines the origin and orientation of a coordinate system with respect to the earth. +/// The definition may include text and/or numeric parameters tied to physical locations +/// (such as the center of mass) and physical directions (such as the axis of spin). +/// It may also include temporal behavior, such as the rate of change of the coordinate +/// axes orientation. +/// +public abstract class Datum : Info { - /// - /// A set of quantities from which other quantities are calculated. - /// - /// - /// For the OGC abstract model, it can be defined as a set of real points on the earth - /// that have coordinates. EG. A datum can be thought of as a set of parameters - /// defining completely the origin and orientation of a coordinate system with respect - /// to the earth. A textual description and/or a set of parameters describing the - /// relationship of a coordinate system to some predefined physical locations (such - /// as center of mass) and physical directions (such as axis of spin). The definition - /// of the datum may also include the temporal behavior (such as the rate of change of - /// the orientation of the coordinate axes). - /// - [Serializable] - public abstract class Datum : Info - { - /// - /// Initializes a new instance of a Datum object - /// - /// Datum type - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal Datum(DatumType type, - string name, string authority, long code, string alias, - string remarks, string abbreviation) - : base(name, authority, code, alias, abbreviation, remarks) - { - DatumType = type; - } - #region IDatum Members + /// + /// Initializes a new instance of the class. + /// + /// Datum type. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + /// Retained datum-ensemble metadata. + internal Datum( + DatumType type, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation, + DatumEnsemble? ensemble = null) + : base(name, authority, code, alias, abbreviation, remarks) + { + this.DatumType = type; + this.Ensemble = ensemble; + } + /// + /// Gets the type of the datum as an enumerated code. + /// + public DatumType DatumType { get; } - /// - /// Gets or sets the type of the datum as an enumerated code. - /// - public DatumType DatumType { get; set; } + /// + /// Gets retained datum-ensemble metadata when this datum represents an ensemble-backed CRS definition. + /// + public DatumEnsemble? Ensemble { get; } - #endregion + /// + /// Creates a copy of this datum with updated retained datum-ensemble metadata. + /// + /// Replacement ensemble metadata, or to clear it. + /// A new datum instance with updated ensemble metadata. + public Datum WithEnsemble(DatumEnsemble? ensemble) + { + return InfoAuthorityCloneHelper.CloneWithEnsemble(this, ensemble); + } - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is Ellipsoid)) - return false; - return (obj as Datum).DatumType == this.DatumType; - } - } + /// + public override bool EqualParams(object obj) + { + return obj is Datum datum && datum.DatumType == this.DatumType; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) + { + return this switch + { + HorizontalDatum horizontalDatum => horizontalDatum.WithAuthority(authority, code), + VerticalDatum verticalDatum => verticalDatum.WithAuthority(authority, code), + EngineeringDatum engineeringDatum => engineeringDatum.WithAuthority(authority, code), + ParametricDatum parametricDatum => parametricDatum.WithAuthority(authority, code), + TemporalDatum temporalDatum => temporalDatum.WithAuthority(authority, code), + _ => throw new NotSupportedException($"WithAuthority is not supported for datum type '{this.GetType().FullName}'."), + }; + } + + /// + private protected override Info CloneWithNameCore(string name) + { + return this switch + { + HorizontalDatum horizontalDatum => horizontalDatum.WithName(name), + VerticalDatum verticalDatum => verticalDatum.WithName(name), + EngineeringDatum engineeringDatum => engineeringDatum.WithName(name), + ParametricDatum parametricDatum => parametricDatum.WithName(name), + TemporalDatum temporalDatum => temporalDatum.WithName(name), + _ => throw new NotSupportedException($"WithName is not supported for datum type '{this.GetType().FullName}'."), + }; + } } diff --git a/src/ProjNet/CoordinateSystems/DatumEnsemble.cs b/src/ProjNet/CoordinateSystems/DatumEnsemble.cs new file mode 100644 index 00000000..d58d8893 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/DatumEnsemble.cs @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using ProjNet.IO.Wkt; + +/// +/// Describes a datum ensemble with member identifiers and ensemble accuracy metadata. +/// +public sealed class DatumEnsemble : IEquatable +{ + private const double EqualityTolerance = 1e-12d; + private readonly ReadOnlyCollection members; + + /// + /// Initializes a new instance of the class without an ellipsoid or identifier. + /// + /// Ensemble name. + /// Ensemble members. + /// Ensemble accuracy. + public DatumEnsemble(string name, IReadOnlyList members, double accuracy) + : this(name, members, accuracy, null, string.Empty, -1) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Ensemble name. + /// Ensemble members. + /// Ensemble accuracy. + /// Shared ellipsoid for geodetic ensembles, when available. + /// Authority name. + /// Authority-specific identification code. + public DatumEnsemble( + string name, + IReadOnlyList members, + double accuracy, + Ellipsoid? ellipsoid, + string authority, + long authorityCode) + { + this.Name = ArgumentGuard.ThrowIfNullOrWhiteSpace(name, nameof(name)); + members = ArgumentGuard.ThrowIfNull(members, nameof(members)); + if (members.Count == 0) + { + ArgumentGuard.ThrowArgument("Datum ensembles must contain at least one member.", nameof(members)); + } + + var memberArray = new DatumEnsembleMember[members.Count]; + for (int i = 0; i < members.Count; i++) + { + memberArray[i] = ArgumentGuard.ThrowIfNull(members[i], nameof(members)); + } + + ArgumentGuard.ThrowIfNotFinite(accuracy, nameof(accuracy), "Datum ensemble accuracy must be finite."); + if (accuracy < 0d) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(accuracy), accuracy, "Datum ensemble accuracy must be non-negative."); + } + + this.members = Array.AsReadOnly(memberArray); + this.Accuracy = accuracy; + this.Ellipsoid = ellipsoid; + this.Authority = authority ?? string.Empty; + this.AuthorityCode = authorityCode; + } + + /// + /// Gets the ensemble name. + /// + public string Name { get; } + + /// + /// Gets the ensemble members in declaration order. + /// + public IReadOnlyList Members => this.members; + + /// + /// Gets the stated ensemble accuracy. + /// + public double Accuracy { get; } + + /// + /// Gets the shared ellipsoid for geodetic ensembles, when available. + /// + public Ellipsoid? Ellipsoid { get; } + + /// + /// Gets the authority name. + /// + public string Authority { get; } + + /// + /// Gets the authority-specific identification code. + /// + public long AuthorityCode { get; } + + /// + public bool Equals(DatumEnsemble? other) + { + if (other is null + || !string.Equals(this.Name, other.Name, StringComparison.Ordinal) + || !string.Equals(this.Authority, other.Authority, StringComparison.Ordinal) + || this.AuthorityCode != other.AuthorityCode + || Math.Abs(this.Accuracy - other.Accuracy) > EqualityTolerance + || (this.Ellipsoid is null) != (other.Ellipsoid is null)) + { + return false; + } + + if (this.Ellipsoid is not null + && other.Ellipsoid is not null + && !this.Ellipsoid.EqualParams(other.Ellipsoid)) + { + return false; + } + + if (this.members.Count != other.members.Count) + { + return false; + } + + for (int i = 0; i < this.members.Count; i++) + { + if (!this.members[i].Equals(other.members[i])) + { + return false; + } + } + + return true; + } + + /// + public override bool Equals(object? obj) => this.Equals(obj as DatumEnsemble); + + /// + public override int GetHashCode() + { + HashCode hash = default; + hash.Add(this.Name, StringComparer.Ordinal); + hash.Add(this.Authority, StringComparer.Ordinal); + hash.Add(this.AuthorityCode); + hash.Add(this.Accuracy); + if (this.Ellipsoid is not null) + { + hash.Add(this.Ellipsoid.SemiMajorAxis); + hash.Add(this.Ellipsoid.SemiMinorAxis); + hash.Add(this.Ellipsoid.InverseFlattening); + hash.Add(this.Ellipsoid.IsIvfDefinitive); + hash.Add(this.Ellipsoid.AxisUnit.MetersPerUnit); + } + + for (int i = 0; i < this.members.Count; i++) + { + hash.Add(this.members[i]); + } + + return hash.ToHashCode(); + } + + /// + public override string ToString() => this.Name; + + /// + /// Converts this datum ensemble to a WKT2 ENSEMBLE node. + /// + /// The WKT dialect to emit. + /// A representing this datum ensemble. + internal WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + throw WktVersionSupport.CreateNotSupportedException(nameof(DatumEnsemble), version); + } + + var children = new List + { + new WktQuotedString(this.Name), + }; + + for (int i = 0; i < this.members.Count; i++) + { + children.Add(this.members[i].ToWktNode(version)); + } + + if (this.Ellipsoid is not null) + { + children.Add(this.Ellipsoid.ToWktNode(version)); + } + + children.Add(new WktKeywordNode("ENSEMBLEACCURACY", new WktNumber(this.Accuracy))); + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("ENSEMBLE", children); + } +} diff --git a/src/ProjNet/CoordinateSystems/DatumEnsembleMember.cs b/src/ProjNet/CoordinateSystems/DatumEnsembleMember.cs new file mode 100644 index 00000000..a23f17d1 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/DatumEnsembleMember.cs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using ProjNet.IO.Wkt; + +/// +/// Identifies a single member of a datum ensemble. +/// +public sealed class DatumEnsembleMember : IEquatable +{ + /// + /// Initializes a new instance of the class without an identifier. + /// + /// Member name. + public DatumEnsembleMember(string name) + : this(name, string.Empty, -1) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Member name. + /// Authority name. + /// Authority-specific identification code. + public DatumEnsembleMember(string name, string authority, long authorityCode) + { + this.Name = ArgumentGuard.ThrowIfNullOrWhiteSpace(name, nameof(name)); + this.Authority = authority ?? string.Empty; + this.AuthorityCode = authorityCode; + } + + /// + /// Gets the member name. + /// + public string Name { get; } + + /// + /// Gets the authority name. + /// + public string Authority { get; } + + /// + /// Gets the authority-specific identification code. + /// + public long AuthorityCode { get; } + + /// + public bool Equals(DatumEnsembleMember? other) + { + return other is not null + && string.Equals(this.Name, other.Name, StringComparison.Ordinal) + && string.Equals(this.Authority, other.Authority, StringComparison.Ordinal) + && this.AuthorityCode == other.AuthorityCode; + } + + /// + public override bool Equals(object? obj) => this.Equals(obj as DatumEnsembleMember); + + /// + public override int GetHashCode() => HashCode.Combine(this.Name, this.Authority, this.AuthorityCode); + + /// + public override string ToString() => this.Name; + + /// + /// Converts this ensemble member to a WKT2 MEMBER node. + /// + /// The WKT dialect to emit. + /// A representing this ensemble member. + internal WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + throw WktVersionSupport.CreateNotSupportedException(nameof(DatumEnsembleMember), version); + } + + var memberNode = new WktKeywordNode("MEMBER", new WktQuotedString(this.Name)); + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + return idNode is null + ? memberNode + : new WktKeywordNode("MEMBER", new WktQuotedString(this.Name), idNode); + } +} diff --git a/src/ProjNet/CoordinateSystems/DatumType.cs b/src/ProjNet/CoordinateSystems/DatumType.cs index 1c1e8cb4..d1979f2b 100644 --- a/src/ProjNet/CoordinateSystems/DatumType.cs +++ b/src/ProjNet/CoordinateSystems/DatumType.cs @@ -1,126 +1,152 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -namespace ProjNet.CoordinateSystems +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +/// +/// A vertical datum of geoid model derived heights, also called GPS-derived heights. +/// These heights are approximations of orthometric heights (H), constructed from the +/// ellipsoidal heights (h) by the use of the given geoid undulation model (N) through +/// the equation: H=h-N. +/// +public enum DatumType : int { + /// + /// Lowest possible value for horizontal datum types. + /// + HD_Min = 1000, + + /// + /// Unspecified horizontal datum type. Horizontal datums with this type should never + /// supply a conversion to WGS84 using Bursa Wolf parameters. + /// + HD_Other = HD_Min, + + /// + /// These datums, such as ED50, NAD27 and NAD83, have been designed to support + /// horizontal positions on the ellipsoid as opposed to positions in 3-D space. These datums were designed mainly to support a horizontal component of a position in a domain of limited extent, such as a country, a region or a continent. + /// + HD_Classic = 1001, + + /// + /// A geocentric datum is a "satellite age" modern geodetic datum mainly of global + /// extent, such as WGS84 (used in GPS), PZ90 (used in GLONASS) and ITRF. These + /// datums were designed to support both a horizontal component of position and + /// a vertical component of position (through ellipsoidal heights). The regional + /// realizations of ITRF, such as ETRF, are also included in this category. + /// + HD_Geocentric = 1002, + + /// + /// Highest possible value for horizontal datum types. + /// + HD_Max = 1999, + + /// + /// Lowest possible value for vertical datum types. + /// + VD_Min = 2000, + + /// + /// Unspecified vertical datum type. + /// + VD_Other = VD_Min, + + /// + /// A vertical datum for orthometric heights that are measured along the plumb line. + /// + VD_Orthometric = 2001, + + /// + /// A vertical datum for ellipsoidal heights that are measured along the normal to + /// the ellipsoid used in the definition of horizontal datum. + /// + VD_Ellipsoidal = 2002, + + /// + /// The vertical datum of altitudes or heights in the atmosphere. These are + /// approximations of orthometric heights obtained with the help of a barometer or + /// a barometric altimeter. These values are usually expressed in one of the + /// following units: meters, feet, millibars (used to measure pressure levels), or + /// θ value (units used to measure geopotential height). + /// + VD_AltitudeBarometric = 2003, + + /// + /// A normal height system. + /// + VD_Normal = 2004, + /// /// A vertical datum of geoid model derived heights, also called GPS-derived heights. /// These heights are approximations of orthometric heights (H), constructed from the - /// ellipsoidal heights (h) by the use of the given geoid undulation model (N) through - /// the equation: H=h-N. - /// - public enum DatumType : int - { - /// - /// Lowest possible value for horizontal datum types - /// - HD_Min = 1000, - - /// - /// Unspecified horizontal datum type. Horizontal datums with this type should never - /// supply a conversion to WGS84 using Bursa Wolf parameters. - /// - HD_Other = 1000, - - /// - /// These datums, such as ED50, NAD27 and NAD83, have been designed to support - /// horizontal positions on the ellipsoid as opposed to positions in 3-D space. These datums were designed mainly to support a horizontal component of a position in a domain of limited extent, such as a country, a region or a continent. - /// - HD_Classic = 1001, - - /// - /// A geocentric datum is a "satellite age" modern geodetic datum mainly of global - /// extent, such as WGS84 (used in GPS), PZ90 (used in GLONASS) and ITRF. These - /// datums were designed to support both a horizontal component of position and - /// a vertical component of position (through ellipsoidal heights). The regional - /// realizations of ITRF, such as ETRF, are also included in this category. - /// - HD_Geocentric = 1002, - - /// - /// Highest possible value for horizontal datum types. - /// - HD_Max = 1999, - - /// - /// Lowest possible value for vertical datum types. - /// - VD_Min = 2000, - - /// - /// Unspecified vertical datum type. - /// - VD_Other = 2000, - - /// - /// A vertical datum for orthometric heights that are measured along the plumb line. - /// - VD_Orthometric = 2001, - - /// - /// A vertical datum for ellipsoidal heights that are measured along the normal to - /// the ellipsoid used in the definition of horizontal datum. - /// - VD_Ellipsoidal = 2002, - - /// - /// The vertical datum of altitudes or heights in the atmosphere. These are - /// approximations of orthometric heights obtained with the help of a barometer or - /// a barometric altimeter. These values are usually expressed in one of the - /// following units: meters, feet, millibars (used to measure pressure levels), or - /// theta value (units used to measure geopotential height). - /// - VD_AltitudeBarometric = 2003, - - /// - /// A normal height system. - /// - VD_Normal = 2004, - - /// - /// A vertical datum of geoid model derived heights, also called GPS-derived heights. - /// These heights are approximations of orthometric heights (H), constructed from the - /// ellipsoidal heights (h) by the use of the given geoid undulation model (N) - /// through the equation: H=h-N. - /// - VD_GeoidModelDerived = 2005, - - /// - /// This attribute is used to support the set of datums generated for hydrographic - /// engineering projects where depth measurements below sea level are needed. It is - /// often called a hydrographic or a marine datum. Depths are measured in the - /// direction perpendicular (approximately) to the actual equipotential surfaces of - /// the earth's gravity field, using such procedures as echo-sounding. - /// - VD_Depth = 2006, - - /// - /// Highest possible value for vertical datum types. - /// - VD_Max = 2999, - - /// - /// Lowest possible value for local datum types. - /// - LD_Min = 10000, - - /// - /// Highest possible value for local datum types. - /// - LD_Max = 32767 - } + /// ellipsoidal heights (h) by the use of the given geoid undulation model (N) + /// through the equation: H=h-N. + /// + VD_GeoidModelDerived = 2005, + + /// + /// This attribute is used to support the set of datums generated for hydrographic + /// engineering projects where depth measurements below sea level are needed. It is + /// often called a hydrographic or a marine datum. Depths are measured in the + /// direction perpendicular (approximately) to the actual equipotential surfaces of + /// the earth's gravity field, using such procedures as echo-sounding. + /// + VD_Depth = 2006, + + /// + /// Highest possible value for vertical datum types. + /// + VD_Max = 2999, + + /// + /// Lowest possible value for local datum types. + /// + LD_Min = 10000, + + /// + /// Unspecified local or engineering datum type. + /// + LD_Other = LD_Min, + + /// + /// A local engineering datum that defines coordinates in a local operational frame. + /// + LD_Engineering = 10001, + + /// + /// Highest possible value for local datum types. + /// + LD_Max = 32767, + + /// + /// Lowest possible value for temporal datum types. + /// + TD_Min = 40000, + + /// + /// Unspecified temporal datum type. + /// + TD_Other = TD_Min, + + /// + /// Highest possible value for temporal datum types. + /// + TD_Max = 40999, + + /// + /// Lowest possible value for parametric datum types. + /// + PD_Min = 50000, + + /// + /// Unspecified parametric datum type. + /// + PD_Other = PD_Min, + + /// + /// Highest possible value for parametric datum types. + /// + PD_Max = 50999, } diff --git a/src/ProjNet/CoordinateSystems/DerivedCoordinateSystemSupport.cs b/src/ProjNet/CoordinateSystems/DerivedCoordinateSystemSupport.cs new file mode 100644 index 00000000..036f36f5 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/DerivedCoordinateSystemSupport.cs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Shared helpers for affine derived coordinate-system serialization and parsing. +/// +internal static class DerivedCoordinateSystemSupport +{ + /// + /// Gets the WKT2/PROJJSON method name used for affine deriving conversions. + /// + internal const string AffineParametricTransformationMethodName = "Affine parametric transformation"; + + /// + /// Gets the fallback deriving-conversion name emitted when no explicit name is available. + /// + internal const string DefaultDerivingConversionName = "unnamed"; + private const double AffineMatrixTolerance = 1e-12d; + + /// + /// Creates an affine deriving conversion from a fitted-system math transform. + /// + /// The fitted-system transform to serialize. + /// The deriving-conversion name to emit. + /// The affine deriving conversion. + internal static Projection CreateAffineConversion(MathTransform transform, string conversionName) + { + if (!TryGetAffineParameters(transform, out DerivedAffineParameters parameters)) + { + throw new NotSupportedException("Derived coordinate-system support currently requires a two-dimensional affine transform with a standard homogeneous 3x3 matrix."); + } + + string name = string.IsNullOrWhiteSpace(conversionName) ? DefaultDerivingConversionName : conversionName; + return new Projection( + AffineParametricTransformationMethodName, + new List + { + new("A0", parameters.A0), + new("A1", parameters.A1), + new("A2", parameters.A2), + new("B0", parameters.B0), + new("B1", parameters.B1), + new("B2", parameters.B2), + }, + name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } + + /// + /// Reconstructs an affine math transform from a parsed deriving conversion. + /// + /// The parsed deriving conversion. + /// The affine math transform represented by the conversion. + internal static AffineTransform CreateAffineTransform(Projection conversion) + { + ArgumentGuard.ThrowIfNull(conversion, nameof(conversion)); + if (!string.Equals(conversion.ClassName, AffineParametricTransformationMethodName, StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"Derived coordinate-system support currently recognizes only '{AffineParametricTransformationMethodName}' deriving conversions."); + } + + double a0 = GetRequiredParameter(conversion, "A0"); + double a1 = GetRequiredParameter(conversion, "A1"); + double a2 = GetRequiredParameter(conversion, "A2"); + double b0 = GetRequiredParameter(conversion, "B0"); + double b1 = GetRequiredParameter(conversion, "B1"); + double b2 = GetRequiredParameter(conversion, "B2"); + + return new AffineTransform(a1, a2, a0, b1, b2, b0); + } + + private static bool TryGetAffineParameters(MathTransform transform, out DerivedAffineParameters parameters) + { + parameters = default; + if (transform is not AffineTransform affineTransform) + { + return false; + } + + double[,] matrix = affineTransform.GetMatrix(); + if (matrix.GetLength(0) != 3 + || matrix.GetLength(1) != 3 + || !ApproximatelyZero(matrix[2, 0]) + || !ApproximatelyZero(matrix[2, 1]) + || !ApproximatelyEqual(matrix[2, 2], 1d)) + { + return false; + } + + parameters = new DerivedAffineParameters( + matrix[0, 2], + matrix[0, 0], + matrix[0, 1], + matrix[1, 2], + matrix[1, 0], + matrix[1, 1]); + return true; + } + + private static double GetRequiredParameter(Projection conversion, string name) + { + ProjectionParameter? parameter = conversion.GetParameter(name); + if (parameter is null) + { + throw new NotSupportedException($"Derived affine conversion is missing the required '{name}' parameter."); + } + + return parameter.Value; + } + + private static bool ApproximatelyZero(double value) => Math.Abs(value) <= AffineMatrixTolerance; + + private static bool ApproximatelyEqual(double left, double right) => Math.Abs(left - right) <= AffineMatrixTolerance; + + /// + /// Captures the 2D affine coefficients used by WKT2/PROJJSON derived conversions. + /// + /// Translation term for the first target axis. + /// Source X scale/shear term for the first target axis. + /// Source Y scale/shear term for the first target axis. + /// Translation term for the second target axis. + /// Source X scale/shear term for the second target axis. + /// Source Y scale/shear term for the second target axis. + private readonly record struct DerivedAffineParameters(double A0, double A1, double A2, double B0, double B1, double B2); +} diff --git a/src/ProjNet/CoordinateSystems/Ellipsoid.cs b/src/ProjNet/CoordinateSystems/Ellipsoid.cs index 1ceecada..8686fb15 100644 --- a/src/ProjNet/CoordinateSystems/Ellipsoid.cs +++ b/src/ProjNet/CoordinateSystems/Ellipsoid.cs @@ -1,259 +1,442 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; +using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// Defines the standard information stored with an ellipsoid used as the reference surface for a geodetic datum. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// The predefined ellipsoid accessors are thread-safe because they only expose immutable value objects. +/// +/// +public class Ellipsoid : Info { - /// - /// The IEllipsoid interface defines the standard information stored with ellipsoid objects. + /// + /// Initializes a new instance of the class. + /// + /// Semi major axis. + /// Semi minor axis. + /// Inverse flattening. + /// Inverse Flattening is definitive for this ellipsoid (Semi-minor axis will be overridden). + /// Axis unit. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + internal Ellipsoid( + double semiMajorAxis, + double semiMinorAxis, + double inverseFlattening, + bool isIvfDefinitive, + LinearUnit axisUnit, + string name, + string authority, + long code, + string alias, + string abbreviation, + string remarks) + : base(name, authority, code, alias, abbreviation, remarks) + { + this.SemiMajorAxis = semiMajorAxis; + this.InverseFlattening = inverseFlattening; + this.AxisUnit = axisUnit; + this.IsIvfDefinitive = isIvfDefinitive; + if (isIvfDefinitive && (inverseFlattening == 0 || double.IsInfinity(inverseFlattening))) + { + this.SemiMinorAxis = semiMajorAxis; + } + else if (isIvfDefinitive) + { + this.SemiMinorAxis = (1.0 - (1.0 / this.InverseFlattening)) * semiMajorAxis; + } + else + { + this.SemiMinorAxis = semiMinorAxis; + } + } + + /// + /// Gets the Airy 1830 ellipsoid. + /// + public static Ellipsoid Airy1830 + { + get + { + return new Ellipsoid( + 6377563.396d, + 0d, + 299.3249646d, + true, + LinearUnit.Metre, + "Airy 1830", + "EPSG", + 7001, + string.Empty, + string.Empty, + string.Empty); + } + } + + /// + /// Gets the WGS 84 ellipsoid. + /// + /// + /// Inverse flattening derived from four defining parameters + /// (semi-major axis; + /// C20 = -484.16685*10e-6; + /// earth's angular velocity w = 7292115e11 rad/sec; + /// gravitational constant GM = 3986005e8 m*m*m/s/s). + /// This convenience accessor intentionally retains the legacy runtime alias and remarks metadata instead of + /// exposing the generated EPSG catalog entry verbatim, so existing callers keep the established public representation + /// while the higher-level WGS84 CRS statics resolve through the catalog. + /// + public static Ellipsoid WGS84 + { + get + { + // Keep the legacy public representation stable even though the generated catalog can resolve EPSG:7030. + return new Ellipsoid( + 6378137, + 0, + 298.257223563, + true, + LinearUnit.Metre, + "WGS 84", + "EPSG", + 7030, + "WGS84", + string.Empty, + "Inverse flattening derived from four defining parameters (semi-major axis; C20 = -484.16685*10e-6; earth's angular velocity w = 7292115e11 rad/sec; gravitational constant GM = 3986005e8 m*m*m/s/s)."); + } + } + + /// + /// Gets the WGS 72 ellipsoid. + /// + public static Ellipsoid WGS72 + { + get + { + return new Ellipsoid( + 6378135.0, + 0, + 298.26, + true, + LinearUnit.Metre, + "WGS 72", + "EPSG", + 7043, + "WGS 72", + string.Empty, + string.Empty); + } + } + + /// + /// Gets the GRS 1980 / International 1979 ellipsoid. + /// + /// + /// Adopted by IUGG 1979 Canberra. + /// Inverse flattening is derived from + /// geocentric gravitational constant GM = 3986005e8 m*m*m/s/s; + /// dynamic form factor J2 = 108263e8 and Earth's angular velocity = 7292115e-11 rad/s."). + /// + public static Ellipsoid GRS80 + { + get + { + return new Ellipsoid( + 6378137, + 0, + 298.257222101, + true, + LinearUnit.Metre, + "GRS 1980", + "EPSG", + 7019, + "International 1979", + string.Empty, + "Adopted by IUGG 1979 Canberra. Inverse flattening is derived from geocentric gravitational constant GM = 3986005e8 m*m*m/s/s; dynamic form factor J2 = 108263e8 and Earth's angular velocity = 7292115e-11 rad/s."); + } + } + + /// + /// Gets the International 1924 / Hayford 1909 ellipsoid. + /// + /// + /// Described as a=6378388 m. and b=6356909m. from which 1/f derived to be 296.95926. + /// The figure was adopted as the International ellipsoid in 1924 but with 1/f taken as + /// 297 exactly from which b is derived as 6356911.946m. + /// + public static Ellipsoid International1924 + { + get + { + return new Ellipsoid( + 6378388, + 0, + 297, + true, + LinearUnit.Metre, + "International 1924", + "EPSG", + 7022, + "Hayford 1909", + string.Empty, + "Described as a=6378388 m. and b=6356909 m. from which 1/f derived to be 296.95926. The figure was adopted as the International ellipsoid in 1924 but with 1/f taken as 297 exactly from which b is derived as 6356911.946m."); + } + } + + /// + /// Gets the Bessel 1841 ellipsoid. /// - [Serializable] - public class Ellipsoid : Info - { - /// - /// Initializes a new instance of an Ellipsoid - /// - /// Semi major axis - /// Semi minor axis - /// Inverse flattening - /// Inverse Flattening is definitive for this ellipsoid (Semi-minor axis will be overridden) - /// Axis unit - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal Ellipsoid( - double semiMajorAxis, - double semiMinorAxis, - double inverseFlattening, - bool isIvfDefinitive, - LinearUnit axisUnit, string name, string authority, long code, string alias, - string abbreviation, string remarks) - : base(name, authority, code, alias, abbreviation, remarks) - { - SemiMajorAxis = semiMajorAxis; - InverseFlattening = inverseFlattening; - AxisUnit = axisUnit; - IsIvfDefinitive = isIvfDefinitive; - if (isIvfDefinitive && (inverseFlattening == 0 || double.IsInfinity(inverseFlattening))) - SemiMinorAxis = semiMajorAxis; - else if (isIvfDefinitive) - SemiMinorAxis = (1.0 - (1.0 / InverseFlattening)) * semiMajorAxis; - else - SemiMinorAxis = semiMinorAxis; - } - - #region Predefined ellipsoids - /// - /// WGS 84 ellipsoid - /// - /// - /// Inverse flattening derived from four defining parameters - /// (semi-major axis; - /// C20 = -484.16685*10e-6; - /// earth's angular velocity w = 7292115e11 rad/sec; - /// gravitational constant GM = 3986005e8 m*m*m/s/s). - /// - public static Ellipsoid WGS84 - { - get - { - return new Ellipsoid(6378137, 0, 298.257223563, true, LinearUnit.Metre, "WGS 84", "EPSG", 7030, "WGS84", "", - "Inverse flattening derived from four defining parameters (semi-major axis; C20 = -484.16685*10e-6; earth's angular velocity w = 7292115e11 rad/sec; gravitational constant GM = 3986005e8 m*m*m/s/s)."); - } - } - - /// - /// WGS 72 Ellipsoid - /// - public static Ellipsoid WGS72 - { - get - { - return new Ellipsoid(6378135.0, 0, 298.26, true, LinearUnit.Metre, "WGS 72", "EPSG", 7043, "WGS 72", string.Empty, string.Empty); - } - } - - /// - /// GRS 1980 / International 1979 ellipsoid - /// - /// - /// Adopted by IUGG 1979 Canberra. - /// Inverse flattening is derived from - /// geocentric gravitational constant GM = 3986005e8 m*m*m/s/s; - /// dynamic form factor J2 = 108263e8 and Earth's angular velocity = 7292115e-11 rad/s.") - /// - public static Ellipsoid GRS80 - { - get - { - return new Ellipsoid(6378137, 0, 298.257222101, true, LinearUnit.Metre, "GRS 1980", "EPSG", 7019, "International 1979", "", - "Adopted by IUGG 1979 Canberra. Inverse flattening is derived from geocentric gravitational constant GM = 3986005e8 m*m*m/s/s; dynamic form factor J2 = 108263e8 and Earth's angular velocity = 7292115e-11 rad/s."); - } - } - - /// - /// International 1924 / Hayford 1909 ellipsoid - /// - /// - /// Described as a=6378388 m. and b=6356909m. from which 1/f derived to be 296.95926. - /// The figure was adopted as the International ellipsoid in 1924 but with 1/f taken as - /// 297 exactly from which b is derived as 6356911.946m. - /// - public static Ellipsoid International1924 - { - get - { - return new Ellipsoid(6378388, 0, 297, true, LinearUnit.Metre, "International 1924", "EPSG", 7022, "Hayford 1909", string.Empty, - "Described as a=6378388 m. and b=6356909 m. from which 1/f derived to be 296.95926. The figure was adopted as the International ellipsoid in 1924 but with 1/f taken as 297 exactly from which b is derived as 6356911.946m."); - } - } - - /// - /// Clarke 1880 - /// - /// - /// Clarke gave a and b and also 1/f=293.465 (to 3 decimal places). 1/f derived from a and b = 293.4663077 - /// - public static Ellipsoid Clarke1880 - { - get - { - return new Ellipsoid(20926202, 0, 297, true, LinearUnit.ClarkesFoot, "Clarke 1880", "EPSG", 7034, "Clarke 1880", string.Empty, - "Clarke gave a and b and also 1/f=293.465 (to 3 decimal places). 1/f derived from a and b = 293.4663077"); - } - } - - /// - /// Clarke 1866 - /// - /// - /// Original definition a=20926062 and b=20855121 (British) feet. Uses Clarke's 1865 inch-metre ratio of 39.370432 to obtain metres. (Metric value then converted to US survey feet for use in the United States using 39.37 exactly giving a=20925832.16 ft US). - /// - public static Ellipsoid Clarke1866 - { - get - { - return new Ellipsoid(6378206.4, 6356583.8, double.PositiveInfinity, false, LinearUnit.Metre, "Clarke 1866", "EPSG", 7008, "Clarke 1866", string.Empty, - "Original definition a=20926062 and b=20855121 (British) feet. Uses Clarke's 1865 inch-metre ratio of 39.370432 to obtain metres. (Metric value then converted to US survey feet for use in the United States using 39.37 exactly giving a=20925832.16 ft US)."); - } - } - - /// - /// Sphere - /// - /// - /// Authalic sphere derived from GRS 1980 ellipsoid (code 7019). (An authalic sphere is - /// one with a surface area equal to the surface area of the ellipsoid). 1/f is infinite. - /// - public static Ellipsoid Sphere - { - get - { - return new Ellipsoid(6370997.0, 6370997.0, double.PositiveInfinity, false, LinearUnit.Metre, "GRS 1980 Authalic Sphere", "EPSG", 7048, "Sphere", "", - "Authalic sphere derived from GRS 1980 ellipsoid (code 7019). (An authalic sphere is one with a surface area equal to the surface area of the ellipsoid). 1/f is infinite."); - } - } - #endregion - - #region IEllipsoid Members - - /// - /// Gets or sets the value of the semi-major axis. - /// - public double SemiMajorAxis { get; set; } - - /// - /// Gets or sets the value of the semi-minor axis. - /// - public double SemiMinorAxis { get; set; } - - /// - /// Gets or sets the value of the inverse of the flattening constant of the ellipsoid. - /// - public double InverseFlattening { get; set; } - - /// - /// Gets or sets the value of the axis unit. - /// - public LinearUnit AxisUnit { get; set; } - - /// - /// Tells if the Inverse Flattening is definitive for this ellipsoid. Some ellipsoids use - /// the IVF as the defining value, and calculate the polar radius whenever asked. Other - /// ellipsoids use the polar radius to calculate the IVF whenever asked. This - /// distinction can be important to avoid floating-point rounding errors. - /// - public bool IsIvfDefinitive { get; set; } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, "SPHEROID[\"{0}\", {1}, {2}", Name, SemiMajorAxis, InverseFlattening); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, - "{4}{5}", - SemiMajorAxis, SemiMinorAxis, InverseFlattening, (IsIvfDefinitive ? 1 : 0), InfoXml, AxisUnit.XML); ; - } - } - - #endregion - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is Ellipsoid)) - return false; - var e = obj as Ellipsoid; - return (e.InverseFlattening == this.InverseFlattening && - e.IsIvfDefinitive == this.IsIvfDefinitive && - e.SemiMajorAxis == this.SemiMajorAxis && - e.SemiMinorAxis == this.SemiMinorAxis && - e.AxisUnit.EqualParams(this.AxisUnit)); - } - } + public static Ellipsoid Bessel1841 + { + get + { + return new Ellipsoid( + 6377397.155d, + 0d, + 299.1528128d, + true, + LinearUnit.Metre, + "Bessel 1841", + "EPSG", + 7004, + string.Empty, + string.Empty, + string.Empty); + } + } + + /// + /// Gets the Clarke 1880 ellipsoid. + /// + /// + /// Clarke gave a and b and also 1/f=293.465 (to 3 decimal places). 1/f derived from a and b = 293.4663077. + /// + public static Ellipsoid Clarke1880 + { + get + { + return new Ellipsoid( + 20926202, + 0, + 297, + true, + LinearUnit.ClarkesFoot, + "Clarke 1880", + "EPSG", + 7034, + "Clarke 1880", + string.Empty, + "Clarke gave a and b and also 1/f=293.465 (to 3 decimal places). 1/f derived from a and b = 293.4663077�"); + } + } + + /// + /// Gets the Clarke 1866 ellipsoid. + /// + /// + /// Original definition a=20926062 and b=20855121 (British) feet. Uses Clarke's 1865 inch-metre ratio of 39.370432 to obtain metres. (Metric value then converted to US survey feet for use in the United States using 39.37 exactly giving a=20925832.16 ft US). + /// + public static Ellipsoid Clarke1866 + { + get + { + return new Ellipsoid( + 6378206.4, + 6356583.8, + double.PositiveInfinity, + false, + LinearUnit.Metre, + "Clarke 1866", + "EPSG", + 7008, + "Clarke 1866", + string.Empty, + "Original definition a=20926062 and b=20855121 (British) feet. Uses Clarke's 1865 inch-metre ratio of 39.370432 to obtain metres. (Metric value then converted to US survey feet for use in the United States using 39.37 exactly giving a=20925832.16 ft US)."); + } + } + + /// + /// Gets the GRS 1980 Authalic Sphere. + /// + /// + /// Authalic sphere derived from GRS 1980 ellipsoid (code 7019). (An authalic sphere is + /// one with a surface area equal to the surface area of the ellipsoid). 1/f is infinite. + /// + public static Ellipsoid Sphere + { + get + { + return new Ellipsoid( + 6370997.0, + 6370997.0, + double.PositiveInfinity, + false, + LinearUnit.Metre, + "GRS 1980 Authalic Sphere", + "EPSG", + 7048, + "Sphere", + string.Empty, + "Authalic sphere derived from GRS 1980 ellipsoid (code 7019). (An authalic sphere is one with a surface area equal to the surface area of the ellipsoid). 1/f is infinite."); + } + } + + /// + /// Gets the value of the semi-major axis. + /// + public double SemiMajorAxis { get; } + + /// + /// Gets the value of the semi-minor axis. + /// + public double SemiMinorAxis { get; } + + /// + /// Gets the value of the inverse of the flattening constant of the ellipsoid. + /// + public double InverseFlattening { get; } + + /// + /// Gets the value of the axis unit. + /// + public LinearUnit AxisUnit { get; } + + /// + /// Gets a value indicating whether the inverse flattening value is the defining parameter for this ellipsoid. + /// + /// + /// When , the semi-minor axis is derived from the inverse flattening value. + /// When , the inverse flattening is derived from the semi-minor axis. + /// This distinction can be important to avoid floating-point rounding errors. + /// + public bool IsIvfDefinitive { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this ellipsoid with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new Ellipsoid WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this ellipsoid with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new Ellipsoid WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this ellipsoid as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_Ellipsoid", + new XAttribute("SemiMajorAxis", this.SemiMajorAxis.ToString(CultureInfo.InvariantCulture)), + new XAttribute("SemiMinorAxis", this.SemiMinorAxis.ToString(CultureInfo.InvariantCulture)), + new XAttribute("InverseFlattening", this.InverseFlattening.ToString(CultureInfo.InvariantCulture)), + new XAttribute("IvfDefinitive", this.IsIvfDefinitive ? "1" : "0")); + element.Add(this.InfoXmlElement); + element.Add(this.AxisUnit.ToXml()); + return element; + } + + /// + /// Converts this ellipsoid to a WKT syntax tree node. + /// + /// A representing this ellipsoid. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.SemiMajorAxis), + new WktNumber(this.InverseFlattening), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("SPHEROID", children); + } + + /// + /// Converts this ellipsoid to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this ellipsoid in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.SemiMajorAxis), + new WktNumber(this.InverseFlattening), + this.AxisUnit.ToWktNode(version), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("ELLIPSOID", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is Ellipsoid ellipsoid && ellipsoid.InverseFlattening == this.InverseFlattening && + ellipsoid.IsIvfDefinitive == this.IsIvfDefinitive && + ellipsoid.SemiMajorAxis == this.SemiMajorAxis && + ellipsoid.SemiMinorAxis == this.SemiMinorAxis && + ellipsoid.AxisUnit.EqualParams(this.AxisUnit); + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); } diff --git a/src/ProjNet/CoordinateSystems/EngineeringCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/EngineeringCoordinateSystem.cs new file mode 100644 index 00000000..33bcfdf9 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/EngineeringCoordinateSystem.cs @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A coordinate system for local engineering reference frames. +/// +public sealed class EngineeringCoordinateSystem : CoordinateSystem +{ + private readonly List units; + + /// + /// Initializes a new instance of the class. + /// + /// Engineering datum. + /// Coordinate system type from the WKT2 CS block. + /// Axis definitions. + /// Axis units. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public EngineeringCoordinateSystem( + EngineeringDatum engineeringDatum, + string coordinateSystemType, + IReadOnlyList axisInfo, + IReadOnlyList units, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks, CreateAxisInfo(axisInfo), null) + { + this.EngineeringDatum = ArgumentGuard.ThrowIfNull(engineeringDatum, nameof(engineeringDatum)); + this.CoordinateSystemType = string.IsNullOrWhiteSpace(coordinateSystemType) + ? ArgumentGuard.ThrowArgument("Engineering coordinate systems require a CS type.", nameof(coordinateSystemType)) + : coordinateSystemType; + + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + units = ArgumentGuard.ThrowIfNull(units, nameof(units)); + if (axisInfo.Count == 0) + { + ArgumentGuard.ThrowArgument("Engineering coordinate systems require at least one axis.", nameof(axisInfo)); + } + + if (axisInfo.Count != units.Count) + { + ArgumentGuard.ThrowArgument("Engineering coordinate system axes and units must have the same length.", nameof(units)); + } + + this.units = units.Select(unit => ArgumentGuard.ThrowIfNull(unit, nameof(units))).ToList(); + } + + /// + /// Gets the engineering datum. + /// + public EngineeringDatum EngineeringDatum { get; } + + /// + /// Gets the WKT2 coordinate system type from the CS block. + /// + public string CoordinateSystemType { get; } + + /// + /// Gets the units for each engineering axis. + /// + public IReadOnlyList AxisUnits => this.units; + + /// + public override string WKT => this.ToWktNode(WktVersion.Wkt22019).ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new EngineeringCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new EngineeringCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + public override XElement ToXml() + { + var innerElement = new XElement( + "CS_EngineeringCoordinateSystem", + new XAttribute("CoordinateSystemType", this.CoordinateSystemType)); + innerElement.Add(this.InfoXmlElement); + foreach (AxisInfo axis in this.AxisInfo) + { + innerElement.Add(axis.ToXml()); + } + + innerElement.Add(this.EngineeringDatum.ToXml()); + foreach (IUnit unit in this.units) + { + innerElement.Add(CreateUnitXml(unit)); + } + + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override IUnit GetUnits(int dimension) + { + if (dimension < 0 || dimension >= this.units.Count) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(dimension), "Engineering coordinate system dimension is out of range."); + } + + return this.units[dimension]; + } + + /// + public override WktNode ToWktNode() + { + return this.ToWktNode(WktVersion.Wkt22019); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.CreateLegacyWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.EngineeringDatum.ToWktNode(version), + new WktKeywordNode( + "CS", + new WktIdentifier(this.CoordinateSystemType), + new WktInteger(this.Dimension)), + }; + + bool shareUnit = this.units.Count > 0 && this.units.All(unit => UnitsEqual(this.units[0], unit)); + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(shareUnit + ? this.AxisInfo[i].ToWktNode(version) + : CreateAxisNodeWithUnit(this.AxisInfo[i], this.units[i])); + } + + if (shareUnit) + { + children.Add(CreateWkt2UnitNode(this.units[0])); + } + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("ENGCRS", children); + } + + /// + public override bool EqualParams(object obj) + { + if (obj is not EngineeringCoordinateSystem engineeringCoordinateSystem) + { + return false; + } + + if (!this.EngineeringDatum.EqualParams(engineeringCoordinateSystem.EngineeringDatum) + || !string.Equals(this.CoordinateSystemType, engineeringCoordinateSystem.CoordinateSystemType, StringComparison.OrdinalIgnoreCase) + || this.Dimension != engineeringCoordinateSystem.Dimension + || this.units.Count != engineeringCoordinateSystem.units.Count) + { + return false; + } + + for (int i = 0; i < this.Dimension; i++) + { + if (this.GetAxis(i).Orientation != engineeringCoordinateSystem.GetAxis(i).Orientation + || !this.units[i].EqualParams(engineeringCoordinateSystem.units[i])) + { + return false; + } + } + + return true; + } + + private static bool UnitsEqual(IUnit left, IUnit right) + { + return left.GetType() == right.GetType() && left.EqualParams(right); + } + + private static List CreateAxisInfo(IReadOnlyList axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + if (axisInfo.Count == 0) + { + ArgumentGuard.ThrowArgument("Engineering coordinate systems require at least one axis.", nameof(axisInfo)); + } + + return axisInfo.Select(axis => new AxisInfo(axis)).ToList(); + } + + private static XElement CreateUnitXml(IUnit unit) + { + return unit switch + { + AngularUnit angularUnit => angularUnit.ToXml(), + LinearUnit linearUnit => linearUnit.ToXml(), + TimeUnit timeUnit => timeUnit.ToXml(), + ParametricUnit parametricUnit => parametricUnit.ToXml(), + Unit genericUnit => genericUnit.ToXml(), + _ => throw new NotSupportedException($"Engineering coordinate system XML output does not support unit type '{unit.GetType().Name}'."), + }; + } + + private static WktNode CreateWkt2UnitNode(IUnit unit) + { + return unit switch + { + AngularUnit angularUnit => angularUnit.ToWktNode(WktVersion.Wkt22019), + LinearUnit linearUnit => linearUnit.ToWktNode(WktVersion.Wkt22019), + TimeUnit timeUnit => timeUnit.ToWktNode(WktVersion.Wkt22019), + ParametricUnit parametricUnit => parametricUnit.ToWktNode(WktVersion.Wkt22019), + Unit genericUnit => new WktKeywordNode( + "SCALEUNIT", + new WktQuotedString(genericUnit.Name), + new WktNumber(genericUnit.ConversionFactor)), + _ => throw new NotSupportedException($"Engineering coordinate system WKT2 output does not support unit type '{unit.GetType().Name}'."), + }; + } + + private static WktKeywordNode CreateAxisNodeWithUnit(AxisInfo axisInfo, IUnit unit) + { + return new WktKeywordNode( + "AXIS", + new WktQuotedString(axisInfo.Name), + new WktIdentifier(GetOrientationIdentifier(axisInfo.Orientation)), + CreateWkt2UnitNode(unit)); + } + + private static string GetOrientationIdentifier(AxisOrientationEnum orientation) + { + return orientation switch + { + AxisOrientationEnum.North => "north", + AxisOrientationEnum.South => "south", + AxisOrientationEnum.East => "east", + AxisOrientationEnum.West => "west", + AxisOrientationEnum.Up => "up", + AxisOrientationEnum.Down => "down", + _ => "other", + }; + } + + private static WktKeywordNode CreateLegacyUnitNode(Unit unit) + { + var children = new List + { + new WktQuotedString(unit.Name), + new WktNumber(unit.ConversionFactor), + }; + + if (!string.IsNullOrWhiteSpace(unit.Authority) && unit.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(unit.Authority), + new WktQuotedString(unit.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("UNIT", children); + } + + private WktKeywordNode CreateLegacyWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + this.EngineeringDatum.ToWktNode(), + this.units[0] switch + { + AngularUnit angularUnit => angularUnit.ToWktNode(), + LinearUnit linearUnit => linearUnit.ToWktNode(), + TimeUnit timeUnit => timeUnit.ToWktNode(), + ParametricUnit parametricUnit => parametricUnit.ToWktNode(), + Unit genericUnit => CreateLegacyUnitNode(genericUnit), + _ => throw new NotSupportedException($"Engineering coordinate system legacy WKT output does not support unit type '{this.units[0].GetType().Name}'."), + }, + }; + + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.AxisInfo[i].ToWktNode()); + } + + return new WktKeywordNode("LOCAL_CS", children); + } +} diff --git a/src/ProjNet/CoordinateSystems/EngineeringDatum.cs b/src/ProjNet/CoordinateSystems/EngineeringDatum.cs new file mode 100644 index 00000000..5506babd --- /dev/null +++ b/src/ProjNet/CoordinateSystems/EngineeringDatum.cs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A local engineering datum used by engineering coordinate reference systems. +/// +public sealed class EngineeringDatum : Datum +{ + /// + /// Initializes a new instance of the class. + /// + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Provider-supplied remarks. + /// Abbreviation. + public EngineeringDatum(string name, string authority, long authorityCode, string alias, string remarks, string abbreviation) + : base(DatumType.LD_Engineering, name, authority, authorityCode, alias, remarks, abbreviation) + { + } + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this datum with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new EngineeringDatum WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this datum with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new EngineeringDatum WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Creates a copy of this datum with updated retained datum-ensemble metadata. + /// + /// Replacement ensemble metadata, or to keep this datum non-ensemble-backed. + /// A new with updated ensemble metadata. + /// Thrown when is not . + public new EngineeringDatum WithEnsemble(DatumEnsemble? ensemble) => InfoAuthorityCloneHelper.CloneWithEnsemble(this, ensemble); + + /// + /// Returns an XML representation of this engineering datum as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_EngineeringDatum", + new XAttribute("DatumType", ((int)this.DatumType).ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this engineering datum to a WKT syntax tree node. + /// + /// A representing this engineering datum. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktInteger((int)this.DatumType), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("LOCAL_DATUM", children); + } + + /// + /// Converts this engineering datum to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this engineering datum in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("EDATUM", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is EngineeringDatum engineeringDatum && base.EqualParams(engineeringDatum); + } +} diff --git a/src/ProjNet/CoordinateSystems/FittedCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/FittedCoordinateSystem.cs index 0962dabe..fbc09115 100644 --- a/src/ProjNet/CoordinateSystems/FittedCoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/FittedCoordinateSystem.cs @@ -1,154 +1,317 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; -using System.Globalization; -using System.Text; +using System.Xml.Linq; using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A coordinate system which sits inside another coordinate system. The fitted +/// coordinate system can be rotated and shifted, or use any other math transform +/// to inject itself into the base coordinate system. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// +/// +public class FittedCoordinateSystem : CoordinateSystem // , IFittedCoordinateSystem { /// - /// A coordinate system which sits inside another coordinate system. The fitted - /// coordinate system can be rotated and shifted, or use any other math transform - /// to inject itself into the base coordinate system. + /// Initializes a new instance of the class. /// - [Serializable] - public class FittedCoordinateSystem : CoordinateSystem //, IFittedCoordinateSystem + /// Underlying coordinate system. + /// Transformation from fitted coordinate system to the base one. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + /// Optional axis definitions for the fitted system. + protected internal FittedCoordinateSystem( + CoordinateSystem baseSystem, + MathTransform transform, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation, + IReadOnlyList? axisInfo = null) + : base(name, authority, code, alias, abbreviation, remarks, CreateAxisInfo(baseSystem, axisInfo, name), null) { - /// - /// Creates an instance of FittedCoordinateSystem using the specified parameters - /// - /// Underlying coordinate system. - /// Transformation from fitted coordinate system to the base one - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - protected internal FittedCoordinateSystem (CoordinateSystem baseSystem, MathTransform transform, - string name, string authority, long code, string alias, string remarks, string abbreviation) - : base(name, authority, code, alias, abbreviation, remarks) - { - BaseCoordinateSystem = baseSystem; - ToBaseTransform = transform; - //get axis infos from the source - base.AxisInfo = new List (baseSystem.Dimension); - for (int dim = 0; dim < baseSystem.Dimension; dim++) - { - base.AxisInfo.Add (baseSystem.GetAxis (dim)); - } - } + this.BaseCoordinateSystem = ArgumentGuard.ThrowIfNull(baseSystem, nameof(baseSystem)); + this.ToBaseTransform = ArgumentGuard.ThrowIfNull(transform, nameof(transform)); + } - #region public properties + /// + /// Gets the math transform that maps this fitted coordinate system into the base coordinate system. + /// + public MathTransform ToBaseTransform { get; } + /// + /// Gets underlying coordinate system. + /// + public CoordinateSystem BaseCoordinateSystem { get; } + + /// + /// Gets the Well-known text for this object as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); - /// - /// Represents math transform that injects itself into the base coordinate system. - /// - public MathTransform ToBaseTransform { get; } - #endregion public properties + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); - #region FittedCoordinateSystem Members + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new FittedCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); - /// - /// Gets underlying coordinate system. - /// - public CoordinateSystem BaseCoordinateSystem { get; } + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new FittedCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); - /// - /// Gets Well-Known Text of a math transform to the base coordinate system. - /// The dimension of this fitted coordinate system is determined by the source - /// dimension of the math transform. The transform should be one-to-one within - /// this coordinate system's domain, and the base coordinate system dimension - /// must be at least as big as the dimension of this coordinate system. - /// - /// - public string ToBase () - { - return ToBaseTransform.WKT; - } + /// + /// Returns an XML representation of this fitted coordinate system as an . + /// + /// No value is returned because XML serialization is not supported for fitted coordinate systems. + /// Always thrown because XML serialization is not supported for fitted coordinate systems. + public override XElement ToXml() => throw new NotSupportedException("XML serialization is not supported for fitted coordinate systems."); - #endregion + /// + public override WktNode ToWktNode() + { + return new WktKeywordNode( + "FITTED_CS", + new WktQuotedString(this.Name), + new WktIdentifier(this.ToBaseTransform.WKT), + this.BaseCoordinateSystem.ToWktNode()); + } - #region ICoordinateSystem Members + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + return version == WktVersion.Wkt1 + ? this.ToWktNode() + : this.CreateWkt2Node(); + } - /// - /// Returns the Well-known text for this object as defined in the simple features specification. - /// - public override string WKT + /// + /// Gets the Well-Known Text of the math transform to the base coordinate system. + /// + /// + /// The dimension of this fitted coordinate system is determined by the source + /// dimension of the math transform. The transform must be one-to-one within + /// this coordinate system's domain, and the base coordinate system dimension + /// must be at least as large as the dimension of this coordinate system. + /// + /// The WKT string of the transform to the base coordinate system. + public string ToBase() => this.ToBaseTransform.WKT; + + /// + public override bool EqualParams(object obj) + { + var fcs = obj as FittedCoordinateSystem; + if (fcs is not null) { - get + if (fcs.Dimension != this.Dimension) { - // = FITTED_CS["", , ] + return false; + } - var sb = new StringBuilder(); - sb.AppendFormat ("FITTED_CS[\"{0}\", {1}, {2}]", Name, this.ToBaseTransform.WKT, this.BaseCoordinateSystem.WKT); - return sb.ToString(); + for (int i = 0; i < fcs.Dimension; i++) + { + if (fcs.GetAxis(i).Orientation != this.GetAxis(i).Orientation) + { + return false; + } + + if (!fcs.GetUnits(i).EqualParams(this.GetUnits(i))) + { + return false; + } + } + + if (fcs.BaseCoordinateSystem.EqualParams(this.BaseCoordinateSystem)) + { + string fcsToBase = fcs.ToBase(); + string thisToBase = this.ToBase(); + if (string.Equals(fcsToBase, thisToBase, StringComparison.Ordinal)) + { + return true; + } } } - /// - /// Gets an XML representation of this object. - /// - public override string XML + return false; + } + + /// + public override IUnit GetUnits(int dimension) => this.BaseCoordinateSystem.GetUnits(dimension); + + private static List CreateAxisInfo(CoordinateSystem baseSystem, IReadOnlyList? axisInfo, string name) + { + baseSystem = ArgumentGuard.ThrowIfNull(baseSystem, nameof(baseSystem)); + if (axisInfo is null || axisInfo.Count == 0) { - get + var clonedAxisInfo = new List(baseSystem.Dimension); + for (int dim = 0; dim < baseSystem.Dimension; dim++) { - throw new NotImplementedException (); + clonedAxisInfo.Add(baseSystem.GetAxis(dim)); } + + return clonedAxisInfo; + } + + if (axisInfo.Count != baseSystem.Dimension) + { + ArgumentGuard.ThrowArgument($"Fitted coordinate system '{name}' expects {baseSystem.Dimension} axes but received {axisInfo.Count}.", nameof(axisInfo)); } - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams (object obj) - { - var fcs = obj as FittedCoordinateSystem; - if (fcs != null) + var explicitAxisInfo = new List(axisInfo.Count); + foreach (AxisInfo axis in axisInfo) + { + explicitAxisInfo.Add(new AxisInfo(axis)); + } + + return explicitAxisInfo; + } + + private static WktKeywordNode CreateWkt2DerivingConversionNode(IProjection derivingConversion, WktNode translationUnitNode) + { + static WktKeywordNode CreateWkt2DerivingParameterNode(ProjectionParameter parameter, WktNode translationUnitNode) + { + var children = new List { - if (fcs.BaseCoordinateSystem.EqualParams (this.BaseCoordinateSystem)) - { - string fcsToBase = fcs.ToBase (); - string thisToBase = this.ToBase (); - if (string.Equals (fcsToBase, thisToBase)) - { - return true; - } - } + new WktQuotedString(parameter.Name), + new WktNumber(parameter.Value), + }; + + if (parameter.Name is "A0" or "B0") + { + children.Add(translationUnitNode); + } + else + { + children.Add(new WktKeywordNode( + "SCALEUNIT", + new WktQuotedString("unity"), + new WktNumber(1))); } - return false; + + return new WktKeywordNode("PARAMETER", children); + } + + string conversionName = string.IsNullOrWhiteSpace(derivingConversion.Name) + ? DerivedCoordinateSystemSupport.DefaultDerivingConversionName + : derivingConversion.Name; + var children = new List + { + new WktQuotedString(conversionName), + new WktKeywordNode( + "METHOD", + new WktQuotedString(derivingConversion.ClassName)), + }; + + for (int i = 0; i < derivingConversion.NumParameters; i++) + { + children.Add(CreateWkt2DerivingParameterNode(derivingConversion.GetParameter(i), translationUnitNode)); + } + + return new WktKeywordNode("DERIVINGCONVERSION", children); + } + + private WktKeywordNode CreateWkt2Node() + { + Projection derivingConversion = DerivedCoordinateSystemSupport.CreateAffineConversion(this.ToBaseTransform, DerivedCoordinateSystemSupport.DefaultDerivingConversionName); + return this.BaseCoordinateSystem switch + { + GeographicCoordinateSystem geographicCoordinateSystem => this.CreateWkt2DerivedGeographicNode(geographicCoordinateSystem, derivingConversion), + ProjectedCoordinateSystem projectedCoordinateSystem => this.CreateWkt2DerivedProjectedNode(projectedCoordinateSystem, derivingConversion), + _ => throw new NotSupportedException("WKT2 output for fitted coordinate systems currently supports only affine transforms based on two-dimensional geographic or projected coordinate systems."), + }; + } + + private WktKeywordNode CreateWkt2DerivedGeographicNode(GeographicCoordinateSystem baseCoordinateSystem, IProjection derivingConversion) + { + if (this.Dimension != 2 || this.AxisInfo.Count != this.Dimension) + { + throw new NotSupportedException("WKT2 output for fitted geographic coordinate systems currently supports only two axes."); + } + + var children = new List + { + new WktQuotedString(this.Name), + baseCoordinateSystem.CreateWkt2BaseNode("BASEGEOGCRS"), + CreateWkt2DerivingConversionNode(derivingConversion, baseCoordinateSystem.AngularUnit.ToWktNode(WktVersion.Wkt22019)), + new WktKeywordNode( + "CS", + new WktIdentifier("ellipsoidal"), + new WktInteger(this.Dimension)), + }; + + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode(WktVersion.Wkt22019)); + } + + children.Add(baseCoordinateSystem.AngularUnit.ToWktNode(WktVersion.Wkt22019)); + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); } - /// - /// Gets the units for the dimension within coordinate system. - /// Each dimension in the coordinate system has corresponding units. - /// - public override IUnit GetUnits(int dimension) + return new WktKeywordNode("GEOGCRS", children); + } + + private WktKeywordNode CreateWkt2DerivedProjectedNode(ProjectedCoordinateSystem baseCoordinateSystem, IProjection derivingConversion) + { + if (this.Dimension != 2 || this.AxisInfo.Count != this.Dimension) + { + throw new NotSupportedException("WKT2 output for fitted projected coordinate systems currently supports only two axes."); + } + + var children = new List + { + new WktQuotedString(this.Name), + baseCoordinateSystem.CreateWkt2BaseNode("BASEPROJCRS"), + CreateWkt2DerivingConversionNode(derivingConversion, baseCoordinateSystem.LinearUnit.ToWktNode(WktVersion.Wkt22019)), + new WktKeywordNode( + "CS", + new WktIdentifier("Cartesian"), + new WktInteger(this.Dimension)), + }; + + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode(WktVersion.Wkt22019)); + } + + children.Add(baseCoordinateSystem.LinearUnit.ToWktNode(WktVersion.Wkt22019)); + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) { - return BaseCoordinateSystem.GetUnits (dimension); + children.Add(idNode); } - #endregion + return new WktKeywordNode("DERIVEDPROJCRS", children); } } diff --git a/src/ProjNet/CoordinateSystems/GeocentricCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/GeocentricCoordinateSystem.cs index 2acb2253..499ef2b5 100644 --- a/src/ProjNet/CoordinateSystems/GeocentricCoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/GeocentricCoordinateSystem.cs @@ -1,153 +1,256 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; using System.Globalization; -using System.Runtime.CompilerServices; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A 3D coordinate system, with its origin at the center of the Earth. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// is initialized once and then reused safely; after that one-time path, +/// callers do not contend on additional locks when reading the property. +/// +/// +public class GeocentricCoordinateSystem : CoordinateSystem { - /// - /// A 3D coordinate system, with its origin at the center of the Earth. + private static readonly Lazy Wgs84CoordinateSystem = + new(CreateWgs84CoordinateSystem, true); + + /// + /// Initializes a new instance of the class. + /// + /// Horizontal datum used by this coordinate system. + /// Linear unit applied to all axes. + /// Prime meridian used for longitude reference. + /// Axis definition list (must contain 3 axes). + /// Coordinate system name. + /// Authority name. + /// Authority code. + /// Alias name. + /// Additional remarks. + /// Abbreviation. + /// Default envelope for the coordinate system domain. + internal GeocentricCoordinateSystem( + HorizontalDatum datum, + LinearUnit linearUnit, + PrimeMeridian primeMeridian, + List axisInfo, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation, + double[]? defaultEnvelope = null) + : base(name, authority, code, alias, abbreviation, remarks, ValidateAxisInfo(axisInfo), defaultEnvelope) + { + this.HorizontalDatum = ArgumentGuard.ThrowIfNull(datum, nameof(datum)); + this.LinearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + this.PrimeMeridian = ArgumentGuard.ThrowIfNull(primeMeridian, nameof(primeMeridian)); + } + + /// + /// Gets a geocentric coordinate system based on the WGS84 ellipsoid, suitable for GPS measurements. + /// + public static GeocentricCoordinateSystem WGS84 => Wgs84CoordinateSystem.Value; + + /// + /// Gets the HorizontalDatum. The horizontal datum is used to determine where + /// the centre of the Earth is considered to be. All coordinate points will be + /// measured from the centre of the Earth, and not the surface. /// - [Serializable] - public class GeocentricCoordinateSystem : CoordinateSystem - { - internal GeocentricCoordinateSystem(HorizontalDatum datum, LinearUnit linearUnit, PrimeMeridian primeMeridian, List axisInfo, - string name, string authority, long code, string alias, - string remarks, string abbreviation) - : base(name, authority, code, alias, abbreviation, remarks) - { - HorizontalDatum = datum; - LinearUnit = linearUnit; - PrimeMeridian = primeMeridian; - if (axisInfo.Count != 3) - throw new ArgumentException("Axis info should contain three axes for geocentric coordinate systems"); - base.AxisInfo = axisInfo; - } - - #region Predefined geographic coordinate systems - - /// - /// Creates a geocentric coordinate system based on the WGS84 ellipsoid, suitable for GPS measurements - /// - public static GeocentricCoordinateSystem WGS84 - { - get - { - return new CoordinateSystemFactory().CreateGeocentricCoordinateSystem("WGS84 Geocentric", - HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); - } - } - - #endregion - - #region GeocentricCoordinateSystem Members - - - /// - /// Returns the HorizontalDatum. The horizontal datum is used to determine where - /// the centre of the Earth is considered to be. All coordinate points will be - /// measured from the centre of the Earth, and not the surface. - /// - public HorizontalDatum HorizontalDatum { get; set; } - - /// - /// Gets the units used along all the axes. - /// - public LinearUnit LinearUnit { get; set; } - - /// - /// Gets units for dimension within coordinate system. Each dimension in - /// the coordinate system has corresponding units. - /// - /// Dimension - /// Unit - public override IUnit GetUnits(int dimension) - { - return LinearUnit; - } - - /// - /// Returns the PrimeMeridian. - /// - public PrimeMeridian PrimeMeridian { get; set; } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat("GEOCCS[\"{0}\", {1}, {2}, {3}", Name, HorizontalDatum.WKT, PrimeMeridian.WKT, LinearUnit.WKT); - //Skip axis info if they contain default values - if (AxisInfo.Count != 3 || - AxisInfo[0].Name != "X" || AxisInfo[0].Orientation != AxisOrientationEnum.Other || - AxisInfo[1].Name != "Y" || AxisInfo[1].Orientation != AxisOrientationEnum.East || - AxisInfo[2].Name != "Z" || AxisInfo[2].Orientation != AxisOrientationEnum.North) - for (int i = 0; i < AxisInfo.Count; i++) - sb.AppendFormat(", {0}", GetAxis(i).WKT); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode>0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, - "{1}", - Dimension, InfoXml); - foreach (var ai in AxisInfo) - sb.Append(ai.XML); - sb.AppendFormat("{0}{1}{2}", - HorizontalDatum.XML, LinearUnit.XML, PrimeMeridian.XML); - return sb.ToString(); - } - } - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is GeocentricCoordinateSystem gcc)) - return false; - return gcc.HorizontalDatum.EqualParams(HorizontalDatum) && - gcc.LinearUnit.EqualParams(LinearUnit) && - gcc.PrimeMeridian.EqualParams(PrimeMeridian); - } - - #endregion - } + public HorizontalDatum HorizontalDatum { get; } + + /// + /// Gets the units used along all the axes. + /// + public LinearUnit LinearUnit { get; } + + /// + /// Gets the prime meridian used as the longitude reference for this coordinate system. + /// + public PrimeMeridian PrimeMeridian { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new GeocentricCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new GeocentricCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this geocentric coordinate system as an . + /// + /// An containing the XML representation. + public override XElement ToXml() + { + var innerElement = new XElement("CS_GeocentricCoordinateSystem"); + innerElement.Add(this.InfoXmlElement); + foreach (AxisInfo ai in this.AxisInfo) + { + innerElement.Add(ai.ToXml()); + } + + innerElement.Add(this.HorizontalDatum.ToXml()); + innerElement.Add(this.LinearUnit.ToXml()); + innerElement.Add(this.PrimeMeridian.ToXml()); + + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override IUnit GetUnits(int dimension) => this.LinearUnit; + + /// + public override WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + this.HorizontalDatum.ToWktNode(), + this.PrimeMeridian.ToWktNode(), + this.LinearUnit.ToWktNode(), + }; + + // Skip axis info if they contain default values + if (this.AxisInfo.Count != 3 || + this.AxisInfo[0].Name != "X" || this.AxisInfo[0].Orientation != AxisOrientationEnum.Other || + this.AxisInfo[1].Name != "Y" || this.AxisInfo[1].Orientation != AxisOrientationEnum.East || + this.AxisInfo[2].Name != "Z" || this.AxisInfo[2].Orientation != AxisOrientationEnum.North) + { + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode()); + } + } + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("GEOCCS", children); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + if (this.Dimension != 3) + { + throw new NotSupportedException("WKT2 GEODCRS output currently supports only three-dimensional geocentric coordinate systems."); + } + + if (this.AxisInfo.Count != this.Dimension) + { + throw new InvalidOperationException($"Geocentric coordinate system '{this.Name}' declared dimension {this.Dimension}, but provides {this.AxisInfo.Count} axes."); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.HorizontalDatum.ToWktNode(version), + }; + + if (!this.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)) + { + children.Add(this.PrimeMeridian.ToWktNode(version)); + } + + children.Add(new WktKeywordNode( + "CS", + new WktIdentifier("Cartesian"), + new WktInteger(this.Dimension))); + + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(new WktKeywordNode( + "AXIS", + new WktQuotedString(this.GetAxis(i).Name), + new WktIdentifier(i switch + { + 0 => "geocentricX", + 1 => "geocentricY", + 2 => "geocentricZ", + _ => throw new NotSupportedException("WKT2 GEODCRS output currently supports only X, Y, and Z geocentric axes."), + }))); + } + + children.Add(this.LinearUnit.ToWktNode(version)); + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("GEODCRS", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is GeocentricCoordinateSystem gcc && gcc.HorizontalDatum.EqualParams(this.HorizontalDatum) && + gcc.LinearUnit.EqualParams(this.LinearUnit) && + gcc.PrimeMeridian.EqualParams(this.PrimeMeridian); + } + + private static GeocentricCoordinateSystem CreateWgs84CoordinateSystem() + { + return Wgs84CatalogBootstrap.TryGetCoordinateSystem( + Wgs84CatalogBootstrap.Wgs84GeocentricSrid, + out GeocentricCoordinateSystem? coordinateSystem) + ? coordinateSystem + : throw new InvalidOperationException("The generated EPSG catalog could not resolve the WGS 84 geocentric coordinate system."); + } + + private static List ValidateAxisInfo(List axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + if (axisInfo.Count != 3) + { + ArgumentGuard.ThrowArgument("Axis info should contain three axes for geocentric coordinate systems", nameof(axisInfo)); + } + + return axisInfo; + } } diff --git a/src/ProjNet/CoordinateSystems/GeographicCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/GeographicCoordinateSystem.cs index 296251b9..5ed5dd11 100644 --- a/src/ProjNet/CoordinateSystems/GeographicCoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/GeographicCoordinateSystem.cs @@ -1,194 +1,386 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A coordinate system based on latitude and longitude. +/// +/// +/// +/// Some geographic coordinate systems are Lat/Lon, and some are Lon/Lat. +/// You can find out which this is by examining the axes. You should also +/// check the angular units, since not all geographic coordinate systems +/// use degrees. +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// is initialized once and then reused safely; after that one-time path, +/// callers do not contend on additional locks when reading the property. +/// +/// +public class GeographicCoordinateSystem : HorizontalCoordinateSystem { - /// - /// A coordinate system based on latitude and longitude. - /// - /// - /// Some geographic coordinate systems are Lat/Lon, and some are Lon/Lat. - /// You can find out which this is by examining the axes. You should also - /// check the angular units, since not all geographic coordinate systems - /// use degrees. - /// - [Serializable] - public class GeographicCoordinateSystem : HorizontalCoordinateSystem - { - - /// - /// Creates an instance of a Geographic Coordinate System - /// - /// Angular units - /// Horizontal datum - /// Prime meridian - /// Axis info - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal GeographicCoordinateSystem(AngularUnit angularUnit, HorizontalDatum horizontalDatum, PrimeMeridian primeMeridian, List axisInfo, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) - : - base(horizontalDatum, axisInfo, name, authority, authorityCode, alias, remarks, abbreviation) - { - AngularUnit = angularUnit; - PrimeMeridian = primeMeridian; - } - - #region Predefined geographic coordinate systems - - /// - /// Creates a decimal degrees geographic coordinate system based on the WGS84 ellipsoid, suitable for GPS measurements - /// - public static GeographicCoordinateSystem WGS84 - { - get { - var axes = new List(2); - axes.Add(new AxisInfo("Lon", AxisOrientationEnum.East)); - axes.Add(new AxisInfo("Lat", AxisOrientationEnum.North)); - return new GeographicCoordinateSystem(CoordinateSystems.AngularUnit.Degrees, - CoordinateSystems.HorizontalDatum.WGS84, CoordinateSystems.PrimeMeridian.Greenwich, axes, - "WGS 84", "EPSG", 4326, string.Empty, string.Empty, string.Empty); - } - } - - #endregion - - #region IGeographicCoordinateSystem Members - - - /// - /// Gets or sets the angular units of the geographic coordinate system. - /// - public AngularUnit AngularUnit { get; set; } - - /// - /// Gets units for dimension within coordinate system. Each dimension in - /// the coordinate system has corresponding units. - /// - /// Dimension - /// Unit - public override IUnit GetUnits(int dimension) - { - return AngularUnit; - } - - /// - /// Gets or sets the prime meridian of the geographic coordinate system. - /// - public PrimeMeridian PrimeMeridian { get; set; } - - /// - /// Gets the number of available conversions to WGS84 coordinates. - /// - public int NumConversionToWGS84 - { - get { return WGS84ConversionInfo.Count; } - } - - internal List WGS84ConversionInfo { get; set; } - - /// - /// Gets details on a conversion to WGS84. - /// - public Wgs84ConversionInfo GetWgs84ConversionInfo(int index) - { - return WGS84ConversionInfo[index]; - } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat("GEOGCS[\"{0}\", {1}, {2}, {3}",Name, HorizontalDatum.WKT, PrimeMeridian.WKT, AngularUnit.WKT); - //Skip axis info if they contain default values - if (AxisInfo.Count != 2 || - AxisInfo[0].Name != "Lon" || AxisInfo[0].Orientation != AxisOrientationEnum.East || - AxisInfo[1].Name != "Lat" || AxisInfo[1].Orientation != AxisOrientationEnum.North) - for (int i = 0; i < AxisInfo.Count; i++) - sb.AppendFormat(", {0}", GetAxis(i).WKT); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, - "{1}", - this.Dimension, InfoXml); - foreach(var ai in AxisInfo) - sb.Append(ai.XML); - sb.AppendFormat("{0}{1}{2}", - HorizontalDatum.XML, AngularUnit.XML, PrimeMeridian.XML); - return sb.ToString(); - } - } - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is GeographicCoordinateSystem gcs)) - return false; - - if (gcs.Dimension != Dimension) return false; - if (WGS84ConversionInfo != null && gcs.WGS84ConversionInfo == null) return false; - if (WGS84ConversionInfo == null && gcs.WGS84ConversionInfo != null) return false; - if (WGS84ConversionInfo != null && gcs.WGS84ConversionInfo != null) - { - if (WGS84ConversionInfo.Count != gcs.WGS84ConversionInfo.Count) return false; - for (int i = 0; i < WGS84ConversionInfo.Count; i++) - if (!gcs.WGS84ConversionInfo[i].Equals(WGS84ConversionInfo[i])) - return false; - } - if (AxisInfo.Count != gcs.AxisInfo.Count) return false; - for (int i = 0; i < gcs.AxisInfo.Count; i++) - if (gcs.AxisInfo[i].Orientation != AxisInfo[i].Orientation) - return false; - return gcs.AngularUnit.EqualParams(AngularUnit) && - gcs.HorizontalDatum.EqualParams(HorizontalDatum) && - gcs.PrimeMeridian.EqualParams(PrimeMeridian); - } - #endregion - } + private static readonly Lazy Wgs84CoordinateSystem = + new(CreateWgs84CoordinateSystem, true); + + /// + /// Initializes a new instance of the class. + /// + /// Angular units. + /// Horizontal datum. + /// Prime meridian. + /// Axis info. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + /// Default envelope for the coordinate system domain. + /// Conversion definitions to WGS84 carried by this coordinate system. + internal GeographicCoordinateSystem( + AngularUnit angularUnit, + HorizontalDatum horizontalDatum, + PrimeMeridian primeMeridian, + List axisInfo, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks, + double[]? defaultEnvelope = null, + List? wgs84ConversionInfo = null) + : base(horizontalDatum, axisInfo, name, authority, authorityCode, alias, remarks, abbreviation, defaultEnvelope) + { + this.AngularUnit = angularUnit; + this.PrimeMeridian = primeMeridian; + this.WGS84ConversionInfo = CloneWgs84ConversionInfoList(wgs84ConversionInfo); + } + + /// + /// Gets a decimal degrees geographic coordinate system based on the WGS84 ellipsoid, suitable for GPS measurements. + /// + public static GeographicCoordinateSystem WGS84 => Wgs84CoordinateSystem.Value; + + /// + /// Gets the angular units of the geographic coordinate system. + /// + public AngularUnit AngularUnit { get; } + + /// + /// Gets the prime meridian of the geographic coordinate system. + /// + public PrimeMeridian PrimeMeridian { get; } + + /// + /// Gets the number of available conversions to WGS84 coordinates. + /// + public int NumConversionToWGS84 + { + get { return this.WGS84ConversionInfo.Count; } + } + + /// + /// Gets the WGS84 conversion definitions. + /// + internal List WGS84ConversionInfo { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new GeographicCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new GeographicCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this geographic coordinate system as an . + /// + /// An containing the XML representation. + public override XElement ToXml() + { + var innerElement = new XElement("CS_GeographicCoordinateSystem"); + innerElement.Add(this.InfoXmlElement); + foreach (AxisInfo ai in this.AxisInfo) + { + innerElement.Add(ai.ToXml()); + } + + innerElement.Add(this.HorizontalDatum.ToXml()); + innerElement.Add(this.AngularUnit.ToXml()); + innerElement.Add(this.PrimeMeridian.ToXml()); + + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override IUnit GetUnits(int dimension) => this.AngularUnit; + + /// + public override WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + this.HorizontalDatum.ToWktNode(), + this.PrimeMeridian.ToWktNode(), + this.AngularUnit.ToWktNode(), + }; + + // Skip axis info if they contain default values + if (this.AxisInfo.Count != 2 || + this.AxisInfo[0].Name != "Lon" || this.AxisInfo[0].Orientation != AxisOrientationEnum.East || + this.AxisInfo[1].Name != "Lat" || this.AxisInfo[1].Orientation != AxisOrientationEnum.North) + { + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode()); + } + } + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("GEOGCS", children); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + if (this.Dimension != 2) + { + throw new NotSupportedException("WKT2 GEOGCRS output currently supports only two-dimensional geographic coordinate systems."); + } + + if (this.WGS84ConversionInfo.Count > 0 || this.HorizontalDatum.Wgs84Parameters is not null) + { + BoundCoordinateSystem boundCoordinateSystem = BoundCoordinateSystemSupport.CreateLegacyBoundCoordinateSystemForSerialization(this) + ?? throw new NotSupportedException("WKT2 GEOGCRS output for coordinate systems with WGS84 conversion parameters could not be normalized to BOUNDCRS."); + return BoundCoordinateSystemSupport.CreateWkt2BoundCoordinateSystemNode(boundCoordinateSystem); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.HorizontalDatum.ToWktNode(version), + }; + + if (!this.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)) + { + children.Add(this.PrimeMeridian.ToWktNode(version)); + } + + children.Add(new WktKeywordNode( + "CS", + new WktIdentifier("ellipsoidal"), + new WktInteger(this.Dimension))); + + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode(version)); + } + + children.Add(this.AngularUnit.ToWktNode(version)); + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("GEOGCRS", children); + } + + /// + /// Creates a WKT2 base geographic CRS node for use inside compound WKT2 coordinate-system constructs. + /// + /// The WKT2 keyword to emit, for example BASEGEOGCRS. + /// A WKT2 geographic base node without the top-level CS, AXIS, and root angle-unit blocks. + internal WktKeywordNode CreateWkt2BaseNode(string keyword) + { + if (string.IsNullOrWhiteSpace(keyword)) + { + ArgumentGuard.ThrowArgument("Invalid WKT2 base keyword.", nameof(keyword)); + } + + if (this.Dimension != 2) + { + throw new NotSupportedException("WKT2 geographic base output currently supports only two-dimensional geographic coordinate systems."); + } + + if (this.WGS84ConversionInfo.Count > 0 || this.HorizontalDatum.Wgs84Parameters is not null) + { + throw new NotSupportedException("WKT2 geographic base output for coordinate systems with WGS84 conversion parameters is not supported inside BASEGEOGCRS. Serialize the top-level CRS as BOUNDCRS instead."); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.HorizontalDatum.ToWktNode(WktVersion.Wkt22019), + }; + + if (!this.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)) + { + children.Add(this.PrimeMeridian.ToWktNode(WktVersion.Wkt22019)); + } + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode(keyword, children); + } + + /// + /// Gets details on a conversion to WGS84. + /// + /// Zero-based index of the WGS84 conversion definition. + /// The at the specified index. + public Wgs84ConversionInfo GetWgs84ConversionInfo(int index) => this.WGS84ConversionInfo[index]; + + /// + public override bool EqualParams(object obj) + { + if (obj is not GeographicCoordinateSystem gcs) + { + return false; + } + + if (gcs.Dimension != this.Dimension) + { + return false; + } + + if (this.WGS84ConversionInfo.Count != gcs.WGS84ConversionInfo.Count) + { + return false; + } + + for (int i = 0; i < this.WGS84ConversionInfo.Count; i++) + { + if (!gcs.WGS84ConversionInfo[i].Equals(this.WGS84ConversionInfo[i])) + { + return false; + } + } + + if (this.AxisInfo.Count != gcs.AxisInfo.Count) + { + return false; + } + + for (int i = 0; i < gcs.AxisInfo.Count; i++) + { + if (gcs.AxisInfo[i].Orientation != this.AxisInfo[i].Orientation) + { + return false; + } + } + + return gcs.AngularUnit.EqualParams(this.AngularUnit) && + gcs.HorizontalDatum.EqualParams(this.HorizontalDatum) && + gcs.PrimeMeridian.EqualParams(this.PrimeMeridian); + } + + private static GeographicCoordinateSystem CreateWgs84CoordinateSystem() + { + return Wgs84CatalogBootstrap.TryGetCoordinateSystem( + Wgs84CatalogBootstrap.Wgs84GeographicSrid, + out GeographicCoordinateSystem? coordinateSystem) + ? NormalizeToLegacyRuntimeAxisOrder(coordinateSystem) + : throw new InvalidOperationException("The generated EPSG catalog could not resolve the WGS 84 geographic coordinate system."); + } + + private static GeographicCoordinateSystem NormalizeToLegacyRuntimeAxisOrder(GeographicCoordinateSystem coordinateSystem) + { + if (coordinateSystem.AxisInfo.Count == 2 && + coordinateSystem.AxisInfo[0].Name == "Lon" && + coordinateSystem.AxisInfo[0].Orientation == AxisOrientationEnum.East && + coordinateSystem.AxisInfo[1].Name == "Lat" && + coordinateSystem.AxisInfo[1].Orientation == AxisOrientationEnum.North) + { + return coordinateSystem; + } + + return new GeographicCoordinateSystem( + coordinateSystem.AngularUnit, + coordinateSystem.HorizontalDatum, + coordinateSystem.PrimeMeridian, + [new AxisInfo("Lon", AxisOrientationEnum.East), new AxisInfo("Lat", AxisOrientationEnum.North)], + coordinateSystem.Name, + coordinateSystem.Authority, + coordinateSystem.AuthorityCode, + coordinateSystem.Alias, + coordinateSystem.Abbreviation, + coordinateSystem.Remarks, + wgs84ConversionInfo: coordinateSystem.WGS84ConversionInfo); + } + + private static List CloneWgs84ConversionInfoList(List? conversionInfo) + { + if (conversionInfo is null || conversionInfo.Count == 0) + { + return []; + } + + var clone = new List(conversionInfo.Count); + for (int i = 0; i < conversionInfo.Count; i++) + { + Wgs84ConversionInfo parameters = conversionInfo[i]; + clone.Add(new Wgs84ConversionInfo( + parameters.Dx, + parameters.Dy, + parameters.Dz, + parameters.Ex, + parameters.Ey, + parameters.Ez, + parameters.Ppm, + parameters.AreaOfUse)); + } + + return clone; + } } diff --git a/src/ProjNet/CoordinateSystems/HorizontalCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/HorizontalCoordinateSystem.cs index cbf6c5f8..0b508eff 100644 --- a/src/ProjNet/CoordinateSystems/HorizontalCoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/HorizontalCoordinateSystem.cs @@ -1,61 +1,58 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; -namespace ProjNet.CoordinateSystems +/// +/// A 2D coordinate system suitable for positions on the Earth's surface. +/// +public abstract class HorizontalCoordinateSystem : CoordinateSystem { - /// - /// A 2D coordinate system suitable for positions on the Earth's surface. + /// + /// Initializes a new instance of the class. + /// Creates an instance of HorizontalCoordinateSystem. /// - [Serializable] - public abstract class HorizontalCoordinateSystem : CoordinateSystem - { - /// - /// Creates an instance of HorizontalCoordinateSystem - /// - /// Horizontal datum - /// Axis information - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal HorizontalCoordinateSystem(HorizontalDatum datum, List axisInfo, - string name, string authority, long code, string alias, - string remarks, string abbreviation) - : base(name, authority, code, alias, abbreviation, remarks) - { - HorizontalDatum = datum; - if (axisInfo.Count != 2) - throw new ArgumentException("Axis info should contain two axes for horizontal coordinate systems"); - base.AxisInfo = axisInfo; - } - - #region IHorizontalCoordinateSystem Members + /// Horizontal datum. + /// Axis information. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Provider-supplied remarks. + /// Abbreviation. + /// Default envelope for the coordinate system domain. + internal HorizontalCoordinateSystem( + HorizontalDatum datum, + List axisInfo, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation, + double[]? defaultEnvelope = null) + : base(name, authority, code, alias, abbreviation, remarks, ValidateAxisInfo(axisInfo), defaultEnvelope) + { + this.HorizontalDatum = ArgumentGuard.ThrowIfNull(datum, nameof(datum)); + } + /// + /// Gets the horizontal datum. + /// + public HorizontalDatum HorizontalDatum { get; } - /// - /// Gets or sets the HorizontalDatum. - /// - public HorizontalDatum HorizontalDatum { get; set; } + private static List ValidateAxisInfo(List axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + if (axisInfo.Count != 2) + { + ArgumentGuard.ThrowArgument("Axis info should contain two axes for horizontal coordinate systems", nameof(axisInfo)); + } - #endregion + return axisInfo; } } diff --git a/src/ProjNet/CoordinateSystems/HorizontalDatum.cs b/src/ProjNet/CoordinateSystems/HorizontalDatum.cs index e7deebe8..9046a7a1 100644 --- a/src/ProjNet/CoordinateSystems/HorizontalDatum.cs +++ b/src/ProjNet/CoordinateSystems/HorizontalDatum.cs @@ -1,210 +1,338 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; +using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// Horizontal datum defining the standard datum information. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// The predefined datum accessors are thread-safe because they only expose immutable value objects. +/// +/// +public class HorizontalDatum : Datum { - /// - /// Horizontal datum defining the standard datum information. - /// - [Serializable] - public class HorizontalDatum : Datum - { - /// - /// Initializes a new instance of a horizontal datum - /// - /// Ellipsoid - /// Parameters for a Bursa Wolf transformation into WGS84 - /// Datum type - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal HorizontalDatum( - Ellipsoid ellipsoid, Wgs84ConversionInfo toWgs84, DatumType type, - string name, string authority, long code, string alias, string remarks, string abbreviation) - : base(type, name, authority, code, alias, remarks, abbreviation) - { - Ellipsoid = ellipsoid; - Wgs84Parameters = toWgs84; - } - - #region Predefined datums - /// - /// EPSG's WGS 84 datum has been the then current realisation. No distinction is made between the original WGS 84 - /// frame, WGS 84 (G730), WGS 84 (G873) and WGS 84 (G1150). Since 1997, WGS 84 has been maintained within 10cm of - /// the then current ITRF. - /// - /// - /// Area of use: World - /// Origin description: Defined through a consistent set of station coordinates. These have changed with time: by 0.7m - /// on 29/6/1994 [WGS 84 (G730)], a further 0.2m on 29/1/1997 [WGS 84 (G873)] and a further 0.06m on - /// 20/1/2002 [WGS 84 (G1150)]. - /// - public static HorizontalDatum WGS84 - { - get - { - return new HorizontalDatum(CoordinateSystems.Ellipsoid.WGS84, - null, DatumType.HD_Geocentric, "World Geodetic System 1984", "EPSG", 6326, string.Empty, - "EPSG's WGS 84 datum has been the then current realisation. No distinction is made between the original WGS 84 frame, WGS 84 (G730), WGS 84 (G873) and WGS 84 (G1150). Since 1997, WGS 84 has been maintained within 10cm of the then current ITRF.", string.Empty); - } - } - - /// - /// World Geodetic System 1972 - /// - /// - /// Used by GPS before 1987. For Transit satellite positioning see also WGS 72BE. Datum code 6323 reserved for southern hemisphere ProjCS's. - /// Area of use: World - /// Origin description: Developed from a worldwide distribution of terrestrial and - /// geodetic satellite observations and defined through a set of station coordinates. - /// - public static HorizontalDatum WGS72 - { - get - { - var datum = - new HorizontalDatum(CoordinateSystems.Ellipsoid.WGS72, - null, DatumType.HD_Geocentric, "World Geodetic System 1972", "EPSG", 6322, string.Empty, - "Used by GPS before 1987. For Transit satellite positioning see also WGS 72BE. Datum code 6323 reserved for southern hemisphere ProjCS's.", string.Empty); - datum.Wgs84Parameters = new Wgs84ConversionInfo(0, 0, 4.5, 0, 0, 0.554, 0.219); - return datum; - } - } - - - /// - /// European Terrestrial Reference System 1989 - /// - /// - /// Area of use: - /// Europe: Albania; Andorra; Austria; Belgium; Bosnia and Herzegovina; Bulgaria; Croatia; - /// Cyprus; Czech Republic; Denmark; Estonia; Finland; Faroe Islands; France; Germany; Greece; - /// Hungary; Ireland; Italy; Latvia; Liechtenstein; Lithuania; Luxembourg; Malta; Netherlands; - /// Norway; Poland; Portugal; Romania; San Marino; Serbia and Montenegro; Slovakia; Slovenia; - /// Spain; Svalbard; Sweden; Switzerland; United Kingdom (UK) including Channel Islands and - /// Isle of Man; Vatican City State. - /// Origin description: Fixed to the stable part of the Eurasian continental - /// plate and consistent with ITRS at the epoch 1989.0. - /// - public static HorizontalDatum ETRF89 - { - get - { - var datum = new HorizontalDatum(CoordinateSystems.Ellipsoid.GRS80, null, DatumType.HD_Geocentric, - "European Terrestrial Reference System 1989", "EPSG", 6258, "ETRF89", "The distinction in usage between ETRF89 and ETRS89 is confused: although in principle conceptually different in practice both are used for the realisation.", string.Empty); - datum.Wgs84Parameters = new Wgs84ConversionInfo(); - return datum; - } - } - - /// - /// European Datum 1950 - /// - /// - /// Area of use: - /// Europe - west - Denmark; Faroe Islands; France offshore; Israel offshore; Italy including San - /// Marino and Vatican City State; Ireland offshore; Netherlands offshore; Germany; Greece (offshore); - /// North Sea; Norway; Spain; Svalbard; Turkey; United Kingdom UKCS offshore. Egypt - Western Desert. - /// - /// Origin description: Fundamental point: Potsdam (Helmert Tower). - /// Latitude: 52 deg 22 min 51.4456 sec N; Longitude: 13 deg 3 min 58.9283 sec E (of Greenwich). - /// - public static HorizontalDatum ED50 - { - get - { - return new HorizontalDatum(CoordinateSystems.Ellipsoid.International1924, new Wgs84ConversionInfo(-87, -98, -121, 0, 0, 0, 0), DatumType.HD_Geocentric, - "European Datum 1950", "EPSG", 6230, "ED50", string.Empty, string.Empty); - } - } - #endregion - - #region IHorizontalDatum Members - - - /// - /// Gets or sets the ellipsoid of the datum - /// - public Ellipsoid Ellipsoid { get; set; } - - /// - /// Gets preferred parameters for a Bursa Wolf transformation into WGS84 - /// - public Wgs84ConversionInfo Wgs84Parameters { get; set; } - - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat("DATUM[\"{0}\", {1}", Name, Ellipsoid.WKT); - if (Wgs84Parameters != null) - sb.AppendFormat(", {0}", Wgs84Parameters.WKT); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, - "{1}{2}{3}", - (int)DatumType, InfoXml, Ellipsoid.XML, (Wgs84Parameters == null ? string.Empty : Wgs84Parameters.XML)); - } - } - - #endregion - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is HorizontalDatum)) - return false; - var datum = obj as HorizontalDatum; - if (datum.Wgs84Parameters == null && this.Wgs84Parameters != null) return false; - if (datum.Wgs84Parameters != null && !datum.Wgs84Parameters.Equals(this.Wgs84Parameters)) - return false; - return (datum != null && this.Ellipsoid != null && - datum.Ellipsoid.EqualParams(this.Ellipsoid) || datum == null && this.Ellipsoid == null) && this.DatumType == datum.DatumType; - } - } + /// + /// Initializes a new instance of the class. + /// + /// Ellipsoid. + /// Parameters for a Bursa Wolf transformation into WGS84. + /// Datum type. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + /// Retained datum-ensemble metadata. + internal HorizontalDatum( + Ellipsoid ellipsoid, + Wgs84ConversionInfo? toWgs84, + DatumType type, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation, + DatumEnsemble? ensemble = null) + : base(type, name, authority, code, alias, remarks, abbreviation, ensemble) + { + this.Ellipsoid = ellipsoid; + this.Wgs84Parameters = toWgs84; + } + + /// + /// Gets the World Geodetic System 1984 (WGS 84) horizontal datum. + /// + /// + /// No distinction is made between the original WGS 84 frame, WGS 84 (G730), WGS 84 (G873), and WGS 84 (G1150). + /// Since 1997, WGS 84 has been maintained within 10 cm of the current ITRF. + /// Area of use: World. + /// Origin description: Defined through a consistent set of station coordinates. These have changed with time: by 0.7m + /// on 29/6/1994 [WGS 84 (G730)], a further 0.2m on 29/1/1997 [WGS 84 (G873)] and a further 0.06m on + /// 20/1/2002 [WGS 84 (G1150)]. + /// This convenience accessor intentionally keeps the historical non-ensemble runtime metadata instead of + /// surfacing the generated EPSG ensemble record verbatim, because changing the public datum identity here would + /// alter established application behavior even though the catalog-backed WGS84 CRS statics now consume EPSG data. + /// + public static HorizontalDatum WGS84 + { + get + { + // Keep the legacy public datum identity stable instead of switching this accessor to the EPSG ensemble record. + return new HorizontalDatum( + CoordinateSystems.Ellipsoid.WGS84, + null, + DatumType.HD_Geocentric, + "World Geodetic System 1984", + "EPSG", + 6326, + string.Empty, + "EPSG's WGS 84 datum has been the then current realisation. No distinction is made between the original WGS 84 frame, WGS 84 (G730), WGS 84 (G873) and WGS 84 (G1150). Since 1997, WGS 84 has been maintained within 10cm of the then current ITRF.", + string.Empty); + } + } + + /// + /// Gets the World Geodetic System 1972 (WGS 72) horizontal datum. + /// + /// + /// Used by GPS before 1987. For Transit satellite positioning see also WGS 72BE. Datum code 6323 reserved for southern hemisphere ProjCS's. + /// Area of use: World. + /// Origin description: Developed from a worldwide distribution of terrestrial and + /// geodetic satellite observations and defined through a set of station coordinates. + /// + public static HorizontalDatum WGS72 + { + get + { + return new HorizontalDatum( + CoordinateSystems.Ellipsoid.WGS72, + new Wgs84ConversionInfo(0, 0, 4.5, 0, 0, 0.554, 0.219), + DatumType.HD_Geocentric, + "World Geodetic System 1972", + "EPSG", + 6322, + string.Empty, + "Used by GPS before 1987. For Transit satellite positioning see also WGS 72BE. Datum code 6323 reserved for southern hemisphere ProjCS's.", + string.Empty); + } + } + + /// + /// Gets the European Terrestrial Reference System 1989 (ETRS89) horizontal datum. + /// + /// + /// Area of use: + /// Europe: Albania; Andorra; Austria; Belgium; Bosnia and Herzegovina; Bulgaria; Croatia; + /// Cyprus; Czech Republic; Denmark; Estonia; Finland; Faroe Islands; France; Germany; Greece; + /// Hungary; Ireland; Italy; Latvia; Liechtenstein; Lithuania; Luxembourg; Malta; Netherlands; + /// Norway; Poland; Portugal; Romania; San Marino; Serbia and Montenegro; Slovakia; Slovenia; + /// Spain; Svalbard; Sweden; Switzerland; United Kingdom (UK) including Channel Islands and + /// Isle of Man; Vatican City State. + /// Origin description: Fixed to the stable part of the Eurasian continental + /// plate and consistent with ITRS at the epoch 1989.0. + /// + public static HorizontalDatum ETRF89 + { + get + { + return new HorizontalDatum( + CoordinateSystems.Ellipsoid.GRS80, + new Wgs84ConversionInfo(), + DatumType.HD_Geocentric, + "European Terrestrial Reference System 1989", + "EPSG", + 6258, + "ETRF89", + "The distinction in usage between ETRF89 and ETRS89 is confused: although in principle conceptually different in practice both are used for the realisation.", + string.Empty); + } + } + + /// + /// Gets the European Datum 1950 (ED50) horizontal datum. + /// + /// + /// Area of use: + /// Europe - west - Denmark; Faroe Islands; France offshore; Israel offshore; Italy including San + /// Marino and Vatican City State; Ireland offshore; Netherlands offshore; Germany; Greece (offshore); + /// North Sea; Norway; Spain; Svalbard; Turkey; United Kingdom UKCS offshore. Egypt - Western Desert. + /// + /// Origin description: Fundamental point: Potsdam (Helmert Tower). + /// Latitude: 52 deg 22 min 51.4456 sec N; Longitude: 13 deg 3 min 58.9283 sec E (of Greenwich). + /// + public static HorizontalDatum ED50 + { + get + { + return new HorizontalDatum( + CoordinateSystems.Ellipsoid.International1924, + new Wgs84ConversionInfo(-87, -98, -121, 0, 0, 0, 0), + DatumType.HD_Geocentric, + "European Datum 1950", + "EPSG", + 6230, + "ED50", + string.Empty, + string.Empty); + } + } + + /// + /// Gets the ellipsoid of the datum. + /// + public Ellipsoid Ellipsoid { get; } + + /// + /// Gets preferred parameters for a Bursa Wolf transformation into WGS84. + /// + public Wgs84ConversionInfo? Wgs84Parameters { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this datum with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new HorizontalDatum WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this datum with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new HorizontalDatum WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Creates a copy of this datum with updated retained datum-ensemble metadata. + /// + /// Replacement ensemble metadata, or to clear it. + /// A new with updated ensemble metadata. + public new HorizontalDatum WithEnsemble(DatumEnsemble? ensemble) => InfoAuthorityCloneHelper.CloneWithEnsemble(this, ensemble); + + /// + /// Creates a copy of this datum with updated Bursa-Wolf parameters for transformations into WGS84. + /// + /// Replacement WGS84 conversion parameters, or to clear them. + /// A new datum instance with updated WGS84 conversion parameters. + public HorizontalDatum WithWgs84Parameters(Wgs84ConversionInfo? toWgs84) + { + return InfoAuthorityCloneHelper.CloneWithWgs84Parameters(this, toWgs84); + } + + /// + /// Returns an XML representation of this horizontal datum as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_HorizontalDatum", + new XAttribute("DatumType", ((int)this.DatumType).ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + element.Add(this.Ellipsoid.ToXml()); + if (this.Wgs84Parameters is not null) + { + element.Add(this.Wgs84Parameters.ToXml()); + } + + return element; + } + + /// + /// Converts this horizontal datum to a WKT syntax tree node. + /// + /// A representing this horizontal datum. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + this.Ellipsoid.ToWktNode(), + }; + + if (this.Wgs84Parameters is not null) + { + children.Add(this.Wgs84Parameters.ToWktNode()); + } + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("DATUM", children); + } + + /// + /// Converts this horizontal datum to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this horizontal datum in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + if (this.Wgs84Parameters is not null) + { + throw new NotSupportedException("WKT2 DATUM output for horizontal datums with WGS84 conversion parameters is not implemented. A BOUNDCRS writer is required to preserve those transformations."); + } + + if (this.Ensemble is not null) + { + return this.Ensemble.ToWktNode(version); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.Ellipsoid.ToWktNode(version), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("DATUM", children); + } + + /// + public override bool EqualParams(object obj) + { + if (obj is not HorizontalDatum datum) + { + return false; + } + + if ((datum.Wgs84Parameters is null) != (this.Wgs84Parameters is null)) + { + return false; + } + + if (datum.Wgs84Parameters is not null + && this.Wgs84Parameters is not null + && !datum.Wgs84Parameters.Equals(this.Wgs84Parameters)) + { + return false; + } + + bool ellipsoidMatches = + (this.Ellipsoid is null && datum.Ellipsoid is null) + || (this.Ellipsoid is not null + && datum.Ellipsoid is not null + && datum.Ellipsoid.EqualParams(this.Ellipsoid)); + + return ellipsoidMatches && this.DatumType == datum.DatumType; + } } diff --git a/src/ProjNet/CoordinateSystems/IInfo.cs b/src/ProjNet/CoordinateSystems/IInfo.cs index ba5e5a11..cdb7681c 100644 --- a/src/ProjNet/CoordinateSystems/IInfo.cs +++ b/src/ProjNet/CoordinateSystems/IInfo.cs @@ -1,80 +1,62 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; -namespace ProjNet.CoordinateSystems +/// +/// Defines standard metadata stored with spatial reference objects. +/// +public interface IInfo { /// - /// The ISpatialReferenceInfo interface defines the standard - /// information stored with spatial reference objects. This - /// interface is reused for many of the spatial reference - /// objects in the system. + /// Gets the name of the object. /// - public interface IInfo - { - /// - /// Gets or sets the name of the object. - /// - string Name { get; } + string Name { get; } - /// - /// Gets or sets the authority name for this object, e.g., "EPSG", - /// is this is a standard object with an authority specific - /// identity code. Returns CUSTOM if this is a custom object. - /// - string Authority { get; } + /// + /// Gets the authority name for this object, e.g., "EPSG", + /// is this is a standard object with an authority specific + /// identity code. Returns CUSTOM if this is a custom object. + /// + string Authority { get; } - /// - /// Gets or sets the authority specific identification code of the object - /// - long AuthorityCode { get; } + /// + /// Gets the authority specific identification code of the object. + /// + long AuthorityCode { get; } - /// - /// Gets or sets the alias of the object. - /// - string Alias { get; } + /// + /// Gets the alias of the object. + /// + string Alias { get; } - /// - /// Gets or sets the abbreviation of the object. - /// - string Abbreviation { get; } + /// + /// Gets the abbreviation of the object. + /// + string Abbreviation { get; } - /// - /// Gets or sets the provider-supplied remarks for the object. - /// - string Remarks { get; } + /// + /// Gets the provider-supplied remarks for the object. + /// + string Remarks { get; } - /// - /// Returns the Well-known text for this spatial reference object - /// as defined in the simple features specification. - /// - string WKT { get; } + /// + /// Gets the Well-known text for this spatial reference object + /// as defined in the simple features specification. + /// + string WKT { get; } - /// - /// Gets an XML representation of this object. - /// - string XML { get; } + /// + /// Gets an XML representation of this object. + /// + string XML { get; } - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - bool EqualParams(object obj); - } + /// + /// Checks whether the coordinate system parameter values of this instance are equal to those of another instance. + /// Name, abbreviation, authority, alias, and remarks are excluded from the comparison. + /// + /// The object to compare against. + /// if all coordinate system parameters are equal; otherwise, . + bool EqualParams(object obj); } diff --git a/src/ProjNet/CoordinateSystems/IProjection.cs b/src/ProjNet/CoordinateSystems/IProjection.cs index 19d2c89d..e7135b3e 100644 --- a/src/ProjNet/CoordinateSystems/IProjection.cs +++ b/src/ProjNet/CoordinateSystems/IProjection.cs @@ -1,55 +1,41 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; -namespace ProjNet.CoordinateSystems +/// +/// The IProjection interface defines the standard information stored with projection +/// objects. A projection object implements a coordinate transformation from a geographic +/// coordinate system to a projected coordinate system, given the ellipsoid for the +/// geographic coordinate system. It is expected that each coordinate transformation of +/// interest, e.g., Transverse Mercator, Lambert, will be implemented as a COM class of +/// coType Projection, supporting the IProjection interface. +/// +public interface IProjection : IInfo { /// - /// The IProjection interface defines the standard information stored with projection - /// objects. A projection object implements a coordinate transformation from a geographic - /// coordinate system to a projected coordinate system, given the ellipsoid for the - /// geographic coordinate system. It is expected that each coordinate transformation of - /// interest, e.g., Transverse Mercator, Lambert, will be implemented as a COM class of - /// coType Projection, supporting the IProjection interface. + /// Gets number of parameters of the projection. /// - public interface IProjection : IInfo - { - /// - /// Gets number of parameters of the projection. - /// - int NumParameters { get; } + int NumParameters { get; } - /// - /// Gets the projection classification name (e.g. 'Transverse_Mercator'). - /// - string ClassName { get; } + /// + /// Gets the projection classification name (e.g. 'Transverse_Mercator'). + /// + string ClassName { get; } - /// - /// Gets an indexed parameter of the projection. - /// - /// Index of parameter - /// n'th parameter - ProjectionParameter GetParameter(int index); + /// + /// Gets an indexed parameter of the projection. + /// + /// Index of parameter. + /// n'th parameter. + ProjectionParameter GetParameter(int index); - /// - /// Gets an named parameter of the projection. - /// - /// The parameter name is case insensitive - /// Name of parameter - /// parameter or null if not found - ProjectionParameter GetParameter(string name); - } + /// + /// Gets an named parameter of the projection. + /// + /// The parameter name is case insensitive. + /// Name of parameter. + /// parameter or null if not found. + ProjectionParameter? GetParameter(string name); } diff --git a/src/ProjNet/CoordinateSystems/IUnit.cs b/src/ProjNet/CoordinateSystems/IUnit.cs index 7da4e3ad..c0340195 100644 --- a/src/ProjNet/CoordinateSystems/IUnit.cs +++ b/src/ProjNet/CoordinateSystems/IUnit.cs @@ -1,24 +1,12 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; -namespace ProjNet.CoordinateSystems +/// +/// Marker interface for unit types used in coordinate system definitions. +/// +public interface IUnit : IInfo { - /// - /// The IUnit interface abstracts different kinds of units, it has no methods. - /// - public interface IUnit : IInfo { } } diff --git a/src/ProjNet/CoordinateSystems/Info.cs b/src/ProjNet/CoordinateSystems/Info.cs index 56caa3ed..59546074 100644 --- a/src/ProjNet/CoordinateSystems/Info.cs +++ b/src/ProjNet/CoordinateSystems/Info.cs @@ -1,186 +1,188 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; -using System.Text; -namespace ProjNet.CoordinateSystems +using System.Globalization; +using System.Xml.Linq; + +/// +/// The Info object defines the standard information +/// stored with spatial reference objects. +/// +public abstract class Info : IInfo { - /// - /// The Info object defines the standard information - /// stored with spatial reference objects - /// - [Serializable] - public abstract class Info : IInfo - { - /// - /// A base interface for metadata applicable to coordinate system objects. - /// - /// - /// The metadata items Abbreviation, Alias, Authority, AuthorityCode, Name and Remarks - /// were specified in the Simple Features interfaces, so they have been kept here. - /// This specification does not dictate what the contents of these items - /// should be. However, the following guidelines are suggested: - /// When is used to create an object, the Authority - /// and 'AuthorityCode' values should be set to the authority name of the factory object, and the authority - /// code supplied by the client, respectively. The other values may or may not be set. (If the authority is - /// EPSG, the implementer may consider using the corresponding metadata values in the EPSG tables.) - /// When creates an object, the 'Name' should be set to the value - /// supplied by the client. All of the other metadata items should be left empty - /// - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal Info( - string name, - string authority, - long code, - string alias, - string abbreviation, - string remarks) - { - _Name = name; - _Authority = authority; - _Code = code; - _Alias = alias; - _Abbreviation = abbreviation; - _Remarks = remarks; - } - - #region ISpatialReferenceInfo Members - - private string _Name; - - /// - /// Gets or sets the name of the object. - /// - public string Name - { - get { return _Name; } - set { _Name = value; } - } - - private string _Authority; - - /// - /// Gets or sets the authority name for this object, e.g., "EPSG", - /// is this is a standard object with an authority specific - /// identity code. Returns "CUSTOM" if this is a custom object. - /// - public string Authority - { - get { return _Authority; } - set { _Authority = value; } - } - - private long _Code; - - /// - /// Gets or sets the authority specific identification code of the object - /// - public long AuthorityCode - { - get { return _Code; } - set { _Code = value; } - } - - private string _Alias; - - /// - /// Gets or sets the alias of the object. - /// - public string Alias - { - get { return _Alias; } - set { _Alias = value; } - } - - private string _Abbreviation; - - /// - /// Gets or sets the abbreviation of the object. - /// - public string Abbreviation - { - get { return _Abbreviation; } - set { _Abbreviation = value; } - } - - private string _Remarks; - - /// - /// Gets or sets the provider-supplied remarks for the object. - /// - public string Remarks - { - get { return _Remarks; } - set { _Remarks = value; } - } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string ToString() - { - return WKT; - } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public abstract string WKT {get ;} - - /// - /// Gets an XML representation of this object. - /// - public abstract string XML { get; } - - /// - /// Returns an XML string of the info object - /// - internal string InfoXml - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat("0) sb.AppendFormat(" AuthorityCode=\"{0}\"",AuthorityCode); - if (!string.IsNullOrWhiteSpace(Abbreviation)) sb.AppendFormat(" Abbreviation=\"{0}\"", Abbreviation); - if (!string.IsNullOrWhiteSpace(Authority)) sb.AppendFormat(" Authority=\"{0}\"", Authority); - if (!string.IsNullOrWhiteSpace(Name)) sb.AppendFormat(" Name=\"{0}\"", Name); - sb.Append("/>"); - return sb.ToString(); - } - } - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public abstract bool EqualParams(object obj); - - #endregion - } + /// + /// Initializes a new instance of the class. + /// + /// + /// The metadata items �Abbreviation�, �Alias�, �Authority�, �AuthorityCode�, �Name� and �Remarks� + /// were specified in the Simple Features interfaces, so they have been kept here. + /// This specification does not dictate what the contents of these items + /// should be. However, the following guidelines are suggested: + /// When ICoordinateSystemAuthorityFactory is used to create an object, the �Authority� + /// and 'AuthorityCode' values should be set to the authority name of the factory object, and the authority + /// code supplied by the client, respectively. The other values may or may not be set. (If the authority is + /// EPSG, the implementer may consider using the corresponding metadata values in the EPSG tables.) + /// When creates an object, the 'Name' should be set to the value + /// supplied by the client. All of the other metadata items should be left empty. + /// + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + internal Info( + string name, + string authority, + long code, + string alias, + string abbreviation, + string remarks) + { + this.Name = name ?? string.Empty; + this.Authority = authority ?? string.Empty; + this.AuthorityCode = code; + this.Alias = alias ?? string.Empty; + this.Abbreviation = abbreviation ?? string.Empty; + this.Remarks = remarks ?? string.Empty; + } + + /// + /// Gets the name of the object. + /// + public string Name { get; } + + /// + /// Gets the authority name for this object, e.g., "EPSG", + /// is this is a standard object with an authority specific + /// identity code. Returns "CUSTOM" if this is a custom object. + /// + public string Authority { get; } + + /// + /// Gets the authority specific identification code of the object. + /// + public long AuthorityCode { get; } + + /// + /// Gets the alias of the object. + /// + public string Alias { get; } + + /// + /// Gets the abbreviation of the object. + /// + public string Abbreviation { get; } + + /// + /// Gets the provider-supplied remarks for the object. + /// + public string Remarks { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public abstract string WKT { get; } + + /// + /// Gets an XML representation of this object. + /// + public abstract string XML { get; } + + /// + /// Gets an XML element of the info object. + /// + internal XElement InfoXmlElement + { + get + { + var element = new XElement("CS_Info"); + if (this.AuthorityCode > 0) + { + element.Add(new XAttribute("AuthorityCode", this.AuthorityCode.ToString(CultureInfo.InvariantCulture))); + } + + if (!string.IsNullOrWhiteSpace(this.Abbreviation)) + { + element.Add(new XAttribute("Abbreviation", this.Abbreviation)); + } + + if (!string.IsNullOrWhiteSpace(this.Authority)) + { + element.Add(new XAttribute("Authority", this.Authority)); + } + + if (!string.IsNullOrWhiteSpace(this.Name)) + { + element.Add(new XAttribute("Name", this.Name)); + } + + return element; + } + } + + /// + /// Creates a copy of this object with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new instance of the same runtime type with updated authority metadata. + public Info WithAuthority(string authority, long code) + { + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return this.CloneWithAuthorityCore(authority, code); + } + + /// + /// Creates a copy of this object with an updated name. + /// + /// Replacement name. + /// A new instance of the same runtime type with the updated name. + public Info WithName(string name) + { + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return this.CloneWithNameCore(name); + } + + /// + /// Returns the Well-known text for this object + /// as defined in the simple features specification. + /// + /// The Well-known text representation of this object. + public override string ToString() => this.WKT; + + /// + /// Checks whether the coordinate system parameter values of this instance are equal to those of another instance. + /// Name, abbreviation, authority, alias, and remarks are excluded from the comparison. + /// + /// The object to compare against. + /// if all coordinate system parameters are equal; otherwise, . + public abstract bool EqualParams(object obj); + + /// + /// Creates a clone of this instance with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A cloned instance of the same runtime type. + private protected virtual Info CloneWithAuthorityCore(string authority, long code) + { + throw new NotSupportedException($"WithAuthority is not supported for info type '{this.GetType().FullName}'."); + } + + /// + /// Creates a clone of this instance with an updated name. + /// + /// Replacement name. + /// A cloned instance of the same runtime type. + private protected virtual Info CloneWithNameCore(string name) + { + throw new NotSupportedException($"WithName is not supported for info type '{this.GetType().FullName}'."); + } } diff --git a/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.CoordinateSystems.cs b/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.CoordinateSystems.cs new file mode 100644 index 00000000..806f99d0 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.CoordinateSystems.cs @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Rebuilds immutable info-backed coordinate system objects while replacing top-level metadata. +/// +internal static partial class InfoAuthorityCloneHelper +{ + /// + /// Creates a deep clone of the supplied geographic coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned geographic coordinate system with the requested authority metadata. + internal static GeographicCoordinateSystem CloneWithAuthority(GeographicCoordinateSystem geographicCoordinateSystem, string authority, long authorityCode) + { + geographicCoordinateSystem = ArgumentGuard.ThrowIfNull(geographicCoordinateSystem, nameof(geographicCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneGeographicCoordinateSystem(geographicCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied projected coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned projected coordinate system with the requested authority metadata. + internal static ProjectedCoordinateSystem CloneWithAuthority(ProjectedCoordinateSystem projectedCoordinateSystem, string authority, long authorityCode) + { + projectedCoordinateSystem = ArgumentGuard.ThrowIfNull(projectedCoordinateSystem, nameof(projectedCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneProjectedCoordinateSystem(projectedCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied geocentric coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned geocentric coordinate system with the requested authority metadata. + internal static GeocentricCoordinateSystem CloneWithAuthority(GeocentricCoordinateSystem geocentricCoordinateSystem, string authority, long authorityCode) + { + geocentricCoordinateSystem = ArgumentGuard.ThrowIfNull(geocentricCoordinateSystem, nameof(geocentricCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneGeocentricCoordinateSystem(geocentricCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied vertical coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned vertical coordinate system with the requested authority metadata. + internal static VerticalCoordinateSystem CloneWithAuthority(VerticalCoordinateSystem verticalCoordinateSystem, string authority, long authorityCode) + { + verticalCoordinateSystem = ArgumentGuard.ThrowIfNull(verticalCoordinateSystem, nameof(verticalCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneVerticalCoordinateSystem(verticalCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied compound coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned compound coordinate system with the requested authority metadata. + internal static CompoundCoordinateSystem CloneWithAuthority(CompoundCoordinateSystem compoundCoordinateSystem, string authority, long authorityCode) + { + compoundCoordinateSystem = ArgumentGuard.ThrowIfNull(compoundCoordinateSystem, nameof(compoundCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneCompoundCoordinateSystem(compoundCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied bound coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned bound coordinate system with the requested authority metadata. + internal static BoundCoordinateSystem CloneWithAuthority(BoundCoordinateSystem boundCoordinateSystem, string authority, long authorityCode) + { + boundCoordinateSystem = ArgumentGuard.ThrowIfNull(boundCoordinateSystem, nameof(boundCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneBoundCoordinateSystem(boundCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied fitted coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned fitted coordinate system with the requested authority metadata. + internal static FittedCoordinateSystem CloneWithAuthority(FittedCoordinateSystem fittedCoordinateSystem, string authority, long authorityCode) + { + fittedCoordinateSystem = ArgumentGuard.ThrowIfNull(fittedCoordinateSystem, nameof(fittedCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneFittedCoordinateSystem(fittedCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied engineering coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned engineering coordinate system with the requested authority metadata. + internal static EngineeringCoordinateSystem CloneWithAuthority(EngineeringCoordinateSystem engineeringCoordinateSystem, string authority, long authorityCode) + { + engineeringCoordinateSystem = ArgumentGuard.ThrowIfNull(engineeringCoordinateSystem, nameof(engineeringCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneEngineeringCoordinateSystem(engineeringCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied parametric coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned parametric coordinate system with the requested authority metadata. + internal static ParametricCoordinateSystem CloneWithAuthority(ParametricCoordinateSystem parametricCoordinateSystem, string authority, long authorityCode) + { + parametricCoordinateSystem = ArgumentGuard.ThrowIfNull(parametricCoordinateSystem, nameof(parametricCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneParametricCoordinateSystem(parametricCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied temporal coordinate system with replacement authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned temporal coordinate system with the requested authority metadata. + internal static TemporalCoordinateSystem CloneWithAuthority(TemporalCoordinateSystem temporalCoordinateSystem, string authority, long authorityCode) + { + temporalCoordinateSystem = ArgumentGuard.ThrowIfNull(temporalCoordinateSystem, nameof(temporalCoordinateSystem)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneTemporalCoordinateSystem(temporalCoordinateSystem, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied geographic coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned geographic coordinate system with the requested name. + internal static GeographicCoordinateSystem CloneWithName(GeographicCoordinateSystem geographicCoordinateSystem, string name) + { + geographicCoordinateSystem = ArgumentGuard.ThrowIfNull(geographicCoordinateSystem, nameof(geographicCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneGeographicCoordinateSystem(geographicCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied projected coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned projected coordinate system with the requested name. + internal static ProjectedCoordinateSystem CloneWithName(ProjectedCoordinateSystem projectedCoordinateSystem, string name) + { + projectedCoordinateSystem = ArgumentGuard.ThrowIfNull(projectedCoordinateSystem, nameof(projectedCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneProjectedCoordinateSystem(projectedCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied geocentric coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned geocentric coordinate system with the requested name. + internal static GeocentricCoordinateSystem CloneWithName(GeocentricCoordinateSystem geocentricCoordinateSystem, string name) + { + geocentricCoordinateSystem = ArgumentGuard.ThrowIfNull(geocentricCoordinateSystem, nameof(geocentricCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneGeocentricCoordinateSystem(geocentricCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied vertical coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned vertical coordinate system with the requested name. + internal static VerticalCoordinateSystem CloneWithName(VerticalCoordinateSystem verticalCoordinateSystem, string name) + { + verticalCoordinateSystem = ArgumentGuard.ThrowIfNull(verticalCoordinateSystem, nameof(verticalCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneVerticalCoordinateSystem(verticalCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied compound coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned compound coordinate system with the requested name. + internal static CompoundCoordinateSystem CloneWithName(CompoundCoordinateSystem compoundCoordinateSystem, string name) + { + compoundCoordinateSystem = ArgumentGuard.ThrowIfNull(compoundCoordinateSystem, nameof(compoundCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneCompoundCoordinateSystem(compoundCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied bound coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned bound coordinate system with the requested name. + internal static BoundCoordinateSystem CloneWithName(BoundCoordinateSystem boundCoordinateSystem, string name) + { + boundCoordinateSystem = ArgumentGuard.ThrowIfNull(boundCoordinateSystem, nameof(boundCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneBoundCoordinateSystem(boundCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied fitted coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned fitted coordinate system with the requested name. + internal static FittedCoordinateSystem CloneWithName(FittedCoordinateSystem fittedCoordinateSystem, string name) + { + fittedCoordinateSystem = ArgumentGuard.ThrowIfNull(fittedCoordinateSystem, nameof(fittedCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneFittedCoordinateSystem(fittedCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied engineering coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned engineering coordinate system with the requested name. + internal static EngineeringCoordinateSystem CloneWithName(EngineeringCoordinateSystem engineeringCoordinateSystem, string name) + { + engineeringCoordinateSystem = ArgumentGuard.ThrowIfNull(engineeringCoordinateSystem, nameof(engineeringCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneEngineeringCoordinateSystem(engineeringCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied parametric coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned parametric coordinate system with the requested name. + internal static ParametricCoordinateSystem CloneWithName(ParametricCoordinateSystem parametricCoordinateSystem, string name) + { + parametricCoordinateSystem = ArgumentGuard.ThrowIfNull(parametricCoordinateSystem, nameof(parametricCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneParametricCoordinateSystem(parametricCoordinateSystem, name: name); + } + + /// + /// Creates a deep clone of the supplied temporal coordinate system with a replacement name. + /// + /// Coordinate system to clone. + /// Replacement name. + /// A cloned temporal coordinate system with the requested name. + internal static TemporalCoordinateSystem CloneWithName(TemporalCoordinateSystem temporalCoordinateSystem, string name) + { + temporalCoordinateSystem = ArgumentGuard.ThrowIfNull(temporalCoordinateSystem, nameof(temporalCoordinateSystem)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneTemporalCoordinateSystem(temporalCoordinateSystem, name: name); + } + + private static GeographicCoordinateSystem CloneGeographicCoordinateSystem( + GeographicCoordinateSystem geographicCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + HorizontalDatum horizontalDatum = CloneHorizontalDatum(geographicCoordinateSystem.HorizontalDatum); + return CloneGeographicCoordinateSystem(geographicCoordinateSystem, horizontalDatum, authority, authorityCode, name); + } + + private static GeographicCoordinateSystem CloneGeographicCoordinateSystem( + GeographicCoordinateSystem geographicCoordinateSystem, + HorizontalDatum horizontalDatum, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + var clone = new GeographicCoordinateSystem( + CloneAngularUnit(geographicCoordinateSystem.AngularUnit), + horizontalDatum, + ClonePrimeMeridian(geographicCoordinateSystem.PrimeMeridian), + CloneAxisInfo(geographicCoordinateSystem), + name ?? geographicCoordinateSystem.Name, + authority ?? geographicCoordinateSystem.Authority, + authorityCode ?? geographicCoordinateSystem.AuthorityCode, + geographicCoordinateSystem.Alias, + geographicCoordinateSystem.Abbreviation, + geographicCoordinateSystem.Remarks, + geographicCoordinateSystem.DefaultEnvelope, + CloneWgs84ConversionInfoList(geographicCoordinateSystem.WGS84ConversionInfo)); + + return clone; + } + + private static ProjectedCoordinateSystem CloneProjectedCoordinateSystem( + ProjectedCoordinateSystem projectedCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + HorizontalDatum horizontalDatum = CloneHorizontalDatum(projectedCoordinateSystem.HorizontalDatum); + GeographicCoordinateSystem geographicCoordinateSystem = CloneGeographicCoordinateSystem(projectedCoordinateSystem.GeographicCoordinateSystem, horizontalDatum); + + return new ProjectedCoordinateSystem( + horizontalDatum, + geographicCoordinateSystem, + CloneLinearUnit(projectedCoordinateSystem.LinearUnit), + CloneProjection(projectedCoordinateSystem.Projection), + CloneAxisInfo(projectedCoordinateSystem), + name ?? projectedCoordinateSystem.Name, + authority ?? projectedCoordinateSystem.Authority, + authorityCode ?? projectedCoordinateSystem.AuthorityCode, + projectedCoordinateSystem.Alias, + projectedCoordinateSystem.Remarks, + projectedCoordinateSystem.Abbreviation, + projectedCoordinateSystem.DefaultEnvelope); + } + + private static GeocentricCoordinateSystem CloneGeocentricCoordinateSystem( + GeocentricCoordinateSystem geocentricCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + HorizontalDatum horizontalDatum = CloneHorizontalDatum(geocentricCoordinateSystem.HorizontalDatum); + return new GeocentricCoordinateSystem( + horizontalDatum, + CloneLinearUnit(geocentricCoordinateSystem.LinearUnit), + ClonePrimeMeridian(geocentricCoordinateSystem.PrimeMeridian), + CloneAxisInfo(geocentricCoordinateSystem), + name ?? geocentricCoordinateSystem.Name, + authority ?? geocentricCoordinateSystem.Authority, + authorityCode ?? geocentricCoordinateSystem.AuthorityCode, + geocentricCoordinateSystem.Alias, + geocentricCoordinateSystem.Remarks, + geocentricCoordinateSystem.Abbreviation, + geocentricCoordinateSystem.DefaultEnvelope); + } + + private static VerticalCoordinateSystem CloneVerticalCoordinateSystem( + VerticalCoordinateSystem verticalCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + var clone = new VerticalCoordinateSystem( + CloneLinearUnit(verticalCoordinateSystem.LinearUnit), + CloneVerticalDatum(verticalCoordinateSystem.VerticalDatum), + CloneAxisInfo(verticalCoordinateSystem), + name ?? verticalCoordinateSystem.Name, + authority ?? verticalCoordinateSystem.Authority, + authorityCode ?? verticalCoordinateSystem.AuthorityCode, + verticalCoordinateSystem.Alias, + verticalCoordinateSystem.Abbreviation, + verticalCoordinateSystem.Remarks, + verticalCoordinateSystem.DefaultEnvelope, + CloneVerticalBoundGridTransformation(verticalCoordinateSystem.BoundGridTransformation)); + + return clone; + } + + private static CompoundCoordinateSystem CloneCompoundCoordinateSystem( + CompoundCoordinateSystem compoundCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new CompoundCoordinateSystem( + CloneCoordinateSystem(compoundCoordinateSystem.HeadCoordinateSystem), + CloneCoordinateSystem(compoundCoordinateSystem.TailCoordinateSystem), + name ?? compoundCoordinateSystem.Name, + authority ?? compoundCoordinateSystem.Authority, + authorityCode ?? compoundCoordinateSystem.AuthorityCode, + compoundCoordinateSystem.Alias, + compoundCoordinateSystem.Abbreviation, + compoundCoordinateSystem.Remarks, + CloneAxisInfo(compoundCoordinateSystem), + compoundCoordinateSystem.DefaultEnvelope); + } + + private static BoundCoordinateSystem CloneBoundCoordinateSystem( + BoundCoordinateSystem boundCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new BoundCoordinateSystem( + CloneCoordinateSystem(boundCoordinateSystem.SourceCoordinateSystem), + CloneCoordinateSystem(boundCoordinateSystem.TargetCoordinateSystem), + CloneBoundTransformation(boundCoordinateSystem.Transformation), + name ?? boundCoordinateSystem.Name, + authority ?? boundCoordinateSystem.Authority, + authorityCode ?? boundCoordinateSystem.AuthorityCode, + boundCoordinateSystem.Alias, + boundCoordinateSystem.Abbreviation, + boundCoordinateSystem.Remarks); + } + + private static FittedCoordinateSystem CloneFittedCoordinateSystem( + FittedCoordinateSystem fittedCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new FittedCoordinateSystem( + CloneCoordinateSystem(fittedCoordinateSystem.BaseCoordinateSystem), + fittedCoordinateSystem.ToBaseTransform, + name ?? fittedCoordinateSystem.Name, + authority ?? fittedCoordinateSystem.Authority, + authorityCode ?? fittedCoordinateSystem.AuthorityCode, + fittedCoordinateSystem.Alias, + fittedCoordinateSystem.Remarks, + fittedCoordinateSystem.Abbreviation, + CloneAxisInfo(fittedCoordinateSystem)); + } + + private static EngineeringCoordinateSystem CloneEngineeringCoordinateSystem( + EngineeringCoordinateSystem engineeringCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new EngineeringCoordinateSystem( + CloneEngineeringDatum(engineeringCoordinateSystem.EngineeringDatum), + engineeringCoordinateSystem.CoordinateSystemType, + CloneAxisInfo(engineeringCoordinateSystem), + CloneUnits(engineeringCoordinateSystem.AxisUnits), + name ?? engineeringCoordinateSystem.Name, + authority ?? engineeringCoordinateSystem.Authority, + authorityCode ?? engineeringCoordinateSystem.AuthorityCode, + engineeringCoordinateSystem.Alias, + engineeringCoordinateSystem.Abbreviation, + engineeringCoordinateSystem.Remarks); + } + + private static ParametricCoordinateSystem CloneParametricCoordinateSystem( + ParametricCoordinateSystem parametricCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new ParametricCoordinateSystem( + CloneParametricUnit(parametricCoordinateSystem.ParametricUnit), + CloneParametricDatum(parametricCoordinateSystem.ParametricDatum), + new AxisInfo(parametricCoordinateSystem.GetAxis(0)), + name ?? parametricCoordinateSystem.Name, + authority ?? parametricCoordinateSystem.Authority, + authorityCode ?? parametricCoordinateSystem.AuthorityCode, + parametricCoordinateSystem.Alias, + parametricCoordinateSystem.Abbreviation, + parametricCoordinateSystem.Remarks); + } + + private static TemporalCoordinateSystem CloneTemporalCoordinateSystem( + TemporalCoordinateSystem temporalCoordinateSystem, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new TemporalCoordinateSystem( + CloneTimeUnit(temporalCoordinateSystem.TimeUnit), + CloneTemporalDatum(temporalCoordinateSystem.TemporalDatum), + new AxisInfo(temporalCoordinateSystem.GetAxis(0)), + name ?? temporalCoordinateSystem.Name, + authority ?? temporalCoordinateSystem.Authority, + authorityCode ?? temporalCoordinateSystem.AuthorityCode, + temporalCoordinateSystem.Alias, + temporalCoordinateSystem.Abbreviation, + temporalCoordinateSystem.Remarks); + } + + private static CoordinateSystem CloneCoordinateSystem(CoordinateSystem coordinateSystem) + { + return coordinateSystem switch + { + GeographicCoordinateSystem geographicCoordinateSystem => CloneGeographicCoordinateSystem(geographicCoordinateSystem), + ProjectedCoordinateSystem projectedCoordinateSystem => CloneProjectedCoordinateSystem(projectedCoordinateSystem), + GeocentricCoordinateSystem geocentricCoordinateSystem => CloneGeocentricCoordinateSystem(geocentricCoordinateSystem), + VerticalCoordinateSystem verticalCoordinateSystem => CloneVerticalCoordinateSystem(verticalCoordinateSystem), + CompoundCoordinateSystem compoundCoordinateSystem => CloneCompoundCoordinateSystem(compoundCoordinateSystem), + BoundCoordinateSystem boundCoordinateSystem => CloneBoundCoordinateSystem(boundCoordinateSystem), + FittedCoordinateSystem fittedCoordinateSystem => CloneFittedCoordinateSystem(fittedCoordinateSystem), + EngineeringCoordinateSystem engineeringCoordinateSystem => CloneEngineeringCoordinateSystem(engineeringCoordinateSystem), + ParametricCoordinateSystem parametricCoordinateSystem => CloneParametricCoordinateSystem(parametricCoordinateSystem), + TemporalCoordinateSystem temporalCoordinateSystem => CloneTemporalCoordinateSystem(temporalCoordinateSystem), + _ => throw new NotSupportedException($"Coordinate system cloning is not supported for type '{coordinateSystem.GetType().FullName}'."), + }; + } + + private static List CloneUnits(IReadOnlyList units) + { + var clone = new List(units.Count); + for (int i = 0; i < units.Count; i++) + { + clone.Add(CloneUnit(units[i])); + } + + return clone; + } + + private static List CloneAxisInfo(CoordinateSystem coordinateSystem) + { + var clone = new List(coordinateSystem.Dimension); + for (int i = 0; i < coordinateSystem.Dimension; i++) + { + clone.Add(new AxisInfo(coordinateSystem.GetAxis(i))); + } + + return clone; + } +} diff --git a/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.Operations.cs b/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.Operations.cs new file mode 100644 index 00000000..f79db1d0 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.Operations.cs @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Rebuilds immutable info-backed operation and projection objects while replacing top-level metadata. +/// +internal static partial class InfoAuthorityCloneHelper +{ + /// + /// Creates a deep clone of the supplied projection with replacement authority metadata. + /// + /// Projection to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned projection with the requested authority metadata. + internal static Projection CloneWithAuthority(Projection projection, string authority, long authorityCode) + { + projection = ArgumentGuard.ThrowIfNull(projection, nameof(projection)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneProjection(projection, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied coordinate operation with replacement authority metadata. + /// + /// Coordinate operation to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned coordinate operation with the requested authority metadata. + internal static CoordinateOperation CloneWithAuthority(CoordinateOperation coordinateOperation, string authority, long authorityCode) + { + coordinateOperation = ArgumentGuard.ThrowIfNull(coordinateOperation, nameof(coordinateOperation)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneCoordinateOperation(coordinateOperation, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied concatenated operation with replacement authority metadata. + /// + /// Concatenated operation to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned concatenated operation with the requested authority metadata. + internal static ConcatenatedOperation CloneWithAuthority(ConcatenatedOperation concatenatedOperation, string authority, long authorityCode) + { + concatenatedOperation = ArgumentGuard.ThrowIfNull(concatenatedOperation, nameof(concatenatedOperation)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneConcatenatedOperation(concatenatedOperation, authority: authority, authorityCode: authorityCode); + } + + /// + /// Creates a deep clone of the supplied projection with a replacement name. + /// + /// Projection to clone. + /// Replacement name. + /// A cloned projection with the requested name. + internal static Projection CloneWithName(Projection projection, string name) + { + projection = ArgumentGuard.ThrowIfNull(projection, nameof(projection)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneProjection(projection, name: name); + } + + /// + /// Creates a deep clone of the supplied coordinate operation with a replacement name. + /// + /// Coordinate operation to clone. + /// Replacement name. + /// A cloned coordinate operation with the requested name. + internal static CoordinateOperation CloneWithName(CoordinateOperation coordinateOperation, string name) + { + coordinateOperation = ArgumentGuard.ThrowIfNull(coordinateOperation, nameof(coordinateOperation)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneCoordinateOperation(coordinateOperation, name: name); + } + + /// + /// Creates a deep clone of the supplied concatenated operation with a replacement name. + /// + /// Concatenated operation to clone. + /// Replacement name. + /// A cloned concatenated operation with the requested name. + internal static ConcatenatedOperation CloneWithName(ConcatenatedOperation concatenatedOperation, string name) + { + concatenatedOperation = ArgumentGuard.ThrowIfNull(concatenatedOperation, nameof(concatenatedOperation)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneConcatenatedOperation(concatenatedOperation, name: name); + } + + private static Projection CloneProjection(IProjection projection, string? authority = null, long? authorityCode = null, string? name = null) + { + return new Projection( + projection.ClassName, + CloneProjectionParameters(projection), + name ?? projection.Name, + authority ?? projection.Authority, + authorityCode ?? projection.AuthorityCode, + projection.Alias, + projection.Remarks, + projection.Abbreviation); + } + + private static CoordinateOperation CloneCoordinateOperation( + CoordinateOperation coordinateOperation, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new CoordinateOperation( + coordinateOperation.MethodName, + CloneParameters(coordinateOperation.Parameters), + CloneCoordinateSystem(coordinateOperation.SourceCoordinateSystem), + CloneCoordinateSystem(coordinateOperation.TargetCoordinateSystem), + name ?? coordinateOperation.Name, + authority ?? coordinateOperation.Authority, + authorityCode ?? coordinateOperation.AuthorityCode, + coordinateOperation.Alias, + coordinateOperation.Abbreviation, + coordinateOperation.Remarks); + } + + private static ConcatenatedOperation CloneConcatenatedOperation( + ConcatenatedOperation concatenatedOperation, + string? authority = null, + long? authorityCode = null, + string? name = null) + { + return new ConcatenatedOperation( + CloneCoordinateOperations(concatenatedOperation.Steps), + CloneCoordinateSystem(concatenatedOperation.SourceCoordinateSystem), + CloneCoordinateSystem(concatenatedOperation.TargetCoordinateSystem), + name ?? concatenatedOperation.Name, + authority ?? concatenatedOperation.Authority, + authorityCode ?? concatenatedOperation.AuthorityCode, + concatenatedOperation.Alias, + concatenatedOperation.Abbreviation, + concatenatedOperation.Remarks); + } + + private static List CloneProjectionParameters(IProjection projection) + { + var clone = new List(projection.NumParameters); + for (int i = 0; i < projection.NumParameters; i++) + { + ProjectionParameter parameter = projection.GetParameter(i); + clone.Add(new ProjectionParameter(parameter.Name, parameter.Value)); + } + + return clone; + } + + private static List CloneParameters(IReadOnlyList parameters) + { + var clone = new List(parameters.Count); + for (int i = 0; i < parameters.Count; i++) + { + clone.Add(new Parameter(parameters[i].Name, parameters[i].Value)); + } + + return clone; + } + + private static List CloneCoordinateOperations(IReadOnlyList steps) + { + var clone = new List(steps.Count); + for (int i = 0; i < steps.Count; i++) + { + clone.Add(CloneCoordinateOperation(steps[i])); + } + + return clone; + } + + private static BoundTransformation CloneBoundTransformation(BoundTransformation transformation) + { + if (transformation.Wgs84Parameters is not null) + { + return new BoundTransformation( + transformation.MethodName, + CloneWgs84ConversionInfo(transformation.Wgs84Parameters)); + } + + return new BoundTransformation( + transformation.MethodName, + ArgumentGuard.ThrowIfNull(transformation.ParameterFileName, nameof(transformation.ParameterFileName))); + } + + private static VerticalBoundGridTransformation? CloneVerticalBoundGridTransformation(VerticalBoundGridTransformation? transformation) + { + if (transformation is null) + { + return null; + } + + return new VerticalBoundGridTransformation( + transformation.MethodName, + transformation.ParameterFileName, + CloneCompoundCoordinateSystem(transformation.HubCoordinateSystem)); + } +} diff --git a/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.cs b/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.cs new file mode 100644 index 00000000..b6fcf900 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/InfoAuthorityCloneHelper.cs @@ -0,0 +1,687 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Rebuilds immutable info-backed model objects while replacing top-level metadata for the typed With* APIs. +/// +internal static partial class InfoAuthorityCloneHelper +{ + /// + /// Creates a deep clone of the supplied angular unit with replacement authority metadata. + /// + /// Unit to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned angular unit with the requested authority metadata. + internal static AngularUnit CloneWithAuthority(AngularUnit angularUnit, string authority, long authorityCode) + { + angularUnit = ArgumentGuard.ThrowIfNull(angularUnit, nameof(angularUnit)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneAngularUnit(angularUnit, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied linear unit with replacement authority metadata. + /// + /// Unit to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned linear unit with the requested authority metadata. + internal static LinearUnit CloneWithAuthority(LinearUnit linearUnit, string authority, long authorityCode) + { + linearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneLinearUnit(linearUnit, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied parametric unit with replacement authority metadata. + /// + /// Unit to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned parametric unit with the requested authority metadata. + internal static ParametricUnit CloneWithAuthority(ParametricUnit parametricUnit, string authority, long authorityCode) + { + parametricUnit = ArgumentGuard.ThrowIfNull(parametricUnit, nameof(parametricUnit)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneParametricUnit(parametricUnit, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied time unit with replacement authority metadata. + /// + /// Unit to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned time unit with the requested authority metadata. + internal static TimeUnit CloneWithAuthority(TimeUnit timeUnit, string authority, long authorityCode) + { + timeUnit = ArgumentGuard.ThrowIfNull(timeUnit, nameof(timeUnit)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneTimeUnit(timeUnit, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied ellipsoid with replacement authority metadata. + /// + /// Ellipsoid to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned ellipsoid with the requested authority metadata. + internal static Ellipsoid CloneWithAuthority(Ellipsoid ellipsoid, string authority, long authorityCode) + { + ellipsoid = ArgumentGuard.ThrowIfNull(ellipsoid, nameof(ellipsoid)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneEllipsoid(ellipsoid, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied prime meridian with replacement authority metadata. + /// + /// Prime meridian to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned prime meridian with the requested authority metadata. + internal static PrimeMeridian CloneWithAuthority(PrimeMeridian primeMeridian, string authority, long authorityCode) + { + primeMeridian = ArgumentGuard.ThrowIfNull(primeMeridian, nameof(primeMeridian)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return ClonePrimeMeridian(primeMeridian, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied horizontal datum with replacement authority metadata. + /// + /// Datum to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned horizontal datum with the requested authority metadata. + internal static HorizontalDatum CloneWithAuthority(HorizontalDatum horizontalDatum, string authority, long authorityCode) + { + horizontalDatum = ArgumentGuard.ThrowIfNull(horizontalDatum, nameof(horizontalDatum)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneHorizontalDatum(horizontalDatum, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied vertical datum with replacement authority metadata. + /// + /// Datum to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned vertical datum with the requested authority metadata. + internal static VerticalDatum CloneWithAuthority(VerticalDatum verticalDatum, string authority, long authorityCode) + { + verticalDatum = ArgumentGuard.ThrowIfNull(verticalDatum, nameof(verticalDatum)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneVerticalDatum(verticalDatum, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied engineering datum with replacement authority metadata. + /// + /// Datum to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned engineering datum with the requested authority metadata. + internal static EngineeringDatum CloneWithAuthority(EngineeringDatum engineeringDatum, string authority, long authorityCode) + { + engineeringDatum = ArgumentGuard.ThrowIfNull(engineeringDatum, nameof(engineeringDatum)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneEngineeringDatum(engineeringDatum, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied parametric datum with replacement authority metadata. + /// + /// Datum to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned parametric datum with the requested authority metadata. + internal static ParametricDatum CloneWithAuthority(ParametricDatum parametricDatum, string authority, long authorityCode) + { + parametricDatum = ArgumentGuard.ThrowIfNull(parametricDatum, nameof(parametricDatum)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneParametricDatum(parametricDatum, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied temporal datum with replacement authority metadata. + /// + /// Datum to clone. + /// Replacement authority name. + /// Replacement authority code. + /// A cloned temporal datum with the requested authority metadata. + internal static TemporalDatum CloneWithAuthority(TemporalDatum temporalDatum, string authority, long authorityCode) + { + temporalDatum = ArgumentGuard.ThrowIfNull(temporalDatum, nameof(temporalDatum)); + authority = ArgumentGuard.ThrowIfNull(authority, nameof(authority)); + return CloneTemporalDatum(temporalDatum, authority, authorityCode); + } + + /// + /// Creates a deep clone of the supplied horizontal datum with replacement WGS84 conversion parameters. + /// + /// Datum to clone. + /// Replacement WGS84 conversion parameters, or to clear them. + /// A cloned datum with the requested WGS84 conversion parameters. + internal static HorizontalDatum CloneWithWgs84Parameters(HorizontalDatum horizontalDatum, Wgs84ConversionInfo? wgs84Parameters) + { + horizontalDatum = ArgumentGuard.ThrowIfNull(horizontalDatum, nameof(horizontalDatum)); + return CloneHorizontalDatum(horizontalDatum, wgs84Parameters); + } + + /// + /// Creates a deep clone of the supplied angular unit with a replacement name. + /// + /// Unit to clone. + /// Replacement name. + /// A cloned angular unit with the requested name. + internal static AngularUnit CloneWithName(AngularUnit angularUnit, string name) + { + angularUnit = ArgumentGuard.ThrowIfNull(angularUnit, nameof(angularUnit)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneAngularUnit(angularUnit, name: name); + } + + /// + /// Creates a deep clone of the supplied linear unit with a replacement name. + /// + /// Unit to clone. + /// Replacement name. + /// A cloned linear unit with the requested name. + internal static LinearUnit CloneWithName(LinearUnit linearUnit, string name) + { + linearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneLinearUnit(linearUnit, name: name); + } + + /// + /// Creates a deep clone of the supplied parametric unit with a replacement name. + /// + /// Unit to clone. + /// Replacement name. + /// A cloned parametric unit with the requested name. + internal static ParametricUnit CloneWithName(ParametricUnit parametricUnit, string name) + { + parametricUnit = ArgumentGuard.ThrowIfNull(parametricUnit, nameof(parametricUnit)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneParametricUnit(parametricUnit, name: name); + } + + /// + /// Creates a deep clone of the supplied time unit with a replacement name. + /// + /// Unit to clone. + /// Replacement name. + /// A cloned time unit with the requested name. + internal static TimeUnit CloneWithName(TimeUnit timeUnit, string name) + { + timeUnit = ArgumentGuard.ThrowIfNull(timeUnit, nameof(timeUnit)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneTimeUnit(timeUnit, name: name); + } + + /// + /// Creates a deep clone of the supplied ellipsoid with a replacement name. + /// + /// Ellipsoid to clone. + /// Replacement name. + /// A cloned ellipsoid with the requested name. + internal static Ellipsoid CloneWithName(Ellipsoid ellipsoid, string name) + { + ellipsoid = ArgumentGuard.ThrowIfNull(ellipsoid, nameof(ellipsoid)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneEllipsoid(ellipsoid, name: name); + } + + /// + /// Creates a deep clone of the supplied prime meridian with a replacement name. + /// + /// Prime meridian to clone. + /// Replacement name. + /// A cloned prime meridian with the requested name. + internal static PrimeMeridian CloneWithName(PrimeMeridian primeMeridian, string name) + { + primeMeridian = ArgumentGuard.ThrowIfNull(primeMeridian, nameof(primeMeridian)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return ClonePrimeMeridian(primeMeridian, name: name); + } + + /// + /// Creates a deep clone of the supplied horizontal datum with a replacement name. + /// + /// Datum to clone. + /// Replacement name. + /// A cloned horizontal datum with the requested name. + internal static HorizontalDatum CloneWithName(HorizontalDatum horizontalDatum, string name) + { + horizontalDatum = ArgumentGuard.ThrowIfNull(horizontalDatum, nameof(horizontalDatum)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneHorizontalDatum(horizontalDatum, name: name); + } + + /// + /// Creates a deep clone of the supplied vertical datum with a replacement name. + /// + /// Datum to clone. + /// Replacement name. + /// A cloned vertical datum with the requested name. + internal static VerticalDatum CloneWithName(VerticalDatum verticalDatum, string name) + { + verticalDatum = ArgumentGuard.ThrowIfNull(verticalDatum, nameof(verticalDatum)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneVerticalDatum(verticalDatum, name: name); + } + + /// + /// Creates a deep clone of the supplied engineering datum with a replacement name. + /// + /// Datum to clone. + /// Replacement name. + /// A cloned engineering datum with the requested name. + internal static EngineeringDatum CloneWithName(EngineeringDatum engineeringDatum, string name) + { + engineeringDatum = ArgumentGuard.ThrowIfNull(engineeringDatum, nameof(engineeringDatum)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneEngineeringDatum(engineeringDatum, name: name); + } + + /// + /// Creates a deep clone of the supplied parametric datum with a replacement name. + /// + /// Datum to clone. + /// Replacement name. + /// A cloned parametric datum with the requested name. + internal static ParametricDatum CloneWithName(ParametricDatum parametricDatum, string name) + { + parametricDatum = ArgumentGuard.ThrowIfNull(parametricDatum, nameof(parametricDatum)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneParametricDatum(parametricDatum, name: name); + } + + /// + /// Creates a deep clone of the supplied temporal datum with a replacement name. + /// + /// Datum to clone. + /// Replacement name. + /// A cloned temporal datum with the requested name. + internal static TemporalDatum CloneWithName(TemporalDatum temporalDatum, string name) + { + temporalDatum = ArgumentGuard.ThrowIfNull(temporalDatum, nameof(temporalDatum)); + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + return CloneTemporalDatum(temporalDatum, name: name); + } + + /// + /// Creates a deep clone of the supplied datum with replacement retained datum-ensemble metadata. + /// + /// Datum to clone. + /// Replacement ensemble metadata, or to clear it. + /// A cloned datum of the same runtime type when supported. + internal static Datum CloneWithEnsemble(Datum datum, DatumEnsemble? ensemble) + { + datum = ArgumentGuard.ThrowIfNull(datum, nameof(datum)); + + return datum switch + { + HorizontalDatum horizontalDatum => CloneHorizontalDatum(horizontalDatum, ensemble), + VerticalDatum verticalDatum => CloneVerticalDatum(verticalDatum, ensemble), + EngineeringDatum engineeringDatum when ensemble is null => CloneEngineeringDatum(engineeringDatum), + ParametricDatum parametricDatum when ensemble is null => CloneParametricDatum(parametricDatum), + TemporalDatum temporalDatum when ensemble is null => CloneTemporalDatum(temporalDatum), + _ => throw new NotSupportedException($"Datum ensembles are not supported for datum type '{datum.GetType().FullName}'."), + }; + } + + /// + /// Creates a deep clone of the supplied horizontal datum with replacement retained datum-ensemble metadata. + /// + /// Datum to clone. + /// Replacement ensemble metadata, or to clear it. + /// A cloned horizontal datum with the requested ensemble metadata. + internal static HorizontalDatum CloneWithEnsemble(HorizontalDatum horizontalDatum, DatumEnsemble? ensemble) + { + horizontalDatum = ArgumentGuard.ThrowIfNull(horizontalDatum, nameof(horizontalDatum)); + return CloneHorizontalDatum(horizontalDatum, ensemble); + } + + /// + /// Creates a deep clone of the supplied vertical datum with replacement retained datum-ensemble metadata. + /// + /// Datum to clone. + /// Replacement ensemble metadata, or to clear it. + /// A cloned vertical datum with the requested ensemble metadata. + internal static VerticalDatum CloneWithEnsemble(VerticalDatum verticalDatum, DatumEnsemble? ensemble) + { + verticalDatum = ArgumentGuard.ThrowIfNull(verticalDatum, nameof(verticalDatum)); + return CloneVerticalDatum(verticalDatum, ensemble); + } + + /// + /// Creates a deep clone of the supplied engineering datum with replacement retained datum-ensemble metadata. + /// + /// Datum to clone. + /// Replacement ensemble metadata, or to keep the datum non-ensemble-backed. + /// A cloned engineering datum with the requested ensemble metadata. + /// Thrown when is not . + internal static EngineeringDatum CloneWithEnsemble(EngineeringDatum engineeringDatum, DatumEnsemble? ensemble) + { + engineeringDatum = ArgumentGuard.ThrowIfNull(engineeringDatum, nameof(engineeringDatum)); + if (ensemble is not null) + { + throw new NotSupportedException($"Datum ensembles are not supported for datum type '{engineeringDatum.GetType().FullName}'."); + } + + return CloneEngineeringDatum(engineeringDatum); + } + + /// + /// Creates a deep clone of the supplied parametric datum with replacement retained datum-ensemble metadata. + /// + /// Datum to clone. + /// Replacement ensemble metadata, or to keep the datum non-ensemble-backed. + /// A cloned parametric datum with the requested ensemble metadata. + /// Thrown when is not . + internal static ParametricDatum CloneWithEnsemble(ParametricDatum parametricDatum, DatumEnsemble? ensemble) + { + parametricDatum = ArgumentGuard.ThrowIfNull(parametricDatum, nameof(parametricDatum)); + if (ensemble is not null) + { + throw new NotSupportedException($"Datum ensembles are not supported for datum type '{parametricDatum.GetType().FullName}'."); + } + + return CloneParametricDatum(parametricDatum); + } + + /// + /// Creates a deep clone of the supplied temporal datum with replacement retained datum-ensemble metadata. + /// + /// Datum to clone. + /// Replacement ensemble metadata, or to keep the datum non-ensemble-backed. + /// A cloned temporal datum with the requested ensemble metadata. + /// Thrown when is not . + internal static TemporalDatum CloneWithEnsemble(TemporalDatum temporalDatum, DatumEnsemble? ensemble) + { + temporalDatum = ArgumentGuard.ThrowIfNull(temporalDatum, nameof(temporalDatum)); + if (ensemble is not null) + { + throw new NotSupportedException($"Datum ensembles are not supported for datum type '{temporalDatum.GetType().FullName}'."); + } + + return CloneTemporalDatum(temporalDatum); + } + + private static AngularUnit CloneAngularUnit(AngularUnit angularUnit, string? authority = null, long? authorityCode = null, string? name = null) + { + return new AngularUnit( + angularUnit.RadiansPerUnit, + name ?? angularUnit.Name, + authority ?? angularUnit.Authority, + authorityCode ?? angularUnit.AuthorityCode, + angularUnit.Alias, + angularUnit.Abbreviation, + angularUnit.Remarks); + } + + private static LinearUnit CloneLinearUnit(LinearUnit linearUnit, string? authority = null, long? authorityCode = null, string? name = null) + { + return new LinearUnit( + linearUnit.MetersPerUnit, + name ?? linearUnit.Name, + authority ?? linearUnit.Authority, + authorityCode ?? linearUnit.AuthorityCode, + linearUnit.Alias, + linearUnit.Abbreviation, + linearUnit.Remarks); + } + + private static Unit CloneUnit(Unit unit, string? authority = null, long? authorityCode = null, string? name = null) + { + return new Unit( + unit.ConversionFactor, + name ?? unit.Name, + authority ?? unit.Authority, + authorityCode ?? unit.AuthorityCode, + unit.Alias, + unit.Abbreviation, + unit.Remarks); + } + + private static ParametricUnit CloneParametricUnit(ParametricUnit parametricUnit, string? authority = null, long? authorityCode = null, string? name = null) + { + return new ParametricUnit( + parametricUnit.ConversionFactor, + name ?? parametricUnit.Name, + authority ?? parametricUnit.Authority, + authorityCode ?? parametricUnit.AuthorityCode, + parametricUnit.Alias, + parametricUnit.Abbreviation, + parametricUnit.Remarks); + } + + private static TimeUnit CloneTimeUnit(TimeUnit timeUnit, string? authority = null, long? authorityCode = null, string? name = null) + { + return new TimeUnit( + timeUnit.ConversionFactor, + name ?? timeUnit.Name, + authority ?? timeUnit.Authority, + authorityCode ?? timeUnit.AuthorityCode, + timeUnit.Alias, + timeUnit.Abbreviation, + timeUnit.Remarks); + } + + private static Ellipsoid CloneEllipsoid(Ellipsoid ellipsoid, string? authority = null, long? authorityCode = null, string? name = null) + { + return new Ellipsoid( + ellipsoid.SemiMajorAxis, + ellipsoid.SemiMinorAxis, + ellipsoid.InverseFlattening, + ellipsoid.IsIvfDefinitive, + CloneLinearUnit(ellipsoid.AxisUnit), + name ?? ellipsoid.Name, + authority ?? ellipsoid.Authority, + authorityCode ?? ellipsoid.AuthorityCode, + ellipsoid.Alias, + ellipsoid.Abbreviation, + ellipsoid.Remarks); + } + + private static PrimeMeridian ClonePrimeMeridian(PrimeMeridian primeMeridian, string? authority = null, long? authorityCode = null, string? name = null) + { + return new PrimeMeridian( + primeMeridian.Longitude, + CloneAngularUnit(primeMeridian.AngularUnit), + name ?? primeMeridian.Name, + authority ?? primeMeridian.Authority, + authorityCode ?? primeMeridian.AuthorityCode, + primeMeridian.Alias, + primeMeridian.Abbreviation, + primeMeridian.Remarks); + } + + private static HorizontalDatum CloneHorizontalDatum(HorizontalDatum horizontalDatum, string? authority = null, long? authorityCode = null, string? name = null) + { + Ellipsoid ellipsoid = CloneEllipsoid(horizontalDatum.Ellipsoid); + return new HorizontalDatum( + ellipsoid, + CloneOptionalWgs84ConversionInfo(horizontalDatum.Wgs84Parameters), + horizontalDatum.DatumType, + name ?? horizontalDatum.Name, + authority ?? horizontalDatum.Authority, + authorityCode ?? horizontalDatum.AuthorityCode, + horizontalDatum.Alias, + horizontalDatum.Remarks, + horizontalDatum.Abbreviation, + CloneDatumEnsemble(horizontalDatum.Ensemble, ellipsoid)); + } + + private static HorizontalDatum CloneHorizontalDatum(HorizontalDatum horizontalDatum, Wgs84ConversionInfo? wgs84Parameters) + { + Ellipsoid ellipsoid = CloneEllipsoid(horizontalDatum.Ellipsoid); + return new HorizontalDatum( + ellipsoid, + CloneOptionalWgs84ConversionInfo(wgs84Parameters), + horizontalDatum.DatumType, + horizontalDatum.Name, + horizontalDatum.Authority, + horizontalDatum.AuthorityCode, + horizontalDatum.Alias, + horizontalDatum.Remarks, + horizontalDatum.Abbreviation, + CloneDatumEnsemble(horizontalDatum.Ensemble, ellipsoid)); + } + + private static HorizontalDatum CloneHorizontalDatum(HorizontalDatum horizontalDatum, DatumEnsemble? ensemble) + { + Ellipsoid ellipsoid = CloneEllipsoid(horizontalDatum.Ellipsoid); + return new HorizontalDatum( + ellipsoid, + CloneOptionalWgs84ConversionInfo(horizontalDatum.Wgs84Parameters), + horizontalDatum.DatumType, + horizontalDatum.Name, + horizontalDatum.Authority, + horizontalDatum.AuthorityCode, + horizontalDatum.Alias, + horizontalDatum.Remarks, + horizontalDatum.Abbreviation, + CloneDatumEnsemble(ensemble, ellipsoid)); + } + + private static VerticalDatum CloneVerticalDatum(VerticalDatum verticalDatum, string? authority = null, long? authorityCode = null, string? name = null) + { + return new VerticalDatum( + verticalDatum.DatumType, + name ?? verticalDatum.Name, + authority ?? verticalDatum.Authority, + authorityCode ?? verticalDatum.AuthorityCode, + verticalDatum.Alias, + verticalDatum.Remarks, + verticalDatum.Abbreviation, + CloneDatumEnsemble(verticalDatum.Ensemble)); + } + + private static VerticalDatum CloneVerticalDatum(VerticalDatum verticalDatum, DatumEnsemble? ensemble) + { + return new VerticalDatum( + verticalDatum.DatumType, + verticalDatum.Name, + verticalDatum.Authority, + verticalDatum.AuthorityCode, + verticalDatum.Alias, + verticalDatum.Remarks, + verticalDatum.Abbreviation, + CloneDatumEnsemble(ensemble)); + } + + private static EngineeringDatum CloneEngineeringDatum(EngineeringDatum engineeringDatum, string? authority = null, long? authorityCode = null, string? name = null) + { + return new EngineeringDatum( + name ?? engineeringDatum.Name, + authority ?? engineeringDatum.Authority, + authorityCode ?? engineeringDatum.AuthorityCode, + engineeringDatum.Alias, + engineeringDatum.Remarks, + engineeringDatum.Abbreviation); + } + + private static ParametricDatum CloneParametricDatum(ParametricDatum parametricDatum, string? authority = null, long? authorityCode = null, string? name = null) + { + return new ParametricDatum( + name ?? parametricDatum.Name, + authority ?? parametricDatum.Authority, + authorityCode ?? parametricDatum.AuthorityCode, + parametricDatum.Alias, + parametricDatum.Remarks, + parametricDatum.Abbreviation); + } + + private static TemporalDatum CloneTemporalDatum(TemporalDatum temporalDatum, string? authority = null, long? authorityCode = null, string? name = null) + { + return new TemporalDatum( + temporalDatum.TimeOrigin, + name ?? temporalDatum.Name, + authority ?? temporalDatum.Authority, + authorityCode ?? temporalDatum.AuthorityCode, + temporalDatum.Alias, + temporalDatum.Remarks, + temporalDatum.Abbreviation); + } + + private static IUnit CloneUnit(IUnit unit) + { + return unit switch + { + AngularUnit angularUnit => CloneAngularUnit(angularUnit), + LinearUnit linearUnit => CloneLinearUnit(linearUnit), + Unit genericUnit => CloneUnit(genericUnit), + ParametricUnit parametricUnit => CloneParametricUnit(parametricUnit), + TimeUnit timeUnit => CloneTimeUnit(timeUnit), + _ => throw new NotSupportedException($"Unit cloning is not supported for type '{unit.GetType().FullName}'."), + }; + } + + private static DatumEnsemble? CloneDatumEnsemble(DatumEnsemble? ensemble, Ellipsoid? ellipsoidOverride = null) + { + if (ensemble is null) + { + return null; + } + + return new DatumEnsemble( + ensemble.Name, + CloneDatumEnsembleMembers(ensemble.Members), + ensemble.Accuracy, + ellipsoidOverride ?? (ensemble.Ellipsoid is null ? null : CloneEllipsoid(ensemble.Ellipsoid)), + ensemble.Authority, + ensemble.AuthorityCode); + } + + private static List CloneDatumEnsembleMembers(IReadOnlyList members) + { + var clone = new List(members.Count); + for (int i = 0; i < members.Count; i++) + { + DatumEnsembleMember member = members[i]; + clone.Add(new DatumEnsembleMember(member.Name, member.Authority, member.AuthorityCode)); + } + + return clone; + } + + private static List CloneWgs84ConversionInfoList(List conversions) + { + var clone = new List(conversions.Count); + for (int i = 0; i < conversions.Count; i++) + { + clone.Add(CloneWgs84ConversionInfo(conversions[i])); + } + + return clone; + } + + private static Wgs84ConversionInfo CloneWgs84ConversionInfo(Wgs84ConversionInfo conversionInfo) + { + return new Wgs84ConversionInfo( + conversionInfo.Dx, + conversionInfo.Dy, + conversionInfo.Dz, + conversionInfo.Ex, + conversionInfo.Ey, + conversionInfo.Ez, + conversionInfo.Ppm, + conversionInfo.AreaOfUse); + } + + private static Wgs84ConversionInfo? CloneOptionalWgs84ConversionInfo(Wgs84ConversionInfo? conversionInfo) + => conversionInfo is null ? null : CloneWgs84ConversionInfo(conversionInfo); +} diff --git a/src/ProjNet/CoordinateSystems/LinearUnit.cs b/src/ProjNet/CoordinateSystems/LinearUnit.cs index f387fad8..2b632600 100644 --- a/src/ProjNet/CoordinateSystems/LinearUnit.cs +++ b/src/ProjNet/CoordinateSystems/LinearUnit.cs @@ -1,143 +1,176 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; +using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// Definition of linear units. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// The predefined linear-unit accessors are thread-safe because they only expose immutable value objects. +/// +/// +public class LinearUnit : Info, IUnit { - /// - /// Definition of linear units. + /// + /// Initializes a new instance of the class. + /// + /// Number of meters per . + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public LinearUnit(double metersPerUnit, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + this.MetersPerUnit = metersPerUnit; + } + + /// + /// Gets the meters linear unit. + /// Also known as International metre. SI standard unit. + /// + public static LinearUnit Metre => new(1.0, "metre", "EPSG", 9001, "m", string.Empty, "Also known as International metre. SI standard unit."); + + /// + /// Gets the foot linear unit (1ft = 0.3048m). + /// + public static LinearUnit Foot => new(0.3048, "foot", "EPSG", 9002, "ft", string.Empty, string.Empty); + + /// + /// Gets the US Survey foot linear unit (1ftUS = 0.304800609601219m). + /// + public static LinearUnit USSurveyFoot => new(0.304800609601219, "US survey foot", "EPSG", 9003, "American foot", "ftUS", "Used in USA."); + + /// + /// Gets the Nautical Mile linear unit (1NM = 1852m). /// - [Serializable] - public class LinearUnit : Info, IUnit - { - /// - /// Creates an instance of a linear unit - /// - /// Number of meters per - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - public LinearUnit(double metersPerUnit, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) - : - base(name, authority, authorityCode, alias, abbreviation, remarks) - { - MetersPerUnit = metersPerUnit; - } - - #region Predefined units - /// - /// Returns the meters linear unit. - /// Also known as International metre. SI standard unit. - /// - public static LinearUnit Metre - { - get { return new LinearUnit(1.0,"metre", "EPSG", 9001, "m", string.Empty, "Also known as International metre. SI standard unit."); } - } - /// - /// Returns the foot linear unit (1ft = 0.3048m). - /// - public static LinearUnit Foot - { - get { return new LinearUnit(0.3048, "foot", "EPSG", 9002, "ft", string.Empty, string.Empty); } - } - /// - /// Returns the US Survey foot linear unit (1ftUS = 0.304800609601219m). - /// - public static LinearUnit USSurveyFoot - { - get { return new LinearUnit(0.304800609601219, "US survey foot", "EPSG", 9003, "American foot", "ftUS", "Used in USA."); } - } - /// - /// Returns the Nautical Mile linear unit (1NM = 1852m). - /// - public static LinearUnit NauticalMile - { - get { return new LinearUnit(1852, "nautical mile", "EPSG", 9030, "NM", string.Empty, string.Empty); } - } - - /// - /// Returns Clarke's foot. - /// - /// - /// Assumes Clarke's 1865 ratio of 1 British foot = 0.3047972654 French legal metres applies to the international metre. - /// Used in older Australian, southern African & British West Indian mapping. - /// - public static LinearUnit ClarkesFoot - { - get { return new LinearUnit(0.3047972654, "Clarke's foot", "EPSG", 9005, "Clarke's foot", string.Empty, "Assumes Clarke's 1865 ratio of 1 British foot = 0.3047972654 French legal metres applies to the international metre. Used in older Australian, southern African & British West Indian mapping."); } - } - #endregion - - #region ILinearUnit Members - - - /// - /// Gets or sets the number of meters per . - /// - public double MetersPerUnit { get; set; } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, "UNIT[\"{0}\", {1}", Name, MetersPerUnit); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, "{1}", MetersPerUnit, InfoXml); - } - } - - #endregion - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is LinearUnit)) - return false; - return (obj as LinearUnit).MetersPerUnit == this.MetersPerUnit; - } - } + public static LinearUnit NauticalMile => new(1852, "nautical mile", "EPSG", 9030, "NM", string.Empty, string.Empty); + + /// + /// Gets clarke's foot. + /// + /// + /// Assumes Clarke's 1865 ratio of 1 British foot = 0.3047972654 French legal metres applies to the international metre. + /// Used in older Australian, southern African & British West Indian mapping. + /// + public static LinearUnit ClarkesFoot => new(0.3047972654, "Clarke's foot", "EPSG", 9005, "Clarke's foot", string.Empty, "Assumes Clarke's 1865 ratio of 1 British foot = 0.3047972654 French legal metres applies to the international metre. Used in older Australian, southern African & British West Indian mapping."); + + /// + /// Gets the number of meters per . + /// + public double MetersPerUnit { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this unit with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new LinearUnit WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this unit with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new LinearUnit WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this linear unit as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_LinearUnit", + new XAttribute("MetersPerUnit", this.MetersPerUnit.ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this linear unit to a WKT syntax tree node. + /// + /// A representing this linear unit. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.MetersPerUnit), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("UNIT", children); + } + + /// + /// Converts this linear unit to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this linear unit in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.MetersPerUnit), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("LENGTHUNIT", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is LinearUnit linearUnit && linearUnit.MetersPerUnit == this.MetersPerUnit; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); } diff --git a/src/ProjNet/CoordinateSystems/Parameter.cs b/src/ProjNet/CoordinateSystems/Parameter.cs index c22ecf5b..314e002c 100644 --- a/src/ProjNet/CoordinateSystems/Parameter.cs +++ b/src/ProjNet/CoordinateSystems/Parameter.cs @@ -1,50 +1,35 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; -namespace ProjNet.CoordinateSystems +/// +/// A named parameter value. +/// +public sealed class Parameter { /// - /// A named parameter value. + /// Initializes a new instance of the class. /// - [Serializable] - public class Parameter + /// Units are always either meters or degrees. + /// Name of parameter. + /// Value. + public Parameter(string name, double value) { - /// - /// Creates an instance of a parameter - /// - /// Units are always either meters or degrees. - /// Name of parameter - /// Value - public Parameter(string name, double value) - { - Name = name; - Value = value; - } + this.Name = name; + this.Value = value; + } - /// - /// Parameter name - /// - public string Name { get; set; } + /// + /// Gets parameter name. + /// + public string Name { get; } - /// - /// Parameter value - /// - public double Value { get; set; } - } + /// + /// Gets parameter value. + /// + public double Value { get; } } diff --git a/src/ProjNet/CoordinateSystems/ParameterInfo.cs b/src/ProjNet/CoordinateSystems/ParameterInfo.cs index b94d0825..23fbebeb 100644 --- a/src/ProjNet/CoordinateSystems/ParameterInfo.cs +++ b/src/ProjNet/CoordinateSystems/ParameterInfo.cs @@ -1,85 +1,57 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; -namespace ProjNet.CoordinateSystems +/// +/// Simple class that implements the IParameterInfo interface for providing general set of the parameters. +/// It allows discovering the names, and for setting and getting parameter values. +/// +internal sealed class ParameterInfo { /// - /// Simple class that implements the IParameterInfo interface for providing general set of the parameters. - /// It allows discovering the names, and for setting and getting parameter values. + /// Gets the number of parameters expected. /// - [Serializable] - internal class ParameterInfo - { - /// - /// Gets the number of parameters expected. - /// - public int NumParameters - { - get - { - if (Parameters != null) - { - return Parameters.Count; - } - return 0; - } - } + public int NumParameters => this.Parameters?.Count ?? 0; - /// - /// Gets or sets the parameters set for this projection. - /// - public List Parameters - { - get; - set; - } + /// + /// Gets or sets the parameters set for this projection. + /// + public List? Parameters + { + get; + set; + } - /// - /// Returns the default parameters for this projection. - /// - /// - public Parameter[] DefaultParameters () - { - return new Parameter[0]; - } + /// + /// Returns the default parameters for this projection. + /// + /// The transformation result. + public Parameter[] DefaultParameters() => []; - /// - /// Gets the parameter by its name - /// - /// - /// - public Parameter GetParameterByName (string name) + /// + /// Gets the parameter by its name. + /// + /// The name parameter. + /// The matching parameter, or when not found. + public Parameter? GetParameterByName(string name) + { + if (this.Parameters is not null) { - if (Parameters != null) + // search parameter collection by name + foreach (Parameter? param in this.Parameters) { - //search parameter collection by name - foreach (var param in Parameters) + if (param is not null && param.Name == name) { - if (param != null && param.Name == name) - { - return param; - } + return param; } } - - return null; } + + return null; } } diff --git a/src/ProjNet/CoordinateSystems/ParametricCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/ParametricCoordinateSystem.cs new file mode 100644 index 00000000..0c4dee4d --- /dev/null +++ b/src/ProjNet/CoordinateSystems/ParametricCoordinateSystem.cs @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A one-dimensional parametric coordinate system. +/// +public sealed class ParametricCoordinateSystem : CoordinateSystem +{ + /// + /// Initializes a new instance of the class. + /// + /// Parametric unit. + /// Parametric datum. + /// Axis information. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public ParametricCoordinateSystem( + ParametricUnit parametricUnit, + ParametricDatum parametricDatum, + AxisInfo axisInfo, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks, CreateAxisInfo(axisInfo), null) + { + this.ParametricUnit = ArgumentGuard.ThrowIfNull(parametricUnit, nameof(parametricUnit)); + this.ParametricDatum = ArgumentGuard.ThrowIfNull(parametricDatum, nameof(parametricDatum)); + } + + /// + /// Gets the parametric datum. + /// + public ParametricDatum ParametricDatum { get; } + + /// + /// Gets the parametric unit. + /// + public ParametricUnit ParametricUnit { get; } + + /// + public override string WKT => this.ToWktNode(WktVersion.Wkt22019).ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new ParametricCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new ParametricCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + public override XElement ToXml() + { + var innerElement = new XElement("CS_ParametricCoordinateSystem"); + innerElement.Add(this.InfoXmlElement); + innerElement.Add(this.GetAxis(0).ToXml()); + innerElement.Add(this.ParametricDatum.ToXml()); + innerElement.Add(this.ParametricUnit.ToXml()); + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override IUnit GetUnits(int dimension) + { + if (dimension != 0) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(dimension), "Parametric coordinate systems have only one dimension."); + } + + return this.ParametricUnit; + } + + /// + public override WktNode ToWktNode() + { + return this.ToWktNode(WktVersion.Wkt22019); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return new WktKeywordNode( + "LOCAL_CS", + new WktQuotedString(this.Name), + this.ParametricDatum.ToWktNode(), + this.ParametricUnit.ToWktNode(), + this.GetAxis(0).ToWktNode()); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.ParametricDatum.ToWktNode(version), + new WktKeywordNode( + "CS", + new WktIdentifier("parametric"), + new WktInteger(this.Dimension)), + this.GetAxis(0).ToWktNode(version), + this.ParametricUnit.ToWktNode(version), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("PARAMETRICCRS", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is ParametricCoordinateSystem parametricCoordinateSystem + && parametricCoordinateSystem.ParametricDatum.EqualParams(this.ParametricDatum) + && parametricCoordinateSystem.ParametricUnit.EqualParams(this.ParametricUnit) + && parametricCoordinateSystem.GetAxis(0).Orientation == this.GetAxis(0).Orientation; + } + + private static List CreateAxisInfo(AxisInfo axisInfo) + => [ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo))]; +} diff --git a/src/ProjNet/CoordinateSystems/ParametricDatum.cs b/src/ProjNet/CoordinateSystems/ParametricDatum.cs new file mode 100644 index 00000000..7559bd52 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/ParametricDatum.cs @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System.Collections.Generic; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A parametric datum used by parametric coordinate reference systems. +/// +public sealed class ParametricDatum : Datum +{ + /// + /// Initializes a new instance of the class. + /// + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Provider-supplied remarks. + /// Abbreviation. + public ParametricDatum(string name, string authority, long authorityCode, string alias, string remarks, string abbreviation) + : base(DatumType.PD_Other, name, authority, authorityCode, alias, remarks, abbreviation) + { + } + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this datum with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new ParametricDatum WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this datum with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new ParametricDatum WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Creates a copy of this datum with updated retained datum-ensemble metadata. + /// + /// Replacement ensemble metadata, or to keep this datum non-ensemble-backed. + /// A new with updated ensemble metadata. + /// Thrown when is not . + public new ParametricDatum WithEnsemble(DatumEnsemble? ensemble) => InfoAuthorityCloneHelper.CloneWithEnsemble(this, ensemble); + + /// + /// Returns an XML representation of this parametric datum as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement("CS_ParametricDatum"); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this parametric datum to a WKT syntax tree node. + /// + /// A representing this parametric datum. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("LOCAL_DATUM", children); + } + + /// + /// Converts this parametric datum to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this parametric datum in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("PDATUM", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is ParametricDatum parametricDatum && base.EqualParams(parametricDatum); + } +} diff --git a/src/ProjNet/CoordinateSystems/ParametricUnit.cs b/src/ProjNet/CoordinateSystems/ParametricUnit.cs new file mode 100644 index 00000000..5286b375 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/ParametricUnit.cs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// Definition of parametric units. +/// +public sealed class ParametricUnit : Info, IUnit +{ + /// + /// Initializes a new instance of the class. + /// + /// Conversion factor to the underlying parametric reference unit. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public ParametricUnit(double conversionFactor, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + this.ConversionFactor = conversionFactor; + } + + /// + /// Gets the conversion factor to the underlying parametric reference unit. + /// + public double ConversionFactor { get; } + + /// + /// Gets the Well-known text for this object. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this unit with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new ParametricUnit WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this unit with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new ParametricUnit WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this parametric unit as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_ParametricUnit", + new XAttribute("ConversionFactor", this.ConversionFactor.ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this parametric unit to a WKT syntax tree node. + /// + /// A representing this parametric unit. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.ConversionFactor), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("UNIT", children); + } + + /// + /// Converts this parametric unit to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this parametric unit in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.ConversionFactor), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("PARAMETRICUNIT", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is ParametricUnit parametricUnit && parametricUnit.ConversionFactor == this.ConversionFactor; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); +} diff --git a/src/ProjNet/CoordinateSystems/PrimeMeridian.cs b/src/ProjNet/CoordinateSystems/PrimeMeridian.cs index 5ef876e6..2e97e55f 100644 --- a/src/ProjNet/CoordinateSystems/PrimeMeridian.cs +++ b/src/ProjNet/CoordinateSystems/PrimeMeridian.cs @@ -1,207 +1,228 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; +using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A meridian used to take longitude measurements from. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// The predefined prime-meridian accessors are thread-safe because they only expose immutable value objects. +/// +/// +public class PrimeMeridian : Info { - /// - /// A meridian used to take longitude measurements from. - /// - [Serializable] - public class PrimeMeridian : Info - { - /// - /// Initializes a new instance of a prime meridian - /// - /// Longitude of prime meridian - /// Angular unit - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal PrimeMeridian(double longitude, AngularUnit angularUnit, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) - : - base(name, authority, authorityCode, alias, abbreviation, remarks) - { - Longitude = longitude; - AngularUnit = angularUnit; - } - #region Predefined prime meridans - /// - /// Greenwich prime meridian - /// - public static PrimeMeridian Greenwich - { - get { return new PrimeMeridian(0.0, CoordinateSystems.AngularUnit.Degrees, "Greenwich", "EPSG", 8901, string.Empty, string.Empty, string.Empty); } - } - /// - /// Lisbon prime meridian - /// - public static PrimeMeridian Lisbon - { - get { return new PrimeMeridian(-9.0754862, CoordinateSystems.AngularUnit.Degrees, "Lisbon", "EPSG", 8902, string.Empty, string.Empty, string.Empty); } - } - /// - /// Paris prime meridian. - /// Value adopted by IGN (Paris) in 1936. Equivalent to 2 deg 20min 14.025sec. Preferred by EPSG to earlier value of 2deg 20min 13.95sec (2.596898 grads) used by RGS London. - /// - public static PrimeMeridian Paris - { - get { return new PrimeMeridian(2.5969213, CoordinateSystems.AngularUnit.Degrees, "Paris", "EPSG", 8903, string.Empty, string.Empty, "Value adopted by IGN (Paris) in 1936. Equivalent to 2 deg 20min 14.025sec. Preferred by EPSG to earlier value of 2deg 20min 13.95sec (2.596898 grads) used by RGS London."); } - } - /// - /// Bogota prime meridian - /// - public static PrimeMeridian Bogota - { - get { return new PrimeMeridian(-74.04513, CoordinateSystems.AngularUnit.Degrees, "Bogota", "EPSG", 8904, string.Empty, string.Empty, string.Empty); } - } - /// - /// Madrid prime meridian - /// - public static PrimeMeridian Madrid - { - get { return new PrimeMeridian(-3.411658, CoordinateSystems.AngularUnit.Degrees, "Madrid", "EPSG", 8905, string.Empty, string.Empty, string.Empty); } - } - /// - /// Rome prime meridian - /// - public static PrimeMeridian Rome - { - get { return new PrimeMeridian(12.27084, CoordinateSystems.AngularUnit.Degrees, "Rome", "EPSG", 8906, string.Empty, string.Empty, string.Empty); } - } - /// - /// Bern prime meridian. - /// 1895 value. Newer value of 7 deg 26 min 22.335 sec E determined in 1938. - /// - public static PrimeMeridian Bern - { - get { return new PrimeMeridian(7.26225, CoordinateSystems.AngularUnit.Degrees, "Bern", "EPSG", 8907, string.Empty, string.Empty, "1895 value. Newer value of 7 deg 26 min 22.335 sec E determined in 1938."); } - } - /// - /// Jakarta prime meridian - /// - public static PrimeMeridian Jakarta - { - get { return new PrimeMeridian(106.482779, CoordinateSystems.AngularUnit.Degrees, "Jakarta", "EPSG", 8908, string.Empty, string.Empty, string.Empty); } - } - /// - /// Ferro prime meridian. - /// Used in Austria and former Czechoslovakia. - /// - public static PrimeMeridian Ferro - { - get { return new PrimeMeridian(-17.66666666666667, CoordinateSystems.AngularUnit.Degrees, "Ferro", "EPSG", 8909, string.Empty, string.Empty, "Used in Austria and former Czechoslovakia."); } - } - /// - /// Brussels prime meridian - /// - public static PrimeMeridian Brussels - { - get { return new PrimeMeridian(4.220471, CoordinateSystems.AngularUnit.Degrees, "Brussels", "EPSG", 8910, string.Empty, string.Empty, string.Empty); } - } - /// - /// Stockholm prime meridian - /// - public static PrimeMeridian Stockholm - { - get { return new PrimeMeridian(18.03298, CoordinateSystems.AngularUnit.Degrees, "Stockholm", "EPSG", 8911, string.Empty, string.Empty, string.Empty); } - } - /// - /// Athens prime meridian. - /// Used in Greece for older mapping based on Hatt projection. - /// - public static PrimeMeridian Athens - { - get { return new PrimeMeridian(23.4258815, CoordinateSystems.AngularUnit.Degrees, "Athens", "EPSG", 8912, string.Empty, string.Empty, "Used in Greece for older mapping based on Hatt projection."); } - } - /// - /// Oslo prime meridian. - /// Formerly known as Kristiania or Christiania. - /// - public static PrimeMeridian Oslo - { - get { return new PrimeMeridian(10.43225, CoordinateSystems.AngularUnit.Degrees, "Oslo", "EPSG", 8913, string.Empty, string.Empty, "Formerly known as Kristiania or Christiania."); } - } - #endregion - - #region IPrimeMeridian Members - - - /// - /// Gets or sets the longitude of the prime meridian (relative to the Greenwich prime meridian). - /// - public double Longitude { get; set; } - - /// - /// Gets or sets the AngularUnits. - /// - public AngularUnit AngularUnit { get; set; } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, "PRIMEM[\"{0}\", {1}", Name, Longitude); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, - "{1}{2}", Longitude, InfoXml, AngularUnit.XML); - } - } - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is PrimeMeridian)) - return false; - var prime = obj as PrimeMeridian; - return prime.AngularUnit.EqualParams(this.AngularUnit) && prime.Longitude == this.Longitude; - } - - #endregion - - } + /// + /// Initializes a new instance of the class. + /// + /// Longitude of prime meridian. + /// Angular unit. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + internal PrimeMeridian(double longitude, AngularUnit angularUnit, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + this.Longitude = longitude; + this.AngularUnit = angularUnit; + } + + /// + /// Gets the Greenwich prime meridian (0° longitude). + /// + public static PrimeMeridian Greenwich => new(0.0, CoordinateSystems.AngularUnit.Degrees, "Greenwich", "EPSG", 8901, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Lisbon prime meridian. + /// + public static PrimeMeridian Lisbon => new(-9.0754862, CoordinateSystems.AngularUnit.Degrees, "Lisbon", "EPSG", 8902, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Paris prime meridian. + /// + /// + /// Value adopted by IGN (Paris) in 1936. Equivalent to 2 deg 20 min 14.025 sec. + /// Preferred by EPSG over the earlier value of 2 deg 20 min 13.95 sec (2.596898 grads) used by RGS London. + /// + public static PrimeMeridian Paris => new(2.5969213, CoordinateSystems.AngularUnit.Degrees, "Paris", "EPSG", 8903, string.Empty, string.Empty, "Value adopted by IGN (Paris) in 1936. Equivalent to 2 deg 20min 14.025sec. Preferred by EPSG to earlier value of 2deg 20min 13.95sec (2.596898 grads) used by RGS London."); + + /// + /// Gets the Bogota prime meridian. + /// + public static PrimeMeridian Bogota => new(-74.04513, CoordinateSystems.AngularUnit.Degrees, "Bogota", "EPSG", 8904, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Madrid prime meridian. + /// + public static PrimeMeridian Madrid => new(-3.411658, CoordinateSystems.AngularUnit.Degrees, "Madrid", "EPSG", 8905, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Rome prime meridian. + /// + public static PrimeMeridian Rome => new(12.27084, CoordinateSystems.AngularUnit.Degrees, "Rome", "EPSG", 8906, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Bern prime meridian. + /// + /// 1895 value. A newer value of 7 deg 26 min 22.335 sec E was determined in 1938. + public static PrimeMeridian Bern => new(7.26225, CoordinateSystems.AngularUnit.Degrees, "Bern", "EPSG", 8907, string.Empty, string.Empty, "1895 value. Newer value of 7 deg 26 min 22.335 sec E determined in 1938."); + + /// + /// Gets the Jakarta prime meridian. + /// + public static PrimeMeridian Jakarta => new(106.482779, CoordinateSystems.AngularUnit.Degrees, "Jakarta", "EPSG", 8908, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Ferro prime meridian. + /// + /// Used in Austria and former Czechoslovakia. + public static PrimeMeridian Ferro => new(-17.66666666666667, CoordinateSystems.AngularUnit.Degrees, "Ferro", "EPSG", 8909, string.Empty, string.Empty, "Used in Austria and former Czechoslovakia."); + + /// + /// Gets the Brussels prime meridian. + /// + public static PrimeMeridian Brussels => new(4.220471, CoordinateSystems.AngularUnit.Degrees, "Brussels", "EPSG", 8910, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Stockholm prime meridian. + /// + public static PrimeMeridian Stockholm => new(18.03298, CoordinateSystems.AngularUnit.Degrees, "Stockholm", "EPSG", 8911, string.Empty, string.Empty, string.Empty); + + /// + /// Gets the Athens prime meridian. + /// + /// Used in Greece for older mapping based on the Hatt projection. + public static PrimeMeridian Athens => new(23.4258815, CoordinateSystems.AngularUnit.Degrees, "Athens", "EPSG", 8912, string.Empty, string.Empty, "Used in Greece for older mapping based on Hatt projection."); + + /// + /// Gets the Oslo prime meridian. + /// + /// Formerly known as Kristiania or Christiania. + public static PrimeMeridian Oslo => new(10.43225, CoordinateSystems.AngularUnit.Degrees, "Oslo", "EPSG", 8913, string.Empty, string.Empty, "Formerly known as Kristiania or Christiania."); + + /// + /// Gets the longitude of the prime meridian (relative to the Greenwich prime meridian). + /// + public double Longitude { get; } + + /// + /// Gets the angular unit used to express the longitude of this prime meridian. + /// + public AngularUnit AngularUnit { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this prime meridian with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new PrimeMeridian WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this prime meridian with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new PrimeMeridian WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this prime meridian as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_PrimeMeridian", + new XAttribute("Longitude", this.Longitude.ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + element.Add(this.AngularUnit.ToXml()); + return element; + } + + /// + /// Converts this prime meridian to a WKT syntax tree node. + /// + /// A representing this prime meridian. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.Longitude), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("PRIMEM", children); + } + + /// + /// Converts this prime meridian to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this prime meridian in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.Longitude), + this.AngularUnit.ToWktNode(version), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("PRIMEM", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is PrimeMeridian prime && prime.AngularUnit.EqualParams(this.AngularUnit) && prime.Longitude == this.Longitude; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); } diff --git a/src/ProjNet/CoordinateSystems/ProjectedCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/ProjectedCoordinateSystem.cs index 0d29f5cb..9cb8f224 100644 --- a/src/ProjNet/CoordinateSystems/ProjectedCoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/ProjectedCoordinateSystem.cs @@ -1,227 +1,504 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A 2D cartographic coordinate system. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// is initialized once and then reused safely. The +/// WGS84_UTM(int, bool) helper is also thread-safe for concurrent callers; the generated +/// EPSG cache may synchronize the first materialization of a requested UTM SRID, but steady-state +/// access remains lock-free. +/// +/// +public class ProjectedCoordinateSystem : HorizontalCoordinateSystem { - /// - /// A 2D cartographic coordinate system. + private const string LegacyWebMercatorAlias = "WGS 84 / Popular Visualisation Pseudo-Mercator"; + + private const string LegacyWebMercatorAbbreviation = "WebMercator"; + + private const string LegacyWebMercatorRemarks = "Certain Web mapping and visualisation applications. " + + "Uses spherical development of ellipsoidal coordinates. Relative to an ellipsoidal development errors of up to 800 metres in position and 0.7 percent in scale may arise. It is not a recognised geodetic system: see WGS 84 / World Mercator (CRS code 3395)."; + + private const string LegacyWgs84UtmRemarks = "Large and medium scale topographic mapping and engineering survey."; + + private static readonly Lazy WebMercatorCoordinateSystem = + new(CreateWebMercatorCoordinateSystem, true); + + /// + /// Initializes a new instance of the class. /// - [Serializable] - public class ProjectedCoordinateSystem : HorizontalCoordinateSystem - { - /// - /// Initializes a new instance of a projected coordinate system - /// - /// Horizontal datum - /// Geographic coordinate system - /// Linear unit - /// Projection - /// Axis info - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal ProjectedCoordinateSystem(HorizontalDatum datum, GeographicCoordinateSystem geographicCoordinateSystem, - LinearUnit linearUnit, IProjection projection, List axisInfo, - string name, string authority, long code, string alias, - string remarks, string abbreviation) - : base(datum, axisInfo, name, authority, code, alias, remarks, abbreviation) - { - GeographicCoordinateSystem = geographicCoordinateSystem; - LinearUnit = linearUnit; - Projection = projection; - } - - #region Predefined projected coordinate systems - - /// - /// Universal Transverse Mercator - WGS84 - /// - /// UTM zone - /// true of Northern hemisphere, false if southern - /// UTM/WGS84 coordsys - public static ProjectedCoordinateSystem WGS84_UTM(int zone, bool zoneIsNorth) - { - var pInfo = new List(); - pInfo.Add(new ProjectionParameter("latitude_of_origin", 0)); - pInfo.Add(new ProjectionParameter("central_meridian", zone * 6 - 183)); - pInfo.Add(new ProjectionParameter("scale_factor", 0.9996)); - pInfo.Add(new ProjectionParameter("false_easting", 500000)); - pInfo.Add(new ProjectionParameter("false_northing", zoneIsNorth ? 0 : 10000000)); - //IProjection projection = cFac.CreateProjection("UTM" + Zone.ToString() + (ZoneIsNorth ? "N" : "S"), "Transverse_Mercator", parameters); - var proj = new Projection("Transverse_Mercator", pInfo, "UTM" + zone.ToString(CultureInfo.InvariantCulture) + (zoneIsNorth ? "N" : "S"), - "EPSG", 32600 + zone + (zoneIsNorth ? 0 : 100), string.Empty, string.Empty, string.Empty); - var axes = new List - { - new AxisInfo("East", AxisOrientationEnum.East), - new AxisInfo("North", AxisOrientationEnum.North) - }; - return new ProjectedCoordinateSystem(CoordinateSystems.HorizontalDatum.WGS84, - CoordinateSystems.GeographicCoordinateSystem.WGS84, CoordinateSystems.LinearUnit.Metre, proj, axes, - "WGS 84 / UTM zone " + zone.ToString(CultureInfo.InvariantCulture) + (zoneIsNorth ? "N" : "S"), "EPSG", 32600 + zone + (zoneIsNorth ? 0 : 100), - string.Empty, "Large and medium scale topographic mapping and engineering survey.", string.Empty); - } - - /// - /// Gets a WebMercator coordinate reference system - /// - public static ProjectedCoordinateSystem WebMercator - { - get - { - var pInfo = new List - { - /* - new ProjectionParameter("semi_major", 6378137.0), - new ProjectionParameter("semi_minor", 6378137.0), - new ProjectionParameter("scale_factor", 1.0), - */ - new ProjectionParameter("latitude_of_origin", 0.0), - new ProjectionParameter("central_meridian", 0.0), - new ProjectionParameter("false_easting", 0.0), - new ProjectionParameter("false_northing", 0.0) - }; - - var proj = new Projection("Popular Visualisation Pseudo-Mercator", pInfo, "Popular Visualisation Pseudo-Mercator", "EPSG", 3856, - "Pseudo-Mercator", string.Empty, string.Empty); - - var axes = new List - { - new AxisInfo("East", AxisOrientationEnum.East), - new AxisInfo("North", AxisOrientationEnum.North) - }; - - return new ProjectedCoordinateSystem(CoordinateSystems.HorizontalDatum.WGS84, - CoordinateSystems.GeographicCoordinateSystem.WGS84, CoordinateSystems.LinearUnit.Metre, proj, axes, - "WGS 84 / Pseudo-Mercator", "EPSG", 3857, "WGS 84 / Popular Visualisation Pseudo-Mercator", - "Certain Web mapping and visualisation applications." + - "Uses spherical development of ellipsoidal coordinates. Relative to an ellipsoidal development errors of up to 800 metres in position and 0.7 percent in scale may arise. It is not a recognised geodetic system: see WGS 84 / World Mercator (CRS code 3395).", - "WebMercator"); + /// Horizontal datum. + /// Geographic coordinate system. + /// Linear unit. + /// Projection. + /// Axis info. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Provider-supplied remarks. + /// Abbreviation. + /// Default envelope for the coordinate system domain. + internal ProjectedCoordinateSystem( + HorizontalDatum datum, + GeographicCoordinateSystem geographicCoordinateSystem, + LinearUnit linearUnit, + IProjection projection, + List axisInfo, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation, + double[]? defaultEnvelope = null) + : base(datum, axisInfo, name, authority, code, alias, remarks, abbreviation, defaultEnvelope) + { + this.GeographicCoordinateSystem = geographicCoordinateSystem; + this.LinearUnit = linearUnit; + this.Projection = projection; + } + + /// + /// Gets a WebMercator coordinate reference system. + /// + public static ProjectedCoordinateSystem WebMercator + { + get { return WebMercatorCoordinateSystem.Value; } + } + + /// + /// Gets the geographic coordinate system on which this projection is based. + /// + public GeographicCoordinateSystem GeographicCoordinateSystem { get; } + + /// + /// Gets the LinearUnits. The linear unit must be the same as the units. + /// + public LinearUnit LinearUnit { get; } + + /// + /// Gets the projection. + /// + public IProjection Projection { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Universal Transverse Mercator - WGS84. + /// + /// UTM zone. + /// for the Northern Hemisphere; for the Southern Hemisphere. + /// UTM/WGS84 coordsys. + public static ProjectedCoordinateSystem WGS84_UTM(int zone, bool zoneIsNorth) + { + return CreateWgs84UtmCoordinateSystem(zone, zoneIsNorth); + } + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new ProjectedCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new ProjectedCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this projected coordinate system as an . + /// + /// An containing the XML representation. + public override XElement ToXml() + { + var innerElement = new XElement("CS_ProjectedCoordinateSystem"); + innerElement.Add(this.InfoXmlElement); + foreach (AxisInfo ai in this.AxisInfo) + { + innerElement.Add(ai.ToXml()); + } + + innerElement.Add(this.GeographicCoordinateSystem.ToXml()); + innerElement.Add(this.LinearUnit.ToXml()); + if (this.Projection is Projection projection) + { + innerElement.Add(projection.ToXml()); + } + else + { + innerElement.Add(XElement.Parse(this.Projection.XML)); + } + + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override IUnit GetUnits(int dimension) => this.LinearUnit; + + /// + public override WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + this.GeographicCoordinateSystem.ToWktNode(), + }; + + if (this.Projection is Projection proj) + { + children.Add(proj.ToWktNode()); + for (int i = 0; i < proj.NumParameters; i++) + { + children.Add(proj.GetParameter(i).ToWktNode()); } - } - - /// - /// Gets or sets the GeographicCoordinateSystem. - /// - public GeographicCoordinateSystem GeographicCoordinateSystem { get; set; } - - /// - /// Gets or sets the LinearUnits. The linear unit must be the same as the units. - /// - public LinearUnit LinearUnit { get; set; } - - /// - /// Gets units for dimension within coordinate system. Each dimension in - /// the coordinate system has corresponding units. - /// - /// Dimension - /// Unit - public override IUnit GetUnits(int dimension) - { - return LinearUnit; - } - - /// - /// Gets or sets the projection - /// - public IProjection Projection { get; set; } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat("PROJCS[\"{0}\", {1}, {2}", Name, GeographicCoordinateSystem.WKT, Projection.WKT); - for(int i=0;i 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object. - /// - public override string XML - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, - "{1}", - Dimension, InfoXml); - foreach (var ai in AxisInfo) - sb.Append(ai.XML); - - sb.AppendFormat("{0}{1}{2}", - GeographicCoordinateSystem.XML, LinearUnit.XML, Projection.XML); - return sb.ToString(); - } - } - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is ProjectedCoordinateSystem pcs)) - return false; - - if (pcs.Dimension != Dimension) - return false; - for (int i = 0; i < pcs.Dimension; i++) - { - if(pcs.GetAxis(i).Orientation != GetAxis(i).Orientation) - return false; - if (!pcs.GetUnits(i).EqualParams(GetUnits(i))) - return false; - } - - return pcs.GeographicCoordinateSystem.EqualParams(GeographicCoordinateSystem) && - pcs.HorizontalDatum.EqualParams(HorizontalDatum) && - pcs.LinearUnit.EqualParams(LinearUnit) && - pcs.Projection.EqualParams(Projection); - } - - #endregion - } + } + else + { + children.Add(new WktIdentifier(this.Projection.WKT)); + for (int i = 0; i < this.Projection.NumParameters; i++) + { + children.Add(this.Projection.GetParameter(i).ToWktNode()); + } + } + + children.Add(this.LinearUnit.ToWktNode()); + + // Skip axis info if they contain default values + if (this.AxisInfo.Count != 2 || + this.AxisInfo[0].Name != "X" || this.AxisInfo[0].Orientation != AxisOrientationEnum.East || + this.AxisInfo[1].Name != "Y" || this.AxisInfo[1].Orientation != AxisOrientationEnum.North) + { + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode()); + } + } + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("PROJCS", children); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + if (this.Dimension != 2) + { + throw new NotSupportedException("WKT2 PROJCRS output currently supports only two-dimensional projected coordinate systems."); + } + + if (this.AxisInfo.Count != this.Dimension) + { + throw new InvalidOperationException($"Projected coordinate system '{this.Name}' declared dimension {this.Dimension}, but provides {this.AxisInfo.Count} axes."); + } + + BoundCoordinateSystem? boundCoordinateSystem = BoundCoordinateSystemSupport.CreateLegacyBoundCoordinateSystemForSerialization(this); + if (boundCoordinateSystem is not null) + { + return BoundCoordinateSystemSupport.CreateWkt2BoundCoordinateSystemNode(boundCoordinateSystem); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.GeographicCoordinateSystem.CreateWkt2BaseNode("BASEGEOGCRS"), + CreateWkt2ConversionNode(this.Projection, this.GeographicCoordinateSystem.AngularUnit, this.LinearUnit), + new WktKeywordNode( + "CS", + new WktIdentifier("Cartesian"), + new WktInteger(this.Dimension)), + }; + + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode(version)); + } + + children.Add(this.LinearUnit.ToWktNode(version)); + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("PROJCRS", children); + } + + /// + public override bool EqualParams(object obj) + { + if (obj is not ProjectedCoordinateSystem pcs) + { + return false; + } + + if (pcs.Dimension != this.Dimension) + { + return false; + } + + for (int i = 0; i < pcs.Dimension; i++) + { + if (pcs.GetAxis(i).Orientation != this.GetAxis(i).Orientation) + { + return false; + } + + if (!pcs.GetUnits(i).EqualParams(this.GetUnits(i))) + { + return false; + } + } + + return pcs.GeographicCoordinateSystem.EqualParams(this.GeographicCoordinateSystem) && + pcs.HorizontalDatum.EqualParams(this.HorizontalDatum) && + pcs.LinearUnit.EqualParams(this.LinearUnit) && + pcs.Projection.EqualParams(this.Projection); + } + + /// + /// Creates a WKT2 base projected CRS node for use inside derived WKT2 coordinate-system constructs. + /// + /// The WKT2 keyword to emit, for example BASEPROJCRS. + /// A WKT2 projected base node without the surrounding top-level derived-CRS wrapper. + internal WktKeywordNode CreateWkt2BaseNode(string keyword) + { + if (string.IsNullOrWhiteSpace(keyword)) + { + ArgumentGuard.ThrowArgument("Invalid WKT2 base keyword.", nameof(keyword)); + } + + if (this.Dimension != 2) + { + throw new NotSupportedException("WKT2 projected base output currently supports only two-dimensional projected coordinate systems."); + } + + if (this.AxisInfo.Count != this.Dimension) + { + throw new InvalidOperationException($"Projected coordinate system '{this.Name}' declared dimension {this.Dimension}, but provides {this.AxisInfo.Count} axes."); + } + + if (BoundCoordinateSystemSupport.CreateLegacyBoundCoordinateSystemForSerialization(this) is not null) + { + throw new NotSupportedException("WKT2 projected base output for coordinate systems with retained bound metadata is not supported inside BASEPROJCRS. Serialize the top-level CRS as BOUNDCRS instead."); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.GeographicCoordinateSystem.CreateWkt2BaseNode("BASEGEOGCRS"), + CreateWkt2ConversionNode(this.Projection, this.GeographicCoordinateSystem.AngularUnit, this.LinearUnit), + new WktKeywordNode( + "CS", + new WktIdentifier("Cartesian"), + new WktInteger(this.Dimension)), + }; + + for (int i = 0; i < this.AxisInfo.Count; i++) + { + children.Add(this.GetAxis(i).ToWktNode(WktVersion.Wkt22019)); + } + + children.Add(this.LinearUnit.ToWktNode(WktVersion.Wkt22019)); + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode(keyword, children); + } + + private static WktKeywordNode CreateWkt2ConversionNode(IProjection projection, AngularUnit angularUnit, LinearUnit linearUnit) + { + string methodKey = ProjectionSerializationSupport.NormalizeMethodKey(projection.ClassName); + string methodName = ProjectionSerializationSupport.GetMethodName(projection.ClassName); + string conversionName = string.IsNullOrWhiteSpace(projection.Name) ? methodName : projection.Name; + + var children = new List + { + new WktQuotedString(conversionName), + new WktKeywordNode( + "METHOD", + new WktQuotedString(methodName)), + }; + + for (int i = 0; i < projection.NumParameters; i++) + { + children.Add(CreateWkt2ProjectionParameterNode(methodKey, projection.GetParameter(i), angularUnit, linearUnit)); + } + + return new WktKeywordNode("CONVERSION", children); + } + + private static WktKeywordNode CreateWkt2ProjectionParameterNode(string methodKey, ProjectionParameter parameter, AngularUnit angularUnit, LinearUnit linearUnit) + { + var children = new List + { + new WktQuotedString(ProjectionSerializationSupport.GetParameterName(methodKey, parameter.Name)), + new WktNumber(parameter.Value), + }; + + WktNode? unitNode = CreateWkt2ProjectionParameterUnitNode(parameter.Name, angularUnit, linearUnit); + if (unitNode is not null) + { + children.Add(unitNode); + } + + return new WktKeywordNode("PARAMETER", children); + } + + private static WktNode? CreateWkt2ProjectionParameterUnitNode(string parameterName, AngularUnit angularUnit, LinearUnit linearUnit) + { + if (ProjectionSerializationSupport.ParameterUsesAngularUnit(parameterName)) + { + return angularUnit.ToWktNode(WktVersion.Wkt22019); + } + + if (ProjectionSerializationSupport.ParameterUsesLinearUnit(parameterName)) + { + return linearUnit.ToWktNode(WktVersion.Wkt22019); + } + + return ProjectionSerializationSupport.ParameterUsesScaleUnit(parameterName) + ? new WktKeywordNode( + "SCALEUNIT", + new WktQuotedString("unity"), + new WktNumber(1)) + : null; + } + + private static ProjectedCoordinateSystem CreateWebMercatorCoordinateSystem() + { + return Wgs84CatalogBootstrap.TryGetCoordinateSystem( + Wgs84CatalogBootstrap.WebMercatorSrid, + out ProjectedCoordinateSystem? coordinateSystem) + ? NormalizeToLegacyWebMercatorShape(coordinateSystem) + : throw new InvalidOperationException("The generated EPSG catalog could not resolve the WGS 84 / Pseudo-Mercator projected coordinate system."); + } + + private static ProjectedCoordinateSystem NormalizeToLegacyWebMercatorShape(ProjectedCoordinateSystem coordinateSystem) + { + GeographicCoordinateSystem geographicCoordinateSystem = GeographicCoordinateSystem.WGS84; + return new ProjectedCoordinateSystem( + geographicCoordinateSystem.HorizontalDatum, + geographicCoordinateSystem, + coordinateSystem.LinearUnit, + new Projection( + "Popular Visualisation Pseudo-Mercator", + CopyProjectionParameters(coordinateSystem.Projection), + "Popular Visualisation Pseudo-Mercator", + "EPSG", + 3856, + "Pseudo-Mercator", + string.Empty, + string.Empty), + [new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)], + coordinateSystem.Name, + coordinateSystem.Authority, + coordinateSystem.AuthorityCode, + LegacyWebMercatorAlias, + LegacyWebMercatorRemarks, + LegacyWebMercatorAbbreviation); + } + + private static List CopyProjectionParameters(IProjection projection) + { + var parameters = new List(projection.NumParameters); + for (int i = 0; i < projection.NumParameters; i++) + { + ProjectionParameter parameter = projection.GetParameter(i); + parameters.Add(new ProjectionParameter(parameter.Name, parameter.Value)); + } + + return parameters; + } + + private static ProjectedCoordinateSystem CreateWgs84UtmCoordinateSystem(int zone, bool zoneIsNorth) + { + int srid = GetWgs84UtmSrid(zone, zoneIsNorth); + return Wgs84CatalogBootstrap.TryGetCoordinateSystem( + srid, + out ProjectedCoordinateSystem? coordinateSystem) + ? NormalizeToLegacyWgs84UtmShape(coordinateSystem, zone, zoneIsNorth, srid) + : throw new InvalidOperationException($"The generated EPSG catalog could not resolve the WGS 84 / UTM zone {zone.ToString(CultureInfo.InvariantCulture)}{(zoneIsNorth ? "N" : "S")} projected coordinate system."); + } + + private static ProjectedCoordinateSystem NormalizeToLegacyWgs84UtmShape(ProjectedCoordinateSystem coordinateSystem, int zone, bool zoneIsNorth, int srid) + { + GeographicCoordinateSystem geographicCoordinateSystem = GeographicCoordinateSystem.WGS84; + return new ProjectedCoordinateSystem( + geographicCoordinateSystem.HorizontalDatum, + geographicCoordinateSystem, + coordinateSystem.LinearUnit, + new Projection( + "Transverse_Mercator", + CopyProjectionParameters(coordinateSystem.Projection), + CreateLegacyUtmProjectionName(zone, zoneIsNorth), + "EPSG", + srid, + string.Empty, + string.Empty, + string.Empty), + [new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)], + coordinateSystem.Name, + coordinateSystem.Authority, + coordinateSystem.AuthorityCode, + string.Empty, + LegacyWgs84UtmRemarks, + string.Empty); + } + + private static int GetWgs84UtmSrid(int zone, bool zoneIsNorth) + { + return 32600 + zone + (zoneIsNorth ? 0 : 100); + } + + private static string CreateLegacyUtmProjectionName(int zone, bool zoneIsNorth) + { + return $"UTM{zone.ToString(CultureInfo.InvariantCulture)}{(zoneIsNorth ? "N" : "S")}"; + } } diff --git a/src/ProjNet/CoordinateSystems/Projection.cs b/src/ProjNet/CoordinateSystems/Projection.cs index 1372986c..b9a8f00e 100644 --- a/src/ProjNet/CoordinateSystems/Projection.cs +++ b/src/ProjNet/CoordinateSystems/Projection.cs @@ -1,163 +1,210 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; using System; using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// The Projection class defines the standard information stored with a projection +/// objects. A projection object implements a coordinate transformation from a geographic +/// coordinate system to a projected coordinate system, given the ellipsoid for the +/// geographic coordinate system. It is expected that each coordinate transformation of +/// interest, e.g., Transverse Mercator, Lambert, will be implemented as a class of +/// type Projection, supporting the IProjection interface. +/// +public class Projection : Info, IProjection { - /// - /// The Projection class defines the standard information stored with a projection - /// objects. A projection object implements a coordinate transformation from a geographic - /// coordinate system to a projected coordinate system, given the ellipsoid for the - /// geographic coordinate system. It is expected that each coordinate transformation of - /// interest, e.g., Transverse Mercator, Lambert, will be implemented as a class of - /// type Projection, supporting the IProjection interface. + private readonly string className; + private readonly List parameters; + + /// + /// Initializes a new instance of the class. + /// + /// Projection class name, for example Transverse_Mercator. + /// Projection parameters. + /// Projection display name. + /// Authority name. + /// Authority code. + /// Alias name. + /// Additional remarks. + /// Abbreviation. + internal Projection( + string className, + List parameters, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation) + : base(name, authority, code, alias, abbreviation, remarks) + { + this.parameters = parameters; + this.className = className; + } + + /// + /// Gets the number of parameters of the projection. + /// + public int NumParameters => this.parameters.Count; + + /// + /// Gets the parameters of the projection. + /// + internal List Parameters => this.parameters; + + /// + /// Gets the projection classification name (e.g. "Transverse_Mercator"). + /// + public string ClassName => this.className; + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. /// - [Serializable] - public class Projection : Info, IProjection - { - internal Projection(string className, List parameters, - string name, string authority, long code, string alias, - string remarks, string abbreviation) - : base(name, authority, code, alias, abbreviation, remarks) - { - _parameters = parameters; - _ClassName = className; - } - - #region Predefined projections - #endregion - - #region IProjection Members - - /// - /// Gets the number of parameters of the projection. - /// - public int NumParameters - { - get { return _parameters.Count; } - } - - private List _parameters; - - /// - /// Gets or sets the parameters of the projection - /// - internal List Parameters - { - get { return _parameters; } - set { _parameters = value; } - } - - /// - /// Gets an indexed parameter of the projection. - /// - /// Index of parameter - /// n'th parameter - public ProjectionParameter GetParameter(int index) - { - return _parameters[index]; - } - - /// - /// Gets an named parameter of the projection. - /// - /// The parameter name is case insensitive - /// Name of parameter - /// parameter or null if not found - public ProjectionParameter GetParameter(string name) - { - foreach (ProjectionParameter par in _parameters) - if (par.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) - return par; - return null; - } - - private string _ClassName; - - /// - /// Gets the projection classification name (e.g. "Transverse_Mercator"). - /// - public string ClassName - { - get { return _ClassName; } - } - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("PROJECTION[\"{0}\"", ClassName); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } - - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, "{1}", ClassName, InfoXml); - foreach (ProjectionParameter param in Parameters) - sb.Append(param.XML); - sb.Append(""); - return sb.ToString(); - } - } - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is Projection)) - return false; - Projection proj = obj as Projection; - if (proj.NumParameters != this.NumParameters) - return false; - for (int i = 0; i < _parameters.Count; i++) - { - ProjectionParameter param = GetParameter(proj.GetParameter(i).Name); - if (param == null) - return false; - if (param.Value != proj.GetParameter(i).Value) - return false; - } - return true; - } - - #endregion - } + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this projection with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new Projection WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this projection with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new Projection WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this projection as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_Projection", + new XAttribute("Classname", this.ClassName)); + element.Add(this.InfoXmlElement); + foreach (ProjectionParameter param in this.Parameters) + { + element.Add(param.ToXml()); + } + + return element; + } + + /// + /// Converts this projection to a WKT syntax tree node. + /// + /// A representing this projection. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.ClassName), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("PROJECTION", children); + } + + /// + /// Converts this projection to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this projection in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + return version == WktVersion.Wkt1 + ? this.ToWktNode() + : throw WktVersionSupport.CreateNotSupportedException(nameof(Projection), version); + } + + /// + /// Gets an indexed parameter of the projection. + /// + /// Index of parameter. + /// The projection parameter at the specified index. + public ProjectionParameter GetParameter(int index) + { + return this.parameters[index]; + } + + /// + /// Gets a named parameter of the projection. + /// + /// The parameter name is case insensitive. + /// Name of the parameter to find. + /// The matching , or if not found. + public ProjectionParameter? GetParameter(string name) + { + foreach (ProjectionParameter par in this.parameters) + { + if (par.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + return par; + } + } + + return null; + } + + /// + public override bool EqualParams(object obj) + { + if (obj is not Projection projection) + { + return false; + } + + if (projection.NumParameters != this.NumParameters) + { + return false; + } + + for (int i = 0; i < this.parameters.Count; i++) + { + ProjectionParameter? param = this.GetParameter(projection.GetParameter(i).Name); + if (param is null) + { + return false; + } + + if (param.Value != projection.GetParameter(i).Value) + { + return false; + } + } + + return true; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); } diff --git a/src/ProjNet/CoordinateSystems/ProjectionParameter.cs b/src/ProjNet/CoordinateSystems/ProjectionParameter.cs index a849f523..fc5ac182 100644 --- a/src/ProjNet/CoordinateSystems/ProjectionParameter.cs +++ b/src/ProjNet/CoordinateSystems/ProjectionParameter.cs @@ -1,104 +1,112 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A named projection parameter value. +/// +/// +/// The linear units of parameters' values match the linear units of the containing +/// projected coordinate system. The angular units of parameter values match the +/// angular units of the geographic coordinate system that the projected coordinate +/// system is based on. (Notice that this is different from , +/// where the units are always meters and degrees.) +/// +public sealed class ProjectionParameter { /// - /// A named projection parameter value. + /// Initializes a new instance of the class. /// - /// - /// The linear units of parameters' values match the linear units of the containing - /// projected coordinate system. The angular units of parameter values match the - /// angular units of the geographic coordinate system that the projected coordinate - /// system is based on. (Notice that this is different from , - /// where the units are always meters and degrees.) - /// - [Serializable] - public class ProjectionParameter + /// Name of parameter. + /// Parameter value. + public ProjectionParameter(string name, double value) { - /// - /// Initializes an instance of a ProjectionParameter - /// - /// Name of parameter - /// Parameter value - public ProjectionParameter(string name, double value) - { - _Name = name; - _Value = value; - } - + this.Name = name; + this.Value = value; + } - private string _Name; + /// + /// Gets parameter name. + /// + public string Name { get; } - /// - /// Parameter name. - /// - public string Name - { - get { return _Name; } - set { _Name = value; } - } + /// + /// Gets the parameter value. + /// + /// + /// The linear units of parameter values match the linear units of the containing + /// projected coordinate system. The angular units of parameter values match the + /// angular units of the underlying geographic coordinate system. + /// + public double Value { get; } - private double _Value; + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public string WKT + { + get => FormattableString.Invariant($"PARAMETER[\"{this.Name}\", {this.Value}]"); + } - /// - /// Parameter value. - /// The linear units of a parameters' values match the linear units of the containing - /// projected coordinate system. The angular units of parameter values match the - /// angular units of the geographic coordinate system that the projected coordinate - /// system is based on. - /// - public double Value + /// + /// Gets an XML representation of this object. + /// + public string XML + { + get { - get { return _Value; } - set { _Value = value; } + return FormattableString.Invariant($""); } + } - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public string WKT - { - get => string.Format(CultureInfo.InvariantCulture.NumberFormat, "PARAMETER[\"{0}\", {1}]", Name, Value); - - } + /// + /// Returns an XML representation of this projection parameter as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + return new XElement( + "CS_ProjectionParameter", + new XAttribute("Name", this.Name), + new XAttribute("Value", this.Value.ToString(CultureInfo.InvariantCulture))); + } - /// - /// Gets an XML representation of this object - /// - public string XML - { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, "", Name, Value); - } - } + /// + /// Converts this projection parameter to a WKT syntax tree node. + /// + /// A representing this projection parameter. + public WktNode ToWktNode() + { + return new WktKeywordNode( + "PARAMETER", + new WktQuotedString(this.Name), + new WktNumber(this.Value)); + } - /// - /// Function to get a textual representation of this envelope - /// - /// A textual representation of this envelope - public override string ToString() - { - return $"ProjectionParameter '{Name}': {Value}"; - } + /// + /// Converts this projection parameter to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this projection parameter in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + return version == WktVersion.Wkt1 + ? this.ToWktNode() + : throw WktVersionSupport.CreateNotSupportedException(nameof(ProjectionParameter), version); } + + /// + /// Returns a string representation of this projection parameter. + /// + /// A string in the format ProjectionParameter 'name': value. + public override string ToString() => $"ProjectionParameter '{this.Name}': {this.Value}"; } diff --git a/src/ProjNet/CoordinateSystems/ProjectionSerializationSupport.cs b/src/ProjNet/CoordinateSystems/ProjectionSerializationSupport.cs new file mode 100644 index 00000000..6d925564 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/ProjectionSerializationSupport.cs @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Text; + +/// +/// Provides shared outward projection naming and unit conventions for CRS serialization formats. +/// +internal static class ProjectionSerializationSupport +{ + /// + /// Gets the outward-facing projection method name for serialization. + /// + /// The internal projection classification name. + /// The serialization-friendly projection method name. + internal static string GetMethodName(string className) + { + return NormalizeMethodKey(className) switch + { + "TRANSVERSE_MERCATOR" => "Transverse Mercator", + "LAMBERT_CONFORMAL_CONIC_1SP" => "Lambert Conic Conformal (1SP)", + "LAMBERT_CONFORMAL_CONIC_2SP" => "Lambert Conic Conformal (2SP)", + "MERCATOR_1SP" => "Mercator (variant A)", + "MERCATOR_2SP" => "Mercator (variant B)", + "CASSINI_SOLDNER" => "Cassini-Soldner", + "ALBERS_CONIC_EQUAL_AREA" => "Albers Equal Area", + "OBLIQUE_STEREOGRAPHIC" => "Oblique Stereographic", + "LAMBERT_AZIMUTHAL_EQUAL_AREA" => "Lambert Azimuthal Equal Area", + "KROVAK" => "Krovak", + "POPULAR_VISUALISATION_PSEUDO_MERCATOR" => "Popular Visualisation Pseudo Mercator", + _ => className, + }; + } + + /// + /// Gets the normalized projection method key used by serialization helpers. + /// + /// The internal projection classification name. + /// The normalized method key. + internal static string NormalizeMethodKey(string className) + { + if (string.IsNullOrWhiteSpace(className)) + { + return string.Empty; + } + + string normalized = NormalizeKey(className, replacePeriods: false); + return normalized switch + { + "LAMBERT_CONIC_CONFORMAL_1SP" => "LAMBERT_CONFORMAL_CONIC_1SP", + "LAMBERT_CONIC_CONFORMAL_2SP" => "LAMBERT_CONFORMAL_CONIC_2SP", + "MERCATOR_VARIANT_A" => "MERCATOR_1SP", + "MERCATOR_VARIANT_B" => "MERCATOR_2SP", + _ => normalized, + }; + } + + /// + /// Gets the outward-facing projection parameter name for serialization. + /// + /// The normalized projection method key. + /// The internal projection parameter name. + /// The serialization-friendly parameter name. + internal static string GetParameterName(string methodKey, string parameterName) + { + string parameterKey = NormalizeParameterKey(parameterName); + return methodKey switch + { + "LAMBERT_CONFORMAL_CONIC_2SP" => parameterKey switch + { + "LATITUDE_OF_ORIGIN" => "Latitude of false origin", + "CENTRAL_MERIDIAN" => "Longitude of false origin", + "STANDARD_PARALLEL_1" => "Latitude of 1st standard parallel", + "STANDARD_PARALLEL_2" => "Latitude of 2nd standard parallel", + "FALSE_EASTING" => "Easting at false origin", + "FALSE_NORTHING" => "Northing at false origin", + _ => GetDefaultParameterName(parameterKey, parameterName), + }, + _ => GetDefaultParameterName(parameterKey, parameterName), + }; + } + + /// + /// Determines whether a projection parameter should be emitted with an angular unit. + /// + /// The internal projection parameter name. + /// when the parameter uses angular units. + internal static bool ParameterUsesAngularUnit(string parameterName) + { + return NormalizeParameterKey(parameterName) switch + { + "LATITUDE_OF_ORIGIN" or + "LONGITUDE_OF_ORIGIN" or + "CENTRAL_MERIDIAN" or + "STANDARD_PARALLEL_1" or + "STANDARD_PARALLEL_2" or + "LATITUDE_OF_CENTER" or + "LONGITUDE_OF_CENTER" or + "LATITUDE_OF_PROJECTION_CENTER" or + "LONGITUDE_OF_PROJECTION_CENTER" or + "AZIMUTH" or + "RECTIFIED_GRID_ANGLE" => true, + _ => false, + }; + } + + /// + /// Determines whether a projection parameter should be emitted with a linear unit. + /// + /// The internal projection parameter name. + /// when the parameter uses linear units. + internal static bool ParameterUsesLinearUnit(string parameterName) + { + return NormalizeParameterKey(parameterName) switch + { + "FALSE_EASTING" or + "FALSE_NORTHING" or + "EASTING" or + "NORTHING" or + "SEMI_MAJOR" or + "SEMI_MINOR" => true, + _ => false, + }; + } + + /// + /// Determines whether a projection parameter should be emitted with a scale unit. + /// + /// The internal projection parameter name. + /// when the parameter uses a scale unit. + internal static bool ParameterUsesScaleUnit(string parameterName) + { + return NormalizeParameterKey(parameterName) == "SCALE_FACTOR"; + } + + private static string GetDefaultParameterName(string parameterKey, string parameterName) + { + return parameterKey switch + { + "LATITUDE_OF_ORIGIN" => "Latitude of natural origin", + "LONGITUDE_OF_ORIGIN" or "CENTRAL_MERIDIAN" => "Longitude of natural origin", + "STANDARD_PARALLEL_1" => "Latitude of 1st standard parallel", + "STANDARD_PARALLEL_2" => "Latitude of 2nd standard parallel", + "FALSE_EASTING" => "False easting", + "FALSE_NORTHING" => "False northing", + "SCALE_FACTOR" => "Scale factor at natural origin", + "LATITUDE_OF_CENTER" or "LATITUDE_OF_PROJECTION_CENTER" => "Latitude of projection centre", + "LONGITUDE_OF_CENTER" or "LONGITUDE_OF_PROJECTION_CENTER" => "Longitude of projection centre", + "AZIMUTH" => "Azimuth of initial line", + "RECTIFIED_GRID_ANGLE" => "Angle from Rectified to Skew Grid", + _ => parameterName, + }; + } + + private static string NormalizeParameterKey(string parameterName) + { + if (string.IsNullOrWhiteSpace(parameterName)) + { + return string.Empty; + } + + return NormalizeKey(parameterName, replacePeriods: true); + } + + private static string NormalizeKey(string value, bool replacePeriods) + { + string normalizedValue = value + .ToUpperInvariant() + .Trim(); + var builder = new StringBuilder(normalizedValue.Length); + bool previousWasUnderscore = false; + + foreach (char character in normalizedValue) + { + if (character == '(' || character == ')') + { + continue; + } + + char normalizedCharacter = character switch + { + '-' or '/' or ' ' => '_', + '.' when replacePeriods => '_', + _ => character, + }; + + if (normalizedCharacter == '_') + { + if (previousWasUnderscore) + { + continue; + } + + previousWasUnderscore = true; + } + else + { + previousWasUnderscore = false; + } + + builder.Append(normalizedCharacter); + } + + return builder.ToString(); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AdamsHemisphereInSquareProjection.cs b/src/ProjNet/CoordinateSystems/Projections/AdamsHemisphereInSquareProjection.cs new file mode 100644 index 00000000..009b9837 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AdamsHemisphereInSquareProjection.cs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + + /// +/// Implements the Adams Hemisphere in a Square projection (adams_hemi). +/// +/// +/// Adams Hemisphere in a Square is the hemispherical specialization of +/// . It uses the common Adams conformal square machinery +/// but restricts the domain to a single hemisphere arranged in a square. Inverse +/// projection is not supported in this implementation. +/// +internal sealed class AdamsHemisphereInSquareProjection : AdamsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AdamsHemisphereInSquareProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AdamsHemisphereInSquareProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Adams_Hemisphere_In_A_Square", AdamsMode.AdamsHemi) + { + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new AdamsHemisphereInSquareProjection(this.Parameters.ToProjectionParameter(), this)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AdamsProjectionBase.cs b/src/ProjNet/CoordinateSystems/Projections/AdamsProjectionBase.cs new file mode 100644 index 00000000..a00360bf --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AdamsProjectionBase.cs @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Shared implementation for Adams/Guyou/Peirce quincuncial projections. +/// +/// +/// AdamsProjectionBase implements the conformal square family associated with Adams and the +/// closely related Guyou and Peirce variants. Each mode converts the spherical input to a +/// pair of auxiliary angles, evaluates the quarter-period elliptic integral used by the +/// square construction, and then arranges the result into a hemisphere, world-in-a-square, +/// or quincuncial layout. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 7, Sect. 7.4, pp. 206-208. +/// Adams, O. S. (1925), "Elliptic Functions Applied to Conformal World Maps", U.S. Coast and Geodetic Survey. +/// Peirce, C. S. (1879), "A Quincuncial Projection of the Sphere", American Journal of Mathematics, 2(4), 394-397. +/// Guyou, E. (1887), "Sur une projection nouvelle de la sphere terrestre", Annales Hydrographiques. +/// PROJ identifiers: guyou, peirce_q, adams_hemi, adams_ws1, adams_ws2. +internal abstract class AdamsProjectionBase : MapProjection +{ + private const double Tolerance = 1e-9d; + private const double InverseTolerance = 1e-10d; + private const double FiniteDifferenceStep = 1e-6d; + private const double StepClamp = 0.3d; + private const double OneTolerance = 1.00000000000001d; + private const double CompleteEllipticHalf = 1.8540746773013719d; + private const double ShapeShiftDistance = CompleteEllipticHalf * 2d; + + private static readonly double[] EllipticIntegralCoefficients = + [ + -8.58691003636495e-07d, + 2.02692115653689e-07d, + 3.12960480765314e-05d, + 5.30394739921063e-05d, + -0.0012804644680613d, + -0.00575574836830288d, + 0.0914203033408211d, + ]; + + private readonly AdamsMode mode; + private readonly PeirceShape peirceShape; + private readonly double scrollX; + private readonly double scrollY; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + /// Projection name. + /// Projection mode. + protected AdamsProjectionBase(IEnumerable parameters, MapProjection? inverse, string name, AdamsMode mode) + : base(parameters, inverse) + { + this.Name = name; + this.mode = mode; + + if (mode == AdamsMode.PeirceQ) + { + double shapeCode = this.Parameters.GetOptionalParameterValue("shape", 1d); + this.peirceShape = ParsePeirceShape(shapeCode, nameof(parameters)); + this.scrollX = this.Parameters.GetOptionalParameterValue("scrollx", 0d); + this.scrollY = this.Parameters.GetOptionalParameterValue("scrolly", 0d); + if (this.peirceShape == PeirceShape.Horizontal && Math.Abs(this.scrollX) > 1d) + { + ArgumentGuard.ThrowArgument("Invalid value for scrollx: |scrollx| should be between -1 and 1.", nameof(parameters)); + } + + if (this.peirceShape == PeirceShape.Vertical && Math.Abs(this.scrollY) > 1d) + { + ArgumentGuard.ThrowArgument("Invalid value for scrolly: |scrolly| should be between -1 and 1.", nameof(parameters)); + } + } + else + { + this.peirceShape = PeirceShape.Diamond; + this.scrollX = 0d; + this.scrollY = 0d; + } + } + + /// + /// Enumerates supported Adams-family projection modes. + /// + protected enum AdamsMode + { + /// + /// Guyou projection. + /// + Guyou, + + /// + /// Peirce quincuncial projection. + /// + PeirceQ, + + /// + /// Adams Hemisphere in a Square. + /// + AdamsHemi, + + /// + /// Adams World in a Square I. + /// + AdamsWs1, + + /// + /// Adams World in a Square II. + /// + AdamsWs2, + } + + private enum PeirceShape + { + Square = 0, + Diamond = 1, + NHemisphere = 2, + SHemisphere = 3, + Horizontal = 4, + Vertical = 5, + } + + /// + protected override bool HasInverseSupport => + this.mode == AdamsMode.AdamsWs2 + || (this.mode == AdamsMode.PeirceQ + && (this.peirceShape == PeirceShape.Square || this.peirceShape == PeirceShape.Diamond)); + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + + this.ForwardNormalized(lambda, phi, out double xUnit, out double yUnit); + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + bool canInvert = this.mode == AdamsMode.AdamsWs2 + || (this.mode == AdamsMode.PeirceQ + && (this.peirceShape == PeirceShape.Square || this.peirceShape == PeirceShape.Diamond)); + + if (!canInvert) + { + throw new InvalidOperationException($"{this.Name} does not support inverse projection in this wave."); + } + + if (this.mode == AdamsMode.AdamsWs2) + { + if (!this.TryInverseAdamsWs2(xUnit, yUnit, out double lambda, out double phi)) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + return; + } + + if (!this.TryInversePeirce(xUnit, yUnit, out double peirceLambda, out double peircePhi)) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + peirceLambda); + y = peircePhi; + } + + private static PeirceShape ParsePeirceShape(double shapeCode, string paramName) + { + int code = (int)Math.Round(shapeCode, MidpointRounding.AwayFromZero); + if (Math.Abs(shapeCode - code) > ProjectionConstants.Tolerance1E12) + { + ArgumentGuard.ThrowArgument("Invalid value for shape parameter.", paramName); + } + + return code switch + { + 0 => PeirceShape.Square, + 1 => PeirceShape.Diamond, + 2 => PeirceShape.NHemisphere, + 3 => PeirceShape.SHemisphere, + 4 => PeirceShape.Horizontal, + 5 => PeirceShape.Vertical, + _ => ArgumentGuard.ThrowArgument("Invalid value for shape parameter.", paramName), + }; + } + + private static void RotateFortyFive(ref double x, ref double y) + { + double temp = x; + x = ProjectionConstants.OneOverSqrt2 * (x - y); + y = ProjectionConstants.OneOverSqrt2 * (temp + y); + } + + private static double EllipticIntegralHalf(double phi) + { + const double c0 = 2.19174570831038d; + double y = phi * (2d / PI); + y = (2d * y * y) - 1d; + double y2 = 2d * y; + double d1 = 0d; + double d2 = 0d; + foreach (double c in EllipticIntegralCoefficients) + { + double temp = d1; + d1 = (y2 * d1) - d2 + c; + d2 = temp; + } + + return phi * ((y * d1) - d2 + (0.5d * c0)); + } + + private static bool IsApproximatelyZero(double value) + { + return Math.Abs(value) <= ProjectionConstants.Tolerance1E12; + } + + private static double Aasin(double value) + { + double absoluteValue = Math.Abs(value); + if (absoluteValue >= 1d) + { + if (absoluteValue > OneTolerance) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + return value < 0d ? -HalfPi : HalfPi; + } + + return Math.Asin(value); + } + + private static double Aacos(double value) + { + double absoluteValue = Math.Abs(value); + if (absoluteValue >= 1d) + { + if (absoluteValue > OneTolerance) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + return value < 0d ? PI : 0d; + } + + return Math.Acos(value); + } + + private void ForwardNormalized(double lambda, double phi, out double x, out double y) + { + if ((Math.Abs(phi) - Tolerance) > HalfPi) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double a = 0d; + double b = 0d; + bool sm = false; + bool sn = false; + + switch (this.mode) + { + case AdamsMode.Guyou: + if ((Math.Abs(lambda) - Tolerance) > HalfPi) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + if (Math.Abs(Math.Abs(phi) - HalfPi) < Tolerance) + { + x = 0d; + y = phi < 0d ? -CompleteEllipticHalf : CompleteEllipticHalf; + return; + } + + { + double sl = Math.Sin(lambda); + double sp = Math.Sin(phi); + double cp = Math.Cos(phi); + a = Aacos(((cp * sl) - sp) * ProjectionConstants.OneOverSqrt2); + b = Aacos(((cp * sl) + sp) * ProjectionConstants.OneOverSqrt2); + sm = lambda < 0d; + sn = phi < 0d; + } + + break; + + case AdamsMode.PeirceQ: + if (this.peirceShape == PeirceShape.NHemisphere && phi < -Tolerance) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + if (this.peirceShape == PeirceShape.SHemisphere && phi > -Tolerance) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + { + double sl = Math.Sin(lambda); + double cl = Math.Cos(lambda); + double cp = Math.Cos(phi); + a = Aacos(cp * (sl + cl) * ProjectionConstants.OneOverSqrt2); + b = Aacos(cp * (sl - cl) * ProjectionConstants.OneOverSqrt2); + sm = sl < 0d; + sn = cl > 0d; + } + + break; + + case AdamsMode.AdamsHemi: + if ((Math.Abs(lambda) - Tolerance) > HalfPi) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + { + double sp = Math.Sin(phi); + a = Math.Cos(phi) * Math.Sin(lambda); + sm = (sp + a) < 0d; + sn = (sp - a) < 0d; + a = Aacos(a); + b = HalfPi - phi; + } + + break; + + case AdamsMode.AdamsWs1: + { + double sp = Math.Tan(0.5d * phi); + b = Math.Cos(Aasin(sp)) * Math.Sin(0.5d * lambda); + a = Aacos((b - sp) * ProjectionConstants.OneOverSqrt2); + b = Aacos((b + sp) * ProjectionConstants.OneOverSqrt2); + sm = lambda < 0d; + sn = phi < 0d; + } + + break; + + case AdamsMode.AdamsWs2: + { + double sp = Math.Tan(0.5d * phi); + a = Math.Cos(Aasin(sp)) * Math.Sin(0.5d * lambda); + sm = (sp + a) < 0d; + sn = (sp - a) < 0d; + b = Aacos(sp); + a = Aacos(a); + } + + break; + } + + double m = Aasin(Math.Sqrt(1d + Math.Min(0d, Math.Cos(a + b)))); + if (sm) + { + m = -m; + } + + double n = Aasin(Math.Sqrt(Math.Abs(1d - Math.Max(0d, Math.Cos(a - b))))); + if (sn) + { + n = -n; + } + + x = EllipticIntegralHalf(m); + y = EllipticIntegralHalf(n); + + if (this.mode == AdamsMode.PeirceQ) + { + this.ApplyPeirceLayout(ref x, ref y, lambda, phi); + } + + if (this.mode == AdamsMode.AdamsHemi || this.mode == AdamsMode.AdamsWs2) + { + RotateFortyFive(ref x, ref y); + } + } + + private void ApplyPeirceLayout(ref double x, ref double y, double lambda, double phi) + { + if (this.peirceShape == PeirceShape.Square || this.peirceShape == PeirceShape.Diamond) + { + if (phi < 0d) + { + if (lambda < (-0.75d * PI)) + { + y = ShapeShiftDistance - y; + } + + if (lambda < (-0.25d * PI) && lambda >= (-0.75d * PI)) + { + x = -ShapeShiftDistance - x; + } + + if (lambda < (0.25d * PI) && lambda >= (-0.25d * PI)) + { + y = -ShapeShiftDistance - y; + } + + if (lambda < (0.75d * PI) && lambda >= (0.25d * PI)) + { + x = ShapeShiftDistance - x; + } + + if (lambda >= (0.75d * PI)) + { + y = ShapeShiftDistance - y; + } + } + } + + if (this.peirceShape == PeirceShape.Square) + { + RotateFortyFive(ref x, ref y); + } + + if (this.peirceShape == PeirceShape.Horizontal) + { + if (phi < 0d) + { + x = ShapeShiftDistance - x; + } + + x -= ShapeShiftDistance * 0.5d; + if (Math.Abs(this.scrollX) > 0d) + { + const double scale = 2d; + double threshold = ShapeShiftDistance * 0.5d; + x += this.scrollX * (threshold * 2d * scale); + if (x >= threshold * scale) + { + x -= ShapeShiftDistance * scale; + } + else if (x < -(threshold * scale)) + { + x += ShapeShiftDistance * scale; + } + } + } + + if (this.peirceShape == PeirceShape.Vertical) + { + if (phi < 0d) + { + y = ShapeShiftDistance - y; + } + + y -= ShapeShiftDistance * 0.5d; + if (Math.Abs(this.scrollY) > 0d) + { + const double scale = 2d; + double threshold = ShapeShiftDistance * 0.5d; + y += this.scrollY * (threshold * 2d * scale); + if (y >= threshold * scale) + { + y -= ShapeShiftDistance * scale; + } + else if (y < -(threshold * scale)) + { + y += ShapeShiftDistance * scale; + } + } + } + } + + private bool TryInverseAdamsWs2(double x, double y, out double lambda, out double phi) + { + phi = ProjectionConstants.Clamp(y / 2.62181347d, -1d, 1d) * HalfPi; + lambda = Math.Abs(phi) >= HalfPi + ? 0d + : ProjectionConstants.Clamp(x / 2.62205760d / Math.Cos(phi), -1d, 1d) * PI; + + return this.TryGenericInverse2D(x, y, lambda, phi, InverseTolerance, out lambda, out phi); + } + + private bool TryInversePeirce(double x, double y, out double lambda, out double phi) + { + return this.peirceShape == PeirceShape.Square + ? this.TryInversePeirceSquare(x, y, out lambda, out phi) + : this.TryInversePeirceDiamond(x, y, out lambda, out phi); + } + + private bool TryInversePeirceSquare(double x, double y, out double lambda, out double phi) + { + phi = 0d; + if (IsApproximatelyZero(x) && y < 0d) + { + lambda = -PI / 4d; + if (Math.Abs(y) < 2.622057580396d) + { + phi = PI / 4d; + } + } + else if (x > 0d && IsApproximatelyZero(y)) + { + lambda = PI / 4d; + } + else if (x < 0d && IsApproximatelyZero(y)) + { + lambda = -3d * PI / 4d; + phi = ((PI / 2d) / 2.622057574224d * x) + (PI / 2d); + } + else if (IsApproximatelyZero(x) && y > 0d) + { + lambda = 3d * PI / 4d; + } + else if (x >= 0d && y <= 0d) + { + lambda = 0d; + if (IsApproximatelyZero(x) && IsApproximatelyZero(y)) + { + phi = PI / 2d; + return true; + } + } + else if (x >= 0d && y >= 0d) + { + lambda = PI / 2d; + } + else if (x <= 0d && y >= 0d) + { + lambda = Math.Abs(x) < Math.Abs(y) ? PI * 0.9d : -PI * 0.9d; + } + else + { + lambda = -PI / 2d; + } + + return this.TryGenericInverse2D(x, y, lambda, phi, InverseTolerance, out lambda, out phi); + } + + private bool TryInversePeirceDiamond(double x, double y, out double lambda, out double phi) + { + phi = 0d; + if (x >= 0d && y <= 0d) + { + lambda = PI / 4d; + if (x > 0d && IsApproximatelyZero(y)) + { + lambda = PI / 2d; + phi = 0d; + } + else if (IsApproximatelyZero(x) && IsApproximatelyZero(y)) + { + lambda = 0d; + phi = PI / 2d; + return true; + } + else if (IsApproximatelyZero(x) && y < 0d) + { + lambda = 0d; + phi = PI / 4d; + } + } + else if (x >= 0d && y >= 0d) + { + lambda = 3d * PI / 4d; + } + else if (x <= 0d && y >= 0d) + { + lambda = -3d * PI / 4d; + } + else + { + lambda = -PI / 4d; + } + + if (Math.Abs(x) > CompleteEllipticHalf + 1e-3d || Math.Abs(y) > CompleteEllipticHalf + 1e-3d) + { + phi = -PI / 4d; + } + + return this.TryGenericInverse2D(x, y, lambda, phi, InverseTolerance, out lambda, out phi); + } + + private bool TryGenericInverse2D( + double targetX, + double targetY, + double initialLambda, + double initialPhi, + double deltaXyTolerance, + out double lambda, + out double phi) + { + lambda = initialLambda; + phi = initialPhi; + bool haveDerivatives = false; + double derivLamX = 0d; + double derivLamY = 0d; + double derivPhiX = 0d; + double derivPhiY = 0d; + + for (int i = 0; i < 15; i++) + { + if (!this.TryForwardForInverse(lambda, phi, out double approxX, out double approxY)) + { + return false; + } + + double deltaX = approxX - targetX; + double deltaY = approxY - targetY; + if (Math.Abs(deltaX) < deltaXyTolerance && Math.Abs(deltaY) < deltaXyTolerance) + { + return true; + } + + if (i == 0 || Math.Abs(deltaX) > FiniteDifferenceStep || Math.Abs(deltaY) > FiniteDifferenceStep || !haveDerivatives) + { + if (!this.TryComputeInverseJacobian(lambda, phi, approxX, approxY, out derivLamX, out derivLamY, out derivPhiX, out derivPhiY)) + { + if (!haveDerivatives) + { + return false; + } + } + else + { + haveDerivatives = true; + } + } + + if (!haveDerivatives) + { + return false; + } + + double deltaLambda = ProjectionConstants.Clamp((deltaX * derivLamX) + (deltaY * derivLamY), -StepClamp, StepClamp); + lambda -= deltaLambda; + lambda = ProjectionConstants.Clamp(lambda, -PI, PI); + + double deltaPhi = ProjectionConstants.Clamp((deltaX * derivPhiX) + (deltaY * derivPhiY), -StepClamp, StepClamp); + phi -= deltaPhi; + phi = ProjectionConstants.Clamp(phi, -HalfPi, HalfPi); + } + + if (Math.Abs(lambda) < Eps7) + { + lambda = 0d; + } + + if (Math.Abs(phi) < Eps7) + { + phi = 0d; + } + + return false; + } + + private bool TryComputeInverseJacobian( + double lambda, + double phi, + double approxX, + double approxY, + out double derivLamX, + out double derivLamY, + out double derivPhiX, + out double derivPhiY) + { + derivLamX = 0d; + derivLamY = 0d; + derivPhiX = 0d; + derivPhiY = 0d; + + double dLam = lambda > 0d ? -FiniteDifferenceStep : FiniteDifferenceStep; + if (!this.TryForwardForInverse(lambda + dLam, phi, out double xLam, out double yLam)) + { + return false; + } + + double derivXLam = (xLam - approxX) / dLam; + double derivYLam = (yLam - approxY) / dLam; + + double dPhi = phi > 0d ? -FiniteDifferenceStep : FiniteDifferenceStep; + if (!this.TryForwardForInverse(lambda, phi + dPhi, out double xPhi, out double yPhi)) + { + return false; + } + + double derivXPhi = (xPhi - approxX) / dPhi; + double derivYPhi = (yPhi - approxY) / dPhi; + double det = (derivXLam * derivYPhi) - (derivXPhi * derivYLam); + if (Math.Abs(det) <= ProjectionConstants.JacobianTolerance) + { + return false; + } + + derivLamX = derivYPhi / det; + derivLamY = -derivXPhi / det; + derivPhiX = -derivYLam / det; + derivPhiY = derivXLam / det; + return true; + } + + private bool TryForwardForInverse(double lambda, double phi, out double x, out double y) + { + try + { + this.ForwardNormalized(lambda, phi, out x, out y); + return !(double.IsNaN(x) || double.IsNaN(y)); + } + catch (ArgumentException) + { + x = 0d; + y = 0d; + return false; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AdamsWorldInSquare1Projection.cs b/src/ProjNet/CoordinateSystems/Projections/AdamsWorldInSquare1Projection.cs new file mode 100644 index 00000000..05f94d97 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AdamsWorldInSquare1Projection.cs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + + /// +/// Implements the Adams World in a Square I projection (adams_ws1). +/// +/// +/// Adams World in a Square I is the first full-world specialization of +/// . It applies Adams' conformal square construction to the +/// entire world with the World-in-a-Square I arrangement. Inverse projection is not +/// supported in this implementation. +/// +internal sealed class AdamsWorldInSquare1Projection : AdamsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AdamsWorldInSquare1Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AdamsWorldInSquare1Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Adams_World_In_A_Square_I", AdamsMode.AdamsWs1) + { + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new AdamsWorldInSquare1Projection(this.Parameters.ToProjectionParameter(), this)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AdamsWorldInSquare2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/AdamsWorldInSquare2Projection.cs new file mode 100644 index 00000000..88c162b1 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AdamsWorldInSquare2Projection.cs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Adams World in a Square II projection (adams_ws2). +/// +/// +/// Adams World in a Square II is the second full-world specialization of +/// . It uses the alternate Adams world-in-a-square layout +/// that also supports the corresponding inverse routine in this implementation for the +/// full-world Adams WS2 mode. +/// +internal sealed class AdamsWorldInSquare2Projection : AdamsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AdamsWorldInSquare2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AdamsWorldInSquare2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Adams_World_In_A_Square_II", AdamsMode.AdamsWs2) + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new AdamsWorldInSquare2Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AiroceanProjection.cs b/src/ProjNet/CoordinateSystems/Projections/AiroceanProjection.cs new file mode 100644 index 00000000..a2aff88e --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AiroceanProjection.cs @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Airocean projection (airocean). +/// +/// +/// Airocean is an icosahedral world map inspired by Buckminster Fuller's +/// Dymaxion concept. The implementation projects the geographic position onto a +/// selected icosahedron face, transforms that face into the unfolded Airocean +/// layout, and optionally rotates the final arrangement between vertical and +/// horizontal orientations. Inverse projection is supported for both orientations +/// in this implementation. +/// This implementation follows PROJ's airocean formulation for the +/// Fuller/Sadao icosahedral world-map concept: Buckminster Fuller introduced the +/// Dymaxion world map in 1943, Fuller and Shoji Sadao moved the layout to an +/// icosahedral Airocean form in 1954, and Robert W. Gray later published exact +/// transformation equations in Cartographica 32(3), 1995, +/// doi:10.3138/1677-3273-Q862-1885. +/// +/// PROJ documentation: Airocean. +/// Gray, R.W. (1995): Exact Transformation Equations for Fuller's World Map. +/// Wikipedia: Dymaxion map. +internal sealed class AiroceanProjection : MapProjection +{ + private const int OrientationVertical = 0; + private const int OrientationHorizontal = 1; + + private readonly bool horizontalOrientation; + private readonly double oneMinusF; + private readonly double oneMinusFSquared; + private readonly double semiMajorSquared; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AiroceanProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AiroceanProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Airocean"; + this.oneMinusF = this.semiMajor == 0d ? 1d : this.semiMinor / this.semiMajor; + this.oneMinusFSquared = this.oneMinusF * this.oneMinusF; + this.semiMajorSquared = this.semiMajor * this.semiMajor; + + double orientationCode = this.Parameters.GetOptionalParameterValue( + "airocean_orient", + this.Parameters.GetOptionalParameterValue("orient", OrientationVertical)); + int orientation = ReadDiscreteCode(orientationCode, "orient", nameof(parameters)); + this.horizontalOrientation = orientation switch + { + OrientationVertical => false, + OrientationHorizontal => true, + _ => ArgumentGuard.ThrowArgument("Invalid value for orient: only vertical or horizontal are supported.", nameof(parameters)), + }; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new AiroceanProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double projectionLatitude = lat; + if (this.es != 0d) + { + projectionLatitude = Math.Atan(this.oneMinusFSquared * Math.Tan(lat)); + } + + Sincos(projectionLatitude, out double sinLatitude, out double cosLatitude); + Sincos(lon, out double sinLongitude, out double cosLongitude); + + var cartesianPoint = new Vector3(cosLatitude * cosLongitude, cosLatitude * sinLongitude, sinLatitude); + + int faceId = GetIcosahedronFaceIndex(cartesianPoint); + if (faceId < 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + Vector3 icosahedronPoint = CartesianToIcosahedron(cartesianPoint, faceId); + Vector2 projectedPoint = IcosahedronToAirocean(icosahedronPoint, faceId); + if (this.horizontalOrientation) + { + projectedPoint = HorizontalTransform.Transform(projectedPoint); + } + + lon = projectedPoint.X * this.SphericalRadius; + lat = projectedPoint.Y * this.SphericalRadius; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + var projectedPoint = new Vector2(x * this.InverseSphericalRadius, y * this.InverseSphericalRadius); + if (this.horizontalOrientation) + { + projectedPoint = HorizontalInverseTransform.Transform(projectedPoint); + } + + int faceId = GetAiroceanFaceIndex(projectedPoint); + if (faceId < 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + Vector3 sphereCoordinates = AiroceanToIcosahedron(projectedPoint, faceId); + + double norm = Math.Sqrt((sphereCoordinates.X * sphereCoordinates.X) + (sphereCoordinates.Y * sphereCoordinates.Y) + (sphereCoordinates.Z * sphereCoordinates.Z)); + if (norm <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double q = sphereCoordinates.X / norm; + double r = sphereCoordinates.Y / norm; + double s = sphereCoordinates.Z / norm; + + double latitude = Math.Acos(-s) - HalfPi; + double longitude = Math.Atan2(r, q); + + if (this.es != 0d) + { + bool invertSign = latitude < 0d; + double tanLatitude = Math.Tan(latitude); + double xa = this.semiMinor / Math.Sqrt((tanLatitude * tanLatitude) + (this.oneMinusF * this.oneMinusF)); + if (Math.Abs(xa) <= Eps10) + { + latitude = HalfPi; + } + else + { + double inside = this.semiMajorSquared - (xa * xa); + if (inside < 0d) + { + inside = 0d; + } + + latitude = Math.Atan(Math.Sqrt(inside) / (this.oneMinusF * xa)); + } + + if (invertSign) + { + latitude = -latitude; + } + } + + x = Adjust_lon(longitude); + y = latitude; + } + + private static int ReadDiscreteCode(double value, string parameterName, string paramName) + { + if (double.IsNaN(value) || double.IsInfinity(value)) + { + ArgumentGuard.ThrowArgument($"Invalid value for {parameterName}.", paramName); + } + + int rounded = (int)Math.Round(value, MidpointRounding.AwayFromZero); + if (Math.Abs(value - rounded) > ProjectionConstants.Tolerance1E12) + { + ArgumentGuard.ThrowArgument($"Invalid value for {parameterName}.", paramName); + } + + return rounded; + } + + private static int GetIcosahedronFaceIndex(in Vector3 point) + { + for (int i = 0; i < IcoFaces.Length; i++) + { + if (IsPointInFace(point, IcoFaces[i].P1, IcoFaces[i].P2, IcoFaces[i].P3)) + { + return i; + } + } + + return -1; + } + + private static int GetAiroceanFaceIndex(in Vector2 point) + { + var homogeneousPoint = new Vector3(point.X, point.Y, 1d); + for (int i = 0; i < AiroceanFaces.Length; i++) + { + Face2D face = AiroceanFaces[i]; + var p1 = new Vector3(face.P1.X, face.P1.Y, 1d); + var p2 = new Vector3(face.P2.X, face.P2.Y, 1d); + var p3 = new Vector3(face.P3.X, face.P3.Y, 1d); + if (IsPointInFace(homogeneousPoint, p1, p2, p3)) + { + return i; + } + } + + return -1; + } + + private static bool IsPointInFace(in Vector3 point, in Vector3 p1, in Vector3 p2, in Vector3 p3) + { + return Determinant(point, p2, p3) <= 0d + && Determinant(p1, point, p3) <= 0d + && Determinant(p1, p2, point) <= 0d; + } + + private static double Determinant(in Vector3 u, in Vector3 v, in Vector3 w) + { + return (u.X * ((v.Y * w.Z) - (v.Z * w.Y))) + - (v.X * ((u.Y * w.Z) - (u.Z * w.Y))) + + (w.X * ((u.Y * v.Z) - (u.Z * v.Y))); + } + + private static Vector3 CartesianToIcosahedron(in Vector3 point, int faceId) + { + Vector3 center = IcoCenters[faceId]; + Vector3 normal = IcoNormals[faceId]; + + double denominator = (point.X * normal.X) + (point.Y * normal.Y) + (point.Z * normal.Z); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double numerator = (center.X * normal.X) + (center.Y * normal.Y) + (center.Z * normal.Z); + double factor = numerator / denominator; + + return new Vector3(point.X * factor, point.Y * factor, point.Z * factor); + } + + private static Vector2 IcosahedronToAirocean(in Vector3 point, int faceId) + { + Matrix4x4 transform = IcoToAiroceanTransforms[faceId]; + return new Vector2( + (transform.M00 * point.X) + (transform.M01 * point.Y) + (transform.M02 * point.Z) + transform.M03, + (transform.M10 * point.X) + (transform.M11 * point.Y) + (transform.M12 * point.Z) + transform.M13); + } + + private static Vector3 AiroceanToIcosahedron(in Vector2 point, int faceId) + { + Matrix4x4 transform = AiroceanToIcoTransforms[faceId]; + return new Vector3( + (transform.M00 * point.X) + (transform.M01 * point.Y) + transform.M03, + (transform.M10 * point.X) + (transform.M11 * point.Y) + transform.M13, + (transform.M20 * point.X) + (transform.M21 * point.Y) + transform.M23); + } + + private static readonly Face[] IcoFaces = + [ + new Face(new Vector3(0.42015242670871d, 0.07814524940278296d, 0.9040825506150193d), new Vector3(0.51883673032736444d, 0.83542038037823585d, 0.18133183755726245d), new Vector3(0.99500943943624165d, -0.091347795276427932d, 0.040147175877166645d)), + new Face(new Vector3(0.42015242670871d, 0.07814524940278296d, 0.9040825506150193d), new Vector3(-0.41468222532033522d, 0.65596240543480078d, 0.63067580789147537d), new Vector3(0.51883673032736444d, 0.83542038037823585d, 0.18133183755726245d)), + new Face(new Vector3(0.42015242670871d, 0.07814524940278296d, 0.9040825506150193d), new Vector3(-0.51545595994404181d, -0.38171689828713301d, 0.76720099251774754d), new Vector3(-0.41468222532033522d, 0.65596240543480078d, 0.63067580789147537d)), + new Face(new Vector3(0.42015242670871d, 0.07814524940278296d, 0.9040825506150193d), new Vector3(0.35578140253294471d, -0.84358000246617815d, 0.40223422660292557d), new Vector3(-0.51545595994404181d, -0.38171689828713301d, 0.76720099251774754d)), + new Face(new Vector3(0.42015242670871d, 0.07814524940278296d, 0.9040825506150193d), new Vector3(0.99500943943624165d, -0.091347795276427932d, 0.040147175877166645d), new Vector3(0.35578140253294471d, -0.84358000246617815d, 0.40223422660292557d)), + new Face(new Vector3(0.99500943943624165d, -0.091347795276427932d, 0.040147175877166645d), new Vector3(0.51883673032736444d, 0.83542038037823585d, 0.18133183755726245d), new Vector3(0.51545595994404181d, 0.38171689828713301d, -0.76720099251774754d)), + new Face(new Vector3(0.51545595994404181d, 0.38171689828713301d, -0.76720099251774754d), new Vector3(0.51883673032736444d, 0.83542038037823585d, 0.18133183755726245d), new Vector3(-0.35578140253294471d, 0.84358000246617815d, -0.40223422660292557d)), + new Face(new Vector3(-0.35578140253294471d, 0.84358000246617815d, -0.40223422660292557d), new Vector3(0.51883673032736444d, 0.83542038037823585d, 0.18133183755726245d), new Vector3(-0.41468222532033522d, 0.65596240543480078d, 0.63067580789147537d)), + new Face(new Vector3(-0.51545595994404181d, -0.38171689828713301d, 0.76720099251774754d), new Vector3(-0.99500943943624165d, 0.091347795276427932d, -0.040147175877166645d), new Vector3(-0.41468222532033522d, 0.65596240543480078d, 0.63067580789147537d)), + new Face(new Vector3(-0.51545595994404181d, -0.38171689828713301d, 0.76720099251774754d), new Vector3(-0.51883673032736444d, -0.83542038037823585d, -0.18133183755726245d), new Vector3(-0.99500943943624165d, 0.091347795276427932d, -0.040147175877166645d)), + new Face(new Vector3(-0.51545595994404181d, -0.38171689828713301d, 0.76720099251774754d), new Vector3(0.35578140253294471d, -0.84358000246617815d, 0.40223422660292557d), new Vector3(-0.51883673032736444d, -0.83542038037823585d, -0.18133183755726245d)), + new Face(new Vector3(-0.51883673032736444d, -0.83542038037823585d, -0.18133183755726245d), new Vector3(0.35578140253294471d, -0.84358000246617815d, 0.40223422660292557d), new Vector3(0.41468222532033522d, -0.65596240543480078d, -0.63067580789147537d)), + new Face(new Vector3(0.41468222532033522d, -0.65596240543480078d, -0.63067580789147537d), new Vector3(0.35578140253294471d, -0.84358000246617815d, 0.40223422660292557d), new Vector3(0.99500943943624165d, -0.091347795276427932d, 0.040147175877166645d)), + new Face(new Vector3(0.51545595994404181d, 0.38171689828713301d, -0.76720099251774754d), new Vector3(0.41468222532033522d, -0.65596240543480078d, -0.63067580789147537d), new Vector3(0.99500943943624165d, -0.091347795276427932d, 0.040147175877166645d)), + new Face(new Vector3(-0.42015242670871d, -0.07814524940278296d, -0.9040825506150193d), new Vector3(-0.35578140253294471d, 0.84358000246617815d, -0.40223422660292557d), new Vector3(-0.99500943943624165d, 0.091347795276427932d, -0.040147175877166645d)), + new Face(new Vector3(-0.42015242670871d, -0.07814524940278296d, -0.9040825506150193d), new Vector3(-0.99500943943624165d, 0.091347795276427932d, -0.040147175877166645d), new Vector3(-0.51883673032736444d, -0.83542038037823585d, -0.18133183755726245d)), + new Face(new Vector3(-0.42015242670871d, -0.07814524940278296d, -0.9040825506150193d), new Vector3(-0.51883673032736444d, -0.83542038037823585d, -0.18133183755726245d), new Vector3(0.41468222532033522d, -0.65596240543480078d, -0.63067580789147537d)), + new Face(new Vector3(-0.42015242670871d, -0.07814524940278296d, -0.9040825506150193d), new Vector3(0.41468222532033522d, -0.65596240543480078d, -0.63067580789147537d), new Vector3(0.51545595994404181d, 0.38171689828713301d, -0.76720099251774754d)), + new Face(new Vector3(-0.35578140253294471d, 0.84358000246617815d, -0.40223422660292557d), new Vector3(-0.38796691462082733d, 0.38271737653169757d, -0.65315838860897246d), new Vector3(0.51545595994404181d, 0.38171689828713301d, -0.76720099251774754d)), + new Face(new Vector3(-0.42015242670871d, -0.07814524940278296d, -0.9040825506150193d), new Vector3(0.51545595994404181d, 0.38171689828713301d, -0.76720099251774754d), new Vector3(-0.38796691462082733d, 0.38271737653169757d, -0.65315838860897246d)), + new Face(new Vector3(-0.99500943943624165d, 0.091347795276427932d, -0.040147175877166645d), new Vector3(-0.35578140253294471d, 0.84358000246617815d, -0.40223422660292557d), new Vector3(-0.58849102242984053d, 0.53029673439246894d, 0.062764801803794387d)), + new Face(new Vector3(-0.35578140253294471d, 0.84358000246617815d, -0.40223422660292557d), new Vector3(-0.41468222532033522d, 0.65596240543480078d, 0.63067580789147537d), new Vector3(-0.58849102242984053d, 0.53029673439246894d, 0.062764801803794387d)), + new Face(new Vector3(-0.99500943943624165d, 0.091347795276427932d, -0.040147175877166645d), new Vector3(-0.58849102242984053d, 0.53029673439246894d, 0.062764801803794387d), new Vector3(-0.41468222532033522d, 0.65596240543480078d, 0.63067580789147537d)), + ]; + + private static readonly Vector3[] IcoCenters = + [ + new Vector3(0.64466619882410536d, 0.27407261150153034d, 0.37518718801648276d), + new Vector3(0.17476897723857973d, 0.52317601173860651d, 0.57203006535458567d), + new Vector3(-0.16999525285188902d, 0.1174635855168169d, 0.7673197836747474d), + new Vector3(0.086825956432537613d, -0.3823838837835094d, 0.69117258991189745d), + new Vector3(0.59031442289263214d, -0.28559418277994103d, 0.44882131769837047d), + new Vector3(0.67643404323588252d, 0.37526316112964703d, -0.18190732636110615d), + new Vector3(0.22617042924615385d, 0.68690576037718232d, -0.32936779385447018d), + new Vector3(-0.083875632508638498d, 0.77832092942640496d, 0.13659113961527075d), + new Vector3(-0.64171587490020621d, 0.12186443414136523d, 0.45257654151068544d), + new Vector3(-0.67643404323588252d, -0.37526316112964703d, 0.18190732636110615d), + new Vector3(-0.22617042924615385d, -0.68690576037718232d, 0.32936779385447024d), + new Vector3(0.083875632508638498d, -0.77832092942640496d, -0.13659113961527075d), + new Vector3(0.58849102242984053d, -0.53029673439246894d, -0.062764801803794387d), + new Vector3(0.64171587490020621d, -0.12186443414136523d, -0.45257654151068549d), + new Vector3(-0.59031442289263214d, 0.28559418277994103d, -0.44882131769837047d), + new Vector3(-0.64466619882410536d, -0.27407261150153028d, -0.37518718801648276d), + new Vector3(-0.17476897723857973d, -0.52317601173860651d, -0.57203006535458567d), + new Vector3(0.16999525285188902d, -0.11746358551681692d, -0.7673197836747474d), + new Vector3(-0.076097452403243393d, 0.53600475909500289d, -0.60753120257654858d), + new Vector3(-0.097554460461831846d, 0.22876300847201589d, -0.77481397724724632d), + new Vector3(-0.64642728813300898d, 0.48840817737835834d, -0.12653886689209928d), + new Vector3(-0.45298488342770682d, 0.67661304743114936d, 0.097068794364114738d), + new Vector3(-0.66606089572880578d, 0.42586897836789922d, 0.21776447793936771d), + ]; + + private static readonly Vector3[] IcoNormals = + [ + new Vector3(0.81125347091409694d, 0.34489532376393844d, 0.47213877364139306d), + new Vector3(0.21993077914046083d, 0.65836917802749961d, 0.71984753789261824d), + new Vector3(-0.21392348345014195d, 0.14781718295507021d, 0.96560179352142061d), + new Vector3(0.10926252787847963d, -0.48119515728732082d, 0.86977751212872534d), + new Vector3(0.74285673015867926d, -0.35939416782780276d, 0.56480059365170343d), + new Vector3(0.85123039864742922d, 0.47223437885826819d, -0.2289137388687808d), + new Vector3(0.28461480697879088d, 0.86440809726542034d, -0.41447925524735379d), + new Vector3(-0.10554981496139187d, 0.97944572964114118d, 0.17188746100093646d), + new Vector3(-0.8075407579970092d, 0.15335524858988167d, 0.56952619948826877d), + new Vector3(-0.85123039864742922d, -0.47223437885826819d, 0.22891373886878083d), + new Vector3(-0.28461480697879088d, -0.86440809726542034d, 0.41447925524735379d), + new Vector3(0.10554981496139185d, -0.97944572964114118d, -0.17188746100093638d), + new Vector3(0.74056214738544823d, -0.66732995645655235d, -0.078983764632673467d), + new Vector3(0.8075407579970092d, -0.15335524858988167d, -0.56952619948826877d), + new Vector3(-0.74285673015867926d, 0.35939416782780276d, -0.56480059365170343d), + new Vector3(-0.81125347091409694d, -0.34489532376393844d, -0.47213877364139306d), + new Vector3(-0.21993077914046083d, -0.65836917802749961d, -0.71984753789261824d), + new Vector3(0.21392348345014195d, -0.14781718295507021d, -0.96560179352142061d), + new Vector3(-0.10926252787847963d, 0.48119515728732087d, -0.86977751212872534d), + new Vector3(-0.10926252787847968d, 0.48119515728732087d, -0.86977751212872534d), + new Vector3(-0.74056214738544801d, 0.66732995645655235d, 0.078983764632673537d), + new Vector3(-0.74056214738544812d, 0.66732995645655235d, 0.078983764632673467d), + new Vector3(-0.74056214738544812d, 0.66732995645655246d, 0.078983764632673287d), + ]; + + private static readonly Face2D[] AiroceanFaces = + [ + new Face2D(new Vector2(1.8211859946200586d, 3.1543866727148018d), new Vector2(1.8211859946200586d, 4.2058488969530687d), new Vector2(2.7317789919300877d, 3.6801177848339353d)), + new Face2D(new Vector2(1.8211859946200586d, 3.1543866727148018d), new Vector2(0.9105929973100293d, 3.6801177848339353d), new Vector2(1.8211859946200586d, 4.2058488969530687d)), + new Face2D(new Vector2(1.8211859946200586d, 3.1543866727148018d), new Vector2(0.9105929973100293d, 2.6286555605956679d), new Vector2(0.9105929973100293d, 3.6801177848339353d)), + new Face2D(new Vector2(1.8211859946200586d, 3.1543866727148018d), new Vector2(1.8211859946200586d, 2.1029244484765344d), new Vector2(0.9105929973100293d, 2.6286555605956679d)), + new Face2D(new Vector2(1.8211859946200586d, 3.1543866727148018d), new Vector2(2.7317789919300877d, 3.6801177848339353d), new Vector2(2.7317789919300877d, 2.6286555605956679d)), + new Face2D(new Vector2(2.7317789919300877d, 3.6801177848339353d), new Vector2(1.8211859946200586d, 4.2058488969530687d), new Vector2(2.7317789919300877d, 4.7315800090722027d)), + new Face2D(new Vector2(1.8211859946200586d, 5.2573111211913357d), new Vector2(1.8211859946200586d, 4.2058488969530687d), new Vector2(0.9105929973100293d, 4.7315800090722027d)), + new Face2D(new Vector2(0.9105929973100293d, 4.7315800090722027d), new Vector2(1.8211859946200586d, 4.2058488969530687d), new Vector2(0.9105929973100293d, 3.6801177848339353d)), + new Face2D(new Vector2(0.9105929973100293d, 2.6286555605956679d), new Vector2(0.0d, 3.1543866727148018d), new Vector2(0.9105929973100293d, 3.6801177848339353d)), + new Face2D(new Vector2(0.9105929973100293d, 2.6286555605956679d), new Vector2(0.9105929973100293d, 1.5771933363574009d), new Vector2(0.0d, 2.1029244484765344d)), + new Face2D(new Vector2(0.9105929973100293d, 2.6286555605956679d), new Vector2(1.8211859946200586d, 2.1029244484765344d), new Vector2(0.9105929973100293d, 1.5771933363574009d)), + new Face2D(new Vector2(0.9105929973100293d, 1.5771933363574009d), new Vector2(1.8211859946200586d, 2.1029244484765344d), new Vector2(1.8211859946200586d, 1.0514622242382672d)), + new Face2D(new Vector2(1.8211859946200586d, 1.0514622242382672d), new Vector2(1.8211859946200586d, 2.1029244484765344d), new Vector2(2.7317789919300877d, 1.5771933363574009d)), + new Face2D(new Vector2(1.8211859946200586d, 0.0d), new Vector2(1.8211859946200586d, 1.0514622242382672d), new Vector2(2.7317789919300877d, 0.52573111211913359d)), + new Face2D(new Vector2(0.0d, 5.2573111211913357d), new Vector2(0.9105929973100293d, 4.7315800090722027d), new Vector2(0.0d, 4.2058488969530687d)), + new Face2D(new Vector2(0.0d, 1.0514622242382672d), new Vector2(0.0d, 2.1029244484765344d), new Vector2(0.9105929973100293d, 1.5771933363574009d)), + new Face2D(new Vector2(0.9105929973100293d, 0.52573111211913359d), new Vector2(0.9105929973100293d, 1.5771933363574009d), new Vector2(1.8211859946200586d, 1.0514622242382672d)), + new Face2D(new Vector2(0.9105929973100293d, 0.52573111211913359d), new Vector2(1.8211859946200586d, 1.0514622242382672d), new Vector2(1.8211859946200586d, 0.0d)), + new Face2D(new Vector2(0.9105929973100293d, 4.7315800090722027d), new Vector2(0.45529649865501465d, 4.9944455651317687d), new Vector2(0.9105929973100293d, 5.7830422333104696d)), + new Face2D(new Vector2(0.9105929973100293d, 0.52573111211913359d), new Vector2(1.8211859946200586d, 0.0d), new Vector2(0.9105929973100293d, 0.0d)), + new Face2D(new Vector2(0.0d, 4.2058488969530687d), new Vector2(0.9105929973100293d, 4.7315800090722027d), new Vector2(0.60706199820668616d, 4.2058488969530687d)), + new Face2D(new Vector2(0.9105929973100293d, 4.7315800090722027d), new Vector2(0.9105929973100293d, 3.6801177848339353d), new Vector2(0.60706199820668616d, 4.2058488969530687d)), + new Face2D(new Vector2(0.0d, 3.1543866727148018d), new Vector2(0.30353099910334308d, 3.6801177848339353d), new Vector2(0.9105929973100293d, 3.6801177848339353d)), + ]; + + private static readonly Matrix4x4[] IcoToAiroceanTransforms = + [ + new Matrix4x4( + 0.57711278526259346d, -0.6019490725122667d, -0.55190411050115662d, 2.1247169937234016d, + 0.093854350012571169d, 0.72021144794247027d, -0.68737677531054842d, 3.6801177848339357d, + 0.81125347091409672d, 0.34489532376393839d, 0.47213877364139289d, -0.79465447229176589d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97099012011986363d, -0.21873613253416729d, -0.096605853619785673d, 1.5176549955167156d, + 0.093854350012570892d, 0.72021144794247083d, -0.68737677531054797d, 3.6801177848339353d, + 0.21993077914046077d, 0.6583691780274995d, 0.71984753789261813d, -0.79465447229176589d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97213741150647925d, -0.06476823821979226d, 0.2252863255224028d, 1.2141239964133725d, + 0.095841516985275071d, 0.98689166362936331d, -0.12984316647721492d, 3.1543866727148013d, + -0.21392348345014195d, 0.14781718295507021d, 0.96560179352142073d, -0.79465447229176633d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99212587537314545d, -0.0010987106726278763d, -0.12523993073268391d, 1.5176549955167151d, + 0.06122048200295415d, 0.87661280702376732d, 0.47728611874350335d, 2.6286555605956674d, + 0.10926252787847969d, -0.48119515728732087d, 0.86977751212872556d, -0.79465447229176633d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.28030414798915965d, -0.59918003969486144d, -0.74994190751773271d, 2.4282479928267451d, + 0.60794198989543957d, 0.71541534241489813d, -0.34436524905882676d, 3.1543866727148013d, + 0.74285673015867904d, -0.3593941678278027d, 0.56480059365170321d, -0.794654472291766d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.25960661905056537d, -0.75800695910456128d, -0.59835595868528879d, 2.4282479928267442d, + -0.45608246158307081d, 0.44991125944279409d, -0.76783373647094033d, 4.2058488969530687d, + 0.85123039864742922d, 0.47223437885826813d, -0.22891373886878083d, -0.79465447229176611d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.95863657006736502d, -0.25808606460596301d, 0.12003128669511368d, 1.5176549955167158d, + -0.0032153037031572929d, -0.43149765310854393d, -0.90210832896272153d, 4.7315800090722018d, + 0.28461480697879082d, 0.86440809726542056d, -0.41447925524735385d, -0.79465447229176622d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99283494044507403d, 0.094058681189907414d, 0.073700376689958561d, 1.2141239964133723d, + 0.056018011327093935d, 0.17843493822832973d, -0.98235581905255198d, 4.2058488969530679d, + -0.10554981496139189d, 0.97944572964114129d, 0.17188746100093644d, -0.79465447229176622d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.58197278956629672d, 0.050269394155928272d, 0.81165304177069308d, 0.60706199820668649d, + 0.095841516985274419d, 0.98689166362936342d, -0.12984316647721489d, 3.1543866727148013d, + -0.80754075799700931d, 0.1533552485898817d, 0.56952619948826888d, -0.79465447229176633d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.52478230747676247d, -0.76863805967839183d, 0.36578554232391575d, 0.60706199820668671d, + 0.0032153037031566203d, 0.43149765310854449d, 0.90210832896272142d, 2.1029244484765348d, + -0.85123039864742922d, -0.47223437885826813d, 0.22891373886878078d, -0.79465447229176611d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.95863657006736525d, -0.25808606460596317d, 0.12003128669511379d, 1.2141239964133719d, + 0.0032153037031568779d, 0.43149765310854465d, 0.90210832896272175d, 2.1029244484765344d, + -0.28461480697879088d, -0.86440809726542045d, 0.41447925524735379d, -0.79465447229176622d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99283494044507381d, 0.094058681189907595d, 0.073700376689958685d, 1.5176549955167153d, + -0.056018011327093879d, -0.17843493822832968d, 0.98235581905255132d, 1.5771933363574009d, + 0.10554981496139189d, -0.97944572964114129d, -0.17188746100093644d, -0.79465447229176622d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.6696489291164518d, 0.72307102143229862d, 0.16952465808265391d, 2.1247169937234016d, + -0.05601801132709365d, -0.17843493822832943d, 0.98235581905255165d, 1.5771933363574009d, + 0.74056214738544823d, -0.66732995645655235d, -0.078983764632673467d, -0.79465447229176622d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.5819727895662965d, 0.050269394155928314d, 0.81165304177069297d, 2.1247169937234016d, + -0.095841516985274836d, -0.98689166362936265d, 0.129843166477215d, 0.52573111211913326d, + 0.80754075799700931d, -0.1533552485898817d, -0.56952619948826888d, -0.79465447229176633d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.3863411332821331d, 0.91915788063587522d, 0.07674189989336716d, 0.30353099910334314d, + 0.54672150789248386d, -0.16119746460886833d, -0.82165136780233039d, 4.7315800090722027d, + -0.74285673015867904d, 0.35939416782780265d, -0.56480059365170321d, -0.794654472291766d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.20727614126473407d, -0.92469594627068652d, 0.31933369413978446d, 0.30353099910334302d, + -0.54672150789248486d, 0.16119746460886911d, 0.82165136780233028d, 1.5771933363574007d, + -0.81125347091409672d, -0.34489532376393833d, -0.47213877364139295d, -0.794654472291766d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97099012011986385d, -0.21873613253416718d, -0.096605853619785353d, 1.2141239964133725d, + -0.093854350012570725d, -0.72021144794247038d, 0.68737677531054842d, 1.0514622242382674d, + -0.21993077914046086d, -0.6583691780274995d, -0.71984753789261802d, -0.794654472291766d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97213741150647937d, -0.064768238219792301d, 0.2252863255224031d, 1.5176549955167156d, + -0.095841516985274766d, -0.98689166362936265d, 0.12984316647721514d, 0.52573111211913359d, + 0.21392348345014198d, -0.14781718295507021d, -0.9656017935214205d, -0.79465447229176611d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.54908143033305934d, 0.75861960482905411d, 0.35072193833920801d, 0.60706199820668616d, + 0.82859597082354086d, -0.43925791486578636d, -0.34710402095445991d, 5.2573111211913348d, + -0.10926252787847968d, 0.48119515728732093d, -0.86977751212872545d, -0.79465447229176633d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99212587537314534d, -0.0010987106726278503d, -0.125239930732684d, 1.2141239964133725d, + -0.061220482002954366d, -0.87661280702376732d, -0.47728611874350341d, 0.0d, + -0.10926252787847965d, 0.48119515728732093d, -0.86977751212872545d, -0.79465447229176633d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.66964892911645213d, 0.72307102143229884d, 0.16952465808265399d, 0.60706199820668605d, + 0.056018011327093963d, 0.17843493822832968d, -0.98235581905255176d, 4.2058488969530687d, + -0.74056214738544823d, 0.66732995645655246d, 0.078983764632673342d, -0.79465447229176622d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.66964892911645246d, 0.72307102143229873d, 0.1695246580826538d, 0.60706199820668627d, + 0.05601801132709517d, 0.1784349382283307d, -0.98235581905255176d, 4.2058488969530687d, + -0.74056214738544834d, 0.66732995645655258d, 0.078983764632673481d, -0.79465447229176633d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.28631144367947836d, 0.20700632128770896d, 0.93550742389630615d, 0.3035309991033428d, + 0.60794198989543913d, 0.71541534241489779d, -0.34436524905882632d, 3.6801177848339357d, + -0.74056214738544812d, 0.66732995645655246d, 0.078983764632673412d, -0.79465447229176611d, + 0.0d, 0.0d, 0.0d, 1.0d), + ]; + + private static readonly Matrix4x4[] AiroceanToIcoTransforms = + [ + new Matrix4x4( + 0.57711278526259402d, 0.093854350012570739d, 0.81125347091409716d, -0.9269302059836626d, + -0.60194907251226693d, 0.7202114479424705d, 0.3448953237639385d, -1.0974189231897016d, + -0.55190411050115762d, -0.68737677531054819d, 0.47213877364139284d, 4.0774547262062395d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97099012011986396d, 0.093854350012570753d, 0.21993077914046097d, -1.6442540918239978d, + -0.21873613253416777d, 0.7202114479424705d, 0.65836917802749917d, -1.7953209624349933d, + -0.096605853619785173d, -0.68737677531054853d, 0.71984753789261868d, 3.2482719173989589d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97213741150647925d, 0.095841516985274683d, -0.21392348345014173d, -1.6526118158442071d, + -0.064768238219793189d, 0.98689166362936276d, 0.14781718295507035d, -2.9169376534209159d, + 0.22528632552240307d, -0.12984316647721503d, 0.9656017935214205d, 0.90336980367301989d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99212587537314545d, 0.061220482002954296d, 0.10926252787847969d, -1.5798063949483236d, + -0.0010987106726281115d, 0.87661280702376676d, -0.48119515728732043d, -2.6850295497149701d, + -0.12523993073268413d, 0.47728611874350318d, 0.86977751212872534d, -0.3733772136037109d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.28030414798916031d, 0.60794198989543913d, 0.74285673015867915d, -2.0080176725529477d, + -0.59918003969486111d, 0.7154153424148979d, -0.35939416782780287d, -1.0873330756182955d, + -0.74994190751773349d, -0.34436524905882648d, 0.56480059365170354d, 3.3561274015422433d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.25960661905056542d, -0.4560824615830712d, 0.85123039864742922d, 1.9642587095706099d, + -0.75800695910456173d, 0.44991125944279492d, 0.47223437885826836d, 0.32363326386975849d, + -0.59835595868528868d, -0.76783373647094011d, -0.22891373886878078d, 4.5004420028920258d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.95863657006736502d, -0.0032153037031569672d, 0.28461480697879094d, -1.2134956834766393d, + -0.25808606460596312d, -0.43149765310854504d, 0.86440809726542034d, 3.1202570350096352d, + 0.12003128669511316d, -0.90210832896272219d, -0.41447925524735352d, 3.7568638596119381d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99283494044507403d, 0.056018011327093671d, -0.10554981496139178d, -1.5249036493302057d, + 0.094058681189907539d, 0.17843493822832954d, 0.97944572964114118d, -0.086348360602766461d, + 0.073700376689957992d, -0.98235581905255132d, 0.17188746100093619d, 4.1787498817088897d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.58197278956629706d, 0.095841516985274933d, -0.8075407579970092d, -1.2973306433073619d, + 0.050269394155928723d, 0.98689166362936298d, 0.15335524858988142d, -3.0216901158893736d, + 0.81165304177069342d, -0.12984316647721506d, 0.56952619948826888d, 0.36942837800164929d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.52478230747676236d, 0.0032153037031565812d, -0.85123039864742911d, -1.0017709802028867d, + -0.76863805967839227d, 0.43149765310854499d, -0.47223437885826824d, -0.8160591689057779d, + 0.36578554232391597d, 0.90210832896272208d, 0.22891373886878089d, -1.937212836027187d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.95863657006736536d, 0.0032153037031565886d, -0.28461480697879071d, -1.3968356335709964d, + -0.25808606460596301d, 0.43149765310854504d, -0.86440809726542023d, -1.2809642403813966d, + 0.12003128669511362d, 0.90210832896272242d, 0.41447925524735396d, -1.7134307317924613d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99283494044507392d, -0.056018011327093609d, 0.10554981496139221d, -1.3345540404002831d, + 0.094058681189907414d, -0.17843493822832956d, -0.97944572964114107d, -0.63964316125891596d, + 0.073700376689958561d, 0.98235581905255198d, -0.17188746100093616d, -1.7978079362118518d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.66964892911645235d, -0.056018011327093886d, 0.74056214738544812d, -0.7459722029114777d, + 0.72307102143229895d, -0.1784349382283297d, -0.66732995645655224d, -1.7851916257515466d, + 0.16952465808265363d, 0.9823558190525522d, -0.078983764632673731d, -1.9723217754287594d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.58197278956629683d, -0.095841516985274766d, 0.80754075799700908d, -0.54442473366406441d, + 0.05026939415592821d, -0.98689166362936309d, -0.15335524858988164d, 0.29016698169232114d, + 0.81165304177069353d, 0.12984316647721511d, -0.56952619948826899d, -2.2453721446813035d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.38634113328213288d, 0.54672150789248519d, -0.74285673015867948d, -3.2944374903463687d, + 0.91915788063587534d, -0.16119746460886916d, 0.35939416782780292d, 0.76931997399327168d, + 0.076741899893367715d, -0.82165136780233039d, -0.56480059365170299d, 3.4155943230742447d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.20727614126473443d, -0.54672150789248497d, -0.81125347091409694d, 0.15470458601882164d, + -0.92469594627068674d, 0.16119746460886913d, -0.34489532376393839d, -0.24763829408199384d, + 0.31933369413978435d, 0.82165136780233017d, -0.47213877364139312d, -1.7680179253528721d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97099012011986419d, -0.093854350012570684d, -0.21993077914046077d, -1.2549870787377553d, + -0.2187361325341676d, -0.72021144794247027d, -0.65836917802749972d, 0.49967190662923489d, + -0.096605853619785464d, 0.68737677531054819d, -0.71984753789261813d, -1.1774892933385632d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.97213741150647937d, -0.095841516985274586d, 0.21392348345014212d, -1.2549870787377553d, + -0.064768238219792662d, -0.98689166362936276d, -0.14781718295507024d, 0.49967190662923483d, + 0.2252863255224028d, 0.12984316647721506d, -0.96560179352142039d, -1.1774892933385632d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.5490814303330579d, 0.82859597082354119d, -0.10926252787847955d, -4.7763392390936437d, + 0.75861960482905522d, -0.43925791486578841d, 0.48119515728732087d, 2.231170271492442d, + 0.35072193833920873d, -0.34710402095445941d, -0.86977751212872534d, 0.92075127895909092d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.99212587537314556d, -0.061220482002954546d, -0.10926252787847962d, -1.2913897891856965d, + -0.0010987106726276701d, -0.8766128070237672d, 0.48119515728732093d, 0.38371785477626236d, + -0.12523993073268386d, -0.47728611874350324d, -0.86977751212872523d, -0.53911578470019728d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.66964892911645257d, 0.056018011327093199d, -0.74056214738544823d, -1.2306127305858023d, + 0.72307102143229895d, 0.17843493822832968d, 0.66732995645655224d, -0.6591225928490807d, + 0.16952465808265377d, -0.98235581905255032d, 0.078983764632673259d, 4.0914929621004301d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.66964892911645202d, 0.056018011327093706d, -0.74056214738544801d, -1.230612730585803d, + 0.72307102143229884d, 0.17843493822832959d, 0.66732995645655235d, -0.6591225928490807d, + 0.16952465808265541d, -0.98235581905255143d, 0.078983764632673106d, 4.0914929621004337d, + 0.0d, 0.0d, 0.0d, 1.0d), + new Matrix4x4( + 0.28631144367947847d, 0.6079419898954399d, -0.74056214738544857d, -2.9126935501461353d, + 0.20700632128770891d, 0.71541534241489835d, 0.66732995645655213d, -2.1653488262928251d, + 0.93550742389630603d, -0.34436524905882709d, 0.078983764632673509d, 1.046113976300111d, + 0.0d, 0.0d, 0.0d, 1.0d), + ]; + + private static readonly Matrix4x4 HorizontalTransform = + new( + 0.0d, -1.0d, 0.0d, 5.7830422333104696d, + 1.0d, 0.0d, 0.0d, 0.0d, + 0.0d, 0.0d, 1.0d, 0.0d, + 0.0d, 0.0d, 0.0d, 1.0d); + + private static readonly Matrix4x4 HorizontalInverseTransform = + new( + 0.0d, 1.0d, 0.0d, 0.0d, + -1.0d, -0.0d, -0.0d, 5.7830422333104696d, + 0.0d, 0.0d, 1.0d, 0.0d, + 0.0d, 0.0d, 0.0d, 1.0d); + + private readonly struct Vector2(double x, double y) + { + public double X { get; } = x; + + public double Y { get; } = y; + } + + private readonly struct Vector3(double x, double y, double z) + { + public double X { get; } = x; + + public double Y { get; } = y; + + public double Z { get; } = z; + } + + private readonly struct Face(Vector3 p1, Vector3 p2, Vector3 p3) + { + public Vector3 P1 { get; } = p1; + + public Vector3 P2 { get; } = p2; + + public Vector3 P3 { get; } = p3; + } + + private readonly struct Face2D(Vector2 p1, Vector2 p2, Vector2 p3) + { + public Vector2 P1 { get; } = p1; + + public Vector2 P2 { get; } = p2; + + public Vector2 P3 { get; } = p3; + } + + private readonly struct Matrix4x4( + double m00, + double m01, + double m02, + double m03, + double m10, + double m11, + double m12, + double m13, + double m20, + double m21, + double m22, + double m23, + double m30, + double m31, + double m32, + double m33) + { + public double M00 { get; } = m00; + + public double M01 { get; } = m01; + + public double M02 { get; } = m02; + + public double M03 { get; } = m03; + + public double M10 { get; } = m10; + + public double M11 { get; } = m11; + + public double M12 { get; } = m12; + + public double M13 { get; } = m13; + + public double M20 { get; } = m20; + + public double M21 { get; } = m21; + + public double M22 { get; } = m22; + + public double M23 { get; } = m23; + + public double M30 { get; } = m30; + + public double M31 { get; } = m31; + + public double M32 { get; } = m32; + + public double M33 { get; } = m33; + + public Vector2 Transform(in Vector2 vector) + { + return new Vector2( + (this.M00 * vector.X) + (this.M01 * vector.Y) + this.M03, + (this.M10 * vector.X) + (this.M11 * vector.Y) + this.M13); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AiryProjection.cs b/src/ProjNet/CoordinateSystems/Projections/AiryProjection.cs new file mode 100644 index 00000000..3ef7ca69 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AiryProjection.cs @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Airy projection (airy). +/// +/// +/// The Airy projection is a spherical azimuthal minimum-error construction for +/// the region bounded by an angular distance from the tangency point. This +/// implementation follows PROJ's airy formulation, including the +/// aspect-dependent forward equations, the optional lat_b minimum-error +/// radius parameter, and the no_cut option for hemisphere clipping. +/// George Biddell Airy introduced this minimum-error azimuthal projection in +/// 1861. Snyder's summary notes that the construction approaches azimuthal +/// equidistant behaviour for β values up to 90 degrees. Inverse projection is +/// not supported in this implementation. +/// +/// PROJ documentation: Airy. +/// MathWorld: Airy Projection. +internal sealed class AiryProjection : MapProjection +{ + private readonly double cb; + private readonly double sinPhi0; + private readonly double cosPhi0; + private readonly double pHalfPi; + private readonly bool noCut; + private readonly Mode mode; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AiryProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AiryProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Airy"; + this.noCut = this.Parameters.ContainsKey("no_cut"); + + double beta = 0.5d * (HalfPi - DegreesToRadians(this.Parameters.GetOptionalParameterValue("lat_b", 0d))); + if (Math.Abs(beta) < Eps10) + { + this.cb = -0.5d; + } + else + { + double cotBeta = 1d / Math.Tan(beta); + this.cb = (cotBeta * cotBeta) * Math.Log(Math.Cos(beta)); + } + + if (Math.Abs(Math.Abs(this.latOrigin) - HalfPi) < Eps10) + { + this.mode = this.latOrigin < 0d ? Mode.SouthPole : Mode.NorthPole; + this.pHalfPi = this.latOrigin < 0d ? -HalfPi : HalfPi; + } + else if (Math.Abs(this.latOrigin) < Eps10) + { + this.mode = Mode.Equatorial; + this.pHalfPi = 0d; + } + else + { + this.mode = Mode.Oblique; + Sincos(this.latOrigin, out this.sinPhi0, out this.cosPhi0); + this.pHalfPi = 0d; + } + } + + private enum Mode + { + NorthPole = 0, + SouthPole = 1, + Equatorial = 2, + Oblique = 3, + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new AiryProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double sinLam = Math.Sin(lambda); + double cosLam = Math.Cos(lambda); + + switch (this.mode) + { + case Mode.Equatorial: + case Mode.Oblique: + { + double sinPhi = Math.Sin(lat); + double cosPhi = Math.Cos(lat); + double cosz = cosPhi * cosLam; + if (this.mode == Mode.Oblique) + { + cosz = (this.sinPhi0 * sinPhi) + (this.cosPhi0 * cosz); + } + + if (!this.noCut && cosz < -Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double s = 1d - cosz; + double kRho; + if (Math.Abs(s) > Eps10) + { + double t = 0.5d * (1d + cosz); + if (Math.Abs(t) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + kRho = (-Math.Log(t) / s) - (this.cb / t); + } + else + { + kRho = 0.5d - this.cb; + } + + double x = kRho * cosPhi * sinLam; + double y = this.mode == Mode.Oblique + ? kRho * ((this.cosPhi0 * sinPhi) - (this.sinPhi0 * cosPhi * cosLam)) + : kRho * sinPhi; + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + break; + } + + case Mode.NorthPole: + case Mode.SouthPole: + default: + { + double phi = Math.Abs(this.pHalfPi - lat); + if (!this.noCut && (phi - Eps10) > HalfPi) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + phi *= 0.5d; + if (phi > Eps10) + { + double t = Math.Tan(phi); + double kRho = -2d * ((Math.Log(Math.Cos(phi)) / t) + (t * this.cb)); + double x = kRho * sinLam; + double y = kRho * cosLam; + if (this.mode == Mode.NorthPole) + { + y = -y; + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + else + { + lon = 0d; + lat = 0d; + } + + break; + } + } + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Airy does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AitoffMath.cs b/src/ProjNet/CoordinateSystems/Projections/AitoffMath.cs new file mode 100644 index 00000000..9026d703 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AitoffMath.cs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; + +/// +/// Shared normalized Aitoff/Winkel Tripel forward and inverse equations. +/// +internal static class AitoffMath +{ + private const double Tolerance = 1e-12d; + private const int MaxIterations = 10; + private const int MaxRounds = 20; + private const double Pi = Math.PI; + private const double HalfPi = 0.5d * Pi; + + /// + /// Evaluates the normalized forward equations for Aitoff and Winkel Tripel. + /// + /// Longitude in radians relative to central meridian. + /// Latitude in radians. + /// Whether Winkel Tripel blending is enabled. + /// Cosine of standard parallel (Winkel Tripel only). + /// Projected x in normalized units. + /// Projected y in normalized units. + internal static void Forward(double lambda, double phi, bool winkelTripel, double cosphi1, out double x, out double y) + { + double c = 0.5d * lambda; + double d = Math.Acos(ProjectionConstants.Clamp(Math.Cos(phi) * Math.Cos(c), -1d, 1d)); + if (d == 0d) + { + x = 0d; + y = 0d; + } + else + { + double inverseSinD = 1d / Math.Sin(d); + x = 2d * d * Math.Cos(phi) * Math.Sin(c) * inverseSinD; + y = d * Math.Sin(phi) * inverseSinD; + } + + if (winkelTripel) + { + x = 0.5d * (x + (lambda * cosphi1)); + y = 0.5d * (y + phi); + } + } + + /// + /// Solves the normalized inverse equations for Aitoff and Winkel Tripel. + /// + /// Projected x in normalized units. + /// Projected y in normalized units. + /// Whether Winkel Tripel blending is enabled. + /// Cosine of standard parallel (Winkel Tripel only). + /// Recovered longitude in radians relative to central meridian. + /// Recovered latitude in radians. + internal static void Inverse(double x, double y, bool winkelTripel, double cosphi1, out double lambda, out double phi) + { + if (Math.Abs(x) <= Tolerance && Math.Abs(y) <= Tolerance) + { + lambda = 0d; + phi = 0d; + return; + } + + lambda = x; + phi = y; + + bool converged = false; + for (int round = 0; round < MaxRounds; round++) + { + for (int iteration = 0; iteration < MaxIterations; iteration++) + { + double sl = Math.Sin(0.5d * lambda); + double cl = Math.Cos(0.5d * lambda); + double sp = Math.Sin(phi); + double cp = Math.Cos(phi); + + double value = cp * cl; + double c = 1d - (value * value); + if (c <= Tolerance) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double denominator = Math.Pow(c, 1.5d); + if (Math.Abs(denominator) <= ProjectionConstants.JacobianTolerance) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double d = Math.Acos(ProjectionConstants.Clamp(value, -1d, 1d)) / denominator; + double f1 = 2d * d * c * cp * sl; + double f2 = d * c * sp; + double f1p = 2d * (((sl * cl * sp * cp) / c) - (d * sp * sl)); + double f1l = ((cp * cp * sl * sl) / c) + (d * cp * cl * sp * sp); + double f2p = ((sp * sp * cl) / c) + (d * sl * sl * cp); + double f2l = 0.5d * (((sp * cp * sl) / c) - (d * sp * cp * cp * sl * cl)); + + if (winkelTripel) + { + f1 = 0.5d * (f1 + (lambda * cosphi1)); + f2 = 0.5d * (f2 + phi); + f1p *= 0.5d; + f1l = 0.5d * (f1l + cosphi1); + f2p = 0.5d * (f2p + 1d); + f2l *= 0.5d; + } + + f1 -= x; + f2 -= y; + + double determinant = (f1p * f2l) - (f2p * f1l); + if (Math.Abs(determinant) <= ProjectionConstants.JacobianTolerance) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double deltaLambda = ((f2 * f1p) - (f1 * f2p)) / determinant; + double deltaPhi = ((f1 * f2l) - (f2 * f1l)) / determinant; + deltaLambda -= Math.Truncate(deltaLambda / Pi) * Pi; + + phi -= deltaPhi; + lambda -= deltaLambda; + + if (Math.Abs(deltaPhi) <= Tolerance && Math.Abs(deltaLambda) <= Tolerance) + { + break; + } + } + + if (phi > HalfPi) + { + phi -= 2d * (phi - HalfPi); + } + else if (phi < -HalfPi) + { + phi -= 2d * (phi + HalfPi); + } + + if (!winkelTripel && Math.Abs(Math.Abs(phi) - HalfPi) < Tolerance) + { + lambda = 0d; + } + + Forward(lambda, phi, winkelTripel, cosphi1, out double projectedX, out double projectedY); + if (Math.Abs(x - projectedX) <= Tolerance && Math.Abs(y - projectedY) <= Tolerance) + { + converged = true; + break; + } + } + + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AitoffProjection.cs b/src/ProjNet/CoordinateSystems/Projections/AitoffProjection.cs new file mode 100644 index 00000000..b9e18261 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AitoffProjection.cs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Aitoff projection (aitoff). +/// +/// +/// Aitoff is a spherical compromise projection obtained by applying the azimuthal +/// equidistant construction to halved longitudes and then doubling the horizontal result. +/// The formulation was independently verified against the Wikipedia article +/// "Aitoff projection". The auxiliary angle +/// d = acos(cos(φ) * cos(λ / 2)) together with the normalized forward +/// relations used by matches the implementation here. +/// +/// Wikipedia: Aitoff projection. +internal sealed class AitoffProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AitoffProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AitoffProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Aitoff"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new AitoffProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + AitoffMath.Forward(lambda, lat, false, 0d, out double x, out double y); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + AitoffMath.Inverse(xx, yy, false, 0d, out double lambda, out double phi); + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AlbersProjection.cs b/src/ProjNet/CoordinateSystems/Projections/AlbersProjection.cs index 8fe880de..20877fe4 100644 --- a/src/ProjNet/CoordinateSystems/Projections/AlbersProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/AlbersProjection.cs @@ -1,243 +1,220 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Albers projection. +/// +/// +/// Implements the Albers projection. The Albers projection is most commonly +/// used to project the United States of America. It gives the northern +/// border with Canada a curved appearance. +/// +/// The Albers Equal Area projection has the property that the area bounded +/// by any pair of parallels and meridians is exactly reproduced between the +/// image of those parallels and meridians in the projected domain, that is, +/// the projection preserves the correct area of the earth though distorts +/// direction, distance and shape somewhat. +/// +/// The ellipsoidal formulation was independently verified against IOGP, +/// "Geomatics Guidance Note 7, part 2: Coordinate Conversions and +/// Transformations including Formulas" (publication 373-7-2, 2019), EPSG +/// method 9822, Albers Equal Area. The authalic q-function and +/// derived ρ relationships match the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 14, +/// pp. 98-103, eqs. (14-1) through (14-12), for the classic Albers +/// equal-area conic derivation. +/// +/// EPSG method 9822: Albers Equal Area. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.1.3, pp. 93-95. +internal class AlbersProjection : MapProjection { /// - /// Implements the Albers projection. + /// Albers projection constant c. + /// + private readonly double c; + + /// + /// Radial distance at the latitude of origin. + /// + private readonly double ro0; + + /// + /// Projection exponent n. /// + private readonly double n; + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. /// - /// Implements the Albers projection. The Albers projection is most commonly - /// used to project the United States of America. It gives the northern - /// border with Canada a curved appearance. - /// - /// The Albers Equal Area - /// projection has the property that the area bounded - /// by any pair of parallels and meridians is exactly reproduced between the - /// image of those parallels and meridians in the projected domain, that is, - /// the projection preserves the correct area of the earth though distorts - /// direction, distance and shape somewhat. + /// The parameters this projection expects are listed below. + /// + /// ItemsDescriptions + /// latitude_of_false_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// longitude_of_false_originThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// latitude_of_1st_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. + /// latitude_of_2nd_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. + /// easting_at_false_originThe easting value assigned to the false origin. + /// northing_at_false_originThe northing value assigned to the false origin. + /// /// - [Serializable] - internal class AlbersProjection : MapProjection + public AlbersProjection(IEnumerable parameters) + : this(parameters, null) { - private readonly double _c; //constant c - private readonly double _ro0; - private readonly double _n; - - #region Constructors - - /// - /// Creates an instance of an Albers projection object. - /// - /// List of parameters to initialize the projection. - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// latitude_of_false_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// longitude_of_false_originThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// latitude_of_1st_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. - /// latitude_of_2nd_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. - /// easting_at_false_originThe easting value assigned to the false origin. - /// northing_at_false_originThe northing value assigned to the false origin. - /// - /// - public AlbersProjection(IEnumerable parameters) - : this(parameters, null) - { - } - - /// - /// Creates an instance of an Albers projection object. - /// - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// latitude_of_centerThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// longitude_of_centerThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// standard_parallel_1For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. - /// standard_parallel_2For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. - /// false_eastingThe easting value assigned to the false origin. - /// false_northingThe northing value assigned to the false origin. - /// - /// - /// List of parameters to initialize the projection. - /// Indicates whether the projection forward (meters to degrees or degrees to meters). - protected AlbersProjection(IEnumerable parameters, AlbersProjection inverse) - : base(parameters, inverse) - { - Name = "Albers_Conic_Equal_Area"; - - double lat0 = lat_origin; - double lat1 = DegreesToRadians(_Parameters.GetParameterValue("standard_parallel_1")); - double lat2 = DegreesToRadians(_Parameters.GetParameterValue("standard_parallel_2")); + } - if (Math.Abs(lat1 + lat2) < double.Epsilon) - throw new ArgumentException("Equal latitudes for standard parallels on opposite sides of Equator."); + /// + /// Initializes a new instance of the class. + /// + /// + /// The parameters this projection expects are listed below. + /// + /// ItemsDescriptions + /// latitude_of_centerThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// longitude_of_centerThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// standard_parallel_1For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. + /// standard_parallel_2For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. + /// false_eastingThe easting value assigned to the false origin. + /// false_northingThe northing value assigned to the false origin. + /// + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + protected AlbersProjection(IEnumerable parameters, AlbersProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Albers_Conic_Equal_Area"; - double alpha1 = alpha(lat1); - double alpha2 = alpha(lat2); + double lat0 = this.latOrigin; + double lat1 = DegreesToRadians(this.Parameters.GetParameterValue("standard_parallel_1")); + double lat2 = DegreesToRadians(this.Parameters.GetParameterValue("standard_parallel_2")); - double m1 = Math.Cos(lat1) / Math.Sqrt(1 - _es * Math.Pow(Math.Sin(lat1), 2)); - double m2 = Math.Cos(lat2) / Math.Sqrt(1 - _es * Math.Pow(Math.Sin(lat2), 2)); + if (Math.Abs(lat1 + lat2) < Eps10) + { + ArgumentGuard.ThrowArgument("Equal latitudes for standard parallels on opposite sides of Equator.", nameof(parameters)); + } - _n = (Math.Pow(m1, 2) - Math.Pow(m2, 2)) / (alpha2 - alpha1); - _c = Math.Pow(m1, 2) + (_n * alpha1); + double alpha1 = this.Alpha(lat1); + double sinLat1 = Math.Sin(lat1); + double cosLat1 = Math.Cos(lat1); + double m1 = Msfnz(this.e, sinLat1, cosLat1); + bool secant = Math.Abs(lat1 - lat2) >= Eps10; - _ro0 = Ro(alpha(lat0)); - /* - double sin_p0 = Math.Sin(lat0); - double cos_p0 = Math.Cos(lat0); - double q0 = qsfnz(e, sin_p0, cos_p0); + this.n = sinLat1; + if (secant) + { + double alpha2 = this.Alpha(lat2); + double sinLat2 = Math.Sin(lat2); + double cosLat2 = Math.Cos(lat2); + double m2 = Msfnz(this.e, sinLat2, cosLat2); - double sin_p1 = Math.Sin(lat1); - double cos_p1 = Math.Cos(lat1); - double m1 = msfnz(e,sin_p1,cos_p1); - double q1 = qsfnz(e,sin_p1,cos_p1); + this.n = ((m1 * m1) - (m2 * m2)) / (alpha2 - alpha1); + } + this.c = (m1 * m1) + (this.n * alpha1); - double sin_p2 = Math.Sin(lat2); - double cos_p2 = Math.Cos(lat2); - double m2 = msfnz(e,sin_p2,cos_p2); - double q2 = qsfnz(e,sin_p2,cos_p2); + this.ro0 = this.Ro(this.Alpha(lat0)); + } - if (Math.Abs(lat1 - lat2) > EPSLN) - ns0 = (m1 * m1 - m2 * m2)/ (q2 - q1); - else - ns0 = sin_p1; - C = m1 * m1 + ns0 * q1; - rh = this._semiMajor * Math.Sqrt(C - ns0 * q0)/ns0; - */ - } - #endregion + /// + /// Converts coordinates in radians to projected meters. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + protected sealed override void RadiansToMeters(ref double lon, ref double lat) + { + double a = this.Alpha(lat); + double ro = this.Ro(a); + double theta = this.n * (lon - this.centralMeridian); - #region Public methods + lon = ro * Math.Sin(theta); + lat = this.ro0 - (ro * Math.Cos(theta)); + } - /// - /// Converts coordinates in decimal degrees to projected meters. - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-in ordinate meters after exit. - protected sealed override void RadiansToMeters(ref double lon, ref double lat) + /// + /// Converts coordinates in projected meters to radians. + /// + /// The x-ordinate of the point in meters when entering, its longitude in radians after exit. + /// The y-ordinate of the point in meters when entering, its latitude in radians after exit. + protected sealed override void MetersToRadians(ref double x, ref double y) + { + double deltaY = this.ro0 - y; + double theta = Math.Atan2(x, deltaY); + double ro = Math.Sqrt((x * x) + (deltaY * deltaY)); + if (this.n < 0.0) { - double a = alpha(lat); - double ro = Ro(a); - double theta = _n * (lon - central_meridian); - - lon = ro * Math.Sin(theta); - lat = _ro0 - ro * Math.Cos(theta); + ro = -ro; + x = -x; + deltaY = -deltaY; + theta = Math.Atan2(x, deltaY); } - /// - /// Converts coordinates in projected meters to decimal degrees. - /// - /// The x-ordinate of the point in meters when entering, its longitude in radians after exit. - /// The y-ordinate of the point in meters when entering, its latitude in radians after exit. - protected sealed override void MetersToRadians(ref double x, ref double y) - { - double theta = Math.Atan(x / (_ro0 - y)); - double ro = Math.Sqrt(Math.Pow(x, 2) + Math.Pow(_ro0 - y, 2)); - double q = (_c - Math.Pow(ro, 2) * Math.Pow(_n, 2) / Math.Pow(_semiMajor, 2)) / _n; - //double b = Math.Sin(q / (1 - ((1 - _es) / (2 * _e)) * Math.Log((1 - _e) / (1 + _e)))); + double q = (this.c - (Math.Pow(ro, 2) * Math.Pow(this.n, 2) / Math.Pow(this.semiMajor, 2))) / this.n; - double lat = Math.Asin(q * 0.5); + double lat = this.es <= Eps10 + ? Asinz(q * 0.5) + : Math.Asin(q * 0.5); + if (this.es > Eps10) + { double preLat = double.MaxValue; int iterationCounter = 0; while (Math.Abs(lat - preLat) > 0.000001) { preLat = lat; double sin = Math.Sin(lat); - double e2sin2 = _es * Math.Pow(sin, 2); + double e2sin2 = this.es * Math.Pow(sin, 2); lat += Math.Pow(1 - e2sin2, 2) / (2 * Math.Cos(lat)) * - (q / (1 - _es) - sin / (1 - e2sin2) + - 1 / (2 * _e) * Math.Log((1 - _e * sin) / (1 + _e * sin))); + ((q / (1 - this.es)) - (sin / (1 - e2sin2)) + + (1 / (2 * this.e) * Math.Log((1 - (this.e * sin)) / (1 + (this.e * sin))))); iterationCounter++; if (iterationCounter > 25) - throw new ArgumentException( + { + ProjectionThrowHelper.ThrowInvalidOperation( "Transformation failed to converge in Albers backwards transformation"); + } } - - x = central_meridian + (theta / _n); - y = lat; } - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new AlbersProjection(_Parameters.ToProjectionParameter(), this); - return _inverse; - } + x = this.centralMeridian + (theta / this.n); + y = lat; + } - #endregion - - #region Math helper functions - - //private double ToAuthalic(double lat) - //{ - // return Math.Atan(Q(lat) / Q(Math.PI * 0.5)); - //} - //private double Q(double angle) - //{ - // double sin = Math.Sin(angle); - // double esin = e * sin; - // return Math.Abs(sin / (1 - Math.Pow(esin, 2)) - 0.5 * e) * Math.Log((1 - esin) / (1 + esin))); - //} - private double alpha(double lat) - { - double sin = Math.Sin(lat); - double sinsq = Math.Pow(sin, 2); - return (1 - _es) * (((sin / (1 - _es * sinsq)) - 1 / (2 * _e) * Math.Log((1 - _e * sin) / (1 + _e * sin)))); - } + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= new AlbersProjection(this.Parameters.ToProjectionParameter(), this); - private double Ro(double a) + return this.inverse; + } + + private double Alpha(double lat) + { + double sin = Math.Sin(lat); + if (this.es <= Eps10) { - return _semiMajor * Math.Sqrt((_c - _n * a)) / _n; + return sin + sin; } - #endregion + double sinsq = Math.Pow(sin, 2); + return (1 - this.es) * ((sin / (1 - (this.es * sinsq))) - (1 / (2 * this.e) * Math.Log((1 - (this.e * sin)) / (1 + (this.e * sin))))); + } + + private double Ro(double a) + { + return this.semiMajor * Math.Sqrt(this.c - (this.n * a)) / this.n; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/ApianProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ApianProjection.cs new file mode 100644 index 00000000..c3464571 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ApianProjection.cs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Apian Globular I projection (apian). +/// +/// +/// Apian Globular I is a historical sixteenth-century member of the +/// family. It reuses the shared globular construction +/// without Bacon latitude scaling and without the Ortelius wide-longitude branch. +/// This derived class represents Petrus Apianus's 1524 first globular world +/// projection and delegates the shared circular-arc meridian construction to +/// with the Bacon and Ortelius special cases disabled. +/// +/// PROJ documentation: Apian Globular I. +internal sealed class ApianProjection : BaconProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ApianProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ApianProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, bacon: false, ortelius: false, "Apian_Globular_I") + { + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AugustProjection.cs b/src/ProjNet/CoordinateSystems/Projections/AugustProjection.cs new file mode 100644 index 00000000..de203785 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AugustProjection.cs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical August Epicycloidal projection (august). +/// +/// +/// The August Epicycloidal projection is a spherical conformal world-map +/// construction that transforms the reduced longitude and latitude through an +/// epicycloidal polynomial form. This implementation follows PROJ's +/// august formulation and uses the standard 4/3 scale factor in the +/// forward equations. +/// Snyder catalogs it as F. W. O. August's conformal epicycloidal world +/// projection, whose 180-degree meridians form a two-cusped epicycloid while the +/// equator and central meridian remain straight. +/// This projection remains forward-only in PROJ and in this implementation, so +/// inverse projection is not supported. +/// +/// PROJ documentation: August Epicycloidal. +internal sealed class AugustProjection : MapProjection +{ + private const double M = 1.333333333333333d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AugustProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AugustProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "August_Epicycloidal"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new AugustProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double t = Math.Tan(0.5d * lat); + double c1 = Math.Sqrt(1d - (t * t)); + lambda *= 0.5d; + double c = 1d + (c1 * Math.Cos(lambda)); + if (Math.Abs(c) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double x1 = Math.Sin(lambda) * c1 / c; + double y1 = t / c; + double x12 = x1 * x1; + double y12 = y1 * y1; + + lon = this.SphericalRadius * (M * x1 * (3d + x12 - (3d * y12))); + lat = this.SphericalRadius * (M * y1 * (3d + (3d * x12) - y12)); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("August Epicycloidal does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AuxiliaryLatitudeSeries.cs b/src/ProjNet/CoordinateSystems/Projections/AuxiliaryLatitudeSeries.cs new file mode 100644 index 00000000..e1b2958c --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AuxiliaryLatitudeSeries.cs @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; + +/// +/// Provides the subset of PROJ auxiliary-latitude series needed by the exact ETMERC kernel. +/// +/// +/// The series are sixth-order expansions in the third flattening +/// n = (a - b) / (a + b), following PROJ's implementation from +/// latitudes.cpp. Only the conformal/geographic and conformal/rectifying +/// conversions used by ETMERC are included here. +/// +internal static class AuxiliaryLatitudeSeries +{ + private const int Order = 6; + + private static readonly double[] ConformalToGeographicSeries = + [ + 2.0d, -2.0d / 3.0d, -2.0d, 116.0d / 45.0d, 26.0d / 45.0d, -2854.0d / 675.0d, + 7.0d / 3.0d, -8.0d / 5.0d, -227.0d / 45.0d, 2704.0d / 315.0d, 2323.0d / 945.0d, + 56.0d / 15.0d, -136.0d / 35.0d, -1262.0d / 105.0d, 73814.0d / 2835.0d, + 4279.0d / 630.0d, -332.0d / 35.0d, -399572.0d / 14175.0d, + 4174.0d / 315.0d, -144838.0d / 6237.0d, + 601676.0d / 22275.0d, + ]; + + private static readonly double[] GeographicToConformalSeries = + [ + -2.0d, 2.0d / 3.0d, 4.0d / 3.0d, -82.0d / 45.0d, 32.0d / 45.0d, 4642.0d / 4725.0d, + 5.0d / 3.0d, -16.0d / 15.0d, -13.0d / 9.0d, 904.0d / 315.0d, -1522.0d / 945.0d, + -26.0d / 15.0d, 34.0d / 21.0d, 8.0d / 5.0d, -12686.0d / 2835.0d, + 1237.0d / 630.0d, -12.0d / 5.0d, -24832.0d / 14175.0d, + -734.0d / 315.0d, 109598.0d / 31185.0d, + 444337.0d / 155925.0d, + ]; + + private static readonly double[] ConformalToRectifyingSeries = + [ + 1.0d / 2.0d, -2.0d / 3.0d, 5.0d / 16.0d, 41.0d / 180.0d, -127.0d / 288.0d, 7891.0d / 37800.0d, + 13.0d / 48.0d, -3.0d / 5.0d, 557.0d / 1440.0d, 281.0d / 630.0d, -1983433.0d / 1935360.0d, + 61.0d / 240.0d, -103.0d / 140.0d, 15061.0d / 26880.0d, 167603.0d / 181440.0d, + 49561.0d / 161280.0d, -179.0d / 168.0d, 6601661.0d / 7257600.0d, + 34729.0d / 80640.0d, -3418889.0d / 1995840.0d, + 212378941.0d / 319334400.0d, + ]; + + private static readonly double[] RectifyingToConformalSeries = + [ + -1.0d / 2.0d, 2.0d / 3.0d, -37.0d / 96.0d, 1.0d / 360.0d, 81.0d / 512.0d, -96199.0d / 604800.0d, + -1.0d / 48.0d, -1.0d / 15.0d, 437.0d / 1440.0d, -46.0d / 105.0d, 1118711.0d / 3870720.0d, + -17.0d / 480.0d, 37.0d / 840.0d, 209.0d / 4480.0d, -5569.0d / 90720.0d, + -4397.0d / 161280.0d, 11.0d / 504.0d, 830251.0d / 7257600.0d, + -4583.0d / 161280.0d, 108847.0d / 3991680.0d, + -20648693.0d / 638668800.0d, + ]; + + private static readonly double[] RectifyingRadiusSeries = + [ + 1.0d, + 1.0d / 4.0d, + 1.0d / 64.0d, + 1.0d / 256.0d, + ]; + + /// + /// Builds the conformal-to-geographic conversion coefficients for the supplied third flattening. + /// + /// The third flattening (a - b) / (a + b). + /// An array of six Fourier coefficients. + internal static double[] BuildConformalToGeographicCoefficients(double thirdFlattening) + { + return BuildCoefficients(thirdFlattening, ConformalToGeographicSeries); + } + + /// + /// Builds the geographic-to-conformal conversion coefficients for the supplied third flattening. + /// + /// The third flattening (a - b) / (a + b). + /// An array of six Fourier coefficients. + internal static double[] BuildGeographicToConformalCoefficients(double thirdFlattening) + { + return BuildCoefficients(thirdFlattening, GeographicToConformalSeries); + } + + /// + /// Builds the conformal-to-rectifying conversion coefficients for the supplied third flattening. + /// + /// The third flattening (a - b) / (a + b). + /// An array of six Fourier coefficients. + internal static double[] BuildConformalToRectifyingCoefficients(double thirdFlattening) + { + return BuildCoefficients(thirdFlattening, ConformalToRectifyingSeries); + } + + /// + /// Builds the rectifying-to-conformal conversion coefficients for the supplied third flattening. + /// + /// The third flattening (a - b) / (a + b). + /// An array of six Fourier coefficients. + internal static double[] BuildRectifyingToConformalCoefficients(double thirdFlattening) + { + return BuildCoefficients(thirdFlattening, RectifyingToConformalSeries); + } + + /// + /// Converts an auxiliary latitude angle using a precomputed Fourier series. + /// + /// The source auxiliary latitude in radians. + /// The Fourier coefficients for the desired conversion. + /// The converted auxiliary latitude in radians. + internal static double Convert(double latitude, double[] coefficients) + { + return Convert(latitude, Math.Sin(latitude), Math.Cos(latitude), coefficients); + } + + /// + /// Converts an auxiliary latitude angle using a precomputed Fourier series and supplied trigonometric terms. + /// + /// The source auxiliary latitude in radians. + /// The sine of . + /// The cosine of . + /// The Fourier coefficients for the desired conversion. + /// The converted auxiliary latitude in radians. + internal static double Convert(double latitude, double sinLatitude, double cosLatitude, double[] coefficients) + { + coefficients = ArgumentGuard.ThrowIfNull(coefficients, nameof(coefficients)); + return latitude + Clenshaw(sinLatitude, cosLatitude, coefficients); + } + + /// + /// Computes the rectifying radius used by PROJ's exact ETMERC kernel. + /// + /// The third flattening (a - b) / (a + b). + /// The rectifying radius normalized by the semi-major axis. + internal static double RectifyingRadius(double thirdFlattening) + { + return Polyval(thirdFlattening * thirdFlattening, RectifyingRadiusSeries.AsSpan()) / (1d + thirdFlattening); + } + + private static double[] BuildCoefficients(double thirdFlattening, double[] seriesCoefficients) + { + double[] coefficients = new double[Order]; + double power = thirdFlattening; + int offset = 0; + + for (int coefficientIndex = 0; coefficientIndex < Order; coefficientIndex++) + { + int polynomialOrder = Order - coefficientIndex - 1; + coefficients[coefficientIndex] = power * Polyval(thirdFlattening, seriesCoefficients.AsSpan(offset, polynomialOrder + 1)); + offset += polynomialOrder + 1; + power *= thirdFlattening; + } + + return coefficients; + } + + private static double Polyval(double x, ReadOnlySpan coefficients) + { + double value = coefficients[coefficients.Length - 1]; + for (int i = coefficients.Length - 2; i >= 0; i--) + { + value = (value * x) + coefficients[i]; + } + + return value; + } + + private static double Clenshaw(double sinLatitude, double cosLatitude, double[] coefficients) + { + double accumulator0 = 0d; + double accumulator1 = 0d; + double x = 2d * (cosLatitude - sinLatitude) * (cosLatitude + sinLatitude); + + for (int i = coefficients.Length - 1; i >= 0; i--) + { + double next = (x * accumulator0) - accumulator1 + coefficients[i]; + accumulator1 = accumulator0; + accumulator0 = next; + } + + return 2d * sinLatitude * cosLatitude * accumulator0; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/AzimuthalEquidistantProjection.cs b/src/ProjNet/CoordinateSystems/Projections/AzimuthalEquidistantProjection.cs new file mode 100644 index 00000000..11f97127 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/AzimuthalEquidistantProjection.cs @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Azimuthal Equidistant map projection. +/// +/// +/// The Azimuthal Equidistant projection preserves both distance and direction from +/// the projection centre. All points on the map are at proportionally correct distances +/// from the centre, and the azimuth (bearing) from the centre to any other point is +/// correctly represented. This implementation supports both spherical and ellipsoidal +/// formulations for forward and inverse transformations. +/// The spherical formulation was independently verified against Snyder, "Map +/// Projections - A Working Manual" (USGS Professional Paper 1395, 1987), section 25, +/// Azimuthal Equidistant. The published forward scale factor k = c / sin(c) and +/// inverse recovery from c = ρ / R match the equatorial, oblique, and polar +/// aspect branches implemented here. The ellipsoidal extension uses direct and inverse +/// geodesic solvers for the general case together with meridional-arc handling for +/// polar aspects, matching the rigorous azimuthal-equidistant geodesic method carried +/// by EPSG method 1125 and documented by PROJ for aeqd. +/// +/// EPSG method 1125: Azimuthal Equidistant. +/// USGS Professional Paper 1395: Map Projections - A Working Manual. +/// PROJ documentation: Azimuthal Equidistant. +/// Wikipedia: Azimuthal equidistant projection. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.2.4, pp. 105-107. +internal sealed class AzimuthalEquidistantProjection : MapProjection +{ + private const double PathologicalTolerance = 1e-14d; + + private readonly bool ellipsoidal; + private readonly bool guam; + private readonly ProjectionMode mode; + private readonly double sinPhi0; + private readonly double cosPhi0; + private readonly double meridionalOriginDistance; + private readonly double meridionalPoleDistance; + private readonly double flattening; + private readonly double eccentricityPrimeSquared; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public AzimuthalEquidistantProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public AzimuthalEquidistantProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Azimuthal_Equidistant"; + this.ellipsoidal = this.es > 0d; + this.guam = this.ellipsoidal && Math.Abs(this.Parameters.GetOptionalParameterValue("guam", 0d)) > 0d; + this.mode = DetermineMode(this.latOrigin); + Sincos(this.latOrigin, out this.sinPhi0, out this.cosPhi0); + this.flattening = (this.semiMajor - this.semiMinor) / this.semiMajor; + this.eccentricityPrimeSquared = ((this.semiMajor * this.semiMajor) - (this.semiMinor * this.semiMinor)) / (this.semiMinor * this.semiMinor); + if (this.guam) + { + this.meridionalOriginDistance = this.Mlfn(this.latOrigin, this.sinPhi0, this.cosPhi0); + } + + if (this.ellipsoidal && (this.mode == ProjectionMode.NorthPole || this.mode == ProjectionMode.SouthPole)) + { + double poleLatitude = this.mode == ProjectionMode.NorthPole ? HalfPi : -HalfPi; + double poleSine = this.mode == ProjectionMode.NorthPole ? 1d : -1d; + this.meridionalPoleDistance = this.Mlfn(poleLatitude, poleSine, 0d); + } + } + + private enum ProjectionMode + { + NorthPole, + SouthPole, + Equatorial, + Oblique, + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new AzimuthalEquidistantProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + + if (this.ellipsoidal) + { + if (this.guam) + { + this.ForwardEllipsoidalGuam(lambda, lat, out lon, out lat); + return; + } + + if (this.mode == ProjectionMode.NorthPole || this.mode == ProjectionMode.SouthPole) + { + this.ForwardEllipsoidalPolar(this.mode, lambda, lat, out lon, out lat); + return; + } + + this.ForwardEllipsoidalGeneral(lambda, lat, out lon, out lat); + return; + } + + this.ForwardSpherical(lambda, lat, out lon, out lat); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + if (this.ellipsoidal) + { + if (this.guam) + { + this.InverseEllipsoidalGuam(x, y, out x, out y); + return; + } + + if (this.mode == ProjectionMode.NorthPole || this.mode == ProjectionMode.SouthPole) + { + this.InverseEllipsoidalPolar(this.mode, x, y, out x, out y); + return; + } + + this.InverseEllipsoidalGeneral(x, y, out x, out y); + return; + } + + this.InverseSpherical(x, y, out x, out y); + } + + private static ProjectionMode DetermineMode(double latitudeOfOrigin) + { + if (Math.Abs(Math.Abs(latitudeOfOrigin) - HalfPi) < Eps10) + { + return latitudeOfOrigin < 0d ? ProjectionMode.SouthPole : ProjectionMode.NorthPole; + } + + if (Math.Abs(latitudeOfOrigin) < Eps10) + { + return ProjectionMode.Equatorial; + } + + return ProjectionMode.Oblique; + } + + private void ForwardSpherical(double lambda, double phi, out double x, out double y) + { + if (this.mode == ProjectionMode.NorthPole || this.mode == ProjectionMode.SouthPole) + { + double cosineLambda = Math.Cos(lambda); + if (this.mode == ProjectionMode.NorthPole) + { + phi = -phi; + cosineLambda = -cosineLambda; + } + + if (Math.Abs(phi - HalfPi) < Eps10) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(phi), "Coordinate is outside the valid Azimuthal Equidistant domain."); + } + + double rho = this.SphericalRadius * (HalfPi + phi); + x = rho * Math.Sin(lambda); + y = rho * cosineLambda; + return; + } + + double cosPhi = Math.Cos(phi); + double sinPhi = Math.Sin(phi); + double cosLambda = Math.Cos(lambda); + double sinLambda = Math.Sin(lambda); + + if (this.mode == ProjectionMode.Equatorial) + { + double cosC = cosPhi * cosLambda; + if (Math.Abs(Math.Abs(cosC) - 1d) < PathologicalTolerance) + { + if (cosC < 0d) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(phi), "Coordinate is outside the valid Azimuthal Equidistant domain."); + } + + this.ForwardEllipsoidalGeneral(lambda, phi, out x, out y); + return; + } + + double c = Math.Acos(cosC); + double k = c / Math.Sin(c); + x = this.SphericalRadius * k * cosPhi * sinLambda; + y = this.SphericalRadius * k * sinPhi; + return; + } + + double cosPhiCosLambda = cosPhi * cosLambda; + double cosCOblique = (this.sinPhi0 * sinPhi) + (this.cosPhi0 * cosPhiCosLambda); + if (Math.Abs(Math.Abs(cosCOblique) - 1d) < PathologicalTolerance) + { + if (cosCOblique < 0d) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(phi), "Coordinate is outside the valid Azimuthal Equidistant domain."); + } + + this.ForwardEllipsoidalGeneral(lambda, phi, out x, out y); + return; + } + + double cOblique = Math.Acos(cosCOblique); + double kOblique = cOblique / Math.Sin(cOblique); + x = this.SphericalRadius * kOblique * cosPhi * sinLambda; + y = this.SphericalRadius * kOblique * ((this.cosPhi0 * sinPhi) - (this.sinPhi0 * cosPhiCosLambda)); + } + + private void InverseSpherical(double xMeter, double yMeter, out double lon, out double lat) + { + double x = xMeter * this.InverseSphericalRadius; + double y = yMeter * this.InverseSphericalRadius; + + double rho = Hypot(x, y); + if (rho > PI) + { + if ((rho - Eps10) > PI) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(xMeter), "Coordinate is outside the valid Azimuthal Equidistant domain."); + } + + rho = PI; + } + else if (rho < Eps10) + { + lon = this.centralMeridian; + lat = this.latOrigin; + return; + } + + if (this.mode == ProjectionMode.NorthPole) + { + lat = HalfPi - rho; + lon = Adjust_lon(this.centralMeridian + Math.Atan2(x, -y)); + return; + } + + if (this.mode == ProjectionMode.SouthPole) + { + lat = rho - HalfPi; + lon = Adjust_lon(this.centralMeridian + Math.Atan2(x, y)); + return; + } + + double sinc = Math.Sin(rho); + double cosc = Math.Cos(rho); + if (this.mode == ProjectionMode.Equatorial) + { + lat = Asinz(y * sinc / rho); + double xAdjusted = x * sinc; + double yAdjusted = cosc * rho; + lon = Adjust_lon(this.centralMeridian + (yAdjusted == 0d ? 0d : Math.Atan2(xAdjusted, yAdjusted))); + return; + } + + lat = Asinz((cosc * this.sinPhi0) + ((y * sinc * this.cosPhi0) / rho)); + double yTerm = (cosc - (this.sinPhi0 * Math.Sin(lat))) * rho; + double xTerm = x * sinc * this.cosPhi0; + lon = Adjust_lon(this.centralMeridian + (yTerm == 0d ? 0d : Math.Atan2(xTerm, yTerm))); + } + + private void ForwardEllipsoidalPolar(ProjectionMode polarMode, double lambda, double phi, out double x, out double y) + { + double cosPhi = Math.Cos(phi); + double sinPhi = Math.Sin(phi); + double rho = Math.Abs(this.meridionalPoleDistance - this.Mlfn(phi, sinPhi, cosPhi)); + double cosineLambda = Math.Cos(lambda); + if (polarMode == ProjectionMode.NorthPole) + { + cosineLambda = -cosineLambda; + } + + x = this.SphericalRadius * rho * Math.Sin(lambda); + y = this.SphericalRadius * rho * cosineLambda; + } + + private void ForwardEllipsoidalGuam(double lambda, double phi, out double x, out double y) + { + double cosPhi = Math.Cos(phi); + double sinPhi = Math.Sin(phi); + double t = 1d / Math.Sqrt(1d - (this.es * sinPhi * sinPhi)); + x = this.SphericalRadius * lambda * cosPhi * t; + y = this.SphericalRadius * ((this.Mlfn(phi, sinPhi, cosPhi) - this.meridionalOriginDistance) + (0.5d * lambda * lambda * cosPhi * sinPhi * t)); + } + + private void InverseEllipsoidalPolar(ProjectionMode polarMode, double xMeter, double yMeter, out double lon, out double lat) + { + double x = xMeter * this.InverseSphericalRadius; + double y = yMeter * this.InverseSphericalRadius; + double rho = Hypot(x, y); + + lat = this.Inv_mlfn( + polarMode == ProjectionMode.NorthPole + ? this.meridionalPoleDistance - rho + : this.meridionalPoleDistance + rho); + + lon = Adjust_lon(this.centralMeridian + Math.Atan2(x, polarMode == ProjectionMode.NorthPole ? -y : y)); + } + + private void InverseEllipsoidalGuam(double xMeter, double yMeter, out double lon, out double lat) + { + if (this.scaleFactor == 0d) + { + ProjectionThrowHelper.ThrowInvalidOperation("Scale factor must be non-zero for Guam Azimuthal Equidistant inverse."); + } + + double x = xMeter * this.InverseSphericalRadius; + double y = yMeter * this.InverseSphericalRadius; + double xSquaredHalf = 0.5d * x * x; + lat = this.latOrigin; + double t = 0d; + for (int i = 0; i < 3; i++) + { + t = this.e * Math.Sin(lat); + t = Math.Sqrt(1d - (t * t)); + lat = this.Inv_mlfn(this.meridionalOriginDistance + y - (xSquaredHalf * Math.Tan(lat) * t)); + } + + lon = Adjust_lon(this.centralMeridian + ((x * t) / Math.Cos(lat))); + } + + private void ForwardEllipsoidalGeneral(double lambda, double phi, out double x, out double y) + { + if (Math.Abs(lambda) < Eps10 && Math.Abs(phi - this.latOrigin) < Eps10) + { + x = 0d; + y = 0d; + return; + } + + if (!EllipsoidalGeodesic.TryVincentyInverse(this.semiMinor, this.flattening, this.eccentricityPrimeSquared, this.latOrigin, 0d, phi, lambda, out double distance, out double azimuth)) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(phi), "Coordinate is outside the valid Azimuthal Equidistant domain."); + } + + double scaledDistance = distance * this.scaleFactor; + x = scaledDistance * Math.Sin(azimuth); + y = scaledDistance * Math.Cos(azimuth); + } + + private void InverseEllipsoidalGeneral(double xMeter, double yMeter, out double lon, out double lat) + { + double rho = Hypot(xMeter, yMeter); + if (rho < Eps10) + { + lon = this.centralMeridian; + lat = this.latOrigin; + return; + } + + if (this.scaleFactor == 0d) + { + ProjectionThrowHelper.ThrowInvalidOperation("Scale factor must be non-zero for ellipsoidal Azimuthal Equidistant inverse."); + } + + double azimuth = Math.Atan2(xMeter, yMeter); + double distance = rho / this.scaleFactor; + if (!EllipsoidalGeodesic.TryVincentyDirect(this.semiMinor, this.flattening, this.eccentricityPrimeSquared, this.latOrigin, 0d, azimuth, distance, out double phi, out double lambda)) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(xMeter), "Coordinate is outside the valid Azimuthal Equidistant domain."); + } + + lon = Adjust_lon(this.centralMeridian + lambda); + lat = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/BaconProjection.cs b/src/ProjNet/CoordinateSystems/Projections/BaconProjection.cs new file mode 100644 index 00000000..769706ec --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/BaconProjection.cs @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Bacon / Apian / Ortelius globular projections. +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward family logic was independently verified against the historical oval and +/// globular constructions associated with Bacon, Apian, and Ortelius. The implementation +/// matches the shared circular-arc longitude construction and the Ortelius wide-longitude +/// branch used beyond ±90°. +/// +internal class BaconProjection : MapProjection +{ + private const double HalfPiSquared = 2.46740110027233965467d; + + private readonly bool bacon; + private readonly bool ortelius; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public BaconProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public BaconProjection(IEnumerable parameters, MapProjection? inverse) + : this(parameters, inverse, bacon: true, ortelius: false, "Bacon_Globular") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + /// Whether to use Bacon latitude scaling. + /// Whether to use Ortelius branch for wide longitudes. + /// Projection display name. + protected BaconProjection( + IEnumerable parameters, + MapProjection? inverse, + bool bacon, + bool ortelius, + string name) + : base(parameters, inverse) + { + this.Name = name; + this.bacon = bacon; + this.ortelius = ortelius; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new BaconProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double y = this.bacon ? HalfPi * Math.Sin(lat) : lat; + double absLambda = Math.Abs(lambda); + double x = 0d; + + if (absLambda >= Eps10) + { + if (this.ortelius && absLambda >= HalfPi) + { + x = Math.Sqrt(HalfPiSquared - (lat * lat) + Eps10) + absLambda - HalfPi; + } + else + { + double f = 0.5d * ((HalfPiSquared / absLambda) + absLambda); + x = absLambda - f + Math.Sqrt((f * f) - (y * y)); + } + + if (lambda < 0d) + { + x = -x; + } + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Bacon globular family does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Bertin1953Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Bertin1953Projection.cs new file mode 100644 index 00000000..915f5da0 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Bertin1953Projection.cs @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + + /// +/// Implements the Bertin 1953 projection (bertin1953). +/// +/// +/// The Bertin 1953 projection is a spherical world map projection derived from the +/// historical design introduced by Jacques Bertin in 1953. This implementation follows +/// the fixed-parameter computational formulation documented by PROJ and Philippe Riviere +/// (2017), using the published constants Fu = 1.4, K = 12, W = 1.68, +/// a latitude rotation of -42 degrees, and a longitude offset of -16.5 degrees before +/// the final warping step. +/// Inverse projection is not supported in this implementation. +/// +/// PROJ documentation: Bertin 1953. +/// Philippe Riviere (2017): Bertin Projection (1953). +internal sealed class Bertin1953Projection : MapProjection +{ + private const double Fu = 1.4d; + private const double K = 12d; + private const double W = 1.68d; + private const double DeltaPhi = -42d * PI / 180d; + private const double DeltaGamma = 0d; + private const double LambdaOffset = -16.5d * PI / 180d; + + private readonly double cosDeltaPhi; + private readonly double sinDeltaPhi; + private readonly double cosDeltaGamma; + private readonly double sinDeltaGamma; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Bertin1953Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Bertin1953Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Bertin_1953"; + this.cosDeltaPhi = Math.Cos(DeltaPhi); + this.sinDeltaPhi = Math.Sin(DeltaPhi); + this.cosDeltaGamma = Math.Cos(DeltaGamma); + this.sinDeltaGamma = Math.Sin(DeltaGamma); + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new Bertin1953Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = lon + LambdaOffset; + double phi = lat; + + double cosPhi = Math.Cos(phi); + double x = Math.Cos(lambda) * cosPhi; + double y = Math.Sin(lambda) * cosPhi; + double z = Math.Sin(phi); + + double z0 = (z * this.cosDeltaPhi) + (x * this.sinDeltaPhi); + lambda = Math.Atan2( + (y * this.cosDeltaGamma) - (z0 * this.sinDeltaGamma), + (x * this.cosDeltaPhi) - (z * this.sinDeltaPhi)); + z0 = (z0 * this.cosDeltaGamma) + (y * this.sinDeltaGamma); + phi = Asinz(z0); + lambda = Adjust_lon(lambda); + + if ((lambda + phi) < -Fu) + { + double d = (lambda - phi + 1.6d) * (lambda + phi + Fu) / 8d; + lambda += d; + phi -= 0.8d * d * Math.Sin(phi + (PI * 0.5d)); + } + + cosPhi = Math.Cos(phi); + double denom = 1d + (cosPhi * Math.Cos(lambda * 0.5d)); + if (Math.Abs(denom) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double dd = Math.Sqrt(2d / denom); + double xOut = W * dd * cosPhi * Math.Sin(lambda * 0.5d); + double yOut = dd * Math.Sin(phi); + + double post = (1d - Math.Cos(lambda * phi)) / K; + if (yOut < 0d) + { + xOut *= 1d + post; + } + + if (yOut > 0d) + { + yOut *= 1d + ((post / 1.5d) * xOut * xOut); + } + + lon = this.SphericalRadius * xOut; + lat = this.SphericalRadius * yOut; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Bertin 1953 does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/BipolarConicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/BipolarConicProjection.cs new file mode 100644 index 00000000..7b305ddb --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/BipolarConicProjection.cs @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical bipolar conic projection of the western hemisphere (bipc). +/// +/// +/// Bipolar Conic is the spherical western-hemisphere projection described by Snyder. The +/// implementation switches between the two cone centers, evaluates the shared bipolar-conic +/// radius and azimuth relations, and optionally applies the historical noskew +/// presentation used by PROJ. +/// +internal sealed class BipolarConicProjection : MapProjection +{ + private const double OneEpsilon = 1.000000001d; + private const int Iterations = 10; + private const double LamB = -0.34894976726250681539d; + private const double N = 0.63055844881274687180d; + private const double F = 1.89724742567461030582d; + private const double Azab = 0.81650043674686363166d; + private const double Azba = 1.82261843856185925133d; + private const double T = 1.27246578267089012270d; + private const double Rhoc = 1.20709121521568721927d; + private const double CosAzc = 0.69691523038678375519d; + private const double SinAzc = 0.71715351331143607555d; + private const double Cos20 = 0.93969262078590838411d; + private const double Sin20 = -0.34202014332566873287d; + private const double R110 = 1.91986217719376253360d; + private const double R104 = 1.81514242207410275904d; + + private readonly bool noSkew; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public BipolarConicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public BipolarConicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Bipolar_Conic"; + this.noSkew = this.Parameters.ContainsKey("ns") || this.Parameters.ContainsKey("noskew"); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new BipolarConicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double cphi = Math.Cos(lat); + double sphi = Math.Sin(lat); + double sdlam = LamB - lambda; + double cdlam = Math.Cos(sdlam); + sdlam = Math.Sin(sdlam); + + bool atPole = Math.Abs(Math.Abs(lat) - HalfPi) < Eps10; + double az = atPole + ? (lat < 0d ? PI : 0d) + : Math.Atan2(sdlam, ProjectionConstants.OneOverSqrt2 * ((sphi / cphi) - cdlam)); + + bool tag = az > Azba; + double y = tag ? Rhoc : -Rhoc; + double av = tag ? Azab : Azba; + double z = 0d; + if (tag) + { + sdlam = lambda + R110; + cdlam = Math.Cos(sdlam); + sdlam = Math.Sin(sdlam); + z = (Sin20 * sphi) + (Cos20 * cphi * cdlam); + if (Math.Abs(z) > 1d) + { + if (Math.Abs(z) > OneEpsilon) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + z = z < 0d ? -1d : 1d; + } + else + { + z = Math.Acos(z); + } + + if (!atPole) + { + double tphi = sphi / cphi; + az = Math.Atan2(sdlam, (Cos20 * tphi) - (Sin20 * cdlam)); + } + } + else + { + z = ProjectionConstants.OneOverSqrt2 * (sphi + (cphi * cdlam)); + if (Math.Abs(z) > 1d) + { + if (Math.Abs(z) > OneEpsilon) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + z = z < 0d ? -1d : 1d; + } + else + { + z = Math.Acos(z); + } + } + + if (z < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double t = Math.Pow(Math.Tan(0.5d * z), N); + double r = F * t; + double al = 0.5d * (R104 - z); + if (al < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + al = (t + Math.Pow(al, N)) / T; + if (Math.Abs(al) > 1d) + { + if (Math.Abs(al) > OneEpsilon) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + al = al < 0d ? -1d : 1d; + } + else + { + al = Math.Acos(al); + } + + t = N * (av - az); + if (Math.Abs(t) < al) + { + double denominator = Math.Cos(al + (tag ? t : -t)); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + r /= denominator; + } + + double x = r * Math.Sin(t); + y += (tag ? -r : r) * Math.Cos(t); + if (this.noSkew) + { + double xt = x; + x = (-x * CosAzc) - (y * SinAzc); + y = (-y * CosAzc) + (xt * SinAzc); + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + if (this.noSkew) + { + double t = xx; + xx = (-xx * CosAzc) + (yy * SinAzc); + yy = (-yy * CosAzc) - (t * SinAzc); + } + + bool neg = xx < 0d; + double s = neg ? Sin20 : ProjectionConstants.OneOverSqrt2; + double c = neg ? Cos20 : ProjectionConstants.OneOverSqrt2; + double av = neg ? Azab : Azba; + if (neg) + { + yy = Rhoc - yy; + } + else + { + yy += Rhoc; + } + + double r = Hypot(xx, yy); + double rl = r; + double rp = r; + double az = Math.Atan2(xx, yy); + double absAz = Math.Abs(az); + double z = 0d; + + int i = Iterations; + for (; i > 0; i--) + { + z = 2d * Math.Atan(Math.Pow(r / F, 1d / N)); + double alCosArg = (Math.Pow(Math.Tan(0.5d * z), N) + Math.Pow(Math.Tan(0.5d * (R104 - z)), N)) / T; + alCosArg = ProjectionConstants.ClampToUnit(alCosArg); + double al = Math.Acos(alCosArg); + if (absAz < al) + { + r = rp * Math.Cos(al + (neg ? az : -az)); + } + + if (Math.Abs(rl - r) < Eps10) + { + break; + } + + rl = r; + } + + if (i == 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + az = av - (az / N); + double phi = Asinz((s * Math.Cos(z)) + (c * Math.Sin(z) * Math.Cos(az))); + double lambda = Math.Atan2(Math.Sin(az), (c / Math.Tan(z)) - (s * Math.Cos(az))); + if (neg) + { + lambda -= R110; + } + else + { + lambda = LamB - lambda; + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/BoggsProjection.cs b/src/ProjNet/CoordinateSystems/Projections/BoggsProjection.cs new file mode 100644 index 00000000..4d7879db --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/BoggsProjection.cs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Boggs Eumorphic projection (boggs). +/// +/// +/// The Boggs Eumorphic projection is a compromise pseudocylindrical projection combining +/// properties of the sinusoidal and Mollweide projections. +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the standard Boggs +/// construction as the mean of sinusoidal and Mollweide-style behavior. The implementation +/// matches the auxiliary-angle iteration for θ + sin(θ) = π * sin(φ) and +/// the resulting easting and northing equations. +/// +internal sealed class BoggsProjection : MapProjection +{ + private const int Iterations = 20; + private const double Fxc = 2.00276d; + private const double Fxc2 = 1.11072d; + private const double Fyc = 0.49931d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public BoggsProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public BoggsProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Boggs"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new BoggsProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double theta = lat; + double x = 0d; + if (Math.Abs(Math.Abs(lat) - HalfPi) >= Eps7) + { + double c = Math.Sin(theta) * PI; + for (int i = Iterations; i > 0; i--) + { + double th1 = (theta + Math.Sin(theta) - c) / (1d + Math.Cos(theta)); + theta -= th1; + if (Math.Abs(th1) < Eps7) + { + break; + } + } + + theta *= 0.5d; + double denominator = (1d / Math.Cos(lat)) + (Fxc2 / Math.Cos(theta)); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Fxc * lambda / denominator; + } + + double y = Fyc * (lat + (ProjectionConstants.Sqrt2 * Math.Sin(theta))); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Boggs does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/BonneProjection.cs b/src/ProjNet/CoordinateSystems/Projections/BonneProjection.cs new file mode 100644 index 00000000..8289f1fe --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/BonneProjection.cs @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Bonne projection (bonne). +/// +/// +/// A pseudoconical equal-area projection in which all parallels are represented as concentric +/// circular arcs with true spacing, and all meridians are equally spaced along each parallel. +/// Both spherical and ellipsoidal forms are supported. The standard parallel lat_1 +/// must be non-zero; at ±90° the projection degenerates to a Werner projection. +/// The formulation was independently verified against IOGP, "Geomatics Guidance Note 7, +/// part 2: Coordinate Conversions and Transformations including Formulas" (publication +/// 373-7-2, 2019), EPSG method 9827, Bonne. The ρ computation +/// a * m0 / sin(lat0) + M0 - M and the associated easting and northing equations +/// using the meridian arc M match the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 19, pp. 138-140, +/// for the Bonne development and its meridian-arc-based formulation. +/// +/// EPSG method 9827: Bonne. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.4.2, pp. 126-127. +internal sealed class BonneProjection : MapProjection +{ + private readonly double standardParallel; + private readonly double sineStandardParallel; + private readonly double cotStandardParallel; + private readonly double meridianDistanceAtStandardParallel; + private readonly double reducedCosphiOverSinphiAtStandardParallel; + private readonly bool isEllipsoidal; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public BonneProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public BonneProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Bonne"; + this.standardParallel = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lat_1", RadiansToDegrees(this.latOrigin), "standard_parallel_1")); + + if (Math.Abs(this.standardParallel) <= Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_1: |lat_1| should be > 0.", nameof(parameters)); + } + + this.sineStandardParallel = Math.Sin(this.standardParallel); + this.isEllipsoidal = this.es > 0d; + + if (this.isEllipsoidal) + { + double cosStandardParallel = Math.Cos(this.standardParallel); + double denominator = Math.Sqrt(1d - (this.es * this.sineStandardParallel * this.sineStandardParallel)) * this.sineStandardParallel; + this.reducedCosphiOverSinphiAtStandardParallel = cosStandardParallel / denominator; + this.meridianDistanceAtStandardParallel = this.Mlfn(this.standardParallel, this.sineStandardParallel, cosStandardParallel); + this.cotStandardParallel = 0d; + return; + } + + this.cotStandardParallel = (Math.Abs(Math.Abs(this.standardParallel) - HalfPi) <= Eps10) ? 0d : (1d / Math.Tan(this.standardParallel)); + this.meridianDistanceAtStandardParallel = 0d; + this.reducedCosphiOverSinphiAtStandardParallel = 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new BonneProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + + if (!this.isEllipsoidal) + { + double rhoSphere = this.cotStandardParallel + this.standardParallel - phi; + if (Math.Abs(rhoSphere) <= Eps10) + { + lon = 0d; + lat = 0d; + return; + } + + double angularTermSphere = lambda * Math.Cos(phi) / rhoSphere; + lon = this.SphericalRadius * rhoSphere * Math.Sin(angularTermSphere); + lat = this.SphericalRadius * (this.cotStandardParallel - (rhoSphere * Math.Cos(angularTermSphere))); + return; + } + + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + double rho = this.reducedCosphiOverSinphiAtStandardParallel + this.meridianDistanceAtStandardParallel - this.Mlfn(phi, sinPhi, cosPhi); + if (Math.Abs(rho) <= Eps10) + { + lon = 0d; + lat = 0d; + return; + } + + double angularDenominatorEllipsoid = rho * Math.Sqrt(1d - (this.es * sinPhi * sinPhi)); + double angularTermEllipsoid = (cosPhi * lambda) / angularDenominatorEllipsoid; + lon = this.SphericalRadius * rho * Math.Sin(angularTermEllipsoid); + lat = this.SphericalRadius * (this.reducedCosphiOverSinphiAtStandardParallel - (rho * Math.Cos(angularTermEllipsoid))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + if (!this.isEllipsoidal) + { + double translatedY = this.cotStandardParallel - yUnit; + double rhoSphere = Sign(this.standardParallel) * Hypot(xUnit, translatedY); + double phiSphere = this.cotStandardParallel + this.standardParallel - rhoSphere; + double absPhiSphere = Math.Abs(phiSphere); + if (absPhiSphere > HalfPi) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambdaSphere = 0d; + if (HalfPi - absPhiSphere > Eps10) + { + double scale = rhoSphere / Math.Cos(phiSphere); + lambdaSphere = this.standardParallel > 0d + ? scale * Math.Atan2(xUnit, translatedY) + : scale * Math.Atan2(-xUnit, -translatedY); + } + + x = Adjust_lon(this.centralMeridian + lambdaSphere); + y = phiSphere; + return; + } + + double translatedEllipsoidalY = this.reducedCosphiOverSinphiAtStandardParallel - yUnit; + double rho = Sign(this.standardParallel) * Hypot(xUnit, translatedEllipsoidalY); + double phi = this.Inv_mlfn(this.reducedCosphiOverSinphiAtStandardParallel + this.meridianDistanceAtStandardParallel - rho); + double absPhi = Math.Abs(phi); + + double lambda = 0d; + if (absPhi < HalfPi) + { + double sinPhi = Math.Sin(phi); + double scale = (rho * Math.Sqrt(1d - (this.es * sinPhi * sinPhi))) / Math.Cos(phi); + lambda = this.standardParallel > 0d + ? scale * Math.Atan2(xUnit, translatedEllipsoidalY) + : scale * Math.Atan2(-xUnit, -translatedEllipsoidalY); + } + else if ((absPhi - HalfPi) > Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/CalCoFiProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CalCoFiProjection.cs new file mode 100644 index 00000000..09dd0cc3 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/CalCoFiProjection.cs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the CalCOFI line/station projection (calcofi). +/// +/// +/// CalCOFI is the historical line/station indexing projection used by the +/// California Cooperative Oceanic Fisheries Investigations. The implementation +/// derives line and station coordinates from a rotated Mercator construction +/// anchored at the published CalCOFI origin. +/// This implementation matches PROJ's calcofi formulation and the +/// conversion algorithms published by L. E. Eber and Roger P. Hewitt, +/// Conversion algorithms for the CalCOFI station grid, +/// California Cooperative Oceanic Fisheries Investigations Reports 20, +/// 1979. It uses the historical line 80 / station 60 origin at 34.15 degrees N, +/// 121.15 degrees W, the -30 degree coastline rotation, and the Clarke 1866 +/// ellipsoid convention described for the CalCOFI grid. +/// +/// PROJ documentation: CalCOFI. +/// Eber and Hewitt (1979): Conversion algorithms for the CalCOFI station grid. +internal sealed class CalCoFiProjection : MapProjection +{ + private const double DegToLine = 5d; + private const double DegToStation = 15d; + private const double LineToRad = 0.0034906585039886592d; + private const double StationToRad = 0.0011635528346628863d; + private const double PointOLine = 80d; + private const double PointOStation = 60d; + private const double PointOLambda = -2.1144663887911301d; + private const double PointOPhi = 0.59602993955606354d; + private const double RotationAngle = 0.52359877559829882d; + + private readonly bool isEllipsoidal; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CalCoFiProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CalCoFiProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Cal_Coop_Ocean_Fish_Invest_Lines_Stations"; + this.isEllipsoidal = this.es != 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CalCoFiProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + if (Math.Abs(Math.Abs(lat) - HalfPi) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double xMercator = lon; + double yMercator = this.isEllipsoidal + ? -Math.Log(Tsfnz(this.e, lat, Math.Sin(lat))) + : Math.Log(Math.Tan(FortPi + (0.5d * lat))); + double oYMercator = this.isEllipsoidal + ? -Math.Log(Tsfnz(this.e, PointOPhi, Math.Sin(PointOPhi))) + : Math.Log(Math.Tan(FortPi + (0.5d * PointOPhi))); + double l1 = (yMercator - oYMercator) * Math.Tan(RotationAngle); + double l2 = -xMercator - l1 + PointOLambda; + double rYMercator = (l2 * Math.Cos(RotationAngle) * Math.Sin(RotationAngle)) + yMercator; + double ry = this.isEllipsoidal + ? Phi2z(this.e, Math.Exp(-rYMercator), out _) + : HalfPi - (2d * Math.Atan(Math.Exp(-rYMercator))); + + lon = PointOLine - (RadiansToDegrees(ry - PointOPhi) * DegToLine / Math.Cos(RotationAngle)); + lat = PointOStation + (RadiansToDegrees(ry - lat) * DegToStation / Math.Sin(RotationAngle)); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double ry = PointOPhi - (LineToRad * (x - PointOLine) * Math.Cos(RotationAngle)); + y = ry - (StationToRad * (y - PointOStation) * Math.Sin(RotationAngle)); + double oYMercator = this.isEllipsoidal + ? -Math.Log(Tsfnz(this.e, PointOPhi, Math.Sin(PointOPhi))) + : Math.Log(Math.Tan(FortPi + (0.5d * PointOPhi))); + double rYMercator = this.isEllipsoidal + ? -Math.Log(Tsfnz(this.e, ry, Math.Sin(ry))) + : Math.Log(Math.Tan(FortPi + (0.5d * ry))); + double xYMercator = this.isEllipsoidal + ? -Math.Log(Tsfnz(this.e, y, Math.Sin(y))) + : Math.Log(Math.Tan(FortPi + (0.5d * y))); + double l1 = (xYMercator - oYMercator) * Math.Tan(RotationAngle); + double l2 = (rYMercator - xYMercator) / (Math.Cos(RotationAngle) * Math.Sin(RotationAngle)); + x = PointOLambda - (l1 + l2); + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "central_meridian", 0d); + ReplaceOrAdd(merged, "scale_factor", 1d); + ReplaceOrAdd(merged, "false_easting", 0d); + ReplaceOrAdd(merged, "false_northing", 0d); + ReplaceOrAdd(merged, "unit", 1d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/CassiniSoldnerProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CassiniSoldnerProjection.cs index 99658cf4..1416ce2c 100644 --- a/src/ProjNet/CoordinateSystems/Projections/CassiniSoldnerProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/CassiniSoldnerProjection.cs @@ -1,154 +1,284 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Cassini-Soldner (Transverse Cylindrical Equidistant) map projection. +/// +/// +/// The Cassini-Soldner projection is a transverse cylindrical equidistant projection. +/// It maps the central meridian and its perpendicular cross-sections to straight lines while +/// preserving distances along lines perpendicular to the central meridian. Scale is true +/// along the central meridian and along lines perpendicular to it, but distortion increases +/// with distance from the central meridian. +/// The forward easting series was independently verified against IOGP, "Geomatics +/// Guidance Note 7, part 2: Coordinate Conversions and Transformations including +/// Formulas" (publication 373-7-2, 2019), EPSG method 9806, and John P. Snyder, +/// Map Projections - A Working Manual, U.S. Geological Survey Professional +/// Paper 1395 (1987). The third- and fifth-order T terms are subtractive, +/// matching the polynomial implemented here. +/// +/// EPSG method 9806: Cassini-Soldner. +internal sealed class CassiniSoldnerProjection : MapProjection { - internal class CassiniSoldnerProjection : MapProjection + /// + /// Fraction constant 1/120 used in polynomial terms. + /// + private const double One120th = 0.00833333333333333333d; + + /// + /// Fraction constant 1/24 used in polynomial terms. + /// + private const double One24th = 0.04166666666666666666d; + + /// + /// Fraction constant 1/15 used in polynomial terms. + /// + private const double One15th = 0.06666666666666666666d; + + private const int InverseRefinementIterations = 15; + private const double InverseRefinementTolerance = 1e-12d; + private const double InverseFiniteDifferenceStep = 1e-6d; + private const double InverseStepClamp = 0.3d; + private const double InversePoleClamp = HalfPi - 1e-10d; + + /// + /// Ellipsoid eccentricity helper factor e² / (1 - e²). + /// + private readonly double cFactor; + + /// + /// Meridional distance at latitude of origin. + /// + private readonly double m0; + + /// + /// Reciprocal of the semi-major axis length. + /// + private readonly double reciprocalSemiMajor; + + /// + /// Indicates whether the Hyperbolic Cassini-Soldner forward correction is enabled. + /// + private readonly bool hyperbolic; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CassiniSoldnerProjection(IEnumerable parameters) + : this(parameters, null) { - // ReSharper disable InconsistentNaming - private const double One6th = 0.16666666666666666666d; //C1 - private const double One120th = 0.00833333333333333333d; //C2 - private const double One24th = 0.04166666666666666666d; //C3 - private const double One3rd = 0.33333333333333333333d; //C4 - private const double One15th = 0.06666666666666666666d; //C5 - // ReSharper restore InconsistentNaming - - private readonly double _cFactor; - private readonly double _m0; - private readonly double _reciprocalSemiMajor; - - public CassiniSoldnerProjection(IEnumerable parameters) : this(parameters, null) + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CassiniSoldnerProjection(IEnumerable parameters, CassiniSoldnerProjection? inverse) + : base(parameters, inverse) + { + this.Authority = "EPSG"; + this.AuthorityCode = 9806; + this.Name = "Cassini_Soldner"; + + this.cFactor = this.es / (1 - this.es); + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + this.m0 = this.Mlfn(this.latOrigin, sinLatitudeOrigin, cosLatitudeOrigin); + this.reciprocalSemiMajor = 1d / this.semiMajor; + this.hyperbolic = Math.Abs(this.Parameters.GetOptionalParameterValue("hyperbolic", 0d)) > 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CassiniSoldnerProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = lon - this.centralMeridian; + double phi = lat; + this.ForwardNormalized(lambda, phi, out double x, out double y); + lon = x * this.semiMajor; + lat = y * this.semiMajor; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocalSemiMajor; + y *= this.reciprocalSemiMajor; + double targetX = x; + double targetY = y; + double phi1 = this.Phi1(this.m0 + targetY); + + double tn = Math.Tan(phi1); + double t = tn * tn; + double n = Math.Sin(phi1); + double r = 1.0d / (1.0d - (this.es * n * n)); + n = Math.Sqrt(r); + r *= (1.0d - this.es) * n; + double dd = targetX / n; + double d2 = dd * dd; + + double phi = phi1 - ((n * tn / r) * d2 * (.5 - ((1.0 + (3.0 * t)) * d2 * One24th))); + double lambda = dd * (1.0 + (t * d2 * (-ProjectionConstants.OneThird + ((1.0 + (3.0 * t)) * d2 * One15th)))) / Math.Cos(phi1); + + if (this.hyperbolic && !this.TryRefineInverseNormalized(targetX, targetY, ref lambda, ref phi)) { + ProjectionThrowHelper.ThrowInvalidOperation("Convergence error."); } - public CassiniSoldnerProjection(IEnumerable parameters, CassiniSoldnerProjection inverse) - : base(parameters, inverse) - { - Authority = "EPSG"; - AuthorityCode = 9806; - Name = "Cassini_Soldner"; + x = Adjust_lon(lambda + this.centralMeridian); + y = phi; + } - _cFactor = _es / (1 - _es); - _m0 = mlfn(lat_origin, Math.Sin(lat_origin), Math.Cos(lat_origin)); - _reciprocalSemiMajor = 1d / _semiMajor; + private double Phi1(double arg) + { + const int maxIter = 10; + const double eps = 1e-11d; + + double k = 1.0d / (1.0d - this.es); + + double phi = arg; + for (int i = maxIter; i > 0; --i) + { // rarely goes over 2 iterations + double sinPhi = Math.Sin(phi); + double t = 1.0d - (this.es * sinPhi * sinPhi); + t = (this.Mlfn(phi, sinPhi, Math.Cos(phi)) - arg) * (t * Math.Sqrt(t)) * k; + phi -= t; + if (Math.Abs(t) < eps) + { + return phi; + } } - public override MathTransform Inverse() + return ProjectionThrowHelper.ThrowInvalidOperation("Convergence error."); + } + + private void ForwardNormalized(double lambda, double phi, out double x, out double y) + { + Sincos(phi, out double sinPhi, out double cosPhi); + + y = this.Mlfn(phi, sinPhi, cosPhi); + double n = 1.0d / Math.Sqrt(1 - (this.es * sinPhi * sinPhi)); + double tn = Math.Tan(phi); + double t = tn * tn; + double a1 = lambda * cosPhi; + double a2 = a1 * a1; + double c = this.cFactor * Math.Pow(cosPhi, 2.0d); + + x = n * a1 * (1.0d - (a2 * t * (ProjectionConstants.OneSixth + ((8.0d - t + (8.0d * c)) * a2 * One120th)))); + y -= this.m0 - (n * tn * a2 * (0.5d + ((5.0d - t + (6.0d * c)) * a2 * One24th))); + + if (this.hyperbolic) { - if (_inverse == null) - _inverse = new CassiniSoldnerProjection(_Parameters.ToProjectionParameter(), this); - return _inverse; + double rho = (n * n) * (1.0d - this.es) * n; + y -= (y * y * y) / (6.0d * rho * n); } + } - //protected override double[] RadiansToMeters(double[] lonlat) - //{ - // var lambda = lonlat[0] - central_meridian; - // var phi = lonlat[1]; + private bool TryRefineInverseNormalized(double targetX, double targetY, ref double lambda, ref double phi) + { + for (int i = 0; i < InverseRefinementIterations; i++) + { + if (!this.TryForwardNormalized(lambda, phi, out double approxX, out double approxY)) + { + return false; + } - // double sinPhi, cosPhi; // sin and cos value - // sincos(phi, out sinPhi, out cosPhi); + double deltaX = approxX - targetX; + double deltaY = approxY - targetY; + if (Math.Abs(deltaX) < InverseRefinementTolerance && Math.Abs(deltaY) < InverseRefinementTolerance) + { + return true; + } - // var y = mlfn(phi, sinPhi, cosPhi); - // var n = 1.0d / Math.Sqrt(1 - _es * sinPhi * sinPhi); - // var tn = Math.Tan(phi); - // var t = tn * tn; - // var a1 = lambda * cosPhi; - // var a2 = a1 * a1; - // var c = _cFactor * Math.Pow(cosPhi, 2.0d); + if (!this.TryComputeInverseJacobian(lambda, phi, approxX, approxY, out double derivLamX, out double derivLamY, out double derivPhiX, out double derivPhiY)) + { + return false; + } - // var x = n * a1 * (1.0d - a2 * t * (One6th - (8.0d - t + 8.0d * c) * a2 * One120th)); - // y -= _m0 - n * tn * a2 * (0.5d + (5.0d - t + 6.0d * c) * a2 * One24th); + double deltaLambda = ProjectionConstants.Clamp((deltaX * derivLamX) + (deltaY * derivLamY), -InverseStepClamp, InverseStepClamp); + lambda = Adjust_lon(lambda - deltaLambda); - // return lonlat.Length == 2 - // ? new[] {_semiMajor*x, _semiMajor*y} - // : new[] {_semiMajor*x, _semiMajor*y, lonlat[2]}; - //} + double deltaPhi = ProjectionConstants.Clamp((deltaX * derivPhiX) + (deltaY * derivPhiY), -InverseStepClamp, InverseStepClamp); + phi = ProjectionConstants.Clamp(phi - deltaPhi, -InversePoleClamp, InversePoleClamp); + } - protected override void RadiansToMeters(ref double lon, ref double lat) - { - double lambda = lon - central_meridian; - double phi = lat; + return false; + } - double sinPhi, cosPhi; // sin and cos value - sincos(phi, out sinPhi, out cosPhi); + private bool TryComputeInverseJacobian( + double lambda, + double phi, + double approxX, + double approxY, + out double derivLamX, + out double derivLamY, + out double derivPhiX, + out double derivPhiY) + { + derivLamX = 0d; + derivLamY = 0d; + derivPhiX = 0d; + derivPhiY = 0d; - double y = mlfn(phi, sinPhi, cosPhi); - double n = 1.0d / Math.Sqrt(1 - _es * sinPhi * sinPhi); - double tn = Math.Tan(phi); - double t = tn * tn; - double a1 = lambda * cosPhi; - double a2 = a1 * a1; - double c = _cFactor * Math.Pow(cosPhi, 2.0d); + double dLam = lambda > 0d ? -InverseFiniteDifferenceStep : InverseFiniteDifferenceStep; + double lambdaOffset = Adjust_lon(lambda + dLam); + dLam = lambdaOffset - lambda; + if (Math.Abs(dLam) < Eps10 || !this.TryForwardNormalized(lambdaOffset, phi, out double xLam, out double yLam)) + { + return false; + } - double x = n * a1 * (1.0d - a2 * t * (One6th - (8.0d - t + 8.0d * c) * a2 * One120th)); - y -= _m0 - n * tn * a2 * (0.5d + (5.0d - t + 6.0d * c) * a2 * One24th); + double derivXLam = (xLam - approxX) / dLam; + double derivYLam = (yLam - approxY) / dLam; - lon = x * _semiMajor; - lat = y * _semiMajor; + double dPhi = phi > 0d ? -InverseFiniteDifferenceStep : InverseFiniteDifferenceStep; + double phiOffset = ProjectionConstants.Clamp(phi + dPhi, -InversePoleClamp, InversePoleClamp); + dPhi = phiOffset - phi; + if (Math.Abs(dPhi) < Eps10 || !this.TryForwardNormalized(lambda, phiOffset, out double xPhi, out double yPhi)) + { + return false; } - //protected override double[] MetersToRadians(double[] p) - //{ - - // var x = p[0] * _reciprocalSemiMajor; - // var y = p[1] * _reciprocalSemiMajor; - // var phi1 = Phi1(_m0 + y); - - // var tn = Math.Tan(phi1); - // var t = tn * tn; - // var n = Math.Sin(phi1); - // var r = 1.0d / (1.0d - _es * n * n); - // n = Math.Sqrt(r); - // r *= (1.0d - _es) * n; - // var dd = x / n; - // var d2 = dd * dd; - - // var phi = phi1 - (n * tn / r) * d2 * (.5 - (1.0 + 3.0 * t) * d2 * One24th); - // var lambda = dd * (1.0 + t * d2 * (-One3rd + (1.0 + 3.0 * t) * d2 * One15th)) / Math.Cos(phi1); - // lambda = adjust_lon(lambda + central_meridian); - - // return p.Length == 2 - // ? new[] {lambda, phi} - // : new[] {lambda, phi, p[2]}; - //} - protected override void MetersToRadians(ref double x, ref double y) + + double derivXPhi = (xPhi - approxX) / dPhi; + double derivYPhi = (yPhi - approxY) / dPhi; + double det = (derivXLam * derivYPhi) - (derivXPhi * derivYLam); + if (Math.Abs(det) <= ProjectionConstants.JacobianTolerance) { - x *= _reciprocalSemiMajor; - y *= _reciprocalSemiMajor; - double phi1 = Phi1(_m0 + y); - - double tn = Math.Tan(phi1); - double t = tn * tn; - double n = Math.Sin(phi1); - double r = 1.0d / (1.0d - _es * n * n); - n = Math.Sqrt(r); - r *= (1.0d - _es) * n; - double dd = x / n; - double d2 = dd * dd; - - y = phi1 - (n * tn / r) * d2 * (.5 - (1.0 + 3.0 * t) * d2 * One24th); - double lambda = dd * (1.0 + t * d2 * (-One3rd + (1.0 + 3.0 * t) * d2 * One15th)) / Math.Cos(phi1); - x = adjust_lon(lambda + central_meridian); + return false; } - private double Phi1(double arg) + derivLamX = derivYPhi / det; + derivLamY = -derivXPhi / det; + derivPhiX = -derivYLam / det; + derivPhiY = derivXLam / det; + return true; + } + + private bool TryForwardNormalized(double lambda, double phi, out double x, out double y) + { + x = 0d; + y = 0d; + if (Math.Abs(phi) >= InversePoleClamp) { - const int maxIter = 10; - const double eps = 1e-11; - - double k = 1.0d / (1.0d - _es); - - double phi = arg; - for (int i = maxIter; i > 0; --i) - { // rarely goes over 2 iterations - double sinPhi = Math.Sin(phi); - double t = 1.0d - _es * sinPhi * sinPhi; - t = (mlfn(phi, sinPhi, Math.Cos(phi)) - arg) * (t * Math.Sqrt(t)) * k; - phi -= t; - if (Math.Abs(t) < eps) return phi; - } - throw new ArgumentException("Convergence error."); + return false; } + this.ForwardNormalized(lambda, phi, out x, out y); + return !(double.IsNaN(x) || double.IsInfinity(x) || double.IsNaN(y) || double.IsInfinity(y)); } } diff --git a/src/ProjNet/CoordinateSystems/Projections/CentralConicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CentralConicProjection.cs new file mode 100644 index 00000000..f3458497 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/CentralConicProjection.cs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Central Conic projection (ccon). +/// +/// +/// A perspective conic projection defined by a single standard parallel +/// (lat_1, which must be non-zero). Graticule lines are constructed by central +/// (gnomonic) projection onto the cone. Only spherical input is supported. +/// This simple historical projection corresponds to PROJ's ccon +/// implementation, described there as a central (centrographic) projection onto a cone +/// tangent at the standard parallel. It is neither conformal, equal-area, nor +/// equidistant, and is mainly retained for compatibility with historical grid systems. +/// +/// PROJ documentation: Central Conic. +internal sealed class CentralConicProjection : MapProjection +{ + private readonly double phi1; + private readonly double sinPhi1; + private readonly double ctgPhi1; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CentralConicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CentralConicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Central_Conic"; + this.phi1 = DegreesToRadians(this.Parameters.GetParameterValue("lat_1", "standard_parallel_1")); + if (Math.Abs(this.phi1) < Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_1: |lat_1| should be > 0.", nameof(parameters)); + } + + this.sinPhi1 = Math.Sin(this.phi1); + this.ctgPhi1 = Math.Cos(this.phi1) / this.sinPhi1; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CentralConicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double r = this.ctgPhi1 - Math.Tan(lat - this.phi1); + double xUnit = r * Math.Sin(lambda * this.sinPhi1); + double yUnit = this.ctgPhi1 - (r * Math.Cos(lambda * this.sinPhi1)); + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = this.ctgPhi1 - (y * this.InverseSphericalRadius); + double lambda = Math.Atan2(xUnit, yUnit) / this.sinPhi1; + double phi = this.phi1 - Math.Atan(Hypot(xUnit, yUnit) - this.ctgPhi1); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/CentralCylindricalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CentralCylindricalProjection.cs new file mode 100644 index 00000000..1b3621e7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/CentralCylindricalProjection.cs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Central Cylindrical projection (cc). +/// +/// +/// Central Cylindrical is a spherical perspective cylindrical projection obtained +/// by projecting from the center of the sphere onto a tangent cylinder. Its compact +/// forward form is x = λ, y = tan(φ), so the poles are outside the +/// projection domain. +/// The projection is mathematically trivial and was independently checked against +/// PROJ's cc description and standard cartographic references. It is neither +/// conformal nor equal-area and is primarily useful as a didactic perspective +/// construction rather than as a practical mapping method. +/// +/// PROJ documentation: Central Cylindrical. +/// Wikipedia: Central cylindrical projection. +internal sealed class CentralCylindricalProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CentralCylindricalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CentralCylindricalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Central_Cylindrical"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CentralCylindricalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + if (Math.Abs(Math.Abs(lat) - HalfPi) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + lon = this.SphericalRadius * lambda; + lat = this.SphericalRadius * Math.Tan(lat); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + x = Adjust_lon(this.centralMeridian + xx); + y = Math.Atan(yy); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ChamberlinTrimetricProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ChamberlinTrimetricProjection.cs new file mode 100644 index 00000000..d2398378 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ChamberlinTrimetricProjection.cs @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Chamberlin Trimetric projection (chamb). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward construction was independently verified against Chamberlin's trimetric +/// method. The implementation matches the three-control-point setup, the law-of-cosines +/// angle recovery, and the mean-point blending used to place interior points. +/// +internal sealed class ChamberlinTrimetricProjection : MapProjection +{ + private const double Tolerance = 1e-9d; + + private readonly ControlPoint[] control = [new(), new(), new()]; + private readonly Point meanPoint = new(); + private readonly double beta1; + private readonly double beta2; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ChamberlinTrimetricProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ChamberlinTrimetricProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Chamberlin_Trimetric"; + + for (int i = 0; i < 3; i++) + { + int index = i + 1; + double phi = DegreesToRadians(this.Parameters.GetOptionalParameterValue($"lat_{index}", 0d, $"latitude_{index}")); + double lambda = DegreesToRadians(this.Parameters.GetOptionalParameterValue($"lon_{index}", 0d, $"longitude_{index}")); + lambda = Adjust_lon(lambda - this.centralMeridian); + + this.control[i].Phi = phi; + this.control[i].Lambda = lambda; + this.control[i].CosPhi = Math.Cos(phi); + this.control[i].SinPhi = Math.Sin(phi); + } + + for (int i = 0; i < 3; i++) + { + int j = i == 2 ? 0 : i + 1; + this.control[i].Arc = Vect( + this.control[j].Phi - this.control[i].Phi, + this.control[i].CosPhi, + this.control[i].SinPhi, + this.control[j].CosPhi, + this.control[j].SinPhi, + this.control[j].Lambda - this.control[i].Lambda); + + if (Math.Abs(this.control[i].Arc.R) <= Tolerance) + { + ArgumentGuard.ThrowArgument("Invalid value for control points: they should be distinct.", nameof(parameters)); + } + } + + double beta0 = LawOfCosines(this.control[0].Arc.R, this.control[2].Arc.R, this.control[1].Arc.R); + this.beta1 = LawOfCosines(this.control[0].Arc.R, this.control[1].Arc.R, this.control[2].Arc.R); + this.beta2 = PI - beta0; + + this.control[0].Projected.Y = this.control[2].Arc.R * Math.Sin(beta0); + this.control[1].Projected.Y = this.control[0].Projected.Y; + this.meanPoint.Y = this.control[0].Projected.Y + this.control[0].Projected.Y; + this.control[2].Projected.Y = 0d; + + this.control[1].Projected.X = 0.5d * this.control[0].Arc.R; + this.control[0].Projected.X = -this.control[1].Projected.X; + this.control[2].Projected.X = this.control[0].Projected.X + (this.control[2].Arc.R * Math.Cos(beta0)); + this.meanPoint.X = this.control[2].Projected.X; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new ChamberlinTrimetricProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double sinPhi = Math.Sin(lat); + double cosPhi = Math.Cos(lat); + var v = new Arc[3]; + int hitControl = -1; + + for (int i = 0; i < 3; i++) + { + v[i] = Vect( + lat - this.control[i].Phi, + this.control[i].CosPhi, + this.control[i].SinPhi, + cosPhi, + sinPhi, + lambda - this.control[i].Lambda); + + if (Math.Abs(v[i].R) <= Tolerance) + { + hitControl = i; + break; + } + + v[i] = new Arc(v[i].R, Adjust_lon(v[i].Az - this.control[i].Arc.Az)); + } + + double x = hitControl >= 0 ? this.control[hitControl].Projected.X : this.meanPoint.X; + double y = hitControl >= 0 ? this.control[hitControl].Projected.Y : this.meanPoint.Y; + if (hitControl < 0) + { + for (int i = 0; i < 3; i++) + { + int j = i == 2 ? 0 : i + 1; + double a = LawOfCosines(this.control[i].Arc.R, v[i].R, v[j].R); + if (v[i].Az < 0d) + { + a = -a; + } + + if (i == 0) + { + x += v[i].R * Math.Cos(a); + y -= v[i].R * Math.Sin(a); + } + else if (i == 1) + { + a = this.beta1 - a; + x -= v[i].R * Math.Cos(a); + y -= v[i].R * Math.Sin(a); + } + else + { + a = this.beta2 - a; + x += v[i].R * Math.Cos(a); + y += v[i].R * Math.Sin(a); + } + } + + x *= ProjectionConstants.OneThird; + y *= ProjectionConstants.OneThird; + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Chamberlin Trimetric does not support inverse projection in this wave."); + } + + private static Arc Vect(double dphi, double c1, double s1, double c2, double s2, double dlam) + { + double cdl = Math.Cos(dlam); + double r; + if (Math.Abs(dphi) > 1d || Math.Abs(dlam) > 1d) + { + r = Math.Acos(ProjectionConstants.ClampToUnit((s1 * s2) + (c1 * c2 * cdl))); + } + else + { + double dp = Math.Sin(0.5d * dphi); + double dl = Math.Sin(0.5d * dlam); + r = 2d * Asinz(Math.Sqrt((dp * dp) + (c1 * c2 * dl * dl))); + } + + if (Math.Abs(r) <= Tolerance) + { + return new Arc(0d, 0d); + } + + double az = Math.Atan2(c2 * Math.Sin(dlam), (c1 * s2) - (s1 * c2 * cdl)); + return new Arc(r, az); + } + + private static double LawOfCosines(double b, double c, double a) + { + double value = 0.5d * ((b * b) + (c * c) - (a * a)) / (b * c); + return Math.Acos(ProjectionConstants.ClampToUnit(value)); + } + + private readonly struct Arc(double r, double az) + { + public double R { get; } = r; + + public double Az { get; } = az; + } + + private sealed class ControlPoint + { + public double Phi { get; set; } + + public double Lambda { get; set; } + + public double CosPhi { get; set; } + + public double SinPhi { get; set; } + + public Arc Arc { get; set; } = new(0d, 0d); + + public Point Projected { get; } = new(); + } + + private sealed class Point + { + public double X { get; set; } + + public double Y { get; set; } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/CollignonProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CollignonProjection.cs new file mode 100644 index 00000000..d78ad27b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/CollignonProjection.cs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Collignon projection (collg). +/// +/// +/// Collignon is a spherical equal-area pseudocylindrical projection, historically used for +/// triangular world maps after its introduction by Edouard Collignon in 1865. The +/// forward equations use the auxiliary term sqrt(1 - sin(φ)) to produce the compact +/// relation x ~ λ * sqrt(1 - sin(φ)) and a linearized polar distance in +/// y. +/// +internal sealed class CollignonProjection : MapProjection +{ + private const double Fxc = 1.12837916709551257390d; + private const double Fyc = 1.77245385090551602729d; + private const double OneEps = ProjectionConstants.OnePlusEps7; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CollignonProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CollignonProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Collignon"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CollignonProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double yRoot = 1d - Math.Sin(lat); + if (yRoot <= 0d) + { + yRoot = 0d; + } + else + { + yRoot = Math.Sqrt(yRoot); + } + + double x = Fxc * lambda * yRoot; + double y = Fyc * (1d - yRoot); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = (yy / Fyc) - 1d; + phi = 1d - (phi * phi); + double absPhi = Math.Abs(phi); + if (absPhi < 1d) + { + phi = Math.Asin(phi); + } + else if (absPhi > OneEps) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + else + { + phi = phi < 0d ? -HalfPi : HalfPi; + } + + double lamFactor = 1d - Math.Sin(phi); + double lambda = lamFactor <= 0d ? 0d : xx / (Fxc * Math.Sqrt(lamFactor)); + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ColombiaUrbanProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ColombiaUrbanProjection.cs new file mode 100644 index 00000000..9b3d9d36 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ColombiaUrbanProjection.cs @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Colombia Urban projection (col_urban). +/// +/// +/// Applies a height-above-ellipsoid correction via the mandatory h_0 parameter +/// (height in meters above the ellipsoid), which scales coordinates to account for +/// terrain elevation and is intended for large-scale urban surveys in Colombia. +/// The forward and inverse relations were independently verified against the IGAC-style +/// Colombia Urban formulation. The implementation matches the published a, b, +/// c, and d coefficients derived from the latitude of origin and the mandatory +/// ellipsoidal height parameter h_0. +/// +internal sealed class ColombiaUrbanProjection : MapProjection +{ + private readonly double h0; + private readonly double rho0; + private readonly double a; + private readonly double b; + private readonly double c; + private readonly double d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ColombiaUrbanProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ColombiaUrbanProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Colombia_Urban"; + double unscaledH0 = this.Parameters.GetParameterValue("h_0"); + this.h0 = unscaledH0 / this.semiMajor; + + double sinPhi0 = Math.Sin(this.latOrigin); + double nu0 = 1d / Math.Sqrt(1d - (this.es * sinPhi0 * sinPhi0)); + this.a = 1d + (this.h0 / nu0); + this.rho0 = (1d - this.es) / Math.Pow(1d - (this.es * sinPhi0 * sinPhi0), 1.5d); + this.b = Math.Tan(this.latOrigin) / (2d * this.rho0 * nu0); + this.c = 1d + this.h0; + this.d = this.rho0 * (1d + (this.h0 / (1d - this.es))); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ColombiaUrbanProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double cosPhi = Math.Cos(lat); + double sinPhi = Math.Sin(lat); + double nu = 1d / Math.Sqrt(1d - (this.es * sinPhi * sinPhi)); + double lambdaNuCosPhi = lambda * nu * cosPhi; + double x = this.a * lambdaNuCosPhi; + double sinPhiM = Math.Sin(0.5d * (lat + this.latOrigin)); + double rhoM = (1d - this.es) / Math.Pow(1d - (this.es * sinPhiM * sinPhiM), 1.5d); + double g = 1d + (this.h0 / rhoM); + double y = g * this.rho0 * ((lat - this.latOrigin) + (this.b * lambdaNuCosPhi * lambdaNuCosPhi)); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = this.latOrigin + (yy / this.d) - (this.b * (xx / this.c) * (xx / this.c)); + double sinPhi = Math.Sin(phi); + double nu = 1d / Math.Sqrt(1d - (this.es * sinPhi * sinPhi)); + double lambda = xx / (this.c * nu * Math.Cos(phi)); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + if (!HasParameter(merged, "h_0")) + { + ArgumentGuard.ThrowArgument("Missing mandatory projection parameter 'h_0'.", nameof(parameters)); + } + + return merged; + } + + private static bool HasParameter(List parameters, string name) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/CompactMillerProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CompactMillerProjection.cs new file mode 100644 index 00000000..849e9d99 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/CompactMillerProjection.cs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Compact Miller projection (comill). +/// +/// +/// Compact Miller is a cylindrical compromise projection published by Tom Patterson in the +/// course of refining the Miller family for atlas use. The implementation keeps longitude +/// linear and evaluates latitude with the odd polynomial +/// y = φ * (K1 + K2 * φ² + K3 * φ⁴), using Newton iteration for the inverse. +/// +internal sealed class CompactMillerProjection : MapProjection +{ + private const double K1 = 0.9902d; + private const double K2 = 0.1604d; + private const double K3 = -0.03054d; + private const double C1 = K1; + private const double C2 = 3d * K2; + private const double C3 = 5d * K3; + private const double Epsilon = 1e-11d; + private const double MaxYFactor = 0.6000207669862655d; + private const int MaxIterations = 100; + + private readonly double maxY; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CompactMillerProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CompactMillerProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Compact_Miller"; + this.maxY = MaxYFactor * PI; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CompactMillerProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double latSquared = lat * lat; + double y = lat * (K1 + (latSquared * (K2 + (K3 * latSquared)))); + lon = this.SphericalRadius * lambda; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + if (yy > this.maxY) + { + yy = this.maxY; + } + else if (yy < -this.maxY) + { + yy = -this.maxY; + } + + double yc = yy; + bool converged = false; + for (int i = MaxIterations; i > 0; i--) + { + double y2 = yc * yc; + double f = (yc * (K1 + (y2 * (K2 + (K3 * y2))))) - yy; + double fDerivative = C1 + (y2 * (C2 + (C3 * y2))); + double tolerance = f / fDerivative; + yc -= tolerance; + if (Math.Abs(tolerance) < Epsilon) + { + converged = true; + break; + } + } + + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + xx); + y = yc; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/CrasterProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CrasterProjection.cs new file mode 100644 index 00000000..72226452 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/CrasterProjection.cs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Craster Parabolic projection (crast). +/// +/// +/// Craster Parabolic is a spherical equal-area pseudocylindrical projection introduced by +/// J. E. E. Craster in 1929. The implementation uses the one-third latitude form +/// x = Xm * λ * (2 * cos(2 * φ / 3) - 1) and +/// y = Ym * sin(φ / 3). +/// +internal sealed class CrasterProjection : MapProjection +{ + private const double Xm = 0.97720502380583984317d; + private const double Rxm = 1.02332670794648848847d; + private const double Ym = 3.06998012383946546542d; + private const double Rym = 0.32573500793527994772d; + private const double Third = ProjectionConstants.OneThird; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CrasterProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CrasterProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Craster"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CrasterProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phiThird = lat * Third; + double x = Xm * lambda * ((2d * Math.Cos(phiThird + phiThird)) - 1d); + double y = Ym * Math.Sin(phiThird); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = 3d * Asinz(yy * Rym); + double denominator = (2d * Math.Cos((phi + phi) * Third)) - 1d; + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = (xx * Rxm) / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/CylindricalEqualAreaProjection.cs b/src/ProjNet/CoordinateSystems/Projections/CylindricalEqualAreaProjection.cs new file mode 100644 index 00000000..652942da --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/CylindricalEqualAreaProjection.cs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Cylindrical Equal Area projection (cea). +/// +/// +/// Preserves area by mapping latitude to y = R * sin(φ) / cos(phi1), where +/// phi1 is the standard parallel. When the standard parallel is at the equator +/// this is equivalent to the Lambert Cylindrical Equal Area projection. +/// The ellipsoidal formulation was independently verified against EPSG method 9835, +/// Lambert Cylindrical Equal Area, and Snyder, "Map Projections - A Working Manual" +/// (USGS Professional Paper 1395, 1987), section 10. The published authalic +/// q-function and polar limit qP match the ellipsoidal branch here, +/// where and qp are +/// used to correct the earlier spherical-only implementation. +/// +/// EPSG method 9835: Lambert Cylindrical Equal Area. +/// USGS Professional Paper 1395: Map Projections - A Working Manual. +/// Wikipedia: Cylindrical equal-area projection. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.1.3, pp. 51-53. +internal sealed class CylindricalEqualAreaProjection : MapProjection +{ + private readonly double cosStandardParallel; + private readonly bool isEllipsoidal; + private readonly double oneEs; + private readonly double qp; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public CylindricalEqualAreaProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public CylindricalEqualAreaProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Cylindrical_Equal_Area"; + this.isEllipsoidal = this.es > 0d; + + double standardParallel = DegreesToRadians(this.Parameters.GetOptionalParameterValue("standard_parallel_1", 0d, "lat_ts")); + this.cosStandardParallel = Math.Cos(standardParallel); + if (Math.Abs(this.cosStandardParallel) <= Eps10) + { + ArgumentGuard.ThrowArgument("The standard parallel cannot be at the poles.", nameof(parameters)); + } + + if (this.isEllipsoidal) + { + this.oneEs = 1d - this.es; + double sinStandardParallel = Math.Sin(standardParallel); + this.cosStandardParallel /= Math.Sqrt(1d - (this.es * sinStandardParallel * sinStandardParallel)); + this.qp = Qsfn(1d, this.e, this.oneEs); + } + else + { + this.oneEs = 0d; + this.qp = 0d; + } + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new CylindricalEqualAreaProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + lon = this.SphericalRadius * lambda * this.cosStandardParallel; + + if (this.isEllipsoidal) + { + lat = this.SphericalRadius * (0.5d * Qsfn(Math.Sin(lat), this.e, this.oneEs)) / this.cosStandardParallel; + } + else + { + lat = this.SphericalRadius * Math.Sin(lat) / this.cosStandardParallel; + } + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x = Adjust_lon(this.centralMeridian + ((x * this.InverseSphericalRadius) / this.cosStandardParallel)); + + double normalized = (y * this.cosStandardParallel) * this.InverseSphericalRadius; + if (this.isEllipsoidal) + { + double q = ProjectionConstants.Clamp(2d * normalized, -this.qp, this.qp); + y = Phi1z(this.e, q, out long _); + return; + } + + if (Math.Abs(normalized) - Eps10 <= 1d) + { + if (Math.Abs(normalized) >= 1d) + { + y = normalized < 0d ? -HalfPi : HalfPi; + } + else + { + y = Math.Asin(normalized); + } + } + else + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(y), "Coordinate is outside the valid Cylindrical Equal Area domain."); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/DenoyerProjection.cs b/src/ProjNet/CoordinateSystems/Projections/DenoyerProjection.cs new file mode 100644 index 00000000..74ab9472 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/DenoyerProjection.cs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Denoyer Semi-Elliptical projection (denoy). +/// +/// +/// The Denoyer Semi-Elliptical projection is a compromise pseudocylindrical projection +/// intended for atlas use. +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the standard Denoyer +/// semi-elliptical equation. The implementation matches the cosine longitude scaling with +/// the published polynomial-in-|λ| and latitude modulation terms. +/// +internal sealed class DenoyerProjection : MapProjection +{ + private const double C0 = 0.95d; + private const double C1 = -0.08333333333333333333d; + private const double C3 = 0.00166666666666666666d; + private const double D1 = 0.9d; + private const double D5 = 0.03d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public DenoyerProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public DenoyerProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Denoyer_Semi_Elliptical"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new DenoyerProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = lambda; + double absLambda = Math.Abs(lambda); + x *= Math.Cos( + (C0 + (absLambda * (C1 + ((absLambda * absLambda) * C3)))) + * (lat * (D1 + (D5 * (lat * lat * lat * lat))))); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * lat; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Denoyer does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Eckert1Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Eckert1Projection.cs new file mode 100644 index 00000000..bda4bae7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Eckert1Projection.cs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Eckert I projection (eck1). +/// +/// +/// Eckert I is a spherical pseudocylindrical projection with straight parallels and a linear +/// reduction of the meridian lengths toward the poles. The formulation was independently +/// verified against the modern summary in the Wikipedia article "Eckert projection" and +/// Max Eckert's 1906 description of the family. The scale term +/// x = 0.9213177319 * λ * (1 - |φ| / π) together with +/// y = 0.9213177319 * φ matches the implementation here. +/// +/// Wikipedia: Eckert projection family. +internal sealed class Eckert1Projection : MapProjection +{ + private const double Fc = 0.92131773192356127802d; + private const double Rp = 0.31830988618379067154d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Eckert1Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Eckert1Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Eckert_I"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Eckert1Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = Fc * lambda * (1d - (Rp * Math.Abs(lat))); + double y = Fc * lat; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = yy / Fc; + double denominator = Fc * (1d - (Rp * Math.Abs(phi))); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Eckert2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Eckert2Projection.cs new file mode 100644 index 00000000..a65fc839 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Eckert2Projection.cs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Eckert II projection (eck2). +/// +/// +/// Eckert II is a spherical equal-area pseudocylindrical projection with shortened, broken +/// meridians. The formulation was independently verified against the Wikipedia article +/// "Eckert II projection" and Max Eckert's 1906 description of the family. The auxiliary +/// term sqrt(4 - 3 * sin(|φ|)) used in both the forward equations and the inverse +/// recovery of φ matches the implementation here. +/// +/// Wikipedia: Eckert II projection. +internal sealed class Eckert2Projection : MapProjection +{ + private const double Fxc = 0.46065886596178063902d; + private const double Fyc = 1.44720250911653531871d; + private const double C13 = ProjectionConstants.OneThird; + private const double OneEps = ProjectionConstants.OnePlusEps7; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Eckert2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Eckert2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Eckert_II"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Eckert2Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double yTmp = Math.Sqrt(4d - (3d * Math.Sin(Math.Abs(lat)))); + double x = Fxc * lambda * yTmp; + double y = Fyc * (2d - yTmp); + if (lat < 0d) + { + y = -y; + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phiTmp = 2d - (Math.Abs(yy) / Fyc); + double denominator = Fxc * phiTmp; + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + double phi = (4d - (phiTmp * phiTmp)) * C13; + double absPhi = Math.Abs(phi); + if (absPhi >= 1d) + { + if (absPhi > OneEps) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + phi = phi < 0d ? -HalfPi : HalfPi; + } + else + { + phi = Math.Asin(phi); + } + + if (yy < 0d) + { + phi = -phi; + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Eckert3Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Eckert3Projection.cs new file mode 100644 index 00000000..f9694d39 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Eckert3Projection.cs @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Eckert III projection (eck3). +/// +/// +/// Eckert III is a spherical pseudocylindrical projection with elliptical meridians. +/// The formulation was independently verified against John P. Snyder, +/// Map Projections - A Working Manual (USGS Professional Paper 1395, 1987), +/// section 32, and Max Eckert's 1906 description of the family. The forward relation +/// x = Cx * λ * (a + sqrt(1 - b * φ²)) together with the linear +/// y = Cy * φ term matches the implementation here. +/// +/// Snyder section 32: pseudocylindrical projections. +internal class Eckert3Projection : MapProjection +{ + private const double DefaultCx = 0.42223820031577120149d; + private const double DefaultCy = 0.84447640063154240298d; + private const double DefaultA = 1d; + private const double DefaultB = 0.4052847345693510857755d; + + private readonly double cx; + private readonly double cy; + private readonly double a; + private readonly double b; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Eckert3Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Eckert3Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Eckert_III"; + this.a = this.Parameters.GetOptionalParameterValue("eck3_a", DefaultA); + this.b = this.Parameters.GetOptionalParameterValue("eck3_b", DefaultB); + this.cx = this.Parameters.GetOptionalParameterValue("eck3_cx", DefaultCx); + this.cy = this.Parameters.GetOptionalParameterValue("eck3_cy", DefaultCy); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Eckert3Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double y = this.cy * lat; + double underRoot = 1d - (this.b * lat * lat); + if (underRoot < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double x = this.cx * lambda * (this.a + Math.Sqrt(underRoot)); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = yy / this.cy; + + double underRoot = 1d - (this.b * phi * phi); + if (underRoot < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double denominator = this.cx * (this.a + Math.Sqrt(underRoot)); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Eckert4Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Eckert4Projection.cs new file mode 100644 index 00000000..b1adf79f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Eckert4Projection.cs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Eckert IV projection (eck4). +/// +/// +/// Eckert IV is a spherical equal-area pseudocylindrical projection whose forward path +/// solves an auxiliary angle iteratively. The formulation was independently verified +/// against the Wikipedia article "Eckert IV projection" and Eric W. Weisstein's +/// MathWorld entry "Eckert IV Projection". The Newton iteration for +/// θ + sin(θ) * (cos(θ) + 2) = (2 + π / 2) * sin(φ) and the resulting +/// x = Cx * λ * (1 + cos(θ)), y = Cy * sin(θ) equations match the +/// implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 32, pp. 253-258, +/// for the Eckert IV and related equal-area pseudocylindrical developments. +/// +/// Wikipedia: Eckert IV projection. +/// MathWorld: Eckert IV Projection. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.2.2, pp. 75-76. +internal sealed class Eckert4Projection : MapProjection +{ + private const double OneTol = 1.00000000000001d; + private const double Cx = 0.42223820031577120149d; + private const double Cy = 1.32650042817700232218d; + private const double RCy = 0.75386330736002178205d; + private const double Cp = 3.57079632679489661922d; + private const double RCp = 0.28004957675577868795d; + private const int Iterations = 6; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Eckert4Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Eckert4Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Eckert_IV"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Eckert4Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double p = Cp * Math.Sin(lat); + double v = lat * lat; + double theta = lat * (0.895168d + (v * (0.0218849d + (v * 0.00826809d)))); + int i = Iterations; + + for (; i > 0; i--) + { + double c = Math.Cos(theta); + double s = Math.Sin(theta); + v = (theta + (s * (c + 2d)) - p) / (1d + (c * (c + 2d)) - (s * s)); + theta -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + double x = i == 0 + ? Cx * lambda + : Cx * lambda * (1d + Math.Cos(theta)); + double y = i == 0 + ? (theta < 0d ? -Cy : Cy) + : Cy * Math.Sin(theta); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double sinTheta = yy * RCy; + double absSinTheta = Math.Abs(sinTheta); + if (absSinTheta > OneTol) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double oneMinusAbs = 1d - Math.Abs(sinTheta); + double lambda = xx / Cx; + double phi = sinTheta > 0d ? HalfPi : -HalfPi; + if (oneMinusAbs < 0d || oneMinusAbs > ProjectionConstants.Tolerance1E12) + { + double theta = Asinz(sinTheta); + double cosTheta = Math.Cos(theta); + double denominator = Cx * (1d + cosTheta); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + lambda = xx / denominator; + double sinPhi = (theta + (sinTheta * (cosTheta + 2d))) * RCp; + phi = Asinz(sinPhi); + } + + double absLamMinusPi = Math.Abs(lambda) - PI; + if (absLamMinusPi > 0d) + { + if (absLamMinusPi > Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + lambda = lambda > 0d ? PI : -PI; + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Eckert5Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Eckert5Projection.cs new file mode 100644 index 00000000..b2dc282e --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Eckert5Projection.cs @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Eckert V projection (eck5). +/// +/// +/// Eckert V is a spherical pseudocylindrical projection with sinusoidal meridians and +/// evenly spaced straight parallels. The formulation was independently verified against +/// John P. Snyder, Map Projections - A Working Manual +/// (USGS Professional Paper 1395, 1987) and Max Eckert's 1906 description of the family. +/// The forward equations x = Xf * (1 + cos(φ)) * λ and y = Yf * φ +/// match the implementation here. +/// +/// Wikipedia: Eckert projection family. +internal sealed class Eckert5Projection : MapProjection +{ + private const double Xf = 0.44101277172455148219d; + private const double Rxf = 2.26750802723822639137d; + private const double Yf = 0.88202554344910296438d; + private const double Ryf = 1.13375401361911319568d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Eckert5Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Eckert5Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Eckert_V"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Eckert5Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = Xf * (1d + Math.Cos(lat)) * lambda; + double y = Yf * lat; + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = Ryf * yy; + double denominator = 1d + Math.Cos(phi); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = Rxf * xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Eckert6Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Eckert6Projection.cs new file mode 100644 index 00000000..51b20aa4 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Eckert6Projection.cs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Eckert VI projection (eck6). +/// +/// +/// This projection specializes with the +/// fixed Eckert VI parameter set, so its numerical behavior follows the verified +/// generalized sinusoidal formulation used by the base class. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.2.2, pp. 69-71. +internal sealed class Eckert6Projection : GeneralSinusoidalProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Eckert6Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Eckert6Projection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Eckert_VI"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "m", 1d); + ReplaceOrAdd(merged, "n", 2.570796326794896619231321691d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/EllipsoidalGeodesic.cs b/src/ProjNet/CoordinateSystems/Projections/EllipsoidalGeodesic.cs new file mode 100644 index 00000000..fe0d3a8c --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/EllipsoidalGeodesic.cs @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; + +/// +/// Provides reusable ellipsoidal geodesic helpers for projection kernels. +/// +internal static class EllipsoidalGeodesic +{ + /// + /// Gets the maximum number of Vincenty iterations used by the shared geodesic helpers. + /// + internal const int MaxIterations = 100; + + /// + /// Gets the convergence tolerance used by the shared geodesic helpers. + /// + internal const double Tolerance = 1e-12d; + + private const int MinJacobiSteps = 512; + private const int MaxJacobiSteps = 16384; + + /// + /// Computes the Vincenty delta-sigma correction term. + /// + /// The series coefficient. + /// The sine of sigma. + /// The cosine of sigma. + /// The cosine of twice sigma sub m. + /// The computed delta-sigma correction. + internal static double ComputeDeltaSigma(double bCoeff, double sinSigma, double cosSigma, double cos2SigmaM) + { + double cos2SigmaMSquared = cos2SigmaM * cos2SigmaM; + double sinSigmaSquared = sinSigma * sinSigma; + double firstTerm = cosSigma * (-1d + (2d * cos2SigmaMSquared)); + double secondTerm = (bCoeff / 6d) * cos2SigmaM * (-3d + (4d * sinSigmaSquared)) * (-3d + (4d * cos2SigmaMSquared)); + double bracket = cos2SigmaM + ((bCoeff / 4d) * (firstTerm - secondTerm)); + return bCoeff * sinSigma * bracket; + } + + /// + /// Normalizes a longitude to the principal interval. + /// + /// The longitude to normalize. + /// The normalized longitude. + internal static double NormalizeLongitude(double longitude) + { + return Math.Atan2(Math.Sin(longitude), Math.Cos(longitude)); + } + + /// + /// Solves the ellipsoidal inverse geodesic by Vincenty's method. + /// + /// The semi-minor axis. + /// The ellipsoid flattening. + /// The second eccentricity squared. + /// The start latitude in radians. + /// The start longitude in radians. + /// The end latitude in radians. + /// The end longitude in radians. + /// The solved geodesic distance in meters. + /// The forward azimuth in radians. + /// when the iteration converged; otherwise . + internal static bool TryVincentyInverse( + double semiMinor, + double flattening, + double eccentricityPrimeSquared, + double latitude1, + double longitude1, + double latitude2, + double longitude2, + out double distance, + out double azimuth) + { + distance = 0d; + azimuth = 0d; + + if (Math.Abs(latitude1 - latitude2) < Tolerance && Math.Abs(longitude1 - longitude2) < Tolerance) + { + return true; + } + + double oneMinusF = 1d - flattening; + double tanU1 = oneMinusF * Math.Tan(latitude1); + double tanU2 = oneMinusF * Math.Tan(latitude2); + double u1 = Math.Atan(tanU1); + double u2 = Math.Atan(tanU2); + double sinU1 = Math.Sin(u1); + double cosU1 = Math.Cos(u1); + double sinU2 = Math.Sin(u2); + double cosU2 = Math.Cos(u2); + + double l = NormalizeLongitude(longitude2 - longitude1); + double lambda = l; + + for (int iteration = 0; iteration < MaxIterations; iteration++) + { + double sinLambda = Math.Sin(lambda); + double cosLambda = Math.Cos(lambda); + + double term1 = cosU2 * sinLambda; + double term2 = (cosU1 * sinU2) - (sinU1 * cosU2 * cosLambda); + double sinSigma = Math.Sqrt((term1 * term1) + (term2 * term2)); + if (sinSigma < Tolerance) + { + return true; + } + + double cosSigma = (sinU1 * sinU2) + (cosU1 * cosU2 * cosLambda); + double sigma = Math.Atan2(sinSigma, cosSigma); + double sinAlpha = (cosU1 * cosU2 * sinLambda) / sinSigma; + double cosSqAlpha = 1d - (sinAlpha * sinAlpha); + double cos2SigmaM = cosSqAlpha < Tolerance + ? 0d + : cosSigma - ((2d * sinU1 * sinU2) / cosSqAlpha); + + double c = (flattening / 16d) * cosSqAlpha * (4d + (flattening * (4d - (3d * cosSqAlpha)))); + double lambdaPrevious = lambda; + lambda = l + ((1d - c) * flattening * sinAlpha * (sigma + (c * sinSigma * (cos2SigmaM + (c * cosSigma * (-1d + (2d * cos2SigmaM * cos2SigmaM))))))); + if (Math.Abs(lambda - lambdaPrevious) <= Tolerance) + { + double uSq = cosSqAlpha * eccentricityPrimeSquared; + double aCoeff = 1d + ((uSq / 16384d) * (4096d + (uSq * (-768d + (uSq * (320d - (175d * uSq))))))); + double bCoeff = (uSq / 1024d) * (256d + (uSq * (-128d + (uSq * (74d - (47d * uSq)))))); + double deltaSigma = ComputeDeltaSigma(bCoeff, sinSigma, cosSigma, cos2SigmaM); + + distance = semiMinor * aCoeff * (sigma - deltaSigma); + azimuth = Math.Atan2( + cosU2 * Math.Sin(lambda), + (cosU1 * sinU2) - (sinU1 * cosU2 * Math.Cos(lambda))); + return true; + } + } + + return false; + } + + /// + /// Solves the ellipsoidal direct geodesic by Vincenty's method. + /// + /// The semi-minor axis. + /// The ellipsoid flattening. + /// The second eccentricity squared. + /// The start latitude in radians. + /// The start longitude in radians. + /// The forward azimuth in radians. + /// The geodesic distance in meters. + /// The solved end latitude in radians. + /// The solved end longitude in radians. + /// when the iteration converged; otherwise . + internal static bool TryVincentyDirect( + double semiMinor, + double flattening, + double eccentricityPrimeSquared, + double latitude1, + double longitude1, + double azimuth1, + double distance, + out double latitude2, + out double longitude2) + { + latitude2 = latitude1; + longitude2 = longitude1; + + double oneMinusF = 1d - flattening; + double tanU1 = oneMinusF * Math.Tan(latitude1); + double u1 = Math.Atan(tanU1); + double sinU1 = Math.Sin(u1); + double cosU1 = Math.Cos(u1); + double sinAlpha1 = Math.Sin(azimuth1); + double cosAlpha1 = Math.Cos(azimuth1); + + double sigma1 = Math.Atan2(tanU1, cosAlpha1); + double sinAlpha = cosU1 * sinAlpha1; + double cosSqAlpha = 1d - (sinAlpha * sinAlpha); + double uSq = cosSqAlpha * eccentricityPrimeSquared; + double aCoeff = 1d + ((uSq / 16384d) * (4096d + (uSq * (-768d + (uSq * (320d - (175d * uSq))))))); + double bCoeff = (uSq / 1024d) * (256d + (uSq * (-128d + (uSq * (74d - (47d * uSq)))))); + + double sigma = distance / (semiMinor * aCoeff); + for (int iteration = 0; iteration < MaxIterations; iteration++) + { + double cos2SigmaM = Math.Cos((2d * sigma1) + sigma); + double sinSigma = Math.Sin(sigma); + double cosSigma = Math.Cos(sigma); + double deltaSigma = ComputeDeltaSigma(bCoeff, sinSigma, cosSigma, cos2SigmaM); + double sigmaPrevious = sigma; + sigma = (distance / (semiMinor * aCoeff)) + deltaSigma; + if (Math.Abs(sigma - sigmaPrevious) <= Tolerance) + { + double sinSigmaFinal = Math.Sin(sigma); + double cosSigmaFinal = Math.Cos(sigma); + double tmp = (sinU1 * sinSigmaFinal) - (cosU1 * cosSigmaFinal * cosAlpha1); + + latitude2 = Math.Atan2( + (sinU1 * cosSigmaFinal) + (cosU1 * sinSigmaFinal * cosAlpha1), + oneMinusF * Math.Sqrt((sinAlpha * sinAlpha) + (tmp * tmp))); + + double lambda = Math.Atan2( + sinSigmaFinal * sinAlpha1, + (cosU1 * cosSigmaFinal) - (sinU1 * sinSigmaFinal * cosAlpha1)); + + double c = (flattening / 16d) * cosSqAlpha * (4d + (flattening * (4d - (3d * cosSqAlpha)))); + double l = lambda - ((1d - c) * flattening * sinAlpha * (sigma + (c * sinSigmaFinal * (cos2SigmaM + (c * cosSigmaFinal * (-1d + (2d * cos2SigmaM * cos2SigmaM))))))); + longitude2 = NormalizeLongitude(longitude1 + l); + return true; + } + } + + return false; + } + + /// + /// Solves the end position together with reduced length and geodesic scale for the given geodesic. + /// + /// The semi-major axis. + /// The semi-minor axis. + /// The ellipsoid flattening. + /// The first eccentricity squared. + /// The second eccentricity squared. + /// The start latitude in radians. + /// The start longitude in radians. + /// The forward azimuth in radians. + /// The geodesic distance in meters. + /// The solved end latitude in radians. + /// The solved end longitude in radians. + /// The reduced length m12. + /// The geodesic scale M12. + /// when the solve succeeded; otherwise . + internal static bool TrySolvePositionReducedLengthAndScale( + double semiMajor, + double semiMinor, + double flattening, + double eccentricitySquared, + double eccentricityPrimeSquared, + double latitude1, + double longitude1, + double azimuth1, + double distance, + out double latitude2, + out double longitude2, + out double reducedLength, + out double geodesicScale) + { + latitude2 = latitude1; + longitude2 = longitude1; + reducedLength = 0d; + geodesicScale = 1d; + + if (Math.Abs(distance) <= Tolerance) + { + return true; + } + + if (Math.Abs(Math.Sin(azimuth1)) <= 1e-15d) + { + return TrySolveMeridionalPositionReducedLengthAndScale( + semiMajor, + eccentricitySquared, + latitude1, + longitude1, + azimuth1, + distance, + out latitude2, + out longitude2, + out reducedLength, + out geodesicScale); + } + + int stepCount = DetermineJacobiStepCount(semiMajor, distance); + double stepSize = distance / stepCount; + double jacobiReducedLength = 0d; + double jacobiReducedLengthDerivative = 1d; + double jacobiScale = 1d; + double jacobiScaleDerivative = 0d; + + for (int stepIndex = 0; stepIndex < stepCount; stepIndex++) + { + double baseDistance = stepIndex * stepSize; + if (!TrySampleGaussianCurvature( + semiMajor, + semiMinor, + flattening, + eccentricitySquared, + eccentricityPrimeSquared, + latitude1, + longitude1, + azimuth1, + baseDistance, + out double curvatureStart)) + { + return false; + } + + if (!TrySampleGaussianCurvature( + semiMajor, + semiMinor, + flattening, + eccentricitySquared, + eccentricityPrimeSquared, + latitude1, + longitude1, + azimuth1, + baseDistance + (0.5d * stepSize), + out double curvatureMid)) + { + return false; + } + + if (!TrySampleGaussianCurvature( + semiMajor, + semiMinor, + flattening, + eccentricitySquared, + eccentricityPrimeSquared, + latitude1, + longitude1, + azimuth1, + baseDistance + stepSize, + out double curvatureEnd)) + { + return false; + } + + double k1ReducedLength = stepSize * jacobiReducedLengthDerivative; + double k1ReducedLengthDerivative = stepSize * (-curvatureStart * jacobiReducedLength); + double k1Scale = stepSize * jacobiScaleDerivative; + double k1ScaleDerivative = stepSize * (-curvatureStart * jacobiScale); + + double reducedLengthMid1 = jacobiReducedLength + (0.5d * k1ReducedLength); + double reducedLengthDerivativeMid1 = jacobiReducedLengthDerivative + (0.5d * k1ReducedLengthDerivative); + double scaleMid1 = jacobiScale + (0.5d * k1Scale); + double scaleDerivativeMid1 = jacobiScaleDerivative + (0.5d * k1ScaleDerivative); + + double k2ReducedLength = stepSize * reducedLengthDerivativeMid1; + double k2ReducedLengthDerivative = stepSize * (-curvatureMid * reducedLengthMid1); + double k2Scale = stepSize * scaleDerivativeMid1; + double k2ScaleDerivative = stepSize * (-curvatureMid * scaleMid1); + + double reducedLengthMid2 = jacobiReducedLength + (0.5d * k2ReducedLength); + double reducedLengthDerivativeMid2 = jacobiReducedLengthDerivative + (0.5d * k2ReducedLengthDerivative); + double scaleMid2 = jacobiScale + (0.5d * k2Scale); + double scaleDerivativeMid2 = jacobiScaleDerivative + (0.5d * k2ScaleDerivative); + + double k3ReducedLength = stepSize * reducedLengthDerivativeMid2; + double k3ReducedLengthDerivative = stepSize * (-curvatureMid * reducedLengthMid2); + double k3Scale = stepSize * scaleDerivativeMid2; + double k3ScaleDerivative = stepSize * (-curvatureMid * scaleMid2); + + double reducedLengthEnd = jacobiReducedLength + k3ReducedLength; + double reducedLengthDerivativeEnd = jacobiReducedLengthDerivative + k3ReducedLengthDerivative; + double scaleEnd = jacobiScale + k3Scale; + double scaleDerivativeEnd = jacobiScaleDerivative + k3ScaleDerivative; + + double k4ReducedLength = stepSize * reducedLengthDerivativeEnd; + double k4ReducedLengthDerivative = stepSize * (-curvatureEnd * reducedLengthEnd); + double k4Scale = stepSize * scaleDerivativeEnd; + double k4ScaleDerivative = stepSize * (-curvatureEnd * scaleEnd); + + jacobiReducedLength += (k1ReducedLength + (2d * (k2ReducedLength + k3ReducedLength)) + k4ReducedLength) / 6d; + jacobiReducedLengthDerivative += (k1ReducedLengthDerivative + (2d * (k2ReducedLengthDerivative + k3ReducedLengthDerivative)) + k4ReducedLengthDerivative) / 6d; + jacobiScale += (k1Scale + (2d * (k2Scale + k3Scale)) + k4Scale) / 6d; + jacobiScaleDerivative += (k1ScaleDerivative + (2d * (k2ScaleDerivative + k3ScaleDerivative)) + k4ScaleDerivative) / 6d; + } + + if (!TryVincentyDirect( + semiMinor, + flattening, + eccentricityPrimeSquared, + latitude1, + longitude1, + azimuth1, + distance, + out latitude2, + out longitude2)) + { + return false; + } + + reducedLength = jacobiReducedLength; + geodesicScale = jacobiScale; + return true; + } + + private static bool TrySolveMeridionalPositionReducedLengthAndScale( + double semiMajor, + double eccentricitySquared, + double latitude1, + double longitude1, + double azimuth1, + double distance, + out double latitude2, + out double longitude2, + out double reducedLength, + out double geodesicScale) + { + latitude2 = latitude1; + longitude2 = longitude1; + reducedLength = 0d; + geodesicScale = 1d; + + int stepCount = DetermineMeridionalStepCount(semiMajor, distance); + double stepSize = distance / stepCount; + double direction = Math.Cos(azimuth1) >= 0d ? 1d : -1d; + + double latitude = latitude1; + double jacobiReducedLength = 0d; + double jacobiReducedLengthDerivative = 1d; + double jacobiScale = 1d; + double jacobiScaleDerivative = 0d; + + for (int stepIndex = 0; stepIndex < stepCount; stepIndex++) + { + EvaluateMeridionalDerivatives( + semiMajor, + eccentricitySquared, + direction, + latitude, + jacobiReducedLength, + jacobiReducedLengthDerivative, + jacobiScale, + jacobiScaleDerivative, + out double latitudeDerivative1, + out double reducedLengthDerivative1, + out double reducedLengthSecondDerivative1, + out double scaleDerivative1, + out double scaleSecondDerivative1); + + double latitudeMid1 = latitude + (0.5d * stepSize * latitudeDerivative1); + double reducedLengthMid1 = jacobiReducedLength + (0.5d * stepSize * reducedLengthDerivative1); + double reducedLengthDerivativeMid1 = jacobiReducedLengthDerivative + (0.5d * stepSize * reducedLengthSecondDerivative1); + double scaleMid1 = jacobiScale + (0.5d * stepSize * scaleDerivative1); + double scaleDerivativeMid1 = jacobiScaleDerivative + (0.5d * stepSize * scaleSecondDerivative1); + + EvaluateMeridionalDerivatives( + semiMajor, + eccentricitySquared, + direction, + latitudeMid1, + reducedLengthMid1, + reducedLengthDerivativeMid1, + scaleMid1, + scaleDerivativeMid1, + out double latitudeDerivative2, + out double reducedLengthDerivative2, + out double reducedLengthSecondDerivative2, + out double scaleDerivative2, + out double scaleSecondDerivative2); + + double latitudeMid2 = latitude + (0.5d * stepSize * latitudeDerivative2); + double reducedLengthMid2 = jacobiReducedLength + (0.5d * stepSize * reducedLengthDerivative2); + double reducedLengthDerivativeMid2 = jacobiReducedLengthDerivative + (0.5d * stepSize * reducedLengthSecondDerivative2); + double scaleMid2 = jacobiScale + (0.5d * stepSize * scaleDerivative2); + double scaleDerivativeMid2 = jacobiScaleDerivative + (0.5d * stepSize * scaleSecondDerivative2); + + EvaluateMeridionalDerivatives( + semiMajor, + eccentricitySquared, + direction, + latitudeMid2, + reducedLengthMid2, + reducedLengthDerivativeMid2, + scaleMid2, + scaleDerivativeMid2, + out double latitudeDerivative3, + out double reducedLengthDerivative3, + out double reducedLengthSecondDerivative3, + out double scaleDerivative3, + out double scaleSecondDerivative3); + + double latitudeEnd = latitude + (stepSize * latitudeDerivative3); + double reducedLengthEnd = jacobiReducedLength + (stepSize * reducedLengthDerivative3); + double reducedLengthDerivativeEnd = jacobiReducedLengthDerivative + (stepSize * reducedLengthSecondDerivative3); + double scaleEnd = jacobiScale + (stepSize * scaleDerivative3); + double scaleDerivativeEnd = jacobiScaleDerivative + (stepSize * scaleSecondDerivative3); + + EvaluateMeridionalDerivatives( + semiMajor, + eccentricitySquared, + direction, + latitudeEnd, + reducedLengthEnd, + reducedLengthDerivativeEnd, + scaleEnd, + scaleDerivativeEnd, + out double latitudeDerivative4, + out double reducedLengthDerivative4, + out double reducedLengthSecondDerivative4, + out double scaleDerivative4, + out double scaleSecondDerivative4); + + latitude += (stepSize / 6d) * (latitudeDerivative1 + (2d * (latitudeDerivative2 + latitudeDerivative3)) + latitudeDerivative4); + jacobiReducedLength += (stepSize / 6d) * (reducedLengthDerivative1 + (2d * (reducedLengthDerivative2 + reducedLengthDerivative3)) + reducedLengthDerivative4); + jacobiReducedLengthDerivative += (stepSize / 6d) * (reducedLengthSecondDerivative1 + (2d * (reducedLengthSecondDerivative2 + reducedLengthSecondDerivative3)) + reducedLengthSecondDerivative4); + jacobiScale += (stepSize / 6d) * (scaleDerivative1 + (2d * (scaleDerivative2 + scaleDerivative3)) + scaleDerivative4); + jacobiScaleDerivative += (stepSize / 6d) * (scaleSecondDerivative1 + (2d * (scaleSecondDerivative2 + scaleSecondDerivative3)) + scaleSecondDerivative4); + } + + latitude2 = Math.Max(-Math.PI / 2d, Math.Min(Math.PI / 2d, latitude)); + longitude2 = longitude1; + reducedLength = jacobiReducedLength; + geodesicScale = jacobiScale; + return true; + } + + private static int DetermineJacobiStepCount(double semiMajor, double distance) + { + double angularDistance = semiMajor <= 0d ? 0d : Math.Abs(distance) / semiMajor; + double normalized = angularDistance / (Math.PI / 2d); + + if (normalized >= 0.995d) + { + return MaxJacobiSteps; + } + + if (normalized >= 0.95d) + { + return 8192; + } + + if (normalized >= 0.8d) + { + return 4096; + } + + if (normalized >= 0.5d) + { + return 2048; + } + + int stepCount = (int)Math.Ceiling(2048d * normalized); + return stepCount < MinJacobiSteps ? MinJacobiSteps : stepCount; + } + + private static int DetermineMeridionalStepCount(double semiMajor, double distance) + { + double angularDistance = semiMajor <= 0d ? 0d : Math.Abs(distance) / semiMajor; + double normalized = angularDistance / (Math.PI / 2d); + + if (normalized >= 0.995d) + { + return 131072; + } + + if (normalized >= 0.9d) + { + return 65536; + } + + if (normalized >= 0.5d) + { + return 32768; + } + + return 16384; + } + + private static bool TrySampleGaussianCurvature( + double semiMajor, + double semiMinor, + double flattening, + double eccentricitySquared, + double eccentricityPrimeSquared, + double latitude1, + double longitude1, + double azimuth1, + double distance, + out double curvature) + { + curvature = 0d; + double latitude = latitude1; + if (Math.Abs(distance) > Tolerance) + { + if (!TryVincentyDirect( + semiMinor, + flattening, + eccentricityPrimeSquared, + latitude1, + longitude1, + azimuth1, + distance, + out latitude, + out _)) + { + return false; + } + } + + curvature = ComputeGaussianCurvature(semiMajor, eccentricitySquared, latitude); + return true; + } + + private static double ComputeGaussianCurvature(double semiMajor, double eccentricitySquared, double latitude) + { + double sinLatitude = Math.Sin(latitude); + double oneMinusEsSinSquared = 1d - (eccentricitySquared * sinLatitude * sinLatitude); + double sqrtOneMinusEsSinSquared = Math.Sqrt(oneMinusEsSinSquared); + double meridianRadius = (semiMajor * (1d - eccentricitySquared)) / (oneMinusEsSinSquared * sqrtOneMinusEsSinSquared); + double primeVerticalRadius = semiMajor / sqrtOneMinusEsSinSquared; + return 1d / (meridianRadius * primeVerticalRadius); + } + + private static void EvaluateMeridionalDerivatives( + double semiMajor, + double eccentricitySquared, + double direction, + double latitude, + double reducedLength, + double reducedLengthDerivative, + double geodesicScale, + double geodesicScaleDerivative, + out double latitudeDerivative, + out double reducedLengthFirstDerivative, + out double reducedLengthSecondDerivative, + out double geodesicScaleFirstDerivative, + out double geodesicScaleSecondDerivative) + { + double sinLatitude = Math.Sin(latitude); + double oneMinusEsSinSquared = 1d - (eccentricitySquared * sinLatitude * sinLatitude); + double sqrtOneMinusEsSinSquared = Math.Sqrt(oneMinusEsSinSquared); + double meridianRadius = (semiMajor * (1d - eccentricitySquared)) / (oneMinusEsSinSquared * sqrtOneMinusEsSinSquared); + double curvature = ComputeGaussianCurvature(semiMajor, eccentricitySquared, latitude); + + latitudeDerivative = direction / meridianRadius; + reducedLengthFirstDerivative = reducedLengthDerivative; + reducedLengthSecondDerivative = -curvature * reducedLength; + geodesicScaleFirstDerivative = geodesicScaleDerivative; + geodesicScaleSecondDerivative = -curvature * geodesicScale; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/EqualEarthProjection.cs b/src/ProjNet/CoordinateSystems/Projections/EqualEarthProjection.cs new file mode 100644 index 00000000..95114235 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/EqualEarthProjection.cs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Equal Earth projection (eqearth). +/// +/// +/// Equal Earth is an equal-area pseudocylindrical projection with curved parallels and +/// a polynomial forward formula. The inverse is solved iteratively. +/// The coefficient set and authalic-latitude relation were independently verified against +/// Bojan Savric, Tom Patterson, and Bernhard Jenny, "The Equal Earth map projection," +/// International Journal of Geographical Information Science, vol. 33, no. 3, +/// pp. 454-465, 2018, doi:10.1080/13658816.2018.1504949. The published A1 +/// through A4 coefficients and sin(θ) = (sqrt(3) / 2) * sin(φ) +/// relation match the implementation here. +/// +/// Wikipedia: Equal Earth projection. +/// Savric, Patterson, Jenny (2018): The Equal Earth map projection. +internal sealed class EqualEarthProjection : MapProjection +{ + private const double A1 = 1.340264; + private const double A2 = -0.081106; + private const double A3 = 0.000893; + private const double A4 = 0.003796; + private const int Iterations = 12; + private const double MaxY = 1.3173627591574d; + + private static readonly double M = Math.Sqrt(3.0) * 0.5; + + private readonly double radius; + private readonly double inverseRadius; + private readonly bool isEllipsoidal; + private readonly double oneEs; + private readonly double qp; + private readonly double[] apa; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public EqualEarthProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public EqualEarthProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Equal_Earth"; + this.isEllipsoidal = this.es > 0d; + if (this.isEllipsoidal) + { + this.oneEs = 1d - this.es; + this.qp = Qsfn(1d, this.e, this.oneEs); + this.apa = Authset(this.es); + } + else + { + this.oneEs = 0d; + this.qp = 0d; + this.apa = []; + } + + double authalicScale = this.isEllipsoidal ? Math.Sqrt(0.5d * this.qp) : 1d; + this.radius = this.semiMajor * this.scaleFactor * authalicScale; + this.inverseRadius = 1.0 / this.radius; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new EqualEarthProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double sinPhi = Math.Sin(lat); + if (this.isEllipsoidal) + { + double q = Qsfn(sinPhi, this.e, this.oneEs); + sinPhi = ProjectionConstants.Clamp(q / this.qp, -1d, 1d); + } + + double theta = Math.Asin(ProjectionConstants.Clamp(M * sinPhi, -1d, 1d)); + + double theta2 = theta * theta; + double theta6 = theta2 * theta2 * theta2; + double denominator = A1 + (3d * A2 * theta2) + (theta6 * ((7d * A3) + (9d * A4 * theta2))); + + lon = this.radius * lambda * Math.Cos(theta) / (M * denominator); + lat = this.radius * theta * (A1 + (A2 * theta2) + (theta6 * (A3 + (A4 * theta2)))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double theta = y * this.inverseRadius; + if (theta > MaxY) + { + theta = MaxY; + } + else if (theta < -MaxY) + { + theta = -MaxY; + } + + bool converged = false; + for (int i = 0; i < Iterations; i++) + { + double theta2 = theta * theta; + double theta6 = theta2 * theta2 * theta2; + double value = (theta * (A1 + (A2 * theta2) + (theta6 * (A3 + (A4 * theta2))))) - (y * this.inverseRadius); + double derivative = A1 + (3d * A2 * theta2) + (theta6 * ((7d * A3) + (9d * A4 * theta2))); + double delta = value / derivative; + theta -= delta; + if (Math.Abs(delta) < ProjectionConstants.Tolerance1E12) + { + converged = true; + break; + } + } + + if (!converged) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(y), "Equal Earth inverse did not converge."); + } + + double theta2Final = theta * theta; + double theta6Final = theta2Final * theta2Final * theta2Final; + double denominatorFinal = A1 + (3d * A2 * theta2Final) + (theta6Final * ((7d * A3) + (9d * A4 * theta2Final))); + double cosTheta = Math.Cos(theta); + + if (Math.Abs(cosTheta) <= Eps10) + { + x = this.centralMeridian; + } + else + { + x = Adjust_lon(this.centralMeridian + ((x * this.inverseRadius) * M * denominatorFinal / cosTheta)); + } + + double beta = Math.Asin(ProjectionConstants.Clamp(Math.Sin(theta) / M, -1d, 1d)); + y = this.isEllipsoidal ? Authlat(beta, this.apa) : beta; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/EquidistantConicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/EquidistantConicProjection.cs new file mode 100644 index 00000000..845c44c7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/EquidistantConicProjection.cs @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Equidistant Conic projection (eqdc). +/// +/// +/// Distances along all meridians and along the two standard parallels are +/// preserved. Supports both one-standard-parallel and two-standard-parallel forms; +/// when a single parallel is specified via standard_parallel_1, the cone +/// constant is set to the sine of that parallel. +/// The spherical and ellipsoidal formulations were independently verified against +/// Snyder, "Map Projections - A Working Manual" (USGS Professional Paper 1395, 1987), +/// section 16, Equidistant Conic, and the PROJ eqdc documentation. The +/// ellipsoidal branch matches the published use of meridional distances and parallel +/// scale factors through Mlfn and Msfnz, which corrects the earlier +/// spherical-only implementation. +/// +/// EPSG method 1119: Equidistant Conic. +/// USGS Professional Paper 1395: Map Projections - A Working Manual. +/// PROJ documentation: Equidistant Conic. +/// Wikipedia: Equidistant conic projection. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.1.4, pp. 95-98. +internal sealed class EquidistantConicProjection : MapProjection +{ + private readonly bool ellipsoidal; + private readonly double n; + private readonly double g; + private readonly double rho0; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public EquidistantConicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public EquidistantConicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Equidistant_Conic"; + this.ellipsoidal = this.es > 0d; + + double standardParallel1 = DegreesToRadians(this.Parameters.GetParameterValue("standard_parallel_1", "lat_1")); + double standardParallel2 = DegreesToRadians(this.Parameters.GetOptionalParameterValue("standard_parallel_2", RadiansToDegrees(standardParallel1), "lat_2")); + + bool secant = Math.Abs(standardParallel1 - standardParallel2) >= Eps10; + double sinParallel1 = Math.Sin(standardParallel1); + this.n = sinParallel1; + if (this.ellipsoidal) + { + double cosParallel1 = Math.Cos(standardParallel1); + double m1 = Msfnz(this.e, sinParallel1, cosParallel1); + double ml1 = this.Mlfn(standardParallel1, sinParallel1, cosParallel1); + if (secant) + { + double sinParallel2 = Math.Sin(standardParallel2); + double cosParallel2 = Math.Cos(standardParallel2); + double ml2 = this.Mlfn(standardParallel2, sinParallel2, cosParallel2); + if (ml1 == ml2) + { + ArgumentGuard.ThrowArgument("Invalid standard parallels for equidistant conic projection.", nameof(parameters)); + } + + this.n = (m1 - Msfnz(this.e, sinParallel2, cosParallel2)) / (ml2 - ml1); + } + + if (Math.Abs(this.n) <= Eps10) + { + ArgumentGuard.ThrowArgument("Invalid standard parallels for equidistant conic projection.", nameof(parameters)); + } + + this.g = ml1 + (m1 / this.n); + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + this.rho0 = this.g - this.Mlfn(this.latOrigin, sinLatitudeOrigin, cosLatitudeOrigin); + } + else + { + if (secant) + { + this.n = (Math.Cos(standardParallel1) - Math.Cos(standardParallel2)) / (standardParallel2 - standardParallel1); + } + + if (Math.Abs(this.n) <= Eps10) + { + ArgumentGuard.ThrowArgument("Invalid standard parallels for equidistant conic projection.", nameof(parameters)); + } + + this.g = (Math.Cos(standardParallel1) / this.n) + standardParallel1; + this.rho0 = this.g - this.latOrigin; + } + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new EquidistantConicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double theta = this.n * Adjust_lon(lon - this.centralMeridian); + double rho = this.g + - (this.ellipsoidal + ? this.Mlfn(lat, Math.Sin(lat), Math.Cos(lat)) + : lat); + + lon = this.SphericalRadius * rho * Math.Sin(theta); + lat = this.SphericalRadius * (this.rho0 - (rho * Math.Cos(theta))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + double rhoPrime = this.rho0 - yUnit; + double rho = Sign(this.n) * Math.Sqrt((xUnit * xUnit) + (rhoPrime * rhoPrime)); + + double theta = 0d; + if (Math.Abs(rho) > Eps10) + { + theta = Math.Atan2(xUnit, rhoPrime); + } + + x = Adjust_lon(this.centralMeridian + (theta / this.n)); + y = this.ellipsoidal ? this.Inv_mlfn(this.g - rho) : this.g - rho; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/EquidistantCylindricalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/EquidistantCylindricalProjection.cs new file mode 100644 index 00000000..59966b4b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/EquidistantCylindricalProjection.cs @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Equidistant Cylindrical projection (eqc). +/// +/// +/// Maps longitude linearly scaled by the standard-parallel factor and latitude by +/// angular or meridional distance from the origin latitude. In the spherical form, +/// E = a * cos(latSP) * λ and N = a * (φ - φ₀); in the ellipsoidal +/// form, the longitude scale becomes ν₁ * cos(latSP) and northing uses the +/// meridional distance difference M - M₀. When the standard parallel is at +/// the equator in the spherical branch this is equivalent to the Plate Carrée projection. +/// The spherical formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG methods 1029 and 1028. The easting and northing +/// equations E = a * cos(latSP) * λ, N = a * (φ - φ₀), +/// E = a * ν₁ * cos(latSP) * λ, and N = a * (M - M₀) match +/// the implementation here. +/// +/// EPSG method 1029: Equidistant Cylindrical (spherical). +/// EPSG method 1028: Equidistant Cylindrical (ellipsoidal). +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.1.4, pp. 53-55. +internal sealed class EquidistantCylindricalProjection : MapProjection +{ + private readonly double longitudeScale; + private readonly double meridionalDistanceAtOrigin; + private readonly bool isEllipsoidal; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public EquidistantCylindricalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public EquidistantCylindricalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Equidistant_Cylindrical"; + this.isEllipsoidal = this.es > 0d; + + double standardParallel = DegreesToRadians(this.Parameters.GetOptionalParameterValue("standard_parallel_1", 0d, "lat_ts", "latitude_true_scale", "latitude_of_true_scale")); + double cosStandardParallel = Math.Cos(standardParallel); + if (Math.Abs(cosStandardParallel) <= Eps10) + { + ArgumentGuard.ThrowArgument("The standard parallel cannot be at the poles.", nameof(parameters)); + } + + if (this.isEllipsoidal) + { + double sinStandardParallel = Math.Sin(standardParallel); + double nu1 = 1d / Math.Sqrt(1d - (this.es * sinStandardParallel * sinStandardParallel)); + this.longitudeScale = nu1 * cosStandardParallel; + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + this.meridionalDistanceAtOrigin = this.Mlfn(this.latOrigin, sinLatitudeOrigin, cosLatitudeOrigin); + return; + } + + this.longitudeScale = cosStandardParallel; + this.meridionalDistanceAtOrigin = 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new EquidistantCylindricalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + lon = this.SphericalRadius * lambda * this.longitudeScale; + + if (this.isEllipsoidal) + { + lat = this.SphericalRadius * (this.Mlfn(lat, Math.Sin(lat), Math.Cos(lat)) - this.meridionalDistanceAtOrigin); + return; + } + + lat = this.SphericalRadius * (lat - this.latOrigin); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x = Adjust_lon(this.centralMeridian + ((x * this.InverseSphericalRadius) / this.longitudeScale)); + + if (this.isEllipsoidal) + { + y = this.Inv_mlfn((y * this.InverseSphericalRadius) + this.meridionalDistanceAtOrigin); + return; + } + + y = this.latOrigin + (y * this.InverseSphericalRadius); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/EulerProjection.cs b/src/ProjNet/CoordinateSystems/Projections/EulerProjection.cs new file mode 100644 index 00000000..f546fe23 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/EulerProjection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Euler projection (euler). +/// +/// +/// Euler is a simple spherical conic specialization of . +/// Its numerical behavior is fully determined by the shared simple-conic equations together +/// with the Euler-specific cone constant and reference-radius terms. +/// +internal sealed class EulerProjection : SimpleConicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public EulerProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public EulerProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, SimpleConicType.Euler, "Euler") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new EulerProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ExtendedTransverseMercator.cs b/src/ProjNet/CoordinateSystems/Projections/ExtendedTransverseMercator.cs new file mode 100644 index 00000000..f06d0fe7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ExtendedTransverseMercator.cs @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Extended Transverse Mercator projection (etmerc). +/// +/// +/// This implementation ports the exact Poder/Engsager ETMERC kernel used by modern PROJ. +/// It is limited to ellipsoidal inputs and now backs the default ellipsoidal +/// tmerc/transverse_mercator/utm aliases in addition to the +/// explicit etmerc / extended_transverse_mercator names. Spherical +/// and explicit +approx routes remain on the classic Snyder-style +/// implementation. +/// +internal sealed class ExtendedTransverseMercator : MapProjection +{ + private const double DomainLimit = 2.623395162778d; + + private readonly double[] conformalToGeographic; + private readonly double[] geographicToConformal; + private readonly double[] conformalToRectifying; + private readonly double[] rectifyingToConformal; + private readonly double meridianQuadrantScale; + private readonly double originNorthingOffset; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ExtendedTransverseMercator(IEnumerable parameters) + : this(parameters, null) + { + } + + private ExtendedTransverseMercator(IEnumerable parameters, ExtendedTransverseMercator? inverse) + : base(parameters, inverse) + { + if (this.es <= 0d) + { + ProjectionThrowHelper.ThrowNotSupported("Extended Transverse Mercator requires an ellipsoidal model."); + } + + this.Name = "Extended_Transverse_Mercator"; + + double thirdFlattening = (this.semiMajor - this.semiMinor) / (this.semiMajor + this.semiMinor); + this.conformalToGeographic = AuxiliaryLatitudeSeries.BuildConformalToGeographicCoefficients(thirdFlattening); + this.geographicToConformal = AuxiliaryLatitudeSeries.BuildGeographicToConformalCoefficients(thirdFlattening); + this.conformalToRectifying = AuxiliaryLatitudeSeries.BuildConformalToRectifyingCoefficients(thirdFlattening); + this.rectifyingToConformal = AuxiliaryLatitudeSeries.BuildRectifyingToConformalCoefficients(thirdFlattening); + + this.meridianQuadrantScale = this.scaleFactor * this.semiMajor * AuxiliaryLatitudeSeries.RectifyingRadius(thirdFlattening); + double originConformalLatitude = AuxiliaryLatitudeSeries.Convert(this.latOrigin, this.geographicToConformal); + this.originNorthingOffset = -this.meridianQuadrantScale * AuxiliaryLatitudeSeries.Convert(originConformalLatitude, this.conformalToRectifying); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ExtendedTransverseMercator(this.Parameters.ToProjectionParameter(), this); + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double conformalLatitude = AuxiliaryLatitudeSeries.Convert(lat, this.geographicToConformal); + double sinConformalLatitude = Math.Sin(conformalLatitude); + double cosConformalLatitude = Math.Cos(conformalLatitude); + double sinLambda = Math.Sin(lambda); + double cosLambda = Math.Cos(lambda); + + double cosLatitudeLongitude = cosConformalLatitude * cosLambda; + double normalizedNorthing = Math.Atan2(sinConformalLatitude, cosLatitudeLongitude); + double inverseDenominator = 1d / Hypot(sinConformalLatitude, cosLatitudeLongitude); + double tangentEasting = sinLambda * cosConformalLatitude * inverseDenominator; + double normalizedEasting = Asinh(tangentEasting); + + double twiceInverseDenominator = 2d * inverseDenominator; + double twiceInverseDenominatorSquared = twiceInverseDenominator * inverseDenominator; + double realFactor = cosLatitudeLongitude * twiceInverseDenominatorSquared; + double sinArgumentReal = sinConformalLatitude * realFactor; + double cosArgumentReal = (cosLatitudeLongitude * realFactor) - 1d; + double sinhArgumentImaginary = tangentEasting * twiceInverseDenominator; + double coshArgumentImaginary = twiceInverseDenominatorSquared - 1d; + + ComplexClenshaw( + this.conformalToRectifying, + sinArgumentReal, + cosArgumentReal, + sinhArgumentImaginary, + coshArgumentImaginary, + out double deltaNorthing, + out double deltaEasting); + + normalizedNorthing += deltaNorthing; + normalizedEasting += deltaEasting; + + if (Math.Abs(normalizedEasting) > DomainLimit) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + lon = this.meridianQuadrantScale * normalizedEasting; + lat = (this.meridianQuadrantScale * normalizedNorthing) + this.originNorthingOffset; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double normalizedNorthing = (y - this.originNorthingOffset) / this.meridianQuadrantScale; + double normalizedEasting = x / this.meridianQuadrantScale; + + if (Math.Abs(normalizedEasting) > DomainLimit) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double sinArgumentReal = Math.Sin(2d * normalizedNorthing); + double cosArgumentReal = Math.Cos(2d * normalizedNorthing); + double expDoubleEasting = Math.Exp(2d * normalizedEasting); + double halfInverseExpDoubleEasting = 0.5d / expDoubleEasting; + double sinhArgumentImaginary = (0.5d * expDoubleEasting) - halfInverseExpDoubleEasting; + double coshArgumentImaginary = (0.5d * expDoubleEasting) + halfInverseExpDoubleEasting; + + ComplexClenshaw( + this.rectifyingToConformal, + sinArgumentReal, + cosArgumentReal, + sinhArgumentImaginary, + coshArgumentImaginary, + out double deltaNorthing, + out double deltaEasting); + + normalizedNorthing += deltaNorthing; + normalizedEasting += deltaEasting; + + double sinConformalLatitude = Math.Sin(normalizedNorthing); + double cosConformalLatitude = Math.Cos(normalizedNorthing); + double sinhNormalizedEasting = Math.Sinh(normalizedEasting); + double lambda = Math.Atan2(sinhNormalizedEasting, cosConformalLatitude); + double modulusEasting = Hypot(sinhNormalizedEasting, cosConformalLatitude); + double normalization = Hypot(sinConformalLatitude, modulusEasting); + double conformalLatitude = Math.Atan2(sinConformalLatitude, modulusEasting); + double phi = AuxiliaryLatitudeSeries.Convert( + conformalLatitude, + sinConformalLatitude / normalization, + modulusEasting / normalization, + this.conformalToGeographic); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static double Asinh(double value) + { + return Math.Log(value + Hypot(1d, value)); + } + + private static void ComplexClenshaw( + double[] coefficients, + double sinArgumentReal, + double cosArgumentReal, + double sinhArgumentImaginary, + double coshArgumentImaginary, + out double real, + out double imaginary) + { + coefficients = ArgumentGuard.ThrowIfNull(coefficients, nameof(coefficients)); + + double recurrenceReal = 2d * cosArgumentReal * coshArgumentImaginary; + double recurrenceImaginary = -2d * sinArgumentReal * sinhArgumentImaginary; + double previousImaginary = 0d; + double previousReal = 0d; + double currentImaginary = 0d; + double currentReal = coefficients[coefficients.Length - 1]; + + for (int i = coefficients.Length - 2; i >= 0; i--) + { + double previousPreviousReal = previousReal; + double previousPreviousImaginary = previousImaginary; + previousReal = currentReal; + previousImaginary = currentImaginary; + currentReal = -previousPreviousReal + (recurrenceReal * previousReal) - (recurrenceImaginary * previousImaginary) + coefficients[i]; + currentImaginary = -previousPreviousImaginary + (recurrenceImaginary * previousReal) + (recurrenceReal * previousImaginary); + } + + double baseReal = sinArgumentReal * coshArgumentImaginary; + double baseImaginary = cosArgumentReal * sinhArgumentImaginary; + real = (baseReal * currentReal) - (baseImaginary * currentImaginary); + imaginary = (baseReal * currentImaginary) + (baseImaginary * currentReal); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/FaheyProjection.cs b/src/ProjNet/CoordinateSystems/Projections/FaheyProjection.cs new file mode 100644 index 00000000..925d5b26 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/FaheyProjection.cs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Fahey projection (fahey). +/// +/// +/// Fahey is a spherical pseudocylindrical compromise projection commonly associated with +/// Fahey's modern atlas usage. The implementation uses the half-angle substitution +/// t = tan(φ / 2), followed by the compact relations +/// x = XFactor * λ * sqrt(1 - t²) and y = YFactor * t. +/// +internal sealed class FaheyProjection : MapProjection +{ + private const double Tolerance = 1e-6d; + private const double XFactor = 0.819152d; + private const double YFactor = 1.819152d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public FaheyProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public FaheyProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Fahey"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new FaheyProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double t = Math.Tan(0.5d * lat); + double y = YFactor * t; + double x = XFactor * lambda * Math.Sqrt(Math.Max(0d, 1d - (t * t))); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double t = yy / YFactor; + double phi = 2d * Math.Atan(t); + double oneMinusTSquared = 1d - (t * t); + double lambda = Math.Abs(oneMinusTSquared) < Tolerance + ? 0d + : xx / (XFactor * Math.Sqrt(oneMinusTSquared)); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/FoucautProjection.cs b/src/ProjNet/CoordinateSystems/Projections/FoucautProjection.cs new file mode 100644 index 00000000..f99d26b0 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/FoucautProjection.cs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Foucaut projection (fouc). +/// +/// +/// Foucaut is a spherical member of the STS family implemented by +/// . This specialization fixes the family constants to +/// p = 2 and q = 2 with tangent-mode scaling, producing the compact Foucaut +/// forward and inverse relations used in atlas practice. +/// +internal sealed class FoucautProjection : StsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public FoucautProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public FoucautProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Foucaut", 2d, 2d, true) + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new FoucautProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/FoucautSinusoidalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/FoucautSinusoidalProjection.cs new file mode 100644 index 00000000..a0a66d12 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/FoucautSinusoidalProjection.cs @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Foucaut Sinusoidal projection (fouc_s). +/// +/// +/// Foucaut Sinusoidal is a spherical pseudocylindrical projection that blends sinusoidal +/// behavior with a configurable Foucaut weighting parameter n in the range +/// [0, 1]. The implementation evaluates +/// x = λ * cos(φ) / (n + (1 - n) * cos(φ)) and +/// y = n * φ + (1 - n) * sin(φ), with an iterative inverse when +/// n != 0. +/// +internal sealed class FoucautSinusoidalProjection : MapProjection +{ + private const int MaximumIterations = 10; + + private readonly double n; + private readonly double n1; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public FoucautSinusoidalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public FoucautSinusoidalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Foucaut_Sinusoidal"; + this.n = this.Parameters.GetOptionalParameterValue("n", 0d); + if (this.n < 0d || this.n > 1d) + { + ArgumentGuard.ThrowArgument("Invalid value for n: it should be in [0,1] range.", nameof(parameters)); + } + + this.n1 = 1d - this.n; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new FoucautSinusoidalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double t = Math.Cos(lat); + double denominator = this.n + (this.n1 * t); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + lon = this.SphericalRadius * (lambda * t / denominator); + lat = this.SphericalRadius * ((this.n * lat) + (this.n1 * Math.Sin(lat))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = Asinz(yy); + if (this.n != 0d) + { + phi = yy; + int i = MaximumIterations; + for (; i > 0; i--) + { + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + double denominator = this.n + (this.n1 * cosPhi); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double v = ((this.n * phi) + (this.n1 * sinPhi) - yy) / denominator; + phi -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + if (i == 0) + { + phi = yy < 0d ? -HalfPi : HalfPi; + } + } + + double cos = Math.Cos(phi); + if (Math.Abs(cos) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx * (this.n + (this.n1 * cos)) / cos; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/GallProjection.cs b/src/ProjNet/CoordinateSystems/Projections/GallProjection.cs new file mode 100644 index 00000000..29e1c443 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/GallProjection.cs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Gall (Gall Stereographic) projection (gall). +/// +/// +/// Gall Stereographic is a cylindrical compromise projection introduced by James Gall in +/// 1855. In normalized form it scales longitude by cos(π / 4) / sqrt(2) and uses the +/// latitude relation y = (1 + sqrt(2)) * tan(φ / 2). +/// +internal sealed class GallProjection : MapProjection +{ + private const double Yf = 1.70710678118654752440d; + private const double Ryf = 0.58578643762690495119d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public GallProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public GallProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Gall"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GallProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = ProjectionConstants.OneOverSqrt2 * lambda; + double y = Yf * Math.Tan(0.5d * lat); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double lambda = ProjectionConstants.Sqrt2 * xx; + double phi = 2d * Math.Atan(yy * Ryf); + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/GaussSchreiberTransverseMercatorProjection.cs b/src/ProjNet/CoordinateSystems/Projections/GaussSchreiberTransverseMercatorProjection.cs new file mode 100644 index 00000000..6e282348 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/GaussSchreiberTransverseMercatorProjection.cs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Gauss-Schreiber Transverse Mercator projection (gstmerc). +/// +/// +/// Supports ellipsoidal computation. The forward transform first maps geodetic coordinates +/// onto a conformal sphere, then applies a transverse Mercator development on that sphere. +/// The conformal-sphere reduction was independently verified against the +/// Gauss-Schreiber/Laborde construction. The implementation matches the intermediate +/// conformal latitude, the n1/n2 scale terms, and the final transverse +/// Mercator mapping performed on the conformal sphere. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 5, Sect. 5.1.5, p. 163. +internal sealed class GaussSchreiberTransverseMercatorProjection : MapProjection +{ + private readonly double n1; + private readonly double c; + private readonly double n2; + private readonly double ys; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public GaussSchreiberTransverseMercatorProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public GaussSchreiberTransverseMercatorProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Gauss_Schreiber_Transverse_Mercator"; + + Sincos(this.latOrigin, out double sinPhi0, out double cosPhi0); + double cosPhi0Pow4 = cosPhi0 * cosPhi0; + cosPhi0Pow4 *= cosPhi0Pow4; + + this.n1 = Math.Sqrt(1d + ((this.es * cosPhi0Pow4) / (1d - this.es))); + double phic = Asinz(sinPhi0 / this.n1); + this.c = Math.Log(Tsfnz(0d, -phic, -Math.Sin(phic))) - (this.n1 * Math.Log(Tsfnz(this.e, -this.latOrigin, -sinPhi0))); + this.n2 = this.scaleFactor * this.semiMajor * Math.Sqrt(1d - this.es) / (1d - (this.es * sinPhi0 * sinPhi0)); + this.ys = -this.n2 * phic; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GaussSchreiberTransverseMercatorProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double l = this.n1 * lambda; + double ls = this.c + (this.n1 * Math.Log(Tsfnz(this.e, -lat, -Math.Sin(lat)))); + double sinLs1 = Math.Sin(l) / Math.Cosh(ls); + double ls1 = Math.Log(Tsfnz(0d, -Asinz(sinLs1), -sinLs1)); + lon = this.n2 * ls1; + lat = this.ys + (this.n2 * Math.Atan(Math.Sinh(ls) / Math.Cos(l))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double l = Math.Atan(Math.Sinh(x / this.n2) / Math.Cos((y - this.ys) / this.n2)); + double sinC = Math.Sin((y - this.ys) / this.n2) / Math.Cosh(x / this.n2); + double lc = Math.Log(Tsfnz(0d, -Asinz(sinC), -sinC)); + double lambda = l / this.n1; + + double phi = -Phi2z(this.e, Math.Exp((lc - this.c) / this.n1), out long flag); + if (flag != 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/GeneralSinusoidalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/GeneralSinusoidalProjection.cs new file mode 100644 index 00000000..f94af535 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/GeneralSinusoidalProjection.cs @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical General Sinusoidal series projection (gn_sinu). +/// +/// +/// General Sinusoidal is a parameterized spherical pseudocylindrical base described by +/// Snyder for several related map projections. The parameters m and n control +/// whether the auxiliary latitude is obtained directly through asin(n * sin(φ)) +/// or by solving m * φ' + sin(φ') = n * sin(φ) iteratively, after which the +/// implementation applies the scaled relations +/// x = cX * λ * (m + cos(φ')) and y = cY * φ'. +/// +internal class GeneralSinusoidalProjection : MapProjection +{ + private const int MaximumIterations = 8; + + private readonly double m; + private readonly double n; + private readonly double cX; + private readonly double cY; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public GeneralSinusoidalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public GeneralSinusoidalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "General_Sinusoidal"; + this.n = this.Parameters.GetParameterValue("n"); + this.m = this.Parameters.GetParameterValue("m"); + + if (this.n <= 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for n: it should be > 0.", nameof(parameters)); + } + + if (this.m < 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for m: it should be >= 0.", nameof(parameters)); + } + + this.cY = Math.Sqrt((this.m + 1d) / this.n); + this.cX = this.cY / (this.m + 1d); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GeneralSinusoidalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + if (this.m == 0d) + { + phi = this.n != 1d ? Asinz(this.n * Math.Sin(phi)) : phi; + } + else + { + double k = this.n * Math.Sin(phi); + int i = MaximumIterations; + for (; i > 0; i--) + { + double denominator = this.m + Math.Cos(phi); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double v = ((this.m * phi) + Math.Sin(phi) - k) / denominator; + phi -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + if (i == 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + } + + lon = this.SphericalRadius * this.cX * lambda * (this.m + Math.Cos(phi)); + lat = this.SphericalRadius * this.cY * phi; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phiNormalized = yy / this.cY; + double phi = this.m != 0d + ? Asinz(((this.m * phiNormalized) + Math.Sin(phiNormalized)) / this.n) + : (this.n != 1d ? Asinz(Math.Sin(phiNormalized) / this.n) : phiNormalized); + double denominator = this.cX * (this.m + Math.Cos(phiNormalized)); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/GeostationarySatelliteProjection.cs b/src/ProjNet/CoordinateSystems/Projections/GeostationarySatelliteProjection.cs new file mode 100644 index 00000000..4d6e766f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/GeostationarySatelliteProjection.cs @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the geostationary satellite projection (geos). +/// +/// +/// This perspective projection models the view from a satellite in geostationary +/// orbit. It supports both sweep-X and sweep-Y scanning geometries and handles both +/// spherical and ellipsoidal Earth models. +/// The formulation was independently verified against CGMS 03, +/// LRIT/HRIT Global Specification, section 4.4.3.2, and the PROJ geostationary +/// satellite projection documentation. The scan-angle conversion through the satellite +/// height term, the switch between the sweep-X and sweep-Y conventions, and the +/// ellipsoidal geocentric-radius path match the implementation here. +/// +/// PROJ documentation: Geostationary Satellite View. +internal sealed class GeostationarySatelliteProjection : MapProjection +{ + private const double MaximumHeightRatio = 1e10d; + + private readonly bool flipAxis; + private readonly double radiusP; + private readonly double radiusP2; + private readonly double radiusPInv2; + private readonly double radiusG; + private readonly double radiusG1; + private readonly double c; + private readonly bool ellipsoidal; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public GeostationarySatelliteProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public GeostationarySatelliteProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Geostationary_Satellite"; + + double h = this.Parameters.GetParameterValue("h", "satellite_height"); + this.radiusG1 = h / this.semiMajor; + if (this.radiusG1 <= 0d || this.radiusG1 > MaximumHeightRatio) + { + ArgumentGuard.ThrowArgument("Invalid value for h.", nameof(parameters)); + } + + this.radiusG = 1d + this.radiusG1; + this.c = (this.radiusG * this.radiusG) - 1d; + this.flipAxis = this.ReadFlipAxis(); + this.ellipsoidal = this.es != 0d; + + if (this.ellipsoidal) + { + this.radiusP = this.semiMinor / this.semiMajor; + this.radiusP2 = this.radiusP * this.radiusP; + this.radiusPInv2 = 1d / this.radiusP2; + } + else + { + this.radiusP = 1d; + this.radiusP2 = 1d; + this.radiusPInv2 = 1d; + } + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GeostationarySatelliteProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + + if (this.ellipsoidal) + { + this.ForwardEllipsoidal(lambda, phi, out double xEllps, out double yEllps); + lon = this.SphericalRadius * xEllps; + lat = this.SphericalRadius * yEllps; + return; + } + + this.ForwardSpherical(lambda, phi, out double xSphere, out double ySphere); + lon = this.SphericalRadius * xSphere; + lat = this.SphericalRadius * ySphere; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + if (this.ellipsoidal) + { + this.InverseEllipsoidal(xx, yy, out double lambdaEllps, out double phiEllps); + x = Adjust_lon(this.centralMeridian + lambdaEllps); + y = phiEllps; + return; + } + + this.InverseSpherical(xx, yy, out double lambdaSphere, out double phiSphere); + x = Adjust_lon(this.centralMeridian + lambdaSphere); + y = phiSphere; + } + + private bool ReadFlipAxis() + { + if (this.Parameters.ContainsKey("sweep_x")) + { + return this.Parameters.GetParameterValue("sweep_x") != 0d; + } + + return this.Parameters.ContainsKey("sweep_angle_axis") && this.Parameters.GetParameterValue("sweep_angle_axis") != 0d; + } + + private void ForwardSpherical(double lambda, double phi, out double x, out double y) + { + double cosPhi = Math.Cos(phi); + double vx = Math.Cos(lambda) * cosPhi; + double vy = Math.Sin(lambda) * cosPhi; + double vz = Math.Sin(phi); + double tmp = this.radiusG - vx; + + if (this.flipAxis) + { + x = this.radiusG1 * Math.Atan(vy / Hypot(vz, tmp)); + y = this.radiusG1 * Math.Atan(vz / tmp); + } + else + { + x = this.radiusG1 * Math.Atan(vy / tmp); + y = this.radiusG1 * Math.Atan(vz / Hypot(vy, tmp)); + } + } + + private void ForwardEllipsoidal(double lambda, double phi, out double x, out double y) + { + double geocentricPhi = Math.Atan(this.radiusP2 * Math.Tan(phi)); + double cosGeocentricPhi = Math.Cos(geocentricPhi); + double sinGeocentricPhi = Math.Sin(geocentricPhi); + double radiusSurface = this.radiusP / Hypot(this.radiusP * cosGeocentricPhi, sinGeocentricPhi); + + double vx = radiusSurface * Math.Cos(lambda) * cosGeocentricPhi; + double vy = radiusSurface * Math.Sin(lambda) * cosGeocentricPhi; + double vz = radiusSurface * sinGeocentricPhi; + + if (((this.radiusG - vx) * vx) - (vy * vy) - (vz * vz * this.radiusPInv2) < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double tmp = this.radiusG - vx; + if (this.flipAxis) + { + x = this.radiusG1 * Math.Atan(vy / Hypot(vz, tmp)); + y = this.radiusG1 * Math.Atan(vz / tmp); + } + else + { + x = this.radiusG1 * Math.Atan(vy / tmp); + y = this.radiusG1 * Math.Atan(vz / Hypot(vy, tmp)); + } + } + + private void InverseSpherical(double x, double y, out double lambda, out double phi) + { + double vx = -1d; + double vy = Math.Tan(x / this.radiusG1); + double vz = Math.Tan(y / this.radiusG1); + + if (this.flipAxis) + { + vy = Math.Tan(x / this.radiusG1) * Math.Sqrt(1d + (vz * vz)); + } + else + { + vz = Math.Tan(y / this.radiusG1) * Math.Sqrt(1d + (vy * vy)); + } + + double a = (vy * vy) + (vz * vz) + (vx * vx); + double b = 2d * this.radiusG * vx; + double det = (b * b) - (4d * a * this.c); + if (det < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double k = (-b - Math.Sqrt(det)) / (2d * a); + vx = this.radiusG + (k * vx); + vy *= k; + vz *= k; + + lambda = Math.Atan2(vy, vx); + phi = Math.Atan(vz / Hypot(vx, vy)); + } + + private void InverseEllipsoidal(double x, double y, out double lambda, out double phi) + { + double vx = -1d; + double vy = Math.Tan(x / this.radiusG1); + double vz = Math.Tan(y / this.radiusG1); + + if (this.flipAxis) + { + vy = Math.Tan(x / this.radiusG1) * Hypot(1d, vz); + } + else + { + vz = Math.Tan(y / this.radiusG1) * Hypot(1d, vy); + } + + double vzOverRadiusP = vz / this.radiusP; + double a = (vy * vy) + (vzOverRadiusP * vzOverRadiusP) + (vx * vx); + double b = 2d * this.radiusG * vx; + double det = (b * b) - (4d * a * this.c); + if (det < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double k = (-b - Math.Sqrt(det)) / (2d * a); + vx = this.radiusG + (k * vx); + vy *= k; + vz *= k; + + lambda = Math.Atan2(vy, vx); + phi = Math.Atan(this.radiusPInv2 * vz / Hypot(vx, vy)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Ginsburg8Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Ginsburg8Projection.cs new file mode 100644 index 00000000..86e3f4e2 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Ginsburg8Projection.cs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Ginsburg VIII projection (gins8). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the standard Ginsburg +/// VIII polynomial approximation. The implementation matches the latitude series +/// y = φ * (1 + φ² / 12) and the longitude scaling that combines latitude and +/// quartic longitude damping. +/// +internal sealed class Ginsburg8Projection : MapProjection +{ + private const double Cl = 0.000952426d; + private const double Cp = 0.162388d; + private const double C12 = 0.08333333333333333d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Ginsburg8Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Ginsburg8Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Ginsburg_VIII"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new Ginsburg8Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double t = lat * lat; + double y = lat * (1d + (t * C12)); + double x = lambda * (1d - (Cp * t)); + t = lambda * lambda; + x *= 0.87d - (Cl * t * t); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Ginsburg VIII does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/GnomonicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/GnomonicProjection.cs new file mode 100644 index 00000000..13c715e1 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/GnomonicProjection.cs @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Gnomonic map projection (gnom). +/// +/// +/// The Gnomonic projection is a perspective azimuthal projection from the center of the +/// sphere onto a tangent plane. All great circles (geodesics) project as straight lines. +/// Points at or beyond 90° angular distance from the projection center cannot be projected +/// and produce output coordinates. +/// The formulation was independently verified against the Wikipedia article +/// "Gnomonic projection" and Eric W. Weisstein's MathWorld entry "Gnomonic Projection". +/// The perspective scale k = 1 / cos(c) together with the azimuthal forward and +/// inverse relations based on the angular distance c match the implementation here. +/// The ellipsoidal extension follows the PROJ gnom implementation and Karney's +/// geodesic formulation, using the reduced length m12 and geodesic scale M12 +/// so that ρ = m12 / M12. The inverse uses Newton iteration on geodesic distance, +/// matching PROJ's stabilized small- and large-ρ updates. +/// +/// Wikipedia: Gnomonic projection. +/// MathWorld: Gnomonic Projection. +/// PROJ documentation: Gnomonic. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.3.1, pp. 109-115. +internal sealed class GnomonicProjection : MapProjection +{ + private const int MaxInverseIterations = 10; + private const double InverseDistanceTolerance = 1.4901161193847656e-10d; + + private readonly bool ellipsoidal; + private readonly double sinPhi0; + private readonly double cosPhi0; + private readonly double flattening; + private readonly double eccentricityPrimeSquared; + private readonly double meridionalOriginDistance; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public GnomonicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public GnomonicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Gnomonic"; + this.ellipsoidal = this.es > 0d; + Sincos(this.latOrigin, out this.sinPhi0, out this.cosPhi0); + this.flattening = (this.semiMajor - this.semiMinor) / this.semiMajor; + this.eccentricityPrimeSquared = ((this.semiMajor * this.semiMajor) - (this.semiMinor * this.semiMinor)) / (this.semiMinor * this.semiMinor); + this.meridionalOriginDistance = this.Mlfn(this.latOrigin, this.sinPhi0, this.cosPhi0); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GnomonicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + if (this.ellipsoidal) + { + this.ForwardEllipsoidal(ref lon, ref lat); + return; + } + + double lambda = Adjust_lon(lon - this.centralMeridian); + double sinPhi = Math.Sin(lat); + double cosPhi = Math.Cos(lat); + double cosLambda = Math.Cos(lambda); + + double cosC = (this.sinPhi0 * sinPhi) + (this.cosPhi0 * cosPhi * cosLambda); + if (cosC <= Eps10) + { + lon = double.NaN; + lat = double.NaN; + return; + } + + double k = 1d / cosC; + lon = this.SphericalRadius * k * cosPhi * Math.Sin(lambda); + lat = this.SphericalRadius * k * ((this.cosPhi0 * sinPhi) - (this.sinPhi0 * cosPhi * cosLambda)); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + if (this.ellipsoidal) + { + this.InverseEllipsoidal(ref x, ref y); + return; + } + + double rho = Hypot(x, y); + if (rho <= Eps10) + { + x = this.centralMeridian; + y = this.latOrigin; + return; + } + + double c = Math.Atan(rho * this.InverseSphericalRadius); + double sinC = Math.Sin(c); + double cosC = Math.Cos(c); + + double phi = Math.Asin(ProjectionConstants.Clamp((cosC * this.sinPhi0) + ((y * sinC * this.cosPhi0) / rho), -1d, 1d)); + double lambda = Math.Atan2(x * sinC, (rho * this.cosPhi0 * cosC) - (y * this.sinPhi0 * sinC)); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private void ForwardEllipsoidal(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + if (Math.Abs(lambda) <= 1e-14d) + { + double sinPhi = Math.Sin(lat); + double cosPhi = Math.Cos(lat); + double meridionalDistance = this.semiMajor * Math.Abs(this.Mlfn(lat, sinPhi, cosPhi) - this.meridionalOriginDistance); + double meridionalAzimuth = lat >= this.latOrigin ? 0d : Math.PI; + + if (!EllipsoidalGeodesic.TrySolvePositionReducedLengthAndScale( + this.semiMajor, + this.semiMinor, + this.flattening, + this.es, + this.eccentricityPrimeSquared, + this.latOrigin, + 0d, + meridionalAzimuth, + meridionalDistance, + out _, + out _, + out double meridionalReducedLength, + out double meridionalGeodesicScale)) + { + lon = double.NaN; + lat = double.NaN; + return; + } + + if (meridionalGeodesicScale <= 0d) + { + lon = double.NaN; + lat = double.NaN; + return; + } + + lon = 0d; + lat = this.scaleFactor * (meridionalReducedLength / meridionalGeodesicScale) * Math.Cos(meridionalAzimuth); + return; + } + + if (!EllipsoidalGeodesic.TryVincentyInverse( + this.semiMinor, + this.flattening, + this.eccentricityPrimeSquared, + this.latOrigin, + 0d, + lat, + lambda, + out double distance, + out double azimuth)) + { + lon = double.NaN; + lat = double.NaN; + return; + } + + if (!EllipsoidalGeodesic.TrySolvePositionReducedLengthAndScale( + this.semiMajor, + this.semiMinor, + this.flattening, + this.es, + this.eccentricityPrimeSquared, + this.latOrigin, + 0d, + azimuth, + distance, + out _, + out _, + out double reducedLength, + out double geodesicScale)) + { + lon = double.NaN; + lat = double.NaN; + return; + } + + if (geodesicScale <= 0d) + { + lon = double.NaN; + lat = double.NaN; + return; + } + + double rho = this.scaleFactor * (reducedLength / geodesicScale); + lon = rho * Math.Sin(azimuth); + lat = rho * Math.Cos(azimuth); + } + + private void InverseEllipsoidal(ref double x, ref double y) + { + double rhoProjected = Hypot(x, y); + if (rhoProjected <= Eps10) + { + x = this.centralMeridian; + y = this.latOrigin; + return; + } + + double azimuth = Math.Atan2(x, y); + double rho = rhoProjected / this.scaleFactor; + bool little = rho <= this.semiMajor; + double iterationRho = little ? rho : 1d / rho; + double distance = this.semiMajor * Math.Atan(rho * this.InverseSphericalRadius); + double tolerance = InverseDistanceTolerance * this.semiMajor; + + for (int iteration = 0; iteration < MaxInverseIterations; iteration++) + { + if (!EllipsoidalGeodesic.TrySolvePositionReducedLengthAndScale( + this.semiMajor, + this.semiMinor, + this.flattening, + this.es, + this.eccentricityPrimeSquared, + this.latOrigin, + 0d, + azimuth, + distance, + out _, + out _, + out double reducedLength, + out double geodesicScale)) + { + x = double.NaN; + y = double.NaN; + return; + } + + double deltaDistance = little + ? (reducedLength - (iterationRho * geodesicScale)) * geodesicScale + : ((iterationRho * reducedLength) - geodesicScale) * reducedLength; + + distance -= deltaDistance; + if (Math.Abs(deltaDistance) < tolerance) + { + if (!EllipsoidalGeodesic.TrySolvePositionReducedLengthAndScale( + this.semiMajor, + this.semiMinor, + this.flattening, + this.es, + this.eccentricityPrimeSquared, + this.latOrigin, + 0d, + azimuth, + distance, + out double latitude, + out double longitude, + out _, + out _)) + { + x = double.NaN; + y = double.NaN; + return; + } + + x = Math.Abs(Math.Abs(latitude) - HalfPi) < 1e-9d + ? this.centralMeridian + : Adjust_lon(this.centralMeridian + longitude); + y = latitude; + return; + } + } + + x = double.NaN; + y = double.NaN; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/GoodeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/GoodeProjection.cs new file mode 100644 index 00000000..8b679727 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/GoodeProjection.cs @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Goode Homolosine projection (goode). +/// +/// +/// The Goode Homolosine projection combines the sinusoidal projection for +/// latitudes within approximately ±40.7 degrees and the Mollweide projection for +/// higher latitudes, providing an equal-area representation with interrupted +/// distortion at the seam. Both spherical and ellipsoidal modes are supported. +/// The composite construction was independently verified against the published +/// Goode homolosine transition latitude of 40 degrees 44 minutes 11.8 seconds +/// (0.7109307819 rad) and the +/// PROJ goode documentation. The implementation switches at +/// PhiLim = 0.71093078197902358062, uses a sinusoidal branch below that +/// latitude, and applies the Mollweide branch with the standard YCor = 0.05280 +/// seam correction above it, matching the established Goode formulation first +/// published by J. Paul Goode in 1925. +/// +/// PROJ documentation: Goode Homolosine. +/// Goode, J.P. (1925): The Homolosine projection. +/// Wikipedia: Goode homolosine projection. +internal sealed class GoodeProjection : MapProjection +{ + private const int MollweideIterations = 12; + private const double YCor = 0.05280; + private const double PhiLim = 0.71093078197902358062; + private readonly bool isEllipsoidal; + private readonly double oneEs; + private readonly double qp; + private readonly double[] apa; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public GoodeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public GoodeProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Goode_Homolosine"; + this.isEllipsoidal = this.es > 0d; + if (this.isEllipsoidal) + { + this.oneEs = 1d - this.es; + this.qp = Qsfn(1d, this.e, this.oneEs); + this.apa = Authset(this.es); + } + else + { + this.oneEs = 0d; + this.qp = 0d; + this.apa = []; + } + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GoodeProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = this.isEllipsoidal ? this.GeographicToAuthalic(lat) : lat; + + double xUnit = lambda * Math.Cos(phi); + double yUnit = phi; + if (Math.Abs(phi) > PhiLim) + { + MollweideForwardUnit(lambda, phi, out xUnit, out yUnit); + yUnit -= phi >= 0d ? YCor : -YCor; + } + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + double phi = yUnit; + double cosPhi = Math.Cos(phi); + double lambda = Math.Abs(cosPhi) <= Eps10 ? 0d : (xUnit / cosPhi); + if (Math.Abs(yUnit) > PhiLim) + { + double correctedY = yUnit + (yUnit >= 0d ? YCor : -YCor); + MollweideInverseUnit(xUnit, correctedY, out lambda, out phi); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = this.isEllipsoidal ? this.AuthalicToGeographic(phi) : phi; + } + + private static void MollweideForwardUnit(double lambda, double phi, out double x, out double y) + { + double theta = Sign(phi) * HalfPi; + if (Math.Abs(Math.Abs(phi) - HalfPi) >= ProjectionConstants.Tolerance1E12) + { + theta = phi; + double target = PI * Math.Sin(phi); + for (int i = 0; i < MollweideIterations; i++) + { + double twoTheta = 2d * theta; + double delta = ((twoTheta + Math.Sin(twoTheta)) - target) / (2d + (2d * Math.Cos(twoTheta))); + theta -= delta; + if (Math.Abs(delta) < ProjectionConstants.Tolerance1E12) + { + break; + } + } + } + + x = (2d * ProjectionConstants.Sqrt2 / PI) * lambda * Math.Cos(theta); + y = ProjectionConstants.Sqrt2 * Math.Sin(theta); + } + + private static void MollweideInverseUnit(double x, double y, out double lambda, out double phi) + { + double theta = Math.Asin(ProjectionConstants.Clamp(y / ProjectionConstants.Sqrt2, -1d, 1d)); + double cosTheta = Math.Cos(theta); + + if (Math.Abs(cosTheta) <= Eps10) + { + lambda = 0d; + } + else + { + lambda = x * PI / (2d * ProjectionConstants.Sqrt2 * cosTheta); + } + + phi = Math.Asin(ProjectionConstants.Clamp(((2d * theta) + Math.Sin(2d * theta)) / PI, -1d, 1d)); + } + + private double GeographicToAuthalic(double phi) + { + double sinPhi = Math.Sin(phi); + double q = Qsfn(sinPhi, this.e, this.oneEs); + return Math.Asin(ProjectionConstants.Clamp(q / this.qp, -1d, 1d)); + } + + private double AuthalicToGeographic(double beta) + { + return Authlat(beta, this.apa); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/GuyouProjection.cs b/src/ProjNet/CoordinateSystems/Projections/GuyouProjection.cs new file mode 100644 index 00000000..0c8a8844 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/GuyouProjection.cs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Guyou projection (guyou). +/// +/// +/// Guyou is the historical conformal square projection developed by Émile Guyou +/// in 1887. It is represented here as a specialized mode of +/// , which supplies the shared Adams-family +/// hemisphere-in-a-square construction and the Guyou-specific orientation. +/// The spherical formulation was independently verified against the historical +/// Guyou hemisphere-in-a-square description and the PROJ guyou projection +/// documentation. This implementation delegates all mathematical work to +/// with AdamsMode.Guyou, matching the shared +/// conformal square construction used for the Guyou and related Adams-family variants. +/// Inverse projection is not supported in this implementation. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 7, Sect. 7.4.2, pp. 206-208. +/// PROJ documentation: Guyou. +/// Wikipedia: Guyou hemisphere-in-a-square projection. +internal sealed class GuyouProjection : AdamsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public GuyouProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public GuyouProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Guyou", AdamsMode.Guyou) + { + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new GuyouProjection(this.Parameters.ToProjectionParameter(), this)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/HammerProjection.cs b/src/ProjNet/CoordinateSystems/Projections/HammerProjection.cs new file mode 100644 index 00000000..f8db6a92 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/HammerProjection.cs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Hammer projection (hammer). +/// +/// +/// Hammer is an equal-area pseudocylindrical projection derived from the Lambert azimuthal +/// equal-area construction and parameterized here through the optional w and m +/// scale factors. The formulation was independently verified against the Wikipedia article +/// "Hammer projection" and Eric W. Weisstein's MathWorld entry +/// "Hammer-Aitoff Equal-Area Projection". The normalized factor +/// sqrt(2 / (1 + cos(φ) * cos(w * λ))) and the resulting scaled forward +/// equations match the implementation here. +/// +/// Wikipedia: Hammer projection. +/// MathWorld: Hammer-Aitoff Equal-Area Projection. +internal sealed class HammerProjection : MapProjection +{ + private readonly double w; + private readonly double m; + private readonly double inverseM; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public HammerProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public HammerProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Hammer"; + + this.w = Math.Abs(this.Parameters.GetOptionalParameterValue("W", 0.5d, "w")); + if (this.w <= 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for W: it should be > 0.", nameof(parameters)); + } + + this.m = Math.Abs(this.Parameters.GetOptionalParameterValue("M", 1d, "m")); + if (this.m <= 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for M: it should be > 0.", nameof(parameters)); + } + + this.inverseM = 1d / this.m; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new HammerProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = this.w * Adjust_lon(lon - this.centralMeridian); + double cosPhi = Math.Cos(lat); + double denominator = 1d + (cosPhi * Math.Cos(lambda)); + if (denominator == 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double d = Math.Sqrt(2d / denominator); + lon = this.SphericalRadius * ((this.m / this.w) * d * cosPhi * Math.Sin(lambda)); + lat = this.SphericalRadius * (this.inverseM * d * Math.Sin(lat)); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + double z = 1d - (0.25d * this.w * this.w * xUnit * xUnit) - (0.25d * yUnit * yUnit); + if (z < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + z = Math.Sqrt(z); + if (Math.Abs((2d * z * z) - 1d) < Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = Math.Atan2(this.w * xUnit * z, (2d * z * z) - 1d) / this.w; + double phi = Math.Asin(ProjectionConstants.Clamp(z * yUnit, -1d, 1d)); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/HatanoProjection.cs b/src/ProjNet/CoordinateSystems/Projections/HatanoProjection.cs new file mode 100644 index 00000000..a6df9c0f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/HatanoProjection.cs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Hatano Asymmetrical Equal Area projection (hatano). +/// +/// +/// Hatano Asymmetrical Equal Area is a spherical equal-area pseudocylindrical projection +/// introduced by Masataka Hatano in 1972. The northern and southern hemispheres use +/// distinct constants, and the implementation solves φ + sin(φ) = c * sin(lat) +/// iteratively before applying the final half-angle scaling. +/// +internal sealed class HatanoProjection : MapProjection +{ + private const int Iterations = 20; + private const double OneTol = ProjectionConstants.OnePlusEps6; + private const double Cn = 2.67595d; + private const double Csz = 2.43763d; + private const double Rcn = 0.37369906014686373063d; + private const double Rcs = 0.41023453108141924738d; + private const double Fycn = 1.75859d; + private const double Fycs = 1.93052d; + private const double Rycn = 0.56863737426006061674d; + private const double Rycs = 0.51799515156538134803d; + private const double Fxc = 0.85d; + private const double Rxc = 1.17647058823529411764d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public HatanoProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public HatanoProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Hatano"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new HatanoProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double c = Math.Sin(phi) * (phi < 0d ? Csz : Cn); + for (int i = Iterations; i > 0; i--) + { + double denominator = 1d + Math.Cos(phi); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double th1 = (phi + Math.Sin(phi) - c) / denominator; + phi -= th1; + if (Math.Abs(th1) < Eps7) + { + break; + } + } + + phi *= 0.5d; + double x = Fxc * lambda * Math.Cos(phi); + double y = Math.Sin(phi) * (phi < 0d ? Fycs : Fycn); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double th = yy * (yy < 0d ? Rycs : Rycn); + double absTh = Math.Abs(th); + if (absTh > 1d) + { + if (absTh > OneTol) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + th = th > 0d ? HalfPi : -HalfPi; + } + else + { + th = Math.Asin(th); + } + + double cosTh = Math.Cos(th); + if (Math.Abs(cosTh) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = (Rxc * xx) / cosTh; + double thetaDouble = th + th; + double phi = (thetaDouble + Math.Sin(thetaDouble)) * (yy < 0d ? Rcs : Rcn); + double absPhi = Math.Abs(phi); + if (absPhi > 1d) + { + if (absPhi > OneTol) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + phi = phi > 0d ? HalfPi : -HalfPi; + } + else + { + phi = Math.Asin(phi); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/HealpixProjection.cs b/src/ProjNet/CoordinateSystems/Projections/HealpixProjection.cs new file mode 100644 index 00000000..ce3c02d7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/HealpixProjection.cs @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the HEALPix (Hierarchical Equal Area isoLatitude Pixelization) projection (healpix). +/// +/// +/// HEALPix is an equal-area pseudocylindrical projection that combines an +/// equatorial Lambert cylindrical equal-area zone with polar regions based on an +/// interrupted Collignon construction. +/// The formulation was independently verified against K. M. Gorski et al., +/// "HEALPix: A Framework for High-Resolution Discretization and Fast Analysis of +/// Data Distributed on the Sphere," Astrophysical Journal, vol. 622, no. 2, +/// pp. 759-771, 2005. The equatorial relation +/// y = 3 * π / 8 * sin(φ) and the polar +/// σ = sqrt(3 * (1 - abs(sin(φ)))) construction match the +/// implementation here. +/// +/// Wikipedia: HEALPix. +internal sealed class HealpixProjection : MapProjection +{ + private const double CapEpsilon = 1e-15d; + private static readonly double Phi0Limit = Math.Asin(ProjectionConstants.TwoThirds); + + private readonly double radius; + private readonly double inverseRadius; + private readonly bool isEllipsoidal; + private readonly bool isRhealpix; + private readonly double oneEs; + private readonly double qp; + private readonly double[] apa; + private readonly double rotationRadians; + private readonly int northSquare; + private readonly int southSquare; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public HealpixProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public HealpixProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.isRhealpix = this.Parameters.GetOptionalParameterValue("rhealpix_mode", 0d) > Eps10; + this.northSquare = ReadSquareIndex(this.Parameters, "north_square"); + this.southSquare = ReadSquareIndex(this.Parameters, "south_square"); + this.Name = this.isRhealpix ? "rHEALPix" : "HEALPix"; + this.rotationRadians = DegreesToRadians(this.Parameters.GetOptionalParameterValue("rot_xy", 0d)); + this.isEllipsoidal = this.es > 0d; + + if (this.isEllipsoidal) + { + this.oneEs = 1d - this.es; + this.qp = Qsfn(1d, this.e, this.oneEs); + this.apa = Authset(this.es); + } + else + { + this.oneEs = 0d; + this.qp = 0d; + this.apa = []; + } + + double authalicScale = this.isEllipsoidal ? Math.Sqrt(0.5d * this.qp) : 1d; + this.radius = this.semiMajor * this.scaleFactor * authalicScale; + this.inverseRadius = 1d / this.radius; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new HealpixProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = this.isEllipsoidal ? this.GeographicToAuthalic(lat) : lat; + + ToHealpixSphere(lambda, phi, out double xUnit, out double yUnit); + if (this.isRhealpix) + { + CombineCaps(ref xUnit, ref yUnit, this.northSquare, this.southSquare, inverse: false); + } + else + { + Rotate(ref xUnit, ref yUnit, -this.rotationRadians); + } + + lon = this.radius * xUnit; + lat = this.radius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.inverseRadius; + double yUnit = y * this.inverseRadius; + + if (this.isRhealpix) + { + CombineCaps(ref xUnit, ref yUnit, this.northSquare, this.southSquare, inverse: true); + } + else + { + Rotate(ref xUnit, ref yUnit, this.rotationRadians); + } + + FromHealpixSphere(xUnit, yUnit, out double lambda, out double phiAuthalic); + + x = Adjust_lon(this.centralMeridian + lambda); + y = this.isEllipsoidal ? Authlat(phiAuthalic, this.apa) : phiAuthalic; + } + + private static void ToHealpixSphere(double lambda, double phi, out double x, out double y) + { + if (Math.Abs(phi) <= Phi0Limit) + { + x = lambda; + y = (3d * PI / 8d) * Math.Sin(phi); + return; + } + + double sigma = Math.Sqrt(Math.Max(0d, 3d * (1d - Math.Abs(Math.Sin(phi))))); + int capNumber = (int)Math.Floor((2d * lambda / PI) + 2d); + if (capNumber < 0) + { + capNumber = 0; + } + else if (capNumber > 3) + { + capNumber = 3; + } + + double lambdaCenter = (-3d * FortPi) + (HalfPi * capNumber); + x = lambdaCenter + ((lambda - lambdaCenter) * sigma); + y = Sign(phi) * FortPi * (2d - sigma); + } + + private static void FromHealpixSphere(double x, double y, out double lambda, out double phi) + { + if (Math.Abs(y) <= FortPi) + { + lambda = x; + phi = Math.Asin(ProjectionConstants.Clamp((8d * y) / (3d * PI), -1d, 1d)); + return; + } + + if (Math.Abs(y) < HalfPi) + { + int capNumber = (int)Math.Floor((2d * x / PI) + 2d); + if (capNumber < 0) + { + capNumber = 0; + } + else if (capNumber > 3) + { + capNumber = 3; + } + + double xCenter = (-3d * FortPi) + (HalfPi * capNumber); + double tau = 2d - ((4d * Math.Abs(y)) / PI); + if (Math.Abs(tau) <= Eps10) + { + lambda = xCenter; + phi = Sign(y) * HalfPi; + return; + } + + lambda = xCenter + ((x - xCenter) / tau); + phi = Sign(y) * Math.Asin(ProjectionConstants.Clamp(1d - ((tau * tau) / 3d), -1d, 1d)); + return; + } + + lambda = -PI; + phi = Sign(y) * HalfPi; + } + + private static void CombineCaps(ref double x, ref double y, int northSquare, int southSquare, bool inverse) + { + GetCap( + x, + y, + northSquare, + southSquare, + inverse, + out bool isPolar, + out bool isNorth, + out int capNumber, + out double capX, + out double capY); + if (!isPolar) + { + return; + } + + double deltaX = x - capX; + double deltaY = y - capY; + int pole = isNorth ? northSquare : southSquare; + int quarterTurns = inverse + ? (isNorth ? -(capNumber - pole) : capNumber - pole) + : (isNorth ? capNumber - pole : -(capNumber - pole)); + RotateQuarterTurns(ref deltaX, ref deltaY, quarterTurns); + + double anchorX = (-3d * FortPi) + ((inverse ? capNumber : pole) * HalfPi); + double anchorY = isNorth ? HalfPi : -HalfPi; + x = deltaX + anchorX; + y = deltaY + anchorY; + } + + private static void GetCap( + double x, + double y, + int northSquare, + int southSquare, + bool inverse, + out bool isPolar, + out bool isNorth, + out int capNumber, + out double capX, + out double capY) + { + isPolar = false; + isNorth = false; + capNumber = 0; + capX = x; + capY = y; + + if (!inverse) + { + if (y > FortPi) + { + isPolar = true; + isNorth = true; + capY = HalfPi; + } + else if (y < -FortPi) + { + isPolar = true; + capY = -HalfPi; + } + else + { + return; + } + + if (x < -HalfPi) + { + capX = -3d * FortPi; + capNumber = 0; + } + else if (x < 0d) + { + capX = -FortPi; + capNumber = 1; + } + else if (x < HalfPi) + { + capX = FortPi; + capNumber = 2; + } + else + { + capX = 3d * FortPi; + capNumber = 3; + } + + return; + } + + double classificationX = x; + if (y > FortPi) + { + isPolar = true; + isNorth = true; + capX = (-3d * FortPi) + (northSquare * HalfPi); + capY = HalfPi; + classificationX -= northSquare * HalfPi; + } + else if (y < -FortPi) + { + isPolar = true; + capX = (-3d * FortPi) + (southSquare * HalfPi); + capY = -HalfPi; + classificationX -= southSquare * HalfPi; + } + else + { + return; + } + + if (isNorth) + { + if (y >= -classificationX - FortPi - CapEpsilon && y < classificationX + (5d * FortPi) - CapEpsilon) + { + capNumber = (northSquare + 1) % 4; + return; + } + + if (y > -classificationX - FortPi + CapEpsilon && y >= classificationX + (5d * FortPi) - CapEpsilon) + { + capNumber = (northSquare + 2) % 4; + return; + } + + if (y <= -classificationX - FortPi + CapEpsilon && y > classificationX + (5d * FortPi) + CapEpsilon) + { + capNumber = (northSquare + 3) % 4; + return; + } + + capNumber = northSquare; + return; + } + + if (y <= classificationX + FortPi + CapEpsilon && y > -classificationX - (5d * FortPi) + CapEpsilon) + { + capNumber = (southSquare + 1) % 4; + return; + } + + if (y < classificationX + FortPi - CapEpsilon && y <= -classificationX - (5d * FortPi) + CapEpsilon) + { + capNumber = (southSquare + 2) % 4; + return; + } + + if (y >= classificationX + FortPi - CapEpsilon && y < -classificationX - (5d * FortPi) - CapEpsilon) + { + capNumber = (southSquare + 3) % 4; + return; + } + + capNumber = southSquare; + } + + private static int ReadSquareIndex(ProjectionParameterSet parameters, string name) + { + double value = parameters.GetOptionalParameterValue(name, 0d); + if (double.IsNaN(value) || double.IsInfinity(value)) + { + ArgumentGuard.ThrowArgument($"Invalid value for {name} parameter: expected an integer between 0 and 3.", nameof(parameters)); + } + + int square = (int)Math.Round(value); + if (Math.Abs(value - square) > Eps10 || square < 0 || square > 3) + { + return ArgumentGuard.ThrowArgument($"Invalid value for {name} parameter: expected an integer between 0 and 3.", nameof(parameters)); + } + + return square; + } + + private static void RotateQuarterTurns(ref double x, ref double y, int quarterTurns) + { + int normalized = quarterTurns % 4; + if (normalized < 0) + { + normalized += 4; + } + + double originalX = x; + double originalY = y; + switch (normalized) + { + case 1: + x = -originalY; + y = originalX; + break; + case 2: + x = -originalX; + y = -originalY; + break; + case 3: + x = originalY; + y = -originalX; + break; + } + } + + private static void Rotate(ref double x, ref double y, double angle) + { + if (Math.Abs(angle) <= Eps10) + { + return; + } + + double cos = Math.Cos(angle); + double sin = Math.Sin(angle); + double xr = (x * cos) - (y * sin); + double yr = (y * cos) + (x * sin); + x = xr; + y = yr; + } + + private double GeographicToAuthalic(double phi) + { + double q = Qsfn(Math.Sin(phi), this.e, this.oneEs); + return Math.Asin(ProjectionConstants.Clamp(q / this.qp, -1d, 1d)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/HotineObliqueMercatorProjection.cs b/src/ProjNet/CoordinateSystems/Projections/HotineObliqueMercatorProjection.cs index 69e7b291..a59dc372 100644 --- a/src/ProjNet/CoordinateSystems/Projections/HotineObliqueMercatorProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/HotineObliqueMercatorProjection.cs @@ -1,238 +1,360 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Hotine Oblique Mercator map projection (EPSG method 9812). +/// +/// +/// The Hotine Oblique Mercator projects a region along a central oblique line +/// defined by an azimuth at the projection centre. The cylinder axis is tilted with +/// respect to the Earth's axis, making it suitable for regions with a predominant +/// oblique extent. It is a conformal projection. False easting and northing are +/// applied relative to the centre of the initial line. +/// The formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG method 9812, Hotine Oblique Mercator (variant A). +/// The u0 offset at the intersection of the central line and aposphere equator +/// and the rectified-skew rotation by γ match the implementation here. +/// +/// EPSG method 9812: Hotine Oblique Mercator (variant A). +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.1.7, pp. 63-66. +internal class HotineObliqueMercatorProjection : MapProjection { - [Serializable] - internal class HotineObliqueMercatorProjection : MapProjection + private readonly bool noRotation; + private readonly double sinP20; + private readonly double cosP20; + private readonly double bl; + private readonly double al; + private readonly double d; + private readonly double el; + private readonly double singrid; + private readonly double cosgrid; + private readonly double singam; + private readonly double cosgam; + private readonly double u; + private readonly double vPoleNorth; + private readonly double vPoleSouth; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public HotineObliqueMercatorProjection(IEnumerable parameters) + : this(parameters, null) { - private readonly double _azimuth; - private readonly double _sinP20, _cosP20; - private readonly double _bl, _al; - private readonly double _d, _el; - private readonly double _singrid, _cosgrid; - private readonly double _singam, _cosgam; - private readonly double _sinaz, _cosaz; - private readonly double _u; + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public HotineObliqueMercatorProjection(IEnumerable parameters, HotineObliqueMercatorProjection? inverse) + : base(parameters, inverse) + { + this.Authority = "EPSG"; + this.AuthorityCode = 9812; + this.Name = "Hotine_Oblique_Mercator"; + this.noRotation = this.Parameters.ContainsKey("no_rot"); + + double alpha = 0d; + double gamma0; + double rotationAngle; + Sincos(this.latOrigin, out this.sinP20, out this.cosP20); + double con = 1.0 - (this.es * Math.Pow(this.sinP20, 2)); + double com = Math.Sqrt(1.0 - this.es); + this.bl = Math.Sqrt(1.0 + (this.es * Math.Pow(this.cosP20, 4.0) / (1.0 - this.es))); + this.al = this.semiMajor * this.bl * this.scaleFactor * com / con; - private bool NaturalOriginOffsets { - get + double fValue = 1.0; + if (Math.Abs(this.latOrigin) < Epsln) + { + this.d = 1.0; + this.el = 1.0; + } + else + { + double ts = Tsfnz(this.e, this.latOrigin, this.sinP20); + con = Math.Sqrt(con); + this.d = this.bl * com / (this.cosP20 * con); + if (((this.d * this.d) - 1.0) > 0.0) + { + if (this.latOrigin >= 0.0) + { + fValue = this.d + Math.Sqrt((this.d * this.d) - 1.0); + } + else + { + fValue = this.d - Math.Sqrt((this.d * this.d) - 1.0); + } + } + else { - if (AuthorityCode == 9812) return false; - if (AuthorityCode == 9815) return true; - throw new ArgumentException("AuthorityCode"); + fValue = this.d; } + + this.el = fValue * Math.Pow(ts, this.bl); } - public HotineObliqueMercatorProjection(IEnumerable parameters) - : this(parameters, null) + bool hasAzimuth = this.Parameters.ContainsKey("alpha") || this.Parameters.ContainsKey("azimuth"); + bool hasRotationAngle = this.Parameters.ContainsKey("gamma") || this.Parameters.ContainsKey("rectified_grid_angle"); + if (hasAzimuth || hasRotationAngle) { - } + alpha = DegreesToRadians(this.Parameters.GetOptionalParameterValue("alpha", this.Parameters.GetOptionalParameterValue("azimuth", 0d))); + rotationAngle = DegreesToRadians(this.Parameters.GetOptionalParameterValue("gamma", this.Parameters.GetOptionalParameterValue("rectified_grid_angle", RadiansToDegrees(alpha)))); + if (Math.Abs(Math.Abs(this.latOrigin) - HalfPi) <= Eps7) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_0: |lat_0| should be < 90°", nameof(parameters)); + } + + if (hasAzimuth) + { + gamma0 = Asinz(Math.Sin(alpha) / this.d); + if (!hasRotationAngle) + { + rotationAngle = alpha; + } + } + else + { + gamma0 = rotationAngle; + alpha = Asinz(this.d * Math.Sin(gamma0)); + } - public HotineObliqueMercatorProjection(IEnumerable parameters, HotineObliqueMercatorProjection inverse) - : base(parameters, inverse) + double g = 0.5 * (fValue - (1.0 / fValue)); + this.Lon_origin -= Asinz(g * Math.Tan(gamma0)) / this.bl; + } + else { - Authority = "EPSG"; - AuthorityCode = 9812; - Name = "Hotine_Oblique_Mercator"; + double phi1 = DegreesToRadians(this.Parameters.GetParameterValue("lat_1", "standard_parallel_1")); + double phi2 = DegreesToRadians(this.Parameters.GetParameterValue("lat_2", "standard_parallel_2")); + double lam1 = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lon_1", 0d)); + double lam2 = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lon_2", 0d)); - _azimuth = DegreesToRadians(_Parameters.GetParameterValue("azimuth")); - double rectifiedGridAngle = DegreesToRadians(_Parameters.GetParameterValue("rectified_grid_angle")); - + if (Math.Abs(phi1) > HalfPi - Eps7) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_1: |lat_1| should be < 90°", nameof(parameters)); + } - sincos(lat_origin, out _sinP20, out _cosP20); - double con = 1.0 - _es * Math.Pow(_sinP20, 2); - double com = Math.Sqrt(1.0 - _es); - _bl = Math.Sqrt(1.0 + _es * Math.Pow(_cosP20, 4.0) / ( 1.0 - _es )); - _al = _semiMajor * _bl * scale_factor * com / con; + if (Math.Abs(phi2) > HalfPi - Eps7) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_2: |lat_2| should be < 90°", nameof(parameters)); + } - double f; - if (Math.Abs(lat_origin) < EPSLN) + if (Math.Abs(phi1 - phi2) <= Eps7) { - //ts = 1.0; - _d = 1.0; - _el = 1.0; - f = 1.0; + ArgumentGuard.ThrowArgument("Invalid value for lat_1/lat_2: lat_1 should be different from lat_2", nameof(parameters)); } - else + + if (Math.Abs(phi1) <= Eps7) { - double ts = tsfnz(_e, lat_origin, _sinP20); - con = Math.Sqrt(con); - _d = _bl * com / ( _cosP20 * con ); - if ( ( _d * _d - 1.0 ) > 0.0 ) - { - if ( lat_origin >= 0.0 ) - f = _d + Math.Sqrt(_d * _d - 1.0); - else - f = _d - Math.Sqrt(_d * _d - 1.0); - } - else - f = _d; - _el = f * Math.Pow(ts, _bl); + ArgumentGuard.ThrowArgument("Invalid value for lat_1: lat_1 should be different from 0", nameof(parameters)); + } + + if (Math.Abs(Math.Abs(this.latOrigin) - HalfPi) <= Eps7) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_0: |lat_0| should be < 90°", nameof(parameters)); } - double g = .5 * ( f - 1.0 / f ); - double gama = asinz(Math.Sin(_azimuth) / _d); - lon_origin = lon_origin - asinz(g * Math.Tan(gama)) / _bl; + double h = Math.Pow(Tsfnz(this.e, phi1, Math.Sin(phi1)), this.bl); + double l = Math.Pow(Tsfnz(this.e, phi2, Math.Sin(phi2)), this.bl); + double f = this.el / h; + double p = (l - h) / (l + h); + if (Math.Abs(p) <= Epsln) + { + ArgumentGuard.ThrowArgument("Invalid value for eccentricity", nameof(parameters)); + } - con = Math.Abs(lat_origin); - if ( ( con > EPSLN ) && ( Math.Abs(con - HALF_PI) > EPSLN ) ) + double j = this.el * this.el; + j = (j - (l * h)) / (j + (l * h)); + double lamDifference = lam1 - lam2; + if (lamDifference < -PI) { - sincos(gama, out _singam, out _cosgam); - sincos(_azimuth, out _sinaz, out _cosaz); - if ( lat_origin >= 0 ) - _u = ( _al / _bl ) * Math.Atan(Math.Sqrt(_d * _d - 1.0) / _cosaz); - else - _u = -( _al / _bl ) * Math.Atan(Math.Sqrt(_d * _d - 1.0) / _cosaz); + lam2 -= TwoPi; + } + else if (lamDifference > PI) + { + lam2 += TwoPi; + } + + this.Lon_origin = Adjust_lon((0.5 * (lam1 + lam2)) - (Math.Atan(j * Math.Tan(0.5 * this.bl * (lam1 - lam2)) / p) / this.bl)); + double denominator = f - (1.0 / f); + if (Math.Abs(denominator) <= Epsln) + { + ArgumentGuard.ThrowArgument("Invalid value for eccentricity", nameof(parameters)); + } + + gamma0 = Math.Atan(2.0 * Math.Sin(this.bl * Adjust_lon(lam1 - this.Lon_origin)) / denominator); + rotationAngle = alpha = Asinz(this.d * Math.Sin(gamma0)); + } + + Sincos(gamma0, out this.singam, out this.cosgam); + Sincos(rotationAngle, out this.singrid, out this.cosgrid); + double arB = this.al / this.bl; + if (!this.NaturalOriginOffsets) + { + this.u = Math.Abs(arB * Math.Atan(Math.Sqrt(Math.Max(0d, (this.d * this.d) - 1.0)) / Math.Cos(alpha))); + if (this.latOrigin < 0.0) + { + this.u = -this.u; + } + } + else + { + this.u = 0d; + } + + double halfGamma = 0.5 * gamma0; + this.vPoleNorth = arB * Math.Log(Math.Tan(FortPi - halfGamma)); + this.vPoleSouth = arB * Math.Log(Math.Tan(FortPi + halfGamma)); + } + + private bool NaturalOriginOffsets + { + get + { + if (this.AuthorityCode == 9812) + { + return false; + } + + if (this.AuthorityCode == 9815) + { + return true; + } + + return ProjectionThrowHelper.ThrowInvalidOperation($"Unexpected Hotine Oblique Mercator authority code {this.AuthorityCode}."); + } + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new HotineObliqueMercatorProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double us; + double ul; + double vs; + + // Forward equations + // ----------------- + double sin_phi = Math.Sin(lat); + double dlon = Adjust_lon(lon - this.Lon_origin); + double vl = Math.Sin(this.bl * dlon); + if (Math.Abs(Math.Abs(lat) - HalfPi) > Epsln) + { + double ts1 = Tsfnz(this.e, lat, sin_phi); + double q = this.el / Math.Pow(ts1, this.bl); + double s = .5 * (q - (1.0 / q)); + double t = .5 * (q + (1.0 / q)); + ul = ((s * this.singam) - (vl * this.cosgam)) / t; + double con = Math.Cos(this.bl * dlon); + if (Math.Abs(con) < .0000001) + { + us = this.al * this.bl * dlon; } else { - throw new ArgumentException("Input data error"); - } - - sincos(rectifiedGridAngle, out _singrid, out _cosgrid); - - } - - public override MathTransform Inverse() - { - if (_inverse == null) - { - _inverse = new HotineObliqueMercatorProjection(_Parameters.ToProjectionParameter(), this); - } - return _inverse; - - } - - //protected override double[] RadiansToMeters(double[] lonlat) - //{ - // var lon = lonlat[0]; - // var lat = lonlat[1]; - - // Double us, ul; - - // // Forward equations - // // ----------------- - // var sin_phi = Math.Sin(lat); - // var dlon = adjust_lon(lon - lon_origin); - // var vl = Math.Sin(_bl * dlon); - // if (Math.Abs(Math.Abs(lat) - HALF_PI) > EPSLN) - // { - // var ts1 = tsfnz(_e, lat, sin_phi); - // var q = _el / (Math.Pow(ts1, _bl)); - // var s = .5 * (q - 1.0 / q); - // var t = .5 * (q + 1.0 / q); - // ul = (s * _singam - vl * _cosgam) / t; - // var con = Math.Cos(_bl * dlon); - // if (Math.Abs(con) < .0000001) - // { - // us = _al * _bl * dlon; - // } - // else - // { - // us = _al * Math.Atan((s * _cosgam + vl * _singam) / con) / _bl; - // if (con < 0) - // us = us + PI * _al / _bl; - // } - // } - // else - // { - // if (lat >= 0) - // ul = _singam; - // else - // ul = -_singam; - // us = _al * lat / _bl; - // } - // if (Math.Abs(Math.Abs(ul) - 1.0) <= EPSLN) - // { - // throw new Exception("Point projects into infinity"); - // } - - // var vs = .5 * _al * Math.Log((1.0 - ul) / (1.0 + ul)) / _bl; - // if (!NaturalOriginOffsets) us = us - _u; - // var x = vs * _cosgrid + us * _singrid; - // var y = us * _cosgrid - vs * _singrid; - - // return lonlat.Length == 2 - // ? new [] {x, y} : - // new [] {x, y, lonlat[2]}; - //} - - protected override void RadiansToMeters(ref double lon, ref double lat) - { - double us, ul; - - // Forward equations - // ----------------- - double sin_phi = Math.Sin(lat); - double dlon = adjust_lon(lon - lon_origin); - double vl = Math.Sin(_bl * dlon); - if (Math.Abs(Math.Abs(lat) - HALF_PI) > EPSLN) - { - double ts1 = tsfnz(_e, lat, sin_phi); - double q = _el / (Math.Pow(ts1, _bl)); - double s = .5 * (q - 1.0 / q); - double t = .5 * (q + 1.0 / q); - ul = (s * _singam - vl * _cosgam) / t; - double con = Math.Cos(_bl * dlon); - if (Math.Abs(con) < .0000001) + us = this.al * Math.Atan(((s * this.cosgam) + (vl * this.singam)) / con) / this.bl; + if (con < 0) { - us = _al * _bl * dlon; - } - else - { - us = _al * Math.Atan((s * _cosgam + vl * _singam) / con) / _bl; - if (con < 0) - us = us + PI * _al / _bl; + us += PI * this.al / this.bl; } } + + vs = .5 * this.al * Math.Log((1.0 - ul) / (1.0 + ul)) / this.bl; + } + else + { + if (lat >= 0) + { + ul = this.singam; + vs = this.vPoleNorth; + } else { - if (lat >= 0) - ul = _singam; - else - ul = -_singam; - us = _al * lat / _bl; + ul = -this.singam; + vs = this.vPoleSouth; } - if (Math.Abs(Math.Abs(ul) - 1.0) <= EPSLN) - throw new Exception("Point projects into infinity"); - - double vs = .5 * _al * Math.Log((1.0 - ul) / (1.0 + ul)) / _bl; - if (!NaturalOriginOffsets) us = us - _u; + us = this.al * lat / this.bl; + } - lon = vs * _cosgrid + us * _singrid; - lat = us * _cosgrid - vs * _singrid; + if (Math.Abs(Math.Abs(ul) - 1.0) <= Epsln) + { + throw new InvalidOperationException("Point projects into infinity"); } - protected override void MetersToRadians(ref double x, ref double y) + if (this.noRotation) { - // Inverse equations - // ----------------- - double vs = x * _cosgrid - y * _singrid; - double us = y * _cosgrid + x * _singrid; - if (!NaturalOriginOffsets) us = us + _u; - double q = Math.Exp(-_bl * vs / _al); - double s = .5 * (q - 1.0 / q); - double t = .5 * (q + 1.0 / q); - double vl = Math.Sin(_bl * us / _al); - double ul = (vl * _cosgam + s * _singam) / t; - if (Math.Abs(Math.Abs(ul) - 1.0) <= EPSLN) + lon = us; + lat = vs; + } + else + { + if (!this.NaturalOriginOffsets) { - x = lon_origin; - y = sign(ul) * HALF_PI; + us -= this.u; } - else + + lon = (vs * this.cosgrid) + (us * this.singrid); + lat = (us * this.cosgrid) - (vs * this.singrid); + } + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + // Inverse equations + // ----------------- + double vs; + double us; + if (this.noRotation) + { + vs = y; + us = x; + } + else + { + vs = (x * this.cosgrid) - (y * this.singrid); + us = (y * this.cosgrid) + (x * this.singrid); + if (!this.NaturalOriginOffsets) { - double con = 1.0 / _bl; - double ts1 = Math.Pow((_el / Math.Sqrt((1.0 + ul) / (1.0 - ul))), con); - long flag; - y = phi2z(_e, ts1, out flag); - con = Math.Cos(_bl * us / _al); - double theta = lon_origin - Math.Atan2((s * _cosgam - vl * _singam), con) / _bl; - x = adjust_lon(theta); + us += this.u; } } + + double q = Math.Exp(-this.bl * vs / this.al); + double s = .5 * (q - (1.0 / q)); + double t = .5 * (q + (1.0 / q)); + double vl = Math.Sin(this.bl * us / this.al); + double ul = ((vl * this.cosgam) + (s * this.singam)) / t; + if (Math.Abs(Math.Abs(ul) - 1.0) <= Epsln) + { + x = this.Lon_origin; + y = Sign(ul) * HalfPi; + } + else + { + double con = 1.0 / this.bl; + double ts1 = Math.Pow(this.el / Math.Sqrt((1.0 + ul) / (1.0 - ul)), con); + y = Phi2z(this.e, ts1, out _); + con = Math.Cos(this.bl * us / this.al); + double theta = this.Lon_origin - (Math.Atan2((s * this.cosgam) - (vl * this.singam), con) / this.bl); + x = Adjust_lon(theta); + } } } diff --git a/src/ProjNet/CoordinateSystems/Projections/IghProjection.cs b/src/ProjNet/CoordinateSystems/Projections/IghProjection.cs new file mode 100644 index 00000000..da33a0e9 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/IghProjection.cs @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Interrupted Goode Homolosine projection (igh). +/// +/// +/// Combines the sinusoidal projection for latitudes within approximately +/// ±40°44′12″ and the Mollweide projection for higher latitudes, with interruptions +/// optimised for the continental landmasses. The projection uses 12 zones: two +/// Mollweide and two sinusoidal zones in the northern hemisphere, and four +/// sinusoidal and four Mollweide zones in the southern hemisphere. +/// This interrupted equal-area projection matches PROJ's igh definition +/// and the standard Goode homolosine construction first published by J. P. Goode in +/// 1925. The implementation uses the published transition latitude of +/// 40 degrees 44 minutes 11.8 seconds and an explicit 12-zone land-oriented lobe +/// arrangement. +/// +/// PROJ documentation: Interrupted Goode Homolosine. +/// Goode, J.P. (1925): The Homolosine projection. +/// Wikipedia: Goode homolosine projection. +internal sealed class IghProjection : MapProjection +{ + private const int MollweideIterations = 12; + + private static readonly double PhiBoundary = DegreesToRadians(40d + (44d / 60d) + (11.8d / 3600d)); + + private static readonly double D20 = DegreesToRadians(20d); + private static readonly double D30 = DegreesToRadians(30d); + private static readonly double D40 = DegreesToRadians(40d); + private static readonly double D50 = DegreesToRadians(50d); + private static readonly double D60 = DegreesToRadians(60d); + private static readonly double D80 = DegreesToRadians(80d); + private static readonly double D100 = DegreesToRadians(100d); + private static readonly double D140 = DegreesToRadians(140d); + private static readonly double D160 = DegreesToRadians(160d); + private static readonly double D180 = DegreesToRadians(180d); + + private readonly double dy0; + private readonly ZoneDefinition[] zones; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public IghProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public IghProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Interrupted_Goode_Homolosine"; + + MollweideForwardUnit(0d, PhiBoundary, out _, out double mollweideBoundaryY); + this.dy0 = PhiBoundary - mollweideBoundaryY; + this.zones = CreateZones(this.dy0); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new IghProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + int zoneIndex = DetermineForwardZone(lat, lambda); + ZoneDefinition zone = this.zones[zoneIndex]; + + double localLambda = lambda - zone.Lambda0; + double xUnit = localLambda * Math.Cos(lat); + double yUnit = lat; + if (zone.IsMollweide) + { + MollweideForwardUnit(localLambda, lat, out xUnit, out yUnit); + } + + lon = this.SphericalRadius * (zone.X0 + xUnit); + lat = this.SphericalRadius * (zone.Y0 + yUnit); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + int zoneIndex = DetermineInverseZone(xUnit, yUnit, this.dy0); + if (zoneIndex < 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + ZoneDefinition zone = this.zones[zoneIndex]; + double localX = xUnit - zone.X0; + double localY = yUnit - zone.Y0; + + double phi = localY; + double cosPhi = Math.Cos(phi); + double lambdaLocal = Math.Abs(cosPhi) <= Eps10 ? 0d : (localX / cosPhi); + if (zone.IsMollweide) + { + MollweideInverseUnit(localX, localY, out lambdaLocal, out phi); + } + + double lambda = lambdaLocal + zone.Lambda0; + if (!IsPointInZone(zoneIndex, lambda, phi)) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static ZoneDefinition[] CreateZones(double dy0) + { + return + [ + new ZoneDefinition(true, -D100, -D100, dy0), // 1 + new ZoneDefinition(true, D30, D30, dy0), // 2 + new ZoneDefinition(false, -D100, -D100, 0d), // 3 + new ZoneDefinition(false, D30, D30, 0d), // 4 + new ZoneDefinition(false, -D160, -D160, 0d), // 5 + new ZoneDefinition(false, -D60, -D60, 0d), // 6 + new ZoneDefinition(false, D20, D20, 0d), // 7 + new ZoneDefinition(false, D140, D140, 0d), // 8 + new ZoneDefinition(true, -D160, -D160, -dy0), // 9 + new ZoneDefinition(true, -D60, -D60, -dy0), // 10 + new ZoneDefinition(true, D20, D20, -dy0), // 11 + new ZoneDefinition(true, D140, D140, -dy0), // 12 + ]; + } + + private static int DetermineForwardZone(double phi, double lambda) + { + if (phi >= PhiBoundary) + { + return lambda <= -D40 ? 0 : 1; + } + + if (phi >= 0d) + { + return lambda <= -D40 ? 2 : 3; + } + + if (phi >= -PhiBoundary) + { + if (lambda <= -D100) + { + return 4; + } + + return lambda <= -D20 ? 5 : lambda <= D80 ? 6 : 7; + } + + if (lambda <= -D100) + { + return 8; + } + + return lambda <= -D20 ? 9 : lambda <= D80 ? 10 : 11; + } + + private static int DetermineInverseZone(double x, double y, double dy0) + { + double y90 = dy0 + ProjectionConstants.Sqrt2; + if (y > (y90 + Eps10) || y < (-y90 - Eps10)) + { + return -1; + } + + if (y >= PhiBoundary) + { + return x <= -D40 ? 0 : 1; + } + + if (y >= 0d) + { + return x <= -D40 ? 2 : 3; + } + + if (y >= -PhiBoundary) + { + if (x <= -D100) + { + return 4; + } + + return x <= -D20 ? 5 : x <= D80 ? 6 : 7; + } + + if (x <= -D100) + { + return 8; + } + + return x <= -D20 ? 9 : x <= D80 ? 10 : 11; + } + + private static bool IsPointInZone(int zoneIndex, double lambda, double phi) + { + return zoneIndex switch + { + 0 => ((lambda >= -D180 - Eps10) && (lambda <= -D40 + Eps10)) + || (((lambda >= -D40 - Eps10) && (lambda <= -DegreesToRadians(10d) + Eps10)) + && ((phi >= D60 - Eps10) && (phi <= HalfPi + Eps10))), + 1 => ((lambda >= -D40 - Eps10) && (lambda <= D180 + Eps10)) + || (((lambda >= -D180 - Eps10) && (lambda <= -D160 + Eps10)) + && ((phi >= D50 - Eps10) && (phi <= HalfPi + Eps10))) + || (((lambda >= -DegreesToRadians(50d) - Eps10) && (lambda <= -D40 + Eps10)) + && ((phi >= D60 - Eps10) && (phi <= HalfPi + Eps10))), + 2 => (lambda >= -D180 - Eps10) && (lambda <= -D40 + Eps10), + 3 => (lambda >= -D40 - Eps10) && (lambda <= D180 + Eps10), + 4 or 8 => (lambda >= -D180 - Eps10) && (lambda <= -D100 + Eps10), + 5 or 9 => (lambda >= -D100 - Eps10) && (lambda <= -D20 + Eps10), + 6 or 10 => (lambda >= -D20 - Eps10) && (lambda <= D80 + Eps10), + 7 or 11 => (lambda >= D80 - Eps10) && (lambda <= D180 + Eps10), + _ => false, + }; + } + + private static void MollweideForwardUnit(double lambda, double phi, out double x, out double y) + { + double theta = Sign(phi) * HalfPi; + if (Math.Abs(Math.Abs(phi) - HalfPi) >= ProjectionConstants.Tolerance1E12) + { + theta = phi; + double target = PI * Math.Sin(phi); + for (int i = 0; i < MollweideIterations; i++) + { + double twoTheta = 2d * theta; + double delta = ((twoTheta + Math.Sin(twoTheta)) - target) / (2d + (2d * Math.Cos(twoTheta))); + theta -= delta; + if (Math.Abs(delta) < ProjectionConstants.Tolerance1E12) + { + break; + } + } + } + + x = (2d * ProjectionConstants.Sqrt2 / PI) * lambda * Math.Cos(theta); + y = ProjectionConstants.Sqrt2 * Math.Sin(theta); + } + + private static void MollweideInverseUnit(double x, double y, out double lambda, out double phi) + { + double theta = Math.Asin(ProjectionConstants.Clamp(y / ProjectionConstants.Sqrt2, -1d, 1d)); + double cosTheta = Math.Cos(theta); + if (Math.Abs(cosTheta) <= Eps10) + { + lambda = 0d; + } + else + { + lambda = x * PI / (2d * ProjectionConstants.Sqrt2 * cosTheta); + } + + phi = Math.Asin(ProjectionConstants.Clamp(((2d * theta) + Math.Sin(2d * theta)) / PI, -1d, 1d)); + } + + private readonly struct ZoneDefinition(bool isMollweide, double lambda0, double x0, double y0) + { + public bool IsMollweide { get; } = isMollweide; + + public double Lambda0 { get; } = lambda0; + + public double X0 { get; } = x0; + + public double Y0 { get; } = y0; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/InternationalMapWorldPolyconicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/InternationalMapWorldPolyconicProjection.cs new file mode 100644 index 00000000..7179af81 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/InternationalMapWorldPolyconicProjection.cs @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the International Map of the World Polyconic projection (imw_p). +/// +/// +/// The International Map of the World Polyconic projection is the modified +/// polyconic sheet projection adopted for the 1:1,000,000 International Map of the +/// World series. This implementation supports spherical and ellipsoidal forward and +/// inverse forms for the standard IMW quadrangle layout. +/// This implementation matches PROJ's imw_p formulation and Snyder's +/// summary of the Charles Lallemand 1909 modification approved for the IMW program +/// proposed by Albrecht Penck in 1891. The constructor reproduces the IMW sheet +/// logic with two standard parallels and latitude-zone-dependent standard meridians +/// at ±2, ±4, or ±8 degrees, while the forward and inverse paths use meridian-arc +/// evaluation with iterative longitude and latitude recovery. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 4, Sect. 4.3.2, pp. 151-153; see also Ch. 5, Sect. 5.2.1, p. 165. +/// PROJ documentation: International Map of the World Polyconic. +internal sealed class InternationalMapWorldPolyconicProjection : MapProjection +{ + private const int MaximumIterations = 1000; + + private readonly double phi1; + private readonly double phi2; + private readonly double sinPhi1; + private readonly double sinPhi2; + private readonly double r1; + private readonly double r2; + private readonly double c2; + private readonly double p; + private readonly double pp; + private readonly double q; + private readonly double qp; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public InternationalMapWorldPolyconicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public InternationalMapWorldPolyconicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "International_Map_of_the_World_Polyconic"; + + this.phi1 = DegreesToRadians(this.Parameters.GetParameterValue("lat_1", "standard_parallel_1")); + this.phi2 = DegreesToRadians(this.Parameters.GetParameterValue("lat_2", "standard_parallel_2")); + + double del = 0.5d * (this.phi2 - this.phi1); + double sig = 0.5d * (this.phi2 + this.phi1); + if (Math.Abs(del) < Eps10 || Math.Abs(sig) < Eps10) + { + ArgumentGuard.ThrowArgument("Illegal value for lat_1 and lat_2: |lat_1 - lat_2| and |lat_1 + lat_2| should be > 0.", nameof(parameters)); + } + + double lam1 = this.Parameters.ContainsKey("lon_1") + ? DegreesToRadians(this.Parameters.GetParameterValue("lon_1")) + : (Math.Abs(sig * 180d / PI) <= 60d ? 2d * PI / 180d : (Math.Abs(sig * 180d / PI) <= 76d ? 4d * PI / 180d : 8d * PI / 180d)); + + this.sinPhi1 = Math.Sin(this.phi1); + this.sinPhi2 = Math.Sin(this.phi2); + this.r1 = this.phi1 == 0d ? 0d : 1d / (Math.Tan(this.phi1) * Math.Sqrt(1d - (this.es * this.sinPhi1 * this.sinPhi1))); + this.r2 = this.phi2 == 0d ? 0d : 1d / (Math.Tan(this.phi2) * Math.Sqrt(1d - (this.es * this.sinPhi2 * this.sinPhi2))); + double x1 = this.phi1 == 0d ? lam1 : this.r1 * Math.Sin(lam1 * this.sinPhi1); + double y1 = this.phi1 == 0d ? 0d : this.r1 * (1d - Math.Cos(lam1 * this.sinPhi1)); + double x2 = this.phi2 == 0d ? lam1 : this.r2 * Math.Sin(lam1 * this.sinPhi2); + double t2 = this.phi2 == 0d ? 0d : this.r2 * (1d - Math.Cos(lam1 * this.sinPhi2)); + double m1 = this.Mlfn(this.phi1, this.sinPhi1, Math.Cos(this.phi1)); + double m2 = this.Mlfn(this.phi2, this.sinPhi2, Math.Cos(this.phi2)); + double t = m2 - m1; + double s = x2 - x1; + double y2 = Math.Sqrt(Math.Max(0d, (t * t) - (s * s))) + y1; + this.c2 = y2 - t2; + double invT = 1d / t; + this.p = ((m2 * y1) - (m1 * y2)) * invT; + this.q = (y2 - y1) * invT; + this.pp = ((m2 * x1) - (m1 * x2)) * invT; + this.qp = (x2 - x1) * invT; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new InternationalMapWorldPolyconicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + this.ComputeLocalForward(lon, lat, out double xUnit, out double yUnit, out _); + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + double phi = this.phi2; + double lambda = xUnit / Math.Cos(phi); + for (int i = 0; i < MaximumIterations; i++) + { + this.ComputeLocalForward(lambda, phi, out double tx, out double ty, out double yc); + + double denominator = ty - yc; + if (denominator == 0d && Math.Abs(ty - yUnit) > Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + if (denominator != 0d || Math.Abs(ty - yUnit) <= Eps10) + { + if (denominator != 0d) + { + phi = ((phi - this.phi1) * (yUnit - yc) / denominator) + this.phi1; + } + } + + if (tx != 0d && Math.Abs(tx - xUnit) > Eps10) + { + lambda = lambda * xUnit / tx; + } + + if (Math.Abs(tx - xUnit) <= Eps10 && Math.Abs(ty - yUnit) <= Eps10) + { + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + return; + } + } + + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + private void ComputeLocalForward(double lambda, double phi, out double x, out double y, out double yc) + { + if (phi == 0d) + { + x = lambda; + y = 0d; + yc = 0d; + return; + } + + double sinPhi = Math.Sin(phi); + double m = this.Mlfn(phi, sinPhi, Math.Cos(phi)); + double xa = this.pp + (this.qp * m); + double ya = this.p + (this.q * m); + double r = 1d / (Math.Tan(phi) * Math.Sqrt(1d - (this.es * sinPhi * sinPhi))); + double cTerm = (r * r) - (xa * xa); + if (cTerm < 0d) + { + cTerm = 0d; + } + + double c = Math.Sqrt(cTerm); + if (phi < 0d) + { + c = -c; + } + + c += ya - r; + + double xb = lambda; + double yb = this.c2; + if (this.phi2 != 0d) + { + double t = lambda * this.sinPhi2; + xb = this.r2 * Math.Sin(t); + yb = this.c2 + (this.r2 * (1d - Math.Cos(t))); + } + + double xc = lambda; + yc = 0d; + if (this.phi1 != 0d) + { + double t = lambda * this.sinPhi1; + xc = this.r1 * Math.Sin(t); + yc = this.r1 * (1d - Math.Cos(t)); + } + + double d = (xb - xc) / (yb - yc); + double b = xc + (d * (c + r - yc)); + double root = (r * r * (1d + (d * d))) - (b * b); + if (root < 0d) + { + root = 0d; + } + + x = d * Math.Sqrt(root); + if (phi > 0d) + { + x = -x; + } + + x = (b + x) / (1d + (d * d)); + double yRoot = (r * r) - (x * x); + if (yRoot < 0d) + { + yRoot = 0d; + } + + y = Math.Sqrt(yRoot); + if (phi > 0d) + { + y = -y; + } + + y += c + r; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/InterruptedGoodeHomolosineOceanicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/InterruptedGoodeHomolosineOceanicProjection.cs new file mode 100644 index 00000000..38198d80 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/InterruptedGoodeHomolosineOceanicProjection.cs @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the interrupted Goode Homolosine oceanic projection (igh_o). +/// +/// +/// Uses the same sinusoidal/Mollweide blend as the standard Interrupted Goode +/// Homolosine projection, but with interruptions placed over the continental landmasses +/// so that the oceanic regions appear continuous. The projection uses 12 zones arranged +/// in three longitudinal panels per hemisphere. +/// This ocean-centered variant matches PROJ's igh_o definition. It keeps +/// the standard Goode homolosine transition latitude of 40 degrees 44 minutes 11.8 +/// seconds while shifting the lobes to emphasize the continuity of the world's oceans, +/// especially when used with a central longitude near -160 degrees. +/// +/// PROJ documentation: Interrupted Goode Homolosine (Oceanic View). +/// Goode, J.P. (1925): The Homolosine projection. +/// Wikipedia: Goode homolosine projection. +internal sealed class InterruptedGoodeHomolosineOceanicProjection : MapProjection +{ + private const int MollweideIterations = 12; + private const double SeamSlack = 1e-10d; + + private static readonly double PhiBoundary = DegreesToRadians(40d + (44d / 60d) + (11.8d / 3600d)); + + private static readonly double D10 = DegreesToRadians(10d); + private static readonly double D20 = DegreesToRadians(20d); + private static readonly double D40 = DegreesToRadians(40d); + private static readonly double D50 = DegreesToRadians(50d); + private static readonly double D60 = DegreesToRadians(60d); + private static readonly double D90 = DegreesToRadians(90d); + private static readonly double D100 = DegreesToRadians(100d); + private static readonly double D110 = DegreesToRadians(110d); + private static readonly double D130 = DegreesToRadians(130d); + private static readonly double D140 = DegreesToRadians(140d); + private static readonly double D150 = DegreesToRadians(150d); + private static readonly double D160 = DegreesToRadians(160d); + private static readonly double D180 = DegreesToRadians(180d); + + private readonly double dy0; + private readonly ZoneDefinition[] zones; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public InterruptedGoodeHomolosineOceanicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public InterruptedGoodeHomolosineOceanicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Interrupted_Goode_Homolosine_Oceanic_View"; + + MollweideForwardUnit(0d, PhiBoundary, out _, out double mollweideBoundaryY); + this.dy0 = PhiBoundary - mollweideBoundaryY; + + this.zones = + [ + new ZoneDefinition(true, -D140, -D140, this.dy0), // 1 + new ZoneDefinition(true, -D10, -D10, this.dy0), // 2 + new ZoneDefinition(true, D130, D130, this.dy0), // 3 + new ZoneDefinition(false, -D140, -D140, 0d), // 4 + new ZoneDefinition(false, -D10, -D10, 0d), // 5 + new ZoneDefinition(false, D130, D130, 0d), // 6 + new ZoneDefinition(false, -D110, -D110, 0d), // 7 + new ZoneDefinition(false, D20, D20, 0d), // 8 + new ZoneDefinition(false, D150, D150, 0d), // 9 + new ZoneDefinition(true, -D110, -D110, -this.dy0), // 10 + new ZoneDefinition(true, D20, D20, -this.dy0), // 11 + new ZoneDefinition(true, D150, D150, -this.dy0), // 12 + ]; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new InterruptedGoodeHomolosineOceanicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + int zone = DetermineForwardZone(lat, lambda); + ZoneDefinition def = this.zones[zone - 1]; + + double localLambda = lambda - def.Lambda0; + double xUnit = localLambda * Math.Cos(lat); + double yUnit = lat; + if (def.IsMollweide) + { + MollweideForwardUnit(localLambda, lat, out xUnit, out yUnit); + } + + lon = this.SphericalRadius * (xUnit + def.X0); + lat = this.SphericalRadius * (yUnit + def.Y0); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + int zone = DetermineInverseZone(xUnit, yUnit, this.dy0); + if (zone == 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + ZoneDefinition def = this.zones[zone - 1]; + double localX = xUnit - def.X0; + double localY = yUnit - def.Y0; + + double phi = localY; + double cosPhi = Math.Cos(phi); + double lambdaLocal = Math.Abs(cosPhi) <= Eps10 ? 0d : (localX / cosPhi); + if (def.IsMollweide) + { + MollweideInverseUnit(localX, localY, out lambdaLocal, out phi); + } + + double lambda = lambdaLocal + def.Lambda0; + if (!IsInZone(zone, lambda, phi)) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static int DetermineForwardZone(double phi, double lambda) + { + if (phi >= PhiBoundary) + { + return lambda <= -D90 ? 1 : lambda >= D60 ? 3 : 2; + } + + if (phi >= 0d) + { + return lambda <= -D90 ? 4 : lambda >= D60 ? 6 : 5; + } + + if (phi >= -PhiBoundary) + { + return lambda <= -D60 ? 7 : lambda >= D90 ? 9 : 8; + } + + return lambda <= -D60 ? 10 : lambda >= D90 ? 12 : 11; + } + + private static int DetermineInverseZone(double x, double y, double dy0) + { + double y90 = dy0 + ProjectionConstants.Sqrt2; + if (y > y90 + SeamSlack || y < -y90 - SeamSlack) + { + return 0; + } + + if (y >= PhiBoundary) + { + return x <= -D90 ? 1 : x >= D60 ? 3 : 2; + } + + if (y >= 0d) + { + return x <= -D90 ? 4 : x >= D60 ? 6 : 5; + } + + if (y >= -PhiBoundary) + { + return x <= -D60 ? 7 : x >= D90 ? 9 : 8; + } + + return x <= -D60 ? 10 : x >= D90 ? 12 : 11; + } + + private static bool IsInZone(int zone, double lambda, double phi) + { + return zone switch + { + 1 => (lambda >= -D180 - SeamSlack && lambda <= -D90 + SeamSlack) + || (lambda >= D160 - SeamSlack && lambda <= D180 + SeamSlack && phi >= D50 - SeamSlack && phi <= D90 + SeamSlack), + 2 => lambda >= -D90 - SeamSlack && lambda <= D60 + SeamSlack, + 3 => (lambda >= D60 - SeamSlack && lambda <= D180 + SeamSlack) + || (lambda >= -D180 - SeamSlack && lambda <= -D160 + SeamSlack && phi >= D50 - SeamSlack && phi <= D90 + SeamSlack), + 4 => lambda >= -D180 - SeamSlack && lambda <= -D90 + SeamSlack, + 5 => lambda >= -D90 - SeamSlack && lambda <= D60 + SeamSlack, + 6 => lambda >= D60 - SeamSlack && lambda <= D180 + SeamSlack, + 7 => lambda >= -D180 - SeamSlack && lambda <= -D60 + SeamSlack, + 8 => lambda >= -D60 - SeamSlack && lambda <= D90 + SeamSlack, + 9 => lambda >= D90 - SeamSlack && lambda <= D180 + SeamSlack, + 10 => lambda >= -D180 - SeamSlack && lambda <= -D60 + SeamSlack, + 11 => (lambda >= -D60 - SeamSlack && lambda <= D90 + SeamSlack) + || (lambda >= D90 - SeamSlack && lambda <= D100 + SeamSlack && phi >= -D90 - SeamSlack && phi <= -D40 + SeamSlack), + 12 => lambda >= D90 - SeamSlack && lambda <= D180 + SeamSlack, + _ => false, + }; + } + + private static void MollweideForwardUnit(double lambda, double phi, out double x, out double y) + { + double theta = Sign(phi) * HalfPi; + if (Math.Abs(Math.Abs(phi) - HalfPi) >= ProjectionConstants.Tolerance1E12) + { + theta = phi; + double target = PI * Math.Sin(phi); + for (int i = 0; i < MollweideIterations; i++) + { + double twoTheta = 2d * theta; + double delta = ((twoTheta + Math.Sin(twoTheta)) - target) / (2d + (2d * Math.Cos(twoTheta))); + theta -= delta; + if (Math.Abs(delta) < ProjectionConstants.Tolerance1E12) + { + break; + } + } + } + + x = (2d * ProjectionConstants.Sqrt2 / PI) * lambda * Math.Cos(theta); + y = ProjectionConstants.Sqrt2 * Math.Sin(theta); + } + + private static void MollweideInverseUnit(double x, double y, out double lambda, out double phi) + { + double theta = Math.Asin(ProjectionConstants.Clamp(y / ProjectionConstants.Sqrt2, -1d, 1d)); + double cosTheta = Math.Cos(theta); + lambda = Math.Abs(cosTheta) <= Eps10 ? 0d : (x * PI / (2d * ProjectionConstants.Sqrt2 * cosTheta)); + phi = Math.Asin(ProjectionConstants.Clamp(((2d * theta) + Math.Sin(2d * theta)) / PI, -1d, 1d)); + } + + private readonly struct ZoneDefinition(bool isMollweide, double lambda0, double x0, double y0) + { + public bool IsMollweide { get; } = isMollweide; + + public double Lambda0 { get; } = lambda0; + + public double X0 { get; } = x0; + + public double Y0 { get; } = y0; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideBaseProjection.cs b/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideBaseProjection.cs new file mode 100644 index 00000000..064f4639 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideBaseProjection.cs @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Provides shared Mollweide helper logic for interrupted Mollweide-family projections. +/// +internal abstract class InterruptedMollweideBaseProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + protected InterruptedMollweideBaseProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + } + + /// + /// Determines whether a longitude/latitude pair is valid for the specified interrupted zone. + /// + /// 1-based zone index. + /// Longitude in radians in the projection's local domain. + /// Latitude in radians in the projection's local domain. + /// Zone longitude ranges and hemisphere constraints. + /// when the coordinate is inside the zone envelope; otherwise . + protected static bool IsInZone(int zone, double lambda, double phi, (double MinLambda, double MaxLambda, bool IsNorthernHemisphere)[] zoneEnvelopes) + { + const double seamSlack = 1e-10d; + + if (zone < 1 || zone > zoneEnvelopes.Length) + { + return false; + } + + (double minLambda, double maxLambda, bool isNorthernHemisphere) = zoneEnvelopes[zone - 1]; + bool latitudeInRange = isNorthernHemisphere + ? phi >= 0d - seamSlack + : phi <= 0d + seamSlack; + + return latitudeInRange + && lambda >= minLambda - seamSlack + && lambda <= maxLambda + seamSlack; + } + + /// + /// Projects local Mollweide input coordinates into unit-space coordinates. + /// + /// Longitude offset from zone central meridian, in radians. + /// Latitude in radians. + /// Projected unit-space x coordinate. + /// Projected unit-space y coordinate. + protected static void MollweideForwardUnit(double lambda, double phi, out double x, out double y) + { + const int mollweideIterations = 12; + double theta = Sign(phi) * HalfPi; + if (Math.Abs(Math.Abs(phi) - HalfPi) >= ProjectionConstants.Tolerance1E12) + { + theta = phi; + double target = PI * Math.Sin(phi); + for (int i = 0; i < mollweideIterations; i++) + { + double twoTheta = 2d * theta; + double delta = ((twoTheta + Math.Sin(twoTheta)) - target) / (2d + (2d * Math.Cos(twoTheta))); + theta -= delta; + if (Math.Abs(delta) < ProjectionConstants.Tolerance1E12) + { + break; + } + } + } + + x = (2d * ProjectionConstants.Sqrt2 / PI) * lambda * Math.Cos(theta); + y = ProjectionConstants.Sqrt2 * Math.Sin(theta); + } + + /// + /// Inverts unit-space Mollweide coordinates to local longitude/latitude. + /// + /// Projected unit-space x coordinate. + /// Projected unit-space y coordinate. + /// Recovered local longitude offset from zone central meridian, in radians. + /// Recovered latitude in radians. + protected static void MollweideInverseUnit(double x, double y, out double lambda, out double phi) + { + double theta = Math.Asin(ProjectionConstants.Clamp(y / ProjectionConstants.Sqrt2, -1d, 1d)); + double cosTheta = Math.Cos(theta); + lambda = Math.Abs(cosTheta) <= Eps10 ? 0d : (x * PI / (2d * ProjectionConstants.Sqrt2 * cosTheta)); + phi = Math.Asin(ProjectionConstants.Clamp(((2d * theta) + Math.Sin(2d * theta)) / PI, -1d, 1d)); + } + + /// + /// Computes zone-local Mollweide forward coordinates including per-zone offsets. + /// + /// Zone definition array. + /// 1-based zone index. + /// Longitude in radians. + /// Latitude in radians. + /// Resulting zone-relative x coordinate. + /// Resulting zone-relative y coordinate. + protected static void MollweideForward(IReadOnlyList zones, int zone, double lambda, double phi, out double x, out double y) + { + MollweideZoneDefinition def = zones[zone - 1]; + MollweideForwardUnit(lambda - def.Lambda0, phi, out double xUnit, out double yUnit); + x = xUnit + def.X0; + y = yUnit + def.Y0; + } + + /// + /// Computes the seam boundary x-position between adjacent zones. + /// + /// Zone definition array. + /// Forward zone selection function. + /// Seam longitude in radians. + /// Latitude in radians. + /// Average x-position across both seam sides. + protected static double ComputeZoneBoundaryX(IReadOnlyList zones, Func determineForwardZone, double lambda, double phi) + { + const double seamSlack = 1e-10d; + + MollweideForward(zones, determineForwardZone(phi, lambda - seamSlack), lambda - seamSlack, phi, out double x1, out _); + MollweideForward(zones, determineForwardZone(phi, lambda + seamSlack), lambda + seamSlack, phi, out double x2, out _); + return (x1 + x2) * 0.5d; + } + + /// + /// Computes x-offset alignment between two zones at the given seam sample points. + /// + /// Zone definition array. + /// First 1-based zone index. + /// Second 1-based zone index. + /// Seam longitude sample in radians. + /// Latitude sample for zone 1 in radians. + /// Latitude sample for zone 2 in radians. + /// The x-offset δ that aligns zone 1 with zone 2 at the seam. + protected static double ComputeZoneOffset(IReadOnlyList zones, int zone1, int zone2, double lambda, double phi1, double phi2) + { + MollweideForward(zones, zone1, lambda, phi1, out double x1, out _); + MollweideForward(zones, zone2, lambda, phi2, out double x2, out _); + return x2 - x1; + } + + /// + /// Represents one interrupted Mollweide zone definition. + /// + protected sealed class MollweideZoneDefinition + { + /// + /// Initializes a new instance of the class. + /// + /// Zone x-offset in unit-space coordinates. + /// Zone central meridian in radians. + /// Zone y-offset in unit-space coordinates. + public MollweideZoneDefinition(double x0, double lambda0, double y0) + { + this.X0 = x0; + this.Lambda0 = lambda0; + this.Y0 = y0; + } + + /// + /// Gets or sets the zone x-offset in unit-space coordinates. + /// + public double X0 { get; set; } + + /// + /// Gets the zone central meridian in radians. + /// + public double Lambda0 { get; } + + /// + /// Gets the zone y-offset in unit-space coordinates. + /// + public double Y0 { get; } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideOceanicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideOceanicProjection.cs new file mode 100644 index 00000000..e2e742bf --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideOceanicProjection.cs @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the interrupted Mollweide Oceanic projection (imoll_o). +/// +/// +/// The interrupted Mollweide Oceanic projection is a six-lobe equal-area +/// Mollweide variant arranged to keep the major ocean basins visually continuous. In +/// contrast with the interrupted Goode homolosine oceanic projection, it keeps the +/// Mollweide construction at all latitudes and therefore omits the sinusoidal transition +/// latitude. +/// This implementation matches PROJ's imoll_o definition for the +/// ocean-centered interrupted Mollweide arrangement, also attributed to J. P. Goode's +/// 1919 interrupted homolographic work. The six zone definitions encode the standard +/// oceanic interruption pattern recommended for central longitude near -160 degrees. +/// +/// PROJ documentation: Interrupted Mollweide Oceanic View. +/// Wikipedia: Mollweide projection. +internal sealed class InterruptedMollweideOceanicProjection : InterruptedMollweideBaseProjection +{ + private const double SeamSlack = 1e-10d; + + private static readonly double D10 = DegreesToRadians(10d); + private static readonly double D20 = DegreesToRadians(20d); + private static readonly double D60 = DegreesToRadians(60d); + private static readonly double D90 = DegreesToRadians(90d); + private static readonly double D110 = DegreesToRadians(110d); + private static readonly double D130 = DegreesToRadians(130d); + private static readonly double D140 = DegreesToRadians(140d); + private static readonly double D150 = DegreesToRadians(150d); + private static readonly double D180 = DegreesToRadians(180d); + + private readonly MollweideZoneDefinition[] zones; + private readonly double boundary12; + private readonly double boundary23; + private readonly double boundary45; + private readonly double boundary56; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public InterruptedMollweideOceanicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public InterruptedMollweideOceanicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Interrupted_Mollweide_Oceanic_View"; + + this.zones = + [ + new MollweideZoneDefinition(-D140, -D140, 0d), // 1 + new MollweideZoneDefinition(-D10, -D10, 0d), // 2 + new MollweideZoneDefinition(D130, D130, 0d), // 3 + new MollweideZoneDefinition(-D110, -D110, 0d), // 4 + new MollweideZoneDefinition(D20, D20, 0d), // 5 + new MollweideZoneDefinition(D150, D150, 0d), // 6 + ]; + + this.zones[1].X0 += ComputeZoneOffset(this.zones, 2, 1, -D90, 0d + SeamSlack, 0d + SeamSlack); + this.zones[2].X0 += ComputeZoneOffset(this.zones, 3, 2, D60, 0d + SeamSlack, 0d + SeamSlack); + this.zones[3].X0 += ComputeZoneOffset(this.zones, 4, 1, -D180, 0d - SeamSlack, 0d + SeamSlack); + this.zones[4].X0 += ComputeZoneOffset(this.zones, 5, 2, -D60, 0d - SeamSlack, 0d + SeamSlack); + this.zones[5].X0 += ComputeZoneOffset(this.zones, 6, 3, D90, 0d - SeamSlack, 0d + SeamSlack); + + this.boundary12 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, -D90, 0d + SeamSlack); + this.boundary23 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, D60, 0d + SeamSlack); + this.boundary45 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, -D60, 0d - SeamSlack); + this.boundary56 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, D90, 0d - SeamSlack); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new InterruptedMollweideOceanicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + int zone = DetermineForwardZone(lat, lambda); + MollweideZoneDefinition def = this.zones[zone - 1]; + MollweideForwardUnit(lambda - def.Lambda0, lat, out double xUnit, out double yUnit); + lon = this.SphericalRadius * (xUnit + def.X0); + lat = this.SphericalRadius * (yUnit + def.Y0); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + int zone = DetermineInverseZone(xUnit, yUnit, this.boundary12, this.boundary23, this.boundary45, this.boundary56); + if (zone == 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + MollweideZoneDefinition def = this.zones[zone - 1]; + MollweideInverseUnit(xUnit - def.X0, yUnit - def.Y0, out double lambdaLocal, out double phi); + double lambda = lambdaLocal + def.Lambda0; + if (!IsInZone( + zone, + lambda, + phi, + [ + (-D180, -D90, true), + (-D90, D60, true), + (D60, D180, true), + (-D180, -D60, false), + (-D60, D90, false), + (D90, D180, false), + ])) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static int DetermineForwardZone(double phi, double lambda) + { + if (phi >= 0d) + { + return lambda <= -D90 ? 1 : lambda >= D60 ? 3 : 2; + } + + return lambda <= -D60 ? 4 : lambda >= D90 ? 6 : 5; + } + + private static int DetermineInverseZone( + double x, + double y, + double seam12, + double seam23, + double seam45, + double seam56) + { + double y90 = ProjectionConstants.Sqrt2; + if (y > y90 + SeamSlack || y < -y90 - SeamSlack) + { + return 0; + } + + if (y >= 0d) + { + return x <= seam12 ? 1 : x >= seam23 ? 3 : 2; + } + + return x <= seam45 ? 4 : x >= seam56 ? 6 : 5; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideProjection.cs b/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideProjection.cs new file mode 100644 index 00000000..260fd8a6 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/InterruptedMollweideProjection.cs @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the interrupted Mollweide projection (imoll). +/// +/// +/// The interrupted Mollweide projection is an equal-area pseudocylindrical world +/// map formed from six separate Mollweide lobes. Unlike the Goode homolosine family it +/// does not switch to a sinusoidal formula at low latitudes, so it offers more lobe +/// continuity at the cost of greater equatorial distortion. +/// This implementation matches PROJ's imoll definition for the land-focused +/// interrupted Mollweide arrangement first published by J. P. Goode in 1919. The six +/// zone definitions and seam boundaries encode the standard land-oriented interruption +/// pattern. +/// +/// PROJ documentation: Interrupted Mollweide. +/// Wikipedia: Mollweide projection. +internal sealed class InterruptedMollweideProjection : InterruptedMollweideBaseProjection +{ + private const double SeamSlack = 1e-10d; + + private static readonly double D20 = DegreesToRadians(20d); + private static readonly double D30 = DegreesToRadians(30d); + private static readonly double D40 = DegreesToRadians(40d); + private static readonly double D60 = DegreesToRadians(60d); + private static readonly double D80 = DegreesToRadians(80d); + private static readonly double D100 = DegreesToRadians(100d); + private static readonly double D140 = DegreesToRadians(140d); + private static readonly double D160 = DegreesToRadians(160d); + private static readonly double D180 = DegreesToRadians(180d); + + private readonly MollweideZoneDefinition[] zones; + private readonly double boundary12; + private readonly double boundary34; + private readonly double boundary45; + private readonly double boundary56; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public InterruptedMollweideProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public InterruptedMollweideProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Interrupted_Mollweide"; + + this.zones = + [ + new MollweideZoneDefinition(-D100, -D100, 0d), // 1 + new MollweideZoneDefinition(D30, D30, 0d), // 2 + new MollweideZoneDefinition(-D160, -D160, 0d), // 3 + new MollweideZoneDefinition(-D60, -D60, 0d), // 4 + new MollweideZoneDefinition(D20, D20, 0d), // 5 + new MollweideZoneDefinition(D140, D140, 0d), // 6 + ]; + + this.zones[2].X0 += ComputeZoneOffset(this.zones, 3, 1, -D160, 0d - SeamSlack, 0d + SeamSlack); + this.zones[1].X0 += ComputeZoneOffset(this.zones, 2, 1, -D40, 0d + SeamSlack, 0d + SeamSlack); + this.zones[3].X0 += ComputeZoneOffset(this.zones, 4, 1, -D100, 0d - SeamSlack, 0d + SeamSlack); + this.zones[4].X0 += ComputeZoneOffset(this.zones, 5, 2, -D20, 0d - SeamSlack, 0d + SeamSlack); + this.zones[5].X0 += ComputeZoneOffset(this.zones, 6, 2, D80, 0d - SeamSlack, 0d + SeamSlack); + + this.boundary12 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, -D40, 0d + SeamSlack); + this.boundary34 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, -D100, 0d - SeamSlack); + this.boundary45 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, -D20, 0d - SeamSlack); + this.boundary56 = ComputeZoneBoundaryX(this.zones, DetermineForwardZone, D80, 0d - SeamSlack); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new InterruptedMollweideProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + int zone = DetermineForwardZone(lat, lambda); + MollweideZoneDefinition def = this.zones[zone - 1]; + MollweideForwardUnit(lambda - def.Lambda0, lat, out double xUnit, out double yUnit); + lon = this.SphericalRadius * (xUnit + def.X0); + lat = this.SphericalRadius * (yUnit + def.Y0); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + int zone = DetermineInverseZone(xUnit, yUnit, this.boundary12, this.boundary34, this.boundary45, this.boundary56); + if (zone == 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + MollweideZoneDefinition def = this.zones[zone - 1]; + MollweideInverseUnit(xUnit - def.X0, yUnit - def.Y0, out double lambdaLocal, out double phi); + double lambda = lambdaLocal + def.Lambda0; + if (!IsInZone( + zone, + lambda, + phi, + [ + (-D180, -D40, true), + (-D40, D180, true), + (-D180, -D100, false), + (-D100, -D20, false), + (-D20, D80, false), + (D80, D180, false), + ])) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static int DetermineForwardZone(double phi, double lambda) + { + if (phi >= 0d) + { + return lambda <= -D40 ? 1 : 2; + } + + if (lambda <= -D100) + { + return 3; + } + + return lambda <= -D20 ? 4 : lambda <= D80 ? 5 : 6; + } + + private static int DetermineInverseZone( + double x, + double y, + double seam12, + double seam34, + double seam45, + double seam56) + { + double y90 = ProjectionConstants.Sqrt2; + if (y > y90 + SeamSlack || y < -y90 - SeamSlack) + { + return 0; + } + + if (y >= 0d) + { + return x <= seam12 ? 1 : 2; + } + + if (x <= seam34) + { + return 3; + } + + return x <= seam45 ? 4 : x <= seam56 ? 5 : 6; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/IseaProjection.Types.cs b/src/ProjNet/CoordinateSystems/Projections/IseaProjection.Types.cs new file mode 100644 index 00000000..5ead7394 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/IseaProjection.Types.cs @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; + +/// +/// Nested geometry and inverse-projection helper types for the ISEA projection. +/// +internal sealed partial class IseaProjection +{ + private enum IseaOutputMode + { + Plane, + Di, + Dd, + Hex, + } + + private readonly struct GeoPoint(double lat, double lon) + { + public double Lat { get; } = lat; + + public double Lon { get; } = lon; + } + + private readonly struct IseaSinCos(double sin, double cos) + { + public double Sin { get; } = sin; + + public double Cos { get; } = cos; + } + + private struct IseaPoint(double x, double y) + { + public double X = x; + + public double Y = y; + } + + private sealed class IseaPlanarState + { + public IseaPlanarState( + double r2, + double rPrime2X, + double rPrimeTang, + double rPrime2Tan2g, + double centerToBase, + double triangleWidth, + double[] yOffsets, + double xOffset, + double yOffset, + double scaleX, + double scaleY) + { + this.R2 = r2; + this.RPrime2X = rPrime2X; + this.RPrimeTang = rPrimeTang; + this.RPrime2Tan2g = rPrime2Tan2g; + this.CenterToBase = centerToBase; + this.TriangleWidth = triangleWidth; + this.YOffsets = yOffsets; + this.XOffset = xOffset; + this.YOffset = yOffset; + this.ScaleX = scaleX; + this.ScaleY = scaleY; + } + + public double R2 { get; } + + public double RPrime2X { get; } + + public double RPrimeTang { get; } + + public double RPrime2Tan2g { get; } + + public double CenterToBase { get; } + + public double TriangleWidth { get; } + + public double[] YOffsets { get; } + + public double XOffset { get; } + + public double YOffset { get; } + + public double ScaleX { get; } + + public double ScaleY { get; } + } + + private sealed class IseaPlanarInverseProjection + { + private readonly double orientationLatitude; + private readonly double orientationLongitude; + private readonly double cosOrientationLatitude; + private readonly double sinOrientationLatitude; + + public IseaPlanarInverseProjection(double orientationLatitude, double orientationLongitude) + { + this.orientationLatitude = orientationLatitude; + this.orientationLongitude = orientationLongitude; + this.cosOrientationLatitude = Math.Cos(orientationLatitude); + this.sinOrientationLatitude = Math.Sin(orientationLatitude); + } + + public bool TryCartesianToGeo( + double inputX, + double inputY, + IseaPlanarState state, + IseaSinCos[] vertexLatSinCos, + out GeoPoint result) + { + const double epsilon = 1e-11d; + int face = 0; + double positionX = inputX; + double positionY = inputY; + + const double sr = -Sin60; + const double cr = 0.5d; + + if (positionX < 0d + || (positionX < (state.TriangleWidth * 0.5d) + && positionY < 0d + && ((positionY * cr) < (positionX * sr)))) + { + positionX += 5d * state.TriangleWidth; + } + + double yp = -((positionX * sr) + (positionY * cr)); + double x = ((positionX * cr) - (positionY * sr) + (yp * (1d / Sqrt3))) * state.ScaleX; + double y = yp * state.ScaleY; + + if (x < 0d || (y > x && x < 5d - epsilon)) + { + x += epsilon; + } + else if (x > 5d || (y < x && x > epsilon)) + { + x -= epsilon; + } + + if (y < 0d || (x > y && y < 6d - epsilon)) + { + y += epsilon; + } + else if (y > 6d || (x < y && y > epsilon)) + { + y -= epsilon; + } + + if (x >= 0d && x <= 5d && y >= 0d && y <= 6d) + { + int ix = ClampInt((int)x, 0, 4); + int iy = ClampInt((int)y, 0, 5); + + if (iy == ix || iy == ix + 1) + { + int rhombus = ix + iy; + bool top = (x - ix) > (y - iy); + face = rhombus switch + { + 0 => top ? 0 : 5, + 2 => top ? 1 : 6, + 4 => top ? 2 : 7, + 6 => top ? 3 : 8, + 8 => top ? 4 : 9, + 1 => top ? 10 : 15, + 3 => top ? 11 : 16, + 5 => top ? 12 : 17, + 7 => top ? 13 : 18, + 9 => top ? 14 : 19, + _ => -1, + }; + face++; + } + } + + if (face == 0) + { + result = default; + return false; + } + + int faceIndex = face - 1; + int fy = faceIndex / 5; + int fx = faceIndex - (5 * fy); + + // Match PROJ integer arithmetic: fy/2 is integer division in the source implementation. + double rx = positionX - (((2d * fx) + (fy / 2) + 1d) * state.TriangleWidth * 0.5d); + double ry = positionY - (state.YOffsets[fy] + (3d * state.CenterToBase)); + + if (!this.TryIcosahedronToSphere(faceIndex, rx, ry, state, vertexLatSinCos, out GeoPoint dst)) + { + result = default; + return false; + } + + double lon = dst.Lon; + if (lon < -PI - epsilon) + { + lon += TwoPi; + } + else if (lon > PI + epsilon) + { + lon -= TwoPi; + } + + result = new GeoPoint(dst.Lat, lon); + return true; + } + + private static double FaceOrientation(int face) + { + return (face <= 4 || (face >= 10 && face <= 14)) ? 0d : PI; + } + + private bool TryIcosahedronToSphere( + int face, + double x, + double y, + IseaPlanarState state, + IseaSinCos[] vertexLatSinCos, + out GeoPoint result) + { + if (face < 0 || face >= NumIcosahedronFaces) + { + result = default; + return false; + } + + double az = Math.Atan2(x, y); + double rho = Math.Sqrt((x * x) + (y * y)); + double azAdjustment = FaceOrientation(face); + + az += azAdjustment; + while (az < 0d) + { + azAdjustment += AzMax; + az += AzMax; + } + + while (az > AzMax) + { + azAdjustment -= AzMax; + az -= AzMax; + } + + double sinAz = Math.Sin(az); + double cosAz = Math.Cos(az); + double cotAz = cosAz / sinAz; + double area = state.RPrime2Tan2g / (2d * (cotAz + CotTheta)); + double deltaAz = 10d * Precision; + double degAreaOverR2Plus180Minus36 = (area / state.R2) - WestVertexLon; + double azEarth = az; + + while (Math.Abs(deltaAz) > Precision) + { + double sinAzEarth = Math.Sin(azEarth); + double cosAzEarth = Math.Cos(azEarth); + double h = Math.Acos(ProjectionConstants.Clamp((sinAzEarth * SinGcosSdc2VoS) - (cosAzEarth * CosG), -1d, 1d)); + double function = degAreaOverR2Plus180Minus36 - h - azEarth; + double derivativeDenominator = Math.Sin(h); + if (Math.Abs(derivativeDenominator) <= Eps10) + { + result = default; + return false; + } + + double derivative = (((cosAzEarth * SinGcosSdc2VoS) + (sinAzEarth * CosG)) / derivativeDenominator) - 1d; + if (Math.Abs(derivative) <= Eps10) + { + result = default; + return false; + } + + deltaAz = -function / derivative; + azEarth += deltaAz; + } + + double sinAzEarthFinal = Math.Sin(azEarth); + double cosAzEarthFinal = Math.Cos(azEarth); + double q = Math.Atan2(Tang, cosAzEarthFinal + (sinAzEarthFinal * CotTheta)); + double denominator = Math.Cos(az) + (Math.Sin(az) * CotTheta); + if (Math.Abs(denominator) <= Eps10) + { + result = default; + return false; + } + + double d = state.RPrimeTang / denominator; + double sinQHalf = Math.Sin(q * 0.5d); + if (Math.Abs(sinQHalf) <= Eps10) + { + result = default; + return false; + } + + double f = d / (state.RPrime2X * sinQHalf); + if (Math.Abs(f) <= Eps10) + { + result = default; + return false; + } + + double z = 2d * Math.Asin(ProjectionConstants.Clamp(rho / (state.RPrime2X * f), -1d, 1d)); + + azEarth -= azAdjustment; + + IseaSinCos latSinCos = vertexLatSinCos[face]; + double sinLat0 = latSinCos.Sin; + double cosLat0 = latSinCos.Cos; + double sinZ = Math.Sin(z); + double cosZ = Math.Cos(z); + double cosLat0SinZ = cosLat0 * sinZ; + double latSin = (sinLat0 * cosZ) + (cosLat0SinZ * Math.Cos(azEarth)); + double lat = SafeArcSin(latSin); + double lon = FacesCenterDodecahedronVertices[face].Lon + + Math.Atan2( + Math.Sin(azEarth) * cosLat0SinZ, + cosZ - (sinLat0 * Math.Sin(lat))); + + this.RevertOrientation(new GeoPoint(lat, lon), out result); + return true; + } + + private void RevertOrientation(in GeoPoint point, out GeoPoint result) + { + double lon = (point.Lat < (-HalfPi + PrecisionPerDefinition) + || point.Lat > (HalfPi - PrecisionPerDefinition)) + ? 0d + : point.Lon; + + if (this.orientationLatitude != 0d || this.orientationLongitude != 0d) + { + double sinLat = Math.Sin(point.Lat); + double cosLat = Math.Cos(point.Lat); + double sinLon = Math.Sin(lon); + double cosLon = Math.Cos(lon); + double cosLonCosLat = cosLon * cosLat; + double orientedLon = Math.Atan2( + sinLon * cosLat, + (cosLonCosLat * this.cosOrientationLatitude) + (sinLat * this.sinOrientationLatitude)) + - this.orientationLongitude; + + result = new GeoPoint( + Math.Asin((sinLat * this.cosOrientationLatitude) - (cosLonCosLat * this.sinOrientationLatitude)), + orientedLon); + } + else + { + result = new GeoPoint(point.Lat, lon); + } + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/IseaProjection.cs b/src/ProjNet/CoordinateSystems/Projections/IseaProjection.cs new file mode 100644 index 00000000..5e585f37 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/IseaProjection.cs @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Icosahedral Snyder Equal Area projection (isea). +/// +/// +/// The Icosahedral Snyder Equal Area projection distributes the globe over the +/// twenty faces of an icosahedron and applies a modified Lambert azimuthal equal-area +/// construction within each triangular face. The inverse projection follows PROJ's +/// current planar implementation and is therefore available for the supported +/// +proj=isea / +orient=pole planar subset with zero azimuth, +/// aperture 3, and resolution 4. +/// The formulation was independently verified against John P. Snyder, +/// "An equal-area map projection for polyhedral globes," Cartographica, +/// vol. 29, no. 1, pp. 10-21, 1992. The face subdivision into isosceles triangles and +/// the Snyder forward equations used within each face match the implementation here. +/// +/// Wikipedia: Snyder equal-area projection. +internal sealed partial class IseaProjection : MapProjection +{ + private const int NumIcosahedronFaces = 20; + + private const double DegToRad = PI / 180d; + private const double Deg120 = 2.09439510239319549229d; + private const double Deg180 = PI; + + private const double ERad = 0.91843818701052843323d; + private const double FRad = 0.18871053078356206978d; + + private const double Sdc2VoS = 0.6523581397843681859886783d; + private const double Tang = 0.76393202250021030358019673567d; + private const double Tan30 = 0.57735026918962576450914878d; + private const double CotTheta = 1d / Tan30; + + private const double CosG = 0.80901699437494742410229341718281905886d; + private const double SinG = 0.587785252292473129168705954639072768597652d; + private const double CosSdc2VoS = 0.7946544722917661229596057297879189448539d; + private const double SinGcosSdc2VoS = SinG * CosSdc2VoS; + + private const double Sqrt3 = 1.73205080756887729352744634150587236694280525381038d; + private const double Sin60 = Sqrt3 / 2d; + + private const double TableG = Tang * Sin60; + private const double RPrimeOverR = 0.9103832815095032d; + private const double TableH = 0.25d * Tang; + + private const double IseaStdLat = 1.01722196792335072101d; + private const double IseaStdLon = 0.19634954084936207740d; + + private const double StandardInverseOrientationLat = (ERad + FRad) * 0.5d; + private const double StandardInverseOrientationLon = -11.25d * DegToRad; + + private const double Precision = DegToRad * 1e-11d; + private const double PrecisionPerDefinition = DegToRad * 1e-5d; + private const double AzMax = 120d * DegToRad; + private const double WestVertexLon = -144d * DegToRad; + + private const double SafeArcEpsilon = 1e-15d; + + private const int ModePlane = 0; + private const int ModeDi = 1; + private const int ModeDd = 2; + private const int ModeHex = 3; + + private const int OrientIsea = 0; + private const int OrientPole = 1; + + private static readonly GeoPoint[] FacesCenterDodecahedronVertices = + [ + new GeoPoint(ERad, -144d * DegToRad), + new GeoPoint(ERad, -72d * DegToRad), + new GeoPoint(ERad, 0d * DegToRad), + new GeoPoint(ERad, 72d * DegToRad), + new GeoPoint(ERad, 144d * DegToRad), + new GeoPoint(FRad, -144d * DegToRad), + new GeoPoint(FRad, -72d * DegToRad), + new GeoPoint(FRad, 0d * DegToRad), + new GeoPoint(FRad, 72d * DegToRad), + new GeoPoint(FRad, 144d * DegToRad), + new GeoPoint(-FRad, -108d * DegToRad), + new GeoPoint(-FRad, -36d * DegToRad), + new GeoPoint(-FRad, 36d * DegToRad), + new GeoPoint(-FRad, 108d * DegToRad), + new GeoPoint(-FRad, 180d * DegToRad), + new GeoPoint(-ERad, -108d * DegToRad), + new GeoPoint(-ERad, -36d * DegToRad), + new GeoPoint(-ERad, 36d * DegToRad), + new GeoPoint(-ERad, 108d * DegToRad), + new GeoPoint(-ERad, 180d * DegToRad), + ]; + + private readonly IseaOutputMode outputMode; + private readonly int aperture; + private readonly int resolution; + private readonly double orientationLatitude; + private readonly double orientationLongitude; + private readonly double orientationAzimuth; + private readonly IseaSinCos[] vertexLatSinCos = new IseaSinCos[NumIcosahedronFaces]; + [field: NonSerialized] + private readonly IseaPlanarState planarState; + [field: NonSerialized] + private readonly IseaPlanarInverseProjection? planarInverseProjection; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public IseaProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public IseaProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Icosahedral_Snyder_Equal_Area"; + + int orientationCode = ReadDiscreteCode( + this.Parameters.GetOptionalParameterValue("isea_orient", OrientIsea, "orient"), + "orient", + nameof(parameters)); + switch (orientationCode) + { + case OrientIsea: + this.orientationLatitude = IseaStdLat; + this.orientationLongitude = IseaStdLon; + break; + case OrientPole: + this.orientationLatitude = HalfPi; + this.orientationLongitude = 0d; + break; + default: + ArgumentGuard.ThrowArgument("Invalid value for orient: only isea or pole are supported.", nameof(parameters)); + break; + } + + this.orientationAzimuth = DegreesToRadians( + this.Parameters.GetOptionalParameterValue("isea_azimuth", 0d, "isea_o_az", "isea_az", "azi")); + + this.aperture = ReadDiscreteCode( + this.Parameters.GetOptionalParameterValue("isea_aperture", 3d, "aperture"), + "aperture", + nameof(parameters)); + this.resolution = ReadDiscreteCode( + this.Parameters.GetOptionalParameterValue("isea_resolution", 4d, "resolution"), + "resolution", + nameof(parameters)); + + int modeCode = ReadDiscreteCode( + this.Parameters.GetOptionalParameterValue("isea_mode", ModePlane, "mode"), + "mode", + nameof(parameters)); + this.outputMode = modeCode switch + { + ModePlane => IseaOutputMode.Plane, + ModeDi => IseaOutputMode.Di, + ModeDd => IseaOutputMode.Dd, + ModeHex => IseaOutputMode.Hex, + _ => ArgumentGuard.ThrowArgument("Invalid value for mode: only plane, di, dd or hex are supported.", nameof(parameters)), + }; + + if (this.outputMode != IseaOutputMode.Plane) + { + ProjectionThrowHelper.ThrowNotSupported("ISEA mode is not supported in this wave. Only plane mode is currently implemented."); + } + + for (int i = 0; i < NumIcosahedronFaces; i++) + { + GeoPoint center = FacesCenterDodecahedronVertices[i]; + this.vertexLatSinCos[i] = new IseaSinCos(Math.Sin(center.Lat), Math.Cos(center.Lat)); + } + + this.planarState = this.CreatePlanarState(); + this.planarInverseProjection = this.TryCreatePlanarInverseProjection(); + } + + /// + protected override bool HasInverseSupport => this.planarInverseProjection is not null; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new IseaProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + var input = new GeoPoint(lat, lon); + int triangle = this.IseaTransform(input, out IseaPoint projected); + + IseaTriPlane(triangle, ref projected); + + lon = projected.X * this.SphericalRadius; + lat = projected.Y * this.SphericalRadius; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + if (this.planarInverseProjection is null) + { + throw new InvalidOperationException("ISEA does not support inverse projection for this parameter set in this wave."); + } + + double normalizedX = x * this.InverseSphericalRadius; + double normalizedY = y * this.InverseSphericalRadius; + + double shiftedX = normalizedX + this.planarState.XOffset; + double shiftedY = normalizedY + this.planarState.YOffset; + + if (!this.planarInverseProjection.TryCartesianToGeo( + shiftedX, + shiftedY, + this.planarState, + this.vertexLatSinCos, + out GeoPoint geographicPoint)) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(geographicPoint.Lon); + y = geographicPoint.Lat; + } + + private static int ReadDiscreteCode(double value, string parameterName, string paramName) + { + if (double.IsNaN(value) || double.IsInfinity(value)) + { + ArgumentGuard.ThrowArgument($"Invalid value for {parameterName}.", paramName); + } + + int rounded = (int)Math.Round(value, MidpointRounding.AwayFromZero); + if (Math.Abs(value - rounded) > ProjectionConstants.Tolerance1E12) + { + ArgumentGuard.ThrowArgument($"Invalid value for {parameterName}.", paramName); + } + + return rounded; + } + + private static bool IsNearlyEqual(double x, double y) + { + return Math.Abs(x - y) <= ProjectionConstants.Tolerance1E12; + } + + private static GeoPoint SnyderCtran(in GeoPoint np, in GeoPoint point) + { + double phi = point.Lat; + double lambda = point.Lon; + double alpha = np.Lat; + double beta = np.Lon; + double deltaLambda = lambda - beta; + double cosP = Math.Cos(phi); + double sinP = Math.Sin(phi); + double cosA = Math.Cos(alpha); + double sinA = Math.Sin(alpha); + double cosDeltaLambda = Math.Cos(deltaLambda); + double sinDeltaLambda = Math.Sin(deltaLambda); + + double sinPhiPrime = (sinA * sinP) - (cosA * cosP * cosDeltaLambda); + + double lambdaPrimeMinusBeta = Math.Atan2( + cosP * sinDeltaLambda, + (sinA * cosP * cosDeltaLambda) + (cosA * sinP)); + double lambdaPrime = NormalizeLongitude(lambdaPrimeMinusBeta + beta); + + return new GeoPoint(SafeArcSin(sinPhiPrime), lambdaPrime); + } + + private static GeoPoint IseaCtran(in GeoPoint np, in GeoPoint point, double lon0) + { + var cnp = new GeoPoint(np.Lat, np.Lon + PI); + GeoPoint transformed = SnyderCtran(cnp, point); + double longitude = transformed.Lon - (-lon0 + np.Lon); + return new GeoPoint(transformed.Lat, NormalizeLongitude(longitude)); + } + + private static void IseaTriPlane(int triangle, ref IseaPoint point) + { + if (IsDownTriangle(triangle)) + { + point.X = -point.X; + point.Y = -point.Y; + } + + IseaPoint center = IseaTriangleXY(triangle); + point.X += center.X; + point.Y += center.Y; + } + + private static bool IsDownTriangle(int triangle) + { + return ((triangle / 5) % 2) == 1; + } + + private static IseaPoint IseaTriangleXY(int triangle) + { + int normalizedTriangle = triangle % NumIcosahedronFaces; + if (normalizedTriangle < 0) + { + normalizedTriangle += NumIcosahedronFaces; + } + + double x = TableG * ((normalizedTriangle % 5) - 2d) * 2d; + if (normalizedTriangle > 9) + { + x += TableG; + } + + double y = (normalizedTriangle / 5) switch + { + 0 => 5d * TableH, + 1 => TableH, + 2 => -TableH, + 3 => -5d * TableH, + _ => ProjectionThrowHelper.ThrowOutsideProjectionDomain(), + }; + + return new IseaPoint(x * RPrimeOverR, y * RPrimeOverR); + } + + private static double AzAdjustment(int triangle) + { + if ((triangle >= 5 && triangle <= 9) || triangle == 15 || triangle == 16) + { + return PI; + } + + return triangle >= 17 ? -PI : 0d; + } + + private static double SafeArcSin(double value) + { + if (Math.Abs(value) < SafeArcEpsilon) + { + return 0d; + } + + if (Math.Abs(value - 1d) < SafeArcEpsilon) + { + return HalfPi; + } + + return Math.Abs(value + 1d) < SafeArcEpsilon ? -HalfPi : Math.Asin(value); + } + + private static double SafeArcCos(double value) + { + if (Math.Abs(value) < SafeArcEpsilon) + { + return HalfPi; + } + + if (Math.Abs(value + 1d) < SafeArcEpsilon) + { + return PI; + } + + return Math.Abs(value - 1d) < SafeArcEpsilon ? 0d : Math.Acos(value); + } + + private static int ClampInt(int value, int minimum, int maximum) + { + if (value < minimum) + { + return minimum; + } + + return value > maximum ? maximum : value; + } + + private static double NormalizeLongitude(double longitude) + { + double normalized = longitude % TwoPi; + if (normalized > PI) + { + normalized -= TwoPi; + } + else if (normalized < -PI) + { + normalized += TwoPi; + } + + return normalized; + } + + private IseaPlanarInverseProjection? TryCreatePlanarInverseProjection() + { + if (this.outputMode != IseaOutputMode.Plane) + { + return null; + } + + if (this.aperture != 3 || this.resolution != 4 || Math.Abs(this.orientationAzimuth) > ProjectionConstants.Tolerance1E12) + { + return null; + } + + if (IsNearlyEqual(this.orientationLatitude, IseaStdLat) && IsNearlyEqual(this.orientationLongitude, IseaStdLon)) + { + return new IseaPlanarInverseProjection(StandardInverseOrientationLat, StandardInverseOrientationLon); + } + + return IsNearlyEqual(this.orientationLatitude, HalfPi) && IsNearlyEqual(this.orientationLongitude, 0d) + ? new IseaPlanarInverseProjection(0d, 0d) + : null; + } + + private IseaPlanarState CreatePlanarState() + { + double normalizedR2 = 1d; + if (this.e > Eps10) + { + double bOverA = this.semiMinor / this.semiMajor; + double bOverASquared = bOverA * bOverA; + double log1pe1me = Math.Log((1d + this.e) / (1d - this.e)); + normalizedR2 = 0.5d + ((bOverASquared * log1pe1me) / (4d * this.e)); + } + + double rPrime = RPrimeOverR * Math.Sqrt(normalizedR2); + double rPrime2X = 2d * rPrime; + double rPrimeTang = rPrime * Tang; + double centerToBase = rPrimeTang * 0.5d; + double triangleWidth = rPrimeTang * Sqrt3; + double rPrime2Tan2g = rPrimeTang * rPrimeTang; + + double[] yOffsets = + [ + -2d * centerToBase, + -4d * centerToBase, + -5d * centerToBase, + -7d * centerToBase, + ]; + + double xOffset = 2.5d * triangleWidth; + double yOffset = -1.5d * centerToBase; + double scaleX = 1d / triangleWidth; + double scaleY = 1d / (3d * centerToBase); + + return new IseaPlanarState( + normalizedR2, + rPrime2X, + rPrimeTang, + rPrime2Tan2g, + centerToBase, + triangleWidth, + yOffsets, + xOffset, + yOffset, + scaleX, + scaleY); + } + + private int IseaTransform(in GeoPoint input, out IseaPoint output) + { + GeoPoint pole = new(this.orientationLatitude, this.orientationLongitude); + GeoPoint transformed = IseaCtran(pole, input, this.orientationAzimuth); + int triangle = this.IseaSnyderForward(transformed, out output); + return triangle; + } + + private int IseaSnyderForward(in GeoPoint input, out IseaPoint output) + { + double sinLat = Math.Sin(input.Lat); + double cosLat = Math.Cos(input.Lat); + + for (int i = 0; i < NumIcosahedronFaces; i++) + { + GeoPoint center = FacesCenterDodecahedronVertices[i]; + IseaSinCos centerLatSinCos = this.vertexLatSinCos[i]; + double deltaLon = input.Lon - center.Lon; + double cosLatCosLon = cosLat * Math.Cos(deltaLon); + double cosZ = (centerLatSinCos.Sin * sinLat) + (centerLatSinCos.Cos * cosLatCosLon); + double z = SafeArcCos(cosZ); + + if (z > Sdc2VoS + 0.000005d) + { + continue; + } + + double azimuth = Math.Atan2( + cosLat * Math.Sin(deltaLon), + (centerLatSinCos.Cos * sinLat) - (centerLatSinCos.Sin * cosLatCosLon)); + + double azOffset = AzAdjustment(i); + azimuth -= azOffset; + if (azimuth < 0d) + { + azimuth += TwoPi; + } + + int azimuthAdjustMultiples = 0; + while (azimuth < 0d) + { + azimuth += Deg120; + azimuthAdjustMultiples--; + } + + while (azimuth > Deg120 + double.Epsilon) + { + azimuth -= Deg120; + azimuthAdjustMultiples++; + } + + double cosAz = Math.Cos(azimuth); + double sinAz = Math.Sin(azimuth); + double q = Math.Atan2(Tang, cosAz + (sinAz * CotTheta)); + + if (z > q + 0.000005d) + { + continue; + } + + double h = Math.Acos(ProjectionConstants.Clamp((sinAz * SinGcosSdc2VoS) - (cosAz * CosG), -1d, 1d)); + double area = azimuth + (36d * DegToRad) + h - Deg180; + double azimuthPrime = Math.Atan2( + 2d * area, + (RPrimeOverR * RPrimeOverR * Tang * Tang) - (2d * area * CotTheta)); + + double denominator = Math.Cos(azimuthPrime) + (Math.Sin(azimuthPrime) * CotTheta); + if (Math.Abs(denominator) <= Eps10) + { + continue; + } + + double dPrime = (RPrimeOverR * Tang) / denominator; + double sinQHalf = Math.Sin(q * 0.5d); + if (Math.Abs(sinQHalf) <= Eps10) + { + continue; + } + + double f = dPrime / (2d * RPrimeOverR * sinQHalf); + double rho = 2d * RPrimeOverR * f * Math.Sin(z * 0.5d); + + azimuthPrime += Deg120 * azimuthAdjustMultiples; + + output = new IseaPoint( + rho * Math.Sin(azimuthPrime), + rho * Math.Cos(azimuthPrime)); + return i; + } + + output = default; + return ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Kavrayskiy5Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Kavrayskiy5Projection.cs new file mode 100644 index 00000000..8dc17bbb --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Kavrayskiy5Projection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Kavrayskiy V projection (kav5). +/// +/// +/// Kavrayskiy V is an STS-family specialization attributed to Kavrayskiy in the early 1930s. +/// Its numerical behavior is provided by with the +/// Kavrayskiy-specific p and q constants. +/// +internal sealed class Kavrayskiy5Projection : StsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Kavrayskiy5Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Kavrayskiy5Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Kavrayskiy_V", 1.50488d, 1.35439d, false) + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Kavrayskiy5Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Kavrayskiy7Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Kavrayskiy7Projection.cs new file mode 100644 index 00000000..67c8a410 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Kavrayskiy7Projection.cs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Kavrayskiy VII projection (kav7). +/// +/// +/// This projection specializes with the Kavrayskiy VII +/// coefficient set, so its numerical behavior follows the same verified Eckert III style +/// base formulation. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.2.3, pp. 77-79. +internal sealed class Kavrayskiy7Projection : Eckert3Projection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Kavrayskiy7Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Kavrayskiy7Projection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Kavrayskiy_VII"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "eck3_a", 0d); + ReplaceOrAdd(merged, "eck3_b", 0.30396355092701331433d); + ReplaceOrAdd(merged, "eck3_cx", 0.8660254037844d); + ReplaceOrAdd(merged, "eck3_cy", 1d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/KrovakProjection.cs b/src/ProjNet/CoordinateSystems/Projections/KrovakProjection.cs index d709cad3..62d89186 100644 --- a/src/ProjNet/CoordinateSystems/Projections/KrovakProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/KrovakProjection.cs @@ -1,270 +1,329 @@ -// Copyright 2008 -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Krovak Oblique Conformal Conic map projection. +/// +/// +/// The normal case of the Lambert Conformal conic is for the axis of the cone +/// to be coincident with the minor axis of the ellipsoid, that is the axis of the cone +/// is normal to the ellipsoid at a pole. For the Oblique Conformal Conic the axis +/// of the cone is normal to the ellipsoid at a defined location and its extension +/// cuts the minor axis at a defined angle. This projection is used in the Czech Republic +/// and Slovakia under the name "Krovak" projection. +/// The formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG method 9819, Krovak. The oblique-conic setup on +/// the conformal sphere and the resulting parameter usage match the implementation here. +/// +/// EPSG method 9819: Krovak. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 5, Sect. 5.1.6, pp. 163-164. +internal class KrovakProjection : MapProjection { + // Maximum number of iterations for iterative computations. + private const int MaximumIterations = 15; + + // When to stop the iteration. + private const double IterationTolerance = 1E-11d; + + private const double DefaultAzimuthDegrees = 30.2881397527778d; + private const double DefaultPseudoStandardParallelDegrees = 78.5d; + + // Azimuth of the centre line passing through the centre of the projection. + // This is equals to the co-latitude of the cone axis at point of intersection + // with the ellipsoid. + private readonly double azimuth; - /// - /// Implemetns the Krovak Projection. - /// - /// - /// The normal case of the Lambert Conformal conic is for the axis of the cone - /// to be coincident with the minor axis of the ellipsoid, that is the axis of the cone - /// is normal to the ellipsoid at a pole. For the Oblique Conformal Conic the axis - /// of the cone is normal to the ellipsoid at a defined location and its extension - /// cuts the minor axis at a defined angle. This projection is used in the Czech Republic - /// and Slovakia under the name "Krovak" projection. + // Latitude of pseudo standard parallel. + private readonly double pseudoStandardParallel; + + // Useful variables calculated from parameters defined by user. + private readonly double sinAzim; + private readonly double cosAzim; + private readonly double n; + private readonly double tanS2; + private readonly double alfa; + private readonly double hae; + private readonly double k1; + private readonly double ka; + private readonly double ro0; + private readonly double rop; + + private readonly double reciprocSemiMajor; + private readonly bool eastingNorthing; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The parameters this projection expects are listed below. + /// + /// ParameterDescription + /// latitude_of_centerGeodetic latitude of the projection centre. + /// longitude_of_centerLongitude of the projection centre (central meridian). + /// azimuthAzimuth of the centre line at the projection centre. + /// pseudo_standard_parallel_1Latitude of the pseudo standard parallel. + /// scale_factorScale factor on the pseudo standard parallel. + /// false_eastingEasting assigned to the projection centre. + /// false_northingNorthing assigned to the projection centre. + /// /// - [Serializable] - internal class KrovakProjection : MapProjection - { - /** - * Maximum number of iterations for iterative computations. - */ - private const int MaximumIterations = 15; - - /** - * When to stop the iteration. - */ - private const double IterationTolerance = 1E-11; - - /** - * Azimuth of the centre line passing through the centre of the projection. - * This is equals to the co-latitude of the cone axis at point of intersection - * with the ellipsoid. - */ - private readonly double _azimuth; - - /** - * Latitude of pseudo standard parallel. - */ - private readonly double _pseudoStandardParallel; - - /** - * Useful variables calculated from parameters defined by user. - */ - private readonly double _sinAzim, _cosAzim, _n, _tanS2, _alfa, _hae, _k1, _ka, _ro0, _rop; - - private readonly double _reciprocSemiMajor; - - /** - * Useful constant - 45° in radians. - */ - private const double S45 = 0.785398163397448; - - #region Constructors - - /// - /// Creates an instance of an LambertConformalConic2SPProjection projection object. - /// - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// latitude_of_false_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// longitude_of_false_originThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// latitude_of_1st_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. - /// latitude_of_2nd_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. - /// easting_at_false_originThe easting value assigned to the false origin. - /// northing_at_false_originThe northing value assigned to the false origin. - /// - /// - /// List of parameters to initialize the projection. - public KrovakProjection(IEnumerable parameters) - : this(parameters,null) - { - } - - /// - /// Creates an instance of an Krovak projection object. - /// - /// - /// The parameters this projection expects are listed below. - /// - /// ParameterDescription - /// latitude_of_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// central_meridianThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// standard_parallel_1For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. - /// standard_parallel_2For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. - /// false_eastingThe easting value assigned to the false origin. - /// false_northingThe northing value assigned to the false origin. - /// - /// - /// List of parameters to initialize the projection. - /// Indicates whether the projection forward (meters to degrees or degrees to meters). - protected KrovakProjection(IEnumerable parameters, KrovakProjection inverse) - : base(parameters, inverse) - { - Name = "Krovak"; - - Authority = "EPSG"; - AuthorityCode = 9819; - - //PROJCS["S-JTSK (Ferro) / Krovak", - //GEOGCS["S-JTSK (Ferro)", - // DATUM["D_S_JTSK_Ferro", - // SPHEROID["Bessel 1841",6377397.155,299.1528128]], - // PRIMEM["Ferro",-17.66666666666667], - // UNIT["degree",0.0174532925199433]], - //PROJECTION["Krovak"], - //PARAMETER["latitude_of_center",49.5], - //PARAMETER["longitude_of_center",42.5], - //PARAMETER["azimuth",30.28813972222222], - //PARAMETER["pseudo_standard_parallel_1",78.5], - //PARAMETER["scale_factor",0.9999], - //PARAMETER["false_easting",0], - //PARAMETER["false_northing",0], - //UNIT["metre",1]] - - //Check for missing parameters - _azimuth = DegreesToRadians(_Parameters.GetParameterValue("azimuth")); - _pseudoStandardParallel = DegreesToRadians(_Parameters.GetParameterValue("pseudo_standard_parallel_1")); - - // Calculates useful constants. - _sinAzim = Math.Sin(_azimuth); - _cosAzim = Math.Cos(_azimuth); - _n = Math.Sin(_pseudoStandardParallel); - _tanS2 = Math.Tan(_pseudoStandardParallel / 2 + S45); - - double sinLat = Math.Sin(lat_origin); - double cosLat = Math.Cos(lat_origin); - double cosL2 = cosLat * cosLat; - _alfa = Math.Sqrt(1 + ((_es * (cosL2 * cosL2)) / (1 - _es))); // parameter B - _hae = _alfa * _e / 2; - double u0 = Math.Asin(sinLat / _alfa); - - double esl = _e * sinLat; - double g = Math.Pow((1 - esl) / (1 + esl), (_alfa * _e) / 2); - _k1 = Math.Pow(Math.Tan(lat_origin / 2 + S45), _alfa) * g / Math.Tan(u0 / 2 + S45); - _ka = Math.Pow(1 / _k1, -1 / _alfa); - - double radius = Math.Sqrt(1 - _es) / (1 - (_es * (sinLat * sinLat))); - - _ro0 = scale_factor * radius / Math.Tan(_pseudoStandardParallel); - _rop = _ro0 * Math.Pow(_tanS2, _n); - - _reciprocSemiMajor = 1 / _semiMajor; + /// List of parameters to initialize the projection. + public KrovakProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + protected KrovakProjection(IEnumerable parameters, KrovakProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Krovak"; + + this.Authority = "EPSG"; + this.AuthorityCode = 9819; + + this.eastingNorthing = this.Parameters.GetOptionalParameterValue("czech", 0d) == 0d; + this.azimuth = DegreesToRadians(this.Parameters.GetOptionalParameterValue("azimuth", DefaultAzimuthDegrees)); + this.pseudoStandardParallel = DegreesToRadians(this.Parameters.GetOptionalParameterValue("pseudo_standard_parallel_1", DefaultPseudoStandardParallelDegrees)); + + // Calculates useful constants. + this.sinAzim = Math.Sin(this.azimuth); + this.cosAzim = Math.Cos(this.azimuth); + this.n = Math.Sin(this.pseudoStandardParallel); + this.tanS2 = Math.Tan((this.pseudoStandardParallel / 2) + FortPi); + + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + double cosLatitudeOriginSquared = cosLatitudeOrigin * cosLatitudeOrigin; + this.alfa = Math.Sqrt(1 + ((this.es * (cosLatitudeOriginSquared * cosLatitudeOriginSquared)) / (1 - this.es))); // parameter B + this.hae = this.alfa * this.e / 2; + double u0 = Math.Asin(sinLatitudeOrigin / this.alfa); + + double eccentricityLatitude = this.e * sinLatitudeOrigin; + double g = Math.Pow((1 - eccentricityLatitude) / (1 + eccentricityLatitude), (this.alfa * this.e) / 2); + this.k1 = Math.Pow(Math.Tan((this.latOrigin / 2) + FortPi), this.alfa) * g / Math.Tan((u0 / 2) + FortPi); + this.ka = Math.Pow(1 / this.k1, -1 / this.alfa); + + double meridionalRadius = Math.Sqrt(1 - this.es) / (1 - (this.es * (sinLatitudeOrigin * sinLatitudeOrigin))); + + this.ro0 = this.scaleFactor * meridionalRadius / Math.Tan(this.pseudoStandardParallel); + this.rop = this.ro0 * Math.Pow(this.tanS2, this.n); + + this.reciprocSemiMajor = 1 / this.semiMajor; + } + + private static double ClampToUnit(double value) + { + if (value > 1d) + { + return 1d; } - #endregion - - /// - /// Converts coordinates in radians to projected meters. - /// - /// - /// - protected override void RadiansToMeters(ref double lon, ref double lat) - { - double lambda = lon - central_meridian; - double phi = lat; - - double esp = _e * Math.Sin(phi); - double gfi = Math.Pow(((1.0 - esp) / (1.0 + esp)), _hae); - double u = 2 * (Math.Atan(Math.Pow(Math.Tan(phi / 2 + S45), _alfa) / _k1 * gfi) - S45); - double deltav = -lambda * _alfa; - double cosU = Math.Cos(u); - double s = Math.Asin((_cosAzim * Math.Sin(u)) + (_sinAzim * cosU * Math.Cos(deltav))); - double d = Math.Asin(cosU * Math.Sin(deltav) / Math.Cos(s)); - double eps = _n * d; - double ro = _rop / Math.Pow(Math.Tan(s / 2 + S45), _n); - - /* x and y are reverted */ - lat = -(ro * Math.Cos(eps)) * _semiMajor; - lon = -(ro * Math.Sin(eps)) * _semiMajor; - } - - /// - /// Converts coordinates in projected meters to radians. - /// - /// - /// - protected override void MetersToRadians(ref double x, ref double y) + + return value < -1d ? -1d : value; + } + + /// + protected override void DegreesToTarget(ref double lon, ref double lat) + { + this.DegreesToMeters(ref lon, ref lat); + this.MetersToKrovakTarget(ref lon, ref lat); + } + + /// + protected override void DegreesToTarget(Span lons, Span lats, int strideX, int strideY) + { + this.DegreesToMeters(lons, lats, strideX, strideY); + for (int i = 0, j = 0; i < lons.Length; i += strideX, j += strideY) { - x *= _reciprocSemiMajor; - y *= _reciprocSemiMajor; - - // x -> southing, y -> westing - double ro = Math.Sqrt(x * x + y * y); - double eps = Math.Atan2(-x, -y); - double d = eps / _n; - double s = 2 * (Math.Atan(Math.Pow(_ro0 / ro, 1 / _n) * _tanS2) - S45); - double cs = Math.Cos(s); - double u = Math.Asin((_cosAzim * Math.Sin(s)) - (_sinAzim * cs * Math.Cos(d))); - double kau = _ka * Math.Pow(Math.Tan((u / 2.0) + S45), 1 / _alfa); - double deltav = Math.Asin((cs * Math.Sin(d)) / Math.Cos(u)); - double lambda = -deltav / _alfa; - double phi = 0d; - - // iteration calculation - for (int iter = MaximumIterations;;) + this.MetersToKrovakTarget(ref lons[i], ref lats[j]); + } + } + + /// + protected override void SourceToDegrees(ref double x, ref double y) + { + this.KrovakTargetToMeters(ref x, ref y); + this.MetersToDegrees(ref x, ref y); + } + + /// + protected override void SourceToDegrees(Span xs, Span ys, int strideX, int strideY) + { + for (int i = 0, j = 0; i < xs.Length; i += strideX, j += strideY) + { + this.KrovakTargetToMeters(ref xs[i], ref ys[j]); + } + + this.MetersToDegrees(xs, ys, strideX, strideY); + } + + /// + /// Converts coordinates in radians to projected meters. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = lon - this.centralMeridian; + double phi = lat; + + double eccentricitySinPhi = this.e * Math.Sin(phi); + double conformalScale = Math.Pow((1.0 - eccentricitySinPhi) / (1.0 + eccentricitySinPhi), this.hae); + double conformalLatitude = 2 * (Math.Atan(Math.Pow(Math.Tan((phi / 2) + FortPi), this.alfa) / this.k1 * conformalScale) - FortPi); + double deltaV = -lambda * this.alfa; + double cosConformalLatitude = Math.Cos(conformalLatitude); + double pseudoLatitude = Math.Asin(ClampToUnit((this.cosAzim * Math.Sin(conformalLatitude)) + (this.sinAzim * cosConformalLatitude * Math.Cos(deltaV)))); + double pseudoLongitude = Math.Asin(ClampToUnit(cosConformalLatitude * Math.Sin(deltaV) / Math.Cos(pseudoLatitude))); + double eps = this.n * pseudoLongitude; + double radialDistance = this.rop / Math.Pow(Math.Tan((pseudoLatitude / 2) + FortPi), this.n); + + // x and y are reverted + lat = -(radialDistance * Math.Cos(eps)) * this.semiMajor; + lon = -(radialDistance * Math.Sin(eps)) * this.semiMajor; + } + + /// + /// Converts coordinates in projected meters to radians. + /// + /// The x-ordinate in projected meters when entering, the longitude in radians after exit. + /// The y-ordinate in projected meters when entering, the latitude in radians after exit. + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocSemiMajor; + y *= this.reciprocSemiMajor; + + // x -> southing, y -> westing + double radialDistance = Math.Sqrt((x * x) + (y * y)); + double eps = Math.Atan2(-x, -y); + double pseudoLongitude = eps / this.n; + double pseudoLatitude = 2 * (Math.Atan(Math.Pow(this.ro0 / radialDistance, 1 / this.n) * this.tanS2) - FortPi); + double cosPseudoLatitude = Math.Cos(pseudoLatitude); + double conformalLatitude = Math.Asin(ClampToUnit((this.cosAzim * Math.Sin(pseudoLatitude)) - (this.sinAzim * cosPseudoLatitude * Math.Cos(pseudoLongitude)))); + double inverseConformalScale = this.ka * Math.Pow(Math.Tan((conformalLatitude / 2.0) + FortPi), 1 / this.alfa); + double deltaV = Math.Asin(ClampToUnit((cosPseudoLatitude * Math.Sin(pseudoLongitude)) / Math.Cos(conformalLatitude))); + double lambda = -deltaV / this.alfa; + double phi = 0d; + + // iteration calculation + for (int iter = MaximumIterations; ;) + { + double fi1 = phi; + double esf = this.e * Math.Sin(fi1); + phi = 2.0 * (Math.Atan(inverseConformalScale * Math.Pow((1.0 + esf) / (1.0 - esf), this.e / 2.0)) - FortPi); + if (Math.Abs(fi1 - phi) <= IterationTolerance) { - double fi1 = phi; - double esf = _e * Math.Sin(fi1); - phi = 2.0 * (Math.Atan(kau * Math.Pow((1.0 + esf) / (1.0 - esf), _e / 2.0)) - S45); - if (Math.Abs(fi1 - phi) <= IterationTolerance) - { - break; - } - - if (--iter < 0) - { - break; - //throw new ProjectionException(Errors.format(ErrorKeys.NO_CONVERGENCE)); - } + break; } - x = lambda + central_meridian; - y = phi; + if (--iter < 0) + { + break; + } + } + + x = lambda + this.centralMeridian; + y = phi; + } + + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= this.CreateInverseProjection(); + + return this.inverse; + } + + /// + /// Creates the cached inverse projection instance for the current variant. + /// + /// The inverse projection instance. + protected virtual KrovakProjection CreateInverseProjection() => new(this.Parameters.ToProjectionParameter(), this); + + /// + /// Computes the modified Krovak correction terms for the current variant. + /// + /// The southing in metres. + /// The westing in metres. + /// Receives the correction term for the southing component. + /// Receives the correction term for the westing component. + /// when the current variant applies a modified Krovak correction; otherwise . + protected virtual bool TryComputeModifiedDelta( + double southing, + double westing, + out double deltaSouthing, + out double deltaWesting) + { + deltaSouthing = 0d; + deltaWesting = 0d; + return false; + } + + private void MetersToKrovakTarget(ref double x, ref double y) + { + double southing = -y; + double westing = -x; + if (this.TryComputeModifiedDelta(southing, westing, out double deltaSouthing, out double deltaWesting)) + { + southing -= deltaSouthing; + westing -= deltaWesting; + } + + if (this.eastingNorthing) + { + x = -westing - this.falseEasting; + y = -southing - this.falseNorthing; + } + else + { + x = westing + this.falseEasting; + y = southing + this.falseNorthing; + } + + x *= this.reciprocalMetersPerUnit; + y *= this.reciprocalMetersPerUnit; + } + + private void KrovakTargetToMeters(ref double x, ref double y) + { + x *= this.metersPerUnit; + y *= this.metersPerUnit; + + double southing; + double westing; + if (this.eastingNorthing) + { + westing = -x - this.falseEasting; + southing = -y - this.falseNorthing; + } + else + { + westing = x - this.falseEasting; + southing = y - this.falseNorthing; + } + + if (this.TryComputeModifiedDelta(southing, westing, out double deltaSouthing, out double deltaWesting)) + { + southing += deltaSouthing; + westing += deltaWesting; } - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse == null) - { - _inverse = new KrovakProjection(_Parameters.ToProjectionParameter(), this); - } - - return _inverse; - } - } + x = -westing; + y = -southing; + } } diff --git a/src/ProjNet/CoordinateSystems/Projections/LabordeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LabordeProjection.cs new file mode 100644 index 00000000..523f038d --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LabordeProjection.cs @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Laborde projection (labrd). +/// +/// +/// Laborde is the oblique conformal projection introduced for Madagascar by Jean Laborde in +/// 1928. The implementation follows the classical conformal-sphere setup and then applies +/// the Laborde polynomial correction terms that distinguish this projection from standard +/// oblique Mercator forms. +/// +internal sealed class LabordeProjection : MapProjection +{ + private const int MaximumIterations = 20; + private const double IterationTolerance = 1e-10d; + + private readonly double kRg; + private readonly double p0s; + private readonly double a; + private readonly double c; + private readonly double ca; + private readonly double cb; + private readonly double cc; + private readonly double cd; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LabordeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LabordeProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Laborde"; + + if (Math.Abs(this.latOrigin) < Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_0: lat_0 should be different from 0.", nameof(parameters)); + } + + double azimuth = DegreesToRadians(this.Parameters.GetOptionalParameterValue("azi", this.Parameters.GetOptionalParameterValue("azimuth", 0d))); + double sinPhi0 = Math.Sin(this.latOrigin); + double t = 1d - (this.es * sinPhi0 * sinPhi0); + double n = 1d / Math.Sqrt(t); + double r = (1d - this.es) * n / t; + this.kRg = this.scaleFactor * Math.Sqrt(n * r); + this.p0s = Math.Atan(Math.Sqrt(r / n) * Math.Tan(this.latOrigin)); + this.a = sinPhi0 / Math.Sin(this.p0s); + t = this.e * sinPhi0; + this.c = (0.5d * this.e * this.a * Math.Log((1d + t) / (1d - t))) + - (this.a * Math.Log(Math.Tan(FortPi + (0.5d * this.latOrigin)))) + + Math.Log(Math.Tan(FortPi + (0.5d * this.p0s))); + t = azimuth + azimuth; + double cbTmp = 1d / (12d * this.kRg * this.kRg); + this.ca = (1d - Math.Cos(t)) * cbTmp; + this.cb = cbTmp * Math.Sin(t); + this.cc = 3d * ((this.ca * this.ca) - (this.cb * this.cb)); + this.cd = 6d * this.ca * this.cb; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new LabordeProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + + double v1 = this.a * Math.Log(Math.Tan(FortPi + (0.5d * lat))); + double t = this.e * Math.Sin(lat); + double v2 = 0.5d * this.e * this.a * Math.Log((1d + t) / (1d - t)); + double ps = 2d * (Math.Atan(Math.Exp(v1 - v2 + this.c)) - FortPi); + double i1 = ps - this.p0s; + + double cosps = Math.Cos(ps); + double cosps2 = cosps * cosps; + double sinps = Math.Sin(ps); + double sinps2 = sinps * sinps; + double i4 = this.a * cosps; + double i2 = 0.5d * this.a * i4 * sinps; + double i3 = i2 * this.a * this.a * ((5d * cosps2) - sinps2) / 12d; + double i6 = i4 * this.a * this.a; + double i5 = i6 * (cosps2 - sinps2) / 6d; + i6 *= this.a * this.a * ((5d * cosps2 * cosps2) + (sinps2 * (sinps2 - (18d * cosps2)))) / 120d; + + t = lambda * lambda; + double x = this.kRg * lambda * (i4 + (t * (i5 + (t * i6)))); + double y = this.kRg * (i1 + (t * (i2 + (t * i3)))); + double x2 = x * x; + double y2 = y * y; + v1 = (3d * x * y2) - (x * x2); + v2 = (y * y2) - (3d * x2 * y); + x += (this.ca * v1) + (this.cb * v2); + y += (this.ca * v2) - (this.cb * v1); + + lon = this.semiMajor * x; + lat = this.semiMajor * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x / this.semiMajor; + double yy = y / this.semiMajor; + + double x2 = xx * xx; + double y2 = yy * yy; + double v1 = (3d * xx * y2) - (xx * x2); + double v2 = (yy * y2) - (3d * x2 * yy); + double v3 = xx * ((5d * y2 * y2) + (x2 * ((-10d * y2) + x2))); + double v4 = yy * ((5d * x2 * x2) + (y2 * ((-10d * x2) + y2))); + xx += (-this.ca * v1) - (this.cb * v2) + (this.cc * v3) + (this.cd * v4); + yy += (this.cb * v1) - (this.ca * v2) - (this.cd * v3) + (this.cc * v4); + + double ps = this.p0s + (yy / this.kRg); + double pe = ps + this.latOrigin - this.p0s; + + bool converged = false; + for (int i = 0; i < MaximumIterations; i++) + { + v1 = this.a * Math.Log(Math.Tan(FortPi + (0.5d * pe))); + double tpe = this.e * Math.Sin(pe); + v2 = 0.5d * this.e * this.a * Math.Log((1d + tpe) / (1d - tpe)); + double delta = ps - (2d * (Math.Atan(Math.Exp(v1 - v2 + this.c)) - FortPi)); + pe += delta; + if (Math.Abs(delta) < IterationTolerance) + { + converged = true; + break; + } + } + + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double t = this.e * Math.Sin(pe); + t = 1d - (t * t); + double re = (1d - this.es) / (t * Math.Sqrt(t)); + t = Math.Tan(ps); + double t2 = t * t; + double s = this.kRg * this.kRg; + double d = re * this.scaleFactor * this.kRg; + double i7 = t / (2d * d); + double i8 = t * (5d + (3d * t2)) / (24d * d * s); + d = Math.Cos(ps) * this.kRg * this.a; + double i9 = 1d / d; + d *= s; + double i10 = (1d + (2d * t2)) / (6d * d); + double i11 = (5d + (t2 * (28d + (24d * t2)))) / (120d * d * s); + x2 = xx * xx; + double phi = pe + (x2 * (-i7 + (i8 * x2))); + double lambda = xx * (i9 + (x2 * (-i10 + (x2 * i11)))); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LagrangeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LagrangeProjection.cs new file mode 100644 index 00000000..fa7d4533 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LagrangeProjection.cs @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Lagrange projection (lagrng). +/// +/// +/// Lagrange is the conformal spherical projection associated with Lambert and Lagrange in +/// the eighteenth century. The implementation uses the parameter W and the reference +/// latitude lat_1 to build the classic circular conformal mapping. +/// +internal sealed class LagrangeProjection : MapProjection +{ + private readonly double a1; + private readonly double a2; + private readonly double hrw; + private readonly double hw; + private readonly double rw; + private readonly double w; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LagrangeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LagrangeProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeDefaults(parameters), inverse) + { + this.Name = "Lagrange"; + + this.w = this.Parameters.GetOptionalParameterValue("W", 2d); + if (this.w <= 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for W: it should be > 0", nameof(parameters)); + } + + this.hw = 0.5d * this.w; + this.rw = 1d / this.w; + this.hrw = 0.5d * this.rw; + double sinPhi1 = Math.Sin(DegreesToRadians(this.Parameters.GetOptionalParameterValue("lat_1", 0d, "standard_parallel_1"))); + if (Math.Abs(Math.Abs(sinPhi1) - 1d) < Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_1: |lat_1| should be < 90°", nameof(parameters)); + } + + this.a1 = Math.Pow((1d - sinPhi1) / (1d + sinPhi1), this.hrw); + this.a2 = this.a1 * this.a1; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new LagrangeProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double sinPhi = Math.Sin(lat); + double x = 0d; + double y = lat < 0d ? -2d : 2d; + if (Math.Abs(Math.Abs(sinPhi) - 1d) >= Eps10) + { + double v = this.a1 * Math.Pow((1d + sinPhi) / (1d - sinPhi), this.hrw); + double lambdaScaled = lambda * this.rw; + double c = (0.5d * (v + (1d / v))) + Math.Cos(lambdaScaled); + if (c < Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = 2d * Math.Sin(lambdaScaled) / c; + y = (v - (1d / v)) / c; + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = yy < 0d ? -HalfPi : HalfPi; + double lambda = 0d; + if (Math.Abs(Math.Abs(yy) - 2d) >= Eps10) + { + double x2 = xx * xx; + double y2p = 2d + yy; + double y2m = 2d - yy; + double c = (y2p * y2m) - x2; + if (Math.Abs(c) < Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + phi = + (2d * Math.Atan(Math.Pow( + ((y2p * y2p) + x2) / (this.a2 * ((y2m * y2m) + x2)), + this.hw))) + - HalfPi; + lambda = this.w * Math.Atan2(4d * xx, c); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static List MergeDefaults(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + bool hasLat1 = false; + bool hasW = false; + for (int i = 0; i < merged.Count; i++) + { + if (merged[i].Name.Equals("lat_1", StringComparison.OrdinalIgnoreCase)) + { + hasLat1 = true; + } + else if (merged[i].Name.Equals("W", StringComparison.OrdinalIgnoreCase)) + { + hasW = true; + } + } + + if (!hasLat1) + { + merged.Add(new ProjectionParameter("lat_1", 0d)); + } + + if (!hasW) + { + merged.Add(new ProjectionParameter("W", 2d)); + } + + return merged; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LambertAzimuthalEqualAreaProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LambertAzimuthalEqualAreaProjection.cs index 910c7142..70a71bcd 100644 --- a/src/ProjNet/CoordinateSystems/Projections/LambertAzimuthalEqualAreaProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/LambertAzimuthalEqualAreaProjection.cs @@ -1,420 +1,446 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Lambert Azimuthal Equal Area projection for spherical and ellipsoidal models. +/// +/// +/// The ellipsoidal formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG method 9820, Lambert Azimuthal Equal Area. The +/// q, qP, β, and Rq relationships match the implementation +/// here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 24, pp. 182-190, +/// eqs. (24-1) through (24-18), for the classic Lambert azimuthal equal-area +/// derivation. +/// +/// EPSG method 9820: Lambert Azimuthal Equal Area. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.2.3, pp. 103-105. +public sealed class LambertAzimuthalEqualAreaProjection : MapProjection { /// - /// + /// The delegate to perform forward transformation. + /// + private readonly Transformer radiansToMeters; + + /// + /// The delegate to perform reverse transformation. + /// + private readonly Transformer metersToRadians; + + private readonly Mode mode; + private readonly double qp; + private readonly double oneEs; + private readonly double[]? apa; + + private readonly double dd; + private readonly double sinb1; + private readonly double cosb1; + private readonly double rq; + private readonly double xmf; + private readonly double ymf; + + private readonly double reciprocSemiMajorTimesScaleFactor; + + /// + /// Initializes a new instance of the class. /// - public class LambertAzimuthalEqualAreaProjection : MapProjection + /// List of parameters to initialize the projection. + public LambertAzimuthalEqualAreaProjection(IEnumerable parameters) + : this(parameters, null) { - /// - /// An enumeration of modes - /// - private enum Mode - { - /// - /// North pole - /// - N_POLE, - /// - /// South pole - /// - S_POLE, - /// - /// Equitorial - /// - EQUIT, - /// - /// Oblique - /// - OBLIQ - } + } - /// - /// A function to perform the actual transformation - /// - /// The horizontal ordinate - /// The vertical ordinate - delegate void Transformer(ref double o1, ref double o2); + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + public LambertAzimuthalEqualAreaProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Lambert_Azimuthal_Equal_Area"; - /// - /// The delegate to perform forward transformation - /// - private readonly Transformer _radiansToMeters; - /// - /// The delegate to perform reverse transformation - /// - private readonly Transformer _metersToRadians; - - private readonly Mode _mode; - private readonly double _qp; - private readonly double _one_es; - private readonly double[] _apa; - //private readonly double _mmf; - private readonly double _dd; - private readonly double _sinb1; - private readonly double _cosb1; - private readonly double _rq; - private readonly double _xmf; - private readonly double _ymf; - - private readonly double _reciprocSemiMajorTimesScaleFactor; + double phi0 = this.latOrigin; - /// - /// Creates an instance of this class - /// - /// An enumeration of Projection parameters - public LambertAzimuthalEqualAreaProjection(IEnumerable parameters) : this(parameters, null) + double t = Math.Abs(phi0); + if (t > HalfPi + Eps10) { + ArgumentGuard.ThrowArgument("Latitude of origin is outside the valid range.", nameof(parameters)); } - /// - /// Creates an instance of this class - /// - /// An enumeration of Projection parameters - /// The inverse projection - public LambertAzimuthalEqualAreaProjection(IEnumerable parameters, MapProjection inverse) - : base(parameters, inverse) + if (Math.Abs(t - HalfPi) < Eps10) { - Name = "Lambert_Azimuthal_Equal_Area"; - - double phi0 = lat_origin; + this.mode = phi0 < 0.0 ? Mode.S_POLE : Mode.N_POLE; + } + else if (Math.Abs(t) < Eps10) + { + this.mode = Mode.EQUIT; + } + else + { + this.mode = Mode.OBLIQ; + } - double t = Math.Abs(phi0); - if (t > HALF_PI + EPS10) - throw new ArgumentException(nameof(parameters)); + if (this.es != 0d) + { + this.oneEs = 1.0 - this.es; + this.qp = Qsfn(1, this.e, this.oneEs); - if (Math.Abs(t - HALF_PI) < EPS10) { - _mode = phi0 < 0.0 ? Mode.S_POLE : Mode.N_POLE; - } - else if (Math.Abs(t) < EPS10) { - _mode = Mode.EQUIT; } - else { - _mode = Mode.OBLIQ; + this.apa = Authset(this.es); + if (this.apa is null) + { + ArgumentGuard.ThrowArgument("Failed to initialize authalic coefficients from projection parameters.", nameof(parameters)); } - if (_es != 0d) + switch (this.mode) { - _one_es = 1.0 - _es; - _qp = qsfn(1, _e, _one_es); - //_mmf = 0.5 / (1.0 - _es); - _apa = authset(_es); - if (_apa == null) - throw new ArgumentException(nameof(parameters)); - - switch (_mode) - { - case Mode.N_POLE: - case Mode.S_POLE: - _dd = 1.0; - break; - case Mode.EQUIT: - _dd = 1.0 / (_rq = Math.Sqrt(0.5 * _qp)); - _xmf = 1.0; - _ymf = 0.5 * _qp; - break; - case Mode.OBLIQ: - _rq = Math.Sqrt(0.5 * _qp); - double sinphi = Math.Sin(phi0); - _sinb1 = qsfn(sinphi, _e, _one_es) / _qp; - _cosb1 = Math.Sqrt(1.0 - _sinb1 * _sinb1); - _dd = Math.Cos(phi0) / (Math.Sqrt(1.0 - _es * sinphi * sinphi) * _rq * _cosb1); - _ymf = (_xmf = _rq) / _dd; - _xmf *= _dd; - break; - } - _radiansToMeters = EllipsoidalRadiansToMeters; - _metersToRadians = EllipsoidalMetersToRadians; + case Mode.N_POLE: + case Mode.S_POLE: + this.dd = 1.0; + break; + case Mode.EQUIT: + this.dd = 1.0 / (this.rq = Math.Sqrt(0.5 * this.qp)); + this.xmf = 1.0; + this.ymf = 0.5 * this.qp; + break; + case Mode.OBLIQ: + this.rq = Math.Sqrt(0.5 * this.qp); + double sinphi = Math.Sin(phi0); + this.sinb1 = Qsfn(sinphi, this.e, this.oneEs) / this.qp; + this.cosb1 = Math.Sqrt(1.0 - (this.sinb1 * this.sinb1)); + this.dd = Math.Cos(phi0) / (Math.Sqrt(1.0 - (this.es * sinphi * sinphi)) * this.rq * this.cosb1); + this.ymf = (this.xmf = this.rq) / this.dd; + this.xmf *= this.dd; + break; } - else - { - if (_mode == Mode.OBLIQ) - { - _sinb1 = Math.Sin(phi0); - _cosb1 = Math.Cos(phi0); - } - _radiansToMeters = SphericalRadiansToMeters; - _metersToRadians = SphericalMetersToRadians; + this.radiansToMeters = this.EllipsoidalRadiansToMeters; + this.metersToRadians = this.EllipsoidalMetersToRadians; + } + else + { + if (this.mode == Mode.OBLIQ) + { + this.sinb1 = Math.Sin(phi0); + this.cosb1 = Math.Cos(phi0); } - _reciprocSemiMajorTimesScaleFactor = 1d / (scale_factor * _semiMajor); + this.radiansToMeters = this.SphericalRadiansToMeters; + this.metersToRadians = this.SphericalMetersToRadians; } + this.reciprocSemiMajorTimesScaleFactor = 1d / (this.scaleFactor * this.semiMajor); + } + + /// + /// A function to perform the actual transformation. + /// + /// The horizontal ordinate. + /// The vertical ordinate. + private delegate void Transformer(ref double o1, ref double o2); + /// + /// An enumeration of modes. + /// + private enum Mode + { /// - /// Creates the inverse transform of this object. + /// North pole. /// - /// This method may fail if the transform is not one to one. However, all cartographic projections should succeed. - /// - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new LambertAzimuthalEqualAreaProjection(_Parameters.ToProjectionParameter(), this); - return _inverse; - } + N_POLE, - #region forward + /// + /// South pole. + /// + S_POLE, /// - /// Method to convert a point (lon, lat) in radians to (x, y) in meters + /// Equatorial. /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - protected override void RadiansToMeters(ref double lon, ref double lat) - { - _radiansToMeters(ref lon, ref lat); - lon *= scale_factor * _semiMajor; - lat *= scale_factor * _semiMajor; - } + EQUIT, - private void EllipsoidalRadiansToMeters(ref double lon, ref double lat) - { - double sinb = 0.0, cosb = 0.0, b = 0.0; + /// + /// Oblique. + /// + OBLIQ, + } - double lam = adjust_lon(lon-central_meridian); - double phi = lat; + /// + public override MathTransform Inverse() + { + this.inverse ??= new LambertAzimuthalEqualAreaProjection(this.Parameters.ToProjectionParameter(), this); - double coslam = Math.Cos(lam); - double sinlam = Math.Sin(lam); - double sinphi = Math.Sin(phi); - double q = qsfn(sinphi, _e, _one_es); + return this.inverse; + } - if (_mode == Mode.OBLIQ || _mode == Mode.EQUIT) - { - sinb = q / _qp; - cosb = Math.Sqrt(1.0 - sinb * sinb); - } + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + this.radiansToMeters(ref lon, ref lat); + lon *= this.scaleFactor * this.semiMajor; + lat *= this.scaleFactor * this.semiMajor; + } - switch (_mode) - { - case Mode.OBLIQ: - b = 1.0 + _sinb1 * sinb + _cosb1 * cosb * coslam; - break; - case Mode.EQUIT: - b = 1.0 + cosb * coslam; - break; - case Mode.N_POLE: - b = HALF_PI + phi; - q = _qp - q; - break; - case Mode.S_POLE: - b = phi - HALF_PI; - q = _qp + q; - break; - } + private void EllipsoidalRadiansToMeters(ref double lon, ref double lat) + { + double sinb = 0.0; + double cosb = 0.0; + double b = 0.0; - double x = HUGE_VAL; - double y = HUGE_VAL; - if (Math.Abs(b) < EPS10) - { - //proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return; - } + double lam = Adjust_lon(lon - this.centralMeridian); + double phi = lat; - switch (_mode) - { - case Mode.OBLIQ: - b = Math.Sqrt(2.0 / b); - y = _ymf * b * (_cosb1 * sinb - _sinb1 * cosb * coslam); - goto eqcon; - case Mode.EQUIT: - b = Math.Sqrt(2.0 / (1.0 + cosb * coslam)); - y = b * sinb * _ymf; - eqcon: - x = _xmf * b * cosb * sinlam; - break; - case Mode.N_POLE: - case Mode.S_POLE: - if (q >= 1e-15) - { - b = Math.Sqrt(q); - x = b * sinlam; - y = coslam * (_mode == Mode.S_POLE ? b : -b); - } - else - x = y = 0.0; - break; - } + double coslam = Math.Cos(lam); + double sinlam = Math.Sin(lam); + double sinphi = Math.Sin(phi); + double q = Qsfn(sinphi, this.e, this.oneEs); - lon = x; - lat = y; + if (this.mode == Mode.OBLIQ || this.mode == Mode.EQUIT) + { + sinb = q / this.qp; + cosb = Math.Sqrt(1.0 - (sinb * sinb)); + } + switch (this.mode) + { + case Mode.OBLIQ: + b = 1.0 + (this.sinb1 * sinb) + (this.cosb1 * cosb * coslam); + break; + case Mode.EQUIT: + b = 1.0 + (cosb * coslam); + break; + case Mode.N_POLE: + b = HalfPi + phi; + q = this.qp - q; + break; + case Mode.S_POLE: + b = phi - HalfPi; + q = this.qp + q; + break; } - private void SphericalRadiansToMeters(ref double lon, ref double lat) + double x = HugeVal; + double y = HugeVal; + if (Math.Abs(b) < Eps10) { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } - double lam = adjust_lon(lon - central_meridian); - double phi = lat; + switch (this.mode) + { + case Mode.OBLIQ: + b = Math.Sqrt(2.0 / b); + y = this.ymf * b * ((this.cosb1 * sinb) - (this.sinb1 * cosb * coslam)); + goto eqcon; + case Mode.EQUIT: + b = Math.Sqrt(2.0 / (1.0 + (cosb * coslam))); + y = b * sinb * this.ymf; + eqcon: + x = this.xmf * b * cosb * sinlam; + break; + case Mode.N_POLE: + case Mode.S_POLE: + if (q >= 1e-15d) + { + b = Math.Sqrt(q); + x = b * sinlam; + y = coslam * (this.mode == Mode.S_POLE ? b : -b); + } + else + { + x = y = 0.0; + } - double sinphi = Math.Sin(phi); - double cosphi = Math.Cos(phi); - double coslam = Math.Sin(lam); + break; + } - double x = HUGE_VAL; - double y = HUGE_VAL; + lon = x; + lat = y; + } - switch (_mode) - { - case Mode.EQUIT: - y = 1.0 + cosphi * coslam; - goto oblcon; - case Mode.OBLIQ: - y = 1.0 + _sinb1 * sinphi + _cosb1 * cosphi * coslam; - oblcon: - if (y <= EPS10) - { - //proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return; - } - y = Math.Sqrt(2.0 / y); - x = y * cosphi * Math.Sin(lam); - y *= _mode == Mode.EQUIT ? sinphi : - _cosb1 * sinphi - _sinb1 * cosphi * coslam; - break; - case Mode.N_POLE: - coslam = -coslam; - goto continue_S_POLE; - /*-fallthrough*/ - case Mode.S_POLE: - continue_S_POLE: - if (Math.Abs(phi + lat_origin) < EPS10) - { - //proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return; - } - y = FORT_PI - phi * 0.5; - y = 2.0 * (_mode == Mode.S_POLE ? Math.Cos(y) : Math.Sin(y)); - x = y * Math.Sin(lam); - y *= coslam; - break; - } + private void SphericalRadiansToMeters(ref double lon, ref double lat) + { + double lam = Adjust_lon(lon - this.centralMeridian); + double phi = lat; - lon = x; - lat = y; - } - #endregion + double sinphi = Math.Sin(phi); + double cosphi = Math.Cos(phi); + double coslam = Math.Cos(lam); - #region Reverse + double x = HugeVal; + double y = HugeVal; - /// - /// Method to convert a point from meters to radians - /// - /// The x-ordinate when entering, the longitude value upon exit. - /// The y-ordinate when entering, the latitude value upon exit. - protected override void MetersToRadians(ref double x, ref double y) + switch (this.mode) { - x *= _reciprocSemiMajorTimesScaleFactor; - y *= _reciprocSemiMajorTimesScaleFactor; + case Mode.EQUIT: + y = 1.0 + (cosphi * coslam); + goto oblcon; + case Mode.OBLIQ: + y = 1.0 + (this.sinb1 * sinphi) + (this.cosb1 * cosphi * coslam); + oblcon: + if (y <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + y = Math.Sqrt(2.0 / y); + x = y * cosphi * Math.Sin(lam); + y *= this.mode == Mode.EQUIT ? sinphi : + (this.cosb1 * sinphi) - (this.sinb1 * cosphi * coslam); + break; + case Mode.N_POLE: + coslam = -coslam; + goto continue_S_POLE; + + // -fallthrough + case Mode.S_POLE: + continue_S_POLE: + if (Math.Abs(phi + this.latOrigin) < Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } - _metersToRadians(ref x, ref y); + y = FortPi - (phi * 0.5); + y = 2.0 * (this.mode == Mode.S_POLE ? Math.Cos(y) : Math.Sin(y)); + x = y * Math.Sin(lam); + y *= coslam; + break; } - private void EllipsoidalMetersToRadians(ref double x, ref double y) + lon = x; + lat = y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocSemiMajorTimesScaleFactor; + y *= this.reciprocSemiMajorTimesScaleFactor; + + this.metersToRadians(ref x, ref y); + } + + private void EllipsoidalMetersToRadians(ref double x, ref double y) + { + double ab = 0.0; + + switch (this.mode) { - double cCe, sCe, q, rho, ab = 0.0; + case Mode.EQUIT: + case Mode.OBLIQ: + x /= this.dd; + y *= this.dd; + double rho = Hypot(x, y); + if (rho < Eps10) + { + x = this.centralMeridian; // lam + y = this.latOrigin; // phi + return; + } - switch (_mode) - { - case Mode.EQUIT: - case Mode.OBLIQ: - x /= _dd; - y *= _dd; - rho = hypot(x, y); - if (rho < EPS10) - { - x = central_meridian; // lam - y = lat_origin; // phi - return; - } - sCe = 2.0 * Math.Asin(0.5 * rho / _rq); - cCe = Math.Cos(sCe); - sCe = Math.Sin(sCe); - x *= sCe; - if (_mode == Mode.OBLIQ) - { - ab = cCe * _sinb1 + y * sCe * _cosb1 / rho; - y = rho * _cosb1 * cCe - y * _sinb1 * sCe; - } - else - { - ab = y * sCe / rho; - y = rho * cCe; - } - break; - case Mode.N_POLE: - y = -y; - goto continue_S_POLE; - /*-fallthrough*/ - case Mode.S_POLE: - continue_S_POLE: - q = (x * x + y * y); - if (q == 0.0) - { - x = central_meridian; // lam - y = lat_origin; // phi - return ; - } - ab = 1.0 - q / _qp; - if (_mode == Mode.S_POLE) - ab = -ab; - break; - } + double asinArgument = 0.5 * rho / this.rq; + if (asinArgument > 1d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } - x = x = adjust_lon(Math.Atan2(x, y) + central_meridian); // lam - y = authlat(Math.Asin(ab), _apa); // phi + double sCe = 2.0 * Math.Asin(asinArgument); + double cCe = Math.Cos(sCe); + sCe = Math.Sin(sCe); + x *= sCe; + if (this.mode == Mode.OBLIQ) + { + ab = (cCe * this.sinb1) + (y * sCe * this.cosb1 / rho); + y = (rho * this.cosb1 * cCe) - (y * this.sinb1 * sCe); + } + else + { + ab = y * sCe / rho; + y = rho * cCe; + } + + break; + case Mode.N_POLE: + y = -y; + goto continue_S_POLE; + + // -fallthrough + case Mode.S_POLE: + continue_S_POLE: + double q = (x * x) + (y * y); + if (q == 0.0) + { + x = this.centralMeridian; // lam + y = this.latOrigin; // phi + return; + } + + ab = 1.0 - (q / this.qp); + if (this.mode == Mode.S_POLE) + { + ab = -ab; + } + + break; } - private void SphericalMetersToRadians(ref double x, ref double y) + x = _ = Adjust_lon(Math.Atan2(x, y) + this.centralMeridian); // lam + y = Authlat(Math.Asin(ab), ArgumentGuard.ThrowIfNull(this.apa, nameof(this.apa))); // phi + } + + private void SphericalMetersToRadians(ref double x, ref double y) + { + double rh = Hypot(x, y); + double phi = rh * .5; + double cosz = 0.0; + double sinz = 0.0; + if (phi > 1.0) { - double cosz = 0.0, rh, sinz = 0.0; + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } - rh = hypot(x, y); - double phi = rh * .5; - if (phi > 1.0) - { - x = 0; // lam - y = 0; // phi - return ; - } - phi = 2.0 * Math.Asin(phi); - if (_mode == Mode.OBLIQ || _mode == Mode.EQUIT) - { - sinz = Math.Sin(phi); - cosz = Math.Cos(phi); - } - switch (_mode) - { - case Mode.EQUIT: - phi = Math.Abs(rh) <= EPS10 ? 0.0 : Math.Asin(y * sinz / rh); - x *= sinz; - y = cosz * rh; - break; - case Mode.OBLIQ: - phi = Math.Abs(rh) <= EPS10 ? lat_origin : - Math.Asin(cosz * _sinb1 + y * sinz * _cosb1 / rh); - x *= sinz * _cosb1; - y = (cosz - Math.Sin(phi) * _sinb1) * rh; - break; - case Mode.N_POLE: - y = -y; - phi = HALF_PI - phi; - break; - case Mode.S_POLE: - phi -= HALF_PI; - break; - } + phi = 2.0 * Math.Asin(phi); + if (this.mode == Mode.OBLIQ || this.mode == Mode.EQUIT) + { + sinz = Math.Sin(phi); + cosz = Math.Cos(phi); + } - double lam = (y == 0.0 && (_mode == Mode.EQUIT || _mode == Mode.OBLIQ)) ? - 0.0 : Math.Atan2(x, y); + switch (this.mode) + { + case Mode.EQUIT: + phi = Math.Abs(rh) <= Eps10 ? 0.0 : Math.Asin(y * sinz / rh); + x *= sinz; + y = cosz * rh; + break; + case Mode.OBLIQ: + phi = Math.Abs(rh) <= Eps10 ? this.latOrigin : + Math.Asin((cosz * this.sinb1) + (y * sinz * this.cosb1 / rh)); + x *= sinz * this.cosb1; + y = (cosz - (Math.Sin(phi) * this.sinb1)) * rh; + break; + case Mode.N_POLE: + y = -y; + phi = HalfPi - phi; + break; + case Mode.S_POLE: + phi -= HalfPi; + break; + } - x = adjust_lon(lam + central_meridian); - y = phi; + double lam = (y == 0.0 && (this.mode == Mode.EQUIT || this.mode == Mode.OBLIQ)) ? + 0.0 : Math.Atan2(x, y); - } - #endregion + x = Adjust_lon(lam + this.centralMeridian); + y = phi; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/LambertConformalConic2SP.cs b/src/ProjNet/CoordinateSystems/Projections/LambertConformalConic2SP.cs index 16c1d3c8..fc1d83f0 100644 --- a/src/ProjNet/CoordinateSystems/Projections/LambertConformalConic2SP.cs +++ b/src/ProjNet/CoordinateSystems/Projections/LambertConformalConic2SP.cs @@ -1,257 +1,209 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Lambert Conformal Conic 2SP Projection. +/// +/// +/// The Lambert Conformal Conic projection is a standard projection for presenting maps +/// of land areas whose East-West extent is large compared with their North-South extent. +/// This projection is "conformal" in the sense that lines of latitude and longitude, +/// which are perpendicular to one another on the earth's surface, are also perpendicular +/// to one another in the projected domain. +/// The 2SP formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG method 9802, Lambert Conic Conformal (2SP). +/// The defining n, F, r, and θ relationships match +/// the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 15, pp. 104-110, +/// eqs. (15-1) through (15-11), for the classic Lambert conformal conic +/// development. +/// +/// EPSG method 9802: Lambert Conic Conformal (2SP). +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.1.2, pp. 90-93. +internal sealed class LambertConformalConic2SP : MapProjection { - - /// - /// Implements the Lambert Conformal Conic 2SP Projection. - /// - /// - /// The Lambert Conformal Conic projection is a standard projection for presenting maps - /// of land areas whose East-West extent is large compared with their North-South extent. - /// This projection is "conformal" in the sense that lines of latitude and longitude, - /// which are perpendicular to one another on the earth's surface, are also perpendicular - /// to one another in the projected domain. + private static readonly string[] LatitudeOfOriginFallback = ["latitude_of_origin"]; + + /// + /// Ratio of angular change between meridians. + /// + private readonly double ns; + + /// + /// Projection constant derived from standard parallels. + /// + private readonly double f0; + + /// + /// Radial distance at the latitude of origin. + /// + private readonly double rh; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The parameters this projection expects are listed below. + /// + /// ItemsDescriptions + /// latitude_of_false_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// longitude_of_false_originThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// latitude_of_1st_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. + /// latitude_of_2nd_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. + /// easting_at_false_originThe easting value assigned to the false origin. + /// northing_at_false_originThe northing value assigned to the false origin. + /// /// - [Serializable] - internal class LambertConformalConic2SP : MapProjection - { - - //private double readonly _falseEasting; - //private double readonly _falseNorthing; - - //private readonly double es; /* eccentricity squared */ - //private readonly double e; /* eccentricity */ - //private readonly double center_lon; /* center longitude */ - //private readonly double center_lat; /* center latitude */ - private readonly double _ns; /* ratio of angle between meridian */ - private readonly double _f0; /* flattening of ellipsoid */ - private readonly double _rh; /* height above ellipsoid */ + /// List of parameters to initialize the projection. + public LambertConformalConic2SP(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The parameters this projection expects are listed below. + /// + /// ParameterDescription + /// latitude_of_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// central_meridianThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. + /// standard_parallel_1For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. + /// standard_parallel_2For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. + /// false_eastingThe easting value assigned to the false origin. + /// false_northingThe northing value assigned to the false origin. + /// + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + private LambertConformalConic2SP(IEnumerable parameters, LambertConformalConic2SP? inverse) + : base(parameters, inverse) + { + this.Name = "Lambert_Conformal_Conic_2SP"; + this.Authority = "EPSG"; + this.AuthorityCode = 9802; + + // This implementation supports both 2SP and 1SP-style inputs by falling back to latitude_of_origin. + double lat1 = DegreesToRadians(this.Parameters.GetParameterValue("standard_parallel_1", LatitudeOfOriginFallback)); + double lat2 = DegreesToRadians(this.Parameters.GetParameterValue("standard_parallel_2", LatitudeOfOriginFallback)); + + // Standard parallels cannot be equal and on opposite sides of the equator. + if (Math.Abs(lat1 + lat2) < Epsln) + { + ArgumentGuard.ThrowArgument("Equal latitudes for St. Parallels on opposite sides of equator.", nameof(parameters)); + } - #region Constructors + Sincos(lat1, out double sinLatitude1, out double cosLatitude1); + double ms1 = Msfnz(this.e, sinLatitude1, cosLatitude1); + double ts1 = Tsfnz(this.e, lat1, sinLatitude1); - /// - /// Creates an instance of an LambertConformalConic2SPProjection projection object. - /// - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// latitude_of_false_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// longitude_of_false_originThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// latitude_of_1st_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. - /// latitude_of_2nd_standard_parallelFor a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. - /// easting_at_false_originThe easting value assigned to the false origin. - /// northing_at_false_originThe northing value assigned to the false origin. - /// - /// - /// List of parameters to initialize the projection. - public LambertConformalConic2SP(IEnumerable parameters) - : this(parameters,null) - { - } - - /// - /// Creates an instance of an Albers projection object. - /// - /// - /// The parameters this projection expects are listed below. - /// - /// ParameterDescription - /// latitude_of_originThe latitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// central_meridianThe longitude of the point which is not the natural origin and at which grid coordinate values false easting and false northing are defined. - /// standard_parallel_1For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole. Scale is true along this parallel. - /// standard_parallel_2For a conic projection with two standard parallels, this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole. Scale is true along this parallel. - /// false_eastingThe easting value assigned to the false origin. - /// false_northingThe northing value assigned to the false origin. - /// - /// - /// List of parameters to initialize the projection. - /// Indicates whether the projection forward (meters to degrees or degrees to meters). - protected LambertConformalConic2SP(IEnumerable parameters, LambertConformalConic2SP inverse) - : base(parameters, inverse) - { - Name = "Lambert_Conformal_Conic_2SP"; - Authority = "EPSG"; - AuthorityCode = 9802; + Sincos(lat2, out double sinLatitude2, out double cosLatitude2); + double ms2 = Msfnz(this.e, sinLatitude2, cosLatitude2); + double ts2 = Tsfnz(this.e, lat2, sinLatitude2); - //Check for missing parameters - //Since this implementation supports conic 1SP and 2SP we add the support for the 1SP implementation here. - //There is no need for standard_parallel_1 and standard_parallel_2 parameters in this version: https://pro.arcgis.com/en/pro-app/latest/help/mapping/properties/lambert-conformal-conic.htm - double lat1 = DegreesToRadians(_Parameters.GetParameterValue("standard_parallel_1", new[] { "latitude_of_origin" })); - double lat2 = DegreesToRadians(_Parameters.GetParameterValue("standard_parallel_2", new[] { "latitude_of_origin" })); + double sinLatitudeOrigin = Math.Sin(this.latOrigin); + double ts0 = Tsfnz(this.e, this.latOrigin, sinLatitudeOrigin); - double sin_po; /* sin value */ - double cos_po; /* cos value */ - double con; /* temporary variable */ - double ms1; /* small m 1 */ - double ms2; /* small m 2 */ - double ts0; /* small t 0 */ - double ts1; /* small t 1 */ - double ts2; /* small t 2 */ + if (Math.Abs(lat1 - lat2) > Epsln) + { + this.ns = Math.Log(ms1 / ms2) / Math.Log(ts1 / ts2); + } + else + { + this.ns = sinLatitude1; + } + this.f0 = ms1 / (this.ns * Math.Pow(ts1, this.ns)); + this.rh = this.semiMajor * this.f0 * Math.Pow(ts0, this.ns); + } + + /// + /// Method to convert a point (lon, lat) in radians to (x, y) in meters. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double longitude = lon; + double latitude = lat; + double radialDistance = 0d; + + double latitudeDistanceFromPole = Math.Abs(Math.Abs(latitude) - HalfPi); + if (latitudeDistanceFromPole > Epsln) + { + double sinLatitude = Math.Sin(latitude); + double ts = Tsfnz(this.e, latitude, sinLatitude); + radialDistance = this.semiMajor * this.f0 * Math.Pow(ts, this.ns); + } + else + { + double signedLatitude = latitude * this.ns; + if (signedLatitude <= 0) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(lat), "Latitude is outside the valid range for this projection."); + } + radialDistance = 0; + } - /* Standard Parallels cannot be equal and on opposite sides of the equator - ------------------------------------------------------------------------*/ - if (Math.Abs(lat1+lat2) < EPSLN) - { - //Debug.Assert(true,"LambertConformalConic:LambertConformalConic() - Equal Latitiudes for St. Parallels on opposite sides of equator"); - throw new ArgumentException("Equal latitudes for St. Parallels on opposite sides of equator."); - } + double theta = this.ns * Adjust_lon(longitude - this.centralMeridian); - sincos(lat1,out sin_po,out cos_po); - con = sin_po; - ms1 = msfnz(_e,sin_po,cos_po); - ts1 = tsfnz(_e,lat1,sin_po); - sincos(lat2,out sin_po,out cos_po); - ms2 = msfnz(_e,sin_po,cos_po); - ts2 = tsfnz(_e,lat2,sin_po); - sin_po = Math.Sin(lat_origin); - ts0 = tsfnz(_e,lat_origin,sin_po); + lon = this.scaleFactor * radialDistance * Math.Sin(theta); + lat = this.scaleFactor * (this.rh - (radialDistance * Math.Cos(theta))); + } - if (Math.Abs(lat1 - lat2) > EPSLN) - _ns = Math.Log(ms1/ms2)/ Math.Log (ts1/ts2); - else - _ns = con; - _f0 = ms1 / (_ns * Math.Pow(ts1,_ns)); - _rh = _semiMajor * _f0 * Math.Pow(ts0,_ns); - } - #endregion + /// + /// Method to convert a point from meters to radians. + /// + /// The x-ordinate when entering, the longitude value upon exit. + /// The y-ordinate when entering, the latitude value upon exit. + protected override void MetersToRadians(ref double x, ref double y) + { + double dX = x / this.scaleFactor; + double dY = this.rh - (y / this.scaleFactor); + double sign = this.ns > 0 ? 1.0 : -1.0; + double radialDistance = sign * Math.Sqrt((dX * dX) + (dY * dY)); + double theta = radialDistance != 0 ? Math.Atan2(sign * dX, sign * dY) : 0.0; - /// - /// Method to convert a point (lon, lat) in radians to (x, y) in meters - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - protected override void RadiansToMeters(ref double lon, ref double lat) + if ((radialDistance != 0) || (this.ns > 0.0)) { - double dLongitude = lon; - double dLatitude = lat; - - double con; /* temporary angle variable */ - double rh1; /* height above ellipsoid */ - double sinphi; /* sin value */ - double theta; /* angle */ - double ts; /* small value t */ - - - con = Math.Abs(Math.Abs(dLatitude) - HALF_PI); - if (con > EPSLN) + double exponent = 1.0 / this.ns; + double ts = Math.Pow(radialDistance / (this.semiMajor * this.f0), exponent); + y = Phi2z(this.e, ts, out long flag); + if (flag != 0) { - sinphi = Math.Sin(dLatitude); - ts = tsfnz(_e, dLatitude, sinphi); - rh1 = _semiMajor * _f0 * Math.Pow(ts, _ns); + ProjectionThrowHelper.ThrowInvalidOperation("Inverse projection failed to converge."); } - else - { - con = dLatitude * _ns; - if (con <= 0) - throw new ArgumentException(); - rh1 = 0; - } - - theta = _ns * adjust_lon(dLongitude - central_meridian); - - lon = rh1 * Math.Sin(theta); - lat = _rh - rh1 * Math.Cos(theta); } - - /// - /// Method to convert a point from meters to radians - /// - /// The x-ordinate when entering, the longitude value upon exit. - /// The y-ordinate when entering, the latitude value upon exit. - protected override void MetersToRadians(ref double x, ref double y) + else { - double rh1; /* height above ellipsoid */ - double con; /* sign variable */ - double ts; /* small t */ - double theta; /* angle */ - //long flag; /* error flag */ - - double dX = x; - double dY = _rh - y; - if (_ns > 0) - { - rh1 = Math.Sqrt(dX * dX + dY * dY); - con = 1.0; - } - else - { - rh1 = -Math.Sqrt(dX * dX + dY * dY); - con = -1.0; - } - - theta = 0.0; - if (rh1 != 0) - theta = Math.Atan2((con * dX), (con * dY)); - if ((rh1 != 0) || (_ns > 0.0)) - { - con = 1.0 / _ns; - ts = Math.Pow((rh1 / (_semiMajor * _f0)), con); - y = phi2z(_e, ts, out long flag); - if (flag != 0) - throw new ArgumentException(); - } - else y = -HALF_PI; + y = -HalfPi; + } - x = adjust_lon(theta / _ns + central_meridian); + x = Adjust_lon((theta / this.ns) + this.centralMeridian); + } - //return (x, y, z); - } + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= new LambertConformalConic2SP(this.Parameters.ToProjectionParameter(), this); - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse == null) - { - _inverse = new LambertConformalConic2SP(_Parameters.ToProjectionParameter(), this); - } - return _inverse; - } - } + return this.inverse; + } } diff --git a/src/ProjNet/CoordinateSystems/Projections/LambertConformalConicAlternativeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LambertConformalConicAlternativeProjection.cs new file mode 100644 index 00000000..0ceb04fb --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LambertConformalConicAlternativeProjection.cs @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Lambert Conformal Conic Alternative projection (lcca). +/// +/// +/// Differs from the standard Lambert Conformal Conic in that it is defined by a +/// single latitude of origin (lat_0, which must be non-zero) rather than two +/// standard parallels. The inverse transform uses Newton-Raphson iteration (up to +/// 10 steps) to recover the meridian arc length. +/// This historical alternative variant was independently verified against PROJ's +/// lcca implementation and the general Lambert conformal conic treatment in +/// Snyder, "Map Projections - A Working Manual" (USGS Professional Paper 1395, 1987). +/// The forward path applies the same cubic radial correction f(S) = S * (1 + S² * C) +/// as the PROJ reference, and the inverse path uses Newton-Raphson iteration on that +/// correction before Inv_mlfn, matching the implementation here. This +/// alternative projection has no dedicated EPSG method; EPSG method 9826 is Lambert +/// Conic Conformal (West Orientated) and is not the same operation. +/// +/// PROJ documentation: Lambert Conformal Conic Alternative. +/// USGS Professional Paper 1395: Map Projections - A Working Manual. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.1.2, pp. 90-93. +internal sealed class LambertConformalConicAlternativeProjection : MapProjection +{ + private const int MaximumIterations = 10; + private const double DeltaTolerance = ProjectionConstants.Tolerance1E12; + + private readonly double l; + private readonly double m0; + private readonly double r0; + private readonly double c; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LambertConformalConicAlternativeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LambertConformalConicAlternativeProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Lambert_Conformal_Conic_Alternative"; + + if (Math.Abs(this.latOrigin) < Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_0: it should be different from 0.", nameof(parameters)); + } + + Sincos(this.latOrigin, out this.l, out double cosLatitudeOrigin); + this.m0 = this.Mlfn(this.latOrigin, this.l, cosLatitudeOrigin); + + double s2p0 = this.l * this.l; + double r = 1d / (1d - (this.es * s2p0)); + double n0 = Math.Sqrt(r); + r *= (1d - this.es) * n0; + double tan0 = Math.Tan(this.latOrigin); + this.r0 = n0 / tan0; + this.c = 1d / (6d * r * n0); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new LambertConformalConicAlternativeProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double s = this.Mlfn(lat, Math.Sin(lat), Math.Cos(lat)) - this.m0; + double dr = Fs(s, this.c); + double r = this.r0 - dr; + double theta = lambda * this.l; + + lon = this.SphericalRadius * (r * Math.Sin(theta)); + lat = this.SphericalRadius * (this.r0 - (r * Math.Cos(theta))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + double theta = Math.Atan2(xUnit, this.r0 - yUnit); + double dr = yUnit - (xUnit * Math.Tan(0.5d * theta)); + double lambda = theta / this.l; + + double s = dr; + int iteration = 0; + for (; iteration < MaximumIterations; iteration++) + { + double diff = (Fs(s, this.c) - dr) / Fsp(s, this.c); + s -= diff; + if (Math.Abs(diff) < DeltaTolerance) + { + break; + } + } + + if (iteration == MaximumIterations) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double phi = this.Inv_mlfn(s + this.m0); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static double Fs(double s, double c) + { + return s * (1d + ((s * s) * c)); + } + + private static double Fsp(double s, double c) + { + return 1d + (3d * s * s * c); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LambertEqualAreaConicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LambertEqualAreaConicProjection.cs new file mode 100644 index 00000000..16a8eb97 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LambertEqualAreaConicProjection.cs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements PROJ's leac projection by normalizing parameters to the Albers implementation. +/// +/// +/// Maps the appropriate pole (north or south) as the first Albers standard parallel and the +/// user-supplied lat_1 as the second. Set the south parameter to a non-zero +/// value to select the southern hemisphere variant. +/// This class delegates its numerical work to after +/// normalizing the pole and standard-parallel parameters, so its behavior is covered by +/// the independently verified Albers equal-area formulation implemented there. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.1.3, pp. 93-95. +internal sealed class LambertEqualAreaConicProjection : AlbersProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LambertEqualAreaConicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + private LambertEqualAreaConicProjection(IEnumerable parameters, LambertEqualAreaConicProjection? inverse) + : base(NormalizeParameters(parameters), inverse) + { + this.Name = "Lambert_Equal_Area_Conic"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new LambertEqualAreaConicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + private static IEnumerable NormalizeParameters(IEnumerable parameters) + { + var normalized = new ProjectionParameterSet(parameters); + double lat1 = normalized.GetParameterValue("lat_1", "standard_parallel_1"); + bool south = Math.Abs(normalized.GetOptionalParameterValue("south", 0d)) > 0d; + + // PROJ leac uses the pole (+90 / -90) as first standard parallel and lat_1 as second. + normalized.SetParameterValue("lat_1", lat1); + normalized.SetParameterValue("standard_parallel_1", south ? -90d : 90d); + normalized.SetParameterValue("standard_parallel_2", lat1); + return normalized.ToProjectionParameter(); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LarriveeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LarriveeProjection.cs new file mode 100644 index 00000000..34f8faf3 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LarriveeProjection.cs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Larrivee projection (larr). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the published Larrivee +/// equations. The implementation matches the characteristic +/// x = 0.5 * λ * (1 + sqrt(cos(φ))) term and the denominator used for the +/// corresponding y coordinate. +/// +internal sealed class LarriveeProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LarriveeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LarriveeProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Larrivee"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new LarriveeProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double denominator = Math.Cos(0.5d * lat) * Math.Cos(ProjectionConstants.OneSixth * lambda); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double x = 0.5d * lambda * (1d + Math.Sqrt(Math.Cos(lat))); + double y = lat / denominator; + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Larrivee does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LaskowskiProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LaskowskiProjection.cs new file mode 100644 index 00000000..a8b7d7ba --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LaskowskiProjection.cs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Laskowski projection (lask). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the published Laskowski +/// polynomial. The implementation matches the tabulated x/y coefficient set used for the +/// fifth-order pseudocylindrical approximation. +/// +internal sealed class LaskowskiProjection : MapProjection +{ + private const double A10 = 0.975534d; + private const double A12 = -0.119161d; + private const double A32 = -0.0143059d; + private const double A14 = -0.0547009d; + private const double B01 = 1.00384d; + private const double B21 = 0.0802894d; + private const double B03 = 0.0998909d; + private const double B41 = 0.000199025d; + private const double B23 = -0.02855d; + private const double B05 = -0.0491032d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LaskowskiProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LaskowskiProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Laskowski"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new LaskowskiProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double l2 = lambda * lambda; + double p2 = lat * lat; + double x = lambda * (A10 + (p2 * (A12 + (l2 * A32) + (p2 * A14)))); + double y = lat * (B01 + (l2 * (B21 + (p2 * B23) + (l2 * B41))) + (p2 * (B03 + (p2 * B05)))); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Laskowski does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LatLongProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LatLongProjection.cs new file mode 100644 index 00000000..23b2f2f3 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LatLongProjection.cs @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Represents the geographic identity projection used by PROJ's latlong/longlat aliases. +/// +/// +/// This projection is the geographic identity mapping: longitude and latitude are +/// passed through unchanged except for the configured central-meridian and +/// latitude-of-origin offsets. It therefore corresponds to the trivial relation +/// x = lon, y = lat in normalized geographic coordinates. +/// It serves as the managed equivalent of PROJ's latlong/longlat +/// aliases and is the degenerate plate carrée identity case commonly used for geographic +/// coordinate display and raster indexing rather than for distortion control. +/// +/// Wikipedia: Equirectangular projection. +internal sealed class LatLongProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LatLongProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LatLongProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "LatLong"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new LatLongProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + lon -= this.centralMeridian; + lat -= this.latOrigin; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x += this.centralMeridian; + y += this.latOrigin; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LeeOblatedStereographicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LeeOblatedStereographicProjection.cs new file mode 100644 index 00000000..c0e936ff --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LeeOblatedStereographicProjection.cs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Lee Oblated Stereographic projection (lee_os). +/// +/// +/// Lee Oblated Stereographic is a two-coefficient specialization of +/// attributed to Lee. It uses the shared +/// modified-stereographic polynomial correction with Lee's coefficient set and origin. +/// +internal sealed class LeeOblatedStereographicProjection : ModifiedStereographicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LeeOblatedStereographicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LeeOblatedStereographicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Lee_Oblated_Stereographic") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new LeeOblatedStereographicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void ConfigureVariant( + out double lambda0, + out double phi0, + out double semiMajor, + out double es, + out ComplexNumber[] coefficients, + out int polynomialOrder) + { + lambda0 = DegreesToRadians(-165d); + phi0 = DegreesToRadians(-10d); + semiMajor = this.semiMajor; + es = 0d; + coefficients = GetLeeOsCoefficients(); + polynomialOrder = 2; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/LoximuthalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/LoximuthalProjection.cs new file mode 100644 index 00000000..f2246716 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/LoximuthalProjection.cs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Loximuthal projection (loxim). +/// +/// +/// The Loximuthal projection preserves the shape of loxodromes (rhumb lines) as straight lines +/// emanating from a user-defined reference latitude (lat_1). The reference latitude must +/// not be at the poles. +/// The forward formulation was independently verified against the standard loximuthal +/// relation x = λ * (φ - phi1) / ln(tan(π / 4 + φ / 2) / tan(π / 4 + phi1 / 2)), +/// including the limiting case x = λ * cos(phi1) at the reference latitude. +/// +internal sealed class LoximuthalProjection : MapProjection +{ + private readonly double referenceLatitude; + private readonly double referenceMercatorTerm; + private readonly double cosReferenceLatitude; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public LoximuthalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public LoximuthalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Loximuthal"; + + this.referenceLatitude = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lat_1", RadiansToDegrees(this.latOrigin), "standard_parallel_1", "latitude_of_origin")); + this.cosReferenceLatitude = Math.Cos(this.referenceLatitude); + if (Math.Abs(Math.Abs(this.referenceLatitude) - HalfPi) <= Epsln) + { + ArgumentGuard.ThrowArgument("The reference latitude cannot be at the poles.", nameof(parameters)); + } + + this.referenceMercatorTerm = Math.Log(Math.Tan(FortPi + (0.5d * this.referenceLatitude))); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new LoximuthalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double deltaPhi = phi - this.referenceLatitude; + lat = this.SphericalRadius * deltaPhi; + + if (Math.Abs(deltaPhi) <= Eps10) + { + lon = this.SphericalRadius * lambda * this.cosReferenceLatitude; + return; + } + + double mercatorTerm = Math.Log(Math.Tan(FortPi + (0.5d * phi))); + double denominator = mercatorTerm - this.referenceMercatorTerm; + if (Math.Abs(denominator) <= Eps10) + { + lon = this.SphericalRadius * lambda * this.cosReferenceLatitude; + return; + } + + lon = this.SphericalRadius * lambda * deltaPhi / denominator; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double lat = this.referenceLatitude + (y * this.InverseSphericalRadius); + double deltaPhi = lat - this.referenceLatitude; + + double lambda = x * this.InverseSphericalRadius / this.cosReferenceLatitude; + if (Math.Abs(deltaPhi) > Eps10) + { + double mercatorTerm = Math.Log(Math.Tan(FortPi + (0.5d * lat))); + double numerator = mercatorTerm - this.referenceMercatorTerm; + if (Math.Abs(numerator) > Eps10) + { + lambda = (x * this.InverseSphericalRadius) * numerator / deltaPhi; + } + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = lat; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/MapProjection.LegacyAliases.cs b/src/ProjNet/CoordinateSystems/Projections/MapProjection.LegacyAliases.cs new file mode 100644 index 00000000..dcccef66 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/MapProjection.LegacyAliases.cs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Diagnostics.CodeAnalysis; + +// Legacy backward-compatible aliases are isolated in this partial file for easy removal in a future major version. +#pragma warning disable CS1591 // Legacy alias members intentionally omit duplicate XML docs; canonical members remain documented on the main partial. +#pragma warning disable CA1707 // Legacy alias names intentionally preserve underscores for backward compatibility. +#pragma warning disable SA1300 // Legacy alias properties intentionally preserve historic lower_snake_case names. +#pragma warning disable SA1303 // Legacy alias constants intentionally preserve historic all-caps naming. +#pragma warning disable SA1307 // Legacy alias constants intentionally preserve historic naming rather than the current style. +#pragma warning disable SA1310 // Legacy alias names intentionally preserve underscores for backward compatibility. +#pragma warning disable IDE1006 // Legacy alias members intentionally preserve historic naming rather than the current style. +#pragma warning disable SA1600 // Legacy alias members intentionally omit duplicate XML docs; canonical members remain documented on the main partial. +#pragma warning disable SA1601 // Legacy alias partial intentionally keeps documentation on the canonical main partial declaration. + +[SuppressMessage("Naming", "CA1708:Identifiers should differ by more than case", Justification = "Obsolete compatibility aliases intentionally preserve legacy all-caps names alongside PascalCase names.")] +public abstract partial class MapProjection +{ + [Obsolete("Use FortPi instead.")] + protected const double FORTPI = FortPi; + + [Obsolete("Use HalfPi instead.")] + protected const double HALFPI = HalfPi; + + [Obsolete("Use HugeVal instead.")] + protected const double HUGEVAL = HugeVal; + + [Obsolete("Use MaxVal instead.")] + protected const double MAXVAL = MaxVal; + + [Obsolete("Use TwoPi instead.")] + protected const double TWOPI = TwoPi; + + [Obsolete("Use Eps10 instead.")] + protected const double EPS10 = Eps10; + + [Obsolete("Use Eps7 instead.")] + protected const double EPS7 = Eps7; + + [Obsolete("Use Epsln instead.")] + protected const double EPSLN = Epsln; + + [Obsolete("Use DblLong instead.")] + protected const double DBLLONG = DblLong; + + [Obsolete("Use FortPi instead.")] + protected const double FORT_PI = FortPi; + + [Obsolete("Use HalfPi instead.")] + protected const double HALF_PI = HalfPi; + + [Obsolete("Use HugeVal instead.")] + protected const double HUGE_VAL = HugeVal; + + [Obsolete("Use MaxVal instead.")] + protected const double MAX_VAL = MaxVal; + + [Obsolete("Use TwoPi instead.")] + protected const double TWO_PI = TwoPi; + + [Obsolete("Use centralMeridian instead.")] + protected double central_meridian + { + get => this.centralMeridian; + set => this.centralMeridian = value; + } + + [Obsolete("Use falseEasting instead.")] + protected double false_easting => this.falseEasting; + + [Obsolete("Use falseNorthing instead.")] + protected double false_northing => this.falseNorthing; + + [Obsolete("Use latOrigin instead.")] + protected double lat_origin => this.latOrigin; + + [Obsolete("Use scaleFactor instead.")] + protected double scale_factor => this.scaleFactor; +} + +#pragma warning restore SA1600 +#pragma warning restore SA1601 +#pragma warning restore IDE1006 +#pragma warning restore SA1310 +#pragma warning restore SA1307 +#pragma warning restore SA1303 +#pragma warning restore SA1300 +#pragma warning restore CA1707 +#pragma warning restore CS1591 diff --git a/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs b/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs index 4dac2046..78b37f9b 100644 --- a/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/MapProjection.cs @@ -1,1210 +1,1224 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; -using System.Globalization; -using System.Text; +using System.Threading; +using System.Xml.Linq; using ProjNet.CoordinateSystems.Transformations; - -namespace ProjNet.CoordinateSystems.Projections +using ProjNet.IO.Wkt; + +/// +/// Abstract base class for all map projections, providing shared mathematical utilities and +/// coordinate transformation infrastructure. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 1, pp. 7-38. +/// Snyder, J. P. (1987), "Map Projections - A Working Manual", USGS Professional Paper 1395. +/// OGC 01-009, "Coordinate Transformation Services". +[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Legacy PROJ-compatible API surface is preserved for compatibility.")] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Legacy PROJ-compatible API surface is preserved for compatibility.")] +public abstract partial class MapProjection : MathTransform, IProjection { /// - /// Projections inherit from this abstract class to get access to useful mathematical functions. + /// Tolerance constant equal to 1e-10, used for near-zero comparisons in projection formulas. /// - [Serializable] - public abstract class MapProjection : MathTransform, IProjection - { - /// - /// EPS10 => 1e-10. - /// - protected const double EPS10 = 1e-10; - - /// - /// EPS7 => 1e-7. - /// - protected const double EPS7 = 1e-7; - - /// - /// HUGE_VAL => double.NaN. - /// - protected const double HUGE_VAL = double.NaN; - - // ReSharper disable InconsistentNaming - /// - /// Eccentricity - /// - protected readonly double _e; - /// - /// Eccentricity squared _e * _e - /// - protected readonly double _es; - - /// - /// Length of semi major axis of ellipse - /// - protected readonly double _semiMajor; - - /// - /// Length of semi minor axis of ellipse - /// - protected readonly double _semiMinor; - - /// - /// Meters per unit - /// - protected readonly double _metersPerUnit; - - /// - /// Reciprocal meters per unit 1.0 / - /// - protected readonly double _reciprocalMetersPerUnit; - - /// - /// Scale factor - /// - protected readonly double scale_factor; /* scale factor */ - - /// - /// Center longitude (projection center) - /// - protected double central_meridian; /* Center longitude (projection center) */ - - /// - /// Substitute for - /// - protected double lon_origin { get { return central_meridian; } set { central_meridian = value; } } - - /// - /// Center latitude (projection center), same as lat_origin - /// - protected double central_parallel { get { return lat_origin; } } - - /// - /// Center latitude (projection center), same as lat_origin - /// - protected double phi0 { get { return lat_origin; } } - - /// - /// Center latitude - /// - protected readonly double lat_origin; /* center latitude */ - - /// - /// Y offset in meters - /// - protected readonly double false_northing; /* y offset in meters */ - - /// - /// X offset in meters - /// - protected readonly double false_easting; /* x offset in meters */ - - /// - /// Constants for - /// - protected readonly double en0, en1, en2, en3, en4; - - /// - /// A set of projection parameters for this projection - /// - protected readonly ProjectionParameterSet _Parameters; - - /// - /// The inverse - /// - protected MathTransform _inverse; - - // ReSharper restore InconsistentNaming - - /// - /// Creates an instance of this class - /// - /// An enumeration of projection parameters - /// Indicator if this projection is inverse - protected MapProjection(IEnumerable parameters, MapProjection inverse) - : this(parameters) - { - _inverse = inverse; - if (_inverse != null) - { - inverse._inverse = this; - IsInverse = !inverse.IsInverse; - } - } + protected const double Eps10 = 1e-10d; - /// - /// Creates an instance of this class - /// - /// An enumeration of projection parameters - protected MapProjection(IEnumerable parameters) - { - _Parameters = new ProjectionParameterSet(parameters); + /// + /// Tolerance constant equal to 1e-7, used for near-zero comparisons in projection formulas. + /// + protected const double Eps7 = 1e-7d; - _semiMajor = _Parameters.GetParameterValue("semi_major"); - _semiMinor = _Parameters.GetParameterValue("semi_minor"); + /// + /// Sentinel value equal to , used to signal an undefined or out-of-range projection result. + /// + protected const double HugeVal = double.NaN; - //_es = 1.0 - (_semiMinor * _semiMinor) / (_semiMajor * _semiMajor); - _es = EccentricySquared(_semiMajor, _semiMinor); - _e = Math.Sqrt(_es); + /// + /// The constant π, equal to . + /// + protected const double PI = Math.PI; - scale_factor = _Parameters.GetOptionalParameterValue("scale_factor", 1); + /// + /// A fourth of . + /// + protected const double FortPi = PI * 0.25; - central_meridian = DegreesToRadians(_Parameters.GetParameterValue("central_meridian", "longitude_of_center")); - lat_origin = DegreesToRadians(_Parameters.GetOptionalParameterValue("latitude_of_origin", 0d, "latitude_of_center")); + /// + /// Half of PI. + /// + protected const double HalfPi = PI * 0.5; - _metersPerUnit = _Parameters.GetParameterValue("unit"); - _reciprocalMetersPerUnit = 1 / _metersPerUnit; + /// + /// PI * 2. + /// + protected const double TwoPi = PI * 2.0; - false_easting = _Parameters.GetOptionalParameterValue("false_easting", 0) * _metersPerUnit; - false_northing = _Parameters.GetOptionalParameterValue("false_northing", 0) * _metersPerUnit; + /// + /// Tolerance threshold for near-zero comparisons; equal to . + /// + protected const double Epsln = Eps10; - // TODO: Should really convert to the correct linear units?? + /// + /// Maximum iteration count used in longitude normalisation loops. + /// + protected const double MaxVal = 4; - // Compute constants for the mlfn - double t; - en0 = C00 - _es * (C02 + _es * - (C04 + _es * (C06 + _es * C08))); - en1 = _es * (C22 - _es * - (C04 + _es * (C06 + _es * C08))); - en2 = (t = _es * _es) * - (C44 - _es * (C46 + _es * C48)); - en3 = (t *= _es) * (C66 - _es * C68); - en4 = t * _es * C88; + /// + /// Maximum 32-bit integer value (2 147 483 647) used as a scale threshold in longitude normalisation. + /// + protected const double prjMAXLONG = 2147483647; - } + /// + /// Large double constant used as an upper-bound threshold in longitude normalisation. + /// + protected const double DblLong = 4.61168601e18d; - /// - /// Returns a list of projection "cloned" projection parameters - /// - /// - protected internal static List CloneParametersList( - IEnumerable projectionParameters) - { - var res = new List(); - foreach (var pp in projectionParameters) - res.Add(new ProjectionParameter(pp.Name, pp.Value)); - return res; - } +#pragma warning disable IDE1006 // Naming Styles + /// + /// Eccentricity. + /// + protected readonly double e; + /// + /// Square of . + /// + protected readonly double es; - #region Implementation of IProjection + /// + /// Length of semi major axis of ellipse. + /// + protected readonly double semiMajor; - /// - /// Gets the projection classification name (e.g. 'Transverse_Mercator'). - /// - public string ClassName - { - get { return Name; } - } + /// + /// Length of semi minor axis of ellipse. + /// + protected readonly double semiMinor; - /// - /// - /// - /// - /// - public ProjectionParameter GetParameter(int index) - { - return _Parameters.GetAtIndex(index); - } + /// + /// Meters per unit. + /// + protected readonly double metersPerUnit; - /// - /// Gets an named parameter of the projection. - /// - /// The parameter name is case insensitive - /// Name of parameter - /// parameter or null if not found - public ProjectionParameter GetParameter(string name) - { - return _Parameters.Find(name); - } + /// + /// Reciprocal meters per unit 1.0 / . + /// + protected readonly double reciprocalMetersPerUnit; - /// - /// - /// - public int NumParameters - { - get { return _Parameters.Count; } - } + /// + /// Scale factor. + /// + protected readonly double scaleFactor; - /// - /// Gets or sets the abbreviation of the object. - /// - public string Abbreviation { get; set; } - - /// - /// Gets or sets the alias of the object. - /// - public string Alias { get; set; } - - /// - /// Gets or sets the authority name for this object, e.g., "EPSG", - /// is this is a standard object with an authority specific - /// identity code. Returns "CUSTOM" if this is a custom object. - /// - public string Authority { get; set; } - - /// - /// Gets or sets the authority specific identification code of the object - /// - public long AuthorityCode { get; set; } - - /// - /// Gets or sets the name of the object. - /// - public string Name { get; set; } - - /// - /// Gets or sets the provider-supplied remarks for the object. - /// - public string Remarks { get; set; } - - - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - if (IsInverse) - sb.Append("INVERSE_MT["); - sb.AppendFormat("PARAM_MT[\"{0}\"", Name); - for (int i = 0; i < NumParameters; i++) - sb.AppendFormat(", {0}", GetParameter(i).WKT); - //if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - // sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - if (IsInverse) - sb.Append("]"); - return sb.ToString(); - } - } + /// + /// Center latitude. + /// + protected readonly double latOrigin; - /// - /// Gets an XML representation of this object - /// - public override string XML - { - get - { - var sb = new StringBuilder(); - sb.Append(""); - sb.AppendFormat( - IsInverse - ? "" - : "", ClassName); - for (int i = 0; i < NumParameters; i++) - sb.AppendFormat(GetParameter(i).XML); - sb.Append(IsInverse ? "" : ""); - sb.Append(""); - return sb.ToString(); - } - } + /// + /// Y offset in meters. + /// + protected readonly double falseNorthing; - #endregion + /// + /// X offset in meters. + /// + protected readonly double falseEasting; - #region IMathTransform + /// + /// Coefficient 0 for . + /// + protected readonly double en0; - /// - public sealed override int DimSource - { - get { return 2; } - } + /// + /// Coefficient 1 for . + /// + protected readonly double en1; - /// - public sealed override int DimTarget - { - get { return 2; } - } + /// + /// Coefficient 2 for . + /// + protected readonly double en2; - #region Transform overrides + /// + /// Coefficient 3 for . + /// + protected readonly double en3; - /// - public sealed override void Transform(ref double x, ref double y, ref double z) - { - if (IsInverse) - { - SourceToDegrees(ref x, ref y); - } - else - { - DegreesToTarget(ref x, ref y); - } - } + /// + /// Coefficient 4 for . + /// + protected readonly double en4; + + /// + /// A set of projection parameters for this projection. + /// + protected readonly ProjectionParameterSet Parameters; + /// + /// The inverse . + /// + protected MathTransform? inverse; + + /// + /// Center longitude (projection center). + /// + protected double centralMeridian; + + private const double C00 = 1.0; + private const double C02 = 0.25; + private const double C04 = 0.046875; + private const double C06 = 0.01953125; + private const double C08 = 0.01068115234375; + private const double C22 = 0.75; + private const double C44 = 0.46875; + private const double C46 = 0.01302083333333333333; + private const double C48 = 0.00712076822916666666; + private const double C66 = 0.36458333333333333333; + private const double C68 = 0.00569661458333333333; + private const double C88 = 0.3076171875; + + /// + /// Fraction constant 1/3 used in inverse meridional distance series. + /// + private const double P00 = 0.33333333333333333333; + + /// + /// Fraction constant 31/180 used in inverse meridional distance series. + /// + private const double P01 = 0.17222222222222222222; + + /// + /// Fraction constant 517/5040 used in inverse meridional distance series. + /// + private const double P02 = 0.10257936507936507937; + + /// + /// Fraction constant 23/360 used in inverse meridional distance series. + /// + private const double P10 = 0.06388888888888888888; + + /// + /// Fraction constant 251/3780 used in inverse meridional distance series. + /// + private const double P11 = 0.06640211640211640212; + + /// + /// Fraction constant 761/45360 used in inverse meridional distance series. + /// + private const double P20 = 0.01677689594356261023; - /// - protected sealed override void TransformCore(Span xs, Span ys, Span zs, int strideX, int strideY, int strideZ) + private static readonly AsyncLocal CurrentProjectionIdentityOverride = new(); + + private string abbreviation = string.Empty; + private string alias = string.Empty; + private string authority = string.Empty; + private long authorityCode; + private string name = string.Empty; + private string remarks = string.Empty; + + /// + /// Initializes a new instance of the class with a paired inverse projection. + /// + /// An enumeration of projection parameters. + /// The paired inverse projection, or if not yet available. + protected MapProjection(IEnumerable parameters, MapProjection? inverse) + : this(parameters) + { + this.inverse = inverse; + if (inverse is not null) { - if (IsInverse) - SourceToDegrees(xs, ys, strideX, strideY); - else - DegreesToTarget(xs, ys, strideX, strideY); + inverse.inverse = this; + this.IsInverse = !inverse.IsInverse; } + } - #endregion +#pragma warning restore IDE1006 - #region Forward methods + /// + /// Initializes a new instance of the class from a set of projection parameters. + /// + /// An enumeration of projection parameters. + protected MapProjection(IEnumerable parameters) + { + this.Parameters = new ProjectionParameterSet(parameters); + + this.semiMajor = this.Parameters.GetParameterValue("semi_major"); + this.semiMinor = this.Parameters.GetParameterValue("semi_minor"); + + this.es = EccentricySquared(this.semiMajor, this.semiMinor); + this.e = Math.Sqrt(this.es); + + this.scaleFactor = this.Parameters.GetOptionalParameterValue("scale_factor", 1); + + this.centralMeridian = DegreesToRadians(this.Parameters.GetParameterValue("central_meridian", "longitude_of_center")); + this.latOrigin = DegreesToRadians(this.Parameters.GetOptionalParameterValue("latitude_of_origin", 0d, "latitude_of_center")); + + this.metersPerUnit = this.Parameters.GetParameterValue("unit"); + this.reciprocalMetersPerUnit = 1 / this.metersPerUnit; - /// - /// Abstract method to convert a point (lon, lat) in radians to (x, y) in meters - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - protected abstract void RadiansToMeters(ref double lon, ref double lat); + this.falseEasting = this.Parameters.GetOptionalParameterValue("false_easting", 0) * this.metersPerUnit; + this.falseNorthing = this.Parameters.GetOptionalParameterValue("false_northing", 0) * this.metersPerUnit; + // Store the false offsets in metres so projection implementations can work in a + // consistent SI domain internally regardless of the declared projection unit. + + // Compute constants for the mlfn + double t; + this.en0 = C00 - (this.es * (C02 + (this.es * + (C04 + (this.es * (C06 + (this.es * C08))))))); + this.en1 = this.es * (C22 - (this.es * + (C04 + (this.es * (C06 + (this.es * C08)))))); + this.en2 = (t = this.es * this.es) * + (C44 - (this.es * (C46 + (this.es * C48)))); + this.en3 = (t *= this.es) * (C66 - (this.es * C68)); + this.en4 = t * this.es * C88; + } + + /// + /// Gets the projection classification name (e.g. 'Transverse_Mercator'). + /// + public string ClassName => this.Name; + + /// + /// Gets the number of projection parameters. + /// + public int NumParameters => this.Parameters.Count; + + /// + /// Gets the abbreviation of the object. + /// + public string Abbreviation + { + get => this.abbreviation; + internal init => this.abbreviation = value ?? string.Empty; + } + + /// + /// Gets the alias of the object. + /// + public string Alias + { + get => this.alias; + internal init => this.alias = value ?? string.Empty; + } + + /// + /// Gets the authority name for this object, e.g., "EPSG", + /// is this is a standard object with an authority specific + /// identity code. Returns "CUSTOM" if this is a custom object. + /// + public string Authority + { + get => this.authority; + internal init => this.authority = value ?? string.Empty; + } - /// - /// Method to convert a series of points defined by (lon, lat) in radians to (x, y) in meters - /// - /// The longitudes of the points in radians when entering, their x-ordinates in meters after exit. - /// The latitudes of the points in radians when entering, their y-ordinates in meters after exit. - /// A stride value for longitude-ordinates - /// A stride value for latitude-ordinates - protected virtual void RadiansToMeters(Span lons, Span lats, int strideX, int strideY) + /// + /// Gets the authority specific identification code of the object. + /// + public long AuthorityCode + { + get => this.authorityCode; + internal init => this.authorityCode = value; + } + + /// + /// Gets the name of the object. + /// + public string Name + { + get => this.name; + internal init { - for (int i = 0, j = 0; i < lons.Length; i += strideX, j += strideY) + string assignedName = value ?? string.Empty; + ProjectionIdentityOverride? identityOverride = CurrentProjectionIdentityOverride.Value; + if (identityOverride is not null && identityOverride.AppliesTo(this.GetType())) { - RadiansToMeters(ref lons[i], ref lats[j]); + if (string.Equals(assignedName, identityOverride.RequestedName, StringComparison.OrdinalIgnoreCase)) + { + this.alias = string.Empty; + this.name = assignedName; + return; + } + + this.alias = assignedName; + this.name = identityOverride.RequestedName; + return; } - } - /// - /// Converts a point (lon, lat) in degrees to (x, y) in meters - /// - /// The longitude in degree - /// The latitude in degree - protected virtual void DegreesToMeters(ref double lon, ref double lat) - { - lon = DegreesToRadians(lon); - lat = DegreesToRadians(lat); - RadiansToMeters(ref lon, ref lat); + this.name = assignedName; } + } + + /// + /// Gets the provider-supplied remarks for the object. + /// + public string Remarks + { + get => this.remarks; + internal init => this.remarks = value ?? string.Empty; + } + + /// + /// Calculates the UTM zone number for the given longitude. + /// + /// The longitude in decimal degrees. + /// The UTM zone number (1-60). + [Obsolete("Use ProjNet.CoordinateSystems.CoordinateSystemUtilities.CalcUtmZone instead.")] + public static long CalcUtmZone(double lon) => CoordinateSystemUtilities.CalcUtmZone(lon); + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + public sealed override int DimSource => 2; + + /// + public sealed override int DimTarget => 2; - /// - /// Converts points (lon, lat) in degrees to (x, y) in meters - /// - /// The longitudes of the points in degree when entering, their x-ordinates in meters after exit. - /// The latitudes of the points in degree when entering, their y-ordinates in meters after exit. - /// A stride value for longitude-ordinates - /// A stride value for latitude-ordinates - protected virtual void DegreesToMeters(Span lons, Span lats, int strideX, int strideY) + /// + public override WktNode ToWktNode() + { + var children = new List(this.NumParameters + 1) { - DegreesToRadians(lons, strideX); - DegreesToRadians(lats, strideY); - RadiansToMeters(lons, lats, strideX, strideY); - } + new WktQuotedString(this.Name), + }; - /// - /// Converts a point from degrees to target units - /// - /// The longitude in degree - /// The latitude in degree - protected virtual void DegreesToTarget(ref double lon, ref double lat) + for (int i = 0; i < this.NumParameters; i++) { - DegreesToMeters(ref lon, ref lat); - MetersToTarget(ref lon, ref lat); + children.Add(this.GetParameter(i).ToWktNode()); } - /// - /// Converts a series of points from degrees to target units to degrees - /// - /// A series of x-ordinate values - /// A series of y-ordinate values - /// A stride value for x-ordinates - /// A stride value for y-ordinates - protected virtual void DegreesToTarget(Span lons, Span lats, - int strideX, int strideY) + WktNode parameterizedNode = new WktKeywordNode("PARAM_MT", children); + return this.IsInverse + ? new WktKeywordNode("INVERSE_MT", parameterizedNode) + : parameterizedNode; + } + + /// + public override XElement ToXml() + { + var children = new List(this.NumParameters + 1) { - DegreesToMeters(lons, lats, strideX, strideY); - MetersToTarget(lons, lats, strideX, strideY); - } + new XAttribute("Name", this.ClassName), + }; - /// - /// Transforms point from meters to unit of output coordinate. This is done by - /// adding or and - /// multiplying with - /// - /// A x-ordinate - /// A y-ordinate - /// A point. - protected void MetersToTarget(ref double x, ref double y) + for (int i = 0; i < this.NumParameters; i++) { - x = (x + false_easting) * _reciprocalMetersPerUnit; - y = (y + false_northing) * _reciprocalMetersPerUnit; + children.Add(this.GetParameter(i).ToXml()); } - /// - /// Transforms point from meters to unit of output coordinate. This is done by - /// adding or and - /// multiplying with - /// - /// A x-ordinates - /// A y-ordinates - /// A stride value for x-ordinates - /// A stride value for y-ordinates - /// A point. - protected void MetersToTarget(Span xs, Span ys, int strideX, int strideY) + return new XElement( + "CT_MathTransform", + new XElement( + this.IsInverse ? "CT_InverseTransform" : "CT_ParameterizedMathTransform", + children)); + } + + /// + public sealed override void Transform(ref double x, ref double y, ref double z) + { + if (this.IsInverse) { - AddThenMultiplyInPlace(xs, strideX, false_easting, _reciprocalMetersPerUnit); - AddThenMultiplyInPlace(ys, strideY, false_northing, _reciprocalMetersPerUnit); + this.SourceToDegrees(ref x, ref y); } - #endregion - - #region Reverse methods - - /// - /// Abstract method to convert a point from meters to radians - /// - /// The x-ordinate when entering, the longitude value upon exit. - /// The y-ordinate when entering, the latitude value upon exit. - protected abstract void MetersToRadians(ref double x, ref double y); - - /// - /// Method to convert a series of points defined by (x, y) in meters to (lon, lat) in radians - /// - /// The x-ordinates of the points in meters when entering, their longitudes in radians after exit. - /// The y-ordinates of the points in meters when entering, their latitudes in radians after exit. - /// A stride value for x-ordinates - /// A stride value for y-ordinates - protected virtual void MetersToRadians(Span xs, Span ys, int strideX, int strideY) + else { - for (int i = 0, j = 0; i < xs.Length; i += strideX, j += strideY) - { - MetersToRadians(ref xs[i], ref ys[j]); - } + this.DegreesToTarget(ref x, ref y); } + } - /// - /// Method to convert a point from meters to degrees - /// - /// The x-ordinate when entering, the longitude value upon exit. - /// The y-ordinate when entering, the latitude value upon exit. - protected virtual void MetersToDegrees(ref double x, ref double y) + /// + /// Reverses the transformation. + /// + public override void Invert() + { + this.IsInverse = !this.IsInverse; + if (this.inverse is not null) { - MetersToRadians(ref x, ref y); - x = RadiansToDegrees(x); - y = RadiansToDegrees(y); + ((MapProjection)this.inverse).Invert(false); } + } - /// - /// Method to convert a point from meters to degrees - /// - /// The x-ordinate values when entering, the longitude values upon exit - /// The y-ordinate values when entering, the latitude values upon exit - /// - /// - protected virtual void MetersToDegrees(Span xs, Span ys, int strideX, int strideY) + /// + /// Determines whether this projection is equal to another projection by comparing only the + /// coordinate-system parameters. + /// + /// + /// Name, abbreviation, authority, alias, and remarks are ignored in the comparison. + /// + /// The object to compare with. + /// if the projection parameters and direction are equal; otherwise . + public bool EqualParams(object obj) + { + if (obj is not MapProjection projection) { - MetersToRadians(xs, ys, strideX, strideY); - RadiansToDegrees(xs, strideX); - RadiansToDegrees(ys, strideY); + return false; } - /// - /// Converts a point from source units to degrees - /// - /// The x-ordinate - /// The y-ordinate - /// Converted point. - protected virtual void SourceToDegrees(ref double x, ref double y) - { - SourceToMeters(ref x, ref y); - MetersToDegrees(ref x, ref y); - } + return this.Parameters.Equals(projection.Parameters) && this.IsInverse == projection.IsInverse; + } - /// - /// Converts a series of points from source units to degrees - /// - /// A series of x-ordinate values - /// A series of y-ordinate values - /// A stride value for x-ordinates - /// A stride value for y-ordinates - protected virtual void SourceToDegrees(Span xs, Span ys, - int strideX, int strideY) - { - SourceToMeters(xs, ys, strideX, strideY); - MetersToDegrees(xs, ys, strideX, strideY); - } + /// + /// Returns the projection parameter at the specified index. + /// + /// The zero-based index of the parameter. + /// The at . + public ProjectionParameter GetParameter(int index) => this.Parameters.GetAtIndex(index); - /// - /// Transforms unit of input coordinates to meters. This is done by multiplying with - /// and subtracting - /// or - /// - /// A series of x-ordinates - /// A series of y-ordinates - /// A stride value for x-ordinates - /// A stride value for y-ordinates - protected void SourceToMeters(Span xs, Span ys, int strideX, int strideY) - { - MultiplyThenAddInPlace(xs, strideX, _metersPerUnit, -false_easting); - MultiplyThenAddInPlace(ys, strideY, _metersPerUnit, -false_northing); - } + /// + /// Gets a named parameter of the projection. + /// + /// The parameter name is case insensitive. + /// Name of parameter. + /// The named , or if not found. + public ProjectionParameter? GetParameter(string name) => this.Parameters.Find(name); - /// - /// Transforms unit of input coordinate to meters. This is done by multiplying with - /// and subtracting - /// or - /// - /// A x-ordinate - /// A y-ordinate - /// A point. - protected void SourceToMeters(ref double x, ref double y) - { - x = x * _metersPerUnit - false_easting; - y = y * _metersPerUnit - false_northing; - } + /// + /// Begins a scoped projection-identity override so registry aliases can be applied during projection construction. + /// + /// The concrete projection type being instantiated. + /// The projection name requested from the registry. + /// An that restores the previous override when disposed. + internal static IDisposable BeginProjectionIdentityOverride(Type projectionType, string requestedName) + { + projectionType = ArgumentGuard.ThrowIfNull(projectionType, nameof(projectionType)); + requestedName = ArgumentGuard.ThrowIfNull(requestedName, nameof(requestedName)); - #endregion + ProjectionIdentityOverride? previous = CurrentProjectionIdentityOverride.Value; + CurrentProjectionIdentityOverride.Value = new ProjectionIdentityOverride(projectionType, requestedName); + return new ProjectionIdentityOverrideScope(previous); + } - /// - /// Reverses the transformation - /// - public override void Invert() - { - IsInverse = !IsInverse; - if (_inverse != null) ((MapProjection)_inverse).Invert(false); - } + /// + /// Gets a value indicating whether this projection can create a usable inverse transform. + /// + public override bool IsInvertible => this.HasInverseSupport; - /// - /// Reverses this transformation - /// - /// A flag indicating to reverse the "/> projection as well. - protected void Invert(bool invertInverse) - { - IsInverse = !IsInverse; - if (invertInverse && _inverse != null) ((MapProjection)_inverse).Invert(false); - } + /// + /// Gets a value indicating whether this projection operates in the inverse direction. + /// + /// + /// Most map projections define the forward direction as geographic-to-projection (lon/lat -> x/y) + /// and the inverse direction as projection-to-geographic (x/y -> lon/lat). + /// + protected internal bool IsInverse { get; private set; } - /// - /// Returns true if this projection is inverted. - /// Most map projections define forward projection as "from geographic to projection", and backwards - /// as "from projection to geographic". If this projection is inverted, this will be the other way around. - /// - protected internal bool IsInverse { get; private set; } - - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public bool EqualParams(object obj) - { - if (!(obj is MapProjection)) - return false; - var proj = obj as MapProjection; - - if (!_Parameters.Equals(proj._Parameters)) - return false; - /* - if (proj.NumParameters != NumParameters) - return false; - - for (var i = 0; i < _Parameters.Count; i++) - { - var param = _Parameters.Find(par => par.Name.Equals(proj.GetParameter(i).Name, StringComparison.OrdinalIgnoreCase)); - if (param == null) - return false; - if (param.Value != proj.GetParameter(i).Value) - return false; - } - */ - return IsInverse == proj.IsInverse; - } + /// + /// Gets a value indicating whether this projection can create a usable inverse transform + /// for the current parameter set. + /// + protected virtual bool HasInverseSupport => true; + + /// + /// Gets the inverse of . + /// + protected double InverseSphericalRadius => 1d / this.SphericalRadius; - #endregion - - #region Helper mathmatical functions - - // defines some useful constants that are used in the projection routines - // ReSharper disable InconsistentNaming - - /// - /// PI - /// - protected const double PI = Math.PI; - - /// - /// A fourth of - /// - protected const double FORT_PI = (PI * 0.25); - - /// - /// Half of PI - /// - protected const double HALF_PI = (PI * 0.5); - - /// - /// PI * 2 - /// - protected const double TWO_PI = (PI * 2.0); - - /// - /// EPSLN - /// - protected const double EPSLN = EPS10; - - /// - /// S2R - /// - protected const double S2R = 4.848136811095359e-6; - - /// - /// MAX_VAL - /// - protected const double MAX_VAL = 4; - - /// - /// prjMAXLONG - /// - protected const double prjMAXLONG = 2147483647; - - /// - /// DBLLONG - /// - protected const double DBLLONG = 4.61168601e18; - - /// - /// Returns the cube of a number. - /// - /// - protected static double CUBE(double x) + /// + /// Gets the spherical radius scaled by the projection scale factor. + /// + protected double SphericalRadius => this.semiMajor * this.scaleFactor; + + /// + protected sealed override void TransformCore(Span xs, Span ys, Span zs, int strideX, int strideY, int strideZ) + { + if (this.IsInverse) { - return Math.Pow(x, 3); /* x^3 */ + this.SourceToDegrees(xs, ys, strideX, strideY); } - - /// - /// Returns the quad of a number. - /// - /// - protected static double QUAD(double x) + else { - return Math.Pow(x, 4); /* x^4 */ + this.DegreesToTarget(xs, ys, strideX, strideY); } + } + + /// + /// Abstract method to convert a point (lon, lat) in radians to (x, y) in meters. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + protected abstract void RadiansToMeters(ref double lon, ref double lat); - /// - /// - /// - /// - /// - /// - protected static double GMAX(ref double A, ref double B) + /// + /// Method to convert a series of points defined by (lon, lat) in radians to (x, y) in meters. + /// + /// The longitudes of the points in radians when entering, their x-ordinates in meters after exit. + /// The latitudes of the points in radians when entering, their y-ordinates in meters after exit. + /// A stride value for longitude-ordinates. + /// A stride value for latitude-ordinates. + protected virtual void RadiansToMeters(Span lons, Span lats, int strideX, int strideY) + { + for (int i = 0, j = 0; i < lons.Length; i += strideX, j += strideY) { - return Math.Max(A, B); /* assign maximum of a and b */ + this.RadiansToMeters(ref lons[i], ref lats[j]); } + } + + /// + /// Returns the cached inverse transform or creates it after verifying that inverse support + /// is available for the current parameter set. + /// + /// Factory that creates the inverse transform. + /// The cached or newly created inverse transform. + /// Thrown when the projection does not support inverse transformation. + protected MathTransform GetOrCreateInverse(Func createInverse) + { + ArgumentGuard.ThrowIfNull(createInverse, nameof(createInverse)); - /// - /// - /// - /// - /// - /// - protected static double GMIN(ref double A, ref double B) + if (!this.HasInverseSupport) { - return ((A) < (B) ? (A) : (B)); /* assign minimum of a and b */ + throw new NotSupportedException($"{this.Name} does not support inverse projection."); } - /// - /// IMOD - /// - /// - /// - /// - protected static double IMOD(double A, double B) - { - return (A) - (((A) / (B)) * (B)); /* Integer mod function */ + this.inverse ??= createInverse(); + return this.inverse; + } - } + /// + /// Converts a point (lon, lat) in degrees to (x, y) in meters. + /// + /// The longitude in degree. + /// The latitude in degree. + protected virtual void DegreesToMeters(ref double lon, ref double lat) + { + lon = DegreesToRadians(lon); + lat = DegreesToRadians(lat); + this.RadiansToMeters(ref lon, ref lat); + } - /// - ///Function to return the sign of an argument - /// - protected static double sign(double x) - { - if (x < 0.0) - return (-1); - else return (1); - } + /// + /// Converts points (lon, lat) in degrees to (x, y) in meters. + /// + /// The longitudes of the points in degree when entering, their x-ordinates in meters after exit. + /// The latitudes of the points in degree when entering, their y-ordinates in meters after exit. + /// A stride value for longitude-ordinates. + /// A stride value for latitude-ordinates. + protected virtual void DegreesToMeters(Span lons, Span lats, int strideX, int strideY) + { + DegreesToRadians(lons, strideX); + DegreesToRadians(lats, strideY); + this.RadiansToMeters(lons, lats, strideX, strideY); + } - /// - /// - /// - /// - /// - protected static double adjust_lon(double x) - { - long count = 0; - for (; ; ) - { - if (Math.Abs(x) <= PI) - break; - else if (((long)Math.Abs(x / Math.PI)) < 2) - x = x - (sign(x) * TWO_PI); - else if (((long)Math.Abs(x / TWO_PI)) < prjMAXLONG) - { - x = x - (((long)(x / TWO_PI)) * TWO_PI); - } - else if (((long)Math.Abs(x / (prjMAXLONG * TWO_PI))) < prjMAXLONG) - { - x = x - (((long)(x / (prjMAXLONG * TWO_PI))) * (TWO_PI * prjMAXLONG)); - } - else if (((long)Math.Abs(x / (DBLLONG * TWO_PI))) < prjMAXLONG) - { - x = x - (((long)(x / (DBLLONG * TWO_PI))) * (TWO_PI * DBLLONG)); - } - else - x = x - (sign(x) * TWO_PI); - count++; - if (count > MAX_VAL) - break; - } - return (x); - } + /// + /// Converts a point from degrees to target units. + /// + /// The longitude in degree. + /// The latitude in degree. + protected virtual void DegreesToTarget(ref double lon, ref double lat) + { + this.DegreesToMeters(ref lon, ref lat); + this.MetersToTarget(ref lon, ref lat); + } - /// - /// Function to compute the constant small m which is the radius of - /// a parallel of latitude, phi, divided by the semimajor axis. - /// - protected static double msfnz(double eccent, double sinphi, double cosphi) - { - double con; + /// + /// Converts a series of points from geographic degrees to target projection units. + /// + /// A series of x-ordinate values. + /// A series of y-ordinate values. + /// A stride value for x-ordinates. + /// A stride value for y-ordinates. + protected virtual void DegreesToTarget( + Span lons, + Span lats, + int strideX, + int strideY) + { + this.DegreesToMeters(lons, lats, strideX, strideY); + this.MetersToTarget(lons, lats, strideX, strideY); + } - con = eccent * sinphi; - return ((cosphi / (Math.Sqrt(1.0 - con * con)))); - } + /// + /// Transforms point from meters to unit of output coordinate. This is done by + /// adding or and + /// multiplying with . + /// + /// A x-ordinate. + /// A y-ordinate. + protected void MetersToTarget(ref double x, ref double y) + { + x = (x + this.falseEasting) * this.reciprocalMetersPerUnit; + y = (y + this.falseNorthing) * this.reciprocalMetersPerUnit; + } - /// - /// Function to compute constant small q which is the radius of a - /// parallel of latitude, phi, divided by the semimajor axis. - /// - protected static double qsfnz(double sinphi, double eccent) - { - if (eccent > 1.0e-7) - { - double con = eccent * sinphi; - return ((1.0 - eccent * eccent) * (sinphi / (1.0 - con * con) - (.5 / eccent) * - Math.Log((1.0 - con) / (1.0 + con)))); - } + /// + /// Transforms point from meters to unit of output coordinate. This is done by + /// adding or and + /// multiplying with . + /// + /// The x-ordinates. + /// The y-ordinates. + /// A stride value for x-ordinates. + /// A stride value for y-ordinates. + protected void MetersToTarget(Span xs, Span ys, int strideX, int strideY) + { + AddThenMultiplyInPlace(xs, strideX, this.falseEasting, this.reciprocalMetersPerUnit); + AddThenMultiplyInPlace(ys, strideY, this.falseNorthing, this.reciprocalMetersPerUnit); + } - return 2.0 * sinphi; - } + /// + /// Abstract method to convert a point from meters to radians. + /// + /// The x-ordinate when entering, the longitude value upon exit. + /// The y-ordinate when entering, the latitude value upon exit. + protected abstract void MetersToRadians(ref double x, ref double y); - /// - /// Function to compute constant small q which is the radius of a - /// parallel of latitude, phi, divided by the semimajor axis. - /// - protected static double qsfn(double sinphi, double eccent, double one_es) + /// + /// Method to convert a series of points defined by (x, y) in meters to (lon, lat) in radians. + /// + /// The x-ordinates of the points in meters when entering, their longitudes in radians after exit. + /// The y-ordinates of the points in meters when entering, their latitudes in radians after exit. + /// A stride value for x-ordinates. + /// A stride value for y-ordinates. + protected virtual void MetersToRadians(Span xs, Span ys, int strideX, int strideY) + { + for (int i = 0, j = 0; i < xs.Length; i += strideX, j += strideY) { - if (eccent >= EPS7) - { - double con = eccent * sinphi; - double div1 = 1.0 - con * con; - double div2 = 1.0 + con; + this.MetersToRadians(ref xs[i], ref ys[j]); + } + } - /* avoid zero division, fail gracefully */ - if (div1 == 0.0 || div2 == 0.0) - return HUGE_VAL; + /// + /// Method to convert a point from meters to degrees. + /// + /// The x-ordinate when entering, the longitude value upon exit. + /// The y-ordinate when entering, the latitude value upon exit. + protected virtual void MetersToDegrees(ref double x, ref double y) + { + this.MetersToRadians(ref x, ref y); + x = RadiansToDegrees(x); + y = RadiansToDegrees(y); + } - return (one_es * (sinphi / div1 - (.5 / eccent) * Math.Log((1.0 - con) / div2))); - } - else - return (sinphi + sinphi); + /// + /// Method to convert a point from meters to degrees. + /// + /// The x-ordinate values when entering, the longitude values upon exit. + /// The y-ordinate values when entering, the latitude values upon exit. + /// A stride value for x-ordinates. + /// A stride value for y-ordinates. + protected virtual void MetersToDegrees(Span xs, Span ys, int strideX, int strideY) + { + this.MetersToRadians(xs, ys, strideX, strideY); + RadiansToDegrees(xs, strideX); + RadiansToDegrees(ys, strideY); + } - } - /// - /// Function to calculate the sine and cosine in one call. Some computer - /// systems have implemented this function, resulting in a faster implementation - /// than calling each function separately. It is provided here for those - /// computer systems which don`t implement this function - /// - protected static void sincos(double val, out double sin_val, out double cos_val) + /// + /// Converts a point from source units to degrees. + /// + /// The x-ordinate. + /// The y-ordinate. + protected virtual void SourceToDegrees(ref double x, ref double y) + { + this.SourceToMeters(ref x, ref y); + this.MetersToDegrees(ref x, ref y); + } + + /// + /// Converts a series of points from source units to degrees. + /// + /// A series of x-ordinate values. + /// A series of y-ordinate values. + /// A stride value for x-ordinates. + /// A stride value for y-ordinates. + protected virtual void SourceToDegrees( + Span xs, + Span ys, + int strideX, + int strideY) + { + this.SourceToMeters(xs, ys, strideX, strideY); + this.MetersToDegrees(xs, ys, strideX, strideY); + } + + /// + /// Transforms the unit of input coordinates to meters by multiplying by + /// and subtracting + /// or . + /// + /// The x-ordinates. + /// The y-ordinates. + /// A stride value for x-ordinates. + /// A stride value for y-ordinates. + protected void SourceToMeters(Span xs, Span ys, int strideX, int strideY) + { + MultiplyThenAddInPlace(xs, strideX, this.metersPerUnit, -this.falseEasting); + MultiplyThenAddInPlace(ys, strideY, this.metersPerUnit, -this.falseNorthing); + } + + /// + /// Transforms the unit of the input coordinate to meters by multiplying by + /// and subtracting + /// or . + /// + /// A x-ordinate. + /// A y-ordinate. + protected void SourceToMeters(ref double x, ref double y) + { + x = (x * this.metersPerUnit) - this.falseEasting; + y = (y * this.metersPerUnit) - this.falseNorthing; + } + /// + /// Reverses this transformation. + /// + /// If , also inverts the paired projection. + protected void Invert(bool invertInverse) + { + this.IsInverse = !this.IsInverse; + if (invertInverse && this.inverse is not null) { - sin_val = Math.Sin(val); - cos_val = Math.Cos(val); + ((MapProjection)this.inverse).Invert(false); } + } + + /// + /// Gets or sets the central meridian (projection centre longitude) in radians; an alias for . + /// + protected double Lon_origin + { + get => this.centralMeridian; + set => this.centralMeridian = value; + } + + /// + /// Gets the central parallel (projection centre latitude) in radians; an alias for . + /// + protected double Central_parallel => this.latOrigin; + + /// + /// Gets the origin latitude phi0 in radians; an alias for . + /// + protected double Phi0 => this.latOrigin; - /// - /// Function to compute the constant small t for use in the forward - /// computations in the Lambert Conformal Conic and the Polar - /// Stereographic projections. - /// - protected static double tsfnz(double eccent, double phi, double sinphi) + /// + /// Returns a list of cloned projection parameters. + /// + /// The projection parameters to clone. + /// A new list containing a copy of each . + protected internal static List CloneParametersList( + IEnumerable projectionParameters) + { + projectionParameters = ArgumentGuard.ThrowIfNull(projectionParameters, nameof(projectionParameters)); + + int capacity = projectionParameters is ICollection collection + ? collection.Count + : 0; + List res = capacity > 0 + ? new List(capacity) + : []; + foreach (ProjectionParameter pp in projectionParameters) { - double con; - double com; - con = eccent * sinphi; - com = .5 * eccent; - con = Math.Pow(((1.0 - con) / (1.0 + con)), com); - return (Math.Tan(.5 * (HALF_PI - phi)) / con); + res.Add(new ProjectionParameter(pp.Name, pp.Value)); } - /// - /// - /// - /// - /// - /// - /// - /// - protected static double phi1z(double eccent, double qs, out long flag) + return res; + } + + /// + /// Returns the sign of an argument. + /// + /// The value to evaluate. + /// 1 if is non-negative; otherwise -1. + protected static double Sign(double x) + { + return x < 0.0 ? -1 : 1; + } + + /// + /// Normalises a longitude angle into the canonical interval [-π, π]. + /// + /// The longitude in radians to normalise. + /// The normalised longitude in radians, within [-π, π]. + protected static double Adjust_lon(double x) + { + for (long count = 0; count <= MaxVal; count++) { - double eccnts; - double dphi; - double con; - double com; - double sinpi; - double cospi; - double phi; - flag = 0; - //double asinz(); - long i; - - phi = asinz(.5 * qs); - if (eccent < EPSLN) - return (phi); - eccnts = eccent * eccent; - for (i = 1; i <= 25; i++) + if (Math.Abs(x) <= PI) { - sincos(phi, out sinpi, out cospi); - con = eccent * sinpi; - com = 1.0 - con * con; - dphi = .5 * com * com / cospi * (qs / (1.0 - eccnts) - sinpi / com + - .5 / eccent * Math.Log((1.0 - con) / (1.0 + con))); - phi = phi + dphi; - if (Math.Abs(dphi) <= 1e-7) - return (phi); + break; } - //p_error ("Convergence error","phi1z-conv"); - //ASSERT(FALSE); - throw new ArgumentException("Convergence error."); - } - - /// - ///Function to eliminate roundoff errors in asin - /// - protected static double asinz(double con) - { - if (Math.Abs(con) > 1.0) + else if (((long)Math.Abs(x / Math.PI)) < 2) { - if (con > 1.0) - con = 1.0; - else - con = -1.0; + x -= Sign(x) * TwoPi; } - return (Math.Asin(con)); - } - - /// - /// Function to compute the latitude angle, phi2, for the inverse of the - /// Lambert Conformal Conic and Polar Stereographic projections. - /// - /// Spheroid eccentricity - /// Constant value t - /// Error flag number - protected static double phi2z(double eccent, double ts, out long flag) - { - double con; - double dphi; - double sinpi; - long i; - - flag = 0; - double eccnth = .5 * eccent; - double chi = HALF_PI - 2 * Math.Atan(ts); - for (i = 0; i <= 15; i++) + else if (((long)Math.Abs(x / TwoPi)) < prjMAXLONG) + { + x -= ((long)(x / TwoPi)) * TwoPi; + } + else if (((long)Math.Abs(x / (prjMAXLONG * TwoPi))) < prjMAXLONG) + { + x -= ((long)(x / (prjMAXLONG * TwoPi))) * (TwoPi * prjMAXLONG); + } + else if (((long)Math.Abs(x / (DblLong * TwoPi))) < prjMAXLONG) { - sinpi = Math.Sin(chi); - con = eccent * sinpi; - dphi = HALF_PI - 2 * Math.Atan(ts * (Math.Pow(((1.0 - con) / (1.0 + con)), eccnth))) - chi; - chi += dphi; - if (Math.Abs(dphi) <= .0000000001) - return (chi); + x -= ((long)(x / (DblLong * TwoPi))) * (TwoPi * DblLong); + } + else + { + x -= Sign(x) * TwoPi; } - throw new ArgumentException("Convergence error - phi2z-conv"); } - private const double C00 = 1.0, - C02 = 0.25, - C04 = 0.046875, - C06 = 0.01953125, - C08 = 0.01068115234375, - C22 = 0.75, - C44 = 0.46875, - C46 = 0.01302083333333333333, - C48 = 0.00712076822916666666, - C66 = 0.36458333333333333333, - C68 = 0.00569661458333333333, - C88 = 0.3076171875; - - /// - ///Functions to compute the constants e0, e1, e2, and e3 which are used - ///in a series for calculating the distance along a meridian. The - ///input x represents the eccentricity squared. - /// - protected static double e0fn(double x) - { - return (1.0 - 0.25 * x * (1.0 + x / 16.0 * (3.0 + 1.25 * x))); - } + return x; + } - /// - /// - /// - /// - /// - protected static double e1fn(double x) - { - return (0.375 * x * (1.0 + 0.25 * x * (1.0 + 0.46875 * x))); - } + /// + /// Computes the small m function: the radius of a parallel of latitude φ divided by the semi-major axis. + /// + /// The ellipsoid eccentricity. + /// The sine of the latitude angle phi. + /// The cosine of the latitude angle phi. + /// The value of the small m function for latitude φ. + protected static double Msfnz(double eccent, double sinphi, double cosphi) + { + double con = eccent * sinphi; + return cosphi / Math.Sqrt(1.0 - (con * con)); + } - /// - /// - /// - /// - /// - protected static double e2fn(double x) + /// + /// Computes the small q function: the authalic latitude weighting used in equal-area projections. + /// + /// The sine of the latitude angle phi. + /// The ellipsoid eccentricity. + /// The value of the small q function for latitude φ. + protected static double Qsfnz(double sinphi, double eccent) + { + if (eccent > 1.0e-7d) { - return (0.05859375 * x * x * (1.0 + 0.75 * x)); + double eccentricitySquared = eccent * eccent; + double con = eccent * sinphi; + double inverseConSquared = 1.0 - (con * con); + double logTerm = Math.Log((1.0 - con) / (1.0 + con)); + return (1.0 - eccentricitySquared) * ((sinphi / inverseConSquared) - ((.5 / eccent) * logTerm)); } - /// - /// - /// - /// - /// - protected static double e3fn(double x) - { - return (x * x * x * (35.0 / 3072.0)); - } + return 2.0 * sinphi; + } - /// - /// Function to compute the constant e4 from the input of the eccentricity - /// of the spheroid, x. This constant is used in the Polar Stereographic - /// projection. - /// - protected static double e4fn(double x) + /// + /// Computes the small q function with an explicit (1 - e²) factor supplied by the caller. + /// + /// The sine of the latitude angle phi. + /// The ellipsoid eccentricity. + /// One minus the square of the eccentricity (1 - e^2). + /// The value of the small q function for latitude φ, or on a singularity. + protected static double Qsfn(double sinphi, double eccent, double one_es) + { + if (eccent >= Eps7) { - double con; - double com; - con = 1.0 + x; - com = 1.0 - x; - return (Math.Sqrt((Math.Pow(con, con)) * (Math.Pow(com, com)))); - } + double con = eccent * sinphi; + double div1 = 1.0 - (con * con); + double div2 = 1.0 + con; - /// - /// Function computes the value of M which is the distance along a meridian - /// from the Equator to latitude phi. - /// - protected static double mlfn(double e0, double e1, double e2, double e3, double phi) + // avoid zero division, fail gracefully + return div1 == 0.0 || div2 == 0.0 ? HugeVal : one_es * ((sinphi / div1) - ((.5 / eccent) * Math.Log((1.0 - con) / div2))); + } + else { - return (e0 * phi - e1 * Math.Sin(2.0 * phi) + e2 * Math.Sin(4.0 * phi) - e3 * Math.Sin(6.0 * phi)); + return sinphi + sinphi; } + } + + /// + /// Computes the sine and cosine of an angle in a single call. + /// + /// The angle in radians. + /// The sine of . + /// The cosine of . + protected static void Sincos(double val, out double sin_val, out double cos_val) + { + sin_val = Math.Sin(val); + cos_val = Math.Cos(val); + } - /// - /// Calculates the meridian distance. This is the distance along the central - /// meridian from the equator to . Accurate to < 1e-5 meters - /// when used in conjuction with typical major axis values. - /// - /// - /// - /// - /// - protected double mlfn(double phi, double sphi, double cphi) + /// + /// Computes the small t value used in forward Lambert Conformal Conic and Polar Stereographic projections. + /// + /// The ellipsoid eccentricity. + /// The latitude angle in radians. + /// The sine of . + /// The small t value for the given latitude. + protected static double Tsfnz(double eccent, double phi, double sinphi) + { + double con = eccent * sinphi; + double com = .5 * eccent; + con = Math.Pow((1.0 - con) / (1.0 + con), com); + return Math.Tan(.5 * (HalfPi - phi)) / con; + } + + /// + /// Computes latitude from the Snyder q-function using an iterative solution. + /// + /// The ellipsoid eccentricity. + /// The value of the Snyder q-function. + /// Set to a non-zero value on error; otherwise 0. + /// The latitude in radians corresponding to the given q value. + protected static double Phi1z(double eccent, double qs, out long flag) + { + flag = 0; + double phi = Asinz(.5 * qs); + if (eccent < Epsln) { - cphi *= sphi; - sphi *= sphi; - return en0 * phi - cphi * (en1 + sphi * (en2 + sphi * (en3 + sphi * en4))); + return phi; } - /// - /// Calculates the latitude (phi) from a meridian distance. - /// Determines phi to TOL (1e-11) radians, about 1e-6 seconds. - /// - /// The meridonial distance - /// The latitude of the meridian distance. - protected double inv_mlfn(double arg) + double eccnts = eccent * eccent; + for (int i = 1; i <= 25; i++) { - const double MLFN_TOL = 1E-11; - const int MAXIMUM_ITERATIONS = 20; - double s, t, phi, k = 1.0 / (1.0 - _es); - int i; - phi = arg; - for (i = MAXIMUM_ITERATIONS; /*true*/;) + Sincos(phi, out double sinpi, out double cospi); + double con = eccent * sinpi; + double com = 1.0 - (con * con); + double dphi = .5 * com * com / cospi * ((qs / (1.0 - eccnts)) - (sinpi / com) + + (.5 / eccent * Math.Log((1.0 - con) / (1.0 + con)))); + phi += dphi; + if (Math.Abs(dphi) <= Eps7) { - // rarely goes over 5 iterations - if (--i < 0) - { - throw new InvalidOperationException("No convergence"); - } - s = Math.Sin(phi); - t = 1.0 - _es * s * s; - t = (mlfn(phi, s, Math.Cos(phi)) - arg) * (t * Math.Sqrt(t)) * k; - phi -= t; - if (Math.Abs(t) < MLFN_TOL) - { - return phi; - } + return phi; } } - /// - /// Calculates the flattening factor, ( - ) / . - /// - /// The radius of the equator - /// The radius of a circle touching the poles - /// The flattening factor - private static double FlatteningFactor(double equatorialRadius, double polarRadius) - { - return (equatorialRadius - polarRadius) / equatorialRadius; - } + return ProjectionThrowHelper.ThrowInvalidOperation("Convergence error."); + } - /// - /// Calculates the square of eccentricity according to es = (2f - f^2) where f is the flattening factor. - /// - /// The radius of the equator - /// The radius of a circle touching the poles - /// The square of eccentricity - private static double EccentricySquared(double equatorialRadius, double polarRadius) + /// + /// Function to eliminate roundoff errors in asin. + /// + /// The con value. + /// The computed value. + protected static double Asinz(double con) + { + if (Math.Abs(con) > 1.0) { - double f = FlatteningFactor(equatorialRadius, polarRadius); - return 2 * f - f * f; + if (con > 1.0) + { + con = 1.0; + } + else + { + con = -1.0; + } } + return Math.Asin(con); + } - /// - /// Function to calculate UTM zone number - /// - /// The longitudinal value (in Degrees!) - /// The UTM zone number - public static long CalcUtmZone(double lon) + /// + /// Computes the latitude angle phi2 for the inverse of the Lambert Conformal Conic and Polar Stereographic projections. + /// + /// Spheroid eccentricity. + /// The small t value from . + /// Set to a non-zero value on error; otherwise 0. + /// The latitude phi2 in radians. + protected static double Phi2z(double eccent, double ts, out long flag) + { + flag = 0; + double eccnth = .5 * eccent; + double chi = HalfPi - (2 * Math.Atan(ts)); + for (int i = 0; i <= 15; i++) { - return (long)((lon + 180.0) / 6.0 + 1.0); + double sinpi = Math.Sin(chi); + double con = eccent * sinpi; + double dphi = HalfPi - (2 * Math.Atan(ts * Math.Pow((1.0 - con) / (1.0 + con), eccnth))) - chi; + chi += dphi; + if (Math.Abs(dphi) <= .0000000001) + { + return chi; + } } - #endregion + return ProjectionThrowHelper.ThrowInvalidOperation("Convergence error - phi2z-conv"); + } - #region Static Methods; + /// + /// Computes the meridian distance M from the equator to latitude φ using a four-coefficient series. + /// + /// The meridional arc coefficient e0. + /// The meridional arc coefficient e1. + /// The meridional arc coefficient e2. + /// The meridional arc coefficient e3. + /// The latitude in radians. + /// The meridian distance M from the equator to latitude . + protected static double Mlfn(double e0, double e1, double e2, double e3, double phi) => (e0 * phi) - (e1 * Math.Sin(2.0 * phi)) + (e2 * Math.Sin(4.0 * phi)) - (e3 * Math.Sin(6.0 * phi)); - /// - /// Converts a longitude value in degrees to radians. - /// - /// The value in degrees to convert to radians. - /// If true, -180 and +180 are valid, otherwise they are considered out of range. - /// - protected static double LongitudeToRadians(double x, bool edge) - { - if (edge ? (x >= -180 && x <= 180) : (x > -180 && x < 180)) - return DegreesToRadians(x); - throw new ArgumentOutOfRangeException("x", - x.ToString(CultureInfo.InvariantCulture) + - " not a valid longitude in degrees."); - } + /// + /// Calculates the meridian distance. This is the distance along the central + /// meridian from the equator to . Accurate to < 1e-5 meters + /// when used in conjuction with typical major axis values. + /// + /// The latitude in radians. + /// The sine of . + /// The cosine of . + /// The meridian distance M from the equator to latitude . + protected double Mlfn(double phi, double sphi, double cphi) + { + cphi *= sphi; + sphi *= sphi; + return (this.en0 * phi) - (cphi * (this.en1 + (sphi * (this.en2 + (sphi * (this.en3 + (sphi * this.en4))))))); + } - /// - /// Converts a latitude value in degrees to radians. - /// - /// The value in degrees to to radians. - /// If true, -90 and +90 are valid, otherwise they are considered out of range. - /// - protected static double LatitudeToRadians(double y, bool edge) + /// + /// Calculates the latitude (φ) from a meridian distance. + /// Determines φ to TOL (1e-11) radians, about 1e-6 seconds. + /// + /// The meridional distance. + /// The latitude in radians corresponding to meridian distance . + protected double Inv_mlfn(double arg) + { + const double MLFN_TOL = 1E-11d; + const int MAXIMUM_ITERATIONS = 20; + double k = 1.0 / (1.0 - this.es); + double phi = arg; + int i = MAXIMUM_ITERATIONS; + while (true) { - if (edge ? (y >= -90 && y <= 90) : (y > -90 && y < 90)) - return DegreesToRadians(y); - throw new ArgumentOutOfRangeException("y", - y.ToString(CultureInfo.InvariantCulture) + - " not a valid latitude in degrees."); + // rarely goes over 5 iterations + if (--i < 0) + { + throw new InvalidOperationException("No convergence"); + } + + double s = Math.Sin(phi); + double t = 1.0 - (this.es * s * s); + t = (this.Mlfn(phi, s, Math.Cos(phi)) - arg) * (t * Math.Sqrt(t)) * k; + phi -= t; + if (Math.Abs(t) < MLFN_TOL) + { + return phi; + } } + } + + /// + /// Converts a longitude value in degrees to radians. + /// + /// The value in degrees to convert to radians. + /// If true, -180 and +180 are valid, otherwise they are considered out of range. + /// The longitude converted to radians. + [Obsolete("Use ProjNet.CoordinateSystems.CoordinateSystemUtilities.LongitudeToRadians instead.")] + protected static double LongitudeToRadians(double x, bool edge) => CoordinateSystemUtilities.LongitudeToRadians(x, edge); + + /// + /// Converts a latitude value in degrees to radians. + /// + /// The value in degrees to convert to radians. + /// If true, -90 and +90 are valid, otherwise they are considered out of range. + /// The latitude converted to radians. + [Obsolete("Use ProjNet.CoordinateSystems.CoordinateSystemUtilities.LatitudeToRadians instead.")] + protected static double LatitudeToRadians(double y, bool edge) => CoordinateSystemUtilities.LatitudeToRadians(y, edge); - private const double P00 = 0.33333333333333333333; /* 1 / 3 */ - private const double P01 = 0.17222222222222222222; /* 31 / 180 */ - private const double P02 = 0.10257936507936507937; /* 517 / 5040 */ - private const double P10 = 0.06388888888888888888; /* 23 / 360 */ - private const double P11 = 0.06640211640211640212; /* 251 / 3780 */ - private const double P20 = 0.01677689594356261023; /* 761 / 45360 */ + /// + /// Computes the series coefficients used by for authalic latitude conversion. + /// + /// The squared eccentricity (e^2) of the ellipsoid. + /// An array of three series coefficients for the authalic latitude series. + protected static double[] Authset(double es) + { + double[] aPA = new double[3]; + aPA[0] = es * P00; + double t = es * es; + aPA[0] += t * P01; + aPA[1] = t * P10; + t *= es; + aPA[0] += t * P02; + aPA[1] += t * P11; + aPA[2] = t * P20; + + return aPA; + } + /// + /// Converts an authalic latitude to a geodetic latitude using a series approximation. + /// + /// The authalic latitude in radians. + /// The series coefficients from . + /// The geodetic latitude in radians. + protected static double Authlat(double beta, double[] apa) + { + apa = ArgumentGuard.ThrowIfNull(apa, nameof(apa)); - /// - /// authset - /// - /// - /// - protected static double[] authset(double es) + double t = beta + beta; + return beta + (apa[0] * Math.Sin(t)) + (apa[1] * Math.Sin(t + t)) + (apa[2] * Math.Sin(t + t + t)); + } + + /// + /// Calculates the hypotenuse of a triangle: Sqrt(x*x + y*y). + /// + /// The length of one orthogonal leg of the triangle. + /// The length of the other orthogonal leg of the triangle. + /// The length of the diagonal. + protected static double Hypot(double x, double y) => Math.Sqrt((x * x) + (y * y)); + + private sealed class ProjectionIdentityOverride + { + internal ProjectionIdentityOverride(Type projectionType, string requestedName) { - double[] APA = new double[3]; - APA[0] = es * P00; - double t = es * es; - APA[0] += t * P01; - APA[1] = t * P10; - t *= es; - APA[0] += t * P02; - APA[1] += t * P11; - APA[2] = t * P20; - - return APA; + this.ProjectionType = projectionType; + this.RequestedName = requestedName; } - /// - /// authlat - /// - /// - /// - /// - protected static double authlat(double beta, double[] APA) + internal Type ProjectionType { get; } + + internal string RequestedName { get; } + + internal bool AppliesTo(Type projectionType) => projectionType == this.ProjectionType; + } + + private sealed class ProjectionIdentityOverrideScope : IDisposable + { + private readonly ProjectionIdentityOverride? previous; + private bool disposed; + + internal ProjectionIdentityOverrideScope(ProjectionIdentityOverride? previous) { - double t = beta + beta; - return (beta + APA[0] * Math.Sin(t) + APA[1] * Math.Sin(t + t) + APA[2] * Math.Sin(t + t + t)); + this.previous = previous; } - /// - /// Calculates the hypotenuse of a triangle: Sqrt(x*x + y*y); - /// - /// The length of one orthogonal leg of the triangle - /// The length of the other orthogonal leg of the triangle - /// The length of the diagonal. - protected static double hypot(double x, double y) + public void Dispose() { - return Math.Sqrt(x * x + y * y); + if (this.disposed) + { + return; + } + + CurrentProjectionIdentityOverride.Value = this.previous; + this.disposed = true; } - #endregion + } + + /// + /// Calculates the flattening factor, ( - ) / . + /// + /// The radius of the equator. + /// The radius of a circle touching the poles. + /// The flattening factor. + private static double FlatteningFactor(double equatorialRadius, double polarRadius) => (equatorialRadius - polarRadius) / equatorialRadius; + + /// + /// Calculates the square of eccentricity according to es = (2f - f²) where f is the flattening factor. + /// + /// The radius of the equator. + /// The radius of a circle touching the poles. + /// The square of eccentricity. + private static double EccentricySquared(double equatorialRadius, double polarRadius) + { + double f = FlatteningFactor(equatorialRadius, polarRadius); + return (2 * f) - (f * f); } } diff --git a/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarParabolicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarParabolicProjection.cs new file mode 100644 index 00000000..316aea6f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarParabolicProjection.cs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical McBryde-Thomas Flat-Polar Parabolic projection (mbtfpp). +/// +/// +/// McBryde-Thomas Flat-Polar Parabolic is one of the spherical pseudocylindrical projections +/// developed by McBryde and Thomas in the mid-20th century. This variant uses the +/// authalic-latitude substitution asin(Csy * sin(φ)) together with the parabolic +/// x/y scaling that distinguishes the family member. +/// +internal sealed class McBrydeThomasFlatPolarParabolicProjection : MapProjection +{ + private const double Csy = 0.95257934441568037152d; + private const double Fxc = 0.92582009977255146156d; + private const double Fyc = 3.40168025708304504493d; + private const double C23 = ProjectionConstants.TwoThirds; + private const double C13 = ProjectionConstants.OneThird; + private const double OneEps = ProjectionConstants.OnePlusEps7; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public McBrydeThomasFlatPolarParabolicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public McBrydeThomasFlatPolarParabolicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "McBryde_Thomas_Flat_Polar_Parabolic"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new McBrydeThomasFlatPolarParabolicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = Asinz(Csy * Math.Sin(lat)); + double x = Fxc * lambda * ((2d * Math.Cos(C23 * phi)) - 1d); + double y = Fyc * Math.Sin(C13 * phi); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = yy / Fyc; + if (Math.Abs(phi) >= 1d) + { + if (Math.Abs(phi) > OneEps) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + phi = phi < 0d ? -HalfPi : HalfPi; + } + else + { + phi = Math.Asin(phi); + } + + phi *= 3d; + double lambda = xx / (Fxc * ((2d * Math.Cos(C23 * phi)) - 1d)); + phi = Math.Sin(phi) / Csy; + if (Math.Abs(phi) >= 1d) + { + if (Math.Abs(phi) > OneEps) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + phi = phi < 0d ? -HalfPi : HalfPi; + } + else + { + phi = Math.Asin(phi); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarQuarticProjection.cs b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarQuarticProjection.cs new file mode 100644 index 00000000..de6dc39d --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarQuarticProjection.cs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical McBryde-Thomas Flat-Polar Quartic projection (mbtfpq). +/// +/// +/// McBryde-Thomas Flat-Polar Quartic is one of the spherical pseudocylindrical projections +/// developed by McBryde and Thomas in the mid-20th century. This variant solves its +/// auxiliary latitude iteratively and then applies the quartic family's characteristic +/// half-angle x/y scaling. +/// +internal sealed class McBrydeThomasFlatPolarQuarticProjection : MapProjection +{ + private const int Iterations = 20; + private const double OneTol = ProjectionConstants.OnePlusEps6; + private const double C = 1.70710678118654752440d; + private const double Rc = 0.58578643762690495119d; + private const double Fyc = 1.87475828462269495505d; + private const double Ryc = 0.53340209679417701685d; + private const double Fxc = 0.31245971410378249250d; + private const double Rxc = 3.20041258076506210122d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public McBrydeThomasFlatPolarQuarticProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public McBrydeThomasFlatPolarQuarticProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "McBryde_Thomas_Flat_Polar_Quartic"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new McBrydeThomasFlatPolarQuarticProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double c = C * Math.Sin(phi); + for (int i = Iterations; i > 0; i--) + { + double deltaNumerator = (Math.Sin(0.5d * phi) + Math.Sin(phi)) - c; + double deltaDenominator = (0.5d * Math.Cos(0.5d * phi)) + Math.Cos(phi); + double delta = deltaNumerator / deltaDenominator; + phi -= delta; + if (Math.Abs(delta) < Eps7) + { + break; + } + } + + double x = Fxc * lambda * (1d + ((2d * Math.Cos(phi)) / Math.Cos(0.5d * phi))); + double y = Fyc * Math.Sin(0.5d * phi); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = Ryc * yy; + double t = phi; + if (Math.Abs(phi) > 1d) + { + if (Math.Abs(phi) > OneTol) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + if (phi < 0d) + { + t = -1d; + phi = -PI; + } + else + { + t = 1d; + phi = PI; + } + } + else + { + phi = 2d * Math.Asin(phi); + } + + double lambda = Rxc * xx / (1d + ((2d * Math.Cos(phi)) / Math.Cos(0.5d * phi))); + phi = Rc * (t + Math.Sin(phi)); + if (Math.Abs(phi) > 1d) + { + if (Math.Abs(phi) > OneTol) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + phi = phi < 0d ? -HalfPi : HalfPi; + } + else + { + phi = Math.Asin(phi); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarSineProjection.cs b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarSineProjection.cs new file mode 100644 index 00000000..62cca7b8 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarSineProjection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical McBryde-Thomas Flat-Polar Sine (No. 1) projection (mbt_s). +/// +/// +/// McBryde-Thomas Flat-Polar Sine (No. 1) is the STS-family member of the McBryde-Thomas +/// series. Its numerical behavior is provided by with the +/// McBryde-Thomas constants for the first flat-polar sine variant. +/// +internal sealed class McBrydeThomasFlatPolarSineProjection : StsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public McBrydeThomasFlatPolarSineProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public McBrydeThomasFlatPolarSineProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "McBryde_Thomas_Flat_Polar_Sine", 1.48875d, 1.36509d, false) + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new McBrydeThomasFlatPolarSineProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarSinusoidalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarSinusoidalProjection.cs new file mode 100644 index 00000000..751a8562 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPolarSinusoidalProjection.cs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical McBryde-Thomas Flat-Polar Sinusoidal projection (mbtfps). +/// +/// +/// McBryde-Thomas Flat-Polar Sinusoidal specializes +/// with the parameter set used for the +/// McBryde-Thomas sinusoidal variant. Its numerical behavior therefore follows the verified +/// parameterized sinusoidal base formulation. +/// +internal sealed class McBrydeThomasFlatPolarSinusoidalProjection : GeneralSinusoidalProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public McBrydeThomasFlatPolarSinusoidalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public McBrydeThomasFlatPolarSinusoidalProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "McBryde_Thomas_Flat_Polar_Sinusoidal"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "m", 0.5d); + ReplaceOrAdd(merged, "n", 1.785398163397448309615660845d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPoleSineProjection.cs b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPoleSineProjection.cs new file mode 100644 index 00000000..71e4326c --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/McBrydeThomasFlatPoleSineProjection.cs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical McBryde-Thomas Flat-Pole Sine (No. 2) projection (mbt_fps). +/// +/// +/// McBryde-Thomas Flat-Pole Sine (No. 2) is the second sine-based spherical member of the +/// McBryde-Thomas family. The implementation iteratively solves the auxiliary latitude used +/// by the original formulation and then applies the published flat-pole sine scaling +/// constants. +/// +internal sealed class McBrydeThomasFlatPoleSineProjection : MapProjection +{ + private const int MaximumIterations = 10; + private const double C1 = 0.45503d; + private const double C2 = 1.36509d; + private const double C3 = 1.41546d; + private const double CX = 0.22248d; + private const double CY = 1.44492d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public McBrydeThomasFlatPoleSineProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public McBrydeThomasFlatPoleSineProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "McBryde_Thomas_Flat_Pole_Sine"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new McBrydeThomasFlatPoleSineProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double k = C3 * Math.Sin(phi); + for (int i = 0; i < MaximumIterations; i++) + { + double t = phi / C2; + double denominator = (ProjectionConstants.OneThird * Math.Cos(t)) + Math.Cos(phi); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double v = ((C1 * Math.Sin(t)) + Math.Sin(phi) - k) / denominator; + phi -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + double tt = phi / C2; + lon = this.SphericalRadius * (CX * lambda * (1d + ((3d * Math.Cos(phi)) / Math.Cos(tt)))); + lat = this.SphericalRadius * (CY * Math.Sin(tt)); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + double t = Asinz(yUnit / CY); + double phi = C2 * t; + double denominator = CX * (1d + ((3d * Math.Cos(phi)) / Math.Cos(t))); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xUnit / denominator; + phi = Asinz(((C1 * Math.Sin(t)) + Math.Sin(phi)) / C3); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Mercator.cs b/src/ProjNet/CoordinateSystems/Projections/Mercator.cs index 34028ce3..ba29a369 100644 --- a/src/ProjNet/CoordinateSystems/Projections/Mercator.cs +++ b/src/ProjNet/CoordinateSystems/Projections/Mercator.cs @@ -1,180 +1,173 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Mercator projection. +/// +/// +/// This map projection introduced in 1569 by Gerardus Mercator. It is often described as a cylindrical projection, +/// but it must be derived mathematically. The meridians are equally spaced, parallel vertical lines, and the +/// parallels of latitude are parallel, horizontal straight lines, spaced farther and farther apart as their distance +/// from the Equator increases. This projection is widely used for navigation charts, because any straight line +/// on a Mercator-projection map is a line of constant true bearing that enables a navigator to plot a straight-line +/// course. It is less practical for world maps because the scale is distorted; areas farther away from the equator +/// appear disproportionately large. On a Mercator projection, for example, the landmass of Greenland appears to be +/// greater than that of the continent of South America; in actual area, Greenland is smaller than the Arabian Peninsula. +/// +/// The ellipsoidal 1SP formulation was independently verified against IOGP, "Geomatics +/// Guidance Note 7, part 2: Coordinate Conversions and Transformations including +/// Formulas" (publication 373-7-2, 2019), EPSG method 9804, Mercator (variant A). +/// The forward northing and easting equations match the published a * k0 * ln(...) +/// and a * k0 * (lon - lon0) form. +/// The ellipsoidal 2SP formulation was independently verified against IOGP, "Geomatics +/// Guidance Note 7, part 2: Coordinate Conversions and Transformations including +/// Formulas" (publication 373-7-2, 2019), EPSG method 9805, Mercator (variant B). +/// The scale factor computation cos(latSP) / sqrt(1 - e² * sin²(latSP)) and its +/// reuse in the forward easting and northing equations match the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 7, pp. 41-47, +/// eqs. (7-1) through (7-12), for the classic Mercator development. +/// +/// EPSG method 9804: Mercator (variant A). +/// EPSG method 9805: Mercator (variant B). +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.1.2, pp. 49-51. +internal class Mercator : MapProjection { /// - /// Implements the Mercator projection. - /// - /// - /// This map projection introduced in 1569 by Gerardus Mercator. It is often described as a cylindrical projection, - /// but it must be derived mathematically. The meridians are equally spaced, parallel vertical lines, and the - /// parallels of latitude are parallel, horizontal straight lines, spaced farther and farther apart as their distance - /// from the Equator increases. This projection is widely used for navigation charts, because any straight line - /// on a Mercator-projection map is a line of constant true bearing that enables a navigator to plot a straight-line - /// course. It is less practical for world maps because the scale is distorted; areas farther away from the equator - /// appear disproportionately large. On a Mercator projection, for example, the landmass of Greenland appears to be - /// greater than that of the continent of South America; in actual area, Greenland is smaller than the Arabian Peninsula. - /// + /// Scale coefficient at the projection origin. + /// + private readonly double k0; + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + public Mercator(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + /// + /// The parameters this projection expects are listed below. + /// + /// ItemsDescriptions + /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). + /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). + /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. + /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). + /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. + /// /// - [Serializable] - internal class Mercator : MapProjection + protected Mercator(IEnumerable parameters, Mercator? isInverse) + : base(parameters, isInverse) { - //double lon_center; //Center longitude (projection center) - //double lat_origin; //center latitude - //double e,e2; //eccentricity constants - private readonly double _k0; //small value m - - /// - /// Initializes the MercatorProjection object with the specified parameters to project points. - /// - /// ParameterList with the required parameters. - /// - /// - public Mercator(IEnumerable parameters) - : this(parameters, null) + this.Authority = "EPSG"; + ProjectionParameter? scaleFactor = this.GetParameter("scale_factor"); + + // This is a two standard parallel Mercator projection (2SP). + if (scaleFactor is null) { + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + this.k0 = cosLatitudeOrigin / Math.Sqrt(1.0 - (this.es * sinLatitudeOrigin * sinLatitudeOrigin)); + this.AuthorityCode = 9805; + this.Name = "Mercator_2SP"; } - /// - /// Initializes the MercatorProjection object with the specified parameters. - /// - /// List of parameters to initialize the projection. - /// Indicates whether the projection forward (meters to degrees or degrees to meters). - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. - /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). - /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. - /// - /// - protected Mercator(IEnumerable parameters, Mercator isInverse) - : base(parameters, isInverse) + // This is a one standard parallel Mercator projection (1SP). + else { - Authority = "EPSG"; - var scaleFactor = GetParameter("scale_factor"); - - if (scaleFactor == null) //This is a two standard parallel Mercator projection (2SP) - { - _k0 = Math.Cos(lat_origin) / Math.Sqrt(1.0 - _es * Math.Sin(lat_origin) * Math.Sin(lat_origin)); - AuthorityCode = 9805; - Name = "Mercator_2SP"; - } - else //This is a one standard parallel Mercator projection (1SP) - { - _k0 = scaleFactor.Value; - Name = "Mercator_1SP"; - } + this.k0 = scaleFactor.Value; + this.Name = "Mercator_1SP"; } + } - /// - /// Converts coordinates in decimal degrees to projected meters. - /// - /// The longitude of the point in decimal degrees. - /// The latitude of the point in decimal degrees. - /// Point in projected meters - protected override void RadiansToMeters(ref double lon, ref double lat) + /// + /// Converts coordinates in radians to projected meters. + /// + /// The longitude of the point in radians. + /// The latitude of the point in radians. + protected override void RadiansToMeters(ref double lon, ref double lat) + { + if (double.IsNaN(lon) || double.IsNaN(lat)) { - if (double.IsNaN(lon) || double.IsNaN(lat)) - { - lon = double.NaN; - lat = double.NaN; - return; - } - - double dLongitude = lon; - double dLatitude = lat; - - /* Forward equations */ - if (Math.Abs(Math.Abs(dLatitude) - HALF_PI) <= EPSLN) - throw new ArgumentException("Transformation cannot be computed at the poles."); - - double esinphi = _e * Math.Sin(dLatitude); - lon = _semiMajor * _k0 * (dLongitude - central_meridian); - lat = _semiMajor * _k0 * Math.Log(Math.Tan(PI * 0.25 + dLatitude * 0.5) * - Math.Pow((1 - esinphi) / (1 + esinphi), _e * 0.5)); + lon = double.NaN; + lat = double.NaN; + return; } - /// - /// Converts coordinates in projected meters to decimal degrees. - /// - /// The x-ordinate in projected meters - /// The y-ordinate in projected meters - /// Transformed point in decimal degrees - protected override void MetersToRadians(ref double x, ref double y) + double dLongitude = lon; + double dLatitude = lat; + + if (Math.Abs(dLatitude) >= HalfPi) { - /* Inverse equations - -----------------*/ - double dX = x; // * _metersPerUnit - this._falseEasting; - double dY = y; // * _metersPerUnit - this._falseNorthing; - double ts = Math.Exp(-dY / (_semiMajor * _k0)); //t + ProjectionThrowHelper.ThrowInvalidOperation("Transformation cannot be computed at the poles."); + } - double chi = HALF_PI - 2 * Math.Atan(ts); - double e4 = Math.Pow(_e, 4); - double e6 = Math.Pow(_e, 6); - double e8 = Math.Pow(_e, 8); + Sincos(dLatitude, out double sinLatitude, out double cosLatitude); + double esinphi = this.e * sinLatitude; + lon = this.semiMajor * this.k0 * (dLongitude - this.centralMeridian); + lat = this.semiMajor * this.k0 * (Asinh(sinLatitude / cosLatitude) - (this.e * Atanh(esinphi))); + } - y = chi + (_es * 0.5 + 5 * e4 / 24 + e6 / 12 + 13 * e8 / 360) * Math.Sin(2 * chi) - + (7 * e4 / 48 + 29 * e6 / 240 + 811 * e8 / 11520) * Math.Sin(4 * chi) + - +(7 * e6 / 120 + 81 * e8 / 1120) * Math.Sin(6 * chi) + - +(4279 * e8 / 161280) * Math.Sin(8 * chi); + /// + /// Converts coordinates in projected meters to decimal degrees. + /// + /// The x-ordinate in projected meters. + /// The y-ordinate in projected meters. + protected override void MetersToRadians(ref double x, ref double y) + { + // Inverse equations + double dX = x; + double dY = y; + double ts = Math.Exp(-dY / (this.semiMajor * this.k0)); // t + + double chi = HalfPi - (2 * Math.Atan(ts)); + double e4 = Math.Pow(this.e, 4); + double e6 = Math.Pow(this.e, 6); + double e8 = Math.Pow(this.e, 8); + + y = chi + (((this.es * 0.5) + (5 * e4 / 24) + (e6 / 12) + (13 * e8 / 360)) * Math.Sin(2 * chi)) + + (((7 * e4 / 48) + (29 * e6 / 240) + (811 * e8 / 11520)) * Math.Sin(4 * chi)) + + (+((7 * e6 / 120) + (81 * e8 / 1120)) * Math.Sin(6 * chi)) + + (+(4279 * e8 / 161280) * Math.Sin(8 * chi)); + + x = (dX / (this.semiMajor * this.k0)) + this.centralMeridian; + } - x = dX / (_semiMajor * _k0) + central_meridian; + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= new Mercator(this.Parameters.ToProjectionParameter(), this); - //return (x, y, z); - } + return this.inverse; + } - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new Mercator(_Parameters.ToProjectionParameter(), this); - return _inverse; - } + private static double Asinh(double value) + { + return value >= 0d + ? Math.Log(value + Hypot(1d, value)) + : -Math.Log(-value + Hypot(1d, value)); + } + + private static double Atanh(double value) + { + return 0.5d * Math.Log((1d + value) / (1d - value)); } } diff --git a/src/ProjNet/CoordinateSystems/Projections/MercatorAuxiliarySphere.cs b/src/ProjNet/CoordinateSystems/Projections/MercatorAuxiliarySphere.cs index f7114429..e096eba9 100644 --- a/src/ProjNet/CoordinateSystems/Projections/MercatorAuxiliarySphere.cs +++ b/src/ProjNet/CoordinateSystems/Projections/MercatorAuxiliarySphere.cs @@ -1,110 +1,100 @@ -using ProjNet.CoordinateSystems.Transformations; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Mercator Auxiliary Sphere projection (Web Mercator, EPSG:3857). +/// +/// +/// Applies a spherical Mercator formula using the semi-major axis as the sphere radius, +/// without ellipsoidal correction. This is the projection used by most web mapping services. +/// This class implements the spherical special case of , so its +/// core longitude and logarithmic latitude mapping is covered by the independently +/// verified Mercator formulation documented there. +/// +internal sealed class MercatorAuxiliarySphere : MapProjection { + // Scale factor – for the spherical (auxiliary) Mercator this is 1. + private const double k0 = 1.0; + /// - /// Implements the Mercator Auxiliary Sphere projection (Web Mercator). - /// This projection uses a spherical model with a constant radius. + /// Initializes a new instance of the class. /// - [Serializable] - internal class MercatorAuxiliarySphere : MapProjection + /// List of projection parameters. + public MercatorAuxiliarySphere(IEnumerable parameters) + : this(parameters, null) { - // Scale factor – for the spherical (auxiliary) Mercator this is 1. - private const double _k0 = 1.0; - - /// - /// Initializes the MercatorAuxiliarySphere projection with the specified parameters. - /// - /// List of projection parameters. - public MercatorAuxiliarySphere(IEnumerable parameters) - : this(parameters, null) - { - } + } - /// - /// Initializes the MercatorAuxiliarySphere projection with the specified parameters. - /// - /// List of projection parameters. - /// Reference to the inverse projection. - protected MercatorAuxiliarySphere(IEnumerable parameters, MercatorAuxiliarySphere isInverse) - : base(parameters, isInverse) - { - Authority = "EPSG"; - Name = "Mercator_Auxiliary_Sphere"; - } + /// + /// Initializes a new instance of the class. + /// + /// List of projection parameters. + /// Inverse transform instance when cloning. + private MercatorAuxiliarySphere(IEnumerable parameters, MercatorAuxiliarySphere? isInverse) + : base(parameters, isInverse) + { + this.Authority = "EPSG"; + this.Name = "Mercator_Auxiliary_Sphere"; + } - /// - /// Converts geographic coordinates (in radians) to projected coordinates (in meters). - /// - /// Longitude in radians. - /// Latitude in radians. - /// - /// It is assumed that _semiMajor and central_meridian (as well as other parameters like false_easting/false_northing) - /// are already set in the base class. - /// - protected override void RadiansToMeters(ref double lon, ref double lat) + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + if (double.IsNaN(lon) || double.IsNaN(lat)) { - if (double.IsNaN(lon) || double.IsNaN(lat)) - { - lon = double.NaN; - lat = double.NaN; - return; - } - - double dLon = lon; - double dLat = lat; - - if (Math.Abs(Math.Abs(dLat) - HALF_PI) <= EPSLN) - { - throw new ArgumentException("Transformation cannot be computed at the poles."); - } - - // Forward equations for the Spherical (Auxiliary) Mercator Projection: - // X = semiMajor * k0 * (lon - central_meridian) - // Y = semiMajor * k0 * ln( tan(PI/4 + lat/2) ) - lon = _semiMajor * _k0 * (dLon - central_meridian); - lat = _semiMajor * _k0 * Math.Log(Math.Tan((PI * 0.25) + (dLat * 0.5))); - // Note: false_easting and false_northing can be added here if necessary. + lon = double.NaN; + lat = double.NaN; + return; } - /// - /// Converts projected coordinates (in meters) to geographic coordinates (in radians). - /// - /// X coordinate in meters. - /// Y coordinate in meters. - /// - /// Uses the inverse transformation of the Spherical Mercator Projection. - /// - protected override void MetersToRadians(ref double x, ref double y) - { - double dX = x; - double dY = y; - - // Inverse equations: - // lon = central_meridian + X / (semiMajor * k0) - // lat = PI/2 - 2 * atan( exp( -Y / (semiMajor * k0) ) ) - double ts = Math.Exp(-dY / (_semiMajor * _k0)); - double dLat = HALF_PI - (2 * Math.Atan(ts)); - double dLon = central_meridian + (dX / (_semiMajor * _k0)); - - x = dLon; - y = dLat; - // Note: false_easting/false_northing can be subtracted here if provided in the parameter list. - } + double dLon = lon; + double dLat = lat; - /// - /// Returns the inverse transformation of this projection. - /// - /// The inverse projection as MathTransform. - public override MathTransform Inverse() + if (Math.Abs(Math.Abs(dLat) - HalfPi) <= Epsln) { - if (_inverse is null) - { - _inverse = new MercatorAuxiliarySphere(_Parameters.ToProjectionParameter(), this); - } - return _inverse; + ProjectionThrowHelper.ThrowInvalidOperation("Transformation cannot be computed at the poles."); } + + // Forward equations for the Spherical (Auxiliary) Mercator Projection: + // X = semiMajor * k0 * (lon - central_meridian) + // Y = semiMajor * k0 * ln( tan(PI/4 + lat/2) ) + lon = this.semiMajor * k0 * (dLon - this.centralMeridian); + lat = this.semiMajor * k0 * Math.Log(Math.Tan((PI * 0.25) + (dLat * 0.5))); + + // Note: false_easting and false_northing can be added here if necessary. + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double dX = x; + double dY = y; + + // Inverse equations: + // lon = central_meridian + X / (semiMajor * k0) + // lat = PI/2 - 2 * atan( exp( -Y / (semiMajor * k0) ) ) + double ts = Math.Exp(-dY / (this.semiMajor * k0)); + double dLat = HalfPi - (2 * Math.Atan(ts)); + double dLon = this.centralMeridian + (dX / (this.semiMajor * k0)); + + x = dLon; + y = dLat; + + // Note: false_easting/false_northing can be subtracted here if provided in the parameter list. + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new MercatorAuxiliarySphere(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/MillerCylindricalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/MillerCylindricalProjection.cs new file mode 100644 index 00000000..0101184f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/MillerCylindricalProjection.cs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Miller Cylindrical projection (mill). +/// +/// +/// A compromise cylindrical projection that reduces the high-latitude area exaggeration +/// of the Mercator projection by compressing the latitude formula. Poles cannot be projected. +/// The formulation was independently verified against the Wikipedia article +/// "Miller cylindrical projection". The forward northing +/// 1.25 * ln(tan(π / 4 + 0.4 * φ)) and its inverse recovery +/// 2.5 * (atan(exp(0.8 * y)) - π / 4) match the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 11, pp. 86-89, +/// eqs. (11-1) through (11-4), for the Miller cylindrical derivation. +/// +/// Wikipedia: Miller cylindrical projection. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 6, Sect. 6.3.8, pp. 182-184. +internal sealed class MillerCylindricalProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public MillerCylindricalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public MillerCylindricalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Miller_Cylindrical"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new MillerCylindricalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + if (double.IsNaN(lon) || double.IsNaN(lat)) + { + lon = double.NaN; + lat = double.NaN; + return; + } + + if (Math.Abs(Math.Abs(lat) - HalfPi) <= Epsln) + { + ProjectionThrowHelper.ThrowInvalidOperation("Transformation cannot be computed at the poles."); + } + + double lambda = Adjust_lon(lon - this.centralMeridian); + lon = this.SphericalRadius * lambda; + lat = this.SphericalRadius * 1.25d * Math.Log(Math.Tan(FortPi + (0.4d * lat))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x = Adjust_lon(this.centralMeridian + (x * this.InverseSphericalRadius)); + y = 2.5d * (Math.Atan(Math.Exp((0.8d * y) * this.InverseSphericalRadius)) - FortPi); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/MillerOblatedStereographicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/MillerOblatedStereographicProjection.cs new file mode 100644 index 00000000..72ef137a --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/MillerOblatedStereographicProjection.cs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Miller Oblated Stereographic projection (mil_os). +/// +/// +/// Miller Oblated Stereographic is a two-coefficient specialization of +/// attributed to Miller. It reuses the +/// shared modified-stereographic workflow with the Miller-specific polynomial coefficients. +/// +internal sealed class MillerOblatedStereographicProjection : ModifiedStereographicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public MillerOblatedStereographicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public MillerOblatedStereographicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Miller_Oblated_Stereographic") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new MillerOblatedStereographicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void ConfigureVariant( + out double lambda0, + out double phi0, + out double semiMajor, + out double es, + out ComplexNumber[] coefficients, + out int polynomialOrder) + { + lambda0 = DegreesToRadians(20d); + phi0 = DegreesToRadians(18d); + semiMajor = this.semiMajor; + es = 0d; + coefficients = GetMilOsCoefficients(); + polynomialOrder = 2; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ModifiedKrovakProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ModifiedKrovakProjection.cs new file mode 100644 index 00000000..74d46113 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ModifiedKrovakProjection.cs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Modified Krovak projection variant with the PROJ polynomial correction terms. +/// +internal sealed class ModifiedKrovakProjection : KrovakProjection +{ + private const double X0 = 1089000.0; + private const double Y0 = 654000.0; + private const double C1 = 2.946529277E-02d; + private const double C2 = 2.515965696E-02d; + private const double C3 = 1.193845912E-07d; + private const double C4 = -4.668270147E-07d; + private const double C5 = 9.233980362E-12d; + private const double C6 = 1.523735715E-12d; + private const double C7 = 1.696780024E-18d; + private const double C8 = 4.408314235E-18d; + private const double C9 = -8.331083518E-24d; + private const double C10 = -3.689471323E-24d; + + /// + /// Initializes a new instance of the class. + /// + /// The projection parameters. + public ModifiedKrovakProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + private ModifiedKrovakProjection(IEnumerable parameters, KrovakProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Modified Krovak"; + } + + /// + protected override KrovakProjection CreateInverseProjection() => new ModifiedKrovakProjection(this.Parameters.ToProjectionParameter(), this); + + /// + protected override bool TryComputeModifiedDelta( + double southing, + double westing, + out double deltaSouthing, + out double deltaWesting) + { + double reducedSouthing = southing - X0; + double reducedWesting = westing - Y0; + double reducedSouthingSquared = reducedSouthing * reducedSouthing; + double reducedWestingSquared = reducedWesting * reducedWesting; + double reducedSouthingFourth = reducedSouthingSquared * reducedSouthingSquared; + double reducedWestingFourth = reducedWestingSquared * reducedWestingSquared; + + deltaSouthing = C1 + + (C3 * reducedSouthing) + - (C4 * reducedWesting) + - (2d * C6 * reducedSouthing * reducedWesting) + + (C5 * (reducedSouthingSquared - reducedWestingSquared)) + + (C7 * reducedSouthing * (reducedSouthingSquared - (3d * reducedWestingSquared))) + - (C8 * reducedWesting * ((3d * reducedSouthingSquared) - reducedWestingSquared)) + + (4d * C9 * reducedSouthing * reducedWesting * (reducedSouthingSquared - reducedWestingSquared)) + + (C10 * (reducedSouthingFourth + reducedWestingFourth - (6d * reducedSouthingSquared * reducedWestingSquared))); + + deltaWesting = C2 + + (C3 * reducedWesting) + + (C4 * reducedSouthing) + + (2d * C5 * reducedSouthing * reducedWesting) + + (C6 * (reducedSouthingSquared - reducedWestingSquared)) + + (C8 * reducedSouthing * (reducedSouthingSquared - (3d * reducedWestingSquared))) + + (C7 * reducedWesting * ((3d * reducedSouthingSquared) - reducedWestingSquared)) + - (4d * C10 * reducedSouthing * reducedWesting * (reducedSouthingSquared - reducedWestingSquared)) + + (C9 * (reducedSouthingFourth + reducedWestingFourth - (6d * reducedSouthingSquared * reducedWestingSquared))); + + return true; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographic48USProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographic48USProjection.cs new file mode 100644 index 00000000..eef46488 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographic48USProjection.cs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the modified stereographic projection of the 48 U.S. (gs48). +/// +/// +/// Modified Stereographic of 48 U.S. is a Snyder-era regional specialization of +/// . It uses the shared complex polynomial +/// correction with the coefficient set published for the contiguous United States. +/// +internal sealed class ModifiedStereographic48USProjection : ModifiedStereographicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ModifiedStereographic48USProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ModifiedStereographic48USProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Modified_Stereographic_Of_48_US") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ModifiedStereographic48USProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void ConfigureVariant( + out double lambda0, + out double phi0, + out double semiMajor, + out double es, + out ComplexNumber[] coefficients, + out int polynomialOrder) + { + lambda0 = DegreesToRadians(-96d); + phi0 = DegreesToRadians(39d); + semiMajor = 6370997d; + es = 0d; + coefficients = GetGs48Coefficients(); + polynomialOrder = 4; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographic50USProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographic50USProjection.cs new file mode 100644 index 00000000..5704a871 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographic50USProjection.cs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the modified stereographic projection of the 50 U.S. (gs50). +/// +/// +/// Modified Stereographic of 50 U.S. is a Snyder-era regional specialization of +/// . It switches between the spherical and +/// ellipsoidal coefficient sets defined for the 50-state composite layout. +/// +internal sealed class ModifiedStereographic50USProjection : ModifiedStereographicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ModifiedStereographic50USProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ModifiedStereographic50USProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Modified_Stereographic_Of_50_US") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ModifiedStereographic50USProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void ConfigureVariant( + out double lambda0, + out double phi0, + out double semiMajor, + out double es, + out ComplexNumber[] coefficients, + out int polynomialOrder) + { + lambda0 = DegreesToRadians(-120d); + phi0 = DegreesToRadians(45d); + polynomialOrder = 9; + + if (this.es != 0d) + { + semiMajor = Ellipsoid.Clarke1866.SemiMajorAxis; + es = 0.00676866d; + coefficients = GetGs50EllipsoidalCoefficients(); + } + else + { + semiMajor = 6370997d; + es = 0d; + coefficients = GetGs50SphericalCoefficients(); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographicAlaskaProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographicAlaskaProjection.cs new file mode 100644 index 00000000..dd42be36 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographicAlaskaProjection.cs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the modified stereographic projection of Alaska (alsk). +/// +/// +/// Modified Stereographic of Alaska is the Alaska specialization of +/// . It uses the shared complex polynomial +/// workflow with separate spherical and ellipsoidal coefficient sets for the Alaska map. +/// +internal sealed class ModifiedStereographicAlaskaProjection : ModifiedStereographicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ModifiedStereographicAlaskaProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ModifiedStereographicAlaskaProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Modified_Stereographic_Of_Alaska") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ModifiedStereographicAlaskaProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void ConfigureVariant( + out double lambda0, + out double phi0, + out double semiMajor, + out double es, + out ComplexNumber[] coefficients, + out int polynomialOrder) + { + lambda0 = DegreesToRadians(-152d); + phi0 = DegreesToRadians(64d); + polynomialOrder = 5; + + if (this.es != 0d) + { + semiMajor = Ellipsoid.Clarke1866.SemiMajorAxis; + es = 0.00676866d; + coefficients = GetAlskEllipsoidalCoefficients(); + } + else + { + semiMajor = 6370997d; + es = 0d; + coefficients = GetAlskSphericalCoefficients(); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographicProjectionBase.cs b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographicProjectionBase.cs new file mode 100644 index 00000000..f554d276 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ModifiedStereographicProjectionBase.cs @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Provides the shared implementation of the modified stereographic projection family. +/// +/// +/// ModifiedStereographicProjectionBase implements Snyder's modified stereographic family, +/// where a stereographic conformal sphere is followed by a complex polynomial correction. +/// The shared algorithm applies the forward projection by evaluating the configured complex +/// coefficient series and recovers the inverse by Newton iteration over the same polynomial. +/// +internal abstract class ModifiedStereographicProjectionBase : MapProjection +{ + private const int MaximumNewtonIterations = 20; + private const double NewtonTolerance = 1e-12d; + + private static readonly ComplexNumber[] MilOsCoefficients = + [ + new ComplexNumber(0.924500d, 0d), + new ComplexNumber(0d, 0d), + new ComplexNumber(0.019430d, 0d), + ]; + + private static readonly ComplexNumber[] LeeOsCoefficients = + [ + new ComplexNumber(0.721316d, 0d), + new ComplexNumber(0d, 0d), + new ComplexNumber(-0.0088162d, -0.00617325d), + ]; + + private static readonly ComplexNumber[] Gs48Coefficients = + [ + new ComplexNumber(0.98879d, 0d), + new ComplexNumber(0d, 0d), + new ComplexNumber(-0.050909d, 0d), + new ComplexNumber(0d, 0d), + new ComplexNumber(0.075528d, 0d), + ]; + + private static readonly ComplexNumber[] AlskEllipsoidalCoefficients = + [ + new ComplexNumber(0.9945303d, 0d), + new ComplexNumber(0.0052083d, -0.0027404d), + new ComplexNumber(0.0072721d, 0.0048181d), + new ComplexNumber(-0.0151089d, -0.1932526d), + new ComplexNumber(0.0642675d, -0.1381226d), + new ComplexNumber(0.3582802d, -0.2884586d), + ]; + + private static readonly ComplexNumber[] AlskSphericalCoefficients = + [ + new ComplexNumber(0.9972523d, 0d), + new ComplexNumber(0.0052513d, -0.0041175d), + new ComplexNumber(0.0074606d, 0.0048125d), + new ComplexNumber(-0.0153783d, -0.1968253d), + new ComplexNumber(0.0636871d, -0.1408027d), + new ComplexNumber(0.3660976d, -0.2937382d), + ]; + + private static readonly ComplexNumber[] Gs50EllipsoidalCoefficients = + [ + new ComplexNumber(0.9827497d, 0d), + new ComplexNumber(0.0210669d, 0.0053804d), + new ComplexNumber(-0.1031415d, -0.0571664d), + new ComplexNumber(-0.0323337d, -0.0322847d), + new ComplexNumber(0.0502303d, 0.1211983d), + new ComplexNumber(0.0251805d, 0.0895678d), + new ComplexNumber(-0.0012315d, -0.1416121d), + new ComplexNumber(0.0072202d, -0.1317091d), + new ComplexNumber(-0.0194029d, 0.0759677d), + new ComplexNumber(-0.0210072d, 0.0834037d), + ]; + + private static readonly ComplexNumber[] Gs50SphericalCoefficients = + [ + new ComplexNumber(0.9842990d, 0d), + new ComplexNumber(0.0211642d, 0.0037608d), + new ComplexNumber(-0.1036018d, -0.0575102d), + new ComplexNumber(-0.0329095d, -0.0320119d), + new ComplexNumber(0.0499471d, 0.1223335d), + new ComplexNumber(0.0260460d, 0.0899805d), + new ComplexNumber(0.0007388d, -0.1435792d), + new ComplexNumber(0.0075848d, -0.1334108d), + new ComplexNumber(-0.0216473d, 0.0776645d), + new ComplexNumber(-0.0225161d, 0.0853673d), + ]; + + private readonly double lambda0; + private readonly double phi0; + private readonly double effectiveSemiMajor; + private readonly double effectiveScale; + private readonly double inverseEffectiveScale; + private readonly double effectiveEs; + private readonly double effectiveE; + private readonly ComplexNumber[] coefficients; + private readonly int polynomialOrder; + private readonly double schio; + private readonly double cchio; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + /// Projection name. + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA2214:Do not call overridable methods in constructors", + Justification = "Variant-specific constants must be provided by derived projection types during initialization.")] + protected ModifiedStereographicProjectionBase(IEnumerable parameters, MapProjection? inverse, string name) + : base(parameters, inverse) + { + this.Name = name; + + this.ConfigureVariant( + out this.lambda0, + out this.phi0, + out this.effectiveSemiMajor, + out this.effectiveEs, + out this.coefficients, + out this.polynomialOrder); + + this.effectiveE = Math.Sqrt(this.effectiveEs); + this.effectiveScale = this.effectiveSemiMajor * this.scaleFactor; + if (Math.Abs(this.effectiveScale) <= Eps10) + { + ArgumentGuard.ThrowArgument("Scale factor must be non-zero for modified stereographic projection.", nameof(parameters)); + } + + this.inverseEffectiveScale = 1d / this.effectiveScale; + double chi0 = this.effectiveEs != 0d + ? this.ComputeConformalLatitude(this.phi0) + : this.phi0; + + this.schio = Math.Sin(chi0); + this.cchio = Math.Cos(chi0); + } + + /// + /// Gets the coefficient set used by mil_os. + /// + /// The coefficient array. + protected static ComplexNumber[] GetMilOsCoefficients() => MilOsCoefficients; + + /// + /// Gets the coefficient set used by lee_os. + /// + /// The coefficient array. + protected static ComplexNumber[] GetLeeOsCoefficients() => LeeOsCoefficients; + + /// + /// Gets the coefficient set used by gs48. + /// + /// The coefficient array. + protected static ComplexNumber[] GetGs48Coefficients() => Gs48Coefficients; + + /// + /// Gets the coefficient set used by ellipsoidal alsk. + /// + /// The coefficient array. + protected static ComplexNumber[] GetAlskEllipsoidalCoefficients() => AlskEllipsoidalCoefficients; + + /// + /// Gets the coefficient set used by spherical alsk. + /// + /// The coefficient array. + protected static ComplexNumber[] GetAlskSphericalCoefficients() => AlskSphericalCoefficients; + + /// + /// Gets the coefficient set used by ellipsoidal gs50. + /// + /// The coefficient array. + protected static ComplexNumber[] GetGs50EllipsoidalCoefficients() => Gs50EllipsoidalCoefficients; + + /// + /// Gets the coefficient set used by spherical gs50. + /// + /// The coefficient array. + protected static ComplexNumber[] GetGs50SphericalCoefficients() => Gs50SphericalCoefficients; + + /// + /// Configures variant-specific constants. + /// + /// Variant central meridian. + /// Variant latitude of origin. + /// Variant semi-major axis. + /// Variant eccentricity squared. + /// Complex polynomial coefficients. + /// Polynomial order used by PROJ. + protected abstract void ConfigureVariant( + out double lambda0, + out double phi0, + out double semiMajor, + out double es, + out ComplexNumber[] coefficients, + out int polynomialOrder); + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.lambda0); + double chi = this.ComputeConformalLatitude(lat); + double schi = Math.Sin(chi); + double cchi = Math.Cos(chi); + double sinLambda = Math.Sin(lambda); + double cosLambda = Math.Cos(lambda); + + double denominator = 1d + (this.schio * schi) + (this.cchio * cchi * cosLambda); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double s = 2d / denominator; + ComplexNumber p = new( + s * cchi * sinLambda, + s * ((this.cchio * schi) - (this.schio * cchi * cosLambda))); + + p = EvaluateComplexPolynomial(p, this.coefficients, this.polynomialOrder); + lon = this.effectiveScale * p.Real; + lat = this.effectiveScale * p.Imaginary; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double targetX = x * this.inverseEffectiveScale; + double targetY = y * this.inverseEffectiveScale; + + ComplexNumber p = new(targetX, targetY); + bool converged = false; + for (int i = 0; i < MaximumNewtonIterations; i++) + { + ComplexNumber f = EvaluateComplexPolynomialAndDerivative(p, this.coefficients, this.polynomialOrder, out ComplexNumber derivative); + f = new ComplexNumber(f.Real - targetX, f.Imaginary - targetY); + + double denominator = (derivative.Real * derivative.Real) + (derivative.Imaginary * derivative.Imaginary); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + ComplexNumber delta = new( + -((f.Real * derivative.Real) + (f.Imaginary * derivative.Imaginary)) / denominator, + -((f.Imaginary * derivative.Real) - (f.Real * derivative.Imaginary)) / denominator); + + p = new ComplexNumber(p.Real + delta.Real, p.Imaginary + delta.Imaginary); + if (Math.Abs(delta.Real) + Math.Abs(delta.Imaginary) <= NewtonTolerance) + { + converged = true; + break; + } + } + + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double rh = Hypot(p.Real, p.Imaginary); + if (rh <= NewtonTolerance) + { + x = Adjust_lon(this.lambda0); + y = this.phi0; + return; + } + + double z = 2d * Math.Atan(0.5d * rh); + double sinZ = Math.Sin(z); + double cosZ = Math.Cos(z); + double chi = Asinz((cosZ * this.schio) + ((p.Imaginary * sinZ * this.cchio) / rh)); + double phi = this.ComputeGeodeticLatitude(chi); + double lambda = Math.Atan2( + p.Real * sinZ, + (rh * this.cchio * cosZ) - (p.Imaginary * this.schio * sinZ)); + + x = Adjust_lon(this.lambda0 + lambda); + y = phi; + } + + private static ComplexNumber EvaluateComplexPolynomial(ComplexNumber z, IReadOnlyList coefficients, int order) + { + int index = order; + ComplexNumber value = coefficients[index]; + while (index > 0) + { + index--; + double previousReal = value.Real; + value = new ComplexNumber( + coefficients[index].Real + (z.Real * previousReal) - (z.Imaginary * value.Imaginary), + coefficients[index].Imaginary + (z.Real * value.Imaginary) + (z.Imaginary * previousReal)); + } + + double tailReal = value.Real; + return new ComplexNumber( + (z.Real * tailReal) - (z.Imaginary * value.Imaginary), + (z.Real * value.Imaginary) + (z.Imaginary * tailReal)); + } + + private static ComplexNumber EvaluateComplexPolynomialAndDerivative( + ComplexNumber z, + IReadOnlyList coefficients, + int order, + out ComplexNumber derivative) + { + int index = order; + ComplexNumber value = coefficients[index]; + ComplexNumber derivativeValue = value; + bool first = true; + + while (index > 0) + { + if (first) + { + first = false; + } + else + { + double derivativeReal = derivativeValue.Real; + derivativeValue = new ComplexNumber( + value.Real + (z.Real * derivativeReal) - (z.Imaginary * derivativeValue.Imaginary), + value.Imaginary + (z.Real * derivativeValue.Imaginary) + (z.Imaginary * derivativeReal)); + } + + index--; + double valueReal = value.Real; + value = new ComplexNumber( + coefficients[index].Real + (z.Real * valueReal) - (z.Imaginary * value.Imaginary), + coefficients[index].Imaginary + (z.Real * value.Imaginary) + (z.Imaginary * valueReal)); + } + + double derivativeTail = derivativeValue.Real; + derivativeValue = new ComplexNumber( + value.Real + (z.Real * derivativeTail) - (z.Imaginary * derivativeValue.Imaginary), + value.Imaginary + (z.Real * derivativeValue.Imaginary) + (z.Imaginary * derivativeTail)); + + double valueTail = value.Real; + value = new ComplexNumber( + (z.Real * valueTail) - (z.Imaginary * value.Imaginary), + (z.Real * value.Imaginary) + (z.Imaginary * valueTail)); + + derivative = derivativeValue; + return value; + } + + private double ComputeConformalLatitude(double geodeticLatitude) + { + if (this.effectiveEs == 0d) + { + return geodeticLatitude; + } + + double eSinPhi = this.effectiveE * Math.Sin(geodeticLatitude); + return (2d * Math.Atan( + Math.Tan((HalfPi + geodeticLatitude) * 0.5d) * + Math.Pow((1d - eSinPhi) / (1d + eSinPhi), this.effectiveE * 0.5d))) + - HalfPi; + } + + private double ComputeGeodeticLatitude(double conformalLatitude) + { + if (this.effectiveEs == 0d) + { + return conformalLatitude; + } + + double phi = conformalLatitude; + for (int i = 0; i < MaximumNewtonIterations; i++) + { + double eSinPhi = this.effectiveE * Math.Sin(phi); + double deltaPhi = (2d * Math.Atan( + Math.Tan((HalfPi + conformalLatitude) * 0.5d) * + Math.Pow((1d + eSinPhi) / (1d - eSinPhi), this.effectiveE * 0.5d))) + - HalfPi + - phi; + + phi += deltaPhi; + if (Math.Abs(deltaPhi) <= NewtonTolerance) + { + return phi; + } + } + + return ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + /// + /// Represents an immutable complex number used by the PROJ complex polynomial functions. + /// + /// Real component. + /// Imaginary component. + protected readonly struct ComplexNumber(double real, double imaginary) + { + /// + /// Gets the real component. + /// + public double Real { get; } = real; + + /// + /// Gets the imaginary component. + /// + public double Imaginary { get; } = imaginary; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/MollweideProjection.cs b/src/ProjNet/CoordinateSystems/Projections/MollweideProjection.cs new file mode 100644 index 00000000..717aead4 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/MollweideProjection.cs @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Mollweide projection (moll). +/// +/// +/// The Mollweide projection is an equal-area pseudocylindrical projection with an elliptical +/// outline. The forward formula solves an auxiliary angle iteratively. The projection supports +/// an optional pole parameter (moll_p, in degrees; default 90°) for parameterised variants +/// such as Wagner IV and Wagner V. +/// The formulation was independently verified against the Wikipedia article +/// "Mollweide projection" and Eric W. Weisstein's MathWorld entry "Mollweide Projection". +/// The auxiliary-angle equation 2 * θ + sin(2 * θ) = cp * sin(φ) together +/// with the forward relations x = cx * λ * cos(θ) and +/// y = cy * sin(θ) match the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 31, pp. 249-252, +/// eqs. (31-1) through (31-10), for the Mollweide development. +/// +/// Wikipedia: Mollweide projection. +/// MathWorld: Mollweide Projection. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.2.2, pp. 71-74. +internal class MollweideProjection : MapProjection +{ + private const int Iterations = 30; + private const double DefaultP = 90d; + + private readonly double cx; + private readonly double cy; + private readonly double cp; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public MollweideProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public MollweideProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Mollweide"; + + double p = DegreesToRadians(this.Parameters.GetOptionalParameterValue("moll_p", DefaultP)); + double sp = Math.Sin(p); + double p2 = p + p; + double denominator = p2 + Math.Sin(p2); + if (Math.Abs(sp) <= Eps10 || Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double r = Math.Sqrt(TwoPi * sp / denominator); + this.cx = this.Parameters.GetOptionalParameterValue("moll_cx", 2d * r / PI); + this.cy = this.Parameters.GetOptionalParameterValue("moll_cy", r / sp); + this.cp = this.Parameters.GetOptionalParameterValue("moll_cp", denominator); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new MollweideProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double k = this.cp * Math.Sin(phi); + int i = Iterations; + for (; i > 0; i--) + { + double v = (phi + Math.Sin(phi) - k) / (1d + Math.Cos(phi)); + phi -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + if (i == 0) + { + phi = phi < 0d ? -HalfPi : HalfPi; + } + else + { + phi *= 0.5d; + } + + lon = this.SphericalRadius * this.cx * lambda * Math.Cos(phi); + lat = this.SphericalRadius * this.cy * Math.Sin(phi); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = Asinz(yy / this.cy); + double cosPhi = Math.Cos(phi); + if (Math.Abs(cosPhi) <= Eps10) + { + x = this.centralMeridian; + y = phi >= 0d ? HalfPi : -HalfPi; + return; + } + + double lambda = xx / (this.cx * cosPhi); + if (Math.Abs(lambda) < PI) + { + phi += phi; + phi = Asinz((phi + Math.Sin(phi)) / this.cp); + } + else + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Murdoch1Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Murdoch1Projection.cs new file mode 100644 index 00000000..cc6f9ded --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Murdoch1Projection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Murdoch I projection (murd1). +/// +/// +/// Murdoch I is a simple spherical conic specialization of +/// . Its numerical behavior follows the shared +/// simple-conic equations with the Murdoch I parameter construction. +/// +internal sealed class Murdoch1Projection : SimpleConicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Murdoch1Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Murdoch1Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, SimpleConicType.Murdoch1, "Murdoch_I") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Murdoch1Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Murdoch2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Murdoch2Projection.cs new file mode 100644 index 00000000..ad3dc954 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Murdoch2Projection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Murdoch II projection (murd2). +/// +/// +/// Murdoch II is a simple spherical conic specialization of +/// . It is the family member that switches the +/// shared radial distance from a linear expression to the tangent-based Murdoch II form. +/// +internal sealed class Murdoch2Projection : SimpleConicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Murdoch2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Murdoch2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, SimpleConicType.Murdoch2, "Murdoch_II") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Murdoch2Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Murdoch3Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Murdoch3Projection.cs new file mode 100644 index 00000000..921e78ce --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Murdoch3Projection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Murdoch III projection (murd3). +/// +/// +/// Murdoch III is a simple spherical conic specialization of +/// . Its numerical behavior follows the shared +/// simple-conic equations with the Murdoch III cone construction. +/// +internal sealed class Murdoch3Projection : SimpleConicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Murdoch3Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Murdoch3Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, SimpleConicType.Murdoch3, "Murdoch_III") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Murdoch3Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/NaturalEarth2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/NaturalEarth2Projection.cs new file mode 100644 index 00000000..64137a3f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/NaturalEarth2Projection.cs @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Natural Earth II projection (natearth2). +/// +/// +/// Natural Earth II is an updated pseudocylindrical projection with revised polynomial +/// scaling coefficients for a smoother visual appearance. The inverse is solved iteratively +/// via Newton–Raphson iteration. +/// The formulation was independently verified against Bojan Savric, Tom Patterson, +/// and Bernhard Jenny, "The Natural Earth II map projection", 2015. The revised +/// x/y scaling polynomials and the Newton iteration used to recover φ from the +/// northing polynomial match the implementation here. +/// +/// Natural Earth II projection paper. +internal sealed class NaturalEarth2Projection : MapProjection +{ + private const int Iterations = 12; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public NaturalEarth2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public NaturalEarth2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Natural_Earth_2"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new NaturalEarth2Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double phi2 = phi * phi; + double phi4 = phi2 * phi2; + _ = phi4 * phi2; + double phi8 = phi4 * phi4; + double phi10 = phi8 * phi2; + double phi12 = phi10 * phi2; + double phi14 = phi12 * phi2; + double phi16 = phi8 * phi8; + + double xScale = 0.84719 - (0.13063 * phi2) - (0.04515 * phi12) + (0.05494 * phi14) - (0.02326 * phi16) + (0.00331 * phi16 * phi2); + double yScale = 1.01183 - (0.02625 * phi8) + (0.01926 * phi10) - (0.00396 * phi12); + + lon = this.SphericalRadius * lambda * xScale; + lat = this.SphericalRadius * phi * yScale; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double yy = y * this.InverseSphericalRadius; + double phi = yy; + + for (int i = 0; i < Iterations; i++) + { + double phi2 = phi * phi; + double phi4 = phi2 * phi2; + _ = phi4 * phi2; + double phi8 = phi4 * phi4; + double phi10 = phi8 * phi2; + double phi12 = phi10 * phi2; + + double fy = (phi * (1.01183 - (0.02625 * phi8) + (0.01926 * phi10) - (0.00396 * phi12))) - yy; + double fpy = 1.01183 - (9d * 0.02625 * phi8) + (11d * 0.01926 * phi10) - (13d * 0.00396 * phi12); + + double delta = fy / fpy; + phi -= delta; + if (Math.Abs(delta) < ProjectionConstants.Tolerance1E12) + { + break; + } + } + + double phi2Final = phi * phi; + double phi4Final = phi2Final * phi2Final; + double phi8Final = phi4Final * phi4Final; + double phi12Final = phi8Final * phi4Final; + double phi14Final = phi12Final * phi2Final; + double phi16Final = phi8Final * phi8Final; + double phi18Final = phi16Final * phi2Final; + + double xScaleFinal = 0.84719 - (0.13063 * phi2Final) - (0.04515 * phi12Final) + (0.05494 * phi14Final) - (0.02326 * phi16Final) + (0.00331 * phi18Final); + + x = Adjust_lon(this.centralMeridian + ((x * this.InverseSphericalRadius) / xScaleFinal)); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/NaturalEarthProjection.cs b/src/ProjNet/CoordinateSystems/Projections/NaturalEarthProjection.cs new file mode 100644 index 00000000..ffcb8eef --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/NaturalEarthProjection.cs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Natural Earth projection (natearth). +/// +/// +/// The Natural Earth projection is a pseudocylindrical projection with polynomial scaling +/// functions for x and y. The inverse is solved iteratively via Newton–Raphson iteration. +/// The formulation was independently verified against Bojan Savric, Bernhard Jenny, +/// and Tom Patterson, "A Polynomial Equation for the Natural Earth Projection", +/// Cartography and Geographic Information Science, vol. 38, no. 4, pp. 363-372, 2011. +/// The published x/y scaling polynomials and the Newton iteration used to recover +/// φ from the northing polynomial match the implementation here. +/// +/// Natural Earth projection paper. +/// Wikipedia: Natural Earth projection. +internal sealed class NaturalEarthProjection : MapProjection +{ + private const int Iterations = 12; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public NaturalEarthProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public NaturalEarthProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Natural_Earth"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new NaturalEarthProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double phi2 = phi * phi; + double phi4 = phi2 * phi2; + double phi6 = phi4 * phi2; + double phi8 = phi4 * phi4; + double phi10 = phi8 * phi2; + double phi12 = phi10 * phi2; + + double xScale = 0.8707 - (0.131979 * phi2) - (0.013791 * phi4) + (0.003971 * phi10) - (0.001529 * phi12); + double yScale = 1.007226 + (0.015085 * phi2) - (0.044475 * phi6) + (0.028874 * phi8) - (0.005916 * phi10); + + lon = this.SphericalRadius * lambda * xScale; + lat = this.SphericalRadius * phi * yScale; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double yy = y * this.InverseSphericalRadius; + double phi = yy; + + for (int i = 0; i < Iterations; i++) + { + double phi2 = phi * phi; + double phi4 = phi2 * phi2; + double phi6 = phi4 * phi2; + double phi8 = phi4 * phi4; + double phi10 = phi8 * phi2; + + double fy = (phi * (1.007226 + (0.015085 * phi2) - (0.044475 * phi6) + (0.028874 * phi8) - (0.005916 * phi10))) - yy; + double fpy = 1.007226 + (3d * 0.015085 * phi2) - (7d * 0.044475 * phi6) + (9d * 0.028874 * phi8) - (11d * 0.005916 * phi10); + + double delta = fy / fpy; + phi -= delta; + if (Math.Abs(delta) < ProjectionConstants.Tolerance1E12) + { + break; + } + } + + double phi2Final = phi * phi; + double phi4Final = phi2Final * phi2Final; + double phi10Final = phi4Final * phi4Final * phi2Final; + double phi12Final = phi10Final * phi2Final; + double xScaleFinal = 0.8707 - (0.131979 * phi2Final) - (0.013791 * phi4Final) + (0.003971 * phi10Final) - (0.001529 * phi12Final); + + x = Adjust_lon(this.centralMeridian + ((x * this.InverseSphericalRadius) / xScaleFinal)); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/NearSidedPerspectiveProjection.cs b/src/ProjNet/CoordinateSystems/Projections/NearSidedPerspectiveProjection.cs new file mode 100644 index 00000000..200fd46c --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/NearSidedPerspectiveProjection.cs @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the near-sided and tilted perspective projections (nsper, tpers). +/// +/// +/// NearSidedPerspectiveProjection implements Snyder's near-sided perspective family. The +/// shared code supports polar, equatorial, and oblique viewpoints and optionally applies +/// the additional tilt rotation used by the tilted-perspective variant. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.3.2, pp. 116-121. +internal sealed class NearSidedPerspectiveProjection : MapProjection +{ + private readonly double sinph0; + private readonly double cosph0; + private readonly double p; + private readonly double rp; + private readonly double pn1; + private readonly double pfact; + private readonly double h; + private readonly double cg; + private readonly double sg; + private readonly double sw; + private readonly double cw; + private readonly Mode mode; + private readonly bool tilt; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public NearSidedPerspectiveProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public NearSidedPerspectiveProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + double height = this.Parameters.GetParameterValue("h", "satellite_height"); + this.pn1 = height / this.semiMajor; + if (this.pn1 <= 0d || this.pn1 > 1e10d) + { + ArgumentGuard.ThrowArgument("Invalid value for h.", nameof(parameters)); + } + + this.p = 1d + this.pn1; + this.rp = 1d / this.p; + this.h = 1d / this.pn1; + this.pfact = (this.p + 1d) * this.h; + + if (Math.Abs(Math.Abs(this.latOrigin) - HalfPi) < Eps10) + { + this.mode = this.latOrigin < 0d ? Mode.SPole : Mode.NPole; + } + else if (Math.Abs(this.latOrigin) < Eps10) + { + this.mode = Mode.Equit; + } + else + { + this.mode = Mode.Obliq; + Sincos(this.latOrigin, out this.sinph0, out this.cosph0); + } + + bool hasTilt = this.Parameters.ContainsKey("tilt"); + bool hasAzi = this.Parameters.ContainsKey("azi") || this.Parameters.ContainsKey("azimuth"); + this.tilt = hasTilt || hasAzi; + if (this.tilt) + { + double omega = DegreesToRadians(this.Parameters.GetOptionalParameterValue("tilt", 0d)); + double gamma = DegreesToRadians(this.Parameters.GetOptionalParameterValue("azi", this.Parameters.GetOptionalParameterValue("azimuth", 0d))); + this.cg = Math.Cos(gamma); + this.sg = Math.Sin(gamma); + this.cw = Math.Cos(omega); + this.sw = Math.Sin(omega); + this.Name = "Tilted_Perspective"; + } + else + { + this.Name = "Near_Sided_Perspective"; + } + } + + private enum Mode + { + NPole = 0, + SPole = 1, + Equit = 2, + Obliq = 3, + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new NearSidedPerspectiveProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double sinPhi = Math.Sin(lat); + double cosPhi = Math.Cos(lat); + double cosLam = Math.Cos(lambda); + + double yValue = this.mode switch + { + Mode.Obliq => (this.sinph0 * sinPhi) + (this.cosph0 * cosPhi * cosLam), + Mode.Equit => cosPhi * cosLam, + Mode.SPole => -sinPhi, + _ => sinPhi, + }; + + if (yValue < this.rp) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + yValue = this.pn1 / (this.p - yValue); + double xValue = yValue * cosPhi * Math.Sin(lambda); + + yValue *= this.mode switch + { + Mode.Obliq => (this.cosph0 * sinPhi) - (this.sinph0 * cosPhi * cosLam), + Mode.Equit => sinPhi, + Mode.NPole => -cosPhi * cosLam, + _ => cosPhi * cosLam, + }; + + if (this.tilt) + { + double yt = (yValue * this.cg) + (xValue * this.sg); + double ba = 1d / ((yt * this.sw * this.h) + this.cw); + xValue = ((xValue * this.cg) - (yValue * this.sg)) * this.cw * ba; + yValue = yt * ba; + } + + lon = this.SphericalRadius * xValue; + lat = this.SphericalRadius * yValue; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xValue = x * this.InverseSphericalRadius; + double yValue = y * this.InverseSphericalRadius; + + if (this.tilt) + { + double yt = 1d / (this.pn1 - (yValue * this.sw)); + double bm = this.pn1 * xValue * yt; + double bq = this.pn1 * yValue * this.cw * yt; + xValue = (bm * this.cg) + (bq * this.sg); + yValue = (bq * this.cg) - (bm * this.sg); + } + + double rh = Hypot(xValue, yValue); + double lambda = 0d; + double phi = this.latOrigin; + if (Math.Abs(rh) > Eps10) + { + double sinz = 1d - ((rh * rh) * this.pfact); + if (sinz < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + sinz = (this.p - Math.Sqrt(sinz)) / ((this.pn1 / rh) + (rh / this.pn1)); + if (Math.Abs(sinz) > 1d + Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + sinz = Math.Max(-1d, Math.Min(1d, sinz)); + double cosz = Math.Sqrt(Math.Max(0d, 1d - (sinz * sinz))); + + switch (this.mode) + { + case Mode.Obliq: + phi = Asinz((cosz * this.sinph0) + ((yValue * sinz * this.cosph0) / rh)); + yValue = (cosz - (this.sinph0 * Math.Sin(phi))) * rh; + xValue *= sinz * this.cosph0; + break; + case Mode.Equit: + phi = Asinz((yValue * sinz) / rh); + yValue = cosz * rh; + xValue *= sinz; + break; + case Mode.NPole: + phi = Asinz(cosz); + yValue = -yValue; + break; + default: + phi = -Asinz(cosz); + break; + } + + lambda = Math.Atan2(xValue, yValue); + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/NellHammerProjection.cs b/src/ProjNet/CoordinateSystems/Projections/NellHammerProjection.cs new file mode 100644 index 00000000..5ef93286 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/NellHammerProjection.cs @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Nell-Hammer projection (nell_h). +/// +/// +/// Nell-Hammer is a spherical pseudocylindrical compromise projection that combines the +/// Nell longitude scale with a Hammer-style latitude spacing. Its forward form is +/// x = 0.5 * λ * (1 + cos(φ)), +/// y = 2 * (φ - tan(φ / 2)), and the inverse recovers φ by Newton +/// iteration. +/// +internal sealed class NellHammerProjection : MapProjection +{ + private const int Iterations = 9; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public NellHammerProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public NellHammerProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Nell_Hammer"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new NellHammerProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = 0.5d * lambda * (1d + Math.Cos(lat)); + double y = 2d * (lat - Math.Tan(0.5d * lat)); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double p = 0.5d * yy; + double phi = 0d; + int iteration = Iterations; + for (; iteration > 0; iteration--) + { + double c = Math.Cos(0.5d * phi); + double denominator = 1d - (0.5d / (c * c)); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double v = (phi - Math.Tan(phi / 2d) - p) / denominator; + phi -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + double lambda = (2d * xx) / (1d + Math.Cos(phi)); + if (iteration == 0) + { + phi = p < 0d ? -HalfPi : HalfPi; + lambda = 2d * xx; + } + else + { + if (Math.Abs(1d + Math.Cos(phi)) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/NellProjection.cs b/src/ProjNet/CoordinateSystems/Projections/NellProjection.cs new file mode 100644 index 00000000..7832df96 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/NellProjection.cs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Nell projection (nell). +/// +/// +/// Nell is a spherical pseudocylindrical projection with straight parallels and curved +/// meridians. The implementation solves the auxiliary relation +/// φ' + sin(φ') = 2 * sin(φ) and then evaluates +/// x = 0.5 * λ * (1 + cos(φ')), y = φ'. +/// +internal sealed class NellProjection : MapProjection +{ + private const int Iterations = 10; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public NellProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public NellProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Nell"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new NellProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double k = 2d * Math.Sin(lat); + double phiSquared = lat * lat; + double phi = lat * (1.00371d + (phiSquared * (-0.0935382d + (phiSquared * -0.011412d)))); + for (int i = Iterations; i > 0; i--) + { + double denominator = 1d + Math.Cos(phi); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double v = (phi + Math.Sin(phi) - k) / denominator; + phi -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + double x = 0.5d * lambda * (1d + Math.Cos(phi)); + double y = phi; + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double denominator = 1d + Math.Cos(yy); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = (2d * xx) / denominator; + double phi = Asinz(0.5d * (yy + Math.Sin(yy))); + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/NewZealandMapGridProjection.cs b/src/ProjNet/CoordinateSystems/Projections/NewZealandMapGridProjection.cs new file mode 100644 index 00000000..bd1e9f7e --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/NewZealandMapGridProjection.cs @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the New Zealand Map Grid projection (nzmg). +/// +/// +/// The projection was independently verified against IOGP, "Geomatics Guidance Note 7, +/// part 2: Coordinate Conversions and Transformations including Formulas" (publication +/// 373-7-2, 2019), EPSG method 9811, and LINZ's New Zealand Map Grid specification, +/// including Technical Report TR04 on conversion between latitude/longitude and NZMG. +/// NZMG is implemented as a sixth-order complex polynomial with the standard +/// lat0, lon0, false easting, and false northing parameters, +/// and the coefficient sets used here match the published formulation. +/// +/// LINZ: New Zealand Map Grid specification. +/// LINZ TR04: Conversion between latitude/longitude and NZMG. +internal sealed class NewZealandMapGridProjection : MapProjection +{ + private const int Nbf = 5; + private const int Ntpsi = 9; + private const int Ntphi = 8; + private const int NewtonIterations = 20; + private const double Sec5ToRad = 0.4848136811095359935899141023d; + private const double RadToSec5 = 2.062648062470963551564733573d; + + private static readonly double ProjectionSemiMajor = Ellipsoid.International1924.SemiMajorAxis; + private static readonly ComplexNumber[] Bf = + [ + new ComplexNumber(0.7557853228d, 0d), + new ComplexNumber(0.249204646d, 0.003371507d), + new ComplexNumber(-0.001541739d, 0.041058560d), + new ComplexNumber(-0.10162907d, 0.01727609d), + new ComplexNumber(-0.26623489d, -0.36249218d), + new ComplexNumber(-0.6870983d, -1.1651967d), + ]; + + private static readonly double[] Tpsi = + [ + 0.6399175073d, + -0.1358797613d, + 0.063294409d, + -0.02526853d, + 0.0117879d, + -0.0055161d, + 0.0026906d, + -0.001333d, + 0.00067d, + -0.00034d, + ]; + + private static readonly double[] Tphi = + [ + 1.5627014243d, + 0.5185406398d, + -0.03333098d, + -0.1052906d, + -0.0368594d, + 0.007317d, + 0.01220d, + 0.00394d, + -0.0013d, + ]; + + private readonly double latitudeOfOrigin; + private readonly double centralMeridianNz; + private readonly double projectionRadius; + private readonly double inverseProjectionRadius; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public NewZealandMapGridProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public NewZealandMapGridProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "New_Zealand_Map_Grid"; + this.centralMeridianNz = DegreesToRadians(this.Parameters.GetOptionalParameterValue("central_meridian", 173d, "longitude_of_center")); + this.latitudeOfOrigin = DegreesToRadians(this.Parameters.GetOptionalParameterValue("latitude_of_origin", -41d, "latitude_of_center")); + this.projectionRadius = ProjectionSemiMajor * this.scaleFactor; + this.inverseProjectionRadius = 1d / this.projectionRadius; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new NewZealandMapGridProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double phi = (lat - this.latitudeOfOrigin) * RadToSec5; + double pReal = Tpsi[Ntpsi]; + for (int i = Ntpsi; i > 0; i--) + { + pReal = Tpsi[i - 1] + (phi * pReal); + } + + pReal *= phi; + var p = new ComplexNumber(pReal, Adjust_lon(lon - this.centralMeridianNz)); + p = EvaluateComplexPolynomial(p, Bf, Nbf); + + lon = this.projectionRadius * p.Imaginary; + lat = this.projectionRadius * p.Real; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double yNormalized = y * this.inverseProjectionRadius; + double xNormalized = x * this.inverseProjectionRadius; + var p = new ComplexNumber(yNormalized, xNormalized); + bool converged = false; + + for (int i = 0; i < NewtonIterations; i++) + { + ComplexNumber f = EvaluateComplexPolynomialAndDerivative(p, Bf, Nbf, out ComplexNumber derivative); + f.Real -= yNormalized; + f.Imaginary -= xNormalized; + + double denominator = (derivative.Real * derivative.Real) + (derivative.Imaginary * derivative.Imaginary); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + var delta = new ComplexNumber( + -((f.Real * derivative.Real) + (f.Imaginary * derivative.Imaginary)) / denominator, + -((f.Imaginary * derivative.Real) - (f.Real * derivative.Imaginary)) / denominator); + + p.Real += delta.Real; + p.Imaginary += delta.Imaginary; + + if (Math.Abs(delta.Real) + Math.Abs(delta.Imaginary) <= Eps10) + { + converged = true; + break; + } + } + + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double phi = Tphi[Ntphi]; + for (int i = Ntphi; i > 0; i--) + { + phi = Tphi[i - 1] + (p.Real * phi); + } + + x = Adjust_lon(this.centralMeridianNz + p.Imaginary); + y = this.latitudeOfOrigin + (p.Real * phi * Sec5ToRad); + } + + private static ComplexNumber EvaluateComplexPolynomial(ComplexNumber z, IReadOnlyList coefficients, int order) + { + int index = order; + ComplexNumber value = coefficients[index]; + for (int i = order; i > 0; i--) + { + index--; + double previousReal = value.Real; + value.Real = coefficients[index].Real + (z.Real * previousReal) - (z.Imaginary * value.Imaginary); + value.Imaginary = coefficients[index].Imaginary + (z.Real * value.Imaginary) + (z.Imaginary * previousReal); + } + + double finalReal = value.Real; + value.Real = (z.Real * finalReal) - (z.Imaginary * value.Imaginary); + value.Imaginary = (z.Real * value.Imaginary) + (z.Imaginary * finalReal); + return value; + } + + private static ComplexNumber EvaluateComplexPolynomialAndDerivative( + ComplexNumber z, + IReadOnlyList coefficients, + int order, + out ComplexNumber derivative) + { + int index = order; + ComplexNumber value = coefficients[index]; + ComplexNumber derivativeValue = value; + bool first = true; + + for (int i = order; i > 0; i--) + { + if (first) + { + first = false; + } + else + { + double derivativeReal = derivativeValue.Real; + derivativeValue.Real = value.Real + (z.Real * derivativeReal) - (z.Imaginary * derivativeValue.Imaginary); + derivativeValue.Imaginary = value.Imaginary + (z.Real * derivativeValue.Imaginary) + (z.Imaginary * derivativeReal); + } + + index--; + double valueReal = value.Real; + value.Real = coefficients[index].Real + (z.Real * valueReal) - (z.Imaginary * value.Imaginary); + value.Imaginary = coefficients[index].Imaginary + (z.Real * value.Imaginary) + (z.Imaginary * valueReal); + } + + double derivativeTailReal = derivativeValue.Real; + derivativeValue.Real = value.Real + (z.Real * derivativeTailReal) - (z.Imaginary * derivativeValue.Imaginary); + derivativeValue.Imaginary = value.Imaginary + (z.Real * derivativeValue.Imaginary) + (z.Imaginary * derivativeTailReal); + + double valueTailReal = value.Real; + value.Real = (z.Real * valueTailReal) - (z.Imaginary * value.Imaginary); + value.Imaginary = (z.Real * value.Imaginary) + (z.Imaginary * valueTailReal); + + derivative = derivativeValue; + return value; + } + + private struct ComplexNumber(double real, double imaginary) + { + public double Real = real; + public double Imaginary = imaginary; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/NicolosiProjection.cs b/src/ProjNet/CoordinateSystems/Projections/NicolosiProjection.cs new file mode 100644 index 00000000..0a42cd4b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/NicolosiProjection.cs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Nicolosi Globular projection (nicol). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the classical Nicolosi +/// globular construction. The implementation matches the special-case branches for the +/// central meridian, equator, poles, and ±90° meridians before evaluating the general +/// square-root form for interior points. +/// +internal sealed class NicolosiProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public NicolosiProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public NicolosiProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Nicolosi"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new NicolosiProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = 0d; + double y = lat; + + if (Math.Abs(lambda) < Eps10) + { + } + else if (Math.Abs(lat) < Eps10) + { + x = lambda; + y = 0d; + } + else if (Math.Abs(Math.Abs(lambda) - HalfPi) < Eps10) + { + x = lambda * Math.Cos(lat); + y = HalfPi * Math.Sin(lat); + } + else if (Math.Abs(Math.Abs(lat) - HalfPi) < Eps10) + { + x = 0d; + y = lat; + } + else + { + double tb = (HalfPi / lambda) - (lambda / HalfPi); + double c = lat / HalfPi; + double sp = Math.Sin(lat); + double d = (1d - (c * c)) / (sp - c); + double r2 = tb / d; + r2 *= r2; + double m = ((tb * sp / d) - (0.5d * tb)) / (1d + r2); + double n = ((sp / r2) + (0.5d * d)) / (1d + (1d / r2)); + double cosLat = Math.Cos(lat); + double xTerm = Math.Sqrt((m * m) + ((cosLat * cosLat) / (1d + r2))); + x = HalfPi * (m + (lambda < 0d ? -xTerm : xTerm)); + double yTerm = Math.Sqrt((n * n) - (((sp * sp / r2) + (d * sp) - 1d) / (1d + (1d / r2)))); + y = HalfPi * (n + (lat < 0d ? yTerm : -yTerm)); + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Nicolosi does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/OblatedEqualAreaProjection.cs b/src/ProjNet/CoordinateSystems/Projections/OblatedEqualAreaProjection.cs new file mode 100644 index 00000000..b0f1ed84 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/OblatedEqualAreaProjection.cs @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Oblated Equal Area projection (oea). +/// +/// +/// Oblated Equal Area is Snyder's parameterized equal-area azimuthal transformation. The +/// implementation rotates the central azimuth by θ and then applies the paired +/// m/n angular distortions that create the oblated equal-area layout. +/// +internal sealed class OblatedEqualAreaProjection : MapProjection +{ + private readonly double theta; + private readonly double m; + private readonly double n; + private readonly double twoRM; + private readonly double twoRN; + private readonly double rm; + private readonly double rn; + private readonly double hm; + private readonly double hn; + private readonly double cp0; + private readonly double sp0; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public OblatedEqualAreaProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public OblatedEqualAreaProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Oblated_Equal_Area"; + + this.n = this.Parameters.GetParameterValue("n"); + if (this.n <= 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for n: it should be > 0.", nameof(parameters)); + } + + this.m = this.Parameters.GetParameterValue("m"); + if (this.m <= 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for m: it should be > 0.", nameof(parameters)); + } + + this.theta = DegreesToRadians(this.Parameters.GetOptionalParameterValue("theta", 0d)); + Sincos(this.latOrigin, out this.sp0, out this.cp0); + this.rn = 1d / this.n; + this.rm = 1d / this.m; + this.twoRN = 2d * this.rn; + this.twoRM = 2d * this.rm; + this.hm = 0.5d * this.m; + this.hn = 0.5d * this.n; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new OblatedEqualAreaProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double cp = Math.Cos(lat); + double sp = Math.Sin(lat); + double cl = Math.Cos(lambda); + double az = Math.Atan2(cp * Math.Sin(lambda), (this.cp0 * sp) - (this.sp0 * cp * cl)) + this.theta; + double cosCentral = ProjectionConstants.Clamp((this.sp0 * sp) + (this.cp0 * cp * cl), -1d, 1d); + double shz = Math.Sin(0.5d * Math.Acos(cosCentral)); + double mAngle = Asinz(shz * Math.Sin(az)); + + double denominator = Math.Cos(mAngle * this.twoRM); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double nAngle = Asinz((shz * Math.Cos(az) * Math.Cos(mAngle)) / denominator); + double cosNScaled = Math.Cos(nAngle * this.twoRN); + if (Math.Abs(cosNScaled) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double yUnit = this.n * Math.Sin(nAngle * this.twoRN); + double xUnit = this.m * Math.Sin(mAngle * this.twoRM) * Math.Cos(nAngle) / cosNScaled; + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + double nArg = ProjectionConstants.Clamp(yUnit * this.rn, -1d, 1d); + double nAngle = this.hn * Math.Asin(nArg); + double cosN = Math.Cos(nAngle); + if (Math.Abs(cosN) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double mArg = ProjectionConstants.Clamp(xUnit * this.rm * Math.Cos(nAngle * this.twoRN) / cosN, -1d, 1d); + double mAngle = this.hm * Math.Asin(mArg); + + double xp = 2d * Math.Sin(mAngle); + double cosM = Math.Cos(mAngle); + if (Math.Abs(cosM) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double yp = 2d * Math.Sin(nAngle) * Math.Cos(mAngle * this.twoRM) / cosM; + double az = Math.Atan2(xp, yp) - this.theta; + double caz = Math.Cos(az); + double z = 2d * Math.Asin(ProjectionConstants.Clamp(0.5d * Hypot(xp, yp), -1d, 1d)); + double sz = Math.Sin(z); + double cz = Math.Cos(z); + + double phi = Asinz((this.sp0 * cz) + (this.cp0 * sz * caz)); + double lambda = Math.Atan2(sz * Math.Sin(az), (this.cp0 * cz) - (this.sp0 * sz * caz)); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ObliqueCylindricalEqualAreaProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ObliqueCylindricalEqualAreaProjection.cs new file mode 100644 index 00000000..13a0bfb1 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ObliqueCylindricalEqualAreaProjection.cs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Oblique Cylindrical Equal Area projection (ocea). +/// +/// +/// The oblique pole can be defined either by an azimuth angle (α or azimuth +/// together with lonc) or by two geographic points via lat_1, lon_1, +/// lat_2, and lon_2. +/// The oblique equal-area construction was independently verified against Snyder's +/// oblique cylindrical equal-area formulation. The implementation matches both parameter +/// initialization paths for the oblique pole and the final equal-area forward/inverse +/// relations in the rotated coordinate system. +/// +internal sealed class ObliqueCylindricalEqualAreaProjection : MapProjection +{ + private readonly double rok; + private readonly double rtk; + private readonly double sinPhiP; + private readonly double cosPhiP; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ObliqueCylindricalEqualAreaProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ObliqueCylindricalEqualAreaProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Oblique_Cylindrical_Equal_Area"; + this.rtk = this.scaleFactor; + this.rok = 1d / this.scaleFactor; + + double lamP = 0d; + double phiP = 0d; + if (this.Parameters.ContainsKey("alpha") || this.Parameters.ContainsKey("azimuth")) + { + double alpha = PI + DegreesToRadians(this.Parameters.GetOptionalParameterValue("alpha", this.Parameters.GetOptionalParameterValue("azimuth", 0d))); + double lonc = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lonc", this.Parameters.GetOptionalParameterValue("longitude_of_center", 0d))); + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + lamP = Math.Atan2(-Math.Cos(alpha), -sinLatitudeOrigin * Math.Sin(alpha)) + lonc; + phiP = Asinz(cosLatitudeOrigin * Math.Sin(alpha)); + } + else + { + double phi1 = DegreesToRadians(this.Parameters.GetParameterValue("lat_1", "standard_parallel_1")); + double phi2 = DegreesToRadians(this.Parameters.GetParameterValue("lat_2", "standard_parallel_2")); + double lam1 = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lon_1", 0d)); + double lam2 = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lon_2", 0d)); + + lamP = Math.Atan2( + (Math.Cos(phi1) * Math.Sin(phi2) * Math.Cos(lam1)) - (Math.Sin(phi1) * Math.Cos(phi2) * Math.Cos(lam2)), + (Math.Sin(phi1) * Math.Cos(phi2) * Math.Sin(lam2)) - (Math.Cos(phi1) * Math.Sin(phi2) * Math.Sin(lam1))); + if (Math.Abs(lam1 + MapProjection.HalfPi) <= Eps10) + { + lamP = -lamP; + } + + double cosLamDiff = Math.Cos(lamP - lam1); + double tanPhi1 = Math.Tan(phi1); + phiP = Math.Abs(tanPhi1) <= Eps10 + ? (cosLamDiff >= 0d ? -MapProjection.HalfPi : MapProjection.HalfPi) + : Math.Atan(-cosLamDiff / tanPhi1); + } + + this.centralMeridian = Adjust_lon(lamP + MapProjection.HalfPi); + this.sinPhiP = Math.Sin(phiP); + this.cosPhiP = Math.Cos(phiP); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ObliqueCylindricalEqualAreaProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double sinLam = Math.Sin(lambda); + double cosLam = Math.Cos(lambda); + double tanPhi = Math.Tan(lat); + + double xUnit = Math.Atan(((tanPhi * this.cosPhiP) + (this.sinPhiP * sinLam)) / cosLam); + if (cosLam < 0d) + { + xUnit += PI; + } + + xUnit *= this.rtk; + double yUnit = this.rok * ((this.sinPhiP * Math.Sin(lat)) - (this.cosPhiP * Math.Cos(lat) * sinLam)); + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = (x * this.InverseSphericalRadius) / this.rtk; + double yUnit = (y * this.InverseSphericalRadius) / this.rok; + double t = Math.Sqrt(Math.Max(0d, 1d - (yUnit * yUnit))); + double s = Math.Sin(xUnit); + double phi = Asinz((yUnit * this.sinPhiP) + (t * this.cosPhiP * s)); + double lambda = Math.Atan2((t * this.sinPhiP * s) - (yUnit * this.cosPhiP), t * Math.Cos(xUnit)); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ObliqueMercatorProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ObliqueMercatorProjection.cs index bf873aca..2d5069cc 100644 --- a/src/ProjNet/CoordinateSystems/Projections/ObliqueMercatorProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/ObliqueMercatorProjection.cs @@ -1,29 +1,56 @@ -using System; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + +using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Oblique Mercator map projection (EPSG method 9815). +/// +/// +/// The Oblique Mercator is a variant of the Hotine Oblique Mercator in which +/// false easting and northing are referenced to the natural origin of the projection +/// rather than to the centre of the initial line. This variant corresponds to EPSG +/// method 9815. +/// The formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG method 9815, Hotine Oblique Mercator (variant B). +/// The natural-origin coordinate convention and the omission of the u0 offset +/// used by variant A match the implementation here. +/// +/// EPSG method 9815: Hotine Oblique Mercator (variant B). +internal sealed class ObliqueMercatorProjection : HotineObliqueMercatorProjection { - [Serializable] - internal class ObliqueMercatorProjection : HotineObliqueMercatorProjection + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ObliqueMercatorProjection(IEnumerable parameters) + : this(parameters, null) { - public ObliqueMercatorProjection(IEnumerable parameters) - : this(parameters, null) - { - } + } - public ObliqueMercatorProjection(IEnumerable parameters, ObliqueMercatorProjection inverse) - : base(parameters, inverse) - { - AuthorityCode = 9815; - Name = "Oblique_Mercator"; - } + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ObliqueMercatorProjection(IEnumerable parameters, ObliqueMercatorProjection? inverse) + : base(parameters, inverse) + { + this.AuthorityCode = 9815; + this.Name = "Oblique_Mercator"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ObliqueMercatorProjection(this.Parameters.ToProjectionParameter(), this); - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new ObliqueMercatorProjection(_Parameters.ToProjectionParameter(), this); - return _inverse; - } + return this.inverse; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/ObliqueStereographicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ObliqueStereographicProjection.cs index 72db8d90..bf5ac906 100644 --- a/src/ProjNet/CoordinateSystems/Projections/ObliqueStereographicProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/ObliqueStereographicProjection.cs @@ -1,214 +1,187 @@ -// Copyright 2015 -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Oblique Stereographic Projection. +/// +/// +/// The formulation was independently verified against IOGP, "Geomatics Guidance Note 7, +/// part 2: Coordinate Conversions and Transformations including Formulas" (publication +/// 373-7-2, 2019), EPSG method 9809, and Apache SIS projection notes. The +/// implementation follows the documented Gauss-conformal plus stereographic double +/// projection, including recovery of conformal latitude and conformal sphere radius. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 21, for the +/// stereographic derivation that underlies the oblique case. +/// +/// EPSG method 9809: Oblique Stereographic. +/// Apache SIS: Oblique stereographic projection notes. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.2.2, pp. 102-104. +internal sealed class ObliqueStereographicProjection : MapProjection { + private const double IterationTolerance = 1E-14d; + private const int MaximumIterations = 15; + private const double Epsilon = 1E-6d; + private const double SouthPoleInitializationTolerance = 1E-10d; + + private readonly double globalScale; + private readonly double reciprocGlobalScale; + private readonly double c; + private readonly double k; + private readonly double ratexp; + private readonly double phic0; + private readonly double cosc0; + private readonly double sinc0; + private readonly double r2; + /// - /// Implements the Oblique Stereographic Projection. + /// Initializes a new instance of the class. /// - [Serializable] - internal class ObliqueStereographicProjection : MapProjection + /// List of parameters to initialize the projection. + /// + /// The parameters this projection expects are listed below. + /// + /// ItemsDescriptions + /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). + /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). + /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. + /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). + /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. + /// + /// + public ObliqueStereographicProjection(IEnumerable parameters) + : this(parameters, null) { - private readonly double _globalScale; - private readonly double _reciprocGlobalScale; - - private static double ITERATION_TOLERANCE = 1E-14; - private static int MAXIMUM_ITERATIONS = 15; - private static double EPSILON = 1E-6; - private double C, K, ratexp; - private double phic0, cosc0, sinc0, R2; - - - /// - /// Initializes the ObliqueStereographicProjection object with the specified parameters. - /// - /// List of parameters to initialize the projection. - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. - /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). - /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. - /// - /// - public ObliqueStereographicProjection(IEnumerable parameters) - : this(parameters, null) - { - } + } + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + public ObliqueStereographicProjection(IEnumerable parameters, ObliqueStereographicProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Oblique_Stereographic"; + + this.globalScale = this.scaleFactor * this.semiMajor; + this.reciprocGlobalScale = 1 / this.globalScale; + + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + double cosLatitudeOriginSquared = cosLatitudeOrigin * cosLatitudeOrigin; + this.r2 = 2.0 * Math.Sqrt(1 - this.es) / (1 - (this.es * sinLatitudeOrigin * sinLatitudeOrigin)); + this.c = Math.Sqrt(1.0 + (this.es * cosLatitudeOriginSquared * cosLatitudeOriginSquared / (1.0 - this.es))); + this.phic0 = Math.Asin(sinLatitudeOrigin / this.c); + this.sinc0 = Math.Sin(this.phic0); + this.cosc0 = Math.Cos(this.phic0); + this.ratexp = 0.5 * this.c * this.e; + double sratValue = this.Srat(this.e * sinLatitudeOrigin, this.ratexp); + double originAngle = (0.5 * this.latOrigin) + FortPi; + this.k = originAngle < SouthPoleInitializationTolerance + ? 1.0 / sratValue + : Math.Tan((0.5 * this.phic0) + FortPi) / (Math.Pow(Math.Tan(originAngle), this.c) * sratValue); + } - /// - /// Initializes the ObliqueStereographicProjection object with the specified parameters. - /// - /// List of parameters to initialize the projection. - /// Inverse projection - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. - /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). - /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. - /// - /// - public ObliqueStereographicProjection(IEnumerable parameters, ObliqueStereographicProjection inverse) - : base(parameters, inverse) + /// + /// Converts coordinates in projected meters to radians. + /// + /// The x-ordinate in projected meters. + /// The y-ordinate in projected meters. + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocGlobalScale; + y *= this.reciprocGlobalScale; + + double rho = Math.Sqrt((x * x) + (y * y)); + if (Math.Abs(rho) < Epsilon) { - Name = "Oblique_Stereographic"; - - _globalScale = scale_factor * _semiMajor; - _reciprocGlobalScale = 1 / _globalScale; - - double sphi = Math.Sin(lat_origin); - double cphi = Math.Cos(lat_origin); - cphi *= cphi; - R2 = 2.0 * Math.Sqrt(1 - _es) / (1 - _es * sphi * sphi); - C = Math.Sqrt(1.0 + _es * cphi * cphi / (1.0 - _es)); - phic0 = Math.Asin(sphi / C); - sinc0 = Math.Sin(phic0); - cosc0 = Math.Cos(phic0); - ratexp = 0.5 * C * _e; - K = Math.Tan(0.5 * phic0 + Math.PI / 4) / (Math.Pow(Math.Tan(0.5 * lat_origin + Math.PI / 4), C) * srat(_e * sphi, ratexp)); + x = 0.0; + y = this.phic0; } - - /// - /// Converts coordinates in projected meters to radians. - /// - /// - /// - protected override void MetersToRadians(ref double x, ref double y) + else { - x *= _reciprocGlobalScale; - y *= _reciprocGlobalScale; - - double rho = Math.Sqrt((x * x) + (y * y)); - if (Math.Abs(rho) < EPSILON) + double centralAngle = 2.0 * Math.Atan2(rho, this.r2); + double sinCentralAngle = Math.Sin(centralAngle); + double cosCentralAngle = Math.Cos(centralAngle); + double denominator = (rho * this.cosc0 * cosCentralAngle) - (y * this.sinc0 * sinCentralAngle); + x = Math.Atan2(x * sinCentralAngle, denominator); + y = (cosCentralAngle * this.sinc0) + (y * sinCentralAngle * this.cosc0 / rho); + + if (Math.Abs(y) >= 1.0) { - x = 0.0; - y = phic0; + y = (y < 0.0) ? -Math.PI / 2.0 : Math.PI / 2.0; } else { - double ce = 2.0 * Math.Atan2(rho, R2); - double sinc = Math.Sin(ce); - double cosc = Math.Cos(ce); - x = Math.Atan2(x * sinc, rho * cosc0 * cosc - y * sinc0 - * sinc); - y = (cosc * sinc0) + (y * sinc * cosc0 / rho); - - if (Math.Abs(y) >= 1.0) - { - y = (y < 0.0) ? -Math.PI / 2.0 : Math.PI / 2.0; - } - else - { - y = Math.Asin(y); - } + y = Math.Asin(y); } + } - x /= C; - double num = Math.Pow(Math.Tan(0.5 * y + Math.PI / 4.0) / K, 1.0 / C); - for (int iter = MAXIMUM_ITERATIONS; ;) + x /= this.c; + double num = Math.Pow(Math.Tan((0.5 * y) + (Math.PI / 4.0)) / this.k, 1.0 / this.c); + for (int iter = MaximumIterations; ;) + { + double phi = (2.0 * Math.Atan(num * this.Srat(this.e * Math.Sin(y), -0.5 * this.e))) - (Math.PI / 2.0); + if (Math.Abs(phi - y) < IterationTolerance) { - double phi = 2.0 * Math.Atan(num * srat(_e * Math.Sin(y), -0.5 * _e)) - Math.PI / 2.0; - if (Math.Abs(phi - y) < ITERATION_TOLERANCE) - { - break; - } - - y = phi; - if (--iter < 0) - { - throw new Exception("Oblique Stereographics doesn't converge"); - } + break; } - x += central_meridian; - } - - /// - /// Method to convert a point (lon, lat) in radians to (x, y) in meters - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - protected override void RadiansToMeters(ref double lon, ref double lat) - { - double x = lon - central_meridian; - double y = lat; - - y = 2.0 * Math.Atan(K * Math.Pow(Math.Tan(0.5 * y + Math.PI / 4), C) - * srat(_e * Math.Sin(y), ratexp)) - - Math.PI / 2; - x *= C; - double sinc = Math.Sin(y); - double cosc = Math.Cos(y); - double cosl = Math.Cos(x); - double k_ = R2 / (1.0 + sinc0 * sinc + cosc0 * cosc * cosl); - - lon = k_ * cosc * Math.Sin(x) * _globalScale; - lat = k_ * (cosc0 * sinc - sinc0 * cosc * cosl) * _globalScale; + y = phi; + if (--iter < 0) + { + throw new InvalidOperationException("Oblique Stereographics doesn't converge"); + } } + x += this.centralMeridian; + } - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse == null) - { - _inverse = new ObliqueStereographicProjection(_Parameters.ToProjectionParameter(), this); - } + /// + /// Method to convert a point (lon, lat) in radians to (x, y) in meters. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double longitude = lon - this.centralMeridian; + double latitude = lat; + + latitude = (2.0 * Math.Atan(this.k * Math.Pow(Math.Tan((0.5 * latitude) + (Math.PI / 4)), this.c) + * this.Srat(this.e * Math.Sin(latitude), this.ratexp))) + - (Math.PI / 2); + longitude *= this.c; + double sinLatitude = Math.Sin(latitude); + double cosLatitude = Math.Cos(latitude); + double cosLongitude = Math.Cos(longitude); + double radialScale = this.r2 / (1.0 + (this.sinc0 * sinLatitude) + (this.cosc0 * cosLatitude * cosLongitude)); + + lon = radialScale * cosLatitude * Math.Sin(longitude) * this.globalScale; + lat = radialScale * ((this.cosc0 * sinLatitude) - (this.sinc0 * cosLatitude * cosLongitude)) * this.globalScale; + } - return _inverse; - } + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= new ObliqueStereographicProjection(this.Parameters.ToProjectionParameter(), this); + return this.inverse; + } - private double srat(double esinp, double exp) - { - return Math.Pow((1.0 - esinp) / (1.0 + esinp), exp); - } + private double Srat(double esinp, double exp) + { + return Math.Pow((1.0 - esinp) / (1.0 + esinp), exp); } } diff --git a/src/ProjNet/CoordinateSystems/Projections/OrteliusProjection.cs b/src/ProjNet/CoordinateSystems/Projections/OrteliusProjection.cs new file mode 100644 index 00000000..afcc9de2 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/OrteliusProjection.cs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Ortelius Oval projection (ortel). +/// +/// +/// Ortelius Oval is a historical sixteenth-century member of the +/// family. It uses the shared globular formulation +/// together with the Ortelius-specific branch for longitudes beyond ±90°. +/// This derived class represents Abraham Ortelius's oval world-map style, +/// popularized in Theatrum Orbis Terrarum from 1570 onward, and delegates the +/// shared front-hemisphere construction to while +/// enabling the wide-longitude branch that distinguishes the Ortelius variant from +/// the Apian form. +/// +/// PROJ documentation: Ortelius Oval. +/// Wikipedia: Ortelius oval projection. +internal sealed class OrteliusProjection : BaconProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public OrteliusProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public OrteliusProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, bacon: false, ortelius: true, "Ortelius_Oval") + { + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/OrthographicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/OrthographicProjection.cs index bdf61ce8..f333dc04 100644 --- a/src/ProjNet/CoordinateSystems/Projections/OrthographicProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/OrthographicProjection.cs @@ -1,427 +1,451 @@ -using ProjNet.CoordinateSystems.Transformations; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; -using System.Text; - -namespace ProjNet.CoordinateSystems.Projections +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Orthographic map projection (ortho). +/// +/// +/// The Orthographic projection is a perspective azimuthal projection from an infinite +/// distance. Only the hemisphere facing the projection center is visible. Both spherical +/// and ellipsoidal models are supported. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.3.1, pp. 109-115. +internal sealed class OrthographicProjection : MapProjection { - [Serializable] - internal class OrthographicProjection : MapProjection + private readonly double sinph0; + private readonly double cosph0; + private readonly double nu0; + private readonly double yShift; + private readonly double yScale; + private readonly double sinalpha; + private readonly double cosalpha; + private readonly Mode mode; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public OrthographicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public OrthographicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) { - private enum Mode + this.Name = "Orthographic"; + double alpha = DegreesToRadians(this.Parameters.GetOptionalParameterValue("alpha", 0d, "azimuth")); + this.sinalpha = Math.Sin(alpha); + this.cosalpha = Math.Cos(alpha); + + Sincos(this.Phi0, out this.sinph0, out this.cosph0); + + if (Math.Abs(Math.Abs(this.Phi0) - HalfPi) <= Eps10) { - N_POLE = 0, - S_POLE = 1, - EQUIT = 2, - OBLIQ = 3 + this.mode = this.Phi0 < 0.0 ? Mode.SouthPole : Mode.NorthPole; + } + else if (Math.Abs(this.Phi0) > Eps10) + { + this.mode = Mode.Oblique; + } + else + { + this.mode = Mode.Equatorial; } - private readonly double _sinph0; - private readonly double _cosph0; - private readonly double _nu0; - private readonly double _y_shift; - private readonly double _y_scale; - private readonly Mode _mode; - /// - /// Initializes the OrthographicProjection object with the specified parameters to project points. - /// - /// ParameterList with the required parameters. - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. - /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). - /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. - /// - /// - public OrthographicProjection(IEnumerable parameters) : this(parameters, null) + if (this.es > 0) { + this.nu0 = this.semiMajor / Math.Sqrt(1.0 - (this.es * this.sinph0 * this.sinph0)); + this.yShift = this.es * this.nu0 / this.semiMajor * this.sinph0 * this.cosph0; + this.yScale = 1.0 / Math.Sqrt(1.0 - (this.es * this.cosph0 * this.cosph0)); } + } + private enum Mode + { /// - /// Initializes the OrthographicProjection object with the specified parameters to project points. + /// Projection center is at the geographic north pole. /// - /// List of parameters to initialize the projection. - /// Null indicates the projection is forward (degrees to meters). - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. - /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). - /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. - /// - /// - public OrthographicProjection(IEnumerable parameters, MapProjection inverse) : base(parameters, inverse) - { - Name = "Orthographic"; - - sincos(phi0, out _sinph0, out _cosph0); + NorthPole = 0, - if( Math.Abs(Math.Abs(phi0) - HALF_PI) <= EPS10 ) - { - _mode = phi0 < 0.0 ? Mode.S_POLE : Mode.N_POLE; - } - else if ( Math.Abs(phi0) > EPS10) - { - _mode = Mode.OBLIQ; - } - else - { - _mode = Mode.EQUIT; - } + /// + /// Projection center is at the geographic south pole. + /// + SouthPole = 1, - if( _es > 0 ) - { - _nu0 = _semiMajor / Math.Sqrt(1.0 - _es * _sinph0 * _sinph0); - _y_shift = _es * _nu0 / _semiMajor * _sinph0 * _cosph0; - _y_scale = 1.0 / Math.Sqrt(1.0 - _es * _cosph0 * _cosph0); - } - } + /// + /// Projection center lies on the equator. + /// + Equatorial = 2, /// - /// Returns the inverse of this projection. + /// Projection center is at an oblique (non-equatorial, non-polar) latitude. /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() + Oblique = 3, + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new OrthographicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + if (this.es == 0.0) + { + this.OrthoSInverse(ref x, ref y); + } + else + { + this.OrthoEInverse(ref x, ref y); + } + } + + /// + /// Converts coordinates in projected meters to radians for spherical orthographic projections. + /// + /// The x-ordinate in meters when entering, longitude in radians after exit. + /// The y-ordinate in meters when entering, latitude in radians after exit. + private void OrthoSInverse(ref double x, ref double y) + { + double xf = x; + double yf = y; + x = ((this.cosalpha * xf) + (this.sinalpha * yf)) / this.scaleFactor; + y = ((-this.sinalpha * xf) + (this.cosalpha * yf)) / this.scaleFactor; + + // Using the algorithm in Map projections: A working manual, by John Snyder pg 150 + double rho = Hypot(x, y); + if (rho > this.semiMajor) { - if (_inverse == null) + if ((rho - this.semiMajor) > Eps10) { - _inverse = new OrthographicProjection(_Parameters.ToProjectionParameter(), this); + ProjectionThrowHelper.ThrowInvalidOperation($"Point ({x:F3}, {y:F3}) is outside of the projection boundary"); } - return _inverse; + rho = this.semiMajor; } - /// - /// Converts coordinates in projected meters to radians. - /// - /// The x-ordinate in meters when entering, longitude in radians ater exit - /// The y-ordinate in meters when entering, latitude in radians after exit - protected override void MetersToRadians(ref double x, ref double y) + double sinc = rho / this.semiMajor; + + double cosc = Math.Sqrt(1.0 - (sinc * sinc)); // in this range OK + + double phi = this.latOrigin; + double lam = this.Lon_origin; + if (Math.Abs(rho) > Eps10) { - if( _es == 0.0 ) - { - OrthoSInverse(ref x, ref y); - } - else + switch (this.mode) { - OrthoEInverse(ref x, ref y); + case Mode.NorthPole: + phi = Math.Asin(cosc); + lam = this.Lon_origin + Math.Atan2(x, -y); + break; + case Mode.SouthPole: + phi = -Math.Asin(cosc); + lam = this.Lon_origin + Math.Atan2(x, y); + break; + case Mode.Equatorial: + if (Math.Abs(y) >= this.semiMajor) + { + phi = y < 0.0 ? -HalfPi : HalfPi; + } + else + { + phi = Math.Asin(y / this.semiMajor); + } + + lam = this.Lon_origin + Math.Atan2(x / this.semiMajor, cosc); + break; + case Mode.Oblique: + phi = Math.Asin((cosc * this.sinph0) + (y * this.cosph0 / this.semiMajor)); + lam = this.Lon_origin + Math.Atan2(x * sinc, (rho * this.cosph0 * cosc) - (y * this.sinph0 * sinc)); + break; + default: + throw new InvalidOperationException("Unsupported orthographic mode."); } } - /// - /// Converts coordinates in projected meters to radians for spherical orthographic projections. - /// - /// The x-ordinate in meters when entering, longitude in radians ater exit - /// The y-ordinate in meters when entering, latitude in radians after exit - private void OrthoSInverse(ref double x, ref double y) + // Return values in passed in parameters + x = lam; + y = phi; + } + + /// + /// Converts coordinates in projected meters to radians for ellipsoidal orthographic projections. + /// + /// The x-ordinate in meters when entering, longitude in radians after exit. + /// The y-ordinate in meters when entering, latitude in radians after exit. + private void OrthoEInverse(ref double x, ref double y) + { + Func sQ = (a) => a * a; + + double xf = x; + double yf = y; + x = ((this.cosalpha * xf) + (this.sinalpha * yf)) / this.scaleFactor; + y = ((-this.sinalpha * xf) + (this.cosalpha * yf)) / this.scaleFactor; + + double x_scaled = x / this.semiMajor; + double y_scaled = y / this.semiMajor; + double phi = this.latOrigin; + double lam = this.Lon_origin; + if (this.mode == Mode.NorthPole || this.mode == Mode.SouthPole) { - //Using the algorithm in Map projections: A working manual, by John Snyder pg 150 - double rho = hypot(x, y); - if( rho > _semiMajor) + // Polar case. Forward case equations can be simplified as: + // x = nu * cosphi * sinlam + // y = nu * -cosphi * coslam * sign(phi0) + // ==> lam = atan2(x, -y * sign(phi0)) + // ==> (x/a)^2 + (y/a)^2 = nu^2 * cosphi^2 + // rh^2 = cosphi^2 / (1 - es * sinphi^2) + // ==> cosphi^2 = rh^2 * (1 - es) / (1 - es * rh^2) + lam = Math.Atan2(x, -y * Sign(this.latOrigin)); + + double rh2 = sQ(x_scaled) + sQ(y_scaled); + if (rh2 >= 1.0 - 1e-15d) { - if( (rho - _semiMajor) > EPS10) + if ((rh2 - 1.0) > Eps10) { - throw new ArgumentOutOfRangeException($"Point ({x:F3}, {y:F3}) is outside of the projection boundary"); + ProjectionThrowHelper.ThrowInvalidOperation($"Point ({x_scaled:F3}, {y_scaled:F3}) is outside of the projection boundary"); } - rho = _semiMajor; - } - double sinc = rho / _semiMajor; - - double cosc = Math.Sqrt(1.0 - sinc * sinc); // in this range OK - - double phi; - double lam; - if (Math.Abs(rho) <= EPS10) - { - phi = lat_origin; - lam = lon_origin; + phi = 0.0; } else { - switch (_mode) - { - case Mode.N_POLE: - phi = Math.Asin(cosc); - lam = lon_origin + Math.Atan2(x, -y); - break; - case Mode.S_POLE: - phi = -Math.Asin(cosc); - lam = lon_origin + Math.Atan2(x, y); - break; - case Mode.EQUIT: - if (Math.Abs(y) >= _semiMajor) - { - phi = y < 0.0 ? -HALF_PI : HALF_PI; - } - else - { - phi = Math.Asin(y/_semiMajor); - } - lam = lon_origin + Math.Atan2(x / _semiMajor, cosc); - break; - case Mode.OBLIQ: - phi = Math.Asin(cosc * _sinph0 + (y * _cosph0 / _semiMajor)); - lam = lon_origin + Math.Atan2(x * sinc, rho * _cosph0 * cosc - y * _sinph0 * sinc); - break; - default: - throw new ArgumentOutOfRangeException(nameof(_mode)); - } + phi = Math.Acos(Math.Sqrt(rh2 * (1 - this.es) / (1 - (this.es * rh2)))) * Sign(this.latOrigin); } - - //Return values in passed in parameters - x = lam; - y = phi; } - - /// - /// Converts coordinates in projected meters to radians for ellipsoidal orthographic projections. - /// - /// The x-ordinate in meters when entering, longitude in radians ater exit - /// The y-ordinate in meters when entering, latitude in radians after exit - private void OrthoEInverse(ref double x, ref double y) + else if (this.mode == Mode.Equatorial) { - Func SQ = (a) => a * a; + // Equatorial case. Forward case equations can be simplified as: + // x = nu * cosphi * sinlam + // y = nu * sinphi * (1 - P->es) + // (x/a)^2 * (1 - es * sinphi^2) = (1 - sinphi^2) * sinlam^2 + // (y/a)^2 / ((1 - es)^2 + (y/a)^2 * es) = sinphi^2 + + // Equation of the ellipse + if (sQ(x_scaled) + sQ(y_scaled * (this.semiMajor / this.semiMinor)) > 1 + 1e-11d) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Point ({x:F3}, {y:F3}) is outside of the projection boundary"); + } - double x_scaled = x / _semiMajor; - double y_scaled = y / _semiMajor; - double phi; - double lam; - if (_mode == Mode.N_POLE || _mode == Mode.S_POLE) + double sinphi2 = sQ(y_scaled) / (sQ(1 - this.es) + (sQ(y_scaled) * this.es)); + if (sinphi2 > 1 - 1e-11d) { - // Polar case. Forward case equations can be simplified as: - // x = nu * cosphi * sinlam - // y = nu * -cosphi * coslam * sign(phi0) - // ==> lam = atan2(x, -y * sign(phi0)) - // ==> (x/a)^2 + (y/a)^2 = nu^2 * cosphi^2 - // rh^2 = cosphi^2 / (1 - es * sinphi^2) - // ==> cosphi^2 = rh^2 * (1 - es) / (1 - es * rh^2) - lam = Math.Atan2(x, -y * sign(lat_origin)); - - double rh2 = SQ(x_scaled) + SQ(y_scaled); - if (rh2 >= 1.0 - 1e-15) - { - if ((rh2 - 1.0) > EPS10) - { - throw new ArgumentOutOfRangeException($"Point ({x_scaled:F3}, {y_scaled:F3}) is outside of the projection boundary"); - } - phi = 0.0; - } - else - { - phi = Math.Acos(Math.Sqrt(rh2 * (1 - _es) / (1 - _es * rh2))) * sign(lat_origin); - } + phi = HalfPi * Sign(y_scaled); + lam = 0.0; } - else if (_mode == Mode.EQUIT) + else { - // Equatorial case. Forward case equations can be simplified as: - // x = nu * cosphi * sinlam - // y = nu * sinphi * (1 - P->es) - // (x/a)^2 * (1 - es * sinphi^2) = (1 - sinphi^2) * sinlam^2 - // (y/a)^2 / ((1 - es)^2 + (y/a)^2 * es) = sinphi^2 - - // Equation of the ellipse - if( SQ(x_scaled) + SQ(y_scaled * (_semiMajor / _semiMinor)) > 1 + 1e-11 ) + phi = Math.Asin(Math.Sqrt(sinphi2)) * Sign(y_scaled); + double sinlam = x_scaled * Math.Sqrt((1 - (this.es * sinphi2)) / (1 - sinphi2)); + if (Math.Abs(sinlam) - 1 > -1e-15d) { - throw new ArgumentOutOfRangeException($"Point ({x:F3}, {y:F3}) is outside of the projection boundary"); - } - - double sinphi2 = SQ(y_scaled) / (SQ(1 - _es) + SQ(y_scaled)*_es); - if (sinphi2 > 1 - 1e-11) - { - phi = HALF_PI * sign(y_scaled); - lam = 0.0; + lam = HalfPi * Sign(x_scaled); } else { - phi = Math.Asin(Math.Sqrt(sinphi2)) * sign(y_scaled); - double sinlam = x_scaled * Math.Sqrt((1 - _es * sinphi2) / (1 - sinphi2)); - if (Math.Abs(sinlam) - 1 > -1e-15) - { - lam = HALF_PI * sign(x_scaled); - } - else - { - lam = Math.Asin(sinlam); - } + lam = Math.Asin(sinlam); } } - else + } + else + { + // Using Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam == 0 (visibity + // condition of the forward case) in the forward equations, and a lot of + // substitution games... + double x_recentered = x; + double y_recentered = (y - this.yShift) / this.yScale; + if (sQ(x_scaled) + sQ(y_scaled) > 1 + 1e-11d) { - // Using Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam == 0 (visibity - // condition of the forward case) in the forward equations, and a lot of - // substitution games... - double x_recentered = x; - double y_recentered = (y - _y_shift) / _y_scale; - if( SQ(x_scaled) + SQ(y_scaled) > 1 + 1e-11) - { - throw new ArgumentOutOfRangeException($"Point ({x_scaled:F3}, {y_scaled:F3}) is outside of the projection boundary"); - } + ProjectionThrowHelper.ThrowInvalidOperation($"Point ({x_scaled:F3}, {y_scaled:F3}) is outside of the projection boundary"); + } - // From EPSG guidance note 7.2, March 2020, §3.3.5 Orthographic + // From EPSG guidance note 7.2, March 2020, §3.3.5 Orthographic - // It suggests as initial guess: - // lp.lam = 0; - // lp.phi = P->phi0; - // But for poles, this will not converge well. Better use: - OrthoSInverse(ref x_recentered, ref y_recentered); - phi = y_recentered; - lam = x_recentered - lon_origin; + // It suggests as initial guess: + // lp.lam = 0; + // lp.phi = P->phi0; + // But for poles, this will not converge well. Better use: + this.OrthoSInverse(ref x_recentered, ref y_recentered); + phi = y_recentered; + lam = x_recentered - this.Lon_origin; - for ( int i = 0; i < 20; ++i ) + bool converged = false; + for (int i = 0; i < 20; ++i) + { + Sincos(phi, out double sinphi, out double cosphi); + Sincos(lam, out double sinlam, out double coslam); + double one_minus_es_sinphi2 = 1.0 - (this.es * sinphi * sinphi); + double nu = this.semiMajor / Math.Sqrt(one_minus_es_sinphi2); + double rho = (1.0 - this.es) * nu / one_minus_es_sinphi2; + + double x_new = nu * cosphi * sinlam; + double y_new = (nu * ((sinphi * this.cosph0) - (cosphi * this.sinph0 * coslam))) + + (this.es * ((this.nu0 * this.sinph0) - (nu * sinphi)) * this.cosph0); + double j11 = -rho * sinphi * sinlam; + double j12 = nu * cosphi * coslam; + double j21 = rho * ((cosphi * this.cosph0) + (sinphi * this.sinph0 * coslam)); + double j22 = nu * this.sinph0 * cosphi * sinlam; + double d = (j11 * j22) - (j12 * j21); + double dx = x - x_new; + double dy = y - y_new; + double dphi = ((j22 * dx) - (j12 * dy)) / d; + double dlam = ((-j21 * dx) + (j11 * dy)) / d; + + phi += dphi; + if (phi > HalfPi) { - sincos(phi, out double sinphi, out double cosphi); - sincos(lam, out double sinlam, out double coslam); - double one_minus_es_sinphi2 = 1.0 - _es * sinphi * sinphi; - double nu = _semiMajor / Math.Sqrt(one_minus_es_sinphi2); - double rho = (1.0 - _es) * nu / one_minus_es_sinphi2; - - double x_new = nu * cosphi * sinlam; - double y_new = nu * (sinphi * _cosph0 - cosphi * _sinph0 * coslam) + - _es * (_nu0 * _sinph0 - nu * sinphi) * _cosph0; - double J11 = -rho * sinphi * sinlam; - double J12 = nu * cosphi * coslam; - double J21 = rho * (cosphi * _cosph0 + sinphi * _sinph0 * coslam); - double J22 = nu * _sinph0 * _cosph0 * sinlam; - double D = J11 * J22 - J12 * J21; - double dx = x - x_new; - double dy = y - y_new; - double dphi = (J22 * dx - J12 * dy) / D; - double dlam = (-J21 * dx + J11 * dy) / D; - - phi += dphi; - if( phi > HALF_PI) - { - phi = HALF_PI; - } - else if (phi < -HALF_PI) - { - phi = -HALF_PI; - } + phi = HalfPi - (phi - HalfPi); + lam = Adjust_lon(lam + PI); + } + else if (phi < -HalfPi) + { + phi = -HalfPi + (-HalfPi - phi); + lam = Adjust_lon(lam + PI); + } - lam += dlam; - if( Math.Abs(dphi) < 1e-12 && Math.Abs(dlam) < 1e-12 ) - { - break; - } + lam += dlam; + if (Math.Abs(dphi) < ProjectionConstants.Tolerance1E12 && Math.Abs(dlam) < ProjectionConstants.Tolerance1E12) + { + converged = true; + break; } } - //Return values - x = lam + lon_origin; - y = phi; + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } } - /// - /// Method to convert a point (lon, lat) in radians to (x, y) in meters - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - protected override void RadiansToMeters(ref double lon, ref double lat) + // Return values + x = lam + this.Lon_origin; + y = phi; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + if (this.es == 0.0) { - if (_es == 0.0) - { - OrthoSForward(ref lon, ref lat); - } - else - { - OrthoEForward(ref lon, ref lat); - } + this.OrthoSForward(ref lon, ref lat); + } + else + { + this.OrthoEForward(ref lon, ref lat); } + } - /// - /// Method to convert a point (lon, lat) in radians to (x, y) in meters for spherical orthographic projections - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - private void OrthoSForward(ref double lam, ref double phi) + /// + /// Method to convert a point (lon, lat) in radians to (x, y) in meters for spherical orthographic projections. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + private void OrthoSForward(ref double lam, ref double phi) + { + double y = HugeVal; + + double cosphi = Math.Cos(phi); + double coslam = Math.Cos(lam - this.Lon_origin); + switch (this.mode) { - double x = HUGE_VAL; - double y = HUGE_VAL; + case Mode.Equatorial: + if (cosphi * coslam < -Eps10) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); + } - double cosphi = Math.Cos(phi); - double coslam = Math.Cos(lam - lon_origin); - double sinphi; - switch (_mode) - { - case Mode.EQUIT: - if (cosphi * coslam < -EPS10) - { - throw new ArgumentOutOfRangeException($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); - } - y = _semiMajor * Math.Sin(phi); - break; - case Mode.OBLIQ: - sinphi = Math.Sin(phi); - - // Is the point visible from the projection plane ? - // From https://lists.osgeo.org/pipermail/proj/2020-September/009831.html - // this is the dot product of the normal of the ellipsoid at the center of - // the projection and at the point considered for projection. - // [cos(phi)*cos(lambda), cos(phi)*sin(lambda), sin(phi)] - // Also from Snyder's Map Projection - A working manual, equation (5-3), page 149 - if (_sinph0 * sinphi + _cosph0 * cosphi * coslam < -EPS10) - { - throw new ArgumentOutOfRangeException($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); - } - y = _semiMajor * ( _cosph0 * sinphi - _sinph0 * cosphi * coslam ); - break; - case Mode.N_POLE: - coslam = -coslam; - if (Math.Abs(phi - phi0) - EPS10 > HALF_PI) - { - throw new ArgumentOutOfRangeException($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); - } - y = _semiMajor * cosphi * coslam; - break; - case Mode.S_POLE: - if (Math.Abs(phi - phi0) - EPS10 > HALF_PI) - { - throw new ArgumentOutOfRangeException($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); - } - y = _semiMajor * cosphi * coslam; - break; - } + y = this.semiMajor * Math.Sin(phi); + break; + case Mode.Oblique: + double sinphi = Math.Sin(phi); + + // Is the point visible from the projection plane ? + // From https://lists.osgeo.org/pipermail/proj/2020-September/009831.html + // this is the dot product of the normal of the ellipsoid at the center of + // the projection and at the point considered for projection. + // [cos(phi)*cos(lambda), cos(phi)*sin(lambda), sin(phi)] + // Also from Snyder's Map Projection - A working manual, equation (5-3), page 149 + if ((this.sinph0 * sinphi) + (this.cosph0 * cosphi * coslam) < -Eps10) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); + } - x = _semiMajor * cosphi * Math.Sin(lam - lon_origin); + y = this.semiMajor * ((this.cosph0 * sinphi) - (this.sinph0 * cosphi * coslam)); + break; + case Mode.NorthPole: + coslam = -coslam; + if (Math.Abs(phi - this.Phi0) - Eps10 > HalfPi) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); + } + + y = this.semiMajor * cosphi * coslam; + break; + case Mode.SouthPole: + if (Math.Abs(phi - this.Phi0) - Eps10 > HalfPi) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); + } - // Set the variables to return - lam = x; - phi = y; + y = this.semiMajor * cosphi * coslam; + break; } - /// - /// Method to convert a point (lon, lat) in radians to (x, y) in meters for ellipsoidal orthographic projections - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - private void OrthoEForward(ref double lam, ref double phi) - { - // From EPSG guidance note 7.2, March 2020, §3.3.5 Orthographic - sincos(phi, out double sinphi, out double cosphi); - sincos(lam - lon_origin, out double sinlam, out double coslam); + double x = this.semiMajor * cosphi * Math.Sin(lam - this.Lon_origin); + double xp = x; + double yp = y; + x = ((xp * this.cosalpha) - (yp * this.sinalpha)) * this.scaleFactor; + y = ((xp * this.sinalpha) + (yp * this.cosalpha)) * this.scaleFactor; - // Is the point visible from the projection plane ? - // Same condition as in spherical case - if( _sinph0 * sinphi + _cosph0 * cosphi * coslam < - EPS10 ) - { - throw new ArgumentOutOfRangeException($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); - } + // Set the variables to return + lam = x; + phi = y; + } - double nu = _semiMajor / Math.Sqrt(1.0 - _es * sinphi * sinphi); - double x = nu * cosphi * sinlam; - double y = nu * (sinphi * _cosph0 - cosphi * _sinph0 * coslam) + - _es * (_nu0 * _sinph0 - nu * sinphi) * _cosph0; + /// + /// Method to convert a point (lon, lat) in radians to (x, y) in meters for ellipsoidal orthographic projections. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + private void OrthoEForward(ref double lam, ref double phi) + { + // From EPSG guidance note 7.2, March 2020, §3.3.5 Orthographic + Sincos(phi, out double sinphi, out double cosphi); + Sincos(lam - this.Lon_origin, out double sinlam, out double coslam); - lam = x; - phi = y; + // Is the point visible from the projection plane ? + // Same condition as in spherical case + if ((this.sinph0 * sinphi) + (this.cosph0 * cosphi * coslam) < -Eps10) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Coordinate ({RadiansToDegrees(lam):F3}, {RadiansToDegrees(phi):F3}) is on the unprojected hemisphere"); } + + double nu = this.semiMajor / Math.Sqrt(1.0 - (this.es * sinphi * sinphi)); + double x = nu * cosphi * sinlam; + double y = (nu * ((sinphi * this.cosph0) - (cosphi * this.sinph0 * coslam))) + + (this.es * ((this.nu0 * this.sinph0) - (nu * sinphi)) * this.cosph0); + double xp = x; + double yp = y; + x = ((xp * this.cosalpha) - (yp * this.sinalpha)) * this.scaleFactor; + y = ((xp * this.sinalpha) + (yp * this.cosalpha)) * this.scaleFactor; + + lam = x; + phi = y; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/PattersonProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PattersonProjection.cs new file mode 100644 index 00000000..712caab4 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PattersonProjection.cs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Patterson cylindrical projection (patterson). +/// +/// +/// The Patterson projection is a cylindrical projection whose y-coordinates are computed via +/// a polynomial formula designed for a visually balanced appearance. The inverse is solved +/// iteratively via Newton–Raphson iteration. +/// The formulation was independently verified against Patterson's published polynomial +/// coefficient set. The implementation matches the forward polynomial in odd powers of +/// φ and the Newton iteration driven by its analytical derivative. +/// +internal sealed class PattersonProjection : MapProjection +{ + private const double K1 = 1.0148d; + private const double K2 = 0.23185d; + private const double K3 = -0.14499d; + private const double K4 = 0.02406d; + private const int Iterations = 12; + + private readonly double maxY; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PattersonProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PattersonProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Patterson"; + this.maxY = ForwardPolynomial(HalfPi); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PattersonProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + lon = this.SphericalRadius * lambda; + lat = this.SphericalRadius * ForwardPolynomial(lat); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x = Adjust_lon(this.centralMeridian + (x * this.InverseSphericalRadius)); + + double targetY = ProjectionConstants.Clamp(y * this.InverseSphericalRadius, -this.maxY, this.maxY); + double phi = targetY / K1; + + for (int i = 0; i < Iterations; i++) + { + double f = ForwardPolynomial(phi) - targetY; + double df = ForwardPolynomialDerivative(phi); + double delta = f / df; + phi -= delta; + if (Math.Abs(delta) <= ProjectionConstants.Tolerance1E12) + { + break; + } + } + + y = ProjectionConstants.Clamp(phi, -HalfPi, HalfPi); + } + + private static double ForwardPolynomial(double phi) + { + double phi2 = phi * phi; + double phi4 = phi2 * phi2; + double phi6 = phi4 * phi2; + double phi8 = phi4 * phi4; + + return (K1 * phi) + (K2 * phi * phi4) + (K3 * phi * phi6) + (K4 * phi * phi8); + } + + private static double ForwardPolynomialDerivative(double phi) + { + double phi2 = phi * phi; + double phi4 = phi2 * phi2; + double phi6 = phi4 * phi2; + double phi8 = phi4 * phi4; + + return K1 + (5d * K2 * phi4) + (7d * K3 * phi6) + (9d * K4 * phi8); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PconicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PconicProjection.cs new file mode 100644 index 00000000..795988b8 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PconicProjection.cs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Perspective Conic projection (pconic). +/// +/// +/// A spherical conic projection in which the cone is defined by two standard parallels and +/// the graticule is constructed by perspective projection from the opposite pole. Only +/// spherical input is supported. +/// The formulation was independently verified against the standard perspective-conic +/// equations. The implementation matches the radial relation +/// ρ = c2 * (c1 - tan(φ - sig)) together with the conic angle +/// θ = n * λ. +/// +internal sealed class PconicProjection : MapProjection +{ + private readonly double n; + private readonly double sig; + private readonly double c1; + private readonly double c2; + private readonly double rho0; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PconicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PconicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Perspective_Conic"; + + double standardParallel1 = DegreesToRadians(this.Parameters.GetParameterValue("standard_parallel_1", "lat_1")); + double standardParallel2 = DegreesToRadians(this.Parameters.GetParameterValue("standard_parallel_2", "lat_2")); + double delta = 0.5d * (standardParallel2 - standardParallel1); + this.sig = 0.5d * (standardParallel2 + standardParallel1); + if (Math.Abs(delta) < Eps10 || Math.Abs(this.sig) < Eps10) + { + ArgumentGuard.ThrowArgument("Illegal value for lat_1 and lat_2: |lat_1 - lat_2| and |lat_1 + lat_2| should be > 0.", nameof(parameters)); + } + + this.n = Math.Sin(this.sig); + this.c2 = Math.Cos(delta); + this.c1 = 1d / Math.Tan(this.sig); + + double latitudeOffset = this.latOrigin - this.sig; + if ((Math.Abs(latitudeOffset) - Eps10) >= HalfPi) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_0/lat_1/lat_2: |lat_0 - 0.5 * (lat_1 + lat_2)| should be < 90°.", nameof(parameters)); + } + + this.rho0 = this.c2 * (this.c1 - Math.Tan(latitudeOffset)); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PconicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double rho = this.c2 * (this.c1 - Math.Tan(lat - this.sig)); + double theta = this.n * Adjust_lon(lon - this.centralMeridian); + + lon = this.SphericalRadius * rho * Math.Sin(theta); + lat = this.SphericalRadius * (this.rho0 - (rho * Math.Cos(theta))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = this.rho0 - (y * this.InverseSphericalRadius); + double rho = Hypot(xUnit, yUnit); + + if (this.n < 0d) + { + rho = -rho; + xUnit = -xUnit; + yUnit = -yUnit; + } + + double lambda = Math.Atan2(xUnit, yUnit) / this.n; + double phi = Math.Atan(this.c1 - (rho / this.c2)) + this.sig; + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PeirceQuincuncialProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PeirceQuincuncialProjection.cs new file mode 100644 index 00000000..d358e2f1 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PeirceQuincuncialProjection.cs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Peirce quincuncial projection (peirce_q). +/// +/// +/// Peirce quincuncial is the quincuncial specialization of +/// attributed to Charles Sanders Peirce. It reuses the shared conformal square machinery +/// and exposes the Peirce-specific shape and scroll parameters. Inverse projection is +/// supported for the square and diamond shapes, but not for the hemisphere, horizontal, +/// or vertical shape variants. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 7, Sect. 7.4.1, pp. 206-208. +internal sealed class PeirceQuincuncialProjection : AdamsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PeirceQuincuncialProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PeirceQuincuncialProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Peirce_Quincuncial", AdamsMode.PeirceQ) + { + } + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new PeirceQuincuncialProjection(this.Parameters.ToProjectionParameter(), this)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PolarStereographicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PolarStereographicProjection.cs index 42692a9a..606fb735 100644 --- a/src/ProjNet/CoordinateSystems/Projections/PolarStereographicProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/PolarStereographicProjection.cs @@ -1,229 +1,206 @@ -// Copyright 2015 -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Polar Stereographic Projection. +/// +/// +/// The formulation was independently verified against IOGP, "Geomatics Guidance Note 7, part 2: +/// Coordinate Conversions and Transformations including Formulas" (publication +/// 373-7-2, 2019), EPSG method 9810, Polar Stereographic (variant A). The +/// ellipsoidal polar formulation keeps the natural-origin scale factor k0 +/// in the numerator of the ρ expression, matching the published method +/// and the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 21, pp. 154-163, +/// eqs. (21-1) through (21-39), for the stereographic and polar stereographic +/// development. +/// +/// EPSG method 9810: Polar Stereographic (variant A). +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 3, Sect. 3.2.2, pp. 102-104. +internal class PolarStereographicProjection : MapProjection { + private const int MaximumIterations = 15; + private const double IterationTolerance = 1E-14d; + private const double Eps15 = 1E-15d; + + private readonly double globalScale; + private readonly double reciprocGlobalScale; + private readonly double phits; + private readonly double akm1; + private readonly bool npole; + /// - /// Implements the Polar Stereographic Projection. + /// Initializes a new instance of the class. /// - [Serializable] - internal class PolarStereographicProjection : MapProjection + /// List of parameters to initialize the projection. + /// + /// The parameters this projection expects are listed below. + /// + /// ItemsDescriptions + /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). + /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). + /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. + /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). + /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. + /// + /// + public PolarStereographicProjection(IEnumerable parameters) + : this(parameters, null) { - private readonly double _globalScale; - private readonly double _reciprocGlobalScale; - - private static int MAXIMUM_ITERATIONS = 15; - private static double ITERATION_TOLERANCE = 1E-14; - private static double EPS15 = 1E-15; - private static double M_HALFPI = 0.5 * Math.PI; - private double phits, akm1; - private bool N_POLE; - - - /// - /// Initializes the PolarStereographicProjection object with the specified parameters. - /// - /// List of parameters to initialize the projection. - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. - /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). - /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. - /// - /// - public PolarStereographicProjection(IEnumerable parameters) - : this(parameters, null) - { - } + } - /// - /// Initializes the PolarStereographicProjection object with the specified parameters. - /// - /// List of parameters to initialize the projection. - /// Inverse projection - /// - /// The parameters this projection expects are listed below. - /// - /// ItemsDescriptions - /// central_meridianThe longitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the longitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// latitude_of_originThe latitude of the point from which the values of both the geographical coordinates on the ellipsoid and the grid coordinates on the projection are deemed to increment or decrement for computational purposes. Alternatively it may be considered as the latitude of the point which in the absence of application of false coordinates has grid coordinates of (0,0). - /// scale_factorThe factor by which the map grid is reduced or enlarged during the projection process, defined by its value at the natural origin. - /// false_eastingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Easting, FE, is the easting value assigned to the abscissa (east). - /// false_northingSince the natural origin may be at or near the centre of the projection and under normal coordinate circumstances would thus give rise to negative coordinates over parts of the mapped area, this origin is usually given false coordinates which are large enough to avoid this inconvenience. The False Northing, FN, is the northing value assigned to the ordinate. - /// - /// - public PolarStereographicProjection(IEnumerable parameters, PolarStereographicProjection inverse) - : base(parameters, inverse) - { - Name = "Polar_Stereographic"; + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + public PolarStereographicProjection(IEnumerable parameters, PolarStereographicProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Polar_Stereographic"; - _globalScale = scale_factor * _semiMajor; - _reciprocGlobalScale = 1.0 / _globalScale; + // Keep k0 in akm1 (matching PROJ's stere setup) and only apply semi-major scale here. + this.globalScale = this.semiMajor; + this.reciprocGlobalScale = 1.0 / this.globalScale; - if (_e == 0.0) throw new Exception("Polar Stereographics: only ellipsoidal formulation"); - N_POLE = (lat_origin > 0.0); // N or S hemisphere - phits = Math.Abs(lat_origin); + if (this.e == 0.0) + { + throw new NotSupportedException("Polar Stereographics: only ellipsoidal formulation"); + } - if (Math.Abs(phits - M_HALFPI) < EPS10) - { - double one_p_e = 1.0 + _e; - double one_m_e = 1.0 - _e; - double pow_p = Math.Pow(one_p_e, one_p_e); - double pow_m = Math.Pow(one_m_e, one_m_e); - akm1 = 2.0 / Math.Sqrt(pow_p * pow_m); - } - else - { - double sinphits = Math.Sin(phits); - double cosphits = Math.Cos(phits); - akm1 = cosphits / tsfn(cosphits, sinphits, _e); + this.npole = this.latOrigin > 0.0; // N or S hemisphere + this.phits = Math.Abs(this.latOrigin); - double t = _e * sinphits; - akm1 /= Math.Sqrt(1.0 - t * t); - } + if (Math.Abs(this.phits - HalfPi) < Eps10) + { + double one_p_e = 1.0 + this.e; + double one_m_e = 1.0 - this.e; + double pow_p = Math.Pow(one_p_e, one_p_e); + double pow_m = Math.Pow(one_m_e, one_m_e); + this.akm1 = (2.0 * this.scaleFactor) / Math.Sqrt(pow_p * pow_m); } + else + { + double sinphits = Math.Sin(this.phits); + double cosphits = Math.Cos(this.phits); + this.akm1 = (this.scaleFactor * cosphits) / this.Tsfn(cosphits, sinphits, this.e); + + double t = this.e * sinphits; + this.akm1 /= Math.Sqrt(1.0 - (t * t)); + } + } + + /// + /// Converts coordinates in projected meters to radians. + /// + /// The x-ordinate in projected meters. + /// The y-ordinate in projected meters. + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocGlobalScale; + y *= this.reciprocGlobalScale; - /// - /// Converts coordinates in projected meters to radians. - /// - /// - /// - protected override void MetersToRadians(ref double x, ref double y) + if (this.npole) { - x *= _reciprocGlobalScale; - y *= _reciprocGlobalScale; + y = -y; + } - if (N_POLE) y = -y; - double rho = Math.Sqrt(x * x + y * y); - double tp = -rho / akm1; - double phi_l = M_HALFPI - 2.0 * Math.Atan(tp); - double halfe = -0.5 * _e; + double rho = Math.Sqrt((x * x) + (y * y)); + double tp = -rho / this.akm1; + double phi_l = HalfPi - (2.0 * Math.Atan(tp)); + double halfe = -0.5 * this.e; - double lp_phi = 0.0; - for (int iter = MAXIMUM_ITERATIONS; ;) + double lp_phi = phi_l; + for (int iter = MaximumIterations; ;) + { + double sinphi = this.e * Math.Sin(phi_l); + double one_p_sinphi = 1.0 + sinphi; + double one_m_sinphi = 1.0 - sinphi; + lp_phi = (2.0 * Math.Atan(tp * Math.Pow(one_p_sinphi / one_m_sinphi, halfe))) + HalfPi; + if (Math.Abs(phi_l - lp_phi) < IterationTolerance) { - double sinphi = _e * Math.Sin(phi_l); - double one_p_sinphi = 1.0 + sinphi; - double one_m_sinphi = 1.0 - sinphi; - lp_phi = 2.0 * Math.Atan(tp * Math.Pow(one_p_sinphi / one_m_sinphi, halfe)) + M_HALFPI; - if (Math.Abs(phi_l - lp_phi) < ITERATION_TOLERANCE) - { - break; - } - - phi_l = lp_phi; - if (--iter < 0) - { - throw new Exception("Polar Stereographics doesn't converge"); - } - + break; } - if (!N_POLE) lp_phi = -lp_phi; - double lp_lam = (x == 0.0 && y == 0.0) ? 0.0 : Math.Atan2(x, y); - - x = lp_lam + central_meridian; - y = lp_phi; + phi_l = lp_phi; + if (--iter < 0) + { + throw new InvalidOperationException("Polar Stereographics doesn't converge"); + } } - /// - /// Method to convert a point (lon, lat) in radians to (x, y) in meters - /// - /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. - /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. - protected override void RadiansToMeters(ref double lon, ref double lat) + if (!this.npole) { - double lp_lam = lon - central_meridian; - double lp_phi = lat; + lp_phi = -lp_phi; + } - double coslam = Math.Cos(lp_lam); - double sinlam = Math.Sin(lp_lam); + double lp_lam = (x == 0.0 && y == 0.0) ? 0.0 : Math.Atan2(x, y); - if (!N_POLE) - { - lp_phi = -lp_phi; - coslam = -coslam; - } + x = lp_lam + this.centralMeridian; + y = lp_phi; + } - double sinphi = Math.Sin(lp_phi); - double cosphi = Math.Cos(lp_phi); + /// + /// Method to convert a point (lon, lat) in radians to (x, y) in meters. + /// + /// The longitude of the point in radians when entering, its x-ordinate in meters after exit. + /// The latitude of the point in radians when entering, its y-ordinate in meters after exit. + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lp_lam = lon - this.centralMeridian; + double lp_phi = lat; + + double coslam = Math.Cos(lp_lam); + double sinlam = Math.Sin(lp_lam); - double x = (Math.Abs(lp_phi - M_HALFPI) < EPS15) ? 0.0 : akm1 * tsfn(cosphi, sinphi, _e); - lon = x * sinlam * _globalScale; - lat = -x * coslam * _globalScale; + if (!this.npole) + { + lp_phi = -lp_phi; + coslam = -coslam; } + double sinphi = Math.Sin(lp_phi); + double cosphi = Math.Cos(lp_phi); - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse == null) - { - _inverse = new PolarStereographicProjection(_Parameters.ToProjectionParameter(), this); - } + double x = (Math.Abs(lp_phi - HalfPi) < Eps15) ? 0.0 : this.akm1 * this.Tsfn(cosphi, sinphi, this.e); + lon = x * sinlam * this.globalScale; + lat = -x * coslam * this.globalScale; + } - return _inverse; - } + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= new PolarStereographicProjection(this.Parameters.ToProjectionParameter(), this); - private double tsfn(double cosphi, double sinphi, double e) - { - double t = (sinphi > 0.0) ? cosphi / (1.0 + sinphi) : (1.0 - sinphi) / cosphi; - return Math.Exp(e * Atanh(e * sinphi)) * t; - } + return this.inverse; + } + private double Tsfn(double cosphi, double sinphi, double e) + { + double t = (sinphi > 0.0) ? cosphi / (1.0 + sinphi) : (1.0 - sinphi) / cosphi; + return Math.Exp(e * Atanh(e * sinphi)) * t; + } - /// - /// Atanh - Inverse of Math.Tanh - /// - /// The Math.Atanh is not available for netstandard2.0. - /// - private static double Atanh(double x) - { - return Math.Log((1 + x) / (1 - x)) * 0.5; - } + /// + /// Atanh - Inverse of Math.Tanh. + /// + /// The Math.Atanh is not available for netstandard2.0. + /// The x parameter. + private static double Atanh(double x) + { + return Math.Log((1 + x) / (1 - x)) * 0.5; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/PolyconicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PolyconicProjection.cs index b749a4ae..50d023e6 100644 --- a/src/ProjNet/CoordinateSystems/Projections/PolyconicProjection.cs +++ b/src/ProjNet/CoordinateSystems/Projections/PolyconicProjection.cs @@ -1,166 +1,169 @@ -/* - * http://svn.osgeo.org/geotools/tags/2.6.2/modules/library/referencing/src/main/java/org/geotools/referencing/operation/projection/Polyconic.java - * http://svn.osgeo.org/geotools/tags/2.6.2/modules/library/referencing/src/main/java/org/geotools/referencing/operation/projection/MapProjection.java - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the American Polyconic map projection. +/// +/// +/// The American Polyconic represents each parallel by its own circular arc while +/// preserving true scale along the central meridian. The ellipsoidal form depends on the +/// meridian arc and the cotangent of latitude. +/// The formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG method 9818, American Polyconic, and John P. +/// Snyder, Map Projections - A Working Manual, USGS Professional Paper 1395, +/// section 18. The forward easting and northing equations using the meridian arc +/// through Mlfn/Inv_mlfn match the implementation here. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 4, Sect. 4.3.1, pp. 149-151. +/// EPSG method 9818: American Polyconic. +internal sealed class PolyconicProjection : MapProjection { /// - /// + /// Maximum difference allowed when comparing real numbers. /// - [Serializable] - internal class PolyconicProjection : MapProjection - { - /// - /// Maximum difference allowed when comparing real numbers. - /// - private const double Epsilon = 1E-10; - - /// - /// Maximum number of iterations for iterative computations. - /// - private const int MaximumIterations = 20; - - /// - /// Difference allowed in iterative computations. - /// - private const double IterationTolerance = 1E-12; - - /// - /// Meridian distance at the latitude of origin. - /// Used for calculations for the ellipsoid. - /// - private readonly double _ml0; - - private readonly double _reciprocSemiMajorTimesScaleFactor; - - /// - /// Constructs a new map projection from the supplied parameters. - /// - /// The parameter values in standard units - public PolyconicProjection(IEnumerable parameters) - : this(parameters, null) - { } - - /// - /// Constructs a new map projection from the supplied parameters. - /// - /// The parameter values in standard units - /// Defines if Projection is inverse - protected PolyconicProjection(IEnumerable parameters, PolyconicProjection inverse) - : base(parameters, inverse) - { - Name = "Polyconic"; - _ml0 = mlfn(lat_origin, Math.Sin(lat_origin), Math.Cos(lat_origin)); - _reciprocSemiMajorTimesScaleFactor = 1 / (_semiMajor * scale_factor); - } + /// + /// Maximum number of iterations for iterative computations. + /// + private const int MaximumIterations = 20; - protected override void RadiansToMeters(ref double lon, ref double lat) - { - double lam = lon; - double phi = lat; + /// + /// Difference allowed in iterative computations. + /// + private const double IterationTolerance = ProjectionConstants.Tolerance1E12; - double delta_lam = adjust_lon(lam - central_meridian); + /// + /// Meridian distance at the latitude of origin. + /// Used for calculations for the ellipsoid. + /// + private readonly double ml0; - double x, y; + private readonly double reciprocSemiMajorTimesScaleFactor; - if (Math.Abs(phi) <= Epsilon) - { - x = delta_lam; //lam; - y = -_ml0; - } - else - { - double sp = Math.Sin(phi); - double cp; - double ms = Math.Abs(cp = Math.Cos(phi)) > Epsilon ? msfn(sp, cp) / sp : 0.0; - /*lam =*/ - delta_lam *= sp; - x = ms * Math.Sin( /*lam*/delta_lam); - y = (mlfn(phi, sp, cp) - _ml0) + ms * (1.0 - Math.Cos( /*lam*/delta_lam)); - } + /// + /// Initializes a new instance of the class. + /// + /// The parameter values in standard units. + public PolyconicProjection(IEnumerable parameters) + : this(parameters, null) + { + } - lon = scale_factor * _semiMajor * x; - lat = scale_factor * _semiMajor * y; - } + /// + /// Initializes a new instance of the class. + /// + /// The parameter values in standard units. + /// The inverse projection instance, or for a forward projection. + private PolyconicProjection(IEnumerable parameters, PolyconicProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Polyconic"; + + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + this.ml0 = this.Mlfn(this.latOrigin, sinLatitudeOrigin, cosLatitudeOrigin); + this.reciprocSemiMajorTimesScaleFactor = 1 / (this.semiMajor * this.scaleFactor); + } - protected override void MetersToRadians(ref double x, ref double y) + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lam = lon; + double phi = lat; + + double delta_lam = Adjust_lon(lam - this.centralMeridian); + + double x = delta_lam; // lam; + double y = -this.ml0; + if (Math.Abs(phi) > Eps10) { - x *= _reciprocSemiMajorTimesScaleFactor; - y *= _reciprocSemiMajorTimesScaleFactor; + double sp = Math.Sin(phi); + double cp = Math.Cos(phi); + double ms = Math.Abs(cp) > Eps10 ? Msfnz(this.e, sp, cp) / sp : 0.0; + + // lam = + delta_lam *= sp; + x = ms * Math.Sin(delta_lam); + y = (this.Mlfn(phi, sp, cp) - this.ml0) + (ms * (1.0 - Math.Cos(delta_lam))); + } - double lam, phi; + lon = this.scaleFactor * this.semiMajor * x; + lat = this.scaleFactor * this.semiMajor * y; + } - y += _ml0; - if (Math.Abs(y) <= Epsilon) - { - lam = x; - phi = 0.0; - } - else + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocSemiMajorTimesScaleFactor; + y *= this.reciprocSemiMajorTimesScaleFactor; + + y += this.ml0; + double lam = x; + double phi = 0.0; + if (Math.Abs(y) <= Eps10) + { + } + else + { + double r = (y * y) + (x * x); + phi = y; + int iter = 0; + for (; iter <= MaximumIterations; iter++) { - double r = y * y + x * x; - phi = y; - int iter = 0; - for (; iter <= MaximumIterations; iter++) + double sp = Math.Sin(phi); + double cp = Math.Cos(phi); + if (Math.Abs(cp) < IterationTolerance) { - double sp = Math.Sin(phi); - double cp = Math.Cos(phi); - if (Math.Abs(cp) < IterationTolerance) - throw new Exception("No Convergence"); - - double s2ph = sp * cp; - double mlp = Math.Sqrt(1.0 - _es * sp * sp); - double c = sp * mlp / cp; - double ml = mlfn(phi, sp, cp); - double mlb = ml * ml + r; - mlp = (1.0 - _es) / (mlp * mlp * mlp); - double dPhi = (ml + ml + c * mlb - 2.0 * y * (c * ml + 1.0)) / ( - _es * s2ph * (mlb - 2.0 * y * ml) / c + - 2.0 * (y - ml) * (c * mlp - 1.0 / s2ph) - mlp - mlp); - if (Math.Abs(dPhi) <= IterationTolerance) - break; - - phi += dPhi; + throw new InvalidOperationException("No Convergence"); } - if (iter > MaximumIterations) - throw new Exception("No Convergence"); - double c2 = Math.Sin(phi); - lam = Math.Asin(x * Math.Tan(phi) * Math.Sqrt(1.0 - _es * c2 * c2)) / Math.Sin(phi); + double s2ph = sp * cp; + double mlp = Math.Sqrt(1.0 - (this.es * sp * sp)); + double c = sp * mlp / cp; + double ml = this.Mlfn(phi, sp, cp); + double mlb = (ml * ml) + r; + mlp = (1.0 - this.es) / (mlp * mlp * mlp); + double dPhi = (ml + ml + (c * mlb) - (2.0 * y * ((c * ml) + 1.0))) / ( + (this.es * s2ph * (mlb - (2.0 * y * ml)) / c) + + (2.0 * (y - ml) * ((c * mlp) - (1.0 / s2ph))) - mlp - mlp); + if (Math.Abs(dPhi) <= IterationTolerance) + { + break; + } + + phi += dPhi; } - x = adjust_lon(lam + central_meridian); - y = phi; - } + if (iter > MaximumIterations) + { + throw new InvalidOperationException("No Convergence"); + } - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new PolyconicProjection(_Parameters.ToProjectionParameter(), this); - return _inverse; + double c2 = Math.Sin(phi); + lam = Math.Asin(x * Math.Tan(phi) * Math.Sqrt(1.0 - (this.es * c2 * c2))) / Math.Sin(phi); } - #region Private helpers - /// - /// Computes function f(s,c,e²) = c/sqrt(1 - s²*e²) needed for the true scale - /// latitude (Snyder 14-15), where s and c are the sine and cosine of - /// the true scale latitude, and is the eccentricity squared. - /// - double msfn(double s, double c) - { - return c / Math.Sqrt(1.0 - (s * s) * _es); - } + x = Adjust_lon(lam + this.centralMeridian); + y = phi; + } - #endregion + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= new PolyconicProjection(this.Parameters.ToProjectionParameter(), this); + return this.inverse; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/ProjectionConstants.cs b/src/ProjNet/CoordinateSystems/Projections/ProjectionConstants.cs new file mode 100644 index 00000000..90dc3eed --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ProjectionConstants.cs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +/// +/// Shared numeric constants reused across projection implementations. +/// +internal static class ProjectionConstants +{ + /// + /// One third. + /// + internal const double OneThird = 0.33333333333333333333d; + + /// + /// Two thirds. + /// + internal const double TwoThirds = 0.66666666666666666666d; + + /// + /// One sixth. + /// + internal const double OneSixth = 0.16666666666666666666d; + + /// + /// One plus a 1e-7 tolerance margin. + /// + internal const double OnePlusEps7 = 1.0000001d; + + /// + /// One plus a 1e-6 tolerance margin. + /// + internal const double OnePlusEps6 = 1.000001d; + + /// + /// Shared 1e-12 tolerance. + /// + internal const double Tolerance1E12 = 1e-12d; + + /// + /// Square root of 2. + /// + internal const double Sqrt2 = 1.41421356237309504880d; + + /// + /// Reciprocal square root of 2. + /// + internal const double OneOverSqrt2 = 0.70710678118654752440d; + + /// + /// Shared 1e-18 tolerance used for Jacobian and determinant singularity checks. + /// + internal const double JacobianTolerance = 1e-18d; + + /// + /// Clamps to the inclusive range [, ]. + /// + /// Input value. + /// Inclusive lower bound. + /// Inclusive upper bound. + /// The clamped value. + internal static double Clamp(double value, double minimum, double maximum) + { + return value < minimum ? minimum : value > maximum ? maximum : value; + } + + /// + /// Clamps to the inclusive range [-1, 1]. + /// + /// Input value. + /// The clamped value. + internal static double ClampToUnit(double value) + { + if (value > 1d) + { + return 1d; + } + + return value < -1d ? -1d : value; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ProjectionParameterSet.cs b/src/ProjNet/CoordinateSystems/Projections/ProjectionParameterSet.cs index db35066c..2f702edd 100644 --- a/src/ProjNet/CoordinateSystems/Projections/ProjectionParameterSet.cs +++ b/src/ProjNet/CoordinateSystems/Projections/ProjectionParameterSet.cs @@ -1,169 +1,229 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; +using System.Globalization; using System.Text; -namespace ProjNet.CoordinateSystems.Projections +/// +/// A named collection of projection parameters, supporting case-insensitive key lookup and insertion-order enumeration. +/// +public sealed class ProjectionParameterSet : Dictionary, IEquatable { + private readonly Dictionary originalNames = []; + private readonly Dictionary originalIndex = []; + /// - /// A set of projection parameters + /// Initializes a new instance of the class from an enumeration of projection parameters. /// - // TODO: KeyedCollection - [Serializable] - public class ProjectionParameterSet : Dictionary, IEquatable + /// The projection parameters to populate the set. + /// Thrown when is . + public ProjectionParameterSet(IEnumerable parameters) { - private readonly Dictionary _originalNames = new Dictionary(); - private readonly Dictionary _originalIndex = new Dictionary(); - /// - /// Needed for serialzation - /// - public ProjectionParameterSet(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - :base(info, context) - {} - - /// - /// Creates an instance of this class - /// - /// An enumeration of parameters - public ProjectionParameterSet(IEnumerable parameters) + parameters = ArgumentGuard.ThrowIfNull(parameters, nameof(parameters)); + + foreach (ProjectionParameter pp in parameters) { - foreach (var pp in parameters) - { - string key = pp.Name.ToLowerInvariant(); - _originalNames.Add(key, pp.Name); - _originalIndex.Add(_originalIndex.Count, key); - Add(key, pp.Value); - } + string key = pp.Name.ToLowerInvariant(); + this.originalNames.Add(key, pp.Name); + this.originalIndex.Add(this.originalIndex.Count, key); + this.Add(key, pp.Value); } - - /// - /// Function to create an enumeration of s of the content of this projection parameter set. - /// - /// An enumeration of s - public IEnumerable ToProjectionParameter() + } + + /// + /// Returns the contents of this set as an enumerable sequence of instances. + /// + /// An enumeration of s in insertion order. + public IEnumerable ToProjectionParameter() + { + foreach (KeyValuePair oi in this.originalIndex) { - foreach (var oi in _originalIndex) - yield return new ProjectionParameter(_originalNames[oi.Value], this[oi.Value]); + yield return new ProjectionParameter(this.originalNames[oi.Value], this[oi.Value]); } + } + + /// + /// Retrieves the value of a mandatory projection parameter. + /// + /// The primary name of the parameter. + /// Optional alternate names to search when is not found. + /// The value of the parameter. + /// Thrown when or is . + /// Thrown when and all are absent from the set. + public double GetParameterValue(string parameterName, params string[] alternateNames) + { + parameterName = ArgumentGuard.ThrowIfNull(parameterName, nameof(parameterName)); + alternateNames = ArgumentGuard.ThrowIfNull(alternateNames, nameof(alternateNames)); - /// - /// Function to get the value of a mandatory projection parameter - /// - /// The value of the parameter - /// The name of the parameter - /// Possible alternate names for - /// Thrown if or any of is not defined. - public double GetParameterValue(string parameterName, params string[] alternateNames) + string name = parameterName.ToLowerInvariant(); + if (!this.ContainsKey(name)) { - string name = parameterName.ToLowerInvariant(); - if (!ContainsKey(name)) + foreach (string alternateName in alternateNames) { - foreach (string alternateName in alternateNames) + if (this.TryGetValue(alternateName.ToLowerInvariant(), out double res)) { - double res; - if (TryGetValue(alternateName.ToLowerInvariant(), out res)) - return res; + return res; } + } - var sb = new StringBuilder(); - sb.AppendFormat("Missing projection parameter '{0}'", parameterName); - if (alternateNames.Length > 0) + var sb = new StringBuilder(); + sb.AppendFormat(CultureInfo.InvariantCulture, "Missing projection parameter '{0}'", parameterName); + if (alternateNames.Length > 0) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "\nIt is also not defined as '{0}'", alternateNames[0]); + for (int i = 1; i < alternateNames.Length; i++) { - sb.AppendFormat("\nIt is also not defined as '{0}'", alternateNames[0]); - for (int i = 1; i < alternateNames.Length; i++) - sb.AppendFormat(", '{0}'", alternateNames[i]); - sb.Append("."); + sb.AppendFormat(CultureInfo.InvariantCulture, ", '{0}'", alternateNames[i]); } - throw new ArgumentException(sb.ToString(), "parameterName"); + sb.Append('.'); } - return this[name]; + + ArgumentGuard.ThrowArgument(sb.ToString(), nameof(parameterName)); } - /// - /// Method to check if all mandatory projection parameters are passed - /// - public double GetOptionalParameterValue(string name, double value, params string[] alternateNames) + return this[name]; + } + + /// + /// Retrieves the value of an optional projection parameter, returning a default value when the parameter is absent. + /// + /// The primary name of the parameter. + /// The default value to return when the parameter is absent. + /// Optional alternate names to search when is not found. + /// + /// The stored parameter value, or if neither nor any of + /// is present. + /// + public double GetOptionalParameterValue(string name, double value, params string[] alternateNames) + { + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + alternateNames = ArgumentGuard.ThrowIfNull(alternateNames, nameof(alternateNames)); + + name = name.ToLowerInvariant(); + if (!this.ContainsKey(name)) { - name = name.ToLowerInvariant(); - if (!ContainsKey(name)) + foreach (string alternateName in alternateNames) { - foreach (string alternateName in alternateNames) + if (this.TryGetValue(alternateName.ToLowerInvariant(), out double res)) { - double res; - if (TryGetValue(alternateName.ToLowerInvariant(), out res)) - return res; + return res; } - //Add(name, value); - return value; } - return this[name]; + + // Add(name, value); + return value; } - /// - /// Function to find a parameter based on its name - /// - /// The name of the parameter - /// The parameter if present, otherwise null - public ProjectionParameter Find(string name) + return this[name]; + } + + /// + /// Finds the parameter with the given name. + /// + /// The name of the parameter. + /// The parameter if present; otherwise . + public ProjectionParameter? Find(string name) + { + name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + + name = name.ToLowerInvariant(); + return this.ContainsKey(name) ? new ProjectionParameter(this.originalNames[name], this[name]) : null; + } + + /// + /// Returns the parameter at the specified index. + /// + /// The zero-based index of the parameter. + /// The at . + /// Thrown when is outside the valid parameter range. + public ProjectionParameter GetAtIndex(int index) + { + if (index < 0 || index >= this.Count) { - name = name.ToLowerInvariant(); - return ContainsKey(name) ? new ProjectionParameter(_originalNames[name], this[name]) : null; + ArgumentGuard.ThrowArgumentOutOfRange(nameof(index)); } - /// - /// Function to get the parameter at the given index - /// - /// The index - /// The parameter - /// - public ProjectionParameter GetAtIndex(int index) + string name = this.originalIndex[index]; + return new ProjectionParameter(this.originalNames[name], this[name]); + } + + /// + /// Determines whether this parameter set is equal to . + /// + /// The parameter set to compare with. + /// if both sets contain the same parameter names and values; otherwise . + public bool Equals(ProjectionParameterSet? other) + { + if (other is null) { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException(nameof(index)); + return false; + } - string name = _originalIndex[index]; - return new ProjectionParameter(_originalNames[name], this[name]); + if (other.Count != this.Count) + { + return false; } - /// - /// Checks this projection parameter set with - - /// - /// The other projection parameter set. - /// true if both sets are equal. - public bool Equals(ProjectionParameterSet other) + foreach (KeyValuePair kvp in this) { - if (other == null) + if (!other.ContainsKey(kvp.Key)) + { return false; + } - if (other.Count != Count) + double otherValue = other.GetParameterValue(kvp.Key); + if (otherValue != kvp.Value) + { return false; + } + } - foreach (var kvp in this) - { - if (!other.ContainsKey(kvp.Key)) - return false; + return true; + } - double otherValue = other.GetParameterValue(kvp.Key); - if (otherValue != kvp.Value) - return false; - } - return true; + /// + public override bool Equals(object? obj) + { + return obj is ProjectionParameterSet other && this.Equals(other); + } + + /// + public override int GetHashCode() + { + HashCode hashCode = default; + foreach (KeyValuePair kvp in this) + { + hashCode.Add(kvp.Key, StringComparer.Ordinal); + hashCode.Add(kvp.Value); } - internal void SetParameterValue(string name, double value) + return hashCode.ToHashCode(); + } + + /// + /// Sets or adds a projection parameter value using case-insensitive key matching. + /// + /// Parameter name. + /// Parameter value. + internal void SetParameterValue(string name, double value) + { + string key = name.ToLowerInvariant(); + if (!this.ContainsKey(key)) { - string key = name.ToLowerInvariant(); - if (!ContainsKey(key)) - { - _originalIndex.Add(_originalIndex.Count, key); - _originalNames.Add(key, name); - Add(key, value); - } - else - { - Remove(key); - Add(key, value); - } + this.originalIndex.Add(this.originalIndex.Count, key); + this.originalNames.Add(key, name); + this.Add(key, value); + } + else + { + this.Remove(key); + this.Add(key, value); } } } diff --git a/src/ProjNet/CoordinateSystems/Projections/ProjectionThrowHelper.cs b/src/ProjNet/CoordinateSystems/Projections/ProjectionThrowHelper.cs new file mode 100644 index 00000000..d9c03937 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ProjectionThrowHelper.cs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Diagnostics.CodeAnalysis; + +/// +/// Centralizes cold projection exception paths to keep hot transform methods smaller. +/// +internal static class ProjectionThrowHelper +{ + /// + /// Throws when a projected coordinate lies outside the supported projection domain. + /// + [DoesNotReturn] + internal static void ThrowOutsideProjectionDomain() + { + throw new InvalidOperationException("Input data outside projection domain."); + } + + /// + /// Throws when a projected coordinate lies outside the supported projection domain. + /// + /// The return type required by the calling expression. + /// This method never returns. + [DoesNotReturn] + internal static T ThrowOutsideProjectionDomain() + { + throw new InvalidOperationException("Input data outside projection domain."); + } + + /// + /// Throws an with the supplied message. + /// + /// Failure description. + [DoesNotReturn] + internal static void ThrowInvalidOperation(string message) + { + throw new InvalidOperationException(message); + } + + /// + /// Throws an with the supplied message. + /// + /// The return type required by the calling expression. + /// Failure description. + /// This method never returns. + [DoesNotReturn] + internal static T ThrowInvalidOperation(string message) + { + throw new InvalidOperationException(message); + } + + /// + /// Throws a with the supplied message. + /// + /// Failure description. + [DoesNotReturn] + internal static void ThrowNotSupported(string message) + { + throw new NotSupportedException(message); + } + + /// + /// Throws a with the supplied message. + /// + /// The return type required by the calling expression. + /// Failure description. + /// This method never returns. + [DoesNotReturn] + internal static T ThrowNotSupported(string message) + { + throw new NotSupportedException(message); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.Factories.cs b/src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.Factories.cs new file mode 100644 index 00000000..3526561b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.Factories.cs @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Maintains a registry of supported projections keyed by their WKT or PROJ aliases. +/// +public partial class ProjectionsRegistry +{ + private static readonly Dictionary, MathTransform>> BuiltInFactories = + new() + { + [typeof(AdamsHemisphereInSquareProjection)] = static parameters => new AdamsHemisphereInSquareProjection(parameters), + [typeof(AdamsWorldInSquare1Projection)] = static parameters => new AdamsWorldInSquare1Projection(parameters), + [typeof(AdamsWorldInSquare2Projection)] = static parameters => new AdamsWorldInSquare2Projection(parameters), + [typeof(AiroceanProjection)] = static parameters => new AiroceanProjection(parameters), + [typeof(AiryProjection)] = static parameters => new AiryProjection(parameters), + [typeof(AitoffProjection)] = static parameters => new AitoffProjection(parameters), + [typeof(AlbersProjection)] = static parameters => new AlbersProjection(parameters), + [typeof(ApianProjection)] = static parameters => new ApianProjection(parameters), + [typeof(AugustProjection)] = static parameters => new AugustProjection(parameters), + [typeof(AzimuthalEquidistantProjection)] = static parameters => new AzimuthalEquidistantProjection(parameters), + [typeof(BaconProjection)] = static parameters => new BaconProjection(parameters), + [typeof(Bertin1953Projection)] = static parameters => new Bertin1953Projection(parameters), + [typeof(BipolarConicProjection)] = static parameters => new BipolarConicProjection(parameters), + [typeof(BoggsProjection)] = static parameters => new BoggsProjection(parameters), + [typeof(BonneProjection)] = static parameters => new BonneProjection(parameters), + [typeof(CalCoFiProjection)] = static parameters => new CalCoFiProjection(parameters), + [typeof(CassiniSoldnerProjection)] = static parameters => new CassiniSoldnerProjection(parameters), + [typeof(CentralConicProjection)] = static parameters => new CentralConicProjection(parameters), + [typeof(CentralCylindricalProjection)] = static parameters => new CentralCylindricalProjection(parameters), + [typeof(ChamberlinTrimetricProjection)] = static parameters => new ChamberlinTrimetricProjection(parameters), + [typeof(CollignonProjection)] = static parameters => new CollignonProjection(parameters), + [typeof(ColombiaUrbanProjection)] = static parameters => new ColombiaUrbanProjection(parameters), + [typeof(CompactMillerProjection)] = static parameters => new CompactMillerProjection(parameters), + [typeof(CrasterProjection)] = static parameters => new CrasterProjection(parameters), + [typeof(CylindricalEqualAreaProjection)] = static parameters => new CylindricalEqualAreaProjection(parameters), + [typeof(DenoyerProjection)] = static parameters => new DenoyerProjection(parameters), + [typeof(Eckert1Projection)] = static parameters => new Eckert1Projection(parameters), + [typeof(Eckert2Projection)] = static parameters => new Eckert2Projection(parameters), + [typeof(Eckert3Projection)] = static parameters => new Eckert3Projection(parameters), + [typeof(Eckert4Projection)] = static parameters => new Eckert4Projection(parameters), + [typeof(Eckert5Projection)] = static parameters => new Eckert5Projection(parameters), + [typeof(Eckert6Projection)] = static parameters => new Eckert6Projection(parameters), + [typeof(EqualEarthProjection)] = static parameters => new EqualEarthProjection(parameters), + [typeof(EquidistantConicProjection)] = static parameters => new EquidistantConicProjection(parameters), + [typeof(EquidistantCylindricalProjection)] = static parameters => new EquidistantCylindricalProjection(parameters), + [typeof(EulerProjection)] = static parameters => new EulerProjection(parameters), + [typeof(ExtendedTransverseMercator)] = static parameters => new ExtendedTransverseMercator(parameters), + [typeof(FaheyProjection)] = static parameters => new FaheyProjection(parameters), + [typeof(FoucautProjection)] = static parameters => new FoucautProjection(parameters), + [typeof(FoucautSinusoidalProjection)] = static parameters => new FoucautSinusoidalProjection(parameters), + [typeof(GallProjection)] = static parameters => new GallProjection(parameters), + [typeof(GaussSchreiberTransverseMercatorProjection)] = static parameters => new GaussSchreiberTransverseMercatorProjection(parameters), + [typeof(GeneralSinusoidalProjection)] = static parameters => new GeneralSinusoidalProjection(parameters), + [typeof(GeostationarySatelliteProjection)] = static parameters => new GeostationarySatelliteProjection(parameters), + [typeof(Ginsburg8Projection)] = static parameters => new Ginsburg8Projection(parameters), + [typeof(GnomonicProjection)] = static parameters => new GnomonicProjection(parameters), + [typeof(GoodeProjection)] = static parameters => new GoodeProjection(parameters), + [typeof(GuyouProjection)] = static parameters => new GuyouProjection(parameters), + [typeof(HammerProjection)] = static parameters => new HammerProjection(parameters), + [typeof(HatanoProjection)] = static parameters => new HatanoProjection(parameters), + [typeof(HealpixProjection)] = static parameters => new HealpixProjection(parameters), + [typeof(HotineObliqueMercatorProjection)] = static parameters => new HotineObliqueMercatorProjection(parameters), + [typeof(IghProjection)] = static parameters => new IghProjection(parameters), + [typeof(InternationalMapWorldPolyconicProjection)] = static parameters => new InternationalMapWorldPolyconicProjection(parameters), + [typeof(InterruptedGoodeHomolosineOceanicProjection)] = static parameters => new InterruptedGoodeHomolosineOceanicProjection(parameters), + [typeof(InterruptedMollweideOceanicProjection)] = static parameters => new InterruptedMollweideOceanicProjection(parameters), + [typeof(InterruptedMollweideProjection)] = static parameters => new InterruptedMollweideProjection(parameters), + [typeof(IseaProjection)] = static parameters => new IseaProjection(parameters), + [typeof(Kavrayskiy5Projection)] = static parameters => new Kavrayskiy5Projection(parameters), + [typeof(Kavrayskiy7Projection)] = static parameters => new Kavrayskiy7Projection(parameters), + [typeof(KrovakProjection)] = static parameters => new KrovakProjection(parameters), + [typeof(LabordeProjection)] = static parameters => new LabordeProjection(parameters), + [typeof(LagrangeProjection)] = static parameters => new LagrangeProjection(parameters), + [typeof(LambertAzimuthalEqualAreaProjection)] = static parameters => new LambertAzimuthalEqualAreaProjection(parameters), + [typeof(LambertConformalConic2SP)] = static parameters => new LambertConformalConic2SP(parameters), + [typeof(LambertConformalConicAlternativeProjection)] = static parameters => new LambertConformalConicAlternativeProjection(parameters), + [typeof(LambertEqualAreaConicProjection)] = static parameters => new LambertEqualAreaConicProjection(parameters), + [typeof(LarriveeProjection)] = static parameters => new LarriveeProjection(parameters), + [typeof(LaskowskiProjection)] = static parameters => new LaskowskiProjection(parameters), + [typeof(LatLongProjection)] = static parameters => new LatLongProjection(parameters), + [typeof(LeeOblatedStereographicProjection)] = static parameters => new LeeOblatedStereographicProjection(parameters), + [typeof(LoximuthalProjection)] = static parameters => new LoximuthalProjection(parameters), + [typeof(McBrydeThomasFlatPolarParabolicProjection)] = static parameters => new McBrydeThomasFlatPolarParabolicProjection(parameters), + [typeof(McBrydeThomasFlatPolarQuarticProjection)] = static parameters => new McBrydeThomasFlatPolarQuarticProjection(parameters), + [typeof(McBrydeThomasFlatPolarSineProjection)] = static parameters => new McBrydeThomasFlatPolarSineProjection(parameters), + [typeof(McBrydeThomasFlatPolarSinusoidalProjection)] = static parameters => new McBrydeThomasFlatPolarSinusoidalProjection(parameters), + [typeof(McBrydeThomasFlatPoleSineProjection)] = static parameters => new McBrydeThomasFlatPoleSineProjection(parameters), + [typeof(Mercator)] = static parameters => new Mercator(parameters), + [typeof(MercatorAuxiliarySphere)] = static parameters => new MercatorAuxiliarySphere(parameters), + [typeof(MillerCylindricalProjection)] = static parameters => new MillerCylindricalProjection(parameters), + [typeof(MillerOblatedStereographicProjection)] = static parameters => new MillerOblatedStereographicProjection(parameters), + [typeof(ModifiedKrovakProjection)] = static parameters => new ModifiedKrovakProjection(parameters), + [typeof(ModifiedStereographic48USProjection)] = static parameters => new ModifiedStereographic48USProjection(parameters), + [typeof(ModifiedStereographic50USProjection)] = static parameters => new ModifiedStereographic50USProjection(parameters), + [typeof(ModifiedStereographicAlaskaProjection)] = static parameters => new ModifiedStereographicAlaskaProjection(parameters), + [typeof(MollweideProjection)] = static parameters => new MollweideProjection(parameters), + [typeof(Murdoch1Projection)] = static parameters => new Murdoch1Projection(parameters), + [typeof(Murdoch2Projection)] = static parameters => new Murdoch2Projection(parameters), + [typeof(Murdoch3Projection)] = static parameters => new Murdoch3Projection(parameters), + [typeof(NaturalEarth2Projection)] = static parameters => new NaturalEarth2Projection(parameters), + [typeof(NaturalEarthProjection)] = static parameters => new NaturalEarthProjection(parameters), + [typeof(NearSidedPerspectiveProjection)] = static parameters => new NearSidedPerspectiveProjection(parameters), + [typeof(NellHammerProjection)] = static parameters => new NellHammerProjection(parameters), + [typeof(NellProjection)] = static parameters => new NellProjection(parameters), + [typeof(NewZealandMapGridProjection)] = static parameters => new NewZealandMapGridProjection(parameters), + [typeof(NicolosiProjection)] = static parameters => new NicolosiProjection(parameters), + [typeof(OblatedEqualAreaProjection)] = static parameters => new OblatedEqualAreaProjection(parameters), + [typeof(ObliqueCylindricalEqualAreaProjection)] = static parameters => new ObliqueCylindricalEqualAreaProjection(parameters), + [typeof(ObliqueMercatorProjection)] = static parameters => new ObliqueMercatorProjection(parameters), + [typeof(ObliqueStereographicProjection)] = static parameters => new ObliqueStereographicProjection(parameters), + [typeof(OrteliusProjection)] = static parameters => new OrteliusProjection(parameters), + [typeof(OrthographicProjection)] = static parameters => new OrthographicProjection(parameters), + [typeof(PattersonProjection)] = static parameters => new PattersonProjection(parameters), + [typeof(PconicProjection)] = static parameters => new PconicProjection(parameters), + [typeof(PeirceQuincuncialProjection)] = static parameters => new PeirceQuincuncialProjection(parameters), + [typeof(PolarStereographicProjection)] = static parameters => new PolarStereographicProjection(parameters), + [typeof(PolyconicProjection)] = static parameters => new PolyconicProjection(parameters), + [typeof(PseudoMercator)] = static parameters => new PseudoMercator(parameters), + [typeof(PutninsP1Projection)] = static parameters => new PutninsP1Projection(parameters), + [typeof(PutninsP2Projection)] = static parameters => new PutninsP2Projection(parameters), + [typeof(PutninsP3PrimeProjection)] = static parameters => new PutninsP3PrimeProjection(parameters), + [typeof(PutninsP3Projection)] = static parameters => new PutninsP3Projection(parameters), + [typeof(PutninsP4PProjection)] = static parameters => new PutninsP4PProjection(parameters), + [typeof(PutninsP5PrimeProjection)] = static parameters => new PutninsP5PrimeProjection(parameters), + [typeof(PutninsP5Projection)] = static parameters => new PutninsP5Projection(parameters), + [typeof(PutninsP6PrimeProjection)] = static parameters => new PutninsP6PrimeProjection(parameters), + [typeof(PutninsP6Projection)] = static parameters => new PutninsP6Projection(parameters), + [typeof(QuadrilateralizedSphericalCubeProjection)] = static parameters => new QuadrilateralizedSphericalCubeProjection(parameters), + [typeof(QuarticAuthalicProjection)] = static parameters => new QuarticAuthalicProjection(parameters), + [typeof(RectangularPolyconicProjection)] = static parameters => new RectangularPolyconicProjection(parameters), + [typeof(RobinsonProjection)] = static parameters => new RobinsonProjection(parameters), + [typeof(RoussilheStereographicProjection)] = static parameters => new RoussilheStereographicProjection(parameters), + [typeof(S2Projection)] = static parameters => new S2Projection(parameters), + [typeof(StereographicProjection)] = static parameters => new StereographicProjection(parameters), + [typeof(SchMathTransform)] = static parameters => new SchMathTransform(AsProjectionParameterList(parameters)), + [typeof(SinusoidalProjection)] = static parameters => new SinusoidalProjection(parameters), + [typeof(SpaceObliqueMercatorProjection)] = static parameters => new SpaceObliqueMercatorProjection(parameters), + [typeof(SpilhausProjection)] = static parameters => new SpilhausProjection(parameters), + [typeof(SwissObliqueMercatorProjection)] = static parameters => new SwissObliqueMercatorProjection(parameters), + [typeof(TimesProjection)] = static parameters => new TimesProjection(parameters), + [typeof(TissotProjection)] = static parameters => new TissotProjection(parameters), + [typeof(ToblerMercatorProjection)] = static parameters => new ToblerMercatorProjection(parameters), + [typeof(TransverseCentralCylindricalProjection)] = static parameters => new TransverseCentralCylindricalProjection(parameters), + [typeof(TransverseCylindricalEqualAreaProjection)] = static parameters => new TransverseCylindricalEqualAreaProjection(parameters), + [typeof(TransverseMercator)] = static parameters => new TransverseMercator(parameters), + [typeof(TwoPointEquidistantProjection)] = static parameters => new TwoPointEquidistantProjection(parameters), + [typeof(UpsProjection)] = static parameters => new UpsProjection(parameters), + [typeof(Urmaev5Projection)] = static parameters => new Urmaev5Projection(parameters), + [typeof(UrmaevFlatPolarSinusoidalProjection)] = static parameters => new UrmaevFlatPolarSinusoidalProjection(parameters), + [typeof(VanDerGrinten2Projection)] = static parameters => new VanDerGrinten2Projection(parameters), + [typeof(VanDerGrinten3Projection)] = static parameters => new VanDerGrinten3Projection(parameters), + [typeof(VanDerGrinten4Projection)] = static parameters => new VanDerGrinten4Projection(parameters), + [typeof(VanDerGrintenProjection)] = static parameters => new VanDerGrintenProjection(parameters), + [typeof(Vitkovsky1Projection)] = static parameters => new Vitkovsky1Projection(parameters), + [typeof(Wagner1Projection)] = static parameters => new Wagner1Projection(parameters), + [typeof(Wagner2Projection)] = static parameters => new Wagner2Projection(parameters), + [typeof(Wagner3Projection)] = static parameters => new Wagner3Projection(parameters), + [typeof(Wagner4Projection)] = static parameters => new Wagner4Projection(parameters), + [typeof(Wagner5Projection)] = static parameters => new Wagner5Projection(parameters), + [typeof(Wagner6Projection)] = static parameters => new Wagner6Projection(parameters), + [typeof(Wagner7Projection)] = static parameters => new Wagner7Projection(parameters), + [typeof(WerenskioldProjection)] = static parameters => new WerenskioldProjection(parameters), + [typeof(Winkel1Projection)] = static parameters => new Winkel1Projection(parameters), + [typeof(Winkel2Projection)] = static parameters => new Winkel2Projection(parameters), + [typeof(WinkelTripelProjection)] = static parameters => new WinkelTripelProjection(parameters), + }; +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.cs b/src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.cs index 7ca6186f..9c411613 100644 --- a/src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.cs +++ b/src/ProjNet/CoordinateSystems/Projections/ProjectionsRegistry.cs @@ -1,161 +1,682 @@ -using ProjNet.CoordinateSystems.Transformations; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Registry that maps projection names and aliases to their corresponding implementation types. +/// +/// +/// +/// Thread safety: The registry is process-wide and protected by an internal lock for registration, +/// alias lookup, and projection creation. Concurrent callers are safe, but CreateProjection, +/// Register, and RegisterAlias briefly serialize on that shared lock. +/// +/// +public partial class ProjectionsRegistry { + private static readonly Dictionary TypeRegistry = []; + + private static readonly object RegistryLock = new(); + /// - /// Registry class for all known s. + /// Initializes static members of the class. /// - public class ProjectionsRegistry + static ProjectionsRegistry() { - private static readonly Dictionary TypeRegistry = new Dictionary(); - private static readonly Dictionary ConstructorRegistry = new Dictionary(); + Register("mercator", typeof(Mercator)); + Register("merc", typeof(Mercator)); + Register("mercator_1sp", typeof(Mercator)); + Register("mercator_2sp", typeof(Mercator)); + Register("mercator_(variant_a)", typeof(Mercator)); + Register("mercator_(variant_b)", typeof(Mercator)); + Register("mercator_auxiliary_sphere", typeof(MercatorAuxiliarySphere)); + Register("pseudo_mercator", typeof(PseudoMercator)); + Register("popular_visualisation_pseudo_mercator", typeof(PseudoMercator)); + Register("google_mercator", typeof(PseudoMercator)); + Register("web_mercator", typeof(PseudoMercator)); + Register("webmerc", typeof(PseudoMercator)); + Register("miller_cylindrical", typeof(MillerCylindricalProjection)); + Register("miller", typeof(MillerCylindricalProjection)); + Register("mill", typeof(MillerCylindricalProjection)); + Register("equidistant_cylindrical", typeof(EquidistantCylindricalProjection)); + Register("equirectangular", typeof(EquidistantCylindricalProjection)); + Register("plate_carree", typeof(EquidistantCylindricalProjection)); + Register("eqc", typeof(EquidistantCylindricalProjection)); + Register("latlong", typeof(LatLongProjection)); + Register("latlon", typeof(LatLongProjection)); + Register("lonlat", typeof(LatLongProjection)); + Register("longlat", typeof(LatLongProjection)); + Register("calcofi", typeof(CalCoFiProjection)); + Register("cal_coop_ocean_fish_invest_lines_stations", typeof(CalCoFiProjection)); + Register("tobmerc", typeof(ToblerMercatorProjection)); + Register("tobler_mercator", typeof(ToblerMercatorProjection)); + Register("col_urban", typeof(ColombiaUrbanProjection)); + Register("colombia_urban", typeof(ColombiaUrbanProjection)); + Register("transverse_cylindrical_equal_area", typeof(TransverseCylindricalEqualAreaProjection)); + Register("tcea", typeof(TransverseCylindricalEqualAreaProjection)); + Register("cylindrical_equal_area", typeof(CylindricalEqualAreaProjection)); + Register("lambert_cylindrical_equal_area", typeof(CylindricalEqualAreaProjection)); + Register("equal_area_cylindrical", typeof(CylindricalEqualAreaProjection)); + Register("cea", typeof(CylindricalEqualAreaProjection)); + Register("loximuthal", typeof(LoximuthalProjection)); + Register("loxim", typeof(LoximuthalProjection)); + Register("patterson", typeof(PattersonProjection)); + + Register("transverse_mercator", typeof(TransverseMercator)); + Register("tmerc", typeof(ExtendedTransverseMercator), CreateAdaptiveTransverseMercatorFactory("tmerc")); + Register("transverse_mercator_south_oriented", typeof(TransverseMercator)); + Register("gauss_kruger", typeof(ExtendedTransverseMercator), CreateAdaptiveTransverseMercatorFactory("gauss_kruger")); + Register("utm", typeof(ExtendedTransverseMercator), CreateExactTransverseMercatorFactory("utm")); + Register("etmerc", typeof(ExtendedTransverseMercator)); + Register("extended_transverse_mercator", typeof(ExtendedTransverseMercator)); + Register("approx_tmerc", typeof(TransverseMercator)); + Register("approx_transverse_mercator", typeof(TransverseMercator)); + Register("swiss_oblique_mercator", typeof(SwissObliqueMercatorProjection)); + Register("somerc", typeof(SwissObliqueMercatorProjection)); + + Register("albers", typeof(AlbersProjection)); + Register("aea", typeof(AlbersProjection)); + Register("albers_conic_equal_area", typeof(AlbersProjection)); + Register("leac", typeof(LambertEqualAreaConicProjection)); + + Register("krovak", typeof(KrovakProjection)); + Register("mod_krovak", typeof(ModifiedKrovakProjection)); + + Register("polyconic", typeof(PolyconicProjection)); + Register("poly", typeof(PolyconicProjection)); + + Register("lambert_conformal_conic", typeof(LambertConformalConic2SP)); + Register("lcc", typeof(LambertConformalConic2SP)); + Register("lambert_conformal_conic_1sp", typeof(LambertConformalConic2SP)); + Register("lambert_conformal_conic_2sp", typeof(LambertConformalConic2SP)); + Register("lambert_conformal_conic_2sp_belgium", typeof(LambertConformalConic2SP)); + Register("lambert_conic_conformal_(1sp)", typeof(LambertConformalConic2SP)); + Register("lambert_conic_conformal_(2sp)", typeof(LambertConformalConic2SP)); + Register("lambert_tangential_conformal_conic_projection", typeof(LambertConformalConic2SP)); + Register("lcca", typeof(LambertConformalConicAlternativeProjection)); + Register("lambert_conformal_conic_alternative", typeof(LambertConformalConicAlternativeProjection)); + Register("equidistant_conic", typeof(EquidistantConicProjection)); + Register("equidistant_conic_(spherical)", typeof(EquidistantConicProjection)); + Register("eqdc", typeof(EquidistantConicProjection)); + Register("euler", typeof(EulerProjection)); + Register("murd1", typeof(Murdoch1Projection)); + Register("murd2", typeof(Murdoch2Projection)); + Register("murd3", typeof(Murdoch3Projection)); + Register("tissot", typeof(TissotProjection)); + Register("vitk1", typeof(Vitkovsky1Projection)); + Register("imw_p", typeof(InternationalMapWorldPolyconicProjection)); + Register("international_map_of_the_world_polyconic", typeof(InternationalMapWorldPolyconicProjection)); + Register("bonne", typeof(BonneProjection)); + Register("perspective_conic", typeof(PconicProjection)); + Register("pconic", typeof(PconicProjection)); + Register("ccon", typeof(CentralConicProjection)); + Register("central_conic", typeof(CentralConicProjection)); + + Register("lambert_azimuthal_equal_area", typeof(LambertAzimuthalEqualAreaProjection)); + Register("laea", typeof(LambertAzimuthalEqualAreaProjection)); + + Register("cass", typeof(CassiniSoldnerProjection)); + Register("cassini_soldner", typeof(CassiniSoldnerProjection)); + Register("omerc", typeof(HotineObliqueMercatorProjection)); + Register("hotine_oblique_mercator", typeof(HotineObliqueMercatorProjection)); + Register("hotine_oblique_mercator_azimuth_center", typeof(HotineObliqueMercatorProjection)); + Register("oblique_mercator", typeof(ObliqueMercatorProjection)); + Register("sterea", typeof(ObliqueStereographicProjection)); + Register("oblique_stereographic", typeof(ObliqueStereographicProjection)); + Register("ortho", typeof(OrthographicProjection)); + Register("orthographic", typeof(OrthographicProjection)); + Register("ocea", typeof(ObliqueCylindricalEqualAreaProjection)); + Register("oblique_cylindrical_equal_area", typeof(ObliqueCylindricalEqualAreaProjection)); + Register("oea", typeof(OblatedEqualAreaProjection)); + Register("oblated_equal_area", typeof(OblatedEqualAreaProjection)); + Register("near_sided_perspective", typeof(NearSidedPerspectiveProjection)); + Register("nsper", typeof(NearSidedPerspectiveProjection)); + Register("tilted_perspective", typeof(NearSidedPerspectiveProjection)); + Register("tpers", typeof(NearSidedPerspectiveProjection)); + Register("laborde", typeof(LabordeProjection)); + Register("labrd", typeof(LabordeProjection)); + Register("gauss_schreiber_transverse_mercator", typeof(GaussSchreiberTransverseMercatorProjection)); + Register("gauss_laborde_reunion", typeof(GaussSchreiberTransverseMercatorProjection)); + Register("gstmerc", typeof(GaussSchreiberTransverseMercatorProjection)); + Register("geostationary_satellite", typeof(GeostationarySatelliteProjection)); + Register("geos", typeof(GeostationarySatelliteProjection)); + Register("new_zealand_map_grid", typeof(NewZealandMapGridProjection)); + Register("nzmg", typeof(NewZealandMapGridProjection)); + Register("stere", typeof(StereographicProjection)); + Register("polar_stereographic", typeof(PolarStereographicProjection)); + Register("ups", typeof(UpsProjection)); + + Register("equal_earth", typeof(EqualEarthProjection)); + Register("eqearth", typeof(EqualEarthProjection)); + Register("eck1", typeof(Eckert1Projection)); + Register("eckert_i", typeof(Eckert1Projection)); + Register("eck2", typeof(Eckert2Projection)); + Register("eckert_ii", typeof(Eckert2Projection)); + Register("eck3", typeof(Eckert3Projection)); + Register("eckert_iii", typeof(Eckert3Projection)); + Register("eck4", typeof(Eckert4Projection)); + Register("eckert_iv", typeof(Eckert4Projection)); + Register("eck5", typeof(Eckert5Projection)); + Register("eckert_v", typeof(Eckert5Projection)); + Register("putp2", typeof(PutninsP2Projection)); + Register("putnins_p2", typeof(PutninsP2Projection)); + Register("putp1", typeof(PutninsP1Projection)); + Register("putnins_p1", typeof(PutninsP1Projection)); + Register("putp3", typeof(PutninsP3Projection)); + Register("putnins_p3", typeof(PutninsP3Projection)); + Register("putp3p", typeof(PutninsP3PrimeProjection)); + Register("putnins_p3p", typeof(PutninsP3PrimeProjection)); + Register("putp4p", typeof(PutninsP4PProjection)); + Register("putnins_p4p", typeof(PutninsP4PProjection)); + Register("weren", typeof(WerenskioldProjection)); + Register("werenskiold_i", typeof(WerenskioldProjection)); + Register("putp5", typeof(PutninsP5Projection)); + Register("putnins_p5", typeof(PutninsP5Projection)); + Register("putp5p", typeof(PutninsP5PrimeProjection)); + Register("putnins_p5p", typeof(PutninsP5PrimeProjection)); + Register("putp6", typeof(PutninsP6Projection)); + Register("putnins_p6", typeof(PutninsP6Projection)); + Register("putp6p", typeof(PutninsP6PrimeProjection)); + Register("putnins_p6p", typeof(PutninsP6PrimeProjection)); + Register("kav7", typeof(Kavrayskiy7Projection)); + Register("kavrayskiy_vii", typeof(Kavrayskiy7Projection)); + Register("wag2", typeof(Wagner2Projection)); + Register("wagner_ii", typeof(Wagner2Projection)); + Register("wag3", typeof(Wagner3Projection)); + Register("wagner_iii", typeof(Wagner3Projection)); + Register("wag4", typeof(Wagner4Projection)); + Register("wagner_iv", typeof(Wagner4Projection)); + Register("wag5", typeof(Wagner5Projection)); + Register("wagner_v", typeof(Wagner5Projection)); + Register("wag6", typeof(Wagner6Projection)); + Register("wagner_vi", typeof(Wagner6Projection)); + Register("wag1", typeof(Wagner1Projection)); + Register("wagner_i", typeof(Wagner1Projection)); + Register("wag7", typeof(Wagner7Projection)); + Register("wagner_vii", typeof(Wagner7Projection)); + Register("cc", typeof(CentralCylindricalProjection)); + Register("central_cylindrical", typeof(CentralCylindricalProjection)); + Register("gall", typeof(GallProjection)); + Register("gall_stereographic", typeof(GallProjection)); + Register("gn_sinu", typeof(GeneralSinusoidalProjection)); + Register("general_sinusoidal", typeof(GeneralSinusoidalProjection)); + Register("eck6", typeof(Eckert6Projection)); + Register("eckert_vi", typeof(Eckert6Projection)); + Register("kav5", typeof(Kavrayskiy5Projection)); + Register("kavrayskiy_v", typeof(Kavrayskiy5Projection)); + Register("qua_aut", typeof(QuarticAuthalicProjection)); + Register("quartic_authalic", typeof(QuarticAuthalicProjection)); + Register("fouc", typeof(FoucautProjection)); + Register("foucaut", typeof(FoucautProjection)); + Register("mbt_s", typeof(McBrydeThomasFlatPolarSineProjection)); + Register("mcbryde_thomas_flat_polar_sine", typeof(McBrydeThomasFlatPolarSineProjection)); + Register("mbtfps", typeof(McBrydeThomasFlatPolarSinusoidalProjection)); + Register("mcbryde_thomas_flat_polar_sinusoidal", typeof(McBrydeThomasFlatPolarSinusoidalProjection)); + Register("mbtfpp", typeof(McBrydeThomasFlatPolarParabolicProjection)); + Register("mbt_fpp", typeof(McBrydeThomasFlatPolarParabolicProjection)); + Register("mcbryde_thomas_flat_polar_parabolic", typeof(McBrydeThomasFlatPolarParabolicProjection)); + Register("mbtfpq", typeof(McBrydeThomasFlatPolarQuarticProjection)); + Register("mcbryde_thomas_flat_polar_quartic", typeof(McBrydeThomasFlatPolarQuarticProjection)); + Register("mbt_fps", typeof(McBrydeThomasFlatPoleSineProjection)); + Register("mcbryde_thomas_flat_pole_sine", typeof(McBrydeThomasFlatPoleSineProjection)); + Register("crast", typeof(CrasterProjection)); + Register("craster_parabolic", typeof(CrasterProjection)); + Register("fahey", typeof(FaheyProjection)); + Register("collg", typeof(CollignonProjection)); + Register("collignon", typeof(CollignonProjection)); + Register("boggs", typeof(BoggsProjection)); + Register("boggs_eumorphic", typeof(BoggsProjection)); + Register("airy", typeof(AiryProjection)); + Register("bipc", typeof(BipolarConicProjection)); + Register("bipolar_conic", typeof(BipolarConicProjection)); + Register("chamb", typeof(ChamberlinTrimetricProjection)); + Register("chamberlin_trimetric", typeof(ChamberlinTrimetricProjection)); + Register("hatano", typeof(HatanoProjection)); + Register("hatano_asymmetrical_equal_area", typeof(HatanoProjection)); + Register("nell", typeof(NellProjection)); + Register("nell_h", typeof(NellHammerProjection)); + Register("nell_hammer", typeof(NellHammerProjection)); + Register("nicol", typeof(NicolosiProjection)); + Register("nicolosi_globular", typeof(NicolosiProjection)); + Register("urm5", typeof(Urmaev5Projection)); + Register("urmaev_v", typeof(Urmaev5Projection)); + Register("urmfps", typeof(UrmaevFlatPolarSinusoidalProjection)); + Register("urmaev_flat_polar_sinusoidal", typeof(UrmaevFlatPolarSinusoidalProjection)); + Register("times", typeof(TimesProjection)); + Register("times_projection", typeof(TimesProjection)); + Register("rpoly", typeof(RectangularPolyconicProjection)); + Register("rectangular_polyconic", typeof(RectangularPolyconicProjection)); + Register("tpeqd", typeof(TwoPointEquidistantProjection)); + Register("two_point_equidistant", typeof(TwoPointEquidistantProjection)); + Register("august", typeof(AugustProjection)); + Register("august_epicycloidal", typeof(AugustProjection)); + Register("bacon", typeof(BaconProjection)); + Register("bacon_globular", typeof(BaconProjection)); + Register("apian", typeof(ApianProjection)); + Register("apian_globular_i", typeof(ApianProjection)); + Register("ortel", typeof(OrteliusProjection)); + Register("ortelius_oval", typeof(OrteliusProjection)); + Register("comill", typeof(CompactMillerProjection)); + Register("compact_miller", typeof(CompactMillerProjection)); + Register("denoy", typeof(DenoyerProjection)); + Register("denoyer_semi_elliptical", typeof(DenoyerProjection)); + Register("fouc_s", typeof(FoucautSinusoidalProjection)); + Register("foucaut_sinusoidal", typeof(FoucautSinusoidalProjection)); + Register("gins8", typeof(Ginsburg8Projection)); + Register("ginsburg_viii", typeof(Ginsburg8Projection)); + Register("lagrng", typeof(LagrangeProjection)); + Register("lagrange", typeof(LagrangeProjection)); + Register("larr", typeof(LarriveeProjection)); + Register("larrivee", typeof(LarriveeProjection)); + Register("lask", typeof(LaskowskiProjection)); + Register("laskowski", typeof(LaskowskiProjection)); + Register("tcc", typeof(TransverseCentralCylindricalProjection)); + Register("transverse_central_cylindrical", typeof(TransverseCentralCylindricalProjection)); + Register("aitoff", typeof(AitoffProjection)); + Register("vandg", typeof(VanDerGrintenProjection)); + Register("vandergrinten", typeof(VanDerGrintenProjection)); + Register("van_der_grinten", typeof(VanDerGrintenProjection)); + Register("van_der_grinten_i", typeof(VanDerGrintenProjection)); + Register("vandg2", typeof(VanDerGrinten2Projection)); + Register("van_der_grinten_ii", typeof(VanDerGrinten2Projection)); + Register("vandg3", typeof(VanDerGrinten3Projection)); + Register("van_der_grinten_iii", typeof(VanDerGrinten3Projection)); + Register("vandg4", typeof(VanDerGrinten4Projection)); + Register("van_der_grinten_iv", typeof(VanDerGrinten4Projection)); + Register("wink1", typeof(Winkel1Projection)); + Register("winkel_i", typeof(Winkel1Projection)); + Register("wink2", typeof(Winkel2Projection)); + Register("winkel_ii", typeof(Winkel2Projection)); + Register("wintri", typeof(WinkelTripelProjection)); + Register("winkel_tripel", typeof(WinkelTripelProjection)); + Register("hammer", typeof(HammerProjection)); + Register("sinu", typeof(SinusoidalProjection)); + Register("sinusoidal", typeof(SinusoidalProjection)); + Register("goode", typeof(GoodeProjection)); + Register("goode_homolosine", typeof(GoodeProjection)); + Register("igh", typeof(IghProjection)); + Register("interrupted_goode_homolosine", typeof(IghProjection)); + Register("imoll", typeof(InterruptedMollweideProjection)); + Register("interrupted_mollweide", typeof(InterruptedMollweideProjection)); + Register("imoll_o", typeof(InterruptedMollweideOceanicProjection)); + Register("interrupted_mollweide_oceanic_view", typeof(InterruptedMollweideOceanicProjection)); + Register("igh_o", typeof(InterruptedGoodeHomolosineOceanicProjection)); + Register("interrupted_goode_homolosine_oceanic_view", typeof(InterruptedGoodeHomolosineOceanicProjection)); + Register("bertin1953", typeof(Bertin1953Projection)); + Register("bertin_1953", typeof(Bertin1953Projection)); + Register("healpix", typeof(HealpixProjection)); + Register("rhealpix", typeof(HealpixProjection)); + Register("s2", typeof(S2Projection)); + Register("s2_projection", typeof(S2Projection)); + Register("sch", typeof(SchMathTransform)); + Register("spherical_cross_track_height", typeof(SchMathTransform)); + Register("som", typeof(SpaceObliqueMercatorProjection)); + Register("space_oblique_mercator", typeof(SpaceObliqueMercatorProjection)); + Register("misrsom", typeof(SpaceObliqueMercatorProjection)); + Register("lsat", typeof(SpaceObliqueMercatorProjection)); + Register("qsc", typeof(QuadrilateralizedSphericalCubeProjection)); + Register("quadrilateralized_spherical_cube", typeof(QuadrilateralizedSphericalCubeProjection)); + Register("rouss", typeof(RoussilheStereographicProjection)); + Register("roussilhe_stereographic", typeof(RoussilheStereographicProjection)); + Register("mil_os", typeof(MillerOblatedStereographicProjection)); + Register("miller_oblated_stereographic", typeof(MillerOblatedStereographicProjection)); + Register("lee_os", typeof(LeeOblatedStereographicProjection)); + Register("lee_oblated_stereographic", typeof(LeeOblatedStereographicProjection)); + Register("gs48", typeof(ModifiedStereographic48USProjection)); + Register("modified_stereographic_48_us", typeof(ModifiedStereographic48USProjection)); + Register("alsk", typeof(ModifiedStereographicAlaskaProjection)); + Register("modified_stereographic_alaska", typeof(ModifiedStereographicAlaskaProjection)); + Register("gs50", typeof(ModifiedStereographic50USProjection)); + Register("modified_stereographic_50_us", typeof(ModifiedStereographic50USProjection)); + Register("guyou", typeof(GuyouProjection)); + Register("peirce_q", typeof(PeirceQuincuncialProjection)); + Register("peirce_quincuncial", typeof(PeirceQuincuncialProjection)); + Register("adams_hemi", typeof(AdamsHemisphereInSquareProjection)); + Register("adams_hemisphere_in_a_square", typeof(AdamsHemisphereInSquareProjection)); + Register("adams_ws1", typeof(AdamsWorldInSquare1Projection)); + Register("adams_world_in_a_square_i", typeof(AdamsWorldInSquare1Projection)); + Register("adams_ws2", typeof(AdamsWorldInSquare2Projection)); + Register("adams_world_in_a_square_ii", typeof(AdamsWorldInSquare2Projection)); + Register("spilhaus", typeof(SpilhausProjection)); + Register("airocean", typeof(AiroceanProjection)); + Register("isea", typeof(IseaProjection)); + Register("icosahedral_snyder_equal_area", typeof(IseaProjection)); - private static readonly object RegistryLock = new object(); + Register("natural_earth", typeof(NaturalEarthProjection)); + Register("natearth", typeof(NaturalEarthProjection)); - /// - /// Static constructor - /// - static ProjectionsRegistry() + Register("natural_earth_2", typeof(NaturalEarth2Projection)); + Register("natural_earth2", typeof(NaturalEarth2Projection)); + Register("natearth2", typeof(NaturalEarth2Projection)); + + Register("robinson", typeof(RobinsonProjection)); + Register("robin", typeof(RobinsonProjection)); + + Register("mollweide", typeof(MollweideProjection)); + Register("moll", typeof(MollweideProjection)); + + Register("azimuthal_equidistant", typeof(AzimuthalEquidistantProjection)); + Register("aeqd", typeof(AzimuthalEquidistantProjection)); + + Register("gnomonic", typeof(GnomonicProjection)); + Register("gnom", typeof(GnomonicProjection)); + + ValidateBuiltInFactories(); + } + + /// + /// Registers a projection type under the given name. + /// + /// The projection name or alias (case-insensitive). + /// The -derived type that implements the projection. + /// Thrown when or is . + /// + /// Thrown when does not derive from , lacks a required + /// constructor, or a different type is already registered under . + /// + public static void Register( + string name, +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type type) + { + if (string.IsNullOrWhiteSpace(name)) { - Register("mercator", typeof(Mercator)); - Register("mercator_1sp", typeof(Mercator)); - Register("mercator_2sp", typeof(Mercator)); - Register("mercator_auxiliary_sphere", typeof(MercatorAuxiliarySphere)); - Register("pseudo_mercator", typeof(PseudoMercator)); - Register("popular_visualisation_pseudo_mercator", typeof(PseudoMercator)); - Register("google_mercator", typeof(PseudoMercator)); - - Register("transverse_mercator", typeof(TransverseMercator)); - Register("gauss_kruger", typeof(TransverseMercator)); - - Register("albers", typeof(AlbersProjection)); - Register("albers_conic_equal_area", typeof(AlbersProjection)); - - Register("krovak", typeof(KrovakProjection)); - - Register("polyconic", typeof(PolyconicProjection)); - - Register("lambert_conformal_conic", typeof(LambertConformalConic2SP)); - Register("lambert_conformal_conic_2sp", typeof(LambertConformalConic2SP)); - Register("lambert_conic_conformal_(2sp)", typeof(LambertConformalConic2SP)); - Register("lambert_tangential_conformal_conic_projection", typeof(LambertConformalConic2SP)); - - Register("lambert_azimuthal_equal_area", typeof(LambertAzimuthalEqualAreaProjection)); - - Register("cassini_soldner", typeof(CassiniSoldnerProjection)); - Register("hotine_oblique_mercator", typeof(HotineObliqueMercatorProjection)); - Register("hotine_oblique_mercator_azimuth_center", typeof(HotineObliqueMercatorProjection)); - Register("oblique_mercator", typeof(ObliqueMercatorProjection)); - Register("oblique_stereographic", typeof(ObliqueStereographicProjection)); - Register("orthographic", typeof(OrthographicProjection)); - Register("polar_stereographic", typeof(PolarStereographicProjection)); + ArgumentGuard.ThrowIfNull(name, nameof(name)); } - /// - /// Method to register a new Map - /// - /// - /// - public static void Register(string name, Type type) +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(type); +#else + if (type is null) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullException(nameof(name)); + throw new ArgumentNullException(nameof(type)); + } +#endif - if (type == null) - throw new ArgumentNullException(nameof(type)); + if (!typeof(MathTransform).IsAssignableFrom(type)) + { + ArgumentGuard.ThrowArgument("The provided type does not implement 'GeoAPI.CoordinateSystems.Transformations.IMathTransform'!", nameof(type)); + } - if (!typeof(MathTransform).IsAssignableFrom(type)) - throw new ArgumentException("The provided type does not implement 'GeoAPI.CoordinateSystems.Transformations.IMathTransform'!", nameof(type)); + Func, MathTransform>? factory = TryGetBuiltInFactory(type); + if (factory is null) + { + ConstructorInfo? constructor = CheckConstructor(type); + if (constructor is null) + { + ArgumentGuard.ThrowArgument("The provided type is lacking a suitable constructor", nameof(type)); + } - var ci = CheckConstructor(type); - if (ci == null) - throw new ArgumentException("The provided type is lacking a suitable constructor", nameof(type)); + factory = CreateReflectionFactory(type, constructor); + } - string key = ProjectionNameToRegistryKey(name); - lock (RegistryLock) - { - if (TypeRegistry.ContainsKey(key)) - { - var rt = TypeRegistry[key]; - if (ReferenceEquals(type, rt)) - return; - throw new ArgumentException("A different projection type has been registered with this name", "name"); - } + Register(name, type, factory); + } - TypeRegistry.Add(key, type); - ConstructorRegistry.Add(key, ci); + /// + /// Registers an alternative name for an already-registered projection type. + /// + /// The new alias to register. + /// The name of the already-registered projection. + /// Thrown when or is . + /// Thrown when is not a registered projection name. + public static void RegisterAlias(string aliasName, string existingName) + { + aliasName = ArgumentGuard.ThrowIfNull(aliasName, nameof(aliasName)); + existingName = ArgumentGuard.ThrowIfNull(existingName, nameof(existingName)); + + lock (RegistryLock) + { + if (!TypeRegistry.TryGetValue(ProjectionNameToRegistryKey(existingName), out ProjectionRegistration? existingRegistration)) + { + ArgumentGuard.ThrowArgument($"{existingName} is not a registered projection type", nameof(existingName)); } + + Register(aliasName, ArgumentGuard.ThrowIfNull(existingRegistration, nameof(existingRegistration)).ProjectionType); } + } - private static string ProjectionNameToRegistryKey(string name) - { - return name.ToLowerInvariant().Replace(' ', '_').Replace("-", "_"); + /// + /// Creates a projection transform instance for the provided projection class name. + /// + /// Projection class name or alias. + /// Projection parameters passed to the constructor. + /// Constructed projection transform. + internal static MathTransform CreateProjection(string className, IEnumerable parameters) + { + parameters = ArgumentGuard.ThrowIfNull(parameters, nameof(parameters)); + string key = ProjectionNameToRegistryKey(className); + + ProjectionRegistration? registration; + + lock (RegistryLock) + { + if (!TypeRegistry.TryGetValue(key, out registration)) + { + throw new NotSupportedException($"Projection {className} is not supported."); + } } - /// - /// Register an alias for an existing Map. - /// - /// - /// - public static void RegisterAlias(string aliasName, string existingName) - { - lock (RegistryLock) - { - if (!TypeRegistry.TryGetValue(ProjectionNameToRegistryKey(existingName), out var existingProjectionType)) - { - throw new ArgumentException($"{existingName} is not a registered projection type"); - } - - Register(aliasName, existingProjectionType); - } + registration = ArgumentGuard.ThrowIfNull(registration, nameof(registration)); + using IDisposable projectionIdentityOverride = MapProjection.BeginProjectionIdentityOverride(registration.ProjectionType, className); + return registration.Factory(parameters); + } + + private static Func, MathTransform> CreateAdaptiveTransverseMercatorFactory(string requestedName) + { + requestedName = ArgumentGuard.ThrowIfNull(requestedName, nameof(requestedName)); + return parameters => + { + List parameterList = AsProjectionParameterList(parameters); + return UsesEllipsoidalModel(parameterList) + ? CreateProjectionWithIdentity(parameterList, requestedName, typeof(ExtendedTransverseMercator), static list => new ExtendedTransverseMercator(list)) + : CreateProjectionWithIdentity(parameterList, requestedName, typeof(TransverseMercator), static list => new TransverseMercator(list)); + }; + } + + private static Func, MathTransform> CreateExactTransverseMercatorFactory(string requestedName) + { + requestedName = ArgumentGuard.ThrowIfNull(requestedName, nameof(requestedName)); + return parameters => + { + List parameterList = AsProjectionParameterList(parameters); + return CreateProjectionWithIdentity( + parameterList, + requestedName, + typeof(ExtendedTransverseMercator), + static list => new ExtendedTransverseMercator(list)); + }; + } + + private static void ValidateBuiltInFactories() + { + lock (RegistryLock) + { + foreach (ProjectionRegistration registration in TypeRegistry.Values) + { + if (!BuiltInFactories.ContainsKey(registration.ProjectionType)) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Built-in projection registration for {registration.ProjectionType.Name} is missing a compiled factory delegate."); + } + } } + } + + private static string ProjectionNameToRegistryKey(string name) + { + return name.ToLowerInvariant().Replace(' ', '_').Replace('-', '_'); + } - private static Type CheckConstructor(Type type) + private static void Register( + string name, +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type type, + Func, MathTransform> factory) + { + string key = ProjectionNameToRegistryKey(name); + lock (RegistryLock) { - // find a constructor that accepts exactly one parameter that's an - // instance of List, and then return the exact - // parameter type so that we can create instances of this type with - // minimal copying in the future, when possible. - foreach (var c in type.GetConstructors()) + if (TypeRegistry.TryGetValue(key, out ProjectionRegistration? registration)) { - var parameters = c.GetParameters(); - if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(typeof(List))) + if (ReferenceEquals(type, registration.ProjectionType)) { - return parameters[0].ParameterType; + return; } + + ArgumentGuard.ThrowArgument("A different projection type has been registered with this name", nameof(name)); } - return null; + TypeRegistry.Add(key, new ProjectionRegistration(type, factory)); } + } + + private static Func, MathTransform>? TryGetBuiltInFactory( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type type) + { + return BuiltInFactories.TryGetValue(type, out Func, MathTransform>? factory) + ? factory + : null; + } - internal static MathTransform CreateProjection(string className, IEnumerable parameters) + private static Func, MathTransform> CreateReflectionFactory( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type projectionType, + ConstructorInfo constructor) + { + Type parameterType = constructor.GetParameters()[0].ParameterType; + return parameters => { - string key = ProjectionNameToRegistryKey(className); + object constructorArgument = parameterType.IsInstanceOfType(parameters) + ? parameters + : AsProjectionParameterList(parameters); + return InvokeProjectionConstructor(projectionType, constructor, constructorArgument); + }; + } - Type projectionType; - Type ci; + private static MathTransform InvokeProjectionConstructor( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type projectionType, + ConstructorInfo constructor, + object constructorArgument) + { + if (constructor.Invoke([constructorArgument]) is MathTransform projection) + { + return projection; + } - lock (RegistryLock) + ThrowProjectionFactoryReturnedNull(projectionType); + return null!; + } + + private static List AsProjectionParameterList(IEnumerable parameters) + { + return parameters as List ?? [.. parameters]; + } + + private static MathTransform CreateProjectionWithIdentity( + List parameters, + string requestedName, + Type projectionType, + Func, MathTransform> factory) + { + using IDisposable projectionIdentityOverride = MapProjection.BeginProjectionIdentityOverride(projectionType, requestedName); + return factory(parameters); + } + + private static bool UsesEllipsoidalModel(List parameters) + { + bool hasSemiMajor = false; + bool hasSemiMinor = false; + double semiMajor = 0d; + double semiMinor = 0d; + + for (int i = 0; i < parameters.Count; i++) + { + ProjectionParameter parameter = parameters[i]; + if (parameter.Name.Equals("semi_major", StringComparison.OrdinalIgnoreCase)) { - if (!TypeRegistry.TryGetValue(key, out projectionType)) - throw new NotSupportedException($"Projection {className} is not supported."); - ci = ConstructorRegistry[key]; + semiMajor = parameter.Value; + hasSemiMajor = true; } - - if (!ci.IsInstanceOfType(parameters)) + else if (parameter.Name.Equals("semi_minor", StringComparison.OrdinalIgnoreCase)) { - parameters = new List(parameters); + semiMinor = parameter.Value; + hasSemiMinor = true; } + } + + return !hasSemiMajor || !hasSemiMinor || Math.Abs(semiMajor - semiMinor) > 1e-12d; + } - var res = (MapProjection)Activator.CreateInstance(projectionType, parameters); - if (!res.Name.Equals(className, StringComparison.InvariantCultureIgnoreCase)) + [DoesNotReturn] + private static void ThrowProjectionFactoryReturnedNull( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type projectionType) + { + ProjectionThrowHelper.ThrowInvalidOperation($"Projection {projectionType.Name} factory returned null."); + } + + private static ConstructorInfo? CheckConstructor( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type type) + { + // find a constructor that accepts exactly one parameter that's an + // instance of List, and then return the exact + // parameter type so that we can create instances of this type with + // minimal copying in the future, when possible. + foreach (ConstructorInfo c in type.GetConstructors()) + { + ParameterInfo[] parameters = c.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(typeof(List))) { - res.Alias = res.Name; - res.Name = className; + return c; } - return res; } + + return null; + } + + private sealed class ProjectionRegistration + { + internal ProjectionRegistration( +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + Type projectionType, + Func, MathTransform> factory) + { + this.ProjectionType = projectionType; + this.Factory = factory; + } + +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + internal Type ProjectionType { get; } + + internal Func, MathTransform> Factory { get; } } } diff --git a/src/ProjNet/CoordinateSystems/Projections/PseudoMercator.cs b/src/ProjNet/CoordinateSystems/Projections/PseudoMercator.cs index 93540052..ce734271 100644 --- a/src/ProjNet/CoordinateSystems/Projections/PseudoMercator.cs +++ b/src/ProjNet/CoordinateSystems/Projections/PseudoMercator.cs @@ -1,40 +1,60 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Projections; + using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Pseudo-Mercator (Web Mercator) projection (EPSG:3856). +/// +/// +/// Applies a spherical Mercator formula by treating the ellipsoidal semi-major axis as +/// the sphere radius and forcing the scale factor to 1. Geodetic latitude is projected +/// without ellipsoidal correction, producing the projection used by most web mapping services. +/// +internal sealed class PseudoMercator : Mercator { - [Serializable] - internal class PseudoMercator : Mercator + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PseudoMercator(IEnumerable parameters) + : this(parameters, null) { - public PseudoMercator(IEnumerable parameters) - :this(parameters, null) - { - - } - protected PseudoMercator(IEnumerable parameters, Mercator inverse) - :base(VerifyParameters(parameters), inverse) - { - Name = "Pseudo-Mercator"; - Authority = "EPSG"; - AuthorityCode = 3856; - } - - private static IEnumerable VerifyParameters(IEnumerable parameters) - { - var p = new ProjectionParameterSet(parameters); - double semi_major = p.GetParameterValue("semi_major"); - p.SetParameterValue("semi_minor", semi_major); - p.SetParameterValue("scale_factor", 1); - - return p.ToProjectionParameter(); - } - - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new PseudoMercator(_Parameters.ToProjectionParameter(), this); - return _inverse; - } + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + private PseudoMercator(IEnumerable parameters, Mercator? inverse) + : base(VerifyParameters(parameters), inverse) + { + this.Name = "Pseudo-Mercator"; + this.Authority = "EPSG"; + this.AuthorityCode = 3856; + } + + private static IEnumerable VerifyParameters(IEnumerable parameters) + { + var p = new ProjectionParameterSet(parameters); + double semi_major = p.GetParameterValue("semi_major"); + p.SetParameterValue("semi_minor", semi_major); + p.SetParameterValue("scale_factor", 1); + + return p.ToProjectionParameter(); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PseudoMercator(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; } } diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP1Projection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP1Projection.cs new file mode 100644 index 00000000..039a5b5e --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP1Projection.cs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Putnins P1 projection (putp1). +/// +/// +/// Putnins P1 is one of the spherical projections published by Reinholds Putnins in 1934. +/// It uses an Eckert-III-like square-root longitude scale with the fixed parameter +/// A = -0.5. +/// +internal sealed class PutninsP1Projection : MapProjection +{ + private const double Cx = 1.89490d; + private const double Cy = 0.94745d; + private const double A = -0.5d; + private const double B = 0.30396355092701331433d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP1Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP1Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Putnins_P1"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PutninsP1Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double y = Cy * lat; + double underRoot = 1d - (B * lat * lat); + if (underRoot < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double x = Cx * lambda * (A + Math.Sqrt(underRoot)); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = yy / Cy; + + double underRoot = 1d - (B * phi * phi); + if (underRoot < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double denominator = Cx * (A + Math.Sqrt(underRoot)); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP2Projection.cs new file mode 100644 index 00000000..dee2648b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP2Projection.cs @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Putnins P2 projection (putp2). +/// +/// +/// Putnins P2 is one of the spherical projections published by Reinholds Putnins in 1934. +/// The implementation iteratively solves the auxiliary latitude used by the original +/// formulation and then applies the characteristic Putnins P2 cosine-shifted x scaling. +/// +internal sealed class PutninsP2Projection : MapProjection +{ + private const double Cx = 1.89490d; + private const double Cy = 1.71848d; + private const double Cp = 0.6141848493043784d; + private const int Iterations = 10; + private const double PiDiv3 = 1.0471975511965977d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Putnins_P2"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PutninsP2Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double p = Cp * Math.Sin(lat); + double latSquared = lat * lat; + double phi = lat * (0.615709d + (latSquared * (0.00909953d + (latSquared * 0.0046292d)))); + int i = Iterations; + + for (; i > 0; i--) + { + double c = Math.Cos(phi); + double s = Math.Sin(phi); + double denominator = 1d + (c * (c - 1d)) - (s * s); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double v = (phi + (s * (c - 1d)) - p) / denominator; + phi -= v; + if (Math.Abs(v) < Eps10) + { + break; + } + } + + if (i == 0) + { + phi = phi < 0d ? -PiDiv3 : PiDiv3; + } + + double x = Cx * lambda * (Math.Cos(phi) - 0.5d); + double y = Cy * Math.Sin(phi); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = Asinz(yy / Cy); + double cosPhi = Math.Cos(phi); + double denominator = Cx * (cosPhi - 0.5d); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + phi = Asinz((phi + (Math.Sin(phi) * (cosPhi - 1d))) / Cp); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP3PrimeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP3PrimeProjection.cs new file mode 100644 index 00000000..2e7bcb6d --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP3PrimeProjection.cs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Putnins P3' projection (putp3p). +/// +/// +/// This projection specializes with the Putnins P3' +/// parameter set, so its numerical behavior follows the same verified base formulation. +/// +internal sealed class PutninsP3PrimeProjection : PutninsP3Projection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP3PrimeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP3PrimeProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Putnins_P3P"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "putp3_a", 2d * 0.1013211836d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP3Projection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP3Projection.cs new file mode 100644 index 00000000..5733f307 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP3Projection.cs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Putnins P3 projection (putp3). +/// +/// +/// Putnins P3 is one of the spherical projections published by Reinholds Putnins in 1934. +/// It is a parameterized cylindrical-like form with the fixed coefficient +/// C = 0.79788456 and a configurable quadratic latitude damping term. +/// +internal class PutninsP3Projection : MapProjection +{ + private const double C = 0.79788456d; + private const double DefaultA = 4d * 0.1013211836d; + + private readonly double a; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP3Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP3Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Putnins_P3"; + this.a = this.Parameters.GetOptionalParameterValue("putp3_a", DefaultA); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PutninsP3Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = C * lambda * (1d - (this.a * lat * lat)); + double y = C * lat; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = yy / C; + double denominator = C * (1d - (this.a * phi * phi)); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP4PProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP4PProjection.cs new file mode 100644 index 00000000..1e37d653 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP4PProjection.cs @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Putnins P4' projection (putp4p). +/// +/// +/// Putnins P4' is the equal-area Putnins family member published by Reinholds Putnins in +/// 1934. The implementation uses the characteristic pre- and post-asin scaling with +/// a one-third angle step for the final y coordinate. +/// +internal class PutninsP4PProjection : MapProjection +{ + private const double PreAsinFactor = 0.883883476d; + private const double PostAsinFactor = 1.13137085d; + private const double DefaultCx = 0.874038744d; + private const double DefaultCy = 3.883251825d; + + private readonly double cx; + private readonly double cy; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP4PProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP4PProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Putnins_P4P"; + this.cx = this.Parameters.GetOptionalParameterValue("putp4p_cx", DefaultCx); + this.cy = this.Parameters.GetOptionalParameterValue("putp4p_cy", DefaultCy); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PutninsP4PProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = Asinz(PreAsinFactor * Math.Sin(lat)); + double x = this.cx * lambda * Math.Cos(phi); + double phiThird = phi / 3d; + double cosPhiThird = Math.Cos(phiThird); + if (Math.Abs(cosPhiThird) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x /= cosPhiThird; + double y = this.cy * Math.Sin(phiThird); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phiThird = Asinz(yy / this.cy); + double cosPhiThird = Math.Cos(phiThird); + if (Math.Abs(this.cx) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = (xx * cosPhiThird) / this.cx; + double phi = 3d * phiThird; + double cosPhi = Math.Cos(phi); + if (Math.Abs(cosPhi) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + lambda /= cosPhi; + phi = Asinz(PostAsinFactor * Math.Sin(phi)); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP5PrimeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP5PrimeProjection.cs new file mode 100644 index 00000000..3572df08 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP5PrimeProjection.cs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Putnins P5' projection (putp5p). +/// +/// +/// This projection specializes with the Putnins P5' +/// parameter set, so its numerical behavior follows the same verified base formulation. +/// +internal sealed class PutninsP5PrimeProjection : PutninsP5Projection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP5PrimeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP5PrimeProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Putnins_P5P"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "putp5_a", 1.5d); + ReplaceOrAdd(merged, "putp5_b", 0.5d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP5Projection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP5Projection.cs new file mode 100644 index 00000000..99eac96b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP5Projection.cs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Putnins P5 projection (putp5). +/// +/// +/// Putnins P5 is one of the spherical projections published by Reinholds Putnins in 1934. +/// Its longitude scale follows the family form +/// A - B * sqrt(1 + D * φ²), with parameter values that can also be specialized +/// for the prime variant. +/// +internal class PutninsP5Projection : MapProjection +{ + private const double C = 1.01346d; + private const double D = 1.2158542d; + private const double DefaultA = 2d; + private const double DefaultB = 1d; + + private readonly double a; + private readonly double b; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP5Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP5Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Putnins_P5"; + this.a = this.Parameters.GetOptionalParameterValue("putp5_a", DefaultA); + this.b = this.Parameters.GetOptionalParameterValue("putp5_b", DefaultB); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PutninsP5Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = C * lambda * (this.a - (this.b * Math.Sqrt(1d + (D * lat * lat)))); + double y = C * lat; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = yy / C; + double denominator = C * (this.a - (this.b * Math.Sqrt(1d + (D * phi * phi)))); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP6PrimeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP6PrimeProjection.cs new file mode 100644 index 00000000..a995c5b7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP6PrimeProjection.cs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Putnins P6' projection (putp6p). +/// +/// +/// This projection specializes with the Putnins P6' +/// parameter set, so its numerical behavior follows the same verified base formulation. +/// +internal sealed class PutninsP6PrimeProjection : PutninsP6Projection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP6PrimeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP6PrimeProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Putnins_P6P"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "putp6_cx", 0.44329d); + ReplaceOrAdd(merged, "putp6_cy", 0.80404d); + ReplaceOrAdd(merged, "putp6_a", 6d); + ReplaceOrAdd(merged, "putp6_b", 5.61125d); + ReplaceOrAdd(merged, "putp6_d", 3d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/PutninsP6Projection.cs b/src/ProjNet/CoordinateSystems/Projections/PutninsP6Projection.cs new file mode 100644 index 00000000..babab48f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/PutninsP6Projection.cs @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Putnins P6 projection (putp6). +/// +/// +/// Putnins P6 is one of the spherical projections published by Reinholds Putnins in 1934. +/// The implementation iteratively solves the logarithmic auxiliary equation used by the +/// original formulation and then applies the configurable Putnins P6 x/y scaling constants. +/// +internal class PutninsP6Projection : MapProjection +{ + private const double DefaultCx = 1.01346d; + private const double DefaultCy = 0.91910d; + private const double DefaultA = 4d; + private const double DefaultB = 2.1471437182129378784d; + private const double DefaultD = 2d; + private const int Iterations = 10; + private const double PoleValue = 1.732050807568877d; + + private readonly double cx; + private readonly double cy; + private readonly double a; + private readonly double b; + private readonly double d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public PutninsP6Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public PutninsP6Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Putnins_P6"; + this.cx = this.Parameters.GetOptionalParameterValue("putp6_cx", DefaultCx); + this.cy = this.Parameters.GetOptionalParameterValue("putp6_cy", DefaultCy); + this.a = this.Parameters.GetOptionalParameterValue("putp6_a", DefaultA); + this.b = this.Parameters.GetOptionalParameterValue("putp6_b", DefaultB); + this.d = this.Parameters.GetOptionalParameterValue("putp6_d", DefaultD); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PutninsP6Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double p = this.b * Math.Sin(lat); + double phi = 1.10265779d * lat; + int i = Iterations; + + for (; i > 0; i--) + { + double r = Math.Sqrt(1d + (phi * phi)); + double denominator = this.a - (2d * r); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double v = (((this.a - r) * phi) - Math.Log(phi + r) - p) / denominator; + phi -= v; + if (Math.Abs(v) < Eps10) + { + break; + } + } + + double sqrtOnePlusPhiSquared = i == 0 ? 2d : Math.Sqrt(1d + (phi * phi)); + if (i == 0) + { + phi = p < 0d ? -PoleValue : PoleValue; + } + + double x = this.cx * lambda * (this.d - sqrtOnePlusPhiSquared); + double y = this.cy * phi; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phi = yy / this.cy; + double r = Math.Sqrt(1d + (phi * phi)); + double denominator = this.cx * (this.d - r); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + double sinPhi = (((this.a - r) * phi) - Math.Log(phi + r)) / this.b; + phi = Asinz(sinPhi); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/QuadrilateralizedSphericalCubeProjection.cs b/src/ProjNet/CoordinateSystems/Projections/QuadrilateralizedSphericalCubeProjection.cs new file mode 100644 index 00000000..b87eda58 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/QuadrilateralizedSphericalCubeProjection.cs @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Quadrilateralized Spherical Cube projection (qsc). +/// +/// +/// The Quadrilateralized Spherical Cube maps the globe onto six cube faces and +/// applies an equal-area transform within each face. The ellipsoidal variant first +/// converts to a geocentric latitude before selecting the target face. +/// The formulation was independently verified against F. M. O'Neill and +/// R. E. Laubscher, Extended Studies of a Quadrilateralized Spherical Cube Earth +/// Data Base, DTIC report ADA026294, 1976. The face selection through dominant +/// direction cosines and the equal-area mapping within each face match the +/// implementation here. +/// +/// DTIC report ADA026294: Quadrilateralized Spherical Cube. +/// Background overview of the quadrilateralized spherical cube projection. +internal sealed class QuadrilateralizedSphericalCubeProjection : MapProjection +{ + private const double HalfPiPlusQuarterPi = HalfPi + FortPi; + private const double HalfPiMinusQuarterPiHalf = HalfPi - (FortPi * 0.5d); + private readonly Face face; + private readonly double aSquared; + private readonly double sphereB; + private readonly double oneMinusF; + private readonly double oneMinusFSquared; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public QuadrilateralizedSphericalCubeProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public QuadrilateralizedSphericalCubeProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Quadrilateralized_Spherical_Cube"; + this.face = DetermineFace(this.latOrigin, this.centralMeridian); + + if (this.es != 0d) + { + this.aSquared = this.semiMajor * this.semiMajor; + this.sphereB = this.semiMajor * Math.Sqrt(1d - this.es); + this.oneMinusF = 1d - ((this.semiMajor - this.sphereB) / this.semiMajor); + this.oneMinusFSquared = this.oneMinusF * this.oneMinusF; + } + } + + private enum Face + { + Front = 0, + Right = 1, + Back = 2, + Left = 3, + Top = 4, + Bottom = 5, + } + + private enum Area + { + Zero = 0, + One = 1, + Two = 2, + Three = 3, + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new QuadrilateralizedSphericalCubeProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double latitude = this.es != 0d + ? Math.Atan(this.oneMinusFSquared * Math.Tan(lat)) + : lat; + + double longitude = lon; + Area area = Area.Zero; + double theta = 0d; + double phi = 0d; + + if (this.face == Face.Top) + { + phi = HalfPi - latitude; + if (longitude >= FortPi && longitude <= HalfPiPlusQuarterPi) + { + area = Area.Zero; + theta = longitude - HalfPi; + } + else if (longitude > HalfPiPlusQuarterPi || longitude <= -HalfPiPlusQuarterPi) + { + area = Area.One; + theta = longitude > 0d ? longitude - PI : longitude + PI; + } + else if (longitude > -HalfPiPlusQuarterPi && longitude <= -FortPi) + { + area = Area.Two; + theta = longitude + HalfPi; + } + else + { + area = Area.Three; + theta = longitude; + } + } + else if (this.face == Face.Bottom) + { + phi = HalfPi + latitude; + if (longitude >= FortPi && longitude <= HalfPiPlusQuarterPi) + { + area = Area.Zero; + theta = -longitude + HalfPi; + } + else if (longitude < FortPi && longitude >= -FortPi) + { + area = Area.One; + theta = -longitude; + } + else if (longitude < -FortPi && longitude >= -HalfPiPlusQuarterPi) + { + area = Area.Two; + theta = -longitude - HalfPi; + } + else + { + area = Area.Three; + theta = longitude > 0d ? -longitude + PI : -longitude - PI; + } + } + else + { + if (this.face == Face.Right) + { + longitude = ShiftLongitudeOrigin(longitude, HalfPi); + } + else if (this.face == Face.Back) + { + longitude = ShiftLongitudeOrigin(longitude, PI); + } + else if (this.face == Face.Left) + { + longitude = ShiftLongitudeOrigin(longitude, -HalfPi); + } + + double sinLatitude = Math.Sin(latitude); + double cosLatitude = Math.Cos(latitude); + double sinLongitude = Math.Sin(longitude); + double cosLongitude = Math.Cos(longitude); + double q = cosLatitude * cosLongitude; + double r = cosLatitude * sinLongitude; + double s = sinLatitude; + + if (this.face == Face.Front) + { + phi = Math.Acos(q); + theta = ForwardEquatorialFaceTheta(phi, s, r, out area); + } + else if (this.face == Face.Right) + { + phi = Math.Acos(r); + theta = ForwardEquatorialFaceTheta(phi, s, -q, out area); + } + else if (this.face == Face.Back) + { + phi = Math.Acos(-q); + theta = ForwardEquatorialFaceTheta(phi, s, -r, out area); + } + else + { + phi = Math.Acos(-r); + theta = ForwardEquatorialFaceTheta(phi, s, q, out area); + } + } + + double mu = Math.Atan((12d / PI) * (theta + Math.Acos(Math.Sin(theta) * Math.Cos(FortPi)) - HalfPi)); + double t = Math.Sqrt((1d - Math.Cos(phi)) / (Math.Cos(mu) * Math.Cos(mu)) / (1d - Math.Cos(Math.Atan(1d / Math.Cos(theta))))); + + if (area == Area.One) + { + mu += HalfPi; + } + else if (area == Area.Two) + { + mu += PI; + } + else if (area == Area.Three) + { + mu += PI + HalfPi; + } + + lon = this.semiMajor * this.scaleFactor * (t * Math.Cos(mu)); + lat = this.semiMajor * this.scaleFactor * (t * Math.Sin(mu)); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x / (this.semiMajor * this.scaleFactor); + double yy = y / (this.semiMajor * this.scaleFactor); + double nu = Math.Atan(Math.Sqrt((xx * xx) + (yy * yy))); + double mu = Math.Atan2(yy, xx); + + Area area; + if (xx >= 0d && xx >= Math.Abs(yy)) + { + area = Area.Zero; + } + else if (yy >= 0d && yy >= Math.Abs(xx)) + { + area = Area.One; + mu -= HalfPi; + } + else if (xx < 0d && -xx >= Math.Abs(yy)) + { + area = Area.Two; + mu = mu < 0d ? mu + PI : mu - PI; + } + else + { + area = Area.Three; + mu += HalfPi; + } + + double t = (PI / 12d) * Math.Tan(mu); + double tanTheta = Math.Sin(t) / (Math.Cos(t) - ProjectionConstants.OneOverSqrt2); + double theta = Math.Atan(tanTheta); + double cosMu = Math.Cos(mu); + double tanNu = Math.Tan(nu); + double cosPhi = 1d - (cosMu * cosMu * tanNu * tanNu * (1d - Math.Cos(Math.Atan(1d / Math.Cos(theta))))); + if (cosPhi < -1d) + { + cosPhi = -1d; + } + else if (cosPhi > 1d) + { + cosPhi = 1d; + } + + double lambda = 0d; + double phi = 0d; + + if (this.face == Face.Top) + { + double phiFace = Math.Acos(cosPhi); + phi = HalfPi - phiFace; + if (area == Area.Zero) + { + lambda = theta + HalfPi; + } + else if (area == Area.One) + { + lambda = theta < 0d ? theta + PI : theta - PI; + } + else if (area == Area.Two) + { + lambda = theta - HalfPi; + } + else + { + lambda = theta; + } + } + else if (this.face == Face.Bottom) + { + double phiFace = Math.Acos(cosPhi); + phi = phiFace - HalfPi; + if (area == Area.Zero) + { + lambda = -theta + HalfPi; + } + else if (area == Area.One) + { + lambda = -theta; + } + else if (area == Area.Two) + { + lambda = -theta - HalfPi; + } + else + { + lambda = theta < 0d ? -theta - PI : -theta + PI; + } + } + else + { + double q = cosPhi; + t = q * q; + double s = t >= 1d ? 0d : Math.Sqrt(1d - t) * Math.Sin(theta); + t += s * s; + double r = t >= 1d ? 0d : Math.Sqrt(1d - t); + + if (area == Area.One) + { + double swap = r; + r = -s; + s = swap; + } + else if (area == Area.Two) + { + r = -r; + s = -s; + } + else if (area == Area.Three) + { + double swap = r; + r = s; + s = -swap; + } + + if (this.face == Face.Right) + { + double swap = q; + q = -r; + r = swap; + } + else if (this.face == Face.Back) + { + q = -q; + r = -r; + } + else if (this.face == Face.Left) + { + double swap = q; + q = r; + r = -swap; + } + + phi = Math.Acos(-s) - HalfPi; + lambda = Math.Atan2(r, q); + if (this.face == Face.Right) + { + lambda = ShiftLongitudeOrigin(lambda, -HalfPi); + } + else if (this.face == Face.Back) + { + lambda = ShiftLongitudeOrigin(lambda, -PI); + } + else if (this.face == Face.Left) + { + lambda = ShiftLongitudeOrigin(lambda, HalfPi); + } + } + + if (this.es != 0d) + { + bool invertSign = phi < 0d; + double tanPhi = Math.Tan(phi); + double xa = this.sphereB / Math.Sqrt((tanPhi * tanPhi) + this.oneMinusFSquared); + phi = Math.Atan(Math.Sqrt(this.aSquared - (xa * xa)) / (this.oneMinusF * xa)); + if (invertSign) + { + phi = -phi; + } + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static double ForwardEquatorialFaceTheta(double phi, double y, double x, out Area area) + { + if (phi < Eps10) + { + area = Area.Zero; + return 0d; + } + + double theta = Math.Atan2(y, x); + if (Math.Abs(theta) <= FortPi) + { + area = Area.Zero; + } + else if (theta > FortPi && theta <= HalfPiPlusQuarterPi) + { + area = Area.One; + theta -= HalfPi; + } + else if (theta > HalfPiPlusQuarterPi || theta <= -HalfPiPlusQuarterPi) + { + area = Area.Two; + theta = theta >= 0d ? theta - PI : theta + PI; + } + else + { + area = Area.Three; + theta += HalfPi; + } + + return theta; + } + + private static double ShiftLongitudeOrigin(double longitude, double offset) + { + double shifted = longitude + offset; + if (shifted < -PI) + { + shifted += TwoPi; + } + else if (shifted > PI) + { + shifted -= TwoPi; + } + + return shifted; + } + + private static Face DetermineFace(double phi0, double lam0) + { + if (phi0 >= HalfPiMinusQuarterPiHalf) + { + return Face.Top; + } + + if (phi0 <= -HalfPiMinusQuarterPiHalf) + { + return Face.Bottom; + } + + if (Math.Abs(lam0) <= FortPi) + { + return Face.Front; + } + + return Math.Abs(lam0) <= HalfPiPlusQuarterPi ? lam0 > 0d ? Face.Right : Face.Left : Face.Back; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/QuarticAuthalicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/QuarticAuthalicProjection.cs new file mode 100644 index 00000000..b81781e5 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/QuarticAuthalicProjection.cs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Quartic Authalic projection (qua_aut). +/// +/// +/// Quartic Authalic is a spherical equal-area member of the STS family implemented by +/// . This specialization fixes the family constants to +/// p = 2 and q = 2 with sine-mode scaling, yielding the quartic-authalic +/// relations commonly listed in Snyder's survey of pseudocylindrical projections. +/// +internal sealed class QuarticAuthalicProjection : StsProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public QuarticAuthalicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public QuarticAuthalicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, "Quartic_Authalic", 2d, 2d, false) + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new QuarticAuthalicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/RectangularPolyconicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/RectangularPolyconicProjection.cs new file mode 100644 index 00000000..9a874e48 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/RectangularPolyconicProjection.cs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Rectangular Polyconic projection (rpoly). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the historical War +/// Department rectangular polyconic construction. The implementation matches the +/// true-scale-latitude branch and the simpler equatorial branch used when lat_ts is +/// not provided. +/// +internal sealed class RectangularPolyconicProjection : MapProjection +{ + private readonly double modeFxa; + private readonly double modeFxb; + private readonly bool mode; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public RectangularPolyconicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public RectangularPolyconicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Rectangular_Polyconic"; + + double phi1 = Math.Abs(DegreesToRadians(this.Parameters.GetOptionalParameterValue("lat_ts", 0d))); + this.mode = phi1 > Eps10; + if (this.mode) + { + this.modeFxb = 0.5d * Math.Sin(phi1); + this.modeFxa = 0.5d / this.modeFxb; + } + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new RectangularPolyconicProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double fa = this.mode ? Math.Tan(lambda * this.modeFxb) * this.modeFxa : 0.5d * lambda; + double xUnit = fa + fa; + double yUnit = -this.latOrigin; + + if (Math.Abs(phi) >= 1e-9d) + { + yUnit = 1d / Math.Tan(phi); + fa = 2d * Math.Atan(fa * Math.Sin(phi)); + xUnit = Math.Sin(fa) * yUnit; + yUnit = (phi - this.latOrigin) + ((1d - Math.Cos(fa)) * yUnit); + } + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Rectangular Polyconic does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/RobinsonProjection.cs b/src/ProjNet/CoordinateSystems/Projections/RobinsonProjection.cs new file mode 100644 index 00000000..6ec37e51 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/RobinsonProjection.cs @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Robinson projection (robin). +/// +/// +/// The Robinson projection is a pseudocylindrical projection defined by a look-up table of +/// x- and y-scale coefficients at 5° latitude intervals. Coefficients are evaluated using +/// cubic polynomials per interval, matching the PROJ reference implementation. +/// Arthur H. Robinson's 1974 paper, "A New Map Projection," International Yearbook of +/// Cartography, introduced the tabulated coefficients for the projection. The modern +/// cubic interpolation scheme used here was independently verified against current GIS +/// practice and secondary references that document the four-coefficient per-band formulation. +/// +/// Wikipedia: Robinson projection. +internal sealed class RobinsonProjection : MapProjection +{ + private const int Nodes = 18; + private const int MaxIterations = 100; + private const double InverseTolerance = 1e-10d; + private const double OneEps = 1.000001; + private const double LatitudeBandScale = 11.45915590261646417544; + private const double FiveDegreesInRadians = 0.08726646259971647884; + + private const double XScale = 0.8487; + private const double YScale = 1.3523; + + private static readonly Coeff[] CoeffX = + [ + new(1.0f, 2.2199e-17f, -7.15515e-05f, 3.1103e-06f), + new(0.9986f, -0.000482243f, -2.4897e-05f, -1.3309e-06f), + new(0.9954f, -0.00083103f, -4.48605e-05f, -9.86701e-07f), + new(0.99f, -0.00135364f, -5.9661e-05f, 3.6777e-06f), + new(0.9822f, -0.00167442f, -4.49547e-06f, -5.72411e-06f), + new(0.973f, -0.00214868f, -9.03571e-05f, 1.8736e-08f), + new(0.96f, -0.00305085f, -9.00761e-05f, 1.64917e-06f), + new(0.9427f, -0.00382792f, -6.53386e-05f, -2.6154e-06f), + new(0.9216f, -0.00467746f, -0.00010457f, 4.81243e-06f), + new(0.8962f, -0.00536223f, -3.23831e-05f, -5.43432e-06f), + new(0.8679f, -0.00609363f, -0.000113898f, 3.32484e-06f), + new(0.835f, -0.00698325f, -6.40253e-05f, 9.34959e-07f), + new(0.7986f, -0.00755338f, -5.00009e-05f, 9.35324e-07f), + new(0.7597f, -0.00798324f, -3.5971e-05f, -2.27626e-06f), + new(0.7186f, -0.00851367f, -7.01149e-05f, -8.6303e-06f), + new(0.6732f, -0.00986209f, -0.000199569f, 1.91974e-05f), + new(0.6213f, -0.010418f, 8.83923e-05f, 6.24051e-06f), + new(0.5722f, -0.00906601f, 0.000182f, 6.24051e-06f), + new(0.5322f, -0.00677797f, 0.000275608f, 6.24051e-06f), + ]; + + private static readonly Coeff[] CoeffY = + [ + new(-5.20417e-18f, 0.0124f, 1.21431e-18f, -8.45284e-11f), + new(0.062f, 0.0124f, -1.26793e-09f, 4.22642e-10f), + new(0.124f, 0.0124f, 5.07171e-09f, -1.60604e-09f), + new(0.186f, 0.0123999f, -1.90189e-08f, 6.00152e-09f), + new(0.248f, 0.0124002f, 7.10039e-08f, -2.24e-08f), + new(0.31f, 0.0123992f, -2.64997e-07f, 8.35986e-08f), + new(0.372f, 0.0124029f, 9.88983e-07f, -3.11994e-07f), + new(0.434f, 0.0123893f, -3.69093e-06f, -4.35621e-07f), + new(0.4958f, 0.0123198f, -1.02252e-05f, -3.45523e-07f), + new(0.5571f, 0.0121916f, -1.54081e-05f, -5.82288e-07f), + new(0.6176f, 0.0119938f, -2.41424e-05f, -5.25327e-07f), + new(0.6769f, 0.011713f, -3.20223e-05f, -5.16405e-07f), + new(0.7346f, 0.0113541f, -3.97684e-05f, -6.09052e-07f), + new(0.7903f, 0.0109107f, -4.89042e-05f, -1.04739e-06f), + new(0.8435f, 0.0103431f, -6.4615e-05f, -1.40374e-09f), + new(0.8936f, 0.00969686f, -6.4636e-05f, -8.547e-06f), + new(0.9394f, 0.00840947f, -0.000192841f, -4.2106e-06f), + new(0.9761f, 0.00616527f, -0.000256f, -4.2106e-06f), + new(1.0f, 0.00328947f, -0.000319159f, -4.2106e-06f), + ]; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public RobinsonProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public RobinsonProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Robinson"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new RobinsonProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phiAbs = Math.Abs(lat); + + int index = GetLatitudeBand(phiAbs); + if (index < 0 || index > Nodes) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double dphi = RadiansToDegrees(phiAbs - (FiveDegreesInRadians * index)); + double xCoeff = Evaluate(CoeffX[index], dphi); + double yCoeff = Evaluate(CoeffY[index], dphi); + + lon = this.SphericalRadius * XScale * lambda * xCoeff; + lat = this.SphericalRadius * YScale * yCoeff * Sign(lat); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double normalizedY = Math.Abs(y) * this.InverseSphericalRadius / YScale; + double lambda = x * this.InverseSphericalRadius / XScale; + + if (normalizedY >= 1d) + { + if (normalizedY > OneEps) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + y = y < 0d ? -HalfPi : HalfPi; + x = Adjust_lon(this.centralMeridian + (lambda / CoeffX[Nodes].C0)); + return; + } + + int index = FindLatitudeBand(normalizedY); + Coeff yc = CoeffY[index]; + + double t = 5d * (normalizedY - yc.C0) / (CoeffY[index + 1].C0 - yc.C0); + bool converged = false; + for (int iteration = 0; iteration < MaxIterations; iteration++) + { + double delta = (Evaluate(yc, t) - normalizedY) / EvaluateDerivative(yc, t); + t -= delta; + if (Math.Abs(delta) < InverseTolerance) + { + converged = true; + break; + } + } + + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double phi = DegreesToRadians((5d * index) + t); + if (y < 0d) + { + phi = -phi; + } + + double lambdaResult = lambda / Evaluate(CoeffX[index], t); + if (Math.Abs(lambdaResult) > PI) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambdaResult); + y = phi; + } + + private static int GetLatitudeBand(double phiAbs) + { + if (double.IsNaN(phiAbs)) + { + return -1; + } + + int index = (int)Math.Floor((phiAbs * LatitudeBandScale) + 1e-15d); + if (index >= Nodes) + { + index = Nodes; + } + + return index; + } + + private static int FindLatitudeBand(double yNormalized) + { + int index = (int)Math.Floor(yNormalized * Nodes); + if (index < 0 || index >= Nodes) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + while (true) + { + if (CoeffY[index].C0 > yNormalized) + { + index--; + } + else if (CoeffY[index + 1].C0 <= yNormalized) + { + index++; + } + else + { + return index; + } + } + } + + private static double Evaluate(Coeff coeff, double z) + { + return coeff.C0 + (z * (coeff.C1 + (z * (coeff.C2 + (z * coeff.C3))))); + } + + private static double EvaluateDerivative(Coeff coeff, double z) + { + return coeff.C1 + (2d * z * coeff.C2) + (3d * z * z * coeff.C3); + } + + private readonly struct Coeff + { + public Coeff(float c0, float c1, float c2, float c3) + { + this.C0 = c0; + this.C1 = c1; + this.C2 = c2; + this.C3 = c3; + } + + public float C0 { get; } + + public float C1 { get; } + + public float C2 { get; } + + public float C3 { get; } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/RoussilheStereographicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/RoussilheStereographicProjection.cs new file mode 100644 index 00000000..7935abe9 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/RoussilheStereographicProjection.cs @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Roussilhe stereographic projection (rouss). +/// +/// +/// Roussilhe Stereographic is a historical French geodetic approximation to +/// oblique stereographic. The implementation precomputes the coefficient set for the +/// Roussilhe Taylor-series expansion and uses those coefficients for the forward and +/// inverse forms. +/// This implementation matches PROJ's rouss formulation and the +/// Roussilhe stereographic description summarized by Snyder for Henri Roussilhe's +/// 1922 quasistereographic conformal projection of the ellipsoid. The forward and +/// inverse paths evaluate the precomputed coefficient families, including the +/// eleven-term inverse series, that approximate the underlying double-conformal +/// ellipsoid-to-sphere then stereographic construction. +/// +/// PROJ documentation: Roussilhe Stereographic. +internal sealed class RoussilheStereographicProjection : MapProjection +{ + private readonly double s0; + private readonly double a1; + private readonly double a2; + private readonly double a3; + private readonly double a4; + private readonly double a5; + private readonly double a6; + private readonly double b1; + private readonly double b2; + private readonly double b3; + private readonly double b4; + private readonly double b5; + private readonly double b6; + private readonly double b7; + private readonly double b8; + private readonly double c1; + private readonly double c2; + private readonly double c3; + private readonly double c4; + private readonly double c5; + private readonly double c6; + private readonly double c7; + private readonly double c8; + private readonly double d1; + private readonly double d2; + private readonly double d3; + private readonly double d4; + private readonly double d5; + private readonly double d6; + private readonly double d7; + private readonly double d8; + private readonly double d9; + private readonly double d10; + private readonly double d11; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public RoussilheStereographicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public RoussilheStereographicProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Roussilhe_Stereographic"; + + Sincos(this.latOrigin, out double sinPhi0, out double cosPhi0); + this.s0 = this.Mlfn(this.latOrigin, sinPhi0, cosPhi0); + double es2 = this.es * sinPhi0 * sinPhi0; + double t = 1d - es2; + double n0 = 1d / Math.Sqrt(t); + double oneMinusEs = 1d - this.es; + double rOverR0Squared = (t * t) / oneMinusEs; + double rOverR0Fourth = rOverR0Squared * rOverR0Squared; + double tanPhi0 = Math.Tan(this.latOrigin); + double tanPhi0Squared = tanPhi0 * tanPhi0; + + this.c1 = this.a1 = rOverR0Squared / 4d; + this.c2 = this.a2 = rOverR0Squared * ((2d * tanPhi0Squared) - 1d - (2d * es2)) / 12d; + this.a3 = rOverR0Squared * tanPhi0 * (1d + (4d * tanPhi0Squared)) / (12d * n0); + this.a4 = rOverR0Fourth / 24d; + this.a5 = rOverR0Fourth * (-1d + (tanPhi0Squared * (11d + (12d * tanPhi0Squared)))) / 24d; + this.a6 = rOverR0Fourth * (-2d + (tanPhi0Squared * (11d - (2d * tanPhi0Squared)))) / 240d; + this.b1 = tanPhi0 / (2d * n0); + this.b2 = rOverR0Squared / 12d; + this.b3 = rOverR0Squared * (1d + (2d * tanPhi0Squared) - (2d * es2)) / 4d; + this.b4 = rOverR0Squared * tanPhi0 * (2d - tanPhi0Squared) / (24d * n0); + this.b5 = rOverR0Squared * tanPhi0 * (5d + (4d * tanPhi0Squared)) / (8d * n0); + this.b6 = rOverR0Fourth * (-2d + (tanPhi0Squared * (-5d + (6d * tanPhi0Squared)))) / 48d; + this.b7 = rOverR0Fourth * (5d + (tanPhi0Squared * (19d + (12d * tanPhi0Squared)))) / 24d; + this.b8 = rOverR0Fourth / 120d; + this.c3 = rOverR0Squared * tanPhi0 * (1d + tanPhi0Squared) / (3d * n0); + this.c4 = rOverR0Fourth * (-3d + (tanPhi0Squared * (34d + (22d * tanPhi0Squared)))) / 240d; + this.c5 = rOverR0Fourth * (4d + (tanPhi0Squared * (13d + (12d * tanPhi0Squared)))) / 24d; + this.c6 = rOverR0Fourth / 16d; + this.c7 = rOverR0Fourth * tanPhi0 * (11d + (tanPhi0Squared * (33d + (tanPhi0Squared * 16d)))) / (48d * n0); + this.c8 = rOverR0Fourth * tanPhi0 * (1d + (tanPhi0Squared * 4d)) / (36d * n0); + this.d1 = tanPhi0 / (2d * n0); + this.d2 = rOverR0Squared / 12d; + this.d3 = rOverR0Squared * ((2d * tanPhi0Squared) + 1d - (2d * es2)) / 4d; + this.d4 = rOverR0Squared * tanPhi0 * (1d + tanPhi0Squared) / (8d * n0); + this.d5 = rOverR0Squared * tanPhi0 * (1d + (tanPhi0Squared * 2d)) / (4d * n0); + this.d6 = rOverR0Fourth * (1d + (tanPhi0Squared * (6d + (tanPhi0Squared * 6d)))) / 16d; + this.d7 = rOverR0Fourth * tanPhi0Squared * (3d + (tanPhi0Squared * 4d)) / 8d; + this.d8 = rOverR0Fourth / 80d; + this.d9 = rOverR0Fourth * tanPhi0 * (-21d + (tanPhi0Squared * (178d - (tanPhi0Squared * 26d)))) / 720d; + this.d10 = rOverR0Fourth * tanPhi0 * (29d + (tanPhi0Squared * (86d + (tanPhi0Squared * 48d)))) / (96d * n0); + this.d11 = rOverR0Fourth * tanPhi0 * (37d + (tanPhi0Squared * 44d)) / (96d * n0); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new RoussilheStereographicProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double cp = Math.Cos(lat); + double sp = Math.Sin(lat); + double s = this.Mlfn(lat, sp, cp) - this.s0; + double s2 = s * s; + double denominator = Math.Sqrt(1d - (this.es * sp * sp)); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double al = lon * cp / denominator; + double al2 = al * al; + double x = this.scaleFactor * al * + (1d + (s2 * (this.a1 + (s2 * this.a4))) - (al2 * (this.a2 + (s * this.a3) + (s2 * this.a5) + (al2 * this.a6)))); + double y = this.scaleFactor * + ((al2 * (this.b1 + (al2 * this.b4))) + + (s * (1d + (al2 * (this.b3 - (al2 * this.b6))) + (s2 * (this.b2 + (s2 * this.b8))) + (s * al2 * (this.b5 + (s * this.b7)))))); + + lon = this.semiMajor * x; + lat = this.semiMajor * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x / (this.semiMajor * this.scaleFactor); + double yy = y / (this.semiMajor * this.scaleFactor); + double x2 = xx * xx; + double y2 = yy * yy; + + double al = xx * (1d - (this.c1 * y2) + + (x2 * (this.c2 + (this.c3 * yy) - (this.c4 * x2) + (this.c5 * y2) - (this.c7 * x2 * yy))) + + (y2 * ((this.c6 * y2) - (this.c8 * x2 * yy)))); + + double s = this.s0 + (yy * (1d + (y2 * (-this.d2 + (this.d8 * y2))))) + + (x2 * (-this.d1 + (yy * (-this.d3 + (yy * (-this.d5 + (yy * (-this.d7 + (yy * this.d11))))))) + + (x2 * (this.d4 + (yy * (this.d6 + (yy * this.d10))) - (x2 * this.d9))))); + + double phi = this.Inv_mlfn(s); + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + if (Math.Abs(cosPhi) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lam = al * Math.Sqrt(1d - (this.es * sinPhi * sinPhi)) / cosPhi; + x = Adjust_lon(this.centralMeridian + lam); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/S2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/S2Projection.cs new file mode 100644 index 00000000..9e6dc637 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/S2Projection.cs @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the S2 projection (s2). +/// +/// +/// The S2 projection maps the sphere onto six cube faces and expresses positions +/// with face-local UV and ST coordinates. It is designed to support hierarchical cell +/// indexing and space-filling curve traversal. +/// The formulation was independently verified against the Google S2 Geometry +/// developer documentation. The face selection by dominant cartesian component, the +/// normalized u/v coordinate construction, and the subsequent ST-space +/// conversion used for Hilbert-style cell addressing match the implementation here. +/// +/// S2Geometry developer guide: S2 cell hierarchy. +/// Background overview of Google's S2 geometry. +internal sealed class S2Projection : MapProjection +{ + private const double HalfPiMinusFortPiHalf = HalfPi - (FortPi * 0.5d); + + /// + /// Small bias to avoid tangent singularities close to machine precision. + /// + private const double TangentBias = 1.1102230246251565e-16d; + + private readonly Face face; + private readonly UvToStProjectionType uvToStProjectionType; + private readonly double aSquared; + private readonly double oneMinusF; + private readonly double oneMinusFSquared; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public S2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public S2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "S2"; + this.face = DetermineFace(this.latOrigin, this.centralMeridian); + this.uvToStProjectionType = ReadUvToStProjectionType(this.Parameters); + + if (this.es != 0d) + { + this.aSquared = this.semiMajor * this.semiMajor; + this.oneMinusF = 1d - ((this.semiMajor - this.semiMinor) / this.semiMajor); + this.oneMinusFSquared = this.oneMinusF * this.oneMinusF; + } + else + { + this.aSquared = 0d; + this.oneMinusF = 1d; + this.oneMinusFSquared = 1d; + } + } + + private enum Face + { + Front = 0, + Right = 1, + Top = 2, + Back = 3, + Left = 4, + Bottom = 5, + } + + private enum UvToStProjectionType + { + Linear = 0, + Quadratic = 1, + Tangent = 2, + None = 3, + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new S2Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + // PROJ's s2 uses lon_0 only for face selection. Geographic input itself is + // not recentered by lon_0 before face UV conversion. + double lambda = lon; + double phi = this.es == 0d + ? lat + : Math.Atan(this.oneMinusFSquared * Math.Tan(lat)); + + Sincos(phi, out double sinPhi, out double cosPhi); + Sincos(lambda, out double sinLambda, out double cosLambda); + double x = cosPhi * cosLambda; + double y = cosPhi * sinLambda; + double z = sinPhi; + + ValidFaceXyzToUv(this.face, x, y, z, out double u, out double v); + lon = UvToSt(u, this.uvToStProjectionType); + lat = UvToSt(v, this.uvToStProjectionType); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double u = StToUv(x, this.uvToStProjectionType); + double v = StToUv(y, this.uvToStProjectionType); + + UvToSphereXyz(this.face, u, v, out double q, out double r, out double s); + double lambda = Math.Atan2(r, q); + double phi = Math.Acos(-s) - HalfPi; + + if (this.es != 0d) + { + bool invertSign = phi < 0d; + double tanPhi = Math.Tan(phi); + double xa = this.semiMinor / Math.Sqrt((tanPhi * tanPhi) + this.oneMinusFSquared); + if (xa == 0d) + { + phi = HalfPi; + } + else + { + double inside = this.aSquared - (xa * xa); + if (inside < 0d) + { + inside = 0d; + } + + phi = Math.Atan(Math.Sqrt(inside) / (this.oneMinusF * xa)); + } + + if (invertSign) + { + phi = -phi; + } + } + + if (Math.Abs(Math.Abs(phi) - HalfPi) <= Eps10) + { + lambda = 0d; + } + else if (Math.Abs(lambda + PI) <= Eps10) + { + lambda = PI; + } + + x = Adjust_lon(lambda); + y = phi; + } + + private static double StToUv(double s, UvToStProjectionType projectionType) + { + switch (projectionType) + { + case UvToStProjectionType.Linear: + return (2d * s) - 1d; + case UvToStProjectionType.Quadratic: + if (s >= 0.5d) + { + return ((4d * (s * s)) - 1d) / 3d; + } + + double oneMinusS = 1d - s; + return (1d - (4d * oneMinusS * oneMinusS)) / 3d; + case UvToStProjectionType.Tangent: + double tangent = Math.Tan((HalfPi * s) - FortPi); + return tangent + (TangentBias * tangent); + default: + return s; + } + } + + private static double UvToSt(double u, UvToStProjectionType projectionType) + { + switch (projectionType) + { + case UvToStProjectionType.Linear: + return 0.5d * (u + 1d); + case UvToStProjectionType.Quadratic: + if (u >= 0d) + { + return 0.5d * Math.Sqrt(Math.Max(0d, 1d + (3d * u))); + } + + return 1d - (0.5d * Math.Sqrt(Math.Max(0d, 1d - (3d * u)))); + case UvToStProjectionType.Tangent: + return (2d / PI) * (Math.Atan(u) + FortPi); + default: + return u; + } + } + + private static UvToStProjectionType ReadUvToStProjectionType(ProjectionParameterSet parameters) + { + double value = parameters.GetOptionalParameterValue("uv_to_st", 1d, "uvtost"); + if (double.IsNaN(value) || double.IsInfinity(value)) + { + ArgumentGuard.ThrowArgument("Invalid value for uv_to_st parameter: expected linear, quadratic, tangent or none.", nameof(parameters)); + } + + int mode = (int)Math.Round(value); + if (Math.Abs(value - mode) > Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for uv_to_st parameter: expected linear, quadratic, tangent or none.", nameof(parameters)); + } + + switch (mode) + { + case 0: + return UvToStProjectionType.Linear; + case 1: + return UvToStProjectionType.Quadratic; + case 2: + return UvToStProjectionType.Tangent; + case 3: + return UvToStProjectionType.None; + default: + return ArgumentGuard.ThrowArgument("Invalid value for uv_to_st parameter: expected linear, quadratic, tangent or none.", nameof(parameters)); + } + } + + private static void ValidFaceXyzToUv(Face face, double x, double y, double z, out double u, out double v) + { + switch (face) + { + case Face.Front: + u = y / x; + v = z / x; + break; + case Face.Right: + u = -x / y; + v = z / y; + break; + case Face.Top: + u = -x / z; + v = -y / z; + break; + case Face.Back: + u = z / x; + v = y / x; + break; + case Face.Left: + u = z / y; + v = -x / y; + break; + default: + u = -y / z; + v = -x / z; + break; + } + } + + private static void UvToSphereXyz(Face face, double u, double v, out double x, out double y, out double z) + { + double majorCoord = 1d / Math.Sqrt(1d + (u * u) + (v * v)); + double minorCoord1 = u * majorCoord; + double minorCoord2 = v * majorCoord; + + switch (face) + { + case Face.Front: + x = majorCoord; + y = minorCoord1; + z = minorCoord2; + break; + case Face.Right: + x = -minorCoord1; + y = majorCoord; + z = minorCoord2; + break; + case Face.Top: + x = -minorCoord1; + y = -minorCoord2; + z = majorCoord; + break; + case Face.Back: + x = -majorCoord; + y = -minorCoord2; + z = -minorCoord1; + break; + case Face.Left: + x = minorCoord2; + y = -majorCoord; + z = -minorCoord1; + break; + default: + x = minorCoord2; + y = minorCoord1; + z = -majorCoord; + break; + } + } + + private static Face DetermineFace(double phi0, double lam0) + { + if (phi0 >= HalfPiMinusFortPiHalf) + { + return Face.Top; + } + + if (phi0 <= -HalfPiMinusFortPiHalf) + { + return Face.Bottom; + } + + if (Math.Abs(lam0) <= FortPi) + { + return Face.Front; + } + + return Math.Abs(lam0) <= HalfPi + FortPi ? lam0 > 0d ? Face.Right : Face.Left : Face.Back; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/SimpleConicProjectionBase.cs b/src/ProjNet/CoordinateSystems/Projections/SimpleConicProjectionBase.cs new file mode 100644 index 00000000..91a8e6e7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/SimpleConicProjectionBase.cs @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Shared implementation for simple spherical conic projections in sconics.cpp. +/// +/// +/// All variants are spherical-only and share a common polar-conic forward and inverse +/// transform. The cone constant n and the reference radius rhoC are computed +/// differently for each variant. The Murdoch II variant uses a tangent-based radial +/// distance rather than the linear distance used by the other variants. +/// The shared formulation was independently verified against the classical simple-conic +/// family used for the Euler, Murdoch, Tissot, and Vitkovsky variants. The implementation +/// matches the common polar-conic structure x = ρ * sin(n * λ), +/// y = rho0 - ρ * cos(n * λ) with variant-specific definitions of +/// n, rhoC, and rho0. +/// +internal abstract class SimpleConicProjectionBase : MapProjection +{ + private readonly SimpleConicType type; + private readonly double n; + private readonly double rhoC; + private readonly double rho0; + private readonly double sig; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + /// Simple conic variant. + /// Projection name. + protected SimpleConicProjectionBase( + IEnumerable parameters, + MapProjection? inverse, + SimpleConicType type, + string name) + : base(parameters, inverse) + { + this.type = type; + this.Name = name; + + double phi1 = DegreesToRadians(this.Parameters.GetParameterValue("lat_1", "standard_parallel_1")); + double phi2 = DegreesToRadians(this.Parameters.GetParameterValue("lat_2", "standard_parallel_2")); + double delta = 0.5d * (phi2 - phi1); + this.sig = 0.5d * (phi2 + phi1); + + if (Math.Abs(delta) < Eps10 || Math.Abs(this.sig) < Eps10) + { + ArgumentGuard.ThrowArgument("Illegal value for lat_1 and lat_2: |lat_1 - lat_2| and |lat_1 + lat_2| should be > 0.", nameof(parameters)); + } + + switch (type) + { + case SimpleConicType.Tissot: + { + this.n = Math.Sin(this.sig); + double cs = Math.Cos(delta); + this.rhoC = (this.n / cs) + (cs / this.n); + double tissotDomain = (this.rhoC - (2d * Math.Sin(this.latOrigin))) / this.n; + if (tissotDomain < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + this.rho0 = Math.Sqrt(tissotDomain); + break; + } + + case SimpleConicType.Murdoch1: + this.rhoC = (Math.Sin(delta) / (delta * Math.Tan(this.sig))) + this.sig; + this.rho0 = this.rhoC - this.latOrigin; + this.n = Math.Sin(this.sig); + break; + + case SimpleConicType.Murdoch2: + { + double cosDelta = Math.Cos(delta); + if (cosDelta < 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double cs = Math.Sqrt(cosDelta); + this.rhoC = cs / Math.Tan(this.sig); + this.rho0 = this.rhoC + Math.Tan(this.sig - this.latOrigin); + this.n = Math.Sin(this.sig) * cs; + break; + } + + case SimpleConicType.Murdoch3: + this.rhoC = (delta / (Math.Tan(this.sig) * Math.Tan(delta))) + this.sig; + this.rho0 = this.rhoC - this.latOrigin; + this.n = Math.Sin(this.sig) * Math.Sin(delta) * Math.Tan(delta) / (delta * delta); + break; + + case SimpleConicType.Euler: + this.n = Math.Sin(this.sig) * Math.Sin(delta) / delta; + delta *= 0.5d; + this.rhoC = (delta / (Math.Tan(delta) * Math.Tan(this.sig))) + this.sig; + this.rho0 = this.rhoC - this.latOrigin; + break; + + case SimpleConicType.Vitkovsky1: + { + double cs = Math.Tan(delta); + this.n = cs * Math.Sin(this.sig) / delta; + this.rhoC = (delta / (cs * Math.Tan(this.sig))) + this.sig; + this.rho0 = this.rhoC - this.latOrigin; + break; + } + + default: + ArgumentGuard.ThrowArgumentOutOfRange(nameof(type)); + break; + } + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double rho = this.type == SimpleConicType.Murdoch2 + ? this.rhoC + Math.Tan(this.sig - lat) + : this.rhoC - lat; + double theta = lambda * this.n; + + lon = this.SphericalRadius * rho * Math.Sin(theta); + lat = this.SphericalRadius * (this.rho0 - (rho * Math.Cos(theta))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = this.rho0 - (y * this.InverseSphericalRadius); + double rho = Hypot(xUnit, yUnit); + if (this.n < 0d) + { + rho = -rho; + xUnit = -xUnit; + yUnit = -yUnit; + } + + double lambda = Math.Atan2(xUnit, yUnit) / this.n; + double phi = this.type == SimpleConicType.Murdoch2 + ? this.sig - Math.Atan(rho - this.rhoC) + : this.rhoC - rho; + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/SimpleConicType.cs b/src/ProjNet/CoordinateSystems/Projections/SimpleConicType.cs new file mode 100644 index 00000000..cfc0c7e5 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/SimpleConicType.cs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +/// +/// Identifies the simple conic projection variant. +/// +internal enum SimpleConicType +{ + /// + /// Euler projection variant. + /// + Euler = 0, + + /// + /// Murdoch projection variant I. + /// + Murdoch1 = 1, + + /// + /// Murdoch projection variant II. + /// + Murdoch2 = 2, + + /// + /// Murdoch projection variant III. + /// + Murdoch3 = 3, + + /// + /// Tissot projection variant. + /// + Tissot = 4, + + /// + /// Vitkovsky projection variant I. + /// + Vitkovsky1 = 5, +} diff --git a/src/ProjNet/CoordinateSystems/Projections/SinusoidalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/SinusoidalProjection.cs new file mode 100644 index 00000000..41ab3932 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/SinusoidalProjection.cs @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Sinusoidal projection (sinu). +/// +/// +/// The Sinusoidal projection is an equal-area pseudocylindrical projection in which parallels +/// are evenly spaced straight lines and meridians are sinusoidal curves. Both spherical and +/// ellipsoidal modes are supported. +/// The formulation was independently verified against the Wikipedia article +/// "Sinusoidal projection". The spherical equations x = λ * cos(φ), +/// y = φ and the ellipsoidal branch that combines the meridian arc +/// Mlfn(φ) with the longitude scaling +/// cos(φ) / sqrt(1 - e² * sin²(φ)) match the implementation here. +/// See also John P. Snyder, "Map Projections - A Working Manual", +/// U.S. Geological Survey Professional Paper 1395, 1987, Ch. 30, pp. 243-248, +/// for the sinusoidal projection in both its spherical and ellipsoidal forms. +/// +/// Wikipedia: Sinusoidal projection. +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.2.2, pp. 67-68. +internal sealed class SinusoidalProjection : MapProjection +{ + private readonly bool isEllipsoidal; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public SinusoidalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public SinusoidalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Sinusoidal"; + this.isEllipsoidal = this.es > 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new SinusoidalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + + if (this.isEllipsoidal) + { + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + lat = this.SphericalRadius * this.Mlfn(phi, sinPhi, cosPhi); + lon = this.SphericalRadius * lambda * cosPhi / Math.Sqrt(1d - (this.es * sinPhi * sinPhi)); + return; + } + + lon = this.SphericalRadius * lambda * Math.Cos(phi); + lat = this.SphericalRadius * phi; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + if (this.isEllipsoidal) + { + double phiEllipsoid = this.Inv_mlfn(yUnit); + double absPhi = Math.Abs(phiEllipsoid); + double lambdaEllipsoid = 0d; + + if (absPhi < HalfPi) + { + double sinPhi = Math.Sin(phiEllipsoid); + lambdaEllipsoid = xUnit * Math.Sqrt(1d - (this.es * sinPhi * sinPhi)) / Math.Cos(phiEllipsoid); + } + else if ((absPhi - Eps10) >= HalfPi) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + lambdaEllipsoid); + y = phiEllipsoid; + return; + } + + double phiSphere = yUnit; + double cosPhiSphere = Math.Cos(phiSphere); + double lambdaSphere = Math.Abs(cosPhiSphere) <= Eps10 ? 0d : (xUnit / cosPhiSphere); + + x = Adjust_lon(this.centralMeridian + lambdaSphere); + y = phiSphere; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/SpaceObliqueMercatorProjection.cs b/src/ProjNet/CoordinateSystems/Projections/SpaceObliqueMercatorProjection.cs new file mode 100644 index 00000000..799658cf --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/SpaceObliqueMercatorProjection.cs @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Space Oblique Mercator projection family +/// (som, misrsom, lsat). +/// +/// +/// This projection models the ground track of an orbiting sensor and is tailored +/// to satellite imagery with Earth-rotation compensation. The implementation supports +/// the Landsat-style parameterization used by PROJ. +/// The formulation was independently verified against John P. Snyder, +/// Space Oblique Mercator Projection - Mathematical Development, USGS Bulletin +/// 1518, 1981, and NASA Landsat documentation. The Simpson-rule integration used to +/// derive the Fourier coefficients and the Landsat-specific orbital parameter handling +/// match the implementation here. +/// +/// USGS Bulletin 1518: Space Oblique Mercator Projection - Mathematical Development. +/// NASA Landsat: Space Oblique Mercator projection overview. +internal sealed class SpaceObliqueMercatorProjection : MapProjection +{ + private readonly double a2; + private readonly double a4; + private readonly double b; + private readonly double c1; + private readonly double c3; + private readonly double q; + private readonly double t; + private readonly double u; + private readonly double w; + private readonly double p22; + private readonly double sa; + private readonly double ca; + private readonly double xj; + private readonly double rlm; + private readonly double rlm2; + private readonly double oneEs; + private readonly double roneEs; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public SpaceObliqueMercatorProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public SpaceObliqueMercatorProjection(IEnumerable parameters, MapProjection? inverse) + : base(PrepareParameters(parameters), inverse) + { + this.Name = "Space_Oblique_Mercator"; + + SomSetupParameters setup = ResolveSetupParameters(this.Parameters); + this.centralMeridian = setup.Lam0; + this.p22 = setup.P22; + this.rlm = setup.Rlm; + this.rlm2 = this.rlm + TwoPi; + + this.sa = Math.Sin(setup.Alf); + this.ca = Math.Cos(setup.Alf); + if (Math.Abs(this.ca) < 1e-9d) + { + this.ca = 1e-9d; + } + + this.oneEs = 1d - this.es; + if (this.oneEs <= 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + this.roneEs = 1d / this.oneEs; + + double esc = this.es * this.ca * this.ca; + double ess = this.es * this.sa * this.sa; + + this.w = (1d - esc) * this.roneEs; + this.w = (this.w * this.w) - 1d; + this.q = ess * this.roneEs; + this.t = ess * (2d - this.es) * this.roneEs * this.roneEs; + this.u = esc * this.roneEs; + this.xj = this.oneEs * this.oneEs * this.oneEs; + + (this.a2, this.a4, this.b, this.c1, this.c3) = this.ComputeSeriesCoefficients(); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new SpaceObliqueMercatorProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double phi = Math.Max(-HalfPi, Math.Min(HalfPi, lat)); + double lambda = Adjust_lon(lon - this.centralMeridian); + double tanphi = Math.Tan(phi); + + double lampp = phi >= 0d ? HalfPi : PI + HalfPi; + double lamdp = 0d; + double lamt = 0d; + int nn = 0; + int l; + while (true) + { + double sav = lampp; + double lamtp = lambda + (this.p22 * lampp); + double cl = Math.Cos(lamtp); + double fac = cl < 0d + ? lampp + (Math.Sin(lampp) * HalfPi) + : lampp - (Math.Sin(lampp) * HalfPi); + + for (l = 50; l >= 0; --l) + { + lamt = lambda + (this.p22 * sav); + double c = Math.Cos(lamt); + if (Math.Abs(c) < Eps7) + { + lamt -= Eps7; + } + + double xlam = ((this.oneEs * tanphi * this.sa) + (Math.Sin(lamt) * this.ca)) / c; + lamdp = Math.Atan(xlam) + fac; + if (Math.Abs(Math.Abs(sav) - Math.Abs(lamdp)) < Eps7) + { + break; + } + + sav = lamdp; + } + + if (l == 0 || ++nn >= 3 || (lamdp > this.rlm && lamdp < this.rlm2)) + { + break; + } + + if (lamdp <= this.rlm) + { + lampp = TwoPi + HalfPi; + } + else if (lamdp >= this.rlm2) + { + lampp = HalfPi; + } + } + + if (l == 0) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double sp = Math.Sin(phi); + double phidp = Asinz( + ((this.oneEs * this.ca * sp) - (this.sa * Math.Cos(phi) * Math.Sin(lamt))) + / Math.Sqrt(1d - (this.es * sp * sp))); + double tanph = Math.Log(Math.Tan(FortPi + (0.5d * phidp))); + + double sd = Math.Sin(lamdp); + double sdsq = sd * sd; + double s = this.p22 * this.sa * Math.Cos(lamdp) + * Math.Sqrt((1d + (this.t * sdsq)) / ((1d + (this.w * sdsq)) * (1d + (this.q * sdsq)))); + double d = Math.Sqrt((this.xj * this.xj) + (s * s)); + + lon = (this.b * lamdp) + + (this.a2 * Math.Sin(2d * lamdp)) + + (this.a4 * Math.Sin(4d * lamdp)) + - ((tanph * s) / d); + lat = (this.c1 * sd) + + (this.c3 * Math.Sin(3d * lamdp)) + + ((tanph * this.xj) / d); + + lon *= this.SphericalRadius; + lat *= this.SphericalRadius; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.InverseSphericalRadius; + y *= this.InverseSphericalRadius; + + double lamdp = x / this.b; + int nn = 50; + double s = 0d; + do + { + double sav = lamdp; + double sd = Math.Sin(lamdp); + double sdsq = sd * sd; + s = this.p22 * this.sa * Math.Cos(lamdp) + * Math.Sqrt((1d + (this.t * sdsq)) / ((1d + (this.w * sdsq)) * (1d + (this.q * sdsq)))); + + lamdp = x + + ((y * s) / this.xj) + - (this.a2 * Math.Sin(2d * lamdp)) + - (this.a4 * Math.Sin(4d * lamdp)) + - ((s / this.xj) * ((this.c1 * Math.Sin(lamdp)) + (this.c3 * Math.Sin(3d * lamdp)))); + lamdp /= this.b; + + if (Math.Abs(lamdp - sav) < Eps7) + { + break; + } + } + while (--nn > 0); + + double sl = Math.Sin(lamdp); + double fac = Math.Exp(Math.Sqrt(1d + ((s * s) / (this.xj * this.xj))) + * (y - (this.c1 * sl) - (this.c3 * Math.Sin(3d * lamdp)))); + double phidp = 2d * (Math.Atan(fac) - FortPi); + + double dd = sl * sl; + if (Math.Abs(Math.Cos(lamdp)) < Eps7) + { + lamdp -= Eps7; + } + + double spp = Math.Sin(phidp); + double sppsq = spp * spp; + double denom = 1d - (sppsq * (1d + this.u)); + if (denom == 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lamt = Math.Atan( + (((1d - (sppsq * this.roneEs)) * Math.Tan(lamdp) * this.ca) + - ((spp * this.sa * Math.Sqrt(((1d + (this.q * dd)) * (1d - sppsq)) - (sppsq * this.u))) / Math.Cos(lamdp))) + / denom); + + double signLam = lamt >= 0d ? 1d : -1d; + double signCosLamdp = Math.Cos(lamdp) >= 0d ? 1d : -1d; + lamt -= HalfPi * (1d - signCosLamdp) * signLam; + + double lambda = lamt - (this.p22 * lamdp); + double phiNumerator = (Math.Tan(lamdp) * Math.Cos(lamt)) - (this.ca * Math.Sin(lamt)); + double phiDenominator = this.oneEs * this.sa; + double phi = Math.Abs(this.sa) < Eps7 + ? Asinz(spp / Math.Sqrt((this.oneEs * this.oneEs) + (this.es * sppsq))) + : Math.Atan(phiNumerator / phiDenominator); + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } + + private static List PrepareParameters(IEnumerable parameters) + { + parameters = ArgumentGuard.ThrowIfNull(parameters, nameof(parameters)); + + List merged = CloneParametersList(parameters); + ProjectionParameterSet input = new(merged); + + bool hasSom = input.ContainsKey("inc_angle") || input.ContainsKey("ps_rev") || input.ContainsKey("asc_lon"); + bool hasLsat = input.ContainsKey("lsat"); + bool hasMisr = input.ContainsKey("path") && !hasLsat && !hasSom; + + if (hasSom) + { + // generic som: caller provides inclination / period / ascending longitude + } + else if (hasLsat) + { + int lsat = ReadPositiveInt(input, "lsat"); + if (lsat < 1 || lsat > 5) + { + ArgumentGuard.ThrowArgument("Invalid value for lsat: lsat should be in [1, 5] range", nameof(parameters)); + } + + int path = ReadPositiveInt(input, "path"); + int maxPath = lsat <= 3 ? 251 : 233; + if (path < 1 || path > maxPath) + { + ArgumentGuard.ThrowArgument($"Invalid value for path: path should be in [1, {maxPath}] range", nameof(parameters)); + } + + if (lsat <= 3) + { + ReplaceParameter(merged, "inc_angle", 99.092d); + ReplaceParameter(merged, "ps_rev", 103.2669323d / 1440d); + ReplaceParameter(merged, "asc_lon", 128.87d - ((360d / 251d) * path)); + } + else + { + ReplaceParameter(merged, "inc_angle", 98.2d); + ReplaceParameter(merged, "ps_rev", 98.8841202d / 1440d); + ReplaceParameter(merged, "asc_lon", 129.3d - ((360d / 233d) * path)); + } + + ReplaceParameter(merged, "som_rlm_mode", 1d); + } + else if (hasMisr) + { + int path = ReadPositiveInt(input, "path"); + if (path < 1 || path > 233) + { + ArgumentGuard.ThrowArgument("Invalid value for path: path should be in [1, 233] range", nameof(parameters)); + } + + ReplaceParameter(merged, "inc_angle", 98.30382d); + ReplaceParameter(merged, "ps_rev", 98.88d / 1440d); + ReplaceParameter(merged, "asc_lon", 129.3056d - ((360d / 233d) * path)); + ReplaceParameter(merged, "som_rlm_mode", 0d); + } + else + { + ReplaceParameter(merged, "som_rlm_mode", 0d); + } + + ProjectionParameterSet resolved = new(merged); + double ascLon = resolved.GetParameterValue("asc_lon"); + double ascLonRadians = ReadAngleRadians(ascLon, "asc_lon", -TwoPi, TwoPi, nameof(parameters)); + ReplaceParameter(merged, "central_meridian", RadiansToDegrees(ascLonRadians)); + + if (!HasParameter(merged, "inc_angle") || !HasParameter(merged, "ps_rev") || !HasParameter(merged, "asc_lon")) + { + ArgumentGuard.ThrowArgument("Missing required SOM parameters: inc_angle, ps_rev, asc_lon.", nameof(parameters)); + } + + return merged; + } + + private static SomSetupParameters ResolveSetupParameters(ProjectionParameterSet parameters) + { + double lam0 = ReadAngleRadians(parameters.GetParameterValue("asc_lon"), "asc_lon", -TwoPi, TwoPi, nameof(parameters)); + double alf = ReadAngleRadians(parameters.GetParameterValue("inc_angle"), "inc_angle", 0d, PI, nameof(parameters)); + double p22 = parameters.GetParameterValue("ps_rev"); + if (p22 < 0d) + { + ArgumentGuard.ThrowArgument("Number of days per rotation should be positive", nameof(parameters)); + } + + bool lsatMode = Math.Abs(parameters.GetOptionalParameterValue("som_rlm_mode", 0d)) > 0.5d; + double rlm = lsatMode ? PI * ((1d / 248d) + 0.5161290322580645d) : 0d; + return new SomSetupParameters(lam0, alf, p22, rlm); + } + + private static double ReadAngleRadians(double raw, string name, double minRadians, double maxRadians, string paramName) + { + if (double.IsNaN(raw) || double.IsInfinity(raw)) + { + ArgumentGuard.ThrowArgument($"Invalid value for {name}.", paramName); + } + + double radians = DegreesToRadians(raw); + if (radians < minRadians || radians > maxRadians) + { + ArgumentGuard.ThrowArgument($"Invalid value for {name}.", paramName); + } + + return radians; + } + + private static int ReadPositiveInt(ProjectionParameterSet parameters, string name) + { + double value = parameters.GetParameterValue(name); + int rounded = (int)Math.Round(value); + if (Math.Abs(value - rounded) > Eps10 || rounded <= 0) + { + ArgumentGuard.ThrowArgument($"Invalid value for {name}.", nameof(parameters)); + } + + return rounded; + } + + private static bool HasParameter(List parameters, string name) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static void ReplaceParameter(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } + + private (double A2, double A4, double B, double C1, double C3) ComputeSeriesCoefficients() + { + double a2 = 0d; + double a4 = 0d; + double b = 0d; + double c1 = 0d; + double c3 = 0d; + + this.AddSerazContribution(0d, 1d, ref a2, ref a4, ref b, ref c1, ref c3); + for (double lam = 9d; lam <= 81.0001d; lam += 18d) + { + this.AddSerazContribution(lam, 4d, ref a2, ref a4, ref b, ref c1, ref c3); + } + + for (double lam = 18d; lam <= 72.0001d; lam += 18d) + { + this.AddSerazContribution(lam, 2d, ref a2, ref a4, ref b, ref c1, ref c3); + } + + this.AddSerazContribution(90d, 1d, ref a2, ref a4, ref b, ref c1, ref c3); + + return (a2 / 30d, a4 / 60d, b / 30d, c1 / 15d, c3 / 45d); + } + + private void AddSerazContribution(double lamDegrees, double multiplier, ref double a2, ref double a4, ref double b, ref double c1, ref double c3) + { + double lam = DegreesToRadians(lamDegrees); + double sd = Math.Sin(lam); + double sdsq = sd * sd; + + double s = this.p22 * this.sa * Math.Cos(lam) + * Math.Sqrt((1d + (this.t * sdsq)) / ((1d + (this.w * sdsq)) * (1d + (this.q * sdsq)))); + + double d1 = 1d + (this.q * sdsq); + double h = Math.Sqrt((1d + (this.q * sdsq)) / (1d + (this.w * sdsq))) + * (((1d + (this.w * sdsq)) / (d1 * d1)) - (this.p22 * this.ca)); + + double sq = Math.Sqrt((this.xj * this.xj) + (s * s)); + double fc = multiplier * ((h * this.xj) - (s * s)) / sq; + + b += fc; + a2 += fc * Math.Cos(2d * lam); + a4 += fc * Math.Cos(4d * lam); + + fc = multiplier * s * (h + this.xj) / sq; + c1 += fc * Math.Cos(lam); + c3 += fc * Math.Cos(3d * lam); + } + + private readonly struct SomSetupParameters(double lam0, double alf, double p22, double rlm) + { + public double Lam0 { get; } = lam0; + + public double Alf { get; } = alf; + + public double P22 { get; } = p22; + + public double Rlm { get; } = rlm; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/SpilhausProjection.cs b/src/ProjNet/CoordinateSystems/Projections/SpilhausProjection.cs new file mode 100644 index 00000000..8a841749 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/SpilhausProjection.cs @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Spilhaus projection (spilhaus). +/// +/// +/// The Spilhaus projection is an ocean-centered world map derived from an oblique +/// Adams world-in-square construction. Its distinctive appearance comes from fixed +/// centering and rotation parameters chosen to place the world ocean into a continuous +/// layout. +/// The formulation was independently verified against Spilhaus reference material +/// and later comparative documentation, including the 2023 Scientific Data +/// article on Spilhaus ocean maps. The delegation to the Adams world-in-square basis +/// together with the fixed lon0, lat0, azimuth, and +/// rotation parameters matches the implementation here. +/// +/// Map Projections blog: Spilhaus projections. +/// Scientific Data (2023): Spilhaus ocean maps. +internal sealed class SpilhausProjection : MapProjection +{ + private const double DefaultLon0Degrees = 66.94970198d; + private const double DefaultLat0Degrees = -49.56371678d; + private const double DefaultAzimuthDegrees = 40.17823482d; + private const double DefaultRotationDegrees = 45d; + + private readonly double sinAlpha; + private readonly double cosAlpha; + private readonly double beta; + private readonly double lambda0; + private readonly double conformalDistortion; + private readonly double cosRot; + private readonly double sinRot; + private readonly double lon0; + private readonly AdamsWorldInSquare2Projection adamsWs2; + private readonly MapProjection adamsWs2Inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public SpilhausProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public SpilhausProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Spilhaus"; + + double lon0Degrees = this.Parameters.GetOptionalParameterValue("central_meridian", DefaultLon0Degrees, "longitude_of_center"); + double lat0Degrees = this.Parameters.GetOptionalParameterValue("latitude_of_origin", DefaultLat0Degrees, "latitude_of_center"); + this.lon0 = DegreesToRadians(lon0Degrees); + double phi0 = DegreesToRadians(lat0Degrees); + + double azimuth = DegreesToRadians(this.Parameters.GetOptionalParameterValue("azi", DefaultAzimuthDegrees)); + double rotation = DegreesToRadians(this.Parameters.GetOptionalParameterValue("rot", DefaultRotationDegrees)); + + Sincos(rotation, out this.sinRot, out this.cosRot); + + double conformalLatCenter = this.ToConformalLatitude(phi0); + this.sinAlpha = -Math.Cos(conformalLatCenter) * Math.Cos(azimuth); + this.cosAlpha = Math.Sqrt(Math.Max(0d, 1d - (this.sinAlpha * this.sinAlpha))); + this.lambda0 = Math.Atan2(Math.Tan(azimuth), -Math.Sin(conformalLatCenter)); + this.beta = PI + Math.Atan2(-Math.Sin(azimuth), -Math.Tan(conformalLatCenter)); + + double sinPhi0 = Math.Sin(phi0); + this.conformalDistortion = + Math.Cos(phi0) / + Math.Sqrt(1d - (this.es * sinPhi0 * sinPhi0)) / + Math.Cos(conformalLatCenter); + + this.adamsWs2 = new AdamsWorldInSquare2Projection(CreateUnitAdamsParameters()); + this.adamsWs2Inverse = (MapProjection)this.adamsWs2.Inverse(); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new SpilhausProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.lon0); + double phiConformal = this.ToConformalLatitude(lat); + double cosPhiConformal = Math.Cos(phiConformal); + double sinPhiConformal = Math.Sin(phiConformal); + + double cosLambda = Math.Cos(lambda - this.lambda0); + double sinLambda = Math.Sin(lambda - this.lambda0); + + double phiAdams = Asinz((this.sinAlpha * sinPhiConformal) - (this.cosAlpha * cosPhiConformal * cosLambda)); + double lambdaAdams = Adjust_lon( + this.beta + + Math.Atan2( + cosPhiConformal * sinLambda, + (this.sinAlpha * cosPhiConformal * cosLambda) + (this.cosAlpha * sinPhiConformal))); + + this.AdamsForward(lambdaAdams, phiAdams, out double xAdams, out double yAdams); + + double factor = this.conformalDistortion * this.scaleFactor; + double xUnit = -((xAdams * this.cosRot) + (yAdams * this.sinRot)) * factor; + double yUnit = -((xAdams * -this.sinRot) + (yAdams * this.cosRot)) * factor; + + lon = this.semiMajor * xUnit; + lat = this.semiMajor * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double factor = 1d / (this.conformalDistortion * this.scaleFactor); + double xUnit = x / this.semiMajor; + double yUnit = y / this.semiMajor; + + double xAdams = -((xUnit * this.cosRot) + (yUnit * -this.sinRot)) * factor; + double yAdams = -((xUnit * this.sinRot) + (yUnit * this.cosRot)) * factor; + + this.AdamsInverse(xAdams, yAdams, out double lambdaAdams, out double phiAdams); + + double cosPhiAdams = Math.Cos(phiAdams); + double sinPhiAdams = Math.Sin(phiAdams); + double cosLambdaAdams = Math.Cos(lambdaAdams - this.beta); + double sinLambdaAdams = Math.Sin(lambdaAdams - this.beta); + + double chi = Asinz((this.sinAlpha * sinPhiAdams) + (this.cosAlpha * cosPhiAdams * cosLambdaAdams)); + double lambda = + this.lambda0 + + Math.Atan2( + cosPhiAdams * sinLambdaAdams, + (this.sinAlpha * cosPhiAdams * cosLambdaAdams) - (this.cosAlpha * sinPhiAdams)); + + x = Adjust_lon(this.lon0 + lambda); + y = this.FromConformalLatitude(chi); + } + + private static IEnumerable CreateUnitAdamsParameters() + { + return + [ + new ProjectionParameter("semi_major", 1d), + new ProjectionParameter("semi_minor", 1d), + new ProjectionParameter("scale_factor", 1d), + new ProjectionParameter("central_meridian", 0d), + new ProjectionParameter("latitude_of_origin", 0d), + new ProjectionParameter("false_easting", 0d), + new ProjectionParameter("false_northing", 0d), + new ProjectionParameter("unit", 1d), + ]; + } + + private void AdamsForward(double lambda, double phi, out double x, out double y) + { + double lon = RadiansToDegrees(lambda); + double lat = RadiansToDegrees(phi); + double z = 0d; + this.adamsWs2.Transform(ref lon, ref lat, ref z); + x = lon; + y = lat; + } + + private void AdamsInverse(double x, double y, out double lambda, out double phi) + { + double xDeg = x; + double yDeg = y; + double z = 0d; + this.adamsWs2Inverse.Transform(ref xDeg, ref yDeg, ref z); + lambda = DegreesToRadians(xDeg); + phi = DegreesToRadians(yDeg); + } + + private double ToConformalLatitude(double phi) + { + if (this.e < Epsln) + { + return phi; + } + + double ts = Tsfnz(this.e, phi, Math.Sin(phi)); + return HalfPi - (2d * Math.Atan(ts)); + } + + private double FromConformalLatitude(double chi) + { + if (this.e < Epsln) + { + return chi; + } + + double ts = Math.Tan(0.5d * (HalfPi - chi)); + return Phi2z(this.e, ts, out _); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/StereographicProjection.cs b/src/ProjNet/CoordinateSystems/Projections/StereographicProjection.cs new file mode 100644 index 00000000..ecf3c162 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/StereographicProjection.cs @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements PROJ-style stereographic projection support for polar, oblique, and equatorial modes. +/// +/// +/// +/// The implementation follows PROJ's stere.cpp and supports ellipsoidal and spherical +/// formulations of +proj=stere, including polar true-scale handling via lat_ts. +/// +/// +/// This projection is distinct from sterea, which uses the double-projection +/// oblique stereographic alternative algorithm. +/// +/// +internal sealed class StereographicProjection : MapProjection +{ + private const int MaximumIterations = 8; + private const double IterationTolerance = 1e-10d; + private const double PolarTolerance = 1e-8d; + private const double PoleEpsilon = 1e-15d; + + private readonly double globalScale; + private readonly double reciprocalGlobalScale; + private readonly double akm1; + private readonly double sinX1; + private readonly double cosX1; + private readonly Mode mode; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public StereographicProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public StereographicProjection(IEnumerable parameters, StereographicProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Stereographic"; + this.globalScale = this.semiMajor; + this.reciprocalGlobalScale = 1d / this.globalScale; + + double absLatitudeOfOrigin = Math.Abs(this.latOrigin); + if (Math.Abs(absLatitudeOfOrigin - HalfPi) < Eps10) + { + this.mode = this.latOrigin < 0d ? Mode.SouthPole : Mode.NorthPole; + } + else + { + this.mode = absLatitudeOfOrigin > Eps10 ? Mode.Oblique : Mode.Equatorial; + } + + double latitudeOfTrueScale = Math.Abs(DegreesToRadians(this.Parameters.GetOptionalParameterValue("lat_ts", 90d))); + if (this.es != 0d) + { + switch (this.mode) + { + case Mode.NorthPole: + case Mode.SouthPole: + if (Math.Abs(latitudeOfTrueScale - HalfPi) < Eps10) + { + this.akm1 = 2d * this.scaleFactor / Math.Sqrt(Math.Pow(1d + this.e, 1d + this.e) * Math.Pow(1d - this.e, 1d - this.e)); + } + else + { + double sinLatitudeOfTrueScale = Math.Sin(latitudeOfTrueScale); + double trueScaleTs = MathHelpers.Tsfn(latitudeOfTrueScale, sinLatitudeOfTrueScale, this.e); + double eccentricTrueScale = this.e * sinLatitudeOfTrueScale; + this.akm1 = Math.Cos(latitudeOfTrueScale) / trueScaleTs; + this.akm1 /= Math.Sqrt(1d - (eccentricTrueScale * eccentricTrueScale)); + } + + break; + case Mode.Oblique: + case Mode.Equatorial: + double sinLatitudeOfOrigin = Math.Sin(this.latOrigin); + double x = (2d * Math.Atan(MathHelpers.Ssfn(this.latOrigin, sinLatitudeOfOrigin, this.e))) - HalfPi; + double eccentricOrigin = this.e * sinLatitudeOfOrigin; + this.akm1 = (2d * this.scaleFactor * Math.Cos(this.latOrigin)) / Math.Sqrt(1d - (eccentricOrigin * eccentricOrigin)); + this.sinX1 = Math.Sin(x); + this.cosX1 = Math.Cos(x); + break; + } + } + else + { + switch (this.mode) + { + case Mode.Oblique: + this.sinX1 = Math.Sin(this.latOrigin); + this.cosX1 = Math.Cos(this.latOrigin); + this.akm1 = 2d * this.scaleFactor; + break; + case Mode.Equatorial: + this.akm1 = 2d * this.scaleFactor; + break; + case Mode.SouthPole: + case Mode.NorthPole: + this.akm1 = Math.Abs(latitudeOfTrueScale - HalfPi) >= Eps10 + ? Math.Cos(latitudeOfTrueScale) / Math.Tan(FortPi - (0.5d * latitudeOfTrueScale)) + : 2d * this.scaleFactor; + break; + } + } + } + + private enum Mode + { + SouthPole = 0, + NorthPole = 1, + Oblique = 2, + Equatorial = 3, + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new StereographicProjection(this.Parameters.ToProjectionParameter(), this); + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = lon - this.centralMeridian; + double phi = lat; + double x; + double y; + + if (this.es != 0d) + { + this.EllipsoidalForward(lambda, phi, out x, out y); + } + else + { + this.SphericalForward(lambda, phi, out x, out y); + } + + lon = x * this.globalScale; + lat = y * this.globalScale; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocalGlobalScale; + y *= this.reciprocalGlobalScale; + + if (this.es != 0d) + { + this.EllipsoidalInverse(ref x, ref y); + } + else + { + this.SphericalInverse(ref x, ref y); + } + } + + private void EllipsoidalForward(double lambda, double phi, out double x, out double y) + { + double cosLambda = Math.Cos(lambda); + double sinLambda = Math.Sin(lambda); + double sinPhi = Math.Sin(phi); + double xUnit; + double yUnit; + + switch (this.mode) + { + case Mode.Oblique: + case Mode.Equatorial: + double xLatitude = (2d * Math.Atan(MathHelpers.Ssfn(phi, sinPhi, this.e))) - HalfPi; + double sinX = Math.Sin(xLatitude); + double cosX = Math.Cos(xLatitude); + if (this.mode == Mode.Oblique) + { + double denominator = this.cosX1 * (1d + (this.sinX1 * sinX) + (this.cosX1 * cosX * cosLambda)); + if (Math.Abs(denominator) <= double.Epsilon) + { + x = HugeVal; + y = HugeVal; + return; + } + + double a = this.akm1 / denominator; + yUnit = a * ((this.cosX1 * sinX) - (this.sinX1 * cosX * cosLambda)); + xUnit = a * cosX; + } + else + { + double denominator = 1d + (cosX * cosLambda); + if (Math.Abs(denominator) <= double.Epsilon) + { + x = HugeVal; + y = HugeVal; + return; + } + + double a = this.akm1 / denominator; + yUnit = a * sinX; + xUnit = a * cosX; + } + + x = xUnit * sinLambda; + y = yUnit; + return; + case Mode.SouthPole: + phi = -phi; + cosLambda = -cosLambda; + sinPhi = -sinPhi; + goto case Mode.NorthPole; + case Mode.NorthPole: + xUnit = Math.Abs(phi - HalfPi) < PoleEpsilon ? 0d : this.akm1 * MathHelpers.Tsfn(phi, sinPhi, this.e); + yUnit = -xUnit * cosLambda; + x = xUnit * sinLambda; + y = yUnit; + return; + default: + throw new InvalidOperationException("Unsupported stereographic mode."); + } + } + + private void SphericalForward(double lambda, double phi, out double x, out double y) + { + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + double cosLambda = Math.Cos(lambda); + double sinLambda = Math.Sin(lambda); + + switch (this.mode) + { + case Mode.Equatorial: + { + double denominator = 1d + (cosPhi * cosLambda); + if (denominator <= Eps10) + { + x = HugeVal; + y = HugeVal; + return; + } + + double radialScale = this.akm1 / denominator; + x = radialScale * cosPhi * sinLambda; + y = radialScale * sinPhi; + return; + } + + case Mode.Oblique: + { + double denominator = 1d + (this.sinX1 * sinPhi) + (this.cosX1 * cosPhi * cosLambda); + if (denominator <= Eps10) + { + x = HugeVal; + y = HugeVal; + return; + } + + double radialScale = this.akm1 / denominator; + x = radialScale * cosPhi * sinLambda; + y = radialScale * ((this.cosX1 * sinPhi) - (this.sinX1 * cosPhi * cosLambda)); + return; + } + + case Mode.NorthPole: + cosLambda = -cosLambda; + phi = -phi; + goto case Mode.SouthPole; + case Mode.SouthPole: + if (Math.Abs(phi - HalfPi) < PolarTolerance) + { + x = HugeVal; + y = HugeVal; + return; + } + + y = this.akm1 * Math.Tan(FortPi + (0.5d * phi)); + x = sinLambda * y; + y *= cosLambda; + return; + default: + throw new InvalidOperationException("Unsupported stereographic mode."); + } + } + + private void EllipsoidalInverse(ref double x, ref double y) + { + double rho = Math.Sqrt((x * x) + (y * y)); + double tp; + double phiL; + double halfPiShift; + double halfE; + + switch (this.mode) + { + case Mode.Oblique: + case Mode.Equatorial: + tp = 2d * Math.Atan2(rho * this.cosX1, this.akm1); + double cosPhi = Math.Cos(tp); + double sinPhi = Math.Sin(tp); + phiL = rho == 0d + ? Math.Asin(ProjectionConstants.Clamp(cosPhi * this.sinX1, -1d, 1d)) + : Math.Asin(ProjectionConstants.Clamp((cosPhi * this.sinX1) + ((y * sinPhi * this.cosX1) / rho), -1d, 1d)); + + tp = Math.Tan(0.5d * (HalfPi + phiL)); + x *= sinPhi; + y = (rho * this.cosX1 * cosPhi) - (y * this.sinX1 * sinPhi); + halfPiShift = HalfPi; + halfE = 0.5d * this.e; + break; + case Mode.NorthPole: + y = -y; + goto case Mode.SouthPole; + case Mode.SouthPole: + tp = -rho / this.akm1; + phiL = HalfPi - (2d * Math.Atan(tp)); + halfPiShift = -HalfPi; + halfE = -0.5d * this.e; + break; + default: + throw new InvalidOperationException("Unsupported stereographic mode."); + } + + for (int i = MaximumIterations; i > 0; i--) + { + double sinPhi = this.e * Math.Sin(phiL); + double phi = (2d * Math.Atan(tp * Math.Pow((1d + sinPhi) / (1d - sinPhi), halfE))) - halfPiShift; + if (Math.Abs(phiL - phi) < IterationTolerance) + { + if (this.mode == Mode.SouthPole) + { + phi = -phi; + } + + x = (x == 0d && y == 0d) ? 0d : Math.Atan2(x, y); + x += this.centralMeridian; + y = phi; + return; + } + + phiL = phi; + } + + throw new InvalidOperationException("Stereographic inverse did not converge."); + } + + private void SphericalInverse(ref double x, ref double y) + { + double rh = Math.Sqrt((x * x) + (y * y)); + double c = 2d * Math.Atan(rh / this.akm1); + double sinC = Math.Sin(c); + double cosC = Math.Cos(c); + double phi; + double lambda = 0d; + + switch (this.mode) + { + case Mode.Equatorial: + phi = Math.Abs(rh) <= Eps10 ? 0d : Math.Asin(ProjectionConstants.Clamp((y * sinC) / rh, -1d, 1d)); + if (cosC != 0d || x != 0d) + { + lambda = Math.Atan2(x * sinC, cosC * rh); + } + + break; + case Mode.Oblique: + phi = Math.Abs(rh) <= Eps10 + ? this.latOrigin + : Math.Asin(ProjectionConstants.Clamp((cosC * this.sinX1) + ((y * sinC * this.cosX1) / rh), -1d, 1d)); + c = cosC - (this.sinX1 * Math.Sin(phi)); + if (c != 0d || x != 0d) + { + lambda = Math.Atan2(x * sinC * this.cosX1, c * rh); + } + + break; + case Mode.NorthPole: + y = -y; + goto case Mode.SouthPole; + case Mode.SouthPole: + phi = Math.Abs(rh) <= Eps10 + ? this.latOrigin + : Math.Asin(ProjectionConstants.Clamp(this.mode == Mode.SouthPole ? -cosC : cosC, -1d, 1d)); + lambda = (x == 0d && y == 0d) ? 0d : Math.Atan2(x, y); + break; + default: + throw new InvalidOperationException("Unsupported stereographic mode."); + } + + x = lambda + this.centralMeridian; + y = phi; + } + + private static class MathHelpers + { + public static double Ssfn(double phi, double sinPhi, double eccentricity) + { + double eccentricSinPhi = eccentricity * sinPhi; + return Math.Tan(0.5d * (HalfPi + phi)) * Math.Pow((1d - eccentricSinPhi) / (1d + eccentricSinPhi), 0.5d * eccentricity); + } + + public static double Tsfn(double phi, double sinPhi, double eccentricity) + { + double cosPhi = Math.Cos(phi); + double t = sinPhi > 0d ? cosPhi / (1d + sinPhi) : (1d - sinPhi) / cosPhi; + return Math.Exp(eccentricity * Atanh(eccentricity * sinPhi)) * t; + } + + public static double Atanh(double x) + { + return 0.5d * Math.Log((1d + x) / (1d - x)); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/StsProjectionBase.cs b/src/ProjNet/CoordinateSystems/Projections/StsProjectionBase.cs new file mode 100644 index 00000000..c8b05aec --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/StsProjectionBase.cs @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Shared implementation for the spherical STS projection family (kav5, qua_aut, fouc, mbt_s). +/// +/// +/// STS ("sine/tangent series") is a shared spherical pseudocylindrical base used for +/// several projections that differ only by the family constants p, q, and +/// by whether the latitude branch is evaluated in sine- or tangent-mode. The common +/// formulation scales longitude by cos(φ) and then applies either the tangent or +/// sine branch controlled by tanMode. +/// +internal abstract class StsProjectionBase : MapProjection +{ + private readonly double cX; + private readonly double cY; + private readonly double cP; + private readonly bool tanMode; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + /// Projection name. + /// Projection family p-constant. + /// Projection family q-constant. + /// Indicates whether tan-mode equations are active. + protected StsProjectionBase( + IEnumerable parameters, + MapProjection? inverse, + string name, + double p, + double q, + bool tanMode) + : base(parameters, inverse) + { + this.Name = name; + this.cX = q / p; + this.cY = p; + this.cP = 1d / q; + this.tanMode = tanMode; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + + double xUnit = this.cX * lambda * Math.Cos(phi); + double yUnit = this.cY; + phi *= this.cP; + double c = Math.Cos(phi); + if (this.tanMode) + { + xUnit *= c * c; + yUnit *= Math.Tan(phi); + } + else + { + if (Math.Abs(c) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + xUnit /= c; + yUnit *= Math.Sin(phi); + } + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = (y * this.InverseSphericalRadius) / this.cY; + + double phi = this.tanMode ? Math.Atan(yUnit) : Asinz(yUnit); + double c = Math.Cos(phi); + double latitude = phi / this.cP; + double cosLatitude = Math.Cos(latitude); + if (Math.Abs(cosLatitude) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xUnit / (this.cX * cosLatitude); + if (this.tanMode) + { + double cSquared = c * c; + if (Math.Abs(cSquared) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + lambda /= cSquared; + } + else + { + lambda *= c; + } + + x = Adjust_lon(this.centralMeridian + lambda); + y = latitude; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/SwissObliqueMercatorProjection.cs b/src/ProjNet/CoordinateSystems/Projections/SwissObliqueMercatorProjection.cs new file mode 100644 index 00000000..45f8a5d0 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/SwissObliqueMercatorProjection.cs @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the Swiss Oblique Mercator projection (somerc). +/// +/// +/// An ellipsoidal oblique Mercator projection used for the Swiss national coordinate systems +/// (LV03 and LV95). The inverse transform applies an iterative Newton-Raphson algorithm +/// to recover geodetic latitude from projected northing. +/// The formulation was independently verified against Swisstopo, "Swiss Map Projections," +/// and the PROJ somerc documentation. The double projection from the ellipsoid to +/// a conformal sphere and then to an oblique Mercator plane, including the Rosenmund 1903 +/// conformal-sphere construction and Bolliger 1967 polynomial terms, matches the +/// implementation here. +/// +/// Swisstopo: Swiss map projections. +/// PROJ documentation: Swiss Oblique Mercator. +internal sealed class SwissObliqueMercatorProjection : MapProjection +{ + private const int MaximumIterations = 6; + private const double IterationTolerance = 1e-10d; + + private readonly double c; + private readonly double reciprocalC; + private readonly double halfE; + private readonly double k; + private readonly double kR; + private readonly double reciprocalKR; + private readonly double sinP0; + private readonly double cosP0; + private readonly double reciprocalOneMinusEs; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public SwissObliqueMercatorProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public SwissObliqueMercatorProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Swiss_Oblique_Mercator"; + + double oneMinusEs = 1d - this.es; + if (oneMinusEs <= 0d) + { + ArgumentGuard.ThrowArgument("Invalid ellipsoid eccentricity for Swiss Oblique Mercator projection.", nameof(parameters)); + } + + this.reciprocalOneMinusEs = 1d / oneMinusEs; + this.halfE = 0.5d * this.e; + + Sincos(this.latOrigin, out double sinPhi0, out double cosPhi0); + double cosPhi0Squared = cosPhi0 * cosPhi0; + this.c = Math.Sqrt(1d + ((this.es * cosPhi0Squared * cosPhi0Squared) * this.reciprocalOneMinusEs)); + this.reciprocalC = 1d / this.c; + + this.sinP0 = sinPhi0 / this.c; + double phiPrime0 = Asinz(this.sinP0); + this.cosP0 = Math.Cos(phiPrime0); + + double eSinPhi0 = this.e * sinPhi0; + this.k = Math.Log(Math.Tan(FortPi + (0.5d * phiPrime0))) + - (this.c * (Math.Log(Math.Tan(FortPi + (0.5d * this.latOrigin))) - (this.halfE * Math.Log((1d + eSinPhi0) / (1d - eSinPhi0))))); + this.kR = this.semiMajor * this.scaleFactor * Math.Sqrt(oneMinusEs) / (1d - (eSinPhi0 * eSinPhi0)); + this.reciprocalKR = 1d / this.kR; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new SwissObliqueMercatorProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double eSinPhi = this.e * Math.Sin(lat); + + double phiPrime = (2d * Math.Atan(Math.Exp( + (this.c * (Math.Log(Math.Tan(FortPi + (0.5d * lat))) - (this.halfE * Math.Log((1d + eSinPhi) / (1d - eSinPhi))))) + + this.k))) + - HalfPi; + double lambdaPrime = this.c * lambda; + double cosPhiPrime = Math.Cos(phiPrime); + double phiDoublePrime = Asinz((this.cosP0 * Math.Sin(phiPrime)) - (this.sinP0 * cosPhiPrime * Math.Cos(lambdaPrime))); + double cosPhiDoublePrime = Math.Cos(phiDoublePrime); + if (Math.Abs(cosPhiDoublePrime) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambdaDoublePrime = Asinz((cosPhiPrime * Math.Sin(lambdaPrime)) / cosPhiDoublePrime); + + lon = this.kR * lambdaDoublePrime; + lat = this.kR * Math.Log(Math.Tan(FortPi + (0.5d * phiDoublePrime))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double phiDoublePrime = 2d * (Math.Atan(Math.Exp(y * this.reciprocalKR)) - FortPi); + double lambdaDoublePrime = x * this.reciprocalKR; + double cosPhiDoublePrime = Math.Cos(phiDoublePrime); + double phiPrime = Asinz((this.cosP0 * Math.Sin(phiDoublePrime)) + (this.sinP0 * cosPhiDoublePrime * Math.Cos(lambdaDoublePrime))); + double cosPhiPrime = Math.Cos(phiPrime); + if (Math.Abs(cosPhiPrime) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambdaPrime = Asinz((cosPhiDoublePrime * Math.Sin(lambdaDoublePrime)) / cosPhiPrime); + double con = (this.k - Math.Log(Math.Tan(FortPi + (0.5d * phiPrime)))) * this.reciprocalC; + + double phi = phiPrime; + bool converged = false; + for (int i = 0; i < MaximumIterations; i++) + { + double eSinPhi = this.e * Math.Sin(phi); + double delta = (con + Math.Log(Math.Tan(FortPi + (0.5d * phi))) - (this.halfE * Math.Log((1d + eSinPhi) / (1d - eSinPhi)))) + * (1d - (eSinPhi * eSinPhi)) + * Math.Cos(phi) + * this.reciprocalOneMinusEs; + phi -= delta; + if (Math.Abs(delta) < IterationTolerance) + { + converged = true; + break; + } + } + + if (!converged) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + x = Adjust_lon(this.centralMeridian + (lambdaPrime * this.reciprocalC)); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/TimesProjection.cs b/src/ProjNet/CoordinateSystems/Projections/TimesProjection.cs new file mode 100644 index 00000000..bd22973f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/TimesProjection.cs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Times projection (times). +/// +/// +/// The Times projection is a spherical compromise projection popularized by +/// The Times Atlas. It combines the substitution t = tan(φ / 2) with a +/// polynomial longitude scale x = λ * (X0 - X1 * sin(π / 4 * t)²) and the +/// simple latitude relation y = Y0 * t. +/// The polynomial formulation was independently checked against PROJ's +/// times documentation, which cites Snyder's Flattening the Earth +/// (1993, pp. 213-214). The implementation uses the published fixed coefficients +/// X0, X1, and Y0 for the standard Times Atlas variant. +/// +/// PROJ documentation: Times projection. +internal sealed class TimesProjection : MapProjection +{ + private const double X0 = 0.74482d; + private const double X1 = 0.34588d; + private const double Y0 = 1.70711d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public TimesProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public TimesProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Times"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new TimesProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double t = Math.Tan(lat / 2d); + double s = Math.Sin(FortPi * t); + double s2 = s * s; + double x = lambda * (X0 - (X1 * s2)); + double y = Y0 * t; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double t = yy / Y0; + double s = Math.Sin(FortPi * t); + double s2 = s * s; + double denominator = X0 - (X1 * s2); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + double phi = 2d * Math.Atan(t); + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/TissotProjection.cs b/src/ProjNet/CoordinateSystems/Projections/TissotProjection.cs new file mode 100644 index 00000000..8f777cb3 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/TissotProjection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Tissot projection (tissot). +/// +/// +/// Tissot is a simple spherical conic specialization of . +/// Its numerical behavior follows the shared simple-conic equations with the Tissot-specific +/// equal-area style radius construction. +/// +internal sealed class TissotProjection : SimpleConicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public TissotProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public TissotProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, SimpleConicType.Tissot, "Tissot") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new TissotProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/ToblerMercatorProjection.cs b/src/ProjNet/CoordinateSystems/Projections/ToblerMercatorProjection.cs new file mode 100644 index 00000000..eaf9ec11 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/ToblerMercatorProjection.cs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Tobler-Mercator projection (tobmerc). +/// +/// +/// Tobler-Mercator is a modified spherical Mercator projection proposed by Waldo Tobler to +/// temper high-latitude east-west exaggeration. The implementation keeps the Mercator +/// northing ln(tan(π / 4 + φ / 2)) but scales longitude by cos(φ)². +/// +internal sealed class ToblerMercatorProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public ToblerMercatorProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public ToblerMercatorProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Tobler_Mercator"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ToblerMercatorProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + if (Math.Abs(lat) >= HalfPi) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = Adjust_lon(lon - this.centralMeridian); + double cosPhi = Math.Cos(lat); + lon = this.SphericalRadius * lambda * cosPhi * cosPhi; + lat = this.SphericalRadius * Math.Log(Math.Tan(FortPi + (0.5d * lat))); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + y = Math.Atan(Math.Sinh(y * this.InverseSphericalRadius)); + double cosPhi = Math.Cos(y); + x = Adjust_lon(this.centralMeridian + ((x * this.InverseSphericalRadius) / (cosPhi * cosPhi))); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/TransverseCentralCylindricalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/TransverseCentralCylindricalProjection.cs new file mode 100644 index 00000000..f49c295d --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/TransverseCentralCylindricalProjection.cs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Transverse Central Cylindrical projection (tcc). +/// +/// +/// The inverse transformation is not supported in this implementation. Transforming coordinates via the inverse +/// projection will throw an . +/// The forward formulation was independently verified against the standard spherical +/// transverse central cylindrical equations. The implementation matches the normalized +/// relations x = b / sqrt(1 - b²) with b = cos(φ) * sin(λ) and +/// y = atan2(tan(φ), cos(λ)). +/// +internal sealed class TransverseCentralCylindricalProjection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public TransverseCentralCylindricalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public TransverseCentralCylindricalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Transverse_Central_Cylindrical"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new TransverseCentralCylindricalProjection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double b = Math.Cos(lat) * Math.Sin(lambda); + double bt = 1d - (b * b); + if (bt < Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double x = b / Math.Sqrt(bt); + double y = Math.Atan2(Math.Tan(lat), Math.Cos(lambda)); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Transverse Central Cylindrical does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/TransverseCylindricalEqualAreaProjection.cs b/src/ProjNet/CoordinateSystems/Projections/TransverseCylindricalEqualAreaProjection.cs new file mode 100644 index 00000000..dc5ef105 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/TransverseCylindricalEqualAreaProjection.cs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Transverse Cylindrical Equal Area projection (tcea). +/// +/// +/// Transverse Cylindrical Equal Area is Snyder's transverse form of cylindrical equal area. +/// The implementation uses the standard spherical relations +/// x = cos(φ) * sin(λ) / k0 and +/// y = k0 * (atan2(tan(φ), cos(λ)) - phi0). +/// +internal sealed class TransverseCylindricalEqualAreaProjection : MapProjection +{ + private readonly double radius; + private readonly double inverseRadius; + private readonly double inverseScaleFactor; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public TransverseCylindricalEqualAreaProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public TransverseCylindricalEqualAreaProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Transverse_Cylindrical_Equal_Area"; + this.radius = this.semiMajor; + this.inverseRadius = 1d / this.radius; + this.inverseScaleFactor = 1d / this.scaleFactor; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new TransverseCylindricalEqualAreaProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + + lon = this.radius * (Math.Cos(lat) * Math.Sin(lambda)) * this.inverseScaleFactor; + lat = this.radius * this.scaleFactor * (Math.Atan2(Math.Tan(lat), Math.Cos(lambda)) - this.latOrigin); + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double phiPrime = (y * this.inverseRadius * this.inverseScaleFactor) + this.latOrigin; + double xScaled = x * this.scaleFactor * this.inverseRadius; + double t = Math.Sqrt(Math.Max(0d, 1d - (xScaled * xScaled))); + + y = Math.Asin(ProjectionConstants.Clamp(t * Math.Sin(phiPrime), -1d, 1d)); + x = Adjust_lon(this.centralMeridian + Math.Atan2(xScaled, t * Math.Cos(phiPrime))); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/TransverseMercator.cs b/src/ProjNet/CoordinateSystems/Projections/TransverseMercator.cs index cc81555b..8dd82e3a 100644 --- a/src/ProjNet/CoordinateSystems/Projections/TransverseMercator.cs +++ b/src/ProjNet/CoordinateSystems/Projections/TransverseMercator.cs @@ -1,254 +1,297 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.CoordinateSystems.Projections; using System; using System.Collections.Generic; using ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Projections +/// +/// Implements the Transverse Mercator map projection. +/// +/// +/// Universal (UTM) and Modified (MTM) Transverse Mercator projections. This +/// is a cylindrical projection in which the cylinder has been rotated 90°. +/// Instead of being tangent to the equator (or to another standard latitude), +/// it is tangent to a central meridian. Distortion increases with distance from +/// the central meridian. The Transverse Mercator projection is appropriate for +/// regions which have a greater extent north-south than east-west. +/// +/// This implementation follows PROJ's Evenden/Snyder approximate transverse +/// Mercator path, including the dedicated spherical formulas. It remains useful +/// as the explicit approximate kernel behind +proj=tmerc +approx and the +/// approx_tmerc registry alias, while the default ellipsoidal +/// tmerc/utm aliases are routed through the exact Poder/Engsager +/// implementation. +/// +/// Reference: John P. Snyder, Map Projections — A Working Manual, +/// U.S. Geological Survey Professional Paper 1395, 1987. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 5, Sect. 5.1.3, pp. 159-162. +internal sealed class TransverseMercator : MapProjection { - /// - /// Summary description for MathTransform. - /// - /// - /// Universal (UTM) and Modified (MTM) Transverses Mercator projections. This - /// is a cylindrical projection, in which the cylinder has been rotated 90°. - /// Instead of being tangent to the equator (or to an other standard latitude), - /// it is tangent to a central meridian. Deformation are more important as we - /// are going further from the central meridian. The Transverse Mercator - /// projection is appropriate for region witch have a greater extent north-south - /// than east-west. - /// - /// Reference: John P. Snyder (Map Projections - A Working Manual, - /// U.S. Geological Survey Professional Paper 1395, 1987) + // Maximum difference allowed when comparing real numbers. + private const double EPSILON = 1E-6d; + + // A derived quantity of eccentricity, computed by e'² = (a²-b²)/b² = es/(1-es) + // where a is the semi-major axis length and b is the semi-minor axis + // length. + private readonly double esp; + + // Meridian distance at the latitude of origin. + // Used for calculations for the ellipsoid. + private readonly double ml0; + + private readonly double reciprocSemiMajor; + + /// + /// Fraction constant 1/1 used in series expansion terms. + /// + private const double FC1 = 1.00000000000000000000000; + + /// + /// Fraction constant 1/2 used in series expansion terms. + /// + private const double FC2 = 0.50000000000000000000000; + + /// + /// Fraction constant 1/12 used in series expansion terms. + /// + private const double FC4 = 0.08333333333333333333333; + + /// + /// Fraction constant 1/20 used in series expansion terms. + /// + private const double FC5 = 0.05000000000000000000000; + + /// + /// Fraction constant 1/30 used in series expansion terms. + /// + private const double FC6 = 0.03333333333333333333333; + + /// + /// Fraction constant 1/42 used in series expansion terms. + /// + private const double FC7 = 0.02380952380952380952380; + + /// + /// Fraction constant 1/56 used in series expansion terms. + /// + private const double FC8 = 0.01785714285714285714285; + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + public TransverseMercator(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + /// The inverse projection instance, or for a forward projection. + /// + /// The parameters this projection expects are listed below. + /// + /// ParameterDescription + /// semi_majorSemi-major axis radius of the ellipsoid. + /// semi_minorSemi-minor axis radius of the ellipsoid. + /// scale_factorScale factor at the central meridian. + /// central_meridianLongitude of the central meridian. + /// latitude_of_originLatitude of the projection origin. + /// false_eastingEasting assigned to the natural origin. + /// false_northingNorthing assigned to the natural origin. + /// /// - [Serializable] - internal class TransverseMercator : MapProjection - { - // /* - // * Maximum number of iterations for iterative computations. - // */ - // private const int MAXIMUM_ITERATIONS = 15; - - // /* - // * Relative iteration precision used in the {@code mlfn} method. - // * This overrides the value in the {@link MapProjection} class. - // */ - // private const double ITERATION_TOLERANCE = 1E-11; - - /* - * Maximum difference allowed when comparing real numbers. - */ - private const double EPSILON = 1E-6; - - // /* - // * Maximum difference allowed when comparing latitudes. - // */ - // private const double EPSILON_LATITUDE = 1E-10; - - /* - * A derived quantity of eccentricity, computed by e'² = (a²-b²)/b² = es/(1-es) - * where a is the semi-major axis length and b is the semi-minor axis - * length. - */ - private readonly double _esp; - - /* - * Meridian distance at the {@code latitudeOfOrigin}. - * Used for calculations for the ellipsoid. - */ - private readonly double _ml0; - - private readonly double _reciprocSemiMajor; - - /* - * Constants used for the forward and inverse transform for the elliptical - * case of the Transverse Mercator. - */ - private const double FC1= 1.00000000000000000000000, // 1/1 - FC2= 0.50000000000000000000000, // 1/2 - FC3= 0.16666666666666666666666, // 1/6 - FC4= 0.08333333333333333333333, // 1/12 - FC5= 0.05000000000000000000000, // 1/20 - FC6= 0.03333333333333333333333, // 1/30 - FC7= 0.02380952380952380952380, // 1/42 - FC8= 0.01785714285714285714285; // 1/56 - - - - // // Variables common to all subroutines in this code file - // // ----------------------------------------------------- - // private double esp; /* eccentricity constants */ - // private double ml0; /* small value m */ - - /// - /// Creates an instance of an TransverseMercatorProjection projection object. - /// - /// List of parameters to initialize the projection. - public TransverseMercator(IEnumerable parameters) - : this(parameters, null) - { - - } - /// - /// Creates an instance of an TransverseMercatorProjection projection object. - /// - /// List of parameters to initialize the projection. - /// Flag indicating wether is a forward/projection (false) or an inverse projection (true). - /// - /// - /// ItemsDescriptions - /// semi_majorSemi major radius - /// semi_minorSemi minor radius - /// scale_factor - /// central meridian - /// latitude_origin - /// false_easting - /// false_northing - /// - /// - protected TransverseMercator(IEnumerable parameters, TransverseMercator inverse) - : base(parameters, inverse) - { - Name = "Transverse_Mercator"; - Authority = "EPSG"; - AuthorityCode = 9807; - - _esp = _es / (1.0 - _es); - _ml0 = mlfn(lat_origin, Math.Sin(lat_origin), Math.Cos(lat_origin)); - - /* - e = Math.Sqrt(_es); - ml0 = _semiMajor*mlfn(lat_origin, Math.Sin(lat_origin), Math.Cos(lat_origin)); - esp = _es / (1.0 - _es); - */ - - _reciprocSemiMajor = 1 / _semiMajor; + private TransverseMercator(IEnumerable parameters, TransverseMercator? inverse) + : base(parameters, inverse) + { + this.Name = "Transverse_Mercator"; + this.Authority = "EPSG"; + this.AuthorityCode = 9807; + + if (this.es == 0d) + { + this.esp = this.scaleFactor; + this.ml0 = 0.5d * this.scaleFactor; + } + else + { + this.esp = this.es / (1.0 - this.es); + Sincos(this.latOrigin, out double sinLatitudeOrigin, out double cosLatitudeOrigin); + this.ml0 = this.Mlfn(this.latOrigin, sinLatitudeOrigin, cosLatitudeOrigin); + } + + this.reciprocSemiMajor = 1d / this.semiMajor; + } + + /// + /// Converts coordinates in radians to projected meters. + /// + /// The longitude of the point in radians. + /// The latitude of the point in radians. + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double x = Adjust_lon(lon - this.centralMeridian); + + if (this.es == 0d) + { + this.RadiansToMetersSpherical(x, lat, out lon, out lat); + return; } - /// - /// Converts coordinates in radians to projected meters. - /// - /// The longitude of the point in radians. - /// The latitude of the point in radians. - /// Point in projected meters - protected override void RadiansToMeters(ref double lon, ref double lat) - { - double x = lon; - x = adjust_lon(x - central_meridian); - - double y = lat; - double sinphi = Math.Sin(y); - double cosphi = Math.Cos(y); - - double t = (Math.Abs(cosphi) > EPSILON) ? sinphi / cosphi : 0; + double y = lat; + double sinphi = Math.Sin(y); + double cosphi = Math.Cos(y); + + double t = (Math.Abs(cosphi) > EPSILON) ? sinphi / cosphi : 0; + t *= t; + double al = cosphi * x; + double als = al * al; + al /= Math.Sqrt(1.0 - (this.es * sinphi * sinphi)); + double n = this.esp * cosphi * cosphi; + + // NOTE: meridinal distance at latitudeOfOrigin is always 0 + y = this.Mlfn(y, sinphi, cosphi) - this.ml0 + + (sinphi * al * x * + FC2 * (1.0 + + (FC4 * als * (5.0 - t + (n * (9.0 + (4.0 * n))) + + (FC6 * als * (61.0 + (t * (t - 58.0)) + (n * (270.0 - (330.0 * t))) + + (FC8 * als * (1385.0 + (t * ((t * (543.0 - t)) - 3111.0)))))))))); + + x = al * (FC1 + (ProjectionConstants.OneSixth * als * (1.0 - t + n + + (FC5 * als * (5.0 + (t * (t - 18.0)) + (n * (14.0 - (58.0 * t))) + + (FC7 * als * (61.0 + (t * ((t * (179.0 - t)) - 479.0))))))))); + + lon = this.scaleFactor * this.semiMajor * x; + lat = this.scaleFactor * this.semiMajor * y; + } + + /// + /// Converts coordinates in projected meters to radians. + /// + /// The x-ordinate of the point. + /// The y-ordinate of the point. + protected override void MetersToRadians(ref double x, ref double y) + { + x *= this.reciprocSemiMajor; + y *= this.reciprocSemiMajor; + + if (this.es == 0d) + { + this.MetersToRadiansSpherical(ref x, ref y); + return; + } + + double phi = this.Inv_mlfn(this.ml0 + (y / this.scaleFactor)); + + if (Math.Abs(phi) >= PI / 2) + { + y = y < 0.0 ? -(PI / 2) : (PI / 2); + x = 0.0; + } + else + { + double sinphi = Math.Sin(phi); + double cosphi = Math.Cos(phi); + double t = (Math.Abs(cosphi) > EPSILON) ? sinphi / cosphi : 0.0; + double n = this.esp * cosphi * cosphi; + double con = 1.0 - (this.es * sinphi * sinphi); + double d = x * Math.Sqrt(con) / this.scaleFactor; + con *= t; t *= t; - double al = cosphi * x; - double als = al * al; - al /= Math.Sqrt(1.0 - _es * sinphi * sinphi); - double n = _esp * cosphi * cosphi; - - /* NOTE: meridinal distance at latitudeOfOrigin is always 0 */ - y = (mlfn(y, sinphi, cosphi) - _ml0 + - sinphi * al * x * - FC2 * (1.0 + - FC4 * als * (5.0 - t + n * (9.0 + 4.0 * n) + - FC6 * als * (61.0 + t * (t - 58.0) + n * (270.0 - 330.0 * t) + - FC8 * als * (1385.0 + t * (t * (543.0 - t) - 3111.0)))))); - - x = al * (FC1 + FC3 * als * (1.0 - t + n + - FC5 * als * (5.0 + t * (t - 18.0) + n * (14.0 - 58.0 * t) + - FC7 * als * (61.0 + t * (t * (179.0 - t) - 479.0))))); - - lon = scale_factor*_semiMajor*x; - lat = scale_factor*_semiMajor*y; - } - - /// - /// Converts coordinates in projected meters to radians. - /// - /// The x-ordinate of the point - /// The y-ordinate of the point - /// Transformed point in decimal degrees - protected override void MetersToRadians(ref double x, ref double y) + double ds = d * d; + + y = phi - ((con * ds / (1.0 - this.es)) * + FC2 * (1.0 - (ds * + FC4 * (5.0 + (t * (3.0 - (9.0 * n))) + (n * (1.0 - (4 * n))) - (ds * + FC6 * (61.0 + (t * (90.0 - (252.0 * n) + (45.0 * t))) + (46.0 * n) - (ds * + FC8 * (1385.0 + (t * (3633.0 + (t * (4095.0 + (1575.0 * t))))))))))))); + + x = Adjust_lon(this.centralMeridian + (d * (FC1 - (ds * ProjectionConstants.OneSixth * (1.0 + (2.0 * t) + n - + (ds * FC5 * (5.0 + (t * (28.0 + (24 * t) + (8.0 * n))) + (6.0 * n) - + (ds * FC7 * (61.0 + (t * (662.0 + (t * (1320.0 + (720.0 * t)))))))))))) / cosphi)); + } + } + + /// + /// Returns the inverse of this projection. + /// + /// IMathTransform that is the reverse of the current projection. + public override MathTransform Inverse() + { + this.inverse ??= new TransverseMercator(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + private void RadiansToMetersSpherical(double lambda, double phi, out double x, out double y) + { + double cosphi = Math.Cos(phi); + double b = cosphi * Math.Sin(lambda); + if (Math.Abs(Math.Abs(b) - 1d) <= Eps10) { - x *= _reciprocSemiMajor; - y *= _reciprocSemiMajor; + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } - double phi = inv_mlfn(_ml0 + y / scale_factor); + x = this.ml0 * Math.Log((1d + b) / (1d - b)); + if (cosphi == 1d) + { + y = (lambda < -HalfPi || lambda > HalfPi) ? PI : 0d; + } + else + { + y = (cosphi * Math.Cos(lambda)) / Math.Sqrt(1d - (b * b)); - if (Math.Abs(phi) >= PI / 2) + double absY = Math.Abs(y); + if (absY >= 1d) { - y = y < 0.0 ? -(PI / 2) : (PI / 2); - x = 0.0; + if ((absY - 1d) > Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + y = 0d; } else { - double sinphi = Math.Sin(phi); - double cosphi = Math.Cos(phi); - double t = (Math.Abs(cosphi) > EPSILON) ? sinphi / cosphi : 0.0; - double n = _esp * cosphi * cosphi; - double con = 1.0 - _es * sinphi * sinphi; - double d = x * Math.Sqrt(con) / scale_factor; - con *= t; - t *= t; - double ds = d * d; - - y = phi - (con * ds / (1.0 - _es)) * - FC2 * (1.0 - ds * - FC4 * (5.0 + t * (3.0 - 9.0 * n) + n * (1.0 - 4 * n) - ds * - FC6 * (61.0 + t * (90.0 - 252.0 * n + 45.0 * t) + 46.0 * n - ds * - FC8 * (1385.0 + t * (3633.0 + t * (4095.0 + 1574.0 * t)))))); - - x = adjust_lon(central_meridian + d * (FC1 - ds * FC3 * (1.0 + 2.0 * t + n - - ds * FC5 * (5.0 + t * (28.0 + 24 * t + 8.0 * n) + 6.0 * n - - ds * FC7 * (61.0 + t * (662.0 + t * (1320.0 + 720.0 * t)))))) / cosphi); + y = Math.Acos(y); } } - /// - /// Returns the inverse of this projection. - /// - /// IMathTransform that is the reverse of the current projection. - public override MathTransform Inverse() - { - if (_inverse==null) - _inverse = new TransverseMercator(_Parameters.ToProjectionParameter(), this); - return _inverse; - } - } + if (phi < 0d) + { + y = -y; + } + + x *= this.semiMajor; + y = this.semiMajor * this.esp * (y - this.latOrigin); + } + + private void MetersToRadiansSpherical(ref double x, ref double y) + { + double h = Math.Exp(x / this.esp); + if (h == 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double g = 0.5d * (h - (1d / h)); + double d = this.latOrigin + (y / this.esp); + h = Math.Cos(d); + + double phiArgument = (1d - (h * h)) / (1d + (g * g)); + phiArgument = ProjectionConstants.Clamp(phiArgument, 0d, 1d); + + double phi = Math.Asin(Math.Sqrt(phiArgument)); + y = d < 0d ? -Math.Abs(phi) : Math.Abs(phi); + x = (g != 0d || h != 0d) ? Adjust_lon(this.centralMeridian + Math.Atan2(g, h)) : this.centralMeridian; + } } diff --git a/src/ProjNet/CoordinateSystems/Projections/TwoPointEquidistantProjection.cs b/src/ProjNet/CoordinateSystems/Projections/TwoPointEquidistantProjection.cs new file mode 100644 index 00000000..7f9e3e41 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/TwoPointEquidistantProjection.cs @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Two Point Equidistant projection (tpeqd). +/// +/// +/// Two Point Equidistant is Snyder's spherical construction that preserves distances from two +/// control points. The implementation precomputes the geometry of the control-point pair and +/// then solves the forward and inverse forms from the two geodesic distances. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 7, Sect. 7.9, pp. 215-217. +internal sealed class TwoPointEquidistantProjection : MapProjection +{ + private readonly double cp1; + private readonly double sp1; + private readonly double cp2; + private readonly double sp2; + private readonly double ccs; + private readonly double cs; + private readonly double sc; + private readonly double r2z0; + private readonly double z02; + private readonly double dlam2; + private readonly double hz0; + private readonly double thz0; + private readonly double rhshz0; + private readonly double ca; + private readonly double sa; + private readonly double lp; + private readonly double lamc; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public TwoPointEquidistantProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public TwoPointEquidistantProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Two_Point_Equidistant"; + + double phi1 = DegreesToRadians(this.Parameters.GetParameterValue("lat_1", "standard_parallel_1")); + double lam1 = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lon_1", 0d)); + double phi2 = DegreesToRadians(this.Parameters.GetParameterValue("lat_2", "standard_parallel_2")); + double lam2 = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lon_2", 0d)); + + if (Math.Abs(phi1 - phi2) < Eps10 && Math.Abs(Adjust_lon(lam1 - lam2)) < Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_1/lon_1/lat_2/lon_2: the 2 points should be distinct.", nameof(parameters)); + } + + this.centralMeridian = Adjust_lon(0.5d * (lam1 + lam2)); + double rawDlam2 = Adjust_lon(lam2 - lam1); + this.cp1 = Math.Cos(phi1); + this.cp2 = Math.Cos(phi2); + this.sp1 = Math.Sin(phi1); + this.sp2 = Math.Sin(phi2); + this.cs = this.cp1 * this.sp2; + this.sc = this.sp1 * this.cp2; + this.ccs = this.cp1 * this.cp2 * Math.Sin(rawDlam2); + + double cp2SinDlam = this.cp2 * Math.Sin(rawDlam2); + double csMinusScCosDlam = this.cs - (this.sc * Math.Cos(rawDlam2)); + double z0 = Math.Atan2( + Math.Sqrt((cp2SinDlam * cp2SinDlam) + (csMinusScCosDlam * csMinusScCosDlam)), + (this.sp1 * this.sp2) + (this.cp1 * this.cp2 * Math.Cos(rawDlam2))); + if (Math.Abs(z0) <= Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_1 and lat_2: their absolute value should be < 90°.", nameof(parameters)); + } + + this.hz0 = 0.5d * z0; + double a12 = Math.Atan2(cp2SinDlam, csMinusScCosDlam); + double pp = Asinz(this.cp1 * Math.Sin(a12)); + this.ca = Math.Cos(pp); + this.sa = Math.Sin(pp); + this.lp = Adjust_lon(Math.Atan2(this.cp1 * Math.Cos(a12), this.sp1) - this.hz0); + this.dlam2 = rawDlam2 * 0.5d; + this.lamc = HalfPi - Math.Atan2(Math.Sin(a12) * this.sp1, Math.Cos(a12)) - this.dlam2; + this.thz0 = Math.Tan(this.hz0); + this.rhshz0 = 0.5d / Math.Sin(this.hz0); + this.r2z0 = 0.5d / z0; + this.z02 = z0 * z0; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new TwoPointEquidistantProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double sp = Math.Sin(phi); + double cp = Math.Cos(phi); + double dl1 = lambda + this.dlam2; + double dl2 = lambda - this.dlam2; + double z1 = Math.Acos(ProjectionConstants.Clamp((this.sp1 * sp) + (this.cp1 * cp * Math.Cos(dl1)), -1d, 1d)); + double z2 = Math.Acos(ProjectionConstants.Clamp((this.sp2 * sp) + (this.cp2 * cp * Math.Cos(dl2)), -1d, 1d)); + double z1Squared = z1 * z1; + double z2Squared = z2 * z2; + + double t = z1Squared - z2Squared; + double xUnit = this.r2z0 * t; + t = this.z02 - t; + double yRadicand = (4d * this.z02 * z2Squared) - (t * t); + if (yRadicand < -ProjectionConstants.Tolerance1E12) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double yUnit = this.r2z0 * Math.Sqrt(Math.Max(0d, yRadicand)); + if ((this.ccs * sp) - (cp * ((this.cs * Math.Sin(dl1)) - (this.sc * Math.Sin(dl2)))) < 0d) + { + yUnit = -yUnit; + } + + lon = this.SphericalRadius * xUnit; + lat = this.SphericalRadius * yUnit; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xUnit = x * this.InverseSphericalRadius; + double yUnit = y * this.InverseSphericalRadius; + + double cz1 = Math.Cos(Hypot(yUnit, xUnit + this.hz0)); + double cz2 = Math.Cos(Hypot(yUnit, xUnit - this.hz0)); + double s = cz1 + cz2; + double d = cz1 - cz2; + double lambda = -Math.Atan2(d, s * this.thz0); + double phi = Math.Acos(ProjectionConstants.Clamp(Hypot(this.thz0 * s, d) * this.rhshz0, -1d, 1d)); + if (yUnit < 0d) + { + phi = -phi; + } + + double sp = Math.Sin(phi); + double cp = Math.Cos(phi); + lambda -= this.lp; + double cosLambda = Math.Cos(lambda); + double phiOut = Asinz((this.sa * sp) + (this.ca * cp * cosLambda)); + double lambdaOut = Math.Atan2(cp * Math.Sin(lambda), (this.sa * cp * cosLambda) - (this.ca * sp)) + this.lamc; + + x = Adjust_lon(this.centralMeridian + lambdaOut); + y = phiOut; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/UpsProjection.cs b/src/ProjNet/CoordinateSystems/Projections/UpsProjection.cs new file mode 100644 index 00000000..f3597cbd --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/UpsProjection.cs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements PROJ's ups projection with fixed UPS defaults. +/// +/// +/// This projection is a parameter-normalized specialization of +/// for the Universal Polar +/// Stereographic grid. Its numerical behavior is therefore covered by the +/// independently verified polar stereographic formulation implemented in the +/// base class. +/// +internal sealed class UpsProjection : PolarStereographicProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public UpsProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + private UpsProjection(IEnumerable parameters, UpsProjection? inverse) + : base(NormalizeParameters(parameters), inverse) + { + this.Name = "Universal_Polar_Stereographic"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new UpsProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + private static IEnumerable NormalizeParameters(IEnumerable parameters) + { + var normalized = new ProjectionParameterSet(parameters); + bool south = Math.Abs(normalized.GetOptionalParameterValue("south", 0d)) > 0d; + + normalized.SetParameterValue("latitude_of_origin", south ? -90d : 90d); + normalized.SetParameterValue("central_meridian", 0d); + normalized.SetParameterValue("scale_factor", 0.994d); + normalized.SetParameterValue("false_easting", 2000000d); + normalized.SetParameterValue("false_northing", 2000000d); + return normalized.ToProjectionParameter(); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Urmaev5Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Urmaev5Projection.cs new file mode 100644 index 00000000..370db8ca --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Urmaev5Projection.cs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Urmaev V projection (urm5, no inverse). +/// +/// +/// Urmaev V is a forward-only spherical projection associated with Urmaev. It +/// combines the parameterized asin(n * sin(φ)) auxiliary latitude with an +/// additional cubic y-scaling term controlled by q. Inverse projection is not +/// supported in this implementation. +/// This implementation matches PROJ's urm5 parameterization with explicit +/// n, q, and α constants. It belongs to the Urmaev family of +/// pseudocylindrical projections and retains the historical forward-only behavior of the +/// published Urmaev V form. +/// +/// PROJ documentation: Urmaev V. +internal sealed class Urmaev5Projection : MapProjection +{ + private readonly double n; + private readonly double m; + private readonly double rmn; + private readonly double q3; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Urmaev5Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Urmaev5Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Urmaev_V"; + this.n = this.Parameters.GetParameterValue("n"); + if (this.n <= 0d || this.n > 1d) + { + ArgumentGuard.ThrowArgument("Invalid value for n: it should be in ]0,1] range.", nameof(parameters)); + } + + double q = this.Parameters.GetOptionalParameterValue("q", 0d); + this.q3 = q / 3d; + + double alpha = DegreesToRadians(this.Parameters.GetOptionalParameterValue("alpha", 0d)); + double t = this.n * Math.Sin(alpha); + double denom = Math.Sqrt(1d - (t * t)); + if (denom == 0d) + { + ArgumentGuard.ThrowArgument("Invalid value for n / alpha: n * sin(|alpha|) should be < 1.", nameof(parameters)); + } + + this.m = Math.Cos(alpha) / denom; + this.rmn = 1d / (this.m * this.n); + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new Urmaev5Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = Asinz(this.n * Math.Sin(lat)); + double t = phi * phi; + lon = this.SphericalRadius * this.m * lambda * Math.Cos(phi); + lat = this.SphericalRadius * phi * (1d + (t * this.q3)) * this.rmn; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Urmaev V does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/UrmaevFlatPolarSinusoidalProjection.cs b/src/ProjNet/CoordinateSystems/Projections/UrmaevFlatPolarSinusoidalProjection.cs new file mode 100644 index 00000000..3c8f928b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/UrmaevFlatPolarSinusoidalProjection.cs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Urmaev Flat-Polar Sinusoidal projection (urmfps). +/// +/// +/// Urmaev Flat-Polar Sinusoidal is a parameterized spherical pseudocylindrical projection +/// associated with Urmaev. The implementation uses the defining relation +/// φ' = asin(n * sin(φ)) and then applies the flat-polar sinusoidal scaling +/// constants, making it the verified base for delegated variants such as Wagner I. +/// +internal class UrmaevFlatPolarSinusoidalProjection : MapProjection +{ + private const double Cx = 0.8773826753d; + private const double Cy = 1.139753528477d; + + private readonly double n; + private readonly double cY; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public UrmaevFlatPolarSinusoidalProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public UrmaevFlatPolarSinusoidalProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Urmaev_Flat_Polar_Sinusoidal"; + this.n = this.Parameters.GetParameterValue("n"); + if (this.n <= 0d || this.n > 1d) + { + ArgumentGuard.ThrowArgument("Invalid value for n: it should be in ]0,1] range.", nameof(parameters)); + } + + this.cY = Cy / this.n; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new UrmaevFlatPolarSinusoidalProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = Asinz(this.n * Math.Sin(lat)); + lon = this.SphericalRadius * Cx * lambda * Math.Cos(phi); + lat = this.SphericalRadius * this.cY * phi; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double phiNormalized = yy / this.cY; + double phi = Asinz(Math.Sin(phiNormalized) / this.n); + double denominator = Cx * Math.Cos(phiNormalized); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten2Projection.cs new file mode 100644 index 00000000..26395f3a --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten2Projection.cs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical van der Grinten II projection (vandg2). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the van der Grinten II +/// construction. The implementation matches the reduced-latitude term bt, the +/// auxiliary quantity at, and the resulting circular-arc placement. +/// +internal sealed class VanDerGrinten2Projection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public VanDerGrinten2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public VanDerGrinten2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Van_der_Grinten_II"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new VanDerGrinten2Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double bt = Math.Abs((2d / PI) * lat); + double ct = 1d - (bt * bt); + if (ct < 0d) + { + ct = 0d; + } + else + { + ct = Math.Sqrt(ct); + } + + double x = 0d; + double y = PI * (lat < 0d ? -bt : bt) / (1d + ct); + if (Math.Abs(lambda) >= Eps10) + { + double at = 0.5d * Math.Abs((PI / lambda) - (lambda / PI)); + double x1 = ((ct * Math.Sqrt(1d + (at * at))) - (at * ct * ct)) / (1d + ((at * at) * (bt * bt))); + x = PI * x1; + y = PI * Math.Sqrt(Math.Max(0d, 1d - (x1 * (x1 + (2d * at))) + Eps10)); + + if (lambda < 0d) + { + x = -x; + } + + if (lat < 0d) + { + y = -y; + } + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("van der Grinten II does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten3Projection.cs b/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten3Projection.cs new file mode 100644 index 00000000..9ae67724 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten3Projection.cs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical van der Grinten III projection (vandg3). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the van der Grinten III +/// construction. The implementation matches the simplified auxiliary expression used to +/// recover x from bt and at, with y taken directly from the reduced latitude. +/// +internal sealed class VanDerGrinten3Projection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public VanDerGrinten3Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public VanDerGrinten3Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Van_der_Grinten_III"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new VanDerGrinten3Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double bt = Math.Abs((2d / PI) * lat); + double ct = 1d - (bt * bt); + if (ct < 0d) + { + ct = 0d; + } + else + { + ct = Math.Sqrt(ct); + } + + double x = 0d; + double y = PI * (lat < 0d ? -bt : bt) / (1d + ct); + if (Math.Abs(lambda) >= Eps10) + { + double at = 0.5d * Math.Abs((PI / lambda) - (lambda / PI)); + double x1 = bt / (1d + ct); + x = PI * (Math.Sqrt((at * at) + 1d - (x1 * x1)) - at); + y = PI * x1; + + if (lambda < 0d) + { + x = -x; + } + + if (lat < 0d) + { + y = -y; + } + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("van der Grinten III does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten4Projection.cs b/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten4Projection.cs new file mode 100644 index 00000000..b78ae282 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/VanDerGrinten4Projection.cs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical van der Grinten IV projection (vandg4). +/// +/// +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the van der Grinten IV +/// construction. The implementation matches the special-case branches and the general +/// auxiliary bt/ct/dt expressions used for interior points. +/// +internal sealed class VanDerGrinten4Projection : MapProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public VanDerGrinten4Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public VanDerGrinten4Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Van_der_Grinten_IV"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new VanDerGrinten4Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = lat; + double x = lambda; + double y = 0d; + + if (Math.Abs(phi) >= Eps10 && (Math.Abs(lambda) < Eps10 || Math.Abs(Math.Abs(phi) - HalfPi) < Eps10)) + { + x = 0d; + y = phi; + } + else if (Math.Abs(phi) >= Eps10) + { + double bt = Math.Abs((2d / PI) * phi); + double bt2 = bt * bt; + double ct = 0.5d * ((bt * (8d - (bt * (2d + bt2)))) - 5d) / (bt2 * (bt - 1d)); + double ct2 = ct * ct; + double dt = (2d / PI) * lambda; + dt += 1d / dt; + dt = Math.Sqrt((dt * dt) - 4d); + if ((Math.Abs(lambda) - HalfPi) < 0d) + { + dt = -dt; + } + + double dt2 = dt * dt; + double x1 = bt + ct; + x1 *= x1; + double t = bt + (3d * ct); + double ft = (x1 * (bt2 + (ct2 * dt2) - 1d)) + + ((1d - bt2) * ((bt2 * ((t * t) + (4d * ct2))) + (ct2 * ((12d * bt * ct) + (4d * ct2))))); + x1 = ((dt * (x1 + ct2 - 1d)) + (2d * Math.Sqrt(ft))) / ((4d * x1) + dt2); + x = HalfPi * x1; + y = HalfPi * Math.Sqrt(1d + (dt * Math.Abs(x1)) - (x1 * x1)); + + if (lambda < 0d) + { + x = -x; + } + + if (phi < 0d) + { + y = -y; + } + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("van der Grinten IV does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/VanDerGrintenProjection.cs b/src/ProjNet/CoordinateSystems/Projections/VanDerGrintenProjection.cs new file mode 100644 index 00000000..9e276bf2 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/VanDerGrintenProjection.cs @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the van der Grinten I projection (vandg). +/// +/// +/// Van der Grinten I maps the world into a circle by combining circular-arc construction +/// steps with polynomial and radical terms away from the equator and central meridian. +/// The formulation was independently verified against the Wikipedia article +/// "Van der Grinten projection" and John P. Snyder, Map Projections - A Working Manual +/// (USGS Professional Paper 1395, 1987), section 29. The branch structure for the equator, +/// central meridian, and general case together with the published auxiliary terms +/// al, g, and p matches the implementation here. +/// +/// Wikipedia: Van der Grinten projection. +internal sealed class VanDerGrintenProjection : MapProjection +{ + private const double TwoTwentySevenths = 2d / 27d; + private const double FourPiOverThree = 4.18879020478639098458d; + private const double PiSquared = PI * PI; + private const double TwoPiSquared = 2d * PiSquared; + private const double HalfPiSquared = 0.5d * PiSquared; + private const double InverseDomainEpsilon = 1e-16d; + private readonly bool over; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public VanDerGrintenProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public VanDerGrintenProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "VanDerGrinten"; + this.over = this.Parameters.GetOptionalParameterValue("over", 0d) != 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new VanDerGrintenProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = this.over ? lon - this.centralMeridian : Adjust_lon(lon - this.centralMeridian); + double p2 = Math.Abs(lat / HalfPi); + if ((p2 - Eps10) > 1d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + if (p2 > 1d) + { + p2 = 1d; + } + + double x = lambda; + double y = 0d; + if (Math.Abs(lat) > Eps10 && (Math.Abs(lambda) <= Eps10 || Math.Abs(p2 - 1d) < Eps10)) + { + x = 0d; + y = PI * Math.Tan(0.5d * Math.Asin(p2)); + if (lat < 0d) + { + y = -y; + } + } + else if (Math.Abs(lat) > Eps10) + { + int sign = this.over && Math.Abs(lambda) > PI ? -1 : 1; + double al = 0.5d * sign * Math.Abs((PI / lambda) - (lambda / PI)); + double al2 = al * al; + double g = Math.Sqrt(1d - (p2 * p2)); + g /= p2 + g - 1d; + double g2 = g * g; + double p = g * ((2d / p2) - 1d); + double pSquared = p * p; + + double diff = g - pSquared; + double sum = pSquared + al2; + double radicand = (al2 * diff * diff) - (sum * (g2 - pSquared)); + if (radicand < -Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + if (radicand < 0d) + { + radicand = 0d; + } + + x = PI * Math.Abs((al * diff) + Math.Sqrt(radicand)) / sum; + if (lambda < 0d) + { + x = -x; + } + + y = Math.Abs(x / PI); + y = 1d - (y * (y + (2d * al))); + if (y < -Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + if (y < 0d) + { + y = 0d; + } + else + { + y = Math.Sqrt(y) * (lat < 0d ? -PI : PI); + } + } + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double x2 = xx * xx; + + if (Math.Abs(yy) < Eps10) + { + y = 0d; + double t = (x2 * x2) + (TwoPiSquared * (x2 + HalfPiSquared)); + double lambdaEquator = Math.Abs(xx) <= Eps10 ? 0d : (0.5d * ((x2 - PiSquared) + Math.Sqrt(t)) / xx); + x = this.over ? this.centralMeridian + lambdaEquator : Adjust_lon(this.centralMeridian + lambdaEquator); + return; + } + + double ay = Math.Abs(yy); + double y2 = yy * yy; + double r = x2 + y2; + double r2 = r * r; + double c1 = -PI * ay * (r + PiSquared); + double ayr = ay * r; + double piTerm = PI * (y2 + (PI * (ay + HalfPi))); + double c3 = r2 + (TwoPi * (ayr + piTerm)); + double c2 = c1 + (PiSquared * (r - (3d * y2))); + double c0 = PI * ay; + + c2 /= c3; + double al = (c1 / c3) - (ProjectionConstants.OneThird * c2 * c2); + double m = 2d * Math.Sqrt(-ProjectionConstants.OneThird * al); + double c2Cubed = c2 * c2 * c2; + double d = (TwoTwentySevenths * c2Cubed) + (((c0 * c0) - (ProjectionConstants.OneThird * c2 * c1)) / c3); + double alMulM = al * m; + if (Math.Abs(alMulM) < InverseDomainEpsilon) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + d = 3d * d / alMulM; + double ad = Math.Abs(d); + if ((ad - Eps10) > 1d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + d = ad > 1d ? (d > 0d ? 0d : PI) : Math.Acos(d); + if (r > PiSquared) + { + d = TwoPi - d; + } + + double phi = PI * ((m * Math.Cos((d * ProjectionConstants.OneThird) + FourPiOverThree)) - (ProjectionConstants.OneThird * c2)); + if (yy < 0d) + { + phi = -phi; + } + + double t2 = r2 + (TwoPiSquared * (x2 - y2 + HalfPiSquared)); + double lambdaDenominator = Math.Abs(xx) <= Eps10 ? 0d : xx; + double lambda = Math.Abs(lambdaDenominator) <= Eps10 + ? 0d + : (0.5d * (r - PiSquared + (t2 <= 0d ? 0d : Math.Sqrt(t2))) / lambdaDenominator); + + x = this.over ? this.centralMeridian + lambda : Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Vitkovsky1Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Vitkovsky1Projection.cs new file mode 100644 index 00000000..23787b1f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Vitkovsky1Projection.cs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Vitkovsky I projection (vitk1). +/// +/// +/// Vitkovsky I is a simple spherical conic specialization of +/// . Its numerical behavior follows the shared +/// simple-conic equations with the Vitkovsky-specific tangent-derived cone constant. +/// +internal sealed class Vitkovsky1Projection : SimpleConicProjectionBase +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Vitkovsky1Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Vitkovsky1Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse, SimpleConicType.Vitkovsky1, "Vitkovsky_I") + { + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Vitkovsky1Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Wagner1Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Wagner1Projection.cs new file mode 100644 index 00000000..d874fc51 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Wagner1Projection.cs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Wagner I projection (wag1). +/// +/// +/// This projection specializes with +/// the Wagner I parameter set, so its numerical behavior follows the verified flat-polar +/// sinusoidal base formulation. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.2.2, pp. 71-72. +internal sealed class Wagner1Projection : UrmaevFlatPolarSinusoidalProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Wagner1Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Wagner1Projection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Wagner_I"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "n", 0.8660254037844386467637231707d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Wagner2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Wagner2Projection.cs new file mode 100644 index 00000000..7bedf1e4 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Wagner2Projection.cs @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Wagner II projection (wag2). +/// +/// +/// Wagner II is one of Karl Wagner's spherical pseudocylindrical projections from the +/// 1930s. The implementation uses Wagner's characteristic double-latitude sine transform +/// asin(Cp1 * sin(Cp2 * φ)) before applying the final x/y scaling constants. +/// +internal sealed class Wagner2Projection : MapProjection +{ + private const double Cx = 0.92483d; + private const double Cy = 1.38725d; + private const double Cp1 = 0.88022d; + private const double Cp2 = 0.88550d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Wagner2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Wagner2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Wagner_II"; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Wagner2Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double phi = Asinz(Cp1 * Math.Sin(Cp2 * lat)); + double x = Cx * lambda * Math.Cos(phi); + double y = Cy * phi; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = yy / Cy; + double denominator = Cx * Math.Cos(phi); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + phi = Asinz(Math.Sin(phi) / Cp1) / Cp2; + + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Wagner3Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Wagner3Projection.cs new file mode 100644 index 00000000..928c227b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Wagner3Projection.cs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Wagner III projection (wag3). +/// +/// +/// Wagner III is one of Karl Wagner's spherical pseudocylindrical projections from the +/// 1930s. It derives its longitude scale from the true-scale latitude parameter through +/// cx = cos(ts) / cos(2 * ts / 3) and then applies the family form +/// x = cx * λ * cos(2 * φ / 3). +/// +internal sealed class Wagner3Projection : MapProjection +{ + private readonly double cx; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Wagner3Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Wagner3Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Wagner_III"; + + double latTsDeg = this.Parameters.GetOptionalParameterValue("lat_ts", 0d, "latitude_true_scale"); + double ts = DegreesToRadians(latTsDeg); + double denominator = Math.Cos((2d * ts) / 3d); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + this.cx = Math.Cos(ts) / denominator; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Wagner3Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = this.cx * lambda * Math.Cos(ProjectionConstants.TwoThirds * lat); + double y = lat; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + double phi = yy; + double denominator = this.cx * Math.Cos(ProjectionConstants.TwoThirds * phi); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Wagner4Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Wagner4Projection.cs new file mode 100644 index 00000000..ba21a4c1 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Wagner4Projection.cs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Wagner IV projection (wag4). +/// +/// +/// This projection specializes through a fixed +/// auxiliary-angle parameter set, so its numerical behavior follows the verified +/// Mollweide base formulation. +/// +/// Bugayevskiy & Snyder (1995), "Map Projections: A Reference Manual", Ch. 2, Sect. 2.2.2, pp. 76-77. +internal sealed class Wagner4Projection : MollweideProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Wagner4Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Wagner4Projection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Wagner_IV"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "moll_p", 60d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Wagner5Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Wagner5Projection.cs new file mode 100644 index 00000000..e182fe8e --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Wagner5Projection.cs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Wagner V projection (wag5). +/// +/// +/// This projection specializes through fixed +/// coefficient overrides, so its numerical behavior follows the verified Mollweide +/// base formulation. +/// +internal sealed class Wagner5Projection : MollweideProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Wagner5Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Wagner5Projection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Wagner_V"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "moll_cx", 0.90977d); + ReplaceOrAdd(merged, "moll_cy", 1.65014d); + ReplaceOrAdd(merged, "moll_cp", 3.00896d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Wagner6Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Wagner6Projection.cs new file mode 100644 index 00000000..280f6e6c --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Wagner6Projection.cs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Wagner VI projection (wag6). +/// +/// +/// This projection specializes with the Wagner VI +/// coefficient set, so its numerical behavior follows the same verified Eckert III +/// style base formulation. +/// +internal sealed class Wagner6Projection : Eckert3Projection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Wagner6Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Wagner6Projection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Wagner_VI"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "eck3_a", 0d); + ReplaceOrAdd(merged, "eck3_b", 0.30396355092701331433d); + ReplaceOrAdd(merged, "eck3_cx", 1d); + ReplaceOrAdd(merged, "eck3_cy", 1d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Wagner7Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Wagner7Projection.cs new file mode 100644 index 00000000..1357177c --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Wagner7Projection.cs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Wagner VII projection (wag7). +/// +/// +/// Wagner VII is an equal-area polyconic projection with curved meridians and parallels. +/// Inverse projection is not supported in this implementation. +/// The forward formulation was independently verified against the standard Wagner VII +/// construction. The implementation matches the auxiliary latitude +/// θ = asin(0.9063077870 * sin(φ)), the one-third longitude step, and the final +/// Hammer-like normalization factor. +/// +internal sealed class Wagner7Projection : MapProjection +{ + private const double YPreFactor = 0.90630778703664996d; + private const double XFactor = 2.66723d; + private const double YFactor = 1.24104d; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Wagner7Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Wagner7Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Wagner_VII"; + } + + /// + protected override bool HasInverseSupport => false; + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new Wagner7Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double yTemp = YPreFactor * Math.Sin(lat); + double theta = Asinz(yTemp); + double cosTheta = Math.Cos(theta); + double lambdaThird = lambda / 3d; + + double x = XFactor * cosTheta * Math.Sin(lambdaThird); + double denominator = Math.Sqrt(0.5d * (1d + (cosTheta * Math.Cos(lambdaThird)))); + if (denominator <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double scale = 1d / denominator; + x *= scale; + double y = yTemp * YFactor * scale; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + throw new InvalidOperationException("Wagner VII does not support inverse projection in this wave."); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/WerenskioldProjection.cs b/src/ProjNet/CoordinateSystems/Projections/WerenskioldProjection.cs new file mode 100644 index 00000000..69fc4d9a --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/WerenskioldProjection.cs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; + +/// +/// Implements the spherical Werenskiold I projection (weren). +/// +/// +/// This projection specializes with the +/// Werenskiold coefficient set, so its numerical behavior follows the same verified +/// base formulation. +/// +internal sealed class WerenskioldProjection : PutninsP4PProjection +{ + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public WerenskioldProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public WerenskioldProjection(IEnumerable parameters, MapProjection? inverse) + : base(MergeParameters(parameters), inverse) + { + this.Name = "Werenskiold_I"; + } + + private static List MergeParameters(IEnumerable parameters) + { + List merged = CloneParametersList(parameters); + ReplaceOrAdd(merged, "putp4p_cx", 1d); + ReplaceOrAdd(merged, "putp4p_cy", 4.442882938d); + return merged; + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(parameters[i].Name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Winkel1Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Winkel1Projection.cs new file mode 100644 index 00000000..320b96cb --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Winkel1Projection.cs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Winkel I projection (wink1). +/// +/// +/// Winkel I is the 1914 projection introduced by Oswald Winkel. The +/// implementation applies the classic averaged longitude scale +/// 0.5 * λ * (cos(phi1) + cos(φ)) with the configurable true-scale +/// latitude parameter. +/// This implementation matches PROJ's wink1 formulation for the +/// arithmetic mean of the sinusoidal and equidistant cylindrical projections, with +/// the configurable true-scale latitude preserving Winkel's standard-parallel +/// variant. +/// +/// PROJ documentation: Winkel I. +/// ArcGIS projection reference: Winkel I. +internal sealed class Winkel1Projection : MapProjection +{ + private readonly double cosphi1; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Winkel1Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Winkel1Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Winkel_I"; + + double latTs = DegreesToRadians(this.Parameters.GetOptionalParameterValue("lat_ts", RadiansToDegrees(this.latOrigin), "latitude_true_scale")); + this.cosphi1 = Math.Cos(latTs); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Winkel1Projection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double x = 0.5d * lambda * (this.cosphi1 + Math.Cos(lat)); + double y = lat; + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + double denominator = this.cosphi1 + Math.Cos(yy); + if (Math.Abs(denominator) <= Eps10) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + double lambda = 2d * xx / denominator; + x = Adjust_lon(this.centralMeridian + lambda); + y = yy; + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/Winkel2Projection.cs b/src/ProjNet/CoordinateSystems/Projections/Winkel2Projection.cs new file mode 100644 index 00000000..40007402 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/Winkel2Projection.cs @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Winkel II projection (wink2). +/// +/// +/// Winkel II is Oswald Winkel's 1918 compromise projection. The +/// implementation iteratively solves the Mollweide-like auxiliary latitude and then +/// combines that result with the Winkel horizontal averaging term. +/// This implementation matches PROJ's wink2 formulation for the +/// arithmetic mean of the Mollweide and equidistant cylindrical projections. The +/// inverse transform follows PROJ's spherical wink2_s_inverse behavior via +/// a numerical inverse over the same forward equations. +/// +/// PROJ documentation: Winkel II. +/// ArcGIS projection reference: Winkel II. +internal sealed class Winkel2Projection : MapProjection +{ + private const int MaximumIterations = 10; + private const int InverseMaximumIterations = 15; + private const double InverseTolerance = 1e-10d; + private const double FiniteDifferenceStep = 1e-8d; + private const double MaximumCorrection = 0.3d; + + private readonly double cosphi1; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public Winkel2Projection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public Winkel2Projection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Winkel_II"; + double lat1Degrees = this.Parameters.GetOptionalParameterValue("lat_1", RadiansToDegrees(this.latOrigin), "standard_parallel_1"); + this.cosphi1 = Math.Cos(DegreesToRadians(lat1Degrees)); + } + + /// + public override MathTransform Inverse() + { + return this.GetOrCreateInverse(() => new Winkel2Projection(this.Parameters.ToProjectionParameter(), this)); + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + double yPrime = lat * (2d / PI); + double k = PI * Math.Sin(lat); + double phi = 1.8d * lat; + int i = MaximumIterations; + + for (; i > 0; i--) + { + double v = (phi + Math.Sin(phi) - k) / (1d + Math.Cos(phi)); + phi -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + phi = i == 0 ? (phi < 0d ? -HalfPi : HalfPi) : (0.5d * phi); + double x = 0.5d * lambda * (Math.Cos(phi) + this.cosphi1); + double y = FortPi * (Math.Sin(phi) + yPrime); + + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double targetX = x / this.SphericalRadius; + double targetY = y / this.SphericalRadius; + + double lambda = targetX; + double phi = targetY; + double derivLamX = 0d; + double derivLamY = 0d; + double derivPhiX = 0d; + double derivPhiY = 0d; + + for (int i = 0; i < InverseMaximumIterations; i++) + { + this.ForwardNormalized(lambda, phi, out double approxX, out double approxY); + double errorX = approxX - targetX; + double errorY = approxY - targetY; + if (Math.Abs(errorX) <= InverseTolerance && Math.Abs(errorY) <= InverseTolerance) + { + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + return; + } + + if (i == 0 || Math.Abs(errorX) > 1e-6d || Math.Abs(errorY) > 1e-6d) + { + double deltaLambdaStep = lambda > 0d ? -FiniteDifferenceStep : FiniteDifferenceStep; + this.ForwardNormalized(lambda + deltaLambdaStep, phi, out double xLambda, out double yLambda); + double derivXLambda = (xLambda - approxX) / deltaLambdaStep; + double derivYLambda = (yLambda - approxY) / deltaLambdaStep; + + double deltaPhiStep = phi > 0d ? -FiniteDifferenceStep : FiniteDifferenceStep; + this.ForwardNormalized(lambda, phi + deltaPhiStep, out double xPhi, out double yPhi); + double derivXPhi = (xPhi - approxX) / deltaPhiStep; + double derivYPhi = (yPhi - approxY) / deltaPhiStep; + + double determinant = (derivXLambda * derivYPhi) - (derivXPhi * derivYLambda); + if (determinant == 0d) + { + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + derivLamX = derivYPhi / determinant; + derivLamY = -derivXPhi / determinant; + derivPhiX = -derivYLambda / determinant; + derivPhiY = derivXLambda / determinant; + } + + double deltaLambda = Math.Max(Math.Min((errorX * derivLamX) + (errorY * derivLamY), MaximumCorrection), -MaximumCorrection); + double deltaPhi = Math.Max(Math.Min((errorX * derivPhiX) + (errorY * derivPhiY), MaximumCorrection), -MaximumCorrection); + + lambda -= deltaLambda; + phi -= deltaPhi; + + if (lambda < -PI) + { + lambda = -PI; + } + else if (lambda > PI) + { + lambda = PI; + } + + if (phi < -HalfPi) + { + phi = -HalfPi; + } + else if (phi > HalfPi) + { + phi = HalfPi; + } + } + + ProjectionThrowHelper.ThrowOutsideProjectionDomain(); + } + + private void ForwardNormalized(double lambda, double phi, out double x, out double y) + { + double yPrime = phi * (2d / PI); + double k = PI * Math.Sin(phi); + double phiWorking = 1.8d * phi; + int i = MaximumIterations; + + for (; i > 0; i--) + { + double denominator = 1d + Math.Cos(phiWorking); + if (Math.Abs(denominator) <= Eps10) + { + break; + } + + double v = (phiWorking + Math.Sin(phiWorking) - k) / denominator; + phiWorking -= v; + if (Math.Abs(v) < Eps7) + { + break; + } + } + + phiWorking = i == 0 ? (phiWorking < 0d ? -HalfPi : HalfPi) : (0.5d * phiWorking); + x = 0.5d * lambda * (Math.Cos(phiWorking) + this.cosphi1); + y = FortPi * (Math.Sin(phiWorking) + yPrime); + } +} diff --git a/src/ProjNet/CoordinateSystems/Projections/WinkelTripelProjection.cs b/src/ProjNet/CoordinateSystems/Projections/WinkelTripelProjection.cs new file mode 100644 index 00000000..24dbcc5d --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Projections/WinkelTripelProjection.cs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Projections; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Implements the spherical Winkel Tripel projection (wintri). +/// +/// +/// Winkel Tripel blends the Aitoff projection with an equirectangular projection at a +/// standard parallel whose cosine defaults to 2 / π. The formulation was +/// independently verified against the Wikipedia article "Winkel tripel projection". +/// The averaged forward relations 0.5 * (xAitoff + λ * cos(phi1)) and +/// 0.5 * (yAitoff + φ) match the implementation here. +/// +/// Wikipedia: Winkel tripel projection. +internal sealed class WinkelTripelProjection : MapProjection +{ + /// + /// Default cosine of the standard parallel (approximately 50°28'). + /// + private const double DefaultCosphi1 = 0.636619772367581343d; + + private readonly double cosphi1; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + public WinkelTripelProjection(IEnumerable parameters) + : this(parameters, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters. + /// Inverse transform instance when cloning. + public WinkelTripelProjection(IEnumerable parameters, MapProjection? inverse) + : base(parameters, inverse) + { + this.Name = "Winkel_Tripel"; + + bool hasLat1 = this.Parameters.ContainsKey("lat_1") || this.Parameters.ContainsKey("standard_parallel_1"); + if (hasLat1) + { + double lat1 = DegreesToRadians(this.Parameters.GetParameterValue("lat_1", "standard_parallel_1")); + this.cosphi1 = Math.Cos(lat1); + if (Math.Abs(this.cosphi1) <= Eps10) + { + ArgumentGuard.ThrowArgument("Invalid value for lat_1: |lat_1| should be < 90°.", nameof(parameters)); + } + } + else + { + this.cosphi1 = DefaultCosphi1; + } + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new WinkelTripelProjection(this.Parameters.ToProjectionParameter(), this); + + return this.inverse; + } + + /// + protected override void RadiansToMeters(ref double lon, ref double lat) + { + double lambda = Adjust_lon(lon - this.centralMeridian); + AitoffMath.Forward(lambda, lat, true, this.cosphi1, out double x, out double y); + lon = this.SphericalRadius * x; + lat = this.SphericalRadius * y; + } + + /// + protected override void MetersToRadians(ref double x, ref double y) + { + double xx = x * this.InverseSphericalRadius; + double yy = y * this.InverseSphericalRadius; + + AitoffMath.Inverse(xx, yy, true, this.cosphi1, out double lambda, out double phi); + x = Adjust_lon(this.centralMeridian + lambda); + y = phi; + } +} diff --git a/src/ProjNet/CoordinateSystems/TemporalCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/TemporalCoordinateSystem.cs new file mode 100644 index 00000000..fc85b380 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/TemporalCoordinateSystem.cs @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A one-dimensional temporal coordinate system. +/// +public sealed class TemporalCoordinateSystem : CoordinateSystem +{ + /// + /// Initializes a new instance of the class. + /// + /// Time unit. + /// Temporal datum. + /// Axis information. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public TemporalCoordinateSystem( + TimeUnit timeUnit, + TemporalDatum temporalDatum, + AxisInfo axisInfo, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks, CreateAxisInfo(axisInfo), null) + { + this.TimeUnit = ArgumentGuard.ThrowIfNull(timeUnit, nameof(timeUnit)); + this.TemporalDatum = ArgumentGuard.ThrowIfNull(temporalDatum, nameof(temporalDatum)); + } + + /// + /// Gets the temporal datum. + /// + public TemporalDatum TemporalDatum { get; } + + /// + /// Gets the time unit. + /// + public TimeUnit TimeUnit { get; } + + /// + public override string WKT => this.ToWktNode(WktVersion.Wkt22019).ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new TemporalCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new TemporalCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + public override XElement ToXml() + { + var innerElement = new XElement("CS_TemporalCoordinateSystem"); + innerElement.Add(this.InfoXmlElement); + innerElement.Add(this.GetAxis(0).ToXml()); + innerElement.Add(this.TemporalDatum.ToXml()); + innerElement.Add(this.TimeUnit.ToXml()); + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override IUnit GetUnits(int dimension) + { + if (dimension != 0) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(dimension), "Temporal coordinate systems have only one dimension."); + } + + return this.TimeUnit; + } + + /// + public override WktNode ToWktNode() + { + return this.ToWktNode(WktVersion.Wkt22019); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return new WktKeywordNode( + "LOCAL_CS", + new WktQuotedString(this.Name), + this.TemporalDatum.ToWktNode(), + this.TimeUnit.ToWktNode(), + this.GetAxis(0).ToWktNode()); + } + + var children = new List + { + new WktQuotedString(this.Name), + this.TemporalDatum.ToWktNode(version), + new WktKeywordNode( + "CS", + new WktIdentifier("temporal"), + new WktInteger(this.Dimension)), + this.GetAxis(0).ToWktNode(version), + this.TimeUnit.ToWktNode(version), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("TIMECRS", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is TemporalCoordinateSystem temporalCoordinateSystem + && temporalCoordinateSystem.TemporalDatum.EqualParams(this.TemporalDatum) + && temporalCoordinateSystem.TimeUnit.EqualParams(this.TimeUnit) + && temporalCoordinateSystem.GetAxis(0).Orientation == this.GetAxis(0).Orientation; + } + + private static List CreateAxisInfo(AxisInfo axisInfo) + => [ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo))]; +} diff --git a/src/ProjNet/CoordinateSystems/TemporalDatum.cs b/src/ProjNet/CoordinateSystems/TemporalDatum.cs new file mode 100644 index 00000000..cfcc4a24 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/TemporalDatum.cs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// A temporal datum used by temporal coordinate reference systems. +/// +public sealed class TemporalDatum : Datum +{ + /// + /// Initializes a new instance of the class. + /// + /// Time origin as declared in the WKT2 TIMEORIGIN block. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Provider-supplied remarks. + /// Abbreviation. + public TemporalDatum(string timeOrigin, string name, string authority, long authorityCode, string alias, string remarks, string abbreviation) + : base(DatumType.TD_Other, name, authority, authorityCode, alias, remarks, abbreviation) + { + this.TimeOrigin = string.IsNullOrWhiteSpace(timeOrigin) + ? ArgumentGuard.ThrowArgument("Temporal datums require a time origin.", nameof(timeOrigin)) + : timeOrigin; + } + + /// + /// Gets the declared time origin. + /// + public string TimeOrigin { get; } + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this datum with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new TemporalDatum WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this datum with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new TemporalDatum WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Creates a copy of this datum with updated retained datum-ensemble metadata. + /// + /// Replacement ensemble metadata, or to keep this datum non-ensemble-backed. + /// A new with updated ensemble metadata. + /// Thrown when is not . + public new TemporalDatum WithEnsemble(DatumEnsemble? ensemble) => InfoAuthorityCloneHelper.CloneWithEnsemble(this, ensemble); + + /// + /// Returns an XML representation of this temporal datum as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement("CS_TemporalDatum", new XAttribute("TimeOrigin", this.TimeOrigin)); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this temporal datum to a WKT syntax tree node. + /// + /// A representing this temporal datum. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("LOCAL_DATUM", children); + } + + /// + /// Converts this temporal datum to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this temporal datum in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktKeywordNode("TIMEORIGIN", new WktQuotedString(this.TimeOrigin)), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("TDATUM", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is TemporalDatum temporalDatum + && base.EqualParams(temporalDatum) + && string.Equals(this.TimeOrigin, temporalDatum.TimeOrigin, StringComparison.Ordinal); + } +} diff --git a/src/ProjNet/CoordinateSystems/TimeUnit.cs b/src/ProjNet/CoordinateSystems/TimeUnit.cs new file mode 100644 index 00000000..55fe0666 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/TimeUnit.cs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; + +/// +/// Definition of temporal units. +/// +public sealed class TimeUnit : Info, IUnit +{ + /// + /// Initializes a new instance of the class. + /// + /// Number of seconds per time unit. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public TimeUnit(double conversionFactor, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) + { + this.ConversionFactor = conversionFactor; + } + + /// + /// Gets the number of seconds per time unit. + /// + public double ConversionFactor { get; } + + /// + /// Gets the Well-known text for this object. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this unit with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new TimeUnit WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this unit with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new TimeUnit WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this time unit as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_TimeUnit", + new XAttribute("SecondsPerUnit", this.ConversionFactor.ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this time unit to a WKT syntax tree node. + /// + /// A representing this time unit. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.ConversionFactor), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("UNIT", children); + } + + /// + /// Converts this time unit to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this time unit in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.ConversionFactor), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("TIMEUNIT", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is TimeUnit timeUnit && timeUnit.ConversionFactor == this.ConversionFactor; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) => this.WithAuthority(authority, code); + + /// + private protected override Info CloneWithNameCore(string name) => this.WithName(name); +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/AffineRuntimeMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/AffineRuntimeMathTransform.cs new file mode 100644 index 00000000..d33638a7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/AffineRuntimeMathTransform.cs @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems.Transformations.Numerics; + +/// +/// Implements PROJ's affine runtime transform. +/// +/// +/// This runtime applies a full 3D affine mapping with translation and optional +/// temporal scaling. The inverse is derived analytically from the 3x3 spatial +/// matrix using the adjugate/cofactor form, and inverse creation is rejected +/// when the determinant magnitude falls below 1e-30 or when +/// +tscale is zero. +/// +/// PROJ: affine transformation. +internal sealed class AffineRuntimeMathTransform : MathTransform +{ + private readonly Vector3D offset; + private readonly double tOffset; + private readonly Matrix3x3 spatialMatrix; + private readonly double tScale; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Offset applied to X. + /// Offset applied to Y. + /// Offset applied to Z. + /// Offset applied to T. + /// Spatial matrix term S11. + /// Spatial matrix term S12. + /// Spatial matrix term S13. + /// Spatial matrix term S21. + /// Spatial matrix term S22. + /// Spatial matrix term S23. + /// Spatial matrix term S31. + /// Spatial matrix term S32. + /// Spatial matrix term S33. + /// Time scale multiplier. + private AffineRuntimeMathTransform( + double xOffset, + double yOffset, + double zOffset, + double tOffset, + double s11, + double s12, + double s13, + double s21, + double s22, + double s23, + double s31, + double s32, + double s33, + double tScale) + { + ArgumentGuard.ThrowIfNotFinite(xOffset, nameof(xOffset), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(yOffset, nameof(yOffset), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(zOffset, nameof(zOffset), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(tOffset, nameof(tOffset), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s11, nameof(s11), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s12, nameof(s12), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s13, nameof(s13), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s21, nameof(s21), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s22, nameof(s22), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s23, nameof(s23), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s31, nameof(s31), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s32, nameof(s32), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(s33, nameof(s33), "Affine parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(tScale, nameof(tScale), "Affine parameters must be finite."); + + this.offset = new Vector3D(xOffset, yOffset, zOffset); + this.tOffset = tOffset; + this.spatialMatrix = new Matrix3x3(s11, s12, s13, s21, s22, s23, s31, s32, s33); + this.tScale = tScale; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + /// Creates an from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "affine arguments were null."; + return false; + } + + if (!SpanParseUtility.TryGetOptionalDouble(args, "xoff", 0d, out double xOffset, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "yoff", 0d, out double yOffset, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "zoff", 0d, out double zOffset, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "toff", 0d, out double tOffset, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s11", 1d, out double s11, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s12", 0d, out double s12, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s13", 0d, out double s13, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s21", 0d, out double s21, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s22", 1d, out double s22, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s23", 0d, out double s23, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s31", 0d, out double s31, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s32", 0d, out double s32, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s33", 1d, out double s33, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "tscale", 1d, out double tScale, out skipReason)) + { + return false; + } + + var affine = new AffineRuntimeMathTransform( + xOffset, + yOffset, + zOffset, + tOffset, + s11, + s12, + s13, + s21, + s22, + s23, + s31, + s32, + s33, + tScale); + + transform = affine.Identity() + ? new IdentityMathTransform(3) + : affine; + + if (args.ContainsKey("inv") + && !transform.Identity() + && transform is AffineRuntimeMathTransform affineTransform) + { + if (!affineTransform.TryCreateInverse(out MathTransform? inverseTransformCandidate, out skipReason)) + { + return false; + } + + transform = ArgumentGuard.ThrowIfNull(inverseTransformCandidate, nameof(inverseTransformCandidate)); + } + + return true; + } + + private static bool TryInvertSpatialMatrix(in Matrix3x3 matrix, out Matrix3x3 inverseMatrix) + { + inverseMatrix = Matrix3x3.Zero; + + double c11 = (matrix.M11 * matrix.M22) - (matrix.M12 * matrix.M21); + double c12 = -((matrix.M10 * matrix.M22) - (matrix.M12 * matrix.M20)); + double c13 = (matrix.M10 * matrix.M21) - (matrix.M11 * matrix.M20); + double c21 = -((matrix.M01 * matrix.M22) - (matrix.M02 * matrix.M21)); + double c22 = (matrix.M00 * matrix.M22) - (matrix.M02 * matrix.M20); + double c23 = -((matrix.M00 * matrix.M21) - (matrix.M01 * matrix.M20)); + double c31 = (matrix.M01 * matrix.M12) - (matrix.M02 * matrix.M11); + double c32 = -((matrix.M00 * matrix.M12) - (matrix.M02 * matrix.M10)); + double c33 = (matrix.M00 * matrix.M11) - (matrix.M01 * matrix.M10); + + double determinant = (matrix.M00 * c11) + (matrix.M01 * c12) + (matrix.M02 * c13); + if (Math.Abs(determinant) < 1e-30d || double.IsNaN(determinant) || double.IsInfinity(determinant)) + { + return false; + } + + double inverseDeterminant = 1d / determinant; + inverseMatrix = new Matrix3x3( + c11 * inverseDeterminant, + c21 * inverseDeterminant, + c31 * inverseDeterminant, + c12 * inverseDeterminant, + c22 * inverseDeterminant, + c32 * inverseDeterminant, + c13 * inverseDeterminant, + c23 * inverseDeterminant, + c33 * inverseDeterminant); + return true; + } + + /// + /// Adds this transform's affine offsets and coefficients to a coordinate. + /// + /// X ordinate to transform. + /// Y ordinate to transform. + /// Z ordinate to transform. + private void ApplySpatialTransform(ref double x, ref double y, ref double z) + { + Vector3D transformed = this.offset + (this.spatialMatrix * new Vector3D(x, y, z)); + x = transformed.X; + y = transformed.Y; + z = transformed.Z; + } + + /// + public override bool Identity() + { + return this.offset.X == 0d + && this.offset.Y == 0d + && this.offset.Z == 0d + && this.tOffset == 0d + && this.spatialMatrix.IsIdentity + && this.tScale == 1d; + } + + /// + public override MathTransform Inverse() + { + if (this.inverse is null) + { + if (!this.TryCreateInverse(out MathTransform? inverseTransformCandidate, out string? error)) + { + throw new InvalidOperationException(error); + } + + this.inverse = ArgumentGuard.ThrowIfNull(inverseTransformCandidate, nameof(inverseTransformCandidate)); + } + + return ArgumentGuard.ThrowIfNull(this.inverse, nameof(this.inverse)); + } + + /// + public override void Invert() + { + throw new NotSupportedException("Affine runtime transform does not support in-place inversion."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + this.ApplySpatialTransform(ref x, ref y, ref z); + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + this.ApplySpatialTransform(ref x, ref y, ref z); + t = this.tOffset + (this.tScale * t); + } + + private bool TryCreateInverse([NotNullWhen(true)] out MathTransform? inverseTransform, out string? error) + { + inverseTransform = null; + error = null; + + if (!TryInvertSpatialMatrix(this.spatialMatrix, out Matrix3x3 inverseMatrix)) + { + error = "affine: transformation matrix is not invertible."; + return false; + } + + if (this.tScale == 0d) + { + error = "affine: +tscale must be non-zero for inverse usage."; + return false; + } + + Vector3D inverseOffset = -(inverseMatrix * this.offset); + double inverseTScale = 1d / this.tScale; + double inverseTOffset = -(this.tOffset * inverseTScale); + + inverseTransform = new AffineRuntimeMathTransform( + inverseOffset.X, + inverseOffset.Y, + inverseOffset.Z, + inverseTOffset, + inverseMatrix.M00, + inverseMatrix.M01, + inverseMatrix.M02, + inverseMatrix.M10, + inverseMatrix.M11, + inverseMatrix.M12, + inverseMatrix.M20, + inverseMatrix.M21, + inverseMatrix.M22, + inverseTScale); + return true; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/AffineTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/AffineTransform.cs index 562252fd..9baeda7d 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/AffineTransform.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/AffineTransform.cs @@ -1,488 +1,425 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; using System; using System.Collections.Generic; using System.Globalization; -using System.Text; - -namespace ProjNet.CoordinateSystems.Transformations +using ProjNet.IO.Wkt; + +/// +/// Represents an affine math transform that transforms input coordinates to target coordinates using an affine transformation matrix. Dimensionality may change. +/// +/// +/// If the transform's input dimension is M, and output dimension is N, then the +/// matrix has size [N+1][M+1]. The extra row and column encode the affine +/// translation terms in homogeneous coordinates. Inverse creation uses standard +/// LUP decomposition with partial pivoting to solve for the inverse matrix. +/// +/// Affine transformation. +public sealed class AffineTransform : MathTransform { /// - /// Represents affine math transform which transforms input coordinates to target using affine transformation matrix. Dimensionality might change. + /// Dimension of source points - it's related to number of transformation matrix rows. + /// + private readonly int dimSource; + + /// + /// Dimension of output points - it's related to number of columns. + /// + private readonly int dimTarget; + + /// + /// Represents transform matrix of this affine transformation from input points to output ones using dimensionality defined within the affine transform + /// Number of rows = dimTarget + 1 + /// Number of columns = dimSource + 1. + /// + private readonly double[,] transformMatrix; + + /// + /// Saved inverse transform. + /// + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class with a 2D affine transform. /// - ///If the transform's input dimension is M, and output dimension is N, then the matrix will have size [N+1][M+1]. - ///The +1 in the matrix dimensions allows the matrix to do a shift, as well as a rotation. - ///The [M][j] element of the matrix will be the j'th ordinate of the moved origin. - ///The [i][N] element of the matrix will be 0 for i less than M, and 1 for i equals M. - /// - [Serializable] - public class AffineTransform : MathTransform + /// Value for row 0, column 0 - AKA ScaleX. + /// Value for row 0, column 1 - AKA ShearX. + /// Value for row 0, column 2 - AKA Translate X. + /// Value for row 1, column 0 - AKA Shear Y. + /// Value for row 1, column 1 - AKA Scale Y. + /// Value for row 1, column 2 - AKA Translate Y. + public AffineTransform(double m00, double m01, double m02, double m10, double m11, double m12) { - #region class variables - /// - /// Saved inverse transform - /// - private MathTransform _inverse; - - /// - /// Dimension of source points - it's related to number of transformation matrix rows - /// - private readonly int _dimSource; - - /// - /// Dimension of output points - it's related to number of columns - /// - private readonly int _dimTarget; - - /// - /// Represents transform matrix of this affine transformation from input points to output ones using dimensionality defined within the affine transform - /// Number of rows = dimTarget + 1 - /// Number of columns = dimSource + 1 - /// - private readonly double[,] _transformMatrix; - #endregion class variables - - #region constructors & finalizers - /// - /// Creates instance of 2D affine transform (source dimensionality 2, target dimensionality 2) using the specified values - /// - /// Value for row 0, column 0 - AKA ScaleX - /// Value for row 0, column 1 - AKA ShearX - /// Value for row 0, column 2 - AKA Translate X - /// Value for row 1, column 0 - AKA Shear Y - /// Value for row 1, column 1 - AKA Scale Y - /// Value for row 1, column 2 - AKA Translate Y - public AffineTransform(double m00, double m01, double m02, double m10, double m11, double m12) - { - //fill dimensionlity - _dimSource = 2; - _dimTarget = 2; - //create matrix - 2D affine transform uses 3x3 matrix (3rd row is the special one) - _transformMatrix = new [,] { { m00, m01, m02 }, { m10, m11, m12 }, { 0, 0, 1 } }; - } + // fill dimensionlity + this.dimSource = 2; + this.dimTarget = 2; - /// - /// Creates instance of affine transform using the specified matrix. - /// - /// If the transform's input dimension is M, and output dimension is N, then the matrix will have size [N+1][M+1]. - /// The +1 in the matrix dimensions allows the matrix to do a shift, as well as a rotation. The [M][j] element of the matrix will be the j'th ordinate of the moved origin. The [i][N] element of the matrix will be 0 for i less than M, and 1 for i equals M. - /// - /// Matrix used to create afiine transform - public AffineTransform(double[,] matrix) + // create matrix - 2D affine transform uses 3x3 matrix (3rd row is the special one) + this.transformMatrix = new[,] { - //check validity - if (matrix == null) - { - throw new ArgumentNullException("matrix"); - } - if (matrix.GetLength(0) <= 1) - { - throw new ArgumentException("Transformation matrix must have at least 2 rows."); - } - if (matrix.GetLength(1) <= 1) - { - throw new ArgumentException("Transformation matrix must have at least 2 columns."); - } + { m00, m01, m02 }, + { m10, m11, m12 }, + { 0, 0, 1 }, + }; + } - //fill dimensionlity - dimension is M, and output dimension is N, then the matrix will have size [N+1][M+1]. - _dimSource = matrix.GetLength(1) - 1; - _dimTarget = matrix.GetLength(0) - 1; - //use specified matrix - _transformMatrix = matrix; - } - #endregion constructors & finalizers - - #region public properties - /// - /// Gets a Well-Known text representation of this affine math transformation. - /// - /// - public override string WKT + /// + /// Initializes a new instance of the class using the specified transformation matrix. + /// + /// + /// If the transform's input dimension is M, and output dimension is N, then + /// the matrix has size [N+1][M+1]. The inverse matrix is obtained + /// with the same LUP decomposition with partial pivoting used by + /// . + /// + /// + /// Matrix used to create the affine transform. + public AffineTransform(double[,] matrix) + { + // check validity + matrix = ArgumentGuard.ThrowIfNull(matrix, nameof(matrix)); + if (matrix.GetLength(0) <= 1) { - get - { - //PARAM_MT["Affine", - // PARAMETER["num_row",3], - // PARAMETER["num_col",3], - // PARAMETER["elt_0_1",1], - // PARAMETER["elt_0_2",2], - // PARAMETER["elt 1 2",3]] - - var sb = new StringBuilder(); - - sb.Append("PARAM_MT[\"Affine\""); - //append parameters - foreach (var param in GetParameterValues()) - { - sb.Append(","); - sb.Append(param.WKT); - } - sb.Append("]"); - return sb.ToString(); - } + ArgumentGuard.ThrowArgument("Transformation matrix must have at least 2 rows.", nameof(matrix)); } - /// - /// Gets an XML representation of this affine transformation. - /// - /// - public override string XML + + if (matrix.GetLength(1) <= 1) { - get { throw new NotImplementedException("The method or operation is not implemented."); } + ArgumentGuard.ThrowArgument("Transformation matrix must have at least 2 columns.", nameof(matrix)); } - /// - /// Gets the dimension of input points. - /// - public override int DimSource { get { return _dimSource; } } + // fill dimensionlity - dimension is M, and output dimension is N, then the matrix will have size [N+1][M+1]. + this.dimSource = matrix.GetLength(1) - 1; + this.dimTarget = matrix.GetLength(0) - 1; - /// - /// Gets the dimension of output points. - /// - public override int DimTarget { get { return _dimTarget; } } - #endregion public properties + // use specified matrix + this.transformMatrix = matrix; + } - #region private methods + /// + /// Gets a Well-Known Text representation of this affine math transformation. + /// + public override string WKT => this.ToWktNode().ToString(); - /// - /// Return affine transformation matrix as group of parameter values that maiy be used for retrieving WKT of this affine transform - /// - /// List of string pairs NAME VALUE - private IList GetParameterValues() + /// + /// Gets an XML representation of this affine transformation. + /// + public override string XML => base.XML; + + /// + public override int DimSource => this.dimSource; + + /// + public override int DimTarget => this.dimTarget; + + /// + public override WktNode ToWktNode() + { + List parameters = this.GetParameterValues(); + var children = new List(parameters.Count + 1) { - int rowCnt = _transformMatrix.GetLength(0); - int colCnt = _transformMatrix.GetLength(1); - var pInfo = new List(); - pInfo.Add(new ProjectionParameter("num_row", rowCnt)); - pInfo.Add(new ProjectionParameter("num_col", colCnt)); - //fill matrix values - for (int row = 0; row < rowCnt; row++) - { - for (int col = 0; col < colCnt; col++) - { - string name = string.Format(CultureInfo.InvariantCulture.NumberFormat, "elt_{0}_{1}", row, col); - pInfo.Add(new ProjectionParameter(name, _transformMatrix[row, col])); - } - } - return pInfo; + new WktQuotedString("Affine"), + }; + + foreach (ProjectionParameter parameter in parameters) + { + children.Add(parameter.ToWktNode()); } + return new WktKeywordNode("PARAM_MT", children); + } - /// - /// Given L,U,P and b solve for x. - /// Input the L and U matrices as a single matrix LU. - /// Return the solution as a double[]. - /// LU will be a n+1xm+1 matrix where the first row and columns are zero. - /// This is for ease of computation and consistency with Cormen et al. - /// pseudocode. - /// The pi array represents the permutation matrix. - /// - /// - /// - /// - /// - /// - private static double[] LUPSolve(double[,] LU, int[] pi, double[] b) + /// + /// Returns the inverse of this affine transformation. + /// + /// IMathTransform that is the reverse of the current affine transformation. + public override MathTransform Inverse() + { + if (this.inverse is null) { - int n = LU.GetLength(0) - 1; - double[] x = new double[n + 1]; - double[] y = new double[n + 1]; - - /* - * Solve for y using formward substitution - * */ - for (int i = 0; i <= n; i++) - { - double suml = 0; - for (int j = 0; j <= i - 1; j++) - { - /* - * Since we've taken L and U as a singular matrix as an input - * the value for L at index i and j will be 1 when i equals j, not LU[i][j], since - * the diagonal values are all 1 for L. - * */ - double lij; - if (i == j) - { - lij = 1; - } - else - { - lij = LU[i, j]; - } - suml += lij * y[j]; - } - y[i] = b[pi[i]] - suml; - } - //Solve for x by using back substitution - for (int i = n; i >= 0; i--) - { - double sumu = 0; - for (int j = i + 1; j <= n; j++) - { - sumu += LU[i, j] * x[j]; - } - x[i] = (y[i] - sumu) / LU[i, i]; - } - return x; + // find the inverse transformation matrix - use cloned matrix array + // remarks about dimensionality: if input dimension is M, and output dimension is N, then the matrix will have size [N+1][M+1]. + double[,] invMatrix = InvertMatrix((double[,])this.transformMatrix.Clone()); + this.inverse = new AffineTransform(invMatrix); } - /// - /// Perform LUP decomposition on a matrix A. - /// Return P as an array of ints and L and U are just in A, "in place". - /// In order to make some of the calculations more straight forward and to - /// match Cormen's et al. pseudocode the matrix A should have its first row and first columns - /// to be all 0. - /// - /// - /// - /// - private static int[] LUPDecomposition(double[,] A) + return this.inverse; + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + (x, y, z) = this.TransformAffine(x, y, z); + } + + /// + /// Reverses the transformation. + /// + public override void Invert() + { + throw new NotSupportedException("The method or operation is not supported."); + } + + /// + /// Returns this affine transform as a cloned transformation matrix. + /// + /// A copy of the internal transformation matrix with dimensions [+1][+1]. + public double[,] GetMatrix() + { + return (double[,])this.transformMatrix.Clone(); + } + + /// + /// Return affine transformation matrix as group of parameter values that maiy be used for retrieving WKT of this affine transform. + /// + /// List of string pairs NAME VALUE. + private List GetParameterValues() + { + int rowCnt = this.transformMatrix.GetLength(0); + int colCnt = this.transformMatrix.GetLength(1); + List pInfo = + [ + new("num_row", rowCnt), + new("num_col", colCnt), + ]; + + // fill matrix values + for (int row = 0; row < rowCnt; row++) { - int n = A.GetLength(0) - 1; - /* - * pi represents the permutation matrix. We implement it as an array - * whose value indicates which column the 1 would appear. We use it to avoid - * dividing by zero or small numbers. - * */ - int[] pi = new int[n + 1]; - int kp = 0; - - //Initialize the permutation matrix, will be the identity matrix - for (int j = 0; j <= n; j++) + for (int col = 0; col < colCnt; col++) { - pi[j] = j; + string name = FormattableString.Invariant($"elt_{row}_{col}"); + pInfo.Add(new ProjectionParameter(name, this.transformMatrix[row, col])); } + } - for (int k = 0; k <= n; k++) - { - /* - * In finding the permutation matrix p that avoids dividing by zero - * we take a slightly different approach. For numerical stability - * We find the element with the largest - * absolute value of those in the current first column (column k). If all elements in - * the current first column are zero then the matrix is singluar and throw an - * error. - * */ - double p = 0; - for (int i = k; i <= n; i++) - { - if (Math.Abs(A[i, k]) > p) - { - p = Math.Abs(A[i, k]); - kp = i; - } - } - if (p == 0) - { - throw new Exception("singular matrix"); - } - /* - * These lines update the pivot array (which represents the pivot matrix) - * by exchanging pi[k] and pi[kp]. - * */ - int pik = pi[k]; - int pikp = pi[kp]; - pi[k] = pikp; - pi[kp] = pik; - - /* - * Exchange rows k and kpi as determined by the pivot - * */ - for (int i = 0; i <= n; i++) - { - double aki = A[k, i]; - double akpi = A[kp, i]; - A[k, i] = akpi; - A[kp, i] = aki; - } + return pInfo; + } - /* - * Compute the Schur complement - * */ - for (int i = k + 1; i <= n; i++) - { - A[i, k] = A[i, k] / A[k, k]; - for (int j = k + 1; j <= n; j++) - { - A[i, j] = A[i, j] - (A[i, k] * A[k, j]); - } - } - } - return pi; + /// + /// Given L, U, P and b, solves for x using forward and back substitution. + /// Input the L and U matrices as a single combined LU matrix. + /// Returns the solution as a array. + /// LU will be a n+1 x m+1 matrix where the first row and columns are zero. + /// This is for ease of computation and consistency with Cormen et al. pseudocode. + /// The π array represents the permutation matrix. + /// + /// The lu parameter. + /// The pi parameter. + /// The b parameter. + /// Destination span for the computed solution vector. + private static void LUPSolve(double[,] lu, int[] pi, ReadOnlySpan b, Span solution) + { + int n = lu.GetLength(0) - 1; + int dimension = n + 1; + if (b.Length < dimension) + { + ArgumentGuard.ThrowArgument("Input vector is too short.", nameof(b)); } - - /// - /// Given an nXn matrix A, solve n linear equations to find the inverse of A. - /// - /// - /// - /// - private static double[,] InvertMatrix(double[,] A) + if (solution.Length < dimension) { - int n = A.GetLength(0); - int m = A.GetLength(1); - - //x will hold the inverse matrix to be returned - double[,] x = new double[n, m]; - - /* - * solve will contain the vector solution for the LUP decomposition as we solve - * for each vector of x. We will combine the solutions into the double[][] array x. - * */ - double[] solve; - - //Get the LU matrix and P matrix (as an array) - int[] P = LUPDecomposition(A); - double[,] LU = A; - - /* - * Solve AX = e for each column ei of the identity matrix using LUP decomposition - * */ - for (int i = 0; i < n; i++) - { - //e will represent each column in the identity matrix - double[] e = new double[m]; - e[i] = 1; - solve = LUPSolve(LU, P, e); - for (int j = 0; j < solve.Length; j++) - { - x[j, i] = solve[j]; - } - } - return x; + ArgumentGuard.ThrowArgument("Solution buffer is too short.", nameof(solution)); } - #endregion private methods - - #region public methods - /// - /// Returns the inverse of this affine transformation. - /// - /// IMathTransform that is the reverse of the current affine transformation. - public override MathTransform Inverse() + + Span xSpan = solution[..dimension]; + Span yBuffer = dimension <= 128 ? stackalloc double[128] : new double[dimension]; + Span ySpan = yBuffer[..dimension]; + + // Solve for y using formward substitution + for (int i = 0; i <= n; i++) { - if (_inverse == null) + double suml = 0; + for (int j = 0; j <= i - 1; j++) { - //find the inverse transformation matrix - use cloned matrix array - //remarks about dimensionality: if input dimension is M, and output dimension is N, then the matrix will have size [N+1][M+1]. - double[,] invMatrix = InvertMatrix((double[,])_transformMatrix.Clone()); - _inverse = new AffineTransform(invMatrix); + // Since we've taken L and U as a singular matrix as an input + // the value for L at index i and j will be 1 when i equals j, not LU[i][j], since + // the diagonal values are all 1 for L. + double lij = i == j ? 1d : lu[i, j]; + suml += lij * ySpan[j]; } - return _inverse; + ySpan[i] = b[pi[i]] - suml; } - /// - /// Transforms a coordinate point. The passed parameter point should not be modified. - /// - /// The x-ordinate value - /// The y-ordinate value - /// The z-ordinate value - /// The converted x-, y- and z-ordinate tuple - private (double x, double y, double z) TransformAffine(double x, double y, double z) + // Solve for x by using back substitution + for (int i = n; i >= 0; i--) { - //check source dimensionality - allow coordinate clipping, if source dimensionality is greater then expected source dimensionality of affine transformation - Span point = stackalloc double[0]; - switch (_dimSource) + double sumu = 0; + for (int j = i + 1; j <= n; j++) { - case 0: - point = default; - break; + sumu += lu[i, j] * xSpan[j]; + } + + xSpan[i] = (ySpan[i] - sumu) / lu[i, i]; + } + } - case 1: - point = stackalloc double[] { x }; - break; + /// + /// Performs LUP decomposition on matrix A in-place and returns the permutation array. + /// The first row and first column of A are expected to be zero (1-based indexing convention). + /// + /// The a parameter. + /// The transformation result. + private static int[] LUPDecomposition(double[,] a) + { + int n = a.GetLength(0) - 1; - case 2: - point = stackalloc double[] { x, y }; - break; + // pi represents the permutation matrix. We implement it as an array + // whose value indicates which column the 1 would appear. We use it to avoid + // dividing by zero or small numbers. + int[] pi = new int[n + 1]; - case 3: - point = stackalloc double[] { x, y, z }; - break; + // Initialize the permutation matrix, will be the identity matrix + for (int j = 0; j <= n; j++) + { + pi[j] = j; + } - default: - throw new NotSupportedException(); + for (int k = 0; k <= n; k++) + { + int kp = k; + + // In finding the permutation matrix p that avoids dividing by zero + // we take a slightly different approach. For numerical stability + // We find the element with the largest + // absolute value of those in the current first column (column k). If all elements in + // the current first column are zero then the matrix is singluar and throw an + // error. + double p = 0; + for (int i = k; i <= n; i++) + { + if (Math.Abs(a[i, k]) > p) + { + p = Math.Abs(a[i, k]); + kp = i; + } } - if (_dimTarget > 3) + if (p == 0) { - throw new NotSupportedException(); + throw new InvalidOperationException("singular matrix"); } - //use transformation matrix to create output points that has dimTarget dimensionality - Span transformed = stackalloc double[_dimTarget]; + // These lines update the pivot array (which represents the pivot matrix) + // by exchanging pi[k] and pi[kp]. + int pik = pi[k]; + int pikp = pi[kp]; + pi[k] = pikp; + pi[kp] = pik; - //count each target dimension using the apropriate row - for (int row = 0; row < _dimTarget; row++) + // Exchange rows k and kpi as determined by the pivot + for (int i = 0; i <= n; i++) { - //start with the last value which is in fact multiplied by 1 - double dimVal = _transformMatrix[row, _dimSource]; - for (int col = 0; col < _dimSource; col++) - { - dimVal += _transformMatrix[row, col] * point[col]; - } - transformed[row] = dimVal; + double aki = a[k, i]; + double akpi = a[kp, i]; + a[k, i] = akpi; + a[kp, i] = aki; } - (double x, double y, double z) ret = default; - if (transformed.Length > 2) + // Compute the Schur complement + for (int i = k + 1; i <= n; i++) { - ret.z = transformed[2]; + a[i, k] = a[i, k] / a[k, k]; + for (int j = k + 1; j <= n; j++) + { + a[i, j] = a[i, j] - (a[i, k] * a[k, j]); + } } + } - if (transformed.Length > 1) - { - ret.y = transformed[1]; - } + return pi; + } - if (transformed.Length > 0) + /// + /// Given an n×n matrix A, solves n linear equations to find the inverse of A using LUP decomposition. + /// + /// The a parameter. + /// The transformation result. + private static double[,] InvertMatrix(double[,] a) + { + int n = a.GetLength(0); + int m = a.GetLength(1); + + // x will hold the inverse matrix to be returned + double[,] x = new double[n, m]; + + // Get the LU matrix and P matrix (as an array) + int[] p = LUPDecomposition(a); + double[,] lU = a; + Span eBuffer = m <= 128 ? stackalloc double[128] : new double[m]; + Span solveBuffer = m <= 128 ? stackalloc double[128] : new double[m]; + Span e = eBuffer[..m]; + Span solve = solveBuffer[..m]; + + // Solve AX = e for each column ei of the identity matrix using LUP decomposition + for (int i = 0; i < n; i++) + { + // e will represent each column in the identity matrix + e.Clear(); + e[i] = 1; + LUPSolve(lU, p, e, solve); + for (int j = 0; j < solve.Length; j++) { - ret.x = transformed[0]; + x[j, i] = solve[j]; } + } - return ret; + return x; + } + + /// + /// Transforms a coordinate point. The passed parameter point should not be modified. + /// + /// The x-ordinate value. + /// The y-ordinate value. + /// The z-ordinate value. + /// The converted x-, y- and z-ordinate tuple. + private (double X, double Y, double Z) TransformAffine(double x, double y, double z) + { + // check source dimensionality - allow coordinate clipping, if source dimensionality is greater then expected source dimensionality of affine transformation + Span point = [x, y, z]; + if (this.dimSource > 3 || this.dimTarget > 3) + { + throw new NotSupportedException(); } + // use transformation matrix to create output points that has dimTarget dimensionality + Span transformed = stackalloc double[this.dimTarget]; - /// - public override void Transform(ref double x, ref double y, ref double z) + // count each target dimension using the apropriate row + for (int row = 0; row < this.dimTarget; row++) { - (x, y, z) = TransformAffine(x, y, z); + // start with the last value which is in fact multiplied by 1 + double dimVal = this.transformMatrix[row, this.dimSource]; + for (int col = 0; col < this.dimSource; col++) + { + dimVal += this.transformMatrix[row, col] * point[col]; + } + + transformed[row] = dimVal; } - /// - /// Reverses the transformation - /// - public override void Invert() + (double X, double Y, double Z) ret = default; + if (transformed.Length > 2) { - throw new NotSupportedException("The method or operation is not supported."); + ret.Z = transformed[2]; } + if (transformed.Length > 1) + { + ret.Y = transformed[1]; + } - /// - /// Returns this affine transform as an affine transform matrix. - /// - /// - public double[,] GetMatrix() + if (transformed.Length > 0) { - return (double[,])this._transformMatrix.Clone(); + ret.X = transformed[0]; } - #endregion public methods + + return ret; } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/AxisOrderHelper.cs b/src/ProjNet/CoordinateSystems/Transformations/AxisOrderHelper.cs new file mode 100644 index 00000000..767a8607 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/AxisOrderHelper.cs @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems; + +/// +/// Creates axis-order correction transforms between source and target coordinate systems. +/// +/// +/// Axis-order resolution classifies coordinate-system axes by semantic role +/// (east/west, north/south, up/down), derives the corresponding source-to-target +/// permutation, and emits either an or an +/// identity transform when both systems already use the same orientation order. +/// +/// PROJ FAQ: axis ordering. +internal static class AxisOrderHelper +{ + /// + /// Tries to create an axis-swap transform that aligns source and target axis orientation. + /// + /// Source coordinate system. + /// Target coordinate system. + /// Created transform when alignment is possible. + /// when a transform was created. + internal static bool TryCreateAxisSwapTransform( + CoordinateSystem source, + CoordinateSystem target, + [NotNullWhen(true)] out MathTransform? transform) + { + transform = null; + if (source is null || target is null) + { + return false; + } + + int dimension = Math.Min(3, Math.Min(source.Dimension, target.Dimension)); + if (dimension < 2) + { + return false; + } + + if (!TryGetRoleByOrientation(source, dimension, out _) + || !TryGetRoleByOrientation(target, dimension, out _)) + { + return false; + } + + int[] sourceIndexByRole = [-1, -1, -1]; + int[] sourceSignByRole = [1, 1, 1]; + int[] targetIndexByRole = [-1, -1, -1]; + int[] targetSignByRole = [1, 1, 1]; + + for (int i = 0; i < dimension; i++) + { + AxisOrientationEnum sourceOrientation = source.GetAxis(i).Orientation; + if (!TryMapOrientation(sourceOrientation, out int sourceRole, out int sourceSign)) + { + return false; + } + + AxisOrientationEnum targetOrientation = target.GetAxis(i).Orientation; + if (!TryMapOrientation(targetOrientation, out int targetRole, out int targetSign)) + { + return false; + } + + sourceIndexByRole[sourceRole] = i; + sourceSignByRole[sourceRole] = sourceSign; + targetIndexByRole[targetRole] = i; + targetSignByRole[targetRole] = targetSign; + } + + int[] sourceIndices = [0, 1, 2]; + int[] signs = [1, 1, 1]; + bool changed = false; + + for (int role = 0; role < 3; role++) + { + int targetIndex = targetIndexByRole[role]; + if (targetIndex < 0 || targetIndex > 2) + { + continue; + } + + int sourceIndex = sourceIndexByRole[role]; + if (sourceIndex < 0 || sourceIndex > 2) + { + return false; + } + + sourceIndices[targetIndex] = sourceIndex; + signs[targetIndex] = sourceSignByRole[role] * targetSignByRole[role]; + if (sourceIndices[targetIndex] != targetIndex || signs[targetIndex] != 1) + { + changed = true; + } + } + + if (!changed) + { + transform = new IdentityMathTransform(Math.Max(source.Dimension, target.Dimension)); + return true; + } + + transform = new AxisSwapMathTransform( + dimension, + sourceIndices[0], + signs[0], + sourceIndices[1], + signs[1], + sourceIndices[2], + signs[2], + 3, + 1); + return true; + } + + private static bool TryGetRoleByOrientation(CoordinateSystem coordinateSystem, int dimension, out int[] roleByAxis) + { + roleByAxis = new int[Math.Min(3, dimension)]; + for (int i = 0; i < roleByAxis.Length; i++) + { + if (!TryMapOrientation(coordinateSystem.GetAxis(i).Orientation, out int role, out _)) + { + return false; + } + + roleByAxis[i] = role; + } + + return true; + } + + private static bool TryMapOrientation(AxisOrientationEnum orientation, out int role, out int sign) + { + role = -1; + sign = 1; + switch (orientation) + { + case AxisOrientationEnum.East: + role = 0; + sign = 1; + return true; + case AxisOrientationEnum.West: + role = 0; + sign = -1; + return true; + case AxisOrientationEnum.North: + role = 1; + sign = 1; + return true; + case AxisOrientationEnum.South: + role = 1; + sign = -1; + return true; + case AxisOrientationEnum.Up: + role = 2; + sign = 1; + return true; + case AxisOrientationEnum.Down: + role = 2; + sign = -1; + return true; + default: + return false; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/AxisSwapMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/AxisSwapMathTransform.cs new file mode 100644 index 00000000..65bf8590 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/AxisSwapMathTransform.cs @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Reorders and optionally flips coordinate ordinates according to axis mapping rules. +/// +/// +/// This transform is a pure signed permutation of the input ordinates. Each +/// output axis selects one source ordinate and optionally multiplies it by +/// -1, so the overall mapping is equivalent to a permutation matrix with +/// diagonal sign changes. +/// +/// PROJ: axis swap. +internal sealed class AxisSwapMathTransform : MathTransform +{ + private readonly int dimension; + private int xSourceIndex; + private int ySourceIndex; + private int zSourceIndex; + private int tSourceIndex; + private int xSign; + private int ySign; + private int zSign; + private int tSign; + + /// + /// Initializes a new instance of the class. + /// + /// Coordinate dimension (2, 3 or 4). + /// Source ordinate index used for X output. + /// Sign multiplier for X output. + /// Source ordinate index used for Y output. + /// Sign multiplier for Y output. + /// Source ordinate index used for Z output. + /// Sign multiplier for Z output. + /// Source ordinate index used for T output. + /// Sign multiplier for T output. + internal AxisSwapMathTransform( + int dimension, + int xSourceIndex, + int xSign, + int ySourceIndex, + int ySign, + int zSourceIndex, + int zSign, + int tSourceIndex, + int tSign) + { + this.dimension = ValidateDimension(dimension, nameof(dimension)); + this.xSourceIndex = ValidateSourceIndex(xSourceIndex, nameof(xSourceIndex)); + this.ySourceIndex = ValidateSourceIndex(ySourceIndex, nameof(ySourceIndex)); + this.zSourceIndex = ValidateSourceIndex(zSourceIndex, nameof(zSourceIndex)); + this.tSourceIndex = ValidateSourceIndex(tSourceIndex, nameof(tSourceIndex)); + + this.xSign = ValidateSign(xSign, nameof(xSign)); + this.ySign = ValidateSign(ySign, nameof(ySign)); + this.zSign = ValidateSign(zSign, nameof(zSign)); + this.tSign = ValidateSign(tSign, nameof(tSign)); + } + + /// + public override int DimSource => this.dimension; + + /// + public override int DimTarget => this.dimension; + + /// + public override bool Identity() + { + bool xyIdentity = this.xSourceIndex == 0 + && this.xSign == 1 + && this.ySourceIndex == 1 + && this.ySign == 1; + + if (this.dimension < 3) + { + return xyIdentity; + } + + bool xyzIdentity = xyIdentity + && this.zSourceIndex == 2 + && this.zSign == 1; + + return this.dimension < 4 + ? xyzIdentity + : xyzIdentity + && this.tSourceIndex == 3 + && this.tSign == 1; + } + + /// + public override MathTransform Inverse() + { + int[] sourceIndices = [this.xSourceIndex, this.ySourceIndex, this.zSourceIndex, this.tSourceIndex]; + int[] targetSigns = [this.xSign, this.ySign, this.zSign, this.tSign]; + + int[] inverseSourceIndices = [0, 1, 2, 3]; + int[] inverseSigns = [1, 1, 1, 1]; + for (int targetIndex = 0; targetIndex < this.dimension; targetIndex++) + { + int sourceIndex = sourceIndices[targetIndex]; + inverseSourceIndices[sourceIndex] = targetIndex; + inverseSigns[sourceIndex] = targetSigns[targetIndex]; + } + + return new AxisSwapMathTransform( + this.dimension, + inverseSourceIndices[0], + inverseSigns[0], + inverseSourceIndices[1], + inverseSigns[1], + inverseSourceIndices[2], + inverseSigns[2], + inverseSourceIndices[3], + inverseSigns[3]); + } + + /// + public override void Invert() + { + int[] sourceIndices = [this.xSourceIndex, this.ySourceIndex, this.zSourceIndex, this.tSourceIndex]; + int[] targetSigns = [this.xSign, this.ySign, this.zSign, this.tSign]; + + int[] inverseSourceIndices = [0, 1, 2, 3]; + int[] inverseSigns = [1, 1, 1, 1]; + for (int targetIndex = 0; targetIndex < this.dimension; targetIndex++) + { + int sourceIndex = sourceIndices[targetIndex]; + inverseSourceIndices[sourceIndex] = targetIndex; + inverseSigns[sourceIndex] = targetSigns[targetIndex]; + } + + this.xSourceIndex = inverseSourceIndices[0]; + this.xSign = inverseSigns[0]; + this.ySourceIndex = inverseSourceIndices[1]; + this.ySign = inverseSigns[1]; + this.zSourceIndex = inverseSourceIndices[2]; + this.zSign = inverseSigns[2]; + this.tSourceIndex = inverseSourceIndices[3]; + this.tSign = inverseSigns[3]; + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + double sourceX = x; + double sourceY = y; + double sourceZ = z; + x = GetSourceValue(this.xSourceIndex, sourceX, sourceY, sourceZ, 0d) * this.xSign; + y = GetSourceValue(this.ySourceIndex, sourceX, sourceY, sourceZ, 0d) * this.ySign; + if (this.dimension > 2) + { + z = GetSourceValue(this.zSourceIndex, sourceX, sourceY, sourceZ, 0d) * this.zSign; + } + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + double sourceX = x; + double sourceY = y; + double sourceZ = z; + double sourceT = t; + + x = GetSourceValue(this.xSourceIndex, sourceX, sourceY, sourceZ, sourceT) * this.xSign; + y = GetSourceValue(this.ySourceIndex, sourceX, sourceY, sourceZ, sourceT) * this.ySign; + if (this.dimension > 2) + { + z = GetSourceValue(this.zSourceIndex, sourceX, sourceY, sourceZ, sourceT) * this.zSign; + } + + if (this.dimension > 3) + { + t = GetSourceValue(this.tSourceIndex, sourceX, sourceY, sourceZ, sourceT) * this.tSign; + } + } + + private static int ValidateDimension(int dimension, string parameterName) + { + if (dimension is < 2 or > 4) + { + ArgumentGuard.ThrowArgumentOutOfRange(parameterName, dimension, "Axis swap dimension must be either 2, 3 or 4."); + } + + return dimension; + } + + private static int ValidateSourceIndex(int sourceIndex, string parameterName) + { + if (sourceIndex is < 0 or > 3) + { + ArgumentGuard.ThrowArgumentOutOfRange(parameterName, sourceIndex, "Axis source index must be 0, 1, 2 or 3."); + } + + return sourceIndex; + } + + private static double GetSourceValue(int sourceIndex, double x, double y, double z, double t) + { + return sourceIndex switch + { + 0 => x, + 1 => y, + 2 => z, + 3 => t, + _ => throw new ArgumentOutOfRangeException(nameof(sourceIndex), sourceIndex, "Axis source index must be between 0 and 3."), + }; + } + + private static int ValidateSign(int sign, string parameterName) + { + if (sign != -1 && sign != 1) + { + ArgumentGuard.ThrowArgumentOutOfRange(parameterName, sign, "Axis sign must be either -1 or 1."); + } + + return sign; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/BaseGeoGrid.cs b/src/ProjNet/CoordinateSystems/Transformations/BaseGeoGrid.cs new file mode 100644 index 00000000..302205a8 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/BaseGeoGrid.cs @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Abstract base class for a geo-referenced raster grid that maps geographic coordinates +/// to grid pixel coordinates using an affine transformation. +/// +internal abstract class BaseGeoGrid +{ + private readonly SampleData sampleData; + private readonly double determinant; + + /// + /// Initializes a new instance of the class. + /// + /// Path of the source file the grid was loaded from. + /// Number of grid columns. + /// Number of grid rows. + /// Geographic coverage area used to order grids by specificity. + /// Tolerance used for geographic boundary checks in degrees. + /// Western boundary of the grid in degrees. + /// Eastern boundary of the grid in degrees. + /// Southern boundary of the grid in degrees. + /// Northern boundary of the grid in degrees. + /// Affine coefficient: longitude change per grid column. + /// Affine coefficient: longitude change per grid row. + /// Affine coefficient: longitude of the grid origin. + /// Affine coefficient: latitude change per grid column. + /// Affine coefficient: latitude change per grid row. + /// Affine coefficient: latitude of the grid origin. + /// The raster sample data store for this grid. + protected BaseGeoGrid( + string sourcePath, + int width, + int height, + double area, + double epsilon, + double west, + double east, + double south, + double north, + double a, + double b, + double c, + double d, + double e, + double f, + SampleData sampleData) + { + this.SourcePath = sourcePath; + this.Width = width; + this.Height = height; + this.Area = area; + this.Epsilon = epsilon; + this.West = west; + this.East = east; + this.South = south; + this.North = north; + this.A = a; + this.B = b; + this.C = c; + this.D = d; + this.E = e; + this.F = f; + this.sampleData = sampleData; + this.determinant = (a * e) - (b * d); + } + + /// + /// Gets the path of the source file from which this grid was loaded. + /// + internal string SourcePath { get; } + + /// + /// Gets the number of grid columns. + /// + internal int Width { get; } + + /// + /// Gets the number of grid rows. + /// + internal int Height { get; } + + /// + /// Gets the geographic coverage area, used to order grids by specificity. + /// + internal double Area { get; } + + /// + /// Gets the tolerance in degrees used for geographic boundary checks. + /// + internal double Epsilon { get; } + + /// + /// Gets the western geographic boundary of the grid in degrees. + /// + internal double West { get; } + + /// + /// Gets the eastern geographic boundary of the grid in degrees. + /// + internal double East { get; } + + /// + /// Gets the southern geographic boundary of the grid in degrees. + /// + internal double South { get; } + + /// + /// Gets the northern geographic boundary of the grid in degrees. + /// + internal double North { get; } + + /// + /// Gets the affine coefficient representing longitude change per grid column. + /// + internal double A { get; } + + /// + /// Gets the affine coefficient representing longitude change per grid row. + /// + internal double B { get; } + + /// + /// Gets the affine coefficient representing the longitude of the grid origin. + /// + internal double C { get; } + + /// + /// Gets the affine coefficient representing latitude change per grid column. + /// + internal double D { get; } + + /// + /// Gets the affine coefficient representing latitude change per grid row. + /// + internal double E { get; } + + /// + /// Gets the affine coefficient representing the latitude of the grid origin. + /// + internal double F { get; } + + /// + /// Determines whether the specified geographic coordinate falls within the extent of this grid. + /// + /// Longitude in degrees. + /// Latitude in degrees. + /// when the coordinate is within the grid extent; otherwise . + internal bool Contains(double longitude, double latitude) + { + double lon = longitude; + if (lon < this.West - this.Epsilon) + { + lon += 360d; + } + else if (lon > this.East + this.Epsilon) + { + lon -= 360d; + } + + return lon >= this.West - this.Epsilon + && lon <= this.East + this.Epsilon + && latitude >= this.South - this.Epsilon + && latitude <= this.North + this.Epsilon; + } + + /// + /// Attempts to map a geographic coordinate to fractional grid pixel coordinates. + /// + /// Longitude in degrees. + /// Latitude in degrees. + /// Fractional column index in grid space on success. + /// Fractional row index in grid space on success. + /// when the mapping succeeds; otherwise . + internal bool TryMapToGridCoordinates(double longitude, double latitude, out double gridX, out double gridY) + { + if (this.TryMapRaw(longitude, latitude, out gridX, out gridY) && this.IsWithinGrid(gridX, gridY)) + { + return true; + } + + if (this.TryMapRaw(longitude + 360d, latitude, out gridX, out gridY) && this.IsWithinGrid(gridX, gridY)) + { + return true; + } + + return this.TryMapRaw(longitude - 360d, latitude, out gridX, out gridY) && this.IsWithinGrid(gridX, gridY); + } + + /// + /// Gets the raw sample value at the specified grid cell for the given sample band index. + /// + /// Zero-based index of the sample band. + /// Column index. + /// Row index. + /// The raw sample value stored at position (, ) in band . + internal double GetSampleValue(int sampleIndex, int x, int y) + { + return this.sampleData.GetValue(sampleIndex, x, y); + } + + private bool IsWithinGrid(double x, double y) + { + const double epsilon = 1e-8d; + return x >= -epsilon + && y >= -epsilon + && x <= (this.Width - 1) + epsilon + && y <= (this.Height - 1) + epsilon; + } + + private bool TryMapRaw(double longitude, double latitude, out double x, out double y) + { + double localX = longitude - this.C; + double localY = latitude - this.F; + x = ((localX * this.E) - (this.B * localY)) / this.determinant; + y = ((this.A * localY) - (localX * this.D)) / this.determinant; + return !double.IsNaN(x) && !double.IsNaN(y) && !double.IsInfinity(x) && !double.IsInfinity(y); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/CompositeMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/CompositeMathTransform.cs new file mode 100644 index 00000000..cabefb09 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/CompositeMathTransform.cs @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; + +/// +/// Composes multiple math transforms into a single sequential transform. +/// +/// +/// Composite transforms execute a fixed ordered chain of child transforms. The +/// inverse is built by reversing the chain and inverting each child, and the +/// cached inverse is invalidated whenever the composite is inverted in place. +/// +/// PROJ: pipeline operator. +internal sealed class CompositeMathTransform : MathTransform +{ + private MathTransform[] transforms; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Ordered transform chain executed from first to last. + internal CompositeMathTransform(IReadOnlyList transforms) + { + transforms = ArgumentGuard.ThrowIfNull(transforms, nameof(transforms)); + if (transforms.Count == 0) + { + ArgumentGuard.ThrowArgument("At least one math transform is required.", nameof(transforms)); + } + + this.transforms = new MathTransform[transforms.Count]; + for (int i = 0; i < transforms.Count; i++) + { + if (transforms[i] is null) + { + ArgumentGuard.ThrowArgument("Math transform list contains null element.", nameof(transforms)); + } + + this.transforms[i] = transforms[i]; + } + } + + /// + public override int DimSource => this.transforms[0].DimSource; + + /// + public override int DimTarget => this.transforms[^1].DimTarget; + + /// + public override bool Identity() + { + for (int i = 0; i < this.transforms.Length; i++) + { + if (!this.transforms[i].Identity()) + { + return false; + } + } + + return true; + } + + /// + public override MathTransform Inverse() + { + if (this.inverse is not null) + { + return this.inverse; + } + + var inverted = new MathTransform[this.transforms.Length]; + int output = 0; + for (int i = this.transforms.Length - 1; i >= 0; i--) + { + inverted[output] = this.transforms[i].Inverse(); + output++; + } + + this.inverse = new CompositeMathTransform(inverted); + return this.inverse; + } + + /// + public override void Invert() + { + Array.Reverse(this.transforms); + for (int i = 0; i < this.transforms.Length; i++) + { + this.transforms[i] = this.transforms[i].Inverse(); + } + + this.inverse = null; + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + for (int i = 0; i < this.transforms.Length; i++) + { + this.transforms[i].Transform(ref x, ref y, ref z); + } + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + for (int i = 0; i < this.transforms.Length; i++) + { + this.transforms[i].Transform(ref x, ref y, ref z, ref t); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/ConcatenatedTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/ConcatenatedTransform.cs index 9674db5c..fe784950 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/ConcatenatedTransform.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/ConcatenatedTransform.cs @@ -1,160 +1,227 @@ -// Copyright 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; using System; using System.Collections.Generic; -using System.Linq; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// Represents a transformation that executes a sequence of coordinate transformations in order. +/// +/// +/// Concatenated transforms model a chained coordinate-operation pipeline over +/// resolved instances. The inverse +/// cache is intentionally cleared during in-place inversion so reversed child +/// transforms are rebuilt from the updated traversal order. +/// +/// PROJ: computation of coordinate operations between two CRS. +internal sealed class ConcatenatedTransform : MathTransform, ICoordinateTransformationCore { + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = + new(); + + private readonly List coordinateTransformationList; + + /// + /// Cached inverse transform. + /// + private ConcatenatedTransform? inverse; + /// - /// + /// Initializes a new instance of the class. /// - [Serializable] - internal class ConcatenatedTransform : MathTransform, ICoordinateTransformationCore - { - /// - /// - /// - private MathTransform _inverse; - private readonly List _coordinateTransformationList; - - /// - /// - /// - public ConcatenatedTransform() - { _coordinateTransformationList = new List();} - - /// - /// - /// - /// - public ConcatenatedTransform(IEnumerable transformList) - : this() - { - _coordinateTransformationList.AddRange(transformList); - } - - - /// - /// - /// - public IList CoordinateTransformationList - { - get { return _coordinateTransformationList; } - /* - set - { - _coordinateTransformationList = value; - _inverse = null; - } - */ - } - - - - public override int DimSource - { - get { return (_coordinateTransformationList[0]).SourceCS.Dimension; } - } - - public override int DimTarget - { - get { return _coordinateTransformationList[_coordinateTransformationList.Count-1].TargetCS.Dimension; } - } - - - /// - public override void Transform(ref double x, ref double y, ref double z) - { - foreach (var ctc in _coordinateTransformationList) - { - if (ctc is CoordinateTransformation ct) - ct.MathTransform.Transform(ref x, ref y, ref z); - else if (ctc is ConcatenatedTransform cct) - cct.Transform(ref x, ref y, ref z); - } - } - - /// - /// Returns the inverse of this conversion. - /// - /// IMathTransform that is the reverse of the current conversion. - public override MathTransform Inverse() - { - if (_inverse == null) - { - _inverse = Clone(); - _inverse.Invert(); - } - return _inverse; - } - - /// - /// Reverses the transformation - /// - public override void Invert() - { - _coordinateTransformationList.Reverse(); - foreach (var ic in _coordinateTransformationList) - { - if (ic is CoordinateTransformation ct) - ct.MathTransform.Invert(); - else if (ic is ConcatenatedTransform cct) - cct.Invert(); - } - } - - public ConcatenatedTransform Clone() - { - var clonedList = new List(_coordinateTransformationList.Count); - foreach (var ct in _coordinateTransformationList) - clonedList.Add(CloneCoordinateTransformation(ct)); - return new ConcatenatedTransform(clonedList); - } - - private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = - new CoordinateTransformationFactory(); - - private static ICoordinateTransformationCore CloneCoordinateTransformation(ICoordinateTransformationCore ict) - { - return CoordinateTransformationFactory.CreateFromCoordinateSystems(ict.SourceCS, ict.TargetCS); - } - - /// - /// Gets a Well-Known text representation of this object. - /// - /// - public override string WKT - { - get { throw new NotImplementedException(); } - } - - /// - /// Gets an XML representation of this object. - /// - /// - public override string XML - { - get { throw new NotImplementedException(); } - } - - public CoordinateSystem SourceCS { get => CoordinateTransformationList[0].SourceCS; } - - public CoordinateSystem TargetCS { get => CoordinateTransformationList[CoordinateTransformationList.Count-1].TargetCS; } + public ConcatenatedTransform() + { + this.coordinateTransformationList = []; + } + + /// + /// Initializes a new instance of the class. + /// + /// Ordered sequence of coordinate transformations to concatenate. + public ConcatenatedTransform(IEnumerable transformList) + : this() + { + transformList = ArgumentGuard.ThrowIfNull(transformList, nameof(transformList)); + if (transformList is ICollection collection) + { + this.coordinateTransformationList.Capacity = collection.Count; + } + + this.coordinateTransformationList.AddRange(transformList); + } + + /// + /// Gets the ordered list of transformations that form this concatenated transform. + /// + public IList CoordinateTransformationList => this.coordinateTransformationList; + + /// + public override int DimSource => this.GetFirstTransform().SourceCS.Dimension; + + /// + public override int DimTarget => this.GetLastTransform().TargetCS.Dimension; + + /// + public CoordinateSystem SourceCS { get => this.GetFirstTransform().SourceCS; } + + /// + public CoordinateSystem TargetCS { get => this.GetLastTransform().TargetCS; } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + foreach (ICoordinateTransformationCore ctc in this.coordinateTransformationList) + { + TransformCore(ctc, ref x, ref y, ref z); + } + } + + /// + /// Returns the inverse of this conversion. + /// + /// A that reverses this concatenated transform. + public override MathTransform Inverse() + { + if (this.inverse is null) + { + this.inverse = new ConcatenatedTransform(BuildInvertedCoordinateTransformations(this.coordinateTransformationList)); + } + + return this.inverse; + } + + /// + /// Reverses the transformation. + /// + public override void Invert() + { + this.inverse = null; + List inverted = BuildInvertedCoordinateTransformations(this.coordinateTransformationList); + this.coordinateTransformationList.Clear(); + foreach (ICoordinateTransformationCore transformation in inverted) + { + this.coordinateTransformationList.Add(transformation); + } + } + + /// + /// Creates a deep clone of this concatenated transform with freshly resolved sub-transformations. + /// + /// A new with cloned sub-transformations. + public ConcatenatedTransform Clone() + { + var clonedList = new List(this.coordinateTransformationList.Count); + foreach (ICoordinateTransformationCore ct in this.coordinateTransformationList) + { + clonedList.Add(CloneCoordinateTransformation(ct)); + } + + return new ConcatenatedTransform(clonedList); + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + foreach (ICoordinateTransformationCore ctc in this.coordinateTransformationList) + { + TransformCore(ctc, ref x, ref y, ref z, ref t); + } + } + + private static ICoordinateTransformationCore CloneCoordinateTransformation(ICoordinateTransformationCore ict) + { + return CoordinateTransformationFactory.CreateFromCoordinateSystems(ict.SourceCS, ict.TargetCS); + } + + private static List BuildInvertedCoordinateTransformations(List transformations) + { + var inverted = new List(transformations.Count); + for (int i = transformations.Count - 1; i >= 0; i--) + { + inverted.Add(InvertCoordinateTransformation(transformations[i])); + } + + return inverted; + } + + private static ICoordinateTransformationCore InvertCoordinateTransformation(ICoordinateTransformationCore transformation) + { + if (transformation is CoordinateTransformation coordinateTransformation) + { + return new CoordinateTransformation( + coordinateTransformation.TargetCS, + coordinateTransformation.SourceCS, + coordinateTransformation.TransformType, + coordinateTransformation.MathTransform.Inverse(), + coordinateTransformation.Name, + coordinateTransformation.Authority, + coordinateTransformation.AuthorityCode, + coordinateTransformation.AreaOfUse, + coordinateTransformation.Remarks); + } + + if (transformation is ConcatenatedTransform concatenatedTransform) + { + return AssertConcatenatedInverse(concatenatedTransform.Inverse()); + } + + throw new NotSupportedException($"Unsupported concatenated child type '{transformation.GetType().FullName}'."); + } + + private static ConcatenatedTransform AssertConcatenatedInverse(MathTransform inverse) + { + if (inverse is ConcatenatedTransform concatenatedTransform) + { + return concatenatedTransform; + } + + throw new InvalidOperationException("Concatenated child inverse did not return a ConcatenatedTransform."); + } + + private static void TransformCore(ICoordinateTransformationCore transformation, ref double x, ref double y, ref double z) + { + if (transformation is CoordinateTransformation coordinateTransformation) + { + coordinateTransformation.MathTransform.Transform(ref x, ref y, ref z); + } + else if (transformation is ConcatenatedTransform concatenatedTransform) + { + concatenatedTransform.Transform(ref x, ref y, ref z); + } + } + + private static void TransformCore(ICoordinateTransformationCore transformation, ref double x, ref double y, ref double z, ref double t) + { + if (transformation is CoordinateTransformation coordinateTransformation) + { + coordinateTransformation.MathTransform.Transform(ref x, ref y, ref z, ref t); + } + else if (transformation is ConcatenatedTransform concatenatedTransform) + { + concatenatedTransform.Transform(ref x, ref y, ref z, ref t); + } + } + + private ICoordinateTransformationCore GetFirstTransform() + { + if (this.coordinateTransformationList.Count == 0) + { + throw new InvalidOperationException("Concatenated transform does not contain any child transformations."); + } + + return this.coordinateTransformationList[0]; + } + + private ICoordinateTransformationCore GetLastTransform() + { + if (this.coordinateTransformationList.Count == 0) + { + throw new InvalidOperationException("Concatenated transform does not contain any child transformations."); + } + + return this.coordinateTransformationList[^1]; } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/CoordinateOperationResolver.cs b/src/ProjNet/CoordinateSystems/Transformations/CoordinateOperationResolver.cs new file mode 100644 index 00000000..d7da3ae8 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/CoordinateOperationResolver.cs @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Resolves the best available coordinate operation candidate for a source/target pair. +/// +/// +/// Resolution is intentionally lightweight: it scores at most two candidates, +/// an identity operation when source and target are parameter-equivalent, and a +/// direct non-identity operation returned by the supplied resolver. The higher +/// score wins, so exact identity is preferred whenever it is valid. +/// +/// PROJ: computation of coordinate operations between two CRS. +internal static class CoordinateOperationResolver +{ + private const int BaselineDirectCandidateScore = 0; + private const int IdentityCandidateScore = 1000; + private const int DistinctAuthorityDirectCandidateScore = IdentityCandidateScore + 1; + + /// + /// Resolves the preferred transformation from identity and direct-operation candidates. + /// + /// Source coordinate system. + /// Target coordinate system. + /// Resolver delegate for non-identity operations. + /// Best scored transformation, or when none is available. + internal static ICoordinateTransformation? Resolve( + CoordinateSystem source, + CoordinateSystem target, + Func directResolver) + { + source = ArgumentGuard.ThrowIfNull(source, nameof(source)); + target = ArgumentGuard.ThrowIfNull(target, nameof(target)); + directResolver = ArgumentGuard.ThrowIfNull(directResolver, nameof(directResolver)); + + OperationCandidate? bestCandidate = null; + bestCandidate = SelectHigherScore(bestCandidate, CreateIdentityCandidate(source, target)); + + ICoordinateTransformation? directCandidate = directResolver(source, target); + if (directCandidate is not null) + { + int directScore = GetDirectCandidateScore(source, target, directCandidate); + bestCandidate = SelectHigherScore(bestCandidate, new OperationCandidate(directCandidate, directScore)); + } + + return bestCandidate?.Transformation; + } + + private static OperationCandidate? CreateIdentityCandidate(CoordinateSystem source, CoordinateSystem target) + { + if (!ReferenceEquals(source, target) && !source.EqualParams(target)) + { + return null; + } + + int dimension = Math.Max(2, Math.Max(source.Dimension, target.Dimension)); + var transformation = new CoordinateTransformation( + source, + target, + TransformType.Conversion, + new IdentityMathTransform(dimension), + string.Empty, + string.Empty, + -1, + string.Empty, + string.Empty); + return new OperationCandidate(transformation, IdentityCandidateScore); + } + + private static OperationCandidate? SelectHigherScore(OperationCandidate? left, OperationCandidate? right) + { + if (right is null) + { + return left; + } + + return left is null ? right : right.Score > left.Score ? right : left; + } + + private static int GetDirectCandidateScore( + CoordinateSystem source, + CoordinateSystem target, + ICoordinateTransformation directCandidate) + { + return !ReferenceEquals(source, target) + && source.EqualParams(target) + && HasDistinctAuthorityIdentity(source, target) + && HasAuthorityMetadata(directCandidate) + && directCandidate.MathTransform is not IdentityMathTransform + ? DistinctAuthorityDirectCandidateScore + : BaselineDirectCandidateScore; + } + + private static bool HasAuthorityMetadata(ICoordinateTransformation transformation) + { + return transformation is not null + && !string.IsNullOrWhiteSpace(transformation.Authority) + && transformation.AuthorityCode >= 0; + } + + private static bool HasDistinctAuthorityIdentity(CoordinateSystem source, CoordinateSystem target) + { + return !string.IsNullOrWhiteSpace(source.Authority) + && !string.IsNullOrWhiteSpace(target.Authority) + && source.AuthorityCode > 0 + && target.AuthorityCode > 0 + && (!source.Authority.Equals(target.Authority, StringComparison.OrdinalIgnoreCase) + || source.AuthorityCode != target.AuthorityCode); + } + + private sealed class OperationCandidate(ICoordinateTransformation transformation, int score) + { + internal int Score { get; } = score; + + internal ICoordinateTransformation Transformation { get; } = transformation; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformation.cs b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformation.cs index b44570c6..108294e8 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformation.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformation.cs @@ -1,111 +1,98 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems.Transformations; using System; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// Describes a coordinate transformation. This class only describes a +/// coordinate transformation, it does not actually perform the transform +/// operation on points. To transform points you must use a . +/// +public sealed class CoordinateTransformation : ICoordinateTransformation { - /// - /// Describes a coordinate transformation. This class only describes a - /// coordinate transformation, it does not actually perform the transform - /// operation on points. To transform points you must use a . + /// + /// Initializes a new instance of the class. /// - [Serializable] - public class CoordinateTransformation : ICoordinateTransformation + /// Source coordinate system. + /// Target coordinate system. + /// Transformation type. + /// Math transform. + /// Name of transform. + /// Authority. + /// Authority code. + /// Area of use. + /// Remarks. + internal CoordinateTransformation( + CoordinateSystem sourceCS, + CoordinateSystem targetCS, + TransformType transformType, + MathTransform mathTransform, + string name, + string authority, + long authorityCode, + string areaOfUse, + string remarks) { - /// - /// Initializes an instance of a CoordinateTransformation - /// - /// Source coordinate system - /// Target coordinate system - /// Transformation type - /// Math transform - /// Name of transform - /// Authority - /// Authority code - /// Area of use - /// Remarks - internal CoordinateTransformation(CoordinateSystem sourceCS, CoordinateSystem targetCS, TransformType transformType, MathTransform mathTransform, - string name, string authority, long authorityCode, string areaOfUse, string remarks) - { - TargetCS = targetCS; - SourceCS = sourceCS; - TransformType = transformType; - MathTransform = mathTransform; - Name = name; - Authority = authority; - AuthorityCode = authorityCode; - AreaOfUse = areaOfUse; - Remarks = remarks; - } - - - - #region ICoordinateTransformation Members - - /// - /// Human readable description of domain in source coordinate system. - /// - public string AreaOfUse { get; } + this.TargetCS = targetCS; + this.SourceCS = sourceCS; + this.TransformType = transformType; + this.MathTransform = mathTransform; + this.Name = name; + this.Authority = authority; + this.AuthorityCode = authorityCode; + this.AreaOfUse = areaOfUse; + this.Remarks = remarks; + } - /// - /// Authority which defined transformation and parameter values. - /// - /// - /// An Authority is an organization that maintains definitions of Authority Codes. For example the European Petroleum Survey Group (EPSG) maintains a database of coordinate systems, and other spatial referencing objects, where each object has a code number ID. For example, the EPSG code for a WGS84 Lat/Lon coordinate system is 4326 - /// - public string Authority { get; } + /// + /// Gets human readable description of domain in source coordinate system. + /// + public string AreaOfUse { get; } - /// - /// Code used by authority to identify transformation. An empty string is used for no code. - /// - /// The AuthorityCode is a compact string defined by an Authority to reference a particular spatial reference object. For example, the European Survey Group (EPSG) authority uses 32 bit integers to reference coordinate systems, so all their code strings will consist of a few digits. The EPSG code for WGS84 Lat/Lon is 4326. - public long AuthorityCode { get; } + /// + /// Gets authority which defined transformation and parameter values. + /// + /// + /// An Authority is an organization that maintains definitions of Authority Codes. For example the European Petroleum Survey Group (EPSG) maintains a database of coordinate systems, and other spatial referencing objects, where each object has a code number ID. For example, the EPSG code for a WGS84 Lat/Lon coordinate system is �4326�. + /// + public string Authority { get; } - /// - /// Gets math transform. - /// - public MathTransform MathTransform { get; } + /// + /// Gets code used by authority to identify transformation. An empty string is used for no code. + /// + /// The AuthorityCode is a compact string defined by an Authority to reference a particular spatial reference object. For example, the European Survey Group (EPSG) authority uses 32 bit integers to reference coordinate systems, so all their code strings will consist of a few digits. The EPSG code for WGS84 Lat/Lon is �4326�. + public long AuthorityCode { get; } - /// - /// Name of transformation. - /// - public string Name { get; } + /// + /// Gets math transform. + /// + public MathTransform MathTransform { get; } - /// - /// Gets the provider-supplied remarks. - /// - public string Remarks { get; } + /// + /// Gets name of transformation. + /// + public string Name { get; } - /// - /// Source coordinate system. - /// - public CoordinateSystem SourceCS { get; } + /// + /// Gets the provider-supplied remarks. + /// + public string Remarks { get; } - /// - /// Target coordinate system. - /// - public CoordinateSystem TargetCS { get; } + /// + /// Gets source coordinate system. + /// + public CoordinateSystem SourceCS { get; } - /// - /// Semantic type of transform. For example, a datum transformation or a coordinate conversion. - /// - public TransformType TransformType { get; } + /// + /// Gets target coordinate system. + /// + public CoordinateSystem TargetCS { get; } - #endregion - } + /// + /// Gets semantic type of transform. For example, a datum transformation or a coordinate conversion. + /// + public TransformType TransformType { get; } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.Core.cs b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.Core.cs new file mode 100644 index 00000000..3a64925a --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.Core.cs @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.Data; +using ProjNet.Data.Generated; +using ProjNet.Resources; + +/// +/// Creates coordinate transformations. +/// +/// +/// Core coordinate-system conversion routing and math-transform composition helpers. +/// +public partial class CoordinateTransformationFactory +{ + private static CoordinateSystemRuntimeKind GetCoordinateSystemRuntimeKind(CoordinateSystem coordinateSystem) + { + if (coordinateSystem is ProjectedCoordinateSystem) + { + return CoordinateSystemRuntimeKind.Projected; + } + + if (coordinateSystem is GeographicCoordinateSystem) + { + return CoordinateSystemRuntimeKind.Geographic; + } + + if (coordinateSystem is GeocentricCoordinateSystem) + { + return CoordinateSystemRuntimeKind.Geocentric; + } + + return coordinateSystem is FittedCoordinateSystem ? CoordinateSystemRuntimeKind.Fitted : CoordinateSystemRuntimeKind.Unknown; + } + + private ICoordinateTransformation CreateFromCoordinateSystemsCore(CoordinateSystem sourceCS, CoordinateSystem targetCS) + { + if (TryCreateSimpleCoordinateSystemConversion(sourceCS, targetCS, out ICoordinateTransformation? simpleConversionCandidate)) + { + return ArgumentGuard.ThrowIfNull(simpleConversionCandidate, nameof(simpleConversionCandidate)); + } + + CoordinateSystemRuntimeKind sourceKind = GetCoordinateSystemRuntimeKind(sourceCS); + CoordinateSystemRuntimeKind targetKind = GetCoordinateSystemRuntimeKind(targetCS); + + // Fitted -> Any + if (sourceKind == CoordinateSystemRuntimeKind.Fitted) + { + return Fitt2Any((FittedCoordinateSystem)sourceCS, targetCS); + } + + // Any -> Fitted + if (targetKind == CoordinateSystemRuntimeKind.Fitted) + { + return Any2Fitt(sourceCS, (FittedCoordinateSystem)targetCS); + } + + // Encode the fixed source/target runtime-kind pair as XY so the switch can stay dense + // without needing a larger tuple-based dispatch structure. + int route = ((int)sourceKind * 10) + (int)targetKind; + return route switch + { + // Projected -> Geographic + 12 => Proj2Geog((ProjectedCoordinateSystem)sourceCS, (GeographicCoordinateSystem)targetCS), + + // Geographic -> Projected + 21 => Geog2Proj((GeographicCoordinateSystem)sourceCS, (ProjectedCoordinateSystem)targetCS), + + // Geographic -> Geocentric + 23 => Geog2Geoc((GeographicCoordinateSystem)sourceCS, (GeocentricCoordinateSystem)targetCS), + + // Geocentric -> Geographic + 32 => Geoc2Geog((GeocentricCoordinateSystem)sourceCS, (GeographicCoordinateSystem)targetCS), + + // Projected -> Projected + 11 => Proj2Proj((ProjectedCoordinateSystem)sourceCS, (ProjectedCoordinateSystem)targetCS), + + // Geocentric -> Geocentric + 33 => CreateGeoc2Geoc((GeocentricCoordinateSystem)sourceCS, (GeocentricCoordinateSystem)targetCS) + ?? CreateTransform( + sourceCS, + targetCS, + TransformType.Conversion, + new IdentityMathTransform(Math.Max(sourceCS.Dimension, targetCS.Dimension))), + + // Geographic -> Geographic + 22 => CreateGeog2Geog((GeographicCoordinateSystem)sourceCS, (GeographicCoordinateSystem)targetCS), + _ => throw new NotSupportedException("No support for transforming between the two specified coordinate systems"), + }; + } + + private static bool TryCreateSimpleCoordinateSystemConversion( + CoordinateSystem source, + CoordinateSystem target, + [NotNullWhen(true)] out ICoordinateTransformation? transformation) + { + transformation = null; + + if (source is null || target is null) + { + return false; + } + + if (source.GetType() != target.GetType()) + { + return false; + } + + if (!HaveEquivalentDefinitionsIgnoringAxisAndUnits(source, target)) + { + return false; + } + + if (!TryCreateAxisSwapConversionTransform(source, target, out MathTransform? axisSwapTransformCandidate)) + { + return false; + } + + if (!TryCreateUnitConversionTransform(source, target, out MathTransform? unitConversionTransformCandidate)) + { + return false; + } + + MathTransform axisSwapTransform = ArgumentGuard.ThrowIfNull(axisSwapTransformCandidate, nameof(axisSwapTransformCandidate)); + MathTransform unitConversionTransform = ArgumentGuard.ThrowIfNull(unitConversionTransformCandidate, nameof(unitConversionTransformCandidate)); + var transforms = new List(2); + if (!unitConversionTransform.Identity()) + { + transforms.Add(unitConversionTransform); + } + + if (!axisSwapTransform.Identity()) + { + transforms.Add(axisSwapTransform); + } + + MathTransform mathTransform; + if (transforms.Count == 0) + { + mathTransform = new IdentityMathTransform(Math.Max(source.Dimension, target.Dimension)); + } + else if (transforms.Count == 1) + { + mathTransform = transforms[0]; + } + else + { + mathTransform = new CompositeMathTransform(transforms); + } + + transformation = CreateTransform(source, target, TransformType.Conversion, mathTransform); + return true; + } + + private static bool TryCreateAxisSwapConversionTransform( + CoordinateSystem source, + CoordinateSystem target, + [NotNullWhen(true)] out MathTransform? transform) + { + if (AxisOrderHelper.TryCreateAxisSwapTransform(source, target, out transform)) + { + return true; + } + + if (HaveSameAxisOrientations(source, target)) + { + transform = new IdentityMathTransform(Math.Max(source.Dimension, target.Dimension)); + return true; + } + + transform = null; + return false; + } + + private static bool HaveSameAxisOrientations(CoordinateSystem source, CoordinateSystem target) + { + if (source.Dimension != target.Dimension) + { + return false; + } + + for (int i = 0; i < source.Dimension; i++) + { + if (source.GetAxis(i).Orientation != target.GetAxis(i).Orientation) + { + return false; + } + } + + return true; + } + + private static bool TryCreateUnitConversionTransform( + CoordinateSystem source, + CoordinateSystem target, + [NotNullWhen(true)] out MathTransform? transform) + { + if (source is GeographicCoordinateSystem sourceGeographic && target is GeographicCoordinateSystem targetGeographic) + { + return TryCreateUnitConversionTransform(sourceGeographic, targetGeographic, out transform); + } + + if (source is ProjectedCoordinateSystem sourceProjected && target is ProjectedCoordinateSystem targetProjected) + { + return TryCreateUnitConversionTransform(sourceProjected, targetProjected, out transform); + } + + if (source is GeocentricCoordinateSystem sourceGeocentric && target is GeocentricCoordinateSystem targetGeocentric) + { + return TryCreateUnitConversionTransform(sourceGeocentric, targetGeocentric, out transform); + } + + transform = null; + return false; + } + + private static bool TryCreateUnitConversionTransform( + GeographicCoordinateSystem source, + GeographicCoordinateSystem target, + [NotNullWhen(true)] out MathTransform? transform) + { + double scale = source.AngularUnit.RadiansPerUnit / target.AngularUnit.RadiansPerUnit; + transform = new UnitConvertMathTransform(source.Dimension, scale, scale); + return true; + } + + private static bool TryCreateUnitConversionTransform( + ProjectedCoordinateSystem source, + ProjectedCoordinateSystem target, + [NotNullWhen(true)] out MathTransform? transform) + { + if (source.LinearUnit is null || target.LinearUnit is null) + { + transform = null; + return false; + } + + double scale = source.LinearUnit.MetersPerUnit / target.LinearUnit.MetersPerUnit; + transform = new UnitConvertMathTransform(source.Dimension, scale, scale); + return true; + } + + private static bool TryCreateUnitConversionTransform( + GeocentricCoordinateSystem source, + GeocentricCoordinateSystem target, + [NotNullWhen(true)] out MathTransform? transform) + { + if (source.LinearUnit is null || target.LinearUnit is null) + { + transform = null; + return false; + } + + double scale = source.LinearUnit.MetersPerUnit / target.LinearUnit.MetersPerUnit; + transform = new UnitConvertMathTransform(source.Dimension, scale, scale); + return true; + } + + private static bool HaveEquivalentDefinitionsIgnoringAxisAndUnits(CoordinateSystem source, CoordinateSystem target) + { + if (source is GeographicCoordinateSystem sourceGeographic && target is GeographicCoordinateSystem targetGeographic) + { + return HaveEquivalentDefinitionsIgnoringAxisAndUnits(sourceGeographic, targetGeographic); + } + + if (source is ProjectedCoordinateSystem sourceProjected && target is ProjectedCoordinateSystem targetProjected) + { + return HaveEquivalentDefinitionsIgnoringAxisAndUnits(sourceProjected, targetProjected); + } + + return source is GeocentricCoordinateSystem sourceGeocentric && target is GeocentricCoordinateSystem targetGeocentric && HaveEquivalentDefinitionsIgnoringAxisAndUnits(sourceGeocentric, targetGeocentric); + } + + private static bool HaveEquivalentDefinitionsIgnoringAxisAndUnits( + GeographicCoordinateSystem source, + GeographicCoordinateSystem target) + { + return source.Dimension == target.Dimension && source.HorizontalDatum.EqualParams(target.HorizontalDatum) + && source.PrimeMeridian.EqualParams(target.PrimeMeridian); + } + + private static bool HaveEquivalentDefinitionsIgnoringAxisAndUnits( + ProjectedCoordinateSystem source, + ProjectedCoordinateSystem target) + { + if (source.Dimension != target.Dimension) + { + return false; + } + + HorizontalDatum? sourceHorizontalDatum = source.HorizontalDatum; + HorizontalDatum? targetHorizontalDatum = target.HorizontalDatum; + if ((sourceHorizontalDatum is null) != (targetHorizontalDatum is null)) + { + return false; + } + + bool horizontalDatumsEqual = sourceHorizontalDatum is null + || sourceHorizontalDatum.EqualParams(ArgumentGuard.ThrowIfNull(targetHorizontalDatum, nameof(targetHorizontalDatum))); + + return horizontalDatumsEqual + && source.Projection.EqualParams(target.Projection) + && HaveEquivalentDefinitionsIgnoringAxisAndUnits(source.GeographicCoordinateSystem, target.GeographicCoordinateSystem); + } + + private static bool HaveEquivalentDefinitionsIgnoringAxisAndUnits( + GeocentricCoordinateSystem source, + GeocentricCoordinateSystem target) + { + return source.Dimension == target.Dimension && source.HorizontalDatum.EqualParams(target.HorizontalDatum) + && source.PrimeMeridian.EqualParams(target.PrimeMeridian); + } + + private static void SimplifyTrans(ConcatenatedTransform mtrans, ref List mts) + { + foreach (ICoordinateTransformationCore t in mtrans.CoordinateTransformationList) + { + if (t is ConcatenatedTransform ct) + { + SimplifyTrans(ct, ref mts); + } + else + { + mts.Add(t); + } + } + } + + private static CoordinateTransformation Geog2Geoc(GeographicCoordinateSystem source, GeocentricCoordinateSystem target) + { + GeocentricTransform geocMathTransform = CreateCoordinateOperation(target); + if (source.PrimeMeridian.EqualParams(target.PrimeMeridian)) + { + return CreateTransform(source, target, TransformType.Conversion, geocMathTransform); + } + + var ct = new ConcatenatedTransform(); + ct.CoordinateTransformationList.Add(CreateTransform(source, target, TransformType.Transformation, new PrimeMeridianTransform(source.PrimeMeridian, target.PrimeMeridian))); + ct.CoordinateTransformationList.Add(CreateTransform(source, target, TransformType.Conversion, geocMathTransform)); + return CreateTransform(source, target, TransformType.Conversion, ct); + } + + private static CoordinateTransformation Geoc2Geog(GeocentricCoordinateSystem source, GeographicCoordinateSystem target) + { + MathTransform geocMathTransform = CreateCoordinateOperation(source).Inverse(); + if (source.PrimeMeridian.EqualParams(target.PrimeMeridian)) + { + return CreateTransform(source, target, TransformType.Conversion, geocMathTransform); + } + + var ct = new ConcatenatedTransform(); + ct.CoordinateTransformationList.Add(CreateTransform(source, target, TransformType.Conversion, geocMathTransform)); + ct.CoordinateTransformationList.Add(CreateTransform(source, target, TransformType.Transformation, new PrimeMeridianTransform(source.PrimeMeridian, target.PrimeMeridian))); + return CreateTransform(source, target, TransformType.Conversion, ct); + } + + private static CoordinateTransformation Proj2Proj(ProjectedCoordinateSystem source, ProjectedCoordinateSystem target) + { + if (source.GeographicCoordinateSystem.EqualParams(target.GeographicCoordinateSystem)) + { + return CreateDirectProjectedTransform(source, target); + } + + var ct = new ConcatenatedTransform(); + var ctFac = new CoordinateTransformationFactory(); + + // First transform from projection to geographic + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source, source.GeographicCoordinateSystem)); + + // Transform geographic to geographic: + ICoordinateTransformation? geogToGeog = ctFac.CreateFromCoordinateSystems( + source.GeographicCoordinateSystem, + target.GeographicCoordinateSystem); + if (geogToGeog is not null) + { + ct.CoordinateTransformationList.Add(geogToGeog); + } + + // Transform to new projection + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(target.GeographicCoordinateSystem, target)); + + return CreateTransform(source, target, TransformType.Transformation, ct); + } + + private static CoordinateTransformation CreateDirectProjectedTransform(ProjectedCoordinateSystem source, ProjectedCoordinateSystem target) + { + MathTransform sourceInverseProjection = CreateCoordinateOperation( + source.Projection, + source.GeographicCoordinateSystem.HorizontalDatum.Ellipsoid, + source.LinearUnit).Inverse(); + + MathTransform targetForwardProjection = CreateCoordinateOperation( + target.Projection, + target.GeographicCoordinateSystem.HorizontalDatum.Ellipsoid, + target.LinearUnit); + + var directMathTransform = new ConcatenatedTransform(); + directMathTransform.CoordinateTransformationList.Add( + CreateTransform(source, target, TransformType.Conversion, sourceInverseProjection)); + directMathTransform.CoordinateTransformationList.Add( + CreateTransform(source, target, TransformType.Conversion, targetForwardProjection)); + + return CreateTransform(source, target, TransformType.Transformation, directMathTransform); + } + + private static CoordinateTransformation Geog2Proj(GeographicCoordinateSystem source, ProjectedCoordinateSystem target) + { + if (source.EqualParams(target.GeographicCoordinateSystem)) + { + MathTransform mathTransform = CreateCoordinateOperation( + target.Projection, + target.GeographicCoordinateSystem.HorizontalDatum.Ellipsoid, + target.LinearUnit); + return CreateTransform(source, target, TransformType.Transformation, mathTransform); + } + + // Geographic coordinatesystems differ - Create concatenated transform + var ct = new ConcatenatedTransform(); + var ctFac = new CoordinateTransformationFactory(); + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source, target.GeographicCoordinateSystem)); + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(target.GeographicCoordinateSystem, target)); + return CreateTransform(source, target, TransformType.Transformation, ct); + } + + private static CoordinateTransformation Proj2Geog(ProjectedCoordinateSystem source, GeographicCoordinateSystem target) + { + if (source.GeographicCoordinateSystem.EqualParams(target)) + { + MathTransform mathTransform = CreateCoordinateOperation( + source.Projection, + source.GeographicCoordinateSystem.HorizontalDatum.Ellipsoid, + source.LinearUnit).Inverse(); + return CreateTransform(source, target, TransformType.Transformation, mathTransform); + } + else + { + // Geographic coordinate systems differ - create concatenated transform + var ct = new ConcatenatedTransform(); + var ctFac = new CoordinateTransformationFactory(); + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source, source.GeographicCoordinateSystem)); + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source.GeographicCoordinateSystem, target)); + return CreateTransform(source, target, TransformType.Transformation, ct); + } + } + + /// + /// Geographic to geographic transformation. + /// + /// Adds a datum shift if necessary. + /// The source parameter. + /// The target parameter. + /// The transformation result. + private static CoordinateTransformation CreateGeog2Geog(GeographicCoordinateSystem source, GeographicCoordinateSystem target) + { + if (source.HorizontalDatum.EqualParams(target.HorizontalDatum)) + { + // No datum shift needed + return CreateTransform(source, target, TransformType.Conversion, new GeographicTransform(source, target)); + } + + // Create datum shift + // Convert to geocentric, perform shift and return to geographic + var ctFac = new CoordinateTransformationFactory(); + var cFac = new CoordinateSystemFactory(); + GeocentricCoordinateSystem sourceCentric = cFac.CreateGeocentricCoordinateSystem( + $"{source.HorizontalDatum.Name} Geocentric", + source.HorizontalDatum, + LinearUnit.Metre, + source.PrimeMeridian); + + // Keep the intermediate geocentric pair on the source prime meridian; the surrounding + // geographic legs handle prime-meridian normalization before and after the datum shift. + GeocentricCoordinateSystem targetCentric = cFac.CreateGeocentricCoordinateSystem( + $"{target.HorizontalDatum.Name} Geocentric", + target.HorizontalDatum, + LinearUnit.Metre, + source.PrimeMeridian); + var ct = new ConcatenatedTransform(); + AddIfNotNull(ct, ctFac.CreateFromCoordinateSystems(source, sourceCentric)); + AddIfNotNull(ct, ctFac.CreateFromCoordinateSystems(sourceCentric, targetCentric)); + AddIfNotNull(ct, ctFac.CreateFromCoordinateSystems(targetCentric, target)); + + return CreateTransform(source, target, TransformType.Transformation, ct); + } + + private static void AddIfNotNull(ConcatenatedTransform concatTrans, ICoordinateTransformation trans) + { + if (trans is not null) + { + concatTrans.CoordinateTransformationList.Add(trans); + } + } + + /// + /// Geocentric to Geocentric transformation. + /// + /// The source parameter. + /// The target parameter. + /// The transformation result. + private static CoordinateTransformation? CreateGeoc2Geoc(GeocentricCoordinateSystem source, GeocentricCoordinateSystem target) + { + var ct = new ConcatenatedTransform(); + + // Does source has a datum different from WGS84 and is there a shift specified? + if (source.HorizontalDatum.Wgs84Parameters is not null && !source.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) + { + ct.CoordinateTransformationList.Add( + CreateTransform( + (target.HorizontalDatum.Wgs84Parameters is null || target.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) ? target : GeocentricCoordinateSystem.WGS84, + source, + TransformType.Transformation, + new DatumTransform(source.HorizontalDatum.Wgs84Parameters))); + } + + // Does target has a datum different from WGS84 and is there a shift specified? + if (target.HorizontalDatum.Wgs84Parameters is not null && !target.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) + { + ct.CoordinateTransformationList.Add( + CreateTransform( + (source.HorizontalDatum.Wgs84Parameters is null || source.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) ? source : GeocentricCoordinateSystem.WGS84, + target, + TransformType.Transformation, + new DatumTransform(target.HorizontalDatum.Wgs84Parameters).Inverse())); + } + + // If we don't have a transformation in this list, return null + if (ct.CoordinateTransformationList.Count == 0) + { + return null; + } + + // If we only have one shift, lets just return the datumshift from/to wgs84 + return ct.CoordinateTransformationList.Count == 1 + ? CreateTransform( + source, + target, + TransformType.ConversionAndTransformation, + ((ICoordinateTransformation)ct.CoordinateTransformationList[0]).MathTransform) + : CreateTransform(source, target, TransformType.ConversionAndTransformation, ct); + } + + /// + /// Creates transformation from fitted coordinate system to the target one. + /// + /// The source parameter. + /// The target parameter. + /// The transformation result. + private static CoordinateTransformation Fitt2Any(FittedCoordinateSystem source, CoordinateSystem target) + { + // transform from fitted to base system of fitted (which is equal to target) + MathTransform mt = CreateFittedTransform(source); + + // case when target system is equal to base system of the fitted + if (source.BaseCoordinateSystem.EqualParams(target)) + { + // Transform form base system of fitted to target coordinate system + return CreateTransform(source, target, TransformType.Transformation, mt); + } + + // Transform form base system of fitted to target coordinate system + var ct = new ConcatenatedTransform(); + ct.CoordinateTransformationList.Add(CreateTransform(source, source.BaseCoordinateSystem, TransformType.Transformation, mt)); + + // Transform form base system of fitted to target coordinate system + var ctFac = new CoordinateTransformationFactory(); + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source.BaseCoordinateSystem, target)); + + return CreateTransform(source, target, TransformType.Transformation, ct); + } + + /// + /// Creates transformation from source coordinate system to specified target system which is the fitted one. + /// + /// The source parameter. + /// The target parameter. + /// The transformation result. + private static CoordinateTransformation Any2Fitt(CoordinateSystem source, FittedCoordinateSystem target) + { + // Transform form base system of fitted to target coordinate system - use invered math transform + MathTransform invMt = CreateFittedTransform(target).Inverse(); + + // case when source system is equal to base system of the fitted + if (target.BaseCoordinateSystem.EqualParams(source)) + { + // Transform form base system of fitted to target coordinate system + return CreateTransform(source, target, TransformType.Transformation, invMt); + } + + var ct = new ConcatenatedTransform(); + + // First transform from source to base system of fitted + var ctFac = new CoordinateTransformationFactory(); + ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source, target.BaseCoordinateSystem)); + + // Transform form base system of fitted to target coordinate system - use invered math transform + ct.CoordinateTransformationList.Add(CreateTransform(target.BaseCoordinateSystem, target, TransformType.Transformation, invMt)); + + return CreateTransform(source, target, TransformType.Transformation, ct); + } + + private static MathTransform CreateFittedTransform(FittedCoordinateSystem fittedSystem) + { + // create transform From fitted to base and inverts it + return fittedSystem.ToBaseTransform; + } + + /// + /// Creates an instance of CoordinateTransformation as an anonymous transformation without neither autohority nor code defined. + /// + /// Source coordinate system. + /// Target coordinate system. + /// Transformation type. + /// Math transform. + private static CoordinateTransformation CreateTransform(CoordinateSystem sourceCS, CoordinateSystem targetCS, TransformType transformType, MathTransform mathTransform) + { + return new CoordinateTransformation(sourceCS, targetCS, transformType, mathTransform, string.Empty, string.Empty, -1, string.Empty, string.Empty); + } + + private static GeocentricTransform CreateCoordinateOperation(GeocentricCoordinateSystem geo) + { + var parameterList = new List(2); + + Ellipsoid ellipsoid = geo.HorizontalDatum.Ellipsoid; + + if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_major", StringComparison.Ordinal)) is null) + { + parameterList.Add(new ProjectionParameter("semi_major", ellipsoid.SemiMajorAxis)); + } + + if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_minor", StringComparison.Ordinal)) is null) + { + parameterList.Add(new ProjectionParameter("semi_minor", ellipsoid.SemiMinorAxis)); + } + + return new GeocentricTransform(parameterList); + } + + private static MathTransform CreateCoordinateOperation(IProjection projection, Ellipsoid ellipsoid, LinearUnit unit) + { + var parameterList = new List(projection.NumParameters); + for (int i = 0; i < projection.NumParameters; i++) + { + parameterList.Add(projection.GetParameter(i)); + } + + if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_major", StringComparison.Ordinal)) is null) + { + parameterList.Add(new ProjectionParameter("semi_major", ellipsoid.SemiMajorAxis)); + } + + if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_minor", StringComparison.Ordinal)) is null) + { + parameterList.Add(new ProjectionParameter("semi_minor", ellipsoid.SemiMinorAxis)); + } + + if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("unit", StringComparison.Ordinal)) is null) + { + parameterList.Add(new ProjectionParameter("unit", unit.MetersPerUnit)); + } + + return ProjectionsRegistry.CreateProjection(projection.ClassName, parameterList); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.Operations.cs b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.Operations.cs new file mode 100644 index 00000000..9218e6f0 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.Operations.cs @@ -0,0 +1,1199 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.Data; +using ProjNet.Data.Generated; +using ProjNet.Resources; + +/// +/// Creates coordinate transformations. +/// +/// +/// Explicit-operation, metadata, and grid-resolution helpers. +/// +public partial class CoordinateTransformationFactory +{ + private static bool TryCreateExplicitOperationTransformation( + CoordinateSystem source, + CoordinateSystem target, + CoordinateOperationDefinition operation, + string? resolvedGridPath, + [NotNullWhen(true)] out ICoordinateTransformation? transformation) + => TryCreateExplicitOperationTransformation(source, target, operation, resolvedGridPath, null, out transformation); + + private static bool TryCreateExplicitOperationTransformation( + CoordinateSystem source, + CoordinateSystem target, + CoordinateOperationDefinition operation, + string? resolvedGridPath, + HashSet? visitedOperationCodes, + [NotNullWhen(true)] out ICoordinateTransformation? transformation) + { + transformation = null; + + if (source is GeographicCoordinateSystem sourceGeographic && target is GeographicCoordinateSystem targetGeographic) + { + if (!TryCreateExplicitGeographicTransformation(sourceGeographic, targetGeographic, operation, visitedOperationCodes, out CoordinateTransformation? geographicTransformation)) + { + return false; + } + + transformation = CreateMetadataBackedTransformation(source, target, geographicTransformation, operation, resolvedGridPath); + return true; + } + + if (source is ProjectedCoordinateSystem sourceProjected && target is ProjectedCoordinateSystem targetProjected) + { + if (!TryCreateExplicitProjectedTransformation(sourceProjected, targetProjected, operation, visitedOperationCodes, out CoordinateTransformation? projectedTransformation)) + { + return false; + } + + transformation = CreateMetadataBackedTransformation(source, target, projectedTransformation, operation, resolvedGridPath); + return true; + } + + if (source is GeocentricCoordinateSystem sourceGeocentric && target is GeocentricCoordinateSystem targetGeocentric) + { + if (!TryCreateExplicitGeocentricTransformation(sourceGeocentric, targetGeocentric, operation, visitedOperationCodes, out CoordinateTransformation? geocentricTransformation)) + { + return false; + } + + transformation = CreateMetadataBackedTransformation(source, target, geocentricTransformation, operation, resolvedGridPath); + return true; + } + + if (operation.OperationKind == CoordinateOperationKind.ConcatenatedOperation + && TryCreateConcatenatedOperationTransformation(source, target, operation, visitedOperationCodes, out CoordinateTransformation? concatenatedTransformation)) + { + transformation = CreateMetadataBackedTransformation(source, target, concatenatedTransformation, operation, resolvedGridPath); + return true; + } + + return false; + } + + private static bool TryCreateDirectProjectedTransformation( + CoordinateSystem source, + CoordinateSystem target, + CoordinateOperationDefinition operation, + string? resolvedGridPath, + [NotNullWhen(true)] out ICoordinateTransformation? transformation) + { + transformation = null; + + if (source is not ProjectedCoordinateSystem sourceProjected || target is not ProjectedCoordinateSystem targetProjected) + { + return false; + } + + CoordinateTransformation fallback = CreateDirectProjectedTransform(sourceProjected, targetProjected); + transformation = CreateMetadataBackedTransformation(source, target, fallback, operation, resolvedGridPath); + return true; + } + + private static bool TryCreateExplicitGeographicTransformation( + GeographicCoordinateSystem source, + GeographicCoordinateSystem target, + CoordinateOperationDefinition operation, + [NotNullWhen(true)] out CoordinateTransformation? transformation) + => TryCreateExplicitGeographicTransformation(source, target, operation, null, out transformation); + + private static bool TryCreateExplicitGeographicTransformation( + GeographicCoordinateSystem source, + GeographicCoordinateSystem target, + CoordinateOperationDefinition operation, + HashSet? visitedOperationCodes, + [NotNullWhen(true)] out CoordinateTransformation? transformation) + { + transformation = null; + + if (operation.OperationKind == CoordinateOperationKind.ConcatenatedOperation) + { + return TryCreateConcatenatedOperationTransformation(source, target, operation, visitedOperationCodes, out transformation); + } + + if (operation.OperationKind != CoordinateOperationKind.Transformation) + { + return false; + } + + if (TryCreateDirectGeographicMathTransform(operation, out MathTransform? directMathTransform)) + { + transformation = CreateTransform(source, target, TransformType.Transformation, directMathTransform); + return true; + } + + MathTransform? geocentricMathTransform = TryCreateExplicitGeocentricMathTransform(operation, out MathTransform? explicitGeocentricMathTransform) + ? explicitGeocentricMathTransform + : TryCreateBursaWolfParameters(operation, out Wgs84ConversionInfo? helmert) + ? new DatumTransform(helmert) + : null; + if (geocentricMathTransform is null) + { + return false; + } + + var ct = new ConcatenatedTransform(); + var csFactory = new CoordinateSystemFactory(); + + GeocentricCoordinateSystem sourceCentric = csFactory.CreateGeocentricCoordinateSystem( + $"{source.HorizontalDatum.Name} Geocentric", + source.HorizontalDatum, + LinearUnit.Metre, + source.PrimeMeridian); + + GeocentricCoordinateSystem targetCentric = csFactory.CreateGeocentricCoordinateSystem( + $"{target.HorizontalDatum.Name} Geocentric", + target.HorizontalDatum, + LinearUnit.Metre, + target.PrimeMeridian); + + AddIfNotNull(ct, Geog2Geoc(source, sourceCentric)); + AddIfNotNull(ct, CreateTransform(sourceCentric, targetCentric, TransformType.Transformation, geocentricMathTransform)); + AddIfNotNull(ct, Geoc2Geog(targetCentric, target)); + + transformation = CreateTransform(source, target, TransformType.Transformation, ct); + return true; + } + + private static bool TryCreateExplicitProjectedTransformation( + ProjectedCoordinateSystem source, + ProjectedCoordinateSystem target, + CoordinateOperationDefinition operation, + [NotNullWhen(true)] out CoordinateTransformation? transformation) + => TryCreateExplicitProjectedTransformation(source, target, operation, null, out transformation); + + private static bool TryCreateExplicitProjectedTransformation( + ProjectedCoordinateSystem source, + ProjectedCoordinateSystem target, + CoordinateOperationDefinition operation, + HashSet? visitedOperationCodes, + [NotNullWhen(true)] out CoordinateTransformation? transformation) + { + transformation = null; + + if (operation.OperationKind == CoordinateOperationKind.ConcatenatedOperation + && TryGetEpsgCode(source, out int sourceSrid) + && TryGetEpsgCode(target, out int targetSrid) + && sourceSrid == operation.SourceSrid + && targetSrid == operation.TargetSrid) + { + return TryCreateConcatenatedOperationTransformation(source, target, operation, visitedOperationCodes, out transformation); + } + + if (!TryCreateExplicitGeographicTransformation( + source.GeographicCoordinateSystem, + target.GeographicCoordinateSystem, + operation, + visitedOperationCodes, + out CoordinateTransformation? geographicTransformation)) + { + return false; + } + + var ct = new ConcatenatedTransform(); + AddIfNotNull(ct, Proj2Geog(source, source.GeographicCoordinateSystem)); + AddIfNotNull(ct, geographicTransformation); + AddIfNotNull(ct, Geog2Proj(target.GeographicCoordinateSystem, target)); + + transformation = CreateTransform(source, target, TransformType.Transformation, ct); + return true; + } + + private static bool TryCreateExplicitGeocentricTransformation( + GeocentricCoordinateSystem source, + GeocentricCoordinateSystem target, + CoordinateOperationDefinition operation, + [NotNullWhen(true)] out CoordinateTransformation? transformation) + => TryCreateExplicitGeocentricTransformation(source, target, operation, null, out transformation); + + private static bool TryCreateExplicitGeocentricTransformation( + GeocentricCoordinateSystem source, + GeocentricCoordinateSystem target, + CoordinateOperationDefinition operation, + HashSet? visitedOperationCodes, + [NotNullWhen(true)] out CoordinateTransformation? transformation) + { + transformation = null; + + if (operation.OperationKind == CoordinateOperationKind.ConcatenatedOperation) + { + return TryCreateConcatenatedOperationTransformation(source, target, operation, visitedOperationCodes, out transformation); + } + + if (operation.OperationKind != CoordinateOperationKind.Transformation) + { + return false; + } + + MathTransform? mathTransform = TryCreateExplicitGeocentricMathTransform(operation, out MathTransform? explicitMathTransform) + ? explicitMathTransform + : TryCreateBursaWolfParameters(operation, out Wgs84ConversionInfo? helmert) + ? new DatumTransform(helmert) + : null; + if (mathTransform is null) + { + return false; + } + + transformation = CreateTransform(source, target, TransformType.Transformation, mathTransform); + return true; + } + + private static bool TryCreateConcatenatedOperationTransformation( + CoordinateSystem source, + CoordinateSystem target, + CoordinateOperationDefinition operation, + HashSet? visitedOperationCodes, + [NotNullWhen(true)] out CoordinateTransformation? transformation) + { + transformation = null; + + if (operation.OperationKind != CoordinateOperationKind.ConcatenatedOperation) + { + return false; + } + + visitedOperationCodes ??= []; + if (!visitedOperationCodes.Add(operation.OperationCode)) + { + return false; + } + + try + { + if (!EpsgGeneratedOperationsCatalog.TryGetConcatenatedOperationStepCount(operation.OperationCode, out int stepCount) + || stepCount <= 0 + || !TryResolveCatalogCoordinateSystem(operation.SourceSrid, out CoordinateSystem? currentCoordinateSystem)) + { + return false; + } + + int currentSrid = operation.SourceSrid; + var concatenatedTransform = new ConcatenatedTransform(); + for (int stepIndex = 0; stepIndex < stepCount; stepIndex++) + { + if (!EpsgGeneratedOperationsCatalog.TryGetConcatenatedOperationStep(operation.OperationCode, stepIndex, out int stepOperationCode) + || !TryGetDirectOperationDefinition(stepOperationCode, out CoordinateOperationDefinition? stepOperation) + || !TryResolveCatalogCoordinateSystem(stepOperation.SourceSrid, out CoordinateSystem? stepSource) + || !TryResolveCatalogCoordinateSystem(stepOperation.TargetSrid, out CoordinateSystem? stepTarget) + || !TryCreateOperationTransformationFromDefinition(stepSource, stepTarget, stepOperation, visitedOperationCodes, out ICoordinateTransformation? stepTransformation)) + { + return false; + } + + bool useForwardDirection; + CoordinateSystem nextCoordinateSystem; + if (stepOperation.SourceSrid == currentSrid) + { + useForwardDirection = true; + nextCoordinateSystem = stepTarget; + } + else if (stepOperation.TargetSrid == currentSrid) + { + useForwardDirection = false; + nextCoordinateSystem = stepSource; + } + else + { + return false; + } + + if (!TryOrientOperationTransformation( + currentCoordinateSystem, + nextCoordinateSystem, + stepTransformation, + useForwardDirection, + out ICoordinateTransformation? orientedStep)) + { + return false; + } + + AddIfNotNull(concatenatedTransform, orientedStep); + currentCoordinateSystem = nextCoordinateSystem; + currentSrid = TryGetEpsgCode(currentCoordinateSystem, out int nextSrid) ? nextSrid : 0; + } + + if (concatenatedTransform.CoordinateTransformationList.Count != stepCount + || currentSrid != operation.TargetSrid) + { + return false; + } + + transformation = CreateTransform(source, target, TransformType.Transformation, concatenatedTransform); + return true; + } + finally + { + visitedOperationCodes.Remove(operation.OperationCode); + } + } + + private static bool TryOrientOperationTransformation( + CoordinateSystem source, + CoordinateSystem target, + ICoordinateTransformation transformation, + bool useForwardDirection, + [NotNullWhen(true)] out ICoordinateTransformation? orientedTransformation) + { + orientedTransformation = null; + + if (useForwardDirection) + { + orientedTransformation = transformation; + return true; + } + + if (!transformation.MathTransform.IsInvertible) + { + return false; + } + + orientedTransformation = new CoordinateTransformation( + source, + target, + transformation.TransformType, + transformation.MathTransform.Inverse(), + transformation.Name, + transformation.Authority, + transformation.AuthorityCode, + transformation.AreaOfUse, + transformation.Remarks); + return true; + } + + private static bool TryCreateOperationTransformationFromDefinition( + CoordinateSystem source, + CoordinateSystem target, + CoordinateOperationDefinition operation, + HashSet visitedOperationCodes, + [NotNullWhen(true)] out ICoordinateTransformation? transformation) + { + transformation = null; + + if (!TryResolveExactOperationGridPath(operation, out string? resolvedGridPath)) + { + return false; + } + + if (TryCreateExplicitOperationTransformation(source, target, operation, resolvedGridPath, visitedOperationCodes, out transformation)) + { + return true; + } + + if (TryCreateDirectProjectedTransformation(source, target, operation, resolvedGridPath, out transformation)) + { + return true; + } + + if (TryCreateVerticalBoundCompoundTransformation(source, target, out ICoordinateTransformation? verticalBoundTransformation)) + { + transformation = CreateMetadataBackedTransformation(source, target, verticalBoundTransformation, operation, resolvedGridPath); + return true; + } + + if (!CanUseOperationCoreFallback(source, target)) + { + return false; + } + + var factory = new CoordinateTransformationFactory(); + ICoordinateTransformation fallback = factory.CreateFromCoordinateSystemsCore(source, target); + transformation = CreateMetadataBackedTransformation(source, target, fallback, operation, resolvedGridPath); + return true; + } + + private static bool TryResolveCatalogCoordinateSystem(int srid, [NotNullWhen(true)] out CoordinateSystem? coordinateSystem) + { + coordinateSystem = null; + return srid > 0 && EpsgCoordinateSystemFactory.TryResolveCoordinateSystem(srid, out coordinateSystem); + } + + private static bool TryResolveExactOperationGridPath(CoordinateOperationDefinition operation, out string? resolvedGridPath) + { + resolvedGridPath = null; + + if (string.IsNullOrWhiteSpace(operation.ParameterFileName)) + { + return true; + } + + if (GetGridResolver().TryResolve(operation.ParameterFileName, out resolvedGridPath)) + { + return true; + } + + return IsGridRequiredModeEnabled() + ? throw new InvalidOperationException($"DataUnavailable: Required grid resource '{operation.ParameterFileName}' was not found.") + : false; + } + + private static bool CanUseOperationCoreFallback(CoordinateSystem source, CoordinateSystem target) + { + CoordinateSystemRuntimeKind sourceKind = GetCoordinateSystemRuntimeKind(source); + CoordinateSystemRuntimeKind targetKind = GetCoordinateSystemRuntimeKind(target); + if (sourceKind == CoordinateSystemRuntimeKind.Unknown || targetKind == CoordinateSystemRuntimeKind.Unknown) + { + return false; + } + + if (sourceKind == CoordinateSystemRuntimeKind.Fitted || targetKind == CoordinateSystemRuntimeKind.Fitted) + { + return true; + } + + int route = ((int)sourceKind * 10) + (int)targetKind; + return route is 11 or 12 or 21 or 22 or 23 or 32 or 33; + } + + private static bool TryCreateDirectGeographicMathTransform( + CoordinateOperationDefinition operation, + [NotNullWhen(true)] out MathTransform? transform) + { + transform = null; + + string normalizedMethodName = NormalizeOperationMethodName(operation.MethodName); + if (!IsGeographicOffsetMethod(normalizedMethodName) + || !TryGetDirectOperationParameters(operation, out IReadOnlyDictionary? parameters)) + { + return false; + } + + double longitudeOffset = GetOperationParameterOrDefault(parameters, "Longitude offset"); + double latitudeOffset = GetOperationParameterOrDefault(parameters, "Latitude offset"); + transform = GeogOffsetMathTransform.Create(longitudeOffset, latitudeOffset, 0d); + return true; + } + + private static bool TryCreateExplicitGeocentricMathTransform( + CoordinateOperationDefinition operation, + [NotNullWhen(true)] out MathTransform? transform) + { + transform = null; + + string normalizedMethodName = NormalizeOperationMethodName(operation.MethodName); + return IsTimeDependentHelmertMethod(normalizedMethodName) + ? TryCreateTimeDependentHelmertMathTransform(operation, normalizedMethodName, out transform) + : IsMolodenskyBadekasMethod(normalizedMethodName) + && TryCreateMolodenskyBadekasMathTransform(operation, normalizedMethodName, out transform); + } + + private static bool TryCreateTimeDependentHelmertMathTransform( + CoordinateOperationDefinition operation, + string normalizedMethodName, + [NotNullWhen(true)] out MathTransform? transform) + { + transform = null; + + if (!TryGetDirectOperationParameters(operation, out IReadOnlyDictionary? parameters)) + { + return false; + } + + bool isPositionVector = IsPositionVectorMethod(normalizedMethodName); + bool isCoordinateFrame = IsCoordinateFrameMethod(normalizedMethodName); + if (!isPositionVector && !isCoordinateFrame) + { + return false; + } + + double translationX = GetOperationParameterOrDefault(parameters, "X-axis translation"); + double translationY = GetOperationParameterOrDefault(parameters, "Y-axis translation"); + double translationZ = GetOperationParameterOrDefault(parameters, "Z-axis translation"); + double rotationX = GetOperationParameterOrDefault(parameters, "X-axis rotation") * TransformationMath.ArcSecondToRadians; + double rotationY = GetOperationParameterOrDefault(parameters, "Y-axis rotation") * TransformationMath.ArcSecondToRadians; + double rotationZ = GetOperationParameterOrDefault(parameters, "Z-axis rotation") * TransformationMath.ArcSecondToRadians; + double scale = GetOperationParameterOrDefault(parameters, "Scale difference"); + double translationRateX = GetOperationParameterOrDefault(parameters, "Rate of change of X-axis translation"); + double translationRateY = GetOperationParameterOrDefault(parameters, "Rate of change of Y-axis translation"); + double translationRateZ = GetOperationParameterOrDefault(parameters, "Rate of change of Z-axis translation"); + double rotationRateX = GetOperationParameterOrDefault(parameters, "Rate of change of X-axis rotation") * TransformationMath.ArcSecondToRadians; + double rotationRateY = GetOperationParameterOrDefault(parameters, "Rate of change of Y-axis rotation") * TransformationMath.ArcSecondToRadians; + double rotationRateZ = GetOperationParameterOrDefault(parameters, "Rate of change of Z-axis rotation") * TransformationMath.ArcSecondToRadians; + double scaleRate = GetOperationParameterOrDefault(parameters, "Rate of change of scale difference"); + double epochReference = GetOperationParameterOrDefault(parameters, "Parameter reference epoch"); + + if (scale <= TransformationMath.MinValidPpmScale) + { + return false; + } + + bool hasKinematicRates = translationRateX != 0d + || translationRateY != 0d + || translationRateZ != 0d + || rotationRateX != 0d + || rotationRateY != 0d + || rotationRateZ != 0d + || scaleRate != 0d; + bool noRotation = rotationX == 0d + && rotationY == 0d + && rotationZ == 0d + && rotationRateX == 0d + && rotationRateY == 0d + && rotationRateZ == 0d; + + transform = HelmertMathTransform.Create( + translationX, + translationY, + translationZ, + rotationX, + rotationY, + rotationZ, + scale, + 0d, + translationRateX, + translationRateY, + translationRateZ, + rotationRateX, + rotationRateY, + rotationRateZ, + scaleRate, + 0d, + hasKinematicRates, + epochReference, + false, + noRotation, + false, + isPositionVector); + return true; + } + + private static bool TryCreateMolodenskyBadekasMathTransform( + CoordinateOperationDefinition operation, + string normalizedMethodName, + [NotNullWhen(true)] out MathTransform? transform) + { + transform = null; + + if (!TryGetDirectOperationParameters(operation, out IReadOnlyDictionary? parameters)) + { + return false; + } + + bool isPositionVector = ContainsOrdinal(normalizedMethodName, "badekaspv"); + bool isCoordinateFrame = ContainsOrdinal(normalizedMethodName, "badekascf"); + if (!isPositionVector && !isCoordinateFrame) + { + return false; + } + + if (!TryGetRequiredOperationParameter(parameters, "Ordinate 1 of evaluation point", out double pivotX) + || !TryGetRequiredOperationParameter(parameters, "Ordinate 2 of evaluation point", out double pivotY) + || !TryGetRequiredOperationParameter(parameters, "Ordinate 3 of evaluation point", out double pivotZ)) + { + return false; + } + + double translationX = GetOperationParameterOrDefault(parameters, "X-axis translation"); + double translationY = GetOperationParameterOrDefault(parameters, "Y-axis translation"); + double translationZ = GetOperationParameterOrDefault(parameters, "Z-axis translation"); + double rotationX = GetOperationParameterOrDefault(parameters, "X-axis rotation"); + double rotationY = GetOperationParameterOrDefault(parameters, "Y-axis rotation"); + double rotationZ = GetOperationParameterOrDefault(parameters, "Z-axis rotation"); + double scale = GetOperationParameterOrDefault(parameters, "Scale difference"); + + if (scale <= TransformationMath.MinValidPpmScale) + { + return false; + } + + transform = MolobadekasMathTransform.Create( + translationX, + translationY, + translationZ, + rotationX, + rotationY, + rotationZ, + scale, + pivotX, + pivotY, + pivotZ, + isPositionVector); + return true; + } + + private static bool TryCreateVerticalBoundCompoundTransformation( + CoordinateSystem source, + CoordinateSystem target, + [NotNullWhen(true)] out ICoordinateTransformation? transformation) + { + transformation = null; + + if (source is CompoundCoordinateSystem sourceCompound + && target is CompoundCoordinateSystem targetCompound + && sourceCompound.TailCoordinateSystem is VerticalCoordinateSystem sourceVertical + && sourceVertical.BoundGridTransformation is VerticalBoundGridTransformation sourceBinding + && sourceCompound.HeadCoordinateSystem.EqualParams(sourceBinding.HubCoordinateSystem.HeadCoordinateSystem) + && targetCompound.EqualParams(sourceBinding.HubCoordinateSystem)) + { + if (!TryCreateVerticalBoundGridMathTransform(sourceBinding.ParameterFileName, out MathTransform? gridMathTransform)) + { + return false; + } + + transformation = CreateTransform(source, target, TransformType.Transformation, gridMathTransform.Inverse()); + return true; + } + + if (source is CompoundCoordinateSystem sourceHubCompound + && target is CompoundCoordinateSystem targetBoundCompound + && targetBoundCompound.TailCoordinateSystem is VerticalCoordinateSystem targetVertical + && targetVertical.BoundGridTransformation is VerticalBoundGridTransformation targetBinding + && targetBoundCompound.HeadCoordinateSystem.EqualParams(targetBinding.HubCoordinateSystem.HeadCoordinateSystem) + && sourceHubCompound.EqualParams(targetBinding.HubCoordinateSystem)) + { + if (!TryCreateVerticalBoundGridMathTransform(targetBinding.ParameterFileName, out MathTransform? gridMathTransform)) + { + return false; + } + + transformation = CreateTransform(source, target, TransformType.Transformation, gridMathTransform); + return true; + } + + return false; + } + + private static bool TryCreateVerticalBoundGridMathTransform(string parameterFileName, [NotNullWhen(true)] out MathTransform? transform) + { + transform = null; + + if (string.IsNullOrWhiteSpace(parameterFileName)) + { + return false; + } + + if (!GetGridResolver().TryResolve(parameterFileName, out string? resolvedGridPath)) + { + if (IsGridRequiredModeEnabled()) + { + throw new InvalidOperationException($"DataUnavailable: Required grid resource '{parameterFileName}' was not found."); + } + + return false; + } + + string gridPath = ArgumentGuard.ThrowIfNull(resolvedGridPath, nameof(resolvedGridPath)); + string[] gridPaths = [gridPath]; + string extension = Path.GetExtension(gridPath); + transform = extension.Equals(".tif", StringComparison.OrdinalIgnoreCase) || extension.Equals(".tiff", StringComparison.OrdinalIgnoreCase) + ? new GeoTiffVGridShiftMathTransform(gridPaths, -1d) + : new GtxVGridShiftMathTransform(gridPaths); + return true; + } + + private static bool TryCreateBursaWolfParameters(CoordinateOperationDefinition operation, [NotNullWhen(true)] out Wgs84ConversionInfo? parameters) + { + parameters = null; + + if (operation is null) + { + return false; + } + + if (!EpsgGeneratedOperationsCatalog.TryGetExplicitOperationParameters(operation.OperationCode, out EpsgExplicitOperationRecord operationParameters)) + { + return false; + } + + parameters = new Wgs84ConversionInfo( + operationParameters.Dx, + operationParameters.Dy, + operationParameters.Dz, + operationParameters.Ex, + operationParameters.Ey, + operationParameters.Ez, + operationParameters.Ppm); + return true; + } + + private static CoordinateTransformation CreateMetadataBackedTransformation( + CoordinateSystem source, + CoordinateSystem target, + ICoordinateTransformation fallback, + CoordinateOperationDefinition operation, + string? resolvedGridPath) + { + string operationName = !string.IsNullOrWhiteSpace(operation.MethodName) + ? operation.MethodName + : fallback.Name; + + string remarks = fallback.Remarks; + if (!string.IsNullOrWhiteSpace(operation.ParameterFileName)) + { + string gridReference = !string.IsNullOrWhiteSpace(resolvedGridPath) + ? $"{operation.ParameterFileName} ({resolvedGridPath})" + : operation.ParameterFileName; + remarks = string.IsNullOrWhiteSpace(remarks) + ? $"Grid: {gridReference}" + : $"{remarks}; Grid: {gridReference}"; + } + + return new CoordinateTransformation( + source, + target, + fallback.TransformType, + fallback.MathTransform, + operationName, + "EPSG", + operation.OperationCode, + fallback.AreaOfUse, + remarks); + } + + private static CoordinateTransformation RebindTransformation( + CoordinateSystem source, + CoordinateSystem target, + ICoordinateTransformation transformation) + { + return new CoordinateTransformation( + source, + target, + transformation.TransformType, + transformation.MathTransform, + transformation.Name, + transformation.Authority, + transformation.AuthorityCode, + transformation.AreaOfUse, + transformation.Remarks); + } + + private static Dictionary> LoadDirectOperationDefinitions() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var definitions = new Dictionary>(); + + foreach (CoordinateOperationDefinition definition in provider.GetDefinitions()) + { + if (definition.SourceSrid <= 0 || definition.TargetSrid <= 0) + { + continue; + } + + if (definition.SourceSrid == definition.TargetSrid) + { + continue; + } + + if (definition.OperationKind == CoordinateOperationKind.PointMotionOperation) + { + continue; + } + + var key = new SridPair(definition.SourceSrid, definition.TargetSrid); + if (!definitions.TryGetValue(key, out List? operations)) + { + operations = []; + definitions[key] = operations; + } + + operations.Add(definition); + } + + var result = new Dictionary>(definitions.Count); + foreach (KeyValuePair> pair in definitions) + { + pair.Value.Sort(OperationDefinitionComparer.Instance); + result[pair.Key] = pair.Value; + } + + return result; + } + + private static Dictionary LoadDirectOperationDefinitionsByCode() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var result = new Dictionary(); + + foreach (CoordinateOperationDefinition definition in provider.GetDefinitions()) + { + if (definition.OperationCode <= 0 || definition.SourceSrid <= 0 || definition.TargetSrid <= 0) + { + continue; + } + + result[definition.OperationCode] = definition; + } + + return result; + } + + private static Dictionary> LoadDirectOperationParameters() + { + var parametersByOperation = new Dictionary>(); + + foreach (EpsgOperationParameterRecord parameter in EpsgGeneratedOperationsCatalog.OperationParameters) + { + if (!parametersByOperation.TryGetValue(parameter.OperationCode, out Dictionary? parameters)) + { + parameters = new Dictionary(StringComparer.Ordinal); + parametersByOperation[parameter.OperationCode] = parameters; + } + + parameters[parameter.Name] = parameter.Value; + } + + var result = new Dictionary>(parametersByOperation.Count); + foreach (KeyValuePair> pair in parametersByOperation) + { + result[pair.Key] = pair.Value; + } + + return result; + } + + private static bool TryGetDirectOperationParameters( + CoordinateOperationDefinition operation, + [NotNullWhen(true)] out IReadOnlyDictionary? parameters) + { + parameters = null; + return operation is not null + && DirectOperationParameters.Value.TryGetValue(operation.OperationCode, out parameters); + } + + private static bool TryGetDirectOperationDefinition( + int operationCode, + [NotNullWhen(true)] out CoordinateOperationDefinition? operation) + { + operation = null; + return operationCode > 0 && DirectOperationDefinitionsByCode.Value.TryGetValue(operationCode, out operation); + } + + private static bool TryGetRequiredOperationParameter( + IReadOnlyDictionary parameters, + string name, + out double value) + { + value = 0d; + return parameters is not null && parameters.TryGetValue(name, out value); + } + + private static double GetOperationParameterOrDefault( + IReadOnlyDictionary parameters, + string name, + double defaultValue = 0d) + { + return parameters is not null && parameters.TryGetValue(name, out double value) + ? value + : defaultValue; + } + + private static string NormalizeOperationMethodName(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return string.Empty; + } + + char[] buffer = new char[value.Length]; + int length = 0; + for (int i = 0; i < value.Length; i++) + { + char character = value[i]; + if (char.IsLetterOrDigit(character)) + { + buffer[length] = char.ToLowerInvariant(character); + length++; + } + } + + return length == 0 ? string.Empty : new string(buffer, 0, length); + } + + private static bool IsCoordinateFrameMethod(string normalizedMethodName) + { + return ContainsOrdinal(normalizedMethodName, "coordinateframe"); + } + + private static bool IsGeographicOffsetMethod(string normalizedMethodName) + { + return ContainsOrdinal(normalizedMethodName, "geographic2doffsets"); + } + + private static bool IsMolodenskyBadekasMethod(string normalizedMethodName) + { + return ContainsOrdinal(normalizedMethodName, "molodenskybadekas"); + } + + private static bool IsPositionVectorMethod(string normalizedMethodName) + { + return ContainsOrdinal(normalizedMethodName, "positionvector"); + } + + private static bool IsTimeDependentHelmertMethod(string normalizedMethodName) + { + return ContainsOrdinal(normalizedMethodName, "timedependent") + && (IsPositionVectorMethod(normalizedMethodName) || IsCoordinateFrameMethod(normalizedMethodName)); + } + + private static bool ContainsOrdinal(string value, string substring) + { +#if NETSTANDARD2_0 + return value.IndexOf(substring, StringComparison.Ordinal) >= 0; +#else + return value.Contains(substring, StringComparison.Ordinal); +#endif + } + + private static GridResourceResolver GetGridResolver() + { + lock (GridResolverSync) + { + return gridResolverInstance; + } + } + + private static GridResourceResolver CreateGridResolver() + { + string[] localDirectories = ReadGridDirectoriesFromEnvironment(); + string? cacheDirectory = Environment.GetEnvironmentVariable(GridCacheEnvironmentVariable); + GridResourceResolutionMode mode = ParseGridResolutionMode(Environment.GetEnvironmentVariable(GridModeEnvironmentVariable)); + IGridResourceFetchClient? fetchClient = CreateGridFetchClientFromEnvironment(mode); + + var options = new GridResourceResolverOptions(localDirectories, cacheDirectory, mode); + return new GridResourceResolver(options, fetchClient); + } + + private static string[] ReadGridDirectoriesFromEnvironment() + { + string? configuredPaths = Environment.GetEnvironmentVariable(GridPathEnvironmentVariable); + return string.IsNullOrWhiteSpace(configuredPaths) + ? [] + : configuredPaths.Split([';', Path.PathSeparator], StringSplitOptions.RemoveEmptyEntries); + } + + private static GridResourceResolutionMode ParseGridResolutionMode(string? configuredMode) + { + return "LocalThenNetwork".Equals(configuredMode, StringComparison.OrdinalIgnoreCase) + || "network".Equals(configuredMode, StringComparison.OrdinalIgnoreCase) + ? GridResourceResolutionMode.LocalThenNetwork + : GridResourceResolutionMode.LocalOnly; + } + + private static HttpGridResourceFetchClient? CreateGridFetchClientFromEnvironment(GridResourceResolutionMode mode) + { + if (mode != GridResourceResolutionMode.LocalThenNetwork) + { + return null; + } + + string? baseUrl = Environment.GetEnvironmentVariable(GridBaseUrlEnvironmentVariable); + return string.IsNullOrWhiteSpace(baseUrl) + ? null + : new HttpGridResourceFetchClient(baseUrl); + } + + private static double NormalizeAccuracy(double accuracy) => accuracy > 0d ? accuracy : double.MaxValue; + + private static bool TryGetDirectProjectedOperation( + CoordinateSystem source, + CoordinateSystem target, + [NotNullWhen(true)] out CoordinateOperationDefinition? operation, + out string? resolvedGridPath) + { + operation = null; + resolvedGridPath = null; + + if (source is not ProjectedCoordinateSystem || target is not ProjectedCoordinateSystem) + { + return false; + } + + return TryGetEpsgCode(source, out int sourceSrid) && TryGetEpsgCode(target, out int targetSrid) && TryGetDirectOperationBySridPair(sourceSrid, targetSrid, out operation, out resolvedGridPath); + } + + private static bool TryGetDirectOperation( + CoordinateSystem source, + CoordinateSystem target, + [NotNullWhen(true)] out CoordinateOperationDefinition? operation, + out string? resolvedGridPath) + { + operation = null; + resolvedGridPath = null; + + return TryGetEpsgCode(source, out int sourceSrid) && TryGetEpsgCode(target, out int targetSrid) && TryGetDirectOperationBySridPair(sourceSrid, targetSrid, out operation, out resolvedGridPath); + } + + private static bool TryGetDirectOperationBySridPair( + int sourceSrid, + int targetSrid, + [NotNullWhen(true)] out CoordinateOperationDefinition? operation, + out string? resolvedGridPath) + { + operation = null; + resolvedGridPath = null; + + if (!DirectOperationDefinitions.Value.TryGetValue(new SridPair(sourceSrid, targetSrid), out IReadOnlyList? operations)) + { + return false; + } + + string? missingGridFile = null; + foreach (CoordinateOperationDefinition candidate in operations) + { + if (string.IsNullOrWhiteSpace(candidate.ParameterFileName)) + { + operation = candidate; + return true; + } + + if (GetGridResolver().TryResolve(candidate.ParameterFileName, out resolvedGridPath)) + { + operation = candidate; + return true; + } + + missingGridFile ??= candidate.ParameterFileName; + } + + if (!string.IsNullOrWhiteSpace(missingGridFile)) + { + return IsGridRequiredModeEnabled() + ? throw new InvalidOperationException($"DataUnavailable: Required grid resource '{missingGridFile}' was not found.") + : false; + } + + return false; + } + + private static bool TryCreateExplicitOperationTransformationBySridPair( + CoordinateSystem source, + CoordinateSystem target, + int sourceSrid, + int targetSrid, + [NotNullWhen(true)] out ICoordinateTransformation? transformation) + { + transformation = null; + + if (!DirectOperationDefinitions.Value.TryGetValue(new SridPair(sourceSrid, targetSrid), out IReadOnlyList? operations)) + { + return false; + } + + string? missingGridFile = null; + foreach (CoordinateOperationDefinition candidate in operations) + { + string? resolvedGridPath = null; + if (!string.IsNullOrWhiteSpace(candidate.ParameterFileName)) + { + if (!GetGridResolver().TryResolve(candidate.ParameterFileName, out resolvedGridPath)) + { + missingGridFile ??= candidate.ParameterFileName; + continue; + } + } + + if (TryCreateExplicitOperationTransformation(source, target, candidate, resolvedGridPath, out transformation)) + { + return true; + } + } + + if (!string.IsNullOrWhiteSpace(missingGridFile)) + { + return IsGridRequiredModeEnabled() + ? throw new InvalidOperationException($"DataUnavailable: Required grid resource '{missingGridFile}' was not found.") + : false; + } + + return false; + } + + private static bool IsGridRequiredModeEnabled() + { + string? configuredValue = Environment.GetEnvironmentVariable(GridRequiredEnvironmentVariable); + if (string.IsNullOrWhiteSpace(configuredValue)) + { + return false; + } + + if ("1".Equals(configuredValue, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return "true".Equals(configuredValue, StringComparison.OrdinalIgnoreCase) || "yes".Equals(configuredValue, StringComparison.OrdinalIgnoreCase); + } + + private static bool TryGetEpsgCode(CoordinateSystem coordinateSystem, out int srid) + { + srid = 0; + + if (coordinateSystem is null) + { + return false; + } + + if (!"EPSG".Equals(coordinateSystem.Authority, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (coordinateSystem.AuthorityCode <= 0 || coordinateSystem.AuthorityCode > int.MaxValue) + { + return false; + } + + srid = (int)coordinateSystem.AuthorityCode; + return true; + } + + private sealed class OperationDefinitionComparer : IComparer + { + internal static readonly OperationDefinitionComparer Instance = new(); + + public int Compare(CoordinateOperationDefinition? left, CoordinateOperationDefinition? right) + { + if (ReferenceEquals(left, right)) + { + return 0; + } + + if (left is null) + { + return 1; + } + + if (right is null) + { + return -1; + } + + int accuracyComparison = NormalizeAccuracy(left.Accuracy).CompareTo(NormalizeAccuracy(right.Accuracy)); + if (accuracyComparison != 0) + { + return accuracyComparison; + } + + bool leftRequiresGrid = !string.IsNullOrWhiteSpace(left.ParameterFileName); + bool rightRequiresGrid = !string.IsNullOrWhiteSpace(right.ParameterFileName); + if (leftRequiresGrid != rightRequiresGrid) + { + return leftRequiresGrid ? 1 : -1; + } + + bool leftHasMethod = !string.IsNullOrWhiteSpace(left.MethodName); + bool rightHasMethod = !string.IsNullOrWhiteSpace(right.MethodName); + if (leftHasMethod != rightHasMethod) + { + return leftHasMethod ? -1 : 1; + } + + int areaComparison = left.GetApproximateAreaOfUseCoverage().CompareTo(right.GetApproximateAreaOfUseCoverage()); + return areaComparison != 0 ? areaComparison : left.OperationCode.CompareTo(right.OperationCode); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.cs b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.cs index 4d96dbc7..7be82b9f 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/CoordinateTransformationFactory.cs @@ -1,419 +1,188 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems.Transformations; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; using ProjNet.CoordinateSystems.Projections; - -namespace ProjNet.CoordinateSystems.Transformations +using ProjNet.Data; +using ProjNet.Data.Generated; +using ProjNet.Resources; + +/// +/// Creates coordinate transformations. +/// +/// +/// +/// Thread safety: Instances are stateless and may be reused across threads. Shared direct-operation +/// caches initialize once through . Grid resolution uses a process-wide resolver +/// protected by an internal lock, so grid-backed resolution and resolver reconfiguration may briefly +/// serialize on that shared state. +/// +/// +public partial class CoordinateTransformationFactory { - /// - /// Creates coordinate transformations. - /// - public class CoordinateTransformationFactory - { - #region ICoordinateTransformationFactory Members - - /// - /// Creates a transformation between two coordinate systems. - /// - /// - /// This method will examine the coordinate systems in order to construct - /// a transformation between them. This method may fail if no path between - /// the coordinate systems is found, using the normal failing behavior of - /// the DCP (e.g. throwing an exception). - /// Source coordinate system - /// Target coordinate system - /// - public ICoordinateTransformation CreateFromCoordinateSystems(CoordinateSystem sourceCS, CoordinateSystem targetCS) - { - ICoordinateTransformation trans; - if (sourceCS is ProjectedCoordinateSystem && targetCS is GeographicCoordinateSystem) //Projected -> Geographic - trans = Proj2Geog((ProjectedCoordinateSystem)sourceCS, (GeographicCoordinateSystem)targetCS); - else if (sourceCS is GeographicCoordinateSystem && targetCS is ProjectedCoordinateSystem) //Geographic -> Projected - trans = Geog2Proj((GeographicCoordinateSystem)sourceCS, (ProjectedCoordinateSystem)targetCS); - - else if (sourceCS is GeographicCoordinateSystem && targetCS is GeocentricCoordinateSystem) //Geocentric -> Geographic - trans = Geog2Geoc((GeographicCoordinateSystem)sourceCS, (GeocentricCoordinateSystem)targetCS); - - else if (sourceCS is GeocentricCoordinateSystem && targetCS is GeographicCoordinateSystem) //Geocentric -> Geographic - trans = Geoc2Geog((GeocentricCoordinateSystem)sourceCS, (GeographicCoordinateSystem)targetCS); - - else if (sourceCS is ProjectedCoordinateSystem && targetCS is ProjectedCoordinateSystem) //Projected -> Projected - trans = Proj2Proj((sourceCS as ProjectedCoordinateSystem), (targetCS as ProjectedCoordinateSystem)); - - else if (sourceCS is GeocentricCoordinateSystem && targetCS is GeocentricCoordinateSystem) //Geocentric -> Geocentric - trans = CreateGeoc2Geoc((GeocentricCoordinateSystem)sourceCS, (GeocentricCoordinateSystem)targetCS); - - else if (sourceCS is GeographicCoordinateSystem && targetCS is GeographicCoordinateSystem) //Geographic -> Geographic - trans = CreateGeog2Geog(sourceCS as GeographicCoordinateSystem, targetCS as GeographicCoordinateSystem); - else if (sourceCS is FittedCoordinateSystem) //Fitted -> Any - trans = Fitt2Any ((FittedCoordinateSystem)sourceCS, targetCS); - else if (targetCS is FittedCoordinateSystem) //Any -> Fitted - trans = Any2Fitt (sourceCS, (FittedCoordinateSystem)targetCS); - else - throw new NotSupportedException("No support for transforming between the two specified coordinate systems"); - - //if (trans.MathTransform is ConcatenatedTransform) { - // List MTs = new List(); - // SimplifyTrans(trans.MathTransform as ConcatenatedTransform, ref MTs); - // return new CoordinateTransformation(sourceCS, - // targetCS, TransformType.Transformation, new ConcatenatedTransform(MTs), - // string.Empty, string.Empty, -1, string.Empty, string.Empty); - //} - return trans; - } - #endregion - - private static void SimplifyTrans(ConcatenatedTransform mtrans, ref List MTs) - { - foreach(var t in mtrans.CoordinateTransformationList) - { - if(t is ConcatenatedTransform ct) - SimplifyTrans(ct, ref MTs); - else - MTs.Add(t); - } - } - - #region Methods for converting between specific systems - - private static CoordinateTransformation Geog2Geoc(GeographicCoordinateSystem source, GeocentricCoordinateSystem target) + private const string GridCacheEnvironmentVariable = "PROJNET_GRID_CACHE"; + private const string GridBaseUrlEnvironmentVariable = "PROJNET_GRID_BASE_URL"; + private const string GridModeEnvironmentVariable = "PROJNET_GRID_MODE"; + private const string GridPathEnvironmentVariable = "PROJNET_GRID_PATHS"; + private const string GridRequiredEnvironmentVariable = "PROJNET_GRID_REQUIRED"; + private static readonly Lazy>> DirectOperationDefinitions = + new(LoadDirectOperationDefinitions, true); + + private static readonly Lazy> DirectOperationDefinitionsByCode = + new(LoadDirectOperationDefinitionsByCode, true); + + private static readonly Lazy>> DirectOperationParameters = + new(LoadDirectOperationParameters, true); + + private static readonly object GridResolverSync = new(); + private static GridResourceResolver gridResolverInstance = CreateGridResolver(); + + private enum CoordinateSystemRuntimeKind : byte + { + Unknown = 0, + Projected = 1, + Geographic = 2, + Geocentric = 3, + Fitted = 4, + } + + /// + /// Creates a transformation between two coordinate systems. + /// + /// + /// This method will examine the coordinate systems in order to construct + /// a transformation between them. This method may fail if no path between + /// the coordinate systems is found, using the normal failing behavior of + /// the DCP (e.g. throwing an exception). + /// Source coordinate system. + /// Target coordinate system. + /// The coordinate transformation from to . + /// + /// Thrown when no transformation path can be found between and . + /// + public ICoordinateTransformation CreateFromCoordinateSystems(CoordinateSystem sourceCS, CoordinateSystem targetCS) + { + return CoordinateOperationResolver.Resolve(sourceCS, targetCS, this.CreateFromCoordinateSystemsWithMetadata) + ?? throw new NotSupportedException("No support for transforming between the two specified coordinate systems"); + } + + /// + /// Configures the grid resource resolution subsystem with a custom fetch client and search paths. + /// + /// + /// Calling this method replaces the current grid resolver instance. When no parameters are supplied, + /// a default resolver is created using environment-variable configuration. This method is thread-safe. + /// + /// + /// Custom fetch client used for network retrieval; disables network fetching. + /// + /// + /// Directories to search for grid files; falls back to directories configured + /// via the PROJNET_GRID_PATHS environment variable. + /// + /// + /// Directory used to store network-fetched grid files; falls back to the + /// PROJNET_GRID_CACHE environment variable. + /// + /// Resolution mode controlling whether network retrieval is attempted. + public static void ConfigureGridResolution( + IGridResourceFetchClient? fetchClient = null, + IEnumerable? localDirectories = null, + string? cacheDirectory = null, + GridResourceResolutionMode mode = GridResourceResolutionMode.LocalOnly) + { + if (fetchClient is null + && localDirectories is null + && cacheDirectory is null + && mode == GridResourceResolutionMode.LocalOnly) { - var geocMathTransform = CreateCoordinateOperation(target); - if (source.PrimeMeridian.EqualParams(target.PrimeMeridian)) + lock (GridResolverSync) { - return new CoordinateTransformation(source, target, TransformType.Conversion, geocMathTransform, string.Empty, string.Empty, -1, string.Empty, string.Empty); + gridResolverInstance = CreateGridResolver(); } - var ct = new ConcatenatedTransform(); - ct.CoordinateTransformationList.Add(new CoordinateTransformation(source, target, TransformType.Transformation, new PrimeMeridianTransform(source.PrimeMeridian, target.PrimeMeridian), string.Empty, string.Empty, -1, string.Empty, string.Empty)); - ct.CoordinateTransformationList.Add(new CoordinateTransformation(source, target, TransformType.Conversion, geocMathTransform, string.Empty, string.Empty, -1, string.Empty, string.Empty)); - return new CoordinateTransformation(source, target, TransformType.Conversion, ct, string.Empty, string.Empty, -1, string.Empty, string.Empty); + return; } - private static CoordinateTransformation Geoc2Geog(GeocentricCoordinateSystem source, GeographicCoordinateSystem target) - { - var geocMathTransform = CreateCoordinateOperation(source).Inverse(); - if (source.PrimeMeridian.EqualParams(target.PrimeMeridian)) - { - return new CoordinateTransformation(source, target, TransformType.Conversion, geocMathTransform, string.Empty, string.Empty, -1, string.Empty, string.Empty); - } - - var ct = new ConcatenatedTransform(); - ct.CoordinateTransformationList.Add(new CoordinateTransformation(source, target, TransformType.Conversion, geocMathTransform, string.Empty, string.Empty, -1, string.Empty, string.Empty)); - ct.CoordinateTransformationList.Add(new CoordinateTransformation(source, target, TransformType.Transformation, new PrimeMeridianTransform(source.PrimeMeridian, target.PrimeMeridian), string.Empty, string.Empty, -1, string.Empty, string.Empty)); - return new CoordinateTransformation(source, target, TransformType.Conversion, ct, string.Empty, string.Empty, -1, string.Empty, string.Empty); - } - - private static CoordinateTransformation Proj2Proj(ProjectedCoordinateSystem source, ProjectedCoordinateSystem target) - { - var ct = new ConcatenatedTransform(); - var ctFac = new CoordinateTransformationFactory(); - //First transform from projection to geographic - ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source, source.GeographicCoordinateSystem)); - //Transform geographic to geographic: - var geogToGeog = ctFac.CreateFromCoordinateSystems(source.GeographicCoordinateSystem, - target.GeographicCoordinateSystem); - if (geogToGeog != null) - ct.CoordinateTransformationList.Add(geogToGeog); - //Transform to new projection - ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(target.GeographicCoordinateSystem, target)); + string[] dirs = localDirectories?.ToArray() ?? ReadGridDirectoriesFromEnvironment(); + string? cache = cacheDirectory ?? Environment.GetEnvironmentVariable(GridCacheEnvironmentVariable); - return new CoordinateTransformation(source, - target, TransformType.Transformation, ct, - string.Empty, string.Empty, -1, string.Empty, string.Empty); - } + var options = new GridResourceResolverOptions(dirs, cache, mode); + var resolver = new GridResourceResolver(options, fetchClient); - private static CoordinateTransformation Geog2Proj(GeographicCoordinateSystem source, ProjectedCoordinateSystem target) + lock (GridResolverSync) { - if (source.EqualParams(target.GeographicCoordinateSystem)) - { - var mathTransform = CreateCoordinateOperation(target.Projection, - target.GeographicCoordinateSystem.HorizontalDatum.Ellipsoid, target.LinearUnit); - return new CoordinateTransformation(source, target, TransformType.Transformation, mathTransform, - string.Empty, string.Empty, -1, string.Empty, string.Empty); - } - - // Geographic coordinatesystems differ - Create concatenated transform - var ct = new ConcatenatedTransform(); - var ctFac = new CoordinateTransformationFactory(); - ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source,target.GeographicCoordinateSystem)); - ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(target.GeographicCoordinateSystem, target)); - return new CoordinateTransformation(source, - target, TransformType.Transformation, ct, - string.Empty, string.Empty, -1, string.Empty, string.Empty); + gridResolverInstance = resolver; } - - private static CoordinateTransformation Proj2Geog(ProjectedCoordinateSystem source, GeographicCoordinateSystem target) + } + + /// + /// Attempts to resolve a grid resource name to a concrete file path. + /// + /// Grid resource name or path token. + /// Resolved local file path when available. + /// when resolution succeeded; otherwise . + internal static bool TryResolveGridResourcePath(string gridName, [NotNullWhen(true)] out string? resolvedPath) => GetGridResolver().TryResolve(gridName, out resolvedPath); + + private ICoordinateTransformation? CreateFromCoordinateSystemsWithMetadata(CoordinateSystem sourceCS, CoordinateSystem targetCS) + { + if (BoundCoordinateSystemSupport.ContainsBoundCoordinateSystem(sourceCS) + || BoundCoordinateSystemSupport.ContainsBoundCoordinateSystem(targetCS)) { - if (source.GeographicCoordinateSystem.EqualParams(target)) - { - var mathTransform = CreateCoordinateOperation(source.Projection, source.GeographicCoordinateSystem.HorizontalDatum.Ellipsoid, source.LinearUnit).Inverse(); - return new CoordinateTransformation(source, target, TransformType.Transformation, mathTransform, - string.Empty, string.Empty, -1, string.Empty, string.Empty); - } - else - { // Geographic coordinatesystems differ - Create concatenated transform - var ct = new ConcatenatedTransform(); - var ctFac = new CoordinateTransformationFactory(); - ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source, source.GeographicCoordinateSystem)); - ct.CoordinateTransformationList.Add(ctFac.CreateFromCoordinateSystems(source.GeographicCoordinateSystem, target)); - return new CoordinateTransformation(source, - target, TransformType.Transformation, ct, - string.Empty, string.Empty, -1, string.Empty, string.Empty); - } - } - - /// - /// Geographic to geographic transformation - /// - /// Adds a datum shift if necessary - /// - /// - /// - private static ICoordinateTransformation CreateGeog2Geog(GeographicCoordinateSystem source, GeographicCoordinateSystem target) - { - if (source.HorizontalDatum.EqualParams(target.HorizontalDatum)) - { - //No datum shift needed - return new CoordinateTransformation(source, - target, TransformType.Conversion, new GeographicTransform(source, target), - string.Empty, string.Empty, -1, string.Empty, string.Empty); - } - - //Create datum shift - //Convert to geocentric, perform shift and return to geographic - var ctFac = new CoordinateTransformationFactory(); - var cFac = new CoordinateSystemFactory(); - var sourceCentric = cFac.CreateGeocentricCoordinateSystem(source.HorizontalDatum.Name + " Geocentric", - source.HorizontalDatum, LinearUnit.Metre, source.PrimeMeridian); - var targetCentric = cFac.CreateGeocentricCoordinateSystem(target.HorizontalDatum.Name + " Geocentric", - target.HorizontalDatum, LinearUnit.Metre, source.PrimeMeridian); - var ct = new ConcatenatedTransform(); - AddIfNotNull(ct, ctFac.CreateFromCoordinateSystems(source, sourceCentric)); - AddIfNotNull(ct, ctFac.CreateFromCoordinateSystems(sourceCentric, targetCentric)); - AddIfNotNull(ct, ctFac.CreateFromCoordinateSystems(targetCentric, target)); - - - return new CoordinateTransformation(source, - target, TransformType.Transformation, ct, - string.Empty, string.Empty, -1, string.Empty, string.Empty); + CoordinateSystem normalizedSource = BoundCoordinateSystemSupport.NormalizeCoordinateSystemForRuntime(sourceCS); + CoordinateSystem normalizedTarget = BoundCoordinateSystemSupport.NormalizeCoordinateSystemForRuntime(targetCS); + ICoordinateTransformation? normalizedTransformation = this.CreateFromCoordinateSystemsWithMetadata(normalizedSource, normalizedTarget); + return normalizedTransformation is null + ? null + : RebindTransformation(sourceCS, targetCS, normalizedTransformation); } - private static void AddIfNotNull(ConcatenatedTransform concatTrans, ICoordinateTransformation trans) + if (TryCreateVerticalBoundCompoundTransformation(sourceCS, targetCS, out ICoordinateTransformation? verticalBoundTransformation)) { - if (trans != null) - concatTrans.CoordinateTransformationList.Add(trans); + return verticalBoundTransformation; } - /// - /// Geocentric to Geocentric transformation - /// - /// - /// - /// - private static CoordinateTransformation CreateGeoc2Geoc(GeocentricCoordinateSystem source, GeocentricCoordinateSystem target) - { - var ct = new ConcatenatedTransform(); - //Does source has a datum different from WGS84 and is there a shift specified? - if (source.HorizontalDatum.Wgs84Parameters != null && !source.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) - ct.CoordinateTransformationList.Add( - new CoordinateTransformation( - ((target.HorizontalDatum.Wgs84Parameters == null || target.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) ? target : GeocentricCoordinateSystem.WGS84), - source, TransformType.Transformation, - new DatumTransform(source.HorizontalDatum.Wgs84Parameters) - , "", "", -1, "", "")); - - //Does target has a datum different from WGS84 and is there a shift specified? - if (target.HorizontalDatum.Wgs84Parameters != null && !target.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) - ct.CoordinateTransformationList.Add( - new CoordinateTransformation( - ((source.HorizontalDatum.Wgs84Parameters == null || source.HorizontalDatum.Wgs84Parameters.HasZeroValuesOnly) ? source : GeocentricCoordinateSystem.WGS84), - target, - TransformType.Transformation, - new DatumTransform(target.HorizontalDatum.Wgs84Parameters).Inverse() - , "", "", -1, "", "")); - - //If we don't have a transformation in this list, return null - if (ct.CoordinateTransformationList.Count == 0) - return null; - //If we only have one shift, lets just return the datumshift from/to wgs84 - if (ct.CoordinateTransformationList.Count == 1) - return new CoordinateTransformation(source, target, TransformType.ConversionAndTransformation, ((ICoordinateTransformation)ct.CoordinateTransformationList[0]).MathTransform, "", "", -1, "", ""); - - return new CoordinateTransformation(source, target, TransformType.ConversionAndTransformation, ct, "", "", -1, "", ""); - } - - /// - /// Creates transformation from fitted coordinate system to the target one - /// - /// - /// - /// - private static CoordinateTransformation Fitt2Any (FittedCoordinateSystem source, CoordinateSystem target) + if (TryGetDirectProjectedOperation(sourceCS, targetCS, out CoordinateOperationDefinition? operation, out string? resolvedGridPath)) { - //transform from fitted to base system of fitted (which is equal to target) - var mt = CreateFittedTransform (source); - - //case when target system is equal to base system of the fitted - if (source.BaseCoordinateSystem.EqualParams (target)) + if (TryCreateExplicitOperationTransformation(sourceCS, targetCS, operation, resolvedGridPath, out ICoordinateTransformation? directExplicitTransformation)) { - //Transform form base system of fitted to target coordinate system - return CreateTransform (source, target, TransformType.Transformation, mt); + return directExplicitTransformation; } - //Transform form base system of fitted to target coordinate system - var ct = new ConcatenatedTransform (); - ct.CoordinateTransformationList.Add (CreateTransform (source, source.BaseCoordinateSystem, TransformType.Transformation, mt)); - - //Transform form base system of fitted to target coordinate system - var ctFac = new CoordinateTransformationFactory (); - ct.CoordinateTransformationList.Add (ctFac.CreateFromCoordinateSystems (source.BaseCoordinateSystem, target)); - - return CreateTransform (source, target, TransformType.Transformation, ct); - } - - /// - /// Creates transformation from source coordinate system to specified target system which is the fitted one - /// - /// - /// - /// - private static CoordinateTransformation Any2Fitt (CoordinateSystem source, FittedCoordinateSystem target) - { - //Transform form base system of fitted to target coordinate system - use invered math transform - var invMt = CreateFittedTransform (target).Inverse (); - - //case when source system is equal to base system of the fitted - if (target.BaseCoordinateSystem.EqualParams (source)) + if (TryCreateDirectProjectedTransformation(sourceCS, targetCS, operation, resolvedGridPath, out ICoordinateTransformation? directTransformation)) { - //Transform form base system of fitted to target coordinate system - return CreateTransform (source, target, TransformType.Transformation, invMt); + return directTransformation; } - var ct = new ConcatenatedTransform (); - //First transform from source to base system of fitted - var ctFac = new CoordinateTransformationFactory (); - ct.CoordinateTransformationList.Add (ctFac.CreateFromCoordinateSystems (source, target.BaseCoordinateSystem)); - - //Transform form base system of fitted to target coordinate system - use invered math transform - ct.CoordinateTransformationList.Add (CreateTransform (target.BaseCoordinateSystem, target, TransformType.Transformation, invMt)); - - return CreateTransform (source, target, TransformType.Transformation, ct); + ICoordinateTransformation? fallbackWithMetadata = this.CreateFromCoordinateSystemsCore(sourceCS, targetCS); + return fallbackWithMetadata is null + ? null + : (ICoordinateTransformation)CreateMetadataBackedTransformation(sourceCS, targetCS, fallbackWithMetadata, operation, resolvedGridPath); } - private static MathTransform CreateFittedTransform (FittedCoordinateSystem fittedSystem) + if (TryGetEpsgCode(sourceCS, out int sourceSrid) + && TryGetEpsgCode(targetCS, out int targetSrid) + && TryCreateExplicitOperationTransformationBySridPair(sourceCS, targetCS, sourceSrid, targetSrid, out ICoordinateTransformation? directMetadataTransformation)) { - //create transform From fitted to base and inverts it - return fittedSystem.ToBaseTransform; - - //MathTransformFactory mtFac = new MathTransformFactory (); - ////create transform From fitted to base and inverts it - //return mtFac.CreateFromWKT (fittedSystem.ToBase ()); - - throw new NotImplementedException (); + return directMetadataTransformation; } - /// - /// Creates an instance of CoordinateTransformation as an anonymous transformation without neither autohority nor code defined. - /// - /// Source coordinate system - /// Target coordinate system - /// Transformation type - /// Math transform - private static CoordinateTransformation CreateTransform (CoordinateSystem sourceCS, CoordinateSystem targetCS, TransformType transformType, MathTransform mathTransform) + if (sourceCS is ProjectedCoordinateSystem sourceProjected + && targetCS is ProjectedCoordinateSystem targetProjected + && TryGetEpsgCode(sourceProjected.GeographicCoordinateSystem, out sourceSrid) + && TryGetEpsgCode(targetProjected.GeographicCoordinateSystem, out targetSrid) + && TryCreateExplicitOperationTransformationBySridPair(sourceCS, targetCS, sourceSrid, targetSrid, out ICoordinateTransformation? baseMetadataTransformation)) { - return new CoordinateTransformation (sourceCS, targetCS, transformType, mathTransform, string.Empty, string.Empty, -1, string.Empty, string.Empty); + return baseMetadataTransformation; } - #endregion - - private static MathTransform CreateCoordinateOperation(GeocentricCoordinateSystem geo) - { - var parameterList = new List(2); - - var ellipsoid = geo.HorizontalDatum.Ellipsoid; - //var toMeter = ellipsoid.AxisUnit.MetersPerUnit; - if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_major")) == null) - parameterList.Add(new ProjectionParameter("semi_major", /*toMeter * */ellipsoid.SemiMajorAxis)); - if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_minor")) == null) - parameterList.Add(new ProjectionParameter("semi_minor", /*toMeter * */ellipsoid.SemiMinorAxis)); - - return new GeocentricTransform(parameterList); - } - private static MathTransform CreateCoordinateOperation(IProjection projection, Ellipsoid ellipsoid, LinearUnit unit) - { - var parameterList = new List(projection.NumParameters); - for (int i = 0; i < projection.NumParameters; i++) - parameterList.Add(projection.GetParameter(i)); - //var toMeter = 1d/ellipsoid.AxisUnit.MetersPerUnit; - if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_major")) == null) - parameterList.Add(new ProjectionParameter("semi_major", /*toMeter * */ellipsoid.SemiMajorAxis)); - if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("semi_minor")) == null) - parameterList.Add(new ProjectionParameter("semi_minor", /*toMeter * */ellipsoid.SemiMinorAxis)); - if (parameterList.Find((p) => p.Name.ToLowerInvariant().Replace(' ', '_').Equals("unit")) == null) - parameterList.Add(new ProjectionParameter("unit", unit.MetersPerUnit)); - - var operation = ProjectionsRegistry.CreateProjection(projection.ClassName, parameterList); - /* - var mpOperation = operation as MapProjection; - if (mpOperation != null && projection.AuthorityCode !=-1) - { - mpOperation.Authority = projection.Authority; - mpOperation.AuthorityCode = projection.AuthorityCode; - } - */ - - return operation; - /* - switch (projection.ClassName.ToLower(CultureInfo.InvariantCulture).Replace(' ', '_')) - { - case "mercator": - case "mercator_1sp": - case "mercator_2sp": - //1SP - transform = new Mercator(parameterList); - break; - case "transverse_mercator": - transform = new TransverseMercator(parameterList); - break; - case "albers": - case "albers_conic_equal_area": - transform = new AlbersProjection(parameterList); - break; - case "krovak": - transform = new KrovakProjection(parameterList); - break; - case "polyconic": - transform = new PolyconicProjection(parameterList); - break; - case "lambert_conformal_conic": - case "lambert_conformal_conic_2sp": - case "lambert_conic_conformal_(2sp)": - transform = new LambertConformalConic2SP(parameterList); - break; - default: - throw new NotSupportedException(String.Format("Projection {0} is not supported.", projection.ClassName)); - } - return transform; - */ - } - } + return this.CreateFromCoordinateSystemsCore(sourceCS, targetCS); + } } - diff --git a/src/ProjNet/CoordinateSystems/Transformations/DatumTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/DatumTransform.cs index 8f414b58..21c2843c 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/DatumTransform.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/DatumTransform.cs @@ -1,126 +1,105 @@ -// Copyright 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems.Transformations; using System; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// Applies a Bursa-Wolf seven-parameter geocentric datum shift using parameters. +/// +/// +/// The Bursa-Wolf seven-parameter formulation applies +/// T + (1 + s) * R * X, where s is the scale difference and +/// R is the linearized rotation matrix. This implementation stores the +/// affine coefficients as [S, Ex, Ey, Ez, Dx, Dy, Dz] from +/// and applies the single +/// scale factor to the fully rotated vector before translation. +/// The formulation was independently verified against ISO 19111 and EPSG +/// methods 1033 (Position Vector) and 1032 (Coordinate Frame rotation). The +/// reviewed implementation specifically preserves the corrected single-scale +/// application so the rotation terms are not multiplied by the scale factor twice. +/// +/// EPSG method 1033: Position Vector transformation (geocentric domain). +/// EPSG method 1032: Coordinate Frame rotation (geocentric domain). +internal sealed class DatumTransform : MathTransform { - /// - /// Transformation for applying - /// - [Serializable] - internal class DatumTransform : MathTransform - { - private MathTransform _inverse; - private readonly Wgs84ConversionInfo _toWgs94; - readonly double[] _v; + private readonly Wgs84ConversionInfo toWgs94; + private readonly double[] v; - private bool _isInverse; + private MathTransform? inverse; - /// - /// Initializes a new instance of the class. - /// - /// - public DatumTransform(Wgs84ConversionInfo towgs84) : this(towgs84,false) - { - } + private bool isInverse; - private DatumTransform(Wgs84ConversionInfo towgs84, bool isInverse) - { - _toWgs94 = towgs84; - _v = _toWgs94.GetAffineTransform(); - _isInverse = isInverse; - } - /// - /// Gets a Well-Known text representation of this object. - /// - /// - public override string WKT - { - get { throw new NotImplementedException(); } - } + /// + /// Initializes a new instance of the class. + /// + /// WGS84 conversion parameters defining the seven-parameter shift. + public DatumTransform(Wgs84ConversionInfo towgs84) + : this(towgs84, false) + { + } - /// - /// Gets an XML representation of this object. - /// - /// - public override string XML - { - get { throw new NotImplementedException(); } - } + private DatumTransform(Wgs84ConversionInfo towgs84, bool isInverse) + { + this.toWgs94 = towgs84; + this.v = this.toWgs94.GetAffineTransform(); + this.isInverse = isInverse; + } - public override int DimSource - { - get { return 3; } - } + /// + public override int DimSource => 3; - public override int DimTarget - { - get { return 3; } - } + /// + public override int DimTarget => 3; - /// - /// Creates the inverse transform of this object. - /// - /// - /// This method may fail if the transform is not one to one. However, all cartographic projections should succeed. - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new DatumTransform(_toWgs94,!_isInverse); - return _inverse; - } + /// + /// Creates the inverse transform of this object. + /// + /// A that is the reverse of this datum shift. + /// This method may fail if the transform is not one to one. However, all cartographic projections should succeed. + public override MathTransform Inverse() + { + this.inverse ??= new DatumTransform(this.toWgs94, !this.isInverse); + return this.inverse; + } - /// - public sealed override void Transform(ref double x, ref double y, ref double z) + /// + public sealed override void Transform(ref double x, ref double y, ref double z) + { + if (this.isInverse) { - if (_isInverse) - { - (x, y, z) = ApplyInverted(x, y, z); - } - else - { - (x, y, z) = Apply(x, y, z); - } + (x, y, z) = this.ApplyInverted(x, y, z); } - - private (double x, double y, double z) Apply(double x, double y, double z) + else { - return ( - x: _v[0] * (x - _v[3] * y + _v[2] * z) + _v[4], - y: _v[0] * (_v[3] * x + y - _v[1] * z) + _v[5], - z: _v[0] * (-_v[2] * x + _v[1] * y + z) + _v[6]); + (x, y, z) = this.Apply(x, y, z); } + } - private (double x, double y, double z) ApplyInverted(double x, double y, double z) - { - return ( - x: (1 - (_v[0] - 1)) * (x + _v[3] * y - _v[2] * z) - _v[4], - y: (1 - (_v[0] - 1)) * (-_v[3] * x + y + _v[1] * z) - _v[5], - z: (1 - (_v[0] - 1)) * (_v[2] * x - _v[1] * y + z) - _v[6]); - } + private (double X, double Y, double Z) Apply(double x, double y, double z) + { + return ( + X: (this.v[0] * (x - (this.v[3] * y) + (this.v[2] * z))) + this.v[4], + Y: (this.v[0] * ((this.v[3] * x) + y - (this.v[1] * z))) + this.v[5], + Z: (this.v[0] * ((-this.v[2] * x) + (this.v[1] * y) + z)) + this.v[6]); + } + + private (double X, double Y, double Z) ApplyInverted(double x, double y, double z) + { + return ( + X: ((1 - (this.v[0] - 1)) * (x + (this.v[3] * y) - (this.v[2] * z))) - this.v[4], + Y: ((1 - (this.v[0] - 1)) * ((-this.v[3] * x) + y + (this.v[1] * z))) - this.v[5], + Z: ((1 - (this.v[0] - 1)) * ((this.v[2] * x) - (this.v[1] * y) + z)) - this.v[6]); + } - /// - /// Reverses the transformation - /// - public override void Invert() - { - _isInverse = !_isInverse; - } - } + /// + /// Reverses the transformation. + /// + public override void Invert() + { + this.isInverse = !this.isInverse; + } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/DefModelMathTransform.Types.cs b/src/ProjNet/CoordinateSystems/Transformations/DefModelMathTransform.Types.cs new file mode 100644 index 00000000..79557c66 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/DefModelMathTransform.Types.cs @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; + +/// +/// Nested model, extent, interpolation, and time-function types for the defmodel transform. +/// +internal sealed partial class DefModelMathTransform +{ + private enum DisplacementType : byte + { + None = 0, + Horizontal = 1, + Vertical = 2, + ThreeDimensional = 3, + } + + private enum InterpolationMethod : byte + { + Bilinear = 0, + GeocentricBilinear = 1, + } + + /// + /// Represents a time-dependent scale-factor function. + /// + private interface ITimeFunction + { + /// + /// Evaluates the scale factor for the supplied observation epoch. + /// + /// Observation epoch expressed in decimal years. + /// The scale factor for the supplied epoch. + double Evaluate(double observationEpoch); + } + + private readonly struct SpatialExtent(double minX, double minY, double maxX, double maxY) + { + internal double MinX { get; } = minX; + + internal double MinY { get; } = minY; + + internal double MaxX { get; } = maxX; + + internal double MaxY { get; } = maxY; + } + + private readonly struct TimeExtent(double first, double last) + { + internal double First { get; } = first; + + internal double Last { get; } = last; + } + + private readonly struct InterpolationCell( + int x0, + int y0, + int x1, + int y1, + double fractionX, + double fractionY, + double w00, + double w01, + double w10, + double w11) + { + internal int X0 { get; } = x0; + + internal int Y0 { get; } = y0; + + internal int X1 { get; } = x1; + + internal int Y1 { get; } = y1; + + internal double FractionX { get; } = fractionX; + + internal double FractionY { get; } = fractionY; + + internal double W00 { get; } = w00; + + internal double W01 { get; } = w01; + + internal double W10 { get; } = w10; + + internal double W11 { get; } = w11; + } + + private readonly struct XyzCornerValues( + double x00, + double x01, + double x10, + double x11, + double y00, + double y01, + double y10, + double y11, + double z00, + double z01, + double z10, + double z11) + { + internal double X00 { get; } = x00; + + internal double X01 { get; } = x01; + + internal double X10 { get; } = x10; + + internal double X11 { get; } = x11; + + internal double Y00 { get; } = y00; + + internal double Y01 { get; } = y01; + + internal double Y10 { get; } = y10; + + internal double Y11 { get; } = y11; + + internal double Z00 { get; } = z00; + + internal double Z01 { get; } = z01; + + internal double Z10 { get; } = z10; + + internal double Z11 { get; } = z11; + } + + private sealed class ModelDefinition + { + internal string FileType { get; set; } = string.Empty; + + internal string FormatVersion { get; set; } = string.Empty; + + internal string SourceCrs { get; set; } = string.Empty; + + internal string TargetCrs { get; set; } = string.Empty; + + internal string DefinitionCrs { get; set; } = string.Empty; + + internal string HorizontalOffsetUnit { get; set; } = string.Empty; + + internal string VerticalOffsetUnit { get; set; } = string.Empty; + + internal string HorizontalOffsetMethod { get; set; } = string.Empty; + + internal SpatialExtent Extent { get; set; } + + internal TimeExtent TimeExtent { get; set; } + + internal ComponentDefinition[] Components { get; set; } = []; + } + + private sealed class ComponentDefinition + { + internal string Description { get; set; } = string.Empty; + + internal DisplacementType DisplacementType { get; set; } + + internal InterpolationMethod InterpolationMethod { get; set; } + + internal SpatialExtent Extent { get; set; } + + internal string SpatialModelFileName { get; set; } = string.Empty; + + internal ITimeFunction TimeFunction { get; set; } = ConstantTimeFunction.Instance; + } + + private sealed class ComponentRuntime + { + internal ComponentRuntime( + ComponentDefinition definition, + IReadOnlyList? xyzGrids, + IReadOnlyList? verticalGrids) + { + this.Definition = definition; + this.XyzGrids = xyzGrids ?? []; + this.VerticalGrids = verticalGrids ?? []; + } + + internal ComponentDefinition Definition { get; } + + internal IReadOnlyList XyzGrids { get; } + + internal IReadOnlyList VerticalGrids { get; } + } + + private sealed class ConstantTimeFunction : ITimeFunction + { + internal static readonly ConstantTimeFunction Instance = new(); + + private ConstantTimeFunction() + { + } + + public double Evaluate(double observationEpoch) + { + _ = observationEpoch; + return 1d; + } + } + + private sealed class VelocityTimeFunction(double referenceEpoch) : ITimeFunction + { + public double Evaluate(double observationEpoch) + { + return observationEpoch - referenceEpoch; + } + } + + private sealed class StepTimeFunction(double stepEpoch) : ITimeFunction + { + public double Evaluate(double observationEpoch) + { + return observationEpoch < stepEpoch ? 0d : 1d; + } + } + + private sealed class ReverseStepTimeFunction(double stepEpoch) : ITimeFunction + { + public double Evaluate(double observationEpoch) + { + return observationEpoch < stepEpoch ? -1d : 0d; + } + } + + private sealed class PiecewiseTimeFunction : ITimeFunction + { + private readonly string beforeFirst; + private readonly string afterLast; + private readonly EpochScaleTuple[] model; + + internal PiecewiseTimeFunction(string beforeFirst, string afterLast, EpochScaleTuple[] model) + { + this.beforeFirst = beforeFirst; + this.afterLast = afterLast; + this.model = model ?? []; + } + + public double Evaluate(double observationEpoch) + { + if (this.model.Length == 0) + { + return 0d; + } + + double firstEpoch = this.model[0].Epoch; + if (observationEpoch < firstEpoch) + { + if (this.beforeFirst == "ZERO") + { + return 0d; + } + + if (this.beforeFirst == "CONSTANT" || this.model.Length == 1) + { + return this.model[0].ScaleFactor; + } + + double f1 = this.model[0].ScaleFactor; + double secondEpoch = this.model[1].Epoch; + double f2 = this.model[1].ScaleFactor; + return firstEpoch == secondEpoch + ? f1 + : ((f1 * (secondEpoch - observationEpoch)) + (f2 * (observationEpoch - firstEpoch))) / (secondEpoch - firstEpoch); + } + + for (int i = 1; i < this.model.Length; i++) + { + double epochIp1 = this.model[i].Epoch; + if (observationEpoch < epochIp1) + { + double epochI = this.model[i - 1].Epoch; + double factorIp1 = this.model[i].ScaleFactor; + double factorI = this.model[i - 1].ScaleFactor; + return ((factorI * (epochIp1 - observationEpoch)) + (factorIp1 * (observationEpoch - epochI))) / (epochIp1 - epochI); + } + } + + if (this.afterLast == "ZERO") + { + return 0d; + } + + if (this.afterLast == "CONSTANT" || this.model.Length == 1) + { + return this.model[^1].ScaleFactor; + } + + double previousEpoch = this.model[^2].Epoch; + double previousFactor = this.model[^2].ScaleFactor; + double lastEpoch = this.model[^1].Epoch; + double lastFactor = this.model[^1].ScaleFactor; + return previousEpoch == lastEpoch + ? lastFactor + : ((previousFactor * (lastEpoch - observationEpoch)) + (lastFactor * (observationEpoch - previousEpoch))) / (lastEpoch - previousEpoch); + } + + internal readonly struct EpochScaleTuple(double epoch, double scaleFactor) + { + internal double Epoch { get; } = epoch; + + internal double ScaleFactor { get; } = scaleFactor; + } + } + + private sealed class ExponentialTimeFunction : ITimeFunction + { + private readonly double referenceEpoch; + private readonly double? endEpoch; + private readonly double relaxationConstant; + private readonly double beforeScaleFactor; + private readonly double initialScaleFactor; + private readonly double finalScaleFactor; + + internal ExponentialTimeFunction( + double referenceEpoch, + double? endEpoch, + double relaxationConstant, + double beforeScaleFactor, + double initialScaleFactor, + double finalScaleFactor) + { + this.referenceEpoch = referenceEpoch; + this.endEpoch = endEpoch; + this.relaxationConstant = relaxationConstant; + this.beforeScaleFactor = beforeScaleFactor; + this.initialScaleFactor = initialScaleFactor; + this.finalScaleFactor = finalScaleFactor; + } + + public double Evaluate(double observationEpoch) + { + if (observationEpoch < this.referenceEpoch) + { + return this.beforeScaleFactor; + } + + double clampedEpoch = observationEpoch; + if (this.endEpoch.HasValue) + { + clampedEpoch = Math.Min(clampedEpoch, this.endEpoch.Value); + } + + return this.initialScaleFactor + + ((this.finalScaleFactor - this.initialScaleFactor) + * (1d - Math.Exp(-(clampedEpoch - this.referenceEpoch) / this.relaxationConstant))); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/DefModelMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/DefModelMathTransform.cs new file mode 100644 index 00000000..c5d9a275 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/DefModelMathTransform.cs @@ -0,0 +1,1514 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.Json; +using ProjNet.CoordinateSystems; + +/// +/// Implements PROJ's defmodel runtime transform. +/// +internal sealed partial class DefModelMathTransform : MathTransform +{ + private const int MaximumModelSizeInBytes = 10 * 1024 * 1024; + private const double InverseHorizontalTolerance = 1e-12d; + private const double InverseVerticalTolerance = 1e-3d; + + private readonly string modelPath; + private readonly double semiMajor; + private readonly double semiMinor; + private readonly double eccentricitySquared; + private readonly bool isGeographicCrs; + private readonly bool isHorizontalUnitDegree; + private readonly bool isAddition; + private readonly SpatialExtent globalExtent; + private readonly TimeExtent timeExtent; + private readonly ComponentRuntime[] components; + private readonly GeocentricTransform geocentricForward; + private readonly GeocentricTransform geocentricInverse; + + private readonly bool isInverted; + private MathTransform? inverse; + + private DefModelMathTransform( + string modelPath, + ModelDefinition model, + double semiMajor, + double semiMinor, + bool isGeographicCrs, + bool isInverted) + { + this.modelPath = modelPath; + this.semiMajor = semiMajor; + this.semiMinor = semiMinor; + this.eccentricitySquared = 1d - ((semiMinor * semiMinor) / (semiMajor * semiMajor)); + this.isGeographicCrs = isGeographicCrs; + this.isHorizontalUnitDegree = string.Equals(model.HorizontalOffsetUnit, "DEGREE", StringComparison.Ordinal); + this.isAddition = string.Equals(model.HorizontalOffsetMethod, "ADDITION", StringComparison.Ordinal); + this.globalExtent = model.Extent; + this.timeExtent = model.TimeExtent; + this.components = LoadComponents(model, modelPath, this.isHorizontalUnitDegree); + this.isInverted = isInverted; + + var parameters = new List + { + new("semi_major", semiMajor), + new("semi_minor", semiMinor), + }; + this.geocentricForward = new GeocentricTransform(parameters, false); + this.geocentricInverse = (GeocentricTransform)this.geocentricForward.Inverse(); + + ValidateCompatibility(model, this.isGeographicCrs, this.isHorizontalUnitDegree, this.isAddition, nameof(model)); + } + + private DefModelMathTransform(DefModelMathTransform source, bool isInverted) + { + this.modelPath = source.modelPath; + this.semiMajor = source.semiMajor; + this.semiMinor = source.semiMinor; + this.eccentricitySquared = source.eccentricitySquared; + this.isGeographicCrs = source.isGeographicCrs; + this.isHorizontalUnitDegree = source.isHorizontalUnitDegree; + this.isAddition = source.isAddition; + this.globalExtent = source.globalExtent; + this.timeExtent = source.timeExtent; + this.components = source.components; + this.geocentricForward = source.geocentricForward; + this.geocentricInverse = source.geocentricInverse; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() => false; + + /// + public override MathTransform Inverse() + { + return this.inverse ??= new DefModelMathTransform(this, !this.isInverted); + } + + /// + public override void Invert() + { + throw new NotSupportedException("DefModelMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + throw new NotSupportedException("defmodel requires observation time (4D input)."); + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "defmodel arguments were null."; + return false; + } + + if (!args.TryGetValue("model", out string? modelToken) || string.IsNullOrWhiteSpace(modelToken)) + { + skipReason = "defmodel requires +model."; + return false; + } + + if (!TryResolveModelPath(modelToken, out string? resolvedModelPathCandidate)) + { + skipReason = $"Cannot open {modelToken}."; + return false; + } + + string resolvedModelPath = ArgumentGuard.ThrowIfNull(resolvedModelPathCandidate, nameof(resolvedModelPathCandidate)); + + if (!ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + operationName: "defmodel", + allowClarke1880Ign: true, + allowBessel: false, + out double semiMajor, + out double semiMinor, + out skipReason)) + { + return false; + } + + string jsonText; + try + { + var fileInfo = new FileInfo(resolvedModelPath); + if (!fileInfo.Exists) + { + skipReason = $"Cannot open {modelToken}."; + return false; + } + + if (fileInfo.Length > MaximumModelSizeInBytes) + { + skipReason = $"File {modelToken} is too large."; + return false; + } + + jsonText = File.ReadAllText(resolvedModelPath); + } + catch (IOException exception) + { + skipReason = $"Cannot read {modelToken}: {exception.Message}"; + return false; + } + catch (UnauthorizedAccessException exception) + { + skipReason = $"Cannot read {modelToken}: {exception.Message}"; + return false; + } + + try + { + ModelDefinition model = ParseModel(jsonText); + bool isGeographicCrs = IsDefinitionCrsGeographic(model.DefinitionCrs); + transform = new DefModelMathTransform(resolvedModelPath, model, semiMajor, semiMinor, isGeographicCrs, false); + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + catch (FormatException exception) + { + skipReason = $"invalid model: {exception.Message}"; + return false; + } + catch (JsonException exception) + { + skipReason = $"invalid model: {exception.Message}"; + return false; + } + catch (InvalidDataException exception) + { + skipReason = $"invalid model: {exception.Message}"; + return false; + } + catch (ArgumentException exception) + { + skipReason = $"invalid model: {exception.Message}"; + return false; + } + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + if (!TransformationMath.IsValidObservationEpoch(t, TransformationMath.MissingObservationEpoch)) + { + ArgumentGuard.ThrowArgument("defmodel requires a valid observation epoch.", nameof(t)); + } + + if (!this.isInverted) + { + if (!this.TryForward(x, y, z, t, false, out double xOut, out double yOut, out double zOut)) + { + TransformationThrowHelper.ThrowInvalidOperation("defmodel forward transformation failed."); + } + + x = xOut; + y = yOut; + z = zOut; + return; + } + + if (!this.TryInverse(x, y, z, t, out double xInv, out double yInv, out double zInv)) + { + TransformationThrowHelper.ThrowInvalidOperation("defmodel inverse transformation failed."); + } + + x = xInv; + y = yInv; + z = zInv; + } + + private static bool TryResolveModelPath(string modelToken, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(modelToken)) + { + return false; + } + + string normalized = NormalizePathToken(modelToken); + if (TryGetExistingPath(normalized, out resolvedPath)) + { + return true; + } + + string appBaseCandidate = Path.Combine(AppContext.BaseDirectory, normalized); + if (TryGetExistingPath(appBaseCandidate, out resolvedPath)) + { + return true; + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(modelToken, out resolvedPath)) + { + return true; + } + + if (!string.Equals(normalized, modelToken, StringComparison.Ordinal) + && CoordinateTransformationFactory.TryResolveGridResourcePath(normalized, out resolvedPath)) + { + return true; + } + + string fileName = Path.GetFileName(normalized); + if (!string.IsNullOrWhiteSpace(fileName)) + { + string fixtureCandidate = Path.Combine(AppContext.BaseDirectory, "Fixtures", "defmodel", fileName); + if (TryGetExistingPath(fixtureCandidate, out resolvedPath)) + { + return true; + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(fileName, out resolvedPath)) + { + return true; + } + } + + return false; + } + + private static bool TryGetExistingPath(string candidate, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(candidate)) + { + return false; + } + + if (!Path.IsPathRooted(candidate)) + { + candidate = Path.GetFullPath(candidate); + } + + if (!File.Exists(candidate)) + { + return false; + } + + resolvedPath = candidate; + return true; + } + + private static string NormalizePathToken(string token) + { + return token.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + + private static bool IsDefinitionCrsGeographic(string definitionCrs) + { + if (!TryParseEpsgCode(definitionCrs, out int epsgCode)) + { + return true; + } + + var services = new CoordinateSystemServices(); + return !services.TryGetCoordinateSystem(epsgCode, out CoordinateSystem? coordinateSystem) || coordinateSystem is GeographicCoordinateSystem; + } + + private static bool TryParseEpsgCode(string crsToken, out int epsgCode) + { + epsgCode = 0; + if (string.IsNullOrWhiteSpace(crsToken)) + { + return false; + } + + const string prefix = "EPSG:"; + if (!crsToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + string suffix = crsToken[prefix.Length..].Trim(); + return int.TryParse(suffix, NumberStyles.Integer, CultureInfo.InvariantCulture, out epsgCode) + && epsgCode > 0; + } + + private static ModelDefinition ParseModel(string jsonText) + { + using var document = JsonDocument.Parse(jsonText); + JsonElement root = document.RootElement; + if (root.ValueKind != JsonValueKind.Object) + { + throw new FormatException("Model root must be a JSON object."); + } + + var model = new ModelDefinition + { + FileType = GetRequiredString(root, "file_type"), + FormatVersion = GetRequiredString(root, "format_version"), + SourceCrs = GetRequiredString(root, "source_crs"), + TargetCrs = GetRequiredString(root, "target_crs"), + DefinitionCrs = GetRequiredString(root, "definition_crs"), + HorizontalOffsetUnit = NormalizeOptionalString(GetOptionalString(root, "horizontal_offset_unit")), + VerticalOffsetUnit = NormalizeOptionalString(GetOptionalString(root, "vertical_offset_unit")), + HorizontalOffsetMethod = NormalizeOptionalString(GetOptionalString(root, "horizontal_offset_method")), + Extent = ParseSpatialExtent(GetRequiredObject(root, "extent"), "extent"), + TimeExtent = ParseTimeExtent(GetRequiredObject(root, "time_extent")), + Components = ParseComponents(GetRequiredArray(root, "components")), + }; + + if (!string.Equals(model.SourceCrs, model.DefinitionCrs, StringComparison.Ordinal)) + { + throw new FormatException("source_crs != definition_crs is not currently supported."); + } + + ValidateModelMetadata(model); + return model; + } + + private static void ValidateModelMetadata(ModelDefinition model) + { + if (!string.IsNullOrEmpty(model.HorizontalOffsetUnit) + && !string.Equals(model.HorizontalOffsetUnit, "METRE", StringComparison.Ordinal) + && !string.Equals(model.HorizontalOffsetUnit, "DEGREE", StringComparison.Ordinal)) + { + throw new FormatException("Unsupported value for horizontal_offset_unit."); + } + + if (!string.IsNullOrEmpty(model.VerticalOffsetUnit) + && !string.Equals(model.VerticalOffsetUnit, "METRE", StringComparison.Ordinal)) + { + throw new FormatException("Unsupported value for vertical_offset_unit."); + } + + if (!string.IsNullOrEmpty(model.HorizontalOffsetMethod) + && !string.Equals(model.HorizontalOffsetMethod, "ADDITION", StringComparison.Ordinal) + && !string.Equals(model.HorizontalOffsetMethod, "GEOCENTRIC", StringComparison.Ordinal)) + { + throw new FormatException("Unsupported value for horizontal_offset_method."); + } + + for (int i = 0; i < model.Components.Length; i++) + { + ComponentDefinition component = model.Components[i]; + if (component.DisplacementType == DisplacementType.Horizontal + || component.DisplacementType == DisplacementType.ThreeDimensional) + { + if (string.IsNullOrEmpty(model.HorizontalOffsetUnit)) + { + throw new FormatException("horizontal_offset_unit must be defined for horizontal/3d components."); + } + + if (string.IsNullOrEmpty(model.HorizontalOffsetMethod)) + { + throw new FormatException("horizontal_offset_method must be defined for horizontal/3d components."); + } + } + + if (component.DisplacementType == DisplacementType.Vertical + || component.DisplacementType == DisplacementType.ThreeDimensional) + { + if (string.IsNullOrEmpty(model.VerticalOffsetUnit)) + { + throw new FormatException("vertical_offset_unit must be defined for vertical/3d components."); + } + } + + if (string.Equals(model.HorizontalOffsetUnit, "DEGREE", StringComparison.Ordinal) + && component.InterpolationMethod != InterpolationMethod.Bilinear) + { + throw new FormatException("horizontal_offset_unit = degree requires interpolation_method = bilinear."); + } + } + + if (string.Equals(model.HorizontalOffsetUnit, "DEGREE", StringComparison.Ordinal) + && !string.Equals(model.HorizontalOffsetMethod, "ADDITION", StringComparison.Ordinal)) + { + throw new FormatException("horizontal_offset_unit = degree requires horizontal_offset_method = addition."); + } + } + + private static ComponentDefinition[] ParseComponents(JsonElement componentsArray) + { + var components = new List(componentsArray.GetArrayLength()); + int index = 0; + foreach (JsonElement componentElement in componentsArray.EnumerateArray()) + { + if (componentElement.ValueKind != JsonValueKind.Object) + { + throw new FormatException($"components[{index.ToString(CultureInfo.InvariantCulture)}] must be an object."); + } + + string displacementTypeToken = NormalizeOptionalString(GetRequiredString(componentElement, "displacement_type")); + DisplacementType displacementType = ParseDisplacementType(displacementTypeToken); + InterpolationMethod interpolationMethod = ParseInterpolationMethod( + NormalizeOptionalString(GetRequiredString(GetRequiredObject(componentElement, "spatial_model"), "interpolation_method"))); + + JsonElement timeFunctionObject = GetRequiredObject(componentElement, "time_function"); + string timeFunctionType = NormalizeOptionalString(GetRequiredString(timeFunctionObject, "type")); + ITimeFunction timeFunction = ParseTimeFunction(timeFunctionType, timeFunctionObject); + + var component = new ComponentDefinition + { + Description = GetOptionalString(componentElement, "description"), + DisplacementType = displacementType, + InterpolationMethod = interpolationMethod, + Extent = ParseSpatialExtent(GetRequiredObject(componentElement, "extent"), $"components[{index.ToString(CultureInfo.InvariantCulture)}].extent"), + SpatialModelFileName = GetRequiredString(GetRequiredObject(componentElement, "spatial_model"), "filename"), + TimeFunction = timeFunction, + }; + components.Add(component); + index++; + } + + return [.. components]; + } + + private static DisplacementType ParseDisplacementType(string token) + { + return token switch + { + "NONE" => DisplacementType.None, + "HORIZONTAL" => DisplacementType.Horizontal, + "VERTICAL" => DisplacementType.Vertical, + "3D" => DisplacementType.ThreeDimensional, + _ => throw new FormatException("Unsupported value for displacement_type."), + }; + } + + private static InterpolationMethod ParseInterpolationMethod(string token) + { + return token switch + { + "BILINEAR" => InterpolationMethod.Bilinear, + "GEOCENTRIC_BILINEAR" => InterpolationMethod.GeocentricBilinear, + _ => throw new FormatException("Unsupported value for interpolation_method."), + }; + } + + private static ITimeFunction ParseTimeFunction(string timeFunctionType, JsonElement timeFunctionObject) + { + if (timeFunctionType == "CONSTANT") + { + return ConstantTimeFunction.Instance; + } + + JsonElement parameters = GetRequiredObject(timeFunctionObject, "parameters"); + if (timeFunctionType == "VELOCITY") + { + return new VelocityTimeFunction(ParseIso8601ToDecimalYear(GetRequiredString(parameters, "reference_epoch"))); + } + + if (timeFunctionType == "STEP") + { + return new StepTimeFunction(ParseIso8601ToDecimalYear(GetRequiredString(parameters, "step_epoch"))); + } + + if (timeFunctionType == "REVERSE_STEP") + { + return new ReverseStepTimeFunction(ParseIso8601ToDecimalYear(GetRequiredString(parameters, "step_epoch"))); + } + + if (timeFunctionType == "PIECEWISE") + { + string beforeFirst = NormalizeOptionalString(GetRequiredString(parameters, "before_first")); + if (beforeFirst != "ZERO" && beforeFirst != "CONSTANT" && beforeFirst != "LINEAR") + { + throw new FormatException("Unsupported value for before_first."); + } + + string afterLast = NormalizeOptionalString(GetRequiredString(parameters, "after_last")); + if (afterLast != "ZERO" && afterLast != "CONSTANT" && afterLast != "LINEAR") + { + throw new FormatException("Unsupported value for after_last."); + } + + JsonElement modelArray = GetRequiredArray(parameters, "model"); + var tuples = new List(modelArray.GetArrayLength()); + foreach (JsonElement tupleElement in modelArray.EnumerateArray()) + { + if (tupleElement.ValueKind != JsonValueKind.Object) + { + throw new FormatException("piecewise model element must be an object."); + } + + tuples.Add(new PiecewiseTimeFunction.EpochScaleTuple( + ParseIso8601ToDecimalYear(GetRequiredString(tupleElement, "epoch")), + GetRequiredDouble(tupleElement, "scale_factor"))); + } + + return new PiecewiseTimeFunction(beforeFirst, afterLast, [.. tuples]); + } + + if (timeFunctionType == "EXPONENTIAL") + { + double referenceEpoch = ParseIso8601ToDecimalYear(GetRequiredString(parameters, "reference_epoch")); + string endEpochString = GetOptionalString(parameters, "end_epoch"); + double? endEpoch = string.IsNullOrWhiteSpace(endEpochString) + ? null + : ParseIso8601ToDecimalYear(endEpochString); + double relaxationConstant = GetRequiredDouble(parameters, "relaxation_constant"); + return relaxationConstant <= 0d + ? throw new FormatException("Invalid value for relaxation_constant.") + : (ITimeFunction)new ExponentialTimeFunction( + referenceEpoch, + endEpoch, + relaxationConstant, + GetRequiredDouble(parameters, "before_scale_factor"), + GetRequiredDouble(parameters, "initial_scale_factor"), + GetRequiredDouble(parameters, "final_scale_factor")); + } + + throw new FormatException($"Unsupported type of time function: {timeFunctionType}."); + } + + private static TimeExtent ParseTimeExtent(JsonElement timeExtentObject) + { + double first = ParseIso8601ToDecimalYear(GetRequiredString(timeExtentObject, "first")); + double last = ParseIso8601ToDecimalYear(GetRequiredString(timeExtentObject, "last")); + return last < first + ? throw new FormatException("time_extent.last must be greater than or equal to time_extent.first.") + : new TimeExtent(first, last); + } + + private static SpatialExtent ParseSpatialExtent(JsonElement extentObject, string context) + { + string type = NormalizeOptionalString(GetRequiredString(extentObject, "type")); + if (type != "BBOX") + { + throw new FormatException($"{context} only supports type=bbox."); + } + + JsonElement parametersObject = GetRequiredObject(extentObject, "parameters"); + JsonElement bboxArray = GetRequiredArray(parametersObject, "bbox"); + if (bboxArray.GetArrayLength() != 4) + { + throw new FormatException($"{context}.parameters.bbox must contain exactly 4 numeric values."); + } + + double minX = GetArrayDouble(bboxArray, 0, context); + double minY = GetArrayDouble(bboxArray, 1, context); + double maxX = GetArrayDouble(bboxArray, 2, context); + double maxY = GetArrayDouble(bboxArray, 3, context); + return maxX < minX || maxY < minY + ? throw new FormatException($"{context}.parameters.bbox has invalid ordering.") + : new SpatialExtent(minX, minY, maxX, maxY); + } + + private static double GetArrayDouble(JsonElement arrayElement, int index, string context) + { + JsonElement value = arrayElement[index]; + return value.ValueKind != JsonValueKind.Number + ? throw new FormatException($"{context}.parameters.bbox contains a non-numeric value.") + : value.GetDouble(); + } + + private static JsonElement GetRequiredObject(JsonElement parent, string propertyName) + { + if (!parent.TryGetProperty(propertyName, out JsonElement value)) + { + throw new FormatException($"Missing \"{propertyName}\" key."); + } + + return value.ValueKind != JsonValueKind.Object ? throw new FormatException($"\"{propertyName}\" must be an object.") : value; + } + + private static JsonElement GetRequiredArray(JsonElement parent, string propertyName) + { + if (!parent.TryGetProperty(propertyName, out JsonElement value)) + { + throw new FormatException($"Missing \"{propertyName}\" key."); + } + + return value.ValueKind != JsonValueKind.Array ? throw new FormatException($"\"{propertyName}\" must be an array.") : value; + } + + private static string GetRequiredString(JsonElement parent, string propertyName) + { + if (!parent.TryGetProperty(propertyName, out JsonElement value)) + { + throw new FormatException($"Missing \"{propertyName}\" key."); + } + + if (value.ValueKind != JsonValueKind.String) + { + throw new FormatException($"\"{propertyName}\" must be a string."); + } + + string? text = value.GetString(); + return text ?? throw new FormatException($"\"{propertyName}\" must not be null."); + } + + private static string GetOptionalString(JsonElement parent, string propertyName) + { + if (!parent.TryGetProperty(propertyName, out JsonElement value)) + { + return string.Empty; + } + + return value.ValueKind != JsonValueKind.String + ? throw new FormatException($"\"{propertyName}\" must be a string.") + : value.GetString() ?? string.Empty; + } + + private static double GetRequiredDouble(JsonElement parent, string propertyName) + { + if (!parent.TryGetProperty(propertyName, out JsonElement value)) + { + throw new FormatException($"Missing \"{propertyName}\" key."); + } + + return value.ValueKind != JsonValueKind.Number + ? throw new FormatException($"\"{propertyName}\" must be numeric.") + : value.GetDouble(); + } + + private static string NormalizeOptionalString(string text) + { + return string.IsNullOrWhiteSpace(text) + ? string.Empty + : text.Trim().ToUpperInvariant(); + } + + private static double ParseIso8601ToDecimalYear(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new FormatException($"Wrong formatting / invalid date-time for {value}."); + } + + if (value.Length != 20 + || value[4] != '-' + || value[7] != '-' + || value[10] != 'T' + || value[13] != ':' + || value[16] != ':' + || value[19] != 'Z') + { + throw new FormatException($"Wrong formatting / invalid date-time for {value}."); + } + + int year = ParseEpochPart(value, 0, 4); + int month = ParseEpochPart(value, 5, 2); + int day = ParseEpochPart(value, 8, 2); + int hour = ParseEpochPart(value, 11, 2); + int minute = ParseEpochPart(value, 14, 2); + int second = ParseEpochPart(value, 17, 2); + + if (year < 1582 || month < 1 || month > 12 || day < 1 || hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 61) + { + throw new FormatException($"Wrong formatting / invalid date-time for {value}."); + } + + bool isLeapYear = ((year % 4) == 0 && (year % 100) != 0) || ((year % 400) == 0); + int[] monthLengths = isLeapYear + ? [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + if (day > monthLengths[month - 1]) + { + throw new FormatException($"Wrong formatting / invalid date-time for {value}."); + } + + int dayInYear = day - 1; + for (int monthIndex = 1; monthIndex < month; monthIndex++) + { + dayInYear += monthLengths[monthIndex - 1]; + } + + double numerator = (dayInYear * 86400d) + (hour * 3600d) + (minute * 60d) + second; + double denominator = (isLeapYear ? 366d : 365d) * 86400d; + return year + (numerator / denominator); + } + + private static int ParseEpochPart(string value, int startIndex, int length) + { +#if NETSTANDARD2_0 + if (!int.TryParse(value.Substring(startIndex, length), NumberStyles.None, CultureInfo.InvariantCulture, out int parsed)) +#else + if (!int.TryParse(value.AsSpan(startIndex, length), NumberStyles.None, CultureInfo.InvariantCulture, out int parsed)) +#endif + { + throw new FormatException($"Wrong formatting / invalid date-time for {value}."); + } + + return parsed; + } + + private static ComponentRuntime[] LoadComponents(ModelDefinition model, string modelPath, bool isHorizontalUnitDegree) + { + string modelDirectory = Path.GetDirectoryName(modelPath) ?? string.Empty; + var loaded = new List(model.Components.Length); + for (int i = 0; i < model.Components.Length; i++) + { + ComponentDefinition component = model.Components[i]; + if (component.DisplacementType == DisplacementType.None) + { + loaded.Add(new ComponentRuntime(component, null, null)); + continue; + } + + if (!TryResolveComponentPath(component.SpatialModelFileName, modelDirectory, out string? componentPathCandidate)) + { + throw new InvalidDataException($"Cannot resolve deformation model component grid '{component.SpatialModelFileName}'."); + } + + string componentPath = ArgumentGuard.ThrowIfNull(componentPathCandidate, nameof(componentPathCandidate)); + + if (component.DisplacementType == DisplacementType.Vertical) + { + IReadOnlyList vertical = GeoTiffGridLoader.LoadVertical(componentPath); + if (vertical.Count == 0) + { + throw new InvalidDataException($"No vertical grid data found in '{component.SpatialModelFileName}'."); + } + + loaded.Add(new ComponentRuntime( + component, + null, + [.. vertical.OrderBy(grid => grid.Area, Comparer.Default)])); + continue; + } + + bool requireMetreUnits = !isHorizontalUnitDegree; + IReadOnlyList xyz = GeoTiffGridLoader.LoadXyz(componentPath, requireMetreUnits); + if (xyz.Count == 0) + { + throw new InvalidDataException($"No XYZ grid data found in '{component.SpatialModelFileName}'."); + } + + loaded.Add(new ComponentRuntime( + component, + [.. xyz.OrderBy(grid => grid.Area, Comparer.Default)], + null)); + } + + return [.. loaded]; + } + + private static bool TryResolveComponentPath(string fileName, string modelDirectory, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(fileName)) + { + return false; + } + + string normalized = NormalizePathToken(fileName); + if (TryGetExistingPath(normalized, out resolvedPath)) + { + return true; + } + + if (!string.IsNullOrWhiteSpace(modelDirectory)) + { + string modelRelativeCandidate = Path.Combine(modelDirectory, normalized); + if (TryGetExistingPath(modelRelativeCandidate, out resolvedPath)) + { + return true; + } + } + + string appBaseCandidate = Path.Combine(AppContext.BaseDirectory, normalized); + if (TryGetExistingPath(appBaseCandidate, out resolvedPath)) + { + return true; + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(fileName, out resolvedPath)) + { + return true; + } + + if (!string.Equals(normalized, fileName, StringComparison.Ordinal) + && CoordinateTransformationFactory.TryResolveGridResourcePath(normalized, out resolvedPath)) + { + return true; + } + + string baseName = Path.GetFileName(normalized); + if (!string.IsNullOrWhiteSpace(baseName)) + { + if (!string.IsNullOrWhiteSpace(modelDirectory) + && TryGetExistingPath(Path.Combine(modelDirectory, baseName), out resolvedPath)) + { + return true; + } + + string fixtureCandidate = Path.Combine(AppContext.BaseDirectory, "Fixtures", "defmodel", "tests", baseName); + if (TryGetExistingPath(fixtureCandidate, out resolvedPath)) + { + return true; + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(baseName, out resolvedPath)) + { + return true; + } + } + + return false; + } + + private static void ValidateCompatibility( + ModelDefinition model, + bool isGeographicCrs, + bool isHorizontalUnitDegree, + bool isAddition, + string modelParamName) + { + if (!isGeographicCrs && isHorizontalUnitDegree) + { + ArgumentGuard.ThrowArgument("definition_crs = projected CRS and horizontal_offset_unit = degree are incompatible.", modelParamName); + } + + if (!isGeographicCrs && !isAddition) + { + ArgumentGuard.ThrowArgument("definition_crs = projected CRS and horizontal_offset_method = geocentric are incompatible.", modelParamName); + } + + if (isGeographicCrs) + { + return; + } + + for (int i = 0; i < model.Components.Length; i++) + { + if (model.Components[i].InterpolationMethod == InterpolationMethod.GeocentricBilinear) + { + ArgumentGuard.ThrowArgument("definition_crs = projected CRS and interpolation_method = geocentric_bilinear are incompatible.", modelParamName); + } + } + } + + private static bool BboxCheck( + ref double x, + ref double y, + bool forInverseComputation, + SpatialExtent extent, + double epsilon, + double extraMarginForInverse) + { + if (x >= extent.MinX - epsilon + && x <= extent.MaxX + epsilon + && y >= extent.MinY - epsilon + && y <= extent.MaxY + epsilon) + { + return true; + } + + if (!forInverseComputation) + { + return false; + } + + bool xOk = false; + if (x >= extent.MinX - epsilon && x <= extent.MaxX + epsilon) + { + xOk = true; + } + else if (x > extent.MinX - extraMarginForInverse && x < extent.MinX) + { + x = extent.MinX; + xOk = true; + } + else if (x < extent.MaxX + extraMarginForInverse && x > extent.MaxX) + { + x = extent.MaxX; + xOk = true; + } + + bool yOk = false; + if (y >= extent.MinY - epsilon && y <= extent.MaxY + epsilon) + { + yOk = true; + } + else if (y > extent.MinY - extraMarginForInverse && y < extent.MinY) + { + y = extent.MinY; + yOk = true; + } + else if (y < extent.MaxY + extraMarginForInverse && y > extent.MaxY) + { + y = extent.MaxY; + yOk = true; + } + + return xOk && yOk; + } + + private static double Clamp(double value, double minimum, double maximum) + { + return value < minimum ? minimum : value > maximum ? maximum : value; + } + + private static bool TryFindXyzGrid( + IReadOnlyList grids, + double x, + double y, + [NotNullWhen(true)] out GeoTiffXyzGridShiftMathTransform.XyzGrid? grid) + { + for (int i = 0; i < grids.Count; i++) + { + if (grids[i].Contains(x, y)) + { + grid = grids[i]; + return true; + } + } + + grid = null; + return false; + } + + private static bool TryFindVerticalGrid( + IReadOnlyList grids, + double x, + double y, + [NotNullWhen(true)] out GeoTiffVGridShiftMathTransform.VerticalGrid? grid) + { + for (int i = 0; i < grids.Count; i++) + { + if (grids[i].Contains(x, y)) + { + grid = grids[i]; + return true; + } + } + + grid = null; + return false; + } + + private static bool TryInterpolateVerticalShift( + GeoTiffVGridShiftMathTransform.VerticalGrid grid, + double x, + double y, + out double value) + { + value = 0d; + if (!TryGetInterpolationCell(grid, x, y, out InterpolationCell cell)) + { + return false; + } + + double v00 = grid.GetValue(cell.X0, cell.Y0); + double v01 = grid.GetValue(cell.X0, cell.Y1); + double v10 = grid.GetValue(cell.X1, cell.Y0); + double v11 = grid.GetValue(cell.X1, cell.Y1); + + bool nd00 = grid.IsNoData(v00); + bool nd01 = grid.IsNoData(v01); + bool nd10 = grid.IsNoData(v10); + bool nd11 = grid.IsNoData(v11); + if (!nd00 && !nd01 && !nd10 && !nd11) + { + value = Bilinear(v00, v01, v10, v11, cell.W00, cell.W01, cell.W10, cell.W11); + return true; + } + + double weightedSum = 0d; + double weightSum = 0d; + if (!nd00) + { + weightedSum += v00 * cell.W00; + weightSum += cell.W00; + } + + if (!nd01) + { + weightedSum += v01 * cell.W01; + weightSum += cell.W01; + } + + if (!nd10) + { + weightedSum += v10 * cell.W10; + weightSum += cell.W10; + } + + if (!nd11) + { + weightedSum += v11 * cell.W11; + weightSum += cell.W11; + } + + if (weightSum == 0d) + { + return false; + } + + value = weightedSum / weightSum; + return true; + } + + private static bool TryGetInterpolationCell( + BaseGeoGrid grid, + double x, + double y, + out InterpolationCell cell) + { + cell = default; + if (!grid.TryMapToGridCoordinates(x, y, out double gridX, out double gridY)) + { + return false; + } + + int x0 = (int)Math.Floor(gridX); + int y0 = (int)Math.Floor(gridY); + double fractionX = gridX - x0; + double fractionY = gridY - y0; + if (!TryNormalizeInterpolationCell(grid.Width, ref x0, ref fractionX) + || !TryNormalizeInterpolationCell(grid.Height, ref y0, ref fractionY)) + { + return false; + } + + int x1 = x0 + 1; + int y1 = y0 + 1; + double xy = fractionX * fractionY; + double w00 = 1d - fractionX - fractionY + xy; + double w10 = fractionX - xy; + double w01 = fractionY - xy; + double w11 = xy; + cell = new InterpolationCell(x0, y0, x1, y1, fractionX, fractionY, w00, w01, w10, w11); + return true; + } + + private static bool TryNormalizeInterpolationCell(int size, ref int index, ref double fraction) + { + if (index < 0) + { + if (index == -1 && fraction > 1d - (10d * 1e-5d)) + { + index = 0; + fraction = 0d; + return true; + } + + return false; + } + + if (index + 1 < size) + { + return true; + } + + if (index + 1 == size && fraction < 10d * 1e-5d) + { + index = size - 2; + fraction = 1d; + return true; + } + + return false; + } + + private static bool TryReadXyzCornerValues( + GeoTiffXyzGridShiftMathTransform.XyzGrid grid, + InterpolationCell cell, + out XyzCornerValues values) + { + values = new XyzCornerValues( + grid.GetXShift(cell.X0, cell.Y0), + grid.GetXShift(cell.X0, cell.Y1), + grid.GetXShift(cell.X1, cell.Y0), + grid.GetXShift(cell.X1, cell.Y1), + grid.GetYShift(cell.X0, cell.Y0), + grid.GetYShift(cell.X0, cell.Y1), + grid.GetYShift(cell.X1, cell.Y0), + grid.GetYShift(cell.X1, cell.Y1), + grid.GetZShift(cell.X0, cell.Y0), + grid.GetZShift(cell.X0, cell.Y1), + grid.GetZShift(cell.X1, cell.Y0), + grid.GetZShift(cell.X1, cell.Y1)); + return TransformationMath.IsFinite(values.X00) + && TransformationMath.IsFinite(values.X01) + && TransformationMath.IsFinite(values.X10) + && TransformationMath.IsFinite(values.X11) + && TransformationMath.IsFinite(values.Y00) + && TransformationMath.IsFinite(values.Y01) + && TransformationMath.IsFinite(values.Y10) + && TransformationMath.IsFinite(values.Y11) + && TransformationMath.IsFinite(values.Z00) + && TransformationMath.IsFinite(values.Z01) + && TransformationMath.IsFinite(values.Z10) + && TransformationMath.IsFinite(values.Z11); + } + + private static bool TryInterpolateGeocentricBilinear( + GeoTiffXyzGridShiftMathTransform.XyzGrid grid, + InterpolationCell cell, + XyzCornerValues values, + double latitudeDegrees, + out double eastOffset, + out double northOffset) + { + eastOffset = 0d; + northOffset = 0d; + + GetGridCoordinate(grid, cell.X0, cell.Y0, out double lon00, out double lat00); + GetGridCoordinate(grid, cell.X1, cell.Y0, out double lon10, out _); + GetGridCoordinate(grid, cell.X0, cell.Y1, out _, out double lat01); + + double resXDegrees = NormalizeLongitudeDelta(lon10 - lon00); + if (Math.Abs(resXDegrees) < 1e-14d) + { + return false; + } + + double halfResXRadians = DegreesToRadians(0.5d * resXDegrees); + double sinHalfResX = Math.Sin(halfResXRadians); + double cosHalfResX = Math.Cos(halfResXRadians); + double phi0 = DegreesToRadians(lat00); + double phi1 = DegreesToRadians(lat01); + double sinPhi0 = Math.Sin(phi0); + double cosPhi0 = Math.Cos(phi0); + double sinPhi1 = Math.Sin(phi1); + double cosPhi1 = Math.Cos(phi1); + + ConvertEnToGeocentricAtCorner(-sinHalfResX, cosHalfResX, sinPhi0, cosPhi0, values.X00, values.Y00, out double dX00, out double dY00, out double dZ00); + ConvertEnToGeocentricAtCorner(-sinHalfResX, cosHalfResX, sinPhi1, cosPhi1, values.X01, values.Y01, out double dX01, out double dY01, out double dZ01); + ConvertEnToGeocentricAtCorner(sinHalfResX, cosHalfResX, sinPhi0, cosPhi0, values.X10, values.Y10, out double dX10, out double dY10, out double dZ10); + ConvertEnToGeocentricAtCorner(sinHalfResX, cosHalfResX, sinPhi1, cosPhi1, values.X11, values.Y11, out double dX11, out double dY11, out double dZ11); + + double dX = Bilinear(dX00, dX01, dX10, dX11, cell.W00, cell.W01, cell.W10, cell.W11); + double dY = Bilinear(dY00, dY01, dY10, dY11, cell.W00, cell.W01, cell.W10, cell.W11); + double dZ = Bilinear(dZ00, dZ01, dZ10, dZ11, cell.W00, cell.W01, cell.W10, cell.W11); + + double lambdaRelativeToCellCenter = (cell.FractionX - 0.5d) * DegreesToRadians(resXDegrees); + double sinLambda = Math.Sin(lambdaRelativeToCellCenter); + double cosLambda = Math.Cos(lambdaRelativeToCellCenter); + double latitudeRadians = DegreesToRadians(latitudeDegrees); + double sinLatitude = Math.Sin(latitudeRadians); + double cosLatitude = Math.Cos(latitudeRadians); + eastOffset = (-dX * sinLambda) + (dY * cosLambda); + northOffset = (((-dX * cosLambda) - (dY * sinLambda)) * sinLatitude) + (dZ * cosLatitude); + return true; + } + + private static void ConvertEnToGeocentricAtCorner( + double sinLambda, + double cosLambda, + double sinPhi, + double cosPhi, + double eastOffset, + double northOffset, + out double deltaX, + out double deltaY, + out double deltaZ) + { + double northOffsetSinPhi = northOffset * sinPhi; + deltaX = (-eastOffset * sinLambda) - (northOffsetSinPhi * cosLambda); + deltaY = (eastOffset * cosLambda) - (northOffsetSinPhi * sinLambda); + deltaZ = northOffset * cosPhi; + } + + private static double Bilinear( + double value00, + double value01, + double value10, + double value11, + double weight00, + double weight01, + double weight10, + double weight11) + { + return (value00 * weight00) + + (value01 * weight01) + + (value10 * weight10) + + (value11 * weight11); + } + + private static void GetGridCoordinate(BaseGeoGrid grid, int x, int y, out double longitude, out double latitude) + { + longitude = (grid.A * x) + (grid.B * y) + grid.C; + latitude = (grid.D * x) + (grid.E * y) + grid.F; + } + + private static double NormalizeLongitudeDelta(double deltaDegrees) + { + double delta = deltaDegrees; + while (delta > 180d) + { + delta -= 360d; + } + + while (delta < -180d) + { + delta += 360d; + } + + return delta; + } + + private static void DeltaEastingNorthingToLongLat( + double cosPhi, + double eastOffset, + double northOffset, + double semiMajor, + double semiMinor, + double eccentricitySquared, + out double deltaLambdaRadians, + out double deltaPhiRadians) + { + double oneMinusX = eccentricitySquared * (1d - (cosPhi * cosPhi)); + double x = 1d - oneMinusX; + double sqrtX = Math.Sqrt(x); + deltaLambdaRadians = eastOffset * sqrtX / (semiMajor * cosPhi); + deltaPhiRadians = northOffset * semiMajor * sqrtX * x / (semiMinor * semiMinor); + } + + private bool TryForward( + double x, + double y, + double z, + double observationEpoch, + bool forInverseComputation, + out double xOut, + out double yOut, + out double zOut) + { + xOut = x; + yOut = y; + zOut = double.IsNaN(z) ? 0d : z; + double xWorking = x; + double yWorking = y; + + double epsilon = this.isGeographicCrs ? 1e-10d : 1e-5d; + double globalMinX = this.globalExtent.MinX; + double globalMaxX = this.globalExtent.MaxX; + if (this.isGeographicCrs) + { + while (xWorking < globalMinX - epsilon) + { + xWorking += 360d; + } + + while (xWorking > globalMaxX + epsilon) + { + xWorking -= 360d; + } + } + + double globalExtraMargin = this.isGeographicCrs ? 0.1d : 10000d; + if (!BboxCheck(ref xWorking, ref yWorking, forInverseComputation, this.globalExtent, epsilon, globalExtraMargin)) + { + return false; + } + + if (observationEpoch < this.timeExtent.First || observationEpoch > this.timeExtent.Last) + { + return false; + } + + double longitudeOffsetDegrees = 0d; + double latitudeOffsetDegrees = 0d; + double eastingOffset = 0d; + double northingOffset = 0d; + double verticalOffset = 0d; + + for (int i = 0; i < this.components.Length; i++) + { + ComponentRuntime component = this.components[i]; + if (component.Definition.DisplacementType == DisplacementType.None) + { + continue; + } + + double xForGrid = xWorking; + double yForGrid = yWorking; + if (!BboxCheck(ref xForGrid, ref yForGrid, forInverseComputation, component.Definition.Extent, epsilon, 0d)) + { + continue; + } + + xForGrid = Clamp(xForGrid, component.Definition.Extent.MinX, component.Definition.Extent.MaxX); + yForGrid = Clamp(yForGrid, component.Definition.Extent.MinY, component.Definition.Extent.MaxY); + + double timeFactor = component.Definition.TimeFunction.Evaluate(observationEpoch); + if (timeFactor == 0d) + { + continue; + } + + if (component.Definition.DisplacementType == DisplacementType.Vertical) + { + if (!TryFindVerticalGrid(component.VerticalGrids, xForGrid, yForGrid, out GeoTiffVGridShiftMathTransform.VerticalGrid? verticalGridCandidate)) + { + continue; + } + + GeoTiffVGridShiftMathTransform.VerticalGrid verticalGrid = ArgumentGuard.ThrowIfNull(verticalGridCandidate, nameof(verticalGridCandidate)); + if (!TryInterpolateVerticalShift(verticalGrid, xForGrid, yForGrid, out double verticalComponent)) + { + return false; + } + + verticalOffset += timeFactor * verticalComponent; + continue; + } + + if (!TryFindXyzGrid(component.XyzGrids, xForGrid, yForGrid, out GeoTiffXyzGridShiftMathTransform.XyzGrid? xyzGridCandidate)) + { + continue; + } + + GeoTiffXyzGridShiftMathTransform.XyzGrid xyzGrid = ArgumentGuard.ThrowIfNull(xyzGridCandidate, nameof(xyzGridCandidate)); + if (!TryGetInterpolationCell(xyzGrid, xForGrid, yForGrid, out InterpolationCell cell)) + { + continue; + } + + if (!TryReadXyzCornerValues(xyzGrid, cell, out XyzCornerValues cornerValues)) + { + return false; + } + + if (component.Definition.DisplacementType == DisplacementType.ThreeDimensional) + { + double zComponent = Bilinear(cornerValues.Z00, cornerValues.Z01, cornerValues.Z10, cornerValues.Z11, cell.W00, cell.W01, cell.W10, cell.W11); + verticalOffset += timeFactor * zComponent; + } + + if (this.isHorizontalUnitDegree) + { + double longitudeComponent = Bilinear(cornerValues.X00, cornerValues.X01, cornerValues.X10, cornerValues.X11, cell.W00, cell.W01, cell.W10, cell.W11); + double latitudeComponent = Bilinear(cornerValues.Y00, cornerValues.Y01, cornerValues.Y10, cornerValues.Y11, cell.W00, cell.W01, cell.W10, cell.W11); + longitudeOffsetDegrees += timeFactor * longitudeComponent; + latitudeOffsetDegrees += timeFactor * latitudeComponent; + continue; + } + + if (component.Definition.InterpolationMethod == InterpolationMethod.Bilinear) + { + double eastComponent = Bilinear(cornerValues.X00, cornerValues.X01, cornerValues.X10, cornerValues.X11, cell.W00, cell.W01, cell.W10, cell.W11); + double northComponent = Bilinear(cornerValues.Y00, cornerValues.Y01, cornerValues.Y10, cornerValues.Y11, cell.W00, cell.W01, cell.W10, cell.W11); + eastingOffset += timeFactor * eastComponent; + northingOffset += timeFactor * northComponent; + continue; + } + + if (!TryInterpolateGeocentricBilinear(xyzGrid, cell, cornerValues, yWorking, out double eastGeocentricComponent, out double northGeocentricComponent)) + { + return false; + } + + eastingOffset += timeFactor * eastGeocentricComponent; + northingOffset += timeFactor * northGeocentricComponent; + } + + xOut = xWorking; + yOut = yWorking; + zOut += verticalOffset; + + if (this.isHorizontalUnitDegree) + { + xOut += longitudeOffsetDegrees; + yOut += latitudeOffsetDegrees; + return true; + } + + if (this.isAddition && !this.isGeographicCrs) + { + xOut += eastingOffset; + yOut += northingOffset; + return true; + } + + if (this.isAddition) + { + double cosPhi = Math.Cos(DegreesToRadians(yWorking)); + if (Math.Abs(cosPhi) < 1e-16d) + { + return false; + } + + DeltaEastingNorthingToLongLat(cosPhi, eastingOffset, northingOffset, this.semiMajor, this.semiMinor, this.eccentricitySquared, out double dLamRadians, out double dPhiRadians); + xOut += RadiansToDegrees(dLamRadians); + yOut += RadiansToDegrees(dPhiRadians); + return true; + } + + double lambdaRadians = DegreesToRadians(xWorking); + double phiRadians = DegreesToRadians(yWorking); + double sinLambda = Math.Sin(lambdaRadians); + double cosLambda = Math.Cos(lambdaRadians); + double sinPhi = Math.Sin(phiRadians); + double cosPhiForGeocentric = Math.Cos(phiRadians); + double dnSinPhi = northingOffset * sinPhi; + double deltaX = (-eastingOffset * sinLambda) - (dnSinPhi * cosLambda); + double deltaY = (eastingOffset * cosLambda) - (dnSinPhi * sinLambda); + double deltaZ = northingOffset * cosPhiForGeocentric; + + double geocentricX = xWorking; + double geocentricY = yWorking; + double geocentricZ = 0d; + this.geocentricForward.Transform(ref geocentricX, ref geocentricY, ref geocentricZ); + geocentricX += deltaX; + geocentricY += deltaY; + geocentricZ += deltaZ; + this.geocentricInverse.Transform(ref geocentricX, ref geocentricY, ref geocentricZ); + xOut = geocentricX; + yOut = geocentricY; + return true; + } + + private bool TryInverse( + double x, + double y, + double z, + double observationEpoch, + out double xOut, + out double yOut, + out double zOut) + { + xOut = x; + yOut = y; + zOut = z; + for (int i = 0; i < TransformationMath.MaxInverseIterations; i++) + { + if (!this.TryForward(xOut, yOut, zOut, observationEpoch, true, out double xNew, out double yNew, out double zNew)) + { + return false; + } + + double dx = xNew - x; + double dy = yNew - y; + double dz = zNew - z; + xOut -= dx; + yOut -= dy; + zOut -= dz; + if (Math.Max(Math.Abs(dx), Math.Abs(dy)) < InverseHorizontalTolerance && Math.Abs(dz) < InverseVerticalTolerance) + { + return true; + } + } + + return false; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/DeformationMathTransform.Types.cs b/src/ProjNet/CoordinateSystems/Transformations/DeformationMathTransform.Types.cs new file mode 100644 index 00000000..76d52d12 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/DeformationMathTransform.Types.cs @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Buffers.Binary; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +/// +/// Nested grid data structures and interpolation helpers for the deformation transform. +/// +internal sealed partial class DeformationMathTransform +{ + private readonly struct InterpolationCell(int x0, int y0, int x1, int y1, double w00, double w01, double w10, double w11) + { + internal int X0 { get; } = x0; + + internal int Y0 { get; } = y0; + + internal int X1 { get; } = x1; + + internal int Y1 { get; } = y1; + + internal double W00 { get; } = w00; + + internal double W01 { get; } = w01; + + internal double W10 { get; } = w10; + + internal double W11 { get; } = w11; + } + + private sealed class CTable2Grid + { + private readonly float[] eastValues; + private readonly float[] northValues; + + private CTable2Grid( + string sourcePath, + double west, + double east, + double south, + double north, + double resolutionX, + double resolutionY, + int width, + int height, + float[] eastValues, + float[] northValues) + { + this.SourcePath = sourcePath; + this.West = west; + this.East = east; + this.South = south; + this.North = north; + this.ResolutionX = resolutionX; + this.ResolutionY = resolutionY; + this.Width = width; + this.Height = height; + this.eastValues = eastValues; + this.northValues = northValues; + this.Epsilon = (Math.Abs(resolutionX) + Math.Abs(resolutionY)) * RelativeTolerance; + this.InvResolutionX = 1d / resolutionX; + this.InvResolutionY = 1d / resolutionY; + this.Area = Math.Abs((east - west) * (north - south)); + } + + internal string SourcePath { get; } + + internal double West { get; } + + internal double East { get; } + + internal double South { get; } + + internal double North { get; } + + internal double ResolutionX { get; } + + internal double ResolutionY { get; } + + internal int Width { get; } + + internal int Height { get; } + + internal double Epsilon { get; } + + internal double InvResolutionX { get; } + + internal double InvResolutionY { get; } + + internal double Area { get; } + + internal static CTable2Grid Load(string path) + { + byte[] bytes = File.ReadAllBytes(path); + if (bytes.Length < 160) + { + throw new InvalidDataException("CTABLE2 file is too small."); + } + + string identifier = Encoding.ASCII.GetString(bytes, 0, 9); + if (!identifier.Equals("CTABLE V2", StringComparison.Ordinal)) + { + throw new InvalidDataException("Horizontal grid file is not a CTable2 grid."); + } + + double west = ReadDoubleLittleEndian(bytes, 96); + double south = ReadDoubleLittleEndian(bytes, 104); + double resolutionX = ReadDoubleLittleEndian(bytes, 112); + double resolutionY = ReadDoubleLittleEndian(bytes, 120); + int width = ReadInt32LittleEndian(bytes, 128); + int height = ReadInt32LittleEndian(bytes, 132); + if (!TransformationMath.IsFinite(west) + || !TransformationMath.IsFinite(south) + || !TransformationMath.IsFinite(resolutionX) + || !TransformationMath.IsFinite(resolutionY) + || width <= 0 + || height <= 0 + || Math.Abs(west) > (4d * Math.PI) + || Math.Abs(south) > (Math.PI + 1e-5d) + || resolutionX <= 1e-10d + || resolutionY <= 1e-10d) + { + throw new InvalidDataException("CTABLE2 header contains invalid extents or resolution."); + } + + long expectedDataBytes = (long)width * height * 8L; + if (bytes.Length < 160 + expectedDataBytes) + { + throw new InvalidDataException("CTABLE2 data is truncated."); + } + + float[] eastValues = new float[width * height]; + float[] northValues = new float[width * height]; + int offset = 160; + for (int i = 0; i < eastValues.Length; i++) + { + eastValues[i] = ReadSingleLittleEndian(bytes, offset); + northValues[i] = ReadSingleLittleEndian(bytes, offset + 4); + offset += 8; + } + + double east = west + ((width - 1) * resolutionX); + double north = south + ((height - 1) * resolutionY); + return new CTable2Grid(path, west, east, south, north, resolutionX, resolutionY, width, height, eastValues, northValues); + } + + internal bool Contains(double longitudeRadians, double latitudeRadians) + { + double lon = longitudeRadians; + if (lon < this.West - this.Epsilon) + { + lon += TwoPi; + } + else if (lon > this.East + this.Epsilon) + { + lon -= TwoPi; + } + + return lon >= this.West - this.Epsilon + && lon <= this.East + this.Epsilon + && latitudeRadians >= this.South - this.Epsilon + && latitudeRadians <= this.North + this.Epsilon; + } + + internal bool TryInterpolate(double longitudeRadians, double latitudeRadians, out double eastMmPerYear, out double northMmPerYear) + { + eastMmPerYear = 0d; + northMmPerYear = 0d; + double normalizedLongitude = longitudeRadians - this.West; + if (normalizedLongitude + this.Epsilon < 0d) + { + normalizedLongitude += TwoPi; + } + else if (normalizedLongitude - this.Epsilon > this.East - this.West) + { + normalizedLongitude -= TwoPi; + } + + double normalizedLatitude = latitudeRadians - this.South; + double gridX = normalizedLongitude * this.InvResolutionX; + double gridY = normalizedLatitude * this.InvResolutionY; + int indexX = (int)Math.Floor(gridX); + int indexY = (int)Math.Floor(gridY); + double fractionX = gridX - indexX; + double fractionY = gridY - indexY; + if (!TryNormalizeInterpolationCell(this.Width, ref indexX, ref fractionX) + || !TryNormalizeInterpolationCell(this.Height, ref indexY, ref fractionY)) + { + return false; + } + + int indexX2 = indexX + 1; + int indexY2 = indexY + 1; + double xy = fractionX * fractionY; + double w00 = 1d - fractionX - fractionY + xy; + double w10 = fractionX - xy; + double w01 = fractionY - xy; + double w11 = xy; + eastMmPerYear = (this.GetEastValue(indexX, indexY) * w00) + + (this.GetEastValue(indexX2, indexY) * w10) + + (this.GetEastValue(indexX, indexY2) * w01) + + (this.GetEastValue(indexX2, indexY2) * w11); + northMmPerYear = (this.GetNorthValue(indexX, indexY) * w00) + + (this.GetNorthValue(indexX2, indexY) * w10) + + (this.GetNorthValue(indexX, indexY2) * w01) + + (this.GetNorthValue(indexX2, indexY2) * w11); + return TransformationMath.IsFinite(eastMmPerYear) && TransformationMath.IsFinite(northMmPerYear); + } + + private static int ReadInt32LittleEndian(byte[] bytes, int offset) + { + return BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(offset, sizeof(int))); + } + + private static double ReadDoubleLittleEndian(byte[] bytes, int offset) + { + long rawBits = BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(offset, sizeof(long))); + return BitConverter.Int64BitsToDouble(rawBits); + } + + private static float ReadSingleLittleEndian(byte[] bytes, int offset) + { + int rawBits = BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(offset, sizeof(int))); + Span bitStorage = stackalloc int[1]; + bitStorage[0] = rawBits; + return MemoryMarshal.Cast(bitStorage)[0]; + } + + private float GetEastValue(int x, int y) + { + return this.eastValues[(y * this.Width) + x]; + } + + private float GetNorthValue(int x, int y) + { + return this.northValues[(y * this.Width) + x]; + } + } + + private sealed class GtxGrid + { + private readonly float[] values; + + private GtxGrid( + string sourcePath, + double west, + double east, + double south, + double north, + double resolutionX, + double resolutionY, + int width, + int height, + bool fullWorldLongitude, + float[] values) + { + this.SourcePath = sourcePath; + this.West = west; + this.East = east; + this.South = south; + this.North = north; + this.ResolutionX = resolutionX; + this.ResolutionY = resolutionY; + this.Width = width; + this.Height = height; + this.FullWorldLongitude = fullWorldLongitude; + this.values = values; + this.Epsilon = (Math.Abs(resolutionX) + Math.Abs(resolutionY)) * RelativeTolerance; + this.InvResolutionX = 1d / resolutionX; + this.InvResolutionY = 1d / resolutionY; + this.Area = Math.Abs((east - west) * (north - south)); + } + + internal string SourcePath { get; } + + internal double West { get; } + + internal double East { get; } + + internal double South { get; } + + internal double North { get; } + + internal double ResolutionX { get; } + + internal double ResolutionY { get; } + + internal int Width { get; } + + internal int Height { get; } + + internal bool FullWorldLongitude { get; } + + internal double Epsilon { get; } + + internal double InvResolutionX { get; } + + internal double InvResolutionY { get; } + + internal double Area { get; } + + internal static GtxGrid Load(string path) + { + byte[] bytes = File.ReadAllBytes(path); + if (bytes.Length < 40) + { + throw new InvalidDataException("GTX file is too small."); + } + + double yOrigin = ReadDoubleBigEndian(bytes, 0); + double xOrigin = ReadDoubleBigEndian(bytes, 8); + double yStep = ReadDoubleBigEndian(bytes, 16); + double xStep = ReadDoubleBigEndian(bytes, 24); + int rows = ReadInt32BigEndian(bytes, 32); + int columns = ReadInt32BigEndian(bytes, 36); + if (columns <= 0 + || rows <= 0 + || xOrigin < -360d + || xOrigin > 360d + || yOrigin < -90d + || yOrigin > 90d) + { + throw new InvalidDataException("GTX header contains invalid extents."); + } + + if (xOrigin >= 180d) + { + xOrigin -= 360d; + } + + if (xStep == 0d || yStep == 0d) + { + throw new InvalidDataException("GTX header contains invalid resolution."); + } + + long expectedDataBytes = (long)rows * columns * sizeof(float); + if (bytes.Length < 40 + expectedDataBytes) + { + throw new InvalidDataException("GTX data is truncated."); + } + + float[] values = new float[rows * columns]; + int offset = 40; + for (int i = 0; i < values.Length; i++) + { + values[i] = ReadSingleBigEndian(bytes, offset); + offset += sizeof(float); + } + + double west = DegreesToRadians(xOrigin); + double south = DegreesToRadians(yOrigin); + double resolutionX = DegreesToRadians(xStep); + double resolutionY = DegreesToRadians(yStep); + double east = west + (resolutionX * (columns - 1)); + double north = south + (resolutionY * (rows - 1)); + double worldWidth = Math.Abs(resolutionX) * columns; + bool fullWorldLongitude = Math.Abs(worldWidth - TwoPi) <= (Math.Abs(resolutionX) * 1e-4d); + return new GtxGrid(path, west, east, south, north, resolutionX, resolutionY, columns, rows, fullWorldLongitude, values); + } + + internal bool Contains(double longitudeRadians, double latitudeRadians) + { + double lon = longitudeRadians; + if (lon < this.West - this.Epsilon) + { + lon += TwoPi; + } + else if (lon > this.East + this.Epsilon) + { + lon -= TwoPi; + } + + return lon >= this.West - this.Epsilon + && lon <= this.East + this.Epsilon + && latitudeRadians >= this.South - this.Epsilon + && latitudeRadians <= this.North + this.Epsilon; + } + + internal bool TryInterpolate(double longitudeRadians, double latitudeRadians, out double valueMmPerYear) + { + valueMmPerYear = 0d; + double gridX = (longitudeRadians - this.West) * this.InvResolutionX; + if (longitudeRadians < this.West) + { + if (this.FullWorldLongitude) + { + gridX = PositiveModulo(gridX, this.Width); + } + else + { + gridX = (longitudeRadians + TwoPi - this.West) * this.InvResolutionX; + } + } + else if (longitudeRadians > this.East) + { + if (this.FullWorldLongitude) + { + gridX = PositiveModulo(gridX, this.Width); + } + else + { + gridX = (longitudeRadians - TwoPi - this.West) * this.InvResolutionX; + } + } + + double gridY = (latitudeRadians - this.South) * this.InvResolutionY; + int gridIx = (int)Math.Floor(gridX); + int gridIy = (int)Math.Floor(gridY); + if (gridIx < 0 || gridIx >= this.Width || gridIy < 0 || gridIy >= this.Height) + { + return false; + } + + double fractionX = gridX - gridIx; + double fractionY = gridY - gridIy; + int gridIx2 = gridIx + 1; + if (gridIx2 >= this.Width) + { + gridIx2 = this.FullWorldLongitude ? 0 : this.Width - 1; + } + + int gridIy2 = gridIy + 1; + if (gridIy2 >= this.Height) + { + gridIy2 = this.Height - 1; + } + + float valueA = this.GetValue(gridIx, gridIy); + float valueB = this.GetValue(gridIx2, gridIy); + float valueC = this.GetValue(gridIx, gridIy2); + float valueD = this.GetValue(gridIx2, gridIy2); + + double gridXy = fractionX * fractionY; + double weightA = 1d - fractionX - fractionY + gridXy; + double weightB = fractionX - gridXy; + double weightC = fractionY - gridXy; + double weightD = gridXy; + + bool aValid = !IsNoData(valueA, 1d); + bool bValid = !IsNoData(valueB, 1d); + bool cValid = !IsNoData(valueC, 1d); + bool dValid = !IsNoData(valueD, 1d); + int validCount = (aValid ? 1 : 0) + (bValid ? 1 : 0) + (cValid ? 1 : 0) + (dValid ? 1 : 0); + if (validCount == 0) + { + return false; + } + + if (validCount == 4) + { + valueMmPerYear = (valueA * weightA) + (valueB * weightB) + (valueC * weightC) + (valueD * weightD); + return TransformationMath.IsFinite(valueMmPerYear); + } + + double weightedValue = 0d; + double totalWeight = 0d; + if (aValid) + { + weightedValue += valueA * weightA; + totalWeight += weightA; + } + + if (bValid) + { + weightedValue += valueB * weightB; + totalWeight += weightB; + } + + if (cValid) + { + weightedValue += valueC * weightC; + totalWeight += weightC; + } + + if (dValid) + { + weightedValue += valueD * weightD; + totalWeight += weightD; + } + + if (totalWeight == 0d) + { + return false; + } + + valueMmPerYear = weightedValue / totalWeight; + return TransformationMath.IsFinite(valueMmPerYear); + } + + private static bool IsNoData(float value, double multiplier) + { + double scaled = value * multiplier; + return scaled > 1000d || scaled < -1000d || value == TransformationMath.GtxNoDataSentinel; + } + + private static double PositiveModulo(double value, int modulus) + { + if (modulus <= 0) + { + return value; + } + + double result = value % modulus; + if (result < 0d) + { + result += modulus; + } + + return result; + } + + private static int ReadInt32BigEndian(byte[] bytes, int offset) + { + return BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(offset, sizeof(int))); + } + + private static double ReadDoubleBigEndian(byte[] bytes, int offset) + { + long rawBits = BinaryPrimitives.ReadInt64BigEndian(bytes.AsSpan(offset, sizeof(long))); + return BitConverter.Int64BitsToDouble(rawBits); + } + + private static float ReadSingleBigEndian(byte[] bytes, int offset) + { + int rawBits = BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(offset, sizeof(int))); + Span bitStorage = stackalloc int[1]; + bitStorage[0] = rawBits; + return MemoryMarshal.Cast(bitStorage)[0]; + } + + private float GetValue(int x, int y) + { + return this.values[(y * this.Width) + x]; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/DeformationMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/DeformationMathTransform.cs new file mode 100644 index 00000000..c63b5c65 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/DeformationMathTransform.cs @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using ProjNet.CoordinateSystems; + +/// +/// Implements PROJ's deformation runtime transform. +/// +/// +/// +/// This implementation follows PROJ's deformation operation for +/// time-dependent datum correction: ENU velocities are interpolated from GeoTIFF +/// XYZ grids or legacy CTable2/GTX grids, converted to geocentric XYZ shift +/// components, and then applied in cartesian space as (t_obs - t_c) * V. +/// +/// +/// The runtime was independently verified against PROJ's published +/// deformation documentation and deformation.cpp. The reviewed +/// inverse implementation preserves the corrected Newton-style residual update in +/// all three components, including Z, so the reverse path converges to the same +/// fixed point as the upstream algorithm. +/// +/// +/// PROJ: deformation. +internal sealed partial class DeformationMathTransform : MathTransform +{ + private const double RelativeTolerance = 1e-5d; + + private const double InverseTolerance = 1e-8d; + private const double TwoPi = 2d * Math.PI; + + private readonly ReadOnlyCollection velocityGrids; + private readonly ReadOnlyCollection horizontalGrids; + private readonly ReadOnlyCollection verticalGrids; + private readonly bool hasFixedDt; + private readonly double fixedDt; + private readonly double tEpoch; + private readonly double semiMajor; + private readonly double semiMinor; + private readonly GeocentricTransform geocentricForward; + private readonly GeocentricTransform geocentricInverse; + + private readonly bool isInverted; + private MathTransform? inverse; + + private DeformationMathTransform( + IReadOnlyList velocityGrids, + IReadOnlyList horizontalGrids, + IReadOnlyList verticalGrids, + bool hasFixedDt, + double fixedDt, + double tEpoch, + double semiMajor, + double semiMinor, + bool isInverted) + { + this.velocityGrids = new ReadOnlyCollection( + [.. velocityGrids ?? []]); + this.horizontalGrids = new ReadOnlyCollection([.. horizontalGrids ?? []]); + this.verticalGrids = new ReadOnlyCollection([.. verticalGrids ?? []]); + this.hasFixedDt = hasFixedDt; + this.fixedDt = fixedDt; + this.tEpoch = tEpoch; + this.semiMajor = semiMajor; + this.semiMinor = semiMinor; + this.isInverted = isInverted; + + var parameters = new List + { + new("semi_major", semiMajor), + new("semi_minor", semiMinor), + }; + this.geocentricForward = new GeocentricTransform(parameters, false); + this.geocentricInverse = (GeocentricTransform)this.geocentricForward.Inverse(); + } + + private DeformationMathTransform(DeformationMathTransform source, bool isInverted) + { + this.velocityGrids = source.velocityGrids; + this.horizontalGrids = source.horizontalGrids; + this.verticalGrids = source.verticalGrids; + this.hasFixedDt = source.hasFixedDt; + this.fixedDt = source.fixedDt; + this.tEpoch = source.tEpoch; + this.semiMajor = source.semiMajor; + this.semiMinor = source.semiMinor; + this.geocentricForward = source.geocentricForward; + this.geocentricInverse = source.geocentricInverse; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() => false; + + /// + public override MathTransform Inverse() + { + return this.inverse ??= new DeformationMathTransform(this, !this.isInverted); + } + + /// + public override void Invert() + { + throw new NotSupportedException("DeformationMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (!this.TryResolveDeltaTime(TransformationMath.MissingObservationEpoch, out double deltaTime, out bool missingTime)) + { + if (missingTime) + { + TransformationThrowHelper.ThrowNotSupported("deformation requires a valid observation time."); + } + + TransformationThrowHelper.ThrowInvalidOperation("deformation could not resolve delta time."); + } + + this.TransformCore(ref x, ref y, ref z, deltaTime); + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + if (args is null) + { + skipReason = "deformation arguments were null."; + return false; + } + + bool hasGenericGrids = args.TryGetValue("grids", out string? genericGridToken) && !string.IsNullOrWhiteSpace(genericGridToken); + bool hasHorizontalGrids = args.TryGetValue("xy_grids", out string? horizontalGridToken) && !string.IsNullOrWhiteSpace(horizontalGridToken); + bool hasVerticalGrids = args.TryGetValue("z_grids", out string? verticalGridToken) && !string.IsNullOrWhiteSpace(verticalGridToken); + if (!hasGenericGrids && (!hasHorizontalGrids || !hasVerticalGrids)) + { + skipReason = "deformation requires either +grids or (+xy_grids and +z_grids)."; + return false; + } + + if (hasGenericGrids && (hasHorizontalGrids || hasVerticalGrids)) + { + skipReason = "+grids is mutually exclusive with +xy_grids/+z_grids for deformation."; + return false; + } + + if (args.ContainsKey("t_obs")) + { + skipReason = "+t_obs parameter is deprecated. Use +dt instead."; + return false; + } + + bool hasDt = args.TryGetValue("dt", out string? dtToken); + bool hasEpoch = args.TryGetValue("t_epoch", out string? epochToken); + if (!hasDt && !hasEpoch) + { + skipReason = "deformation requires +dt or +t_epoch."; + return false; + } + + if (hasDt && hasEpoch) + { + skipReason = "+dt and +t_epoch are mutually exclusive for deformation."; + return false; + } + + bool useFixedDt = false; + double fixedDt = 0d; + double tEpoch = 0d; + if (hasDt) + { + string dtValue = dtToken ?? string.Empty; + if (!SpanParseUtility.TryParseFiniteDouble(dtValue, out fixedDt)) + { + skipReason = "Unable to parse +dt parameter for deformation."; + return false; + } + + useFixedDt = true; + } + else + { + string epochValue = epochToken ?? string.Empty; + if (!SpanParseUtility.TryParseFiniteDouble(epochValue, out tEpoch)) + { + skipReason = "Unable to parse +t_epoch parameter for deformation."; + return false; + } + } + + if (!ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + operationName: "deformation", + allowClarke1880Ign: true, + allowBessel: false, + out double semiMajor, + out double semiMinor, + out skipReason)) + { + return false; + } + + try + { + var velocityGrids = new List(); + var horizontalGrids = new List(); + var verticalGrids = new List(); + + if (hasGenericGrids) + { + string genericGridTokenValue = ArgumentGuard.ThrowIfNull(genericGridToken, nameof(genericGridToken)); + if (!TryResolveGridPaths(genericGridTokenValue, "grids", out IReadOnlyList genericGridPaths, out skipReason)) + { + return false; + } + + for (int i = 0; i < genericGridPaths.Count; i++) + { + string extension = Path.GetExtension(genericGridPaths[i]); + if (!extension.Equals(".tif", StringComparison.OrdinalIgnoreCase) + && !extension.Equals(".tiff", StringComparison.OrdinalIgnoreCase)) + { + skipReason = $"Grid '{Path.GetFileName(genericGridPaths[i])}' is not a supported deformation velocity grid format (.tif/.tiff)."; + return false; + } + + velocityGrids.AddRange(GeoTiffGridLoader.LoadXyz(genericGridPaths[i], requireMetreUnits: false)); + } + + if (velocityGrids.Count == 0) + { + skipReason = "No velocity grid could be loaded from +grids."; + return false; + } + + velocityGrids.Sort(static (left, right) => left.Area.CompareTo(right.Area)); + } + else + { + string horizontalGridTokenValue = ArgumentGuard.ThrowIfNull(horizontalGridToken, nameof(horizontalGridToken)); + string verticalGridTokenValue = ArgumentGuard.ThrowIfNull(verticalGridToken, nameof(verticalGridToken)); + if (!TryResolveGridPaths(horizontalGridTokenValue, "xy_grids", out IReadOnlyList horizontalGridPaths, out skipReason)) + { + return false; + } + + if (!TryResolveGridPaths(verticalGridTokenValue, "z_grids", out IReadOnlyList verticalGridPaths, out skipReason)) + { + return false; + } + + for (int i = 0; i < horizontalGridPaths.Count; i++) + { + horizontalGrids.Add(CTable2Grid.Load(horizontalGridPaths[i])); + } + + for (int i = 0; i < verticalGridPaths.Count; i++) + { + verticalGrids.Add(GtxGrid.Load(verticalGridPaths[i])); + } + + if (horizontalGrids.Count == 0 || verticalGrids.Count == 0) + { + skipReason = "deformation requires both horizontal and vertical velocity grids."; + return false; + } + + horizontalGrids.Sort(static (left, right) => left.Area.CompareTo(right.Area)); + verticalGrids.Sort(static (left, right) => left.Area.CompareTo(right.Area)); + } + + transform = new DeformationMathTransform( + velocityGrids, + horizontalGrids, + verticalGrids, + useFixedDt, + fixedDt, + tEpoch, + semiMajor, + semiMinor, + isInverted: false); + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + catch (IOException exception) + { + skipReason = $"Unable to read deformation grid: {exception.Message}"; + return false; + } + catch (UnauthorizedAccessException exception) + { + skipReason = $"Unable to read deformation grid: {exception.Message}"; + return false; + } + catch (InvalidDataException exception) + { + skipReason = $"Invalid deformation grid: {exception.Message}"; + return false; + } + catch (ArgumentException exception) + { + skipReason = $"Invalid deformation configuration: {exception.Message}"; + return false; + } + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + if (!this.TryResolveDeltaTime(t, out double deltaTime, out bool missingTime)) + { + if (missingTime) + { + ArgumentGuard.ThrowArgument("deformation requires a valid observation time.", nameof(t)); + } + + TransformationThrowHelper.ThrowInvalidOperation("deformation could not resolve delta time."); + } + + this.TransformCore(ref x, ref y, ref z, deltaTime); + } + + private static bool TryResolveGridPaths( + string gridsToken, + string parameterName, + out IReadOnlyList resolvedPaths, + out string? skipReason) + { + resolvedPaths = []; + skipReason = null; + if (string.IsNullOrWhiteSpace(gridsToken)) + { + skipReason = $"deformation requires +{parameterName}."; + return false; + } + + string[] entries = gridsToken.Split([','], StringSplitOptions.RemoveEmptyEntries); + if (entries.Length == 0) + { + skipReason = $"deformation requires at least one grid in +{parameterName}."; + return false; + } + + var resolved = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + { + string token = entries[i].Trim(); + if (string.IsNullOrWhiteSpace(token)) + { + continue; + } + + bool isOptional = token[0] == '@'; + string gridName = isOptional ? token[1..] : token; + if (string.IsNullOrWhiteSpace(gridName)) + { + continue; + } + + if (TryResolveGridPath(gridName, out string? resolvedPathCandidate)) + { + resolved.Add(ArgumentGuard.ThrowIfNull(resolvedPathCandidate, nameof(resolvedPathCandidate))); + continue; + } + + if (!isOptional) + { + skipReason = $"Required grid '{gridName}' was not found."; + return false; + } + } + + if (resolved.Count == 0) + { + skipReason = $"No grid from +{parameterName} could be resolved."; + return false; + } + + resolvedPaths = resolved; + return true; + } + + private static bool TryResolveGridPath(string gridToken, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(gridToken)) + { + return false; + } + + string normalized = NormalizePathToken(gridToken); + if (TryGetExistingPath(normalized, out resolvedPath)) + { + return true; + } + + if (TryGetExistingPath(Path.Combine(AppContext.BaseDirectory, normalized), out resolvedPath)) + { + return true; + } + + string fileName = Path.GetFileName(normalized); + if (!string.IsNullOrWhiteSpace(fileName)) + { + if (TryGetExistingPath(Path.Combine(AppContext.BaseDirectory, "Fixtures", "grids", fileName), out resolvedPath)) + { + return true; + } + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(gridToken, out resolvedPath)) + { + return true; + } + + if (!string.Equals(normalized, gridToken, StringComparison.Ordinal) + && CoordinateTransformationFactory.TryResolveGridResourcePath(normalized, out resolvedPath)) + { + return true; + } + + return !string.IsNullOrWhiteSpace(fileName) + && CoordinateTransformationFactory.TryResolveGridResourcePath(fileName, out resolvedPath); + } + + private static bool TryGetExistingPath(string candidate, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(candidate)) + { + return false; + } + + try + { + string fullPath = Path.IsPathRooted(candidate) + ? candidate + : Path.GetFullPath(candidate); + if (!File.Exists(fullPath)) + { + return false; + } + + resolvedPath = fullPath; + return true; + } + catch (ArgumentException) + { + return false; + } + catch (NotSupportedException) + { + return false; + } + catch (PathTooLongException) + { + return false; + } + catch (System.Security.SecurityException) + { + return false; + } + } + + private static string NormalizePathToken(string token) + { + return token.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + + private static bool TryNormalizeInterpolationCell(int size, ref int index, ref double fraction) + { + if (index < 0) + { + if (index == -1 && fraction > 1d - (10d * RelativeTolerance)) + { + index = 0; + fraction = 0d; + return true; + } + + return false; + } + + if (index + 1 < size) + { + return true; + } + + if (index + 1 == size && fraction < 10d * RelativeTolerance) + { + index = size - 2; + fraction = 1d; + return true; + } + + return false; + } + + private static bool TryGetInterpolationCell( + BaseGeoGrid grid, + double longitudeDegrees, + double latitudeDegrees, + out InterpolationCell cell) + { + cell = default; + if (!grid.TryMapToGridCoordinates(longitudeDegrees, latitudeDegrees, out double gridX, out double gridY)) + { + return false; + } + + int x0 = (int)Math.Floor(gridX); + int y0 = (int)Math.Floor(gridY); + double fractionX = gridX - x0; + double fractionY = gridY - y0; + if (!TryNormalizeInterpolationCell(grid.Width, ref x0, ref fractionX) + || !TryNormalizeInterpolationCell(grid.Height, ref y0, ref fractionY)) + { + return false; + } + + int x1 = x0 + 1; + int y1 = y0 + 1; + double xy = fractionX * fractionY; + cell = new InterpolationCell( + x0, + y0, + x1, + y1, + 1d - fractionX - fractionY + xy, + fractionY - xy, + fractionX - xy, + xy); + return true; + } + + private static double Bilinear(double value00, double value01, double value10, double value11, InterpolationCell cell) + { + return (value00 * cell.W00) + + (value01 * cell.W01) + + (value10 * cell.W10) + + (value11 * cell.W11); + } + + private static bool TryFindXyzGrid( + ReadOnlyCollection grids, + double longitudeDegrees, + double latitudeDegrees, + [NotNullWhen(true)] out GeoTiffXyzGridShiftMathTransform.XyzGrid? grid) + { + for (int i = 0; i < grids.Count; i++) + { + if (grids[i].Contains(longitudeDegrees, latitudeDegrees)) + { + grid = grids[i]; + return true; + } + } + + grid = null; + return false; + } + + private static bool TryFindHorizontalGrid( + IReadOnlyList grids, + double longitudeRadians, + double latitudeRadians, + [NotNullWhen(true)] out CTable2Grid? grid) + { + for (int i = 0; i < grids.Count; i++) + { + if (grids[i].Contains(longitudeRadians, latitudeRadians)) + { + grid = grids[i]; + return true; + } + } + + grid = null; + return false; + } + + private static bool TryFindVerticalGrid( + IReadOnlyList grids, + double longitudeRadians, + double latitudeRadians, + [NotNullWhen(true)] out GtxGrid? grid) + { + for (int i = 0; i < grids.Count; i++) + { + if (grids[i].Contains(longitudeRadians, latitudeRadians)) + { + grid = grids[i]; + return true; + } + } + + grid = null; + return false; + } + + private static void ConvertEnuToCartesianShift( + double longitudeDegrees, + double latitudeDegrees, + double eastVelocity, + double northVelocity, + double upVelocity, + out double xShift, + out double yShift, + out double zShift) + { + double longitudeRadians = DegreesToRadians(longitudeDegrees); + double latitudeRadians = DegreesToRadians(latitudeDegrees); + double sinPhi = Math.Sin(latitudeRadians); + double cosPhi = Math.Cos(latitudeRadians); + double sinLambda = Math.Sin(longitudeRadians); + double cosLambda = Math.Cos(longitudeRadians); + + xShift = (-sinPhi * cosLambda * northVelocity) - (sinLambda * eastVelocity) + (cosPhi * cosLambda * upVelocity); + yShift = (-sinPhi * sinLambda * northVelocity) + (cosLambda * eastVelocity) + (cosPhi * sinLambda * upVelocity); + zShift = (cosPhi * northVelocity) + (sinPhi * upVelocity); + } + + private void TransformCore(ref double x, ref double y, ref double z, double deltaTime) + { + if (!this.isInverted) + { + if (!this.TryGetGridShift(x, y, z, out double shiftX, out double shiftY, out double shiftZ)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside deformation model grid extent."); + } + + x += deltaTime * shiftX; + y += deltaTime * shiftY; + z += deltaTime * shiftZ; + return; + } + + if (!this.TryReverseShift(x, y, z, deltaTime, out double xOut, out double yOut, out double zOut)) + { + TransformationThrowHelper.ThrowInvalidOperation("deformation inverse transformation failed."); + } + + x = xOut; + y = yOut; + z = zOut; + } + + private bool TryResolveDeltaTime(double observationEpoch, out double deltaTime, out bool missingTime) + { + if (this.hasFixedDt) + { + missingTime = false; + deltaTime = this.fixedDt; + return true; + } + + if (!TransformationMath.IsValidObservationEpoch(observationEpoch, TransformationMath.MissingObservationEpoch)) + { + missingTime = true; + deltaTime = 0d; + return false; + } + + missingTime = false; + deltaTime = observationEpoch - this.tEpoch; + return true; + } + + private bool TryGetGridShift(double x, double y, double z, out double shiftX, out double shiftY, out double shiftZ) + { + shiftX = 0d; + shiftY = 0d; + shiftZ = 0d; + if (!TransformationMath.IsFinite(x) || !TransformationMath.IsFinite(y) || !TransformationMath.IsFinite(z)) + { + return false; + } + + double longitudeDegrees = x; + double latitudeDegrees = y; + double height = z; + this.geocentricInverse.Transform(ref longitudeDegrees, ref latitudeDegrees, ref height); + if (!TransformationMath.IsFinite(longitudeDegrees) || !TransformationMath.IsFinite(latitudeDegrees)) + { + return false; + } + + double eastVelocity; + double northVelocity; + double upVelocity; + if (this.velocityGrids.Count > 0) + { + if (!this.TryInterpolateGeoTiffVelocity(longitudeDegrees, latitudeDegrees, out eastVelocity, out northVelocity, out upVelocity)) + { + return false; + } + } + else + { + if (!this.TryInterpolateLegacyVelocity( + DegreesToRadians(longitudeDegrees), + DegreesToRadians(latitudeDegrees), + out eastVelocity, + out northVelocity, + out upVelocity)) + { + return false; + } + } + + ConvertEnuToCartesianShift( + longitudeDegrees, + latitudeDegrees, + eastVelocity, + northVelocity, + upVelocity, + out shiftX, + out shiftY, + out shiftZ); + return TransformationMath.IsFinite(shiftX) && TransformationMath.IsFinite(shiftY) && TransformationMath.IsFinite(shiftZ); + } + + private bool TryInterpolateGeoTiffVelocity( + double longitudeDegrees, + double latitudeDegrees, + out double eastVelocity, + out double northVelocity, + out double upVelocity) + { + eastVelocity = 0d; + northVelocity = 0d; + upVelocity = 0d; + if (!TryFindXyzGrid(this.velocityGrids, longitudeDegrees, latitudeDegrees, out GeoTiffXyzGridShiftMathTransform.XyzGrid? gridCandidate)) + { + return false; + } + + GeoTiffXyzGridShiftMathTransform.XyzGrid grid = ArgumentGuard.ThrowIfNull(gridCandidate, nameof(gridCandidate)); + if (!TryGetInterpolationCell(grid, longitudeDegrees, latitudeDegrees, out InterpolationCell cell)) + { + return false; + } + + double east00 = grid.GetXShift(cell.X0, cell.Y0); + double east01 = grid.GetXShift(cell.X0, cell.Y1); + double east10 = grid.GetXShift(cell.X1, cell.Y0); + double east11 = grid.GetXShift(cell.X1, cell.Y1); + double north00 = grid.GetYShift(cell.X0, cell.Y0); + double north01 = grid.GetYShift(cell.X0, cell.Y1); + double north10 = grid.GetYShift(cell.X1, cell.Y0); + double north11 = grid.GetYShift(cell.X1, cell.Y1); + double up00 = grid.GetZShift(cell.X0, cell.Y0); + double up01 = grid.GetZShift(cell.X0, cell.Y1); + double up10 = grid.GetZShift(cell.X1, cell.Y0); + double up11 = grid.GetZShift(cell.X1, cell.Y1); + if (!TransformationMath.IsFinite(east00) + || !TransformationMath.IsFinite(east01) + || !TransformationMath.IsFinite(east10) + || !TransformationMath.IsFinite(east11) + || !TransformationMath.IsFinite(north00) + || !TransformationMath.IsFinite(north01) + || !TransformationMath.IsFinite(north10) + || !TransformationMath.IsFinite(north11) + || !TransformationMath.IsFinite(up00) + || !TransformationMath.IsFinite(up01) + || !TransformationMath.IsFinite(up10) + || !TransformationMath.IsFinite(up11)) + { + return false; + } + + eastVelocity = Bilinear(east00, east01, east10, east11, cell) / 1000d; + northVelocity = Bilinear(north00, north01, north10, north11, cell) / 1000d; + upVelocity = Bilinear(up00, up01, up10, up11, cell) / 1000d; + return TransformationMath.IsFinite(eastVelocity) && TransformationMath.IsFinite(northVelocity) && TransformationMath.IsFinite(upVelocity); + } + + private bool TryInterpolateLegacyVelocity( + double longitudeRadians, + double latitudeRadians, + out double eastVelocity, + out double northVelocity, + out double upVelocity) + { + eastVelocity = 0d; + northVelocity = 0d; + upVelocity = 0d; + if (!TryFindHorizontalGrid(this.horizontalGrids, longitudeRadians, latitudeRadians, out CTable2Grid? horizontalGridCandidate)) + { + return false; + } + + CTable2Grid horizontalGrid = ArgumentGuard.ThrowIfNull(horizontalGridCandidate, nameof(horizontalGridCandidate)); + if (!horizontalGrid.TryInterpolate(longitudeRadians, latitudeRadians, out double eastMmPerYear, out double northMmPerYear)) + { + return false; + } + + if (!TryFindVerticalGrid(this.verticalGrids, longitudeRadians, latitudeRadians, out GtxGrid? verticalGridCandidate)) + { + return false; + } + + GtxGrid verticalGrid = ArgumentGuard.ThrowIfNull(verticalGridCandidate, nameof(verticalGridCandidate)); + if (!verticalGrid.TryInterpolate(longitudeRadians, latitudeRadians, out double upMmPerYear)) + { + return false; + } + + eastVelocity = eastMmPerYear / 1000d; + northVelocity = northMmPerYear / 1000d; + upVelocity = upMmPerYear / 1000d; + return TransformationMath.IsFinite(eastVelocity) && TransformationMath.IsFinite(northVelocity) && TransformationMath.IsFinite(upVelocity); + } + + private bool TryReverseShift( + double inputX, + double inputY, + double inputZ, + double deltaTime, + out double outputX, + out double outputY, + out double outputZ) + { + outputX = inputX; + outputY = inputY; + outputZ = inputZ; + if (!this.TryGetGridShift(inputX, inputY, inputZ, out double firstDeltaX, out double firstDeltaY, out double firstDeltaZ)) + { + return false; + } + + outputX = inputX - (deltaTime * firstDeltaX); + outputY = inputY - (deltaTime * firstDeltaY); + outputZ = inputZ - (deltaTime * firstDeltaZ); + + for (int i = 0; i < TransformationMath.MaxInverseIterations; i++) + { + if (!this.TryGetGridShift(outputX, outputY, outputZ, out double deltaX, out double deltaY, out double deltaZ)) + { + return false; + } + + double differenceX = outputX + (deltaTime * deltaX) - inputX; + double differenceY = outputY + (deltaTime * deltaY) - inputY; + double differenceZ = outputZ + (deltaTime * deltaZ) - inputZ; + outputX -= differenceX; + outputY -= differenceY; + outputZ -= differenceZ; + + if (Math.Sqrt((differenceX * differenceX) + (differenceY * differenceY)) <= InverseTolerance) + { + break; + } + } + + return true; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/DomainFlags.cs b/src/ProjNet/CoordinateSystems/Transformations/DomainFlags.cs index 00f66fa5..fb9d7375 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/DomainFlags.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/DomainFlags.cs @@ -1,53 +1,40 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// Flags indicating parts of domain covered by a convex hull. +/// +/// +/// These flags can be combined. For example, the value 3 +/// corresponds to a combination of and , +/// which means that some parts of the convex hull are inside the +/// domain, and some parts of the convex hull are outside the domain. +/// +[System.Flags] +public enum DomainFlags : int { /// - /// Flags indicating parts of domain covered by a convex hull. + /// At least one point in a convex hull is inside the transform's domain. /// - /// - /// These flags can be combined. For example, the value 3 - /// corresponds to a combination of and , - /// which means that some parts of the convex hull are inside the - /// domain, and some parts of the convex hull are outside the domain. - /// - public enum DomainFlags : int - { - /// - /// At least one point in a convex hull is inside the transform's domain. - /// - Inside = 1, + Inside = 1, - /// - /// At least one point in a convex hull is outside the transform's domain. - /// - Outside = 2, + /// + /// At least one point in a convex hull is outside the transform's domain. + /// + Outside = 2, - /// - /// At least one point in a convex hull is not transformed continuously. - /// - /// - /// As an example, consider a "Longitude_Rotation" transform which adjusts - /// longitude coordinates to take account of a change in Prime Meridian. If - /// the rotation is 5 degrees east, then the point (Lat=175,Lon=0) is not - /// transformed continuously, since it is on the meridian line which will - /// be split at +180/-180 degrees. - /// - Discontinuous = 4 - } + /// + /// At least one point in a convex hull is not transformed continuously. + /// + /// + /// As an example, consider a "Longitude_Rotation" transform which adjusts + /// longitude coordinates to take account of a change in Prime Meridian. If + /// the rotation is 5 degrees east, then the point (Lat=175,Lon=0) is not + /// transformed continuously, since it is on the meridian line which will + /// be split at +180/-180 degrees. + /// + Discontinuous = 4, } diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeoTiffGridLoader.Types.cs b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffGridLoader.Types.cs new file mode 100644 index 00000000..3bcf3257 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffGridLoader.Types.cs @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; + +/// +/// Nested metadata and page-model types for the GeoTIFF grid loader. +/// +internal static partial class GeoTiffGridLoader +{ + private enum GridMode + { + Horizontal, + Vertical, + Xyz, + } + + private readonly struct GeoTransform( + int width, + int height, + double a, + double b, + double c, + double d, + double e, + double f, + double determinant, + double west, + double east, + double south, + double north, + double area, + double epsilon) + { + internal int Width { get; } = width; + + internal int Height { get; } = height; + + internal double A { get; } = a; + + internal double B { get; } = b; + + internal double C { get; } = c; + + internal double D { get; } = d; + + internal double E { get; } = e; + + internal double F { get; } = f; + + internal double Determinant { get; } = determinant; + + internal double West { get; } = west; + + internal double East { get; } = east; + + internal double South { get; } = south; + + internal double North { get; } = north; + + internal double Area { get; } = area; + + internal double Epsilon { get; } = epsilon; + } + + private readonly struct GeoMetadata( + IReadOnlyDictionary descriptionsBySample, + IReadOnlyDictionary positiveValueBySample, + IReadOnlyDictionary scaleBySample, + IReadOnlyDictionary offsetBySample, + double? noDataValue, + double angularScaleToDegree, + IReadOnlyDictionary unitTypeBySample, + bool useBiquadraticInterpolation) + { + internal IReadOnlyDictionary DescriptionsBySample { get; } = descriptionsBySample; + + internal IReadOnlyDictionary PositiveValueBySample { get; } = positiveValueBySample; + + internal IReadOnlyDictionary ScaleBySample { get; } = scaleBySample; + + internal IReadOnlyDictionary OffsetBySample { get; } = offsetBySample; + + internal double? NoDataValue { get; } = noDataValue; + + internal double AngularScaleToDegree { get; } = angularScaleToDegree; + + internal IReadOnlyDictionary UnitTypeBySample { get; } = unitTypeBySample; + + internal bool UseBiquadraticInterpolation { get; } = useBiquadraticInterpolation; + } + + private sealed class LoadedPage + { + private readonly GeoTransform transform; + private readonly SampleData sampleData; + private readonly GeoMetadata metadata; + private readonly int latitudeSample; + private readonly int longitudeSample; + private readonly int verticalSample; + private readonly int xSample; + private readonly int ySample; + private readonly int zSample; + private readonly bool longitudePositiveWest; + private readonly bool projectedOffsets; + private readonly GridMode mode; + private readonly bool useBiquadraticInterpolation; + + private LoadedPage( + GeoTransform transform, + SampleData sampleData, + GeoMetadata metadata, + int latitudeSample, + int longitudeSample, + int verticalSample, + int xSample, + int ySample, + int zSample, + bool longitudePositiveWest, + bool projectedOffsets, + GridMode mode, + bool useBiquadraticInterpolation) + { + this.transform = transform; + this.sampleData = sampleData; + this.metadata = metadata; + this.latitudeSample = latitudeSample; + this.longitudeSample = longitudeSample; + this.verticalSample = verticalSample; + this.xSample = xSample; + this.ySample = ySample; + this.zSample = zSample; + this.longitudePositiveWest = longitudePositiveWest; + this.projectedOffsets = projectedOffsets; + this.mode = mode; + this.useBiquadraticInterpolation = useBiquadraticInterpolation; + } + + internal static LoadedPage CreateHorizontal( + GeoTransform transform, + SampleData sampleData, + GeoMetadata metadata, + int latitudeSample, + int longitudeSample, + bool longitudePositiveWest, + bool projectedOffsets) + { + return new LoadedPage( + transform, + sampleData, + metadata, + latitudeSample, + longitudeSample, + -1, + -1, + -1, + -1, + longitudePositiveWest, + projectedOffsets, + GridMode.Horizontal, + metadata.UseBiquadraticInterpolation); + } + + internal static LoadedPage CreateVertical( + GeoTransform transform, + SampleData sampleData, + GeoMetadata metadata, + int verticalSample) + { + return new LoadedPage( + transform, + sampleData, + metadata, + -1, + -1, + verticalSample, + -1, + -1, + -1, + false, + false, + GridMode.Vertical, + false); + } + + internal static LoadedPage CreateXyz( + GeoTransform transform, + SampleData sampleData, + GeoMetadata metadata, + int xSample, + int ySample, + int zSample) + { + return new LoadedPage( + transform, + sampleData, + metadata, + -1, + -1, + -1, + xSample, + ySample, + zSample, + false, + false, + GridMode.Xyz, + false); + } + + internal GeoTiffHGridShiftMathTransform.HorizontalGrid? ToHorizontalGrid(string sourcePath) + { + if (this.mode != GridMode.Horizontal) + { + return null; + } + + double latitudeScale = ResolveHorizontalShiftScale(this.metadata, this.latitudeSample, this.projectedOffsets); + double longitudeScale = ResolveHorizontalShiftScale(this.metadata, this.longitudeSample, this.projectedOffsets); + return new GeoTiffHGridShiftMathTransform.HorizontalGrid( + sourcePath, + this.transform.Width, + this.transform.Height, + this.transform.Area, + this.transform.Epsilon, + this.transform.West, + this.transform.East, + this.transform.South, + this.transform.North, + this.transform.A, + this.transform.B, + this.transform.C, + this.transform.D, + this.transform.E, + this.transform.F, + this.sampleData, + this.latitudeSample, + this.longitudeSample, + this.longitudePositiveWest, + latitudeScale, + longitudeScale, + !this.projectedOffsets, + this.useBiquadraticInterpolation); + } + + internal GeoTiffVGridShiftMathTransform.VerticalGrid? ToVerticalGrid(string sourcePath) + { + return this.mode != GridMode.Vertical + ? null + : new GeoTiffVGridShiftMathTransform.VerticalGrid( + sourcePath, + this.transform.Width, + this.transform.Height, + this.transform.Area, + this.transform.Epsilon, + this.transform.West, + this.transform.East, + this.transform.South, + this.transform.North, + this.transform.A, + this.transform.B, + this.transform.C, + this.transform.D, + this.transform.E, + this.transform.F, + this.sampleData, + this.verticalSample, + this.metadata.NoDataValue); + } + + internal GeoTiffXyzGridShiftMathTransform.XyzGrid? ToXyzGrid(string sourcePath) + { + return this.mode != GridMode.Xyz + ? null + : new GeoTiffXyzGridShiftMathTransform.XyzGrid( + sourcePath, + this.transform.Width, + this.transform.Height, + this.transform.Area, + this.transform.Epsilon, + this.transform.West, + this.transform.East, + this.transform.South, + this.transform.North, + this.transform.A, + this.transform.B, + this.transform.C, + this.transform.D, + this.transform.E, + this.transform.F, + this.sampleData, + this.xSample, + this.ySample, + this.zSample); + } + + private static double ResolveHorizontalShiftScale(GeoMetadata metadata, int sampleIndex, bool projectedOffsets) + { + if (metadata.UnitTypeBySample.TryGetValue(sampleIndex, out string? unitType)) + { + string unit = unitType.Trim(); + if (unit.Equals("degree", StringComparison.OrdinalIgnoreCase) + || unit.Equals("degrees", StringComparison.OrdinalIgnoreCase) + || unit.Equals("deg", StringComparison.OrdinalIgnoreCase)) + { + return 1d; + } + + if (unit.Equals("radian", StringComparison.OrdinalIgnoreCase) + || unit.Equals("radians", StringComparison.OrdinalIgnoreCase) + || unit.Equals("rad", StringComparison.OrdinalIgnoreCase)) + { + return 180d / Math.PI; + } + + if (unit.Equals("arc-second", StringComparison.OrdinalIgnoreCase) + || unit.Equals("arc-seconds", StringComparison.OrdinalIgnoreCase) + || unit.Equals("arc_second", StringComparison.OrdinalIgnoreCase) + || unit.Equals("arc_seconds", StringComparison.OrdinalIgnoreCase) + || unit.Equals("arcsecond", StringComparison.OrdinalIgnoreCase) + || unit.Equals("arcseconds", StringComparison.OrdinalIgnoreCase) + || unit.Equals("arcsec", StringComparison.OrdinalIgnoreCase)) + { + return 1d / 3600d; + } + } + + return projectedOffsets ? 1d : 1d / 3600d; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeoTiffGridLoader.cs b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffGridLoader.cs new file mode 100644 index 00000000..13ca86ff --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffGridLoader.cs @@ -0,0 +1,892 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using BitMiracle.LibTiff.Classic; + +/// +/// Loads grid shift data from GeoTIFF files for use in coordinate transformations. +/// +/// +/// PROJ's geodetic GeoTIFF profile was used as the primary verification source +/// for this loader. The implementation accepts both affine +/// ModelTransformationTag georeferencing and the +/// ModelTiePointTag/ModelPixelScaleTag path, derives bounds from +/// the resolved raster-to-model transform, and applies the half-pixel origin +/// offset required when GTRasterTypeGeoKey indicates PixelIsArea instead +/// of PixelIsPoint. +/// +/// PROJ GeoTIFF grid specification. +internal static partial class GeoTiffGridLoader +{ + private const int ModelPixelScaleTag = 33550; + private const int ModelTiePointTag = 33922; + private const int ModelTransformationTag = 34264; + private const int GeoKeyDirectoryTag = 34735; + private const int GdalMetadataTag = 42112; + private const int GdalNoDataTag = 42113; + private const int GeogAngularUnitsGeoKey = 2054; + private const int GtRasterTypeGeoKey = 1025; + private const int RasterPixelIsPoint = 2; + +#if !NET8_0_OR_GREATER + private static readonly Regex MetadataAttributeRegex = new Regex( + "\\b(?[^\\s=]+)\\s*=\\s*\"(?[^\"]*)\"", + RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled); +#endif + + /// + /// Loads all horizontal grid shift pages from a GeoTIFF file. + /// + /// Path to the GeoTIFF grid file. + /// The list of horizontal grid shift grids parsed from the file. + internal static IReadOnlyList LoadHorizontal(string path) + { + return LoadHorizontal(path, ArrayPool.Shared); + } + + /// + /// Loads all horizontal grid shift pages from a GeoTIFF file. + /// + /// Path to the GeoTIFF grid file. + /// Array pool used for temporary sample buffers. + /// The list of horizontal grid shift grids parsed from the file. + internal static IReadOnlyList LoadHorizontal(string path, ArrayPool sampleValueArrayPool) + { + return [.. LoadCore(path, GridMode.Horizontal, requireMetreUnitsForXyz: true, sampleValueArrayPool) + .Select(page => page.ToHorizontalGrid(path)) + .OfType()]; + } + + /// + /// Loads all vertical grid shift pages from a GeoTIFF file. + /// + /// Path to the GeoTIFF grid file. + /// The list of vertical grid shift grids parsed from the file. + internal static IReadOnlyList LoadVertical(string path) + { + return LoadVertical(path, ArrayPool.Shared); + } + + /// + /// Loads all vertical grid shift pages from a GeoTIFF file. + /// + /// Path to the GeoTIFF grid file. + /// Array pool used for temporary sample buffers. + /// The list of vertical grid shift grids parsed from the file. + internal static IReadOnlyList LoadVertical(string path, ArrayPool sampleValueArrayPool) + { + return [.. LoadCore(path, GridMode.Vertical, requireMetreUnitsForXyz: true, sampleValueArrayPool) + .Select(page => page.ToVerticalGrid(path)) + .OfType()]; + } + + /// + /// Loads all XYZ grid shift pages from a GeoTIFF file, requiring metre units. + /// + /// Path to the GeoTIFF grid file. + /// The list of XYZ grid shift grids parsed from the file. + internal static IReadOnlyList LoadXyz(string path) + { + return LoadXyz(path, requireMetreUnits: true); + } + + /// + /// Loads all XYZ grid shift pages from a GeoTIFF file. + /// + /// Path to the GeoTIFF grid file. + /// When , only pages whose XYZ samples are in metres are included. + /// The list of XYZ grid shift grids parsed from the file. + internal static IReadOnlyList LoadXyz(string path, bool requireMetreUnits) + { + return [.. LoadCore(path, GridMode.Xyz, requireMetreUnits, ArrayPool.Shared) + .Select(page => page.ToXyzGrid(path)) + .OfType()]; + } + +#if NET8_0_OR_GREATER + [GeneratedRegex("[^>]*)>(?.*?)", RegexOptions.IgnoreCase | RegexOptions.Singleline)] + private static partial Regex MetadataItemRegex(); + + [GeneratedRegex("\\b(?[^\\s=]+)\\s*=\\s*\"(?[^\"]*)\"", RegexOptions.IgnoreCase | RegexOptions.Singleline)] + private static partial Regex MetadataAttributeRegex(); +#endif + + private static List LoadCore(string path, GridMode mode, bool requireMetreUnitsForXyz, ArrayPool sampleValueArrayPool) + { + if (string.IsNullOrWhiteSpace(path)) + { + ArgumentGuard.ThrowArgument("Path is required.", nameof(path)); + } + + sampleValueArrayPool = ArgumentGuard.ThrowIfNull(sampleValueArrayPool, nameof(sampleValueArrayPool)); + + var pages = new List(); + using var tiff = Tiff.Open(path, "r"); + if (tiff is null) + { + throw new InvalidDataException("Unable to open GeoTIFF grid."); + } + + short pageIndex = 0; + do + { + if (!TryReadPage(path, tiff, mode, requireMetreUnitsForXyz, sampleValueArrayPool, out LoadedPage? pageCandidate)) + { + pageIndex++; + continue; + } + + pages.Add(ArgumentGuard.ThrowIfNull(pageCandidate, nameof(pageCandidate))); + pageIndex++; + } + while (tiff.ReadDirectory()); + + return pages; + } + + private static bool TryReadPage(string path, Tiff tiff, GridMode mode, bool requireMetreUnitsForXyz, ArrayPool sampleValueArrayPool, [NotNullWhen(true)] out LoadedPage? page) + { + page = null; + if (!TryGetIntField(tiff, TiffTag.IMAGEWIDTH, out int width) + || !TryGetIntField(tiff, TiffTag.IMAGELENGTH, out int height) + || width <= 1 + || height <= 1) + { + return false; + } + + int samplesPerPixel = 1; + if (TryGetIntField(tiff, TiffTag.SAMPLESPERPIXEL, out int foundSamples)) + { + samplesPerPixel = Math.Max(1, foundSamples); + } + + if (!TryGetSampleEncoding(tiff, out SampleEncoding encoding)) + { + throw new InvalidDataException("Unsupported GeoTIFF sample encoding."); + } + + if (!TryGetGeoTransform(tiff, width, height, out GeoTransform transform)) + { + return false; + } + + SampleData sampleData = ReadSampleData(tiff, width, height, samplesPerPixel, encoding, sampleValueArrayPool); + GeoMetadata metadata = ReadMetadata(tiff, samplesPerPixel); + sampleData = sampleData.ApplyScaleOffset(metadata.ScaleBySample, metadata.OffsetBySample); + switch (mode) + { + case GridMode.Horizontal: + if (!TryResolveHorizontalSampleIndices(samplesPerPixel, metadata, out int latitudeSample, out int longitudeSample, out bool positiveWest, out bool projectedOffsets)) + { + return false; + } + + page = LoadedPage.CreateHorizontal(transform, sampleData, metadata, latitudeSample, longitudeSample, positiveWest, projectedOffsets); + return true; + case GridMode.Vertical: + if (!TryResolveVerticalSampleIndex(samplesPerPixel, metadata, out int sampleIndex)) + { + return false; + } + + page = LoadedPage.CreateVertical(transform, sampleData, metadata, sampleIndex); + return true; + case GridMode.Xyz: + if (!TryResolveXyzSampleIndices(samplesPerPixel, metadata, requireMetreUnitsForXyz, out int sampleX, out int sampleY, out int sampleZ)) + { + return false; + } + + page = LoadedPage.CreateXyz(transform, sampleData, metadata, sampleX, sampleY, sampleZ); + return true; + default: + ArgumentGuard.ThrowArgumentOutOfRange(nameof(mode), mode, "Unsupported GeoTIFF grid mode."); + return false; + } + } + + private static bool TryResolveHorizontalSampleIndices(int samplesPerPixel, GeoMetadata metadata, out int latitudeSample, out int longitudeSample, out bool positiveWest, out bool projectedOffsets) + { + latitudeSample = -1; + longitudeSample = -1; + positiveWest = false; + projectedOffsets = false; + for (int i = 0; i < samplesPerPixel; i++) + { + if (!metadata.DescriptionsBySample.TryGetValue(i, out string? description)) + { + continue; + } + + if (ContainsOrdinalIgnoreCase(description, "latitude_offset")) + { + latitudeSample = i; + } + else if (ContainsOrdinalIgnoreCase(description, "northing_offset")) + { + latitudeSample = i; + projectedOffsets = true; + } + else if (ContainsOrdinalIgnoreCase(description, "longitude_offset")) + { + longitudeSample = i; + } + else if (ContainsOrdinalIgnoreCase(description, "easting_offset")) + { + longitudeSample = i; + projectedOffsets = true; + } + } + + if (latitudeSample < 0 || longitudeSample < 0) + { + if (samplesPerPixel == 2) + { + latitudeSample = 0; + longitudeSample = 1; + } + else + { + return false; + } + } + + if (metadata.PositiveValueBySample.TryGetValue(longitudeSample, out string? positiveValue) + && positiveValue.Equals("west", StringComparison.OrdinalIgnoreCase)) + { + positiveWest = true; + } + + return true; + } + + private static bool TryResolveVerticalSampleIndex(int samplesPerPixel, GeoMetadata metadata, out int sampleIndex) + { + for (int i = 0; i < samplesPerPixel; i++) + { + if (!metadata.DescriptionsBySample.TryGetValue(i, out string? description)) + { + continue; + } + + if (ContainsOrdinalIgnoreCase(description, "geoid_undulation") + || ContainsOrdinalIgnoreCase(description, "vertical_offset")) + { + sampleIndex = i; + return true; + } + } + + sampleIndex = 0; + return samplesPerPixel >= 1; + } + + private static bool TryResolveXyzSampleIndices(int samplesPerPixel, GeoMetadata metadata, bool requireMetreUnits, out int sampleX, out int sampleY, out int sampleZ) + { + sampleX = -1; + sampleY = -1; + sampleZ = -1; + + for (int i = 0; i < samplesPerPixel; i++) + { + if (!metadata.DescriptionsBySample.TryGetValue(i, out string? description)) + { + continue; + } + + if (ContainsOrdinalIgnoreCase(description, "x_translation")) + { + sampleX = i; + } + else if (ContainsOrdinalIgnoreCase(description, "y_translation")) + { + sampleY = i; + } + else if (ContainsOrdinalIgnoreCase(description, "z_translation")) + { + sampleZ = i; + } + } + + if (sampleX < 0 || sampleY < 0 || sampleZ < 0) + { + if (samplesPerPixel >= 3) + { + sampleX = 0; + sampleY = 1; + sampleZ = 2; + } + else + { + return false; + } + } + + return requireMetreUnits + && (!IsUnitMetreOrEmpty(metadata, sampleX) + || !IsUnitMetreOrEmpty(metadata, sampleY) + || !IsUnitMetreOrEmpty(metadata, sampleZ)) + ? throw new InvalidDataException("xyzgridshift only supports unit=metre for XYZ samples.") + : true; + } + + private static bool IsUnitMetreOrEmpty(GeoMetadata metadata, int sampleIndex) + { + if (!metadata.UnitTypeBySample.TryGetValue(sampleIndex, out string? unitType)) + { + return true; + } + + string? unit = unitType?.Trim(); + return string.IsNullOrWhiteSpace(unit) || string.Equals(unit, "metre", StringComparison.OrdinalIgnoreCase) + || string.Equals(unit, "meter", StringComparison.OrdinalIgnoreCase) + || string.Equals(unit, "metres", StringComparison.OrdinalIgnoreCase) + || string.Equals(unit, "meters", StringComparison.OrdinalIgnoreCase) + || string.Equals(unit, "m", StringComparison.OrdinalIgnoreCase); + } + + private static bool ContainsOrdinalIgnoreCase(string value, string search) + { +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return value.Contains(search, StringComparison.OrdinalIgnoreCase); +#else + return value.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0; +#endif + } + + private static bool TryGetGeoTransform(Tiff tiff, int width, int height, out GeoTransform transform) + { + transform = default; + bool pixelIsPoint = IsPixelIsPoint(tiff); + double pixelOffset = pixelIsPoint ? 0d : 0.5d; + + if (TryGetDoubleArrayField(tiff, (TiffTag)ModelTransformationTag, out double[] matrix) && matrix.Length >= 16) + { + double a = matrix[0]; + double b = matrix[1]; + double c = matrix[3] + (pixelOffset * matrix[0]) + (pixelOffset * matrix[1]); + double d = matrix[4]; + double e = matrix[5]; + double f = matrix[7] + (pixelOffset * matrix[4]) + (pixelOffset * matrix[5]); + return TryCreateGeoTransform(width, height, a, b, c, d, e, f, out transform); + } + + if (!TryGetDoubleArrayField(tiff, (TiffTag)ModelPixelScaleTag, out double[] pixelScale) || pixelScale.Length < 2) + { + return false; + } + + if (!TryGetDoubleArrayField(tiff, (TiffTag)ModelTiePointTag, out double[] tiePoints) || tiePoints.Length < 6) + { + return false; + } + + double tieI = tiePoints[0]; + double tieJ = tiePoints[1]; + double tieX = tiePoints[3]; + double tieY = tiePoints[4]; + double scaleX = pixelScale[0]; + double scaleY = pixelScale[1]; + if (scaleX == 0d || scaleY == 0d) + { + return false; + } + + double aSimple = scaleX; + double bSimple = 0d; + double cSimple = tieX + ((pixelOffset - tieI) * scaleX); + double dSimple = 0d; + double eSimple = -scaleY; + double fSimple = tieY - ((pixelOffset - tieJ) * scaleY); + return TryCreateGeoTransform(width, height, aSimple, bSimple, cSimple, dSimple, eSimple, fSimple, out transform); + } + + private static bool TryCreateGeoTransform(int width, int height, double a, double b, double c, double d, double e, double f, out GeoTransform transform) + { + transform = default; + double determinant = (a * e) - (b * d); + if (Math.Abs(determinant) <= 1e-18d) + { + return false; + } + + (double west, double east, double south, double north, double area, double epsilon) = ComputeBounds(width, height, a, b, c, d, e, f); + transform = new GeoTransform(width, height, a, b, c, d, e, f, determinant, west, east, south, north, area, epsilon); + return true; + } + + private static (double West, double East, double South, double North, double Area, double Epsilon) ComputeBounds( + int width, + int height, + double a, + double b, + double c, + double d, + double e, + double f) + { + double[] xs = [0d, width - 1d, 0d, width - 1d]; + double[] ys = [0d, 0d, height - 1d, height - 1d]; + double west = double.PositiveInfinity; + double east = double.NegativeInfinity; + double south = double.PositiveInfinity; + double north = double.NegativeInfinity; + for (int i = 0; i < 4; i++) + { + double lon = (a * xs[i]) + (b * ys[i]) + c; + double lat = (d * xs[i]) + (e * ys[i]) + f; + west = Math.Min(west, lon); + east = Math.Max(east, lon); + south = Math.Min(south, lat); + north = Math.Max(north, lat); + } + + double area = Math.Max(1e-12d, (east - west) * (north - south)); + double resX = Math.Sqrt((a * a) + (d * d)); + double resY = Math.Sqrt((b * b) + (e * e)); + double epsilon = (resX + resY) * 1e-5d; + return (west, east, south, north, area, epsilon); + } + + private static SampleData ReadSampleData(Tiff tiff, int width, int height, int samplesPerPixel, SampleEncoding encoding, ArrayPool sampleValueArrayPool) + { + int scanlineSize = tiff.ScanlineSize(); + int valueCount = checked(width * height); + double[][] sampleValues = new double[samplesPerPixel][]; + + try + { + for (int i = 0; i < samplesPerPixel; i++) + { + sampleValues[i] = sampleValueArrayPool.Rent(valueCount); + } + + PlanarConfig planarConfig = PlanarConfig.CONTIG; + if (TryGetIntField(tiff, TiffTag.PLANARCONFIG, out int planarConfigValue)) + { + planarConfig = (PlanarConfig)planarConfigValue; + } + + if (planarConfig == PlanarConfig.SEPARATE && samplesPerPixel > 1) + { + for (int sample = 0; sample < samplesPerPixel; sample++) + { + byte[] buffer = new byte[scanlineSize]; + for (int row = 0; row < height; row++) + { + if (!tiff.ReadScanline(buffer, row, (short)sample)) + { + throw new InvalidDataException("Failed to read GeoTIFF scanline."); + } + + for (int column = 0; column < width; column++) + { + int offset = column * encoding.BytesPerSample; + sampleValues[sample][(row * width) + column] = encoding.ReadValue(buffer, offset); + } + } + } + } + else + { + byte[] buffer = new byte[scanlineSize]; + for (int row = 0; row < height; row++) + { + if (!tiff.ReadScanline(buffer, row)) + { + throw new InvalidDataException("Failed to read GeoTIFF scanline."); + } + + for (int column = 0; column < width; column++) + { + int pixelBase = column * encoding.BytesPerSample * samplesPerPixel; + for (int sample = 0; sample < samplesPerPixel; sample++) + { + int offset = pixelBase + (sample * encoding.BytesPerSample); + sampleValues[sample][(row * width) + column] = encoding.ReadValue(buffer, offset); + } + } + } + } + + return new SampleData(CopySampleBuffers(sampleValues, valueCount), width: width); + } + finally + { + ReturnSampleBuffers(sampleValues, sampleValueArrayPool); + } + } + + private static double[][] CopySampleBuffers(double[][] sampleValues, int valueCount) + { + double[][] copied = new double[sampleValues.Length][]; + for (int i = 0; i < sampleValues.Length; i++) + { + double[] destination = new double[valueCount]; + Array.Copy(sampleValues[i], destination, valueCount); + copied[i] = destination; + } + + return copied; + } + + private static void ReturnSampleBuffers(double[][] sampleValues, ArrayPool sampleValueArrayPool) + { + for (int i = 0; i < sampleValues.Length; i++) + { + if (sampleValues[i] is null) + { + continue; + } + + sampleValueArrayPool.Return(sampleValues[i], clearArray: false); + } + } + + private static GeoMetadata ReadMetadata(Tiff tiff, int samplesPerPixel) + { + var descriptionsBySample = new Dictionary(); + var positiveValueBySample = new Dictionary(); + var scaleBySample = new Dictionary(); + var offsetBySample = new Dictionary(); + var unitTypeBySample = new Dictionary(); + bool useBiquadraticInterpolation = false; + + if (TryGetStringField(tiff, (TiffTag)GdalMetadataTag, out string? gdalMetadataCandidate) + && !string.IsNullOrWhiteSpace(gdalMetadataCandidate)) + { + string gdalMetadata = gdalMetadataCandidate; + string sanitizedMetadata = SanitizeXmlMetadata(gdalMetadata); + ParseMetadataItems(sanitizedMetadata, samplesPerPixel, descriptionsBySample, positiveValueBySample, scaleBySample, offsetBySample, unitTypeBySample, ref useBiquadraticInterpolation); + } + + double? noDataValue = default; + if (TryGetStringField(tiff, (TiffTag)GdalNoDataTag, out string? noDataTextCandidate) + && double.TryParse( + CleanMetadataValue(noDataTextCandidate), + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, + out double parsedNoData)) + { + noDataValue = parsedNoData; + } + + double angularScaleToDegree = ResolveAngularScaleToDegree(tiff); + return new GeoMetadata(descriptionsBySample, positiveValueBySample, scaleBySample, offsetBySample, noDataValue, angularScaleToDegree, unitTypeBySample, useBiquadraticInterpolation); + } + + private static string SanitizeXmlMetadata(string metadata) + { + string sanitized = metadata.Trim('\0', '\uFEFF', ' ', '\t', '\r', '\n'); +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int firstTag = sanitized.IndexOf('<', StringComparison.Ordinal); +#else + int firstTag = sanitized.IndexOf('<'); +#endif + if (firstTag > 0) + { + sanitized = sanitized[firstTag..]; + } + + int lastTag = sanitized.LastIndexOf('>'); + if (lastTag >= 0 && lastTag + 1 < sanitized.Length) + { + sanitized = sanitized[..(lastTag + 1)]; + } + + return sanitized; + } + + private static string CleanMetadataValue(string value) + { + return value.Trim('\0', ' ', '\t', '\r', '\n'); + } + + private static void ParseMetadataItems( + string metadata, + int samplesPerPixel, + Dictionary descriptionsBySample, + Dictionary positiveValueBySample, + Dictionary scaleBySample, + Dictionary offsetBySample, + Dictionary unitTypeBySample, + ref bool useBiquadraticInterpolation) + { + if (string.IsNullOrWhiteSpace(metadata)) + { + return; + } + +#if NET8_0_OR_GREATER + MatchCollection matches = MetadataItemRegex().Matches(metadata); +#else + MatchCollection matches = Regex.Matches( + metadata, + "[^>]*)>(?.*?)", + RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled); +#endif + for (int i = 0; i < matches.Count; i++) + { + Match match = matches[i]; + Group attrsGroup = match.Groups["attrs"]; + Group valueGroup = match.Groups["value"]; + if (!attrsGroup.Success || !valueGroup.Success) + { + continue; + } + + string attrs = attrsGroup.Value; + string name = ExtractAttribute(attrs, "name"); + string value = CleanMetadataValue(valueGroup.Value); + if (name.Equals("interpolation_method", StringComparison.OrdinalIgnoreCase) + || name.Equals("recommended_interpolation_method", StringComparison.OrdinalIgnoreCase)) + { + if (value.EndsWith("biquadratic", StringComparison.OrdinalIgnoreCase)) + { + useBiquadraticInterpolation = true; + } + else if (!value.EndsWith("bilinear", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidDataException("Unsupported GeoTIFF interpolation_method metadata value."); + } + + continue; + } + + string sampleValue = ExtractAttribute(attrs, "sample"); + if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(sampleValue)) + { + continue; + } + + if (!int.TryParse(sampleValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out int sample) + || sample < 0 + || sample >= samplesPerPixel) + { + continue; + } + + if (string.IsNullOrWhiteSpace(value)) + { + continue; + } + + if (name.Equals("DESCRIPTION", StringComparison.OrdinalIgnoreCase)) + { + descriptionsBySample[sample] = value; + } + else if (name.Equals("positive_value", StringComparison.OrdinalIgnoreCase)) + { + positiveValueBySample[sample] = value; + } + else if (name.Equals("SCALE", StringComparison.OrdinalIgnoreCase) + && double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double scale)) + { + scaleBySample[sample] = scale; + } + else if ((name.Equals("OFFSET", StringComparison.OrdinalIgnoreCase) + || name.Equals("constant_offset", StringComparison.OrdinalIgnoreCase)) + && double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double offset)) + { + offsetBySample[sample] = offset; + } + else if (name.Equals("UNITTYPE", StringComparison.OrdinalIgnoreCase)) + { + unitTypeBySample[sample] = value; + } + } + } + + private static string ExtractAttribute(string attrs, string attributeName) + { + if (string.IsNullOrWhiteSpace(attrs) || string.IsNullOrWhiteSpace(attributeName)) + { + return string.Empty; + } + +#if NET8_0_OR_GREATER + MatchCollection matches = MetadataAttributeRegex().Matches(attrs); +#else + MatchCollection matches = MetadataAttributeRegex.Matches(attrs); +#endif + for (int i = 0; i < matches.Count; i++) + { + Match match = matches[i]; + Group nameGroup = match.Groups["name"]; + Group valueGroup = match.Groups["value"]; + if (!nameGroup.Success + || !valueGroup.Success + || !nameGroup.Value.Equals(attributeName, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + return CleanMetadataValue(valueGroup.Value); + } + + return string.Empty; + } + + private static double ResolveAngularScaleToDegree(Tiff tiff) + { + if (!TryGetShortArrayField(tiff, (TiffTag)GeoKeyDirectoryTag, out short[] keyDirectory) || keyDirectory.Length < 4) + { + return 1d; + } + + int keyCount = keyDirectory[3]; + for (int i = 0; i < keyCount; i++) + { + int entryOffset = 4 + (i * 4); + if (entryOffset + 3 >= keyDirectory.Length) + { + break; + } + + int keyId = keyDirectory[entryOffset]; + int tiffTagLocation = keyDirectory[entryOffset + 1]; + int valueOffset = keyDirectory[entryOffset + 3]; + if (keyId != GeogAngularUnitsGeoKey) + { + continue; + } + + int angularCode = tiffTagLocation == 0 ? valueOffset : 9102; + return angularCode switch + { + 9101 => 180d / Math.PI, + 9102 => 1d, + 9105 => 0.9d, + _ => 1d, + }; + } + + return 1d; + } + + private static bool IsPixelIsPoint(Tiff tiff) + { + if (!TryGetShortArrayField(tiff, (TiffTag)GeoKeyDirectoryTag, out short[] keyDirectory) || keyDirectory.Length < 4) + { + return false; + } + + int keyCount = keyDirectory[3]; + for (int i = 0; i < keyCount; i++) + { + int entryOffset = 4 + (i * 4); + if (entryOffset + 3 >= keyDirectory.Length) + { + break; + } + + int keyId = keyDirectory[entryOffset]; + int tiffTagLocation = keyDirectory[entryOffset + 1]; + int valueOffset = keyDirectory[entryOffset + 3]; + if (keyId == GtRasterTypeGeoKey && tiffTagLocation == 0) + { + return valueOffset == RasterPixelIsPoint; + } + } + + return false; + } + + private static bool TryGetSampleEncoding(Tiff tiff, out SampleEncoding encoding) + { + encoding = default; + if (!TryGetIntField(tiff, TiffTag.BITSPERSAMPLE, out int bitsPerSample)) + { + return false; + } + + SampleFormat sampleFormat = SampleFormat.IEEEFP; + if (TryGetIntField(tiff, TiffTag.SAMPLEFORMAT, out int sampleFormatRaw)) + { + sampleFormat = (SampleFormat)sampleFormatRaw; + } + + return SampleEncoding.TryCreate(bitsPerSample, sampleFormat, out encoding); + } + + private static bool TryGetIntField(Tiff tiff, TiffTag tag, out int value) + { + value = 0; + FieldValue[] field = tiff.GetField(tag); + if (field is null || field.Length == 0) + { + return false; + } + + value = field[0].ToInt(); + return true; + } + + private static bool TryGetStringField(Tiff tiff, TiffTag tag, [NotNullWhen(true)] out string? value) + { + value = null; + FieldValue[] field = tiff.GetField(tag); + if (field is null || field.Length == 0) + { + return false; + } + + value = field[^1].ToString(); + if (string.IsNullOrEmpty(value) && field.Length > 1) + { + value = field[0].ToString(); + } + + return value is not null; + } + + private static bool TryGetDoubleArrayField(Tiff tiff, TiffTag tag, out double[] values) + { + values = []; + FieldValue[] field = tiff.GetField(tag); + if (field is null || field.Length == 0) + { + return false; + } + + double[] candidate = field[^1].ToDoubleArray(); + if (candidate is null || candidate.Length == 0) + { + return false; + } + + values = candidate; + return true; + } + + private static bool TryGetShortArrayField(Tiff tiff, TiffTag tag, out short[] values) + { + values = []; + FieldValue[] field = tiff.GetField(tag); + if (field is null || field.Length == 0) + { + return false; + } + + short[] candidate = field[^1].ToShortArray(); + if (candidate is null || candidate.Length == 0) + { + return false; + } + + values = candidate; + return true; + } + } diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeoTiffHGridShiftMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffHGridShiftMathTransform.cs new file mode 100644 index 00000000..bc70d104 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffHGridShiftMathTransform.cs @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +/// +/// Applies horizontal grid-shift corrections loaded from GeoTIFF grids. +/// +/// +/// +/// Horizontal GeoTIFF grid shifts are applied by selecting the most specific +/// grid covering the input coordinate and interpolating longitude and latitude +/// offsets from the grid samples. The hgridshift path uses bilinear +/// interpolation, while the general gridshift path can honor GeoTIFF +/// metadata that requests biquadratic interpolation. Bilinear grids use a +/// fixed-point inverse iteration; biquadratic grids follow PROJ's +/// NOAA-compatible first-approximation reverse path. +/// +/// +/// The runtime was independently verified against PROJ's horizontal/grid-shift +/// documentation and the GeoTIFF grid specification used by PROJ. +/// +/// +/// PROJ: hgridshift. +/// PROJ GeoTIFF grid specification. +internal sealed class GeoTiffHGridShiftMathTransform : MathTransform +{ + private const double RelativeTolerance = 1e-5d; + private const double InverseTolerance = 1e-12d; + private readonly ReadOnlyCollection grids; + private readonly bool? biquadraticInterpolationOverride; + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Ordered GeoTIFF grid file paths to load. + /// + /// Overrides the interpolation mode when forces biquadratic interpolation, + /// forces bilinear interpolation, and honors the grid metadata. + /// + internal GeoTiffHGridShiftMathTransform(IReadOnlyList gridPaths, bool? biquadraticInterpolationOverride = null) + { + gridPaths = ArgumentGuard.ThrowIfNull(gridPaths, nameof(gridPaths)); + this.biquadraticInterpolationOverride = biquadraticInterpolationOverride; + + this.grids = GridLoaderHelper.LoadMulti( + gridPaths, + nameof(gridPaths), + "No horizontal grid could be loaded from GeoTIFF input.", + static path => GeoTiffGridLoader.LoadHorizontal(path), + static (left, right) => left.Area.CompareTo(right.Area)); + } + + private GeoTiffHGridShiftMathTransform(GeoTiffHGridShiftMathTransform source, bool isInverted) + { + source = ArgumentGuard.ThrowIfNull(source, nameof(source)); + + this.grids = source.grids; + this.biquadraticInterpolationOverride = source.biquadraticInterpolationOverride; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return false; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GeoTiffHGridShiftMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("GeoTiffHGridShiftMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (!this.TryFindGridForPoint(x, y, out HorizontalGrid? gridCandidate)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal GeoTIFF grid extent."); + } + + HorizontalGrid grid = ArgumentGuard.ThrowIfNull(gridCandidate, nameof(gridCandidate)); + + if (!this.isInverted) + { + (double lonShift, double latShift) = this.InterpolateShift(grid, x, y); + x += lonShift; + y += latShift; + return; + } + + this.InverseTransform(ref x, ref y, grid); + } + + private void InverseTransform(ref double longitude, ref double latitude, HorizontalGrid initialGrid) + { + (double firstLonShift, double firstLatShift) = this.InterpolateShift(initialGrid, longitude, latitude); + double targetLongitude = longitude; + double targetLatitude = latitude; + double candidateLongitude = targetLongitude - firstLonShift; + double candidateLatitude = targetLatitude - firstLatShift; + if (this.ShouldUseBiquadraticInterpolation(initialGrid)) + { + // PROJ follows NOAA NCAT here and uses the first approximation for biquadratic reverse shifts. + longitude = initialGrid.NormalizeFirstAxis(candidateLongitude); + latitude = candidateLatitude; + return; + } + + int iterations = TransformationMath.MaxInverseIterations; + while (iterations-- > 0) + { + (double iterLonShift, double iterLatShift) = this.InterpolateShift(initialGrid, candidateLongitude, candidateLatitude); + double deltaLongitude = candidateLongitude + iterLonShift - targetLongitude; + double deltaLatitude = candidateLatitude + iterLatShift - targetLatitude; + candidateLongitude -= deltaLongitude; + candidateLatitude -= deltaLatitude; + if ((deltaLongitude * deltaLongitude) + (deltaLatitude * deltaLatitude) <= (InverseTolerance * InverseTolerance)) + { + longitude = initialGrid.NormalizeFirstAxis(candidateLongitude); + latitude = candidateLatitude; + return; + } + } + + TransformationThrowHelper.ThrowInvalidOperation("Inverse horizontal GeoTIFF grid shift did not converge."); + } + + private (double LonShift, double LatShift) InterpolateShift(HorizontalGrid grid, double longitude, double latitude) + { + if (!grid.TryMapToGridCoordinates(longitude, latitude, out double gridX, out double gridY)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal GeoTIFF grid extent."); + } + + return this.ShouldUseBiquadraticInterpolation(grid) + ? InterpolationMath.InterpolateBiquadraticShift(grid, gridX, gridY) + : InterpolationMath.InterpolateBilinearShift(grid, gridX, gridY); + } + + private bool ShouldUseBiquadraticInterpolation(HorizontalGrid grid) + { + if (grid.Width < 3 || grid.Height < 3) + { + return false; + } + + return this.biquadraticInterpolationOverride ?? grid.UsesBiquadraticInterpolation; + } + + private bool TryFindGridForPoint(double longitude, double latitude, [NotNullWhen(true)] out HorizontalGrid? grid) + { + for (int i = 0; i < this.grids.Count; i++) + { + if (this.grids[i].Contains(longitude, latitude)) + { + grid = this.grids[i]; + return true; + } + } + + grid = null; + return false; + } + + /// + /// Provides bilinear and biquadratic interpolation helpers for horizontal GeoTIFF grid samples. + /// + internal static class InterpolationMath + { + /// + /// Interpolates a shift from the surrounding four grid samples. + /// + /// The grid supplying the shift samples. + /// The fractional grid x-coordinate. + /// The fractional grid y-coordinate. + /// The interpolated longitude and latitude shift. + internal static (double LonShift, double LatShift) InterpolateBilinearShift(HorizontalGrid grid, double gridX, double gridY) + { + int indexX = (int)Math.Floor(gridX); + int indexY = (int)Math.Floor(gridY); + double fractionX = gridX - indexX; + double fractionY = gridY - indexY; + NormalizeInterpolationCell(grid.Width, ref indexX, ref fractionX); + NormalizeInterpolationCell(grid.Height, ref indexY, ref fractionY); + + int indexX2 = indexX + 1; + int indexY2 = indexY + 1; + double latA = grid.GetLatitudeShift(indexX, indexY); + double latB = grid.GetLatitudeShift(indexX2, indexY); + double latC = grid.GetLatitudeShift(indexX, indexY2); + double latD = grid.GetLatitudeShift(indexX2, indexY2); + + double lonA = grid.GetLongitudeShift(indexX, indexY); + double lonB = grid.GetLongitudeShift(indexX2, indexY); + double lonC = grid.GetLongitudeShift(indexX, indexY2); + double lonD = grid.GetLongitudeShift(indexX2, indexY2); + + double xy = fractionX * fractionY; + double wA = 1d - fractionX - fractionY + xy; + double wB = fractionX - xy; + double wC = fractionY - xy; + double wD = xy; + + double latitudeShift = (latA * wA) + (latB * wB) + (latC * wC) + (latD * wD); + double longitudeShift = (lonA * wA) + (lonB * wB) + (lonC * wC) + (lonD * wD); + return (longitudeShift, latitudeShift); + } + + /// + /// Interpolates a shift from the surrounding nine grid samples using PROJ's biquadratic window. + /// + /// The grid supplying the shift samples. + /// The fractional grid x-coordinate. + /// The fractional grid y-coordinate. + /// The interpolated longitude and latitude shift. + internal static (double LonShift, double LatShift) InterpolateBiquadraticShift(HorizontalGrid grid, double gridX, double gridY) + { + int indexX = (int)Math.Floor(gridX); + int indexY = (int)Math.Floor(gridY); + double fractionX = gridX - indexX; + double fractionY = gridY - indexY; + NormalizeInterpolationCell(grid.Width, ref indexX, ref fractionX); + NormalizeInterpolationCell(grid.Height, ref indexY, ref fractionY); + NormalizeBiquadraticWindow(grid.Width, ref indexX, ref fractionX); + NormalizeBiquadraticWindow(grid.Height, ref indexY, ref fractionY); + + Span latitudeShiftByRow = stackalloc double[3]; + Span longitudeShiftByRow = stackalloc double[3]; + for (int rowOffset = 0; rowOffset < 3; rowOffset++) + { + int sampleY = indexY + rowOffset; + latitudeShiftByRow[rowOffset] = QuadraticInterpolate( + fractionX, + grid.GetLatitudeShift(indexX, sampleY), + grid.GetLatitudeShift(indexX + 1, sampleY), + grid.GetLatitudeShift(indexX + 2, sampleY)); + longitudeShiftByRow[rowOffset] = QuadraticInterpolate( + fractionX, + grid.GetLongitudeShift(indexX, sampleY), + grid.GetLongitudeShift(indexX + 1, sampleY), + grid.GetLongitudeShift(indexX + 2, sampleY)); + } + + return ( + QuadraticInterpolate(fractionY, longitudeShiftByRow[0], longitudeShiftByRow[1], longitudeShiftByRow[2]), + QuadraticInterpolate(fractionY, latitudeShiftByRow[0], latitudeShiftByRow[1], latitudeShiftByRow[2])); + } + + private static void NormalizeInterpolationCell(int size, ref int index, ref double fraction) + { + if (index < 0) + { + if (index == -1 && fraction > 1d - (10d * GeoTiffHGridShiftMathTransform.RelativeTolerance)) + { + index = 0; + fraction = 0d; + return; + } + + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal GeoTIFF grid extent."); + } + + if (index + 1 < size) + { + return; + } + + if (index + 1 == size && fraction < 10d * GeoTiffHGridShiftMathTransform.RelativeTolerance) + { + index = size - 2; + fraction = 1d; + return; + } + + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal GeoTIFF grid extent."); + } + + private static void NormalizeBiquadraticWindow(int size, ref int index, ref double fraction) + { + if ((fraction <= 0.5d && index > 0) || (index + 2 == size)) + { + index -= 1; + fraction += 1d; + } + } + + private static double QuadraticInterpolate(double xToInterpolate, double f0, double f1, double f2) + { + double delta0 = f1 - f0; + double delta1 = f2 - f1; + double secondDelta0 = delta1 - delta0; + return f0 + (xToInterpolate * delta0) + (0.5d * xToInterpolate * (xToInterpolate - 1d) * secondDelta0); + } + } + + /// + /// Represents a single horizontal-shift grid loaded from a GeoTIFF file. + /// + internal sealed class HorizontalGrid : BaseGeoGrid + { + private readonly int latitudeSampleIndex; + private readonly int longitudeSampleIndex; + private readonly bool longitudeIsPositiveWest; + private readonly double latitudeUnitScale; + private readonly double longitudeUnitScale; + private readonly bool normalizeFirstAxis; + private readonly bool usesBiquadraticInterpolation; + + /// + /// Initializes a new instance of the class. + /// + /// Path of the source GeoTIFF file. + /// Number of grid columns. + /// Number of grid rows. + /// Geographic coverage area used to order grids by specificity. + /// Tolerance in degrees used for boundary checks. + /// Western boundary in degrees. + /// Eastern boundary in degrees. + /// Southern boundary in degrees. + /// Northern boundary in degrees. + /// Affine coefficient: longitude change per grid column. + /// Affine coefficient: longitude change per grid row. + /// Affine coefficient: longitude of the grid origin. + /// Affine coefficient: latitude change per grid column. + /// Affine coefficient: latitude change per grid row. + /// Affine coefficient: latitude of the grid origin. + /// The raster sample data store. + /// Zero-based band index of the latitude-shift sample. + /// Zero-based band index of the longitude-shift sample. + /// + /// when the longitude shift values are stored with positive-west convention + /// and must be negated before use. + /// + /// Scale factor to convert the raw second-axis shift sample to runtime coordinate units. + /// Scale factor to convert the raw first-axis shift sample to runtime coordinate units. + /// + /// when the first axis represents wrapped longitudes and inverse results should be normalized. + /// + /// + /// when the grid metadata requests biquadratic interpolation instead of bilinear interpolation. + /// + internal HorizontalGrid( + string sourcePath, + int width, + int height, + double area, + double epsilon, + double west, + double east, + double south, + double north, + double a, + double b, + double c, + double d, + double e, + double f, + SampleData sampleData, + int latitudeSampleIndex, + int longitudeSampleIndex, + bool longitudeIsPositiveWest, + double latitudeUnitScale, + double longitudeUnitScale, + bool normalizeFirstAxis, + bool usesBiquadraticInterpolation) + : base(sourcePath, width, height, area, epsilon, west, east, south, north, a, b, c, d, e, f, sampleData) + { + this.latitudeSampleIndex = latitudeSampleIndex; + this.longitudeSampleIndex = longitudeSampleIndex; + this.longitudeIsPositiveWest = longitudeIsPositiveWest; + this.latitudeUnitScale = latitudeUnitScale; + this.longitudeUnitScale = longitudeUnitScale; + this.normalizeFirstAxis = normalizeFirstAxis; + this.usesBiquadraticInterpolation = usesBiquadraticInterpolation; + } + + /// + /// Gets a value indicating whether this grid uses biquadratic interpolation. + /// + internal bool UsesBiquadraticInterpolation => this.usesBiquadraticInterpolation; + + /// + /// Gets the latitude shift in degrees at the specified grid cell. + /// + /// Column index. + /// Row index. + /// The latitude shift in degrees at (, ). + internal double GetLatitudeShift(int x, int y) + { + return this.GetSampleValue(this.latitudeSampleIndex, x, y) * this.latitudeUnitScale; + } + + /// + /// Gets the longitude shift in degrees at the specified grid cell, with positive-east sign convention applied. + /// + /// Column index. + /// Row index. + /// The longitude shift in degrees at (, ), positive east. + internal double GetLongitudeShift(int x, int y) + { + double value = this.GetSampleValue(this.longitudeSampleIndex, x, y) * this.longitudeUnitScale; + return this.longitudeIsPositiveWest ? -value : value; + } + + /// + /// Normalizes the first-axis result when the grid operates in wrapped-longitude space. + /// + /// The first-axis value produced by inverse interpolation. + /// The normalized first-axis value. + internal double NormalizeFirstAxis(double value) + { + return this.normalizeFirstAxis ? TransformationMath.NormalizeLongitudeDegrees(value) : value; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeoTiffVGridShiftMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffVGridShiftMathTransform.cs new file mode 100644 index 00000000..b23eb57a --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffVGridShiftMathTransform.cs @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +/// +/// Applies vertical datum shifts loaded from GeoTIFF grid files. +/// +/// +/// +/// Vertical GeoTIFF shifts are evaluated by bilinearly interpolating the grid +/// value at the input horizontal coordinate and applying the configured forward +/// multiplier. When one or more corner samples are nodata, the implementation +/// renormalizes the remaining bilinear weights instead of silently treating +/// nodata as zero. +/// +/// +/// The runtime was independently verified against PROJ's vertical/grid-shift +/// documentation and the GeoTIFF grid specification used for geodetic grids. +/// +/// +/// PROJ: vgridshift. +/// PROJ GeoTIFF grid specification. +internal sealed class GeoTiffVGridShiftMathTransform : MathTransform +{ + private const double RelativeTolerance = 1e-5d; + private readonly ReadOnlyCollection grids; + private readonly double forwardMultiplier; + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Ordered GeoTIFF grid file paths to load. + /// Multiplier applied to interpolated shift values in the forward direction. + internal GeoTiffVGridShiftMathTransform(IReadOnlyList gridPaths, double forwardMultiplier) + { + gridPaths = ArgumentGuard.ThrowIfNull(gridPaths, nameof(gridPaths)); + + if (double.IsNaN(forwardMultiplier) || double.IsInfinity(forwardMultiplier)) + { + ArgumentGuard.ThrowArgument("Forward multiplier must be finite.", nameof(forwardMultiplier)); + } + + this.grids = GridLoaderHelper.LoadMulti( + gridPaths, + nameof(gridPaths), + "No vertical grid could be loaded from GeoTIFF input.", + static path => GeoTiffGridLoader.LoadVertical(path), + static (left, right) => left.Area.CompareTo(right.Area)); + this.forwardMultiplier = forwardMultiplier; + } + + private GeoTiffVGridShiftMathTransform(GeoTiffVGridShiftMathTransform source, bool isInverted) + { + source = ArgumentGuard.ThrowIfNull(source, nameof(source)); + + this.grids = source.grids; + this.forwardMultiplier = source.forwardMultiplier; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return false; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GeoTiffVGridShiftMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("GeoTiffVGridShiftMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (!this.TryFindGridForPoint(x, y, out VerticalGrid? gridCandidate)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical GeoTIFF grid extent."); + } + + VerticalGrid grid = ArgumentGuard.ThrowIfNull(gridCandidate, nameof(gridCandidate)); + double shift = InterpolateValue(grid, x, y, this.forwardMultiplier); + if (!this.isInverted) + { + z += shift; + return; + } + + z -= shift; + } + + private static double InterpolateValue(VerticalGrid grid, double longitude, double latitude, double multiplier) + { + if (!grid.TryMapToGridCoordinates(longitude, latitude, out double gridX, out double gridY)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical GeoTIFF grid extent."); + } + + int indexX = (int)Math.Floor(gridX); + int indexY = (int)Math.Floor(gridY); + double fractionX = gridX - indexX; + double fractionY = gridY - indexY; + NormalizeInterpolationCell(grid.Width, ref indexX, ref fractionX); + NormalizeInterpolationCell(grid.Height, ref indexY, ref fractionY); + + int indexX2 = indexX + 1; + int indexY2 = indexY + 1; + + double valueA = grid.GetValue(indexX, indexY); + double valueB = grid.GetValue(indexX2, indexY); + double valueC = grid.GetValue(indexX, indexY2); + double valueD = grid.GetValue(indexX2, indexY2); + + bool aValid = !grid.IsNoData(valueA); + bool bValid = !grid.IsNoData(valueB); + bool cValid = !grid.IsNoData(valueC); + bool dValid = !grid.IsNoData(valueD); + int validCount = (aValid ? 1 : 0) + (bValid ? 1 : 0) + (cValid ? 1 : 0) + (dValid ? 1 : 0); + if (validCount == 0) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate falls on vertical GeoTIFF grid nodata region."); + } + + double xy = fractionX * fractionY; + double wA = 1d - fractionX - fractionY + xy; + double wB = fractionX - xy; + double wC = fractionY - xy; + double wD = xy; + + if (validCount == 4) + { + return ((valueA * wA) + (valueB * wB) + (valueC * wC) + (valueD * wD)) * multiplier; + } + + double weightedValue = 0d; + double totalWeight = 0d; + if (aValid) + { + weightedValue += valueA * wA; + totalWeight += wA; + } + + if (bValid) + { + weightedValue += valueB * wB; + totalWeight += wB; + } + + if (cValid) + { + weightedValue += valueC * wC; + totalWeight += wC; + } + + if (dValid) + { + weightedValue += valueD * wD; + totalWeight += wD; + } + + if (totalWeight == 0d) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate falls on vertical GeoTIFF grid nodata region."); + } + + return (weightedValue / totalWeight) * multiplier; + } + + private static void NormalizeInterpolationCell(int size, ref int index, ref double fraction) + { + if (index < 0) + { + if (index == -1 && fraction > 1d - (10d * RelativeTolerance)) + { + index = 0; + fraction = 0d; + return; + } + + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical GeoTIFF grid extent."); + } + + if (index + 1 < size) + { + return; + } + + if (index + 1 == size && fraction < 10d * RelativeTolerance) + { + index = size - 2; + fraction = 1d; + return; + } + + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical GeoTIFF grid extent."); + } + + private bool TryFindGridForPoint(double longitude, double latitude, [NotNullWhen(true)] out VerticalGrid? grid) + { + for (int i = 0; i < this.grids.Count; i++) + { + if (this.grids[i].Contains(longitude, latitude)) + { + grid = this.grids[i]; + return true; + } + } + + grid = null; + return false; + } + + /// + /// Represents a single vertical-shift band loaded from a GeoTIFF grid file. + /// + internal sealed class VerticalGrid : BaseGeoGrid + { + private readonly int sampleIndex; + private readonly double? noDataValue; + + /// + /// Initializes a new instance of the class. + /// + /// Path of the source GeoTIFF file. + /// Number of grid columns. + /// Number of grid rows. + /// Geographic coverage area used to order grids by specificity. + /// Tolerance in degrees used for boundary checks. + /// Western boundary in degrees. + /// Eastern boundary in degrees. + /// Southern boundary in degrees. + /// Northern boundary in degrees. + /// Affine coefficient: longitude change per grid column. + /// Affine coefficient: longitude change per grid row. + /// Affine coefficient: longitude of the grid origin. + /// Affine coefficient: latitude change per grid column. + /// Affine coefficient: latitude change per grid row. + /// Affine coefficient: latitude of the grid origin. + /// The raster sample data store. + /// Zero-based band index of the vertical-shift sample. + /// No-data sentinel value, or if none is defined. + internal VerticalGrid( + string sourcePath, + int width, + int height, + double area, + double epsilon, + double west, + double east, + double south, + double north, + double a, + double b, + double c, + double d, + double e, + double f, + SampleData sampleData, + int sampleIndex, + double? noDataValue) + : base(sourcePath, width, height, area, epsilon, west, east, south, north, a, b, c, d, e, f, sampleData) + { + this.sampleIndex = sampleIndex; + this.noDataValue = noDataValue; + } + + /// + /// Gets the scaled vertical shift value at the specified grid cell. + /// + /// Column index. + /// Row index. + /// The raw sample value at (, ). + internal double GetValue(int x, int y) + { + return this.GetSampleValue(this.sampleIndex, x, y); + } + + /// + /// Determines whether the specified sample value equals the no-data sentinel. + /// + /// The sample value to test. + /// when matches the no-data value; otherwise . + internal bool IsNoData(double value) + { + return this.noDataValue.HasValue && Math.Abs(value - this.noDataValue.Value) <= 1e-4d; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeoTiffXyzGridShiftMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffXyzGridShiftMathTransform.cs new file mode 100644 index 00000000..de62d3d5 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GeoTiffXyzGridShiftMathTransform.cs @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems; + +/// +/// Applies geocentric XYZ grid-shift corrections loaded from GeoTIFF grids. +/// +/// +/// +/// XYZ GeoTIFF grids provide three cartesian correction components that are +/// bilinearly interpolated in the source grid domain. Depending on the +/// grid_ref semantics, this runtime either applies the correction +/// directly or iteratively solves the complementary direction while converting +/// candidate coordinates through the configured ellipsoid as needed. +/// +/// +/// The runtime was independently verified against PROJ's GeoTIFF grid-shift +/// behavior and the PROJ geodetic GeoTIFF specification, including the +/// input-versus-output grid reference handling and the iterative inverse path. +/// +/// +/// PROJ: gridshift. +/// PROJ GeoTIFF grid specification. +internal sealed class GeoTiffXyzGridShiftMathTransform : MathTransform +{ + private const double RelativeTolerance = 1e-5d; + private readonly ReadOnlyCollection grids; + private readonly GeocentricTransform geocentricInverse; + private readonly double semiMajor; + private readonly double semiMinor; + private readonly double multiplier; + private readonly bool gridReferenceIsInput; + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// GeoTIFF XYZ grid paths. + /// Ellipsoid semi-major axis. + /// Ellipsoid semi-minor axis. + /// Grid shift multiplier. + /// True when grid_ref=input_crs. + internal GeoTiffXyzGridShiftMathTransform( + IReadOnlyList gridPaths, + double semiMajor, + double semiMinor, + double multiplier, + bool gridReferenceIsInput) + { +#if NET8_0_OR_GREATER + gridPaths = ArgumentGuard.ThrowIfNull(gridPaths); +#else + gridPaths = ArgumentGuard.ThrowIfNull(gridPaths, nameof(gridPaths)); +#endif + + if (semiMajor <= 0d || double.IsNaN(semiMajor) || double.IsInfinity(semiMajor)) + { + ArgumentGuard.ThrowArgument("Semi-major axis must be a positive finite value.", nameof(semiMajor)); + } + + if (semiMinor <= 0d || double.IsNaN(semiMinor) || double.IsInfinity(semiMinor)) + { + ArgumentGuard.ThrowArgument("Semi-minor axis must be a positive finite value.", nameof(semiMinor)); + } + + if (double.IsNaN(multiplier) || double.IsInfinity(multiplier)) + { + ArgumentGuard.ThrowArgument("Multiplier must be finite.", nameof(multiplier)); + } + + this.grids = GridLoaderHelper.LoadMulti( + gridPaths, + nameof(gridPaths), + "No XYZ grid could be loaded from GeoTIFF input.", + static path => GeoTiffGridLoader.LoadXyz(path), + static (left, right) => left.Area.CompareTo(right.Area)); + this.semiMajor = semiMajor; + this.semiMinor = semiMinor; + this.multiplier = multiplier; + this.gridReferenceIsInput = gridReferenceIsInput; + + var parameters = new List + { + new("semi_major", semiMajor), + new("semi_minor", semiMinor), + }; + var geocentricForward = new GeocentricTransform(parameters, false); + this.geocentricInverse = (GeocentricTransform)geocentricForward.Inverse(); + } + + private GeoTiffXyzGridShiftMathTransform(GeoTiffXyzGridShiftMathTransform source, bool isInverted) + { +#if NET8_0_OR_GREATER + source = ArgumentGuard.ThrowIfNull(source); +#else + source = ArgumentGuard.ThrowIfNull(source, nameof(source)); +#endif + + this.grids = source.grids; + this.geocentricInverse = source.geocentricInverse; + this.semiMajor = source.semiMajor; + this.semiMinor = source.semiMinor; + this.multiplier = source.multiplier; + this.gridReferenceIsInput = source.gridReferenceIsInput; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() => false; + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GeoTiffXyzGridShiftMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("GeoTiffXyzGridShiftMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (!this.isInverted) + { + if (this.gridReferenceIsInput) + { + this.ApplyDirect(ref x, ref y, ref z, 1d); + } + else + { + this.ApplyIterative(ref x, ref y, ref z, 1d); + } + + return; + } + + if (this.gridReferenceIsInput) + { + this.ApplyIterative(ref x, ref y, ref z, -1d); + } + else + { + this.ApplyDirect(ref x, ref y, ref z, -1d); + } + } + + private static void NormalizeInterpolationCell(int size, ref int index, ref double fraction) + { + if (index < 0) + { + if (index == -1 && fraction > 1d - (10d * RelativeTolerance)) + { + index = 0; + fraction = 0d; + return; + } + + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the XYZ GeoTIFF grid extent."); + } + + if (index + 1 < size) + { + return; + } + + if (index + 1 == size && fraction < 10d * RelativeTolerance) + { + index = size - 2; + fraction = 1d; + return; + } + + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the XYZ GeoTIFF grid extent."); + } + + private static bool IsConverged(double error) + { + return error < 1e-10d; + } + + private static void InterpolateShift(XyzGrid grid, double longitude, double latitude, out double dx, out double dy, out double dz) + { + if (!grid.TryMapToGridCoordinates(longitude, latitude, out double gridX, out double gridY)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the XYZ GeoTIFF grid extent."); + } + + int indexX = (int)Math.Floor(gridX); + int indexY = (int)Math.Floor(gridY); + double fractionX = gridX - indexX; + double fractionY = gridY - indexY; + NormalizeInterpolationCell(grid.Width, ref indexX, ref fractionX); + NormalizeInterpolationCell(grid.Height, ref indexY, ref fractionY); + + int indexX2 = indexX + 1; + int indexY2 = indexY + 1; + double xA = grid.GetXShift(indexX, indexY); + double xB = grid.GetXShift(indexX2, indexY); + double xC = grid.GetXShift(indexX, indexY2); + double xD = grid.GetXShift(indexX2, indexY2); + + double yA = grid.GetYShift(indexX, indexY); + double yB = grid.GetYShift(indexX2, indexY); + double yC = grid.GetYShift(indexX, indexY2); + double yD = grid.GetYShift(indexX2, indexY2); + + double zA = grid.GetZShift(indexX, indexY); + double zB = grid.GetZShift(indexX2, indexY); + double zC = grid.GetZShift(indexX, indexY2); + double zD = grid.GetZShift(indexX2, indexY2); + + double xy = fractionX * fractionY; + double wA = 1d - fractionX - fractionY + xy; + double wB = fractionX - xy; + double wC = fractionY - xy; + double wD = xy; + + dx = (xA * wA) + (xB * wB) + (xC * wC) + (xD * wD); + dy = (yA * wA) + (yB * wB) + (yC * wC) + (yD * wD); + dz = (zA * wA) + (zB * wB) + (zC * wC) + (zD * wD); + } + + private void ApplyDirect(ref double x, ref double y, ref double z, double factor) + { + if (!this.TryGetShift(x, y, z, out double dx, out double dy, out double dz)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the XYZ GeoTIFF grid extent."); + } + + x += factor * dx; + y += factor * dy; + z += factor * dz; + } + + private void ApplyIterative(ref double x, ref double y, ref double z, double factor) + { + double targetX = x; + double targetY = y; + double targetZ = z; + double candidateX = x; + double candidateY = y; + double candidateZ = z; + + bool converged = false; + for (int i = 0; i < TransformationMath.MaxInverseIterations; i++) + { + if (!this.TryGetShift(candidateX, candidateY, candidateZ, out double dx, out double dy, out double dz)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the XYZ GeoTIFF grid extent."); + } + + dx *= factor; + dy *= factor; + dz *= factor; + + double error = ((candidateX - targetX - dx) * (candidateX - targetX - dx)) + + ((candidateY - targetY - dy) * (candidateY - targetY - dy)) + + ((candidateZ - targetZ - dz) * (candidateZ - targetZ - dz)); + + candidateX = targetX + dx; + candidateY = targetY + dy; + candidateZ = targetZ + dz; + + if (IsConverged(error)) + { + converged = true; + break; + } + } + + if (!converged) + { + TransformationThrowHelper.ThrowInvalidOperation("Inverse XYZ GeoTIFF grid shift did not converge."); + } + + x = candidateX; + y = candidateY; + z = candidateZ; + } + + private bool TryFindGridForPoint(double longitude, double latitude, [NotNullWhen(true)] out XyzGrid? grid) + { + for (int i = 0; i < this.grids.Count; i++) + { + if (this.grids[i].Contains(longitude, latitude)) + { + grid = this.grids[i]; + return true; + } + } + + grid = null; + return false; + } + + private bool TryGetShift(double x, double y, double z, out double dx, out double dy, out double dz) + { + dx = 0d; + dy = 0d; + dz = 0d; + + double lon = x; + double lat = y; + double h = z; + this.geocentricInverse.Transform(ref lon, ref lat, ref h); + + if (!this.TryFindGridForPoint(lon, lat, out XyzGrid? grid)) + { + return false; + } + + grid = ArgumentGuard.ThrowIfNull(grid, nameof(grid)); + GeoTiffXyzGridShiftMathTransform.InterpolateShift(grid, lon, lat, out dx, out dy, out dz); + dx *= this.multiplier; + dy *= this.multiplier; + dz *= this.multiplier; + return true; + } + + /// + /// Represents an XYZ shift page loaded from a GeoTIFF. + /// + internal sealed class XyzGrid : BaseGeoGrid + { + private readonly int sampleX; + private readonly int sampleY; + private readonly int sampleZ; + + /// + /// Initializes a new instance of the class. + /// + /// Source grid file path. + /// Grid width. + /// Grid height. + /// Grid area. + /// Grid epsilon. + /// Grid western bound. + /// Grid eastern bound. + /// Grid southern bound. + /// Grid northern bound. + /// GeoTransform A. + /// GeoTransform B. + /// GeoTransform C. + /// GeoTransform D. + /// GeoTransform E. + /// GeoTransform F. + /// Decoded sample data. + /// X-shift sample index. + /// Y-shift sample index. + /// Z-shift sample index. + internal XyzGrid( + string sourcePath, + int width, + int height, + double area, + double epsilon, + double west, + double east, + double south, + double north, + double a, + double b, + double c, + double d, + double e, + double f, + SampleData sampleData, + int sampleX, + int sampleY, + int sampleZ) + : base(sourcePath, width, height, area, epsilon, west, east, south, north, a, b, c, d, e, f, sampleData) + { + this.sampleX = sampleX; + this.sampleY = sampleY; + this.sampleZ = sampleZ; + } + + /// + /// Gets interpolated X-shift source sample value. + /// + /// Horizontal sample index. + /// Vertical sample index. + /// The interpolated X-shift sample value. + internal double GetXShift(int x, int y) => this.GetSampleValue(this.sampleX, x, y); + + /// + /// Gets interpolated Y-shift source sample value. + /// + /// Horizontal sample index. + /// Vertical sample index. + /// The interpolated Y-shift sample value. + internal double GetYShift(int x, int y) => this.GetSampleValue(this.sampleY, x, y); + + /// + /// Gets interpolated Z-shift source sample value. + /// + /// Horizontal sample index. + /// Vertical sample index. + /// The interpolated Z-shift sample value. + internal double GetZShift(int x, int y) => this.GetSampleValue(this.sampleZ, x, y); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeocentricLatitudeMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GeocentricLatitudeMathTransform.cs new file mode 100644 index 00000000..b16f9ab6 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GeocentricLatitudeMathTransform.cs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Converts between geodetic and geocentric latitude. +/// +/// +/// The forward relation is the textbook latitude conversion +/// phi_c = atan((b² / a²) * tan(phi_g)). The inverse path applies the +/// reciprocal factor, so the transform remains a simple one-parameter angular +/// scaling in tangent space. +/// +/// PROJ: geocentric latitude. +internal sealed class GeocentricLatitudeMathTransform : MathTransform +{ + private readonly double geodeticToGeocentricFactor; + private bool isInverse; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Semi-major axis of the ellipsoid. + /// Semi-minor axis of the ellipsoid. + /// + /// to convert geocentric latitude to geodetic latitude; + /// to convert geodetic latitude to geocentric latitude. + /// + internal GeocentricLatitudeMathTransform(double semiMajor, double semiMinor, bool isInverse) + { + if (semiMajor <= 0d || semiMinor <= 0d || double.IsNaN(semiMajor) || double.IsInfinity(semiMajor) || double.IsNaN(semiMinor) || double.IsInfinity(semiMinor)) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(semiMajor), "Semi-major and semi-minor axes must be finite and positive."); + } + + this.geodeticToGeocentricFactor = (semiMinor * semiMinor) / (semiMajor * semiMajor); + this.isInverse = isInverse; + } + + /// + /// Initializes a new instance of the class + /// using a precomputed geodetic-to-geocentric scale factor. + /// + /// Scale factor applied to tangent of latitude in forward mode. + /// + /// to convert geocentric latitude to geodetic latitude; + /// to convert geodetic latitude to geocentric latitude. + /// + private GeocentricLatitudeMathTransform(double geodeticToGeocentricFactor, bool isInverse) + { + if (geodeticToGeocentricFactor <= 0d || double.IsNaN(geodeticToGeocentricFactor) || double.IsInfinity(geodeticToGeocentricFactor)) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(geodeticToGeocentricFactor), "Geocentric latitude factor must be finite and positive."); + } + + this.geodeticToGeocentricFactor = geodeticToGeocentricFactor; + this.isInverse = isInverse; + } + + /// + public override int DimSource => 2; + + /// + public override int DimTarget => 2; + + /// + public override bool Identity() => this.geodeticToGeocentricFactor.Equals(1d); + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GeocentricLatitudeMathTransform(this.geodeticToGeocentricFactor, !this.isInverse); + + return this.inverse; + } + + /// + public override void Invert() + { + this.isInverse = !this.isInverse; + this.inverse = null; + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (double.IsNaN(y)) + { + return; + } + + double tangent = Math.Tan(DegreesToRadians(y)); + double factor = this.isInverse + ? 1d / this.geodeticToGeocentricFactor + : this.geodeticToGeocentricFactor; + y = RadiansToDegrees(Math.Atan(factor * tangent)); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeocentricTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GeocentricTransform.cs index 37550020..8bee9810 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/GeocentricTransform.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/GeocentricTransform.cs @@ -1,280 +1,322 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; using System; using System.Collections.Generic; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// Converts between geodetic and geocentric coordinate representations. +/// +/// +/// Latitude, Longitude and ellipsoidal height in terms of a 3-dimensional geographic system +/// may by expressed in terms of a geocentric (earth centered) Cartesian coordinate reference system +/// X, Y, Z with the Z axis corresponding to the earth's rotation axis positive northwards, the X +/// axis through the intersection of the prime meridian and equator, and the Y axis through +/// the intersection of the equator with longitude 90 degrees east. The geographic and geocentric +/// systems are based on the same geodetic datum. +/// Geocentric coordinate reference systems are conventionally taken to be defined with the X +/// axis through the intersection of the Greenwich meridian and equator. This requires that the equivalent +/// geographic coordinate reference systems based on a non-Greenwich prime meridian should first be +/// transformed to their Greenwich equivalent. Geocentric coordinates X, Y and Z take their units from +/// the units of the ellipsoid axes (a and b). As it is conventional for X, Y and Z to be in metres, +/// if the ellipsoid axis dimensions are given in another linear unit they should first be converted +/// to metres. +/// The forward geographic-to-geocentric conversion was independently verified against IOGP, +/// "Geomatics Guidance Note 7, part 2: Coordinate Conversions and Transformations including +/// Formulas" (publication 373-7-2, 2019), EPSG method 9602, Geographic/geocentric conversions. +/// The cartesian coordinate equations X = (ν + h) * cos(φ) * cos(λ), +/// Y = (ν + h) * cos(φ) * sin(λ), and +/// Z = ((1 - e²) * ν + h) * sin(φ) match the implementation here. +/// The inverse conversion follows the Bowring-style cartesian-to-geodetic formulation +/// used by PROJ's cart.cpp. It derives the latitude estimate from the normalized +/// auxiliary quantities and , switches to a geocentric-radius-based +/// height approximation near the poles to avoid division by zero, and retains an additional +/// iterative refinement only for very high altitudes to preserve round-trip accuracy there. +/// That formulation was independently verified against B. R. Bowring, "Transformation from +/// spatial to geographical coordinates," Survey Review, vol. 23, no. 181, pp. 323-327, +/// 1976, and later comparison literature. +/// +/// EPSG method 9602: Geographic/geocentric conversions. +/// Research comparison of Bowring-style geocentric to geodetic conversion methods. +internal sealed class GeocentricTransform : MathTransform { - /// - /// - /// - /// - /// Latitude, Longitude and ellipsoidal height in terms of a 3-dimensional geographic system - /// may by expressed in terms of a geocentric (earth centered) Cartesian coordinate reference system - /// X, Y, Z with the Z axis corresponding to the earth's rotation axis positive northwards, the X - /// axis through the intersection of the prime meridian and equator, and the Y axis through - /// the intersection of the equator with longitude 90 degrees east. The geographic and geocentric - /// systems are based on the same geodetic datum. - /// Geocentric coordinate reference systems are conventionally taken to be defined with the X - /// axis through the intersection of the Greenwich meridian and equator. This requires that the equivalent - /// geographic coordinate reference systems based on a non-Greenwich prime meridian should first be - /// transformed to their Greenwich equivalent. Geocentric coordinates X, Y and Z take their units from - /// the units of the ellipsoid axes (a and b). As it is conventional for X, Y and Z to be in metres, - /// if the ellipsoid axis dimensions are given in another linear unit they should first be converted - /// to metres. - /// - [Serializable] - internal class GeocentricTransform : MathTransform - { - private const double COS_67P5 = 0.38268343236508977; /* cosine of 67.5 degrees */ - private const double AD_C = 1.0026000; /* Toms region 1 constant */ - - /// - /// - /// - private bool _isInverse; - /// - /// - /// - private MathTransform _inverse; - - /// - /// Eccentricity squared : (a^2 - b^2)/a^2 - /// - private readonly double _es; - - /// - /// major axis - /// - private readonly double _semiMajor; - - /// - /// Minor axis - /// - private readonly double _semiMinor; - /* - private double ab; // Semi_major / semi_minor - private double ba; // Semi_minor / semi_major - */ - private readonly double _ses; // Second eccentricity squared : (a^2 - b^2)/b^2 - - /// - /// - /// - private List _parameters; - - - /// - /// Initializes a geocentric projection object - /// - /// List of parameters to initialize the projection. - /// Indicates whether the projection forward (meters to degrees or degrees to meters). - public GeocentricTransform(List parameters, bool isInverse) : this(parameters) - { - _isInverse = isInverse; - } - - /// - /// Initializes a geocentric projection object - /// - /// List of parameters to initialize the projection. - internal GeocentricTransform(List parameters) - { - _parameters = parameters; - _semiMajor = _parameters.Find(delegate(ProjectionParameter par) - { - // Do not remove the following lines containing "_Parameters = _Parameters;" - // There is an issue deploying code with anonymous delegates to - // SQLCLR because they're compiled using a writable static field - // (which is not allowed in SQLCLR SAFE mode). - // To workaround this, we will use a harmless reference to the - // _Parameters field inside the anonymous delegate code making - // the compiler generates a private nested class with a function - // that is used as the delegate. - // For details, see http://www.hedgate.net/articles/2006/01/27/troubles-with-shared-state-and-anonymous-delegates-in-sqlclr -#pragma warning disable 1717 - _parameters = _parameters; -#pragma warning restore 1717 - - return par.Name.Equals("semi_major", StringComparison.OrdinalIgnoreCase); - }).Value; - - _semiMinor = _parameters.Find(delegate(ProjectionParameter par) - { -#pragma warning disable 1717 - _parameters = _parameters; // See explanation above. -#pragma warning restore 1717 - return par.Name.Equals("semi_minor", StringComparison.OrdinalIgnoreCase); - }).Value; - - _es = 1.0 - (_semiMinor * _semiMinor) / (_semiMajor * _semiMajor); //e^2 - _ses = (Math.Pow(_semiMajor, 2) - Math.Pow(_semiMinor, 2)) / Math.Pow(_semiMinor, 2); - //ba = _semiMinor / _semiMajor; - //ab = _semiMajor / _semiMinor; - } - - public override int DimSource + /// + /// Cosine threshold used to switch to the polar height approximation near the poles. + /// + private const double PolarCosphiThreshold = 1e-6d; + + /// + /// Eccentricity squared : (a² - b²)/a². + /// + private readonly double es; + + /// + /// Semi-major axis length. + /// + private readonly double semiMajor; + + /// + /// Minor axis. + /// + private readonly double semiMinor; + + /// + /// Second eccentricity squared: (a² - b²) / b². + /// + private readonly double ses; + + /// + /// Indicates whether this instance runs in inverse mode. + /// + private bool isInverse; + + /// + /// Cached inverse transform. + /// + private MathTransform? inverse; + + /// + /// Projection parameters used to initialize the transform. + /// + private List parameters; + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + /// + /// to convert geocentric Cartesian (meters) to geodetic (degrees); + /// to convert geodetic (degrees) to geocentric Cartesian (meters). + /// + public GeocentricTransform(List parameters, bool isInverse) + : this(parameters) + { + this.isInverse = isInverse; + } + + /// + /// Initializes a new instance of the class. + /// + /// List of parameters to initialize the projection. + internal GeocentricTransform(List parameters) + { + this.parameters = parameters; + ProjectionParameter? semiMajorParameterCandidate = this.parameters.Find( + par => par.Name.Equals("semi_major", StringComparison.OrdinalIgnoreCase)); + ProjectionParameter semiMajorParameter = ArgumentGuard.ThrowIfNull(semiMajorParameterCandidate, nameof(semiMajorParameterCandidate)); + this.semiMajor = semiMajorParameter.Value; + + ProjectionParameter? semiMinorParameterCandidate = this.parameters.Find( + par => par.Name.Equals("semi_minor", StringComparison.OrdinalIgnoreCase)); + ProjectionParameter semiMinorParameter = ArgumentGuard.ThrowIfNull(semiMinorParameterCandidate, nameof(semiMinorParameterCandidate)); + this.semiMinor = semiMinorParameter.Value; + + this.es = 1.0 - ((this.semiMinor * this.semiMinor) / (this.semiMajor * this.semiMajor)); // e^2 + this.ses = (Math.Pow(this.semiMajor, 2) - Math.Pow(this.semiMinor, 2)) / Math.Pow(this.semiMinor, 2); + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + /// Returns the inverse of this conversion. + /// + /// A that reverses this geocentric conversion. + public override MathTransform Inverse() + { + this.inverse ??= new GeocentricTransform(this.parameters, !this.isInverse); + + return this.inverse; + } + + /// + /// Computes the normal radius of curvature at a latitude. + /// + /// The semi-major axis. + /// The ellipsoid eccentricity squared. + /// The sine of the geodetic latitude. + /// The normal radius of curvature. + private static double GetNormalRadiusOfCurvature(double semiMajorAxis, double eccentricitySquared, double sinPhi) + { + return eccentricitySquared == 0d + ? semiMajorAxis + : semiMajorAxis / Math.Sqrt(1d - (eccentricitySquared * sinPhi * sinPhi)); + } + + /// + /// Computes the geocentric radius used by the polar height approximation. + /// + /// The semi-major axis. + /// The semi-minor to semi-major axis ratio. + /// The cosine of the geodetic latitude. + /// The sine of the geodetic latitude. + /// The geocentric radius at the latitude. + private static double GetGeocentricRadius(double semiMajorAxis, double semiMinorOverSemiMajor, double cosPhi, double sinPhi) + { + double cosPhiSquared = cosPhi * cosPhi; + double sinPhiSquared = sinPhi * sinPhi; + double semiMinorOverSemiMajorSquared = semiMinorOverSemiMajor * semiMinorOverSemiMajor; + double weightedSinPhiSquared = semiMinorOverSemiMajorSquared * sinPhiSquared; + return semiMajorAxis * Math.Sqrt( + (cosPhiSquared + (semiMinorOverSemiMajorSquared * weightedSinPhiSquared)) / + (cosPhiSquared + weightedSinPhiSquared)); + } + + /// + /// Converts a point (lon, lat, z) in degrees to (x, y, z) in meters. + /// + /// The longitude in degree. + /// The latitude in degree. + /// The z-ordinate value. + private void DegreesToMeters(ref double lon, ref double lat, ref double z) + { + lon = DegreesToRadians(lon); + lat = DegreesToRadians(lat); + z = double.IsNaN(z) ? 0 : z; + + double v = this.semiMajor / Math.Sqrt(1 - (this.es * Math.Pow(Math.Sin(lat), 2))); + double x = (v + z) * Math.Cos(lat) * Math.Cos(lon); + double y = (v + z) * Math.Cos(lat) * Math.Sin(lon); + z = (((1 - this.es) * v) + z) * Math.Sin(lat); + + lon = x; + lat = y; + } + + /// + /// Converts coordinates in projected meters to decimal degrees. + /// + /// The x-ordinate when entering, the longitude value upon exit. + /// The y-ordinate when entering, the latitude value upon exit. + /// The z-ordinate value. + private void MetersToDegrees(ref double x, ref double y, ref double z) + { + double xDivA = x / this.semiMajor; + double yDivA = y / this.semiMajor; + double zDivA = z / this.semiMajor; + double pDivA = Math.Sqrt((xDivA * xDivA) + (yDivA * yDivA)); + double semiMinorOverSemiMajor = this.semiMinor / this.semiMajor; + double scaledPDivA = pDivA * semiMinorOverSemiMajor; + double norm = Math.Sqrt((zDivA * zDivA) + (scaledPDivA * scaledPDivA)); + + double c; + double s; + if (norm != 0d) { - get { return 3; } + double inverseNorm = 1d / norm; + c = scaledPDivA * inverseNorm; + s = zDivA * inverseNorm; } - - public override int DimTarget + else { - get { return 3; } + c = 1d; + s = 0d; } - /// - /// Returns the inverse of this conversion. - /// - /// IMathTransform that is the reverse of the current conversion. - public override MathTransform Inverse() - { - if (_inverse == null) - _inverse = new GeocentricTransform(this._parameters, !_isInverse); - return _inverse; - } - - /// - /// Converts a point (lon, lat, z) in degrees to (x, y, z) in meters - /// - /// The longitude in degree - /// The latitude in degree - /// The z-ordinate value - private void DegreesToMeters(ref double lon, ref double lat, ref double z) - { - lon = DegreesToRadians(lon); - lat = DegreesToRadians(lat); - z = double.IsNaN(z) ? 0 : z; + double yPhi = zDivA + (this.ses * semiMinorOverSemiMajor * s * s * s); + double xPhi = pDivA - (this.es * c * c * c); + double normPhi = Math.Sqrt((yPhi * yPhi) + (xPhi * xPhi)); - double v = _semiMajor / Math.Sqrt(1 - _es * Math.Pow(Math.Sin(lat), 2)); - double x = (v + z) * Math.Cos(lat) * Math.Cos(lon); - double y = (v + z) * Math.Cos(lat) * Math.Sin(lon); - z = ((1 - _es) * v + z) * Math.Sin(lat); + double cosPhi; + double sinPhi; + if (normPhi != 0d) + { + double inverseNormPhi = 1d / normPhi; + cosPhi = xPhi * inverseNormPhi; + sinPhi = yPhi * inverseNormPhi; + } + else + { + cosPhi = 1d; + sinPhi = 0d; + } - lon = x; - lat = y; + double lat; + if (xPhi <= 0d) + { + lat = z >= 0d ? Math.PI * 0.5 : -Math.PI * 0.5; + cosPhi = 0d; + sinPhi = z >= 0d ? 1d : -1d; + } + else + { + lat = Math.Atan(yPhi / xPhi); } - /// - /// Converts coordinates in projected meters to decimal degrees. - /// - /// The x-ordinate when entering, the longitude value upon exit. - /// The y-ordinate when entering, the latitude value upon exit. - /// The z-ordinate value - private void MetersToDegrees(ref double x, ref double y, ref double z) + double lon = Math.Atan2(yDivA, xDivA); + double height; + if (cosPhi < PolarCosphiThreshold) + { + double radius = GetGeocentricRadius(this.semiMajor, semiMinorOverSemiMajor, cosPhi, sinPhi); + height = Math.Abs(z) - radius; + } + else { - bool At_Pole = false; // indicates whether location is in polar region */ + double normalRadius = GetNormalRadiusOfCurvature(this.semiMajor, this.es, sinPhi); + height = (this.semiMajor * pDivA / cosPhi) - normalRadius; + } - double lon; - double lat = 0; - double Height; - if (x != 0.0) - lon = Math.Atan2(y, x); - else + if (Math.Abs(height) > 50000d && xPhi > 0d) + { + const double convergenceTolerance = 1e-12d; + for (int i = 0; i < 10; i++) { - if (y > 0) - lon = Math.PI / 2; - else if (y < 0) - lon = -Math.PI * 0.5; - else + sinPhi = Math.Sin(lat); + double normalRadius = GetNormalRadiusOfCurvature(this.semiMajor, this.es, sinPhi); + double nextLatitude = Math.Atan2(z + (this.es * normalRadius * sinPhi), Math.Sqrt((x * x) + (y * y))); + if (Math.Abs(nextLatitude - lat) < convergenceTolerance) { - At_Pole = true; - lon = 0.0; - if (z > 0.0) - { - /* north pole */ - lat = Math.PI * 0.5; - } - else if (z < 0.0) - { - /* south pole */ - lat = -Math.PI * 0.5; - } - else - { - /* center of earth */ - lon = RadiansToDegrees(lon); - lat = RadiansToDegrees(Math.PI * 0.5); - x = lon; - y = lat; - z = -_semiMinor; - return; - } + lat = nextLatitude; + break; } + + lat = nextLatitude; } - double W2 = x * x + y * y; // Square of distance from Z axis - double W = Math.Sqrt(W2); // distance from Z axis - double T0 = z * AD_C; // initial estimate of vertical component - double S0 = Math.Sqrt(T0 * T0 + W2); //initial estimate of horizontal component - double Sin_B0 = T0 / S0; //sin(B0), B0 is estimate of Bowring aux variable - double Cos_B0 = W / S0; //cos(B0) - double Sin3_B0 = Math.Pow(Sin_B0, 3); - double T1 = z + _semiMinor * _ses * Sin3_B0; //corrected estimate of vertical component - double Sum = W - _semiMajor * _es * Cos_B0 * Cos_B0 * Cos_B0; //numerator of cos(phi1) - double S1 = Math.Sqrt(T1 * T1 + Sum * Sum); //corrected estimate of horizontal component - double Sin_p1 = T1 / S1; //sin(phi1), phi1 is estimated latitude - double Cos_p1 = Sum / S1; //cos(phi1) - double Rn = _semiMajor / Math.Sqrt(1.0 - _es * Sin_p1 * Sin_p1); //Earth radius at location - if (Cos_p1 >= COS_67P5) - Height = W / Cos_p1 - Rn; - else if (Cos_p1 <= -COS_67P5) - Height = W / -Cos_p1 - Rn; - else Height = z / Sin_p1 + Rn * (_es - 1.0); - if (!At_Pole) - lat = Math.Atan(Sin_p1 / Cos_p1); - - x = RadiansToDegrees(lon); - y = RadiansToDegrees(lat); - z = Height; + sinPhi = Math.Sin(lat); + cosPhi = Math.Cos(lat); + if (cosPhi < PolarCosphiThreshold) + { + double radius = GetGeocentricRadius(this.semiMajor, semiMinorOverSemiMajor, cosPhi, sinPhi); + height = Math.Abs(z) - radius; + } + else + { + double normalRadius = GetNormalRadiusOfCurvature(this.semiMajor, this.es, sinPhi); + double radialDistance = Math.Sqrt((x * x) + (y * y)); + height = (radialDistance / cosPhi) - normalRadius; + } } - public sealed override void Transform(ref double x, ref double y, ref double z) + x = RadiansToDegrees(lon); + y = RadiansToDegrees(lat); + z = height; + } + + /// + public sealed override void Transform(ref double x, ref double y, ref double z) + { + if (this.isInverse) { - if (_isInverse) - MetersToDegrees(ref x, ref y, ref z); - else - DegreesToMeters(ref x, ref y, ref z); + this.MetersToDegrees(ref x, ref y, ref z); + } + else + { + this.DegreesToMeters(ref x, ref y, ref z); } + } - /// - /// Reverses the transformation - /// - public override void Invert() - { - _isInverse = !_isInverse; - } - - /// - /// Gets a Well-Known text representation of this object. - /// - /// - public override string WKT - { - get { throw new NotImplementedException("The method or operation is not implemented."); } - } - /// - /// Gets an XML representation of this object. - /// - /// - public override string XML - { - get { throw new NotImplementedException("The method or operation is not implemented."); } - } - } + /// + /// Reverses the transformation. + /// + public override void Invert() + { + this.isInverse = !this.isInverse; + } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeogOffsetMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GeogOffsetMathTransform.cs new file mode 100644 index 00000000..bb10b590 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GeogOffsetMathTransform.cs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +/// +/// Implements PROJ's geogoffset runtime transform (geographic offsets). +/// +/// +/// Geographic offsets are a trivial additive transform: +/// x += dlon, y += dlat, and z += dh, with longitude and +/// latitude offsets converted from arc-seconds to degrees during construction. +/// +/// PROJ: geographic offset transformation. +internal sealed class GeogOffsetMathTransform : MathTransform +{ + private const double ArcSecondsPerDegree = 3600d; + + private readonly double longitudeOffsetDegrees; + private readonly double latitudeOffsetDegrees; + private readonly double heightOffsetMeters; + + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Longitude offset in arc-seconds. + /// Latitude offset in arc-seconds. + /// Height offset in metres. + /// + /// to subtract offsets; to add offsets. + /// + private GeogOffsetMathTransform( + double longitudeOffsetArcSeconds, + double latitudeOffsetArcSeconds, + double heightOffsetMeters, + bool isInverted) + { + ArgumentGuard.ThrowIfNotFinite(longitudeOffsetArcSeconds, nameof(longitudeOffsetArcSeconds), "Offset values must be finite."); + ArgumentGuard.ThrowIfNotFinite(latitudeOffsetArcSeconds, nameof(latitudeOffsetArcSeconds), "Offset values must be finite."); + ArgumentGuard.ThrowIfNotFinite(heightOffsetMeters, nameof(heightOffsetMeters), "Offset values must be finite."); + + this.longitudeOffsetDegrees = longitudeOffsetArcSeconds / ArcSecondsPerDegree; + this.latitudeOffsetDegrees = latitudeOffsetArcSeconds / ArcSecondsPerDegree; + this.heightOffsetMeters = heightOffsetMeters; + this.isInverted = isInverted; + } + + /// + /// Initializes a new instance of the class + /// as an inverted clone. + /// + /// Source instance to clone. + /// Whether to apply inverse direction in the clone. + private GeogOffsetMathTransform(GeogOffsetMathTransform source, bool isInverted) + { + this.longitudeOffsetDegrees = source.longitudeOffsetDegrees; + this.latitudeOffsetDegrees = source.latitudeOffsetDegrees; + this.heightOffsetMeters = source.heightOffsetMeters; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return this.longitudeOffsetDegrees == 0d + && this.latitudeOffsetDegrees == 0d + && this.heightOffsetMeters == 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GeogOffsetMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("GeogOffsetMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (double.IsNaN(z)) + { + z = 0d; + } + + double sign = this.isInverted ? -1d : 1d; + x += sign * this.longitudeOffsetDegrees; + y += sign * this.latitudeOffsetDegrees; + z += sign * this.heightOffsetMeters; + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "geogoffset arguments were null."; + return false; + } + + if (!SpanParseUtility.TryGetOptionalDouble(args, "dlon", out double dlonArcSeconds, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "dlat", out double dlatArcSeconds, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "dh", out double dhMeters, out skipReason)) + { + return false; + } + + transform = Create(dlonArcSeconds, dlatArcSeconds, dhMeters, args.ContainsKey("inv")); + return true; + } + + /// + /// Creates a geographic offset transform from resolved numeric parameters. + /// + /// Longitude offset in arc-seconds. + /// Latitude offset in arc-seconds. + /// Height offset in metres. + /// to create the inverse direction. + /// The created transform. + internal static MathTransform Create( + double longitudeOffsetArcSeconds, + double latitudeOffsetArcSeconds, + double heightOffsetMeters, + bool isInverted = false) + { + MathTransform transform = longitudeOffsetArcSeconds == 0d && latitudeOffsetArcSeconds == 0d && heightOffsetMeters == 0d + ? new IdentityMathTransform(3) + : new GeogOffsetMathTransform(longitudeOffsetArcSeconds, latitudeOffsetArcSeconds, heightOffsetMeters, false); + return isInverted ? transform.Inverse() : transform; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/GeographicTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GeographicTransform.cs index bc419a81..329af61f 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/GeographicTransform.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/GeographicTransform.cs @@ -1,111 +1,86 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems.Transformations; using System; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// The GeographicTransform class is implemented on geographic transformation objects and +/// implements datum transformations between geographic coordinate systems. +/// +/// +/// When the source and target geographic coordinate systems share the same +/// horizontal datum, this transform represents only the prime-meridian +/// conversion between them. It adjusts the longitude ordinate by removing the +/// source prime-meridian offset and applying the target prime-meridian offset, +/// while leaving latitude and height unchanged. +/// +/// PROJ glossary: ballpark transformation. +public sealed class GeographicTransform : MathTransform { - /// - /// The GeographicTransform class is implemented on geographic transformation objects and - /// implements datum transformations between geographic coordinate systems. + /// + /// Initializes a new instance of the class. /// - [Serializable] - public class GeographicTransform : MathTransform - { - internal GeographicTransform(GeographicCoordinateSystem sourceGCS, GeographicCoordinateSystem targetGCS) - { - SourceGCS = sourceGCS; - TargetGCS = targetGCS; - } + /// Source geographic coordinate system. + /// Target geographic coordinate system. + internal GeographicTransform(GeographicCoordinateSystem sourceGCS, GeographicCoordinateSystem targetGCS) + { + this.SourceGCS = sourceGCS; + this.TargetGCS = targetGCS; + } - /// - /// Gets or sets the source geographic coordinate system for the transformation. - /// - public GeographicCoordinateSystem SourceGCS { get; set; } + /// + /// Gets the source geographic coordinate system for the transformation. + /// + public GeographicCoordinateSystem SourceGCS { get; } - /// - /// Gets or sets the target geographic coordinate system for the transformation. - /// - public GeographicCoordinateSystem TargetGCS { get; set; } + /// + /// Gets the target geographic coordinate system for the transformation. + /// + public GeographicCoordinateSystem TargetGCS { get; } - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. [NOT IMPLEMENTED]. - /// - public override string WKT - { - get - { - throw new NotImplementedException(); - } - } + /// + public override string WKT => base.WKT; - /// - /// Gets an XML representation of this object [NOT IMPLEMENTED]. - /// - public override string XML - { - get - { - throw new NotImplementedException(); - } - } + /// + public override string XML => base.XML; - /// - /// DimSource - /// - public override int DimSource - { - get { return SourceGCS.Dimension; } - } + /// + /// Gets the dimension of input points. + /// + public override int DimSource => this.SourceGCS.Dimension; - /// - /// DimTarget - /// - public override int DimTarget - { - get { return TargetGCS.Dimension; } - } - - /// - /// Creates the inverse transform of this object. - /// - /// This method may fail if the transform is not one to one. However, all cartographic projections should succeed. - /// - public override MathTransform Inverse() - { - throw new NotImplementedException(); - } + /// + /// Gets the dimension of output points. + /// + public override int DimTarget => this.TargetGCS.Dimension; - /// - public sealed override void Transform(ref double x, ref double y, ref double z) - { - x /= SourceGCS.AngularUnit.RadiansPerUnit; - x -= SourceGCS.PrimeMeridian.Longitude / SourceGCS.PrimeMeridian.AngularUnit.RadiansPerUnit; - x += TargetGCS.PrimeMeridian.Longitude / TargetGCS.PrimeMeridian.AngularUnit.RadiansPerUnit; - x *= SourceGCS.AngularUnit.RadiansPerUnit; - } + /// + /// Creates the inverse transform of this object. + /// + /// A that reverses this geographic transformation. + /// + /// This transform applies only the prime-meridian longitude shift between + /// the two geographic coordinate systems. The longitude is first normalized + /// with the source angular unit and then restored using the target prime- + /// meridian longitude, while unit conversion itself remains the caller's + /// responsibility in the broader transformation chain. + /// + public override MathTransform Inverse() => new GeographicTransform(this.TargetGCS, this.SourceGCS); + + /// + public sealed override void Transform(ref double x, ref double y, ref double z) + { + x /= this.SourceGCS.AngularUnit.RadiansPerUnit; + x -= this.SourceGCS.PrimeMeridian.Longitude / this.SourceGCS.PrimeMeridian.AngularUnit.RadiansPerUnit; + x += this.TargetGCS.PrimeMeridian.Longitude / this.TargetGCS.PrimeMeridian.AngularUnit.RadiansPerUnit; + x *= this.SourceGCS.AngularUnit.RadiansPerUnit; + } - /// - /// Reverses the transformation - /// - public override void Invert() - { - throw new NotImplementedException(); - } - } + /// + /// Reverses the transformation. + /// + public override void Invert() => throw new NotSupportedException("GeographicTransform is immutable. Use Inverse() to obtain inverted transform."); } diff --git a/src/ProjNet/CoordinateSystems/Transformations/GridLoaderHelper.cs b/src/ProjNet/CoordinateSystems/Transformations/GridLoaderHelper.cs new file mode 100644 index 00000000..b7b2a771 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GridLoaderHelper.cs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +/// +/// Shared helper for loading one or more grid files into read-only collections. +/// +internal static class GridLoaderHelper +{ + /// + /// Loads zero or more grid items from each non-empty path in . + /// + /// Loaded grid item type. + /// Ordered grid paths to load. + /// Argument name used when reporting failures. + /// Exception message used when no grid items could be loaded. + /// Per-path loader callback. + /// Optional comparison used to sort the loaded items before materializing the result. + /// A read-only collection containing the loaded grid items. + internal static ReadOnlyCollection LoadMulti( + IReadOnlyList paths, + string argumentName, + string emptyMessage, + Func> loader, + Comparison? comparison = null) + { + paths = ArgumentGuard.ThrowIfNull(paths, argumentName); + argumentName = ArgumentGuard.ThrowIfNullOrWhiteSpace(argumentName, nameof(argumentName)); + emptyMessage = ArgumentGuard.ThrowIfNullOrWhiteSpace(emptyMessage, nameof(emptyMessage)); + loader = ArgumentGuard.ThrowIfNull(loader, nameof(loader)); + + var loadedItems = new List(paths.Count); + for (int i = 0; i < paths.Count; i++) + { + string path = paths[i]; + if (string.IsNullOrWhiteSpace(path)) + { + continue; + } + + loadedItems.AddRange(loader(path)); + } + + if (loadedItems.Count == 0) + { + ArgumentGuard.ThrowArgument(emptyMessage, argumentName); + } + + if (comparison is not null) + { + loadedItems.Sort(comparison); + } + + return new ReadOnlyCollection(loadedItems); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/GtxVGridShiftMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/GtxVGridShiftMathTransform.cs new file mode 100644 index 00000000..e40484a7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/GtxVGridShiftMathTransform.cs @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.InteropServices; + +/// +/// Applies vertical datum shifts using one or more GTX grid files. +/// +/// +/// +/// GTX-based vertical shifts are applied by selecting the first grid covering +/// the input coordinate and bilinearly interpolating the surrounding raster +/// samples. The implementation preserves the historical PROJ convention that +/// the default forward multiplier is -1 and mirrors PROJ's nodata +/// sentinel handling for GTX cells. +/// +/// +/// The runtime was independently verified against PROJ's published +/// vgridshift documentation and vgridshift.cpp. +/// +/// +/// PROJ: vgridshift. +internal sealed class GtxVGridShiftMathTransform : MathTransform +{ + private const double RelativeTolerance = 1e-5d; + + private readonly ReadOnlyCollection grids; + private readonly double forwardMultiplier; + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Ordered GTX grid file paths to load. + /// Multiplier applied to interpolated values in forward direction. + internal GtxVGridShiftMathTransform(IReadOnlyList gridPaths, double forwardMultiplier = -1d) + { + gridPaths = ArgumentGuard.ThrowIfNull(gridPaths, nameof(gridPaths)); + + if (double.IsNaN(forwardMultiplier) || double.IsInfinity(forwardMultiplier)) + { + ArgumentGuard.ThrowArgument("Forward multiplier must be finite.", nameof(forwardMultiplier)); + } + + this.grids = GridLoaderHelper.LoadMulti( + gridPaths, + nameof(gridPaths), + "At least one GTX grid file must be provided.", + static path => new[] { GtxGrid.Load(path) }); + this.forwardMultiplier = forwardMultiplier; + } + + private GtxVGridShiftMathTransform(GtxVGridShiftMathTransform source, bool isInverted) + { + source = ArgumentGuard.ThrowIfNull(source, nameof(source)); + + this.grids = source.grids; + this.forwardMultiplier = source.forwardMultiplier; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return false; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new GtxVGridShiftMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("GtxVGridShiftMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (!this.TryFindGridForPoint(x, y, out GtxGrid? selectedGridCandidate)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical grid extent."); + } + + GtxGrid selectedGrid = ArgumentGuard.ThrowIfNull(selectedGridCandidate, nameof(selectedGridCandidate)); + double value = InterpolateValue(selectedGrid, x, y, this.forwardMultiplier); + if (!this.isInverted) + { + z += value; + return; + } + + z -= value; + } + + private bool TryFindGridForPoint(double longitude, double latitude, [NotNullWhen(true)] out GtxGrid? grid) + { + for (int i = 0; i < this.grids.Count; i++) + { + if (this.grids[i].Contains(longitude, latitude)) + { + grid = this.grids[i]; + return true; + } + } + + grid = null; + return false; + } + + private static double InterpolateValue(GtxGrid grid, double longitude, double latitude, double multiplier) + { + double gridX = (longitude - grid.West) * grid.InvResolutionX; + if (longitude < grid.West) + { + gridX = (longitude + 360d - grid.West) * grid.InvResolutionX; + } + else if (longitude > grid.East) + { + gridX = (longitude - 360d - grid.West) * grid.InvResolutionX; + } + + double gridY = (latitude - grid.South) * grid.InvResolutionY; + + int indexX = (int)Math.Floor(gridX); + int indexY = (int)Math.Floor(gridY); + if (indexX < 0 || indexX >= grid.Width || indexY < 0 || indexY >= grid.Height) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical grid extent."); + } + + double fractionX = gridX - indexX; + double fractionY = gridY - indexY; + + int indexX2 = indexX + 1; + if (indexX2 >= grid.Width) + { + if (indexX2 == grid.Width && fractionX < 10d * RelativeTolerance) + { + indexX = grid.Width - 2; + indexX2 = grid.Width - 1; + fractionX = 1d; + } + else + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical grid extent."); + } + } + + int indexY2 = indexY + 1; + if (indexY2 >= grid.Height) + { + if (indexY2 == grid.Height && fractionY < 10d * RelativeTolerance) + { + indexY = grid.Height - 2; + indexY2 = grid.Height - 1; + fractionY = 1d; + } + else + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the vertical grid extent."); + } + } + + float valueA = grid.GetValue(indexX, indexY); + float valueB = grid.GetValue(indexX2, indexY); + float valueC = grid.GetValue(indexX, indexY2); + float valueD = grid.GetValue(indexX2, indexY2); + + bool aValid = !IsNoData(valueA, multiplier); + bool bValid = !IsNoData(valueB, multiplier); + bool cValid = !IsNoData(valueC, multiplier); + bool dValid = !IsNoData(valueD, multiplier); + int validCount = (aValid ? 1 : 0) + (bValid ? 1 : 0) + (cValid ? 1 : 0) + (dValid ? 1 : 0); + if (validCount == 0) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate falls on vertical grid nodata region."); + } + + double gridXy = fractionX * fractionY; + double weightA = 1d - fractionX - fractionY + gridXy; + double weightB = fractionX - gridXy; + double weightC = fractionY - gridXy; + double weightD = gridXy; + + if (validCount == 4) + { + double value = (valueA * weightA) + (valueB * weightB) + (valueC * weightC) + (valueD * weightD); + return value * multiplier; + } + + double weightedValue = 0d; + double totalWeight = 0d; + if (aValid) + { + weightedValue += valueA * weightA; + totalWeight += weightA; + } + + if (bValid) + { + weightedValue += valueB * weightB; + totalWeight += weightB; + } + + if (cValid) + { + weightedValue += valueC * weightC; + totalWeight += weightC; + } + + if (dValid) + { + weightedValue += valueD * weightD; + totalWeight += weightD; + } + + if (totalWeight == 0d) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate falls on vertical grid nodata region."); + } + + return (weightedValue / totalWeight) * multiplier; + } + + private static bool IsNoData(float value, double multiplier) + { + double scaled = value * multiplier; + return scaled > 1000d || scaled < -1000d || value == TransformationMath.GtxNoDataSentinel; + } + + private sealed class GtxGrid + { + private readonly float[] values; + + private GtxGrid( + string sourcePath, + double west, + double east, + double south, + double north, + double resolutionX, + double resolutionY, + int width, + int height, + float[] values) + { + this.SourcePath = sourcePath; + this.West = west; + this.East = east; + this.South = south; + this.North = north; + this.ResolutionX = resolutionX; + this.ResolutionY = resolutionY; + this.Width = width; + this.Height = height; + this.values = values; + this.Epsilon = (Math.Abs(resolutionX) + Math.Abs(resolutionY)) * RelativeTolerance; + this.InvResolutionX = 1d / resolutionX; + this.InvResolutionY = 1d / resolutionY; + } + + internal string SourcePath { get; } + + internal double West { get; } + + internal double East { get; } + + internal double South { get; } + + internal double North { get; } + + internal double ResolutionX { get; } + + internal double ResolutionY { get; } + + internal int Width { get; } + + internal int Height { get; } + + internal double Epsilon { get; } + + internal double InvResolutionX { get; } + + internal double InvResolutionY { get; } + + internal static GtxGrid Load(string path) + { + byte[] bytes = File.ReadAllBytes(path); + if (bytes.Length < 40) + { + throw new InvalidDataException("GTX file is too small."); + } + + double yOrigin = ReadDoubleBigEndian(bytes, 0); + double xOrigin = ReadDoubleBigEndian(bytes, 8); + double yStep = ReadDoubleBigEndian(bytes, 16); + double xStep = ReadDoubleBigEndian(bytes, 24); + int rows = ReadInt32BigEndian(bytes, 32); + int cols = ReadInt32BigEndian(bytes, 36); + + if (cols <= 0 || rows <= 0 || xOrigin < -360d || xOrigin > 360d || yOrigin < -90d || yOrigin > 90d) + { + throw new InvalidDataException("GTX header contains invalid extents."); + } + + if (xStep == 0d || yStep == 0d) + { + throw new InvalidDataException("GTX header contains invalid resolution."); + } + + if (xOrigin >= 180d) + { + xOrigin -= 360d; + } + + long expectedDataBytes = (long)rows * cols * sizeof(float); + if (bytes.Length < 40 + expectedDataBytes) + { + throw new InvalidDataException("GTX file data is truncated."); + } + + float[] values = new float[rows * cols]; + int offset = 40; + for (int i = 0; i < values.Length; i++) + { + values[i] = ReadSingleBigEndian(bytes, offset); + offset += sizeof(float); + } + + double west = xOrigin; + double south = yOrigin; + double east = xOrigin + (xStep * (cols - 1)); + double north = yOrigin + (yStep * (rows - 1)); + return new GtxGrid( + path, + west, + east, + south, + north, + xStep, + yStep, + cols, + rows, + values); + } + + internal bool Contains(double longitude, double latitude) + { + double lon = longitude; + if (lon < this.West - this.Epsilon) + { + lon += 360d; + } + else if (lon > this.East + this.Epsilon) + { + lon -= 360d; + } + + return lon >= this.West - this.Epsilon + && lon <= this.East + this.Epsilon + && latitude >= this.South - this.Epsilon + && latitude <= this.North + this.Epsilon; + } + + internal float GetValue(int x, int y) + { + return this.values[(y * this.Width) + x]; + } + + private static double ReadDoubleBigEndian(byte[] bytes, int offset) + { + long rawBits = BinaryPrimitives.ReadInt64BigEndian(bytes.AsSpan(offset, sizeof(long))); + Span bitStorage = stackalloc long[1]; + bitStorage[0] = rawBits; + return MemoryMarshal.Cast(bitStorage)[0]; + } + + private static int ReadInt32BigEndian(byte[] bytes, int offset) + { + return (bytes[offset] << 24) + | (bytes[offset + 1] << 16) + | (bytes[offset + 2] << 8) + | bytes[offset + 3]; + } + + private static float ReadSingleBigEndian(byte[] bytes, int offset) + { + int rawBits = BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(offset, sizeof(int))); + Span bitStorage = stackalloc int[1]; + bitStorage[0] = rawBits; + return MemoryMarshal.Cast(bitStorage)[0]; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/HelmertMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/HelmertMathTransform.cs new file mode 100644 index 00000000..251cb43b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/HelmertMathTransform.cs @@ -0,0 +1,808 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems.Transformations.Numerics; + +/// +/// Implements PROJ's helmert runtime transform for static and kinematic operations. +/// +/// +/// The core 7-parameter Bursa-Wolf formulation was independently verified against ISO +/// 19111:2019, Geographic information - Referencing by coordinates. The scale +/// factor multiplies the fully rotated vector before translation, matching the standard +/// T + (1 + s) * R * X form implemented here. +/// The position-vector and coordinate-frame rotation conventions were independently +/// verified against IOGP, "Geomatics Guidance Note 7, part 2: Coordinate Conversions +/// and Transformations including Formulas" (publication 373-7-2, 2019), EPSG methods +/// 1033 and 1032. Those methods differ only in the sign convention for the rotation +/// parameters, and the matrix transposition used here for position-vector mode matches +/// that published relationship. +/// +/// Wikipedia: Helmert transformation. +/// EPSG method 1033: Position Vector transformation (geocentric domain). +/// EPSG method 1032: Coordinate Frame rotation (geocentric domain). +internal sealed class HelmertMathTransform : MathTransform +{ + private readonly HelmertParameterState baseState; + private readonly HelmertRateState rateState; + private readonly bool hasKinematicRates; + private readonly double epochReference; + + private readonly bool fourParameter; + private readonly bool noRotation; + private readonly bool exact; + private readonly bool isPositionVector; + + private readonly HelmertParameterState staticState; + private readonly HelmertRuntimeState staticRuntimeState; + + private readonly bool isInverted; + private MathTransform? inverse; + + private HelmertMathTransform( + HelmertParameterState baseState, + HelmertRateState rateState, + bool hasKinematicRates, + double epochReference, + bool fourParameter, + bool noRotation, + bool exact, + bool isPositionVector, + HelmertRuntimeState staticRuntimeState, + HelmertParameterState staticState, + bool isInverted) + { + this.baseState = baseState; + this.rateState = rateState; + this.hasKinematicRates = hasKinematicRates; + this.epochReference = epochReference; + this.fourParameter = fourParameter; + this.noRotation = noRotation; + this.exact = exact; + this.isPositionVector = isPositionVector; + this.staticRuntimeState = staticRuntimeState; + this.staticState = staticState; + this.isInverted = isInverted; + } + + private HelmertMathTransform(HelmertMathTransform source, bool isInverted) + { + this.baseState = source.baseState; + this.rateState = source.rateState; + this.hasKinematicRates = source.hasKinematicRates; + this.epochReference = source.epochReference; + this.fourParameter = source.fourParameter; + this.noRotation = source.noRotation; + this.exact = source.exact; + this.isPositionVector = source.isPositionVector; + this.staticRuntimeState = source.staticRuntimeState.Clone(); + this.staticState = source.staticState; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + HelmertParameterState state = this.staticState; + return this.fourParameter + ? state.TranslationX == 0d + && state.TranslationY == 0d + && state.Theta == 0d + && state.Scale == 1d + : state.TranslationX == 0d + && state.TranslationY == 0d + && state.TranslationZ == 0d + && this.noRotation + && state.Scale == 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new HelmertMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("HelmertMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + HelmertParameterState state = this.staticState; + if (this.isInverted) + { + this.TransformInverse(ref x, ref y, ref z, state); + } + else + { + this.TransformForward(ref x, ref y, ref z, state); + } + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "helmert arguments were null."; + return false; + } + + if (args.ContainsKey("transpose")) + { + skipReason = "helmert: 'transpose' argument is no longer valid. Use convention=position_vector/coordinate_frame"; + return false; + } + + double translationX = 0d; + double translationY = 0d; + double translationZ = 0d; + double rotationX = 0d; + double rotationY = 0d; + double rotationZ = 0d; + double scale = 0d; + double theta = 0d; + bool fourParameter = false; + + double translationRateX = 0d; + double translationRateY = 0d; + double translationRateZ = 0d; + double rotationRateX = 0d; + double rotationRateY = 0d; + double rotationRateZ = 0d; + double scaleRate = 0d; + double thetaRate = 0d; + bool hasKinematicRates = false; + double epochReference = 0d; + + bool hasTowgs84; + if (!TryApplyTowgs84(args, out hasTowgs84, ref translationX, ref translationY, ref translationZ, ref rotationX, ref rotationY, ref rotationZ, ref scale, out skipReason)) + { + return false; + } + + if (!TryAssignOptionalDouble(args, "x", ref translationX, out skipReason) + || !TryAssignOptionalDouble(args, "y", ref translationY, out skipReason) + || !TryAssignOptionalDouble(args, "z", ref translationZ, out skipReason)) + { + return false; + } + + if (!TryAssignOptionalArcSeconds(args, "rx", ref rotationX, out skipReason) + || !TryAssignOptionalArcSeconds(args, "ry", ref rotationY, out skipReason) + || !TryAssignOptionalArcSeconds(args, "rz", ref rotationZ, out skipReason)) + { + return false; + } + + if (args.TryGetValue("theta", out string? thetaToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(thetaToken, out double thetaArcSeconds)) + { + skipReason = "Invalid value for +theta."; + return false; + } + + fourParameter = true; + theta = thetaArcSeconds * TransformationMath.ArcSecondToRadians; + scale = 1d; + } + + if (args.TryGetValue("dtheta", out string? thetaRateToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(thetaRateToken, out double thetaRateArcSeconds)) + { + skipReason = "Invalid value for +dtheta."; + return false; + } + + thetaRate = thetaRateArcSeconds * TransformationMath.ArcSecondToRadians; + hasKinematicRates = true; + } + + if (args.TryGetValue("s", out string? scaleToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(scaleToken, out scale)) + { + skipReason = "helmert: invalid value for s."; + return false; + } + } + + if (args.TryGetValue("ds", out string? scaleRateToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(scaleRateToken, out scaleRate)) + { + skipReason = "Invalid value for +ds."; + return false; + } + + hasKinematicRates = true; + } + + if (!TryAssignOptionalDouble(args, "dx", ref translationRateX, out skipReason) + || !TryAssignOptionalDouble(args, "dy", ref translationRateY, out skipReason) + || !TryAssignOptionalDouble(args, "dz", ref translationRateZ, out skipReason)) + { + return false; + } + + if (translationRateX != 0d || translationRateY != 0d || translationRateZ != 0d) + { + hasKinematicRates = true; + } + + if (!TryAssignOptionalArcSeconds(args, "drx", ref rotationRateX, out skipReason) + || !TryAssignOptionalArcSeconds(args, "dry", ref rotationRateY, out skipReason) + || !TryAssignOptionalArcSeconds(args, "drz", ref rotationRateZ, out skipReason)) + { + return false; + } + + if (rotationRateX != 0d || rotationRateY != 0d || rotationRateZ != 0d) + { + hasKinematicRates = true; + } + + if (args.TryGetValue("t_epoch", out string? epochToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(epochToken, out epochReference)) + { + skipReason = "Invalid value for +t_epoch."; + return false; + } + } + + if (scale <= TransformationMath.MinValidPpmScale || (fourParameter && scale == 0d)) + { + skipReason = "helmert: invalid value for s."; + return false; + } + + bool noRotation = rotationX == 0d + && rotationY == 0d + && rotationZ == 0d + && rotationRateX == 0d + && rotationRateY == 0d + && rotationRateZ == 0d; + bool isPositionVector = false; + if (!noRotation) + { + if (!args.TryGetValue("convention", out string? convention) || string.IsNullOrWhiteSpace(convention)) + { + skipReason = "helmert: missing 'convention' argument"; + return false; + } + + if (convention.Equals("position_vector", StringComparison.OrdinalIgnoreCase)) + { + isPositionVector = true; + } + else if (convention.Equals("coordinate_frame", StringComparison.OrdinalIgnoreCase)) + { + isPositionVector = false; + } + else + { + skipReason = "helmert: invalid value for 'convention' argument"; + return false; + } + + if (hasTowgs84 && !isPositionVector) + { + skipReason = "helmert: towgs84 should only be used with convention=position_vector"; + return false; + } + } + + bool exact = args.ContainsKey("exact"); + transform = Create( + translationX, + translationY, + translationZ, + rotationX, + rotationY, + rotationZ, + scale, + theta, + translationRateX, + translationRateY, + translationRateZ, + rotationRateX, + rotationRateY, + rotationRateZ, + scaleRate, + thetaRate, + hasKinematicRates, + epochReference, + fourParameter, + noRotation, + exact, + isPositionVector, + args.ContainsKey("inv")); + return true; + } + + /// + /// Creates a Helmert transform from resolved numeric parameters. + /// + /// X translation in metres. + /// Y translation in metres. + /// Z translation in metres. + /// X rotation in radians. + /// Y rotation in radians. + /// Z rotation in radians. + /// Scale difference in ppm. + /// 2D rotation in radians. + /// Rate of change of X translation in metres/year. + /// Rate of change of Y translation in metres/year. + /// Rate of change of Z translation in metres/year. + /// Rate of change of X rotation in radians/year. + /// Rate of change of Y rotation in radians/year. + /// Rate of change of Z rotation in radians/year. + /// Rate of change of scale difference in ppm/year. + /// Rate of change of 2D rotation in radians/year. + /// when any time-dependent rates are present. + /// Reference epoch for kinematic parameters. + /// for the 4-parameter variant. + /// when all rotation terms are zero. + /// to use the exact rotation formulation. + /// for position-vector convention. + /// to create the inverse direction. + /// The created transform. + internal static MathTransform Create( + double translationX, + double translationY, + double translationZ, + double rotationX, + double rotationY, + double rotationZ, + double scale, + double theta, + double translationRateX, + double translationRateY, + double translationRateZ, + double rotationRateX, + double rotationRateY, + double rotationRateZ, + double scaleRate, + double thetaRate, + bool hasKinematicRates, + double epochReference, + bool fourParameter, + bool noRotation, + bool exact, + bool isPositionVector, + bool isInverted = false) + { + var baseState = new HelmertParameterState( + translationX, + translationY, + translationZ, + rotationX, + rotationY, + rotationZ, + scale, + theta); + var rateState = new HelmertRateState( + translationRateX, + translationRateY, + translationRateZ, + rotationRateX, + rotationRateY, + rotationRateZ, + scaleRate, + thetaRate); + HelmertParameterState staticState = hasKinematicRates + ? EvaluateKinematicState(baseState, rateState, epochReference, epochReference) + : baseState; + Matrix3x3 rotationMatrix = BuildRotationMatrix( + staticState.RotationX, + staticState.RotationY, + staticState.RotationZ, + exact, + isPositionVector); + var runtimeState = new HelmertRuntimeState( + new Vector3D(staticState.TranslationX, staticState.TranslationY, staticState.TranslationZ), + staticState.Scale, + staticState.Theta, + rotationMatrix, + hasKinematicRates ? TransformationMath.MissingObservationEpoch : epochReference); + + MathTransform transform = new HelmertMathTransform( + baseState, + rateState, + hasKinematicRates, + epochReference, + fourParameter, + noRotation, + exact, + isPositionVector, + runtimeState, + staticState, + false); + return isInverted ? transform.Inverse() : transform; + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + HelmertParameterState state = this.GetOrCreateStateForObservationEpoch(t); + if (this.isInverted) + { + this.TransformInverse(ref x, ref y, ref z, state); + } + else + { + this.TransformForward(ref x, ref y, ref z, state); + } + } + + private static bool TryApplyTowgs84( + Dictionary args, + out bool hasTowgs84, + ref double translationX, + ref double translationY, + ref double translationZ, + ref double rotationX, + ref double rotationY, + ref double rotationZ, + ref double scale, + out string? skipReason) + { + skipReason = null; + hasTowgs84 = false; + + if (!args.TryGetValue("towgs84", out string? towgs84Token) || string.IsNullOrWhiteSpace(towgs84Token)) + { + return true; + } + + Span values = stackalloc double[7]; + CsvParseStatus parseStatus = SpanParseUtility.TryParseCsvValues(towgs84Token.AsSpan(), values, out int valueCount); + if (parseStatus != CsvParseStatus.Success || (valueCount != 3 && valueCount != 6 && valueCount != 7)) + { + skipReason = "Invalid value for +towgs84."; + return false; + } + + translationX = values[0]; + translationY = values[1]; + translationZ = values[2]; + if (valueCount >= 6) + { + rotationX = values[3] * TransformationMath.ArcSecondToRadians; + rotationY = values[4] * TransformationMath.ArcSecondToRadians; + rotationZ = values[5] * TransformationMath.ArcSecondToRadians; + } + + if (valueCount == 7) + { + scale = values[6]; + } + + hasTowgs84 = true; + return true; + } + + private static bool TryAssignOptionalDouble( + Dictionary args, + string key, + ref double target, + out string? skipReason) + { + skipReason = null; + if (!args.TryGetValue(key, out string? token)) + { + return true; + } + + if (!SpanParseUtility.TryParseFiniteDouble(token, out target)) + { + skipReason = $"Invalid value for +{key}."; + return false; + } + + return true; + } + + private static bool TryAssignOptionalArcSeconds( + Dictionary args, + string key, + ref double target, + out string? skipReason) + { + skipReason = null; + if (!args.TryGetValue(key, out string? token)) + { + return true; + } + + if (!SpanParseUtility.TryParseFiniteDouble(token, out double value)) + { + skipReason = $"Invalid value for +{key}."; + return false; + } + + target = value * TransformationMath.ArcSecondToRadians; + return true; + } + + private static HelmertParameterState EvaluateKinematicState( + HelmertParameterState baseState, + HelmertRateState rateState, + double epochReference, + double observationEpoch) + { + double dt = observationEpoch - epochReference; + return new HelmertParameterState( + baseState.TranslationX + (rateState.TranslationRateX * dt), + baseState.TranslationY + (rateState.TranslationRateY * dt), + baseState.TranslationZ + (rateState.TranslationRateZ * dt), + baseState.RotationX + (rateState.RotationRateX * dt), + baseState.RotationY + (rateState.RotationRateY * dt), + baseState.RotationZ + (rateState.RotationRateZ * dt), + baseState.Scale + (rateState.ScaleRate * dt), + baseState.Theta + (rateState.ThetaRate * dt)); + } + + private static Matrix3x3 BuildRotationMatrix( + double rotationX, + double rotationY, + double rotationZ, + bool exact, + bool isPositionVector) + { + Matrix3x3 matrix; + if (exact) + { + double cf = Math.Cos(rotationX); + double sf = Math.Sin(rotationX); + double ct = Math.Cos(rotationY); + double st = Math.Sin(rotationY); + double cp = Math.Cos(rotationZ); + double sp = Math.Sin(rotationZ); + + matrix = new Matrix3x3( + ct * cp, + (cf * sp) + (sf * st * cp), + (sf * sp) - (cf * st * cp), + -ct * sp, + (cf * cp) - (sf * st * sp), + (sf * cp) + (cf * st * sp), + st, + -sf * ct, + cf * ct); + } + else + { + matrix = new Matrix3x3( + 1d, + rotationZ, + -rotationY, + -rotationZ, + 1d, + rotationX, + rotationY, + -rotationX, + 1d); + } + + return isPositionVector ? matrix.Transpose() : matrix; + } + + private HelmertParameterState GetOrCreateStateForObservationEpoch(double observationEpoch) + { + if (!this.hasKinematicRates) + { + return this.staticState; + } + + double normalizedEpoch = observationEpoch; + if (double.IsNaN(normalizedEpoch) || double.IsInfinity(normalizedEpoch) || normalizedEpoch == TransformationMath.MissingObservationEpoch) + { + normalizedEpoch = this.epochReference; + } + + return EvaluateKinematicState(this.baseState, this.rateState, this.epochReference, normalizedEpoch); + } + + private void TransformForward(ref double x, ref double y, ref double z, HelmertParameterState state) + { + if (this.fourParameter) + { + double cosTheta = Math.Cos(state.Theta) * state.Scale; + double sinTheta = Math.Sin(state.Theta) * state.Scale; + double sourceX = x; + double sourceY = y; + x = (cosTheta * sourceX) + (sinTheta * sourceY) + state.TranslationX; + y = (-sinTheta * sourceX) + (cosTheta * sourceY) + state.TranslationY; + return; + } + + if (this.noRotation && state.Scale == 0d) + { + x += state.TranslationX; + y += state.TranslationY; + z += state.TranslationZ; + return; + } + + Matrix3x3 rotationMatrix = this.GetRotationMatrix(state); + var translation = new Vector3D(state.TranslationX, state.TranslationY, state.TranslationZ); + double scaleFactor = 1d + (state.Scale * 1e-6d); + Vector3D transformed = ((rotationMatrix * new Vector3D(x, y, z)) * scaleFactor) + translation; + x = transformed.X; + y = transformed.Y; + z = transformed.Z; + } + + private void TransformInverse(ref double x, ref double y, ref double z, HelmertParameterState state) + { + if (this.fourParameter) + { + if (state.Scale == 0d) + { + ArgumentGuard.ThrowArgument("helmert: inverse 4-parameter transform requires non-zero scale.", nameof(state)); + } + + double cosTheta = Math.Cos(state.Theta) / state.Scale; + double sinTheta = Math.Sin(state.Theta) / state.Scale; + double sourceX = x - state.TranslationX; + double sourceY = y - state.TranslationY; + x = (sourceX * cosTheta) - (sourceY * sinTheta); + y = (sourceX * sinTheta) + (sourceY * cosTheta); + return; + } + + if (this.noRotation && state.Scale == 0d) + { + x -= state.TranslationX; + y -= state.TranslationY; + z -= state.TranslationZ; + return; + } + + Matrix3x3 rotationMatrix = this.GetRotationMatrix(state); + var translation = new Vector3D(state.TranslationX, state.TranslationY, state.TranslationZ); + double scaleFactor = 1d + (state.Scale * 1e-6d); + Vector3D source = (new Vector3D(x, y, z) - translation) / scaleFactor; + Vector3D transformed = rotationMatrix.Transpose() * source; + x = transformed.X; + y = transformed.Y; + z = transformed.Z; + } + + private Matrix3x3 GetRotationMatrix(HelmertParameterState state) + { + if (!this.hasKinematicRates) + { + return this.staticRuntimeState.RotationMatrix; + } + + return BuildRotationMatrix( + state.RotationX, + state.RotationY, + state.RotationZ, + this.exact, + this.isPositionVector); + } + + private readonly struct HelmertParameterState( + double translationX, + double translationY, + double translationZ, + double rotationX, + double rotationY, + double rotationZ, + double scale, + double theta) + { + internal double TranslationX { get; } = translationX; + + internal double TranslationY { get; } = translationY; + + internal double TranslationZ { get; } = translationZ; + + internal double RotationX { get; } = rotationX; + + internal double RotationY { get; } = rotationY; + + internal double RotationZ { get; } = rotationZ; + + internal double Scale { get; } = scale; + + internal double Theta { get; } = theta; + } + + private readonly struct HelmertRateState( + double translationRateX, + double translationRateY, + double translationRateZ, + double rotationRateX, + double rotationRateY, + double rotationRateZ, + double scaleRate, + double thetaRate) + { + internal double TranslationRateX { get; } = translationRateX; + + internal double TranslationRateY { get; } = translationRateY; + + internal double TranslationRateZ { get; } = translationRateZ; + + internal double RotationRateX { get; } = rotationRateX; + + internal double RotationRateY { get; } = rotationRateY; + + internal double RotationRateZ { get; } = rotationRateZ; + + internal double ScaleRate { get; } = scaleRate; + + internal double ThetaRate { get; } = thetaRate; + } + + private sealed class HelmertRuntimeState + { + internal HelmertRuntimeState( + Vector3D translation, + double scale, + double theta, + Matrix3x3 rotationMatrix, + double observationEpoch) + { + this.Translation = translation; + this.Scale = scale; + this.Theta = theta; + this.RotationMatrix = rotationMatrix; + this.ObservationEpoch = observationEpoch; + } + + internal Vector3D Translation { get; set; } + + internal double Scale { get; set; } + + internal double Theta { get; set; } + + internal Matrix3x3 RotationMatrix { get; set; } + + internal double ObservationEpoch { get; set; } + + internal HelmertRuntimeState Clone() + { + return new HelmertRuntimeState( + this.Translation, + this.Scale, + this.Theta, + this.RotationMatrix, + this.ObservationEpoch); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/HornerMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/HornerMathTransform.cs new file mode 100644 index 00000000..13cca380 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/HornerMathTransform.cs @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +/// +/// Implements PROJ's horner polynomial runtime transform. +/// +/// +/// +/// Horner polynomial transforms evaluate either real polynomial coefficient +/// fields or complex polynomial series around configured origins to model local +/// frame distortions. This implementation supports explicit inverse +/// coefficients when provided and otherwise falls back to the reviewed +/// iterative inverse solver. +/// +/// +/// The runtime was independently verified against PROJ's published +/// horner documentation and horner.cpp, including the coefficient +/// ordering, complex-polynomial handling, and the iterative inverse path for +/// cases where only forward coefficients are available. +/// +/// +/// PROJ: horner. +internal sealed class HornerMathTransform : MathTransform +{ + private const int MaximumSupportedDegree = 10000; + private const int MaxInverseIterations = 32; + private const double DefaultRange = 500000d; + private const double DefaultInverseTolerance = 0.001d; + private const double DeterminantTolerance = 1e-24d; + + private readonly int degree; + private readonly bool isComplex; + private readonly bool hasExplicitInverse; + private readonly bool uneg; + private readonly bool vneg; + private readonly double range; + private readonly double inverseTolerance; + + private readonly double[] fwdU; + private readonly double[] fwdV; + private readonly double[] invU; + private readonly double[] invV; + private readonly double[] fwdC; + private readonly double[] invC; + + private readonly double fwdOriginX; + private readonly double fwdOriginY; + private readonly double invOriginX; + private readonly double invOriginY; + + private readonly bool isInverted; + private MathTransform? inverse; + + private HornerMathTransform( + int degree, + bool isComplex, + bool hasExplicitInverse, + bool uneg, + bool vneg, + double range, + double inverseTolerance, + double[] fwdU, + double[] fwdV, + double[] invU, + double[] invV, + double[] fwdC, + double[] invC, + double fwdOriginX, + double fwdOriginY, + double invOriginX, + double invOriginY, + bool isInverted) + { + this.degree = degree; + this.isComplex = isComplex; + this.hasExplicitInverse = hasExplicitInverse; + this.uneg = uneg; + this.vneg = vneg; + this.range = range; + this.inverseTolerance = inverseTolerance; + this.fwdU = fwdU; + this.fwdV = fwdV; + this.invU = invU; + this.invV = invV; + this.fwdC = fwdC; + this.invC = invC; + this.fwdOriginX = fwdOriginX; + this.fwdOriginY = fwdOriginY; + this.invOriginX = invOriginX; + this.invOriginY = invOriginY; + this.isInverted = isInverted; + } + + private HornerMathTransform(HornerMathTransform source, bool isInverted) + { + this.degree = source.degree; + this.isComplex = source.isComplex; + this.hasExplicitInverse = source.hasExplicitInverse; + this.uneg = source.uneg; + this.vneg = source.vneg; + this.range = source.range; + this.inverseTolerance = source.inverseTolerance; + this.fwdU = source.fwdU; + this.fwdV = source.fwdV; + this.invU = source.invU; + this.invV = source.invV; + this.fwdC = source.fwdC; + this.invC = source.invC; + this.fwdOriginX = source.fwdOriginX; + this.fwdOriginY = source.fwdOriginY; + this.invOriginX = source.invOriginX; + this.invOriginY = source.invOriginY; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return false; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new HornerMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("HornerMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + _ = z; + if (this.isComplex) + { + if (!this.isInverted) + { + this.TransformComplexForward(ref x, ref y); + return; + } + + if (this.hasExplicitInverse) + { + this.TransformComplexInverse(ref x, ref y); + } + else + { + this.TransformComplexIterativeInverse(ref x, ref y); + } + + return; + } + + if (!this.isInverted) + { + this.TransformRealForward(ref x, ref y); + return; + } + + if (this.hasExplicitInverse) + { + this.TransformRealInverse(ref x, ref y); + } + else + { + this.TransformRealIterativeInverse(ref x, ref y); + } + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "horner arguments were null."; + return false; + } + + if (!TryParseDegree(args, out int degree, out skipReason)) + { + return false; + } + + bool isComplex = args.ContainsKey("fwd_c") || args.ContainsKey("inv_c"); + bool hasExplicitInverse = isComplex + ? args.ContainsKey("inv_c") || args.ContainsKey("inv_origin") + : args.ContainsKey("inv_u") || args.ContainsKey("inv_v") || args.ContainsKey("inv_origin"); + int coefficientCount = isComplex + ? GetComplexCoefficientCount(degree) + : GetRealCoefficientCount(degree); + + double[] fwdU = []; + double[] fwdV = []; + double[] invU = []; + double[] invV = []; + double[] fwdC = []; + double[] invC = []; + + if (isComplex) + { + if (!TryParseCoefficientList(args, "fwd_c", coefficientCount, out double[]? parsedFwdC, out skipReason)) + { + return false; + } + + fwdC = ArgumentGuard.ThrowIfNull(parsedFwdC, nameof(parsedFwdC)); + + if (hasExplicitInverse) + { + if (!TryParseCoefficientList(args, "inv_c", coefficientCount, out double[]? parsedInvC, out skipReason)) + { + return false; + } + + invC = ArgumentGuard.ThrowIfNull(parsedInvC, nameof(parsedInvC)); + } + } + else + { + if (!TryParseCoefficientList(args, "fwd_u", coefficientCount, out double[]? parsedFwdU, out skipReason) + || !TryParseCoefficientList(args, "fwd_v", coefficientCount, out double[]? parsedFwdV, out skipReason)) + { + return false; + } + + fwdU = ArgumentGuard.ThrowIfNull(parsedFwdU, nameof(parsedFwdU)); + fwdV = ArgumentGuard.ThrowIfNull(parsedFwdV, nameof(parsedFwdV)); + + if (hasExplicitInverse) + { + if (!TryParseCoefficientList(args, "inv_u", coefficientCount, out double[]? parsedInvU, out skipReason) + || !TryParseCoefficientList(args, "inv_v", coefficientCount, out double[]? parsedInvV, out skipReason)) + { + return false; + } + + invU = ArgumentGuard.ThrowIfNull(parsedInvU, nameof(parsedInvU)); + invV = ArgumentGuard.ThrowIfNull(parsedInvV, nameof(parsedInvV)); + } + } + + if (!TryParseOrigin(args, "fwd_origin", out double fwdOriginX, out double fwdOriginY, out skipReason)) + { + return false; + } + + double invOriginX = 0d; + double invOriginY = 0d; + if (hasExplicitInverse + && !TryParseOrigin(args, "inv_origin", out invOriginX, out invOriginY, out skipReason)) + { + return false; + } + + if (!TryParseOptionalSingle(args, "range", DefaultRange, out double range, out skipReason)) + { + return false; + } + + if (range <= 0d) + { + skipReason = "Invalid value for +range."; + return false; + } + + if (!TryParseOptionalSingle(args, "inv_tolerance", DefaultInverseTolerance, out double inverseTolerance, out skipReason)) + { + return false; + } + + if (!args.ContainsKey("inv_tolerance") && args.ContainsKey("tolerance")) + { + if (!TryParseOptionalSingle(args, "tolerance", DefaultInverseTolerance, out inverseTolerance, out skipReason)) + { + return false; + } + } + + if (inverseTolerance <= 0d) + { + skipReason = "Invalid value for +inv_tolerance."; + return false; + } + + transform = new HornerMathTransform( + degree, + isComplex, + hasExplicitInverse, + args.ContainsKey("uneg"), + args.ContainsKey("vneg"), + range, + inverseTolerance, + fwdU, + fwdV, + invU, + invV, + fwdC, + invC, + fwdOriginX, + fwdOriginY, + invOriginX, + invOriginY, + false); + + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + + private static int GetRealCoefficientCount(int order) + { + return ((order + 1) * (order + 2)) / 2; + } + + private static int GetComplexCoefficientCount(int order) + { + return (2 * order) + 2; + } + + private static bool TryParseDegree( + Dictionary args, + out int degree, + out string? skipReason) + { + degree = 0; + skipReason = null; + + if (!args.TryGetValue("deg", out string? degreeToken) || string.IsNullOrWhiteSpace(degreeToken)) + { + skipReason = "Must specify polynomial degree, (+deg=n)"; + return false; + } + + if (!int.TryParse(degreeToken, NumberStyles.Integer, CultureInfo.InvariantCulture, out degree)) + { + skipReason = "Invalid value for +deg."; + return false; + } + + if (degree is < 0 or > MaximumSupportedDegree) + { + skipReason = $"Degree is unreasonable: {degree.ToString(CultureInfo.InvariantCulture)}"; + return false; + } + + return true; + } + + private static bool TryParseCoefficientList( + Dictionary args, + string key, + int expectedCount, + [NotNullWhen(true)] out double[]? coefficients, + out string? skipReason) + { + coefficients = null; + skipReason = null; + + if (!args.TryGetValue(key, out string? token) || string.IsNullOrWhiteSpace(token)) + { + skipReason = $"missing {key}"; + return false; + } + + double[] values = new double[expectedCount]; + CsvParseStatus parseStatus = SpanParseUtility.TryParseCsvValues(token.AsSpan(), values, out int parsedCount); + if (parseStatus != CsvParseStatus.Success || parsedCount != expectedCount) + { + skipReason = $"Malformed polynomium set {key}. need {expectedCount.ToString(CultureInfo.InvariantCulture)} coefs"; + return false; + } + + coefficients = values; + return true; + } + + private static bool TryParseOrigin( + Dictionary args, + string key, + out double x, + out double y, + out string? skipReason) + { + x = 0d; + y = 0d; + if (!TryParseCoefficientList(args, key, 2, out double[]? values, out skipReason)) + { + return false; + } + + x = values[0]; + y = values[1]; + return true; + } + + private static bool TryParseOptionalSingle( + Dictionary args, + string key, + double defaultValue, + out double value, + out string? skipReason) + { + value = defaultValue; + skipReason = null; + + if (!args.TryGetValue(key, out string? token) || string.IsNullOrWhiteSpace(token)) + { + return true; + } + + Span parsed = stackalloc double[1]; + CsvParseStatus parseStatus = SpanParseUtility.TryParseCsvValues(token.AsSpan(), parsed, out int parsedCount); + if (parseStatus != CsvParseStatus.Success || parsedCount != 1) + { + skipReason = $"Invalid value for +{key}."; + return false; + } + + value = parsed[0]; + return true; + } + + private static (double E, double N) EvaluateReal( + int degree, + double[] cx, + double[] cy, + double e, + double n, + int orderOffset) + { + int size = GetRealCoefficientCount(degree); + int xIndex = size - 1; + int yIndex = size - 1; + + double northing = cy[yIndex--]; + double easting = cx[xIndex--]; + for (int r = degree; r > orderOffset; r--) + { + double u = cy[yIndex--]; + double v = cx[xIndex--]; + for (int c = degree; c >= r; c--) + { + u = (n * u) + cy[yIndex--]; + v = (e * v) + cx[xIndex--]; + } + + northing = (e * northing) + u; + easting = (n * easting) + v; + } + + return (easting, northing); + } + + private static double EvaluateSingleReal(int degree, double[] coefficients, double value, int orderOffset) + { + int index = degree; + double result = coefficients[index--]; + for (int r = degree; r > orderOffset; r--) + { + result = (value * result) + coefficients[index--]; + } + + return result; + } + + private static (double E, double N) EvaluateComplex( + int degree, + double[] coefficients, + double e, + double n, + int orderOffset) + { + int size = GetComplexCoefficientCount(degree); + int begin = orderOffset * 2; + int index = size - 1; + + double outE = coefficients[index--]; + double outN = coefficients[index--]; + while (index >= begin) + { + double intermediate = (n * outE) + (e * outN) + coefficients[index--]; + outN = (n * outN) - (e * outE) + coefficients[index--]; + outE = intermediate; + } + + return (outE, outN); + } + + private void TransformRealForward(ref double x, ref double y) + { + double e = x - this.fwdOriginX; + double n = y - this.fwdOriginY; + this.ValidateRange(n, e); + (x, y) = EvaluateReal(this.degree, this.fwdU, this.fwdV, e, n, 0); + } + + private void TransformRealInverse(ref double x, ref double y) + { + double e = x - this.invOriginX; + double n = y - this.invOriginY; + this.ValidateRange(n, e); + (x, y) = EvaluateReal(this.degree, this.invU, this.invV, e, n, 0); + } + + private void TransformRealIterativeInverse(ref double x, ref double y) + { + double e = x; + double n = y; + this.ValidateRange(n, e); + + double deltaE = e - this.fwdU[0]; + double deltaN = n - this.fwdV[0]; + double x0 = 0d; + double y0 = 0d; + for (int i = 0; i < MaxInverseIterations; i++) + { + (double mb, double mc) = EvaluateReal(this.degree, this.fwdU, this.fwdV, x0, y0, 1); + double ma = EvaluateSingleReal(this.degree, this.fwdU, x0, 1); + double md = EvaluateSingleReal(this.degree, this.fwdV, y0, 1); + + double determinant = (ma * md) - (mb * mc); + if (Math.Abs(determinant) <= DeterminantTolerance) + { + break; + } + + double inverseDeterminant = 1d / determinant; + double nextX = inverseDeterminant * ((md * deltaE) - (mb * deltaN)); + double nextY = inverseDeterminant * ((ma * deltaN) - (mc * deltaE)); + bool converged = Math.Abs(nextX - x0) < this.inverseTolerance + && Math.Abs(nextY - y0) < this.inverseTolerance; + x0 = nextX; + y0 = nextY; + if (converged) + { + x = x0 + this.fwdOriginX; + y = y0 + this.fwdOriginY; + return; + } + } + + TransformationThrowHelper.ThrowInvalidOperation("horner inverse iteration did not converge."); + } + + private void TransformComplexForward(ref double x, ref double y) + { + this.TransformComplexDefault(ref x, ref y, true); + } + + private void TransformComplexInverse(ref double x, ref double y) + { + this.TransformComplexDefault(ref x, ref y, false); + } + + private void TransformComplexDefault(ref double x, ref double y, bool forward) + { + double e = forward ? x - this.fwdOriginX : x - this.invOriginX; + double n = forward ? y - this.fwdOriginY : y - this.invOriginY; + + if (this.uneg) + { + e = -e; + } + + if (this.vneg) + { + n = -n; + } + + this.ValidateRange(n, e); + (x, y) = EvaluateComplex(this.degree, forward ? this.fwdC : this.invC, e, n, 0); + } + + private void TransformComplexIterativeInverse(ref double x, ref double y) + { + double e = x; + double n = y; + this.ValidateRange(n, e); + + // Real component corresponds to northing, imaginary component to easting. + double dzReal = n - this.fwdC[0]; + double dzImaginary = e - this.fwdC[1]; + double w0Real = 0d; + double w0Imaginary = 0d; + for (int i = 0; i < MaxInverseIterations; i++) + { + (double derivativeE, double derivativeN) = EvaluateComplex(this.degree, this.fwdC, w0Imaginary, w0Real, 1); + double detReal = derivativeN; + double detImaginary = derivativeE; + double denominator = (detReal * detReal) + (detImaginary * detImaginary); + if (denominator <= DeterminantTolerance) + { + break; + } + + double nextReal = ((dzReal * detReal) + (dzImaginary * detImaginary)) / denominator; + double nextImaginary = ((dzImaginary * detReal) - (dzReal * detImaginary)) / denominator; + + bool converged = Math.Abs(nextReal - w0Real) < this.inverseTolerance + && Math.Abs(nextImaginary - w0Imaginary) < this.inverseTolerance; + w0Real = nextReal; + w0Imaginary = nextImaginary; + if (converged) + { + double outE = w0Imaginary; + double outN = w0Real; + if (this.uneg) + { + outE = -outE; + } + + if (this.vneg) + { + outN = -outN; + } + + x = outE + this.fwdOriginX; + y = outN + this.fwdOriginY; + return; + } + } + + TransformationThrowHelper.ThrowInvalidOperation("horner inverse iteration did not converge."); + } + + private void ValidateRange(double n, double e) + { + if (Math.Abs(n) > this.range || Math.Abs(e) > this.range) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside horner operation range."); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/ICoordinateTransformation.cs b/src/ProjNet/CoordinateSystems/Transformations/ICoordinateTransformation.cs index 7c48311b..8de3b6d3 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/ICoordinateTransformation.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/ICoordinateTransformation.cs @@ -1,83 +1,52 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -namespace ProjNet.CoordinateSystems.Transformations +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +/// +/// Describes a coordinate transformation. This interface only describes a +/// coordinate transformation, it does not actually perform the transform +/// operation on points. To transform points you must use a math transform. +/// +public interface ICoordinateTransformation : ICoordinateTransformationCore { /// - /// Describes core information of a coordinate transformation + /// Gets human readable description of domain in source coordinate system. /// - public interface ICoordinateTransformationCore - { - - /// - /// Source coordinate system. - /// - CoordinateSystem SourceCS { get; } - - /// - /// Target coordinate system. - /// - CoordinateSystem TargetCS { get; } - } + string AreaOfUse { get; } /// - /// Describes a coordinate transformation. This interface only describes a - /// coordinate transformation, it does not actually perform the transform - /// operation on points. To transform points you must use a math transform. + /// Gets authority which defined transformation and parameter values. /// - public interface ICoordinateTransformation : ICoordinateTransformationCore - { - /// - /// Human readable description of domain in source coordinate system. - /// - string AreaOfUse { get; } + /// + /// An Authority is an organization that maintains definitions of Authority Codes. For example the European Petroleum Survey Group (EPSG) maintains a database of coordinate systems, and other spatial referencing objects, where each object has a code number ID. For example, the EPSG code for a WGS84 Lat/Lon coordinate system is �4326�. + /// + string Authority { get; } - /// - /// Authority which defined transformation and parameter values. - /// - /// - /// An Authority is an organization that maintains definitions of Authority Codes. For example the European Petroleum Survey Group (EPSG) maintains a database of coordinate systems, and other spatial referencing objects, where each object has a code number ID. For example, the EPSG code for a WGS84 Lat/Lon coordinate system is �4326� - /// - string Authority { get; } - - /// - /// Code used by authority to identify transformation. An empty string is used for no code. - /// - /// The AuthorityCode is a compact string defined by an Authority to reference a particular spatial reference object. For example, the European Survey Group (EPSG) authority uses 32 bit integers to reference coordinate systems, so all their code strings will consist of a few digits. The EPSG code for WGS84 Lat/Lon is �4326�. - long AuthorityCode { get; } + /// + /// Gets code used by authority to identify transformation. An empty string is used for no code. + /// + /// The AuthorityCode is a compact string defined by an Authority to reference a particular spatial reference object. For example, the European Survey Group (EPSG) authority uses 32 bit integers to reference coordinate systems, so all their code strings will consist of a few digits. The EPSG code for WGS84 Lat/Lon is �4326�. + long AuthorityCode { get; } - /// - /// Name of transformation. - /// - string Name { get; } + /// + /// Gets name of transformation. + /// + string Name { get; } - /// - /// Gets the provider-supplied remarks. - /// - string Remarks { get; } + /// + /// Gets the provider-supplied remarks. + /// + string Remarks { get; } - /// - /// Gets math transform. - /// - MathTransform MathTransform { get; } + /// + /// Gets math transform. + /// + MathTransform MathTransform { get; } - /// - /// Semantic type of transform. For example, a datum transformation or a coordinate conversion. - /// - TransformType TransformType { get; } - } + /// + /// Gets semantic type of transform. For example, a datum transformation or a coordinate conversion. + /// + TransformType TransformType { get; } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/ICoordinateTransformationCore.cs b/src/ProjNet/CoordinateSystems/Transformations/ICoordinateTransformationCore.cs new file mode 100644 index 00000000..5f27d324 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/ICoordinateTransformationCore.cs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +/// +/// Describes core information of a coordinate transformation. +/// +public interface ICoordinateTransformationCore +{ + /// + /// Gets source coordinate system. + /// + CoordinateSystem SourceCS { get; } + + /// + /// Gets target coordinate system. + /// + CoordinateSystem TargetCS { get; } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/IdentityMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/IdentityMathTransform.cs new file mode 100644 index 00000000..12f067eb --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/IdentityMathTransform.cs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using ProjNet.IO.Wkt; + +/// +/// Represents a pass-through transform that leaves all ordinates unchanged. +/// +/// +/// Identity is the mathematical no-op transform: all ordinates pass through +/// unchanged, and the transform is its own inverse. +/// +/// PROJ: no operation. +internal sealed class IdentityMathTransform : MathTransform +{ + private readonly int dimension; + + /// + /// Initializes a new instance of the class. + /// + /// Requested transform dimension; values below 2 are promoted to 2. + internal IdentityMathTransform(int dimension) + { + this.dimension = dimension < 2 ? 2 : dimension; + } + + /// + public override int DimSource => this.dimension; + + /// + public override int DimTarget => this.dimension; + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override WktNode ToWktNode() + { + return new WktKeywordNode( + "PARAM_MT", + new WktQuotedString("Identity"), + new WktKeywordNode( + "PARAMETER", + new WktQuotedString("dimension"), + new WktInteger(this.dimension))); + } + + /// + public override bool Identity() => true; + + /// + public override MathTransform Inverse() => this; + + /// + public override void Invert() + { + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/LongitudeWrapMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/LongitudeWrapMathTransform.cs new file mode 100644 index 00000000..6fa8aec0 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/LongitudeWrapMathTransform.cs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Normalizes longitudes around a configured wrap center. +/// +/// +/// The transform leaves all ordinates unchanged except longitude, which is mapped +/// into the half-open interval [wrapCenter - 180, wrapCenter + 180). +/// +internal sealed class LongitudeWrapMathTransform : MathTransform +{ + private readonly double wrapCenterDegrees; + + /// + /// Initializes a new instance of the class. + /// + /// Longitude wrap center in degrees. + internal LongitudeWrapMathTransform(double wrapCenterDegrees) + { + ArgumentGuard.ThrowIfNotFinite(wrapCenterDegrees, nameof(wrapCenterDegrees), "Longitude wrap center must be finite."); + this.wrapCenterDegrees = wrapCenterDegrees; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return false; + } + + /// + public override MathTransform Inverse() + { + return this; + } + + /// + public override void Invert() + { + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + x = WrapLongitude(x, this.wrapCenterDegrees); + } + + private static double WrapLongitude(double longitudeDegrees, double wrapCenterDegrees) + { + double wrapped = longitudeDegrees; + double lowerBound = wrapCenterDegrees - 180d; + double upperBound = wrapCenterDegrees + 180d; + + while (wrapped < lowerBound) + { + wrapped += 360d; + } + + while (wrapped >= upperBound) + { + wrapped -= 360d; + } + + return wrapped; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/MathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/MathTransform.cs index 7a96385d..221c3b6e 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/MathTransform.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/MathTransform.cs @@ -1,576 +1,798 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; +using System.Xml.Linq; using ProjNet.Geometries; - -namespace ProjNet.CoordinateSystems.Transformations +using ProjNet.IO.Wkt; + +/// +/// Abstract class for creating multi-dimensional coordinate points transformations. +/// +/// +/// If a client application wishes to query the source and target coordinate +/// systems of a transformation, then it should keep hold of the +/// object, and use the contained +/// math transform object whenever it wishes to perform a transform. +/// +/// Thread safety: Implementations that keep immutable transformation state are safe to share across +/// threads for concurrent read-only transform operations, and the built-in pure transforms follow +/// that model. Grid-backed transforms additionally depend on the shared grid-resolution behavior +/// configured through , so grid resolution or +/// reconfiguration may still serialize on that shared infrastructure. +/// +/// +public abstract class MathTransform { /// - /// Abstract class for creating multi-dimensional coordinate points transformations. + /// Constant for converting Degrees to Radians. /// - /// - /// If a client application wishes to query the source and target coordinate - /// systems of a transformation, then it should keep hold of the - /// object, and use the contained - /// math transform object whenever it wishes to perform a transform. - /// - [Serializable] - public abstract class MathTransform + protected const double D2R = Math.PI / 180; + + /// + /// Constant for converting Radians to Degrees. + /// + protected const double R2D = 180 / Math.PI; + + /// + /// Gets the dimension of input points. + /// + public abstract int DimSource { get; } + + /// + /// Gets the dimension of output points. + /// + public abstract int DimTarget { get; } + + /// + /// Gets a Well-Known text representation of this object. + /// + public virtual string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public virtual string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Gets a value indicating whether can return a usable inverse transform. + /// + public virtual bool IsInvertible => true; + + /// + /// Converts this transform to a WKT syntax tree node. + /// + /// A representing this transform. + public virtual WktNode ToWktNode() => throw new NotSupportedException("WKT node serialization is not supported for this transform type."); + + /// + /// Returns an XML representation of this transform as an . + /// + /// An containing the XML representation. + public virtual XElement ToXml() => throw new NotSupportedException("XML serialization is not supported for this transform type."); + + /// + /// Tests whether this transform does not move any points. + /// + /// when the transform is an identity; otherwise . + public virtual bool Identity() { - /// - /// Gets the dimension of input points. - /// - public abstract int DimSource { get; } - - /// - /// Gets the dimension of output points. - /// - public abstract int DimTarget { get; } - - /// - /// Tests whether this transform does not move any points. - /// - /// - public virtual bool Identity() - { - throw new NotImplementedException(); - } - - /// - /// Gets a Well-Known text representation of this object. - /// - public abstract string WKT { get; } - - /// - /// Gets an XML representation of this object. - /// - public abstract string XML { get; } - - /// - /// Gets the derivative of this transform at a point. If the transform does - /// not have a well-defined derivative at the point, then this function should - /// fail in the usual way for the DCP. The derivative is the matrix of the - /// non-translating portion of the approximate affine map at the point. The - /// matrix will have dimensions corresponding to the source and target - /// coordinate systems. If the input dimension is M, and the output dimension - /// is N, then the matrix will have size [M][N]. The elements of the matrix - /// {elt[n][m] : n=0..(N-1)} form a vector in the output space which is - /// parallel to the displacement caused by a small change in the m'th ordinate - /// in the input space. - /// - /// - /// - public virtual double[,] Derivative(double[] point) - { - throw new NotImplementedException(); - } - - /// - /// Gets transformed convex hull. - /// - /// - /// The supplied ordinates are interpreted as a sequence of points, which generates a convex - /// hull in the source space. The returned sequence of ordinates represents a convex hull in the - /// output space. The number of output points will often be different from the number of input - /// points. Each of the input points should be inside the valid domain (this can be checked by - /// testing the points' domain flags individually). However, the convex hull of the input points - /// may go outside the valid domain. The returned convex hull should contain the transformed image - /// of the intersection of the source convex hull and the source domain. - /// A convex hull is a shape in a coordinate system, where if two positions A and B are - /// inside the shape, then all positions in the straight line between A and B are also inside - /// the shape. So in 3D a cube and a sphere are both convex hulls. Other less obvious examples - /// of convex hulls are straight lines, and single points. (A single point is a convex hull, - /// because the positions A and B must both be the same - i.e. the point itself. So the straight - /// line between A and B has zero length.) - /// Some examples of shapes that are NOT convex hulls are donuts, and horseshoes. - /// - /// - /// - public virtual List GetCodomainConvexHull(List points) - { - throw new NotImplementedException(); - } - - /// - /// Gets flags classifying domain points within a convex hull. - /// - /// - /// The supplied ordinates are interpreted as a sequence of points, which - /// generates a convex hull in the source space. Conceptually, each of the - /// (usually infinite) points inside the convex hull is then tested against - /// the source domain. The flags of all these tests are then combined. In - /// practice, implementations of different transforms will use different - /// short-cuts to avoid doing an infinite number of tests. - /// - /// - /// - public virtual DomainFlags GetDomainFlags(List points) - { - throw new NotImplementedException(); - } - - /// - /// Creates the inverse transform of this object. - /// - /// This method may fail if the transform is not one to one. However, all cartographic projections should succeed. - /// - public abstract MathTransform Inverse(); - - /// - /// Reverses the transformation - /// - public abstract void Invert(); - - /// - /// Constant for converting Degrees to Radians - /// - protected const double D2R = Math.PI / 180; - - /// - /// Converts a degree-value () to a radian-value by multiplying it with / 180.0 - /// - protected static double DegreesToRadians(double deg) - { - return D2R * deg; - } - - /// - /// Converts a series of degree-values () to a radian-values by multiplying them with / 180.0 - /// - /// A series of degree-values - /// A stride value - protected static void DegreesToRadians(Span degrees, int stride) - { - MultiplyInPlace(degrees, stride, D2R); - } - - /// - /// Constant for converting Radians to Degrees - /// - protected const double R2D = 180 / Math.PI; - - /// - /// Converts a radian-value () to a degree-value by multiplying it with 180.0 / - /// - /// - /// - protected static double RadiansToDegrees(double rad) - { - return R2D * rad; - } - - /// - /// Converts a series of radian-values () to a degrees-values by multiplying them with 180.0 / - /// - /// A series of radian-values - /// A stride value - protected static void RadiansToDegrees(Span radians, int stride) - { - MultiplyInPlace(radians, stride, R2D); - } - - /// - /// Transforms a coordinate point. The passed parameter point should not be modified. - /// - /// - /// - public double[] Transform(double[] point) - { - double x = point[0]; - double y = point[1]; - double z = point.Length < 3 ? 0 : point[2]; - - (x, y, z) = Transform(x, y, z); - - return DimTarget == 2 - ? new[] { x, y } - : new[] { x, y, z }; - } - - /// - /// Transforms a list of coordinate point ordinal values. - /// - /// - /// This method is provided for efficiently transforming many points. The supplied array - /// of ordinal values will contain packed ordinal values. For example, if the source - /// dimension is 3, then the ordinals will be packed in this order (x0,y0,z0,x1,y1,z1 ...). - /// The size of the passed array must be an integer multiple of DimSource. The returned - /// ordinal values are packed in a similar way. In some DCPs. the ordinals may be - /// transformed in-place, and the returned array may be the same as the passed array. - /// So any client code should not attempt to reuse the passed ordinal values (although - /// they can certainly reuse the passed array). If there is any problem then the server - /// implementation will throw an exception. If this happens then the client should not - /// make any assumptions about the state of the ordinal values. - /// - /// - /// - public IList TransformList(IList points) + throw new NotImplementedException(); + } + + /// + /// Gets the derivative of this transform at a point. If the transform does + /// not have a well-defined derivative at the point, then this function should + /// fail in the usual way for the DCP. The derivative is the matrix of the + /// non-translating portion of the approximate affine map at the point. The + /// matrix will have dimensions corresponding to the source and target + /// coordinate systems. If the input dimension is M, and the output dimension + /// is N, then the matrix will have size [M][N]. The elements of the matrix + /// {elt[n][m] : n=0..(N-1)} form a vector in the output space which is + /// parallel to the displacement caused by a small change in the m'th ordinate + /// in the input space. + /// + /// The ordinate values of the point at which to compute the derivative. + /// An [N][M] matrix of partial derivatives where N is the output dimension and M is the input dimension. + public virtual double[,] Derivative(double[] point) + { + point = ArgumentGuard.ThrowIfNull(point, nameof(point)); + if (point.Length < 2) { - var result = new List(points.Count); - foreach (double[] point in points) - { - double x = point[0]; - double y = point[1]; - double z = point.Length < 3 ? 0 : point[2]; - (x, y, z) = Transform(x, y, z); - - result.Add(DimTarget == 2 - ? new[] { x, y } - : new[] { x, y, z }); - } + ArgumentGuard.ThrowArgument("At least two ordinate values are required.", nameof(point)); + } + + int sourceDimensions = point.Length; + int targetDimensions = this.GetResultDimensions(sourceDimensions); + double[,] derivative = new double[targetDimensions, sourceDimensions]; + double[] forwardPoint = new double[sourceDimensions]; + double[] backwardPoint = new double[sourceDimensions]; + + for (int sourceIndex = 0; sourceIndex < sourceDimensions; sourceIndex++) + { + point.AsSpan().CopyTo(forwardPoint); + point.AsSpan().CopyTo(backwardPoint); + + double step = GetDerivativeStepSize(point[sourceIndex]); + forwardPoint[sourceIndex] += step; + backwardPoint[sourceIndex] -= step; - return result; - } - - /// - /// Transforms a single 2-dimensional point - /// - /// The ordinate value on the first axis, either x or longitude. - /// The ordinate value on the second axis, either y or latitude. - /// The transformed x- and y-ordinate values - public (double x, double y) Transform(double x, double y) - { - double z = 0; - Transform(ref x, ref y, ref z); - return (x, y); - } - - /// - /// Transforms a single 3-dimensional point - /// - /// The ordinate value on the first axis, either x or longitude. - /// The ordinate value on the second axis, either y or latitude. - /// The ordinate value on the third axis, either z, height or altitude - /// The transformed x-, y- and z-ordinate values - public (double o1, double o2, double o3) Transform(double x, double y, double z) - { - Transform(ref x, ref y, ref z); - return (x, y, z); - } - - /// - /// Transforms a single 2-dimensional point in-place - /// - /// The ordinate value on the first axis, either x or longitude. - /// The ordinate value on the second axis, either y or latitude. - public void Transform(ref double x, ref double y) - { - double z = 0d; - Transform(ref x, ref y, ref z); - } - - /// - /// Transforms a single 3-dimensional point in-place - /// - /// The ordinate value on the first axis, either x or longitude. - /// The ordinate value on the second axis, either y or latitude. - /// The ordinate value on the third axis, either z, height or altitude - public abstract void Transform(ref double x, ref double y, ref double z); - - /// - /// Core method to transform a series of points defined by their ordinates. - /// The transformation is performed in-place. - /// - /// A series of x-ordinate values - /// A series of y-ordinate values - /// A series of z-ordinate values - /// A stride value for the x-ordinate series - /// A stride value for the y-ordinate series - /// A stride value for the z-ordinate series - protected virtual void TransformCore(Span xs, Span ys, Span zs, - int strideX, int strideY, int strideZ) - { - for (int i = 0, j = 0, k = 0; i < xs.Length; i += strideX, j += strideY, k += strideZ) + double[] forwardValue = this.Transform(forwardPoint); + double[] backwardValue = this.Transform(backwardPoint); + double scale = 1d / (2d * step); + + for (int targetIndex = 0; targetIndex < targetDimensions; targetIndex++) { - Transform(ref xs[i], ref ys[j], ref zs[k]); + derivative[targetIndex, sourceIndex] = (forwardValue[targetIndex] - backwardValue[targetIndex]) * scale; } } - /// - /// Core method to transform a series of points defined by their ordinates. - /// The transformation is performed in-place. - /// - /// A series of x-ordinate values - /// A series of y-ordinate values - /// A stride value for the x-ordinate series - /// A stride value for the y-ordinate series - /// If the provided span and stride values don't result in matching number of ordinates. - public void Transform(Span xs, Span ys, int strideX = 1, int strideY = 1) + return derivative; + } + + /// + /// Gets transformed convex hull. + /// + /// + /// The supplied ordinates are interpreted as a sequence of points, which generates a convex + /// hull in the source space. The returned sequence of ordinates represents a convex hull in the + /// output space. The number of output points will often be different from the number of input + /// points. Each of the input points should be inside the valid domain (this can be checked by + /// testing the points' domain flags individually). However, the convex hull of the input points + /// may go outside the valid domain. The returned convex hull should contain the transformed image + /// of the intersection of the source convex hull and the source domain. + /// A convex hull is a shape in a coordinate system, where if two positions A and B are + /// inside the shape, then all positions in the straight line between A and B are also inside + /// the shape. So in 3D a cube and a sphere are both convex hulls. Other less obvious examples + /// of convex hulls are straight lines, and single points. (A single point is a convex hull, + /// because the positions A and B must both be the same - i.e. the point itself. So the straight + /// line between A and B has zero length.) + /// Some examples of shapes that are NOT convex hulls are donuts, and horseshoes. + /// + /// Packed ordinate values representing the source convex hull. + /// Packed ordinate values representing the transformed convex hull in the output space. + public virtual List GetCodomainConvexHull(List points) + { + throw new NotImplementedException(); + } + + /// + /// Gets transformed convex hull. + /// + /// Packed ordinate values representing the source convex hull. + /// Packed ordinate values representing the transformed convex hull in the output space. + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Design", + "CA1002:Do not expose generic lists", + Justification = "Legacy API surface; span overload added as a non-breaking alternative.")] + public virtual List GetCodomainConvexHull(ReadOnlySpan points) + { + return this.GetCodomainConvexHull(CreatePointList(points)); + } + + /// + /// Gets flags classifying domain points within a convex hull. + /// + /// + /// The supplied ordinates are interpreted as a sequence of points, which + /// generates a convex hull in the source space. Conceptually, each of the + /// (usually infinite) points inside the convex hull is then tested against + /// the source domain. The flags of all these tests are then combined. In + /// practice, implementations of different transforms will use different + /// short-cuts to avoid doing an infinite number of tests. + /// + /// Packed ordinate values representing the source convex hull. + /// Combined for all points inside the source convex hull. + public virtual DomainFlags GetDomainFlags(List points) + { + throw new NotImplementedException(); + } + + /// + /// Gets flags classifying domain points within a convex hull. + /// + /// Packed ordinate values representing the source convex hull. + /// Combined for all points inside the source convex hull. + public virtual DomainFlags GetDomainFlags(ReadOnlySpan points) + { + return this.GetDomainFlags(CreatePointList(points)); + } + + /// + /// Creates the inverse transform of this object. + /// + /// This method may fail if the transform is not one to one. However, all cartographic projections should succeed. + /// A that reverses this transform. + public abstract MathTransform Inverse(); + + /// + /// Reverses the transformation. + /// + public abstract void Invert(); + + /// + /// Transforms a coordinate point. The passed parameter point should not be modified. + /// + /// The input coordinate as an array of ordinate values. + /// The transformed coordinate as an array of ordinate values. + public double[] Transform(double[] point) + { + point = ArgumentGuard.ThrowIfNull(point, nameof(point)); + + int pointLength = point.Length; + if (pointLength < 2) { - int elementsX = xs.Length / strideX + xs.Length % strideX != 0 ? 1 : 0; - int elementsY = ys.Length / strideY + ys.Length % strideY != 0 ? 1 : 0; + ArgumentGuard.ThrowArgument("At least two ordinate values are required.", nameof(point)); + } - if (elementsX != elementsY) - throw new ArgumentException("Spans of ordinate values don't match in size."); + int resultDimensions = this.GetResultDimensions(pointLength); + if (resultDimensions <= 4) + { + Span scratch = stackalloc double[4]; + Span scratchResult = scratch[..resultDimensions]; + this.TransformPoint(point, pointLength, scratchResult, resultDimensions); + double[] transformedSmall = new double[resultDimensions]; + scratchResult.CopyTo(transformedSmall); + return transformedSmall; + } - Span dummyZ = stackalloc double[] { 0 }; - TransformCore(xs, ys, dummyZ, strideX, strideY, 0); - } - - /// - /// Core method to transform a series of points defined by their ordinates. - /// The transformation is performed in-place. - /// - /// A series of x-ordinate values - /// A series of y-ordinate values - /// A series of z-ordinate values - /// A stride value for the x-ordinate series - /// A stride value for the y-ordinate series - /// A stride value for the z-ordinate series - /// If the provided span and stride values don't result in matching number of ordinates. - public void Transform(Span xs, Span ys, Span zs, - int strideX = 1, int strideY = 1, int strideZ = 1) - { - int elementsX = xs.Length / strideX + xs.Length % strideX != 0 ? 1 : 0; - int elementsY = ys.Length / strideY + ys.Length % strideY != 0 ? 1 : 0; - if (elementsX != elementsY) - throw new ArgumentException("Spans of ordinate values don't match in size."); - - if (zs.IsEmpty) - { - Span dummyZ = stackalloc double[] { 0 }; - TransformCore(xs, ys, dummyZ, strideX, strideY, 0); - return; - } + double[] transformed = new double[resultDimensions]; + this.TransformPoint(point, pointLength, transformed, resultDimensions); + return transformed; + } - int elementsZ = zs.Length / strideZ + zs.Length % strideZ != 0 ? 1 : 0; - if (elementsZ != elementsX) - throw new ArgumentException("Spans of ordinate values don't match in size."); + /// + /// Transforms a coordinate point from into . + /// + /// The input coordinate as a readonly span of ordinate values. + /// Destination span that receives the transformed ordinate values. + /// + /// Thrown when has fewer than two ordinates or + /// is too small to hold the transformed ordinates. + /// + public void Transform(ReadOnlySpan point, Span result) + { + int pointLength = point.Length; + if (pointLength < 2) + { + ArgumentGuard.ThrowArgument("At least two ordinate values are required.", nameof(point)); + } - TransformCore(xs, ys, zs, strideX, strideY, strideZ); + int resultDimensions = this.GetResultDimensions(pointLength); + if (result.Length < resultDimensions) + { + ArgumentGuard.ThrowArgument( + "Destination span is too small to store the transformed coordinate.", + nameof(result)); } - /// - /// Transforms a series of 2-dimensional -points and (optionally) a series of z-ordinate values. - /// - /// A series of points - /// A series of z-ordinate values. - /// A stride value for z-ordinates - /// If the provided series' and buffers don't match in size. - public void Transform(Span xys, Span zs = default, int strideZ = 0) + this.TransformPoint(point, pointLength, result, resultDimensions); + } + + /// + /// Transforms a list of coordinate point ordinal values. + /// + /// + /// This method is provided for efficiently transforming many points. The supplied array + /// of ordinal values will contain packed ordinal values. For example, if the source + /// dimension is 3, then the ordinals will be packed in this order (x0,y0,z0,x1,y1,z1 ...). + /// The size of the passed array must be an integer multiple of DimSource. The returned + /// ordinal values are packed in a similar way. In some DCPs. the ordinals may be + /// transformed in-place, and the returned array may be the same as the passed array. + /// So any client code should not attempt to reuse the passed ordinal values (although + /// they can certainly reuse the passed array). If there is any problem then the server + /// implementation will throw an exception. If this happens then the client should not + /// make any assumptions about the state of the ordinal values. + /// + /// The packed ordinate values to transform. + /// The transformed packed ordinate values. + public IList TransformList(IList points) + { + points = ArgumentGuard.ThrowIfNull(points, nameof(points)); + + int minimumDimensions = this.DimTarget == 2 ? 2 : 3; + var result = new List(points.Count); + for (int pointIndex = 0; pointIndex < points.Count; pointIndex++) { - if (!zs.IsEmpty) - { - if (strideZ <= 0) strideZ = 1; - if (xys.Length != ((zs.Length / strideZ + (zs.Length % strideZ != 0 ? 1 : 0)))) - throw new ArgumentException("Provided spans don't match in size."); - } + double[] point = points[pointIndex]; + int pointLength = point.Length; + int resultDimensions = pointLength <= 3 + ? minimumDimensions + : Math.Max(minimumDimensions, pointLength); + double[] transformed = new double[resultDimensions]; + this.TransformPoint(point, pointLength, transformed, resultDimensions); + result.Add(transformed); + } - var read = MemoryMarshal.Cast(xys); - var inXs = read.Slice(0); - var inYs = read.Slice(1); + return result; + } - if (zs.IsEmpty) - { - Span dummyZ = stackalloc double[] { 0 }; - TransformCore(inXs, inYs, dummyZ, 2, 2, 0); - } - else - { - TransformCore(inXs, inYs, zs, 2, 2, strideZ); - } + /// + /// Transforms a single 2-dimensional point. + /// + /// + /// Input and output units depend on the source and target coordinate system, for example radians or degrees + /// for geographic systems and metres for projected systems. + /// + /// The ordinate value on the first axis, either x or longitude. + /// The ordinate value on the second axis, either y or latitude. + /// The transformed x- and y-ordinate values. + public (double X, double Y) Transform(double x, double y) + { + double z = 0; + this.Transform(ref x, ref y, ref z); + return (x, y); + } + + /// + /// Transforms a single 3-dimensional point. + /// + /// + /// Input and output units depend on the source and target coordinate system, for example radians or degrees + /// for geographic systems and metres for projected systems. + /// + /// The ordinate value on the first axis, either x or longitude. + /// The ordinate value on the second axis, either y or latitude. + /// The ordinate value on the third axis, either z, height or altitude. + /// The transformed first, second, and third ordinate values. + public (double O1, double O2, double O3) Transform(double x, double y, double z) + { + this.Transform(ref x, ref y, ref z); + return (x, y, z); + } + + /// + /// Transforms a single 2-dimensional point in-place. + /// + /// The ordinate value on the first axis, either x or longitude. + /// The ordinate value on the second axis, either y or latitude. + public void Transform(ref double x, ref double y) + { + double z = 0d; + this.Transform(ref x, ref y, ref z); + } + + /// + /// Transforms a single 3-dimensional point in-place. + /// + /// The ordinate value on the first axis, either x or longitude. + /// The ordinate value on the second axis, either y or latitude. + /// The ordinate value on the third axis, either z, height or altitude. + public abstract void Transform(ref double x, ref double y, ref double z); + + /// + /// Core method to transform a series of points defined by their ordinates. + /// The transformation is performed in-place. + /// + /// A series of x-ordinate values. + /// A series of y-ordinate values. + /// A stride value for the x-ordinate series. + /// A stride value for the y-ordinate series. + /// If the provided span and stride values don't result in matching number of ordinates. + public void Transform(Span xs, Span ys, int strideX = 1, int strideY = 1) + { + int elementsX = (xs.Length / strideX) + (xs.Length % strideX) != 0 ? 1 : 0; + int elementsY = (ys.Length / strideY) + (ys.Length % strideY) != 0 ? 1 : 0; + + if (elementsX != elementsY) + { + ArgumentGuard.ThrowArgument("Spans of ordinate values don't match in size.", nameof(ys)); } - /// - /// Transforms a series of 3-dimensional -points. - /// - /// A series of points - public void Transform(Span xyzs) + Span dummyZ = stackalloc double[] { 0 }; + this.TransformCore(xs, ys, dummyZ, strideX, strideY, 0); + } + + /// + /// Core method to transform a series of points defined by their ordinates. + /// The transformation is performed in-place. + /// + /// A series of x-ordinate values. + /// A series of y-ordinate values. + /// A series of z-ordinate values. + /// A stride value for the x-ordinate series. + /// A stride value for the y-ordinate series. + /// A stride value for the z-ordinate series. + /// If the provided span and stride values don't result in matching number of ordinates. + public void Transform( + Span xs, + Span ys, + Span zs, + int strideX = 1, + int strideY = 1, + int strideZ = 1) + { + int elementsX = (xs.Length / strideX) + (xs.Length % strideX) != 0 ? 1 : 0; + int elementsY = (ys.Length / strideY) + (ys.Length % strideY) != 0 ? 1 : 0; + if (elementsX != elementsY) { - var read = MemoryMarshal.Cast(xyzs); - var inXs = read.Slice(0);//, read.Length - 2); - var inYs = read.Slice(1);//, read.Length - 2); - var inZs = read.Slice(2);//, read.Length - 2); + ArgumentGuard.ThrowArgument("Spans of ordinate values don't match in size.", nameof(ys)); + } - TransformCore(inXs, inYs, inZs, 3,3,3); + if (zs.IsEmpty) + { + Span dummyZ = stackalloc double[] { 0 }; + this.TransformCore(xs, ys, dummyZ, strideX, strideY, 0); + return; } - /// - /// Adds a value to the elements of a in-place, using SIMD when legal - /// and effective. - /// - /// A series of values to transform in-place. - /// The spacing between elements. - /// The value to add to each element in in-place. - protected static void AddInPlace(Span vals, int stride, double addend) + int elementsZ = (zs.Length / strideZ) + (zs.Length % strideZ) != 0 ? 1 : 0; + if (elementsZ != elementsX) { - if (stride < 1) - { - throw new ArgumentOutOfRangeException(nameof(stride), stride, "Must be greater than zero."); - } + ArgumentGuard.ThrowArgument("Spans of ordinate values don't match in size.", nameof(zs)); + } - if (addend == 0) - { - return; - } + this.TransformCore(xs, ys, zs, strideX, strideY, strideZ); + } - int scalarStart = 0; - if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) + /// + /// Transforms a series of 2-dimensional -points and (optionally) a series of z-ordinate values. + /// + /// A series of points. + /// A series of z-ordinate values. + /// A stride value for z-ordinates. + /// If the provided series' and buffers don't match in size. + public void Transform(Span xys, Span zs = default, int strideZ = 0) + { + if (!zs.IsEmpty) + { + if (strideZ <= 0) { - var valsVector = MemoryMarshal.Cast>(vals); - var addendVector = new Vector(addend); - for (int i = 0; i < valsVector.Length; i++) - { - valsVector[i] += addendVector; - } - - scalarStart = valsVector.Length * Vector.Count; + strideZ = 1; } - for (int i = scalarStart; i < vals.Length; i += stride) + if (xys.Length != ((zs.Length / strideZ) + (zs.Length % strideZ != 0 ? 1 : 0))) { - vals[i] += addend; + ArgumentGuard.ThrowArgument("Provided spans don't match in size.", nameof(zs)); } } - /// - /// Multiplies the elements of a in-place by a multiplier, using SIMD - /// when legal and effective. - /// - /// A series of values to transform in-place. - /// The spacing between elements. - /// The value by which to multiply each element in in-place. - protected static void MultiplyInPlace(Span vals, int stride, double multiplier) + Span read = MemoryMarshal.Cast(xys); + Span inXs = read[..]; + Span inYs = read[1..]; + + if (zs.IsEmpty) { - if (stride < 1) - { - throw new ArgumentOutOfRangeException(nameof(stride), stride, "Must be greater than zero."); - } + Span dummyZ = stackalloc double[] { 0 }; + this.TransformCore(inXs, inYs, dummyZ, 2, 2, 0); + } + else + { + this.TransformCore(inXs, inYs, zs, 2, 2, strideZ); + } + } - if (multiplier == 1) - { - return; - } + /// + /// Transforms a series of 3-dimensional -points. + /// + /// A series of points. + public void Transform(Span xyzs) + { + Span read = MemoryMarshal.Cast(xyzs); + Span inXs = read[..]; // , read.Length - 2); + Span inYs = read[1..]; // , read.Length - 2); + Span inZs = read[2..]; // , read.Length - 2); - int scalarStart = 0; - if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) - { - var valsVector = MemoryMarshal.Cast>(vals); - var multiplierVector = new Vector(multiplier); - for (int i = 0; i < valsVector.Length; i++) - { - valsVector[i] *= multiplierVector; - } - - scalarStart = valsVector.Length * Vector.Count; - } + this.TransformCore(inXs, inYs, inZs, 3, 3, 3); + } - for (int i = scalarStart; i < vals.Length; i += stride) - { - vals[i] *= multiplier; - } + /// + /// Transforms a single 4-dimensional point in-place. + /// + /// The ordinate value on the first axis, either x or longitude. + /// The ordinate value on the second axis, either y or latitude. + /// The ordinate value on the third axis, either z, height or altitude. + /// The ordinate value on the fourth axis, typically observation epoch. + internal virtual void Transform(ref double x, ref double y, ref double z, ref double t) + { + this.Transform(ref x, ref y, ref z); + } + + /// + /// Core method to transform a series of points defined by their ordinates. + /// The transformation is performed in-place. + /// + /// A series of x-ordinate values. + /// A series of y-ordinate values. + /// A series of z-ordinate values. + /// A stride value for the x-ordinate series. + /// A stride value for the y-ordinate series. + /// A stride value for the z-ordinate series. + protected virtual void TransformCore( + Span xs, + Span ys, + Span zs, + int strideX, + int strideY, + int strideZ) + { + for (int i = 0, j = 0, k = 0; i < xs.Length; i += strideX, j += strideY, k += strideZ) + { + this.Transform(ref xs[i], ref ys[j], ref zs[k]); } + } - /// - /// Multiplies the elements of a in-place by a multiplier, then adds a - /// value to the product in-place, using SIMD when legal and effective. - /// - /// A series of values to transform in-place. - /// The spacing between elements. - /// The value by which to multiply each element in in-place. - /// The value to add to each multiplied element in in-place. - protected static void MultiplyThenAddInPlace(Span vals, int stride, double multiplier, double addend) + /// + /// Converts a degree value to radians by multiplying it with / 180.0. + /// + /// The value in degrees to convert. + /// The equivalent value in radians. + protected static double DegreesToRadians(double deg) => D2R * deg; + + /// + /// Converts a series of degree-values () to a radian-values by multiplying them with / 180.0. + /// + /// A series of degree-values. + /// A stride value. + protected static void DegreesToRadians(Span degrees, int stride) + { + MultiplyInPlace(degrees, stride, D2R); + } + + /// + /// Converts a radian value to degrees by multiplying it with 180.0 / . + /// + /// The value in radians to convert. + /// The equivalent value in degrees. + protected static double RadiansToDegrees(double rad) => R2D * rad; + + /// + /// Converts a series of radian-values () to a degrees-values by multiplying them with 180.0 / . + /// + /// A series of radian-values. + /// A stride value. + protected static void RadiansToDegrees(Span radians, int stride) + { + MultiplyInPlace(radians, stride, R2D); + } + + /// + /// Adds a value to the elements of a in-place, using SIMD when legal + /// and effective. + /// + /// A series of values to transform in-place. + /// The spacing between elements. + /// The value to add to each element in in-place. + protected static void AddInPlace(Span vals, int stride, double addend) + { + if (stride < 1) { - if (stride < 1) - { - throw new ArgumentOutOfRangeException(nameof(stride), stride, "Must be greater than zero."); - } + ArgumentGuard.ThrowArgumentOutOfRange(nameof(stride), stride, "Must be greater than zero."); + } - if (addend == 0) - { - MultiplyInPlace(vals, stride, multiplier); - return; - } + if (addend == 0) + { + return; + } - if (multiplier == 1) + int scalarStart = 0; + if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) + { + Span> valsVector = MemoryMarshal.Cast>(vals); + var addendVector = new Vector(addend); + for (int i = 0; i < valsVector.Length; i++) { - AddInPlace(vals, stride, addend); - return; + valsVector[i] += addendVector; } - int scalarStart = 0; - if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) - { - var valsVector = MemoryMarshal.Cast>(vals); - var multiplierVector = new Vector(multiplier); - var addendVector = new Vector(addend); - for (int i = 0; i < valsVector.Length; i++) - { - valsVector[i] = valsVector[i] * multiplierVector + addendVector; - } - - scalarStart = valsVector.Length * Vector.Count; - } + scalarStart = valsVector.Length * Vector.Count; + } - for (int i = scalarStart; i < vals.Length; i += stride) - { - vals[i] = vals[i] * multiplier + addend; - } + for (int i = scalarStart; i < vals.Length; i += stride) + { + vals[i] += addend; + } + } + + /// + /// Multiplies the elements of a in-place by a multiplier, using SIMD + /// when legal and effective. + /// + /// A series of values to transform in-place. + /// The spacing between elements. + /// The value by which to multiply each element in in-place. + protected static void MultiplyInPlace(Span vals, int stride, double multiplier) + { + if (stride < 1) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(stride), stride, "Must be greater than zero."); } - /// - /// Adds a value to the elements of a in-place, then multiplies each - /// sum by a multiplier in-place, using SIMD when legal and effective. - /// - /// A series of values to transform in-place. - /// The spacing between elements. - /// The value to add to each element in in-place. - /// The value by which to multiply each summed element in in-place. - protected static void AddThenMultiplyInPlace(Span vals, int stride, double addend, double multiplier) + if (multiplier == 1) { - if (stride < 1) - { - throw new ArgumentOutOfRangeException(nameof(stride), stride, "Must be greater than zero."); - } + return; + } - if (addend == 0) + int scalarStart = 0; + if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) + { + Span> valsVector = MemoryMarshal.Cast>(vals); + var multiplierVector = new Vector(multiplier); + for (int i = 0; i < valsVector.Length; i++) { - MultiplyInPlace(vals, stride, multiplier); - return; + valsVector[i] *= multiplierVector; } - if (multiplier == 1) + scalarStart = valsVector.Length * Vector.Count; + } + + for (int i = scalarStart; i < vals.Length; i += stride) + { + vals[i] *= multiplier; + } + } + + /// + /// Multiplies the elements of a in-place by a multiplier, then adds a + /// value to the product in-place, using SIMD when legal and effective. + /// + /// A series of values to transform in-place. + /// The spacing between elements. + /// The value by which to multiply each element in in-place. + /// The value to add to each multiplied element in in-place. + protected static void MultiplyThenAddInPlace(Span vals, int stride, double multiplier, double addend) + { + if (stride < 1) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(stride), stride, "Must be greater than zero."); + } + + if (addend == 0) + { + MultiplyInPlace(vals, stride, multiplier); + return; + } + + if (multiplier == 1) + { + AddInPlace(vals, stride, addend); + return; + } + + int scalarStart = 0; + if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) + { + Span> valsVector = MemoryMarshal.Cast>(vals); + var multiplierVector = new Vector(multiplier); + var addendVector = new Vector(addend); + for (int i = 0; i < valsVector.Length; i++) { - AddInPlace(vals, stride, addend); - return; + valsVector[i] = (valsVector[i] * multiplierVector) + addendVector; } - int scalarStart = 0; - if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) + scalarStart = valsVector.Length * Vector.Count; + } + + for (int i = scalarStart; i < vals.Length; i += stride) + { + vals[i] = (vals[i] * multiplier) + addend; + } + } + + /// + /// Adds a value to the elements of a in-place, then multiplies each + /// sum by a multiplier in-place, using SIMD when legal and effective. + /// + /// A series of values to transform in-place. + /// The spacing between elements. + /// The value to add to each element in in-place. + /// The value by which to multiply each summed element in in-place. + protected static void AddThenMultiplyInPlace(Span vals, int stride, double addend, double multiplier) + { + if (stride < 1) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(stride), stride, "Must be greater than zero."); + } + + if (addend == 0) + { + MultiplyInPlace(vals, stride, multiplier); + return; + } + + if (multiplier == 1) + { + AddInPlace(vals, stride, addend); + return; + } + + int scalarStart = 0; + if (Vector.IsHardwareAccelerated && stride == 1 && vals.Length >= Vector.Count) + { + Span> valsVector = MemoryMarshal.Cast>(vals); + var addendVector = new Vector(addend); + var multiplierVector = new Vector(multiplier); + for (int i = 0; i < valsVector.Length; i++) { - var valsVector = MemoryMarshal.Cast>(vals); - var addendVector = new Vector(addend); - var multiplierVector = new Vector(multiplier); - for (int i = 0; i < valsVector.Length; i++) - { - valsVector[i] = (valsVector[i] + addendVector) * multiplierVector; - } - - scalarStart = valsVector.Length * Vector.Count; + valsVector[i] = (valsVector[i] + addendVector) * multiplierVector; } - for (int i = scalarStart; i < vals.Length; i += stride) + scalarStart = valsVector.Length * Vector.Count; + } + + for (int i = scalarStart; i < vals.Length; i += stride) + { + vals[i] = (vals[i] + addend) * multiplier; + } + } + + private static List CreatePointList(ReadOnlySpan points) + { + if (points.IsEmpty) + { + return []; + } + + var list = new List(points.Length); + for (int i = 0; i < points.Length; i++) + { + list.Add(points[i]); + } + + return list; + } + + /// + /// Transforms a single source coordinate into an already-sized destination span. + /// + /// Source ordinates. + /// The number of source ordinates available in . + /// Destination ordinates. + /// The number of result ordinates to write. + private void TransformPoint(ReadOnlySpan point, int pointLength, Span result, int resultDimensions) + { + double x = point[0]; + double y = point[1]; + double z = pointLength >= 3 ? point[2] : 0; + double t = pointLength >= 4 ? point[3] : 0; + + if (pointLength >= 4) + { + this.Transform(ref x, ref y, ref z, ref t); + } + else + { + this.Transform(ref x, ref y, ref z); + } + + result[0] = x; + result[1] = y; + if (resultDimensions >= 3) + { + result[2] = z; + } + + if (resultDimensions >= 4) + { + result[3] = t; + if (resultDimensions > 4) { - vals[i] = (vals[i] + addend) * multiplier; + point[4..resultDimensions].CopyTo(result[4..]); } } } + + /// + /// Gets the required number of ordinates in a transformed point. + /// + /// Input point ordinate count. + /// Output ordinate count. + private int GetResultDimensions(int pointLength) + { + int minimumDimensions = this.DimTarget == 2 ? 2 : 3; + return pointLength <= 3 + ? minimumDimensions + : Math.Max(minimumDimensions, pointLength); + } + + private static double GetDerivativeStepSize(double coordinate) + { + return Math.Max(Math.Abs(coordinate) * 1e-8d, 1e-6d); + } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/MolobadekasMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/MolobadekasMathTransform.cs new file mode 100644 index 00000000..8df96a3e --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/MolobadekasMathTransform.cs @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems.Transformations.Numerics; + +/// +/// Implements PROJ's molobadekas runtime transform. +/// +/// +/// Molodensky-Badekas is a 10-parameter similarity transform that augments the +/// standard Helmert model with a rotation pivot. It is commonly used when rotations are +/// defined about a local network centroid rather than the geocentric origin. +/// The formulation was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG methods 1034 and 1061. The +/// X' = T + P + (1 + s) * R * (X - P) structure, including the explicit pivot +/// point translation, matches the implementation here. +/// +/// EPSG method 1034: Molodensky-Badekas (geocentric domain). +/// EPSG method 1061: Molodensky-Badekas (geographic domain). +internal sealed class MolobadekasMathTransform : MathTransform +{ + private readonly Vector3D translation; + private readonly double scalePpm; + private readonly Vector3D pivot; + private readonly Vector3D rotationRadians; + private readonly Matrix3x3 rotationMatrix; + + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// X translation (metres). + /// Y translation (metres). + /// Z translation (metres). + /// X rotation (arc-seconds). + /// Y rotation (arc-seconds). + /// Z rotation (arc-seconds). + /// Scale (ppm). + /// Reference point X (metres). + /// Reference point Y (metres). + /// Reference point Z (metres). + /// Whether rotations use position-vector convention. + /// Whether the transform runs in inverse direction. + private MolobadekasMathTransform( + double translationX, + double translationY, + double translationZ, + double rotationXArcSeconds, + double rotationYArcSeconds, + double rotationZArcSeconds, + double scalePpm, + double pivotX, + double pivotY, + double pivotZ, + bool isPositionVector, + bool isInverted) + { + ArgumentGuard.ThrowIfNotFinite(translationX, nameof(translationX), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(translationY, nameof(translationY), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(translationZ, nameof(translationZ), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(rotationXArcSeconds, nameof(rotationXArcSeconds), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(rotationYArcSeconds, nameof(rotationYArcSeconds), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(rotationZArcSeconds, nameof(rotationZArcSeconds), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(scalePpm, nameof(scalePpm), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(pivotX, nameof(pivotX), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(pivotY, nameof(pivotY), "Molobadekas parameters must be finite."); + ArgumentGuard.ThrowIfNotFinite(pivotZ, nameof(pivotZ), "Molobadekas parameters must be finite."); + + this.translation = new Vector3D(translationX, translationY, translationZ); + this.scalePpm = scalePpm; + this.pivot = new Vector3D(pivotX, pivotY, pivotZ); + this.rotationRadians = new Vector3D( + rotationXArcSeconds * TransformationMath.ArcSecondToRadians, + rotationYArcSeconds * TransformationMath.ArcSecondToRadians, + rotationZArcSeconds * TransformationMath.ArcSecondToRadians); + this.isInverted = isInverted; + + this.rotationMatrix = BuildRotationMatrix(this.rotationRadians, isPositionVector); + } + + /// + /// Initializes a new instance of the class + /// as an inverted clone. + /// + /// Source instance to clone. + /// Whether to apply inverse direction in the clone. + private MolobadekasMathTransform(MolobadekasMathTransform source, bool isInverted) + { + this.translation = source.translation; + this.scalePpm = source.scalePpm; + this.pivot = source.pivot; + this.rotationRadians = source.rotationRadians; + this.rotationMatrix = source.rotationMatrix; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return this.translation.X == 0d + && this.translation.Y == 0d + && this.translation.Z == 0d + && this.rotationRadians.X == 0d + && this.rotationRadians.Y == 0d + && this.rotationRadians.Z == 0d + && this.scalePpm == 0d; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new MolobadekasMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("MolobadekasMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (this.isInverted) + { + this.TransformInverse(ref x, ref y, ref z); + return; + } + + this.TransformForward(ref x, ref y, ref z); + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "molobadekas arguments were null."; + return false; + } + + if (!args.TryGetValue("convention", out string? conventionToken) + || string.IsNullOrWhiteSpace(conventionToken)) + { + skipReason = "molobadekas: missing 'convention' argument"; + return false; + } + + bool isPositionVector; + if (conventionToken.Equals("position_vector", StringComparison.OrdinalIgnoreCase)) + { + isPositionVector = true; + } + else if (conventionToken.Equals("coordinate_frame", StringComparison.OrdinalIgnoreCase)) + { + isPositionVector = false; + } + else + { + skipReason = "molobadekas: invalid value for 'convention' argument"; + return false; + } + + if (!SpanParseUtility.TryGetOptionalDouble(args, "x", out double translationX, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "y", out double translationY, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "z", out double translationZ, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "s", out double scalePpm, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "rx", out double rotationXArcSeconds, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "ry", out double rotationYArcSeconds, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "rz", out double rotationZArcSeconds, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "px", out double pivotX, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "py", out double pivotY, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "pz", out double pivotZ, out skipReason)) + { + return false; + } + + if (scalePpm <= TransformationMath.MinValidPpmScale) + { + skipReason = "molobadekas: invalid value for s."; + return false; + } + + bool hasAnyParameters = translationX != 0d + || translationY != 0d + || translationZ != 0d + || scalePpm != 0d + || rotationXArcSeconds != 0d + || rotationYArcSeconds != 0d + || rotationZArcSeconds != 0d + || pivotX != 0d + || pivotY != 0d + || pivotZ != 0d; + + transform = hasAnyParameters + ? new MolobadekasMathTransform( + translationX, + translationY, + translationZ, + rotationXArcSeconds, + rotationYArcSeconds, + rotationZArcSeconds, + scalePpm, + pivotX, + pivotY, + pivotZ, + isPositionVector, + false) + : new IdentityMathTransform(3); + + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + + /// + /// Creates a Molodensky-Badekas transform from resolved numeric parameters. + /// + /// X translation in metres. + /// Y translation in metres. + /// Z translation in metres. + /// X rotation in arc-seconds. + /// Y rotation in arc-seconds. + /// Z rotation in arc-seconds. + /// Scale difference in ppm. + /// Pivot X ordinate in metres. + /// Pivot Y ordinate in metres. + /// Pivot Z ordinate in metres. + /// for the position-vector convention. + /// to create the inverse direction. + /// The created transform. + internal static MathTransform Create( + double translationX, + double translationY, + double translationZ, + double rotationXArcSeconds, + double rotationYArcSeconds, + double rotationZArcSeconds, + double scalePpm, + double pivotX, + double pivotY, + double pivotZ, + bool isPositionVector, + bool isInverted = false) + { + bool hasAnyParameters = translationX != 0d + || translationY != 0d + || translationZ != 0d + || scalePpm != 0d + || rotationXArcSeconds != 0d + || rotationYArcSeconds != 0d + || rotationZArcSeconds != 0d + || pivotX != 0d + || pivotY != 0d + || pivotZ != 0d; + + MathTransform transform = hasAnyParameters + ? new MolobadekasMathTransform( + translationX, + translationY, + translationZ, + rotationXArcSeconds, + rotationYArcSeconds, + rotationZArcSeconds, + scalePpm, + pivotX, + pivotY, + pivotZ, + isPositionVector, + false) + : new IdentityMathTransform(3); + return isInverted ? transform.Inverse() : transform; + } + + private static Matrix3x3 BuildRotationMatrix(Vector3D rotation, bool isPositionVector) + { + var coordinateFrameMatrix = new Matrix3x3( + 1d, + rotation.Z, + -rotation.Y, + -rotation.Z, + 1d, + rotation.X, + rotation.Y, + -rotation.X, + 1d); + + return isPositionVector ? coordinateFrameMatrix.Transpose() : coordinateFrameMatrix; + } + + private void TransformForward(ref double x, ref double y, ref double z) + { + Vector3D source = new Vector3D(x, y, z) - this.pivot; + double scaleFactor = 1d + (this.scalePpm * 1e-6d); + Vector3D transformed = this.translation + this.pivot + ((this.rotationMatrix * source) * scaleFactor); + x = transformed.X; + y = transformed.Y; + z = transformed.Z; + } + + private void TransformInverse(ref double x, ref double y, ref double z) + { + double scaleFactor = 1d + (this.scalePpm * 1e-6d); + Vector3D source = (new Vector3D(x, y, z) - this.translation - this.pivot) / scaleFactor; + Vector3D transformed = this.pivot + (this.rotationMatrix.Transpose() * source); + x = transformed.X; + y = transformed.Y; + z = transformed.Z; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/MolodenskyMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/MolodenskyMathTransform.cs new file mode 100644 index 00000000..85b7313b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/MolodenskyMathTransform.cs @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems; + +/// +/// Implements PROJ's (abridged) molodensky runtime transform. +/// +/// +/// The abridged branch was independently verified against IOGP, "Geomatics Guidance +/// Note 7, part 2: Coordinate Conversions and Transformations including Formulas" +/// (publication 373-7-2, 2019), EPSG method 9605, Abridged Molodensky. In particular, +/// the combined ellipsoid-difference term follows a * df + f * da in the +/// latitude and height corrections, matching the published method. +/// +/// EPSG method 9605: Abridged Molodensky. +internal sealed class MolodenskyMathTransform : MathTransform +{ + private readonly double semiMajor; + private readonly double flattening; + private readonly double eccentricitySquared; + private readonly double dx; + private readonly double dy; + private readonly double dz; + private readonly double da; + private readonly double df; + private readonly bool abridged; + + private readonly bool isInverted; + private MathTransform? inverse; + + private MolodenskyMathTransform( + double semiMajor, + double semiMinor, + double dx, + double dy, + double dz, + double da, + double df, + bool abridged, + bool isInverted) + { + if (semiMajor <= 0d || double.IsNaN(semiMajor) || double.IsInfinity(semiMajor)) + { + ArgumentGuard.ThrowArgument("Molodensky requires a positive finite semi-major axis.", nameof(semiMajor)); + } + + if (semiMinor <= 0d || double.IsNaN(semiMinor) || double.IsInfinity(semiMinor)) + { + ArgumentGuard.ThrowArgument("Molodensky requires a positive finite semi-minor axis.", nameof(semiMinor)); + } + + this.semiMajor = semiMajor; + this.flattening = (semiMajor - semiMinor) / semiMajor; + this.eccentricitySquared = (2d * this.flattening) - (this.flattening * this.flattening); + this.dx = dx; + this.dy = dy; + this.dz = dz; + this.da = da; + this.df = df; + this.abridged = abridged; + this.isInverted = isInverted; + } + + private MolodenskyMathTransform(MolodenskyMathTransform source, bool isInverted) + { + this.semiMajor = source.semiMajor; + this.flattening = source.flattening; + this.eccentricitySquared = source.eccentricitySquared; + this.dx = source.dx; + this.dy = source.dy; + this.dz = source.dz; + this.da = source.da; + this.df = source.df; + this.abridged = source.abridged; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override MathTransform Inverse() + { + this.inverse ??= new MolodenskyMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("MolodenskyMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (double.IsNaN(z)) + { + z = 0d; + } + + double lam = DegreesToRadians(x); + double phi = DegreesToRadians(y); + double h = z; + + (double dLam, double dPhi, double dH) = this.abridged + ? this.CalculateAbridgedDelta(lam, phi, h) + : this.CalculateStandardDelta(lam, phi, h); + + if (this.isInverted) + { + lam -= dLam; + phi -= dPhi; + h -= dH; + } + else + { + lam += dLam; + phi += dPhi; + h += dH; + } + + x = RadiansToDegrees(lam); + y = RadiansToDegrees(phi); + z = h; + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + if (args is null) + { + skipReason = "molodensky arguments were null."; + return false; + } + + if (!TryGetRequiredDouble(args, "dx", out double dx)) + { + skipReason = "missing dx"; + return false; + } + + if (!TryGetRequiredDouble(args, "dy", out double dy)) + { + skipReason = "missing dy"; + return false; + } + + if (!TryGetRequiredDouble(args, "dz", out double dz)) + { + skipReason = "missing dz"; + return false; + } + + if (!TryGetRequiredDouble(args, "da", out double da)) + { + skipReason = "missing da"; + return false; + } + + if (!TryGetRequiredDouble(args, "df", out double df)) + { + skipReason = "missing df"; + return false; + } + + if (!ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + includeDatumToken: true, + allowClarke1880Ign: false, + allowBessel: false, + out double semiMajor, + out double semiMinor)) + { + skipReason = "Unable to resolve ellipsoid for molodensky."; + return false; + } + + try + { + transform = new MolodenskyMathTransform( + semiMajor, + semiMinor, + dx, + dy, + dz, + da, + df, + args.ContainsKey("abridged"), + false); + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + catch (ArgumentException exception) + { + skipReason = exception.Message; + return false; + } + } + + private static bool TryGetRequiredDouble(Dictionary args, string key, out double value) + { + value = 0d; + return args.TryGetValue(key, out string? token) + && SpanParseUtility.TryParseFiniteDouble(token, out value); + } + + private (double DeltaLam, double DeltaPhi, double DeltaH) CalculateStandardDelta(double lam, double phi, double h) + { + double sinLam = Math.Sin(lam); + double cosLam = Math.Cos(lam); + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + + double rho = this.ComputeRm(phi); + double nu = this.ComputeRn(phi); + + double dPhi = (-this.dx * sinPhi * cosLam) + - (this.dy * sinPhi * sinLam) + + (this.dz * cosPhi) + + ((nu * this.eccentricitySquared * sinPhi * cosPhi * this.da) / this.semiMajor) + + (sinPhi * cosPhi * ((rho / (1d - this.flattening)) + (nu * (1d - this.flattening))) * this.df); + double dPhiDenominator = rho + h; + if (dPhiDenominator == 0d) + { + TransformationThrowHelper.ThrowInvalidOperation("Molodensky standard produced invalid denominator for dphi."); + } + + dPhi /= dPhiDenominator; + + double dLamDenominator = (nu + h) * cosPhi; + if (dLamDenominator == 0d) + { + TransformationThrowHelper.ThrowInvalidOperation("Molodensky standard produced invalid denominator for dlam."); + } + + double dLam = ((-this.dx * sinLam) + (this.dy * cosLam)) / dLamDenominator; + double dH = (this.dx * cosPhi * cosLam) + + (this.dy * cosPhi * sinLam) + + (this.dz * sinPhi) + - ((this.semiMajor / nu) * this.da) + + (nu * (1d - this.flattening) * sinPhi * sinPhi * this.df); + return (dLam, dPhi, dH); + } + + private (double DeltaLam, double DeltaPhi, double DeltaH) CalculateAbridgedDelta(double lam, double phi, double h) + { + _ = h; + double sinLam = Math.Sin(lam); + double cosLam = Math.Cos(lam); + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + double adffda = (this.semiMajor * this.df) + (this.flattening * this.da); + + double dPhi = (-this.dx * sinPhi * cosLam) + - (this.dy * sinPhi * sinLam) + + (this.dz * cosPhi) + + (adffda * Math.Sin(2d * phi)); + double dPhiDenominator = this.ComputeRm(phi); + if (dPhiDenominator == 0d) + { + TransformationThrowHelper.ThrowInvalidOperation("Molodensky abridged produced invalid denominator for dphi."); + } + + dPhi /= dPhiDenominator; + + double dLamDenominator = this.ComputeRn(phi) * cosPhi; + if (dLamDenominator == 0d) + { + TransformationThrowHelper.ThrowInvalidOperation("Molodensky abridged produced invalid denominator for dlam."); + } + + double dLam = ((-this.dx * sinLam) + (this.dy * cosLam)) / dLamDenominator; + double dH = (this.dx * cosPhi * cosLam) + + (this.dy * cosPhi * sinLam) + + (this.dz * sinPhi) + - this.da + + (adffda * sinPhi * sinPhi); + return (dLam, dPhi, dH); + } + + private double ComputeRn(double phi) + { + double sinPhi = Math.Sin(phi); + return this.eccentricitySquared == 0d + ? this.semiMajor + : this.semiMajor / Math.Sqrt(1d - (this.eccentricitySquared * sinPhi * sinPhi)); + } + + private double ComputeRm(double phi) + { + double sinPhi = Math.Sin(phi); + if (this.eccentricitySquared == 0d) + { + return this.semiMajor; + } + + if (phi == 0d) + { + return this.semiMajor * (1d - this.eccentricitySquared); + } + + return Math.Abs(phi) == (Math.PI * 0.5d) + ? this.semiMajor / Math.Sqrt(1d - this.eccentricitySquared) + : (this.semiMajor * (1d - this.eccentricitySquared)) + / Math.Pow(1d - (this.eccentricitySquared * sinPhi * sinPhi), 1.5d); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/Ntv2HGridShiftMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/Ntv2HGridShiftMathTransform.cs new file mode 100644 index 00000000..48b484f9 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/Ntv2HGridShiftMathTransform.cs @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +/// +/// Applies horizontal datum shifts using one or more NTv2 grid files. +/// +/// +/// NTv2 longitude handling was independently verified against published EPSG NTv2 remarks +/// and the PROJ GeoTIFF grid specification. EPSG transformation records explicitly note +/// that NTv2 input expects longitudes to be positive west, and PROJ documents that NTv2 +/// products originally use a west positive-value convention. This implementation +/// therefore negates stored longitudes and mirrors the X index during sample lookup to +/// restore conventional east-positive handling from the original east-to-west row order. +/// The horizontal shift algorithm was independently verified against IOGP, "Geomatics +/// Guidance Note 7, part 2: Coordinate Conversions and Transformations including +/// Formulas" (publication 373-7-2, 2019), EPSG method 9615, NTv2. The bilinear +/// interpolation of grid offsets in the forward path and the iterative inverse recovery +/// by repeated subtraction of interpolated shifts match the implementation here. +/// +/// Wikipedia: NTv2. +/// Esri NTv2 file routines reference implementation. +/// EPSG method 9615: NTv2. +internal sealed class Ntv2HGridShiftMathTransform : MathTransform +{ + private const double ArcSecondToDegree = 1d / 3600d; + private const double RelativeTolerance = 1e-5d; + private const double InverseTolerance = 1e-12d; + private readonly ReadOnlyCollection gridSets; + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Ordered NTv2 grid file paths to load. + internal Ntv2HGridShiftMathTransform(IReadOnlyList gridPaths) + { + gridPaths = ArgumentGuard.ThrowIfNull(gridPaths, nameof(gridPaths)); + + this.gridSets = GridLoaderHelper.LoadMulti( + gridPaths, + nameof(gridPaths), + "At least one NTv2 grid file must be provided.", + static path => new[] { Ntv2GridSet.Load(path) }); + } + + private Ntv2HGridShiftMathTransform(Ntv2HGridShiftMathTransform source, bool isInverted) + { + source = ArgumentGuard.ThrowIfNull(source, nameof(source)); + this.gridSets = source.gridSets; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return false; + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new Ntv2HGridShiftMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("Ntv2HGridShiftMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (!this.TryFindGridForPoint(x, y, out Ntv2Grid? selectedGridCandidate)) + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal grid extent."); + } + + Ntv2Grid selectedGrid = ArgumentGuard.ThrowIfNull(selectedGridCandidate, nameof(selectedGridCandidate)); + + if (!this.isInverted) + { + (double lonShift, double latShift) = InterpolateShift(selectedGrid, x, y, true); + x += lonShift; + y += latShift; + return; + } + + InverseTransform(ref x, ref y, selectedGrid); + } + + private static void InverseTransform(ref double longitude, ref double latitude, Ntv2Grid initialGrid) + { + double epsilon = initialGrid.Epsilon; + double tbLon = NormalizeLongitudeToGrid(longitude, initialGrid.West, initialGrid.East, epsilon); + double tbLat = latitude - initialGrid.South; + (double firstLongShift, double firstLatShift) = InterpolateNormalized(initialGrid, tbLon, tbLat, true); + + double tLon = tbLon - firstLongShift; + double tLat = tbLat - firstLatShift; + int iterations = TransformationMath.MaxInverseIterations; + + while (iterations-- > 0) + { + (double iterLongShift, double iterLatShift) = InterpolateNormalized(initialGrid, tLon, tLat, true); + + double deltaLon = tLon + iterLongShift - tbLon; + double deltaLat = tLat + iterLatShift - tbLat; + tLon -= deltaLon; + tLat -= deltaLat; + + if ((deltaLon * deltaLon) + (deltaLat * deltaLat) <= (InverseTolerance * InverseTolerance)) + { + longitude = TransformationMath.NormalizeLongitudeDegrees(tLon + initialGrid.West); + latitude = tLat + initialGrid.South; + return; + } + } + + TransformationThrowHelper.ThrowInvalidOperation("Inverse horizontal grid shift did not converge."); + } + + private static (double LonShift, double LatShift) InterpolateShift(Ntv2Grid grid, double longitude, double latitude, bool compensateNtConvention) + { + double normalizedLongitude = NormalizeLongitudeToGrid(longitude, grid.West, grid.East, grid.Epsilon); + double normalizedLatitude = latitude - grid.South; + return InterpolateNormalized(grid, normalizedLongitude, normalizedLatitude, compensateNtConvention); + } + + private static (double LonShift, double LatShift) InterpolateNormalized(Ntv2Grid grid, double normalizedLongitude, double normalizedLatitude, bool compensateNtConvention) + { + double x = normalizedLongitude / grid.ResolutionX; + double y = normalizedLatitude / grid.ResolutionY; + + int indexX = (int)Math.Floor(x); + int indexY = (int)Math.Floor(y); + double fractionX = x - indexX; + double fractionY = y - indexY; + + if (indexX < 0) + { + if (indexX == -1 && fractionX > 1d - (10d * RelativeTolerance)) + { + indexX++; + fractionX = 0d; + } + else + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal grid extent."); + } + } + else if (indexX + 1 >= grid.Width) + { + if (indexX + 1 == grid.Width && fractionX < 10d * RelativeTolerance) + { + indexX--; + fractionX = 1d; + } + else + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal grid extent."); + } + } + + if (indexY < 0) + { + if (indexY == -1 && fractionY > 1d - (10d * RelativeTolerance)) + { + indexY++; + fractionY = 0d; + } + else + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal grid extent."); + } + } + else if (indexY + 1 >= grid.Height) + { + if (indexY + 1 == grid.Height && fractionY < 10d * RelativeTolerance) + { + indexY--; + fractionY = 1d; + } + else + { + TransformationThrowHelper.ThrowInvalidOperation("Coordinate is outside the horizontal grid extent."); + } + } + + (double f00Lon, double f00Lat) = grid.GetShift(indexX, indexY, compensateNtConvention); + (double f10Lon, double f10Lat) = grid.GetShift(indexX + 1, indexY, compensateNtConvention); + (double f01Lon, double f01Lat) = grid.GetShift(indexX, indexY + 1, compensateNtConvention); + (double f11Lon, double f11Lat) = grid.GetShift(indexX + 1, indexY + 1, compensateNtConvention); + + double m10 = fractionX; + double m11 = m10; + double m01 = 1d - fractionX; + double m00 = m01; + m11 *= fractionY; + m01 *= fractionY; + fractionY = 1d - fractionY; + m00 *= fractionY; + m10 *= fractionY; + + double lonShift = (m00 * f00Lon) + (m10 * f10Lon) + (m01 * f01Lon) + (m11 * f11Lon); + double latShift = (m00 * f00Lat) + (m10 * f10Lat) + (m01 * f01Lat) + (m11 * f11Lat); + return (lonShift, latShift); + } + + private static double NormalizeLongitudeToGrid(double longitude, double west, double east, double epsilon) + { + double normalized = longitude - west; + if (normalized + epsilon < 0d) + { + normalized += 360d; + } + else if (normalized - epsilon > east - west) + { + normalized -= 360d; + } + + return normalized; + } + + private bool TryFindGridForPoint(double longitude, double latitude, [NotNullWhen(true)] out Ntv2Grid? grid) + { + grid = null; + for (int setIndex = 0; setIndex < this.gridSets.Count; setIndex++) + { + if (this.gridSets[setIndex].TryFindGrid(longitude, latitude, out Ntv2Grid? candidate)) + { + grid = candidate; + return true; + } + } + + return false; + } + + private sealed class Ntv2GridSet + { + private readonly IReadOnlyList rootGrids; + + private Ntv2GridSet(IReadOnlyList sourcePaths, IReadOnlyList rootGrids) + { + this.SourcePaths = sourcePaths; + this.rootGrids = rootGrids; + } + + internal IReadOnlyList SourcePaths { get; } + + internal static Ntv2GridSet Load(string path) + { + byte[] bytes = File.ReadAllBytes(path); + if (bytes.Length < 16 * 22) + { + throw new InvalidDataException("NTv2 file is too small."); + } + + bool littleEndian = BitConverter.ToInt32(bytes, 8) == 11; + bool bigEndian = ReadInt32(bytes, 8, false) == 11; + if (!littleEndian && !bigEndian) + { + throw new InvalidDataException("Unable to detect NTv2 byte order."); + } + + bool isLittleEndian = littleEndian; + int overviewRecordCount = ReadInt32(bytes, 8, isLittleEndian); + if (overviewRecordCount != 11) + { + throw new InvalidDataException("Unsupported NTv2 overview header."); + } + + int gridRecordCount = ReadInt32(bytes, 24, isLittleEndian); + if (gridRecordCount != 11) + { + throw new InvalidDataException("Unsupported NTv2 grid header."); + } + + int gridFileCount = ReadInt32(bytes, 40, isLittleEndian); + if (gridFileCount <= 0) + { + throw new InvalidDataException("NTv2 file does not contain grids."); + } + + int position = overviewRecordCount * 16; + var allGrids = new List(gridFileCount); + var byName = new Dictionary(StringComparer.Ordinal); + for (int gridIndex = 0; gridIndex < gridFileCount; gridIndex++) + { + if (position + (gridRecordCount * 16) > bytes.Length) + { + throw new InvalidDataException("NTv2 grid header exceeds file size."); + } + + string subName = ReadAscii(bytes, position + (0 * 16) + 8, 8); + string parentName = ReadAscii(bytes, position + (1 * 16) + 8, 8); + + double south = ReadDouble(bytes, position + (4 * 16) + 8, isLittleEndian) * ArcSecondToDegree; + double north = ReadDouble(bytes, position + (5 * 16) + 8, isLittleEndian) * ArcSecondToDegree; + double east = -ReadDouble(bytes, position + (6 * 16) + 8, isLittleEndian) * ArcSecondToDegree; + double west = -ReadDouble(bytes, position + (7 * 16) + 8, isLittleEndian) * ArcSecondToDegree; + double resolutionY = ReadDouble(bytes, position + (8 * 16) + 8, isLittleEndian) * ArcSecondToDegree; + double resolutionX = ReadDouble(bytes, position + (9 * 16) + 8, isLittleEndian) * ArcSecondToDegree; + int gsCount = ReadInt32(bytes, position + (10 * 16) + 8, isLittleEndian); + + if (resolutionX <= 0d || resolutionY <= 0d) + { + throw new InvalidDataException("NTv2 grid has invalid resolution."); + } + + int width = (int)(Math.Abs(((east - west) / resolutionX) + 0.5d) + 1d); + int height = (int)(Math.Abs(((north - south) / resolutionY) + 0.5d) + 1d); + if (width <= 1 || height <= 1) + { + throw new InvalidDataException("NTv2 grid dimensions are invalid."); + } + + if (gsCount / width != height) + { + throw new InvalidDataException("NTv2 GS_COUNT does not match grid dimensions."); + } + + int dataOffset = position + (gridRecordCount * 16); + int dataLength = gsCount * 16; + if (dataOffset < 0 || dataOffset + dataLength > bytes.Length) + { + throw new InvalidDataException("NTv2 grid data exceeds file size."); + } + + float[] latShiftSeconds = new float[gsCount]; + float[] lonShiftSeconds = new float[gsCount]; + for (int i = 0; i < gsCount; i++) + { + int cellOffset = dataOffset + (i * 16); + latShiftSeconds[i] = ReadSingle(bytes, cellOffset, isLittleEndian); + lonShiftSeconds[i] = ReadSingle(bytes, cellOffset + 4, isLittleEndian); + } + + var grid = new Ntv2Grid( + string.IsNullOrWhiteSpace(subName) ? $"GRID_{gridIndex.ToString(CultureInfo.InvariantCulture)}" : subName, + parentName, + west, + east, + south, + north, + resolutionX, + resolutionY, + width, + height, + latShiftSeconds, + lonShiftSeconds); + allGrids.Add(grid); + byName[grid.Name] = grid; + + position = dataOffset + dataLength; + } + + var rootGrids = new List(allGrids.Count); + for (int i = 0; i < allGrids.Count; i++) + { + Ntv2Grid grid = allGrids[i]; + if (!string.IsNullOrWhiteSpace(grid.ParentName) && byName.TryGetValue(grid.ParentName, out Ntv2Grid? parentCandidate)) + { + Ntv2Grid parent = ArgumentGuard.ThrowIfNull(parentCandidate, nameof(parentCandidate)); + parent.AddChild(grid); + } + else + { + rootGrids.Add(grid); + } + } + + return new Ntv2GridSet(new[] { path }, new ReadOnlyCollection(rootGrids)); + } + + internal bool TryFindGrid(double longitude, double latitude, [NotNullWhen(true)] out Ntv2Grid? grid) + { + grid = null; + for (int i = 0; i < this.rootGrids.Count; i++) + { + Ntv2Grid root = this.rootGrids[i]; + if (!root.Contains(longitude, latitude)) + { + continue; + } + + grid = root.FindDeepest(longitude, latitude); + return true; + } + + return false; + } + + private static int ReadInt32(byte[] bytes, int offset, bool littleEndian) + { + return littleEndian == BitConverter.IsLittleEndian + ? BitConverter.ToInt32(bytes, offset) + : (bytes[offset] << 24) + | (bytes[offset + 1] << 16) + | (bytes[offset + 2] << 8) + | bytes[offset + 3]; + } + + private static double ReadDouble(byte[] bytes, int offset, bool littleEndian) + { + long rawBits = littleEndian + ? BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(offset, sizeof(long))) + : BinaryPrimitives.ReadInt64BigEndian(bytes.AsSpan(offset, sizeof(long))); + Span bitStorage = stackalloc long[1]; + bitStorage[0] = rawBits; + return MemoryMarshal.Cast(bitStorage)[0]; + } + + private static float ReadSingle(byte[] bytes, int offset, bool littleEndian) + { + int rawBits = littleEndian + ? BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(offset, sizeof(int))) + : BinaryPrimitives.ReadInt32BigEndian(bytes.AsSpan(offset, sizeof(int))); + Span bitStorage = stackalloc int[1]; + bitStorage[0] = rawBits; + return MemoryMarshal.Cast(bitStorage)[0]; + } + + private static string ReadAscii(byte[] bytes, int offset, int length) + { + return Encoding.ASCII.GetString(bytes, offset, length).TrimEnd('\0', ' '); + } + } + + private sealed class Ntv2Grid + { + private readonly List children = []; + private readonly float[] latShiftSeconds; + private readonly float[] lonShiftSeconds; + + internal Ntv2Grid( + string name, + string parentName, + double west, + double east, + double south, + double north, + double resolutionX, + double resolutionY, + int width, + int height, + float[] latShiftSeconds, + float[] lonShiftSeconds) + { + this.Name = name; + this.ParentName = parentName; + this.West = west; + this.East = east; + this.South = south; + this.North = north; + this.ResolutionX = resolutionX; + this.ResolutionY = resolutionY; + this.Width = width; + this.Height = height; + this.latShiftSeconds = latShiftSeconds; + this.lonShiftSeconds = lonShiftSeconds; + this.Epsilon = (resolutionX + resolutionY) * RelativeTolerance; + } + + internal string Name { get; } + + internal string ParentName { get; } + + internal double West { get; } + + internal double East { get; } + + internal double South { get; } + + internal double North { get; } + + internal double ResolutionX { get; } + + internal double ResolutionY { get; } + + internal int Width { get; } + + internal int Height { get; } + + internal double Epsilon { get; } + + internal void AddChild(Ntv2Grid child) + { + this.children.Add(child); + } + + internal Ntv2Grid FindDeepest(double longitude, double latitude) + { + for (int i = 0; i < this.children.Count; i++) + { + Ntv2Grid child = this.children[i]; + if (!child.Contains(longitude, latitude)) + { + continue; + } + + return child.FindDeepest(longitude, latitude); + } + + return this; + } + + internal bool Contains(double longitude, double latitude) + { + return IsPointInExtent(longitude, latitude, this.West, this.East, this.South, this.North, this.Epsilon); + } + + internal (double LonShift, double LatShift) GetShift(int x, int y, bool compensateNtConvention) + { + int fileX = (this.Width - 1) - x; + int index = (y * this.Width) + fileX; + double latShift = this.latShiftSeconds[index] * ArcSecondToDegree; + double lonShift = this.lonShiftSeconds[index] * ArcSecondToDegree; + if (compensateNtConvention) + { + lonShift = -lonShift; + } + + return (lonShift, latShift); + } + + private static bool IsPointInExtent(double longitude, double latitude, double west, double east, double south, double north, double epsilon) + { + double lon = longitude; + if (lon < west - epsilon) + { + lon += 360d; + } + else if (lon > east + epsilon) + { + lon -= 360d; + } + + return lon >= west - epsilon + && lon <= east + epsilon + && latitude >= south - epsilon + && latitude <= north + epsilon; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/Numerics/Matrix3x3.cs b/src/ProjNet/CoordinateSystems/Transformations/Numerics/Matrix3x3.cs new file mode 100644 index 00000000..55af23e3 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/Numerics/Matrix3x3.cs @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations.Numerics; + +using System; + +/// +/// Represents an immutable 3×3 matrix with precision. +/// +internal readonly record struct Matrix3x3( + double M00, + double M01, + double M02, + double M10, + double M11, + double M12, + double M20, + double M21, + double M22) +{ + /// + /// Gets the 3x3 identity matrix. + /// + internal static Matrix3x3 Identity { get; } = new Matrix3x3( + 1d, + 0d, + 0d, + 0d, + 1d, + 0d, + 0d, + 0d, + 1d); + + /// + /// Gets the zero-filled 3x3 matrix. + /// + internal static Matrix3x3 Zero { get; } = new Matrix3x3( + 0d, + 0d, + 0d, + 0d, + 0d, + 0d, + 0d, + 0d, + 0d); + + /// + /// Gets a value indicating whether this matrix equals . + /// + internal bool IsIdentity => this == Identity; + + /// + /// Gets a value indicating whether this matrix equals . + /// + internal bool IsZero => this == Zero; + + public static Matrix3x3 operator *(Matrix3x3 left, Matrix3x3 right) + { + return new Matrix3x3( + (left.M00 * right.M00) + (left.M01 * right.M10) + (left.M02 * right.M20), + (left.M00 * right.M01) + (left.M01 * right.M11) + (left.M02 * right.M21), + (left.M00 * right.M02) + (left.M01 * right.M12) + (left.M02 * right.M22), + (left.M10 * right.M00) + (left.M11 * right.M10) + (left.M12 * right.M20), + (left.M10 * right.M01) + (left.M11 * right.M11) + (left.M12 * right.M21), + (left.M10 * right.M02) + (left.M11 * right.M12) + (left.M12 * right.M22), + (left.M20 * right.M00) + (left.M21 * right.M10) + (left.M22 * right.M20), + (left.M20 * right.M01) + (left.M21 * right.M11) + (left.M22 * right.M21), + (left.M20 * right.M02) + (left.M21 * right.M12) + (left.M22 * right.M22)); + } + + public static Vector3D operator *(Matrix3x3 matrix, Vector3D vector) + { + return new Vector3D( + (matrix.M00 * vector.X) + (matrix.M01 * vector.Y) + (matrix.M02 * vector.Z), + (matrix.M10 * vector.X) + (matrix.M11 * vector.Y) + (matrix.M12 * vector.Z), + (matrix.M20 * vector.X) + (matrix.M21 * vector.Y) + (matrix.M22 * vector.Z)); + } + + /// + /// Creates a new matrix that is the transpose of this matrix. + /// + /// The transposed matrix. + internal Matrix3x3 Transpose() + { + return new Matrix3x3( + this.M00, + this.M10, + this.M20, + this.M01, + this.M11, + this.M21, + this.M02, + this.M12, + this.M22); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/Numerics/Vector3D.cs b/src/ProjNet/CoordinateSystems/Transformations/Numerics/Vector3D.cs new file mode 100644 index 00000000..2821489b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/Numerics/Vector3D.cs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations.Numerics; + +using System; + +/// +/// Represents an immutable 3D vector with precision. +/// +internal readonly struct Vector3D(double x, double y, double z) +{ + /// + /// Gets the X component. + /// + internal double X { get; } = x; + + /// + /// Gets the Y component. + /// + internal double Y { get; } = y; + + /// + /// Gets the Z component. + /// + internal double Z { get; } = z; + + public static Vector3D operator +(Vector3D left, Vector3D right) + { + return new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + public static Vector3D operator -(Vector3D left, Vector3D right) + { + return new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + public static Vector3D operator -(Vector3D value) + { + return new Vector3D(-value.X, -value.Y, -value.Z); + } + + public static Vector3D operator *(Vector3D value, double scalar) + { + return new Vector3D(value.X * scalar, value.Y * scalar, value.Z * scalar); + } + + public static Vector3D operator *(double scalar, Vector3D value) + { + return value * scalar; + } + + public static Vector3D operator /(Vector3D value, double scalar) + { + return new Vector3D(value.X / scalar, value.Y / scalar, value.Z / scalar); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/ObTranMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/ObTranMathTransform.cs new file mode 100644 index 00000000..87b6f195 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/ObTranMathTransform.cs @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; + +/// +/// Implements PROJ's ob_tran runtime transform by rotating geographic coordinates and delegating to a child projection. +/// +/// +/// +/// The general oblique transformation rotates geographic coordinates into a +/// derived pole or equator definition and then delegates the rotated +/// coordinates to a child projection. This implementation supports PROJ's three +/// standard rotation parameterizations: new-pole, rotate-about-point, and +/// two-point new-equator modes. +/// +/// +/// The runtime was independently verified against PROJ's published +/// ob_tran documentation and ob_tran.cpp. The forward and inverse +/// paths preserve the reviewed oblique/transverse rotation logic before and +/// after the delegated child projection step. +/// +/// +/// PROJ: ob_tran. +internal sealed class ObTranMathTransform : MathTransform +{ + private const double Tolerance = 1e-10d; + + private readonly MathTransform childForward; + private readonly MathTransform childInverse; + private readonly bool childIsAngular; + private readonly double lamp; + private readonly double phip; + private readonly double centralMeridian; + private readonly double sphip; + private readonly double cphip; + private readonly bool isOblique; + private readonly bool isInverted; + private MathTransform? inverse; + + private ObTranMathTransform( + MathTransform childForward, + MathTransform childInverse, + bool childIsAngular, + double lamp, + double phip, + double centralMeridian, + bool isInverted) + { + this.childForward = ArgumentGuard.ThrowIfNull(childForward, nameof(childForward)); + this.childInverse = ArgumentGuard.ThrowIfNull(childInverse, nameof(childInverse)); + this.childIsAngular = childIsAngular; + this.lamp = lamp; + this.phip = phip; + this.centralMeridian = centralMeridian; + this.sphip = Math.Sin(phip); + this.cphip = Math.Cos(phip); + this.isOblique = Math.Abs(phip) > Tolerance; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 2; + + /// + public override int DimTarget => 2; + + /// + public override MathTransform Inverse() + { + this.inverse ??= new ObTranMathTransform( + this.childForward, + this.childInverse, + this.childIsAngular, + this.lamp, + this.phip, + this.centralMeridian, + !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("ObTranMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (this.isInverted) + { + this.TransformInverse(ref x, ref y); + } + else + { + this.TransformForward(ref x, ref y); + } + } + + /// + /// Creates an from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (!args.TryGetValue("o_proj", out string? childProjCode) || string.IsNullOrWhiteSpace(childProjCode)) + { + skipReason = "ob_tran requires +o_proj."; + return false; + } + + if (childProjCode.Equals("ob_tran", StringComparison.OrdinalIgnoreCase)) + { + skipReason = "Nested ob_tran is not supported."; + return false; + } + + Dictionary childArgs = BuildChildProjectionArguments(args, childProjCode); + if (!TryCreateProjectionTransform(childArgs, out MathTransform? childForwardCandidate, out MathTransform? childInverseCandidate, out bool childIsAngular, out skipReason)) + { + return false; + } + + MathTransform childForward = ArgumentGuard.ThrowIfNull(childForwardCandidate, nameof(childForwardCandidate)); + MathTransform childInverse = ArgumentGuard.ThrowIfNull(childInverseCandidate, nameof(childInverseCandidate)); + + if (!TryResolveRotation(args, out double lamp, out double phip, out skipReason)) + { + return false; + } + + double centralMeridian = 0d; + if (TryGetFromArgs(args, "lon_0", out double lon0Degrees)) + { + centralMeridian = ToRadians(lon0Degrees); + } + + transform = new ObTranMathTransform(childForward, childInverse, childIsAngular, lamp, phip, centralMeridian, false); + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + + private static Dictionary BuildChildProjectionArguments( + Dictionary args, + string childProjCode) + { + var childArgs = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (KeyValuePair kvp in args) + { + if (kvp.Key.StartsWith("o_", StringComparison.OrdinalIgnoreCase) + || kvp.Key.Equals("proj", StringComparison.OrdinalIgnoreCase) + || kvp.Key.Equals("inv", StringComparison.OrdinalIgnoreCase) + || kvp.Key.Equals("lon_0", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + childArgs[kvp.Key] = kvp.Value; + } + + childArgs["proj"] = childProjCode; + return childArgs; + } + + private static bool TryResolveRotation( + Dictionary args, + out double lamp, + out double phip, + out string? skipReason) + { + lamp = 0d; + phip = 0d; + skipReason = null; + + if (args.ContainsKey("o_alpha")) + { + if (!TryGetRequiredDegrees(args, "o_lon_c", out double lamc) + || !TryGetRequiredDegrees(args, "o_lat_c", out double phic) + || !TryGetRequiredDegrees(args, "o_alpha", out double alpha)) + { + skipReason = "ob_tran with +o_alpha requires +o_lon_c, +o_lat_c and +o_alpha."; + return false; + } + + if (Math.Abs(Math.Abs(phic) - (Math.PI * 0.5d)) <= Tolerance) + { + skipReason = "Invalid value for o_lat_c: |o_lat_c| should be < 90°."; + return false; + } + + lamp = lamc + Math.Atan2(-Math.Cos(alpha), -Math.Sin(alpha) * Math.Sin(phic)); + phip = SafeAsin(Math.Cos(phic) * Math.Sin(alpha)); + return true; + } + + if (args.ContainsKey("o_lat_p")) + { + if (!TryGetRequiredDegrees(args, "o_lat_p", out phip)) + { + skipReason = "ob_tran requires numeric +o_lat_p when using pole mode."; + return false; + } + + if (args.TryGetValue("o_lon_p", out string? lonPoleToken) && !string.IsNullOrWhiteSpace(lonPoleToken)) + { + if (!TryGetDouble(lonPoleToken, out double lonPoleDegrees)) + { + skipReason = "Invalid value for +o_lon_p."; + return false; + } + + lamp = ToRadians(lonPoleDegrees); + } + + return true; + } + + if (!TryGetRequiredDegrees(args, "o_lon_1", out double lam1) + || !TryGetRequiredDegrees(args, "o_lat_1", out double phi1) + || !TryGetRequiredDegrees(args, "o_lon_2", out double lam2) + || !TryGetRequiredDegrees(args, "o_lat_2", out double phi2)) + { + skipReason = "ob_tran requires either (+o_lon_p,+o_lat_p), (+o_alpha,+o_lon_c,+o_lat_c), or (+o_lon_1,+o_lat_1,+o_lon_2,+o_lat_2)."; + return false; + } + + if (Math.Abs(phi1) > (Math.PI * 0.5d) - Tolerance || Math.Abs(phi2) > (Math.PI * 0.5d) - Tolerance) + { + skipReason = "Invalid values for o_lat_1/o_lat_2: |lat| should be < 90°."; + return false; + } + + if (Math.Abs(phi1 - phi2) < Tolerance) + { + skipReason = "Invalid values for o_lat_1 and o_lat_2: they must differ."; + return false; + } + + if (Math.Abs(phi1) < Tolerance) + { + skipReason = "Invalid value for o_lat_1: it should differ from 0."; + return false; + } + + lamp = Math.Atan2( + (Math.Cos(phi1) * Math.Sin(phi2) * Math.Cos(lam1)) - (Math.Sin(phi1) * Math.Cos(phi2) * Math.Cos(lam2)), + (Math.Sin(phi1) * Math.Cos(phi2) * Math.Sin(lam2)) - (Math.Cos(phi1) * Math.Sin(phi2) * Math.Sin(lam1))); + phip = Math.Atan(-Math.Cos(lamp - lam1) / Math.Tan(phi1)); + return true; + } + + private static bool TryCreateProjectionTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? forward, + [NotNullWhen(true)] out MathTransform? inverse, + out bool childIsAngular, + out string? skipReason) + { + forward = null; + inverse = null; + childIsAngular = false; + skipReason = null; + + if (!args.TryGetValue("proj", out string? projCode) || string.IsNullOrWhiteSpace(projCode)) + { + skipReason = "Projection argument +proj is required."; + return false; + } + + if (projCode.Equals("latlon", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("latlong", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("lonlat", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("longlat", StringComparison.OrdinalIgnoreCase)) + { + forward = new IdentityMathTransform(2); + inverse = forward; + childIsAngular = true; + return true; + } + + List parameters = BuildProjectionParameters(args); + if (!ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + includeDatumToken: false, + allowClarke1880Ign: false, + allowBessel: false, + out double semiMajor, + out double semiMinor)) + { + skipReason = "Unable to resolve ellipsoid for ob_tran child projection."; + return false; + } + + ReplaceOrAdd(parameters, "semi_major", semiMajor); + ReplaceOrAdd(parameters, "semi_minor", semiMinor); + ReplaceOrAdd(parameters, "unit", 1d); + + try + { + forward = ProjectionsRegistry.CreateProjection(projCode, parameters); + inverse = forward.Inverse(); + return true; + } + catch (Exception exception) when (exception is ArgumentException || exception is NotSupportedException || exception is InvalidOperationException || exception is System.Reflection.TargetInvocationException) + { + skipReason = $"Unable to create ob_tran child projection '{projCode}': {exception.Message}"; + return false; + } + } + + private static List BuildProjectionParameters(Dictionary args) + { + var parameters = new List + { + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + }; + + AddOptionalParameter(parameters, args, "lat_0", "latitude_of_origin"); + AddOptionalParameter(parameters, args, "lon_0", "central_meridian"); + AddOptionalParameter(parameters, args, "k_0", "scale_factor"); + AddOptionalParameter(parameters, args, "k", "scale_factor"); + AddOptionalParameter(parameters, args, "x_0", "false_easting"); + AddOptionalParameter(parameters, args, "y_0", "false_northing"); + + AddOptionalParameter(parameters, args, "lat_1", "lat_1"); + AddOptionalParameter(parameters, args, "lat_2", "lat_2"); + AddOptionalParameter(parameters, args, "lon_1", "lon_1"); + AddOptionalParameter(parameters, args, "lon_2", "lon_2"); + AddOptionalParameter(parameters, args, "lat_3", "lat_3"); + AddOptionalParameter(parameters, args, "lon_3", "lon_3"); + AddOptionalParameter(parameters, args, "lat_b", "lat_b"); + AddOptionalParameter(parameters, args, "lat_ts", "lat_ts"); + AddOptionalParameter(parameters, args, "alpha", "alpha"); + AddOptionalParameter(parameters, args, "azi", "azi"); + AddOptionalParameter(parameters, args, "lonc", "longitude_of_center"); + AddOptionalParameter(parameters, args, "h", "h"); + AddOptionalParameter(parameters, args, "satellite_height", "h"); + AddOptionalParameter(parameters, args, "m", "m"); + AddOptionalParameter(parameters, args, "n", "n"); + AddOptionalParameter(parameters, args, "q", "q"); + + if (args.ContainsKey("no_cut")) + { + ReplaceOrAdd(parameters, "no_cut", 1d); + } + + if (args.ContainsKey("ns") || args.ContainsKey("noskew")) + { + ReplaceOrAdd(parameters, "ns", 1d); + } + + if (args.TryGetValue("sweep", out string? sweepAxis)) + { + double sweepX = sweepAxis.Equals("x", StringComparison.OrdinalIgnoreCase) ? 1d : 0d; + ReplaceOrAdd(parameters, "sweep_x", sweepX); + } + + return parameters; + } + + private static void AddOptionalParameter( + List parameters, + Dictionary args, + string sourceName, + string targetName) + { + if (!args.TryGetValue(sourceName, out string? valueToken) || string.IsNullOrWhiteSpace(valueToken)) + { + return; + } + + if (!TryGetDouble(valueToken, out double value)) + { + return; + } + + ReplaceOrAdd(parameters, targetName, value); + } + + private static void ReplaceOrAdd(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } + + private static bool TryGetRequiredDegrees( + Dictionary args, + string key, + out double radians) + { + radians = 0d; + if (!TryGetFromArgs(args, key, out double degrees)) + { + return false; + } + + radians = ToRadians(degrees); + return true; + } + + private static bool TryGetFromArgs(Dictionary args, string key, out double value) + { + value = 0d; + return args.TryGetValue(key, out string? token) && !string.IsNullOrWhiteSpace(token) && TryGetDouble(token, out value); + } + + private static bool TryGetDouble(string token, out double value) + { + return double.TryParse(token, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value) + && !double.IsNaN(value) + && !double.IsInfinity(value); + } + + private static double ToRadians(double degrees) + { + return degrees * (Math.PI / 180d); + } + + private static double ToDegrees(double radians) + { + return radians * (180d / Math.PI); + } + + private static double SafeAsin(double value) + { + if (value > 1d) + { + value = 1d; + } + else if (value < -1d) + { + value = -1d; + } + + return Math.Asin(value); + } + + private static double AdjustLon(double value) + { + while (value > Math.PI) + { + value -= 2d * Math.PI; + } + + while (value < -Math.PI) + { + value += 2d * Math.PI; + } + + return value; + } + + private static void RotateForward( + ref double lam, + ref double phi, + double lamp, + double sphip, + double cphip, + bool isOblique) + { + double cosPhi = Math.Cos(phi); + double cosLam = Math.Cos(lam); + + if (isOblique) + { + double sinPhi = Math.Sin(phi); + lam = AdjustLon(Math.Atan2(cosPhi * Math.Sin(lam), (sphip * cosPhi * cosLam) + (cphip * sinPhi)) + lamp); + phi = SafeAsin((sphip * sinPhi) - (cphip * cosPhi * cosLam)); + return; + } + + lam = AdjustLon(Math.Atan2(cosPhi * Math.Sin(lam), Math.Sin(phi)) + lamp); + phi = SafeAsin(-cosPhi * cosLam); + } + + private static void RotateInverse( + ref double lam, + ref double phi, + double lamp, + double sphip, + double cphip, + bool isOblique) + { + if (isOblique) + { + lam -= lamp; + double cosLam = Math.Cos(lam); + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + phi = SafeAsin((sphip * sinPhi) + (cphip * cosPhi * cosLam)); + lam = Math.Atan2(cosPhi * Math.Sin(lam), (sphip * cosPhi * cosLam) - (cphip * sinPhi)); + return; + } + + double cosPhiTransverse = Math.Cos(phi); + double t = lam - lamp; + lam = Math.Atan2(cosPhiTransverse * Math.Sin(t), -Math.Sin(phi)); + phi = SafeAsin(cosPhiTransverse * Math.Cos(t)); + } + + private void TransformForward(ref double x, ref double y) + { + double lam = AdjustLon(ToRadians(x) - this.centralMeridian); + double phi = ToRadians(y); + RotateForward(ref lam, ref phi, this.lamp, this.sphip, this.cphip, this.isOblique); + + Span childInput = stackalloc double[2]; + if (this.childIsAngular) + { + childInput[0] = lam; + childInput[1] = phi; + } + else + { + childInput[0] = ToDegrees(lam); + childInput[1] = ToDegrees(phi); + } + + Span childOutput = stackalloc double[3]; + this.childForward.Transform((ReadOnlySpan)childInput, childOutput); + x = childOutput[0]; + y = childOutput[1]; + } + + private void TransformInverse(ref double x, ref double y) + { + Span childInput = stackalloc double[2]; + childInput[0] = x; + childInput[1] = y; + + Span rotated = stackalloc double[3]; + this.childInverse.Transform((ReadOnlySpan)childInput, rotated); + double lam = this.childIsAngular ? rotated[0] : ToRadians(rotated[0]); + double phi = this.childIsAngular ? rotated[1] : ToRadians(rotated[1]); + RotateInverse(ref lam, ref phi, this.lamp, this.sphip, this.cphip, this.isOblique); + x = ToDegrees(AdjustLon(lam + this.centralMeridian)); + y = ToDegrees(phi); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/PipelineCompositeMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/PipelineCompositeMathTransform.cs new file mode 100644 index 00000000..e0144b9a --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/PipelineCompositeMathTransform.cs @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; + +/// +/// Executes a PROJ pipeline chain while preserving shared pipeline state between steps. +/// +/// +/// This composite variant executes a pipeline step chain against a shared +/// so stack-based and stateful steps can +/// communicate across the pipeline. Each invocation clears the shared context +/// before replaying the ordered step list. +/// +/// PROJ: pipeline operator. +internal sealed class PipelineCompositeMathTransform : MathTransform +{ + private readonly MathTransform[] transforms; + private readonly PipelineExecutionContext executionContext; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Ordered transform chain executed from first to last. + /// Pipeline execution context shared across steps. + internal PipelineCompositeMathTransform(IReadOnlyList transforms, PipelineExecutionContext executionContext) + { + transforms = ArgumentGuard.ThrowIfNull(transforms, nameof(transforms)); + this.executionContext = ArgumentGuard.ThrowIfNull(executionContext, nameof(executionContext)); + + if (transforms.Count == 0) + { + ArgumentGuard.ThrowArgument("At least one math transform is required.", nameof(transforms)); + } + + this.transforms = new MathTransform[transforms.Count]; + for (int i = 0; i < transforms.Count; i++) + { + if (transforms[i] is null) + { + ArgumentGuard.ThrowArgument("Math transform list contains null element.", nameof(transforms)); + } + + this.transforms[i] = transforms[i]; + } + } + + /// + public override int DimSource => this.transforms[0].DimSource; + + /// + public override int DimTarget => this.transforms[^1].DimTarget; + + /// + public override bool Identity() + { + for (int i = 0; i < this.transforms.Length; i++) + { + if (!this.transforms[i].Identity()) + { + return false; + } + } + + return true; + } + + /// + public override MathTransform Inverse() + { + if (this.inverse is not null) + { + return this.inverse; + } + + var inverted = new MathTransform[this.transforms.Length]; + int output = 0; + for (int i = this.transforms.Length - 1; i >= 0; i--) + { + inverted[output] = this.transforms[i].Inverse(); + output++; + } + + this.inverse = new PipelineCompositeMathTransform(inverted, this.executionContext); + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("Pipeline composite transform does not support in-place inversion."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + double t = 0d; + this.TransformCore(ref x, ref y, ref z, ref t, includeTime: false); + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + this.TransformCore(ref x, ref y, ref z, ref t, includeTime: true); + } + + private void TransformCore(ref double x, ref double y, ref double z, ref double t, bool includeTime) + { + this.executionContext.Clear(); + + for (int i = 0; i < this.transforms.Length; i++) + { + if (includeTime) + { + this.transforms[i].Transform(ref x, ref y, ref z, ref t); + } + else + { + this.transforms[i].Transform(ref x, ref y, ref z); + } + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/PipelineExecutionContext.cs b/src/ProjNet/CoordinateSystems/Transformations/PipelineExecutionContext.cs new file mode 100644 index 00000000..8d57412d --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/PipelineExecutionContext.cs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; + +/// +/// Holds pipeline execution state shared by stack-aware runtime steps. +/// +internal sealed class PipelineExecutionContext +{ + private readonly Stack[] stacks = + [ + new Stack(), + new Stack(), + new Stack(), + new Stack(), + ]; + + /// + /// Pushes an ordinate value for a coordinate component. + /// + /// Zero-based coordinate component index (0..3). + /// Value to push. + internal void Push(int coordinateIndex, double value) + { + this.stacks[coordinateIndex].Push(value); + } + + /// + /// Attempts to pop an ordinate value for a coordinate component. + /// + /// Zero-based coordinate component index (0..3). + /// Popped value when available. + /// when a value was popped; otherwise . + internal bool TryPop(int coordinateIndex, out double value) + { + Stack stack = this.stacks[coordinateIndex]; + if (stack.Count == 0) + { + value = 0d; + return false; + } + + value = stack.Pop(); + return true; + } + + /// + /// Clears all stack contents for a new coordinate transformation run. + /// + internal void Clear() + { + for (int i = 0; i < this.stacks.Length; i++) + { + this.stacks[i].Clear(); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/PipelineOmitMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/PipelineOmitMathTransform.cs new file mode 100644 index 00000000..de013715 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/PipelineOmitMathTransform.cs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Wraps a pipeline step and conditionally omits it for forward and inverse traversal. +/// +/// +/// Pipeline omission wraps an inner step but can turn that step into an +/// effective identity in the forward direction, the inverse direction, or both. +/// As a result, is intentionally direction-dependent +/// and reflects whether the current forward traversal skips the wrapped step. +/// +/// PROJ: pipeline operator. +internal sealed class PipelineOmitMathTransform : MathTransform +{ + private readonly MathTransform inner; + private readonly bool skipForward; + private readonly bool skipInverse; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Inner step transform. + /// Whether to skip the step in forward traversal. + /// Whether to skip the step in inverse traversal. + internal PipelineOmitMathTransform(MathTransform inner, bool skipForward, bool skipInverse) + { + this.inner = ArgumentGuard.ThrowIfNull(inner, nameof(inner)); + this.skipForward = skipForward; + this.skipInverse = skipInverse; + } + + /// + public override int DimSource => this.inner.DimSource; + + /// + public override int DimTarget => this.inner.DimTarget; + + /// + public override bool Identity() + { + return this.skipForward || this.inner.Identity(); + } + + /// + public override MathTransform Inverse() + { + this.inverse ??= new PipelineOmitMathTransform(this.inner.Inverse(), this.skipInverse, this.skipForward); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("Pipeline omit transform does not support in-place inversion."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (this.skipForward) + { + return; + } + + this.inner.Transform(ref x, ref y, ref z); + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + if (this.skipForward) + { + return; + } + + this.inner.Transform(ref x, ref y, ref z, ref t); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/PipelineStackTransferMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/PipelineStackTransferMathTransform.cs new file mode 100644 index 00000000..0ad16a5f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/PipelineStackTransferMathTransform.cs @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +/// +/// Implements PROJ pipeline stack transfer steps (push and pop). +/// +/// +/// Stack-transfer steps move selected ordinates between the live coordinate +/// tuple and the shared pipeline execution stack. push stores enabled +/// ordinates for later reuse, while pop restores them, matching PROJ's +/// pipeline context transfer model. +/// +/// PROJ: push coordinate value to pipeline stack. +/// PROJ: pop coordinate value from pipeline stack. +internal sealed class PipelineStackTransferMathTransform : MathTransform +{ + private readonly bool isPush; + private readonly bool[] enabledOrdinateFlags; + private readonly PipelineExecutionContext executionContext; + + private PipelineStackTransferMathTransform(bool isPush, bool[] enabledOrdinateFlags, PipelineExecutionContext executionContext) + { + this.isPush = isPush; + this.enabledOrdinateFlags = enabledOrdinateFlags; + this.executionContext = executionContext; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return false; + } + + /// + public override MathTransform Inverse() + { + return new PipelineStackTransferMathTransform(!this.isPush, (bool[])this.enabledOrdinateFlags.Clone(), this.executionContext); + } + + /// + public override void Invert() + { + throw new NotSupportedException("Pipeline stack transfer transform does not support in-place inversion."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + double t = 0d; + this.TransformCore(ref x, ref y, ref z, ref t, includeTime: false); + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + this.TransformCore(ref x, ref y, ref z, ref t, includeTime: true); + } + + /// + /// Creates a runtime push stack transfer transform. + /// + /// Parsed PROJ argument dictionary. + /// Pipeline execution context. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreatePush( + Dictionary args, + PipelineExecutionContext executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + return TryCreate(args, executionContext, isPush: true, out transform, out skipReason); + } + + /// + /// Creates a runtime pop stack transfer transform. + /// + /// Parsed PROJ argument dictionary. + /// Pipeline execution context. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreatePop( + Dictionary args, + PipelineExecutionContext executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + return TryCreate(args, executionContext, isPush: false, out transform, out skipReason); + } + + private static bool TryCreate( + Dictionary args, + PipelineExecutionContext executionContext, + bool isPush, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "Pipeline stack transfer arguments were null."; + return false; + } + + if (!TryParseEnabledOrdinateFlags(args, out bool[] enabledOrdinateFlags, out skipReason)) + { + return false; + } + + if (executionContext is null) + { + transform = new IdentityMathTransform(3); + return true; + } + + transform = new PipelineStackTransferMathTransform(isPush, enabledOrdinateFlags, executionContext); + return true; + } + + private static bool TryParseEnabledOrdinateFlags( + Dictionary args, + out bool[] enabledOrdinateFlags, + out string? skipReason) + { + enabledOrdinateFlags = [false, false, false, false]; + skipReason = null; + + if (args.TryGetValue("v_1", out string? v1Token) && !IsBooleanFlag("v_1", v1Token)) + { + skipReason = "push/pop does not accept values for +v_1; use +v_1 as a flag."; + return false; + } + + if (args.TryGetValue("v_2", out string? v2Token) && !IsBooleanFlag("v_2", v2Token)) + { + skipReason = "push/pop does not accept values for +v_2; use +v_2 as a flag."; + return false; + } + + if (args.TryGetValue("v_3", out string? v3Token) && !IsBooleanFlag("v_3", v3Token)) + { + skipReason = "push/pop does not accept values for +v_3; use +v_3 as a flag."; + return false; + } + + if (args.TryGetValue("v_4", out string? v4Token) && !IsBooleanFlag("v_4", v4Token)) + { + skipReason = "push/pop does not accept values for +v_4; use +v_4 as a flag."; + return false; + } + + enabledOrdinateFlags[0] = args.ContainsKey("v_1"); + enabledOrdinateFlags[1] = args.ContainsKey("v_2"); + enabledOrdinateFlags[2] = args.ContainsKey("v_3"); + enabledOrdinateFlags[3] = args.ContainsKey("v_4"); + + if (!enabledOrdinateFlags[0] && !enabledOrdinateFlags[1] && !enabledOrdinateFlags[2] && !enabledOrdinateFlags[3]) + { + skipReason = "push/pop requires at least one of +v_1, +v_2, +v_3 or +v_4."; + return false; + } + + return true; + } + + private static bool IsBooleanFlag(string key, string token) + { + return string.IsNullOrEmpty(token) + || token.Equals("true", StringComparison.OrdinalIgnoreCase) + || token.Equals(key, StringComparison.Ordinal); + } + + private void TransformCore(ref double x, ref double y, ref double z, ref double t, bool includeTime) + { + if (this.isPush) + { + if (this.enabledOrdinateFlags[0]) + { + this.executionContext.Push(0, x); + } + + if (this.enabledOrdinateFlags[1]) + { + this.executionContext.Push(1, y); + } + + if (this.enabledOrdinateFlags[2]) + { + this.executionContext.Push(2, z); + } + + if (includeTime && this.enabledOrdinateFlags[3]) + { + this.executionContext.Push(3, t); + } + + return; + } + + if (this.enabledOrdinateFlags[0] && this.executionContext.TryPop(0, out double xValue)) + { + x = xValue; + } + + if (this.enabledOrdinateFlags[1] && this.executionContext.TryPop(1, out double yValue)) + { + y = yValue; + } + + if (this.enabledOrdinateFlags[2] && this.executionContext.TryPop(2, out double zValue)) + { + z = zValue; + } + + if (includeTime && this.enabledOrdinateFlags[3] && this.executionContext.TryPop(3, out double tValue)) + { + t = tValue; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/PrimeMeridianTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/PrimeMeridianTransform.cs index 05dc735c..0688d48c 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/PrimeMeridianTransform.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/PrimeMeridianTransform.cs @@ -1,119 +1,96 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems.Transformations; using System; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// Adjusts target Prime Meridian. +/// +/// +/// Prime-meridian adjustment is a simple longitude translation by the angular +/// difference between the source and target prime meridians: +/// x += source.Longitude - target.Longitude. The inverse applies the +/// same difference with reversed sign. +/// +/// PROJ usage: prime meridian and axis orientation. +internal sealed class PrimeMeridianTransform : MathTransform { + private readonly PrimeMeridian source; + private readonly PrimeMeridian target; + private readonly bool isInverted; /// - /// Adjusts target Prime Meridian + /// Initializes a new instance of the class. /// - [Serializable] - internal class PrimeMeridianTransform : MathTransform + /// Source prime meridian. + /// Target prime meridian. + public PrimeMeridianTransform(PrimeMeridian source, PrimeMeridian target) + : this(source, target, false) { - #region class variables - - private bool _isInverted; - private readonly PrimeMeridian _source; - private readonly PrimeMeridian _target; - #endregion class variables - - #region constructors & finalizers - /// - /// Creates instance prime meridian transform - /// - /// - /// - public PrimeMeridianTransform(PrimeMeridian source, PrimeMeridian target) - { - if (!source.AngularUnit.EqualParams(target.AngularUnit)) - { - throw new NotImplementedException("The method or operation is not implemented."); - } - _source = source; - _target = target; - } - - - #endregion constructors & finalizers + } - #region public properties - /// - /// Gets a Well-Known text representation of this affine math transformation. - /// - /// - public override string WKT - { - get { throw new NotImplementedException("The method or operation is not implemented."); } - } - /// - /// Gets an XML representation of this affine transformation. - /// - /// - public override string XML + private PrimeMeridianTransform(PrimeMeridian source, PrimeMeridian target, bool isInverted) + { + if (!source.AngularUnit.EqualParams(target.AngularUnit)) { - get { throw new NotImplementedException("The method or operation is not implemented."); } + throw new NotSupportedException("Prime meridian transformation requires matching angular units."); } - /// - /// Gets the dimension of input points. - /// - public override int DimSource { get { return 3; } } + this.source = source; + this.target = target; + this.isInverted = isInverted; + } - /// - /// Gets the dimension of output points. - /// - public override int DimTarget { get { return 3; } } - #endregion public properties + /// + /// Gets the dimension of input points. + /// + public override int DimSource => 3; - #region public methods + /// + /// Gets the dimension of output points. + /// + public override int DimTarget => 3; - /// - public override MathTransform Inverse() - { - return new PrimeMeridianTransform(_target, _source); - } + /// + public override MathTransform Inverse() + { + return new PrimeMeridianTransform(this.source, this.target, !this.isInverted); + } - /// - public sealed override void Transform(ref double x, ref double y, ref double z) + /// + public sealed override void Transform(ref double x, ref double y, ref double z) + { + if (this.isInverted) { - if (_isInverted) - x += _target.Longitude - _source.Longitude; - else - x += _source.Longitude - _target.Longitude; + x += this.target.Longitude - this.source.Longitude; } - - /// - protected sealed override void TransformCore(Span xs, Span ys, Span zs, - int strideX, int strideY, int strideZ) + else { - double addend = _isInverted - ? _target.Longitude - _source.Longitude - : _source.Longitude - _target.Longitude; - AddInPlace(xs, strideX, addend); + x += this.source.Longitude - this.target.Longitude; } + } - /// - public override void Invert() - { - _isInverted = !_isInverted; - } + /// + protected sealed override void TransformCore( + Span xs, + Span ys, + Span zs, + int strideX, + int strideY, + int strideZ) + { + double addend = this.isInverted + ? this.target.Longitude - this.source.Longitude + : this.source.Longitude - this.target.Longitude; + AddInPlace(xs, strideX, addend); + } - #endregion public methods + /// + public override void Invert() + { + throw new NotSupportedException("PrimeMeridianTransform is immutable. Use Inverse() to obtain inverted transform."); } } diff --git a/src/ProjNet/CoordinateSystems/Transformations/ProjEllipsoidResolver.cs b/src/ProjNet/CoordinateSystems/Transformations/ProjEllipsoidResolver.cs new file mode 100644 index 00000000..8e86d9ec --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/ProjEllipsoidResolver.cs @@ -0,0 +1,1018 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; + +/// +/// Resolves PROJ ellipsoid tokens to runtime semi-major and semi-minor axes. +/// +internal static class ProjEllipsoidResolver +{ + private const double Clarke1880SemiMajorAxis = 6378249.145d; + private const double Clarke1880InverseFlattening = 293.4663d; + private const double Clarke1880IgnSemiMajorAxis = 6378249.2d; + private const double Clarke1880IgnInverseFlattening = 293.4660212936269d; + private const double ModifiedAirySemiMajorAxis = 6377340.189d; + private const double ModifiedAiryInverseFlattening = 299.3249646d; + private const double Sixth = 1d / 6d; + private const double Ra4 = 17d / 360d; + private const double Ra6 = 67d / 3024d; + private const double Rv4 = 5d / 72d; + private const double Rv6 = 55d / 1296d; + private static readonly string[] SpherificationKeys = ["R_A", "R_V", "R_a", "R_g", "R_h", "R_lat_a", "R_lat_g", "R_C"]; + + /// + /// Tries to resolve a supported PROJ ellipsoid token to metric semi-axis values. + /// + /// The ellipsoid token to resolve. + /// Whether the clrk80ign token is supported by the caller. + /// Whether the bessel token is supported by the caller. + /// The resolved semi-major axis in metres. + /// The resolved semi-minor axis in metres. + /// when the token is recognized. + internal static bool TryResolveKnownEllipsoid( + string token, + bool allowClarke1880Ign, + bool allowBessel, + out double semiMajor, + out double semiMinor) + { + semiMajor = 0d; + semiMinor = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + if (token.Equals("wgs84", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Ellipsoid.WGS84.SemiMajorAxis; + semiMinor = Ellipsoid.WGS84.SemiMinorAxis; + return true; + } + + if (token.Equals("grs80", StringComparison.OrdinalIgnoreCase) + || token.Equals("nad83", StringComparison.OrdinalIgnoreCase) + || token.Equals("ggrs87", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Ellipsoid.GRS80.SemiMajorAxis; + semiMinor = Ellipsoid.GRS80.SemiMinorAxis; + return true; + } + + if (token.Equals("clrk66", StringComparison.OrdinalIgnoreCase) + || token.Equals("nad27", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Ellipsoid.Clarke1866.SemiMajorAxis; + semiMinor = Ellipsoid.Clarke1866.SemiMinorAxis; + return true; + } + + // PROJ token tables define clrk80/clrk80ign in metres, unlike the historic + // public Ellipsoid.Clarke1880 model in this codebase, which preserves foot units. + if (token.Equals("clrk80", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Clarke1880SemiMajorAxis; + semiMinor = ComputeSemiMinorAxis(Clarke1880SemiMajorAxis, Clarke1880InverseFlattening); + return true; + } + + if (allowClarke1880Ign && token.Equals("clrk80ign", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Clarke1880IgnSemiMajorAxis; + semiMinor = ComputeSemiMinorAxis(Clarke1880IgnSemiMajorAxis, Clarke1880IgnInverseFlattening); + return true; + } + + if (token.Equals("intl", StringComparison.OrdinalIgnoreCase) + || token.Equals("nzgd49", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Ellipsoid.International1924.SemiMajorAxis; + semiMinor = Ellipsoid.International1924.SemiMinorAxis; + return true; + } + + if (token.Equals("airy", StringComparison.OrdinalIgnoreCase) + || token.Equals("osgb36", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Ellipsoid.Airy1830.SemiMajorAxis; + semiMinor = Ellipsoid.Airy1830.SemiMinorAxis; + return true; + } + + if (token.Equals("mod_airy", StringComparison.OrdinalIgnoreCase) + || token.Equals("ire65", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = ModifiedAirySemiMajorAxis; + semiMinor = ComputeSemiMinorAxis(ModifiedAirySemiMajorAxis, ModifiedAiryInverseFlattening); + return true; + } + + if (allowBessel + && (token.Equals("bessel", StringComparison.OrdinalIgnoreCase) + || token.Equals("potsdam", StringComparison.OrdinalIgnoreCase))) + { + semiMajor = Ellipsoid.Bessel1841.SemiMajorAxis; + semiMinor = Ellipsoid.Bessel1841.SemiMinorAxis; + return true; + } + + if (token.Equals("sphere", StringComparison.OrdinalIgnoreCase)) + { + semiMajor = Ellipsoid.Sphere.SemiMajorAxis; + semiMinor = Ellipsoid.Sphere.SemiMinorAxis; + return true; + } + + return false; + } + + /// + /// Applies explicit PROJ ellipsoid shape overrides to previously resolved semi-axis values. + /// + /// The PROJ argument dictionary. + /// The resolved semi-major axis in metres. + /// The resolved semi-minor axis in metres. + /// An error message when an override token is invalid. + /// when the override set is valid. + internal static bool TryApplyExplicitShapeOverrides( + IReadOnlyDictionary args, + ref double semiMajor, + ref double semiMinor, + out string? errorMessage) + { + errorMessage = null; + return TryApplyExplicitShapeParameters(args, ref semiMajor, ref semiMinor, out errorMessage) + && TryApplySpherification(args, ref semiMajor, ref semiMinor, out errorMessage); + } + + /// + /// Applies an explicit +a size override while preserving the currently resolved ellipsoid shape. + /// + /// The PROJ argument dictionary. + /// The resolved semi-major axis in metres. + /// The resolved semi-minor axis in metres. + /// An error message when the override token is invalid. + /// when the override set is valid. + internal static bool TryApplySemiMajorOverride( + IReadOnlyDictionary args, + ref double semiMajor, + ref double semiMinor, + out string? errorMessage) + { + errorMessage = null; + if (!TryGetNonEmptyToken(args, "a", out string majorToken)) + { + return true; + } + + if (!SpanParseUtility.TryParseFiniteDouble(majorToken, out double explicitSemiMajor) || explicitSemiMajor <= 0d) + { + errorMessage = "Ellipsoid +a override must be finite and positive."; + return false; + } + + if (semiMajor <= 0d || semiMinor <= 0d) + { + errorMessage = "Ellipsoid +a override requires a previously resolved positive ellipsoid shape."; + return false; + } + + double scale = explicitSemiMajor / semiMajor; + semiMajor = explicitSemiMajor; + semiMinor *= scale; + return TryValidateResolvedAxes(semiMajor, semiMinor, "+a", out errorMessage); + } + + /// + /// Resolves an optional ellipsoid definition from PROJ-style arguments, defaulting to WGS84 when none is supplied. + /// Unsupported named ellipsoids still fail the resolution. + /// + /// The PROJ argument dictionary. + /// Whether +datum should be considered in addition to +ellps. + /// Whether the clrk80ign token is supported by the caller. + /// Whether the bessel token is supported by the caller. + /// The resolved semi-major axis in metres. + /// The resolved semi-minor axis in metres. + /// when the ellipsoid was resolved or defaulted; otherwise . + internal static bool TryResolveEllipsoidOrDefault( + IReadOnlyDictionary args, + bool includeDatumToken, + bool allowClarke1880Ign, + bool allowBessel, + out double semiMajor, + out double semiMinor) + { + if (TryResolveLenientExplicitAxes(args, out semiMajor, out semiMinor)) + { + return true; + } + + if (TryResolveNamedEllipsoid(args, "ellps", allowClarke1880Ign, allowBessel, out semiMajor, out semiMinor, out bool hadEllps)) + { + return true; + } + + if (hadEllps) + { + return false; + } + + bool hadDatum = false; + if (includeDatumToken + && TryResolveNamedEllipsoid(args, "datum", allowClarke1880Ign, allowBessel, out semiMajor, out semiMinor, out hadDatum)) + { + return true; + } + + if (includeDatumToken && hadDatum) + { + return false; + } + + semiMajor = Ellipsoid.WGS84.SemiMajorAxis; + semiMinor = Ellipsoid.WGS84.SemiMinorAxis; + return true; + } + + /// + /// Resolves an optional ellipsoid definition from PROJ-style arguments, defaulting to WGS84 when none is supplied and producing operation-specific diagnostics when a supported token is malformed or unsupported. + /// + /// The PROJ argument dictionary. + /// The operation name used in skip-reason messages. + /// Whether the clrk80ign token is supported by the caller. + /// Whether the bessel token is supported by the caller. + /// The resolved semi-major axis in metres. + /// The resolved semi-minor axis in metres. + /// Receives the diagnostic message on failure. + /// when the ellipsoid was resolved or defaulted. + internal static bool TryResolveEllipsoidOrDefault( + IReadOnlyDictionary args, + string operationName, + bool allowClarke1880Ign, + bool allowBessel, + out double semiMajor, + out double semiMinor, + out string? skipReason) + { + if (!TryResolveStrictExplicitAxes(args, operationName, out semiMajor, out semiMinor, out skipReason, out bool resolvedExplicitly)) + { + return false; + } + + if (resolvedExplicitly) + { + return true; + } + + if (TryResolveNamedEllipsoid(args, "ellps", allowClarke1880Ign, allowBessel, out semiMajor, out semiMinor, out bool hadEllps)) + { + skipReason = null; + return true; + } + + if (hadEllps) + { + skipReason = $"{operationName} received unsupported +ellps value."; + return false; + } + + if (TryResolveNamedEllipsoid(args, "datum", allowClarke1880Ign, allowBessel, out semiMajor, out semiMinor, out bool hadDatum)) + { + skipReason = null; + return true; + } + + if (hadDatum) + { + skipReason = $"{operationName} received unsupported +datum value."; + return false; + } + + semiMajor = Ellipsoid.WGS84.SemiMajorAxis; + semiMinor = Ellipsoid.WGS84.SemiMinorAxis; + skipReason = null; + return true; + } + + /// + /// Resolves a required ellipsoid definition from PROJ-style arguments while honoring shape overrides used by grid-shift style operations. + /// + /// The PROJ argument dictionary. + /// The operation name used in skip-reason messages. + /// Whether the clrk80ign token is supported by the caller. + /// Whether the bessel token is supported by the caller. + /// The resolved semi-major axis in metres. + /// The resolved semi-minor axis in metres. + /// Receives the diagnostic message on failure. + /// when the ellipsoid was resolved. + internal static bool TryResolveRequiredEllipsoidWithOverrides( + IReadOnlyDictionary args, + string operationName, + bool allowClarke1880Ign, + bool allowBessel, + out double semiMajor, + out double semiMinor, + out string? skipReason) + { + semiMajor = 0d; + semiMinor = 0d; + skipReason = null; + + if (args.TryGetValue("r", out string? radiusToken) + && SpanParseUtility.TryParseFiniteDouble(radiusToken, out double radius) + && radius > 0d) + { + semiMajor = radius; + semiMinor = radius; + return true; + } + + if (TryResolveNamedEllipsoid(args, "ellps", allowClarke1880Ign, allowBessel, out semiMajor, out semiMinor, out bool hadEllps)) + { + if (!TryApplySemiMajorOverride(args, ref semiMajor, ref semiMinor, out skipReason)) + { + return false; + } + + return TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason); + } + + if (hadEllps) + { + skipReason = $"{operationName} received unsupported +ellps value."; + return false; + } + + if (TryResolveNamedEllipsoid(args, "datum", allowClarke1880Ign, allowBessel, out semiMajor, out semiMinor, out bool hadDatum)) + { + if (!TryApplySemiMajorOverride(args, ref semiMajor, ref semiMinor, out skipReason)) + { + return false; + } + + return TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason); + } + + if (hadDatum) + { + skipReason = $"{operationName} received unsupported +datum value."; + return false; + } + + if (args.TryGetValue("a", out string? majorToken) + && SpanParseUtility.TryParseFiniteDouble(majorToken, out double major) + && major > 0d) + { + semiMajor = major; + semiMinor = major; + return TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason); + } + + skipReason = $"{operationName} requires ellipsoid definition (+ellps, +datum, +r, or +a with optional +b/+rf/+f/+es)."; + return false; + } + + private static double ComputeSemiMinorAxis(double semiMajor, double inverseFlattening) + { + return (1d - (1d / inverseFlattening)) * semiMajor; + } + + private static bool TryResolveLenientExplicitAxes( + IReadOnlyDictionary args, + out double semiMajor, + out double semiMinor) + { + semiMajor = 0d; + semiMinor = 0d; + + if (args.TryGetValue("r", out string? radiusToken) + && SpanParseUtility.TryParseFiniteDouble(radiusToken, out double radius) + && radius > 0d) + { + semiMajor = radius; + semiMinor = radius; + return true; + } + + if (args.TryGetValue("a", out string? majorToken) + && SpanParseUtility.TryParseFiniteDouble(majorToken, out double major) + && major > 0d) + { + semiMajor = major; + if (args.TryGetValue("b", out string? minorToken) + && SpanParseUtility.TryParseFiniteDouble(minorToken, out double minor) + && minor > 0d) + { + semiMinor = minor; + return true; + } + + if (args.TryGetValue("rf", out string? inverseFlatteningToken) + && SpanParseUtility.TryParseFiniteDouble(inverseFlatteningToken, out double inverseFlattening) + && inverseFlattening > 0d) + { + semiMinor = ComputeSemiMinorAxis(major, inverseFlattening); + return true; + } + + semiMinor = major; + return true; + } + + return false; + } + + private static bool TryResolveStrictExplicitAxes( + IReadOnlyDictionary args, + string operationName, + out double semiMajor, + out double semiMinor, + out string? skipReason, + out bool resolved) + { + semiMajor = 0d; + semiMinor = 0d; + skipReason = null; + resolved = false; + + if (args.TryGetValue("r", out string? radiusToken) + && SpanParseUtility.TryParseFiniteDouble(radiusToken, out double radius)) + { + if (radius <= 0d) + { + skipReason = $"{operationName} +r must be positive."; + return false; + } + + semiMajor = radius; + semiMinor = radius; + resolved = true; + return true; + } + + if (args.TryGetValue("a", out string? majorToken) + && SpanParseUtility.TryParseFiniteDouble(majorToken, out double major)) + { + if (major <= 0d) + { + skipReason = $"{operationName} +a must be positive."; + return false; + } + + semiMajor = major; + if (args.TryGetValue("b", out string? minorToken) + && SpanParseUtility.TryParseFiniteDouble(minorToken, out double minor)) + { + if (minor <= 0d) + { + skipReason = $"{operationName} +b must be positive."; + return false; + } + + semiMinor = minor; + resolved = true; + return true; + } + + if (args.TryGetValue("rf", out string? inverseFlatteningToken) + && SpanParseUtility.TryParseFiniteDouble(inverseFlatteningToken, out double inverseFlattening)) + { + if (inverseFlattening <= 0d) + { + skipReason = $"{operationName} +rf must be positive."; + return false; + } + + semiMinor = ComputeSemiMinorAxis(major, inverseFlattening); + resolved = true; + return true; + } + + semiMinor = major; + resolved = true; + return true; + } + + return true; + } + + private static bool TryResolveNamedEllipsoid( + IReadOnlyDictionary args, + string key, + bool allowClarke1880Ign, + bool allowBessel, + out double semiMajor, + out double semiMinor, + out bool hadToken) + { + hadToken = TryGetNonEmptyToken(args, key, out string token); + if (!hadToken) + { + semiMajor = 0d; + semiMinor = 0d; + return false; + } + + return TryResolveKnownEllipsoid(token, allowClarke1880Ign, allowBessel, out semiMajor, out semiMinor); + } + + private static bool TryApplyExplicitShapeParameters( + IReadOnlyDictionary args, + ref double semiMajor, + ref double semiMinor, + out string? errorMessage) + { + errorMessage = null; + + if (TryGetNonEmptyToken(args, "rf", out string inverseFlatteningToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(inverseFlatteningToken, out double inverseFlattening) || inverseFlattening <= 0d) + { + errorMessage = "Ellipsoid +rf override must be finite and positive."; + return false; + } + + semiMinor = ComputeSemiMinorAxis(semiMajor, inverseFlattening); + return TryValidateResolvedAxes(semiMajor, semiMinor, "+rf", out errorMessage); + } + + if (TryGetNonEmptyToken(args, "f", out string flatteningToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(flatteningToken, out double flattening) || flattening < 0d || flattening >= 1d) + { + errorMessage = "Ellipsoid +f override must be finite and satisfy 0 <= f < 1."; + return false; + } + + semiMinor = (1d - flattening) * semiMajor; + return TryValidateResolvedAxes(semiMajor, semiMinor, "+f", out errorMessage); + } + + if (TryGetNonEmptyToken(args, "es", out string eccentricitySquaredToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(eccentricitySquaredToken, out double eccentricitySquared) || eccentricitySquared < 0d || eccentricitySquared >= 1d) + { + errorMessage = "Ellipsoid +es override must be finite and satisfy 0 <= es < 1."; + return false; + } + + semiMinor = semiMajor * Math.Sqrt(1d - eccentricitySquared); + return TryValidateResolvedAxes(semiMajor, semiMinor, "+es", out errorMessage); + } + + if (TryGetNonEmptyToken(args, "e", out string eccentricityToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(eccentricityToken, out double eccentricity) || eccentricity < 0d || eccentricity >= 1d) + { + errorMessage = "Ellipsoid +e override must be finite and satisfy 0 <= e < 1."; + return false; + } + + semiMinor = semiMajor * Math.Sqrt(1d - (eccentricity * eccentricity)); + return TryValidateResolvedAxes(semiMajor, semiMinor, "+e", out errorMessage); + } + + if (TryGetNonEmptyToken(args, "b", out string minorToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(minorToken, out double explicitMinor) || explicitMinor <= 0d) + { + errorMessage = "Ellipsoid +b override must be finite and positive."; + return false; + } + + semiMinor = explicitMinor; + return TryValidateResolvedAxes(semiMajor, semiMinor, "+b", out errorMessage); + } + + return true; + } + + private static bool TryApplySpherification( + IReadOnlyDictionary args, + ref double semiMajor, + ref double semiMinor, + out string? errorMessage) + { + errorMessage = null; + if (!TryFindSpherificationOverride(args, out string? spherificationKey, out string? spherificationValue)) + { + return true; + } + + if (!TryComputeEccentricitySquared(semiMajor, semiMinor, out double eccentricitySquared)) + { + errorMessage = $"Ellipsoid +{spherificationKey} override produced an invalid eccentricity."; + return false; + } + + switch (spherificationKey) + { + case "R_A": + semiMajor *= 1d - (eccentricitySquared * (Sixth + (eccentricitySquared * (Ra4 + (eccentricitySquared * Ra6))))); + break; + + case "R_V": + semiMajor *= 1d - (eccentricitySquared * (Sixth + (eccentricitySquared * (Rv4 + (eccentricitySquared * Rv6))))); + break; + + case "R_a": + semiMajor = (semiMajor + semiMinor) / 2d; + break; + + case "R_g": + semiMajor = Math.Sqrt(semiMajor * semiMinor); + break; + + case "R_h": + if ((semiMajor + semiMinor) == 0d) + { + errorMessage = "Ellipsoid +R_h override requires a + b to be non-zero."; + return false; + } + + semiMajor = (2d * semiMajor * semiMinor) / (semiMajor + semiMinor); + break; + + case "R_lat_a": + case "R_lat_g": + if (!TryParseSpherificationLatitudeDegrees(spherificationKey, spherificationValue, out double latitudeDegrees, out errorMessage)) + { + return false; + } + + if (!TryComputeLatitudeSpherificationRadius( + spherificationKey, + semiMajor, + eccentricitySquared, + latitudeDegrees, + out semiMajor, + out errorMessage)) + { + return false; + } + + break; + + case "R_C": + if (!TryGetConformalSphereLatitudeDegrees(args, out double conformalLatitudeDegrees, out errorMessage)) + { + return false; + } + + if (!TryComputeConformalSphereRadius( + semiMajor, + eccentricitySquared, + conformalLatitudeDegrees, + out semiMajor, + out errorMessage)) + { + return false; + } + + break; + + default: + errorMessage = $"Unsupported ellipsoid spherification override '+{spherificationKey}'."; + return false; + } + + if (!TryValidateResolvedRadius(semiMajor, $"+{spherificationKey}", out errorMessage)) + { + return false; + } + + semiMinor = semiMajor; + return true; + } + + private static bool TryFindSpherificationOverride( + IReadOnlyDictionary args, + out string? key, + out string? value) + { + key = null; + value = null; + for (int i = 0; i < SpherificationKeys.Length; i++) + { + string expectedKey = SpherificationKeys[i]; + foreach (KeyValuePair arg in args) + { + if (arg.Key.Equals(expectedKey, StringComparison.Ordinal)) + { + key = expectedKey; + value = arg.Value; + return true; + } + } + } + + return false; + } + + private static bool TryGetConformalSphereLatitudeDegrees( + IReadOnlyDictionary args, + out double latitudeDegrees, + out string? errorMessage) + { + errorMessage = null; + latitudeDegrees = 0d; + if (!TryGetNonEmptyToken(args, "lat_0", out string latitudeToken)) + { + return true; + } + + if (!TryParseAngleDegreesToken(latitudeToken, out double parsedLatitudeDegrees)) + { + errorMessage = "Ellipsoid +R_C override requires +lat_0 to be a finite angular value."; + return false; + } + + if (Math.Abs(parsedLatitudeDegrees) > 90d) + { + errorMessage = "Ellipsoid +R_C override requires +lat_0 to satisfy |lat_0| <= 90°."; + return false; + } + + if (TryGetNonEmptyToken(args, "proj", out string projectionToken) + && projectionToken.Equals("merc", StringComparison.OrdinalIgnoreCase)) + { + // PROJ validates +lat_0 for merc +R_C, but the conformal sphere radius + // still behaves like the equatorial case unless +lat_ts changes k0. + return true; + } + + latitudeDegrees = parsedLatitudeDegrees; + return true; + } + + private static bool TryParseSpherificationLatitudeDegrees( + string key, + string? token, + out double latitudeDegrees, + out string? errorMessage) + { + errorMessage = null; + latitudeDegrees = 0d; + if (!TryParseAngleDegreesToken(token ?? string.Empty, out latitudeDegrees)) + { + errorMessage = $"Ellipsoid +{key} override latitude must be a finite angular value."; + return false; + } + + if (Math.Abs(latitudeDegrees) > 90d) + { + errorMessage = $"Ellipsoid +{key} override latitude must satisfy |lat| <= 90°."; + return false; + } + + return true; + } + + private static bool TryComputeLatitudeSpherificationRadius( + string key, + double semiMajor, + double eccentricitySquared, + double latitudeDegrees, + out double radius, + out string? errorMessage) + { + errorMessage = null; + double latitudeRadians = latitudeDegrees * Math.PI / 180d; + double t = 1d - (eccentricitySquared * Math.Sin(latitudeRadians) * Math.Sin(latitudeRadians)); + if (t == 0d) + { + radius = 0d; + errorMessage = $"Ellipsoid +{key} override produced a singular radius at the specified latitude."; + return false; + } + + if (key.Equals("R_lat_a", StringComparison.Ordinal)) + { + radius = semiMajor * ((1d - eccentricitySquared + t) / (2d * t * Math.Sqrt(t))); + return true; + } + + radius = semiMajor * (Math.Sqrt(1d - eccentricitySquared) / t); + return true; + } + + private static bool TryComputeConformalSphereRadius( + double semiMajor, + double eccentricitySquared, + double latitudeDegrees, + out double radius, + out string? errorMessage) + { + errorMessage = null; + double latitudeRadians = latitudeDegrees * Math.PI / 180d; + double t = 1d - (eccentricitySquared * Math.Sin(latitudeRadians) * Math.Sin(latitudeRadians)); + if (t == 0d) + { + radius = 0d; + errorMessage = "Ellipsoid +R_C override produced a singular radius at the specified latitude."; + return false; + } + + radius = semiMajor * (Math.Sqrt(1d - eccentricitySquared) / t); + return true; + } + + private static bool TryValidateResolvedAxes(double semiMajor, double semiMinor, string parameterName, out string? errorMessage) + { + if (!TryValidateResolvedRadius(semiMajor, parameterName, out errorMessage)) + { + return false; + } + + if (!TryValidateResolvedRadius(semiMinor, parameterName, out errorMessage)) + { + errorMessage = $"Ellipsoid {parameterName} override must resolve to a finite positive semi-minor axis."; + return false; + } + + if (!TryComputeEccentricitySquared(semiMajor, semiMinor, out _)) + { + errorMessage = $"Ellipsoid {parameterName} override produced an invalid eccentricity."; + return false; + } + + return true; + } + + private static bool TryValidateResolvedRadius(double radius, string parameterName, out string? errorMessage) + { + errorMessage = null; + if (radius <= 0d || double.IsNaN(radius) || double.IsInfinity(radius)) + { + errorMessage = $"Ellipsoid {parameterName} override must resolve to a finite positive radius."; + return false; + } + + return true; + } + + private static bool TryComputeEccentricitySquared(double semiMajor, double semiMinor, out double eccentricitySquared) + { + eccentricitySquared = double.NaN; + if (semiMajor <= 0d || semiMinor <= 0d || double.IsNaN(semiMajor) || double.IsNaN(semiMinor) || double.IsInfinity(semiMajor) || double.IsInfinity(semiMinor)) + { + return false; + } + + double axisRatio = semiMinor / semiMajor; + eccentricitySquared = 1d - (axisRatio * axisRatio); + return !double.IsNaN(eccentricitySquared) + && !double.IsInfinity(eccentricitySquared) + && eccentricitySquared >= 0d; + } + + private static bool TryGetNonEmptyToken(IReadOnlyDictionary args, string key, out string token) + { + if (args.TryGetValue(key, out string? rawToken) && !string.IsNullOrWhiteSpace(rawToken)) + { + token = rawToken; + return true; + } + + token = string.Empty; + return false; + } + + private static bool TryParseAngleDegreesToken(string token, out double value) + { + value = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + bool radiansSuffix = normalized.Length > 0 && (normalized[^1] == 'r' || normalized[^1] == 'R'); + if (radiansSuffix) + { + normalized = normalized[..^1]; + } + + if (TryParseNumericToken(normalized, out value)) + { + if (radiansSuffix) + { + value *= 180d / Math.PI; + } + + return true; + } + + return TryParseDmsToken(normalized, out value); + } + + private static bool TryParseNumericToken(string token, out double value) + { + value = 0d; + if (!SpanParseUtility.TryParseFiniteDouble(token, out value)) + { + int slashIndex = IndexOfOrdinal(token, '/'); + if (slashIndex <= 0 || slashIndex >= token.Length - 1) + { + return false; + } + + string numeratorToken = token[..slashIndex].Trim(); + string denominatorToken = token[(slashIndex + 1)..].Trim(); + if (!SpanParseUtility.TryParseFiniteDouble(numeratorToken, out double numerator) + || !SpanParseUtility.TryParseFiniteDouble(denominatorToken, out double denominator) + || denominator == 0d) + { + return false; + } + + value = numerator / denominator; + } + + return !double.IsNaN(value) && !double.IsInfinity(value); + } + + private static bool TryParseDmsToken(string token, out double value) + { + value = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string text = token.Trim() + .Replace('°', 'd') + .Replace('º', 'd'); + int sign = 1; + + char last = text[text.Length - 1]; + if (last == 'W' || last == 'w' || last == 'S' || last == 's') + { + sign = -1; + text = text[..^1]; + } + else if (last == 'E' || last == 'e' || last == 'N' || last == 'n') + { + text = text[..^1]; + } + + if (text.Length > 0 && text[0] == '-') + { + sign *= -1; + text = text[1..]; + } + else if (text.Length > 0 && text[0] == '+') + { + text = text[1..]; + } + + int dIndex = IndexOfOrdinal(text, 'd'); + if (dIndex < 0) + { + dIndex = IndexOfOrdinal(text, 'D'); + } + + int mIndex = IndexOfOrdinal(text, '\''); + if (dIndex <= 0 || mIndex <= dIndex) + { + return false; + } + + string degreesToken = text[..dIndex]; + string minutesToken = text.Substring(dIndex + 1, mIndex - dIndex - 1); + if (!SpanParseUtility.TryParseFiniteDouble(degreesToken, out double degrees) + || !SpanParseUtility.TryParseFiniteDouble(minutesToken, out double minutes)) + { + return false; + } + + double seconds = 0d; + int secondsMarker = IndexOfOrdinal(text, '"'); + if (secondsMarker > mIndex + 1) + { + string secondsToken = text.Substring(mIndex + 1, secondsMarker - mIndex - 1); + if (!SpanParseUtility.TryParseFiniteDouble(secondsToken, out seconds)) + { + return false; + } + } + + value = sign * (degrees + (minutes / 60d) + (seconds / 3600d)); + return true; + } + + private static int IndexOfOrdinal(string text, char value) + { +#if NETSTANDARD2_0 + return text.IndexOf(value); +#else + return text.IndexOf(value, StringComparison.Ordinal); +#endif + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.GridResolution.cs b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.GridResolution.cs new file mode 100644 index 00000000..c3d0bff6 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.GridResolution.cs @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Reflection; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; + +/// +/// Creates runtime math transforms from PROJ-style pipeline operation strings. +/// +internal static partial class ProjPipelineMathTransformFactory +{ + private static bool TryResolveGeoTiffHorizontalInterpolationOverride( + Dictionary args, + bool useGridMetadataInterpolation, + bool allowBiquadraticInterpolation, + out bool? biquadraticInterpolationOverride, + out string? skipReason) + { + biquadraticInterpolationOverride = useGridMetadataInterpolation ? null : false; + if (!args.TryGetValue("interpolation", out string? interpolationToken) || string.IsNullOrWhiteSpace(interpolationToken)) + { + skipReason = null; + return true; + } + + if (interpolationToken.Equals("bilinear", StringComparison.OrdinalIgnoreCase)) + { + biquadraticInterpolationOverride = false; + skipReason = null; + return true; + } + + if (interpolationToken.Equals("biquadratic", StringComparison.OrdinalIgnoreCase)) + { + if (!allowBiquadraticInterpolation) + { + skipReason = "The hgridshift operation only supports bilinear interpolation for GeoTIFF grids."; + return false; + } + + biquadraticInterpolationOverride = true; + skipReason = null; + return true; + } + + skipReason = "Horizontal GeoTIFF grid shift interpolation must be bilinear or biquadratic."; + return false; + } + + private static bool TryValidateNtv2HorizontalInterpolation( + Dictionary args, + bool allowBiquadraticInterpolation, + out string? skipReason) + { + if (!args.TryGetValue("interpolation", out string? interpolationToken) || string.IsNullOrWhiteSpace(interpolationToken)) + { + skipReason = null; + return true; + } + + if (interpolationToken.Equals("bilinear", StringComparison.OrdinalIgnoreCase)) + { + skipReason = null; + return true; + } + + if (interpolationToken.Equals("biquadratic", StringComparison.OrdinalIgnoreCase)) + { + skipReason = allowBiquadraticInterpolation + ? "Biquadratic interpolation is only supported for GeoTIFF gridshift inputs." + : "The hgridshift operation only supports bilinear interpolation for NTv2 grids."; + return false; + } + + skipReason = "Horizontal grid shift interpolation must be bilinear or biquadratic."; + return false; + } + + private static bool TryValidateGridExtensions( + IReadOnlyList gridPaths, + IReadOnlyList allowedExtensions, + string gridFamilyName, + out string? skipReason) + { + skipReason = null; + string allowedList = string.Join("/", allowedExtensions); + for (int i = 0; i < gridPaths.Count; i++) + { + string path = gridPaths[i]; + if (!IsPathWithAnyExtension(path, allowedExtensions)) + { + skipReason = $"Grid '{Path.GetFileName(path)}' is not a supported {gridFamilyName} grid format ({allowedList})."; + return false; + } + } + + return true; + } + + private static bool ContainsGeoTiffGrid(IReadOnlyList gridPaths) + { + for (int i = 0; i < gridPaths.Count; i++) + { + if (IsGeoTiffPath(gridPaths[i])) + { + return true; + } + } + + return false; + } + + private static bool IsGeoTiffPath(string path) + { + return IsPathWithAnyExtension(path, XyzGridExtensions); + } + + private static bool IsPathWithAnyExtension(string path, IReadOnlyList extensions) + { + string extension = Path.GetExtension(path); + for (int i = 0; i < extensions.Count; i++) + { + if (extension.Equals(extensions[i], StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static bool TryResolveGridPaths( + string gridsToken, + [NotNullWhen(true)] out IReadOnlyList? resolvedPaths, + out string? skipReason) + { + resolvedPaths = []; + skipReason = null; + + string[] entries = gridsToken.Split(CommaSeparator, StringSplitOptions.RemoveEmptyEntries); + if (entries.Length == 0) + { + skipReason = "Horizontal grid shift requires at least one grid name in +grids."; + return false; + } + + var resolved = new List(entries.Length); + for (int i = 0; i < entries.Length; i++) + { + string token = entries[i].Trim(); + if (string.IsNullOrWhiteSpace(token)) + { + continue; + } + + bool isOptional = token.Length > 0 && token[0] == '@'; + string gridName = isOptional ? token[1..] : token; + if (string.IsNullOrWhiteSpace(gridName)) + { + continue; + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(gridName, out string? resolvedPathCandidate)) + { + resolved.Add(ArgumentGuard.ThrowIfNull(resolvedPathCandidate, nameof(resolvedPathCandidate))); + continue; + } + + if (!isOptional) + { + skipReason = $"Required grid '{gridName}' was not found."; + return false; + } + } + + if (resolved.Count == 0) + { + skipReason = "No grid from +grids could be resolved."; + return false; + } + + resolvedPaths = resolved; + return true; + } + + private static bool TryParseAxisSwapOrder(string orderToken, out int[] order) + { + order = []; + if (string.IsNullOrWhiteSpace(orderToken)) + { + return false; + } + + string[] segments = orderToken.Split(CommaSeparator, StringSplitOptions.RemoveEmptyEntries); + if (segments.Length < 2 || segments.Length > 4) + { + return false; + } + + int[] parsed = new int[segments.Length]; + var seen = new HashSet(); + for (int i = 0; i < segments.Length; i++) + { + if (!int.TryParse(segments[i].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out int value)) + { + return false; + } + + int axis = Math.Abs(value); + if (axis < 1 || axis > 4 || !seen.Add(axis)) + { + return false; + } + + parsed[i] = value; + } + + order = parsed; + return true; + } + + private static bool TryParseAxisOrder(string axisToken, out int[] order) + { + order = []; + if (string.IsNullOrWhiteSpace(axisToken)) + { + return false; + } + + string axis = axisToken.Trim(); + if (axis.Length < 2 || axis.Length > 4) + { + return false; + } + + int[] parsed = new int[axis.Length]; + var seen = new HashSet(); + for (int i = 0; i < axis.Length; i++) + { + char c = char.ToLowerInvariant(axis[i]); + int mapped = c switch + { + 'e' => 1, + 'w' => -1, + 'n' => 2, + 's' => -2, + 'u' => 3, + 'd' => -3, + _ => 0, + }; + + if (mapped == 0) + { + return false; + } + + int absMapped = Math.Abs(mapped); + if (!seen.Add(absMapped)) + { + return false; + } + + parsed[i] = mapped; + } + + order = parsed; + return true; + } + + private static bool TrySplitPipelineSteps(string operation, out IReadOnlyList steps) + { + var parsedSteps = new List(); + var currentStepTokens = new List(); + bool inPipeline = false; + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string token in tokens) + { + string normalized = token.Length > 0 && token[0] == '+' + ? token[1..] + : token; + + if (normalized.Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + inPipeline = true; + continue; + } + + if (normalized.Equals("step", StringComparison.OrdinalIgnoreCase)) + { + inPipeline = true; + if (currentStepTokens.Count > 0) + { + parsedSteps.Add(string.Join(" ", currentStepTokens)); + currentStepTokens.Clear(); + } + + continue; + } + + if (!inPipeline) + { + continue; + } + + currentStepTokens.Add(token); + } + + if (currentStepTokens.Count > 0) + { + parsedSteps.Add(string.Join(" ", currentStepTokens)); + } + + steps = parsedSteps; + return parsedSteps.Count > 0; + } + + private static bool ContainsPipelineProjection(string operation) + { + if (operation is null) + { + return false; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < tokens.Length; i++) + { + string token = tokens[i]; + if (token.Length == 0 || token[0] != '+') + { + continue; + } + + string body = token[1..]; + if (body.Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static bool ContainsNestedPipelineProjection(string operation) + { + if (operation is null) + { + return false; + } + + int pipelineProjectionCount = 0; + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string token in tokens) + { + if (token.Length > 0 + && token[0] == '+' + && token[1..].Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + pipelineProjectionCount++; + if (pipelineProjectionCount > 1) + { + return true; + } + } + } + + return false; + } + + private static bool TryParsePipelineStepArguments( + string operation, + [NotNullWhen(true)] out IReadOnlyList>? steps, + out bool invertPipeline, + out string? skipReason) + { + steps = null; + invertPipeline = false; + skipReason = null; + + if (operation is null) + { + skipReason = "Operation string was null."; + return false; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + var globalArgs = new Dictionary(StringComparer.OrdinalIgnoreCase); + var currentStepArgs = new Dictionary(StringComparer.OrdinalIgnoreCase); + var parsedSteps = new List>(); + bool insidePipelineDefinition = false; + bool insideStepSection = false; + bool previousTokenWasStep = false; + + for (int i = 0; i < tokens.Length; i++) + { + string token = tokens[i]; + if (token.Length == 0 || token[0] != '+') + { + continue; + } + + string body = token[1..]; +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int separatorIndex = body.IndexOf('=', StringComparison.Ordinal); +#else + int separatorIndex = body.IndexOf('='); +#endif + string key = separatorIndex < 0 + ? body + : body[..separatorIndex]; + string value = separatorIndex < 0 + ? "true" + : body[(separatorIndex + 1)..]; + if (key.Equals("proj", StringComparison.OrdinalIgnoreCase) + && value.Equals("pipeline", StringComparison.OrdinalIgnoreCase)) + { + insidePipelineDefinition = true; + continue; + } + + if (!insidePipelineDefinition) + { + continue; + } + + if (key.Equals("step", StringComparison.OrdinalIgnoreCase)) + { + if (!insideStepSection) + { + insideStepSection = true; + } + else if (currentStepArgs.Count == 0) + { + skipReason = "Pipeline contains an empty +step."; + return false; + } + else + { + parsedSteps.Add(BuildPipelineStepArguments(globalArgs, currentStepArgs)); + currentStepArgs.Clear(); + } + + previousTokenWasStep = true; + continue; + } + + previousTokenWasStep = false; + if (!insideStepSection) + { + globalArgs[key] = value; + continue; + } + + currentStepArgs[key] = value; + } + + if (!insidePipelineDefinition) + { + skipReason = "Operation is missing +proj=pipeline."; + return false; + } + + if (!insideStepSection) + { + skipReason = "Pipeline operation did not contain any +step definition."; + return false; + } + + invertPipeline = globalArgs.Remove("inv"); + if (currentStepArgs.Count > 0) + { + parsedSteps.Add(BuildPipelineStepArguments(globalArgs, currentStepArgs)); + } + else if (previousTokenWasStep) + { + skipReason = "Pipeline operation ended with +step but no step parameters."; + return false; + } + + if (parsedSteps.Count == 0) + { + skipReason = "Pipeline operation did not contain any executable step."; + return false; + } + + if (invertPipeline) + { + parsedSteps = BuildInvertedPipelineSteps(parsedSteps); + } + + steps = parsedSteps; + return true; + } + + private static bool TryParseNestedPipelineStepOperations( + string operation, + [NotNullWhen(true)] out IReadOnlyList? steps, + out string? skipReason) + { + steps = null; + skipReason = null; + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + var globalTokens = new List(); + var parsedSteps = new List(); + List? currentStepTokens = null; + bool insidePipelineDefinition = false; + bool insideStepSection = false; + bool previousTokenWasStep = false; + + for (int i = 0; i < tokens.Length; i++) + { + string token = tokens[i]; + if (token.Length == 0 || token[0] != '+') + { + if (insideStepSection && currentStepTokens is not null) + { + currentStepTokens.Add(token); + } + + continue; + } + + string body = token[1..]; + if (!insidePipelineDefinition) + { + if (body.Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + insidePipelineDefinition = true; + } + + continue; + } + + if (body.Equals("step", StringComparison.OrdinalIgnoreCase)) + { + if (!insideStepSection) + { + insideStepSection = true; + currentStepTokens = []; + } + else if (currentStepTokens is null || currentStepTokens.Count == 0) + { + skipReason = "Pipeline contains an empty +step."; + return false; + } + else + { + parsedSteps.Add(BuildPipelineStepOperation(globalTokens, currentStepTokens)); + currentStepTokens = []; + } + + previousTokenWasStep = true; + continue; + } + + if (!insideStepSection) + { + globalTokens.Add(token); + continue; + } + + currentStepTokens ??= []; + currentStepTokens.Add(token); + previousTokenWasStep = false; + + if (body.Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + for (int j = i + 1; j < tokens.Length; j++) + { + currentStepTokens.Add(tokens[j]); + } + + i = tokens.Length; + break; + } + } + + if (!insidePipelineDefinition) + { + skipReason = "Operation is missing +proj=pipeline."; + return false; + } + + if (!insideStepSection) + { + skipReason = "Pipeline operation did not contain any +step definition."; + return false; + } + + bool invertPipeline = RemoveGlobalInvToken(globalTokens); + if (currentStepTokens is not null && currentStepTokens.Count > 0) + { + parsedSteps.Add(BuildPipelineStepOperation(globalTokens, currentStepTokens)); + } + else if (previousTokenWasStep) + { + skipReason = "Pipeline operation ended with +step but no step parameters."; + return false; + } + + if (parsedSteps.Count == 0) + { + skipReason = "Pipeline operation did not contain any executable step."; + return false; + } + + if (invertPipeline) + { + parsedSteps = BuildInvertedPipelineStepOperations(parsedSteps); + } + + steps = parsedSteps; + return true; + } + + private static List> BuildInvertedPipelineSteps(List> parsedSteps) + { + var invertedSteps = new List>(parsedSteps.Count); + for (int i = parsedSteps.Count - 1; i >= 0; i--) + { + var invertedStep = new Dictionary(parsedSteps[i], StringComparer.OrdinalIgnoreCase); + if (!invertedStep.Remove("inv")) + { + invertedStep["inv"] = "true"; + } + + invertedSteps.Add(invertedStep); + } + + return invertedSteps; + } + + private static List BuildInvertedPipelineStepOperations(List parsedSteps) + { + var invertedSteps = new List(parsedSteps.Count); + for (int i = parsedSteps.Count - 1; i >= 0; i--) + { + string step = parsedSteps[i]; +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (step.Contains("+inv", StringComparison.OrdinalIgnoreCase)) + { + invertedSteps.Add(step.Replace("+inv", string.Empty, StringComparison.OrdinalIgnoreCase).Replace(" ", " ", StringComparison.Ordinal).Trim()); + } +#else + if (step.IndexOf("+inv", StringComparison.OrdinalIgnoreCase) >= 0) + { + invertedSteps.Add(step.Replace("+inv", string.Empty).Replace(" ", " ").Trim()); + } +#endif + else + { + invertedSteps.Add($"+inv {step}"); + } + } + + return invertedSteps; + } + + private static Dictionary BuildPipelineStepArguments( + Dictionary globalArgs, + Dictionary stepArgs) + { + var merged = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (KeyValuePair globalArg in globalArgs) + { + if (globalArg.Key.Equals("inv", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + merged[globalArg.Key] = globalArg.Value; + } + + foreach (KeyValuePair step in stepArgs) + { + merged[step.Key] = step.Value; + } + + merged.Remove("step"); + if (merged.TryGetValue("proj", out string? mergedProjCode) + && mergedProjCode.Equals("pipeline", StringComparison.OrdinalIgnoreCase)) + { + merged.Remove("proj"); + } + + return merged; + } + + private static string BuildPipelineStepOperation(List globalTokens, List stepTokens) + { + string globalPrefix = string.Join(" ", globalTokens); + string stepOperation = string.Join(" ", stepTokens); + if (stepTokens.Count > 0 + && stepTokens[0].Length > 1 + && stepTokens[0][0] == '+' + && stepTokens[0][1..].Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + if (string.IsNullOrEmpty(globalPrefix)) + { + return stepOperation; + } + + string nestedStepRemainder = stepTokens.Count > 1 + ? string.Join(" ", stepTokens.GetRange(1, stepTokens.Count - 1)) + : string.Empty; + return string.IsNullOrEmpty(nestedStepRemainder) + ? $"{stepTokens[0]} {globalPrefix}" + : $"{stepTokens[0]} {globalPrefix} {nestedStepRemainder}"; + } + + return string.IsNullOrEmpty(globalPrefix) + ? stepOperation + : $"{globalPrefix} {stepOperation}"; + } + + private static bool RemoveGlobalInvToken(List globalTokens) + { + for (int i = globalTokens.Count - 1; i >= 0; i--) + { + if (globalTokens[i].Equals("+inv", StringComparison.OrdinalIgnoreCase)) + { + globalTokens.RemoveAt(i); + return true; + } + } + + return false; + } + + private static bool TryParseOperationArguments(string operation, out Dictionary args) + { + args = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (operation is null) + { + return false; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string token in tokens) + { + if (token.Length == 0 || token[0] != '+') + { + continue; + } + + string body = token[1..]; +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int index = body.IndexOf('=', StringComparison.Ordinal); +#else + int index = body.IndexOf('='); +#endif + if (index < 0) + { + args[body] = body; + } + else + { + string key = body[..index]; + string value = body[(index + 1)..]; + args[key] = value; + } + } + + return args.Count > 0; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.ProjectionParameters.cs b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.ProjectionParameters.cs new file mode 100644 index 00000000..f9385763 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.ProjectionParameters.cs @@ -0,0 +1,1469 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Reflection; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; + +/// +/// Creates runtime math transforms from PROJ-style pipeline operation strings. +/// +internal static partial class ProjPipelineMathTransformFactory +{ + private static bool TryBuildProjectionStepParameters( + Dictionary args, + string projCode, + [NotNullWhen(true)] out List? parameters, + out string? skipReason) + { + parameters = null; + if (!TryResolveProjectionEllipsoid(args, out double semiMajor, out double semiMinor, out skipReason)) + { + return false; + } + + if (!TryResolveProjectionUnitFactor(args, out double unitFactor, out skipReason)) + { + return false; + } + + List projectionParameters = CreateDefaultProjectionStepParameters(semiMajor, semiMinor, unitFactor); + if (!TryApplyDefaultProjectionStepParameters(args, projectionParameters, out skipReason) + || !TryApplyProjectionScaleFactor(args, projectionParameters, out skipReason)) + { + return false; + } + + if (args.ContainsKey("south")) + { + SetOrAddProjectionParameter(projectionParameters, "south", 1d); + } + + if (!TryApplyProjectionSpecificParameters(args, projCode, unitFactor, projectionParameters, out skipReason)) + { + return false; + } + + parameters = projectionParameters; + return true; + } + + private static List CreateDefaultProjectionStepParameters(double semiMajor, double semiMinor, double unitFactor) + { + return + [ + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + new("semi_major", semiMajor), + new("semi_minor", semiMinor), + new("unit", unitFactor), + ]; + } + + private static bool TryApplyDefaultProjectionStepParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + if (!TryApplyOptionalProjectionParameter(args, "lat_0", "latitude_of_origin", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "lon_0", "central_meridian", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "x_0", "false_easting", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "y_0", "false_northing", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "lat_1", "standard_parallel_1", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "lat_2", "standard_parallel_2", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "lonc", "longitude_of_center", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "lat_ts", "lat_ts", parameters, out skipReason)) + { + return false; + } + + return TryApplyPrimeMeridianOffset(args, parameters, out skipReason); + } + + private static bool TryApplyProjectionScaleFactor( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (args.TryGetValue("k_0", out string? k0Token) && !string.IsNullOrWhiteSpace(k0Token)) + { + if (!SpanParseUtility.TryParseFiniteDouble(k0Token, out double k0)) + { + skipReason = "Invalid value for +k_0."; + return false; + } + + SetOrAddProjectionParameter(parameters, "scale_factor", k0); + return true; + } + + if (args.TryGetValue("k", out string? kToken) && !string.IsNullOrWhiteSpace(kToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(kToken, out double k)) + { + skipReason = "Invalid value for +k."; + return false; + } + + SetOrAddProjectionParameter(parameters, "scale_factor", k); + } + + return true; + } + + private static bool TryApplyProjectionSpecificParameters( + Dictionary args, + string projCode, + double unitFactor, + List parameters, + out string? skipReason) + { + switch (projCode.ToUpperInvariant()) + { + case "CASS": + skipReason = null; + return TryApplyCassProjectionParameters(args, parameters, out skipReason); + case "AEQD": + skipReason = null; + return TryApplyAeqdProjectionParameters(args, parameters, out skipReason); + case "AIROCEAN": + return TryApplyAiroceanProjectionParameters(args, parameters, out skipReason); + case "ISEA": + return TryApplyIseaProjectionParameters(args, parameters, out skipReason); + case "LAGRNG": + return TryApplyLagrangeProjectionParameters(args, parameters, out skipReason); + case "PEIRCE_Q": + return TryApplyPeirceProjectionParameters(args, parameters, out skipReason); + case "HEALPIX": + return TryApplyHealpixProjectionParameters(args, parameters, out skipReason); + case "KROVAK": + case "MOD_KROVAK": + return TryApplyKrovakProjectionParameters(args, parameters, out skipReason); + case "NZMG": + return TryApplyNzmgProjectionParameters(args, parameters, out skipReason); + case "OMERC": + return TryApplyObliqueMercatorProjectionParameters(args, parameters, out skipReason); + case "OCEA": + return TryApplyObliqueCylindricalEqualAreaProjectionParameters(args, parameters, out skipReason); + case "ORTHO": + return TryApplyOrthographicProjectionParameters(args, parameters, out skipReason); + case "SPILHAUS": + return TryApplySpilhausProjectionParameters(args, parameters, out skipReason); + case "AIRY": + return TryApplyAiryProjectionParameters(args, parameters, out skipReason); + case "RHEALPIX": + return TryApplyRhealpixProjectionParameters(args, parameters, out skipReason); + case "S2": + return TryApplyS2ProjectionParameters(args, parameters, out skipReason); + case "TPEQD": + return TryApplyTwoPointEquidistantProjectionParameters(args, parameters, out skipReason); + case "URM5": + return TryApplyUrm5ProjectionParameters(args, parameters, out skipReason); + case "UTM": + return TryApplyUtmProjectionParameters(args, unitFactor, parameters, out skipReason); + case "VANDG": + return TryApplyVanDerGrintenProjectionParameters(args, parameters, out skipReason); + default: + skipReason = null; + return true; + } + } + + private static bool TryApplyCassProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + if (args.ContainsKey("hyperbolic")) + { + SetOrAddProjectionParameter(parameters, "hyperbolic", 1d); + } + + skipReason = null; + return true; + } + + private static bool TryApplyAeqdProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + if (args.ContainsKey("guam")) + { + SetOrAddProjectionParameter(parameters, "guam", 1d); + } + + skipReason = null; + return true; + } + + private static bool TryApplyAiroceanProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (!args.TryGetValue("orient", out string? orientationToken) || string.IsNullOrWhiteSpace(orientationToken)) + { + return true; + } + + if (!TryResolveAiroceanOrientationCode(orientationToken, out double orientationCode)) + { + skipReason = "Invalid value for +orient on airocean step."; + return false; + } + + SetOrAddProjectionParameter(parameters, "airocean_orient", orientationCode); + return true; + } + + private static bool TryApplyIseaProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (args.TryGetValue("orient", out string? orientationToken) && !string.IsNullOrWhiteSpace(orientationToken)) + { + if (!TryResolveIseaOrientationCode(orientationToken, out double orientationCode)) + { + skipReason = "Invalid value for +orient on isea step."; + return false; + } + + SetOrAddProjectionParameter(parameters, "isea_orient", orientationCode); + } + + if (args.TryGetValue("mode", out string? modeToken) && !string.IsNullOrWhiteSpace(modeToken)) + { + if (!TryResolveIseaModeCode(modeToken, out double modeCode)) + { + skipReason = "Invalid value for +mode on isea step."; + return false; + } + + SetOrAddProjectionParameter(parameters, "isea_mode", modeCode); + } + + return TryApplyOptionalProjectionParameter(args, "azi", "isea_azimuth", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "aperture", "isea_aperture", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "resolution", "isea_resolution", parameters, out skipReason); + } + + private static bool TryApplyLagrangeProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + return TryApplyOptionalProjectionParameter(args, "lat_1", "lat_1", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "W", "W", parameters, out skipReason); + } + + private static bool TryApplyPeirceProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (args.TryGetValue("shape", out string? shapeToken) && !string.IsNullOrWhiteSpace(shapeToken)) + { + if (!TryResolvePeirceShapeCode(shapeToken, out double shapeCode)) + { + skipReason = "Invalid value for +shape on peirce_q step."; + return false; + } + + SetOrAddProjectionParameter(parameters, "shape", shapeCode); + } + + return TryApplyOptionalProjectionParameter(args, "scrollx", "scrollx", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "scrolly", "scrolly", parameters, out skipReason); + } + + private static bool TryApplyHealpixProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + return TryApplyOptionalProjectionParameter(args, "rot_xy", "rot_xy", parameters, out skipReason); + } + + private static bool TryApplyRhealpixProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + SetOrAddProjectionParameter(parameters, "rhealpix_mode", 1d); + if (!TryResolveIndexedQuadrant(args, "north_square", out double northSquare, out skipReason) + || !TryResolveIndexedQuadrant(args, "south_square", out double southSquare, out skipReason)) + { + return false; + } + + SetOrAddProjectionParameter(parameters, "north_square", northSquare); + SetOrAddProjectionParameter(parameters, "south_square", southSquare); + return true; + } + + private static bool TryApplyS2ProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (!args.TryGetValue("uv_to_st", out string? token) || string.IsNullOrWhiteSpace(token)) + { + args.TryGetValue("uvtost", out token); + } + + if (string.IsNullOrWhiteSpace(token)) + { + return true; + } + + if (!TryResolveS2ProjectionTypeCode(token, out double modeCode)) + { + skipReason = "Invalid value for +UVtoST on s2 step."; + return false; + } + + SetOrAddProjectionParameter(parameters, "uv_to_st", modeCode); + return true; + } + + private static bool TryApplyKrovakProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (!args.ContainsKey("lat_0")) + { + SetOrAddProjectionParameter(parameters, "latitude_of_origin", 49.5d); + } + + if (!args.ContainsKey("lon_0")) + { + SetOrAddProjectionParameter(parameters, "central_meridian", 24.8333333333333d); + } + + if (!args.ContainsKey("k") && !args.ContainsKey("k_0")) + { + SetOrAddProjectionParameter(parameters, "scale_factor", 0.9999d); + } + + SetOrAddProjectionParameter(parameters, "semi_major", Ellipsoid.Bessel1841.SemiMajorAxis); + SetOrAddProjectionParameter(parameters, "semi_minor", Ellipsoid.Bessel1841.SemiMinorAxis); + + if (args.TryGetValue("lat_1", out string? pseudoStandardParallelToken) && !string.IsNullOrWhiteSpace(pseudoStandardParallelToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(pseudoStandardParallelToken, out double pseudoStandardParallel)) + { + skipReason = "Invalid value for +lat_1 on krovak step."; + return false; + } + + SetOrAddProjectionParameter(parameters, "pseudo_standard_parallel_1", pseudoStandardParallel); + } + else + { + SetOrAddProjectionParameter(parameters, "pseudo_standard_parallel_1", 78.5d); + } + + if (!TryApplyOptionalProjectionParameter(args, "alpha", "azimuth", parameters, out skipReason)) + { + return false; + } + + if (args.ContainsKey("czech")) + { + SetOrAddProjectionParameter(parameters, "czech", 1d); + } + + return true; + } + + private static bool TryApplyNzmgProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (!args.ContainsKey("lat_0")) + { + SetOrAddProjectionParameter(parameters, "latitude_of_origin", -41d); + } + + if (!args.ContainsKey("lon_0")) + { + SetOrAddProjectionParameter(parameters, "central_meridian", 173d); + } + + if (!args.ContainsKey("x_0")) + { + SetOrAddProjectionParameter(parameters, "false_easting", 2510000d); + } + + if (!args.ContainsKey("y_0")) + { + SetOrAddProjectionParameter(parameters, "false_northing", 6023150d); + } + + return true; + } + + private static bool TryApplyOrthographicProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + return TryApplyOptionalProjectionParameter(args, "alpha", "alpha", parameters, out skipReason); + } + + private static bool TryApplySpilhausProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (!args.ContainsKey("lat_0")) + { + SetOrAddProjectionParameter(parameters, "latitude_of_origin", -49.56371678d); + } + + if (!args.ContainsKey("lon_0")) + { + SetOrAddProjectionParameter(parameters, "central_meridian", 66.94970198d); + } + + return TryApplyOptionalProjectionParameter(args, "azi", "azi", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "rot", "rot", parameters, out skipReason); + } + + private static bool TryApplyVanDerGrintenProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (args.ContainsKey("over")) + { + SetOrAddProjectionParameter(parameters, "over", 1d); + } + + return true; + } + + private static bool TryApplyObliqueMercatorProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + if (!TryApplyOptionalProjectionParameter(args, "alpha", "alpha", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "gamma", "gamma", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "lon_1", "lon_1", parameters, out skipReason) + || !TryApplyOptionalProjectionParameter(args, "lon_2", "lon_2", parameters, out skipReason)) + { + return false; + } + + if (args.ContainsKey("no_rot")) + { + SetOrAddProjectionParameter(parameters, "no_rot", 1d); + } + + skipReason = null; + return true; + } + + private static bool TryApplyObliqueCylindricalEqualAreaProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + return TryApplyOptionalProjectionParameter(args, "alpha", "alpha", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "lon_1", "lon_1", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "lon_2", "lon_2", parameters, out skipReason); + } + + private static bool TryApplyAiryProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + if (!TryApplyOptionalProjectionParameter(args, "lat_b", "lat_b", parameters, out skipReason)) + { + return false; + } + + if (args.ContainsKey("no_cut")) + { + SetOrAddProjectionParameter(parameters, "no_cut", 1d); + } + + return true; + } + + private static bool TryApplyUrm5ProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + if (!args.TryGetValue("n", out string? nToken) || string.IsNullOrWhiteSpace(nToken)) + { + skipReason = "urm5 step requires +n parameter."; + return false; + } + + if (!SpanParseUtility.TryParseFiniteDouble(nToken, out double n)) + { + skipReason = "Invalid value for +n."; + return false; + } + + SetOrAddProjectionParameter(parameters, "n", n); + return TryApplyOptionalProjectionParameter(args, "q", "q", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "alpha", "alpha", parameters, out skipReason); + } + + private static bool TryApplyTwoPointEquidistantProjectionParameters( + Dictionary args, + List parameters, + out string? skipReason) + { + return TryApplyOptionalProjectionParameter(args, "lon_1", "lon_1", parameters, out skipReason) + && TryApplyOptionalProjectionParameter(args, "lon_2", "lon_2", parameters, out skipReason); + } + + private static bool TryApplyUtmProjectionParameters( + Dictionary args, + double unitFactor, + List parameters, + out string? skipReason) + { + if (!TryGetZoneCentralMeridian(args, out double centralMeridian)) + { + skipReason = "utm step requires a valid +zone parameter."; + return false; + } + + SetOrAddProjectionParameter(parameters, "latitude_of_origin", 0d); + SetOrAddProjectionParameter(parameters, "central_meridian", centralMeridian); + SetOrAddProjectionParameter(parameters, "scale_factor", 0.9996d); + SetOrAddProjectionParameter(parameters, "false_easting", 500000d / unitFactor); + SetOrAddProjectionParameter(parameters, "false_northing", (args.ContainsKey("south") ? 10000000d : 0d) / unitFactor); + + skipReason = null; + return true; + } + + private static bool TryResolvePeirceShapeCode(string token, out double shapeCode) + { + shapeCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (SpanParseUtility.TryParseFiniteDouble(normalized, out shapeCode)) + { + return true; + } + + shapeCode = normalized.ToUpperInvariant() switch + { + "SQUARE" => 0d, + "DIAMOND" => 1d, + "NHEMISPHERE" => 2d, + "SHEMISPHERE" => 3d, + "HORIZONTAL" => 4d, + "VERTICAL" => 5d, + _ => double.NaN, + }; + + return !double.IsNaN(shapeCode); + } + + private static bool TryResolveIndexedQuadrant( + Dictionary args, + string key, + out double value, + out string? skipReason) + { + value = 0d; + skipReason = null; + if (!args.TryGetValue(key, out string? token) || string.IsNullOrWhiteSpace(token)) + { + return true; + } + + if (!SpanParseUtility.TryParseFiniteDouble(token, out double rawValue)) + { + skipReason = $"Invalid value for +{key}."; + return false; + } + + int quadrant = (int)Math.Round(rawValue); + if (Math.Abs(rawValue - quadrant) > 1e-10d || quadrant < 0 || quadrant > 3) + { + skipReason = $"Invalid value for +{key}."; + return false; + } + + value = quadrant; + return true; + } + + private static bool TryResolveS2ProjectionTypeCode(string token, out double modeCode) + { + modeCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (SpanParseUtility.TryParseFiniteDouble(normalized, out double rawValue)) + { + int mode = (int)Math.Round(rawValue); + if (Math.Abs(rawValue - mode) > 1e-10d) + { + return false; + } + + if (mode < 0 || mode > 3) + { + return false; + } + + modeCode = mode; + return true; + } + + modeCode = normalized.ToUpperInvariant() switch + { + "LINEAR" => 0d, + "QUADRATIC" => 1d, + "TANGENT" => 2d, + "NONE" => 3d, + _ => double.NaN, + }; + + return !double.IsNaN(modeCode); + } + + private static bool TryResolveIseaOrientationCode(string token, out double orientationCode) + { + orientationCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (SpanParseUtility.TryParseFiniteDouble(normalized, out double rawValue)) + { + int orientation = (int)Math.Round(rawValue); + if (Math.Abs(rawValue - orientation) > 1e-10d) + { + return false; + } + + if (orientation < 0 || orientation > 1) + { + return false; + } + + orientationCode = orientation; + return true; + } + + orientationCode = normalized.ToUpperInvariant() switch + { + "ISEA" => 0d, + "POLE" => 1d, + _ => double.NaN, + }; + + return !double.IsNaN(orientationCode); + } + + private static bool TryResolveIseaModeCode(string token, out double modeCode) + { + modeCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (SpanParseUtility.TryParseFiniteDouble(normalized, out double rawValue)) + { + int mode = (int)Math.Round(rawValue); + if (Math.Abs(rawValue - mode) > 1e-10d) + { + return false; + } + + if (mode < 0 || mode > 3) + { + return false; + } + + modeCode = mode; + return true; + } + + modeCode = normalized.ToUpperInvariant() switch + { + "PLANE" => 0d, + "DI" => 1d, + "DD" => 2d, + "HEX" => 3d, + _ => double.NaN, + }; + + return !double.IsNaN(modeCode); + } + + private static bool TryResolveProjectionUnitFactor( + Dictionary args, + out double unitFactor, + out string? skipReason) + { + unitFactor = 1d; + skipReason = null; + + if (args.TryGetValue("to_meter", out string? toMeterToken) && !string.IsNullOrWhiteSpace(toMeterToken)) + { + if (!TryParsePositiveScaleFactor(toMeterToken, out unitFactor)) + { + skipReason = "Unable to parse +to_meter parameter for projection step."; + return false; + } + } + else if (args.TryGetValue("units", out string? unitsToken) + && !string.IsNullOrWhiteSpace(unitsToken) + && !TryResolveUnitFactor(unitsToken, out unitFactor)) + { + skipReason = "Unable to parse +units parameter for projection step."; + return false; + } + + return true; + } + + private static bool TryResolveDatumToWgs84Parameters( + Dictionary args, + out Wgs84ConversionInfo? toWgs84, + out string? skipReason) + { + toWgs84 = null; + skipReason = null; + + if (args.TryGetValue("towgs84", out string? towgs84Token) && !string.IsNullOrWhiteSpace(towgs84Token)) + { + string[] values = towgs84Token.Split(CommaSeparator, StringSplitOptions.None); + if (values.Length != 3 && values.Length != 6 && values.Length != 7) + { + skipReason = "Invalid value for +towgs84."; + return false; + } + + if (!SpanParseUtility.TryParseFiniteDouble(values[0], out double dx) + || !SpanParseUtility.TryParseFiniteDouble(values[1], out double dy) + || !SpanParseUtility.TryParseFiniteDouble(values[2], out double dz)) + { + skipReason = "Invalid value for +towgs84."; + return false; + } + + double rx = 0d; + double ry = 0d; + double rz = 0d; + double ppm = 0d; + + if (values.Length >= 6) + { + if (!SpanParseUtility.TryParseFiniteDouble(values[3], out rx) + || !SpanParseUtility.TryParseFiniteDouble(values[4], out ry) + || !SpanParseUtility.TryParseFiniteDouble(values[5], out rz)) + { + skipReason = "Invalid value for +towgs84."; + return false; + } + } + + if (values.Length == 7 && !SpanParseUtility.TryParseFiniteDouble(values[6], out ppm)) + { + skipReason = "Invalid value for +towgs84."; + return false; + } + + toWgs84 = new Wgs84ConversionInfo(dx, dy, dz, rx, ry, rz, ppm); + return true; + } + + if (args.TryGetValue("datum", out string? datumToken) && !string.IsNullOrWhiteSpace(datumToken)) + { + TryResolveKnownDatumToWgs84Parameters(datumToken, out toWgs84); + } + + return true; + } + + private static bool TryResolveKnownDatumToWgs84Parameters(string datumToken, out Wgs84ConversionInfo? toWgs84) + { + toWgs84 = null; + if (string.IsNullOrWhiteSpace(datumToken)) + { + return false; + } + + if (datumToken.Equals("potsdam", StringComparison.OrdinalIgnoreCase)) + { + toWgs84 = new Wgs84ConversionInfo(598.1, 73.7, 418.2, 0.202, 0.045, -2.455, 6.7); + return true; + } + + if (datumToken.Equals("NAD27", StringComparison.OrdinalIgnoreCase)) + { + toWgs84 = new Wgs84ConversionInfo(-8, 160, 176, 0, 0, 0, 0); + return true; + } + + if (datumToken.Equals("NAD83", StringComparison.OrdinalIgnoreCase) + || datumToken.Equals("WGS84", StringComparison.OrdinalIgnoreCase)) + { + toWgs84 = new Wgs84ConversionInfo(); + return true; + } + + if (datumToken.Equals("nzgd49", StringComparison.OrdinalIgnoreCase)) + { + toWgs84 = new Wgs84ConversionInfo(59.47, -5.04, 187.44, 0.47, -0.1, 1.024, -4.5993); + return true; + } + + if (datumToken.Equals("ire65", StringComparison.OrdinalIgnoreCase)) + { + toWgs84 = new Wgs84ConversionInfo(482.530, -130.596, 564.557, -1.042, -0.214, -0.631, 8.15); + return true; + } + + if (datumToken.Equals("GGRS87", StringComparison.OrdinalIgnoreCase)) + { + toWgs84 = new Wgs84ConversionInfo(-199.87, 74.79, 246.02, 0, 0, 0, 0); + return true; + } + + if (datumToken.Equals("OSGB36", StringComparison.OrdinalIgnoreCase)) + { + toWgs84 = new Wgs84ConversionInfo(446.448, -125.157, 542.060, 0.1502, 0.2470, 0.8421, -20.4894); + return true; + } + + return false; + } + + private static bool TryResolveVerticalUnitFactor( + Dictionary args, + out double unitFactor, + out string? skipReason) + { + unitFactor = 1d; + skipReason = null; + + if (args.TryGetValue("vto_meter", out string? vtoMeterToken) && !string.IsNullOrWhiteSpace(vtoMeterToken)) + { + if (!TryParsePositiveScaleFactor(vtoMeterToken, out unitFactor)) + { + skipReason = "Unable to parse +vto_meter parameter for projection step."; + return false; + } + } + else if (args.TryGetValue("vunits", out string? vunitsToken) + && !string.IsNullOrWhiteSpace(vunitsToken) + && !TryResolveUnitFactor(vunitsToken, out unitFactor)) + { + skipReason = "Unable to parse +vunits parameter for projection step."; + return false; + } + + return true; + } + + private static bool TryApplyOptionalProjectionParameter( + Dictionary args, + string sourceKey, + string targetName, + List parameters, + out string? skipReason) + { + skipReason = null; + if (!args.TryGetValue(sourceKey, out string? token) || string.IsNullOrWhiteSpace(token)) + { + return true; + } + + if (!SpanParseUtility.TryParseFiniteDouble(token, out double value)) + { + skipReason = $"Invalid value for +{sourceKey}."; + return false; + } + + SetOrAddProjectionParameter(parameters, targetName, value); + return true; + } + + private static void SetOrAddProjectionParameter( + List parameters, + string name, + double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } + + private static bool TryApplyPrimeMeridianOffset( + Dictionary args, + List parameters, + out string? skipReason) + { + skipReason = null; + if (!args.TryGetValue("pm", out string? pmToken) || string.IsNullOrWhiteSpace(pmToken)) + { + return true; + } + + if (!TryResolvePrimeMeridianLongitudeDegrees(pmToken, out double primeMeridianLongitudeDegrees)) + { + skipReason = "Invalid or unsupported value for +pm."; + return false; + } + + AddProjectionLongitudeOffset(parameters, "central_meridian", primeMeridianLongitudeDegrees); + AddProjectionLongitudeOffset(parameters, "longitude_of_center", primeMeridianLongitudeDegrees); + return true; + } + + private static void AddProjectionLongitudeOffset( + List parameters, + string name, + double offsetDegrees) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(name, parameters[i].Value + offsetDegrees); + return; + } + } + + parameters.Add(new ProjectionParameter(name, offsetDegrees)); + } + + private static bool TryResolveProjectionEllipsoid( + Dictionary args, + out double semiMajor, + out double semiMinor, + out string? skipReason) + { + skipReason = null; + + if (args.TryGetValue("r", out string? radiusToken) + && SpanParseUtility.TryParseFiniteDouble(radiusToken, out double radius) + && radius > 0d) + { + semiMajor = radius; + semiMinor = radius; + return true; + } + + if (args.TryGetValue("ellps", out string? ellps) && !string.IsNullOrWhiteSpace(ellps)) + { + if (ProjEllipsoidResolver.TryResolveKnownEllipsoid( + ellps, + allowClarke1880Ign: true, + allowBessel: true, + out semiMajor, + out semiMinor)) + { + if (!ProjEllipsoidResolver.TryApplySemiMajorOverride(args, ref semiMajor, ref semiMinor, out skipReason)) + { + return false; + } + + return ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason); + } + + skipReason = "Projection step received unsupported +ellps value."; + return false; + } + + if (args.TryGetValue("datum", out string? datum) && !string.IsNullOrWhiteSpace(datum)) + { + if (ProjEllipsoidResolver.TryResolveKnownEllipsoid( + datum, + allowClarke1880Ign: true, + allowBessel: true, + out semiMajor, + out semiMinor)) + { + if (!ProjEllipsoidResolver.TryApplySemiMajorOverride(args, ref semiMajor, ref semiMinor, out skipReason)) + { + return false; + } + + return ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason); + } + + skipReason = "Projection step received unsupported +datum value."; + return false; + } + + if (args.TryGetValue("a", out string? majorToken) + && SpanParseUtility.TryParseFiniteDouble(majorToken, out double major) + && major > 0d) + { + semiMajor = major; + semiMinor = major; + return ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason); + } + + semiMajor = Ellipsoid.WGS84.SemiMajorAxis; + semiMinor = Ellipsoid.WGS84.SemiMinorAxis; + return ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason); + } + + private static bool TryGetZoneCentralMeridian(Dictionary args, out double centralMeridian) + { + centralMeridian = 0d; + if (!args.TryGetValue("zone", out string? zoneToken) || string.IsNullOrWhiteSpace(zoneToken)) + { + return false; + } + + string digits = zoneToken.Trim(); + int zone = 0; + int index = 0; + while (index < digits.Length && char.IsDigit(digits[index])) + { + int digit = digits[index] - '0'; + if (zone > ((int.MaxValue - digit) / 10)) + { + return false; + } + + zone = (zone * 10) + digit; + index++; + } + + if (index == 0) + { + return false; + } + + if (zone is < 1 or > 60) + { + return false; + } + + centralMeridian = (zone * 6d) - 183d; + return true; + } + + private static bool TryResolveGeocentricScale( + Dictionary args, + out double scale, + out string? skipReason) + { + scale = 1d; + skipReason = null; + + if (args.TryGetValue("units", out string? unitsToken) + && !string.IsNullOrWhiteSpace(unitsToken) + && !unitsToken.Equals("m", StringComparison.OrdinalIgnoreCase)) + { + skipReason = "geocent/cart currently supports only +units=m."; + return false; + } + + if (args.TryGetValue("to_meter", out string? toMeterToken) && !string.IsNullOrWhiteSpace(toMeterToken)) + { + if (!TryParsePositiveScaleFactor(toMeterToken, out scale)) + { + skipReason = "Unable to parse +to_meter parameter for geocent/cart."; + return false; + } + } + + return true; + } + + private static bool TryParsePositiveScaleFactor(string token, out double scale) + { + scale = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + if (double.TryParse(token, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double parsedScale)) + { + if (parsedScale <= 0d || double.IsNaN(parsedScale) || double.IsInfinity(parsedScale)) + { + return false; + } + + scale = parsedScale; + return true; + } + +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int slashIndex = token.IndexOf('/', StringComparison.Ordinal); +#else + int slashIndex = token.IndexOf('/'); +#endif + if (slashIndex <= 0 || slashIndex >= token.Length - 1) + { + return false; + } + + string numeratorToken = token[..slashIndex].Trim(); + string denominatorToken = token[(slashIndex + 1)..].Trim(); + if (!double.TryParse(numeratorToken, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double numerator) + || !double.TryParse(denominatorToken, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double denominator)) + { + return false; + } + + if (denominator == 0d) + { + return false; + } + + parsedScale = numerator / denominator; + if (parsedScale <= 0d || double.IsNaN(parsedScale) || double.IsInfinity(parsedScale)) + { + return false; + } + + scale = parsedScale; + return true; + } + + private static bool TryResolveAiroceanOrientationCode(string token, out double orientationCode) + { + orientationCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (SpanParseUtility.TryParseFiniteDouble(normalized, out orientationCode)) + { + return orientationCode == 0d || orientationCode == 1d; + } + + orientationCode = normalized.ToUpperInvariant() switch + { + "VERTICAL" => 0d, + "HORIZONTAL" => 1d, + _ => double.NaN, + }; + + return !double.IsNaN(orientationCode); + } + + private static bool TryResolvePrimeMeridianLongitudeDegrees(string token, out double longitudeDegrees) + { + longitudeDegrees = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + bool radiansSuffix = normalized.Length > 0 && (normalized[^1] == 'r' || normalized[^1] == 'R'); + if (radiansSuffix) + { + normalized = normalized[..^1]; + } + + if (SpanParseUtility.TryParseFiniteDouble(normalized, out longitudeDegrees)) + { + if (radiansSuffix) + { + longitudeDegrees *= 180d / Math.PI; + } + + return true; + } + + if (TryParsePrimeMeridianDmsToken(normalized, out longitudeDegrees)) + { + return true; + } + + longitudeDegrees = normalized.ToUpperInvariant() switch + { + "GREENWICH" => 0d, + "LISBON" => -(9d + (7d / 60d) + (54.862d / 3600d)), + "PARIS" => 2d + (20d / 60d) + (14.025d / 3600d), + "BOGOTA" => -(74d + (4d / 60d) + (51.3d / 3600d)), + "MADRID" => -(3d + (41d / 60d) + (16.58d / 3600d)), + "ROME" => 12d + (27d / 60d) + (8.4d / 3600d), + "BERN" => 7d + (26d / 60d) + (22.5d / 3600d), + "JAKARTA" => 106d + (48d / 60d) + (27.79d / 3600d), + "FERRO" => -(17d + (40d / 60d)), + "BRUSSELS" => 4d + (22d / 60d) + (4.71d / 3600d), + "STOCKHOLM" => 18d + (3d / 60d) + (29.8d / 3600d), + "ATHENS" => 23d + (42d / 60d) + (58.815d / 3600d), + "OSLO" => 10d + (43d / 60d) + (22.5d / 3600d), + _ => double.NaN, + }; + + return !double.IsNaN(longitudeDegrees); + } + + private static bool TryResolveProjPrimeMeridianLongitudeDegrees(string token, out double longitudeDegrees) + { + return TryResolvePrimeMeridianLongitudeDegrees(token, out longitudeDegrees); + } + + private static bool TryParsePrimeMeridianDmsToken(string token, out double value) + { + value = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string text = token.Trim() + .Replace('°', 'd') + .Replace('º', 'd'); + int sign = 1; + + char last = text[text.Length - 1]; + if (last == 'W' || last == 'w' || last == 'S' || last == 's') + { + sign = -1; + text = text[..^1]; + } + else if (last == 'E' || last == 'e' || last == 'N' || last == 'n') + { + text = text[..^1]; + } + + if (text.Length > 0 && text[0] == '-') + { + sign *= -1; + text = text[1..]; + } + else if (text.Length > 0 && text[0] == '+') + { + text = text[1..]; + } + +#pragma warning disable CA1307, CA1865 // netstandard2.0 lacks char+StringComparison overload; char search is ordinal here. + int dIndex = text.IndexOf('d'); + if (dIndex < 0) + { + dIndex = text.IndexOf('D'); + } + + int mIndex = text.IndexOf('\''); + if (dIndex <= 0 || mIndex <= dIndex) + { + return false; + } + + string degreesToken = text[..dIndex]; + string minutesToken = text.Substring(dIndex + 1, mIndex - dIndex - 1); + if (!double.TryParse(degreesToken, NumberStyles.Float, CultureInfo.InvariantCulture, out double degrees) + || !double.TryParse(minutesToken, NumberStyles.Float, CultureInfo.InvariantCulture, out double minutes)) + { + return false; + } + + double seconds = 0d; + int secondsMarker = text.IndexOf('"'); +#pragma warning restore CA1307, CA1865 + if (secondsMarker > mIndex + 1) + { + string secondsToken = text.Substring(mIndex + 1, secondsMarker - mIndex - 1); + if (!double.TryParse(secondsToken, NumberStyles.Float, CultureInfo.InvariantCulture, out seconds)) + { + return false; + } + } + + value = sign * (degrees + (minutes / 60d) + (seconds / 3600d)); + return true; + } + + private static bool TryResolveUnitScale( + IDictionary args, + string inKey, + string outKey, + bool treatDegRadAsIdentity, + out double scale) + { + scale = 1d; + bool hasIn = args.TryGetValue(inKey, out string? inToken) && !string.IsNullOrWhiteSpace(inToken); + bool hasOut = args.TryGetValue(outKey, out string? outToken) && !string.IsNullOrWhiteSpace(outToken); + if (!hasIn && !hasOut) + { + return true; + } + + if (hasIn != hasOut) + { + return false; + } + + string inUnitToken = ArgumentGuard.ThrowIfNull(inToken, nameof(inToken)); + string outUnitToken = ArgumentGuard.ThrowIfNull(outToken, nameof(outToken)); + if (treatDegRadAsIdentity + && ((inUnitToken.Equals("deg", StringComparison.OrdinalIgnoreCase) && outUnitToken.Equals("rad", StringComparison.OrdinalIgnoreCase)) + || (inUnitToken.Equals("rad", StringComparison.OrdinalIgnoreCase) && outUnitToken.Equals("deg", StringComparison.OrdinalIgnoreCase)))) + { + scale = 1d; + return true; + } + + if (!TryResolveUnitFactor(inUnitToken, out double inFactor) || !TryResolveUnitFactor(outUnitToken, out double outFactor)) + { + return false; + } + + bool inputIsAngular = IsAngularUnitToken(inUnitToken); + bool outputIsAngular = IsAngularUnitToken(outUnitToken); + if (inputIsAngular != outputIsAngular) + { + return false; + } + + if (outFactor == 0d) + { + return false; + } + + scale = inFactor / outFactor; + return true; + } + + private static bool IsAngularUnitToken(string token) + { + return token.Equals("rad", StringComparison.OrdinalIgnoreCase) + || token.Equals("deg", StringComparison.OrdinalIgnoreCase) + || token.Equals("grad", StringComparison.OrdinalIgnoreCase); + } + + private static bool TryResolveUnitFactor(string token, out double factor) + { + factor = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + if (double.TryParse(token, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double numeric)) + { + if (numeric <= 0d || double.IsInfinity(numeric) || double.IsNaN(numeric)) + { + return false; + } + + factor = numeric; + return true; + } + + if (token.Equals("mm", StringComparison.OrdinalIgnoreCase)) + { + factor = 1e-3d; + return true; + } + + if (token.Equals("cm", StringComparison.OrdinalIgnoreCase)) + { + factor = 1e-2d; + return true; + } + + if (token.Equals("dm", StringComparison.OrdinalIgnoreCase)) + { + factor = 1e-1d; + return true; + } + + if (token.Equals("m", StringComparison.OrdinalIgnoreCase)) + { + factor = 1d; + return true; + } + + if (token.Equals("km", StringComparison.OrdinalIgnoreCase)) + { + factor = 1e3d; + return true; + } + + if (token.Equals("ft", StringComparison.OrdinalIgnoreCase)) + { + factor = 0.3048d; + return true; + } + + if (token.Equals("us-ft", StringComparison.OrdinalIgnoreCase)) + { + factor = TransformationMath.MetresPerUsSurveyFoot; + return true; + } + + if (token.Equals("rad", StringComparison.OrdinalIgnoreCase)) + { + factor = 1d; + return true; + } + + if (token.Equals("deg", StringComparison.OrdinalIgnoreCase)) + { + factor = Math.PI / 180d; + return true; + } + + if (token.Equals("grad", StringComparison.OrdinalIgnoreCase)) + { + factor = Math.PI / 200d; + return true; + } + + return false; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.Steps.cs b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.Steps.cs new file mode 100644 index 00000000..b97d7b7b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.Steps.cs @@ -0,0 +1,853 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Reflection; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; + +/// +/// Creates runtime math transforms from PROJ-style pipeline operation strings. +/// +internal static partial class ProjPipelineMathTransformFactory +{ + private static bool TryCreateNoOpStepTransform( + Dictionary args, + PipelineExecutionContext? executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = new IdentityMathTransform(3); + skipReason = null; + return true; + } + + private static bool TryCreatePushStepTransform( + Dictionary args, + PipelineExecutionContext? executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (executionContext is null) + { + skipReason = "push operation requires a pipeline execution context."; + return false; + } + + return PipelineStackTransferMathTransform.TryCreatePush(args, executionContext, out transform, out skipReason); + } + + private static bool TryCreatePopStepTransform( + Dictionary args, + PipelineExecutionContext? executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (executionContext is null) + { + skipReason = "pop operation requires a pipeline execution context."; + return false; + } + + return PipelineStackTransferMathTransform.TryCreatePop(args, executionContext, out transform, out skipReason); + } + + private static bool TryCreateHGridShiftStepTransform( + Dictionary args, + PipelineExecutionContext? executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + return TryCreateHorizontalGridShiftTransform( + args, + useGridMetadataInterpolation: false, + allowBiquadraticInterpolation: false, + out transform, + out skipReason); + } + + private static bool TryCreateGridShiftStepTransform( + Dictionary args, + PipelineExecutionContext? executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + return TryCreateHorizontalGridShiftTransform( + args, + useGridMetadataInterpolation: true, + allowBiquadraticInterpolation: true, + out transform, + out skipReason); + } + + private static bool TryCreateAxisSwapTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + bool hasOrder = args.TryGetValue("order", out string? orderTokenCandidate) && !string.IsNullOrWhiteSpace(orderTokenCandidate); + bool hasAxis = args.TryGetValue("axis", out string? axisTokenCandidate) && !string.IsNullOrWhiteSpace(axisTokenCandidate); + if (hasOrder == hasAxis) + { + skipReason = "Axisswap requires exactly one of +order or +axis."; + return false; + } + + int[] order; + if (hasOrder) + { + string orderToken = ArgumentGuard.ThrowIfNull(orderTokenCandidate, nameof(orderTokenCandidate)); + if (!TryParseAxisSwapOrder(orderToken, out order)) + { + skipReason = "Unable to parse +order parameter for axisswap."; + return false; + } + } + else + { + string axisToken = ArgumentGuard.ThrowIfNull(axisTokenCandidate, nameof(axisTokenCandidate)); + if (!TryParseAxisOrder(axisToken, out order)) + { + skipReason = "Unable to parse +axis parameter for axisswap."; + return false; + } + } + + int dimension = order.Length; + if (dimension is < 2 or > 4) + { + skipReason = "Axisswap supports only 2D, 3D or 4D coordinates in the current runtime."; + return false; + } + + int[] sourceIndices = [0, 1, 2, 3]; + int[] signs = [1, 1, 1, 1]; + for (int i = 0; i < dimension; i++) + { + int rawOrder = order[i]; + int sourceIndex = Math.Abs(rawOrder) - 1; + if (sourceIndex < 0 || sourceIndex >= dimension) + { + skipReason = "Axisswap order references an out-of-range axis."; + return false; + } + + sourceIndices[i] = sourceIndex; + signs[i] = rawOrder < 0 ? -1 : 1; + } + + transform = new AxisSwapMathTransform( + dimension, + sourceIndices[0], + signs[0], + sourceIndices[1], + signs[1], + sourceIndices[2], + signs[2], + sourceIndices[3], + signs[3]); + + return true; + } + + private static bool TryCreateUnitConvertTransform( + IDictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + if (!TryResolveUnitScale(args, "xy_in", "xy_out", true, out double xyScale)) + { + skipReason = "Unable to parse XY units for unitconvert."; + return false; + } + + if (!TryResolveUnitScale(args, "z_in", "z_out", false, out double zScale)) + { + skipReason = "Unable to parse Z units for unitconvert."; + return false; + } + + transform = new UnitConvertMathTransform(3, xyScale, zScale); + return true; + } + + private static bool TryCreateGeocentricCartesianTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (!ProjEllipsoidResolver.TryResolveRequiredEllipsoidWithOverrides( + args, + operationName: "geocent/cart", + allowClarke1880Ign: true, + allowBessel: true, + out double semiMajor, + out double semiMinor, + out skipReason)) + { + return false; + } + + if (!TryResolveGeocentricScale(args, out double toMeterScale, out skipReason)) + { + return false; + } + + // PROJ cart/geocent applies to_meter to cartesian output units. Keep internal + // ellipsoid units aligned by scaling axis values accordingly. + semiMajor /= toMeterScale; + semiMinor /= toMeterScale; + if (semiMajor <= 0d || semiMinor <= 0d || double.IsNaN(semiMajor) || double.IsInfinity(semiMajor) || double.IsNaN(semiMinor) || double.IsInfinity(semiMinor)) + { + skipReason = "geocent/cart resolved ellipsoid axes must be finite and positive."; + return false; + } + + var geocentricParameters = new List(2) + { + new("semi_major", semiMajor), + new("semi_minor", semiMinor), + }; + + transform = new GeocentricTransform(geocentricParameters, false); + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + + private static bool TryCreateGeocentricLatitudeTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (!ProjEllipsoidResolver.TryResolveRequiredEllipsoidWithOverrides( + args, + operationName: "geoc", + allowClarke1880Ign: true, + allowBessel: true, + out double semiMajor, + out double semiMinor, + out skipReason)) + { + return false; + } + + transform = new GeocentricLatitudeMathTransform(semiMajor, semiMinor, args.ContainsKey("inv")); + return true; + } + + private static bool TryCreateProjectionStepTransform( + Dictionary args, + string projCode, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (!TryBuildProjectionStepParameters(args, projCode, out List? parametersCandidate, out skipReason)) + { + return false; + } + + List parameters = ArgumentGuard.ThrowIfNull(parametersCandidate, nameof(parametersCandidate)); + string projectionImplementationCode = ResolveProjectionImplementationCode(args, projCode); + + if (RequiresDatumAwareCrsStep(args)) + { + if (!TryCreateDatumAwareProjectedStepTransform(args, projectionImplementationCode, parameters, out transform, out skipReason)) + { + return false; + } + } + else + { + try + { + transform = ProjectionsRegistry.CreateProjection(projectionImplementationCode, parameters); + } + catch (NotSupportedException) + { + return false; + } + catch (ArgumentException) + { + skipReason = $"{projCode} projection could not be created with the parsed parameter set."; + return false; + } + catch (InvalidOperationException) + { + skipReason = $"{projCode} projection operation could not be constructed for this step."; + return false; + } + catch (TargetInvocationException) + { + skipReason = $"{projCode} projection constructor rejected the current parameter set."; + return false; + } + } + + if (!TryApplyOptionalVerticalUnitScale(args, ArgumentGuard.ThrowIfNull(transform, nameof(transform)), out MathTransform? verticallyScaledTransform, out skipReason)) + { + transform = null; + return false; + } + + MathTransform currentTransform = ArgumentGuard.ThrowIfNull(verticallyScaledTransform, nameof(verticallyScaledTransform)); + + if (args.ContainsKey("inv")) + { + currentTransform = currentTransform.Inverse(); + } + + transform = currentTransform; + return true; + } + + private static string ResolveProjectionImplementationCode( + Dictionary args, + string projCode) + { + if (args.ContainsKey("approx") + && (projCode.Equals("utm", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("tmerc", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("transverse_mercator", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("gauss_kruger", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("transverse_mercator_south_oriented", StringComparison.OrdinalIgnoreCase))) + { + // PROJ's +approx flag opts the transverse Mercator family back into + // the classic Evenden/Snyder implementation. + return "approx_tmerc"; + } + + if (projCode.Equals("utm", StringComparison.OrdinalIgnoreCase) && !args.ContainsKey("approx")) + { + // PROJ routes UTM through the exact Poder/Engsager transverse Mercator kernel + // unless the caller opts back into the approximate Snyder path with +approx. + return "etmerc"; + } + + return projCode; + } + + private static bool RequiresDatumAwareCrsStep(Dictionary args) + { + return args.ContainsKey("towgs84") || args.ContainsKey("datum"); + } + + private static bool TryCreateDatumAwareProjectedStepTransform( + Dictionary args, + string projectionImplementationCode, + List parameters, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + if (!TryResolveProjectionEllipsoid(args, out double semiMajor, out double semiMinor, out skipReason)) + { + return false; + } + + if (!TryResolveProjectionUnitFactor(args, out double unitFactor, out skipReason)) + { + return false; + } + + if (!TryResolveDatumToWgs84Parameters(args, out Wgs84ConversionInfo? toWgs84, out skipReason)) + { + return false; + } + + var csFactory = new CoordinateSystemFactory(); + GeographicCoordinateSystem localGeographic = CreatePipelineGeographicCoordinateSystem(csFactory, semiMajor, semiMinor, toWgs84); + + IProjection projection = csFactory.CreateProjection("PROJ pipeline projection", projectionImplementationCode, parameters); + LinearUnit linearUnit = CreateProjectionLinearUnit(unitFactor); + ProjectedCoordinateSystem projected = csFactory.CreateProjectedCoordinateSystem( + "PROJ pipeline projected", + localGeographic, + projection, + linearUnit, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + GeographicCoordinateSystem wgs84Geographic = GeographicCoordinateSystem.WGS84; + + try + { + transform = new CoordinateTransformationFactory().CreateFromCoordinateSystems(wgs84Geographic, projected).MathTransform; + return true; + } + catch (ArgumentException) + { + skipReason = $"{projectionImplementationCode} projected datum step could not be created with the parsed parameter set."; + return false; + } + catch (NotSupportedException) + { + skipReason = $"{projectionImplementationCode} projected datum step is not supported by the current runtime."; + return false; + } + catch (InvalidOperationException) + { + skipReason = $"{projectionImplementationCode} projected datum step could not be constructed for this step."; + return false; + } + } + + private static GeographicCoordinateSystem CreatePipelineGeographicCoordinateSystem( + CoordinateSystemFactory csFactory, + double semiMajor, + double semiMinor, + Wgs84ConversionInfo? toWgs84) + { + Ellipsoid ellipsoid = csFactory.CreateEllipsoid("PROJ pipeline ellipsoid", semiMajor, semiMinor, LinearUnit.Metre); + HorizontalDatum localDatum = csFactory.CreateHorizontalDatum("PROJ pipeline datum", DatumType.HD_Geocentric, ellipsoid, toWgs84); + return csFactory.CreateGeographicCoordinateSystem( + "PROJ pipeline geographic", + AngularUnit.Degrees, + localDatum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + private static bool TryCreateGeographicIdentityTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + skipReason = null; + var transforms = new List(4); + + if (RequiresDatumAwareCrsStep(args)) + { + if (!TryCreateDatumAwareGeographicStepTransform(args, out MathTransform? datumAwareTransform, out skipReason)) + { + transform = null; + return false; + } + + transforms.Add(ArgumentGuard.ThrowIfNull(datumAwareTransform, nameof(datumAwareTransform))); + } + else + { + transforms.Add(new IdentityMathTransform(3)); + } + + if (args.ContainsKey("geoc")) + { + if (!ProjEllipsoidResolver.TryResolveRequiredEllipsoidWithOverrides( + args, + operationName: "geoc", + allowClarke1880Ign: true, + allowBessel: true, + out double semiMajor, + out double semiMinor, + out skipReason)) + { + transform = null; + return false; + } + + // PROJ's legacy +proj=longlat/+proj=latlong +geoc flag behaves like the + // inverse of the dedicated geoc step, and +inv toggles it back again. + transforms.Add(new GeocentricLatitudeMathTransform(semiMajor, semiMinor, isInverse: true)); + } + + if (args.TryGetValue("pm", out string? pmToken) && !string.IsNullOrWhiteSpace(pmToken)) + { + if (!TryResolveProjPrimeMeridianLongitudeDegrees(pmToken, out double primeMeridianLongitudeDegrees)) + { + skipReason = "Unable to parse +pm value for geographic identity step."; + transform = null; + return false; + } + + var customPrimeMeridian = new PrimeMeridian( + primeMeridianLongitudeDegrees, + AngularUnit.Degrees, + "PROJ pipeline pm", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + transforms.Add(new PrimeMeridianTransform(PrimeMeridian.Greenwich, customPrimeMeridian)); + } + + if (args.TryGetValue("lon_wrap", out string? lonWrapToken) && !string.IsNullOrWhiteSpace(lonWrapToken)) + { + if (!SpanParseUtility.TryParseFiniteDouble(lonWrapToken, out double wrapCenterDegrees)) + { + skipReason = "Unable to parse +lon_wrap value for geographic identity step."; + transform = null; + return false; + } + + transforms.Add(new LongitudeWrapMathTransform(wrapCenterDegrees)); + } + + if (!TryResolveVerticalUnitFactor(args, out double verticalUnitFactor, out skipReason)) + { + transform = null; + return false; + } + + if (!verticalUnitFactor.Equals(1d)) + { + transforms.Add(new UnitConvertMathTransform(3, 1d, 1d / verticalUnitFactor)); + } + + MathTransform currentTransform = transforms.Count == 1 + ? transforms[0] + : new CompositeMathTransform(transforms); + + if (args.ContainsKey("inv")) + { + currentTransform = currentTransform.Inverse(); + } + + transform = currentTransform; + return true; + } + + private static bool TryCreateDatumAwareGeographicStepTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + if (!TryResolveProjectionEllipsoid(args, out double semiMajor, out double semiMinor, out skipReason)) + { + return false; + } + + if (!TryResolveDatumToWgs84Parameters(args, out Wgs84ConversionInfo? toWgs84, out skipReason)) + { + return false; + } + + var csFactory = new CoordinateSystemFactory(); + GeographicCoordinateSystem localGeographic = CreatePipelineGeographicCoordinateSystem(csFactory, semiMajor, semiMinor, toWgs84); + GeographicCoordinateSystem wgs84Geographic = GeographicCoordinateSystem.WGS84; + + try + { + transform = new CoordinateTransformationFactory().CreateFromCoordinateSystems(wgs84Geographic, localGeographic).MathTransform; + return true; + } + catch (ArgumentException) + { + skipReason = "Geographic datum step could not be created with the parsed parameter set."; + return false; + } + catch (NotSupportedException) + { + skipReason = "Geographic datum step is not supported by the current runtime."; + return false; + } + catch (InvalidOperationException) + { + skipReason = "Geographic datum step could not be constructed for this step."; + return false; + } + } + + private static bool TryApplyOptionalVerticalUnitScale( + Dictionary args, + MathTransform transform, + [NotNullWhen(true)] out MathTransform? result, + out string? skipReason) + { + if (!TryResolveVerticalUnitFactor(args, out double verticalUnitFactor, out skipReason)) + { + result = null; + return false; + } + + if (verticalUnitFactor.Equals(1d)) + { + result = transform; + return true; + } + + result = new CompositeMathTransform( + [ + transform, + new UnitConvertMathTransform(3, 1d, 1d / verticalUnitFactor), + ]); + return true; + } + + private static LinearUnit CreateProjectionLinearUnit(double unitFactor) + { + return unitFactor.Equals(LinearUnit.Metre.MetersPerUnit) + ? LinearUnit.Metre + : new LinearUnit(unitFactor, "PROJ pipeline unit", string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + + private static bool TryCreateHorizontalGridShiftTransform( + Dictionary args, + bool useGridMetadataInterpolation, + bool allowBiquadraticInterpolation, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (!args.TryGetValue("grids", out string? gridsToken) || string.IsNullOrWhiteSpace(gridsToken)) + { + skipReason = "Horizontal grid shift requires +grids."; + return false; + } + + if (!TryResolveGridPaths(gridsToken, out IReadOnlyList? gridPathsCandidate, out skipReason)) + { + return false; + } + + IReadOnlyList gridPaths = ArgumentGuard.ThrowIfNull(gridPathsCandidate, nameof(gridPathsCandidate)); + + if (!TryValidateGridExtensions(gridPaths, HorizontalGridExtensions, "horizontal", out skipReason)) + { + return false; + } + + try + { + bool hasGeoTiff = ContainsGeoTiffGrid(gridPaths); + if (hasGeoTiff) + { + if (!TryResolveGeoTiffHorizontalInterpolationOverride( + args, + useGridMetadataInterpolation, + allowBiquadraticInterpolation, + out bool? biquadraticInterpolationOverride, + out skipReason)) + { + return false; + } + + transform = new GeoTiffHGridShiftMathTransform(gridPaths, biquadraticInterpolationOverride); + } + else + { + if (!TryValidateNtv2HorizontalInterpolation(args, allowBiquadraticInterpolation, out skipReason)) + { + return false; + } + + transform = new Ntv2HGridShiftMathTransform(gridPaths); + } + } + catch (IOException ioException) + { + skipReason = $"Unable to read horizontal grid: {ioException.Message}"; + return false; + } + catch (InvalidDataException dataException) + { + skipReason = $"Invalid horizontal grid data: {dataException.Message}"; + return false; + } + catch (ArgumentException argumentException) + { + skipReason = $"Invalid grid parameters: {argumentException.Message}"; + return false; + } + + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + + private static bool TryCreateVerticalGridShiftTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (!args.TryGetValue("grids", out string? gridsToken) || string.IsNullOrWhiteSpace(gridsToken)) + { + skipReason = "Vertical grid shift requires +grids."; + return false; + } + + if (!TryResolveGridPaths(gridsToken, out IReadOnlyList? gridPathsCandidate, out skipReason)) + { + return false; + } + + IReadOnlyList gridPaths = ArgumentGuard.ThrowIfNull(gridPathsCandidate, nameof(gridPathsCandidate)); + + if (!TryValidateGridExtensions(gridPaths, VerticalGridExtensions, "vertical", out skipReason)) + { + return false; + } + + double multiplier = -1d; + if (args.TryGetValue("multiplier", out string? multiplierToken) + && !double.TryParse(multiplierToken, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out multiplier)) + { + skipReason = "Unable to parse +multiplier parameter for vgridshift."; + return false; + } + + if (double.IsNaN(multiplier) || double.IsInfinity(multiplier)) + { + skipReason = "vgridshift +multiplier must be a finite numeric value."; + return false; + } + + try + { + bool hasGeoTiff = ContainsGeoTiffGrid(gridPaths); + transform = hasGeoTiff + ? new GeoTiffVGridShiftMathTransform(gridPaths, multiplier) + : new GtxVGridShiftMathTransform(gridPaths, multiplier); + } + catch (IOException ioException) + { + skipReason = $"Unable to read vertical grid: {ioException.Message}"; + return false; + } + catch (InvalidDataException dataException) + { + skipReason = $"Invalid vertical grid data: {dataException.Message}"; + return false; + } + catch (ArgumentException argumentException) + { + skipReason = $"Invalid grid parameters: {argumentException.Message}"; + return false; + } + + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + + private static bool TryCreateXyzGridShiftTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (!args.TryGetValue("grids", out string? gridsToken) || string.IsNullOrWhiteSpace(gridsToken)) + { + skipReason = "Geocentric grid shift requires +grids."; + return false; + } + + if (!TryResolveGridPaths(gridsToken, out IReadOnlyList? gridPathsCandidate, out skipReason)) + { + return false; + } + + IReadOnlyList gridPaths = ArgumentGuard.ThrowIfNull(gridPathsCandidate, nameof(gridPathsCandidate)); + + if (!TryValidateGridExtensions(gridPaths, XyzGridExtensions, "xyz", out skipReason)) + { + return false; + } + + bool gridRefIsInput = true; + if (args.TryGetValue("grid_ref", out string? gridRefToken) && !string.IsNullOrWhiteSpace(gridRefToken)) + { + if (gridRefToken.Equals("input_crs", StringComparison.OrdinalIgnoreCase)) + { + gridRefIsInput = true; + } + else if (gridRefToken.Equals("output_crs", StringComparison.OrdinalIgnoreCase)) + { + gridRefIsInput = false; + } + else + { + skipReason = "xyzgridshift +grid_ref must be 'input_crs' or 'output_crs'."; + return false; + } + } + + double multiplier = 1d; + if (args.TryGetValue("multiplier", out string? multiplierToken) + && !double.TryParse(multiplierToken, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out multiplier)) + { + skipReason = "Unable to parse +multiplier parameter for xyzgridshift."; + return false; + } + + if (double.IsNaN(multiplier) || double.IsInfinity(multiplier)) + { + skipReason = "xyzgridshift +multiplier must be a finite numeric value."; + return false; + } + + if (!ProjEllipsoidResolver.TryResolveRequiredEllipsoidWithOverrides( + args, + operationName: "xyzgridshift", + allowClarke1880Ign: true, + allowBessel: true, + out double semiMajor, + out double semiMinor, + out skipReason)) + { + return false; + } + + try + { + transform = new GeoTiffXyzGridShiftMathTransform(gridPaths, semiMajor, semiMinor, multiplier, gridRefIsInput); + } + catch (IOException ioException) + { + skipReason = $"Unable to read xyz grid: {ioException.Message}"; + return false; + } + catch (InvalidDataException dataException) + { + skipReason = $"Invalid xyz grid data: {dataException.Message}"; + return false; + } + catch (ArgumentException argumentException) + { + skipReason = $"Invalid xyz grid parameters: {argumentException.Message}"; + return false; + } + + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.cs b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.cs new file mode 100644 index 00000000..2f13c92f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/ProjPipelineMathTransformFactory.cs @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Reflection; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; + +/// +/// Creates runtime math transforms from PROJ-style pipeline operation strings. +/// +internal static partial class ProjPipelineMathTransformFactory +{ + private const int MaxNestedPipelineDepth = 4; + private static readonly char[] CommaSeparator = [',']; + private static readonly char[] OperationTokenSeparators = [' ', '\t']; + private static readonly string[] HorizontalGridExtensions = [".gsb", ".tif", ".tiff"]; + private static readonly string[] VerticalGridExtensions = [".gtx", ".tif", ".tiff"]; + private static readonly string[] XyzGridExtensions = [".tif", ".tiff"]; + private static readonly Dictionary StepTransformDispatch = new(StringComparer.OrdinalIgnoreCase) + { + ["latlong"] = WrapDirect(TryCreateGeographicIdentityTransform), + ["longlat"] = WrapDirect(TryCreateGeographicIdentityTransform), + ["latlon"] = WrapDirect(TryCreateGeographicIdentityTransform), + ["lonlat"] = WrapDirect(TryCreateGeographicIdentityTransform), + ["noop"] = TryCreateNoOpStepTransform, + ["geocent"] = WrapDirect(TryCreateGeocentricCartesianTransform), + ["cart"] = WrapDirect(TryCreateGeocentricCartesianTransform), + ["geoc"] = WrapDirect(TryCreateGeocentricLatitudeTransform), + ["geogoffset"] = WrapDirect(GeogOffsetMathTransform.TryCreate), + ["affine"] = WrapDirect(AffineRuntimeMathTransform.TryCreate), + ["push"] = TryCreatePushStepTransform, + ["pop"] = TryCreatePopStepTransform, + ["set"] = WrapDirect(SetMathTransform.TryCreate), + ["axisswap"] = WrapDirect(TryCreateAxisSwapTransform), + ["unitconvert"] = WrapDirect(TryCreateUnitConvertTransform), + ["hgridshift"] = TryCreateHGridShiftStepTransform, + ["gridshift"] = TryCreateGridShiftStepTransform, + ["vgridshift"] = WrapDirect(TryCreateVerticalGridShiftTransform), + ["xyzgridshift"] = WrapDirect(TryCreateXyzGridShiftTransform), + ["defmodel"] = WrapDirect(DefModelMathTransform.TryCreate), + ["deformation"] = WrapDirect(DeformationMathTransform.TryCreate), + ["tinshift"] = WrapDirect(TinShiftMathTransform.TryCreate), + ["topocentric"] = WrapDirect(TopocentricMathTransform.TryCreate), + ["vertoffset"] = WrapDirect(VertOffsetMathTransform.TryCreate), + ["helmert"] = WrapDirect(HelmertMathTransform.TryCreate), + ["molobadekas"] = WrapDirect(MolobadekasMathTransform.TryCreate), + ["molodensky"] = WrapDirect(MolodenskyMathTransform.TryCreate), + ["horner"] = WrapDirect(HornerMathTransform.TryCreate), + ["ob_tran"] = WrapDirect(ObTranMathTransform.TryCreate), + ["sch"] = WrapDirect(SchMathTransform.TryCreate), + ["spherical_cross_track_height"] = WrapDirect(SchMathTransform.TryCreate), + }; + + private delegate bool TryCreateDirectStepTransform( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason); + + private delegate bool TryCreateDispatchedStepTransform( + Dictionary args, + PipelineExecutionContext? executionContext, + out MathTransform? transform, + out string? skipReason); + + /// + /// Tries to create an executable transform from a full operation or pipeline definition. + /// + /// Operation text to parse. + /// Created transform when parsing succeeds. + /// Reason why transform creation was skipped. + /// when a transform was created. + internal static bool TryCreateMathTransform(string operation, [NotNullWhen(true)] out MathTransform? transform, out string? skipReason) + => TryCreateMathTransform(operation, 0, out transform, out skipReason); + + private static bool TryCreateMathTransform(string operation, int pipelineDepth, [NotNullWhen(true)] out MathTransform? transform, out string? skipReason) + { + transform = null; + skipReason = null; + + if (operation is null) + { + skipReason = "Operation string was null."; + return false; + } + + if (pipelineDepth > MaxNestedPipelineDepth) + { + skipReason = $"Nested pipeline depth exceeded the supported maximum of {MaxNestedPipelineDepth.ToString(CultureInfo.InvariantCulture)}."; + return false; + } + + bool hasPipeline = ContainsPipelineProjection(operation); + IReadOnlyList>? pipelineStepArguments = null; + IReadOnlyList? nestedPipelineStepOperations = null; + if (hasPipeline && ContainsNestedPipelineProjection(operation)) + { + if (!TryParseNestedPipelineStepOperations(operation, out nestedPipelineStepOperations, out skipReason)) + { + return false; + } + } + else if (hasPipeline + && !TryParsePipelineStepArguments(operation, out pipelineStepArguments, out _, out skipReason)) + { + return false; + } + + IReadOnlyList> parsedPipelineSteps = hasPipeline && nestedPipelineStepOperations is null + ? ArgumentGuard.ThrowIfNull(pipelineStepArguments, nameof(pipelineStepArguments)) + : []; + + int stepCount = hasPipeline + ? nestedPipelineStepOperations?.Count ?? parsedPipelineSteps.Count + : 1; + PipelineExecutionContext? executionContext = hasPipeline + ? new PipelineExecutionContext() + : null; + + var stepTransforms = new List(stepCount); + for (int i = 0; i < stepCount; i++) + { + MathTransform? stepTransformCandidate; + string? stepSkipReason; + bool ok = hasPipeline + ? nestedPipelineStepOperations is not null + ? TryCreateStepTransform(nestedPipelineStepOperations[i], executionContext, pipelineDepth, out stepTransformCandidate, out stepSkipReason) + : TryCreateStepTransform(parsedPipelineSteps[i], executionContext, out stepTransformCandidate, out stepSkipReason) + : TryCreateStepTransform(operation, executionContext, pipelineDepth, out stepTransformCandidate, out stepSkipReason); + if (!ok) + { + skipReason = hasPipeline + ? $"Pipeline step {(i + 1).ToString(CultureInfo.InvariantCulture)} failed: {stepSkipReason ?? "unknown reason"}" + : (stepSkipReason ?? "Unable to create transform."); + return false; + } + + stepTransforms.Add(ArgumentGuard.ThrowIfNull(stepTransformCandidate, nameof(stepTransformCandidate))); + } + + if (stepTransforms.Count == 0) + { + skipReason = "Operation did not contain any executable step."; + return false; + } + + if (stepTransforms.Count == 1 && !hasPipeline) + { + transform = stepTransforms[0]; + return true; + } + + if (hasPipeline) + { + PipelineExecutionContext pipelineExecutionContext = ArgumentGuard.ThrowIfNull(executionContext, nameof(executionContext)); + transform = new PipelineCompositeMathTransform(stepTransforms, pipelineExecutionContext); + } + else + { + transform = new CompositeMathTransform(stepTransforms); + } + + return true; + } + + private static bool TryCreateStepTransform( + string operation, + PipelineExecutionContext? executionContext, + int pipelineDepth, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (ContainsPipelineProjection(operation)) + { + return TryCreateMathTransform(operation, pipelineDepth + 1, out transform, out skipReason); + } + + if (!TryParseOperationArguments(operation, out Dictionary args)) + { + skipReason = "Unable to parse operation parameters."; + return false; + } + + return TryCreateStepTransform(args, executionContext, out transform, out skipReason); + } + + private static bool TryCreateStepTransform( + Dictionary args, + PipelineExecutionContext? executionContext, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + if (!args.TryGetValue("proj", out string? projCode)) + { + skipReason = "Operation is missing +proj."; + return false; + } + + bool omitForward = executionContext is not null && args.ContainsKey("omit_fwd"); + bool omitInverse = executionContext is not null && args.ContainsKey("omit_inv"); + + if (StepTransformDispatch.TryGetValue(projCode, out TryCreateDispatchedStepTransform? stepFactory)) + { + if (!stepFactory(args, executionContext, out transform, out skipReason)) + { + return false; + } + + transform = WrapWithOmitFlags(ArgumentGuard.ThrowIfNull(transform, nameof(transform)), omitForward, omitInverse); + return true; + } + + if (TryCreateProjectionStepTransform(args, projCode, out transform, out skipReason)) + { + transform = WrapWithOmitFlags(transform, omitForward, omitInverse); + return true; + } + + if (skipReason is not null) + { + return false; + } + + skipReason = $"Projection '{projCode}' is not part of the current builtins wave."; + return false; + } + + private static MathTransform WrapWithOmitFlags(MathTransform stepTransform, bool omitForward, bool omitInverse) + { + return omitForward || omitInverse + ? new PipelineOmitMathTransform(stepTransform, omitForward, omitInverse) + : stepTransform; + } + + private static TryCreateDispatchedStepTransform WrapDirect(TryCreateDirectStepTransform factory) + { + return (Dictionary args, PipelineExecutionContext? _, out MathTransform? transform, out string? skipReason) => + factory(args, out transform, out skipReason); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/SampleData.cs b/src/ProjNet/CoordinateSystems/Transformations/SampleData.cs new file mode 100644 index 00000000..f25cc34c --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/SampleData.cs @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; + +/// +/// Holds raw grid sample arrays together with per-sample scale and offset factors for decoding grid shift values. +/// +internal readonly struct SampleData +{ + private readonly double[][] valuesBySample; + private readonly double[] scaleBySample; + private readonly double[] offsetBySample; + private readonly int width; + + /// + /// Initializes a new instance of the struct. + /// + /// The valuesBySample value. + /// The scaleBySample value. + /// The offsetBySample value. + /// The width value. + internal SampleData(double[][] valuesBySample, double[]? scaleBySample = null, double[]? offsetBySample = null, int width = 0) + { + this.valuesBySample = valuesBySample; + this.scaleBySample = scaleBySample ?? CreateConstant(valuesBySample?.Length ?? 0, 1d); + this.offsetBySample = offsetBySample ?? CreateConstant(valuesBySample?.Length ?? 0, 0d); + this.width = width; + } + + /// + /// Returns a new with the scale and offset of every sample multiplied by . + /// + /// The angularScaleToDegree value. + /// The computed value. + internal SampleData ApplyAngularScale(double angularScaleToDegree) + { + if (Math.Abs(angularScaleToDegree - 1d) <= 1e-12d) + { + return this; + } + + double[][] scaled = new double[this.valuesBySample.Length][]; + for (int i = 0; i < scaled.Length; i++) + { + scaled[i] = this.valuesBySample[i]; + } + + double[] adjustedScale = new double[this.scaleBySample.Length]; + double[] adjustedOffset = new double[this.offsetBySample.Length]; + for (int i = 0; i < adjustedScale.Length; i++) + { + adjustedScale[i] = this.scaleBySample[i] * angularScaleToDegree; + adjustedOffset[i] = this.offsetBySample[i] * angularScaleToDegree; + } + + return new SampleData(scaled, adjustedScale, adjustedOffset, this.width); + } + + /// + /// Returns a new with per-sample scale and offset overrides applied on top of the existing factors. + /// + /// The scaleBySample value. + /// The offsetBySample value. + /// The computed value. + internal SampleData ApplyScaleOffset(IReadOnlyDictionary scaleBySample, IReadOnlyDictionary offsetBySample) + { + if ((scaleBySample is null || scaleBySample.Count == 0) + && (offsetBySample is null || offsetBySample.Count == 0)) + { + return this; + } + + double[][] copiedValues = new double[this.valuesBySample.Length][]; + for (int i = 0; i < copiedValues.Length; i++) + { + copiedValues[i] = this.valuesBySample[i]; + } + + double[] adjustedScale = new double[this.scaleBySample.Length]; + double[] adjustedOffset = new double[this.offsetBySample.Length]; + for (int i = 0; i < this.scaleBySample.Length; i++) + { + double scaleFactor = 1d; + if (scaleBySample is not null && scaleBySample.TryGetValue(i, out double parsedScale)) + { + scaleFactor = parsedScale; + } + + double offsetValue = 0d; + if (offsetBySample is not null && offsetBySample.TryGetValue(i, out double parsedOffset)) + { + offsetValue = parsedOffset; + } + + adjustedScale[i] = this.scaleBySample[i] * scaleFactor; + adjustedOffset[i] = (this.offsetBySample[i] * scaleFactor) + offsetValue; + } + + return new SampleData(copiedValues, adjustedScale, adjustedOffset, this.width); + } + + /// + /// Returns the decoded value for the specified sample at grid column and row . + /// + /// The sample value. + /// The x value. + /// The y value. + /// The computed value. + internal double GetValue(int sample, int x, int y) + { + int index = (y * this.width) + x; + return (this.valuesBySample[sample][index] * this.scaleBySample[sample]) + this.offsetBySample[sample]; + } + + private static double[] CreateConstant(int count, double value) + { + double[] result = new double[count]; + for (int i = 0; i < count; i++) + { + result[i] = value; + } + + return result; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/SampleEncoding.cs b/src/ProjNet/CoordinateSystems/Transformations/SampleEncoding.cs new file mode 100644 index 00000000..123476ca --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/SampleEncoding.cs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using BitMiracle.LibTiff.Classic; + +/// +/// Describes the binary encoding of a single TIFF sample, capturing the byte width and the delegate used to decode a value from a buffer. +/// +internal readonly struct SampleEncoding +{ + private readonly ValueReader valueReader; + + private SampleEncoding(int bytesPerSample, ValueReader valueReader) + { + this.BytesPerSample = bytesPerSample; + this.valueReader = valueReader; + } + + /// + /// Represents a method that reads a double-precision value from a byte buffer at the specified byte offset. + /// + /// The buffer value. + /// The offset value. + /// The computed value. + internal delegate double ValueReader(byte[] buffer, int offset); + + /// + /// Gets the number of bytes occupied by one sample value in the raw buffer. + /// + internal int BytesPerSample { get; } + + /// + /// Attempts to create a for the given bits-per-sample count and TIFF sample format. + /// + /// The bitsPerSample value. + /// The sampleFormat value. + /// The encoding value. + /// The computed value. + internal static bool TryCreate(int bitsPerSample, SampleFormat sampleFormat, out SampleEncoding encoding) + { + switch (sampleFormat) + { + case SampleFormat.INT: + if (bitsPerSample == 16) + { + encoding = new SampleEncoding(2, (buffer, offset) => BitConverter.ToInt16(buffer, offset)); + return true; + } + + if (bitsPerSample == 32) + { + encoding = new SampleEncoding(4, (buffer, offset) => BitConverter.ToInt32(buffer, offset)); + return true; + } + + break; + case SampleFormat.UINT: + if (bitsPerSample == 16) + { + encoding = new SampleEncoding(2, (buffer, offset) => BitConverter.ToUInt16(buffer, offset)); + return true; + } + + if (bitsPerSample == 32) + { + encoding = new SampleEncoding(4, (buffer, offset) => BitConverter.ToUInt32(buffer, offset)); + return true; + } + + break; + case SampleFormat.IEEEFP: + if (bitsPerSample == 32) + { + encoding = new SampleEncoding(4, (buffer, offset) => BitConverter.ToSingle(buffer, offset)); + return true; + } + + if (bitsPerSample == 64) + { + encoding = new SampleEncoding(8, (buffer, offset) => BitConverter.ToDouble(buffer, offset)); + return true; + } + + break; + } + + encoding = default; + return false; + } + + /// + /// Reads a sample value from starting at the given byte . + /// + /// The buffer value. + /// The offset value. + /// The computed value. + internal double ReadValue(byte[] buffer, int offset) + { + return this.valueReader(buffer, offset); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/SchMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/SchMathTransform.cs new file mode 100644 index 00000000..38c13721 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/SchMathTransform.cs @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations.Numerics; + +/// +/// Implements PROJ's sch (Spherical Cross-track Height) 3D runtime transform. +/// +/// +/// +/// SCH is the JPL sensor-aligned Spherical Cross-track Height system. This +/// implementation follows PROJ's sch operation by deriving the peg-point +/// radius of curvature from the ellipsoid, building the peg-frame rotation +/// matrix, and converting between ellipsoidal geocentric coordinates and the +/// local SCH sphere. +/// +/// +/// The runtime was independently verified against PROJ's published +/// sch documentation and sch.cpp. In particular, PROJ's internal +/// normalization by the semimajor axis is framework-specific; this +/// implementation works directly in metre-valued coordinates and preserves the +/// same geometry without that intermediate scaling step. +/// +/// +/// PROJ: sch. +internal sealed class SchMathTransform : MathTransform +{ + private readonly GeocentricTransform ellipsoidForward; + private readonly GeocentricTransform ellipsoidInverse; + private readonly GeocentricTransform sphereForward; + private readonly GeocentricTransform sphereInverse; + + private readonly double semiMajorAxis; + private readonly double radiusOfCurvature; + + private readonly Vector3D offset; + + private readonly Matrix3x3 rotationMatrix; + + private readonly bool isInverted; + private MathTransform? inverse; + + /// + /// Initializes a new instance of the class. + /// + /// Projection parameters that include SCH and ellipsoid values. + public SchMathTransform(List parameters) + : this(parameters, false) + { + } + + private SchMathTransform(IEnumerable parameters, bool isInverted) + { + parameters = ArgumentGuard.ThrowIfNull(parameters, nameof(parameters)); + var parameterSet = new ProjectionParameterSet(parameters); + + double pegLatitude = DegreesToRadians(parameterSet.GetParameterValue("plat_0", "peg_point_latitude")); + double pegLongitude = DegreesToRadians(parameterSet.GetParameterValue("plon_0", "peg_point_longitude")); + double pegHeading = DegreesToRadians(parameterSet.GetParameterValue("phdg_0", "peg_point_heading")); + double pegHeight = parameterSet.GetOptionalParameterValue("h_0", 0d, "peg_point_height"); + + this.semiMajorAxis = parameterSet.GetOptionalParameterValue("semi_major", 0d, "a"); + double semiMinorAxis = parameterSet.GetOptionalParameterValue("semi_minor", 0d, "b"); + double radius = parameterSet.GetOptionalParameterValue("r", 0d); + if (radius > 0d) + { + this.semiMajorAxis = radius; + semiMinorAxis = radius; + } + + if (this.semiMajorAxis <= 0d) + { + this.semiMajorAxis = Ellipsoid.WGS84.SemiMajorAxis; + } + + if (semiMinorAxis <= 0d) + { + semiMinorAxis = this.semiMajorAxis; + } + + var ellipsoidParameters = new List + { + new("semi_major", this.semiMajorAxis), + new("semi_minor", semiMinorAxis), + }; + + this.ellipsoidForward = new GeocentricTransform(ellipsoidParameters, false); + this.ellipsoidInverse = (GeocentricTransform)this.ellipsoidForward.Inverse(); + + double eccentricitySquared = 1d - ((semiMinorAxis * semiMinorAxis) / (this.semiMajorAxis * this.semiMajorAxis)); + + double cosPegLatitude = Math.Cos(pegLatitude); + double sinPegLatitude = Math.Sin(pegLatitude); + double cosPegLongitude = Math.Cos(pegLongitude); + double sinPegLongitude = Math.Sin(pegLongitude); + double cosPegHeading = Math.Cos(pegHeading); + double sinPegHeading = Math.Sin(pegHeading); + + double temp = Math.Sqrt(1d - (eccentricitySquared * sinPegLatitude * sinPegLatitude)); + double radiusEast = this.semiMajorAxis / temp; + double radiusNorth = this.semiMajorAxis * (1d - eccentricitySquared) / Math.Pow(temp, 3d); + this.radiusOfCurvature = pegHeight + + ((radiusEast * radiusNorth) / + ((radiusEast * cosPegHeading * cosPegHeading) + (radiusNorth * sinPegHeading * sinPegHeading))); + + if (Math.Abs(this.radiusOfCurvature) <= 1e-12d) + { + ArgumentGuard.ThrowArgument("sch produced a zero radius of curvature.", nameof(parameters)); + } + + var sphereParameters = new List + { + new("semi_major", this.radiusOfCurvature), + new("semi_minor", this.radiusOfCurvature), + }; + + this.sphereForward = new GeocentricTransform(sphereParameters, false); + this.sphereInverse = (GeocentricTransform)this.sphereForward.Inverse(); + + this.rotationMatrix = new Matrix3x3( + cosPegLatitude * cosPegLongitude, + (-sinPegHeading * sinPegLongitude) - (sinPegLatitude * cosPegLongitude * cosPegHeading), + (sinPegLongitude * cosPegHeading) - (sinPegLatitude * cosPegLongitude * sinPegHeading), + cosPegLatitude * sinPegLongitude, + (cosPegLongitude * sinPegHeading) - (sinPegLatitude * sinPegLongitude * cosPegHeading), + (-cosPegLongitude * cosPegHeading) - (sinPegLatitude * sinPegLongitude * sinPegHeading), + sinPegLatitude, + cosPegLatitude * cosPegHeading, + cosPegLatitude * sinPegHeading); + + double pegLongitudeDegrees = RadiansToDegrees(pegLongitude); + double pegLatitudeDegrees = RadiansToDegrees(pegLatitude); + double pegZ = pegHeight; + this.ellipsoidForward.Transform(ref pegLongitudeDegrees, ref pegLatitudeDegrees, ref pegZ); + + this.offset = new Vector3D( + pegLongitudeDegrees - (this.radiusOfCurvature * cosPegLatitude * cosPegLongitude), + pegLatitudeDegrees - (this.radiusOfCurvature * cosPegLatitude * sinPegLongitude), + pegZ - (this.radiusOfCurvature * sinPegLatitude)); + + this.isInverted = isInverted; + } + + private SchMathTransform(SchMathTransform source, bool isInverted) + { + this.ellipsoidForward = source.ellipsoidForward; + this.ellipsoidInverse = source.ellipsoidInverse; + this.sphereForward = source.sphereForward; + this.sphereInverse = source.sphereInverse; + + this.semiMajorAxis = source.semiMajorAxis; + this.radiusOfCurvature = source.radiusOfCurvature; + + this.offset = source.offset; + this.rotationMatrix = source.rotationMatrix; + + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override MathTransform Inverse() + { + this.inverse ??= new SchMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("SchMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (this.isInverted) + { + this.TransformInverse(ref x, ref y, ref z); + } + else + { + this.TransformForward(ref x, ref y, ref z); + } + } + + /// + /// Creates an from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + if (args is null) + { + skipReason = "sch arguments were null."; + return false; + } + + if (!TryGetFromArgs(args, "plat_0", out _)) + { + skipReason = "sch requires +plat_0."; + return false; + } + + if (!TryGetFromArgs(args, "plon_0", out _)) + { + skipReason = "sch requires +plon_0."; + return false; + } + + if (!TryGetFromArgs(args, "phdg_0", out _)) + { + skipReason = "sch requires +phdg_0."; + return false; + } + + double pegHeight = 0d; + if (TryGetFromArgs(args, "h_0", out double h0)) + { + pegHeight = h0; + } + + if (!ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + includeDatumToken: true, + allowClarke1880Ign: false, + allowBessel: false, + out double semiMajor, + out double semiMinor)) + { + skipReason = "Unable to resolve ellipsoid for sch."; + return false; + } + + var parameters = new List + { + new("plat_0", Parse(args["plat_0"])), + new("plon_0", Parse(args["plon_0"])), + new("phdg_0", Parse(args["phdg_0"])), + new("h_0", pegHeight), + new("semi_major", semiMajor), + new("semi_minor", semiMinor), + }; + + try + { + transform = new SchMathTransform(parameters); + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + catch (ArgumentException argumentException) + { + skipReason = argumentException.Message; + return false; + } + } + + private static bool TryGetFromArgs(Dictionary args, string key, out double value) + { + value = 0d; + return args.TryGetValue(key, out string? token) && !string.IsNullOrWhiteSpace(token) && double.TryParse(token, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value) + && !double.IsNaN(value) + && !double.IsInfinity(value); + } + + private static double Parse(string token) + { + return double.Parse(token, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture); + } + + private void TransformForward(ref double x, ref double y, ref double z) + { + double lon = x; + double lat = y; + double height = double.IsNaN(z) ? 0d : z; + this.ellipsoidForward.Transform(ref lon, ref lat, ref height); + + Vector3D global = new Vector3D(lon, lat, height) - this.offset; + + Vector3D local = this.rotationMatrix.Transpose() * global; + double localX = local.X; + double localY = local.Y; + double localZ = local.Z; + + this.sphereInverse.Transform(ref localX, ref localY, ref localZ); + + x = DegreesToRadians(localX) * this.radiusOfCurvature; + y = DegreesToRadians(localY) * this.radiusOfCurvature; + z = localZ; + } + + private void TransformInverse(ref double x, ref double y, ref double z) + { + double lon = RadiansToDegrees(x / this.radiusOfCurvature); + double lat = RadiansToDegrees(y / this.radiusOfCurvature); + double height = z; + this.sphereForward.Transform(ref lon, ref lat, ref height); + + Vector3D global = (this.rotationMatrix * new Vector3D(lon, lat, height)) + this.offset; + double globalX = global.X; + double globalY = global.Y; + double globalZ = global.Z; + + this.ellipsoidInverse.Transform(ref globalX, ref globalY, ref globalZ); + x = globalX; + y = globalY; + z = globalZ; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/SetMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/SetMathTransform.cs new file mode 100644 index 00000000..54f65338 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/SetMathTransform.cs @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +/// +/// Implements PROJ's set runtime conversion by overriding selected coordinate components. +/// +/// +/// The set operation replaces selected coordinate components with fixed +/// constants and leaves all other ordinates unchanged. This behavior is +/// intentionally idempotent, so PROJ's self-inverse convention is preserved for +/// non-identity instances. +/// +/// PROJ: set coordinate value. +internal sealed class SetMathTransform : MathTransform +{ + private static readonly MathTransform SharedIdentityInverse = new IdentityMathTransform(3); + + private readonly bool hasV1; + private readonly bool hasV2; + private readonly bool hasV3; + private readonly bool hasV4; + + private readonly double v1; + private readonly double v2; + private readonly double v3; + private readonly double v4; + + private SetMathTransform( + bool hasV1, + double v1, + bool hasV2, + double v2, + bool hasV3, + double v3, + bool hasV4, + double v4) + { + this.hasV1 = hasV1; + this.v1 = v1; + this.hasV2 = hasV2; + this.v2 = v2; + this.hasV3 = hasV3; + this.v3 = v3; + this.hasV4 = hasV4; + this.v4 = v4; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() + { + return !this.hasV1 && !this.hasV2 && !this.hasV3 && !this.hasV4; + } + + /// + public override MathTransform Inverse() + { + return this.Identity() + ? SharedIdentityInverse + : this; + } + + /// + public override void Invert() + { + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (this.hasV1) + { + x = this.v1; + } + + if (this.hasV2) + { + y = this.v2; + } + + if (this.hasV3) + { + z = this.v3; + } + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "set arguments were null."; + return false; + } + + bool hasV1 = TryGetOptionalValue(args, "v_1", out double v1, out skipReason); + if (skipReason is not null) + { + return false; + } + + bool hasV2 = TryGetOptionalValue(args, "v_2", out double v2, out skipReason); + if (skipReason is not null) + { + return false; + } + + bool hasV3 = TryGetOptionalValue(args, "v_3", out double v3, out skipReason); + if (skipReason is not null) + { + return false; + } + + bool hasV4 = TryGetOptionalValue(args, "v_4", out double v4, out skipReason); + if (skipReason is not null) + { + return false; + } + + transform = hasV1 || hasV2 || hasV3 || hasV4 + ? new SetMathTransform(hasV1, v1, hasV2, v2, hasV3, v3, hasV4, v4) + : new IdentityMathTransform(3); + return true; + } + + /// + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + this.Transform(ref x, ref y, ref z); + + if (this.hasV4) + { + t = this.v4; + } + } + + private static bool TryGetOptionalValue( + Dictionary args, + string key, + out double value, + out string? skipReason) + { + skipReason = null; + value = 0d; + if (!args.TryGetValue(key, out string? token)) + { + return false; + } + + if (!SpanParseUtility.TryParseFiniteDouble(token, out value)) + { + skipReason = $"Invalid value for +{key}."; + return false; + } + + return true; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/SpanParseUtility.cs b/src/ProjNet/CoordinateSystems/Transformations/SpanParseUtility.cs new file mode 100644 index 00000000..937803a1 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/SpanParseUtility.cs @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Globalization; + +/// +/// Represents the outcome of parsing a comma-separated numeric token. +/// +internal enum CsvParseStatus +{ + /// + /// The token was parsed successfully. + /// + Success, + + /// + /// The token contained more numeric values than the caller can accept. + /// + TooManyValues, + + /// + /// The token contained a non-finite or invalid numeric value. + /// + InvalidValue, +} + +/// +/// Provides shared span-based parsing helpers for runtime transformation arguments. +/// +internal static class SpanParseUtility +{ + /// + /// Parses a comma-separated list of doubles into the provided destination span. + /// Empty segments are ignored and surrounding whitespace is trimmed. + /// + /// Token to parse. + /// Destination span receiving parsed values. + /// Receives the number of parsed values. + /// The parse status. + internal static CsvParseStatus TryParseCsvValues(ReadOnlySpan token, Span destination, out int parsedCount) + { + parsedCount = 0; + int segmentStart = 0; + for (int i = 0; i <= token.Length; i++) + { + bool atDelimiter = i < token.Length && token[i] == ','; + if (i != token.Length && !atDelimiter) + { + continue; + } + + ReadOnlySpan segment = TrimWhitespace(token[segmentStart..i]); + if (!segment.IsEmpty) + { + if (parsedCount >= destination.Length) + { + return CsvParseStatus.TooManyValues; + } + + if (!TryParseFiniteDouble(segment, out destination[parsedCount])) + { + return CsvParseStatus.InvalidValue; + } + + parsedCount++; + } + + segmentStart = i + 1; + } + + return CsvParseStatus.Success; + } + + /// + /// Parses a string token as a finite double using invariant culture. + /// + /// Token to parse. + /// Receives the parsed value. + /// when a finite number was parsed; otherwise . + internal static bool TryParseFiniteDouble(string token, out double value) + { + value = 0d; + return !string.IsNullOrWhiteSpace(token) + && TryParseFiniteDouble(token.AsSpan(), out value); + } + + /// + /// Tries to read an optional finite double argument that only matters when present. + /// Missing or invalid values both return . + /// + /// Parsed argument dictionary. + /// Argument key without leading plus sign. + /// Receives the parsed numeric value when present and valid. + /// when the key exists and contains a finite numeric value. + internal static bool TryGetOptionalDouble(IReadOnlyDictionary args, string key, out double value) + { + value = 0d; + return args.TryGetValue(key, out string? token) + && TryParseFiniteDouble(token, out value); + } + + /// + /// Tries to read an optional finite double argument, defaulting to zero when absent and reporting invalid tokens. + /// + /// Parsed argument dictionary. + /// Argument key without leading plus sign. + /// Parsed numeric value on success. + /// Failure reason when parsing is not possible. + /// when parsing succeeded or the key is absent. + internal static bool TryGetOptionalDouble( + IReadOnlyDictionary args, + string key, + out double value, + out string? skipReason) + { + return TryGetOptionalDouble(args, key, 0d, out value, out skipReason); + } + + /// + /// Tries to read an optional finite double argument, using the provided default when the key is absent and reporting invalid tokens. + /// + /// Parsed argument dictionary. + /// Argument key without leading plus sign. + /// Fallback value when the key does not exist. + /// Parsed numeric value on success. + /// Failure reason when parsing is not possible. + /// when parsing succeeded or the key is absent. + internal static bool TryGetOptionalDouble( + IReadOnlyDictionary args, + string key, + double defaultValue, + out double value, + out string? skipReason) + { + skipReason = null; + value = defaultValue; + if (!args.TryGetValue(key, out string? token)) + { + return true; + } + + if (!TryParseFiniteDouble(token, out value)) + { + skipReason = $"Invalid value for +{key}."; + return false; + } + + return true; + } + + private static bool TryParseFiniteDouble(ReadOnlySpan token, out double value) + { +#if NETSTANDARD2_0 + bool parsed = double.TryParse(token.ToString(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value); +#else + bool parsed = double.TryParse(token, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value); +#endif + + return parsed && !double.IsNaN(value) && !double.IsInfinity(value); + } + + private static ReadOnlySpan TrimWhitespace(ReadOnlySpan value) + { + int start = 0; + while (start < value.Length && char.IsWhiteSpace(value[start])) + { + start++; + } + + int end = value.Length - 1; + while (end >= start && char.IsWhiteSpace(value[end])) + { + end--; + } + + return end < start ? [] : value.Slice(start, (end - start) + 1); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/SridPair.cs b/src/ProjNet/CoordinateSystems/Transformations/SridPair.cs new file mode 100644 index 00000000..9c6725c7 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/SridPair.cs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +/// +/// Represents a source/target SRID pair for dictionary lookups. +/// +/// The source SRID. +/// The target SRID. +internal readonly record struct SridPair(int SourceSrid, int TargetSrid) +{ +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/TinShiftMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/TinShiftMathTransform.cs new file mode 100644 index 00000000..0f583bd3 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/TinShiftMathTransform.cs @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Text.Json; + +/// +/// Implements PROJ's tinshift runtime transform. +/// +/// +/// +/// TIN-based shifts load a triangulation model from JSON, locate the triangle +/// containing the input coordinate through the in-memory spatial index, and use +/// barycentric weights to interpolate horizontal target coordinates and optional +/// vertical offsets. Supported fallback modes mirror PROJ's +/// none, nearest_side, and nearest_centroid strategies for +/// points outside the triangulated area. +/// +/// +/// The runtime was independently verified against PROJ's published +/// tinshift documentation and tinshift.cpp, including barycentric +/// interpolation and the reviewed fallback semantics. +/// +/// +/// PROJ: tinshift. +internal sealed class TinShiftMathTransform : MathTransform +{ + private const int MaximumModelSizeInBytes = 100 * 1024 * 1024; + private const double TriangleEpsilon = 1e-10d; + + private readonly TinShiftModel model; + private readonly bool isInverted; + private MathTransform? inverse; + + private TinShiftMathTransform(TinShiftModel model, bool isInverted) + { + this.model = ArgumentGuard.ThrowIfNull(model, nameof(model)); + this.isInverted = isInverted; + } + + private enum FallbackStrategy + { + None = 0, + NearestSide = 1, + NearestCentroid = 2, + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override bool Identity() => false; + + /// + public override MathTransform Inverse() + { + this.inverse ??= new TinShiftMathTransform(this.model, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("TinShiftMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (!this.TryTransformInternal(x, y, z, out double xOut, out double yOut, out double zOut)) + { + TransformationThrowHelper.ThrowInvalidOperation("tinshift transformation failed for input coordinate."); + } + + x = xOut; + y = yOut; + z = zOut; + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + + if (args is null) + { + skipReason = "tinshift arguments were null."; + return false; + } + + if (!args.TryGetValue("file", out string? fileToken) || string.IsNullOrWhiteSpace(fileToken)) + { + skipReason = "tinshift requires +file."; + return false; + } + + if (!TryResolveFilePath(fileToken, out string? resolvedPath)) + { + skipReason = $"Cannot open {fileToken}."; + return false; + } + + try + { + var fileInfo = new FileInfo(resolvedPath); + if (!fileInfo.Exists) + { + skipReason = $"Cannot open {fileToken}."; + return false; + } + + if (fileInfo.Length > MaximumModelSizeInBytes) + { + skipReason = $"File {fileToken} too large."; + return false; + } + + string jsonText = File.ReadAllText(resolvedPath); + try + { + TinShiftModel model = ParseModel(jsonText); + transform = new TinShiftMathTransform(model, false); + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + catch (FormatException exception) + { + skipReason = $"invalid model: {exception.Message}"; + return false; + } + catch (JsonException exception) + { + skipReason = $"invalid model: {exception.Message}"; + return false; + } + catch (ArgumentException exception) + { + skipReason = $"invalid model: {exception.Message}"; + return false; + } + } + catch (IOException exception) + { + skipReason = $"Cannot read {fileToken}: {exception.Message}"; + return false; + } + catch (UnauthorizedAccessException exception) + { + skipReason = $"Cannot read {fileToken}: {exception.Message}"; + return false; + } + } + + private static TinShiftModel ParseModel(string jsonText) + { + using var document = JsonDocument.Parse(jsonText); + JsonElement root = document.RootElement; + if (root.ValueKind != JsonValueKind.Object) + { + throw new FormatException("Not an object."); + } + + string fileType = GetRequiredString(root, "file_type"); + if (!string.Equals(fileType, "triangulation_file", StringComparison.Ordinal)) + { + throw new FormatException("Unsupported file_type."); + } + + string formatVersion = GetRequiredString(root, "format_version"); + FallbackStrategy fallback = FallbackStrategy.None; + if (root.TryGetProperty("fallback_strategy", out JsonElement fallbackElement)) + { + if (!string.Equals(formatVersion, "1.1", StringComparison.Ordinal)) + { + throw new FormatException("fallback_strategy needs format_version 1.1."); + } + + if (fallbackElement.ValueKind != JsonValueKind.String) + { + throw new FormatException("fallback_strategy must be a string."); + } + + string? fallbackText = fallbackElement.GetString(); + if (fallbackText is null) + { + throw new FormatException("fallback_strategy must be a string."); + } + + if (string.Equals(fallbackText, "none", StringComparison.Ordinal)) + { + fallback = FallbackStrategy.None; + } + else if (string.Equals(fallbackText, "nearest_side", StringComparison.Ordinal)) + { + fallback = FallbackStrategy.NearestSide; + } + else if (string.Equals(fallbackText, "nearest_centroid", StringComparison.Ordinal)) + { + fallback = FallbackStrategy.NearestCentroid; + } + else + { + throw new FormatException("invalid fallback_strategy."); + } + } + + (bool transformHorizontal, bool transformVertical) = ParseTransformedComponents(GetRequiredArray(root, "transformed_components")); + JsonElement verticesColumns = GetRequiredArray(root, "vertices_columns"); + VerticesColumnMap verticesMap = ParseVerticesColumnMap(verticesColumns); + ValidateVerticesColumns(transformHorizontal, transformVertical, verticesMap); + JsonElement trianglesColumns = GetRequiredArray(root, "triangles_columns"); + TriangleColumnMap triangleMap = ParseTriangleColumnMap(trianglesColumns); + + int vertexColumnCount = 2; + if (transformHorizontal) + { + vertexColumnCount += 2; + } + + if (transformVertical) + { + vertexColumnCount += 1; + } + + JsonElement verticesArray = GetRequiredArray(root, "vertices"); + double[] vertices = ParseVertices(verticesArray, verticesColumns.GetArrayLength(), vertexColumnCount, transformHorizontal, transformVertical, verticesMap); + JsonElement trianglesArray = GetRequiredArray(root, "triangles"); + List triangles = ParseTriangles(trianglesArray, trianglesColumns.GetArrayLength(), triangleMap, verticesArray.GetArrayLength()); + + return new TinShiftModel( + transformHorizontal, + transformVertical, + fallback, + vertexColumnCount, + vertices, + triangles); + } + + private static (bool TransformHorizontal, bool TransformVertical) ParseTransformedComponents(JsonElement transformedComponentsArray) + { + bool transformHorizontal = false; + bool transformVertical = false; + foreach (JsonElement component in transformedComponentsArray.EnumerateArray()) + { + if (component.ValueKind != JsonValueKind.String) + { + throw new FormatException("transformed_components[] item is not a string."); + } + + string? text = component.GetString(); + if (text is null) + { + throw new FormatException("transformed_components[] item is not a string."); + } + + if (string.Equals(text, "horizontal", StringComparison.Ordinal)) + { + transformHorizontal = true; + } + else if (string.Equals(text, "vertical", StringComparison.Ordinal)) + { + transformVertical = true; + } + else + { + throw new FormatException($"transformed_components[] = {text} is not handled."); + } + } + + return (transformHorizontal, transformVertical); + } + + private static VerticesColumnMap ParseVerticesColumnMap(JsonElement verticesColumns) + { + var result = new VerticesColumnMap + { + SourceX = -1, + SourceY = -1, + SourceZ = -1, + TargetX = -1, + TargetY = -1, + TargetZ = -1, + OffsetZ = -1, + }; + int index = 0; + foreach (JsonElement column in verticesColumns.EnumerateArray()) + { + if (column.ValueKind != JsonValueKind.String) + { + throw new FormatException("vertices_columns[] item is not a string."); + } + + string? name = column.GetString(); + if (name is null) + { + throw new FormatException("vertices_columns[] item is not a string."); + } + + if (name == "source_x") + { + result.SourceX = index; + } + else if (name == "source_y") + { + result.SourceY = index; + } + else if (name == "source_z") + { + result.SourceZ = index; + } + else if (name == "target_x") + { + result.TargetX = index; + } + else if (name == "target_y") + { + result.TargetY = index; + } + else if (name == "target_z") + { + result.TargetZ = index; + } + else if (name == "offset_z") + { + result.OffsetZ = index; + } + + index++; + } + + return result; + } + + private static void ValidateVerticesColumns(bool transformHorizontal, bool transformVertical, VerticesColumnMap map) + { + if (map.SourceX < 0) + { + throw new FormatException("source_x must be specified in vertices_columns[]."); + } + + if (map.SourceY < 0) + { + throw new FormatException("source_y must be specified in vertices_columns[]."); + } + + if (transformHorizontal) + { + if (map.TargetX < 0) + { + throw new FormatException("target_x must be specified in vertices_columns[]."); + } + + if (map.TargetY < 0) + { + throw new FormatException("target_y must be specified in vertices_columns[]."); + } + } + + if (transformVertical && map.OffsetZ < 0) + { + if (map.SourceZ < 0) + { + throw new FormatException("source_z or delta_z must be specified in vertices_columns[]."); + } + + if (map.TargetZ < 0) + { + throw new FormatException("target_z must be specified in vertices_columns[]."); + } + } + } + + private static TriangleColumnMap ParseTriangleColumnMap(JsonElement trianglesColumns) + { + var result = new TriangleColumnMap + { + Index1 = -1, + Index2 = -1, + Index3 = -1, + }; + int index = 0; + foreach (JsonElement column in trianglesColumns.EnumerateArray()) + { + if (column.ValueKind != JsonValueKind.String) + { + throw new FormatException("triangles_columns[] item is not a string."); + } + + string? name = column.GetString(); + if (name is null) + { + throw new FormatException("triangles_columns[] item is not a string."); + } + + if (name == "idx_vertex1") + { + result.Index1 = index; + } + else if (name == "idx_vertex2") + { + result.Index2 = index; + } + else if (name == "idx_vertex3") + { + result.Index3 = index; + } + + index++; + } + + if (result.Index1 < 0) + { + throw new FormatException("idx_vertex1 must be specified in triangles_columns[]."); + } + + if (result.Index2 < 0) + { + throw new FormatException("idx_vertex2 must be specified in triangles_columns[]."); + } + + return result.Index3 < 0 ? throw new FormatException("idx_vertex3 must be specified in triangles_columns[].") : result; + } + + private static double[] ParseVertices( + JsonElement verticesArray, + int inputVertexColumnCount, + int runtimeVertexColumnCount, + bool transformHorizontal, + bool transformVertical, + VerticesColumnMap map) + { + double[] vertices = new double[verticesArray.GetArrayLength() * runtimeVertexColumnCount]; + int outputOffset = 0; + foreach (JsonElement vertex in verticesArray.EnumerateArray()) + { + if (vertex.ValueKind != JsonValueKind.Array) + { + throw new FormatException("vertices[] item is not an array."); + } + + if (vertex.GetArrayLength() != inputVertexColumnCount) + { + throw new FormatException("vertices[] item has not expected number of elements."); + } + + double sourceX = ReadNumber(vertex, map.SourceX, "vertices[][] item is not a number."); + double sourceY = ReadNumber(vertex, map.SourceY, "vertices[][] item is not a number."); + vertices[outputOffset++] = sourceX; + vertices[outputOffset++] = sourceY; + + if (transformHorizontal) + { + double targetX = ReadNumber(vertex, map.TargetX, "vertices[][] item is not a number."); + double targetY = ReadNumber(vertex, map.TargetY, "vertices[][] item is not a number."); + vertices[outputOffset++] = targetX; + vertices[outputOffset++] = targetY; + } + + if (transformVertical) + { + if (map.OffsetZ >= 0) + { + double offsetZ = ReadNumber(vertex, map.OffsetZ, "vertices[][] item is not a number."); + vertices[outputOffset++] = offsetZ; + } + else + { + double sourceZ = ReadNumber(vertex, map.SourceZ, "vertices[][] item is not a number."); + double targetZ = ReadNumber(vertex, map.TargetZ, "vertices[][] item is not a number."); + vertices[outputOffset++] = targetZ - sourceZ; + } + } + } + + return vertices; + } + + private static List ParseTriangles( + JsonElement trianglesArray, + int inputTriangleColumnCount, + TriangleColumnMap map, + int vertexCount) + { + var triangles = new List(trianglesArray.GetArrayLength()); + foreach (JsonElement triangle in trianglesArray.EnumerateArray()) + { + if (triangle.ValueKind != JsonValueKind.Array) + { + throw new FormatException("triangles[] item is not an array."); + } + + if (triangle.GetArrayLength() != inputTriangleColumnCount) + { + throw new FormatException("triangles[] item has not expected number of elements."); + } + + int idx1 = ReadNonNegativeInteger(triangle, map.Index1, "triangles[][] item is not an integer."); + int idx2 = ReadNonNegativeInteger(triangle, map.Index2, "triangles[][] item is not an integer."); + int idx3 = ReadNonNegativeInteger(triangle, map.Index3, "triangles[][] item is not an integer."); + + if (idx1 >= vertexCount || idx2 >= vertexCount || idx3 >= vertexCount) + { + throw new FormatException("Invalid value for a vertex index."); + } + + triangles.Add(new TriangleIndices(idx1, idx2, idx3)); + } + + return triangles; + } + + private static bool TryResolveFilePath(string fileToken, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(fileToken)) + { + return false; + } + + string normalized = NormalizePathToken(fileToken); + if (TryGetExistingPath(normalized, out resolvedPath)) + { + return true; + } + + string appBaseCandidate = Path.Combine(AppContext.BaseDirectory, normalized); + if (TryGetExistingPath(appBaseCandidate, out resolvedPath)) + { + return true; + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(fileToken, out resolvedPath)) + { + return true; + } + + if (!string.Equals(normalized, fileToken, StringComparison.Ordinal) + && CoordinateTransformationFactory.TryResolveGridResourcePath(normalized, out resolvedPath)) + { + return true; + } + + string fileName = Path.GetFileName(normalized); + if (!string.IsNullOrWhiteSpace(fileName)) + { + string fixtureCandidate = Path.Combine(AppContext.BaseDirectory, "Fixtures", "tinshift", fileName); + if (TryGetExistingPath(fixtureCandidate, out resolvedPath)) + { + return true; + } + + if (CoordinateTransformationFactory.TryResolveGridResourcePath(fileName, out resolvedPath)) + { + return true; + } + } + + return false; + } + + private static bool TryGetExistingPath(string candidate, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(candidate)) + { + return false; + } + + if (!Path.IsPathRooted(candidate)) + { + candidate = Path.GetFullPath(candidate); + } + + if (!File.Exists(candidate)) + { + return false; + } + + resolvedPath = candidate; + return true; + } + + private static string NormalizePathToken(string token) + { + return token.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + + private static JsonElement GetRequiredArray(JsonElement parent, string propertyName) + { + if (!parent.TryGetProperty(propertyName, out JsonElement value)) + { + throw new FormatException($"Missing \"{propertyName}\" key."); + } + + return value.ValueKind != JsonValueKind.Array + ? throw new FormatException($"The value of \"{propertyName}\" should be a array.") + : value; + } + + private static string GetRequiredString(JsonElement parent, string propertyName) + { + if (!parent.TryGetProperty(propertyName, out JsonElement value)) + { + throw new FormatException($"Missing \"{propertyName}\" key."); + } + + if (value.ValueKind != JsonValueKind.String) + { + throw new FormatException($"The value of \"{propertyName}\" should be a string."); + } + + string? text = value.GetString(); + return text is null ? throw new FormatException($"The value of \"{propertyName}\" should be a string.") : text; + } + + private static double ReadNumber(JsonElement array, int index, string errorMessage) + { + JsonElement value = array[index]; + return value.ValueKind != JsonValueKind.Number ? throw new FormatException(errorMessage) : value.GetDouble(); + } + + private static int ReadNonNegativeInteger(JsonElement array, int index, string errorMessage) + { + JsonElement value = array[index]; + return value.ValueKind != JsonValueKind.Number || !value.TryGetInt32(out int intValue) || intValue < 0 + ? throw new FormatException(errorMessage) + : intValue; + } + + private static bool TryComputeBarycentricCoordinates( + double x, + double y, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + out double lambda1, + out double lambda2, + out double lambda3) + { + double detT = ((y2 - y3) * (x1 - x3)) + ((x3 - x2) * (y1 - y3)); + if (Math.Abs(detT) < TriangleEpsilon) + { + lambda1 = 0d; + lambda2 = 0d; + lambda3 = 0d; + return false; + } + + lambda1 = (((y2 - y3) * (x - x3)) + ((x3 - x2) * (y - y3))) / detT; + lambda2 = (((y3 - y1) * (x - x3)) + ((x1 - x3) * (y - y3))) / detT; + lambda3 = 1d - lambda1 - lambda2; + return true; + } + + private static bool IsInsideTriangle(double lambda1, double lambda2, double lambda3) + { + return lambda1 >= -TriangleEpsilon + && lambda1 <= (1d + TriangleEpsilon) + && lambda2 >= -TriangleEpsilon + && lambda2 <= (1d + TriangleEpsilon) + && lambda3 >= -TriangleEpsilon; + } + + private static double SquaredDistance(double x1, double y1, double x2, double y2) + { + double dx = x1 - x2; + double dy = y1 - y2; + return (dx * dx) + (dy * dy); + } + + private static double DistancePointSegmentSquared( + double x, + double y, + double x1, + double y1, + double x2, + double y2, + double segmentLengthSquared) + { + double t = (((x - x1) * (x2 - x1)) + ((y - y1) * (y2 - y1))) / segmentLengthSquared; + if (t <= 0d) + { + return SquaredDistance(x, y, x1, y1); + } + + if (t >= 1d) + { + return SquaredDistance(x, y, x2, y2); + } + + double projectedX = x1 + (t * (x2 - x1)); + double projectedY = y1 + (t * (y2 - y1)); + return SquaredDistance(x, y, projectedX, projectedY); + } + + private bool TryTransformInternal(double x, double y, double z, out double xOut, out double yOut, out double zOut) + { + bool forward = !this.isInverted; + if (!this.TryFindTriangle( + x, + y, + forward, + out TriangleIndices triangle, + out double lambda1, + out double lambda2, + out double lambda3)) + { + xOut = double.NaN; + yOut = double.NaN; + zOut = double.NaN; + return false; + } + + int idx1 = triangle.Index1; + int idx2 = triangle.Index2; + int idx3 = triangle.Index3; + + int sourceXIndex = 0; + int sourceYIndex = 1; + int targetXIndex = this.model.TransformHorizontal ? 2 : 0; + int targetYIndex = this.model.TransformHorizontal ? 3 : 1; + int offsetZIndex = this.model.TransformHorizontal ? 4 : 2; + + if (this.model.TransformHorizontal) + { + int readXIndex = forward ? targetXIndex : sourceXIndex; + int readYIndex = forward ? targetYIndex : sourceYIndex; + xOut = (this.model.GetVertexValue(idx1, readXIndex) * lambda1) + + (this.model.GetVertexValue(idx2, readXIndex) * lambda2) + + (this.model.GetVertexValue(idx3, readXIndex) * lambda3); + yOut = (this.model.GetVertexValue(idx1, readYIndex) * lambda1) + + (this.model.GetVertexValue(idx2, readYIndex) * lambda2) + + (this.model.GetVertexValue(idx3, readYIndex) * lambda3); + } + else + { + xOut = x; + yOut = y; + } + + if (this.model.TransformVertical) + { + double zOffset = (this.model.GetVertexValue(idx1, offsetZIndex) * lambda1) + + (this.model.GetVertexValue(idx2, offsetZIndex) * lambda2) + + (this.model.GetVertexValue(idx3, offsetZIndex) * lambda3); + zOut = forward + ? z + zOffset + : z - zOffset; + } + else + { + zOut = z; + } + + return true; + } + + private bool TryFindTriangle( + double x, + double y, + bool forward, + out TriangleIndices triangle, + out double lambda1, + out double lambda2, + out double lambda3) + { + for (int i = 0; i < this.model.Triangles.Count; i++) + { + TriangleIndices candidate = this.model.Triangles[i]; + if (!this.TryComputeTriangleLambdas(candidate, x, y, forward, out double l1, out double l2, out double l3)) + { + continue; + } + + if (IsInsideTriangle(l1, l2, l3)) + { + triangle = candidate; + lambda1 = l1; + lambda2 = l2; + lambda3 = l3; + return true; + } + } + + if (this.model.Fallback == FallbackStrategy.None) + { + triangle = default; + lambda1 = 0d; + lambda2 = 0d; + lambda3 = 0d; + return false; + } + + return this.TryFindFallbackTriangle(x, y, forward, out triangle, out lambda1, out lambda2, out lambda3); + } + + private bool TryFindFallbackTriangle( + double x, + double y, + bool forward, + out TriangleIndices triangle, + out double lambda1, + out double lambda2, + out double lambda3) + { + double bestDistanceSquared = double.PositiveInfinity; + TriangleIndices bestTriangle = default; + bool found = false; + + for (int i = 0; i < this.model.Triangles.Count; i++) + { + TriangleIndices candidate = this.model.Triangles[i]; + this.GetTriangleCoordinates(candidate, forward, out double x1, out double y1, out double x2, out double y2, out double x3, out double y3); + + double d12 = SquaredDistance(x1, y1, x2, y2); + double d23 = SquaredDistance(x2, y2, x3, y3); + double d13 = SquaredDistance(x1, y1, x3, y3); + if (d12 < TriangleEpsilon || d23 < TriangleEpsilon || d13 < TriangleEpsilon) + { + continue; + } + + double distanceSquared = this.model.Fallback == FallbackStrategy.NearestSide + ? Math.Min( + DistancePointSegmentSquared(x, y, x1, y1, x2, y2, d12), + Math.Min( + DistancePointSegmentSquared(x, y, x2, y2, x3, y3, d23), + DistancePointSegmentSquared(x, y, x1, y1, x3, y3, d13))) + : SquaredDistance(x, y, (x1 + x2 + x3) / 3d, (y1 + y2 + y3) / 3d); + + if (distanceSquared < bestDistanceSquared) + { + bestDistanceSquared = distanceSquared; + bestTriangle = candidate; + found = true; + } + } + + if (!found) + { + triangle = default; + lambda1 = 0d; + lambda2 = 0d; + lambda3 = 0d; + return false; + } + + if (!this.TryComputeTriangleLambdas(bestTriangle, x, y, forward, out lambda1, out lambda2, out lambda3)) + { + triangle = default; + lambda1 = 0d; + lambda2 = 0d; + lambda3 = 0d; + return false; + } + + triangle = bestTriangle; + return true; + } + + private bool TryComputeTriangleLambdas( + TriangleIndices triangle, + double x, + double y, + bool forward, + out double lambda1, + out double lambda2, + out double lambda3) + { + this.GetTriangleCoordinates( + triangle, + forward, + out double x1, + out double y1, + out double x2, + out double y2, + out double x3, + out double y3); + + return TryComputeBarycentricCoordinates( + x, + y, + x1, + y1, + x2, + y2, + x3, + y3, + out lambda1, + out lambda2, + out lambda3); + } + + private void GetTriangleCoordinates( + TriangleIndices triangle, + bool forward, + out double x1, + out double y1, + out double x2, + out double y2, + out double x3, + out double y3) + { + int baseXIndex = 0; + int baseYIndex = 1; + int transformedXIndex = this.model.TransformHorizontal ? 2 : 0; + int transformedYIndex = this.model.TransformHorizontal ? 3 : 1; + int xIndex = this.model.TransformHorizontal && !forward + ? transformedXIndex + : baseXIndex; + int yIndex = this.model.TransformHorizontal && !forward + ? transformedYIndex + : baseYIndex; + + x1 = this.model.GetVertexValue(triangle.Index1, xIndex); + y1 = this.model.GetVertexValue(triangle.Index1, yIndex); + x2 = this.model.GetVertexValue(triangle.Index2, xIndex); + y2 = this.model.GetVertexValue(triangle.Index2, yIndex); + x3 = this.model.GetVertexValue(triangle.Index3, xIndex); + y3 = this.model.GetVertexValue(triangle.Index3, yIndex); + } + + private readonly struct TriangleIndices(int index1, int index2, int index3) + { + internal int Index1 { get; } = index1; + + internal int Index2 { get; } = index2; + + internal int Index3 { get; } = index3; + } + + private struct VerticesColumnMap + { + internal int SourceX; + internal int SourceY; + internal int SourceZ; + internal int TargetX; + internal int TargetY; + internal int TargetZ; + internal int OffsetZ; + } + + private struct TriangleColumnMap + { + internal int Index1; + internal int Index2; + internal int Index3; + } + + private sealed class TinShiftModel + { + internal TinShiftModel( + bool transformHorizontal, + bool transformVertical, + FallbackStrategy fallback, + int vertexColumnCount, + double[] vertices, + List triangles) + { + this.Vertices = ArgumentGuard.ThrowIfNull(vertices, nameof(vertices)); + this.Triangles = ArgumentGuard.ThrowIfNull(triangles, nameof(triangles)); + this.TransformHorizontal = transformHorizontal; + this.TransformVertical = transformVertical; + this.Fallback = fallback; + this.VertexColumnCount = vertexColumnCount; + } + + internal bool TransformHorizontal { get; } + + internal bool TransformVertical { get; } + + internal FallbackStrategy Fallback { get; } + + internal int VertexColumnCount { get; } + + internal double[] Vertices { get; } + + internal List Triangles { get; } + + internal double GetVertexValue(int vertexIndex, int columnIndex) + { + int baseOffset = vertexIndex * this.VertexColumnCount; + return this.Vertices[baseOffset + columnIndex]; + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/TopocentricMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/TopocentricMathTransform.cs new file mode 100644 index 00000000..67413063 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/TopocentricMathTransform.cs @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems; + +/// +/// Implements PROJ's topocentric 3D runtime conversion. +/// +/// +/// +/// This runtime converts between geocentric XYZ coordinates and a local +/// topocentric east-north-up frame anchored at a specified origin. The origin +/// can be supplied directly in geocentric coordinates or derived from +/// geographic lon_0, lat_0, and h_0 parameters through the +/// companion geocentric conversion. +/// +/// +/// The formulas and axis orientation were independently verified against PROJ's +/// published topocentric documentation, which ties the operation to the +/// IOGP geocentric/topocentric formulas and EPSG methods 9836 and 9837. +/// +/// +/// PROJ: topocentric. +/// EPSG method 9836: Geocentric/topocentric conversions. +/// EPSG method 9837: Geographic/topocentric conversions. +internal sealed class TopocentricMathTransform : MathTransform +{ + private readonly double originX; + private readonly double originY; + private readonly double originZ; + private readonly double sinPhi0; + private readonly double cosPhi0; + private readonly double sinLam0; + private readonly double cosLam0; + + private readonly bool isInverted; + private MathTransform? inverse; + + private TopocentricMathTransform( + double originX, + double originY, + double originZ, + double phi0Radians, + double lam0Radians, + bool isInverted) + { + this.originX = originX; + this.originY = originY; + this.originZ = originZ; + this.sinPhi0 = Math.Sin(phi0Radians); + this.cosPhi0 = Math.Cos(phi0Radians); + this.sinLam0 = Math.Sin(lam0Radians); + this.cosLam0 = Math.Cos(lam0Radians); + this.isInverted = isInverted; + } + + private TopocentricMathTransform(TopocentricMathTransform source, bool isInverted) + { + this.originX = source.originX; + this.originY = source.originY; + this.originZ = source.originZ; + this.sinPhi0 = source.sinPhi0; + this.cosPhi0 = source.cosPhi0; + this.sinLam0 = source.sinLam0; + this.cosLam0 = source.cosLam0; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override MathTransform Inverse() + { + this.inverse ??= new TopocentricMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("TopocentricMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (double.IsNaN(z)) + { + z = 0d; + } + + if (this.isInverted) + { + this.TransformInverse(ref x, ref y, ref z); + } + else + { + this.TransformForward(ref x, ref y, ref z); + } + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + skipReason = null; + if (args is null) + { + skipReason = "topocentric arguments were null."; + return false; + } + + bool hasX0 = args.ContainsKey("X_0"); + bool hasY0 = args.ContainsKey("Y_0"); + bool hasZ0 = args.ContainsKey("Z_0"); + bool hasLon0 = args.ContainsKey("lon_0"); + bool hasLat0 = args.ContainsKey("lat_0"); + bool hasH0 = args.ContainsKey("h_0"); + + if (!hasX0 && !hasLon0) + { + skipReason = "missing X_0 or lon_0"; + return false; + } + + if ((hasX0 || hasY0 || hasZ0) && (hasLon0 || hasLat0 || hasH0)) + { + skipReason = "(X_0,Y_0,Z_0) and (lon_0,lat_0,h_0) are mutually exclusive"; + return false; + } + + if (hasX0 && (!hasY0 || !hasZ0)) + { + skipReason = "missing Y_0 and/or Z_0"; + return false; + } + + if (hasLon0 && !hasLat0) + { + skipReason = "missing lat_0"; + return false; + } + + if (!ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + includeDatumToken: true, + allowClarke1880Ign: false, + allowBessel: false, + out double semiMajor, + out double semiMinor)) + { + skipReason = "Unable to resolve ellipsoid for topocentric."; + return false; + } + + var ellipsoidParameters = new List + { + new("semi_major", semiMajor), + new("semi_minor", semiMinor), + }; + + var geocForward = new GeocentricTransform(ellipsoidParameters, false); + var geocInverse = (GeocentricTransform)geocForward.Inverse(); + if (hasX0) + { + if (!TryGetRequiredDouble(args, "X_0", out double originX) + || !TryGetRequiredDouble(args, "Y_0", out double originY) + || !TryGetRequiredDouble(args, "Z_0", out double originZ)) + { + skipReason = "Unable to parse X_0/Y_0/Z_0 for topocentric."; + return false; + } + + double lon0Degrees = originX; + double lat0Degrees = originY; + double h0 = originZ; + geocInverse.Transform(ref lon0Degrees, ref lat0Degrees, ref h0); + + transform = new TopocentricMathTransform( + originX, + originY, + originZ, + DegreesToRadians(lat0Degrees), + DegreesToRadians(lon0Degrees), + false); + } + else + { + if (!TryGetRequiredDouble(args, "lon_0", out double lon0Degrees) + || !TryGetRequiredDouble(args, "lat_0", out double lat0Degrees)) + { + skipReason = "Unable to parse lon_0/lat_0 for topocentric."; + return false; + } + + double h0 = 0d; + if (hasH0 && !TryGetRequiredDouble(args, "h_0", out h0)) + { + skipReason = "Unable to parse h_0 for topocentric."; + return false; + } + + double originX = lon0Degrees; + double originY = lat0Degrees; + double originZ = h0; + geocForward.Transform(ref originX, ref originY, ref originZ); + + transform = new TopocentricMathTransform( + originX, + originY, + originZ, + DegreesToRadians(lat0Degrees), + DegreesToRadians(lon0Degrees), + false); + } + + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } + + private static bool TryGetRequiredDouble(Dictionary args, string key, out double value) + { + value = 0d; + return args.TryGetValue(key, out string? token) + && SpanParseUtility.TryParseFiniteDouble(token, out value); + } + + private void TransformForward(ref double x, ref double y, ref double z) + { + double dX = x - this.originX; + double dY = y - this.originY; + double dZ = z - this.originZ; + + double outX = (-dX * this.sinLam0) + (dY * this.cosLam0); + double outY = (-dX * this.sinPhi0 * this.cosLam0) - (dY * this.sinPhi0 * this.sinLam0) + (dZ * this.cosPhi0); + double outZ = (dX * this.cosPhi0 * this.cosLam0) + (dY * this.cosPhi0 * this.sinLam0) + (dZ * this.sinPhi0); + + x = outX; + y = outY; + z = outZ; + } + + private void TransformInverse(ref double x, ref double y, ref double z) + { + double inX = x; + double inY = y; + double inZ = z; + + double outX = this.originX - (inX * this.sinLam0) - (inY * this.sinPhi0 * this.cosLam0) + (inZ * this.cosPhi0 * this.cosLam0); + double outY = this.originY + (inX * this.cosLam0) - (inY * this.sinPhi0 * this.sinLam0) + (inZ * this.cosPhi0 * this.sinLam0); + double outZ = this.originZ + (inY * this.cosPhi0) + (inZ * this.sinPhi0); + + x = outX; + y = outY; + z = outZ; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/TransformType.cs b/src/ProjNet/CoordinateSystems/Transformations/TransformType.cs index 287e1cbb..aca686a2 100644 --- a/src/ProjNet/CoordinateSystems/Transformations/TransformType.cs +++ b/src/ProjNet/CoordinateSystems/Transformations/TransformType.cs @@ -1,45 +1,31 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems.Transformations; -namespace ProjNet.CoordinateSystems.Transformations +/// +/// Semantic type of transform used in coordinate transformation. +/// +public enum TransformType : int { /// - /// Semantic type of transform used in coordinate transformation. + /// Unknown or unspecified type of transform. /// - public enum TransformType : int - { - /// - /// Unknown or unspecified type of transform. - /// - Other = 0, + Other = 0, - /// - /// Transform depends only on defined parameters. For example, a cartographic projection. - /// - Conversion = 1, + /// + /// Transform depends only on defined parameters. For example, a cartographic projection. + /// + Conversion = 1, - /// - /// Transform depends only on empirically derived parameters. For example a datum transformation. - /// - Transformation = 2, + /// + /// Transform depends only on empirically derived parameters. For example a datum transformation. + /// + Transformation = 2, - /// - /// Transform depends on both defined and empirical parameters. - /// - ConversionAndTransformation = 3 - } + /// + /// Transform depends on both defined and empirical parameters. + /// + ConversionAndTransformation = 3, } diff --git a/src/ProjNet/CoordinateSystems/Transformations/TransformationMath.cs b/src/ProjNet/CoordinateSystems/Transformations/TransformationMath.cs new file mode 100644 index 00000000..035efcef --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/TransformationMath.cs @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Shared numeric helper methods used by transformation runtime implementations. +/// +internal static class TransformationMath +{ + /// + /// Conversion factor from arc-seconds to radians. + /// + internal const double ArcSecondToRadians = Math.PI / (180d * 3600d); + + /// + /// Sentinel value used when an observation epoch was not supplied. + /// + internal const double MissingObservationEpoch = double.MaxValue; + + /// + /// Lowest valid scale-difference value expressed in parts per million. + /// + internal const double MinValidPpmScale = -1e6d; + + /// + /// Sentinel value used by GTX grids for nodata samples. + /// + internal const float GtxNoDataSentinel = -88.88880f; + + /// + /// Conversion factor from U.S. survey feet to metres. + /// + internal const double MetresPerUsSurveyFoot = 1200d / 3937d; + + /// + /// Default iteration cap for inverse grid or deformation refinement loops. + /// + internal const int MaxInverseIterations = 10; + + /// + /// Determines whether a floating-point value is finite. + /// + /// Value to validate. + /// when the value is neither NaN nor infinity. + internal static bool IsFinite(double value) + { + return !double.IsNaN(value) && !double.IsInfinity(value); + } + + /// + /// Determines whether an observation epoch value is valid. + /// + /// Observation epoch to validate. + /// Sentinel value used for missing observation epoch. + /// when the epoch is finite and not equal to the missing sentinel value. + internal static bool IsValidObservationEpoch(double epoch, double missingObservationEpoch) + { + return IsFinite(epoch) && epoch != missingObservationEpoch; + } + + /// + /// Normalizes a longitude in degrees to the inclusive range [-180, 180]. + /// + /// Longitude in degrees. + /// Normalized longitude in degrees. + internal static double NormalizeLongitudeDegrees(double longitude) + { + double normalized = longitude; + while (normalized < -180d) + { + normalized += 360d; + } + + while (normalized > 180d) + { + normalized -= 360d; + } + + return normalized; + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/TransformationThrowHelper.cs b/src/ProjNet/CoordinateSystems/Transformations/TransformationThrowHelper.cs new file mode 100644 index 00000000..f9d06b64 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/TransformationThrowHelper.cs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Diagnostics.CodeAnalysis; + +/// +/// Provides cold throw paths for transformation runtime failures. +/// +internal static class TransformationThrowHelper +{ + /// + /// Throws an for runtime transformation failures. + /// + /// The failure message. + [DoesNotReturn] + internal static void ThrowInvalidOperation(string message) + { + throw new InvalidOperationException(message); + } + + /// + /// Throws an for runtime transformation failures from expression contexts. + /// + /// The nominal return type. + /// The failure message. + /// Never returns. + [DoesNotReturn] + internal static T ThrowInvalidOperation(string message) + { + throw new InvalidOperationException(message); + } + + /// + /// Throws a for unsupported transformation operations. + /// + /// The failure message. + [DoesNotReturn] + internal static void ThrowNotSupported(string message) + { + throw new NotSupportedException(message); + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/UnitConvertMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/UnitConvertMathTransform.cs new file mode 100644 index 00000000..5dbb6844 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/UnitConvertMathTransform.cs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; + +/// +/// Applies linear unit-conversion scale factors to the X, Y, and optionally Z ordinates of a coordinate. +/// +/// +/// Unit conversion is a pure scaling transform: x *= scale, +/// y *= scale, and for 3D coordinates z *= zScale. Construction +/// rejects non-positive or non-finite scale factors so the inverse always exists. +/// +/// PROJ: unit conversion. +internal sealed class UnitConvertMathTransform : MathTransform +{ + private readonly int dimension; + private double xyScale; + private double zScale; + + /// + /// Initializes a new instance of the class for 3D coordinates. + /// + /// Scale factor applied to X and Y ordinates. + /// Scale factor applied to Z ordinate. + internal UnitConvertMathTransform(double xyScale, double zScale) + : this(3, xyScale, zScale) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Coordinate dimension (2 or 3). + /// Scale factor applied to X and Y ordinates. + /// Scale factor applied to Z ordinate when dimension is 3. + internal UnitConvertMathTransform(int dimension, double xyScale, double zScale) + { + this.dimension = ValidateDimension(dimension, nameof(dimension)); + ValidateScale(xyScale, nameof(xyScale)); + ValidateScale(zScale, nameof(zScale)); + + this.xyScale = xyScale; + this.zScale = zScale; + } + + /// + public override int DimSource => this.dimension; + + /// + public override int DimTarget => this.dimension; + + /// + public override bool Identity() + { + bool xyIdentity = this.xyScale.Equals(1d); + return this.dimension < 3 ? xyIdentity : xyIdentity && this.zScale.Equals(1d); + } + + /// + public override MathTransform Inverse() + { + return new UnitConvertMathTransform(this.dimension, 1d / this.xyScale, 1d / this.zScale); + } + + /// + public override void Invert() + { + this.xyScale = 1d / this.xyScale; + this.zScale = 1d / this.zScale; + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + x *= this.xyScale; + y *= this.xyScale; + if (this.dimension > 2) + { + z *= this.zScale; + } + } + + private static int ValidateDimension(int dimension, string parameterName) + { + if (dimension < 2 || dimension > 3) + { + ArgumentGuard.ThrowArgumentOutOfRange(parameterName, dimension, "Unit conversion dimension must be either 2 or 3."); + } + + return dimension; + } + + private static void ValidateScale(double scale, string parameterName) + { + if (scale <= 0d || double.IsNaN(scale) || double.IsInfinity(scale)) + { + ArgumentGuard.ThrowArgumentOutOfRange(parameterName, scale, "Scale must be finite and positive."); + } + } +} diff --git a/src/ProjNet/CoordinateSystems/Transformations/VertOffsetMathTransform.cs b/src/ProjNet/CoordinateSystems/Transformations/VertOffsetMathTransform.cs new file mode 100644 index 00000000..68a2c307 --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Transformations/VertOffsetMathTransform.cs @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ProjNet.CoordinateSystems; + +/// +/// Implements PROJ's vertoffset runtime transform (Vertical Offset and Slope). +/// +/// +/// This runtime applies the EPSG Vertical Offset and Slope model: a constant +/// vertical offset plus latitude- and longitude-dependent slope terms scaled by +/// the local meridional and prime-vertical radii of curvature at the origin. +/// +/// EPSG method 9657: Vertical Offset and Slope. +internal sealed class VertOffsetMathTransform : MathTransform +{ + private readonly double latOriginRadians; + private readonly double lonOriginDegrees; + private readonly double verticalOffset; + private readonly double slopeLatRadians; + private readonly double slopeLonRadians; + private readonly double rho0; + private readonly double nu0; + + private readonly bool isInverted; + private MathTransform? inverse; + + private VertOffsetMathTransform( + double semiMajor, + double semiMinor, + double latOriginDegrees, + double lonOriginDegrees, + double verticalOffset, + double slopeLatArcSeconds, + double slopeLonArcSeconds, + bool isInverted) + { + if (semiMajor <= 0d || double.IsNaN(semiMajor) || double.IsInfinity(semiMajor)) + { + ArgumentGuard.ThrowArgument("vertoffset requires a positive finite semi-major axis.", nameof(semiMajor)); + } + + if (semiMinor <= 0d || double.IsNaN(semiMinor) || double.IsInfinity(semiMinor)) + { + ArgumentGuard.ThrowArgument("vertoffset requires a positive finite semi-minor axis.", nameof(semiMinor)); + } + + this.latOriginRadians = DegreesToRadians(latOriginDegrees); + this.lonOriginDegrees = lonOriginDegrees; + this.verticalOffset = verticalOffset; + this.slopeLatRadians = slopeLatArcSeconds * TransformationMath.ArcSecondToRadians; + this.slopeLonRadians = slopeLonArcSeconds * TransformationMath.ArcSecondToRadians; + + double eccentricitySquared = 1d - ((semiMinor * semiMinor) / (semiMajor * semiMajor)); + double sinLat0 = Math.Sin(this.latOriginRadians); + double oneMinusEsSinLat0Square = 1d - (eccentricitySquared * sinLat0 * sinLat0); + if (oneMinusEsSinLat0Square <= 0d) + { + TransformationThrowHelper.ThrowInvalidOperation("vertoffset produced invalid ellipsoid curvature terms."); + } + + double sqrtDenominator = Math.Sqrt(oneMinusEsSinLat0Square); + this.rho0 = semiMajor * (1d - eccentricitySquared) / (oneMinusEsSinLat0Square * sqrtDenominator); + this.nu0 = semiMajor / sqrtDenominator; + this.isInverted = isInverted; + } + + private VertOffsetMathTransform(VertOffsetMathTransform source, bool isInverted) + { + this.latOriginRadians = source.latOriginRadians; + this.lonOriginDegrees = source.lonOriginDegrees; + this.verticalOffset = source.verticalOffset; + this.slopeLatRadians = source.slopeLatRadians; + this.slopeLonRadians = source.slopeLonRadians; + this.rho0 = source.rho0; + this.nu0 = source.nu0; + this.isInverted = isInverted; + } + + /// + public override int DimSource => 3; + + /// + public override int DimTarget => 3; + + /// + public override MathTransform Inverse() + { + this.inverse ??= new VertOffsetMathTransform(this, !this.isInverted); + + return this.inverse; + } + + /// + public override void Invert() + { + throw new NotSupportedException("VertOffsetMathTransform is immutable. Use Inverse() to obtain inverted transform."); + } + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + if (double.IsNaN(z)) + { + z = 0d; + } + + double phi = DegreesToRadians(y); + double lam = DegreesToRadians(x - this.lonOriginDegrees); + double offset = this.verticalOffset + + (this.slopeLatRadians * this.rho0 * (phi - this.latOriginRadians)) + + (this.slopeLonRadians * this.nu0 * lam * Math.Cos(phi)); + + if (this.isInverted) + { + z -= offset; + } + else + { + z += offset; + } + } + + /// + /// Creates a from parsed PROJ arguments. + /// + /// Parsed PROJ argument dictionary. + /// Created transform instance on success. + /// Failure reason when creation is not possible. + /// when a transform was created. + internal static bool TryCreate( + Dictionary args, + [NotNullWhen(true)] out MathTransform? transform, + out string? skipReason) + { + transform = null; + if (args is null) + { + skipReason = "vertoffset arguments were null."; + return false; + } + + if (!SpanParseUtility.TryGetOptionalDouble(args, "lat_0", 0d, out double lat0, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "lon_0", 0d, out double lon0, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "dh", 0d, out double dh, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "slope_lat", 0d, out double slopeLat, out skipReason) + || !SpanParseUtility.TryGetOptionalDouble(args, "slope_lon", 0d, out double slopeLon, out skipReason)) + { + return false; + } + + if (!ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + includeDatumToken: true, + allowClarke1880Ign: false, + allowBessel: false, + out double semiMajor, + out double semiMinor)) + { + skipReason = "Unable to resolve ellipsoid for vertoffset."; + return false; + } + + try + { + transform = new VertOffsetMathTransform( + semiMajor, + semiMinor, + lat0, + lon0, + dh, + slopeLat, + slopeLon, + false); + } + catch (ArgumentException exception) + { + skipReason = exception.Message; + return false; + } + + if (args.ContainsKey("inv")) + { + transform = transform.Inverse(); + } + + return true; + } +} diff --git a/src/ProjNet/CoordinateSystems/Unit.cs b/src/ProjNet/CoordinateSystems/Unit.cs index b69eaad2..294d4a08 100644 --- a/src/ProjNet/CoordinateSystems/Unit.cs +++ b/src/ProjNet/CoordinateSystems/Unit.cs @@ -1,104 +1,113 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; +using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// Class for defining units. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// Derived predefined unit accessors are thread-safe because they only expose immutable value objects. +/// +/// +public class Unit : Info, IUnit { - /// - /// Class for defining units + /// + /// Initializes a new instance of the class. /// - [Serializable] - public class Unit : Info, IUnit + /// Conversion factor to base unit. + /// Name of unit. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + internal Unit(double conversionFactor, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) + : base(name, authority, authorityCode, alias, abbreviation, remarks) { - /// - /// Initializes a new unit - /// - /// Conversion factor to base unit - /// Name of unit - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - internal Unit(double conversionFactor, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) - : - base(name, authority, authorityCode, alias, abbreviation, remarks) - { - ConversionFactor = conversionFactor; - } + this.ConversionFactor = conversionFactor; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of unit. + /// Conversion factor to base unit. + internal Unit(string name, double conversionFactor) + : this(conversionFactor, name, string.Empty, -1, string.Empty, string.Empty, string.Empty) + { + } + + /// + /// Gets the number of units per base-unit. + /// + public double ConversionFactor { get; } + + /// + /// Gets the Well-known text for this object + /// as defined in the simple features specification. + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + /// Gets an XML representation of this object. + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); - /// - /// Initializes a new unit - /// - /// Name of unit - /// Conversion factor to base unit - internal Unit(string name, double conversionFactor) - : this(conversionFactor, name, string.Empty, -1, string.Empty, string.Empty, string.Empty) - { - } + /// + /// Returns an XML representation of this unit as an . + /// + /// This method does not return; it always throws. + /// Always thrown because XML serialization is not supported for generic units. + public XElement ToXml() => throw new NotSupportedException("XML serialization is not supported for generic units."); - /// - /// Gets or sets the number of units per base-unit. - /// - public double ConversionFactor { get; set; } + /// + /// Converts this generic unit to a WKT syntax tree node. + /// + /// A representing this unit. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktNumber(this.ConversionFactor), + }; - /// - /// Returns the Well-known text for this object - /// as defined in the simple features specification. - /// - public override string WKT - { - get - { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, "UNIT[\"{0}\", {1}", Name, ConversionFactor); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } - } + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) + { + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } - /// - /// Gets an XML representation of this object [NOT IMPLEMENTED]. - /// - public override string XML - { - get - { - throw new NotImplementedException(); - } - } + return new WktKeywordNode("UNIT", children); + } - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public override bool EqualParams(object obj) - { - if (!(obj is Unit)) - return false; - return (obj as Unit).ConversionFactor == ConversionFactor; - } + /// + public override bool EqualParams(object obj) + { + return obj is Unit unit && unit.ConversionFactor == this.ConversionFactor; + } + + /// + private protected override Info CloneWithAuthorityCore(string authority, long code) + { + return new Unit(this.ConversionFactor, this.Name, authority, code, this.Alias, this.Abbreviation, this.Remarks); + } + + /// + private protected override Info CloneWithNameCore(string name) + { + return new Unit(this.ConversionFactor, name, this.Authority, this.AuthorityCode, this.Alias, this.Abbreviation, this.Remarks); } } diff --git a/src/ProjNet/CoordinateSystems/VerticalBoundGridTransformation.cs b/src/ProjNet/CoordinateSystems/VerticalBoundGridTransformation.cs new file mode 100644 index 00000000..2f82569b --- /dev/null +++ b/src/ProjNet/CoordinateSystems/VerticalBoundGridTransformation.cs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; + +/// +/// Retains the WKT2 vertical BOUNDCRS grid-binding metadata attached to a vertical CRS source. +/// +internal sealed class VerticalBoundGridTransformation +{ + /// + /// Initializes a new instance of the class. + /// + /// Transformation method name from the WKT2 abridged transformation. + /// Grid file reference from the WKT2 PARAMETERFILE block. + /// Operational hub compound coordinate system used for runtime conversion. + internal VerticalBoundGridTransformation(string methodName, string parameterFileName, CompoundCoordinateSystem hubCoordinateSystem) + { + this.MethodName = ArgumentGuard.ThrowIfNull(methodName, nameof(methodName)); + this.ParameterFileName = ArgumentGuard.ThrowIfNull(parameterFileName, nameof(parameterFileName)); + this.HubCoordinateSystem = ArgumentGuard.ThrowIfNull(hubCoordinateSystem, nameof(hubCoordinateSystem)); + } + + /// + /// Gets the transformation method name from the WKT2 abridged transformation block. + /// + internal string MethodName { get; } + + /// + /// Gets the referenced grid file name or path from the WKT2 PARAMETERFILE block. + /// + internal string ParameterFileName { get; } + + /// + /// Gets the operational hub compound coordinate system used for runtime conversion. + /// + internal CompoundCoordinateSystem HubCoordinateSystem { get; } +} diff --git a/src/ProjNet/CoordinateSystems/VerticalCoordinateSystem.cs b/src/ProjNet/CoordinateSystems/VerticalCoordinateSystem.cs index 37812ceb..63ad4270 100644 --- a/src/ProjNet/CoordinateSystems/VerticalCoordinateSystem.cs +++ b/src/ProjNet/CoordinateSystems/VerticalCoordinateSystem.cs @@ -1,119 +1,297 @@ -using System; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A 1D coordinate system suitable vertical coordinates. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// +/// +public class VerticalCoordinateSystem : CoordinateSystem { /// - /// A 1D coordinate system suitable vertical coordinates + /// Initializes a new instance of the class. + /// Creates an instance of a VerticalCoordinateSystem. + /// + /// The linear unit. + /// The vertical datum. + /// Axis information. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public VerticalCoordinateSystem( + LinearUnit linearUnit, + VerticalDatum verticalDatum, + AxisInfo axisInfo, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks) + : this(linearUnit, verticalDatum, CreateSingleAxisInfo(axisInfo), name, authority, authorityCode, alias, abbreviation, remarks) + { + } + + /// + /// Initializes a new instance of the class with explicit axis metadata. + /// + /// The linear unit. + /// The vertical datum. + /// Axis information. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + /// Default envelope for the coordinate system domain. + /// Retained WKT2 vertical bound-grid metadata for this coordinate system. + internal VerticalCoordinateSystem( + LinearUnit linearUnit, + VerticalDatum verticalDatum, + List axisInfo, + string name, + string authority, + long authorityCode, + string alias, + string abbreviation, + string remarks, + double[]? defaultEnvelope = null, + VerticalBoundGridTransformation? boundGridTransformation = null) + : base(name, authority, authorityCode, alias, abbreviation, remarks, ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)), defaultEnvelope) + { + this.VerticalDatum = ArgumentGuard.ThrowIfNull(verticalDatum, nameof(verticalDatum)); + this.LinearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + this.BoundGridTransformation = boundGridTransformation; + } + + /// + /// Gets the VerticalDatum. + /// + public VerticalDatum VerticalDatum { get; } + + /// + /// Gets the LinearUnit. + /// + public LinearUnit LinearUnit { get; } + + /// + /// Gets creates a meter unit coordinate system with . + /// + public static VerticalCoordinateSystem ODN => + new( + LinearUnit.Metre, + VerticalDatum.ODN, + new AxisInfo("Up", AxisOrientationEnum.Up), + "Newlyn", + "EPSG", + 5701, + string.Empty, + "ODN", + string.Empty); + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Gets the retained WKT2 vertical BOUNDCRS grid-binding metadata when available. + /// + internal VerticalBoundGridTransformation? BoundGridTransformation { get; } + + /// + /// Creates a copy of this coordinate system with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new VerticalCoordinateSystem WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); + + /// + /// Creates a copy of this coordinate system with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new VerticalCoordinateSystem WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Returns an XML representation of this vertical coordinate system as an . /// - public class VerticalCoordinateSystem : CoordinateSystem + /// An containing the XML representation. + public override XElement ToXml() { - /// - /// Creates an instance of a VerticalCoordinateSystem - /// - /// The linear unit - /// The vertical datum - /// Axis information - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - public VerticalCoordinateSystem(LinearUnit linearUnit, VerticalDatum verticalDatum, AxisInfo axisInfo, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) : base(name, authority, authorityCode, alias, abbreviation, remarks) + var innerElement = new XElement("CS_VerticalCoordinateSystem"); + innerElement.Add(this.InfoXmlElement); + foreach (AxisInfo ai in this.AxisInfo) { - VerticalDatum = verticalDatum; - AxisInfo = new List() { axisInfo }; - LinearUnit = linearUnit; + innerElement.Add(ai.ToXml()); } - /// - /// Gets or sets the VerticalDatum - /// - public VerticalDatum VerticalDatum { get; set; } - - /// - /// Gets or sets the LinearUnit - /// - public LinearUnit LinearUnit { get; set; } - - /// - /// Creates a meter unit coordinate system with - /// - public static VerticalCoordinateSystem ODN => - new VerticalCoordinateSystem( - new LinearUnit(1, "metre", "EPSG", 9001, string.Empty, "m", string.Empty) - , VerticalDatum.ODN, new AxisInfo("Up", AxisOrientationEnum.Up) - , "Newlyn" - , "EPSG" - , 5701 - , string.Empty - , "ODN" - , string.Empty - ); - /// - public override string WKT + innerElement.Add(this.VerticalDatum.ToXml()); + innerElement.Add(this.LinearUnit.ToXml()); + + return new XElement( + "CS_CoordinateSystem", + new XAttribute("Dimension", this.Dimension.ToString(CultureInfo.InvariantCulture)), + innerElement); + } + + /// + public override bool EqualParams(object obj) + { + if (obj is not VerticalCoordinateSystem vcs) { - get - { - var sb = new StringBuilder(); - sb.AppendFormat("VERT_CS[\"{0}\", {1}, {2}", Name, VerticalDatum.WKT, LinearUnit.WKT); - //Skip axis info if they contain default values - if (AxisInfo.Count != 1 || - AxisInfo[0].Name != "Up" || AxisInfo[0].Orientation != AxisOrientationEnum.Up) - { - sb.AppendFormat(", {0}", GetAxis(0).WKT); - } - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } + return false; } - /// - public override string XML + if (vcs.Dimension != this.Dimension) { - get + return false; + } + + for (int i = 0; i < vcs.AxisInfo.Count; i++) + { + if (vcs.AxisInfo[i].Orientation != this.AxisInfo[i].Orientation) { - var sb = new StringBuilder(); - sb.AppendFormat(CultureInfo.InvariantCulture.NumberFormat, - "{1}", - this.Dimension, InfoXml); - foreach (var ai in AxisInfo) - sb.Append(ai.XML); - sb.AppendFormat("{0}{1}", - VerticalDatum.XML, LinearUnit.XML); - return sb.ToString(); + return false; } } - /// - public override bool EqualParams(object obj) + return vcs.LinearUnit.EqualParams(this.LinearUnit) && + vcs.VerticalDatum.EqualParams(this.VerticalDatum); + } + + /// + public override IUnit GetUnits(int dimension) + { + if (dimension != 0) { - if (!(obj is VerticalCoordinateSystem vcs)) - return false; + ArgumentGuard.ThrowArgumentOutOfRange(nameof(dimension), "Vertical Coordinate Systems have only one dimension"); + } - if (vcs.Dimension != Dimension) return false; - if (AxisInfo.Count != vcs.AxisInfo.Count) return false; - for (int i = 0; i < vcs.AxisInfo.Count; i++) - if (vcs.AxisInfo[i].Orientation != AxisInfo[i].Orientation) - return false; - return vcs.LinearUnit.EqualParams(LinearUnit) && - vcs.VerticalDatum.EqualParams(VerticalDatum); + return this.LinearUnit; + } + + /// + public override WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + this.VerticalDatum.ToWktNode(), + this.LinearUnit.ToWktNode(), + }; + + // Skip axis info if they contain default values + if (this.AxisInfo.Count != 1 || + this.AxisInfo[0].Name != "Up" || this.AxisInfo[0].Orientation != AxisOrientationEnum.Up) + { + children.Add(this.GetAxis(0).ToWktNode()); } - /// - public override IUnit GetUnits(int dimension) + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) { - if( dimension != 0 ) - { - throw new ArgumentOutOfRangeException(nameof(dimension), "Vertical Coordinate Systems have only one dimension"); - } + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); + } + + return new WktKeywordNode("VERT_CS", children); + } + + /// + public override WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) + { + return this.ToWktNode(); + } + + if (this.BoundGridTransformation is not null) + { + BoundCoordinateSystem boundCoordinateSystem = BoundCoordinateSystemSupport.CreateLegacyBoundCoordinateSystemForSerialization(this) + ?? throw new NotSupportedException("WKT2 VERTCRS output for retained bound-grid metadata could not be normalized to BOUNDCRS."); + return BoundCoordinateSystemSupport.CreateWkt2BoundCoordinateSystemNode(boundCoordinateSystem); + } - return LinearUnit; + if (this.AxisInfo.Count != 1) + { + throw new InvalidOperationException($"Vertical coordinate system '{this.Name}' must provide exactly one axis for WKT2 output."); } + + var children = new List + { + new WktQuotedString(this.Name), + this.VerticalDatum.ToWktNode(version), + new WktKeywordNode( + "CS", + new WktIdentifier("vertical"), + new WktInteger(this.Dimension)), + this.GetAxis(0).ToWktNode(version), + this.LinearUnit.ToWktNode(version), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); + } + + return new WktKeywordNode("VERTCRS", children); + } + + /// + /// Creates a copy of this vertical coordinate system with retained WKT2 bound-grid metadata. + /// + /// The bound-grid metadata to attach to the clone. + /// A cloned coordinate system carrying the supplied bound-grid metadata. + internal VerticalCoordinateSystem WithBoundGridTransformation(VerticalBoundGridTransformation boundGridTransformation) + { + boundGridTransformation = ArgumentGuard.ThrowIfNull(boundGridTransformation, nameof(boundGridTransformation)); + return new VerticalCoordinateSystem( + this.LinearUnit, + this.VerticalDatum, + CloneAxisInfo(this.AxisInfo), + this.Name, + this.Authority, + this.AuthorityCode, + this.Alias, + this.Abbreviation, + this.Remarks, + this.DefaultEnvelope, + boundGridTransformation); + } + + private static List CreateSingleAxisInfo(AxisInfo axisInfo) + => [ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo))]; + + private static List CloneAxisInfo(List axisInfo) + { + var clone = new List(axisInfo.Count); + for (int i = 0; i < axisInfo.Count; i++) + { + clone.Add(new AxisInfo(axisInfo[i])); + } + + return clone; } } diff --git a/src/ProjNet/CoordinateSystems/VerticalDatum.cs b/src/ProjNet/CoordinateSystems/VerticalDatum.cs index 8705bdba..53cc7d85 100644 --- a/src/ProjNet/CoordinateSystems/VerticalDatum.cs +++ b/src/ProjNet/CoordinateSystems/VerticalDatum.cs @@ -1,73 +1,175 @@ -using System; +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; using System.Collections.Generic; using System.Globalization; -using System.Text; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// A vertical datum defining the standard datum information. +/// +/// +/// +/// Thread safety: Instances are immutable after construction and may be shared across threads. +/// The predefined datum accessors are thread-safe because they only expose immutable value objects. +/// +/// +public class VerticalDatum : Datum { /// - /// A vertical datum defining the standard datum information + /// Initializes a new instance of the class. + /// + /// Datum type. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Abbreviation. + /// Provider-supplied remarks. + public VerticalDatum(DatumType type, string name, string authority, long code, string alias, string remarks, string abbreviation) + : this(type, name, authority, code, alias, remarks, abbreviation, null) + { + } + + /// + /// Initializes a new instance of the class with retained ensemble metadata. + /// + /// Datum type. + /// Name. + /// Authority name. + /// Authority-specific identification code. + /// Alias. + /// Provider-supplied remarks. + /// Abbreviation. + /// Retained datum-ensemble metadata. + internal VerticalDatum( + DatumType type, + string name, + string authority, + long code, + string alias, + string remarks, + string abbreviation, + DatumEnsemble? ensemble) + : base(type, name, authority, code, alias, remarks, abbreviation, ensemble) + { + } + + /// + /// Gets the Ordnance Datum Newlyn (ODN) vertical datum. /// - public class VerticalDatum : Datum + public static VerticalDatum ODN { - /// - /// Initializes a new instance of a vertical datum - /// - /// Datum type - /// Name - /// Authority name - /// Authority-specific identification code. - /// Alias - /// Abbreviation - /// Provider-supplied remarks - public VerticalDatum(DatumType type, string name, string authority, long code, string alias, string remarks, string abbreviation) : base(type, name, authority, code, alias, remarks, abbreviation) + get { + return new VerticalDatum(DatumType.VD_GeoidModelDerived, "Ordnance Datum Newlyn", "EPSG", 5101, string.Empty, string.Empty, string.Empty); } + } + + /// + public override string WKT => this.ToWktNode().ToString(); + + /// + public override string XML => this.ToXml().ToString(SaveOptions.DisableFormatting); + + /// + /// Creates a copy of this datum with updated authority metadata. + /// + /// Replacement authority name. + /// Replacement authority-specific identification code. + /// A new with updated authority metadata. + public new VerticalDatum WithAuthority(string authority, long code) => InfoAuthorityCloneHelper.CloneWithAuthority(this, authority, code); - /// - /// ODN - VerticalDatum - /// - public static VerticalDatum ODN + /// + /// Creates a copy of this datum with an updated name. + /// + /// Replacement name. + /// A new with the updated name. + public new VerticalDatum WithName(string name) => InfoAuthorityCloneHelper.CloneWithName(this, name); + + /// + /// Creates a copy of this datum with updated retained datum-ensemble metadata. + /// + /// Replacement ensemble metadata, or to clear it. + /// A new with updated ensemble metadata. + public new VerticalDatum WithEnsemble(DatumEnsemble? ensemble) => InfoAuthorityCloneHelper.CloneWithEnsemble(this, ensemble); + + /// + /// Returns an XML representation of this vertical datum as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + var element = new XElement( + "CS_VerticalDatum", + new XAttribute("DatumType", ((int)this.DatumType).ToString(CultureInfo.InvariantCulture))); + element.Add(this.InfoXmlElement); + return element; + } + + /// + /// Converts this vertical datum to a WKT syntax tree node. + /// + /// A representing this vertical datum. + public WktNode ToWktNode() + { + var children = new List + { + new WktQuotedString(this.Name), + new WktInteger((int)this.DatumType), + }; + + if (!string.IsNullOrWhiteSpace(this.Authority) && this.AuthorityCode > 0) { - get - { - return new VerticalDatum(DatumType.VD_GeoidModelDerived, "Ordnance Datum Newlyn", "EPSG", 5101, string.Empty, string.Empty, string.Empty); - } + children.Add(new WktKeywordNode( + "AUTHORITY", + new WktQuotedString(this.Authority), + new WktQuotedString(this.AuthorityCode.ToString(CultureInfo.InvariantCulture)))); } - /// - public override string WKT + return new WktKeywordNode("VERT_DATUM", children); + } + + /// + /// Converts this vertical datum to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this vertical datum in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + if (version == WktVersion.Wkt1) { - get - { - var sb = new StringBuilder(); - sb.AppendFormat("DATUM[\"{0}\", {1}", Name, (int)DatumType); - if (!string.IsNullOrWhiteSpace(Authority) && AuthorityCode > 0) - sb.AppendFormat(", AUTHORITY[\"{0}\", \"{1}\"]", Authority, AuthorityCode); - sb.Append("]"); - return sb.ToString(); - } + return this.ToWktNode(); } - /// - public override string XML + if (this.Ensemble is not null) { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, - "{1}{2}", - (int)DatumType, InfoXml); - } + return this.Ensemble.ToWktNode(version); } - /// - public override bool EqualParams(object obj) + var children = new List { - if( obj is VerticalDatum vertDatum ) - { - return base.EqualParams(vertDatum); - } - return false; + new WktQuotedString(this.Name), + }; + + WktKeywordNode? idNode = WktVersionSupport.CreateIdNode(this.Authority, this.AuthorityCode); + if (idNode is not null) + { + children.Add(idNode); } + + return new WktKeywordNode("VDATUM", children); + } + + /// + public override bool EqualParams(object obj) + { + return obj is VerticalDatum vertDatum && base.EqualParams(vertDatum); } } diff --git a/src/ProjNet/CoordinateSystems/WGS84ConversionInfo.cs b/src/ProjNet/CoordinateSystems/WGS84ConversionInfo.cs index 5761cd45..9a4f7ec8 100644 --- a/src/ProjNet/CoordinateSystems/WGS84ConversionInfo.cs +++ b/src/ProjNet/CoordinateSystems/WGS84ConversionInfo.cs @@ -1,266 +1,314 @@ -// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk) -// -// This file is part of SharpMap. -// SharpMap is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// SharpMap is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with SharpMap; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.CoordinateSystems; using System; using System.Globalization; +using System.Xml.Linq; +using ProjNet.IO.Wkt; -namespace ProjNet.CoordinateSystems +/// +/// Parameters for a geographic transformation into WGS84. The Bursa Wolf parameters should be applied +/// to geocentric coordinates, where the X axis points towards the Greenwich Prime Meridian, the Y axis +/// points East, and the Z axis points North. +/// +/// +/// These parameters can be used to approximate a transformation from the horizontal datum to the +/// WGS84 datum using a Bursa Wolf transformation. However, it must be remembered that this transformation +/// is only an approximation. For a given horizontal datum, different Bursa Wolf transformations can be +/// used to minimize the errors over different regions. +/// If the DATUM clause contains a TOWGS84 clause, then this should be its preferred transformation, +/// which will often be the transformation which gives a broad approximation over the whole area of interest +/// (e.g. the area of interest in the containing geographic coordinate system). +/// Sometimes, only the first three or six parameters are defined. In this case the remaining +/// parameters must be zero. If only three parameters are defined, then they can still be plugged into the +/// Bursa Wolf formulas, or you can take a short cut. The Bursa Wolf transformation works on geocentric +/// coordinates, so you cannot apply it onto geographic coordinates directly. If there are only three +/// parameters then you can use the Molodenski or abridged Molodenski formulas. +/// If a datums ToWgs84Parameters parameter values are zero, then the receiving +/// application can assume that the writing application believed that the datum is approximately equal to +/// WGS84. +/// +[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Legacy Bursa-Wolf parameter fields are part of the long-standing public API.")] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Legacy Bursa-Wolf parameter fields are part of the long-standing public API.")] +public sealed class Wgs84ConversionInfo : IEquatable { /// - /// Parameters for a geographic transformation into WGS84. The Bursa Wolf parameters should be applied - /// to geocentric coordinates, where the X axis points towards the Greenwich Prime Meridian, the Y axis - /// points East, and the Z axis points North. + /// Conversion factor from arc-seconds to radians: (π / 180) / 3600. /// - /// - /// These parameters can be used to approximate a transformation from the horizontal datum to the - /// WGS84 datum using a Bursa Wolf transformation. However, it must be remembered that this transformation - /// is only an approximation. For a given horizontal datum, different Bursa Wolf transformations can be - /// used to minimize the errors over different regions. - /// If the DATUM clause contains a TOWGS84 clause, then this should be its �preferred� transformation, - /// which will often be the transformation which gives a broad approximation over the whole area of interest - /// (e.g. the area of interest in the containing geographic coordinate system). - /// Sometimes, only the first three or six parameters are defined. In this case the remaining - /// parameters must be zero. If only three parameters are defined, then they can still be plugged into the - /// Bursa Wolf formulas, or you can take a short cut. The Bursa Wolf transformation works on geocentric - /// coordinates, so you cannot apply it onto geographic coordinates directly. If there are only three - /// parameters then you can use the Molodenski or abridged Molodenski formulas. - /// If a datums ToWgs84Parameters parameter values are zero, then the receiving - /// application can assume that the writing application believed that the datum is approximately equal to - /// WGS84. - /// - [Serializable] - public class Wgs84ConversionInfo - { - private const double SEC_TO_RAD = 4.84813681109535993589914102357e-6; + private const double SecondsToRadians = 4.84813681109535993589914102357e-6d; - /// - /// Initializes an instance of Wgs84ConversionInfo with default parameters (all values = 0) - /// - public Wgs84ConversionInfo() : this(0, 0, 0, 0, 0, 0, 0, string.Empty) { } + /// + /// Bursa Wolf shift in meters. + /// + public double Dx; - /// - /// Initializes an instance of Wgs84ConversionInfo - /// - /// Bursa Wolf shift in meters. - /// Bursa Wolf shift in meters. - /// Bursa Wolf shift in meters. - /// Bursa Wolf rotation in arc seconds. - /// Bursa Wolf rotation in arc seconds. - /// Bursa Wolf rotation in arc seconds. - /// Bursa Wolf scaling in parts per million. - public Wgs84ConversionInfo(double dx, double dy, double dz, double ex, double ey, double ez, double ppm) - : this(dx, dy, dz, ex, ey, ez, ppm, string.Empty) { } + /// + /// Bursa Wolf shift in meters. + /// + public double Dy; - /// - /// Initializes an instance of Wgs84ConversionInfo - /// - /// Bursa Wolf shift in meters. - /// Bursa Wolf shift in meters. - /// Bursa Wolf shift in meters. - /// Bursa Wolf rotation in arc seconds. - /// Bursa Wolf rotation in arc seconds. - /// Bursa Wolf rotation in arc seconds. - /// Bursa Wolf scaling in parts per million. - /// Area of use for this transformation - public Wgs84ConversionInfo(double dx, double dy, double dz, double ex, double ey, double ez, double ppm, string areaOfUse) - { - Dx = dx; Dy = dy; Dz = dz; - Ex = ex; Ey = ey; Ez = ez; - Ppm = ppm; - AreaOfUse = areaOfUse; - } + /// + /// Bursa Wolf shift in meters. + /// + public double Dz; - /// - /// Bursa Wolf shift in meters. - /// - public double Dx; + /// + /// Bursa Wolf rotation in arc seconds. + /// + public double Ex; - /// - /// Bursa Wolf shift in meters. - /// - public double Dy; + /// + /// Bursa Wolf rotation in arc seconds. + /// + public double Ey; - /// - /// Bursa Wolf shift in meters. - /// - public double Dz; + /// + /// Bursa Wolf rotation in arc seconds. + /// + public double Ez; - /// - /// Bursa Wolf rotation in arc seconds. - /// - public double Ex; + /// + /// Bursa Wolf scaling in parts per million. + /// + public double Ppm; - /// - /// Bursa Wolf rotation in arc seconds. - /// - public double Ey; + /// + /// Human readable text describing intended region of transformation. + /// + public string AreaOfUse; - /// - /// Bursa Wolf rotation in arc seconds. - /// - public double Ez; + /// + /// Initializes a new instance of the class with all parameters set to zero. + /// + public Wgs84ConversionInfo() + : this(0, 0, 0, 0, 0, 0, 0, string.Empty) + { + } - /// - /// Bursa Wolf scaling in parts per million. - /// - public double Ppm; + /// + /// Initializes a new instance of the class. + /// + /// Bursa Wolf X-axis shift in meters. + /// Bursa Wolf Y-axis shift in meters. + /// Bursa Wolf Z-axis shift in meters. + /// Bursa Wolf X-axis rotation in arc seconds. + /// Bursa Wolf Y-axis rotation in arc seconds. + /// Bursa Wolf Z-axis rotation in arc seconds. + /// Bursa Wolf scaling in parts per million. + public Wgs84ConversionInfo(double dx, double dy, double dz, double ex, double ey, double ez, double ppm) + : this(dx, dy, dz, ex, ey, ez, ppm, string.Empty) + { + } - /// - /// Human readable text describing intended region of transformation. - /// - public string AreaOfUse; + /// + /// Initializes a new instance of the class. + /// + /// Bursa Wolf X-axis shift in meters. + /// Bursa Wolf Y-axis shift in meters. + /// Bursa Wolf Z-axis shift in meters. + /// Bursa Wolf X-axis rotation in arc seconds. + /// Bursa Wolf Y-axis rotation in arc seconds. + /// Bursa Wolf Z-axis rotation in arc seconds. + /// Bursa Wolf scaling in parts per million. + /// Area of use for this transformation. + public Wgs84ConversionInfo(double dx, double dy, double dz, double ex, double ey, double ez, double ppm, string areaOfUse) + { + this.Dx = dx; + this.Dy = dy; + this.Dz = dz; + this.Ex = ex; + this.Ey = ey; + this.Ez = ez; + this.Ppm = ppm; + this.AreaOfUse = areaOfUse; + } - /// - /// Affine Bursa-Wolf matrix transformation - /// - /// - /// Transformation of coordinates from one geographic coordinate system into another - /// (also colloquially known as a "datum transformation") is usually carried out as an - /// implicit concatenation of three transformations: - /// [geographical to geocentric >> geocentric to geocentric >> geocentric to geographic - /// - /// The middle part of the concatenated transformation, from geocentric to geocentric, is usually - /// described as a simplified 7-parameter Helmert transformation, expressed in matrix form with 7 - /// parameters, in what is known as the "Bursa-Wolf" formula:
- /// - /// S = 1 + Ppm/1000000 - /// [ Xt ] [ S -Ez*S +Ey*S Dx ] [ Xs ] - /// [ Yt ] = [ +Ez*S S -Ex*S Dy ] [ Ys ] - /// [ Zt ] [ -Ey*S +Ex*S S Dz ] [ Zs ] - /// [ 1 ] [ 0 0 0 1 ] [ 1 ] - ///
- /// The parameters are commonly referred to defining the transformation "from source coordinate system - /// to target coordinate system", whereby (XS, YS, ZS) are the coordinates of the point in the source - /// geocentric coordinate system and (XT, YT, ZT) are the coordinates of the point in the target - /// geocentric coordinate system. But that does not define the parameters uniquely; neither is the - /// definition of the parameters implied in the formula, as is often believed. However, the - /// following definition, which is consistent with the "Position Vector Transformation" convention, - /// is common E&P survey practice: - ///
- /// (dX, dY, dZ): Translation vector, to be added to the point's position vector in the source - /// coordinate system in order to transform from source system to target system; also: the coordinates - /// of the origin of source coordinate system in the target coordinate system - /// (RX, RY, RZ): Rotations to be applied to the point's vector. The sign convention is such that - /// a positive rotation about an axis is defined as a clockwise rotation of the position vector when - /// viewed from the origin of the Cartesian coordinate system in the positive direction of that axis; - /// e.g. a positive rotation about the Z-axis only from source system to target system will result in a - /// larger longitude value for the point in the target system. Although rotation angles may be quoted in - /// any angular unit of measure, the formula as given here requires the angles to be provided in radians. - /// : The scale correction to be made to the position vector in the source coordinate system in order - /// to obtain the correct scale in the target coordinate system. M = (1 + dS*10-6), whereby dS is the scale - /// correction expressed in parts per million. - /// for an explanation of the Bursa-Wolf transformation - ///
- /// - public double[] GetAffineTransform() + /// + /// Gets the Well Known Text (WKT) for this object. + /// + /// The WKT format of this object is: TOWGS84[dx, dy, dz, ex, ey, ez, ppm] + public string WKT + { + get { - double RS = 1 + Ppm * 0.000001; - return new double[7] { RS, Ex * SEC_TO_RAD * RS, Ey * SEC_TO_RAD * RS, Ez * SEC_TO_RAD * RS, Dx, Dy, Dz }; - /*return new double[3,4] { - { RS, -Ez*SEC_TO_RAD*RS, +Ey*SEC_TO_RAD*RS, Dx} , - { Ez*SEC_TO_RAD*RS, RS, -Ex*SEC_TO_RAD*RS, Dy} , - { -Ey*SEC_TO_RAD*RS,Ex*SEC_TO_RAD*RS, RS, Dz} - };*/ + return FormattableString.Invariant($"TOWGS84[{this.Dx}, {this.Dy}, {this.Dz}, {this.Ex}, {this.Ey}, {this.Ez}, {this.Ppm}]"); } + } - /// - /// Returns the Well Known Text (WKT) for this object. - /// - /// The WKT format of this object is: TOWGS84[dx, dy, dz, ex, ey, ez, ppm] - /// WKT representaion - public string WKT + /// + /// Gets an XML representation of this object. + /// + public string XML + { + get { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, - "TOWGS84[{0}, {1}, {2}, {3}, {4}, {5}, {6}]", - Dx, Dy, Dz, Ex, Ey, Ez, Ppm); - } + return FormattableString.Invariant($""); } + } - /// - /// Gets an XML representation of this object - /// - public string XML + /// + /// Gets a value indicating whether all seven Bursa-Wolf parameter values are zero. + /// + public bool HasZeroValuesOnly + { + get { - get - { - return string.Format(CultureInfo.InvariantCulture.NumberFormat, - "", - Dx, Dy, Dz, Ex, Ey, Ez, Ppm); - } + return !(this.Dx != 0 || this.Dy != 0 || this.Dz != 0 || this.Ex != 0 || this.Ey != 0 || this.Ez != 0 || this.Ppm != 0); } + } - /// - /// Returns the Well Known Text (WKT) for this object. - /// - /// The WKT format of this object is: TOWGS84[dx, dy, dz, ex, ey, ez, ppm] - /// WKT representaion - public override string ToString() - { - return WKT; - } + /// + /// Returns an XML representation of this WGS84 conversion info as an . + /// + /// An containing the XML representation. + public XElement ToXml() + { + return new XElement( + "CS_WGS84ConversionInfo", + new XAttribute("Dx", this.Dx.ToString(CultureInfo.InvariantCulture)), + new XAttribute("Dy", this.Dy.ToString(CultureInfo.InvariantCulture)), + new XAttribute("Dz", this.Dz.ToString(CultureInfo.InvariantCulture)), + new XAttribute("Ex", this.Ex.ToString(CultureInfo.InvariantCulture)), + new XAttribute("Ey", this.Ey.ToString(CultureInfo.InvariantCulture)), + new XAttribute("Ez", this.Ez.ToString(CultureInfo.InvariantCulture)), + new XAttribute("Ppm", this.Ppm.ToString(CultureInfo.InvariantCulture))); + } - /// - /// Returns true of all 7 parameter values are 0.0 - /// - /// - public bool HasZeroValuesOnly - { - get - { - return !(Dx != 0 || Dy != 0 || Dz != 0 || Ex != 0 || Ey != 0 || Ez != 0 || Ppm != 0); - } - } + /// + /// Converts this WGS84 conversion info to a WKT syntax tree node. + /// + /// A representing this WGS84 conversion info. + public WktNode ToWktNode() + { + return new WktKeywordNode( + "TOWGS84", + new WktNumber(this.Dx), + new WktNumber(this.Dy), + new WktNumber(this.Dz), + new WktNumber(this.Ex), + new WktNumber(this.Ey), + new WktNumber(this.Ez), + new WktNumber(this.Ppm)); + } - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// - /// - public override bool Equals(object obj) - { - return Equals(obj as Wgs84ConversionInfo); - } + /// + /// Converts this WGS84 conversion info to a WKT syntax tree node for the requested WKT version. + /// + /// The WKT dialect to emit. + /// A representing this WGS84 conversion info in the requested WKT version. + public WktNode ToWktNode(WktVersion version) + { + WktVersionSupport.ThrowIfUnknown(version); + return version == WktVersion.Wkt1 + ? this.ToWktNode() + : throw WktVersionSupport.CreateNotSupportedException(nameof(Wgs84ConversionInfo), version); + } - /// - /// Returns a hash code for the specified object - /// - /// A hash code for the specified object - public override int GetHashCode() - { - return Dx.GetHashCode() ^ Dy.GetHashCode() ^ Dz.GetHashCode() ^ - Ex.GetHashCode() ^ Ey.GetHashCode() ^ Ez.GetHashCode() ^ - Ppm.GetHashCode(); - } + /// + /// Affine Bursa-Wolf matrix transformation. + /// + /// + /// Transformation of coordinates from one geographic coordinate system into another + /// (also colloquially known as a "datum transformation") is usually carried out as an + /// implicit concatenation of three transformations: + /// [geographical to geocentric >> geocentric to geocentric >> geocentric to geographic. + /// + /// The middle part of the concatenated transformation, from geocentric to geocentric, is usually + /// described as a simplified 7-parameter Helmert transformation, expressed in matrix form with 7 + /// parameters, in what is known as the "Bursa-Wolf" formula:
+ /// + /// S = 1 + Ppm/1000000 + /// [ Xt ] [ S -Ez*S +Ey*S Dx ] [ Xs ] + /// [ Yt ] = [ +Ez*S S -Ex*S Dy ] [ Ys ] + /// [ Zt ] [ -Ey*S +Ex*S S Dz ] [ Zs ] + /// [ 1 ] [ 0 0 0 1 ] [ 1 ] + ///
+ /// The parameters are commonly referred to as defining the transformation "from source coordinate system + /// to target coordinate system", whereby (XS, YS, ZS) are the coordinates of the point in the source + /// geocentric coordinate system and (XT, YT, ZT) are the coordinates of the point in the target + /// geocentric coordinate system. But that does not define the parameters uniquely; neither is the + /// definition of the parameters implied in the formula, as is often believed. However, the + /// following definition, which is consistent with the "Position Vector Transformation" convention, + /// is common E&P survey practice: + ///
+ /// (dX, dY, dZ): Translation vector, to be added to the point's position vector in the source + /// coordinate system in order to transform from source system to target system; also: the coordinates + /// of the origin of source coordinate system in the target coordinate system. + /// (RX, RY, RZ): Rotations to be applied to the point's vector. The sign convention is such that + /// a positive rotation about an axis is defined as a clockwise rotation of the position vector when + /// viewed from the origin of the Cartesian coordinate system in the positive direction of that axis; + /// e.g. a positive rotation about the Z-axis only from source system to target system will result in a + /// larger longitude value for the point in the target system. Although rotation angles may be quoted in + /// any angular unit of measure, the formula as given here requires the angles to be provided in radians. + /// : The scale correction to be made to the position vector in the source coordinate system in order + /// to obtain the correct scale in the target coordinate system. M = (1 + dS*10-6), whereby dS is the scale + /// correction expressed in parts per million. + ///
+ /// An array of 7 Bursa-Wolf transformation coefficients [S, Ex, Ey, Ez, Dx, Dy, Dz], where S = 1 + Ppm/1,000,000 and rotations are in radians. + public double[] GetAffineTransform() + { + double[] result = new double[7]; + this.WriteAffineTransform(result); + return result; + } - /// - /// Checks whether the values of this instance is equal to the values of another instance. - /// Only parameters used for coordinate system are used for comparison. - /// Name, abbreviation, authority, alias and remarks are ignored in the comparison. - /// - /// - /// True if equal - public bool Equals(Wgs84ConversionInfo obj) + /// + /// Writes affine Bursa-Wolf transformation coefficients into the supplied destination span. + /// + /// Destination span for 7 Bursa-Wolf coefficients [S, Ex, Ey, Ez, Dx, Dy, Dz], where rotations are in radians. + /// Thrown when has fewer than 7 elements. + public void WriteAffineTransform(Span destination) + { + if (destination.Length < 7) { - if (obj == null) - return false; - return obj.Dx == this.Dx && obj.Dy == this.Dy && obj.Dz == this.Dz && - obj.Ex == this.Ex && obj.Ey == this.Ey && obj.Ez == this.Ez && obj.Ppm == this.Ppm; + ArgumentGuard.ThrowArgument("Destination span must contain at least 7 elements.", nameof(destination)); } + + double rS = 1 + (this.Ppm * 0.000001); + destination[0] = rS; + destination[1] = this.Ex * SecondsToRadians; + destination[2] = this.Ey * SecondsToRadians; + destination[3] = this.Ez * SecondsToRadians; + destination[4] = this.Dx; + destination[5] = this.Dy; + destination[6] = this.Dz; + } + + /// + /// Returns the Well Known Text (WKT) for this object. + /// + /// The WKT format of this object is: TOWGS84[dx, dy, dz, ex, ey, ez, ppm] + /// WKT representation. + public override string ToString() => this.WKT; + + /// + public override bool Equals(object? obj) => this.EqualsCore(obj as Wgs84ConversionInfo); + + /// + /// Returns a hash code for the specified object. + /// + /// A hash code for the specified object. + public override int GetHashCode() + { + return HashCode.Combine(this.Dx, this.Dy, this.Dz, this.Ex, this.Ey, this.Ez, this.Ppm); + } + + /// + /// Checks whether the Bursa-Wolf parameter values of this instance are equal to those of another instance. + /// + /// The instance to compare against. + /// if all seven parameter values are equal; otherwise, . + public bool Equals(Wgs84ConversionInfo? obj) + { + return this.EqualsCore(obj); + } + + private bool EqualsCore(Wgs84ConversionInfo? obj) + { + return obj is not null && obj.Dx == this.Dx && obj.Dy == this.Dy && obj.Dz == this.Dz && + obj.Ex == this.Ex && obj.Ey == this.Ey && obj.Ez == this.Ez && obj.Ppm == this.Ppm; } } diff --git a/src/ProjNet/CoordinateSystems/Wgs84CatalogBootstrap.cs b/src/ProjNet/CoordinateSystems/Wgs84CatalogBootstrap.cs new file mode 100644 index 00000000..2851677f --- /dev/null +++ b/src/ProjNet/CoordinateSystems/Wgs84CatalogBootstrap.cs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.CoordinateSystems; + +using System; +using System.Diagnostics.CodeAnalysis; +using ProjNet.Data.Generated; + +/// +/// Provides bootstrap-safe access to the generated EPSG catalog for the public WGS84 static factories. +/// +/// +/// The generated EPSG factory constructs ellipsoids, datums, and coordinate systems directly from +/// generated records and primitive coordinate-system constructors. It does not depend on the public +/// *.WGS84, , or +/// accessors. +/// This helper captures that dependency boundary so the public convenience accessors can resolve +/// through catalog data without introducing a bootstrap cycle back into the generated factory. +/// +internal static class Wgs84CatalogBootstrap +{ + /// + /// Gets the EPSG ellipsoid code for WGS 84. + /// + internal const int Wgs84EllipsoidCode = 7030; + + /// + /// Gets the EPSG datum code for WGS 84. + /// + internal const int Wgs84DatumCode = 6326; + + /// + /// Gets the EPSG SRID for the two-dimensional WGS 84 geographic CRS. + /// + internal const int Wgs84GeographicSrid = 4326; + + /// + /// Gets the EPSG SRID for the WGS 84 geocentric CRS. + /// + internal const int Wgs84GeocentricSrid = 4978; + + /// + /// Gets the EPSG SRID for Web Mercator. + /// + internal const int WebMercatorSrid = 3857; + + /// + /// Tries to resolve a coordinate system from the generated EPSG catalog. + /// + /// The expected coordinate-system type. + /// The EPSG SRID to resolve. + /// The resolved coordinate system when available. + /// when the coordinate system could be resolved as ; otherwise . + internal static bool TryGetCoordinateSystem(int srid, [NotNullWhen(true)] out TCoordinateSystem? coordinateSystem) + where TCoordinateSystem : CoordinateSystem + { + if (EpsgCoordinateSystemFactory.TryResolveCoordinateSystem(srid, out CoordinateSystem? resolved) && + resolved is TCoordinateSystem typed) + { + coordinateSystem = typed; + return true; + } + + coordinateSystem = null; + return false; + } + + /// + /// Resolves the WGS 84 ellipsoid from the generated EPSG catalog. + /// + /// The generated EPSG ellipsoid instance. + internal static Ellipsoid GetWgs84Ellipsoid() + { + if (EpsgCoordinateSystemFactory.TryResolveEllipsoid(Wgs84EllipsoidCode, out Ellipsoid? ellipsoid)) + { + return ellipsoid; + } + + throw CreateMissingCatalogException($"ellipsoid {Wgs84EllipsoidCode}"); + } + + /// + /// Resolves the WGS 84 horizontal datum from the generated EPSG catalog. + /// + /// The generated EPSG horizontal datum instance. + internal static HorizontalDatum GetWgs84Datum() + { + if (EpsgCoordinateSystemFactory.TryResolveHorizontalDatum(Wgs84DatumCode, out HorizontalDatum? datum)) + { + return datum; + } + + throw CreateMissingCatalogException($"horizontal datum {Wgs84DatumCode}"); + } + + private static InvalidOperationException CreateMissingCatalogException(string item) + => new($"The generated EPSG catalog could not resolve {item} during WGS84 bootstrap."); +} diff --git a/src/ProjNet/Data/CoordinateOperationDefinition.cs b/src/ProjNet/Data/CoordinateOperationDefinition.cs new file mode 100644 index 00000000..f5580850 --- /dev/null +++ b/src/ProjNet/Data/CoordinateOperationDefinition.cs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +/// +/// Represents a coordinate operation catalog definition. +/// +internal sealed class CoordinateOperationDefinition( + CoordinateOperationKind operationKind, + int operationCode, + int sourceSrid, + int targetSrid, + double accuracy, + string methodName, + string parameterFileName, + double areaSouthLatitude, + double areaNorthLatitude, + double areaWestLongitude, + double areaEastLongitude) +{ + /// + /// Gets the expected operation accuracy. + /// + internal double Accuracy { get; } = accuracy; + + /// + /// Gets the operation method name. + /// + internal string MethodName { get; } = methodName; + + /// + /// Gets the operation code. + /// + internal int OperationCode { get; } = operationCode; + + /// + /// Gets the operation kind. + /// + internal CoordinateOperationKind OperationKind { get; } = operationKind; + + /// + /// Gets the optional parameter file name. + /// + internal string ParameterFileName { get; } = parameterFileName; + + /// + /// Gets the source SRID. + /// + internal int SourceSrid { get; } = sourceSrid; + + /// + /// Gets the target SRID. + /// + internal int TargetSrid { get; } = targetSrid; + + /// + /// Gets the approximate south latitude bound of the operation area of use. + /// + internal double AreaSouthLatitude { get; } = areaSouthLatitude; + + /// + /// Gets the approximate north latitude bound of the operation area of use. + /// + internal double AreaNorthLatitude { get; } = areaNorthLatitude; + + /// + /// Gets the approximate west longitude bound of the operation area of use. + /// + internal double AreaWestLongitude { get; } = areaWestLongitude; + + /// + /// Gets the approximate east longitude bound of the operation area of use. + /// + internal double AreaEastLongitude { get; } = areaEastLongitude; + + /// + /// Computes a rough coverage area in degree-squared based on the operation bounds. + /// + /// The approximate area coverage, or when unavailable. + internal double GetApproximateAreaOfUseCoverage() + { + if (double.IsNaN(this.AreaSouthLatitude) + || double.IsNaN(this.AreaNorthLatitude) + || double.IsNaN(this.AreaWestLongitude) + || double.IsNaN(this.AreaEastLongitude)) + { + return double.MaxValue; + } + + if (this.AreaSouthLatitude < -90d || this.AreaSouthLatitude > 90d || this.AreaNorthLatitude < -90d || this.AreaNorthLatitude > 90d) + { + return double.MaxValue; + } + + if (this.AreaSouthLatitude > this.AreaNorthLatitude) + { + return double.MaxValue; + } + + if (this.AreaWestLongitude < -180d || this.AreaWestLongitude > 180d || this.AreaEastLongitude < -180d || this.AreaEastLongitude > 180d) + { + return double.MaxValue; + } + + double latitudeSpan = this.AreaNorthLatitude - this.AreaSouthLatitude; + double longitudeSpan = this.AreaEastLongitude >= this.AreaWestLongitude + ? this.AreaEastLongitude - this.AreaWestLongitude + : 360d - (this.AreaWestLongitude - this.AreaEastLongitude); + if (longitudeSpan < 0d) + { + return double.MaxValue; + } + + return latitudeSpan * longitudeSpan; + } +} diff --git a/src/ProjNet/Data/CoordinateOperationKind.cs b/src/ProjNet/Data/CoordinateOperationKind.cs new file mode 100644 index 00000000..ded8f45c --- /dev/null +++ b/src/ProjNet/Data/CoordinateOperationKind.cs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +/// +/// Defines the operation kind represented by a catalog entry. +/// +internal enum CoordinateOperationKind : byte +{ + /// + /// A direct transformation between source and target coordinate systems. + /// + Transformation = 0, + + /// + /// A chained operation composed from multiple individual operations. + /// + ConcatenatedOperation = 1, + + /// + /// A time-dependent point motion operation. + /// + PointMotionOperation = 2, +} diff --git a/src/ProjNet/Data/CoordinateSystemDefinition.cs b/src/ProjNet/Data/CoordinateSystemDefinition.cs new file mode 100644 index 00000000..d47d3546 --- /dev/null +++ b/src/ProjNet/Data/CoordinateSystemDefinition.cs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +/// +/// Represents a coordinate system definition identified by SRID and serialized as WKT. +/// +/// The SRID of the coordinate system definition. +/// The Well-Known Text representation of the coordinate system. +public readonly record struct CoordinateSystemDefinition(int Srid, string Wkt); diff --git a/src/ProjNet/Data/CoordinateSystemEntry.cs b/src/ProjNet/Data/CoordinateSystemEntry.cs new file mode 100644 index 00000000..9658f4ad --- /dev/null +++ b/src/ProjNet/Data/CoordinateSystemEntry.cs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +using ProjNet.CoordinateSystems; + +/// +/// Represents a resolved coordinate system object identified by SRID. +/// +/// The SRID of the coordinate system. +/// The resolved coordinate system object. +public readonly record struct CoordinateSystemEntry(int Srid, CoordinateSystem CoordinateSystem); diff --git a/src/ProjNet/Data/Generated/EpsgCoordinateSystemFactory.cs b/src/ProjNet/Data/Generated/EpsgCoordinateSystemFactory.cs new file mode 100644 index 00000000..d1cdc130 --- /dev/null +++ b/src/ProjNet/Data/Generated/EpsgCoordinateSystemFactory.cs @@ -0,0 +1,631 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data.Generated; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using ProjNet.CoordinateSystems; +using ProjNet.Data; + +/// +/// Provides factory methods for building instances from the generated EPSG catalog data. +/// +internal static class EpsgCoordinateSystemFactory +{ + private static readonly CoordinateSystem[] CoordinateSystemCache = new CoordinateSystem[EpsgGeneratedCatalog.CoordinateReferenceCount]; + private static readonly object CoordinateSystemCacheSync = new(); + private static readonly Lazy> UnitsByCode = new(BuildUnitsByCode, true); + private static readonly Lazy> EllipsoidsByCode = new(BuildEllipsoidsByCode, true); + private static readonly Lazy> PrimeMeridiansByCode = new(BuildPrimeMeridiansByCode, true); + private static readonly Lazy> GeodeticDatumsByCode = new(BuildGeodeticDatumsByCode, true); + private static readonly Lazy> VerticalDatumsByCode = new(BuildVerticalDatumsByCode, true); + private static readonly Lazy> AxesByCoordinateSystemCode = new(BuildAxesByCoordinateSystemCode, true); + + /// + /// Enumerates all coordinate systems from the generated EPSG catalog as instances. + /// + /// A sequence of instances from the EPSG catalog. + internal static IEnumerable GetCoordinateSystems() + { + for (int cacheIndex = 0; cacheIndex < EpsgGeneratedCatalog.CoordinateReferenceCount; cacheIndex++) + { + if (!EpsgGeneratedCatalog.TryGetCoordinateSridByCacheIndex(cacheIndex, out int srid)) + { + continue; + } + + CoordinateSystem? coordinateSystem = TryCreateCoordinateSystem(srid); + if (coordinateSystem is null) + { + continue; + } + + yield return new CoordinateSystemEntry(srid, coordinateSystem); + } + } + + /// + /// Tries to resolve a coordinate system by SRID from the generated EPSG catalog. + /// + /// The EPSG SRID to resolve. + /// The resolved coordinate system when available. + /// when the coordinate system could be created; otherwise . + internal static bool TryResolveCoordinateSystem(int srid, [NotNullWhen(true)] out CoordinateSystem? coordinateSystem) + { + coordinateSystem = TryCreateCoordinateSystem(srid); + return coordinateSystem is not null; + } + + /// + /// Tries to resolve a horizontal datum by EPSG code from the generated catalog. + /// + /// The EPSG datum code. + /// The resolved datum when available. + /// when the datum could be created; otherwise . + internal static bool TryResolveHorizontalDatum(int datumCode, [NotNullWhen(true)] out HorizontalDatum? datum) + => TryCreateHorizontalDatum(datumCode, out datum); + + /// + /// Tries to resolve an ellipsoid by EPSG code from the generated catalog. + /// + /// The EPSG ellipsoid code. + /// The resolved ellipsoid when available. + /// when the ellipsoid could be created; otherwise . + internal static bool TryResolveEllipsoid(int ellipsoidCode, [NotNullWhen(true)] out Ellipsoid? ellipsoid) + => TryCreateEllipsoid(ellipsoidCode, out ellipsoid); + + private static CoordinateSystem? TryCreateCoordinateSystem(int srid) + { + if (!EpsgGeneratedCatalog.TryGetCoordinateReference(srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex)) + { + return null; + } + + CoordinateSystem? cached = Volatile.Read(ref CoordinateSystemCache[cacheIndex]); + if (cached is not null) + { + return cached; + } + + CoordinateSystem? created = CreateCoordinateSystem(reference); + if (created is null) + { + return null; + } + + lock (CoordinateSystemCacheSync) + { + if (CoordinateSystemCache[cacheIndex] is null) + { + CoordinateSystemCache[cacheIndex] = created; + } + + return CoordinateSystemCache[cacheIndex]; + } + } + + private static CoordinateSystem? CreateCoordinateSystem(EpsgCoordinateReferenceRecord reference) + { + switch (reference.Kind) + { + case EpsgCoordinateSystemKind.Geographic2D: + if (EpsgGeneratedCatalog.TryGetGeographicCrs(reference.RecordIndex, out EpsgGeographicCrsRecord geographicRecord)) + { + return CreateGeographic(geographicRecord); + } + + return null; + case EpsgCoordinateSystemKind.Geocentric: + if (EpsgGeneratedCatalog.TryGetGeocentricCrs(reference.RecordIndex, out EpsgGeocentricCrsRecord geocentricRecord)) + { + return CreateGeocentric(geocentricRecord); + } + + return null; + case EpsgCoordinateSystemKind.Projected: + if (EpsgGeneratedCatalog.TryGetProjectedCrs(reference.RecordIndex, out EpsgProjectedCrsRecord projectedRecord)) + { + return CreateProjected(projectedRecord); + } + + return null; + case EpsgCoordinateSystemKind.Vertical: + if (EpsgGeneratedCatalog.TryGetVerticalCrs(reference.RecordIndex, out EpsgVerticalCrsRecord verticalRecord)) + { + return CreateVertical(verticalRecord); + } + + return null; + case EpsgCoordinateSystemKind.Compound: + if (EpsgGeneratedCatalog.TryGetCompoundCrs(reference.RecordIndex, out EpsgCompoundCrsRecord compoundRecord)) + { + return CreateCompound(compoundRecord); + } + + return null; + default: + return null; + } + } + + private static GeographicCoordinateSystem? CreateGeographic(EpsgGeographicCrsRecord record) + { + if (!TryCreateHorizontalDatum(record.DatumCode, out HorizontalDatum? datum)) + { + return null; + } + + if (!TryGetGeodeticDatumRecord(record.DatumCode, out EpsgGeodeticDatumRecord geodeticDatumRecord)) + { + return null; + } + + if (!TryCreatePrimeMeridian(geodeticDatumRecord.PrimeMeridianCode, out PrimeMeridian? primeMeridian)) + { + return null; + } + + List? axes = GetAxes(record.CoordinateSystemCode, 2); + if (axes is null || axes.Count < 2) + { + return null; + } + + return !TryCreateAngularUnit(GetUnitCode(record.CoordinateSystemCode, 1), out AngularUnit? angularUnit) + ? null + : new GeographicCoordinateSystem( + angularUnit, + datum, + primeMeridian, + axes, + record.Name, + "EPSG", + record.Srid, + string.Empty, + string.Empty, + string.Empty); + } + + private static GeocentricCoordinateSystem? CreateGeocentric(EpsgGeocentricCrsRecord record) + { + if (!TryCreateHorizontalDatum(record.DatumCode, out HorizontalDatum? datum)) + { + return null; + } + + if (!TryGetGeodeticDatumRecord(record.DatumCode, out EpsgGeodeticDatumRecord geodeticDatumRecord)) + { + return null; + } + + if (!TryCreatePrimeMeridian(geodeticDatumRecord.PrimeMeridianCode, out PrimeMeridian? primeMeridian)) + { + return null; + } + + List? axes = GetAxes(record.CoordinateSystemCode, 3); + if (axes is null || axes.Count < 3) + { + return null; + } + + return !TryCreateLinearUnit(GetUnitCode(record.CoordinateSystemCode, 1), out LinearUnit? linearUnit) + ? null + : new GeocentricCoordinateSystem( + datum, + linearUnit, + primeMeridian, + axes, + record.Name, + "EPSG", + record.Srid, + string.Empty, + string.Empty, + string.Empty); + } + + private static ProjectedCoordinateSystem? CreateProjected(EpsgProjectedCrsRecord record) + { + var baseCoordinateSystem = TryCreateCoordinateSystem(record.BaseSrid) as GeographicCoordinateSystem; + if (baseCoordinateSystem is null) + { + return null; + } + + if (!TryCreateLinearUnit(GetUnitCode(record.CoordinateSystemCode, 1), out LinearUnit? linearUnit)) + { + return null; + } + + if (!EpsgGeneratedCatalog.TryGetConversion(record.ConversionCode, out EpsgConversionRecord conversion)) + { + return null; + } + + var parameters = new List(conversion.ParameterCount); + for (int i = 0; i < conversion.ParameterCount; i++) + { + if (!EpsgGeneratedCatalog.TryGetConversionParameter(record.ConversionCode, i, out EpsgConversionParameterRecord parameter)) + { + return null; + } + + parameters.Add(new ProjectionParameter(NormalizeProjectionParameterName(parameter.Name), parameter.Value)); + } + + string projectionName = NormalizeProjectionMethodName(conversion.MethodName); + var projection = new Projection(projectionName, parameters, projectionName, "EPSG", record.ConversionCode, string.Empty, string.Empty, string.Empty); + + List? axes = GetAxes(record.CoordinateSystemCode, 2); + return axes is null || axes.Count < 2 + ? null + : new ProjectedCoordinateSystem( + baseCoordinateSystem.HorizontalDatum, + baseCoordinateSystem, + linearUnit, + projection, + axes, + record.Name, + "EPSG", + record.Srid, + string.Empty, + string.Empty, + string.Empty); + } + + private static VerticalCoordinateSystem? CreateVertical(EpsgVerticalCrsRecord record) + { + if (!TryGetVerticalDatumRecord(record.DatumCode, out EpsgVerticalDatumRecord datumRecord)) + { + return null; + } + + List? axisInfo = GetAxes(record.CoordinateSystemCode, 1); + if (axisInfo is null || axisInfo.Count == 0) + { + return null; + } + + if (!TryCreateLinearUnit(GetUnitCode(record.CoordinateSystemCode, 1), out LinearUnit? linearUnit)) + { + return null; + } + + DatumType datumType = axisInfo[0].Orientation == AxisOrientationEnum.Down + ? DatumType.VD_Depth + : DatumType.VD_GeoidModelDerived; + var datum = new VerticalDatum(datumType, datumRecord.Name, "EPSG", datumRecord.Code, string.Empty, string.Empty, string.Empty); + + return new VerticalCoordinateSystem( + linearUnit, + datum, + axisInfo[0], + record.Name, + "EPSG", + record.Srid, + string.Empty, + string.Empty, + string.Empty); + } + + private static string NormalizeProjectionMethodName(string methodName) + { + if (string.IsNullOrWhiteSpace(methodName)) + { + return methodName; + } + + string normalized = methodName.ToLowerInvariant(); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "(", string.Empty); + normalized = StringCompatibility.ReplaceOrdinal(normalized, ")", string.Empty); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "-", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "/", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, " ", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, ".", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "__", "_"); + + return normalized switch + { + "polar_stereographic_variant_a" or "polar_stereographic_variant_b" => "Polar Stereographic", + _ => methodName, + }; + } + + private static string NormalizeProjectionParameterName(string parameterName) + { + if (string.IsNullOrWhiteSpace(parameterName)) + { + return parameterName; + } + + string normalized = parameterName.ToLowerInvariant(); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "(", string.Empty); + normalized = StringCompatibility.ReplaceOrdinal(normalized, ")", string.Empty); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "-", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "/", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, " ", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, ".", "_"); + normalized = StringCompatibility.ReplaceOrdinal(normalized, "__", "_"); + + return normalized switch + { + "longitude_of_natural_origin" or "longitude_of_false_origin" or "longitude_of_projection_centre" => "central_meridian", + "latitude_of_natural_origin" or "latitude_of_false_origin" or "latitude_of_projection_centre" => "latitude_of_origin", + "scale_factor_at_natural_origin" or "scale_factor_at_projection_centre" or "scale_factor_on_initial_line" => "scale_factor", + "easting_at_false_origin" or "easting_at_projection_centre" => "false_easting", + "northing_at_false_origin" or "northing_at_projection_centre" => "false_northing", + "latitude_of_1st_standard_parallel" => "standard_parallel_1", + "latitude_of_2nd_standard_parallel" => "standard_parallel_2", + _ => normalized, + }; + } + + private static CompoundCoordinateSystem? CreateCompound(EpsgCompoundCrsRecord record) + { + CoordinateSystem? horizontal = TryCreateCoordinateSystem(record.HorizontalSrid); + CoordinateSystem? vertical = TryCreateCoordinateSystem(record.VerticalSrid); + return horizontal is null || vertical is null + ? null + : new CompoundCoordinateSystem( + horizontal, + vertical, + record.Name, + "EPSG", + record.Srid, + string.Empty, + string.Empty, + string.Empty); + } + + private static bool TryCreateHorizontalDatum(int datumCode, [NotNullWhen(true)] out HorizontalDatum? datum) + { + datum = null; + if (!TryGetGeodeticDatumRecord(datumCode, out EpsgGeodeticDatumRecord datumRecord)) + { + return false; + } + + if (!TryCreateEllipsoid(datumRecord.EllipsoidCode, out Ellipsoid? ellipsoid)) + { + return false; + } + + datum = new HorizontalDatum( + ellipsoid, + null, + DatumType.HD_Geocentric, + datumRecord.Name, + "EPSG", + datumRecord.Code, + string.Empty, + string.Empty, + string.Empty); + return true; + } + + private static bool TryCreateEllipsoid(int ellipsoidCode, [NotNullWhen(true)] out Ellipsoid? ellipsoid) + { + ellipsoid = null; + if (!TryGetEllipsoidRecord(ellipsoidCode, out EpsgEllipsoidRecord record)) + { + return false; + } + + if (!TryCreateLinearUnit(record.UnitCode, out LinearUnit? linearUnit)) + { + return false; + } + + ellipsoid = new Ellipsoid( + record.SemiMajor, + record.SemiMinor, + record.InverseFlattening, + record.IsInverseFlatteningDefinitive, + linearUnit, + record.Name, + "EPSG", + record.Code, + string.Empty, + string.Empty, + string.Empty); + + return true; + } + + private static bool TryCreatePrimeMeridian(int primeMeridianCode, [NotNullWhen(true)] out PrimeMeridian? primeMeridian) + { + primeMeridian = null; + if (!TryGetPrimeMeridianRecord(primeMeridianCode, out EpsgPrimeMeridianRecord record)) + { + return false; + } + + if (!TryCreateAngularUnit(record.UnitCode, out AngularUnit? angularUnit)) + { + return false; + } + + primeMeridian = new PrimeMeridian( + record.Longitude, + angularUnit, + record.Name, + "EPSG", + record.Code, + string.Empty, + string.Empty, + string.Empty); + return true; + } + + private static bool TryCreateLinearUnit(int unitCode, [NotNullWhen(true)] out LinearUnit? unit) + { + unit = null; + if (!TryGetUnitRecord(unitCode, out EpsgUnitRecord record) || record.UnitType != 0) + { + return false; + } + + unit = new LinearUnit(record.Factor, record.Name, "EPSG", record.Code, string.Empty, string.Empty, string.Empty); + return true; + } + + private static bool TryCreateAngularUnit(int unitCode, [NotNullWhen(true)] out AngularUnit? unit) + { + unit = null; + if (!TryGetUnitRecord(unitCode, out EpsgUnitRecord record) || record.UnitType != 1) + { + return false; + } + + unit = new AngularUnit(record.Factor, record.Name, "EPSG", record.Code, string.Empty, string.Empty, string.Empty); + return true; + } + + private static List? GetAxes(int coordinateSystemCode, int expectedCount, bool includeAll = false) + { + if (!AxesByCoordinateSystemCode.Value.TryGetValue(coordinateSystemCode, out EpsgAxisRecord[]? orderedAxes)) + { + return null; + } + + if (orderedAxes.Length < expectedCount) + { + return null; + } + + int axisCount = includeAll ? orderedAxes.Length : expectedCount; + var axes = new List(axisCount); + for (int i = 0; i < axisCount; i++) + { + EpsgAxisRecord axis = orderedAxes[i]; + axes.Add(new AxisInfo(axis.Name, (AxisOrientationEnum)axis.Orientation)); + } + + return axes; + } + + private static int GetUnitCode(int coordinateSystemCode, int axisOrder) + { + if (!AxesByCoordinateSystemCode.Value.TryGetValue(coordinateSystemCode, out EpsgAxisRecord[]? axes)) + { + return -1; + } + + foreach (EpsgAxisRecord axis in axes) + { + if (axis.AxisOrder == axisOrder) + { + return axis.UnitCode; + } + } + + return -1; + } + + private static bool TryGetUnitRecord(int code, out EpsgUnitRecord record) + { + return UnitsByCode.Value.TryGetValue(code, out record); + } + + private static bool TryGetEllipsoidRecord(int code, out EpsgEllipsoidRecord record) + { + return EllipsoidsByCode.Value.TryGetValue(code, out record); + } + + private static bool TryGetPrimeMeridianRecord(int code, out EpsgPrimeMeridianRecord record) + { + return PrimeMeridiansByCode.Value.TryGetValue(code, out record); + } + + private static bool TryGetGeodeticDatumRecord(int code, out EpsgGeodeticDatumRecord record) + { + return GeodeticDatumsByCode.Value.TryGetValue(code, out record); + } + + private static bool TryGetVerticalDatumRecord(int code, out EpsgVerticalDatumRecord record) + { + return VerticalDatumsByCode.Value.TryGetValue(code, out record); + } + + private static Dictionary BuildUnitsByCode() + { + var dictionary = new Dictionary(EpsgGeneratedCatalog.Units.Length); + foreach (EpsgUnitRecord item in EpsgGeneratedCatalog.Units) + { + dictionary[item.Code] = item; + } + + return dictionary; + } + + private static Dictionary BuildEllipsoidsByCode() + { + var dictionary = new Dictionary(EpsgGeneratedCatalog.Ellipsoids.Length); + foreach (EpsgEllipsoidRecord item in EpsgGeneratedCatalog.Ellipsoids) + { + dictionary[item.Code] = item; + } + + return dictionary; + } + + private static Dictionary BuildPrimeMeridiansByCode() + { + var dictionary = new Dictionary(EpsgGeneratedCatalog.PrimeMeridians.Length); + foreach (EpsgPrimeMeridianRecord item in EpsgGeneratedCatalog.PrimeMeridians) + { + dictionary[item.Code] = item; + } + + return dictionary; + } + + private static Dictionary BuildGeodeticDatumsByCode() + { + var dictionary = new Dictionary(EpsgGeneratedCatalog.GeodeticDatums.Length); + foreach (EpsgGeodeticDatumRecord item in EpsgGeneratedCatalog.GeodeticDatums) + { + dictionary[item.Code] = item; + } + + return dictionary; + } + + private static Dictionary BuildVerticalDatumsByCode() + { + var dictionary = new Dictionary(EpsgGeneratedCatalog.VerticalDatums.Length); + foreach (EpsgVerticalDatumRecord item in EpsgGeneratedCatalog.VerticalDatums) + { + dictionary[item.Code] = item; + } + + return dictionary; + } + + private static Dictionary BuildAxesByCoordinateSystemCode() + { + var grouped = new Dictionary>(); + foreach (EpsgAxisRecord axis in EpsgGeneratedCatalog.Axes) + { + if (!grouped.TryGetValue(axis.CoordinateSystemCode, out List? axes)) + { + axes = []; + grouped[axis.CoordinateSystemCode] = axes; + } + + axes.Add(axis); + } + + var result = new Dictionary(grouped.Count); + foreach (KeyValuePair> item in grouped) + { + item.Value.Sort((left, right) => left.AxisOrder.CompareTo(right.AxisOrder)); + result[item.Key] = [.. item.Value]; + } + + return result; + } +} diff --git a/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Conversions.g.cs b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Conversions.g.cs new file mode 100644 index 00000000..bed6e995 --- /dev/null +++ b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Conversions.g.cs @@ -0,0 +1,63285 @@ +// +// Generated by tools\Generate-EpsgManagedData.ps1 +// Source: EPSG-v12_054-WKT.Zip +// +#pragma warning disable SA0001, SA1512, SA1518, SA1600, SA1614, SA1616, SA1633, SA1636 +using System; + +namespace ProjNet.Data.Generated +{ + internal static partial class EpsgGeneratedCatalog + { + internal static bool TryGetConversion(int code, out EpsgConversionRecord record) + { + switch (code / 1000) + { + case 3: + return TryGetConversionBucket3(code, out record); + case 4: + return TryGetConversionBucket4(code, out record); + case 5: + return TryGetConversionBucket5(code, out record); + case 6: + return TryGetConversionBucket6(code, out record); + case 7: + return TryGetConversionBucket7(code, out record); + case 8: + return TryGetConversionBucket8(code, out record); + case 9: + return TryGetConversionBucket9(code, out record); + case 10: + return TryGetConversionBucket10(code, out record); + case 11: + return TryGetConversionBucket11(code, out record); + case 12: + return TryGetConversionBucket12(code, out record); + case 13: + return TryGetConversionBucket13(code, out record); + case 14: + return TryGetConversionBucket14(code, out record); + case 15: + return TryGetConversionBucket15(code, out record); + case 16: + return TryGetConversionBucket16(code, out record); + case 17: + return TryGetConversionBucket17(code, out record); + case 18: + return TryGetConversionBucket18(code, out record); + case 19: + return TryGetConversionBucket19(code, out record); + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket3(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 3811: + record = new EpsgConversionRecord(3811, "Lambert Conic Conformal (2SP)", 6); + return true; + case 3813: + record = new EpsgConversionRecord(3813, "Transverse Mercator", 5); + return true; + case 3818: + record = new EpsgConversionRecord(3818, "Transverse Mercator", 5); + return true; + case 3820: + record = new EpsgConversionRecord(3820, "Transverse Mercator", 5); + return true; + case 3831: + record = new EpsgConversionRecord(3831, "Mercator (variant A)", 5); + return true; + case 3853: + record = new EpsgConversionRecord(3853, "Transverse Mercator", 5); + return true; + case 3856: + record = new EpsgConversionRecord(3856, "Popular Visualisation Pseudo Mercator", 4); + return true; + case 3860: + record = new EpsgConversionRecord(3860, "Transverse Mercator", 5); + return true; + case 3861: + record = new EpsgConversionRecord(3861, "Transverse Mercator", 5); + return true; + case 3862: + record = new EpsgConversionRecord(3862, "Transverse Mercator", 5); + return true; + case 3863: + record = new EpsgConversionRecord(3863, "Transverse Mercator", 5); + return true; + case 3864: + record = new EpsgConversionRecord(3864, "Transverse Mercator", 5); + return true; + case 3865: + record = new EpsgConversionRecord(3865, "Transverse Mercator", 5); + return true; + case 3866: + record = new EpsgConversionRecord(3866, "Transverse Mercator", 5); + return true; + case 3867: + record = new EpsgConversionRecord(3867, "Transverse Mercator", 5); + return true; + case 3868: + record = new EpsgConversionRecord(3868, "Transverse Mercator", 5); + return true; + case 3869: + record = new EpsgConversionRecord(3869, "Transverse Mercator", 5); + return true; + case 3870: + record = new EpsgConversionRecord(3870, "Transverse Mercator", 5); + return true; + case 3871: + record = new EpsgConversionRecord(3871, "Transverse Mercator", 5); + return true; + case 3872: + record = new EpsgConversionRecord(3872, "Transverse Mercator", 5); + return true; + case 3897: + record = new EpsgConversionRecord(3897, "Lambert Azimuthal Equal Area (Spherical)", 4); + return true; + case 3898: + record = new EpsgConversionRecord(3898, "Lambert Azimuthal Equal Area (Spherical)", 4); + return true; + case 3899: + record = new EpsgConversionRecord(3899, "Lambert Azimuthal Equal Area (Spherical)", 4); + return true; + case 3967: + record = new EpsgConversionRecord(3967, "Lambert Conic Conformal (2SP)", 6); + return true; + case 3977: + record = new EpsgConversionRecord(3977, "Lambert Conic Conformal (2SP)", 6); + return true; + case 3981: + record = new EpsgConversionRecord(3981, "Transverse Mercator", 5); + return true; + case 3982: + record = new EpsgConversionRecord(3982, "Transverse Mercator", 5); + return true; + case 3983: + record = new EpsgConversionRecord(3983, "Transverse Mercator", 5); + return true; + case 3984: + record = new EpsgConversionRecord(3984, "Transverse Mercator", 5); + return true; + case 3999: + record = new EpsgConversionRecord(3999, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket4(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 4085: + record = new EpsgConversionRecord(4085, "Equidistant Cylindrical", 4); + return true; + case 4089: + record = new EpsgConversionRecord(4089, "Transverse Mercator", 5); + return true; + case 4090: + record = new EpsgConversionRecord(4090, "Transverse Mercator", 5); + return true; + case 4091: + record = new EpsgConversionRecord(4091, "Transverse Mercator", 5); + return true; + case 4092: + record = new EpsgConversionRecord(4092, "Transverse Mercator", 5); + return true; + case 4101: + record = new EpsgConversionRecord(4101, "Transverse Mercator", 5); + return true; + case 4102: + record = new EpsgConversionRecord(4102, "Transverse Mercator", 5); + return true; + case 4103: + record = new EpsgConversionRecord(4103, "Transverse Mercator", 5); + return true; + case 4104: + record = new EpsgConversionRecord(4104, "Transverse Mercator", 5); + return true; + case 4105: + record = new EpsgConversionRecord(4105, "Transverse Mercator", 5); + return true; + case 4106: + record = new EpsgConversionRecord(4106, "Transverse Mercator", 5); + return true; + case 4107: + record = new EpsgConversionRecord(4107, "Transverse Mercator", 5); + return true; + case 4108: + record = new EpsgConversionRecord(4108, "Transverse Mercator", 5); + return true; + case 4109: + record = new EpsgConversionRecord(4109, "Transverse Mercator", 5); + return true; + case 4110: + record = new EpsgConversionRecord(4110, "Transverse Mercator", 5); + return true; + case 4111: + record = new EpsgConversionRecord(4111, "Transverse Mercator", 5); + return true; + case 4112: + record = new EpsgConversionRecord(4112, "Transverse Mercator", 5); + return true; + case 4113: + record = new EpsgConversionRecord(4113, "Transverse Mercator", 5); + return true; + case 4114: + record = new EpsgConversionRecord(4114, "Cassini-Soldner", 4); + return true; + case 4115: + record = new EpsgConversionRecord(4115, "Cassini-Soldner", 4); + return true; + case 4116: + record = new EpsgConversionRecord(4116, "Cassini-Soldner", 4); + return true; + case 4117: + record = new EpsgConversionRecord(4117, "Cassini-Soldner", 4); + return true; + case 4118: + record = new EpsgConversionRecord(4118, "Transverse Mercator", 5); + return true; + case 4119: + record = new EpsgConversionRecord(4119, "Transverse Mercator", 5); + return true; + case 4177: + record = new EpsgConversionRecord(4177, "Cassini-Soldner", 4); + return true; + case 4186: + record = new EpsgConversionRecord(4186, "Transverse Mercator", 5); + return true; + case 4187: + record = new EpsgConversionRecord(4187, "Transverse Mercator", 5); + return true; + case 4305: + record = new EpsgConversionRecord(4305, "Cassini-Soldner", 4); + return true; + case 4320: + record = new EpsgConversionRecord(4320, "Cassini-Soldner", 4); + return true; + case 4321: + record = new EpsgConversionRecord(4321, "Cassini-Soldner", 4); + return true; + case 4323: + record = new EpsgConversionRecord(4323, "Cassini-Soldner", 4); + return true; + case 4325: + record = new EpsgConversionRecord(4325, "Transverse Mercator", 5); + return true; + case 4416: + record = new EpsgConversionRecord(4416, "Lambert Conic Conformal (2SP)", 6); + return true; + case 4436: + record = new EpsgConversionRecord(4436, "Lambert Conic Conformal (2SP)", 6); + return true; + case 4454: + record = new EpsgConversionRecord(4454, "Lambert Conic Conformal (2SP)", 6); + return true; + case 4460: + record = new EpsgConversionRecord(4460, "Lambert Conic Conformal (2SP)", 6); + return true; + case 4648: + record = new EpsgConversionRecord(4648, "Transverse Mercator", 5); + return true; + case 4825: + record = new EpsgConversionRecord(4825, "Lambert Conic Conformal (2SP)", 6); + return true; + case 4838: + record = new EpsgConversionRecord(4838, "Lambert Conic Conformal (2SP)", 6); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket5(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 5019: + record = new EpsgConversionRecord(5019, "Bonne (South Orientated)", 4); + return true; + case 5020: + record = new EpsgConversionRecord(5020, "Transverse Mercator", 5); + return true; + case 5049: + record = new EpsgConversionRecord(5049, "Transverse Mercator", 5); + return true; + case 5068: + record = new EpsgConversionRecord(5068, "Albers Equal Area", 6); + return true; + case 5100: + record = new EpsgConversionRecord(5100, "Transverse Mercator", 5); + return true; + case 5101: + record = new EpsgConversionRecord(5101, "Transverse Mercator", 5); + return true; + case 5102: + record = new EpsgConversionRecord(5102, "Transverse Mercator", 5); + return true; + case 5103: + record = new EpsgConversionRecord(5103, "Transverse Mercator", 5); + return true; + case 5104: + record = new EpsgConversionRecord(5104, "Transverse Mercator", 5); + return true; + case 5131: + record = new EpsgConversionRecord(5131, "Transverse Mercator", 5); + return true; + case 5135: + record = new EpsgConversionRecord(5135, "Transverse Mercator", 5); + return true; + case 5136: + record = new EpsgConversionRecord(5136, "Transverse Mercator", 5); + return true; + case 5137: + record = new EpsgConversionRecord(5137, "Transverse Mercator", 5); + return true; + case 5138: + record = new EpsgConversionRecord(5138, "Transverse Mercator", 5); + return true; + case 5139: + record = new EpsgConversionRecord(5139, "Transverse Mercator", 5); + return true; + case 5140: + record = new EpsgConversionRecord(5140, "Transverse Mercator", 5); + return true; + case 5141: + record = new EpsgConversionRecord(5141, "Transverse Mercator", 5); + return true; + case 5142: + record = new EpsgConversionRecord(5142, "Transverse Mercator", 5); + return true; + case 5143: + record = new EpsgConversionRecord(5143, "Transverse Mercator", 5); + return true; + case 5144: + record = new EpsgConversionRecord(5144, "Transverse Mercator", 5); + return true; + case 5145: + record = new EpsgConversionRecord(5145, "Transverse Mercator", 5); + return true; + case 5146: + record = new EpsgConversionRecord(5146, "Transverse Mercator", 5); + return true; + case 5147: + record = new EpsgConversionRecord(5147, "Transverse Mercator", 5); + return true; + case 5148: + record = new EpsgConversionRecord(5148, "Transverse Mercator", 5); + return true; + case 5149: + record = new EpsgConversionRecord(5149, "Transverse Mercator", 5); + return true; + case 5150: + record = new EpsgConversionRecord(5150, "Transverse Mercator", 5); + return true; + case 5151: + record = new EpsgConversionRecord(5151, "Transverse Mercator", 5); + return true; + case 5152: + record = new EpsgConversionRecord(5152, "Transverse Mercator", 5); + return true; + case 5153: + record = new EpsgConversionRecord(5153, "Transverse Mercator", 5); + return true; + case 5154: + record = new EpsgConversionRecord(5154, "Transverse Mercator", 5); + return true; + case 5155: + record = new EpsgConversionRecord(5155, "Transverse Mercator", 5); + return true; + case 5156: + record = new EpsgConversionRecord(5156, "Transverse Mercator", 5); + return true; + case 5157: + record = new EpsgConversionRecord(5157, "Transverse Mercator", 5); + return true; + case 5158: + record = new EpsgConversionRecord(5158, "Transverse Mercator", 5); + return true; + case 5159: + record = new EpsgConversionRecord(5159, "Transverse Mercator", 5); + return true; + case 5160: + record = new EpsgConversionRecord(5160, "Transverse Mercator", 5); + return true; + case 5161: + record = new EpsgConversionRecord(5161, "Transverse Mercator", 5); + return true; + case 5162: + record = new EpsgConversionRecord(5162, "Transverse Mercator", 5); + return true; + case 5163: + record = new EpsgConversionRecord(5163, "Transverse Mercator", 5); + return true; + case 5164: + record = new EpsgConversionRecord(5164, "Transverse Mercator", 5); + return true; + case 5165: + record = new EpsgConversionRecord(5165, "Transverse Mercator", 5); + return true; + case 5218: + record = new EpsgConversionRecord(5218, "Krovak (North Orientated)", 7); + return true; + case 5219: + record = new EpsgConversionRecord(5219, "Krovak Modified", 19); + return true; + case 5220: + record = new EpsgConversionRecord(5220, "Krovak Modified (North Orientated)", 19); + return true; + case 5222: + record = new EpsgConversionRecord(5222, "Transverse Mercator", 5); + return true; + case 5231: + record = new EpsgConversionRecord(5231, "Transverse Mercator", 5); + return true; + case 5232: + record = new EpsgConversionRecord(5232, "Transverse Mercator", 5); + return true; + case 5265: + record = new EpsgConversionRecord(5265, "Transverse Mercator", 5); + return true; + case 5268: + record = new EpsgConversionRecord(5268, "Transverse Mercator", 5); + return true; + case 5276: + record = new EpsgConversionRecord(5276, "Transverse Mercator", 5); + return true; + case 5277: + record = new EpsgConversionRecord(5277, "Transverse Mercator", 5); + return true; + case 5278: + record = new EpsgConversionRecord(5278, "Transverse Mercator", 5); + return true; + case 5279: + record = new EpsgConversionRecord(5279, "Transverse Mercator", 5); + return true; + case 5280: + record = new EpsgConversionRecord(5280, "Transverse Mercator", 5); + return true; + case 5281: + record = new EpsgConversionRecord(5281, "Transverse Mercator", 5); + return true; + case 5282: + record = new EpsgConversionRecord(5282, "Transverse Mercator", 5); + return true; + case 5283: + record = new EpsgConversionRecord(5283, "Transverse Mercator", 5); + return true; + case 5284: + record = new EpsgConversionRecord(5284, "Transverse Mercator", 5); + return true; + case 5285: + record = new EpsgConversionRecord(5285, "Transverse Mercator", 5); + return true; + case 5286: + record = new EpsgConversionRecord(5286, "Transverse Mercator", 5); + return true; + case 5287: + record = new EpsgConversionRecord(5287, "Transverse Mercator", 5); + return true; + case 5288: + record = new EpsgConversionRecord(5288, "Transverse Mercator", 5); + return true; + case 5289: + record = new EpsgConversionRecord(5289, "Transverse Mercator", 5); + return true; + case 5290: + record = new EpsgConversionRecord(5290, "Transverse Mercator", 5); + return true; + case 5291: + record = new EpsgConversionRecord(5291, "Transverse Mercator", 5); + return true; + case 5312: + record = new EpsgConversionRecord(5312, "Transverse Mercator", 5); + return true; + case 5313: + record = new EpsgConversionRecord(5313, "Transverse Mercator", 5); + return true; + case 5314: + record = new EpsgConversionRecord(5314, "Transverse Mercator", 5); + return true; + case 5315: + record = new EpsgConversionRecord(5315, "Transverse Mercator", 5); + return true; + case 5319: + record = new EpsgConversionRecord(5319, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5326: + record = new EpsgConversionRecord(5326, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5328: + record = new EpsgConversionRecord(5328, "Mercator (variant A)", 5); + return true; + case 5366: + record = new EpsgConversionRecord(5366, "Transverse Mercator", 5); + return true; + case 5390: + record = new EpsgConversionRecord(5390, "Lambert Conic Conformal (1SP)", 5); + return true; + case 5394: + record = new EpsgConversionRecord(5394, "Lambert Conic Conformal (1SP)", 5); + return true; + case 5399: + record = new EpsgConversionRecord(5399, "Lambert Conic Conformal (1SP)", 5); + return true; + case 5439: + record = new EpsgConversionRecord(5439, "Lambert Conic Conformal (1SP)", 5); + return true; + case 5444: + record = new EpsgConversionRecord(5444, "Lambert Conic Conformal (1SP)", 5); + return true; + case 5465: + record = new EpsgConversionRecord(5465, "Transverse Mercator", 5); + return true; + case 5468: + record = new EpsgConversionRecord(5468, "Lambert Conic Conformal (1SP)", 5); + return true; + case 5471: + record = new EpsgConversionRecord(5471, "American Polyconic", 4); + return true; + case 5475: + record = new EpsgConversionRecord(5475, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5476: + record = new EpsgConversionRecord(5476, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5477: + record = new EpsgConversionRecord(5477, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5478: + record = new EpsgConversionRecord(5478, "Polar Stereographic (variant A)", 5); + return true; + case 5509: + record = new EpsgConversionRecord(5509, "Krovak", 7); + return true; + case 5510: + record = new EpsgConversionRecord(5510, "Krovak (North Orientated)", 7); + return true; + case 5511: + record = new EpsgConversionRecord(5511, "Krovak Modified", 19); + return true; + case 5512: + record = new EpsgConversionRecord(5512, "Krovak Modified (North Orientated)", 19); + return true; + case 5517: + record = new EpsgConversionRecord(5517, "Transverse Mercator", 5); + return true; + case 5522: + record = new EpsgConversionRecord(5522, "Transverse Mercator", 5); + return true; + case 5547: + record = new EpsgConversionRecord(5547, "Transverse Mercator", 5); + return true; + case 5548: + record = new EpsgConversionRecord(5548, "Transverse Mercator", 5); + return true; + case 5549: + record = new EpsgConversionRecord(5549, "Transverse Mercator", 5); + return true; + case 5587: + record = new EpsgConversionRecord(5587, "Oblique Stereographic", 5); + return true; + case 5595: + record = new EpsgConversionRecord(5595, "Transverse Mercator", 5); + return true; + case 5640: + record = new EpsgConversionRecord(5640, "Mercator (variant B)", 4); + return true; + case 5642: + record = new EpsgConversionRecord(5642, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5645: + record = new EpsgConversionRecord(5645, "Transverse Mercator", 5); + return true; + case 5647: + record = new EpsgConversionRecord(5647, "Transverse Mercator", 5); + return true; + case 5648: + record = new EpsgConversionRecord(5648, "Transverse Mercator", 5); + return true; + case 5658: + record = new EpsgConversionRecord(5658, "Transverse Mercator", 5); + return true; + case 5824: + record = new EpsgConversionRecord(5824, "Transverse Mercator", 5); + return true; + case 5883: + record = new EpsgConversionRecord(5883, "Transverse Mercator", 5); + return true; + case 5892: + record = new EpsgConversionRecord(5892, "Transverse Mercator", 5); + return true; + case 5893: + record = new EpsgConversionRecord(5893, "Transverse Mercator", 5); + return true; + case 5894: + record = new EpsgConversionRecord(5894, "Transverse Mercator", 5); + return true; + case 5895: + record = new EpsgConversionRecord(5895, "Transverse Mercator", 5); + return true; + case 5901: + record = new EpsgConversionRecord(5901, "Polar Stereographic (variant A)", 5); + return true; + case 5902: + record = new EpsgConversionRecord(5902, "Polar Stereographic (variant A)", 5); + return true; + case 5903: + record = new EpsgConversionRecord(5903, "Polar Stereographic (variant A)", 5); + return true; + case 5904: + record = new EpsgConversionRecord(5904, "Polar Stereographic (variant A)", 5); + return true; + case 5905: + record = new EpsgConversionRecord(5905, "Polar Stereographic (variant A)", 5); + return true; + case 5906: + record = new EpsgConversionRecord(5906, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5907: + record = new EpsgConversionRecord(5907, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5908: + record = new EpsgConversionRecord(5908, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5909: + record = new EpsgConversionRecord(5909, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5910: + record = new EpsgConversionRecord(5910, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5911: + record = new EpsgConversionRecord(5911, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5912: + record = new EpsgConversionRecord(5912, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5913: + record = new EpsgConversionRecord(5913, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5914: + record = new EpsgConversionRecord(5914, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5915: + record = new EpsgConversionRecord(5915, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5916: + record = new EpsgConversionRecord(5916, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5917: + record = new EpsgConversionRecord(5917, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5918: + record = new EpsgConversionRecord(5918, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5919: + record = new EpsgConversionRecord(5919, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5920: + record = new EpsgConversionRecord(5920, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5943: + record = new EpsgConversionRecord(5943, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5944: + record = new EpsgConversionRecord(5944, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5977: + record = new EpsgConversionRecord(5977, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5978: + record = new EpsgConversionRecord(5978, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5979: + record = new EpsgConversionRecord(5979, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5980: + record = new EpsgConversionRecord(5980, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5981: + record = new EpsgConversionRecord(5981, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5982: + record = new EpsgConversionRecord(5982, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5983: + record = new EpsgConversionRecord(5983, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5984: + record = new EpsgConversionRecord(5984, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5985: + record = new EpsgConversionRecord(5985, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5986: + record = new EpsgConversionRecord(5986, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5987: + record = new EpsgConversionRecord(5987, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5988: + record = new EpsgConversionRecord(5988, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5989: + record = new EpsgConversionRecord(5989, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5990: + record = new EpsgConversionRecord(5990, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5991: + record = new EpsgConversionRecord(5991, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5992: + record = new EpsgConversionRecord(5992, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5993: + record = new EpsgConversionRecord(5993, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5994: + record = new EpsgConversionRecord(5994, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5995: + record = new EpsgConversionRecord(5995, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5996: + record = new EpsgConversionRecord(5996, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5997: + record = new EpsgConversionRecord(5997, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5998: + record = new EpsgConversionRecord(5998, "Lambert Conic Conformal (2SP)", 6); + return true; + case 5999: + record = new EpsgConversionRecord(5999, "Lambert Conic Conformal (2SP)", 6); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket6(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 6000: + record = new EpsgConversionRecord(6000, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6001: + record = new EpsgConversionRecord(6001, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6002: + record = new EpsgConversionRecord(6002, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6003: + record = new EpsgConversionRecord(6003, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6004: + record = new EpsgConversionRecord(6004, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6005: + record = new EpsgConversionRecord(6005, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6006: + record = new EpsgConversionRecord(6006, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6007: + record = new EpsgConversionRecord(6007, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6008: + record = new EpsgConversionRecord(6008, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6009: + record = new EpsgConversionRecord(6009, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6010: + record = new EpsgConversionRecord(6010, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6011: + record = new EpsgConversionRecord(6011, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6012: + record = new EpsgConversionRecord(6012, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6013: + record = new EpsgConversionRecord(6013, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6014: + record = new EpsgConversionRecord(6014, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6015: + record = new EpsgConversionRecord(6015, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6016: + record = new EpsgConversionRecord(6016, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6017: + record = new EpsgConversionRecord(6017, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6018: + record = new EpsgConversionRecord(6018, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6019: + record = new EpsgConversionRecord(6019, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6020: + record = new EpsgConversionRecord(6020, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6021: + record = new EpsgConversionRecord(6021, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6022: + record = new EpsgConversionRecord(6022, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6023: + record = new EpsgConversionRecord(6023, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6024: + record = new EpsgConversionRecord(6024, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6025: + record = new EpsgConversionRecord(6025, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6026: + record = new EpsgConversionRecord(6026, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6027: + record = new EpsgConversionRecord(6027, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6028: + record = new EpsgConversionRecord(6028, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6029: + record = new EpsgConversionRecord(6029, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6030: + record = new EpsgConversionRecord(6030, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6031: + record = new EpsgConversionRecord(6031, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6032: + record = new EpsgConversionRecord(6032, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6033: + record = new EpsgConversionRecord(6033, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6034: + record = new EpsgConversionRecord(6034, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6035: + record = new EpsgConversionRecord(6035, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6036: + record = new EpsgConversionRecord(6036, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6037: + record = new EpsgConversionRecord(6037, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6038: + record = new EpsgConversionRecord(6038, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6039: + record = new EpsgConversionRecord(6039, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6040: + record = new EpsgConversionRecord(6040, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6041: + record = new EpsgConversionRecord(6041, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6042: + record = new EpsgConversionRecord(6042, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6043: + record = new EpsgConversionRecord(6043, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6044: + record = new EpsgConversionRecord(6044, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6045: + record = new EpsgConversionRecord(6045, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6046: + record = new EpsgConversionRecord(6046, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6047: + record = new EpsgConversionRecord(6047, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6048: + record = new EpsgConversionRecord(6048, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6049: + record = new EpsgConversionRecord(6049, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6127: + record = new EpsgConversionRecord(6127, "Transverse Mercator", 5); + return true; + case 6198: + record = new EpsgConversionRecord(6198, "Lambert Conic Conformal (2SP Michigan)", 7); + return true; + case 6199: + record = new EpsgConversionRecord(6199, "Lambert Conic Conformal (2SP Michigan)", 7); + return true; + case 6203: + record = new EpsgConversionRecord(6203, "Transverse Mercator", 5); + return true; + case 6212: + record = new EpsgConversionRecord(6212, "Colombia Urban", 5); + return true; + case 6213: + record = new EpsgConversionRecord(6213, "Colombia Urban", 5); + return true; + case 6214: + record = new EpsgConversionRecord(6214, "Colombia Urban", 5); + return true; + case 6215: + record = new EpsgConversionRecord(6215, "Colombia Urban", 5); + return true; + case 6216: + record = new EpsgConversionRecord(6216, "Colombia Urban", 5); + return true; + case 6217: + record = new EpsgConversionRecord(6217, "Colombia Urban", 5); + return true; + case 6218: + record = new EpsgConversionRecord(6218, "Colombia Urban", 5); + return true; + case 6219: + record = new EpsgConversionRecord(6219, "Colombia Urban", 5); + return true; + case 6220: + record = new EpsgConversionRecord(6220, "Colombia Urban", 5); + return true; + case 6221: + record = new EpsgConversionRecord(6221, "Colombia Urban", 5); + return true; + case 6222: + record = new EpsgConversionRecord(6222, "Colombia Urban", 5); + return true; + case 6223: + record = new EpsgConversionRecord(6223, "Colombia Urban", 5); + return true; + case 6224: + record = new EpsgConversionRecord(6224, "Colombia Urban", 5); + return true; + case 6225: + record = new EpsgConversionRecord(6225, "Colombia Urban", 5); + return true; + case 6226: + record = new EpsgConversionRecord(6226, "Colombia Urban", 5); + return true; + case 6227: + record = new EpsgConversionRecord(6227, "Colombia Urban", 5); + return true; + case 6228: + record = new EpsgConversionRecord(6228, "Colombia Urban", 5); + return true; + case 6229: + record = new EpsgConversionRecord(6229, "Colombia Urban", 5); + return true; + case 6230: + record = new EpsgConversionRecord(6230, "Colombia Urban", 5); + return true; + case 6231: + record = new EpsgConversionRecord(6231, "Colombia Urban", 5); + return true; + case 6232: + record = new EpsgConversionRecord(6232, "Colombia Urban", 5); + return true; + case 6233: + record = new EpsgConversionRecord(6233, "Colombia Urban", 5); + return true; + case 6234: + record = new EpsgConversionRecord(6234, "Colombia Urban", 5); + return true; + case 6235: + record = new EpsgConversionRecord(6235, "Colombia Urban", 5); + return true; + case 6236: + record = new EpsgConversionRecord(6236, "Colombia Urban", 5); + return true; + case 6237: + record = new EpsgConversionRecord(6237, "Colombia Urban", 5); + return true; + case 6238: + record = new EpsgConversionRecord(6238, "Colombia Urban", 5); + return true; + case 6239: + record = new EpsgConversionRecord(6239, "Colombia Urban", 5); + return true; + case 6240: + record = new EpsgConversionRecord(6240, "Colombia Urban", 5); + return true; + case 6241: + record = new EpsgConversionRecord(6241, "Colombia Urban", 5); + return true; + case 6242: + record = new EpsgConversionRecord(6242, "Colombia Urban", 5); + return true; + case 6243: + record = new EpsgConversionRecord(6243, "Colombia Urban", 5); + return true; + case 6308: + record = new EpsgConversionRecord(6308, "Transverse Mercator", 5); + return true; + case 6361: + record = new EpsgConversionRecord(6361, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6374: + record = new EpsgConversionRecord(6374, "Transverse Mercator", 5); + return true; + case 6375: + record = new EpsgConversionRecord(6375, "Transverse Mercator", 5); + return true; + case 6376: + record = new EpsgConversionRecord(6376, "Transverse Mercator", 5); + return true; + case 6377: + record = new EpsgConversionRecord(6377, "Transverse Mercator", 5); + return true; + case 6378: + record = new EpsgConversionRecord(6378, "Transverse Mercator", 5); + return true; + case 6379: + record = new EpsgConversionRecord(6379, "Transverse Mercator", 5); + return true; + case 6380: + record = new EpsgConversionRecord(6380, "Transverse Mercator", 5); + return true; + case 6390: + record = new EpsgConversionRecord(6390, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6645: + record = new EpsgConversionRecord(6645, "Albers Equal Area", 6); + return true; + case 6702: + record = new EpsgConversionRecord(6702, "Transverse Mercator", 5); + return true; + case 6716: + record = new EpsgConversionRecord(6716, "Transverse Mercator", 5); + return true; + case 6717: + record = new EpsgConversionRecord(6717, "Transverse Mercator", 5); + return true; + case 6718: + record = new EpsgConversionRecord(6718, "Transverse Mercator", 5); + return true; + case 6719: + record = new EpsgConversionRecord(6719, "Transverse Mercator", 5); + return true; + case 6729: + record = new EpsgConversionRecord(6729, "Transverse Mercator", 5); + return true; + case 6730: + record = new EpsgConversionRecord(6730, "Transverse Mercator", 5); + return true; + case 6731: + record = new EpsgConversionRecord(6731, "Transverse Mercator", 5); + return true; + case 6741: + record = new EpsgConversionRecord(6741, "Transverse Mercator", 5); + return true; + case 6742: + record = new EpsgConversionRecord(6742, "Transverse Mercator", 5); + return true; + case 6743: + record = new EpsgConversionRecord(6743, "Transverse Mercator", 5); + return true; + case 6744: + record = new EpsgConversionRecord(6744, "Transverse Mercator", 5); + return true; + case 6745: + record = new EpsgConversionRecord(6745, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6746: + record = new EpsgConversionRecord(6746, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6747: + record = new EpsgConversionRecord(6747, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6748: + record = new EpsgConversionRecord(6748, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6749: + record = new EpsgConversionRecord(6749, "Transverse Mercator", 5); + return true; + case 6750: + record = new EpsgConversionRecord(6750, "Transverse Mercator", 5); + return true; + case 6751: + record = new EpsgConversionRecord(6751, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6752: + record = new EpsgConversionRecord(6752, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6753: + record = new EpsgConversionRecord(6753, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 6754: + record = new EpsgConversionRecord(6754, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 6755: + record = new EpsgConversionRecord(6755, "Transverse Mercator", 5); + return true; + case 6756: + record = new EpsgConversionRecord(6756, "Transverse Mercator", 5); + return true; + case 6757: + record = new EpsgConversionRecord(6757, "Transverse Mercator", 5); + return true; + case 6758: + record = new EpsgConversionRecord(6758, "Transverse Mercator", 5); + return true; + case 6759: + record = new EpsgConversionRecord(6759, "Transverse Mercator", 5); + return true; + case 6760: + record = new EpsgConversionRecord(6760, "Transverse Mercator", 5); + return true; + case 6761: + record = new EpsgConversionRecord(6761, "Transverse Mercator", 5); + return true; + case 6762: + record = new EpsgConversionRecord(6762, "Transverse Mercator", 5); + return true; + case 6763: + record = new EpsgConversionRecord(6763, "Transverse Mercator", 5); + return true; + case 6764: + record = new EpsgConversionRecord(6764, "Transverse Mercator", 5); + return true; + case 6765: + record = new EpsgConversionRecord(6765, "Transverse Mercator", 5); + return true; + case 6766: + record = new EpsgConversionRecord(6766, "Transverse Mercator", 5); + return true; + case 6767: + record = new EpsgConversionRecord(6767, "Transverse Mercator", 5); + return true; + case 6768: + record = new EpsgConversionRecord(6768, "Transverse Mercator", 5); + return true; + case 6769: + record = new EpsgConversionRecord(6769, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 6770: + record = new EpsgConversionRecord(6770, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 6771: + record = new EpsgConversionRecord(6771, "Transverse Mercator", 5); + return true; + case 6772: + record = new EpsgConversionRecord(6772, "Transverse Mercator", 5); + return true; + case 6773: + record = new EpsgConversionRecord(6773, "Transverse Mercator", 5); + return true; + case 6774: + record = new EpsgConversionRecord(6774, "Transverse Mercator", 5); + return true; + case 6775: + record = new EpsgConversionRecord(6775, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6776: + record = new EpsgConversionRecord(6776, "Lambert Conic Conformal (1SP)", 5); + return true; + case 6777: + record = new EpsgConversionRecord(6777, "Transverse Mercator", 5); + return true; + case 6778: + record = new EpsgConversionRecord(6778, "Transverse Mercator", 5); + return true; + case 6779: + record = new EpsgConversionRecord(6779, "Transverse Mercator", 5); + return true; + case 6780: + record = new EpsgConversionRecord(6780, "Transverse Mercator", 5); + return true; + case 6869: + record = new EpsgConversionRecord(6869, "Transverse Mercator", 5); + return true; + case 6877: + record = new EpsgConversionRecord(6877, "Transverse Mercator", 5); + return true; + case 6878: + record = new EpsgConversionRecord(6878, "Transverse Mercator", 5); + return true; + case 6920: + record = new EpsgConversionRecord(6920, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6921: + record = new EpsgConversionRecord(6921, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6928: + record = new EpsgConversionRecord(6928, "Lambert Cylindrical Equal Area", 4); + return true; + case 6929: + record = new EpsgConversionRecord(6929, "Lambert Azimuthal Equal Area", 4); + return true; + case 6930: + record = new EpsgConversionRecord(6930, "Lambert Azimuthal Equal Area", 4); + return true; + case 6961: + record = new EpsgConversionRecord(6961, "Lambert Conic Conformal (2SP)", 6); + return true; + case 6965: + record = new EpsgConversionRecord(6965, "Lambert Conic Conformal (2SP Michigan)", 7); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket7(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 7043: + record = new EpsgConversionRecord(7043, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7044: + record = new EpsgConversionRecord(7044, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7045: + record = new EpsgConversionRecord(7045, "Transverse Mercator", 5); + return true; + case 7046: + record = new EpsgConversionRecord(7046, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7047: + record = new EpsgConversionRecord(7047, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7048: + record = new EpsgConversionRecord(7048, "Transverse Mercator", 5); + return true; + case 7049: + record = new EpsgConversionRecord(7049, "Transverse Mercator", 5); + return true; + case 7050: + record = new EpsgConversionRecord(7050, "Transverse Mercator", 5); + return true; + case 7051: + record = new EpsgConversionRecord(7051, "Transverse Mercator", 5); + return true; + case 7052: + record = new EpsgConversionRecord(7052, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7053: + record = new EpsgConversionRecord(7053, "Transverse Mercator", 5); + return true; + case 7054: + record = new EpsgConversionRecord(7054, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7055: + record = new EpsgConversionRecord(7055, "Transverse Mercator", 5); + return true; + case 7056: + record = new EpsgConversionRecord(7056, "Transverse Mercator", 5); + return true; + case 7089: + record = new EpsgConversionRecord(7089, "Transverse Mercator", 5); + return true; + case 7090: + record = new EpsgConversionRecord(7090, "Transverse Mercator", 5); + return true; + case 7091: + record = new EpsgConversionRecord(7091, "Transverse Mercator", 5); + return true; + case 7092: + record = new EpsgConversionRecord(7092, "Transverse Mercator", 5); + return true; + case 7093: + record = new EpsgConversionRecord(7093, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7094: + record = new EpsgConversionRecord(7094, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7095: + record = new EpsgConversionRecord(7095, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7096: + record = new EpsgConversionRecord(7096, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7097: + record = new EpsgConversionRecord(7097, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7098: + record = new EpsgConversionRecord(7098, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7099: + record = new EpsgConversionRecord(7099, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7100: + record = new EpsgConversionRecord(7100, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7101: + record = new EpsgConversionRecord(7101, "Transverse Mercator", 5); + return true; + case 7102: + record = new EpsgConversionRecord(7102, "Transverse Mercator", 5); + return true; + case 7103: + record = new EpsgConversionRecord(7103, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7104: + record = new EpsgConversionRecord(7104, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7105: + record = new EpsgConversionRecord(7105, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7106: + record = new EpsgConversionRecord(7106, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7107: + record = new EpsgConversionRecord(7107, "Transverse Mercator", 5); + return true; + case 7108: + record = new EpsgConversionRecord(7108, "Transverse Mercator", 5); + return true; + case 7129: + record = new EpsgConversionRecord(7129, "Transverse Mercator", 5); + return true; + case 7130: + record = new EpsgConversionRecord(7130, "Transverse Mercator", 5); + return true; + case 7141: + record = new EpsgConversionRecord(7141, "Transverse Mercator", 5); + return true; + case 7143: + record = new EpsgConversionRecord(7143, "Transverse Mercator", 5); + return true; + case 7144: + record = new EpsgConversionRecord(7144, "Transverse Mercator", 5); + return true; + case 7145: + record = new EpsgConversionRecord(7145, "Transverse Mercator", 5); + return true; + case 7146: + record = new EpsgConversionRecord(7146, "Transverse Mercator", 5); + return true; + case 7147: + record = new EpsgConversionRecord(7147, "Transverse Mercator", 5); + return true; + case 7148: + record = new EpsgConversionRecord(7148, "Transverse Mercator", 5); + return true; + case 7149: + record = new EpsgConversionRecord(7149, "Transverse Mercator", 5); + return true; + case 7150: + record = new EpsgConversionRecord(7150, "Transverse Mercator", 5); + return true; + case 7151: + record = new EpsgConversionRecord(7151, "Transverse Mercator", 5); + return true; + case 7152: + record = new EpsgConversionRecord(7152, "Transverse Mercator", 5); + return true; + case 7153: + record = new EpsgConversionRecord(7153, "Transverse Mercator", 5); + return true; + case 7154: + record = new EpsgConversionRecord(7154, "Transverse Mercator", 5); + return true; + case 7155: + record = new EpsgConversionRecord(7155, "Transverse Mercator", 5); + return true; + case 7156: + record = new EpsgConversionRecord(7156, "Transverse Mercator", 5); + return true; + case 7157: + record = new EpsgConversionRecord(7157, "Transverse Mercator", 5); + return true; + case 7158: + record = new EpsgConversionRecord(7158, "Transverse Mercator", 5); + return true; + case 7159: + record = new EpsgConversionRecord(7159, "Transverse Mercator", 5); + return true; + case 7160: + record = new EpsgConversionRecord(7160, "Transverse Mercator", 5); + return true; + case 7161: + record = new EpsgConversionRecord(7161, "Transverse Mercator", 5); + return true; + case 7162: + record = new EpsgConversionRecord(7162, "Transverse Mercator", 5); + return true; + case 7163: + record = new EpsgConversionRecord(7163, "Transverse Mercator", 5); + return true; + case 7164: + record = new EpsgConversionRecord(7164, "Transverse Mercator", 5); + return true; + case 7165: + record = new EpsgConversionRecord(7165, "Transverse Mercator", 5); + return true; + case 7166: + record = new EpsgConversionRecord(7166, "Transverse Mercator", 5); + return true; + case 7167: + record = new EpsgConversionRecord(7167, "Transverse Mercator", 5); + return true; + case 7168: + record = new EpsgConversionRecord(7168, "Transverse Mercator", 5); + return true; + case 7169: + record = new EpsgConversionRecord(7169, "Transverse Mercator", 5); + return true; + case 7170: + record = new EpsgConversionRecord(7170, "Transverse Mercator", 5); + return true; + case 7171: + record = new EpsgConversionRecord(7171, "Transverse Mercator", 5); + return true; + case 7172: + record = new EpsgConversionRecord(7172, "Transverse Mercator", 5); + return true; + case 7173: + record = new EpsgConversionRecord(7173, "Transverse Mercator", 5); + return true; + case 7174: + record = new EpsgConversionRecord(7174, "Transverse Mercator", 5); + return true; + case 7175: + record = new EpsgConversionRecord(7175, "Transverse Mercator", 5); + return true; + case 7176: + record = new EpsgConversionRecord(7176, "Transverse Mercator", 5); + return true; + case 7177: + record = new EpsgConversionRecord(7177, "Transverse Mercator", 5); + return true; + case 7178: + record = new EpsgConversionRecord(7178, "Transverse Mercator", 5); + return true; + case 7179: + record = new EpsgConversionRecord(7179, "Transverse Mercator", 5); + return true; + case 7180: + record = new EpsgConversionRecord(7180, "Transverse Mercator", 5); + return true; + case 7181: + record = new EpsgConversionRecord(7181, "Transverse Mercator", 5); + return true; + case 7182: + record = new EpsgConversionRecord(7182, "Transverse Mercator", 5); + return true; + case 7183: + record = new EpsgConversionRecord(7183, "Transverse Mercator", 5); + return true; + case 7184: + record = new EpsgConversionRecord(7184, "Transverse Mercator", 5); + return true; + case 7185: + record = new EpsgConversionRecord(7185, "Transverse Mercator", 5); + return true; + case 7186: + record = new EpsgConversionRecord(7186, "Transverse Mercator", 5); + return true; + case 7187: + record = new EpsgConversionRecord(7187, "Transverse Mercator", 5); + return true; + case 7188: + record = new EpsgConversionRecord(7188, "Transverse Mercator", 5); + return true; + case 7189: + record = new EpsgConversionRecord(7189, "Transverse Mercator", 5); + return true; + case 7190: + record = new EpsgConversionRecord(7190, "Transverse Mercator", 5); + return true; + case 7191: + record = new EpsgConversionRecord(7191, "Transverse Mercator", 5); + return true; + case 7192: + record = new EpsgConversionRecord(7192, "Transverse Mercator", 5); + return true; + case 7193: + record = new EpsgConversionRecord(7193, "Transverse Mercator", 5); + return true; + case 7194: + record = new EpsgConversionRecord(7194, "Transverse Mercator", 5); + return true; + case 7195: + record = new EpsgConversionRecord(7195, "Transverse Mercator", 5); + return true; + case 7196: + record = new EpsgConversionRecord(7196, "Transverse Mercator", 5); + return true; + case 7197: + record = new EpsgConversionRecord(7197, "Transverse Mercator", 5); + return true; + case 7198: + record = new EpsgConversionRecord(7198, "Transverse Mercator", 5); + return true; + case 7199: + record = new EpsgConversionRecord(7199, "Transverse Mercator", 5); + return true; + case 7200: + record = new EpsgConversionRecord(7200, "Transverse Mercator", 5); + return true; + case 7201: + record = new EpsgConversionRecord(7201, "Transverse Mercator", 5); + return true; + case 7202: + record = new EpsgConversionRecord(7202, "Transverse Mercator", 5); + return true; + case 7203: + record = new EpsgConversionRecord(7203, "Transverse Mercator", 5); + return true; + case 7204: + record = new EpsgConversionRecord(7204, "Transverse Mercator", 5); + return true; + case 7205: + record = new EpsgConversionRecord(7205, "Transverse Mercator", 5); + return true; + case 7206: + record = new EpsgConversionRecord(7206, "Transverse Mercator", 5); + return true; + case 7207: + record = new EpsgConversionRecord(7207, "Transverse Mercator", 5); + return true; + case 7208: + record = new EpsgConversionRecord(7208, "Transverse Mercator", 5); + return true; + case 7209: + record = new EpsgConversionRecord(7209, "Transverse Mercator", 5); + return true; + case 7210: + record = new EpsgConversionRecord(7210, "Transverse Mercator", 5); + return true; + case 7211: + record = new EpsgConversionRecord(7211, "Transverse Mercator", 5); + return true; + case 7212: + record = new EpsgConversionRecord(7212, "Transverse Mercator", 5); + return true; + case 7213: + record = new EpsgConversionRecord(7213, "Transverse Mercator", 5); + return true; + case 7214: + record = new EpsgConversionRecord(7214, "Transverse Mercator", 5); + return true; + case 7215: + record = new EpsgConversionRecord(7215, "Transverse Mercator", 5); + return true; + case 7216: + record = new EpsgConversionRecord(7216, "Transverse Mercator", 5); + return true; + case 7217: + record = new EpsgConversionRecord(7217, "Transverse Mercator", 5); + return true; + case 7218: + record = new EpsgConversionRecord(7218, "Transverse Mercator", 5); + return true; + case 7219: + record = new EpsgConversionRecord(7219, "Transverse Mercator", 5); + return true; + case 7220: + record = new EpsgConversionRecord(7220, "Transverse Mercator", 5); + return true; + case 7221: + record = new EpsgConversionRecord(7221, "Transverse Mercator", 5); + return true; + case 7222: + record = new EpsgConversionRecord(7222, "Transverse Mercator", 5); + return true; + case 7223: + record = new EpsgConversionRecord(7223, "Transverse Mercator", 5); + return true; + case 7224: + record = new EpsgConversionRecord(7224, "Transverse Mercator", 5); + return true; + case 7225: + record = new EpsgConversionRecord(7225, "Transverse Mercator", 5); + return true; + case 7226: + record = new EpsgConversionRecord(7226, "Transverse Mercator", 5); + return true; + case 7227: + record = new EpsgConversionRecord(7227, "Transverse Mercator", 5); + return true; + case 7228: + record = new EpsgConversionRecord(7228, "Transverse Mercator", 5); + return true; + case 7229: + record = new EpsgConversionRecord(7229, "Transverse Mercator", 5); + return true; + case 7230: + record = new EpsgConversionRecord(7230, "Transverse Mercator", 5); + return true; + case 7231: + record = new EpsgConversionRecord(7231, "Transverse Mercator", 5); + return true; + case 7232: + record = new EpsgConversionRecord(7232, "Transverse Mercator", 5); + return true; + case 7233: + record = new EpsgConversionRecord(7233, "Transverse Mercator", 5); + return true; + case 7234: + record = new EpsgConversionRecord(7234, "Transverse Mercator", 5); + return true; + case 7235: + record = new EpsgConversionRecord(7235, "Transverse Mercator", 5); + return true; + case 7236: + record = new EpsgConversionRecord(7236, "Transverse Mercator", 5); + return true; + case 7237: + record = new EpsgConversionRecord(7237, "Transverse Mercator", 5); + return true; + case 7238: + record = new EpsgConversionRecord(7238, "Transverse Mercator", 5); + return true; + case 7239: + record = new EpsgConversionRecord(7239, "Transverse Mercator", 5); + return true; + case 7240: + record = new EpsgConversionRecord(7240, "Transverse Mercator", 5); + return true; + case 7241: + record = new EpsgConversionRecord(7241, "Transverse Mercator", 5); + return true; + case 7242: + record = new EpsgConversionRecord(7242, "Transverse Mercator", 5); + return true; + case 7243: + record = new EpsgConversionRecord(7243, "Transverse Mercator", 5); + return true; + case 7244: + record = new EpsgConversionRecord(7244, "Transverse Mercator", 5); + return true; + case 7245: + record = new EpsgConversionRecord(7245, "Transverse Mercator", 5); + return true; + case 7246: + record = new EpsgConversionRecord(7246, "Transverse Mercator", 5); + return true; + case 7247: + record = new EpsgConversionRecord(7247, "Transverse Mercator", 5); + return true; + case 7248: + record = new EpsgConversionRecord(7248, "Transverse Mercator", 5); + return true; + case 7249: + record = new EpsgConversionRecord(7249, "Transverse Mercator", 5); + return true; + case 7250: + record = new EpsgConversionRecord(7250, "Transverse Mercator", 5); + return true; + case 7251: + record = new EpsgConversionRecord(7251, "Transverse Mercator", 5); + return true; + case 7252: + record = new EpsgConversionRecord(7252, "Transverse Mercator", 5); + return true; + case 7253: + record = new EpsgConversionRecord(7253, "Transverse Mercator", 5); + return true; + case 7254: + record = new EpsgConversionRecord(7254, "Transverse Mercator", 5); + return true; + case 7255: + record = new EpsgConversionRecord(7255, "Transverse Mercator", 5); + return true; + case 7256: + record = new EpsgConversionRecord(7256, "Transverse Mercator", 5); + return true; + case 7378: + record = new EpsgConversionRecord(7378, "Transverse Mercator", 5); + return true; + case 7379: + record = new EpsgConversionRecord(7379, "Transverse Mercator", 5); + return true; + case 7380: + record = new EpsgConversionRecord(7380, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7381: + record = new EpsgConversionRecord(7381, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7382: + record = new EpsgConversionRecord(7382, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7383: + record = new EpsgConversionRecord(7383, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7384: + record = new EpsgConversionRecord(7384, "Transverse Mercator", 5); + return true; + case 7385: + record = new EpsgConversionRecord(7385, "Transverse Mercator", 5); + return true; + case 7386: + record = new EpsgConversionRecord(7386, "Transverse Mercator", 5); + return true; + case 7387: + record = new EpsgConversionRecord(7387, "Transverse Mercator", 5); + return true; + case 7388: + record = new EpsgConversionRecord(7388, "Transverse Mercator", 5); + return true; + case 7389: + record = new EpsgConversionRecord(7389, "Transverse Mercator", 5); + return true; + case 7390: + record = new EpsgConversionRecord(7390, "Transverse Mercator", 5); + return true; + case 7391: + record = new EpsgConversionRecord(7391, "Transverse Mercator", 5); + return true; + case 7392: + record = new EpsgConversionRecord(7392, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7393: + record = new EpsgConversionRecord(7393, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7394: + record = new EpsgConversionRecord(7394, "Transverse Mercator", 5); + return true; + case 7395: + record = new EpsgConversionRecord(7395, "Transverse Mercator", 5); + return true; + case 7396: + record = new EpsgConversionRecord(7396, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7397: + record = new EpsgConversionRecord(7397, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7398: + record = new EpsgConversionRecord(7398, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7399: + record = new EpsgConversionRecord(7399, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7424: + record = new EpsgConversionRecord(7424, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7425: + record = new EpsgConversionRecord(7425, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7426: + record = new EpsgConversionRecord(7426, "Transverse Mercator", 5); + return true; + case 7427: + record = new EpsgConversionRecord(7427, "Transverse Mercator", 5); + return true; + case 7428: + record = new EpsgConversionRecord(7428, "Transverse Mercator", 5); + return true; + case 7429: + record = new EpsgConversionRecord(7429, "Transverse Mercator", 5); + return true; + case 7430: + record = new EpsgConversionRecord(7430, "Transverse Mercator", 5); + return true; + case 7431: + record = new EpsgConversionRecord(7431, "Transverse Mercator", 5); + return true; + case 7432: + record = new EpsgConversionRecord(7432, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7433: + record = new EpsgConversionRecord(7433, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7434: + record = new EpsgConversionRecord(7434, "Transverse Mercator", 5); + return true; + case 7435: + record = new EpsgConversionRecord(7435, "Transverse Mercator", 5); + return true; + case 7436: + record = new EpsgConversionRecord(7436, "Transverse Mercator", 5); + return true; + case 7437: + record = new EpsgConversionRecord(7437, "Transverse Mercator", 5); + return true; + case 7438: + record = new EpsgConversionRecord(7438, "Transverse Mercator", 5); + return true; + case 7439: + record = new EpsgConversionRecord(7439, "Transverse Mercator", 5); + return true; + case 7440: + record = new EpsgConversionRecord(7440, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7441: + record = new EpsgConversionRecord(7441, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7450: + record = new EpsgConversionRecord(7450, "Transverse Mercator", 5); + return true; + case 7451: + record = new EpsgConversionRecord(7451, "Transverse Mercator", 5); + return true; + case 7452: + record = new EpsgConversionRecord(7452, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7453: + record = new EpsgConversionRecord(7453, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7454: + record = new EpsgConversionRecord(7454, "Transverse Mercator", 5); + return true; + case 7455: + record = new EpsgConversionRecord(7455, "Transverse Mercator", 5); + return true; + case 7456: + record = new EpsgConversionRecord(7456, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7457: + record = new EpsgConversionRecord(7457, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7458: + record = new EpsgConversionRecord(7458, "Transverse Mercator", 5); + return true; + case 7459: + record = new EpsgConversionRecord(7459, "Transverse Mercator", 5); + return true; + case 7460: + record = new EpsgConversionRecord(7460, "Transverse Mercator", 5); + return true; + case 7461: + record = new EpsgConversionRecord(7461, "Transverse Mercator", 5); + return true; + case 7462: + record = new EpsgConversionRecord(7462, "Transverse Mercator", 5); + return true; + case 7463: + record = new EpsgConversionRecord(7463, "Transverse Mercator", 5); + return true; + case 7464: + record = new EpsgConversionRecord(7464, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7465: + record = new EpsgConversionRecord(7465, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7466: + record = new EpsgConversionRecord(7466, "Transverse Mercator", 5); + return true; + case 7467: + record = new EpsgConversionRecord(7467, "Transverse Mercator", 5); + return true; + case 7468: + record = new EpsgConversionRecord(7468, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7469: + record = new EpsgConversionRecord(7469, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7470: + record = new EpsgConversionRecord(7470, "Transverse Mercator", 5); + return true; + case 7471: + record = new EpsgConversionRecord(7471, "Transverse Mercator", 5); + return true; + case 7472: + record = new EpsgConversionRecord(7472, "Transverse Mercator", 5); + return true; + case 7473: + record = new EpsgConversionRecord(7473, "Transverse Mercator", 5); + return true; + case 7474: + record = new EpsgConversionRecord(7474, "Transverse Mercator", 5); + return true; + case 7475: + record = new EpsgConversionRecord(7475, "Transverse Mercator", 5); + return true; + case 7476: + record = new EpsgConversionRecord(7476, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7477: + record = new EpsgConversionRecord(7477, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7478: + record = new EpsgConversionRecord(7478, "Transverse Mercator", 5); + return true; + case 7479: + record = new EpsgConversionRecord(7479, "Transverse Mercator", 5); + return true; + case 7480: + record = new EpsgConversionRecord(7480, "Transverse Mercator", 5); + return true; + case 7481: + record = new EpsgConversionRecord(7481, "Transverse Mercator", 5); + return true; + case 7482: + record = new EpsgConversionRecord(7482, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7483: + record = new EpsgConversionRecord(7483, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7484: + record = new EpsgConversionRecord(7484, "Transverse Mercator", 5); + return true; + case 7485: + record = new EpsgConversionRecord(7485, "Transverse Mercator", 5); + return true; + case 7486: + record = new EpsgConversionRecord(7486, "Transverse Mercator", 5); + return true; + case 7487: + record = new EpsgConversionRecord(7487, "Transverse Mercator", 5); + return true; + case 7488: + record = new EpsgConversionRecord(7488, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7489: + record = new EpsgConversionRecord(7489, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7490: + record = new EpsgConversionRecord(7490, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7491: + record = new EpsgConversionRecord(7491, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7492: + record = new EpsgConversionRecord(7492, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7493: + record = new EpsgConversionRecord(7493, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7494: + record = new EpsgConversionRecord(7494, "Transverse Mercator", 5); + return true; + case 7495: + record = new EpsgConversionRecord(7495, "Transverse Mercator", 5); + return true; + case 7496: + record = new EpsgConversionRecord(7496, "Transverse Mercator", 5); + return true; + case 7497: + record = new EpsgConversionRecord(7497, "Transverse Mercator", 5); + return true; + case 7498: + record = new EpsgConversionRecord(7498, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7499: + record = new EpsgConversionRecord(7499, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7500: + record = new EpsgConversionRecord(7500, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7501: + record = new EpsgConversionRecord(7501, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7502: + record = new EpsgConversionRecord(7502, "Transverse Mercator", 5); + return true; + case 7503: + record = new EpsgConversionRecord(7503, "Transverse Mercator", 5); + return true; + case 7504: + record = new EpsgConversionRecord(7504, "Transverse Mercator", 5); + return true; + case 7505: + record = new EpsgConversionRecord(7505, "Transverse Mercator", 5); + return true; + case 7506: + record = new EpsgConversionRecord(7506, "Transverse Mercator", 5); + return true; + case 7507: + record = new EpsgConversionRecord(7507, "Transverse Mercator", 5); + return true; + case 7508: + record = new EpsgConversionRecord(7508, "Transverse Mercator", 5); + return true; + case 7509: + record = new EpsgConversionRecord(7509, "Transverse Mercator", 5); + return true; + case 7510: + record = new EpsgConversionRecord(7510, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7511: + record = new EpsgConversionRecord(7511, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7512: + record = new EpsgConversionRecord(7512, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7513: + record = new EpsgConversionRecord(7513, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7514: + record = new EpsgConversionRecord(7514, "Transverse Mercator", 5); + return true; + case 7515: + record = new EpsgConversionRecord(7515, "Transverse Mercator", 5); + return true; + case 7516: + record = new EpsgConversionRecord(7516, "Transverse Mercator", 5); + return true; + case 7517: + record = new EpsgConversionRecord(7517, "Transverse Mercator", 5); + return true; + case 7518: + record = new EpsgConversionRecord(7518, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7519: + record = new EpsgConversionRecord(7519, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7520: + record = new EpsgConversionRecord(7520, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7521: + record = new EpsgConversionRecord(7521, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7522: + record = new EpsgConversionRecord(7522, "Transverse Mercator", 5); + return true; + case 7523: + record = new EpsgConversionRecord(7523, "Transverse Mercator", 5); + return true; + case 7524: + record = new EpsgConversionRecord(7524, "Transverse Mercator", 5); + return true; + case 7525: + record = new EpsgConversionRecord(7525, "Transverse Mercator", 5); + return true; + case 7526: + record = new EpsgConversionRecord(7526, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7527: + record = new EpsgConversionRecord(7527, "Lambert Conic Conformal (1SP)", 5); + return true; + case 7687: + record = new EpsgConversionRecord(7687, "Transverse Mercator", 5); + return true; + case 7688: + record = new EpsgConversionRecord(7688, "Transverse Mercator", 5); + return true; + case 7689: + record = new EpsgConversionRecord(7689, "Transverse Mercator", 5); + return true; + case 7690: + record = new EpsgConversionRecord(7690, "Transverse Mercator", 5); + return true; + case 7691: + record = new EpsgConversionRecord(7691, "Transverse Mercator", 5); + return true; + case 7722: + record = new EpsgConversionRecord(7722, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7723: + record = new EpsgConversionRecord(7723, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7724: + record = new EpsgConversionRecord(7724, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7725: + record = new EpsgConversionRecord(7725, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7726: + record = new EpsgConversionRecord(7726, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7727: + record = new EpsgConversionRecord(7727, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7728: + record = new EpsgConversionRecord(7728, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7729: + record = new EpsgConversionRecord(7729, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7730: + record = new EpsgConversionRecord(7730, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7731: + record = new EpsgConversionRecord(7731, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7732: + record = new EpsgConversionRecord(7732, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7733: + record = new EpsgConversionRecord(7733, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7734: + record = new EpsgConversionRecord(7734, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7735: + record = new EpsgConversionRecord(7735, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7736: + record = new EpsgConversionRecord(7736, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7737: + record = new EpsgConversionRecord(7737, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7738: + record = new EpsgConversionRecord(7738, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7739: + record = new EpsgConversionRecord(7739, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7740: + record = new EpsgConversionRecord(7740, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7741: + record = new EpsgConversionRecord(7741, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7742: + record = new EpsgConversionRecord(7742, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7743: + record = new EpsgConversionRecord(7743, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7744: + record = new EpsgConversionRecord(7744, "Transverse Mercator", 5); + return true; + case 7745: + record = new EpsgConversionRecord(7745, "Transverse Mercator", 5); + return true; + case 7746: + record = new EpsgConversionRecord(7746, "Transverse Mercator", 5); + return true; + case 7747: + record = new EpsgConversionRecord(7747, "Transverse Mercator", 5); + return true; + case 7748: + record = new EpsgConversionRecord(7748, "Transverse Mercator", 5); + return true; + case 7749: + record = new EpsgConversionRecord(7749, "Transverse Mercator", 5); + return true; + case 7750: + record = new EpsgConversionRecord(7750, "Transverse Mercator", 5); + return true; + case 7751: + record = new EpsgConversionRecord(7751, "Transverse Mercator", 5); + return true; + case 7752: + record = new EpsgConversionRecord(7752, "Transverse Mercator", 5); + return true; + case 7753: + record = new EpsgConversionRecord(7753, "Transverse Mercator", 5); + return true; + case 7754: + record = new EpsgConversionRecord(7754, "Transverse Mercator", 5); + return true; + case 7802: + record = new EpsgConversionRecord(7802, "Lambert Conic Conformal (2SP)", 6); + return true; + case 7818: + record = new EpsgConversionRecord(7818, "Transverse Mercator", 5); + return true; + case 7819: + record = new EpsgConversionRecord(7819, "Transverse Mercator", 5); + return true; + case 7820: + record = new EpsgConversionRecord(7820, "Transverse Mercator", 5); + return true; + case 7821: + record = new EpsgConversionRecord(7821, "Transverse Mercator", 5); + return true; + case 7822: + record = new EpsgConversionRecord(7822, "Transverse Mercator", 5); + return true; + case 7823: + record = new EpsgConversionRecord(7823, "Transverse Mercator", 5); + return true; + case 7824: + record = new EpsgConversionRecord(7824, "Transverse Mercator", 5); + return true; + case 7875: + record = new EpsgConversionRecord(7875, "Transverse Mercator", 5); + return true; + case 7876: + record = new EpsgConversionRecord(7876, "Transverse Mercator", 5); + return true; + case 7993: + record = new EpsgConversionRecord(7993, "Transverse Mercator", 5); + return true; + case 7994: + record = new EpsgConversionRecord(7994, "Transverse Mercator", 5); + return true; + case 7995: + record = new EpsgConversionRecord(7995, "Transverse Mercator", 5); + return true; + case 7996: + record = new EpsgConversionRecord(7996, "Transverse Mercator", 5); + return true; + case 7997: + record = new EpsgConversionRecord(7997, "Transverse Mercator", 5); + return true; + case 7998: + record = new EpsgConversionRecord(7998, "Transverse Mercator", 5); + return true; + case 7999: + record = new EpsgConversionRecord(7999, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket8(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 8000: + record = new EpsgConversionRecord(8000, "Transverse Mercator", 5); + return true; + case 8001: + record = new EpsgConversionRecord(8001, "Transverse Mercator", 5); + return true; + case 8002: + record = new EpsgConversionRecord(8002, "Transverse Mercator", 5); + return true; + case 8003: + record = new EpsgConversionRecord(8003, "Transverse Mercator", 5); + return true; + case 8004: + record = new EpsgConversionRecord(8004, "Transverse Mercator", 5); + return true; + case 8005: + record = new EpsgConversionRecord(8005, "Transverse Mercator", 5); + return true; + case 8006: + record = new EpsgConversionRecord(8006, "Transverse Mercator", 5); + return true; + case 8007: + record = new EpsgConversionRecord(8007, "Transverse Mercator", 5); + return true; + case 8008: + record = new EpsgConversionRecord(8008, "Transverse Mercator", 5); + return true; + case 8009: + record = new EpsgConversionRecord(8009, "Transverse Mercator", 5); + return true; + case 8010: + record = new EpsgConversionRecord(8010, "Transverse Mercator", 5); + return true; + case 8011: + record = new EpsgConversionRecord(8011, "Transverse Mercator", 5); + return true; + case 8012: + record = new EpsgConversionRecord(8012, "Transverse Mercator", 5); + return true; + case 8033: + record = new EpsgConversionRecord(8033, "Transverse Mercator", 5); + return true; + case 8034: + record = new EpsgConversionRecord(8034, "Transverse Mercator", 5); + return true; + case 8040: + record = new EpsgConversionRecord(8040, "Cassini-Soldner", 4); + return true; + case 8041: + record = new EpsgConversionRecord(8041, "Cassini-Soldner", 4); + return true; + case 8061: + record = new EpsgConversionRecord(8061, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 8062: + record = new EpsgConversionRecord(8062, "Transverse Mercator", 5); + return true; + case 8063: + record = new EpsgConversionRecord(8063, "Transverse Mercator", 5); + return true; + case 8064: + record = new EpsgConversionRecord(8064, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8080: + record = new EpsgConversionRecord(8080, "Transverse Mercator", 5); + return true; + case 8081: + record = new EpsgConversionRecord(8081, "Transverse Mercator", 5); + return true; + case 8087: + record = new EpsgConversionRecord(8087, "Lambert Conic Conformal (2SP)", 6); + return true; + case 8273: + record = new EpsgConversionRecord(8273, "Transverse Mercator", 5); + return true; + case 8274: + record = new EpsgConversionRecord(8274, "Transverse Mercator", 5); + return true; + case 8275: + record = new EpsgConversionRecord(8275, "Transverse Mercator", 5); + return true; + case 8276: + record = new EpsgConversionRecord(8276, "Transverse Mercator", 5); + return true; + case 8277: + record = new EpsgConversionRecord(8277, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8278: + record = new EpsgConversionRecord(8278, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8279: + record = new EpsgConversionRecord(8279, "Transverse Mercator", 5); + return true; + case 8280: + record = new EpsgConversionRecord(8280, "Transverse Mercator", 5); + return true; + case 8281: + record = new EpsgConversionRecord(8281, "Transverse Mercator", 5); + return true; + case 8282: + record = new EpsgConversionRecord(8282, "Transverse Mercator", 5); + return true; + case 8283: + record = new EpsgConversionRecord(8283, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8284: + record = new EpsgConversionRecord(8284, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8285: + record = new EpsgConversionRecord(8285, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8286: + record = new EpsgConversionRecord(8286, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8287: + record = new EpsgConversionRecord(8287, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8288: + record = new EpsgConversionRecord(8288, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8289: + record = new EpsgConversionRecord(8289, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8290: + record = new EpsgConversionRecord(8290, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8291: + record = new EpsgConversionRecord(8291, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8292: + record = new EpsgConversionRecord(8292, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8293: + record = new EpsgConversionRecord(8293, "Transverse Mercator", 5); + return true; + case 8294: + record = new EpsgConversionRecord(8294, "Transverse Mercator", 5); + return true; + case 8295: + record = new EpsgConversionRecord(8295, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8296: + record = new EpsgConversionRecord(8296, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8297: + record = new EpsgConversionRecord(8297, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8298: + record = new EpsgConversionRecord(8298, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8299: + record = new EpsgConversionRecord(8299, "Transverse Mercator", 5); + return true; + case 8300: + record = new EpsgConversionRecord(8300, "Transverse Mercator", 5); + return true; + case 8301: + record = new EpsgConversionRecord(8301, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8302: + record = new EpsgConversionRecord(8302, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8303: + record = new EpsgConversionRecord(8303, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8304: + record = new EpsgConversionRecord(8304, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8305: + record = new EpsgConversionRecord(8305, "Transverse Mercator", 5); + return true; + case 8306: + record = new EpsgConversionRecord(8306, "Transverse Mercator", 5); + return true; + case 8307: + record = new EpsgConversionRecord(8307, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8308: + record = new EpsgConversionRecord(8308, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8309: + record = new EpsgConversionRecord(8309, "Transverse Mercator", 5); + return true; + case 8310: + record = new EpsgConversionRecord(8310, "Transverse Mercator", 5); + return true; + case 8373: + record = new EpsgConversionRecord(8373, "Transverse Mercator", 5); + return true; + case 8374: + record = new EpsgConversionRecord(8374, "Transverse Mercator", 5); + return true; + case 8375: + record = new EpsgConversionRecord(8375, "Transverse Mercator", 5); + return true; + case 8376: + record = new EpsgConversionRecord(8376, "Transverse Mercator", 5); + return true; + case 8389: + record = new EpsgConversionRecord(8389, "Transverse Mercator", 5); + return true; + case 8432: + record = new EpsgConversionRecord(8432, "Transverse Mercator", 5); + return true; + case 8440: + record = new EpsgConversionRecord(8440, "Laborde Oblique Mercator", 6); + return true; + case 8458: + record = new EpsgConversionRecord(8458, "Transverse Mercator", 5); + return true; + case 8459: + record = new EpsgConversionRecord(8459, "Transverse Mercator", 5); + return true; + case 8490: + record = new EpsgConversionRecord(8490, "Transverse Mercator", 5); + return true; + case 8491: + record = new EpsgConversionRecord(8491, "Transverse Mercator", 5); + return true; + case 8492: + record = new EpsgConversionRecord(8492, "Transverse Mercator", 5); + return true; + case 8493: + record = new EpsgConversionRecord(8493, "Transverse Mercator", 5); + return true; + case 8494: + record = new EpsgConversionRecord(8494, "Transverse Mercator", 5); + return true; + case 8495: + record = new EpsgConversionRecord(8495, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8498: + record = new EpsgConversionRecord(8498, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8499: + record = new EpsgConversionRecord(8499, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8500: + record = new EpsgConversionRecord(8500, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8501: + record = new EpsgConversionRecord(8501, "Transverse Mercator", 5); + return true; + case 8502: + record = new EpsgConversionRecord(8502, "Transverse Mercator", 5); + return true; + case 8503: + record = new EpsgConversionRecord(8503, "Transverse Mercator", 5); + return true; + case 8504: + record = new EpsgConversionRecord(8504, "Transverse Mercator", 5); + return true; + case 8505: + record = new EpsgConversionRecord(8505, "Transverse Mercator", 5); + return true; + case 8506: + record = new EpsgConversionRecord(8506, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8507: + record = new EpsgConversionRecord(8507, "Lambert Conic Conformal (1SP)", 5); + return true; + case 8515: + record = new EpsgConversionRecord(8515, "Transverse Mercator", 5); + return true; + case 8516: + record = new EpsgConversionRecord(8516, "Transverse Mercator", 5); + return true; + case 8825: + record = new EpsgConversionRecord(8825, "Transverse Mercator", 5); + return true; + case 8854: + record = new EpsgConversionRecord(8854, "Equal Earth", 3); + return true; + case 8855: + record = new EpsgConversionRecord(8855, "Equal Earth", 3); + return true; + case 8856: + record = new EpsgConversionRecord(8856, "Equal Earth", 3); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket9(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 9058: + record = new EpsgConversionRecord(9058, "Transverse Mercator", 5); + return true; + case 9190: + record = new EpsgConversionRecord(9190, "Albers Equal Area", 6); + return true; + case 9192: + record = new EpsgConversionRecord(9192, "Transverse Mercator", 5); + return true; + case 9193: + record = new EpsgConversionRecord(9193, "Transverse Mercator", 5); + return true; + case 9194: + record = new EpsgConversionRecord(9194, "Transverse Mercator", 5); + return true; + case 9195: + record = new EpsgConversionRecord(9195, "Transverse Mercator", 5); + return true; + case 9196: + record = new EpsgConversionRecord(9196, "Transverse Mercator", 5); + return true; + case 9197: + record = new EpsgConversionRecord(9197, "Transverse Mercator", 5); + return true; + case 9198: + record = new EpsgConversionRecord(9198, "Transverse Mercator", 5); + return true; + case 9199: + record = new EpsgConversionRecord(9199, "Transverse Mercator", 5); + return true; + case 9200: + record = new EpsgConversionRecord(9200, "Transverse Mercator", 5); + return true; + case 9201: + record = new EpsgConversionRecord(9201, "Transverse Mercator", 5); + return true; + case 9202: + record = new EpsgConversionRecord(9202, "Transverse Mercator", 5); + return true; + case 9203: + record = new EpsgConversionRecord(9203, "Transverse Mercator", 5); + return true; + case 9204: + record = new EpsgConversionRecord(9204, "Transverse Mercator", 5); + return true; + case 9219: + record = new EpsgConversionRecord(9219, "Albers Equal Area", 6); + return true; + case 9220: + record = new EpsgConversionRecord(9220, "Albers Equal Area", 6); + return true; + case 9268: + record = new EpsgConversionRecord(9268, "Transverse Mercator", 5); + return true; + case 9269: + record = new EpsgConversionRecord(9269, "Transverse Mercator", 5); + return true; + case 9270: + record = new EpsgConversionRecord(9270, "Transverse Mercator", 5); + return true; + case 9301: + record = new EpsgConversionRecord(9301, "Transverse Mercator", 5); + return true; + case 9353: + record = new EpsgConversionRecord(9353, "Polar Stereographic (variant B)", 4); + return true; + case 9366: + record = new EpsgConversionRecord(9366, "Transverse Mercator", 5); + return true; + case 9370: + record = new EpsgConversionRecord(9370, "Transverse Mercator", 5); + return true; + case 9376: + record = new EpsgConversionRecord(9376, "Transverse Mercator", 5); + return true; + case 9385: + record = new EpsgConversionRecord(9385, "Transverse Mercator", 5); + return true; + case 9455: + record = new EpsgConversionRecord(9455, "Transverse Mercator", 5); + return true; + case 9497: + record = new EpsgConversionRecord(9497, "Transverse Mercator", 5); + return true; + case 9548: + record = new EpsgConversionRecord(9548, "Lambert Conic Conformal (1SP variant B)", 6); + return true; + case 9673: + record = new EpsgConversionRecord(9673, "Albers Equal Area", 6); + return true; + case 9677: + record = new EpsgConversionRecord(9677, "Transverse Mercator", 5); + return true; + case 9738: + record = new EpsgConversionRecord(9738, "Transverse Mercator", 5); + return true; + case 9746: + record = new EpsgConversionRecord(9746, "Transverse Mercator", 5); + return true; + case 9747: + record = new EpsgConversionRecord(9747, "Transverse Mercator", 5); + return true; + case 9760: + record = new EpsgConversionRecord(9760, "Transverse Mercator", 5); + return true; + case 9765: + record = new EpsgConversionRecord(9765, "Transverse Mercator", 5); + return true; + case 9796: + record = new EpsgConversionRecord(9796, "Transverse Mercator", 5); + return true; + case 9797: + record = new EpsgConversionRecord(9797, "Transverse Mercator", 5); + return true; + case 9798: + record = new EpsgConversionRecord(9798, "Transverse Mercator", 5); + return true; + case 9799: + record = new EpsgConversionRecord(9799, "Transverse Mercator", 5); + return true; + case 9800: + record = new EpsgConversionRecord(9800, "Transverse Mercator", 5); + return true; + case 9801: + record = new EpsgConversionRecord(9801, "Transverse Mercator", 5); + return true; + case 9802: + record = new EpsgConversionRecord(9802, "Transverse Mercator", 5); + return true; + case 9803: + record = new EpsgConversionRecord(9803, "Transverse Mercator", 5); + return true; + case 9804: + record = new EpsgConversionRecord(9804, "Transverse Mercator", 5); + return true; + case 9805: + record = new EpsgConversionRecord(9805, "Transverse Mercator", 5); + return true; + case 9806: + record = new EpsgConversionRecord(9806, "Transverse Mercator", 5); + return true; + case 9807: + record = new EpsgConversionRecord(9807, "Transverse Mercator", 5); + return true; + case 9808: + record = new EpsgConversionRecord(9808, "Transverse Mercator", 5); + return true; + case 9809: + record = new EpsgConversionRecord(9809, "Transverse Mercator", 5); + return true; + case 9810: + record = new EpsgConversionRecord(9810, "Transverse Mercator", 5); + return true; + case 9811: + record = new EpsgConversionRecord(9811, "Transverse Mercator", 5); + return true; + case 9812: + record = new EpsgConversionRecord(9812, "Transverse Mercator", 5); + return true; + case 9813: + record = new EpsgConversionRecord(9813, "Transverse Mercator", 5); + return true; + case 9814: + record = new EpsgConversionRecord(9814, "Transverse Mercator", 5); + return true; + case 9815: + record = new EpsgConversionRecord(9815, "Transverse Mercator", 5); + return true; + case 9816: + record = new EpsgConversionRecord(9816, "Transverse Mercator", 5); + return true; + case 9817: + record = new EpsgConversionRecord(9817, "Transverse Mercator", 5); + return true; + case 9818: + record = new EpsgConversionRecord(9818, "Transverse Mercator", 5); + return true; + case 9819: + record = new EpsgConversionRecord(9819, "Transverse Mercator", 5); + return true; + case 9820: + record = new EpsgConversionRecord(9820, "Transverse Mercator", 5); + return true; + case 9868: + record = new EpsgConversionRecord(9868, "Transverse Mercator", 5); + return true; + case 9872: + record = new EpsgConversionRecord(9872, "Transverse Mercator", 5); + return true; + case 9873: + record = new EpsgConversionRecord(9873, "Transverse Mercator", 5); + return true; + case 9879: + record = new EpsgConversionRecord(9879, "Transverse Mercator", 5); + return true; + case 9894: + record = new EpsgConversionRecord(9894, "Transverse Mercator 3D", 5); + return true; + case 9911: + record = new EpsgConversionRecord(9911, "Transverse Mercator", 5); + return true; + case 9942: + record = new EpsgConversionRecord(9942, "Transverse Mercator", 5); + return true; + case 9946: + record = new EpsgConversionRecord(9946, "Lambert Azimuthal Equal Area", 4); + return true; + case 9966: + record = new EpsgConversionRecord(9966, "Transverse Mercator", 5); + return true; + case 9971: + record = new EpsgConversionRecord(9971, "Transverse Mercator", 5); + return true; + case 9976: + record = new EpsgConversionRecord(9976, "Transverse Mercator", 5); + return true; + case 9981: + record = new EpsgConversionRecord(9981, "Transverse Mercator", 5); + return true; + case 9982: + record = new EpsgConversionRecord(9982, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket10(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 10101: + record = new EpsgConversionRecord(10101, "Transverse Mercator", 5); + return true; + case 10102: + record = new EpsgConversionRecord(10102, "Transverse Mercator", 5); + return true; + case 10127: + record = new EpsgConversionRecord(10127, "Transverse Mercator", 5); + return true; + case 10131: + record = new EpsgConversionRecord(10131, "Transverse Mercator", 5); + return true; + case 10132: + record = new EpsgConversionRecord(10132, "Transverse Mercator", 5); + return true; + case 10147: + record = new EpsgConversionRecord(10147, "Transverse Mercator", 5); + return true; + case 10148: + record = new EpsgConversionRecord(10148, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 10159: + record = new EpsgConversionRecord(10159, "Transverse Mercator", 5); + return true; + case 10182: + record = new EpsgConversionRecord(10182, "Transverse Mercator", 5); + return true; + case 10187: + record = new EpsgConversionRecord(10187, "Transverse Mercator", 5); + return true; + case 10193: + record = new EpsgConversionRecord(10193, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10198: + record = new EpsgConversionRecord(10198, "Transverse Mercator", 5); + return true; + case 10201: + record = new EpsgConversionRecord(10201, "Transverse Mercator", 5); + return true; + case 10202: + record = new EpsgConversionRecord(10202, "Transverse Mercator", 5); + return true; + case 10203: + record = new EpsgConversionRecord(10203, "Transverse Mercator", 5); + return true; + case 10206: + record = new EpsgConversionRecord(10206, "Transverse Mercator", 5); + return true; + case 10211: + record = new EpsgConversionRecord(10211, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10226: + record = new EpsgConversionRecord(10226, "Transverse Mercator", 5); + return true; + case 10231: + record = new EpsgConversionRecord(10231, "Transverse Mercator", 5); + return true; + case 10232: + record = new EpsgConversionRecord(10232, "Transverse Mercator", 5); + return true; + case 10233: + record = new EpsgConversionRecord(10233, "Transverse Mercator", 5); + return true; + case 10234: + record = new EpsgConversionRecord(10234, "Transverse Mercator", 5); + return true; + case 10239: + record = new EpsgConversionRecord(10239, "Transverse Mercator", 5); + return true; + case 10253: + record = new EpsgConversionRecord(10253, "Transverse Mercator", 5); + return true; + case 10257: + record = new EpsgConversionRecord(10257, "Lambert Conic Conformal (1SP variant B)", 6); + return true; + case 10261: + record = new EpsgConversionRecord(10261, "Lambert Conic Conformal (1SP variant B)", 6); + return true; + case 10269: + record = new EpsgConversionRecord(10269, "Transverse Mercator", 5); + return true; + case 10274: + record = new EpsgConversionRecord(10274, "Transverse Mercator", 5); + return true; + case 10279: + record = new EpsgConversionRecord(10279, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10301: + record = new EpsgConversionRecord(10301, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10302: + record = new EpsgConversionRecord(10302, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10313: + record = new EpsgConversionRecord(10313, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10325: + record = new EpsgConversionRecord(10325, "Transverse Mercator", 5); + return true; + case 10331: + record = new EpsgConversionRecord(10331, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10332: + record = new EpsgConversionRecord(10332, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10401: + record = new EpsgConversionRecord(10401, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10402: + record = new EpsgConversionRecord(10402, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10403: + record = new EpsgConversionRecord(10403, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10404: + record = new EpsgConversionRecord(10404, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10405: + record = new EpsgConversionRecord(10405, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10406: + record = new EpsgConversionRecord(10406, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10408: + record = new EpsgConversionRecord(10408, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10420: + record = new EpsgConversionRecord(10420, "Albers Equal Area", 6); + return true; + case 10424: + record = new EpsgConversionRecord(10424, "Transverse Mercator", 5); + return true; + case 10425: + record = new EpsgConversionRecord(10425, "Transverse Mercator", 5); + return true; + case 10426: + record = new EpsgConversionRecord(10426, "Transverse Mercator", 5); + return true; + case 10427: + record = new EpsgConversionRecord(10427, "Transverse Mercator", 5); + return true; + case 10428: + record = new EpsgConversionRecord(10428, "Transverse Mercator", 5); + return true; + case 10429: + record = new EpsgConversionRecord(10429, "Transverse Mercator", 5); + return true; + case 10430: + record = new EpsgConversionRecord(10430, "Transverse Mercator", 5); + return true; + case 10431: + record = new EpsgConversionRecord(10431, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10432: + record = new EpsgConversionRecord(10432, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10433: + record = new EpsgConversionRecord(10433, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10434: + record = new EpsgConversionRecord(10434, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10435: + record = new EpsgConversionRecord(10435, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10436: + record = new EpsgConversionRecord(10436, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10437: + record = new EpsgConversionRecord(10437, "Transverse Mercator", 5); + return true; + case 10438: + record = new EpsgConversionRecord(10438, "Transverse Mercator", 5); + return true; + case 10439: + record = new EpsgConversionRecord(10439, "Transverse Mercator", 5); + return true; + case 10440: + record = new EpsgConversionRecord(10440, "Transverse Mercator", 5); + return true; + case 10441: + record = new EpsgConversionRecord(10441, "Transverse Mercator", 5); + return true; + case 10442: + record = new EpsgConversionRecord(10442, "Transverse Mercator", 5); + return true; + case 10443: + record = new EpsgConversionRecord(10443, "Transverse Mercator", 5); + return true; + case 10444: + record = new EpsgConversionRecord(10444, "Transverse Mercator", 5); + return true; + case 10445: + record = new EpsgConversionRecord(10445, "Transverse Mercator", 5); + return true; + case 10446: + record = new EpsgConversionRecord(10446, "Transverse Mercator", 5); + return true; + case 10447: + record = new EpsgConversionRecord(10447, "Transverse Mercator", 5); + return true; + case 10470: + record = new EpsgConversionRecord(10470, "Transverse Mercator", 5); + return true; + case 10476: + record = new EpsgConversionRecord(10476, "Transverse Mercator", 5); + return true; + case 10479: + record = new EpsgConversionRecord(10479, "Albers Equal Area", 6); + return true; + case 10501: + record = new EpsgConversionRecord(10501, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10502: + record = new EpsgConversionRecord(10502, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10503: + record = new EpsgConversionRecord(10503, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10515: + record = new EpsgConversionRecord(10515, "Transverse Mercator", 5); + return true; + case 10531: + record = new EpsgConversionRecord(10531, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10532: + record = new EpsgConversionRecord(10532, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10533: + record = new EpsgConversionRecord(10533, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10591: + record = new EpsgConversionRecord(10591, "Lambert Azimuthal Equal Area", 4); + return true; + case 10593: + record = new EpsgConversionRecord(10593, "Lambert Azimuthal Equal Area", 4); + return true; + case 10595: + record = new EpsgConversionRecord(10595, "Lambert Azimuthal Equal Area", 4); + return true; + case 10597: + record = new EpsgConversionRecord(10597, "Lambert Azimuthal Equal Area", 4); + return true; + case 10599: + record = new EpsgConversionRecord(10599, "Lambert Azimuthal Equal Area", 4); + return true; + case 10600: + record = new EpsgConversionRecord(10600, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10602: + record = new EpsgConversionRecord(10602, "Lambert Azimuthal Equal Area", 4); + return true; + case 10621: + record = new EpsgConversionRecord(10621, "Local Orthographic", 6); + return true; + case 10625: + record = new EpsgConversionRecord(10625, "Transverse Mercator", 5); + return true; + case 10630: + record = new EpsgConversionRecord(10630, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10631: + record = new EpsgConversionRecord(10631, "Transverse Mercator", 5); + return true; + case 10640: + record = new EpsgConversionRecord(10640, "Transverse Mercator", 5); + return true; + case 10664: + record = new EpsgConversionRecord(10664, "Transverse Mercator", 5); + return true; + case 10700: + record = new EpsgConversionRecord(10700, "Transverse Mercator", 5); + return true; + case 10719: + record = new EpsgConversionRecord(10719, "Transverse Mercator", 5); + return true; + case 10720: + record = new EpsgConversionRecord(10720, "Transverse Mercator", 5); + return true; + case 10721: + record = new EpsgConversionRecord(10721, "Transverse Mercator", 5); + return true; + case 10722: + record = new EpsgConversionRecord(10722, "Transverse Mercator", 5); + return true; + case 10730: + record = new EpsgConversionRecord(10730, "Transverse Mercator", 5); + return true; + case 10743: + record = new EpsgConversionRecord(10743, "Transverse Mercator", 5); + return true; + case 10757: + record = new EpsgConversionRecord(10757, "Transverse Mercator", 5); + return true; + case 10772: + record = new EpsgConversionRecord(10772, "Transverse Mercator", 5); + return true; + case 10819: + record = new EpsgConversionRecord(10819, "Albers Equal Area", 6); + return true; + case 10832: + record = new EpsgConversionRecord(10832, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10856: + record = new EpsgConversionRecord(10856, "Albers Equal Area", 6); + return true; + case 10862: + record = new EpsgConversionRecord(10862, "Transverse Mercator", 5); + return true; + case 10901: + record = new EpsgConversionRecord(10901, "Transverse Mercator", 5); + return true; + case 10902: + record = new EpsgConversionRecord(10902, "Transverse Mercator", 5); + return true; + case 10903: + record = new EpsgConversionRecord(10903, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10931: + record = new EpsgConversionRecord(10931, "Transverse Mercator", 5); + return true; + case 10932: + record = new EpsgConversionRecord(10932, "Transverse Mercator", 5); + return true; + case 10933: + record = new EpsgConversionRecord(10933, "Lambert Conic Conformal (2SP)", 6); + return true; + case 10934: + record = new EpsgConversionRecord(10934, "Albers Equal Area", 6); + return true; + case 10970: + record = new EpsgConversionRecord(10970, "Lambert Conic Conformal (1SP)", 5); + return true; + case 10971: + record = new EpsgConversionRecord(10971, "Lambert Conic Conformal (1SP)", 5); + return true; + case 10972: + record = new EpsgConversionRecord(10972, "Lambert Conic Conformal (1SP)", 5); + return true; + case 10973: + record = new EpsgConversionRecord(10973, "Lambert Conic Conformal (1SP)", 5); + return true; + case 10974: + record = new EpsgConversionRecord(10974, "Lambert Conic Conformal (1SP)", 5); + return true; + case 10975: + record = new EpsgConversionRecord(10975, "Transverse Mercator", 5); + return true; + case 10976: + record = new EpsgConversionRecord(10976, "Transverse Mercator", 5); + return true; + case 10977: + record = new EpsgConversionRecord(10977, "Transverse Mercator", 5); + return true; + case 10978: + record = new EpsgConversionRecord(10978, "Transverse Mercator", 5); + return true; + case 10994: + record = new EpsgConversionRecord(10994, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket11(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 11001: + record = new EpsgConversionRecord(11001, "Transverse Mercator", 5); + return true; + case 11002: + record = new EpsgConversionRecord(11002, "Transverse Mercator", 5); + return true; + case 11031: + record = new EpsgConversionRecord(11031, "Transverse Mercator", 5); + return true; + case 11032: + record = new EpsgConversionRecord(11032, "Transverse Mercator", 5); + return true; + case 11101: + record = new EpsgConversionRecord(11101, "Transverse Mercator", 5); + return true; + case 11102: + record = new EpsgConversionRecord(11102, "Transverse Mercator", 5); + return true; + case 11103: + record = new EpsgConversionRecord(11103, "Transverse Mercator", 5); + return true; + case 11131: + record = new EpsgConversionRecord(11131, "Transverse Mercator", 5); + return true; + case 11132: + record = new EpsgConversionRecord(11132, "Transverse Mercator", 5); + return true; + case 11133: + record = new EpsgConversionRecord(11133, "Transverse Mercator", 5); + return true; + case 11201: + record = new EpsgConversionRecord(11201, "Transverse Mercator", 5); + return true; + case 11202: + record = new EpsgConversionRecord(11202, "Transverse Mercator", 5); + return true; + case 11231: + record = new EpsgConversionRecord(11231, "Transverse Mercator", 5); + return true; + case 11232: + record = new EpsgConversionRecord(11232, "Transverse Mercator", 5); + return true; + case 11233: + record = new EpsgConversionRecord(11233, "Transverse Mercator", 5); + return true; + case 11234: + record = new EpsgConversionRecord(11234, "Transverse Mercator", 5); + return true; + case 11235: + record = new EpsgConversionRecord(11235, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11236: + record = new EpsgConversionRecord(11236, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11237: + record = new EpsgConversionRecord(11237, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11238: + record = new EpsgConversionRecord(11238, "Transverse Mercator", 5); + return true; + case 11239: + record = new EpsgConversionRecord(11239, "Transverse Mercator", 5); + return true; + case 11240: + record = new EpsgConversionRecord(11240, "Transverse Mercator", 5); + return true; + case 11241: + record = new EpsgConversionRecord(11241, "Transverse Mercator", 5); + return true; + case 11242: + record = new EpsgConversionRecord(11242, "Transverse Mercator", 5); + return true; + case 11243: + record = new EpsgConversionRecord(11243, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11244: + record = new EpsgConversionRecord(11244, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11245: + record = new EpsgConversionRecord(11245, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11246: + record = new EpsgConversionRecord(11246, "Transverse Mercator", 5); + return true; + case 11247: + record = new EpsgConversionRecord(11247, "Transverse Mercator", 5); + return true; + case 11248: + record = new EpsgConversionRecord(11248, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11249: + record = new EpsgConversionRecord(11249, "Transverse Mercator", 5); + return true; + case 11250: + record = new EpsgConversionRecord(11250, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11251: + record = new EpsgConversionRecord(11251, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11252: + record = new EpsgConversionRecord(11252, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11253: + record = new EpsgConversionRecord(11253, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11254: + record = new EpsgConversionRecord(11254, "Transverse Mercator", 5); + return true; + case 11255: + record = new EpsgConversionRecord(11255, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11256: + record = new EpsgConversionRecord(11256, "Transverse Mercator", 5); + return true; + case 11257: + record = new EpsgConversionRecord(11257, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11258: + record = new EpsgConversionRecord(11258, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11259: + record = new EpsgConversionRecord(11259, "Transverse Mercator", 5); + return true; + case 11260: + record = new EpsgConversionRecord(11260, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11261: + record = new EpsgConversionRecord(11261, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11262: + record = new EpsgConversionRecord(11262, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11263: + record = new EpsgConversionRecord(11263, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11264: + record = new EpsgConversionRecord(11264, "Lambert Conic Conformal (1SP)", 5); + return true; + case 11265: + record = new EpsgConversionRecord(11265, "Transverse Mercator", 5); + return true; + case 11287: + record = new EpsgConversionRecord(11287, "Transverse Mercator", 5); + return true; + case 11288: + record = new EpsgConversionRecord(11288, "Transverse Mercator", 5); + return true; + case 11289: + record = new EpsgConversionRecord(11289, "Transverse Mercator", 5); + return true; + case 11290: + record = new EpsgConversionRecord(11290, "Transverse Mercator", 5); + return true; + case 11291: + record = new EpsgConversionRecord(11291, "Transverse Mercator", 5); + return true; + case 11292: + record = new EpsgConversionRecord(11292, "Transverse Mercator", 5); + return true; + case 11293: + record = new EpsgConversionRecord(11293, "Transverse Mercator", 5); + return true; + case 11294: + record = new EpsgConversionRecord(11294, "Transverse Mercator", 5); + return true; + case 11295: + record = new EpsgConversionRecord(11295, "Transverse Mercator", 5); + return true; + case 11301: + record = new EpsgConversionRecord(11301, "Transverse Mercator", 5); + return true; + case 11302: + record = new EpsgConversionRecord(11302, "Transverse Mercator", 5); + return true; + case 11331: + record = new EpsgConversionRecord(11331, "Transverse Mercator", 5); + return true; + case 11332: + record = new EpsgConversionRecord(11332, "Transverse Mercator", 5); + return true; + case 11340: + record = new EpsgConversionRecord(11340, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11342: + record = new EpsgConversionRecord(11342, "Transverse Mercator", 5); + return true; + case 11343: + record = new EpsgConversionRecord(11343, "Transverse Mercator", 5); + return true; + case 11344: + record = new EpsgConversionRecord(11344, "Transverse Mercator", 5); + return true; + case 11345: + record = new EpsgConversionRecord(11345, "Transverse Mercator", 5); + return true; + case 11346: + record = new EpsgConversionRecord(11346, "Transverse Mercator", 5); + return true; + case 11347: + record = new EpsgConversionRecord(11347, "Transverse Mercator", 5); + return true; + case 11348: + record = new EpsgConversionRecord(11348, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 11349: + record = new EpsgConversionRecord(11349, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 11350: + record = new EpsgConversionRecord(11350, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 11351: + record = new EpsgConversionRecord(11351, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 11352: + record = new EpsgConversionRecord(11352, "Transverse Mercator", 5); + return true; + case 11353: + record = new EpsgConversionRecord(11353, "Transverse Mercator", 5); + return true; + case 11354: + record = new EpsgConversionRecord(11354, "Transverse Mercator", 5); + return true; + case 11355: + record = new EpsgConversionRecord(11355, "Transverse Mercator", 5); + return true; + case 11356: + record = new EpsgConversionRecord(11356, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 11357: + record = new EpsgConversionRecord(11357, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 11358: + record = new EpsgConversionRecord(11358, "Transverse Mercator", 5); + return true; + case 11359: + record = new EpsgConversionRecord(11359, "Transverse Mercator", 5); + return true; + case 11389: + record = new EpsgConversionRecord(11389, "Local Orthographic", 6); + return true; + case 11401: + record = new EpsgConversionRecord(11401, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11402: + record = new EpsgConversionRecord(11402, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11431: + record = new EpsgConversionRecord(11431, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11432: + record = new EpsgConversionRecord(11432, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11501: + record = new EpsgConversionRecord(11501, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11502: + record = new EpsgConversionRecord(11502, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11531: + record = new EpsgConversionRecord(11531, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11532: + record = new EpsgConversionRecord(11532, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11601: + record = new EpsgConversionRecord(11601, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11602: + record = new EpsgConversionRecord(11602, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11630: + record = new EpsgConversionRecord(11630, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11632: + record = new EpsgConversionRecord(11632, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11701: + record = new EpsgConversionRecord(11701, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11702: + record = new EpsgConversionRecord(11702, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11703: + record = new EpsgConversionRecord(11703, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11731: + record = new EpsgConversionRecord(11731, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11732: + record = new EpsgConversionRecord(11732, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11733: + record = new EpsgConversionRecord(11733, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11801: + record = new EpsgConversionRecord(11801, "Transverse Mercator", 5); + return true; + case 11802: + record = new EpsgConversionRecord(11802, "Transverse Mercator", 5); + return true; + case 11831: + record = new EpsgConversionRecord(11831, "Transverse Mercator", 5); + return true; + case 11832: + record = new EpsgConversionRecord(11832, "Transverse Mercator", 5); + return true; + case 11833: + record = new EpsgConversionRecord(11833, "Transverse Mercator", 5); + return true; + case 11834: + record = new EpsgConversionRecord(11834, "Transverse Mercator", 5); + return true; + case 11851: + record = new EpsgConversionRecord(11851, "Transverse Mercator", 5); + return true; + case 11853: + record = new EpsgConversionRecord(11853, "Transverse Mercator", 5); + return true; + case 11854: + record = new EpsgConversionRecord(11854, "Transverse Mercator", 5); + return true; + case 11900: + record = new EpsgConversionRecord(11900, "Lambert Conic Conformal (2SP)", 6); + return true; + case 11930: + record = new EpsgConversionRecord(11930, "Lambert Conic Conformal (2SP)", 6); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket12(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 12001: + record = new EpsgConversionRecord(12001, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12002: + record = new EpsgConversionRecord(12002, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12031: + record = new EpsgConversionRecord(12031, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12032: + record = new EpsgConversionRecord(12032, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12101: + record = new EpsgConversionRecord(12101, "Transverse Mercator", 5); + return true; + case 12102: + record = new EpsgConversionRecord(12102, "Transverse Mercator", 5); + return true; + case 12103: + record = new EpsgConversionRecord(12103, "Transverse Mercator", 5); + return true; + case 12141: + record = new EpsgConversionRecord(12141, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12142: + record = new EpsgConversionRecord(12142, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12143: + record = new EpsgConversionRecord(12143, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12150: + record = new EpsgConversionRecord(12150, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 12201: + record = new EpsgConversionRecord(12201, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12202: + record = new EpsgConversionRecord(12202, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12203: + record = new EpsgConversionRecord(12203, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12231: + record = new EpsgConversionRecord(12231, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12232: + record = new EpsgConversionRecord(12232, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12233: + record = new EpsgConversionRecord(12233, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12234: + record = new EpsgConversionRecord(12234, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12235: + record = new EpsgConversionRecord(12235, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12236: + record = new EpsgConversionRecord(12236, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12301: + record = new EpsgConversionRecord(12301, "Transverse Mercator", 5); + return true; + case 12302: + record = new EpsgConversionRecord(12302, "Transverse Mercator", 5); + return true; + case 12331: + record = new EpsgConversionRecord(12331, "Transverse Mercator", 5); + return true; + case 12332: + record = new EpsgConversionRecord(12332, "Transverse Mercator", 5); + return true; + case 12401: + record = new EpsgConversionRecord(12401, "Transverse Mercator", 5); + return true; + case 12402: + record = new EpsgConversionRecord(12402, "Transverse Mercator", 5); + return true; + case 12403: + record = new EpsgConversionRecord(12403, "Transverse Mercator", 5); + return true; + case 12431: + record = new EpsgConversionRecord(12431, "Transverse Mercator", 5); + return true; + case 12432: + record = new EpsgConversionRecord(12432, "Transverse Mercator", 5); + return true; + case 12433: + record = new EpsgConversionRecord(12433, "Transverse Mercator", 5); + return true; + case 12501: + record = new EpsgConversionRecord(12501, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12502: + record = new EpsgConversionRecord(12502, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12503: + record = new EpsgConversionRecord(12503, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12530: + record = new EpsgConversionRecord(12530, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12601: + record = new EpsgConversionRecord(12601, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12602: + record = new EpsgConversionRecord(12602, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12630: + record = new EpsgConversionRecord(12630, "Lambert Conic Conformal (2SP)", 6); + return true; + case 12701: + record = new EpsgConversionRecord(12701, "Transverse Mercator", 5); + return true; + case 12702: + record = new EpsgConversionRecord(12702, "Transverse Mercator", 5); + return true; + case 12703: + record = new EpsgConversionRecord(12703, "Transverse Mercator", 5); + return true; + case 12731: + record = new EpsgConversionRecord(12731, "Transverse Mercator", 5); + return true; + case 12732: + record = new EpsgConversionRecord(12732, "Transverse Mercator", 5); + return true; + case 12733: + record = new EpsgConversionRecord(12733, "Transverse Mercator", 5); + return true; + case 12800: + record = new EpsgConversionRecord(12800, "Transverse Mercator", 5); + return true; + case 12830: + record = new EpsgConversionRecord(12830, "Transverse Mercator", 5); + return true; + case 12900: + record = new EpsgConversionRecord(12900, "Transverse Mercator", 5); + return true; + case 12930: + record = new EpsgConversionRecord(12930, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket13(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 13001: + record = new EpsgConversionRecord(13001, "Transverse Mercator", 5); + return true; + case 13002: + record = new EpsgConversionRecord(13002, "Transverse Mercator", 5); + return true; + case 13003: + record = new EpsgConversionRecord(13003, "Transverse Mercator", 5); + return true; + case 13031: + record = new EpsgConversionRecord(13031, "Transverse Mercator", 5); + return true; + case 13032: + record = new EpsgConversionRecord(13032, "Transverse Mercator", 5); + return true; + case 13033: + record = new EpsgConversionRecord(13033, "Transverse Mercator", 5); + return true; + case 13101: + record = new EpsgConversionRecord(13101, "Transverse Mercator", 5); + return true; + case 13102: + record = new EpsgConversionRecord(13102, "Transverse Mercator", 5); + return true; + case 13103: + record = new EpsgConversionRecord(13103, "Transverse Mercator", 5); + return true; + case 13131: + record = new EpsgConversionRecord(13131, "Transverse Mercator", 5); + return true; + case 13132: + record = new EpsgConversionRecord(13132, "Transverse Mercator", 5); + return true; + case 13133: + record = new EpsgConversionRecord(13133, "Transverse Mercator", 5); + return true; + case 13134: + record = new EpsgConversionRecord(13134, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13200: + record = new EpsgConversionRecord(13200, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13230: + record = new EpsgConversionRecord(13230, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13301: + record = new EpsgConversionRecord(13301, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13302: + record = new EpsgConversionRecord(13302, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13331: + record = new EpsgConversionRecord(13331, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13332: + record = new EpsgConversionRecord(13332, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13401: + record = new EpsgConversionRecord(13401, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13402: + record = new EpsgConversionRecord(13402, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13431: + record = new EpsgConversionRecord(13431, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13432: + record = new EpsgConversionRecord(13432, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13433: + record = new EpsgConversionRecord(13433, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13434: + record = new EpsgConversionRecord(13434, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13501: + record = new EpsgConversionRecord(13501, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13502: + record = new EpsgConversionRecord(13502, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13531: + record = new EpsgConversionRecord(13531, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13532: + record = new EpsgConversionRecord(13532, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13601: + record = new EpsgConversionRecord(13601, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13602: + record = new EpsgConversionRecord(13602, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13631: + record = new EpsgConversionRecord(13631, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13632: + record = new EpsgConversionRecord(13632, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13633: + record = new EpsgConversionRecord(13633, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13701: + record = new EpsgConversionRecord(13701, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13731: + record = new EpsgConversionRecord(13731, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13732: + record = new EpsgConversionRecord(13732, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13800: + record = new EpsgConversionRecord(13800, "Transverse Mercator", 5); + return true; + case 13830: + record = new EpsgConversionRecord(13830, "Transverse Mercator", 5); + return true; + case 13901: + record = new EpsgConversionRecord(13901, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13902: + record = new EpsgConversionRecord(13902, "Lambert Conic Conformal (2SP)", 6); + return true; + case 13930: + record = new EpsgConversionRecord(13930, "Lambert Conic Conformal (2SP)", 6); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket14(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 14001: + record = new EpsgConversionRecord(14001, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14002: + record = new EpsgConversionRecord(14002, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14031: + record = new EpsgConversionRecord(14031, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14032: + record = new EpsgConversionRecord(14032, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14130: + record = new EpsgConversionRecord(14130, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14201: + record = new EpsgConversionRecord(14201, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14202: + record = new EpsgConversionRecord(14202, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14203: + record = new EpsgConversionRecord(14203, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14204: + record = new EpsgConversionRecord(14204, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14205: + record = new EpsgConversionRecord(14205, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14231: + record = new EpsgConversionRecord(14231, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14232: + record = new EpsgConversionRecord(14232, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14233: + record = new EpsgConversionRecord(14233, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14234: + record = new EpsgConversionRecord(14234, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14235: + record = new EpsgConversionRecord(14235, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14251: + record = new EpsgConversionRecord(14251, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14252: + record = new EpsgConversionRecord(14252, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14253: + record = new EpsgConversionRecord(14253, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14254: + record = new EpsgConversionRecord(14254, "Albers Equal Area", 6); + return true; + case 14301: + record = new EpsgConversionRecord(14301, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14302: + record = new EpsgConversionRecord(14302, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14303: + record = new EpsgConversionRecord(14303, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14331: + record = new EpsgConversionRecord(14331, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14332: + record = new EpsgConversionRecord(14332, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14333: + record = new EpsgConversionRecord(14333, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14400: + record = new EpsgConversionRecord(14400, "Transverse Mercator", 5); + return true; + case 14430: + record = new EpsgConversionRecord(14430, "Transverse Mercator", 5); + return true; + case 14501: + record = new EpsgConversionRecord(14501, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14502: + record = new EpsgConversionRecord(14502, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14531: + record = new EpsgConversionRecord(14531, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14532: + record = new EpsgConversionRecord(14532, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14601: + record = new EpsgConversionRecord(14601, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14602: + record = new EpsgConversionRecord(14602, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14631: + record = new EpsgConversionRecord(14631, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14632: + record = new EpsgConversionRecord(14632, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14701: + record = new EpsgConversionRecord(14701, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14702: + record = new EpsgConversionRecord(14702, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14731: + record = new EpsgConversionRecord(14731, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14732: + record = new EpsgConversionRecord(14732, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14735: + record = new EpsgConversionRecord(14735, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14736: + record = new EpsgConversionRecord(14736, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14801: + record = new EpsgConversionRecord(14801, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14802: + record = new EpsgConversionRecord(14802, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14803: + record = new EpsgConversionRecord(14803, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14811: + record = new EpsgConversionRecord(14811, "Transverse Mercator", 5); + return true; + case 14831: + record = new EpsgConversionRecord(14831, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14832: + record = new EpsgConversionRecord(14832, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14833: + record = new EpsgConversionRecord(14833, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14841: + record = new EpsgConversionRecord(14841, "Transverse Mercator", 5); + return true; + case 14901: + record = new EpsgConversionRecord(14901, "Transverse Mercator", 5); + return true; + case 14902: + record = new EpsgConversionRecord(14902, "Transverse Mercator", 5); + return true; + case 14903: + record = new EpsgConversionRecord(14903, "Transverse Mercator", 5); + return true; + case 14904: + record = new EpsgConversionRecord(14904, "Transverse Mercator", 5); + return true; + case 14930: + record = new EpsgConversionRecord(14930, "Lambert Conic Conformal (2SP)", 6); + return true; + case 14931: + record = new EpsgConversionRecord(14931, "Transverse Mercator", 5); + return true; + case 14932: + record = new EpsgConversionRecord(14932, "Transverse Mercator", 5); + return true; + case 14933: + record = new EpsgConversionRecord(14933, "Transverse Mercator", 5); + return true; + case 14934: + record = new EpsgConversionRecord(14934, "Transverse Mercator", 5); + return true; + case 14935: + record = new EpsgConversionRecord(14935, "Transverse Mercator", 5); + return true; + case 14936: + record = new EpsgConversionRecord(14936, "Transverse Mercator", 5); + return true; + case 14937: + record = new EpsgConversionRecord(14937, "Transverse Mercator", 5); + return true; + case 14938: + record = new EpsgConversionRecord(14938, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket15(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 15001: + record = new EpsgConversionRecord(15001, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 15002: + record = new EpsgConversionRecord(15002, "Transverse Mercator", 5); + return true; + case 15003: + record = new EpsgConversionRecord(15003, "Transverse Mercator", 5); + return true; + case 15004: + record = new EpsgConversionRecord(15004, "Transverse Mercator", 5); + return true; + case 15005: + record = new EpsgConversionRecord(15005, "Transverse Mercator", 5); + return true; + case 15006: + record = new EpsgConversionRecord(15006, "Transverse Mercator", 5); + return true; + case 15007: + record = new EpsgConversionRecord(15007, "Transverse Mercator", 5); + return true; + case 15008: + record = new EpsgConversionRecord(15008, "Transverse Mercator", 5); + return true; + case 15009: + record = new EpsgConversionRecord(15009, "Transverse Mercator", 5); + return true; + case 15010: + record = new EpsgConversionRecord(15010, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15020: + record = new EpsgConversionRecord(15020, "Albers Equal Area", 6); + return true; + case 15021: + record = new EpsgConversionRecord(15021, "Albers Equal Area", 6); + return true; + case 15031: + record = new EpsgConversionRecord(15031, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 15032: + record = new EpsgConversionRecord(15032, "Transverse Mercator", 5); + return true; + case 15033: + record = new EpsgConversionRecord(15033, "Transverse Mercator", 5); + return true; + case 15034: + record = new EpsgConversionRecord(15034, "Transverse Mercator", 5); + return true; + case 15035: + record = new EpsgConversionRecord(15035, "Transverse Mercator", 5); + return true; + case 15036: + record = new EpsgConversionRecord(15036, "Transverse Mercator", 5); + return true; + case 15037: + record = new EpsgConversionRecord(15037, "Transverse Mercator", 5); + return true; + case 15038: + record = new EpsgConversionRecord(15038, "Transverse Mercator", 5); + return true; + case 15039: + record = new EpsgConversionRecord(15039, "Transverse Mercator", 5); + return true; + case 15040: + record = new EpsgConversionRecord(15040, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15101: + record = new EpsgConversionRecord(15101, "Transverse Mercator", 5); + return true; + case 15102: + record = new EpsgConversionRecord(15102, "Transverse Mercator", 5); + return true; + case 15103: + record = new EpsgConversionRecord(15103, "Transverse Mercator", 5); + return true; + case 15104: + record = new EpsgConversionRecord(15104, "Transverse Mercator", 5); + return true; + case 15105: + record = new EpsgConversionRecord(15105, "Transverse Mercator", 5); + return true; + case 15131: + record = new EpsgConversionRecord(15131, "Transverse Mercator", 5); + return true; + case 15132: + record = new EpsgConversionRecord(15132, "Transverse Mercator", 5); + return true; + case 15133: + record = new EpsgConversionRecord(15133, "Transverse Mercator", 5); + return true; + case 15134: + record = new EpsgConversionRecord(15134, "Transverse Mercator", 5); + return true; + case 15135: + record = new EpsgConversionRecord(15135, "Transverse Mercator", 5); + return true; + case 15138: + record = new EpsgConversionRecord(15138, "Transverse Mercator", 5); + return true; + case 15201: + record = new EpsgConversionRecord(15201, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15202: + record = new EpsgConversionRecord(15202, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15230: + record = new EpsgConversionRecord(15230, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15297: + record = new EpsgConversionRecord(15297, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15298: + record = new EpsgConversionRecord(15298, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15299: + record = new EpsgConversionRecord(15299, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15302: + record = new EpsgConversionRecord(15302, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15303: + record = new EpsgConversionRecord(15303, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15304: + record = new EpsgConversionRecord(15304, "Transverse Mercator", 5); + return true; + case 15305: + record = new EpsgConversionRecord(15305, "Transverse Mercator", 5); + return true; + case 15306: + record = new EpsgConversionRecord(15306, "Transverse Mercator", 5); + return true; + case 15307: + record = new EpsgConversionRecord(15307, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15308: + record = new EpsgConversionRecord(15308, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15309: + record = new EpsgConversionRecord(15309, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15310: + record = new EpsgConversionRecord(15310, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15311: + record = new EpsgConversionRecord(15311, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15312: + record = new EpsgConversionRecord(15312, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15313: + record = new EpsgConversionRecord(15313, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15314: + record = new EpsgConversionRecord(15314, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15315: + record = new EpsgConversionRecord(15315, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15316: + record = new EpsgConversionRecord(15316, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15317: + record = new EpsgConversionRecord(15317, "Transverse Mercator", 5); + return true; + case 15318: + record = new EpsgConversionRecord(15318, "Transverse Mercator", 5); + return true; + case 15319: + record = new EpsgConversionRecord(15319, "Transverse Mercator", 5); + return true; + case 15320: + record = new EpsgConversionRecord(15320, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15321: + record = new EpsgConversionRecord(15321, "Transverse Mercator", 5); + return true; + case 15322: + record = new EpsgConversionRecord(15322, "Transverse Mercator", 5); + return true; + case 15323: + record = new EpsgConversionRecord(15323, "Transverse Mercator", 5); + return true; + case 15324: + record = new EpsgConversionRecord(15324, "Transverse Mercator", 5); + return true; + case 15325: + record = new EpsgConversionRecord(15325, "Transverse Mercator", 5); + return true; + case 15328: + record = new EpsgConversionRecord(15328, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15329: + record = new EpsgConversionRecord(15329, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15330: + record = new EpsgConversionRecord(15330, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15331: + record = new EpsgConversionRecord(15331, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15332: + record = new EpsgConversionRecord(15332, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15333: + record = new EpsgConversionRecord(15333, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15334: + record = new EpsgConversionRecord(15334, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15335: + record = new EpsgConversionRecord(15335, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15336: + record = new EpsgConversionRecord(15336, "Transverse Mercator", 5); + return true; + case 15337: + record = new EpsgConversionRecord(15337, "Transverse Mercator", 5); + return true; + case 15338: + record = new EpsgConversionRecord(15338, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15339: + record = new EpsgConversionRecord(15339, "Transverse Mercator", 5); + return true; + case 15340: + record = new EpsgConversionRecord(15340, "Transverse Mercator", 5); + return true; + case 15341: + record = new EpsgConversionRecord(15341, "Transverse Mercator", 5); + return true; + case 15342: + record = new EpsgConversionRecord(15342, "Transverse Mercator", 5); + return true; + case 15343: + record = new EpsgConversionRecord(15343, "Transverse Mercator", 5); + return true; + case 15344: + record = new EpsgConversionRecord(15344, "Transverse Mercator", 5); + return true; + case 15345: + record = new EpsgConversionRecord(15345, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15346: + record = new EpsgConversionRecord(15346, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15347: + record = new EpsgConversionRecord(15347, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15348: + record = new EpsgConversionRecord(15348, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15349: + record = new EpsgConversionRecord(15349, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15350: + record = new EpsgConversionRecord(15350, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15351: + record = new EpsgConversionRecord(15351, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15352: + record = new EpsgConversionRecord(15352, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15353: + record = new EpsgConversionRecord(15353, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15354: + record = new EpsgConversionRecord(15354, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15355: + record = new EpsgConversionRecord(15355, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15356: + record = new EpsgConversionRecord(15356, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15357: + record = new EpsgConversionRecord(15357, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15358: + record = new EpsgConversionRecord(15358, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15359: + record = new EpsgConversionRecord(15359, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15360: + record = new EpsgConversionRecord(15360, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15361: + record = new EpsgConversionRecord(15361, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15362: + record = new EpsgConversionRecord(15362, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15363: + record = new EpsgConversionRecord(15363, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15364: + record = new EpsgConversionRecord(15364, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15365: + record = new EpsgConversionRecord(15365, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15366: + record = new EpsgConversionRecord(15366, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15367: + record = new EpsgConversionRecord(15367, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15368: + record = new EpsgConversionRecord(15368, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15369: + record = new EpsgConversionRecord(15369, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15370: + record = new EpsgConversionRecord(15370, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15371: + record = new EpsgConversionRecord(15371, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15372: + record = new EpsgConversionRecord(15372, "Transverse Mercator", 5); + return true; + case 15373: + record = new EpsgConversionRecord(15373, "Transverse Mercator", 5); + return true; + case 15374: + record = new EpsgConversionRecord(15374, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15375: + record = new EpsgConversionRecord(15375, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15376: + record = new EpsgConversionRecord(15376, "Lambert Conic Conformal (1SP)", 5); + return true; + case 15377: + record = new EpsgConversionRecord(15377, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15378: + record = new EpsgConversionRecord(15378, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15379: + record = new EpsgConversionRecord(15379, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15380: + record = new EpsgConversionRecord(15380, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15381: + record = new EpsgConversionRecord(15381, "Transverse Mercator", 5); + return true; + case 15382: + record = new EpsgConversionRecord(15382, "Transverse Mercator", 5); + return true; + case 15383: + record = new EpsgConversionRecord(15383, "Transverse Mercator", 5); + return true; + case 15384: + record = new EpsgConversionRecord(15384, "Transverse Mercator", 5); + return true; + case 15385: + record = new EpsgConversionRecord(15385, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15386: + record = new EpsgConversionRecord(15386, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15387: + record = new EpsgConversionRecord(15387, "Transverse Mercator", 5); + return true; + case 15388: + record = new EpsgConversionRecord(15388, "Transverse Mercator", 5); + return true; + case 15389: + record = new EpsgConversionRecord(15389, "Transverse Mercator", 5); + return true; + case 15390: + record = new EpsgConversionRecord(15390, "Transverse Mercator", 5); + return true; + case 15391: + record = new EpsgConversionRecord(15391, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15392: + record = new EpsgConversionRecord(15392, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15393: + record = new EpsgConversionRecord(15393, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15394: + record = new EpsgConversionRecord(15394, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15395: + record = new EpsgConversionRecord(15395, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15396: + record = new EpsgConversionRecord(15396, "Lambert Conic Conformal (2SP)", 6); + return true; + case 15397: + record = new EpsgConversionRecord(15397, "Albers Equal Area", 6); + return true; + case 15398: + record = new EpsgConversionRecord(15398, "Albers Equal Area", 6); + return true; + case 15399: + record = new EpsgConversionRecord(15399, "Modified Azimuthal Equidistant", 4); + return true; + case 15400: + record = new EpsgConversionRecord(15400, "Guam Projection", 4); + return true; + case 15914: + record = new EpsgConversionRecord(15914, "Transverse Mercator", 5); + return true; + case 15915: + record = new EpsgConversionRecord(15915, "Transverse Mercator", 5); + return true; + case 15916: + record = new EpsgConversionRecord(15916, "Transverse Mercator", 5); + return true; + case 15917: + record = new EpsgConversionRecord(15917, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket16(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 16000: + record = new EpsgConversionRecord(16000, "Transverse Mercator Zoned Grid System", 6); + return true; + case 16001: + record = new EpsgConversionRecord(16001, "Transverse Mercator", 5); + return true; + case 16002: + record = new EpsgConversionRecord(16002, "Transverse Mercator", 5); + return true; + case 16003: + record = new EpsgConversionRecord(16003, "Transverse Mercator", 5); + return true; + case 16004: + record = new EpsgConversionRecord(16004, "Transverse Mercator", 5); + return true; + case 16005: + record = new EpsgConversionRecord(16005, "Transverse Mercator", 5); + return true; + case 16006: + record = new EpsgConversionRecord(16006, "Transverse Mercator", 5); + return true; + case 16007: + record = new EpsgConversionRecord(16007, "Transverse Mercator", 5); + return true; + case 16008: + record = new EpsgConversionRecord(16008, "Transverse Mercator", 5); + return true; + case 16009: + record = new EpsgConversionRecord(16009, "Transverse Mercator", 5); + return true; + case 16010: + record = new EpsgConversionRecord(16010, "Transverse Mercator", 5); + return true; + case 16011: + record = new EpsgConversionRecord(16011, "Transverse Mercator", 5); + return true; + case 16012: + record = new EpsgConversionRecord(16012, "Transverse Mercator", 5); + return true; + case 16013: + record = new EpsgConversionRecord(16013, "Transverse Mercator", 5); + return true; + case 16014: + record = new EpsgConversionRecord(16014, "Transverse Mercator", 5); + return true; + case 16015: + record = new EpsgConversionRecord(16015, "Transverse Mercator", 5); + return true; + case 16016: + record = new EpsgConversionRecord(16016, "Transverse Mercator", 5); + return true; + case 16017: + record = new EpsgConversionRecord(16017, "Transverse Mercator", 5); + return true; + case 16018: + record = new EpsgConversionRecord(16018, "Transverse Mercator", 5); + return true; + case 16019: + record = new EpsgConversionRecord(16019, "Transverse Mercator", 5); + return true; + case 16020: + record = new EpsgConversionRecord(16020, "Transverse Mercator", 5); + return true; + case 16021: + record = new EpsgConversionRecord(16021, "Transverse Mercator", 5); + return true; + case 16022: + record = new EpsgConversionRecord(16022, "Transverse Mercator", 5); + return true; + case 16023: + record = new EpsgConversionRecord(16023, "Transverse Mercator", 5); + return true; + case 16024: + record = new EpsgConversionRecord(16024, "Transverse Mercator", 5); + return true; + case 16025: + record = new EpsgConversionRecord(16025, "Transverse Mercator", 5); + return true; + case 16026: + record = new EpsgConversionRecord(16026, "Transverse Mercator", 5); + return true; + case 16027: + record = new EpsgConversionRecord(16027, "Transverse Mercator", 5); + return true; + case 16028: + record = new EpsgConversionRecord(16028, "Transverse Mercator", 5); + return true; + case 16029: + record = new EpsgConversionRecord(16029, "Transverse Mercator", 5); + return true; + case 16030: + record = new EpsgConversionRecord(16030, "Transverse Mercator", 5); + return true; + case 16031: + record = new EpsgConversionRecord(16031, "Transverse Mercator", 5); + return true; + case 16032: + record = new EpsgConversionRecord(16032, "Transverse Mercator", 5); + return true; + case 16033: + record = new EpsgConversionRecord(16033, "Transverse Mercator", 5); + return true; + case 16034: + record = new EpsgConversionRecord(16034, "Transverse Mercator", 5); + return true; + case 16035: + record = new EpsgConversionRecord(16035, "Transverse Mercator", 5); + return true; + case 16036: + record = new EpsgConversionRecord(16036, "Transverse Mercator", 5); + return true; + case 16037: + record = new EpsgConversionRecord(16037, "Transverse Mercator", 5); + return true; + case 16038: + record = new EpsgConversionRecord(16038, "Transverse Mercator", 5); + return true; + case 16039: + record = new EpsgConversionRecord(16039, "Transverse Mercator", 5); + return true; + case 16040: + record = new EpsgConversionRecord(16040, "Transverse Mercator", 5); + return true; + case 16041: + record = new EpsgConversionRecord(16041, "Transverse Mercator", 5); + return true; + case 16042: + record = new EpsgConversionRecord(16042, "Transverse Mercator", 5); + return true; + case 16043: + record = new EpsgConversionRecord(16043, "Transverse Mercator", 5); + return true; + case 16044: + record = new EpsgConversionRecord(16044, "Transverse Mercator", 5); + return true; + case 16045: + record = new EpsgConversionRecord(16045, "Transverse Mercator", 5); + return true; + case 16046: + record = new EpsgConversionRecord(16046, "Transverse Mercator", 5); + return true; + case 16047: + record = new EpsgConversionRecord(16047, "Transverse Mercator", 5); + return true; + case 16048: + record = new EpsgConversionRecord(16048, "Transverse Mercator", 5); + return true; + case 16049: + record = new EpsgConversionRecord(16049, "Transverse Mercator", 5); + return true; + case 16050: + record = new EpsgConversionRecord(16050, "Transverse Mercator", 5); + return true; + case 16051: + record = new EpsgConversionRecord(16051, "Transverse Mercator", 5); + return true; + case 16052: + record = new EpsgConversionRecord(16052, "Transverse Mercator", 5); + return true; + case 16053: + record = new EpsgConversionRecord(16053, "Transverse Mercator", 5); + return true; + case 16054: + record = new EpsgConversionRecord(16054, "Transverse Mercator", 5); + return true; + case 16055: + record = new EpsgConversionRecord(16055, "Transverse Mercator", 5); + return true; + case 16056: + record = new EpsgConversionRecord(16056, "Transverse Mercator", 5); + return true; + case 16057: + record = new EpsgConversionRecord(16057, "Transverse Mercator", 5); + return true; + case 16058: + record = new EpsgConversionRecord(16058, "Transverse Mercator", 5); + return true; + case 16059: + record = new EpsgConversionRecord(16059, "Transverse Mercator", 5); + return true; + case 16060: + record = new EpsgConversionRecord(16060, "Transverse Mercator", 5); + return true; + case 16061: + record = new EpsgConversionRecord(16061, "Polar Stereographic (variant A)", 5); + return true; + case 16065: + record = new EpsgConversionRecord(16065, "Transverse Mercator", 5); + return true; + case 16070: + record = new EpsgConversionRecord(16070, "Transverse Mercator", 5); + return true; + case 16071: + record = new EpsgConversionRecord(16071, "Transverse Mercator", 5); + return true; + case 16072: + record = new EpsgConversionRecord(16072, "Transverse Mercator", 5); + return true; + case 16073: + record = new EpsgConversionRecord(16073, "Transverse Mercator", 5); + return true; + case 16074: + record = new EpsgConversionRecord(16074, "Transverse Mercator", 5); + return true; + case 16075: + record = new EpsgConversionRecord(16075, "Transverse Mercator", 5); + return true; + case 16076: + record = new EpsgConversionRecord(16076, "Transverse Mercator", 5); + return true; + case 16077: + record = new EpsgConversionRecord(16077, "Transverse Mercator", 5); + return true; + case 16078: + record = new EpsgConversionRecord(16078, "Transverse Mercator", 5); + return true; + case 16079: + record = new EpsgConversionRecord(16079, "Transverse Mercator", 5); + return true; + case 16080: + record = new EpsgConversionRecord(16080, "Transverse Mercator", 5); + return true; + case 16081: + record = new EpsgConversionRecord(16081, "Transverse Mercator", 5); + return true; + case 16082: + record = new EpsgConversionRecord(16082, "Transverse Mercator", 5); + return true; + case 16083: + record = new EpsgConversionRecord(16083, "Transverse Mercator", 5); + return true; + case 16084: + record = new EpsgConversionRecord(16084, "Transverse Mercator", 5); + return true; + case 16085: + record = new EpsgConversionRecord(16085, "Transverse Mercator", 5); + return true; + case 16086: + record = new EpsgConversionRecord(16086, "Transverse Mercator", 5); + return true; + case 16087: + record = new EpsgConversionRecord(16087, "Transverse Mercator", 5); + return true; + case 16088: + record = new EpsgConversionRecord(16088, "Transverse Mercator", 5); + return true; + case 16089: + record = new EpsgConversionRecord(16089, "Transverse Mercator", 5); + return true; + case 16091: + record = new EpsgConversionRecord(16091, "Transverse Mercator", 5); + return true; + case 16092: + record = new EpsgConversionRecord(16092, "Transverse Mercator", 5); + return true; + case 16093: + record = new EpsgConversionRecord(16093, "Transverse Mercator", 5); + return true; + case 16094: + record = new EpsgConversionRecord(16094, "Transverse Mercator", 5); + return true; + case 16099: + record = new EpsgConversionRecord(16099, "Transverse Mercator", 5); + return true; + case 16100: + record = new EpsgConversionRecord(16100, "Transverse Mercator Zoned Grid System", 6); + return true; + case 16101: + record = new EpsgConversionRecord(16101, "Transverse Mercator", 5); + return true; + case 16102: + record = new EpsgConversionRecord(16102, "Transverse Mercator", 5); + return true; + case 16103: + record = new EpsgConversionRecord(16103, "Transverse Mercator", 5); + return true; + case 16104: + record = new EpsgConversionRecord(16104, "Transverse Mercator", 5); + return true; + case 16105: + record = new EpsgConversionRecord(16105, "Transverse Mercator", 5); + return true; + case 16106: + record = new EpsgConversionRecord(16106, "Transverse Mercator", 5); + return true; + case 16107: + record = new EpsgConversionRecord(16107, "Transverse Mercator", 5); + return true; + case 16108: + record = new EpsgConversionRecord(16108, "Transverse Mercator", 5); + return true; + case 16109: + record = new EpsgConversionRecord(16109, "Transverse Mercator", 5); + return true; + case 16110: + record = new EpsgConversionRecord(16110, "Transverse Mercator", 5); + return true; + case 16111: + record = new EpsgConversionRecord(16111, "Transverse Mercator", 5); + return true; + case 16112: + record = new EpsgConversionRecord(16112, "Transverse Mercator", 5); + return true; + case 16113: + record = new EpsgConversionRecord(16113, "Transverse Mercator", 5); + return true; + case 16114: + record = new EpsgConversionRecord(16114, "Transverse Mercator", 5); + return true; + case 16115: + record = new EpsgConversionRecord(16115, "Transverse Mercator", 5); + return true; + case 16116: + record = new EpsgConversionRecord(16116, "Transverse Mercator", 5); + return true; + case 16117: + record = new EpsgConversionRecord(16117, "Transverse Mercator", 5); + return true; + case 16118: + record = new EpsgConversionRecord(16118, "Transverse Mercator", 5); + return true; + case 16119: + record = new EpsgConversionRecord(16119, "Transverse Mercator", 5); + return true; + case 16120: + record = new EpsgConversionRecord(16120, "Transverse Mercator", 5); + return true; + case 16121: + record = new EpsgConversionRecord(16121, "Transverse Mercator", 5); + return true; + case 16122: + record = new EpsgConversionRecord(16122, "Transverse Mercator", 5); + return true; + case 16123: + record = new EpsgConversionRecord(16123, "Transverse Mercator", 5); + return true; + case 16124: + record = new EpsgConversionRecord(16124, "Transverse Mercator", 5); + return true; + case 16125: + record = new EpsgConversionRecord(16125, "Transverse Mercator", 5); + return true; + case 16126: + record = new EpsgConversionRecord(16126, "Transverse Mercator", 5); + return true; + case 16127: + record = new EpsgConversionRecord(16127, "Transverse Mercator", 5); + return true; + case 16128: + record = new EpsgConversionRecord(16128, "Transverse Mercator", 5); + return true; + case 16129: + record = new EpsgConversionRecord(16129, "Transverse Mercator", 5); + return true; + case 16130: + record = new EpsgConversionRecord(16130, "Transverse Mercator", 5); + return true; + case 16131: + record = new EpsgConversionRecord(16131, "Transverse Mercator", 5); + return true; + case 16132: + record = new EpsgConversionRecord(16132, "Transverse Mercator", 5); + return true; + case 16133: + record = new EpsgConversionRecord(16133, "Transverse Mercator", 5); + return true; + case 16134: + record = new EpsgConversionRecord(16134, "Transverse Mercator", 5); + return true; + case 16135: + record = new EpsgConversionRecord(16135, "Transverse Mercator", 5); + return true; + case 16136: + record = new EpsgConversionRecord(16136, "Transverse Mercator", 5); + return true; + case 16137: + record = new EpsgConversionRecord(16137, "Transverse Mercator", 5); + return true; + case 16138: + record = new EpsgConversionRecord(16138, "Transverse Mercator", 5); + return true; + case 16139: + record = new EpsgConversionRecord(16139, "Transverse Mercator", 5); + return true; + case 16140: + record = new EpsgConversionRecord(16140, "Transverse Mercator", 5); + return true; + case 16141: + record = new EpsgConversionRecord(16141, "Transverse Mercator", 5); + return true; + case 16142: + record = new EpsgConversionRecord(16142, "Transverse Mercator", 5); + return true; + case 16143: + record = new EpsgConversionRecord(16143, "Transverse Mercator", 5); + return true; + case 16144: + record = new EpsgConversionRecord(16144, "Transverse Mercator", 5); + return true; + case 16145: + record = new EpsgConversionRecord(16145, "Transverse Mercator", 5); + return true; + case 16146: + record = new EpsgConversionRecord(16146, "Transverse Mercator", 5); + return true; + case 16147: + record = new EpsgConversionRecord(16147, "Transverse Mercator", 5); + return true; + case 16148: + record = new EpsgConversionRecord(16148, "Transverse Mercator", 5); + return true; + case 16149: + record = new EpsgConversionRecord(16149, "Transverse Mercator", 5); + return true; + case 16150: + record = new EpsgConversionRecord(16150, "Transverse Mercator", 5); + return true; + case 16151: + record = new EpsgConversionRecord(16151, "Transverse Mercator", 5); + return true; + case 16152: + record = new EpsgConversionRecord(16152, "Transverse Mercator", 5); + return true; + case 16153: + record = new EpsgConversionRecord(16153, "Transverse Mercator", 5); + return true; + case 16154: + record = new EpsgConversionRecord(16154, "Transverse Mercator", 5); + return true; + case 16155: + record = new EpsgConversionRecord(16155, "Transverse Mercator", 5); + return true; + case 16156: + record = new EpsgConversionRecord(16156, "Transverse Mercator", 5); + return true; + case 16157: + record = new EpsgConversionRecord(16157, "Transverse Mercator", 5); + return true; + case 16158: + record = new EpsgConversionRecord(16158, "Transverse Mercator", 5); + return true; + case 16159: + record = new EpsgConversionRecord(16159, "Transverse Mercator", 5); + return true; + case 16160: + record = new EpsgConversionRecord(16160, "Transverse Mercator", 5); + return true; + case 16161: + record = new EpsgConversionRecord(16161, "Polar Stereographic (variant A)", 5); + return true; + case 16170: + record = new EpsgConversionRecord(16170, "Transverse Mercator", 5); + return true; + case 16172: + record = new EpsgConversionRecord(16172, "Transverse Mercator", 5); + return true; + case 16174: + record = new EpsgConversionRecord(16174, "Transverse Mercator", 5); + return true; + case 16176: + record = new EpsgConversionRecord(16176, "Transverse Mercator", 5); + return true; + case 16178: + record = new EpsgConversionRecord(16178, "Transverse Mercator", 5); + return true; + case 16180: + record = new EpsgConversionRecord(16180, "Transverse Mercator", 5); + return true; + case 16182: + record = new EpsgConversionRecord(16182, "Transverse Mercator", 5); + return true; + case 16184: + record = new EpsgConversionRecord(16184, "Transverse Mercator", 5); + return true; + case 16186: + record = new EpsgConversionRecord(16186, "Transverse Mercator", 5); + return true; + case 16188: + record = new EpsgConversionRecord(16188, "Transverse Mercator", 5); + return true; + case 16190: + record = new EpsgConversionRecord(16190, "Transverse Mercator", 5); + return true; + case 16192: + record = new EpsgConversionRecord(16192, "Transverse Mercator", 5); + return true; + case 16194: + record = new EpsgConversionRecord(16194, "Transverse Mercator", 5); + return true; + case 16202: + record = new EpsgConversionRecord(16202, "Transverse Mercator", 5); + return true; + case 16203: + record = new EpsgConversionRecord(16203, "Transverse Mercator", 5); + return true; + case 16204: + record = new EpsgConversionRecord(16204, "Transverse Mercator", 5); + return true; + case 16205: + record = new EpsgConversionRecord(16205, "Transverse Mercator", 5); + return true; + case 16206: + record = new EpsgConversionRecord(16206, "Transverse Mercator", 5); + return true; + case 16207: + record = new EpsgConversionRecord(16207, "Transverse Mercator", 5); + return true; + case 16208: + record = new EpsgConversionRecord(16208, "Transverse Mercator", 5); + return true; + case 16209: + record = new EpsgConversionRecord(16209, "Transverse Mercator", 5); + return true; + case 16210: + record = new EpsgConversionRecord(16210, "Transverse Mercator", 5); + return true; + case 16211: + record = new EpsgConversionRecord(16211, "Transverse Mercator", 5); + return true; + case 16212: + record = new EpsgConversionRecord(16212, "Transverse Mercator", 5); + return true; + case 16213: + record = new EpsgConversionRecord(16213, "Transverse Mercator", 5); + return true; + case 16214: + record = new EpsgConversionRecord(16214, "Transverse Mercator", 5); + return true; + case 16215: + record = new EpsgConversionRecord(16215, "Transverse Mercator", 5); + return true; + case 16216: + record = new EpsgConversionRecord(16216, "Transverse Mercator", 5); + return true; + case 16217: + record = new EpsgConversionRecord(16217, "Transverse Mercator", 5); + return true; + case 16218: + record = new EpsgConversionRecord(16218, "Transverse Mercator", 5); + return true; + case 16219: + record = new EpsgConversionRecord(16219, "Transverse Mercator", 5); + return true; + case 16220: + record = new EpsgConversionRecord(16220, "Transverse Mercator", 5); + return true; + case 16221: + record = new EpsgConversionRecord(16221, "Transverse Mercator", 5); + return true; + case 16222: + record = new EpsgConversionRecord(16222, "Transverse Mercator", 5); + return true; + case 16223: + record = new EpsgConversionRecord(16223, "Transverse Mercator", 5); + return true; + case 16224: + record = new EpsgConversionRecord(16224, "Transverse Mercator", 5); + return true; + case 16225: + record = new EpsgConversionRecord(16225, "Transverse Mercator", 5); + return true; + case 16226: + record = new EpsgConversionRecord(16226, "Transverse Mercator", 5); + return true; + case 16227: + record = new EpsgConversionRecord(16227, "Transverse Mercator", 5); + return true; + case 16228: + record = new EpsgConversionRecord(16228, "Transverse Mercator", 5); + return true; + case 16229: + record = new EpsgConversionRecord(16229, "Transverse Mercator", 5); + return true; + case 16230: + record = new EpsgConversionRecord(16230, "Transverse Mercator", 5); + return true; + case 16231: + record = new EpsgConversionRecord(16231, "Transverse Mercator", 5); + return true; + case 16232: + record = new EpsgConversionRecord(16232, "Transverse Mercator", 5); + return true; + case 16261: + record = new EpsgConversionRecord(16261, "Transverse Mercator", 5); + return true; + case 16262: + record = new EpsgConversionRecord(16262, "Transverse Mercator", 5); + return true; + case 16263: + record = new EpsgConversionRecord(16263, "Transverse Mercator", 5); + return true; + case 16264: + record = new EpsgConversionRecord(16264, "Transverse Mercator", 5); + return true; + case 16265: + record = new EpsgConversionRecord(16265, "Transverse Mercator", 5); + return true; + case 16266: + record = new EpsgConversionRecord(16266, "Transverse Mercator", 5); + return true; + case 16267: + record = new EpsgConversionRecord(16267, "Transverse Mercator", 5); + return true; + case 16268: + record = new EpsgConversionRecord(16268, "Transverse Mercator", 5); + return true; + case 16269: + record = new EpsgConversionRecord(16269, "Transverse Mercator", 5); + return true; + case 16270: + record = new EpsgConversionRecord(16270, "Transverse Mercator", 5); + return true; + case 16271: + record = new EpsgConversionRecord(16271, "Transverse Mercator", 5); + return true; + case 16272: + record = new EpsgConversionRecord(16272, "Transverse Mercator", 5); + return true; + case 16273: + record = new EpsgConversionRecord(16273, "Transverse Mercator", 5); + return true; + case 16274: + record = new EpsgConversionRecord(16274, "Transverse Mercator", 5); + return true; + case 16275: + record = new EpsgConversionRecord(16275, "Transverse Mercator", 5); + return true; + case 16276: + record = new EpsgConversionRecord(16276, "Transverse Mercator", 5); + return true; + case 16277: + record = new EpsgConversionRecord(16277, "Transverse Mercator", 5); + return true; + case 16278: + record = new EpsgConversionRecord(16278, "Transverse Mercator", 5); + return true; + case 16279: + record = new EpsgConversionRecord(16279, "Transverse Mercator", 5); + return true; + case 16280: + record = new EpsgConversionRecord(16280, "Transverse Mercator", 5); + return true; + case 16281: + record = new EpsgConversionRecord(16281, "Transverse Mercator", 5); + return true; + case 16282: + record = new EpsgConversionRecord(16282, "Transverse Mercator", 5); + return true; + case 16283: + record = new EpsgConversionRecord(16283, "Transverse Mercator", 5); + return true; + case 16284: + record = new EpsgConversionRecord(16284, "Transverse Mercator", 5); + return true; + case 16285: + record = new EpsgConversionRecord(16285, "Transverse Mercator", 5); + return true; + case 16286: + record = new EpsgConversionRecord(16286, "Transverse Mercator", 5); + return true; + case 16287: + record = new EpsgConversionRecord(16287, "Transverse Mercator", 5); + return true; + case 16288: + record = new EpsgConversionRecord(16288, "Transverse Mercator", 5); + return true; + case 16289: + record = new EpsgConversionRecord(16289, "Transverse Mercator", 5); + return true; + case 16290: + record = new EpsgConversionRecord(16290, "Transverse Mercator", 5); + return true; + case 16291: + record = new EpsgConversionRecord(16291, "Transverse Mercator", 5); + return true; + case 16292: + record = new EpsgConversionRecord(16292, "Transverse Mercator", 5); + return true; + case 16293: + record = new EpsgConversionRecord(16293, "Transverse Mercator", 5); + return true; + case 16294: + record = new EpsgConversionRecord(16294, "Transverse Mercator", 5); + return true; + case 16295: + record = new EpsgConversionRecord(16295, "Transverse Mercator", 5); + return true; + case 16296: + record = new EpsgConversionRecord(16296, "Transverse Mercator", 5); + return true; + case 16297: + record = new EpsgConversionRecord(16297, "Transverse Mercator", 5); + return true; + case 16298: + record = new EpsgConversionRecord(16298, "Transverse Mercator", 5); + return true; + case 16299: + record = new EpsgConversionRecord(16299, "Transverse Mercator", 5); + return true; + case 16302: + record = new EpsgConversionRecord(16302, "Transverse Mercator", 5); + return true; + case 16304: + record = new EpsgConversionRecord(16304, "Transverse Mercator", 5); + return true; + case 16305: + record = new EpsgConversionRecord(16305, "Transverse Mercator", 5); + return true; + case 16306: + record = new EpsgConversionRecord(16306, "Transverse Mercator", 5); + return true; + case 16307: + record = new EpsgConversionRecord(16307, "Transverse Mercator", 5); + return true; + case 16308: + record = new EpsgConversionRecord(16308, "Transverse Mercator", 5); + return true; + case 16309: + record = new EpsgConversionRecord(16309, "Transverse Mercator", 5); + return true; + case 16310: + record = new EpsgConversionRecord(16310, "Transverse Mercator", 5); + return true; + case 16311: + record = new EpsgConversionRecord(16311, "Transverse Mercator", 5); + return true; + case 16312: + record = new EpsgConversionRecord(16312, "Transverse Mercator", 5); + return true; + case 16313: + record = new EpsgConversionRecord(16313, "Transverse Mercator", 5); + return true; + case 16314: + record = new EpsgConversionRecord(16314, "Transverse Mercator", 5); + return true; + case 16315: + record = new EpsgConversionRecord(16315, "Transverse Mercator", 5); + return true; + case 16316: + record = new EpsgConversionRecord(16316, "Transverse Mercator", 5); + return true; + case 16317: + record = new EpsgConversionRecord(16317, "Transverse Mercator", 5); + return true; + case 16318: + record = new EpsgConversionRecord(16318, "Transverse Mercator", 5); + return true; + case 16319: + record = new EpsgConversionRecord(16319, "Transverse Mercator", 5); + return true; + case 16320: + record = new EpsgConversionRecord(16320, "Transverse Mercator", 5); + return true; + case 16321: + record = new EpsgConversionRecord(16321, "Transverse Mercator", 5); + return true; + case 16322: + record = new EpsgConversionRecord(16322, "Transverse Mercator", 5); + return true; + case 16323: + record = new EpsgConversionRecord(16323, "Transverse Mercator", 5); + return true; + case 16324: + record = new EpsgConversionRecord(16324, "Transverse Mercator", 5); + return true; + case 16325: + record = new EpsgConversionRecord(16325, "Transverse Mercator", 5); + return true; + case 16326: + record = new EpsgConversionRecord(16326, "Transverse Mercator", 5); + return true; + case 16327: + record = new EpsgConversionRecord(16327, "Transverse Mercator", 5); + return true; + case 16328: + record = new EpsgConversionRecord(16328, "Transverse Mercator", 5); + return true; + case 16329: + record = new EpsgConversionRecord(16329, "Transverse Mercator", 5); + return true; + case 16330: + record = new EpsgConversionRecord(16330, "Transverse Mercator", 5); + return true; + case 16331: + record = new EpsgConversionRecord(16331, "Transverse Mercator", 5); + return true; + case 16332: + record = new EpsgConversionRecord(16332, "Transverse Mercator", 5); + return true; + case 16368: + record = new EpsgConversionRecord(16368, "Transverse Mercator", 5); + return true; + case 16370: + record = new EpsgConversionRecord(16370, "Transverse Mercator", 5); + return true; + case 16372: + record = new EpsgConversionRecord(16372, "Transverse Mercator", 5); + return true; + case 16374: + record = new EpsgConversionRecord(16374, "Transverse Mercator", 5); + return true; + case 16376: + record = new EpsgConversionRecord(16376, "Transverse Mercator", 5); + return true; + case 16378: + record = new EpsgConversionRecord(16378, "Transverse Mercator", 5); + return true; + case 16380: + record = new EpsgConversionRecord(16380, "Transverse Mercator", 5); + return true; + case 16382: + record = new EpsgConversionRecord(16382, "Transverse Mercator", 5); + return true; + case 16384: + record = new EpsgConversionRecord(16384, "Transverse Mercator", 5); + return true; + case 16386: + record = new EpsgConversionRecord(16386, "Transverse Mercator", 5); + return true; + case 16388: + record = new EpsgConversionRecord(16388, "Transverse Mercator", 5); + return true; + case 16390: + record = new EpsgConversionRecord(16390, "Transverse Mercator", 5); + return true; + case 16392: + record = new EpsgConversionRecord(16392, "Transverse Mercator", 5); + return true; + case 16394: + record = new EpsgConversionRecord(16394, "Transverse Mercator", 5); + return true; + case 16396: + record = new EpsgConversionRecord(16396, "Transverse Mercator", 5); + return true; + case 16398: + record = new EpsgConversionRecord(16398, "Transverse Mercator", 5); + return true; + case 16400: + record = new EpsgConversionRecord(16400, "Transverse Mercator", 5); + return true; + case 16405: + record = new EpsgConversionRecord(16405, "Transverse Mercator", 5); + return true; + case 16406: + record = new EpsgConversionRecord(16406, "Transverse Mercator", 5); + return true; + case 16411: + record = new EpsgConversionRecord(16411, "Transverse Mercator", 5); + return true; + case 16412: + record = new EpsgConversionRecord(16412, "Transverse Mercator", 5); + return true; + case 16413: + record = new EpsgConversionRecord(16413, "Transverse Mercator", 5); + return true; + case 16430: + record = new EpsgConversionRecord(16430, "Transverse Mercator", 5); + return true; + case 16490: + record = new EpsgConversionRecord(16490, "Transverse Mercator", 5); + return true; + case 16506: + record = new EpsgConversionRecord(16506, "Transverse Mercator", 5); + return true; + case 16586: + record = new EpsgConversionRecord(16586, "Transverse Mercator", 5); + return true; + case 16611: + record = new EpsgConversionRecord(16611, "Transverse Mercator", 5); + return true; + case 16612: + record = new EpsgConversionRecord(16612, "Transverse Mercator", 5); + return true; + case 16636: + record = new EpsgConversionRecord(16636, "Transverse Mercator", 5); + return true; + case 16709: + record = new EpsgConversionRecord(16709, "Transverse Mercator", 5); + return true; + case 16716: + record = new EpsgConversionRecord(16716, "Transverse Mercator", 5); + return true; + case 16732: + record = new EpsgConversionRecord(16732, "Transverse Mercator", 5); + return true; + case 16907: + record = new EpsgConversionRecord(16907, "Transverse Mercator", 5); + return true; + case 16908: + record = new EpsgConversionRecord(16908, "Transverse Mercator", 5); + return true; + case 16909: + record = new EpsgConversionRecord(16909, "Transverse Mercator", 5); + return true; + case 16910: + record = new EpsgConversionRecord(16910, "Transverse Mercator", 5); + return true; + case 16911: + record = new EpsgConversionRecord(16911, "Transverse Mercator", 5); + return true; + case 16912: + record = new EpsgConversionRecord(16912, "Transverse Mercator", 5); + return true; + case 16913: + record = new EpsgConversionRecord(16913, "Transverse Mercator", 5); + return true; + case 16914: + record = new EpsgConversionRecord(16914, "Transverse Mercator", 5); + return true; + case 16915: + record = new EpsgConversionRecord(16915, "Transverse Mercator", 5); + return true; + case 16916: + record = new EpsgConversionRecord(16916, "Transverse Mercator", 5); + return true; + case 16917: + record = new EpsgConversionRecord(16917, "Transverse Mercator", 5); + return true; + case 16918: + record = new EpsgConversionRecord(16918, "Transverse Mercator", 5); + return true; + case 16919: + record = new EpsgConversionRecord(16919, "Transverse Mercator", 5); + return true; + case 16920: + record = new EpsgConversionRecord(16920, "Transverse Mercator", 5); + return true; + case 16921: + record = new EpsgConversionRecord(16921, "Transverse Mercator", 5); + return true; + case 16922: + record = new EpsgConversionRecord(16922, "Transverse Mercator", 5); + return true; + case 16923: + record = new EpsgConversionRecord(16923, "Transverse Mercator", 5); + return true; + case 16924: + record = new EpsgConversionRecord(16924, "Transverse Mercator", 5); + return true; + case 16925: + record = new EpsgConversionRecord(16925, "Transverse Mercator", 5); + return true; + case 16926: + record = new EpsgConversionRecord(16926, "Transverse Mercator", 5); + return true; + case 16927: + record = new EpsgConversionRecord(16927, "Transverse Mercator", 5); + return true; + case 16928: + record = new EpsgConversionRecord(16928, "Transverse Mercator", 5); + return true; + case 16929: + record = new EpsgConversionRecord(16929, "Transverse Mercator", 5); + return true; + case 16930: + record = new EpsgConversionRecord(16930, "Transverse Mercator", 5); + return true; + case 16931: + record = new EpsgConversionRecord(16931, "Transverse Mercator", 5); + return true; + case 16932: + record = new EpsgConversionRecord(16932, "Transverse Mercator", 5); + return true; + case 16933: + record = new EpsgConversionRecord(16933, "Transverse Mercator", 5); + return true; + case 16934: + record = new EpsgConversionRecord(16934, "Transverse Mercator", 5); + return true; + case 16935: + record = new EpsgConversionRecord(16935, "Transverse Mercator", 5); + return true; + case 16936: + record = new EpsgConversionRecord(16936, "Transverse Mercator", 5); + return true; + case 16937: + record = new EpsgConversionRecord(16937, "Transverse Mercator", 5); + return true; + case 16938: + record = new EpsgConversionRecord(16938, "Transverse Mercator", 5); + return true; + case 16939: + record = new EpsgConversionRecord(16939, "Transverse Mercator", 5); + return true; + case 16940: + record = new EpsgConversionRecord(16940, "Transverse Mercator", 5); + return true; + case 16941: + record = new EpsgConversionRecord(16941, "Transverse Mercator", 5); + return true; + case 16942: + record = new EpsgConversionRecord(16942, "Transverse Mercator", 5); + return true; + case 16943: + record = new EpsgConversionRecord(16943, "Transverse Mercator", 5); + return true; + case 16944: + record = new EpsgConversionRecord(16944, "Transverse Mercator", 5); + return true; + case 16945: + record = new EpsgConversionRecord(16945, "Transverse Mercator", 5); + return true; + case 16946: + record = new EpsgConversionRecord(16946, "Transverse Mercator", 5); + return true; + case 16947: + record = new EpsgConversionRecord(16947, "Transverse Mercator", 5); + return true; + case 16948: + record = new EpsgConversionRecord(16948, "Transverse Mercator", 5); + return true; + case 16949: + record = new EpsgConversionRecord(16949, "Transverse Mercator", 5); + return true; + case 16950: + record = new EpsgConversionRecord(16950, "Transverse Mercator", 5); + return true; + case 16951: + record = new EpsgConversionRecord(16951, "Transverse Mercator", 5); + return true; + case 16952: + record = new EpsgConversionRecord(16952, "Transverse Mercator", 5); + return true; + case 16953: + record = new EpsgConversionRecord(16953, "Transverse Mercator", 5); + return true; + case 16954: + record = new EpsgConversionRecord(16954, "Transverse Mercator", 5); + return true; + case 16955: + record = new EpsgConversionRecord(16955, "Transverse Mercator", 5); + return true; + case 16956: + record = new EpsgConversionRecord(16956, "Transverse Mercator", 5); + return true; + case 16957: + record = new EpsgConversionRecord(16957, "Transverse Mercator", 5); + return true; + case 16958: + record = new EpsgConversionRecord(16958, "Transverse Mercator", 5); + return true; + case 16959: + record = new EpsgConversionRecord(16959, "Transverse Mercator", 5); + return true; + case 16960: + record = new EpsgConversionRecord(16960, "Transverse Mercator", 5); + return true; + case 16961: + record = new EpsgConversionRecord(16961, "Transverse Mercator", 5); + return true; + case 16962: + record = new EpsgConversionRecord(16962, "Transverse Mercator", 5); + return true; + case 16963: + record = new EpsgConversionRecord(16963, "Transverse Mercator", 5); + return true; + case 16964: + record = new EpsgConversionRecord(16964, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket17(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 17001: + record = new EpsgConversionRecord(17001, "Transverse Mercator", 5); + return true; + case 17005: + record = new EpsgConversionRecord(17005, "Transverse Mercator", 5); + return true; + case 17054: + record = new EpsgConversionRecord(17054, "Transverse Mercator", 5); + return true; + case 17107: + record = new EpsgConversionRecord(17107, "Transverse Mercator", 5); + return true; + case 17108: + record = new EpsgConversionRecord(17108, "Transverse Mercator", 5); + return true; + case 17109: + record = new EpsgConversionRecord(17109, "Transverse Mercator", 5); + return true; + case 17110: + record = new EpsgConversionRecord(17110, "Transverse Mercator", 5); + return true; + case 17111: + record = new EpsgConversionRecord(17111, "Transverse Mercator", 5); + return true; + case 17112: + record = new EpsgConversionRecord(17112, "Transverse Mercator", 5); + return true; + case 17113: + record = new EpsgConversionRecord(17113, "Transverse Mercator", 5); + return true; + case 17114: + record = new EpsgConversionRecord(17114, "Transverse Mercator", 5); + return true; + case 17115: + record = new EpsgConversionRecord(17115, "Transverse Mercator", 5); + return true; + case 17116: + record = new EpsgConversionRecord(17116, "Transverse Mercator", 5); + return true; + case 17117: + record = new EpsgConversionRecord(17117, "Transverse Mercator", 5); + return true; + case 17118: + record = new EpsgConversionRecord(17118, "Transverse Mercator", 5); + return true; + case 17119: + record = new EpsgConversionRecord(17119, "Transverse Mercator", 5); + return true; + case 17120: + record = new EpsgConversionRecord(17120, "Transverse Mercator", 5); + return true; + case 17121: + record = new EpsgConversionRecord(17121, "Transverse Mercator", 5); + return true; + case 17122: + record = new EpsgConversionRecord(17122, "Transverse Mercator", 5); + return true; + case 17123: + record = new EpsgConversionRecord(17123, "Transverse Mercator", 5); + return true; + case 17124: + record = new EpsgConversionRecord(17124, "Transverse Mercator", 5); + return true; + case 17125: + record = new EpsgConversionRecord(17125, "Transverse Mercator", 5); + return true; + case 17126: + record = new EpsgConversionRecord(17126, "Transverse Mercator", 5); + return true; + case 17127: + record = new EpsgConversionRecord(17127, "Transverse Mercator", 5); + return true; + case 17128: + record = new EpsgConversionRecord(17128, "Transverse Mercator", 5); + return true; + case 17129: + record = new EpsgConversionRecord(17129, "Transverse Mercator", 5); + return true; + case 17130: + record = new EpsgConversionRecord(17130, "Transverse Mercator", 5); + return true; + case 17131: + record = new EpsgConversionRecord(17131, "Transverse Mercator", 5); + return true; + case 17132: + record = new EpsgConversionRecord(17132, "Transverse Mercator", 5); + return true; + case 17133: + record = new EpsgConversionRecord(17133, "Transverse Mercator", 5); + return true; + case 17134: + record = new EpsgConversionRecord(17134, "Transverse Mercator", 5); + return true; + case 17135: + record = new EpsgConversionRecord(17135, "Transverse Mercator", 5); + return true; + case 17136: + record = new EpsgConversionRecord(17136, "Transverse Mercator", 5); + return true; + case 17137: + record = new EpsgConversionRecord(17137, "Transverse Mercator", 5); + return true; + case 17138: + record = new EpsgConversionRecord(17138, "Transverse Mercator", 5); + return true; + case 17139: + record = new EpsgConversionRecord(17139, "Transverse Mercator", 5); + return true; + case 17140: + record = new EpsgConversionRecord(17140, "Transverse Mercator", 5); + return true; + case 17141: + record = new EpsgConversionRecord(17141, "Transverse Mercator", 5); + return true; + case 17142: + record = new EpsgConversionRecord(17142, "Transverse Mercator", 5); + return true; + case 17143: + record = new EpsgConversionRecord(17143, "Transverse Mercator", 5); + return true; + case 17144: + record = new EpsgConversionRecord(17144, "Transverse Mercator", 5); + return true; + case 17145: + record = new EpsgConversionRecord(17145, "Transverse Mercator", 5); + return true; + case 17146: + record = new EpsgConversionRecord(17146, "Transverse Mercator", 5); + return true; + case 17147: + record = new EpsgConversionRecord(17147, "Transverse Mercator", 5); + return true; + case 17148: + record = new EpsgConversionRecord(17148, "Transverse Mercator", 5); + return true; + case 17149: + record = new EpsgConversionRecord(17149, "Transverse Mercator", 5); + return true; + case 17150: + record = new EpsgConversionRecord(17150, "Transverse Mercator", 5); + return true; + case 17151: + record = new EpsgConversionRecord(17151, "Transverse Mercator", 5); + return true; + case 17152: + record = new EpsgConversionRecord(17152, "Transverse Mercator", 5); + return true; + case 17153: + record = new EpsgConversionRecord(17153, "Transverse Mercator", 5); + return true; + case 17154: + record = new EpsgConversionRecord(17154, "Transverse Mercator", 5); + return true; + case 17155: + record = new EpsgConversionRecord(17155, "Transverse Mercator", 5); + return true; + case 17156: + record = new EpsgConversionRecord(17156, "Transverse Mercator", 5); + return true; + case 17157: + record = new EpsgConversionRecord(17157, "Transverse Mercator", 5); + return true; + case 17158: + record = new EpsgConversionRecord(17158, "Transverse Mercator", 5); + return true; + case 17159: + record = new EpsgConversionRecord(17159, "Transverse Mercator", 5); + return true; + case 17160: + record = new EpsgConversionRecord(17160, "Transverse Mercator", 5); + return true; + case 17161: + record = new EpsgConversionRecord(17161, "Transverse Mercator", 5); + return true; + case 17162: + record = new EpsgConversionRecord(17162, "Transverse Mercator", 5); + return true; + case 17163: + record = new EpsgConversionRecord(17163, "Transverse Mercator", 5); + return true; + case 17164: + record = new EpsgConversionRecord(17164, "Transverse Mercator", 5); + return true; + case 17204: + record = new EpsgConversionRecord(17204, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17205: + record = new EpsgConversionRecord(17205, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17206: + record = new EpsgConversionRecord(17206, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17207: + record = new EpsgConversionRecord(17207, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17208: + record = new EpsgConversionRecord(17208, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17209: + record = new EpsgConversionRecord(17209, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17210: + record = new EpsgConversionRecord(17210, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17211: + record = new EpsgConversionRecord(17211, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17212: + record = new EpsgConversionRecord(17212, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17213: + record = new EpsgConversionRecord(17213, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17214: + record = new EpsgConversionRecord(17214, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17215: + record = new EpsgConversionRecord(17215, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17216: + record = new EpsgConversionRecord(17216, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17217: + record = new EpsgConversionRecord(17217, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17218: + record = new EpsgConversionRecord(17218, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17219: + record = new EpsgConversionRecord(17219, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17220: + record = new EpsgConversionRecord(17220, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17221: + record = new EpsgConversionRecord(17221, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17222: + record = new EpsgConversionRecord(17222, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17223: + record = new EpsgConversionRecord(17223, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17224: + record = new EpsgConversionRecord(17224, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17225: + record = new EpsgConversionRecord(17225, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17226: + record = new EpsgConversionRecord(17226, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17227: + record = new EpsgConversionRecord(17227, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17228: + record = new EpsgConversionRecord(17228, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17229: + record = new EpsgConversionRecord(17229, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17230: + record = new EpsgConversionRecord(17230, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17231: + record = new EpsgConversionRecord(17231, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17232: + record = new EpsgConversionRecord(17232, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17233: + record = new EpsgConversionRecord(17233, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17234: + record = new EpsgConversionRecord(17234, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17235: + record = new EpsgConversionRecord(17235, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17236: + record = new EpsgConversionRecord(17236, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17237: + record = new EpsgConversionRecord(17237, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17238: + record = new EpsgConversionRecord(17238, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17239: + record = new EpsgConversionRecord(17239, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17240: + record = new EpsgConversionRecord(17240, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17241: + record = new EpsgConversionRecord(17241, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17242: + record = new EpsgConversionRecord(17242, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17243: + record = new EpsgConversionRecord(17243, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17244: + record = new EpsgConversionRecord(17244, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17245: + record = new EpsgConversionRecord(17245, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17246: + record = new EpsgConversionRecord(17246, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17247: + record = new EpsgConversionRecord(17247, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17248: + record = new EpsgConversionRecord(17248, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17249: + record = new EpsgConversionRecord(17249, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17250: + record = new EpsgConversionRecord(17250, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17251: + record = new EpsgConversionRecord(17251, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17252: + record = new EpsgConversionRecord(17252, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17253: + record = new EpsgConversionRecord(17253, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17254: + record = new EpsgConversionRecord(17254, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17255: + record = new EpsgConversionRecord(17255, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17256: + record = new EpsgConversionRecord(17256, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17257: + record = new EpsgConversionRecord(17257, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17258: + record = new EpsgConversionRecord(17258, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17259: + record = new EpsgConversionRecord(17259, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17260: + record = new EpsgConversionRecord(17260, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17261: + record = new EpsgConversionRecord(17261, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17262: + record = new EpsgConversionRecord(17262, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17263: + record = new EpsgConversionRecord(17263, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17264: + record = new EpsgConversionRecord(17264, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17265: + record = new EpsgConversionRecord(17265, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17266: + record = new EpsgConversionRecord(17266, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17267: + record = new EpsgConversionRecord(17267, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17268: + record = new EpsgConversionRecord(17268, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17269: + record = new EpsgConversionRecord(17269, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17270: + record = new EpsgConversionRecord(17270, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17271: + record = new EpsgConversionRecord(17271, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17272: + record = new EpsgConversionRecord(17272, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17273: + record = new EpsgConversionRecord(17273, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17274: + record = new EpsgConversionRecord(17274, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17275: + record = new EpsgConversionRecord(17275, "Polar Stereographic (variant B)", 4); + return true; + case 17276: + record = new EpsgConversionRecord(17276, "Polar Stereographic (variant B)", 4); + return true; + case 17277: + record = new EpsgConversionRecord(17277, "Polar Stereographic (variant B)", 4); + return true; + case 17278: + record = new EpsgConversionRecord(17278, "Polar Stereographic (variant B)", 4); + return true; + case 17279: + record = new EpsgConversionRecord(17279, "Polar Stereographic (variant B)", 4); + return true; + case 17280: + record = new EpsgConversionRecord(17280, "Polar Stereographic (variant B)", 4); + return true; + case 17281: + record = new EpsgConversionRecord(17281, "Polar Stereographic (variant B)", 4); + return true; + case 17282: + record = new EpsgConversionRecord(17282, "Polar Stereographic (variant B)", 4); + return true; + case 17283: + record = new EpsgConversionRecord(17283, "Polar Stereographic (variant B)", 4); + return true; + case 17284: + record = new EpsgConversionRecord(17284, "Polar Stereographic (variant B)", 4); + return true; + case 17285: + record = new EpsgConversionRecord(17285, "Polar Stereographic (variant B)", 4); + return true; + case 17286: + record = new EpsgConversionRecord(17286, "Polar Stereographic (variant B)", 4); + return true; + case 17287: + record = new EpsgConversionRecord(17287, "Polar Stereographic (variant B)", 4); + return true; + case 17288: + record = new EpsgConversionRecord(17288, "Polar Stereographic (variant B)", 4); + return true; + case 17289: + record = new EpsgConversionRecord(17289, "Polar Stereographic (variant B)", 4); + return true; + case 17290: + record = new EpsgConversionRecord(17290, "Polar Stereographic (variant B)", 4); + return true; + case 17291: + record = new EpsgConversionRecord(17291, "Polar Stereographic (variant B)", 4); + return true; + case 17292: + record = new EpsgConversionRecord(17292, "Polar Stereographic (variant B)", 4); + return true; + case 17293: + record = new EpsgConversionRecord(17293, "Polar Stereographic (variant B)", 4); + return true; + case 17294: + record = new EpsgConversionRecord(17294, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17295: + record = new EpsgConversionRecord(17295, "Lambert Azimuthal Equal Area", 4); + return true; + case 17296: + record = new EpsgConversionRecord(17296, "Lambert Azimuthal Equal Area", 4); + return true; + case 17297: + record = new EpsgConversionRecord(17297, "Lambert Azimuthal Equal Area", 4); + return true; + case 17298: + record = new EpsgConversionRecord(17298, "Lambert Azimuthal Equal Area", 4); + return true; + case 17299: + record = new EpsgConversionRecord(17299, "Lambert Azimuthal Equal Area", 4); + return true; + case 17300: + record = new EpsgConversionRecord(17300, "Lambert Azimuthal Equal Area", 4); + return true; + case 17321: + record = new EpsgConversionRecord(17321, "Transverse Mercator", 5); + return true; + case 17322: + record = new EpsgConversionRecord(17322, "Transverse Mercator", 5); + return true; + case 17323: + record = new EpsgConversionRecord(17323, "Transverse Mercator", 5); + return true; + case 17324: + record = new EpsgConversionRecord(17324, "Transverse Mercator", 5); + return true; + case 17325: + record = new EpsgConversionRecord(17325, "Transverse Mercator", 5); + return true; + case 17326: + record = new EpsgConversionRecord(17326, "Transverse Mercator", 5); + return true; + case 17327: + record = new EpsgConversionRecord(17327, "Transverse Mercator", 5); + return true; + case 17328: + record = new EpsgConversionRecord(17328, "Transverse Mercator", 5); + return true; + case 17329: + record = new EpsgConversionRecord(17329, "Transverse Mercator", 5); + return true; + case 17330: + record = new EpsgConversionRecord(17330, "Transverse Mercator", 5); + return true; + case 17331: + record = new EpsgConversionRecord(17331, "Transverse Mercator", 5); + return true; + case 17332: + record = new EpsgConversionRecord(17332, "Transverse Mercator", 5); + return true; + case 17333: + record = new EpsgConversionRecord(17333, "Transverse Mercator", 5); + return true; + case 17334: + record = new EpsgConversionRecord(17334, "Transverse Mercator", 5); + return true; + case 17335: + record = new EpsgConversionRecord(17335, "Transverse Mercator", 5); + return true; + case 17336: + record = new EpsgConversionRecord(17336, "Transverse Mercator", 5); + return true; + case 17337: + record = new EpsgConversionRecord(17337, "Transverse Mercator", 5); + return true; + case 17338: + record = new EpsgConversionRecord(17338, "Transverse Mercator", 5); + return true; + case 17339: + record = new EpsgConversionRecord(17339, "Transverse Mercator", 5); + return true; + case 17340: + record = new EpsgConversionRecord(17340, "Transverse Mercator", 5); + return true; + case 17341: + record = new EpsgConversionRecord(17341, "Transverse Mercator", 5); + return true; + case 17342: + record = new EpsgConversionRecord(17342, "Transverse Mercator", 5); + return true; + case 17343: + record = new EpsgConversionRecord(17343, "Transverse Mercator", 5); + return true; + case 17344: + record = new EpsgConversionRecord(17344, "Transverse Mercator", 5); + return true; + case 17348: + record = new EpsgConversionRecord(17348, "Transverse Mercator", 5); + return true; + case 17349: + record = new EpsgConversionRecord(17349, "Transverse Mercator", 5); + return true; + case 17350: + record = new EpsgConversionRecord(17350, "Transverse Mercator", 5); + return true; + case 17351: + record = new EpsgConversionRecord(17351, "Transverse Mercator", 5); + return true; + case 17352: + record = new EpsgConversionRecord(17352, "Transverse Mercator", 5); + return true; + case 17353: + record = new EpsgConversionRecord(17353, "Transverse Mercator", 5); + return true; + case 17354: + record = new EpsgConversionRecord(17354, "Transverse Mercator", 5); + return true; + case 17355: + record = new EpsgConversionRecord(17355, "Transverse Mercator", 5); + return true; + case 17356: + record = new EpsgConversionRecord(17356, "Transverse Mercator", 5); + return true; + case 17357: + record = new EpsgConversionRecord(17357, "Transverse Mercator", 5); + return true; + case 17358: + record = new EpsgConversionRecord(17358, "Transverse Mercator", 5); + return true; + case 17359: + record = new EpsgConversionRecord(17359, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17360: + record = new EpsgConversionRecord(17360, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17361: + record = new EpsgConversionRecord(17361, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17362: + record = new EpsgConversionRecord(17362, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17363: + record = new EpsgConversionRecord(17363, "Transverse Mercator", 5); + return true; + case 17364: + record = new EpsgConversionRecord(17364, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17365: + record = new EpsgConversionRecord(17365, "Albers Equal Area", 6); + return true; + case 17412: + record = new EpsgConversionRecord(17412, "Transverse Mercator", 5); + return true; + case 17414: + record = new EpsgConversionRecord(17414, "Transverse Mercator", 5); + return true; + case 17416: + record = new EpsgConversionRecord(17416, "Transverse Mercator", 5); + return true; + case 17418: + record = new EpsgConversionRecord(17418, "Transverse Mercator", 5); + return true; + case 17420: + record = new EpsgConversionRecord(17420, "Transverse Mercator", 5); + return true; + case 17422: + record = new EpsgConversionRecord(17422, "Transverse Mercator", 5); + return true; + case 17424: + record = new EpsgConversionRecord(17424, "Transverse Mercator", 5); + return true; + case 17426: + record = new EpsgConversionRecord(17426, "Transverse Mercator", 5); + return true; + case 17428: + record = new EpsgConversionRecord(17428, "Transverse Mercator", 5); + return true; + case 17430: + record = new EpsgConversionRecord(17430, "Transverse Mercator", 5); + return true; + case 17432: + record = new EpsgConversionRecord(17432, "Transverse Mercator", 5); + return true; + case 17433: + record = new EpsgConversionRecord(17433, "Transverse Mercator", 5); + return true; + case 17434: + record = new EpsgConversionRecord(17434, "Transverse Mercator", 5); + return true; + case 17435: + record = new EpsgConversionRecord(17435, "Transverse Mercator", 5); + return true; + case 17436: + record = new EpsgConversionRecord(17436, "Transverse Mercator", 5); + return true; + case 17437: + record = new EpsgConversionRecord(17437, "Transverse Mercator", 5); + return true; + case 17438: + record = new EpsgConversionRecord(17438, "Transverse Mercator", 5); + return true; + case 17439: + record = new EpsgConversionRecord(17439, "Transverse Mercator", 5); + return true; + case 17440: + record = new EpsgConversionRecord(17440, "Transverse Mercator", 5); + return true; + case 17441: + record = new EpsgConversionRecord(17441, "Transverse Mercator", 5); + return true; + case 17442: + record = new EpsgConversionRecord(17442, "Transverse Mercator", 5); + return true; + case 17443: + record = new EpsgConversionRecord(17443, "Transverse Mercator", 5); + return true; + case 17444: + record = new EpsgConversionRecord(17444, "Transverse Mercator", 5); + return true; + case 17445: + record = new EpsgConversionRecord(17445, "Transverse Mercator", 5); + return true; + case 17446: + record = new EpsgConversionRecord(17446, "Transverse Mercator", 5); + return true; + case 17447: + record = new EpsgConversionRecord(17447, "Transverse Mercator", 5); + return true; + case 17449: + record = new EpsgConversionRecord(17449, "Transverse Mercator", 5); + return true; + case 17450: + record = new EpsgConversionRecord(17450, "Transverse Mercator", 5); + return true; + case 17451: + record = new EpsgConversionRecord(17451, "Transverse Mercator", 5); + return true; + case 17452: + record = new EpsgConversionRecord(17452, "Transverse Mercator", 5); + return true; + case 17453: + record = new EpsgConversionRecord(17453, "Transverse Mercator", 5); + return true; + case 17454: + record = new EpsgConversionRecord(17454, "Transverse Mercator", 5); + return true; + case 17455: + record = new EpsgConversionRecord(17455, "Transverse Mercator", 5); + return true; + case 17456: + record = new EpsgConversionRecord(17456, "Transverse Mercator", 5); + return true; + case 17457: + record = new EpsgConversionRecord(17457, "Transverse Mercator", 5); + return true; + case 17458: + record = new EpsgConversionRecord(17458, "Transverse Mercator", 5); + return true; + case 17515: + record = new EpsgConversionRecord(17515, "Transverse Mercator (South Orientated)", 5); + return true; + case 17517: + record = new EpsgConversionRecord(17517, "Transverse Mercator (South Orientated)", 5); + return true; + case 17519: + record = new EpsgConversionRecord(17519, "Transverse Mercator (South Orientated)", 5); + return true; + case 17521: + record = new EpsgConversionRecord(17521, "Transverse Mercator (South Orientated)", 5); + return true; + case 17523: + record = new EpsgConversionRecord(17523, "Transverse Mercator (South Orientated)", 5); + return true; + case 17525: + record = new EpsgConversionRecord(17525, "Transverse Mercator (South Orientated)", 5); + return true; + case 17527: + record = new EpsgConversionRecord(17527, "Transverse Mercator (South Orientated)", 5); + return true; + case 17529: + record = new EpsgConversionRecord(17529, "Transverse Mercator (South Orientated)", 5); + return true; + case 17531: + record = new EpsgConversionRecord(17531, "Transverse Mercator (South Orientated)", 5); + return true; + case 17533: + record = new EpsgConversionRecord(17533, "Transverse Mercator (South Orientated)", 5); + return true; + case 17611: + record = new EpsgConversionRecord(17611, "Transverse Mercator (South Orientated)", 5); + return true; + case 17613: + record = new EpsgConversionRecord(17613, "Transverse Mercator (South Orientated)", 5); + return true; + case 17615: + record = new EpsgConversionRecord(17615, "Transverse Mercator (South Orientated)", 5); + return true; + case 17617: + record = new EpsgConversionRecord(17617, "Transverse Mercator (South Orientated)", 5); + return true; + case 17619: + record = new EpsgConversionRecord(17619, "Transverse Mercator (South Orientated)", 5); + return true; + case 17621: + record = new EpsgConversionRecord(17621, "Transverse Mercator (South Orientated)", 5); + return true; + case 17623: + record = new EpsgConversionRecord(17623, "Transverse Mercator (South Orientated)", 5); + return true; + case 17625: + record = new EpsgConversionRecord(17625, "Transverse Mercator (South Orientated)", 5); + return true; + case 17701: + record = new EpsgConversionRecord(17701, "Transverse Mercator", 5); + return true; + case 17702: + record = new EpsgConversionRecord(17702, "Transverse Mercator", 5); + return true; + case 17703: + record = new EpsgConversionRecord(17703, "Transverse Mercator", 5); + return true; + case 17704: + record = new EpsgConversionRecord(17704, "Transverse Mercator", 5); + return true; + case 17705: + record = new EpsgConversionRecord(17705, "Transverse Mercator", 5); + return true; + case 17706: + record = new EpsgConversionRecord(17706, "Transverse Mercator", 5); + return true; + case 17707: + record = new EpsgConversionRecord(17707, "Transverse Mercator", 5); + return true; + case 17708: + record = new EpsgConversionRecord(17708, "Transverse Mercator", 5); + return true; + case 17709: + record = new EpsgConversionRecord(17709, "Transverse Mercator", 5); + return true; + case 17710: + record = new EpsgConversionRecord(17710, "Transverse Mercator", 5); + return true; + case 17711: + record = new EpsgConversionRecord(17711, "Transverse Mercator", 5); + return true; + case 17712: + record = new EpsgConversionRecord(17712, "Transverse Mercator", 5); + return true; + case 17713: + record = new EpsgConversionRecord(17713, "Transverse Mercator", 5); + return true; + case 17714: + record = new EpsgConversionRecord(17714, "Transverse Mercator", 5); + return true; + case 17715: + record = new EpsgConversionRecord(17715, "Transverse Mercator", 5); + return true; + case 17716: + record = new EpsgConversionRecord(17716, "Transverse Mercator", 5); + return true; + case 17717: + record = new EpsgConversionRecord(17717, "Transverse Mercator", 5); + return true; + case 17722: + record = new EpsgConversionRecord(17722, "Transverse Mercator", 5); + return true; + case 17723: + record = new EpsgConversionRecord(17723, "Transverse Mercator", 5); + return true; + case 17724: + record = new EpsgConversionRecord(17724, "Transverse Mercator", 5); + return true; + case 17726: + record = new EpsgConversionRecord(17726, "Transverse Mercator", 5); + return true; + case 17771: + record = new EpsgConversionRecord(17771, "Azimuthal Equidistant", 4); + return true; + case 17772: + record = new EpsgConversionRecord(17772, "Azimuthal Equidistant", 4); + return true; + case 17773: + record = new EpsgConversionRecord(17773, "Azimuthal Equidistant", 4); + return true; + case 17774: + record = new EpsgConversionRecord(17774, "Azimuthal Equidistant", 4); + return true; + case 17775: + record = new EpsgConversionRecord(17775, "Azimuthal Equidistant", 4); + return true; + case 17776: + record = new EpsgConversionRecord(17776, "Azimuthal Equidistant", 4); + return true; + case 17777: + record = new EpsgConversionRecord(17777, "Azimuthal Equidistant", 4); + return true; + case 17794: + record = new EpsgConversionRecord(17794, "Transverse Mercator", 5); + return true; + case 17795: + record = new EpsgConversionRecord(17795, "Transverse Mercator", 5); + return true; + case 17801: + record = new EpsgConversionRecord(17801, "Transverse Mercator", 5); + return true; + case 17802: + record = new EpsgConversionRecord(17802, "Transverse Mercator", 5); + return true; + case 17803: + record = new EpsgConversionRecord(17803, "Transverse Mercator", 5); + return true; + case 17804: + record = new EpsgConversionRecord(17804, "Transverse Mercator", 5); + return true; + case 17805: + record = new EpsgConversionRecord(17805, "Transverse Mercator", 5); + return true; + case 17806: + record = new EpsgConversionRecord(17806, "Transverse Mercator", 5); + return true; + case 17807: + record = new EpsgConversionRecord(17807, "Transverse Mercator", 5); + return true; + case 17808: + record = new EpsgConversionRecord(17808, "Transverse Mercator", 5); + return true; + case 17809: + record = new EpsgConversionRecord(17809, "Transverse Mercator", 5); + return true; + case 17810: + record = new EpsgConversionRecord(17810, "Transverse Mercator", 5); + return true; + case 17811: + record = new EpsgConversionRecord(17811, "Transverse Mercator", 5); + return true; + case 17812: + record = new EpsgConversionRecord(17812, "Transverse Mercator", 5); + return true; + case 17813: + record = new EpsgConversionRecord(17813, "Transverse Mercator", 5); + return true; + case 17814: + record = new EpsgConversionRecord(17814, "Transverse Mercator", 5); + return true; + case 17815: + record = new EpsgConversionRecord(17815, "Transverse Mercator", 5); + return true; + case 17816: + record = new EpsgConversionRecord(17816, "Transverse Mercator", 5); + return true; + case 17817: + record = new EpsgConversionRecord(17817, "Transverse Mercator", 5); + return true; + case 17818: + record = new EpsgConversionRecord(17818, "Transverse Mercator", 5); + return true; + case 17819: + record = new EpsgConversionRecord(17819, "Transverse Mercator", 5); + return true; + case 17901: + record = new EpsgConversionRecord(17901, "Transverse Mercator", 5); + return true; + case 17902: + record = new EpsgConversionRecord(17902, "Transverse Mercator", 5); + return true; + case 17903: + record = new EpsgConversionRecord(17903, "Transverse Mercator", 5); + return true; + case 17904: + record = new EpsgConversionRecord(17904, "Transverse Mercator", 5); + return true; + case 17905: + record = new EpsgConversionRecord(17905, "Transverse Mercator", 5); + return true; + case 17906: + record = new EpsgConversionRecord(17906, "Transverse Mercator", 5); + return true; + case 17907: + record = new EpsgConversionRecord(17907, "Transverse Mercator", 5); + return true; + case 17908: + record = new EpsgConversionRecord(17908, "Transverse Mercator", 5); + return true; + case 17909: + record = new EpsgConversionRecord(17909, "Transverse Mercator", 5); + return true; + case 17910: + record = new EpsgConversionRecord(17910, "Transverse Mercator", 5); + return true; + case 17911: + record = new EpsgConversionRecord(17911, "Transverse Mercator", 5); + return true; + case 17912: + record = new EpsgConversionRecord(17912, "Transverse Mercator", 5); + return true; + case 17913: + record = new EpsgConversionRecord(17913, "Transverse Mercator", 5); + return true; + case 17914: + record = new EpsgConversionRecord(17914, "Transverse Mercator", 5); + return true; + case 17915: + record = new EpsgConversionRecord(17915, "Transverse Mercator", 5); + return true; + case 17916: + record = new EpsgConversionRecord(17916, "Transverse Mercator", 5); + return true; + case 17917: + record = new EpsgConversionRecord(17917, "Transverse Mercator", 5); + return true; + case 17918: + record = new EpsgConversionRecord(17918, "Transverse Mercator", 5); + return true; + case 17919: + record = new EpsgConversionRecord(17919, "Transverse Mercator", 5); + return true; + case 17920: + record = new EpsgConversionRecord(17920, "Transverse Mercator", 5); + return true; + case 17921: + record = new EpsgConversionRecord(17921, "Transverse Mercator", 5); + return true; + case 17922: + record = new EpsgConversionRecord(17922, "Transverse Mercator", 5); + return true; + case 17923: + record = new EpsgConversionRecord(17923, "Transverse Mercator", 5); + return true; + case 17924: + record = new EpsgConversionRecord(17924, "Transverse Mercator", 5); + return true; + case 17925: + record = new EpsgConversionRecord(17925, "Transverse Mercator", 5); + return true; + case 17926: + record = new EpsgConversionRecord(17926, "Transverse Mercator", 5); + return true; + case 17927: + record = new EpsgConversionRecord(17927, "Transverse Mercator", 5); + return true; + case 17928: + record = new EpsgConversionRecord(17928, "Transverse Mercator", 5); + return true; + case 17931: + record = new EpsgConversionRecord(17931, "Transverse Mercator", 5); + return true; + case 17932: + record = new EpsgConversionRecord(17932, "Transverse Mercator", 5); + return true; + case 17933: + record = new EpsgConversionRecord(17933, "Transverse Mercator", 5); + return true; + case 17934: + record = new EpsgConversionRecord(17934, "Transverse Mercator", 5); + return true; + case 17935: + record = new EpsgConversionRecord(17935, "Transverse Mercator", 5); + return true; + case 17936: + record = new EpsgConversionRecord(17936, "Transverse Mercator", 5); + return true; + case 17937: + record = new EpsgConversionRecord(17937, "Transverse Mercator", 5); + return true; + case 17938: + record = new EpsgConversionRecord(17938, "Transverse Mercator", 5); + return true; + case 17939: + record = new EpsgConversionRecord(17939, "Transverse Mercator", 5); + return true; + case 17940: + record = new EpsgConversionRecord(17940, "Transverse Mercator", 5); + return true; + case 17941: + record = new EpsgConversionRecord(17941, "Transverse Mercator", 5); + return true; + case 17942: + record = new EpsgConversionRecord(17942, "Transverse Mercator", 5); + return true; + case 17943: + record = new EpsgConversionRecord(17943, "Transverse Mercator", 5); + return true; + case 17944: + record = new EpsgConversionRecord(17944, "Transverse Mercator", 5); + return true; + case 17945: + record = new EpsgConversionRecord(17945, "Transverse Mercator", 5); + return true; + case 17946: + record = new EpsgConversionRecord(17946, "Transverse Mercator", 5); + return true; + case 17947: + record = new EpsgConversionRecord(17947, "Transverse Mercator", 5); + return true; + case 17948: + record = new EpsgConversionRecord(17948, "Transverse Mercator", 5); + return true; + case 17949: + record = new EpsgConversionRecord(17949, "Transverse Mercator", 5); + return true; + case 17950: + record = new EpsgConversionRecord(17950, "Transverse Mercator", 5); + return true; + case 17951: + record = new EpsgConversionRecord(17951, "Transverse Mercator", 5); + return true; + case 17952: + record = new EpsgConversionRecord(17952, "Transverse Mercator", 5); + return true; + case 17953: + record = new EpsgConversionRecord(17953, "Transverse Mercator", 5); + return true; + case 17954: + record = new EpsgConversionRecord(17954, "Transverse Mercator", 5); + return true; + case 17955: + record = new EpsgConversionRecord(17955, "Transverse Mercator", 5); + return true; + case 17956: + record = new EpsgConversionRecord(17956, "Transverse Mercator", 5); + return true; + case 17957: + record = new EpsgConversionRecord(17957, "Transverse Mercator", 5); + return true; + case 17958: + record = new EpsgConversionRecord(17958, "Transverse Mercator", 5); + return true; + case 17959: + record = new EpsgConversionRecord(17959, "Transverse Mercator", 5); + return true; + case 17960: + record = new EpsgConversionRecord(17960, "Transverse Mercator", 5); + return true; + case 17961: + record = new EpsgConversionRecord(17961, "Transverse Mercator", 5); + return true; + case 17962: + record = new EpsgConversionRecord(17962, "Transverse Mercator", 5); + return true; + case 17963: + record = new EpsgConversionRecord(17963, "Transverse Mercator", 5); + return true; + case 17964: + record = new EpsgConversionRecord(17964, "Lambert Conic Conformal (2SP)", 6); + return true; + case 17965: + record = new EpsgConversionRecord(17965, "Transverse Mercator", 5); + return true; + case 17966: + record = new EpsgConversionRecord(17966, "Lambert Conic Conformal (2SP)", 6); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket18(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 18001: + record = new EpsgConversionRecord(18001, "Transverse Mercator", 5); + return true; + case 18002: + record = new EpsgConversionRecord(18002, "Transverse Mercator", 5); + return true; + case 18003: + record = new EpsgConversionRecord(18003, "Transverse Mercator", 5); + return true; + case 18004: + record = new EpsgConversionRecord(18004, "Transverse Mercator", 5); + return true; + case 18005: + record = new EpsgConversionRecord(18005, "Transverse Mercator", 5); + return true; + case 18006: + record = new EpsgConversionRecord(18006, "Transverse Mercator", 5); + return true; + case 18007: + record = new EpsgConversionRecord(18007, "Transverse Mercator", 5); + return true; + case 18008: + record = new EpsgConversionRecord(18008, "Transverse Mercator", 5); + return true; + case 18009: + record = new EpsgConversionRecord(18009, "Transverse Mercator", 5); + return true; + case 18011: + record = new EpsgConversionRecord(18011, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18012: + record = new EpsgConversionRecord(18012, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18021: + record = new EpsgConversionRecord(18021, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18022: + record = new EpsgConversionRecord(18022, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18031: + record = new EpsgConversionRecord(18031, "Transverse Mercator", 5); + return true; + case 18032: + record = new EpsgConversionRecord(18032, "Transverse Mercator", 5); + return true; + case 18033: + record = new EpsgConversionRecord(18033, "Transverse Mercator", 5); + return true; + case 18034: + record = new EpsgConversionRecord(18034, "Transverse Mercator", 5); + return true; + case 18035: + record = new EpsgConversionRecord(18035, "Transverse Mercator", 5); + return true; + case 18036: + record = new EpsgConversionRecord(18036, "Transverse Mercator", 5); + return true; + case 18037: + record = new EpsgConversionRecord(18037, "Transverse Mercator", 5); + return true; + case 18041: + record = new EpsgConversionRecord(18041, "Transverse Mercator", 5); + return true; + case 18042: + record = new EpsgConversionRecord(18042, "Transverse Mercator", 5); + return true; + case 18043: + record = new EpsgConversionRecord(18043, "Transverse Mercator", 5); + return true; + case 18044: + record = new EpsgConversionRecord(18044, "Transverse Mercator", 5); + return true; + case 18045: + record = new EpsgConversionRecord(18045, "Transverse Mercator", 5); + return true; + case 18046: + record = new EpsgConversionRecord(18046, "Transverse Mercator", 5); + return true; + case 18047: + record = new EpsgConversionRecord(18047, "Transverse Mercator", 5); + return true; + case 18048: + record = new EpsgConversionRecord(18048, "Transverse Mercator", 5); + return true; + case 18049: + record = new EpsgConversionRecord(18049, "Transverse Mercator", 5); + return true; + case 18051: + record = new EpsgConversionRecord(18051, "Transverse Mercator", 5); + return true; + case 18052: + record = new EpsgConversionRecord(18052, "Transverse Mercator", 5); + return true; + case 18053: + record = new EpsgConversionRecord(18053, "Transverse Mercator", 5); + return true; + case 18054: + record = new EpsgConversionRecord(18054, "Transverse Mercator", 5); + return true; + case 18055: + record = new EpsgConversionRecord(18055, "Transverse Mercator", 5); + return true; + case 18056: + record = new EpsgConversionRecord(18056, "Transverse Mercator", 5); + return true; + case 18057: + record = new EpsgConversionRecord(18057, "Transverse Mercator", 5); + return true; + case 18058: + record = new EpsgConversionRecord(18058, "Transverse Mercator", 5); + return true; + case 18059: + record = new EpsgConversionRecord(18059, "Transverse Mercator", 5); + return true; + case 18063: + record = new EpsgConversionRecord(18063, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18064: + record = new EpsgConversionRecord(18064, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18065: + record = new EpsgConversionRecord(18065, "Transverse Mercator", 5); + return true; + case 18066: + record = new EpsgConversionRecord(18066, "Transverse Mercator", 5); + return true; + case 18067: + record = new EpsgConversionRecord(18067, "Transverse Mercator", 5); + return true; + case 18068: + record = new EpsgConversionRecord(18068, "Transverse Mercator", 5); + return true; + case 18069: + record = new EpsgConversionRecord(18069, "Transverse Mercator", 5); + return true; + case 18071: + record = new EpsgConversionRecord(18071, "Transverse Mercator", 5); + return true; + case 18072: + record = new EpsgConversionRecord(18072, "Transverse Mercator", 5); + return true; + case 18073: + record = new EpsgConversionRecord(18073, "Transverse Mercator", 5); + return true; + case 18074: + record = new EpsgConversionRecord(18074, "Transverse Mercator", 5); + return true; + case 18081: + record = new EpsgConversionRecord(18081, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18082: + record = new EpsgConversionRecord(18082, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18083: + record = new EpsgConversionRecord(18083, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18084: + record = new EpsgConversionRecord(18084, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18085: + record = new EpsgConversionRecord(18085, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18091: + record = new EpsgConversionRecord(18091, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18092: + record = new EpsgConversionRecord(18092, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18093: + record = new EpsgConversionRecord(18093, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18094: + record = new EpsgConversionRecord(18094, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18101: + record = new EpsgConversionRecord(18101, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18102: + record = new EpsgConversionRecord(18102, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18103: + record = new EpsgConversionRecord(18103, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18104: + record = new EpsgConversionRecord(18104, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18105: + record = new EpsgConversionRecord(18105, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18106: + record = new EpsgConversionRecord(18106, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18107: + record = new EpsgConversionRecord(18107, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18108: + record = new EpsgConversionRecord(18108, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18109: + record = new EpsgConversionRecord(18109, "Lambert Conic Conformal (2SP)", 6); + return true; + case 18110: + record = new EpsgConversionRecord(18110, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18111: + record = new EpsgConversionRecord(18111, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18112: + record = new EpsgConversionRecord(18112, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18113: + record = new EpsgConversionRecord(18113, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18114: + record = new EpsgConversionRecord(18114, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18116: + record = new EpsgConversionRecord(18116, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18121: + record = new EpsgConversionRecord(18121, "Transverse Mercator", 5); + return true; + case 18122: + record = new EpsgConversionRecord(18122, "Transverse Mercator", 5); + return true; + case 18131: + record = new EpsgConversionRecord(18131, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18132: + record = new EpsgConversionRecord(18132, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18134: + record = new EpsgConversionRecord(18134, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18135: + record = new EpsgConversionRecord(18135, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18141: + record = new EpsgConversionRecord(18141, "Transverse Mercator", 5); + return true; + case 18142: + record = new EpsgConversionRecord(18142, "Transverse Mercator", 5); + return true; + case 18151: + record = new EpsgConversionRecord(18151, "Transverse Mercator", 5); + return true; + case 18152: + record = new EpsgConversionRecord(18152, "Transverse Mercator", 5); + return true; + case 18153: + record = new EpsgConversionRecord(18153, "Transverse Mercator", 5); + return true; + case 18161: + record = new EpsgConversionRecord(18161, "Transverse Mercator", 5); + return true; + case 18162: + record = new EpsgConversionRecord(18162, "Transverse Mercator", 5); + return true; + case 18163: + record = new EpsgConversionRecord(18163, "Transverse Mercator", 5); + return true; + case 18171: + record = new EpsgConversionRecord(18171, "Transverse Mercator", 5); + return true; + case 18172: + record = new EpsgConversionRecord(18172, "Transverse Mercator", 5); + return true; + case 18173: + record = new EpsgConversionRecord(18173, "Transverse Mercator", 5); + return true; + case 18174: + record = new EpsgConversionRecord(18174, "Transverse Mercator", 5); + return true; + case 18175: + record = new EpsgConversionRecord(18175, "Transverse Mercator", 5); + return true; + case 18180: + record = new EpsgConversionRecord(18180, "Transverse Mercator", 5); + return true; + case 18181: + record = new EpsgConversionRecord(18181, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18182: + record = new EpsgConversionRecord(18182, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18183: + record = new EpsgConversionRecord(18183, "Transverse Mercator", 5); + return true; + case 18184: + record = new EpsgConversionRecord(18184, "Transverse Mercator", 5); + return true; + case 18185: + record = new EpsgConversionRecord(18185, "Transverse Mercator", 5); + return true; + case 18186: + record = new EpsgConversionRecord(18186, "Transverse Mercator", 5); + return true; + case 18187: + record = new EpsgConversionRecord(18187, "Transverse Mercator", 5); + return true; + case 18188: + record = new EpsgConversionRecord(18188, "Transverse Mercator", 5); + return true; + case 18189: + record = new EpsgConversionRecord(18189, "Transverse Mercator", 5); + return true; + case 18190: + record = new EpsgConversionRecord(18190, "Transverse Mercator", 5); + return true; + case 18191: + record = new EpsgConversionRecord(18191, "Transverse Mercator", 5); + return true; + case 18192: + record = new EpsgConversionRecord(18192, "Transverse Mercator", 5); + return true; + case 18193: + record = new EpsgConversionRecord(18193, "Transverse Mercator", 5); + return true; + case 18194: + record = new EpsgConversionRecord(18194, "Transverse Mercator", 5); + return true; + case 18195: + record = new EpsgConversionRecord(18195, "Transverse Mercator", 5); + return true; + case 18196: + record = new EpsgConversionRecord(18196, "Transverse Mercator", 5); + return true; + case 18197: + record = new EpsgConversionRecord(18197, "Transverse Mercator", 5); + return true; + case 18198: + record = new EpsgConversionRecord(18198, "Transverse Mercator", 5); + return true; + case 18199: + record = new EpsgConversionRecord(18199, "Transverse Mercator", 5); + return true; + case 18201: + record = new EpsgConversionRecord(18201, "Cassini-Soldner", 4); + return true; + case 18202: + record = new EpsgConversionRecord(18202, "Transverse Mercator", 5); + return true; + case 18203: + record = new EpsgConversionRecord(18203, "Cassini-Soldner", 4); + return true; + case 18204: + record = new EpsgConversionRecord(18204, "Transverse Mercator", 5); + return true; + case 18205: + record = new EpsgConversionRecord(18205, "Transverse Mercator", 5); + return true; + case 18211: + record = new EpsgConversionRecord(18211, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18212: + record = new EpsgConversionRecord(18212, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18221: + record = new EpsgConversionRecord(18221, "Transverse Mercator", 5); + return true; + case 18222: + record = new EpsgConversionRecord(18222, "Transverse Mercator", 5); + return true; + case 18223: + record = new EpsgConversionRecord(18223, "Transverse Mercator", 5); + return true; + case 18224: + record = new EpsgConversionRecord(18224, "Transverse Mercator", 5); + return true; + case 18225: + record = new EpsgConversionRecord(18225, "Transverse Mercator", 5); + return true; + case 18226: + record = new EpsgConversionRecord(18226, "Transverse Mercator", 5); + return true; + case 18227: + record = new EpsgConversionRecord(18227, "Transverse Mercator", 5); + return true; + case 18228: + record = new EpsgConversionRecord(18228, "Transverse Mercator", 5); + return true; + case 18231: + record = new EpsgConversionRecord(18231, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18232: + record = new EpsgConversionRecord(18232, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18233: + record = new EpsgConversionRecord(18233, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18234: + record = new EpsgConversionRecord(18234, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18235: + record = new EpsgConversionRecord(18235, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18236: + record = new EpsgConversionRecord(18236, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18237: + record = new EpsgConversionRecord(18237, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18238: + record = new EpsgConversionRecord(18238, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18240: + record = new EpsgConversionRecord(18240, "Transverse Mercator", 5); + return true; + case 18241: + record = new EpsgConversionRecord(18241, "Transverse Mercator", 5); + return true; + case 18242: + record = new EpsgConversionRecord(18242, "Transverse Mercator", 5); + return true; + case 18243: + record = new EpsgConversionRecord(18243, "Transverse Mercator", 5); + return true; + case 18244: + record = new EpsgConversionRecord(18244, "Transverse Mercator", 5); + return true; + case 18245: + record = new EpsgConversionRecord(18245, "Transverse Mercator", 5); + return true; + case 18246: + record = new EpsgConversionRecord(18246, "Transverse Mercator", 5); + return true; + case 18247: + record = new EpsgConversionRecord(18247, "Transverse Mercator", 5); + return true; + case 18248: + record = new EpsgConversionRecord(18248, "Transverse Mercator", 5); + return true; + case 18251: + record = new EpsgConversionRecord(18251, "Transverse Mercator", 5); + return true; + case 18252: + record = new EpsgConversionRecord(18252, "Transverse Mercator", 5); + return true; + case 18253: + record = new EpsgConversionRecord(18253, "Transverse Mercator", 5); + return true; + case 18260: + record = new EpsgConversionRecord(18260, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18261: + record = new EpsgConversionRecord(18261, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18262: + record = new EpsgConversionRecord(18262, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18263: + record = new EpsgConversionRecord(18263, "Lambert Conic Conformal (1SP)", 5); + return true; + case 18275: + record = new EpsgConversionRecord(18275, "Transverse Mercator", 5); + return true; + case 18276: + record = new EpsgConversionRecord(18276, "Transverse Mercator", 5); + return true; + case 18277: + record = new EpsgConversionRecord(18277, "Transverse Mercator", 5); + return true; + case 18278: + record = new EpsgConversionRecord(18278, "Transverse Mercator", 5); + return true; + case 18280: + record = new EpsgConversionRecord(18280, "Oblique Stereographic", 5); + return true; + case 18282: + record = new EpsgConversionRecord(18282, "Oblique Stereographic", 5); + return true; + case 18283: + record = new EpsgConversionRecord(18283, "Oblique Stereographic", 5); + return true; + case 18284: + record = new EpsgConversionRecord(18284, "Oblique Stereographic", 5); + return true; + case 18285: + record = new EpsgConversionRecord(18285, "Transverse Mercator", 5); + return true; + case 18286: + record = new EpsgConversionRecord(18286, "Oblique Stereographic", 5); + return true; + case 18300: + record = new EpsgConversionRecord(18300, "Transverse Mercator", 5); + return true; + case 18305: + record = new EpsgConversionRecord(18305, "Transverse Mercator", 5); + return true; + case 18306: + record = new EpsgConversionRecord(18306, "Transverse Mercator", 5); + return true; + case 18307: + record = new EpsgConversionRecord(18307, "Transverse Mercator", 5); + return true; + case 18308: + record = new EpsgConversionRecord(18308, "Transverse Mercator", 5); + return true; + case 18310: + record = new EpsgConversionRecord(18310, "Transverse Mercator", 5); + return true; + case 18311: + record = new EpsgConversionRecord(18311, "Transverse Mercator", 5); + return true; + case 18312: + record = new EpsgConversionRecord(18312, "Transverse Mercator", 5); + return true; + case 18313: + record = new EpsgConversionRecord(18313, "Transverse Mercator", 5); + return true; + case 18314: + record = new EpsgConversionRecord(18314, "Transverse Mercator", 5); + return true; + case 18315: + record = new EpsgConversionRecord(18315, "Transverse Mercator", 5); + return true; + case 18316: + record = new EpsgConversionRecord(18316, "Transverse Mercator", 5); + return true; + case 18317: + record = new EpsgConversionRecord(18317, "Transverse Mercator", 5); + return true; + case 18318: + record = new EpsgConversionRecord(18318, "Transverse Mercator", 5); + return true; + case 18319: + record = new EpsgConversionRecord(18319, "Transverse Mercator", 5); + return true; + case 18401: + record = new EpsgConversionRecord(18401, "Transverse Mercator", 5); + return true; + case 18402: + record = new EpsgConversionRecord(18402, "Transverse Mercator", 5); + return true; + case 18403: + record = new EpsgConversionRecord(18403, "Transverse Mercator", 5); + return true; + case 18415: + record = new EpsgConversionRecord(18415, "Transverse Mercator", 5); + return true; + case 18425: + record = new EpsgConversionRecord(18425, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18426: + record = new EpsgConversionRecord(18426, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18427: + record = new EpsgConversionRecord(18427, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18428: + record = new EpsgConversionRecord(18428, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18432: + record = new EpsgConversionRecord(18432, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18433: + record = new EpsgConversionRecord(18433, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18434: + record = new EpsgConversionRecord(18434, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18435: + record = new EpsgConversionRecord(18435, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18436: + record = new EpsgConversionRecord(18436, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18437: + record = new EpsgConversionRecord(18437, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 18441: + record = new EpsgConversionRecord(18441, "Transverse Mercator", 5); + return true; + case 18442: + record = new EpsgConversionRecord(18442, "Transverse Mercator", 5); + return true; + case 18443: + record = new EpsgConversionRecord(18443, "Transverse Mercator", 5); + return true; + case 18444: + record = new EpsgConversionRecord(18444, "Transverse Mercator", 5); + return true; + case 18446: + record = new EpsgConversionRecord(18446, "Transverse Mercator", 5); + return true; + case 18447: + record = new EpsgConversionRecord(18447, "Transverse Mercator", 5); + return true; + case 18448: + record = new EpsgConversionRecord(18448, "Transverse Mercator", 5); + return true; + case 18450: + record = new EpsgConversionRecord(18450, "Transverse Mercator", 5); + return true; + case 18451: + record = new EpsgConversionRecord(18451, "Transverse Mercator", 5); + return true; + case 18452: + record = new EpsgConversionRecord(18452, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetConversionBucket19(int code, out EpsgConversionRecord record) + { + switch (code) + { + case 19838: + record = new EpsgConversionRecord(19838, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 19839: + record = new EpsgConversionRecord(19839, "Transverse Mercator", 5); + return true; + case 19840: + record = new EpsgConversionRecord(19840, "Polar Stereographic (variant B)", 4); + return true; + case 19841: + record = new EpsgConversionRecord(19841, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19842: + record = new EpsgConversionRecord(19842, "Polar Stereographic (variant B)", 4); + return true; + case 19843: + record = new EpsgConversionRecord(19843, "Mercator (variant B)", 4); + return true; + case 19844: + record = new EpsgConversionRecord(19844, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19845: + record = new EpsgConversionRecord(19845, "Transverse Mercator", 5); + return true; + case 19848: + record = new EpsgConversionRecord(19848, "Transverse Mercator", 5); + return true; + case 19849: + record = new EpsgConversionRecord(19849, "Transverse Mercator", 5); + return true; + case 19851: + record = new EpsgConversionRecord(19851, "Transverse Mercator", 5); + return true; + case 19852: + record = new EpsgConversionRecord(19852, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19853: + record = new EpsgConversionRecord(19853, "Transverse Mercator", 5); + return true; + case 19854: + record = new EpsgConversionRecord(19854, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19856: + record = new EpsgConversionRecord(19856, "Transverse Mercator", 5); + return true; + case 19857: + record = new EpsgConversionRecord(19857, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19858: + record = new EpsgConversionRecord(19858, "Albers Equal Area", 6); + return true; + case 19859: + record = new EpsgConversionRecord(19859, "Transverse Mercator", 5); + return true; + case 19860: + record = new EpsgConversionRecord(19860, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19861: + record = new EpsgConversionRecord(19861, "Laborde Oblique Mercator", 6); + return true; + case 19862: + record = new EpsgConversionRecord(19862, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19863: + record = new EpsgConversionRecord(19863, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19864: + record = new EpsgConversionRecord(19864, "Transverse Mercator", 5); + return true; + case 19865: + record = new EpsgConversionRecord(19865, "Polar Stereographic (variant B)", 4); + return true; + case 19866: + record = new EpsgConversionRecord(19866, "Polar Stereographic (variant B)", 4); + return true; + case 19869: + record = new EpsgConversionRecord(19869, "Lambert Cylindrical Equal Area (Spherical)", 4); + return true; + case 19870: + record = new EpsgConversionRecord(19870, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 19871: + record = new EpsgConversionRecord(19871, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 19872: + record = new EpsgConversionRecord(19872, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 19873: + record = new EpsgConversionRecord(19873, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19874: + record = new EpsgConversionRecord(19874, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19875: + record = new EpsgConversionRecord(19875, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19876: + record = new EpsgConversionRecord(19876, "Transverse Mercator", 5); + return true; + case 19877: + record = new EpsgConversionRecord(19877, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 19878: + record = new EpsgConversionRecord(19878, "Hyperbolic Cassini-Soldner", 4); + return true; + case 19879: + record = new EpsgConversionRecord(19879, "Cassini-Soldner", 4); + return true; + case 19881: + record = new EpsgConversionRecord(19881, "Transverse Mercator", 5); + return true; + case 19882: + record = new EpsgConversionRecord(19882, "Transverse Mercator", 5); + return true; + case 19883: + record = new EpsgConversionRecord(19883, "Mercator (variant A)", 5); + return true; + case 19884: + record = new EpsgConversionRecord(19884, "Mercator (variant B)", 4); + return true; + case 19885: + record = new EpsgConversionRecord(19885, "Cassini-Soldner", 4); + return true; + case 19886: + record = new EpsgConversionRecord(19886, "Cassini-Soldner", 4); + return true; + case 19887: + record = new EpsgConversionRecord(19887, "Cassini-Soldner", 4); + return true; + case 19888: + record = new EpsgConversionRecord(19888, "Cassini-Soldner", 4); + return true; + case 19889: + record = new EpsgConversionRecord(19889, "Cassini-Soldner", 4); + return true; + case 19890: + record = new EpsgConversionRecord(19890, "Cassini-Soldner", 4); + return true; + case 19891: + record = new EpsgConversionRecord(19891, "Cassini-Soldner", 4); + return true; + case 19892: + record = new EpsgConversionRecord(19892, "Cassini-Soldner", 4); + return true; + case 19893: + record = new EpsgConversionRecord(19893, "Cassini-Soldner", 4); + return true; + case 19894: + record = new EpsgConversionRecord(19894, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 19895: + record = new EpsgConversionRecord(19895, "Hotine Oblique Mercator (variant A)", 7); + return true; + case 19896: + record = new EpsgConversionRecord(19896, "Cassini-Soldner", 4); + return true; + case 19897: + record = new EpsgConversionRecord(19897, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19899: + record = new EpsgConversionRecord(19899, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19900: + record = new EpsgConversionRecord(19900, "Transverse Mercator", 5); + return true; + case 19901: + record = new EpsgConversionRecord(19901, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19902: + record = new EpsgConversionRecord(19902, "Lambert Conic Conformal (2SP Belgium)", 6); + return true; + case 19903: + record = new EpsgConversionRecord(19903, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19904: + record = new EpsgConversionRecord(19904, "Transverse Mercator", 5); + return true; + case 19905: + record = new EpsgConversionRecord(19905, "Mercator (variant A)", 5); + return true; + case 19906: + record = new EpsgConversionRecord(19906, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19907: + record = new EpsgConversionRecord(19907, "Transverse Mercator", 5); + return true; + case 19909: + record = new EpsgConversionRecord(19909, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19910: + record = new EpsgConversionRecord(19910, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19911: + record = new EpsgConversionRecord(19911, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19913: + record = new EpsgConversionRecord(19913, "Oblique Stereographic", 5); + return true; + case 19914: + record = new EpsgConversionRecord(19914, "Oblique Stereographic", 5); + return true; + case 19916: + record = new EpsgConversionRecord(19916, "Transverse Mercator", 5); + return true; + case 19917: + record = new EpsgConversionRecord(19917, "New Zealand Map Grid", 4); + return true; + case 19919: + record = new EpsgConversionRecord(19919, "Transverse Mercator", 5); + return true; + case 19920: + record = new EpsgConversionRecord(19920, "Cassini-Soldner", 4); + return true; + case 19921: + record = new EpsgConversionRecord(19921, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19922: + record = new EpsgConversionRecord(19922, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19923: + record = new EpsgConversionRecord(19923, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19924: + record = new EpsgConversionRecord(19924, "Cassini-Soldner", 4); + return true; + case 19925: + record = new EpsgConversionRecord(19925, "Cassini-Soldner", 4); + return true; + case 19926: + record = new EpsgConversionRecord(19926, "Oblique Stereographic", 5); + return true; + case 19927: + record = new EpsgConversionRecord(19927, "Oblique Stereographic", 5); + return true; + case 19929: + record = new EpsgConversionRecord(19929, "Transverse Mercator", 5); + return true; + case 19930: + record = new EpsgConversionRecord(19930, "Transverse Mercator", 5); + return true; + case 19931: + record = new EpsgConversionRecord(19931, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19933: + record = new EpsgConversionRecord(19933, "Oblique Stereographic", 5); + return true; + case 19934: + record = new EpsgConversionRecord(19934, "Transverse Mercator", 5); + return true; + case 19936: + record = new EpsgConversionRecord(19936, "Transverse Mercator", 5); + return true; + case 19937: + record = new EpsgConversionRecord(19937, "Tunisia Mining Grid", 4); + return true; + case 19938: + record = new EpsgConversionRecord(19938, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19939: + record = new EpsgConversionRecord(19939, "Transverse Mercator", 5); + return true; + case 19940: + record = new EpsgConversionRecord(19940, "Lambert Conic Near-Conformal", 5); + return true; + case 19941: + record = new EpsgConversionRecord(19941, "American Polyconic", 4); + return true; + case 19942: + record = new EpsgConversionRecord(19942, "Transverse Mercator", 5); + return true; + case 19943: + record = new EpsgConversionRecord(19943, "Transverse Mercator", 5); + return true; + case 19944: + record = new EpsgConversionRecord(19944, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19945: + record = new EpsgConversionRecord(19945, "Oblique Stereographic", 5); + return true; + case 19946: + record = new EpsgConversionRecord(19946, "Oblique Stereographic", 5); + return true; + case 19947: + record = new EpsgConversionRecord(19947, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19948: + record = new EpsgConversionRecord(19948, "Lambert Conic Conformal (1SP)", 5); + return true; + case 19949: + record = new EpsgConversionRecord(19949, "Oblique Stereographic", 5); + return true; + case 19950: + record = new EpsgConversionRecord(19950, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19951: + record = new EpsgConversionRecord(19951, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19952: + record = new EpsgConversionRecord(19952, "Krovak", 7); + return true; + case 19953: + record = new EpsgConversionRecord(19953, "Cassini-Soldner", 4); + return true; + case 19954: + record = new EpsgConversionRecord(19954, "Transverse Mercator", 5); + return true; + case 19955: + record = new EpsgConversionRecord(19955, "Transverse Mercator", 5); + return true; + case 19956: + record = new EpsgConversionRecord(19956, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19957: + record = new EpsgConversionRecord(19957, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19958: + record = new EpsgConversionRecord(19958, "Hotine Oblique Mercator (variant B)", 7); + return true; + case 19959: + record = new EpsgConversionRecord(19959, "Transverse Mercator", 5); + return true; + case 19960: + record = new EpsgConversionRecord(19960, "Oblique Stereographic", 5); + return true; + case 19961: + record = new EpsgConversionRecord(19961, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19962: + record = new EpsgConversionRecord(19962, "Transverse Mercator", 5); + return true; + case 19963: + record = new EpsgConversionRecord(19963, "Transverse Mercator", 5); + return true; + case 19964: + record = new EpsgConversionRecord(19964, "Transverse Mercator", 5); + return true; + case 19966: + record = new EpsgConversionRecord(19966, "Transverse Mercator", 5); + return true; + case 19967: + record = new EpsgConversionRecord(19967, "Transverse Mercator", 5); + return true; + case 19969: + record = new EpsgConversionRecord(19969, "Transverse Mercator", 5); + return true; + case 19971: + record = new EpsgConversionRecord(19971, "Transverse Mercator", 5); + return true; + case 19972: + record = new EpsgConversionRecord(19972, "Transverse Mercator", 5); + return true; + case 19973: + record = new EpsgConversionRecord(19973, "Transverse Mercator", 5); + return true; + case 19974: + record = new EpsgConversionRecord(19974, "Transverse Mercator", 5); + return true; + case 19975: + record = new EpsgConversionRecord(19975, "Cassini-Soldner", 4); + return true; + case 19976: + record = new EpsgConversionRecord(19976, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19977: + record = new EpsgConversionRecord(19977, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19978: + record = new EpsgConversionRecord(19978, "Transverse Mercator", 5); + return true; + case 19979: + record = new EpsgConversionRecord(19979, "Bonne (South Orientated)", 4); + return true; + case 19981: + record = new EpsgConversionRecord(19981, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19983: + record = new EpsgConversionRecord(19983, "Polar Stereographic (variant C)", 4); + return true; + case 19984: + record = new EpsgConversionRecord(19984, "Albers Equal Area", 6); + return true; + case 19985: + record = new EpsgConversionRecord(19985, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19986: + record = new EpsgConversionRecord(19986, "Lambert Azimuthal Equal Area", 4); + return true; + case 19987: + record = new EpsgConversionRecord(19987, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 19988: + record = new EpsgConversionRecord(19988, "Lambert Conic Conformal (West Orientated)", 5); + return true; + case 19989: + record = new EpsgConversionRecord(19989, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19990: + record = new EpsgConversionRecord(19990, "Transverse Mercator", 5); + return true; + case 19991: + record = new EpsgConversionRecord(19991, "Transverse Mercator", 5); + return true; + case 19992: + record = new EpsgConversionRecord(19992, "Polar Stereographic (variant B)", 4); + return true; + case 19993: + record = new EpsgConversionRecord(19993, "Polar Stereographic (variant B)", 4); + return true; + case 19994: + record = new EpsgConversionRecord(19994, "Lambert Conic Conformal (2SP)", 6); + return true; + case 19995: + record = new EpsgConversionRecord(19995, "Transverse Mercator", 5); + return true; + case 19996: + record = new EpsgConversionRecord(19996, "Cassini-Soldner", 4); + return true; + case 19997: + record = new EpsgConversionRecord(19997, "Transverse Mercator", 5); + return true; + case 19998: + record = new EpsgConversionRecord(19998, "Transverse Mercator", 5); + return true; + case 19999: + record = new EpsgConversionRecord(19999, "Transverse Mercator", 5); + return true; + default: + record = default; + return false; + } + } + + internal static bool TryGetConversionParameter(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode / 1000) + { + case 3: + return TryGetConversionParameterBucket3(conversionCode, parameterIndex, out parameter); + case 4: + return TryGetConversionParameterBucket4(conversionCode, parameterIndex, out parameter); + case 5: + return TryGetConversionParameterBucket5(conversionCode, parameterIndex, out parameter); + case 6: + return TryGetConversionParameterBucket6(conversionCode, parameterIndex, out parameter); + case 7: + return TryGetConversionParameterBucket7(conversionCode, parameterIndex, out parameter); + case 8: + return TryGetConversionParameterBucket8(conversionCode, parameterIndex, out parameter); + case 9: + return TryGetConversionParameterBucket9(conversionCode, parameterIndex, out parameter); + case 10: + return TryGetConversionParameterBucket10(conversionCode, parameterIndex, out parameter); + case 11: + return TryGetConversionParameterBucket11(conversionCode, parameterIndex, out parameter); + case 12: + return TryGetConversionParameterBucket12(conversionCode, parameterIndex, out parameter); + case 13: + return TryGetConversionParameterBucket13(conversionCode, parameterIndex, out parameter); + case 14: + return TryGetConversionParameterBucket14(conversionCode, parameterIndex, out parameter); + case 15: + return TryGetConversionParameterBucket15(conversionCode, parameterIndex, out parameter); + case 16: + return TryGetConversionParameterBucket16(conversionCode, parameterIndex, out parameter); + case 17: + return TryGetConversionParameterBucket17(conversionCode, parameterIndex, out parameter); + case 18: + return TryGetConversionParameterBucket18(conversionCode, parameterIndex, out parameter); + case 19: + return TryGetConversionParameterBucket19(conversionCode, parameterIndex, out parameter); + default: + parameter = default; + return false; + } + } + + private static bool TryGetConversionParameterBucket3(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 3811: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 50.7978150000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 4.35921583333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 649328.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 665262.0d); + return true; + default: + break; + } + break; + case 3813: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998335d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1300000.0d); + return true; + default: + break; + } + break; + case 3818: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3820: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 121.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3831: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3853: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0578700000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999506d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100182.7406d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -6500620.1207d); + return true; + default: + break; + } + break; + case 3856: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3860: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 19500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3861: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3862: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 21500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3863: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 22.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 22500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3864: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 23500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3865: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 24500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3866: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 25500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3867: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 26.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 26500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3868: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 27500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3869: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 28500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3870: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 29.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 29500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3871: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3872: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 31500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3897: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3898: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3899: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 3967: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 3977: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 49.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -95.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 3981: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -9.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 3982: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -9.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 3983: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -9.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 26.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 3984: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -9.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 3999: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket4(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 4085: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4089: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 4090: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 4091: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 4092: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 800000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 4101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4104: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4105: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4106: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4107: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4108: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4109: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4110: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4111: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4112: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4113: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4114: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 2.04258333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 103.562758333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4115: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 2.71228333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 101.941166666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", -242.005d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", -948.547d); + return true; + default: + break; + } + break; + case 4116: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 3.7109722222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.436177777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4117: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 3.68034444444472d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 101.508244444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", -21759.438d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 55960.906d); + return true; + default: + break; + } + break; + case 4118: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4119: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4177: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.94614166666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.895208333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4186: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4187: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4305: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.42132500000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.345869444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4320: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.9651472222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.637594444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4321: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.85938055555583d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.816766666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 133453.669d); + return true; + default: + break; + } + break; + case 4323: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.8939222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.177291666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4325: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 13.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 144.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 4416: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -9.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 26.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -6.5d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -11.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 4436: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 4454: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.0333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 100000.0d); + return true; + default: + break; + } + break; + case 4460: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -27.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -18.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -36.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 4648: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 32500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 4825: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 15.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 15.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 16.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 161587.83d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 128511.202d); + return true; + default: + break; + } + break; + case 4838: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 51.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 10.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 53.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket5(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 5019: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.13190611111139d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5020: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.13190611111139d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5049: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 131.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 5068: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 23.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 29.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5100: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 5101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 125.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 5102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 5103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 5104: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 131.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 5131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 550000.0d); + return true; + default: + break; + } + break; + case 5135: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 5.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5136: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 6.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5137: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 7.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5138: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 8.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5139: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5140: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5141: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5142: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5143: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5144: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 14.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5145: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5146: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5147: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5148: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5149: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5150: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5151: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5152: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 22.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5153: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5154: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5155: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5156: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 26.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5157: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5158: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5159: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 29.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5160: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5161: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 125.002890277778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 5162: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.002890277778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 5163: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.002890277778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 550000.0d); + return true; + default: + break; + } + break; + case 5164: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.002890277778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 5165: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 131.002890277778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 5218: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 42.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397527781d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5219: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 42.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397222225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 5000000.0d); + return true; + case 7: + parameter = new EpsgConversionParameterRecord("Ordinate 1 of evaluation point", 1089000.0d); + return true; + case 8: + parameter = new EpsgConversionParameterRecord("Ordinate 2 of evaluation point", 654000.0d); + return true; + case 9: + parameter = new EpsgConversionParameterRecord("C1", 0.02946529277d); + return true; + case 10: + parameter = new EpsgConversionParameterRecord("C2", 0.02515965696d); + return true; + case 11: + parameter = new EpsgConversionParameterRecord("C3", 1.193845912e-07d); + return true; + case 12: + parameter = new EpsgConversionParameterRecord("C4", -4.668270147e-07d); + return true; + case 13: + parameter = new EpsgConversionParameterRecord("C5", 9.233980362e-12d); + return true; + case 14: + parameter = new EpsgConversionParameterRecord("C6", 1.523735715e-12d); + return true; + case 15: + parameter = new EpsgConversionParameterRecord("C7", 1.696780024e-18d); + return true; + case 16: + parameter = new EpsgConversionParameterRecord("C8", 4.408314235e-18d); + return true; + case 17: + parameter = new EpsgConversionParameterRecord("C9", -8.331083518e-24d); + return true; + case 18: + parameter = new EpsgConversionParameterRecord("C10", -3.689471323e-24d); + return true; + default: + break; + } + break; + case 5220: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 42.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397222225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 5000000.0d); + return true; + case 7: + parameter = new EpsgConversionParameterRecord("Ordinate 1 of evaluation point", 1089000.0d); + return true; + case 8: + parameter = new EpsgConversionParameterRecord("Ordinate 2 of evaluation point", 654000.0d); + return true; + case 9: + parameter = new EpsgConversionParameterRecord("C1", 0.02946529277d); + return true; + case 10: + parameter = new EpsgConversionParameterRecord("C2", 0.02515965696d); + return true; + case 11: + parameter = new EpsgConversionParameterRecord("C3", 1.193845912e-07d); + return true; + case 12: + parameter = new EpsgConversionParameterRecord("C4", -4.668270147e-07d); + return true; + case 13: + parameter = new EpsgConversionParameterRecord("C5", 9.233980362e-12d); + return true; + case 14: + parameter = new EpsgConversionParameterRecord("C6", 1.523735715e-12d); + return true; + case 15: + parameter = new EpsgConversionParameterRecord("C7", 1.696780024e-18d); + return true; + case 16: + parameter = new EpsgConversionParameterRecord("C8", 4.408314235e-18d); + return true; + case 17: + parameter = new EpsgConversionParameterRecord("C9", -8.331083518e-24d); + return true; + case 18: + parameter = new EpsgConversionParameterRecord("C10", -3.689471323e-24d); + return true; + default: + break; + } + break; + case 5222: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 5231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 7.00048027777806d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 80.7717111111114d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999238418d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 5232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 7.00047152777806d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 80.7717130833336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999238418d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 5265: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5268: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.7333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5276: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 89.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5277: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 89.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5278: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5279: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5280: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 91.1333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5281: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 91.2333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5282: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 89.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5283: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 91.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5284: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5285: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 91.5666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5286: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 89.066666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5287: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.2666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5288: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.1166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5289: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 91.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5290: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5291: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.8666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5312: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 89.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5313: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 89.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5314: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 91.5666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2500000.0d); + return true; + default: + break; + } + break; + case 5315: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -7.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999997d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -6000000.0d); + return true; + default: + break; + } + break; + case 5319: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 54.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5326: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 64.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 65.7500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 300000.0d); + return true; + default: + break; + } + break; + case 5328: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.19228055555583d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.997d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3900000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 900000.0d); + return true; + default: + break; + } + break; + case 5366: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5390: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.4666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995696d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 271820.522d); + return true; + default: + break; + } + break; + case 5394: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 9.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -83.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995696d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 327987.436d); + return true; + default: + break; + } + break; + case 5399: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 13.7833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99996704d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 295809.184d); + return true; + default: + break; + } + break; + case 5439: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 13.8666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99990314d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 359891.816d); + return true; + default: + break; + } + break; + case 5444: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 11.7333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99992228d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 288876.327d); + return true; + default: + break; + } + break; + case 5465: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 17.0612419444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.6318575000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 217259.26d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 445474.83d); + return true; + default: + break; + } + break; + case 5468: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 8.41666666666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -80.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99989909d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 294865.303d); + return true; + default: + break; + } + break; + case 5471: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 8.25000000000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1092972.1d); + return true; + default: + break; + } + break; + case 5475: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -78.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 163.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 7000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5000000.0d); + return true; + default: + break; + } + break; + case 5476: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -74.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 5000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3000000.0d); + return true; + default: + break; + } + break; + case 5477: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -71.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 166.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -70.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -72.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 5478: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5509: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 24.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397527781d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5510: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 24.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397527781d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5511: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 24.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397222225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 5000000.0d); + return true; + case 7: + parameter = new EpsgConversionParameterRecord("Ordinate 1 of evaluation point", 1089000.0d); + return true; + case 8: + parameter = new EpsgConversionParameterRecord("Ordinate 2 of evaluation point", 654000.0d); + return true; + case 9: + parameter = new EpsgConversionParameterRecord("C1", 0.02946529277d); + return true; + case 10: + parameter = new EpsgConversionParameterRecord("C2", 0.02515965696d); + return true; + case 11: + parameter = new EpsgConversionParameterRecord("C3", 1.193845912e-07d); + return true; + case 12: + parameter = new EpsgConversionParameterRecord("C4", -4.668270147e-07d); + return true; + case 13: + parameter = new EpsgConversionParameterRecord("C5", 9.233980362e-12d); + return true; + case 14: + parameter = new EpsgConversionParameterRecord("C6", 1.523735715e-12d); + return true; + case 15: + parameter = new EpsgConversionParameterRecord("C7", 1.696780024e-18d); + return true; + case 16: + parameter = new EpsgConversionParameterRecord("C8", 4.408314235e-18d); + return true; + case 17: + parameter = new EpsgConversionParameterRecord("C9", -8.331083518e-24d); + return true; + case 18: + parameter = new EpsgConversionParameterRecord("C10", -3.689471323e-24d); + return true; + default: + break; + } + break; + case 5512: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 24.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397222225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 5000000.0d); + return true; + case 7: + parameter = new EpsgConversionParameterRecord("Ordinate 1 of evaluation point", 1089000.0d); + return true; + case 8: + parameter = new EpsgConversionParameterRecord("Ordinate 2 of evaluation point", 654000.0d); + return true; + case 9: + parameter = new EpsgConversionParameterRecord("C1", 0.02946529277d); + return true; + case 10: + parameter = new EpsgConversionParameterRecord("C2", 0.02515965696d); + return true; + case 11: + parameter = new EpsgConversionParameterRecord("C3", 1.193845912e-07d); + return true; + case 12: + parameter = new EpsgConversionParameterRecord("C4", -4.668270147e-07d); + return true; + case 13: + parameter = new EpsgConversionParameterRecord("C5", 9.233980362e-12d); + return true; + case 14: + parameter = new EpsgConversionParameterRecord("C6", 1.523735715e-12d); + return true; + case 15: + parameter = new EpsgConversionParameterRecord("C7", 1.696780024e-18d); + return true; + case 16: + parameter = new EpsgConversionParameterRecord("C8", 4.408314235e-18d); + return true; + case 17: + parameter = new EpsgConversionParameterRecord("C9", -8.331083518e-24d); + return true; + case 18: + parameter = new EpsgConversionParameterRecord("C10", -3.689471323e-24d); + return true; + default: + break; + } + break; + case 5517: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -176.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 350000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 650000.0d); + return true; + default: + break; + } + break; + case 5522: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 5500000.0d); + return true; + default: + break; + } + break; + case 5547: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 5548: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 5549: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 5587: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -66.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999912d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 5595: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5640: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -2.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -43.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 5642: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 48.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 10.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 52.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 54.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 815000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5645: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999964286d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.6667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5647: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 31500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5648: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 33500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5658: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500053.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -3999820.0d); + return true; + default: + break; + } + break; + case 5824: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -35.3177362777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 149.009294830556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000086d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 5883: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 5000000.0d); + return true; + default: + break; + } + break; + case 5892: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5893: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5894: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 108.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5895: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 107.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 5901: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 5902: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 5903: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 5904: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 5905: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 5906: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 81.3172260000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 85.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5907: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 81.3172260000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 85.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5908: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 81.3172260000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 85.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5909: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 81.3172260000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 85.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5910: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 81.3172260000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 85.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5911: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 73.1557408611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 69.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5912: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 73.1557408611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 69.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5913: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 73.1557408611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 69.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5914: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 73.1557408611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 69.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5915: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 73.1557408611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 69.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5916: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.1012708888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 69.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 61.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5917: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.1012708888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 69.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 61.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5918: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.1012708888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 69.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 61.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5919: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.1012708888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 69.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 61.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5920: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.1012708888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 69.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 61.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 5943: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 62.0153068888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -52.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 63.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 60.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 20500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 8500000.0d); + return true; + default: + break; + } + break; + case 5944: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 62.0153068888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -37.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 63.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 60.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 22500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 8500000.0d); + return true; + default: + break; + } + break; + case 5977: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 85.4371183333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 87.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 83.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 21500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1500000.0d); + return true; + default: + break; + } + break; + case 5978: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 85.4371183333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 87.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 83.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 23500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1500000.0d); + return true; + default: + break; + } + break; + case 5979: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 85.4371183333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 87.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 83.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 25500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1500000.0d); + return true; + default: + break; + } + break; + case 5980: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 85.4371183333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 87.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 83.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 27500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1500000.0d); + return true; + default: + break; + } + break; + case 5981: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 85.4371183333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 87.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 83.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 29500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1500000.0d); + return true; + default: + break; + } + break; + case 5982: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 85.4371183333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 87.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 83.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 31500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1500000.0d); + return true; + default: + break; + } + break; + case 5983: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 166.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 10500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5984: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -154.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 12500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5985: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 14500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5986: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 16500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5987: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -52.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 18500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5988: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 20500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5989: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 16.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 22500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5990: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 53.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 24500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5991: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 26500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5992: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 82.0584248888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 133.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 83.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 80.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 28500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 5993: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 11500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 5994: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 52.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 13500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 5995: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 83.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 15500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 5996: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 17500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 5997: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 145.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 19500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 5998: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 176.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 21500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 5999: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 23500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket6(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 6000: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 25500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 6001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 27500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 6002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 29500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 6003: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 31500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 6004: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 78.7073375277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -10.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 80.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 33500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3500000.0d); + return true; + default: + break; + } + break; + case 6005: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -155.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 12500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6006: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 14500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6007: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -104.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 16500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6008: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 18500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6009: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -64.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 20500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6010: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 22500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6011: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -14.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 24500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6012: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 10.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 26500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6013: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 34.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 28500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6014: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 58.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 30500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6015: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 82.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 32500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6016: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 106.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 34500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6017: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 130.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 36500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6018: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 154.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 38500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6019: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 75.3644033055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 179.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 77.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 73.666666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 40500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 6020: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 14.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 11500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6021: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 34.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 13500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6022: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 15500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6023: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 17500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6024: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 95.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 19500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6025: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 116.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 21500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6026: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 137.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 23500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6027: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 158.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 25500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6028: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 179.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 27500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6029: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -163.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 29500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6030: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 31500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -131.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 33500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 35500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6033: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 37500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6034: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -71.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 39500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6035: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -62.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 41500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6036: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 43500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6037: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -22.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 45500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6038: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 72.0250091944447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -5.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 73.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 47500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5500000.0d); + return true; + default: + break; + } + break; + case 6039: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 14500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6040: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 16500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6041: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 18500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6042: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -113.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 20500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6043: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -94.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 22500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6044: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 24500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6045: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -56.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 26500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6046: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -38.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 28500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6047: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 68.6874755555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 70.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 67.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 30500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6500000.0d); + return true; + default: + break; + } + break; + case 6048: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.3510393055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 67.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 63.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 11500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 7500000.0d); + return true; + default: + break; + } + break; + case 6049: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.3510393055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -34.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 67.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 63.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 13500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 7500000.0d); + return true; + default: + break; + } + break; + case 6127: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640419.9475d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6198: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.3166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.1833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.7000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Ellipsoid scaling factor", 1.0000382d); + return true; + default: + break; + } + break; + case 6199: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.1000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 43.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Ellipsoid scaling factor", 1.0000382d); + return true; + default: + break; + } + break; + case 6203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6212: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 7.08760639166694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.7583096555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1035263.443d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1275526.621d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 100.0d); + return true; + default: + break; + } + break; + case 6213: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.53232500000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.6734891666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1155824.666d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 993087.465d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 1470.0d); + return true; + default: + break; + } + break; + case 6214: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.9231830833336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.8343313333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 917264.406d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1699839.935d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 100.0d); + return true; + default: + break; + } + break; + case 6215: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.68048611111139d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.146591666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 92334.879d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 109320.965d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 2550.0d); + return true; + default: + break; + } + break; + case 6216: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 7.07888714166694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -73.1973432222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1097241.305d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1274642.278d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 931.0d); + return true; + default: + break; + } + break; + case 6217: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 3.44188333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.5205625000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1061900.18d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 872364.63d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 1000.0d); + return true; + default: + break; + } + break; + case 6218: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.3970475000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.5112069444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 842981.41d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1641887.09d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 0.0d); + return true; + default: + break; + } + break; + case 6219: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 7.88893673611139d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.5028709500003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 842805.406d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1364404.57d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 308.0d); + return true; + default: + break; + } + break; + case 6220: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 1.62101229444472d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.619117602778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1162300.348d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 671068.716d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 300.0d); + return true; + default: + break; + } + break; + case 6221: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.41941282777806d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.1799259333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 877634.33d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 980541.348d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 1100.0d); + return true; + default: + break; + } + break; + case 6222: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 3.84543818333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -67.9052320888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1019177.687d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 491791.326d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 96.0d); + return true; + default: + break; + } + break; + case 6223: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -4.1976840472225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -69.9428110583336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 25978.217d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 27501.365d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 89.7d); + return true; + default: + break; + } + break; + case 6224: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.06815388888917d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.5110947222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1173727.04d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1052391.13d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 2100.0d); + return true; + default: + break; + } + break; + case 6225: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 6.22920888888917d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.5648869444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 835378.647d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1180816.875d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 1510.0d); + return true; + default: + break; + } + break; + case 6226: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 1.24996936666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.2354616555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1093717.398d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 629997.236d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 170.0d); + return true; + default: + break; + } + break; + case 6227: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 1.14002335833361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.6510212194447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1047467.388d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 617828.474d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 655.2d); + return true; + default: + break; + } + break; + case 6228: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 8.77308575555583d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.8795533305559d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1131814.934d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1462131.119d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 15.0d); + return true; + default: + break; + } + break; + case 6229: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 2.94241505555583d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.2964367222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 864476.923d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 817199.827d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 430.0d); + return true; + default: + break; + } + break; + case 6230: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 1.20098951388917d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -77.2531256333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 980469.695d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 624555.332d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 2530.0d); + return true; + default: + break; + } + break; + case 6231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.81359361111139d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.6939513888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1153492.012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1024195.255d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 1500.0d); + return true; + default: + break; + } + break; + case 6232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 2.45615988333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.6060916361114d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1052430.525d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 763366.548d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 1740.0d); + return true; + default: + break; + } + break; + case 6233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 6.18072141388917d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -67.5007502472225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1063834.703d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1175257.481d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 51.58d); + return true; + default: + break; + } + break; + case 6234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.69424766111139d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.6507538583336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1047273.617d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1121443.09d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 44.0d); + return true; + default: + break; + } + break; + case 6235: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 11.5369133277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.9027688694447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1128154.73d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1767887.914d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 6.0d); + return true; + default: + break; + } + break; + case 6236: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 12.5237943250003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.7293759500003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 820439.298d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1877357.828d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 6.0d); + return true; + default: + break; + } + break; + case 6237: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 2.56407894166694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.6400333250003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1159876.62d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 775380.342d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 185.0d); + return true; + default: + break; + } + break; + case 6238: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 11.2196430555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.2250052777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 983892.409d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1732533.518d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 29.0d); + return true; + default: + break; + } + break; + case 6239: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 8.81055036666695d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.7224668250003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 929043.607d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1466125.658d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 20.0d); + return true; + default: + break; + } + break; + case 6240: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.53419473888917d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -73.3519389000002d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1080514.91d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1103772.028d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 2800.0d); + return true; + default: + break; + } + break; + case 6241: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.4472611111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -73.2465713888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1090979.66d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1647208.93d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 200.0d); + return true; + default: + break; + } + break; + case 6242: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.15537510000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -73.6244859861114d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1050678.757d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 950952.124d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 427.19d); + return true; + default: + break; + } + break; + case 6243: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.3539272222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.4200402777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 851184.177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1083954.137d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Projection plane origin height", 300.0d); + return true; + default: + break; + } + break; + case 6308: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -3500000.0d); + return true; + default: + break; + } + break; + case 6361: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 12.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 17.5d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 6374: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6375: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6376: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6377: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6378: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6379: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6380: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6390: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 19.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -80.5666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 19.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 19.7000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2950000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1900000.0d); + return true; + default: + break; + } + break; + case 6645: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -68.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 60.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 6702: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -60.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 6716: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.625d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1300000.0d); + return true; + default: + break; + } + break; + case 6717: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.625d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002514d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1300000.0d); + return true; + default: + break; + } + break; + case 6718: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 96.8750000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1400000.0d); + return true; + default: + break; + } + break; + case 6719: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 96.8750000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999387d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 6729: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 6730: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 6731: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 6741: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00016d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6742: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00016d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 131233.5958d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6743: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -121.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 80000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6744: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -121.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 262467.1916d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6745: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -121.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 80000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 130000.0d); + return true; + default: + break; + } + break; + case 6746: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -121.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 262467.1916d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 426509.1864d); + return true; + default: + break; + } + break; + case 6747: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 120000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 60000.0d); + return true; + default: + break; + } + break; + case 6748: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 393700.7874d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 196850.3937d); + return true; + default: + break; + } + break; + case 6749: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00007d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6750: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00007d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 131233.5958d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6751: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000008d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 30000.0d); + return true; + default: + break; + } + break; + case 6752: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000008d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 492125.9843d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 98425.1969d); + return true; + default: + break; + } + break; + case 6753: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 45.9166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 295.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 295.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 7000000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", -3000000.0d); + return true; + default: + break; + } + break; + case 6754: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 45.9166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 295.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 295.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 22965879.2651d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", -9842519.685d); + return true; + default: + break; + } + break; + case 6755: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6756: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 164041.9948d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6757: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -121.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00011d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 80000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6758: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -121.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00011d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 262467.1916d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6759: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6760: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 164041.9948d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6761: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000043d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6762: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000043d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 164041.9948d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6763: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00005d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 10000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6764: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00005d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 32808.399d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6765: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00013d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6766: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00013d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 131233.5958d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6767: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0001d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 80000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6768: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0001d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 262467.1916d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6769: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 44.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -124.05d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 5.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 5.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", -300000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", -4600000.0d); + return true; + default: + break; + } + break; + case 6770: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 44.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -124.05d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 5.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 5.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", -984251.9685d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", -15091863.5171d); + return true; + default: + break; + } + break; + case 6771: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000045d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6772: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000045d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 196850.3937d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6773: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000175d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6774: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000175d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 98425.1969d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6775: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50000.0d); + return true; + default: + break; + } + break; + case 6776: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.9895d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 164041.9948d); + return true; + default: + break; + } + break; + case 6777: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.083333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00001d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6778: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.083333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00001d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 164041.9948d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6779: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000155d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6780: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000155d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6869: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6877: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9985d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6878: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6920: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 6921: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1312333.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 6928: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6929: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6930: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 6961: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 43.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 6965: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.7833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.4833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.0833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Ellipsoid scaling factor", 1.0000382d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket7(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 7043: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -95.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000052d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 11500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 9600000.0d); + return true; + default: + break; + } + break; + case 7044: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000043d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 12500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 9800000.0d); + return true; + default: + break; + } + break; + case 7045: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.2000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000035d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 13500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 8300000.0d); + return true; + default: + break; + } + break; + case 7046: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -94.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000045d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 14500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 8600000.0d); + return true; + default: + break; + } + break; + case 7047: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000032d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 15500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 8900000.0d); + return true; + default: + break; + } + break; + case 7048: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -95.7333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000039d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 16500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 6600000.0d); + return true; + default: + break; + } + break; + case 7049: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -94.6333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000045d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 17500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 6800000.0d); + return true; + default: + break; + } + break; + case 7050: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -93.716666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000033d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 18500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 7000000.0d); + return true; + default: + break; + } + break; + case 7051: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.8166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 19500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 7200000.0d); + return true; + default: + break; + } + break; + case 7052: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 8000000.0d); + return true; + default: + break; + } + break; + case 7053: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.5333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 21500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 7600000.0d); + return true; + default: + break; + } + break; + case 7054: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -93.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000037d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 22500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 6200000.0d); + return true; + default: + break; + } + break; + case 7055: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.9166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 23500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 6400000.0d); + return true; + default: + break; + } + break; + case 7056: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000018d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 24500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 6200000.0d); + return true; + default: + break; + } + break; + case 7089: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00016d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7090: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00016d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 492125.9843d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7091: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7092: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.9895d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7093: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000145d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 7094: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000145d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 492125.9843d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 656167.979d); + return true; + default: + break; + } + break; + case 7095: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 150000.0d); + return true; + default: + break; + } + break; + case 7096: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656167.979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492125.9843d); + return true; + default: + break; + } + break; + case 7097: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 7098: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656167.979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 328083.9895d); + return true; + default: + break; + } + break; + case 7099: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00009d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50000.0d); + return true; + default: + break; + } + break; + case 7100: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00009d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.9895d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 164041.9938d); + return true; + default: + break; + } + break; + case 7101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000148d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000148d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656167.979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000185d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 7104: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000185d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.9895d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 328083.9895d); + return true; + default: + break; + } + break; + case 7105: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.7833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0001515d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50000.0d); + return true; + default: + break; + } + break; + case 7106: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.7833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0001515d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656167.979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 164041.9948d); + return true; + default: + break; + } + break; + case 7107: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7108: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.3333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7129: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.75d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.45d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000007d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 48000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 24000.0d); + return true; + default: + break; + } + break; + case 7130: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.75d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.45d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000007d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 157480.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 78740.0d); + return true; + default: + break; + } + break; + case 7141: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.7340969444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.2120805555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 170251.555d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 126867.909d); + return true; + default: + break; + } + break; + case 7143: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7144: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7145: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7146: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7147: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7148: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7149: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000029d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7150: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000029d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7151: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7152: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7153: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7154: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7155: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7156: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7157: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7158: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7159: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000028d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7160: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000028d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7161: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.6000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000021d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7162: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.6000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000021d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7163: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7164: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7165: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.6000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000032d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7166: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.6000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000032d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7167: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.1000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7168: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.1000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7169: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000018d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7170: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000018d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7171: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000029d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7172: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000029d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7173: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7174: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7175: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7176: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7177: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7178: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7179: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000033d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7180: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000033d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7181: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7182: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7183: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7184: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7185: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7186: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7187: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000013d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7188: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000013d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7189: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.7000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7190: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.7000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7191: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7192: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7193: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7194: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000036d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7195: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7196: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7197: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000043d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7198: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000043d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7199: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7200: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7204: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7205: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7206: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7207: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7208: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7209: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000028d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7210: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000028d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7211: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7212: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7213: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7214: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7215: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7216: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7217: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000037d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7218: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000037d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7219: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7220: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7221: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7222: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000027d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7223: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000028d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7224: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000028d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7225: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7226: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7227: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7228: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7229: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7230: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.7000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.7000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.8500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.8500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7235: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000013d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7236: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000013d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7237: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000044d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7238: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000044d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7239: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7240: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000038d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7241: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7242: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7243: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7244: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7245: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000041d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7246: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000041d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7247: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000017d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7248: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000017d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7249: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7250: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -86.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7251: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7252: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7253: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7254: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7255: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 240000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36000.0d); + return true; + default: + break; + } + break; + case 7256: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 787400.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 118110.0d); + return true; + default: + break; + } + break; + case 7378: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.7061111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.6222222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000495683d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 172821.9461d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0017d); + return true; + default: + break; + } + break; + case 7379: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.7061111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.6222222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000495683d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 567000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.006d); + return true; + default: + break; + } + break; + case 7380: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.6696483772225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.152777777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000331195d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 228600.4575d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 148551.4837d); + return true; + default: + break; + } + break; + case 7381: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.6696483772225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.152777777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000331195d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 750000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 487372.659d); + return true; + default: + break; + } + break; + case 7382: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.8987148658336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.4577777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000383841d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 64008.1276d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 59445.9043d); + return true; + default: + break; + } + break; + case 7383: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.8987148658336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.4577777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000383841d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 209999.999d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 195032.104d); + return true; + default: + break; + } + break; + case 7384: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.8833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.9166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000385418d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 59131.3183d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0041d); + return true; + default: + break; + } + break; + case 7385: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.8833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.9166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000385418d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 194000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.013d); + return true; + default: + break; + } + break; + case 7386: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.4388888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.141666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000552095d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 133502.6683d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0063d); + return true; + default: + break; + } + break; + case 7387: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.4388888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.141666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000552095d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 438000.004d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.021d); + return true; + default: + break; + } + break; + case 7388: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0055555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.6333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000673004d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 275844.5533d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0157d); + return true; + default: + break; + } + break; + case 7389: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0055555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.6333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000673004d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 905000.005d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.052d); + return true; + default: + break; + } + break; + case 7390: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.4333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.2555555555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000677153d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 220980.4419d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0085d); + return true; + default: + break; + } + break; + case 7391: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.4333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.2555555555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000677153d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 725000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.028d); + return true; + default: + break; + } + break; + case 7392: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.7042237702781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.5444444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000686968d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 70104.1401d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 57588.0346d); + return true; + default: + break; + } + break; + case 7393: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.7042237702781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.5444444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000686968d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 230000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 188936.744d); + return true; + default: + break; + } + break; + case 7394: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.5555555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.4888888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000649554d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 227990.8546d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0109d); + return true; + default: + break; + } + break; + case 7395: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.5555555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.4888888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000649554d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 747999.995d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.036d); + return true; + default: + break; + } + break; + case 7396: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.9000991313892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.1166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000573461d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 216713.2336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 120734.1631d); + return true; + default: + break; + } + break; + case 7397: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.9000991313892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.1166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000573461d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 711000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 396108.667d); + return true; + default: + break; + } + break; + case 7398: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.0778440905558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.4888888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000730142d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 134417.0689d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50337.1092d); + return true; + default: + break; + } + break; + case 7399: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.0778440905558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.4888888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000730142d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 441000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 165147.666d); + return true; + default: + break; + } + break; + case 7424: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.9612198333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.7833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000475376d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 234086.8682d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 188358.6058d); + return true; + default: + break; + } + break; + case 7425: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.9612198333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.7833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000475376d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 768000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 617973.193d); + return true; + default: + break; + } + break; + case 7426: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.1333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000486665d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 93150.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0029d); + return true; + default: + break; + } + break; + case 7427: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.1333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000486665d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 305609.625d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.01d); + return true; + default: + break; + } + break; + case 7428: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 31600.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4600.0d); + return true; + default: + break; + } + break; + case 7429: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 103674.333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 15091.833d); + return true; + default: + break; + } + break; + case 7430: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4813888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.7972222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000382778d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 175260.3502d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0048d); + return true; + default: + break; + } + break; + case 7431: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4813888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.7972222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000382778d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 574999.999d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.016d); + return true; + default: + break; + } + break; + case 7432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.9778568986114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.2944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000391127d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60045.72d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 44091.4346d); + return true; + default: + break; + } + break; + case 7433: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.9778568986114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.2944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000391127d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 197000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 144656.648d); + return true; + default: + break; + } + break; + case 7434: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.7083333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000463003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 199949.1989d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0086d); + return true; + default: + break; + } + break; + case 7435: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.7083333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000463003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 655999.997d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.028d); + return true; + default: + break; + } + break; + case 7436: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.2722222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000187521d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 158801.1176d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0023d); + return true; + default: + break; + } + break; + case 7437: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.2722222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000187521d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 521000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.008d); + return true; + default: + break; + } + break; + case 7438: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.4083333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.8944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000410324d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 51816.104d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.003d); + return true; + default: + break; + } + break; + case 7439: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.4083333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.8944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000410324d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 170000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.01d); + return true; + default: + break; + } + break; + case 7440: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.8722811263892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.2888888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000035079d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 120091.4402d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 91687.9239d); + return true; + default: + break; + } + break; + case 7441: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.8722811263892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.2888888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000035079d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 394000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300812.797d); + return true; + default: + break; + } + break; + case 7450: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.2533351277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.8442965194447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000353d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 27000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 25000.0d); + return true; + default: + break; + } + break; + case 7451: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.2533351277781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.8442965194447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000353d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 88582.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 82020.833d); + return true; + default: + break; + } + break; + case 7452: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.154237105278d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.0333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000627024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 198425.197d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 105279.7829d); + return true; + default: + break; + } + break; + case 7453: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.154237105278d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.0333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000627024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 651000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 345405.421d); + return true; + default: + break; + } + break; + case 7454: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.8444444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.7333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000599003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 116129.0323d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0058d); + return true; + default: + break; + } + break; + case 7455: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.8444444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.7333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000599003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 381000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.019d); + return true; + default: + break; + } + break; + case 7456: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.9009044236114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.7700000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000053289d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 74676.1493d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 55049.2669d); + return true; + default: + break; + } + break; + case 7457: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.9009044236114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.7700000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000053289d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 245000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 180607.47d); + return true; + default: + break; + } + break; + case 7458: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6916666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.7111111111114d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000234982d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 238658.8794d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0032d); + return true; + default: + break; + } + break; + case 7459: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6916666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.7111111111114d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000234982d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 783000.007d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.01d); + return true; + default: + break; + } + break; + case 7460: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.7166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000362499d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 105461.0121d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0029d); + return true; + default: + break; + } + break; + case 7461: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.7166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000362499d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 346000.004d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.01d); + return true; + default: + break; + } + break; + case 7462: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.3972222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.9083333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000236869d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 182880.3676d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0033d); + return true; + default: + break; + } + break; + case 7463: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.3972222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.9083333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000236869d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.006d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.011d); + return true; + default: + break; + } + break; + case 7464: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6361488719447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.2277777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000362977d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 167640.3354d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 86033.0876d); + return true; + default: + break; + } + break; + case 7465: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6361488719447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.2277777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000362977d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 550000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 282260.222d); + return true; + default: + break; + } + break; + case 7466: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6611111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.6333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000433849d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 141732.2823d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0059d); + return true; + default: + break; + } + break; + case 7467: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.6611111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.6333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000433849d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 464999.996d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.019d); + return true; + default: + break; + } + break; + case 7468: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.4168239752781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000039936d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 56388.1128d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50022.1874d); + return true; + default: + break; + } + break; + case 7469: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.4168239752781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000039936d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 185000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 164114.46d); + return true; + default: + break; + } + break; + case 7470: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.9194444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.066666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000495976d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250546.1013d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0234d); + return true; + default: + break; + } + break; + case 7471: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.9194444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.066666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000495976d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 822000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.077d); + return true; + default: + break; + } + break; + case 7472: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0361111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.6055555555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000032144d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 262433.3253d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0096d); + return true; + default: + break; + } + break; + case 7473: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0361111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.6055555555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000032144d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 861000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.031d); + return true; + default: + break; + } + break; + case 7474: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0361111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.6333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000381803d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 165506.7302d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0103d); + return true; + default: + break; + } + break; + case 7475: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0361111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.6333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000381803d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 542999.997d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.034d); + return true; + default: + break; + } + break; + case 7476: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.1778220858336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.4833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000597566d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 187147.5744d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 107746.7522d); + return true; + default: + break; + } + break; + case 7477: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.1778220858336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.4833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000597566d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 614000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 353499.136d); + return true; + default: + break; + } + break; + case 7478: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.1611111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.3666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000361538d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 256946.9138d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0041d); + return true; + default: + break; + } + break; + case 7479: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.1611111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.3666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000361538d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 843000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.013d); + return true; + default: + break; + } + break; + case 7480: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4202777777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.8166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000333645d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 185013.9709d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.007d); + return true; + default: + break; + } + break; + case 7481: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4202777777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.8166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000333645d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 607000.003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.023d); + return true; + default: + break; + } + break; + case 7482: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.3625954694447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000421209d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 208483.6173d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 134589.754d); + return true; + default: + break; + } + break; + case 7483: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.3625954694447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000421209d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 684000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 441566.551d); + return true; + default: + break; + } + break; + case 7484: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.3666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000365285d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 147218.6942d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0037d); + return true; + default: + break; + } + break; + case 7485: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.3666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000365285d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 482999.999d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.012d); + return true; + default: + break; + } + break; + case 7486: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.7194444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000286569d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 244754.8893d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0049d); + return true; + default: + break; + } + break; + case 7487: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.7194444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000286569d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 802999.999d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.016d); + return true; + default: + break; + } + break; + case 7488: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4625466458336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.3944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003498d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 169164.3381d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 111569.6134d); + return true; + default: + break; + } + break; + case 7489: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4625466458336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.3944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003498d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 554999.999d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 366041.307d); + return true; + default: + break; + } + break; + case 7490: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.2000556050003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.9388888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000349151d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 113690.6274d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 53703.1201d); + return true; + default: + break; + } + break; + case 7491: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.2000556050003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.9388888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000349151d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 373000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 176190.987d); + return true; + default: + break; + } + break; + case 7492: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.0695160375003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.4222222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000384786d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 247193.2944d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 146591.9896d); + return true; + default: + break; + } + break; + case 7493: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.0695160375003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.4222222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000384786d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 811000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 480943.886d); + return true; + default: + break; + } + break; + case 7494: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.4722222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.7750000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000346418d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 263347.7263d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0076d); + return true; + default: + break; + } + break; + case 7495: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.4722222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.7750000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000346418d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 863999.999d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.025d); + return true; + default: + break; + } + break; + case 7496: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.4111111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000349452d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 242316.4841d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.01d); + return true; + default: + break; + } + break; + case 7497: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.4111111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000349452d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 794999.998d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.033d); + return true; + default: + break; + } + break; + case 7498: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.6375622769447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.8388888888891d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000390487d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 170078.7403d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 45830.2947d); + return true; + default: + break; + } + break; + case 7499: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.6375622769447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.8388888888891d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000390487d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 558000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 150361.559d); + return true; + default: + break; + } + break; + case 7500: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.807000117778d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.2416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000344057d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150876.3018d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 79170.7795d); + return true; + default: + break; + } + break; + case 7501: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.807000117778d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.2416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000344057d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 495000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 259746.132d); + return true; + default: + break; + } + break; + case 7502: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5388888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.1611111111114d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000394961d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 113081.0261d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0045d); + return true; + default: + break; + } + break; + case 7503: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5388888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.1611111111114d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000394961d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 371000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.015d); + return true; + default: + break; + } + break; + case 7504: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.216666666667d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.8944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000260649d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 185928.3728d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0009d); + return true; + default: + break; + } + break; + case 7505: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.216666666667d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.8944444444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000260649d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 610000.003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.003d); + return true; + default: + break; + } + break; + case 7506: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.2666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000233704d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 79857.7614d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0012d); + return true; + default: + break; + } + break; + case 7507: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.2666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000233704d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 262000.006d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.004d); + return true; + default: + break; + } + break; + case 7508: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4511111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.3166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000319985d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 130454.6598d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0033d); + return true; + default: + break; + } + break; + case 7509: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.4511111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.3166666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000319985d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 427999.996d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.011d); + return true; + default: + break; + } + break; + case 7510: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0000739286114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.6416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000434122d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 204521.209d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 121923.9861d); + return true; + default: + break; + } + break; + case 7511: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0000739286114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.6416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000434122d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 671000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400012.278d); + return true; + default: + break; + } + break; + case 7512: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.3223129275003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.4305555555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000375653d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 202387.6048d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 134255.4253d); + return true; + default: + break; + } + break; + case 7513: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.3223129275003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.4305555555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000375653d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 664000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 440469.675d); + return true; + default: + break; + } + break; + case 7514: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.9444444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.0722222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000337311d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 146304.2926d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0068d); + return true; + default: + break; + } + break; + case 7515: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.9444444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.0722222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000337311d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 480000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.022d); + return true; + default: + break; + } + break; + case 7516: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8194444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000373868d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 185623.5716d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0051d); + return true; + default: + break; + } + break; + case 7517: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8194444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000373868d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 609000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.017d); + return true; + default: + break; + } + break; + case 7518: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5750329397225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.7833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000408158d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 222504.4451d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 47532.0602d); + return true; + default: + break; + } + break; + case 7519: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5750329397225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.7833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000408158d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 730000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 155944.768d); + return true; + default: + break; + } + break; + case 7520: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.6694620969447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.5416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000367192d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 232562.8651d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 111088.2224d); + return true; + default: + break; + } + break; + case 7521: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.6694620969447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.5416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000367192d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 763000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 364461.943d); + return true; + default: + break; + } + break; + case 7522: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.9180555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0638888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003738d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 120091.4415d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.003d); + return true; + default: + break; + } + break; + case 7523: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.9180555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0638888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003738d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 394000.004d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.01d); + return true; + default: + break; + } + break; + case 7524: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5694444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.2250000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000346179d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 208788.418d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0034d); + return true; + default: + break; + } + break; + case 7525: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5694444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.2250000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000346179d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 685000.001d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.011d); + return true; + default: + break; + } + break; + case 7526: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.1139440458336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.2416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000392096d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 120091.4402d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 45069.7587d); + return true; + default: + break; + } + break; + case 7527: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.1139440458336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.2416666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000392096d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 394000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 147866.367d); + return true; + default: + break; + } + break; + case 7687: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 68.516666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 14743.5d); + return true; + default: + break; + } + break; + case 7688: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 71.516666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 14743.5d); + return true; + default: + break; + } + break; + case 7689: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 74.516666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 14743.5d); + return true; + default: + break; + } + break; + case 7690: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 77.516666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 14743.5d); + return true; + default: + break; + } + break; + case 7691: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 80.516666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 14743.5d); + return true; + default: + break; + } + break; + case 7722: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 24.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 80.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 12.4729550000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.1728044444447d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4000000.0d); + return true; + default: + break; + } + break; + case 7723: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 16.25543298d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 80.875d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 13.75d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 18.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7724: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 28.00157897d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 94.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7725: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 26.00257703d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 92.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 24.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 27.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7726: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.87725247d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 85.875d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 24.625d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 27.125d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7727: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 28.62510126d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 77.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 28.3750000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 28.8750000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7728: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 22.37807121d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 71.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 20.7916666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 23.9583333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7729: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 29.25226266d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 76.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 28.0833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 30.4166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7730: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.75183497d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 77.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.75d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7731: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.75570874d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 76.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.0833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.4166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7732: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 23.62652682d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 85.625d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 22.5416666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 24.7083333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7733: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 24.00529821d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 78.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 22.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7734: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 18.88015774d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 76.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 16.6250000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 21.1250000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7735: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 24.75060911d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 94.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 24.0833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 25.4166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7736: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.62524747d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 91.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 25.2083333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.0416666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7737: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 26.12581974d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 94.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 25.3750000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.8750000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7738: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.63452135d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 93.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 23.0416666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 28.2083333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7739: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 20.25305174d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 84.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 18.5833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 21.9166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7740: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.00178226d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 75.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7741: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 26.88505546d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 73.875d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 24.2916666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.4583333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7742: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 27.13270823d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 80.875d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 24.8750000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.3750000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7743: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 30.0017132d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 79.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 29.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 31.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 7744: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.25d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999428d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7745: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 82.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998332d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7746: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 15.375d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999913d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7747: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 15.125d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 76.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7748: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.5d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 76.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7749: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 73.125d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999536d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7750: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 23.125d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 92.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999821d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7751: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 27.625d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 88.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999926d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7752: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.875d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 78.375d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9997942d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7753: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 23.75d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 91.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999822d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7754: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.375d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.875d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998584d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 7802: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.6678756833336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 25.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 43.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4725824.3591d); + return true; + default: + break; + } + break; + case 7818: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.08333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7819: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.08333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 26.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7820: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.08333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 29.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7821: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.08333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 32.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7822: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.08333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7823: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.08333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 38.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7824: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.08333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 41.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 7875: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -15.9666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -5.71666666666694d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 7876: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -15.9666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -5.71666666666694d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 299483.737d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000527.879d); + return true; + default: + break; + } + break; + case 7993: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.883333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000044d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4100000.0d); + return true; + default: + break; + } + break; + case 7994: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2700000.0d); + return true; + default: + break; + } + break; + case 7995: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 122.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000298d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2300000.0d); + return true; + default: + break; + } + break; + case 7996: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.433333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999592d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4000000.0d); + return true; + default: + break; + } + break; + case 7997: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 113.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999796d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3050000.0d); + return true; + default: + break; + } + break; + case 7998: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.625d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002514d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1400000.0d); + return true; + default: + break; + } + break; + case 7999: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 96.8750000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999387d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1600000.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket8(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 8000: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.933333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4100000.0d); + return true; + default: + break; + } + break; + case 8001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 121.883333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000055d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4050000.0d); + return true; + default: + break; + } + break; + case 8002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.066666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000236d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2750000.0d); + return true; + default: + break; + } + break; + case 8003: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000628d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3450000.0d); + return true; + default: + break; + } + break; + case 8004: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 121.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00004949d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3800000.0d); + return true; + default: + break; + } + break; + case 8005: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.983333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000314d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3650000.0d); + return true; + default: + break; + } + break; + case 8006: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.315277777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 55000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3700000.0d); + return true; + default: + break; + } + break; + case 8007: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 116.933333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999989d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2550000.0d); + return true; + default: + break; + } + break; + case 8008: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 128.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000165d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2100000.0d); + return true; + default: + break; + } + break; + case 8009: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.366666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000157d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3750000.0d); + return true; + default: + break; + } + break; + case 8010: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000055d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4050000.0d); + return true; + default: + break; + } + break; + case 8011: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.816666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999906d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3900000.0d); + return true; + default: + break; + } + break; + case 8012: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 118.6d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000135d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2500000.0d); + return true; + default: + break; + } + break; + case 8033: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8034: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8040: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.0384638888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.8041805555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8041: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 48.2087611111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 34.0409222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8061: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 32.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -111.4d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 45.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 45.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.00011d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 160000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 800000.0d); + return true; + default: + break; + } + break; + case 8062: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00009d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1800000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 8063: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -113.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000055d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8064: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -620000.0d); + return true; + default: + break; + } + break; + case 8080: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -61.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 24500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8081: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -64.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 25500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8087: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 64.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 65.7500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 300000.0d); + return true; + default: + break; + } + break; + case 8273: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 90000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8274: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 295275.5906d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8275: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8276: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 65616.7979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8277: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000045d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 20000.0d); + return true; + default: + break; + } + break; + case 8278: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000045d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 98425.1969d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 65616.7979d); + return true; + default: + break; + } + break; + case 8279: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.633333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8280: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.633333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 65616.7979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8281: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 80000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8282: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 262467.1916d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8283: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000085d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 70000.0d); + return true; + default: + break; + } + break; + case 8284: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000085d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 131233.5958d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 229658.7927d); + return true; + default: + break; + } + break; + case 8285: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00004d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -60000.0d); + return true; + default: + break; + } + break; + case 8286: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00004d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 196850.3937d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -196850.3937d); + return true; + default: + break; + } + break; + case 8287: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99927d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 290000.0d); + return true; + default: + break; + } + break; + case 8288: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99927d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 98425.1969d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 951443.5696d); + return true; + default: + break; + } + break; + case 8289: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 140000.0d); + return true; + default: + break; + } + break; + case 8290: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.9895d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 459317.5853d); + return true; + default: + break; + } + break; + case 8291: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00006d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -80000.0d); + return true; + default: + break; + } + break; + case 8292: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00006d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 131233.5958d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -262467.1916d); + return true; + default: + break; + } + break; + case 8293: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00018d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 70000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8294: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00018d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 229658.7927d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8295: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 130000.0d); + return true; + default: + break; + } + break; + case 8296: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 164041.9948d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 426509.1864d); + return true; + default: + break; + } + break; + case 8297: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00017d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8298: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00017d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 196850.3937d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8299: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000215d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 70000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8300: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000215d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 229658.7927d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8301: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 10000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 60000.0d); + return true; + default: + break; + } + break; + case 8302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 32808.399d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 196850.3937d); + return true; + default: + break; + } + break; + case 8303: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 90000.0d); + return true; + default: + break; + } + break; + case 8304: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 98425.1969d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 295275.5906d); + return true; + default: + break; + } + break; + case 8305: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000195d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8306: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000195d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 196850.3937d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8307: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000245d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 60000.0d); + return true; + default: + break; + } + break; + case 8308: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000245d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 131233.5958d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 196850.3937d); + return true; + default: + break; + } + break; + case 8309: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000223d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8310: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000223d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 65616.7979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8373: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.966666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0001d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 8374: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.966666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0001d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.3333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 656166.6667d); + return true; + default: + break; + } + break; + case 8375: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.966666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000135d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400000.0d); + return true; + default: + break; + } + break; + case 8376: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.966666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000135d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 984250.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1312333.3333d); + return true; + default: + break; + } + break; + case 8389: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999929d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 8432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 22.2123972222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 113.536469444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 20000.0d); + return true; + default: + break; + } + break; + case 8440: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", -18.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 46.4372291666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 18.9000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.9995d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 8458: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -101.6d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000156d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8459: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -100.95d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000134d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8490: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -100.35d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000116d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8491: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.4500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000082d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8492: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -98.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000078d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8493: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -98.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000068d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8494: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -97.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000049d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8495: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -96.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000044d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 8498: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -96.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00005d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 9500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 8499: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -95.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00004d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 10500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 8500: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -95.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000033d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 11500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 8501: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -101.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 12500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8502: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -100.4d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000109d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 13500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8503: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000097d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 14500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8504: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.2000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000087d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 15500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8505: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -98.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000069d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 16500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8506: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.7666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -97.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000059d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 17500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400000.0d); + return true; + default: + break; + } + break; + case 8507: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.1833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -97.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000055d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 18500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 8515: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -95.9666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000034d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 19500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8516: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -95.0833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8825: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1200000.0d); + return true; + default: + break; + } + break; + case 8854: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8855: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 8856: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 150.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket9(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 9058: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 103.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9190: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 175.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -30.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -50.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 9192: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 104.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9193: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 104.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9194: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 104.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9195: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9196: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9197: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 106.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9198: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 106.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9199: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 106.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9200: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 107.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 107.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 107.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 108.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9204: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 108.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9219: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -22.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -38.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1300000.0d); + return true; + default: + break; + } + break; + case 9220: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 44.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -34.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -50.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1200000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1300000.0d); + return true; + default: + break; + } + break; + case 9268: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9269: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9270: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9301: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.3d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 198873.0046d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 375064.3871d); + return true; + default: + break; + } + break; + case 9353: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -65.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9366: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.25000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 203252.175d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 407512.765d); + return true; + default: + break; + } + break; + case 9370: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -0.85000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 49350.157d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 108398.212d); + return true; + default: + break; + } + break; + case 9376: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -73.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9992d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 9385: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 57.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.20000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 155828.702d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 115225.707d); + return true; + default: + break; + } + break; + case 9455: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 55.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -4.35000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 93720.394d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 113870.493d); + return true; + default: + break; + } + break; + case 9497: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -34.6292666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -58.4633083333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 70000.0d); + return true; + default: + break; + } + break; + case 9548: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.3791666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.1833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 6.81666666666694d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 150000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 50000.0d); + return true; + default: + break; + } + break; + case 9673: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 48.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 9677: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2000000.0d); + return true; + default: + break; + } + break; + case 9738: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 56.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.75000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 74996.927d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 133508.35d); + return true; + default: + break; + } + break; + case 9746: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9747: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1968500.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9760: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 55.0500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.55000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 112242.8512d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 402313.7432d); + return true; + default: + break; + } + break; + case 9765: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 51.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -0.90000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 192519.9715d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 146942.6806d); + return true; + default: + break; + } + break; + case 9796: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9797: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 34.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9798: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.6666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9799: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9800: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9801: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 37.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9802: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9803: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9804: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9805: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9806: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 32.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9807: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9808: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9809: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9810: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9811: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9812: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9813: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 34.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9814: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9815: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9816: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9817: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9818: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 26.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9819: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 32.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9820: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9868: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.80000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 227286.9881d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 265751.2874d); + return true; + default: + break; + } + break; + case 9872: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 9873: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 9879: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.90000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 226574.2032d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 390894.838d); + return true; + default: + break; + } + break; + case 9894: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 49.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 6.16666666666694d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 80000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 9911: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -4000000.0d); + return true; + default: + break; + } + break; + case 9942: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 51.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.10000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 106702.326d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 119968.1395d); + return true; + default: + break; + } + break; + case 9946: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 65.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1700000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1300000.0d); + return true; + default: + break; + } + break; + case 9966: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -0.95000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 140859.7394d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 247512.2812d); + return true; + default: + break; + } + break; + case 9971: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 56.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.85000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 108600.972d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 239087.349d); + return true; + default: + break; + } + break; + case 9976: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.80000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 139618.9493d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 183110.794d); + return true; + default: + break; + } + break; + case 9981: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -61.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 14500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 9982: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -64.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 15500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket10(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 10101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10127: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.55000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 171975.9382d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 116744.6938d); + return true; + default: + break; + } + break; + case 10131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10132: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10147: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -28.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 10148: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 40.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 58.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 58.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 1500000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 1500000.0d); + return true; + default: + break; + } + break; + case 10159: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.37d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", -210327.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -6034310.0d); + return true; + default: + break; + } + break; + case 10182: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.7000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -4.15000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 64859.6557d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 122266.5277d); + return true; + default: + break; + } + break; + case 10187: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.35000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 56023.5377d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 24567.6764d); + return true; + default: + break; + } + break; + case 10193: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 53.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -3.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 53.1000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 53.4000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 212548.8756d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 495230.9254d); + return true; + default: + break; + } + break; + case 10198: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.90000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 199668.0926d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 89354.3229d); + return true; + default: + break; + } + break; + case 10201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.916666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -113.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10206: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 51.8500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.30000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 511622.854d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 324737.4633d); + return true; + default: + break; + } + break; + case 10211: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 51.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -2.65000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 51.4000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.9000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 168854.016d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 193447.117d); + return true; + default: + break; + } + break; + case 10226: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.15000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 175262.1809d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 174688.2508d); + return true; + default: + break; + } + break; + case 10231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 213360.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.916666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 213360.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -113.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 213360.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 51.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.70000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 134791.6965d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 121872.5056d); + return true; + default: + break; + } + break; + case 10239: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.60000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 110094.4312d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 120623.8396d); + return true; + default: + break; + } + break; + case 10253: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 55.11171d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 14.88927d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", -50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50000.0d); + return true; + default: + break; + } + break; + case 10257: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 56.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 55.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 10.37775d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10261: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 56.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 55.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 14.92775d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", -18831.46d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5614.621d); + return true; + default: + break; + } + break; + case 10269: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.4685d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.233d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10274: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 50.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.85000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 110693.666d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 102089.2943d); + return true; + default: + break; + } + break; + case 10279: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 50.8500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -3.25000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 50.3000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.4500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 372382.8292d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 217764.7796d); + return true; + default: + break; + } + break; + case 10301: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.2333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 32.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.3000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10313: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -21.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 166.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -20.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -22.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2300000.0d); + return true; + default: + break; + } + break; + case 10325: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.2333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 32.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.3000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 400000.0d); + return true; + default: + break; + } + break; + case 10401: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10402: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10403: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.0666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10404: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 35.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10405: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -118.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.466666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10406: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 32.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -116.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.7833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10408: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.1333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -118.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.4166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.866666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4186692.58d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4160926.74d); + return true; + default: + break; + } + break; + case 10420: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", -4000000.0d); + return true; + default: + break; + } + break; + case 10424: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.883333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000044d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4000000.0d); + return true; + default: + break; + } + break; + case 10425: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2600000.0d); + return true; + default: + break; + } + break; + case 10426: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 122.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000298d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2200000.0d); + return true; + default: + break; + } + break; + case 10427: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.433333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999592d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3900000.0d); + return true; + default: + break; + } + break; + case 10428: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 113.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999796d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2950000.0d); + return true; + default: + break; + } + break; + case 10429: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.933333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4000000.0d); + return true; + default: + break; + } + break; + case 10430: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 121.883333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000055d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3950000.0d); + return true; + default: + break; + } + break; + case 10431: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 10432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 10433: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.0666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 10434: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 35.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 10435: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -118.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.466666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 10436: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 32.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -116.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.7833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 10437: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.066666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000236d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2650000.0d); + return true; + default: + break; + } + break; + case 10438: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000628d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3350000.0d); + return true; + default: + break; + } + break; + case 10439: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 121.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00004949d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3700000.0d); + return true; + default: + break; + } + break; + case 10440: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.983333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000314d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3550000.0d); + return true; + default: + break; + } + break; + case 10441: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.315277777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000014d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 55000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3600000.0d); + return true; + default: + break; + } + break; + case 10442: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 116.933333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999989d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2450000.0d); + return true; + default: + break; + } + break; + case 10443: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 128.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000165d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 10444: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.366666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000157d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3650000.0d); + return true; + default: + break; + } + break; + case 10445: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000055d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3950000.0d); + return true; + default: + break; + } + break; + case 10446: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.816666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999906d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3800000.0d); + return true; + default: + break; + } + break; + case 10447: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 118.6d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000135d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2400000.0d); + return true; + default: + break; + } + break; + case 10470: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.55000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 116887.9989d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 102194.9369d); + return true; + default: + break; + } + break; + case 10476: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.9807763055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.5284937500003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000121d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 10479: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.25d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.5d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4921250.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 19685000.0d); + return true; + default: + break; + } + break; + case 10501: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.7166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.7833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10502: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.7500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.4500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10503: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.2333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10515: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.2533353222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.8442965138891d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000353d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 88582.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 82020.833d); + return true; + default: + break; + } + break; + case 10531: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 914401.8289d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 304800.6096d); + return true; + default: + break; + } + break; + case 10532: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.7500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.4500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 914401.8289d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 304800.6096d); + return true; + default: + break; + } + break; + case 10533: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.2333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 914401.8289d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 304800.6096d); + return true; + default: + break; + } + break; + case 10591: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10593: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10595: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 55.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10597: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 50.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10599: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -15.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10600: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -72.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.866666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.2000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10602: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -15.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -60.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10621: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 37.6289686531d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -122.3939412704d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 27.7928209333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.9999968d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 0.0d); + return true; + default: + break; + } + break; + case 10625: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.60000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 108021.121d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 263196.8721d); + return true; + default: + break; + } + break; + case 10630: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -72.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.866666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.2000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 304800.6096d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 152400.3048d); + return true; + default: + break; + } + break; + case 10631: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.15000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 209900.2337d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 401958.2494d); + return true; + default: + break; + } + break; + case 10640: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 29973.97d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -1947925.94d); + return true; + default: + break; + } + break; + case 10664: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 5000000.0d); + return true; + default: + break; + } + break; + case 10700: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10719: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999984965d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 350000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10720: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999730738d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 350000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10721: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999962402d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 350000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10722: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999995546d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 350000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10730: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10743: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 578.55d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -1930396.26d); + return true; + default: + break; + } + break; + case 10757: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 12.1806586750003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -68.2518022805559d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 23209.56d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 21423.99d); + return true; + default: + break; + } + break; + case 10772: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -21.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -47.7833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000092d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 15000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 25000.0d); + return true; + default: + break; + } + break; + case 10819: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.75d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 55.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10832: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 43.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1300000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 400000.0d); + return true; + default: + break; + } + break; + case 10856: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -12.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -2.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -22.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 5000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 10000000.0d); + return true; + default: + break; + } + break; + case 10862: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -0.85000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 39630.9213d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 36016.293d); + return true; + default: + break; + } + break; + case 10901: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10902: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -82.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10903: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 29.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.7500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.5833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10931: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10932: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -82.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 10933: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 29.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.7500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.5833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10934: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 24.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 24.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 31.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 10970: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 28.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1600200.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 533400.0d); + return true; + default: + break; + } + break; + case 10971: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 28.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1750000.0d); + return true; + default: + break; + } + break; + case 10972: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999165d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000033.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 484781.25d); + return true; + default: + break; + } + break; + case 10973: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.3325000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998499d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000111.25d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 544993.75d); + return true; + default: + break; + } + break; + case 10974: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 27.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998844d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000100.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 484512.5d); + return true; + default: + break; + } + break; + case 10975: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999584d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640525.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -528.75d); + return true; + default: + break; + } + break; + case 10976: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9995867d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640461.25d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -548.75d); + return true; + default: + break; + } + break; + case 10977: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9995904d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640403.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -585.0d); + return true; + default: + break; + } + break; + case 10978: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9995987d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640355.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -676.25d); + return true; + default: + break; + } + break; + case 10994: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 51.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -0.15833333333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 78250.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -2800.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket11(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 11001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -82.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -82.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999947368d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999947368d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999947368d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11132: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999947368d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11133: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 800000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3773000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4757000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11235: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.6000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5741000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 755000.0d); + return true; + default: + break; + } + break; + case 11236: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6726000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 755000.0d); + return true; + default: + break; + } + break; + case 11237: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.0500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7743000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 755000.0d); + return true; + default: + break; + } + break; + case 11238: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8694000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11239: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 9678000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11240: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 230000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11241: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1378000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 622000.0d); + return true; + default: + break; + } + break; + case 11242: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.3000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2756000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 427000.0d); + return true; + default: + break; + } + break; + case 11243: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.8500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000031d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3773000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1739000.0d); + return true; + default: + break; + } + break; + case 11244: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.5500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000025d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4757000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1739000.0d); + return true; + default: + break; + } + break; + case 11245: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5741000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1739000.0d); + return true; + default: + break; + } + break; + case 11246: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -91.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6726000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11247: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.8000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.6000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7710000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11248: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000018d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8760000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1739000.0d); + return true; + default: + break; + } + break; + case 11249: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.8000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 9678000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11250: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000026d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2822000.0d); + return true; + default: + break; + } + break; + case 11251: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.6000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1247000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2822000.0d); + return true; + default: + break; + } + break; + case 11252: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.6500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000022d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2329000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2887000.0d); + return true; + default: + break; + } + break; + case 11253: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3773000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2756000.0d); + return true; + default: + break; + } + break; + case 11254: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.8500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4757000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11255: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.3000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00002d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5741000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2756000.0d); + return true; + default: + break; + } + break; + case 11256: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 33.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.4000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000023d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6726000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11257: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.9500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000019d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7710000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2756000.0d); + return true; + default: + break; + } + break; + case 11258: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.1000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000017d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8694000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2756000.0d); + return true; + default: + break; + } + break; + case 11259: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000016d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 9678000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11260: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000015d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3773000.0d); + return true; + default: + break; + } + break; + case 11261: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.5500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.1500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000013d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1247000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3773000.0d); + return true; + default: + break; + } + break; + case 11262: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000012d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2395000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3773000.0d); + return true; + default: + break; + } + break; + case 11263: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.9000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00001d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3642000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3839000.0d); + return true; + default: + break; + } + break; + case 11264: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.2000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000029d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1804000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 755000.0d); + return true; + default: + break; + } + break; + case 11265: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -89.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000029d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2822000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 492000.0d); + return true; + default: + break; + } + break; + case 11287: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11288: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11289: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11290: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11291: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11292: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11293: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 29.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11294: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11295: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11301: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 250000.0d); + return true; + default: + break; + } + break; + case 11332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 900000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 250000.0d); + return true; + default: + break; + } + break; + case 11340: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 27.5d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.65d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.9d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 200000.0d); + return true; + default: + break; + } + break; + case 11342: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000209d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 175000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11343: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000209d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 574146.9816d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11344: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.8d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000252d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11345: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.1500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.8d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000252d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656167.979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11346: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.8d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000188d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11347: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.8d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000188d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656167.979d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11348: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 48.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -114.45d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 320.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", -40.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.000142d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 150000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 150000.0d); + return true; + default: + break; + } + break; + case 11349: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 48.4000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -114.45d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 320.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", -40.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.000142d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 492125.9843d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 492125.9843d); + return true; + default: + break; + } + break; + case 11350: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 47.0500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -104.65d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 31.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 31.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.000105d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 200000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 225000.0d); + return true; + default: + break; + } + break; + case 11351: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 47.0500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -104.65d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 31.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 31.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.000105d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 656167.979d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 738188.9764d); + return true; + default: + break; + } + break; + case 11352: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.65d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000126d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11353: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.65d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000126d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.9895d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11354: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.15d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000158d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11355: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.15d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000158d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.9895d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11356: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 48.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -112.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 39.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 39.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.999985d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 50000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 100000.0d); + return true; + default: + break; + } + break; + case 11357: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 48.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -112.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 39.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 39.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.999985d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 164041.9948d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 328083.9895d); + return true; + default: + break; + } + break; + case 11358: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.65d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00011d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 175000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11359: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.65d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00011d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 574146.9816d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11389: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 51.4701102238892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -0.45165658555583d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 359.700932272223d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.000002816d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 7334.818d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 5637.421d); + return true; + default: + break; + } + break; + case 11401: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.2666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.0666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11402: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.6166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11431: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.2666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.0666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 11432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.6166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11501: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11502: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.566666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.2666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11531: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11532: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.566666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.2666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 400000.0d); + return true; + default: + break; + } + break; + case 11601: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.9666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11602: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -85.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.7333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11630: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -85.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.0833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 11632: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -85.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.9333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 11701: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 30.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 31.1666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11702: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 28.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 29.3000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 30.7000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11703: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11731: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 30.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 32.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 31.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11732: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 28.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.7000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.3000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11733: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11801: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -68.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11802: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11831: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -68.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11832: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 900000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11833: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -68.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 984250.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11834: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2952750.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11851: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -67.8750000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11853: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.3750000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11854: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 43.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -69.1250000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 11900: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.3000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.4500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 800000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 11930: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.4500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.3000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket12(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 12001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -71.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.6833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -70.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.2833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.4833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 200000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -71.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.6833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 200000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 750000.0d); + return true; + default: + break; + } + break; + case 12032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -70.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.4833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.2833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -83.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999942857d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999909091d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999909091d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12141: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.7833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.0833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.4833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 8000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12142: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.3166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.3666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.7000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.1833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12143: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.3666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.1000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12150: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 45.3091666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -86.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 337.25556d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 337.25556d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.9996d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 2546731.496d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", -4354009.816d); + return true; + default: + break; + } + break; + case 12201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 46.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.0333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 48.6333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -94.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.6166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.0500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -94.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.216666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 46.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.6333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 800000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 100000.0d); + return true; + default: + break; + } + break; + case 12232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -94.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.0500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.6166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 800000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 100000.0d); + return true; + default: + break; + } + break; + case 12233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -94.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.216666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 43.7833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 800000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 100000.0d); + return true; + default: + break; + } + break; + case 12234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 46.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.1000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.6333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2624666.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 328083.3333d); + return true; + default: + break; + } + break; + case 12235: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -94.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.0500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.6166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2624666.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 328083.3333d); + return true; + default: + break; + } + break; + case 12236: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -94.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.216666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 43.7833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2624666.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 328083.3333d); + return true; + default: + break; + } + break; + case 12301: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12401: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 35.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12402: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 35.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12403: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -94.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12431: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 35.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 35.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -92.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12433: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -94.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 850000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12501: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -109.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.7166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.8500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12502: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -109.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.4500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12503: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -109.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 46.4000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.866666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12530: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -109.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12601: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.8500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.816666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12602: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -99.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.2833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12630: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 12701: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12702: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -116.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12703: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12731: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 8000000.0d); + return true; + default: + break; + } + break; + case 12732: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -116.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 6000000.0d); + return true; + default: + break; + } + break; + case 12733: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 800000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4000000.0d); + return true; + default: + break; + } + break; + case 12800: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12830: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12900: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 12930: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket13(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 13001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -104.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999909091d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -106.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13003: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999916667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -104.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999909091d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 165000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -106.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13033: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999916667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 830000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.5833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -78.5833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13132: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.5833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13133: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -78.5833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 350000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13134: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.0333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 300000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13200: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13230: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.1666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 609601.22d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13301: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 48.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 46.1833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.4833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.7333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.4333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.4833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.1833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13401: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -82.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.7000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13402: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -82.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.7333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13431: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -82.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.4333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -82.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.0333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13433: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -82.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.4333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13434: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -82.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.0333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13501: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 35.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.566666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.7666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13502: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.9333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.2333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13531: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 35.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.566666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13532: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.2333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13601: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13602: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13631: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 46.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13632: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13633: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13701: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.9500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13731: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.9500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.8833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13732: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13800: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.0833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999938d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13830: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.0833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 13901: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.9666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13902: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 32.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 13930: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 609600.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket14(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 14001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.4166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.6833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.4000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.6833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.4166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.4000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.8333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14130: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -86.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.4166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.2500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -101.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.6500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.1833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -97.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 32.1333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.9666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 29.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.1166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 31.8833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14204: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 27.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 28.3833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 30.2833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14205: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 26.1666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 27.8333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -101.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.1833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.6500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 200000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 14232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.1333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2000000.0d); + return true; + default: + break; + } + break; + case 14233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 29.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 31.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 30.1166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3000000.0d); + return true; + default: + break; + } + break; + case 14234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 27.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.2833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 28.3833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4000000.0d); + return true; + default: + break; + } + break; + case 14235: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 300000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5000000.0d); + return true; + default: + break; + } + break; + case 14251: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.4166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.9166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 14252: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.4166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.9166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3000000.0d); + return true; + default: + break; + } + break; + case 14253: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 18.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5000000.0d); + return true; + default: + break; + } + break; + case 14254: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 18.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6000000.0d); + return true; + default: + break; + } + break; + case 14301: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.7166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.7833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.0166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.6500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14303: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.216666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.3500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 14332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.6500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.0166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2000000.0d); + return true; + default: + break; + } + break; + case 14333: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.3500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.216666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3000000.0d); + return true; + default: + break; + } + break; + case 14400: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999964286d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14430: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999964286d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14501: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -78.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.0333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.2000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14502: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -78.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.9666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14531: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -78.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.2000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2000000.0d); + return true; + default: + break; + } + break; + case 14532: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -78.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.7666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 14601: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 48.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14602: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14631: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.7333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14632: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.8333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14701: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.2500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14702: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.4833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.8833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14731: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14732: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.4833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14735: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14736: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.4833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14801: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.566666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.7666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14802: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14803: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.7333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.0666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14811: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -4500000.0d); + return true; + default: + break; + } + break; + case 14831: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 46.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.566666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14832: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.2500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14833: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.0666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 600000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 14841: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 520000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -4480000.0d); + return true; + default: + break; + } + break; + case 14901: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14902: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14903: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14904: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.083333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14930: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -107.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 200000.0d); + return true; + default: + break; + } + break; + case 14931: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14932: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 14933: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14934: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.083333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 800000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 14935: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.6667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14936: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1312333.3333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 328083.3333d); + return true; + default: + break; + } + break; + case 14937: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -108.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1968500.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 14938: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.083333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2624666.6667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 328083.3333d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket15(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 15001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 57.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -133.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 323.130102361111d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 323.130102361111d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 16404166.67d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", -16404166.67d); + return true; + default: + break; + } + break; + case 15002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -142.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15003: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -146.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15004: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15005: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -154.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15006: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -158.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15007: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15008: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -166.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15009: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -170.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15010: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 51.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -176.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 53.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.8333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15020: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 50.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -154.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 55.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 65.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15021: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 50.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -154.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 55.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 65.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 57.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", -133.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 323.130102361111d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 323.130102361111d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 15032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -142.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15033: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -146.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15034: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15035: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -154.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15036: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -158.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15037: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15038: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -166.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15039: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 54.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -170.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15040: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 51.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -176.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 53.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.8333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 18.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -155.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 20.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -156.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -158.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15104: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -159.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15105: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -160.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 18.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -155.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15132: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 20.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -156.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15133: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -158.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15134: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -159.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15135: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -160.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15138: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 21.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -158.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.6667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 17.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -66.4333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 18.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 18.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 17.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -66.4333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 18.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 18.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 100000.0d); + return true; + default: + break; + } + break; + case 15230: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 17.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -66.4333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 18.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 18.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 200000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 200000.0d); + return true; + default: + break; + } + break; + case 15297: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3280833.3333d); + return true; + default: + break; + } + break; + case 15298: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.6500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.0166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6561666.6667d); + return true; + default: + break; + } + break; + case 15299: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.3500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.216666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 9842500.0d); + return true; + default: + break; + } + break; + case 15302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -86.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.4166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 100000.0d); + return true; + default: + break; + } + break; + case 15303: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.9666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15304: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -110.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15305: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.916666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15306: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -113.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15307: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6561666.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1640416.667d); + return true; + default: + break; + } + break; + case 15308: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -122.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6561666.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1640416.667d); + return true; + default: + break; + } + break; + case 15309: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.0666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6561666.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1640416.667d); + return true; + default: + break; + } + break; + case 15310: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 35.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6561666.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1640416.667d); + return true; + default: + break; + } + break; + case 15311: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -118.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.466666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6561666.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1640416.667d); + return true; + default: + break; + } + break; + case 15312: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 32.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -116.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.7833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6561666.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1640416.667d); + return true; + default: + break; + } + break; + case 15313: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 15314: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.7500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.4500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 15315: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -105.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.4333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.2333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 15316: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -72.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.866666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.2000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 15317: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.416666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15318: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15319: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -82.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15320: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 29.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.7500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.5833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15321: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -82.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15322: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2296583.333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15323: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -112.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999947368d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15324: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999947368d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15325: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999933333d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2624666.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15328: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.9666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15329: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -85.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.9333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1640416.667d); + return true; + default: + break; + } + break; + case 15330: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.4500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.3000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1312333.333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -71.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.6833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 656166.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2460625.0d); + return true; + default: + break; + } + break; + case 15332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -70.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.4833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 41.2833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15333: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.7833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.0833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.4833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 26246719.16d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15334: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.3166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.3666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.7000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.1833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 19685039.37d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15335: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.3666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.1000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 13123359.58d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15336: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 984250.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15337: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2296583.333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15338: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -109.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968503.937d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15339: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -104.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999909091d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 541337.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15340: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -106.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15341: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -107.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999916667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2723091.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15342: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 492125.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15343: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.5833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 820208.333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15344: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -78.5833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1148291.667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15345: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.0333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 984250.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15346: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -79.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.1666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15347: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.7333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.4333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968503.937d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15348: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.4833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.1833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968503.937d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15349: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 35.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.566666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15350: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 33.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.2333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15351: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 46.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 8202099.738d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15352: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4921259.843d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15353: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.9500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.8833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15354: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -77.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15355: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15356: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -86.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.4166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 35.2500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15357: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -101.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.1833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.6500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 656166.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3280833.333d); + return true; + default: + break; + } + break; + case 15358: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 31.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 33.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 32.1333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6561666.667d); + return true; + default: + break; + } + break; + case 15359: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 29.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 31.8833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 30.1166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2296583.333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 9842500.0d); + return true; + default: + break; + } + break; + case 15360: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 27.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.2833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 28.3833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 13123333.333d); + return true; + default: + break; + } + break; + case 15361: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 984250.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 16404166.667d); + return true; + default: + break; + } + break; + case 15362: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640419.948d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3280839.895d); + return true; + default: + break; + } + break; + case 15363: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 40.6500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 39.0166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640419.948d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6561679.79d); + return true; + default: + break; + } + break; + case 15364: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -111.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.3500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.216666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640419.948d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 9842519.685d); + return true; + default: + break; + } + break; + case 15365: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 37.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -78.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.2000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.0333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 11482916.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6561666.667d); + return true; + default: + break; + } + break; + case 15366: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -78.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.9666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 36.7666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 11482916.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3280833.333d); + return true; + default: + break; + } + break; + case 15367: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.7333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15368: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.8333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15369: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 46.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.566666666667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15370: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.2500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15371: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.0666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.7333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15372: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -85.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 820208.333d); + return true; + default: + break; + } + break; + case 15373: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2952750.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 820208.333d); + return true; + default: + break; + } + break; + case 15374: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1312335.958d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15375: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -85.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 37.0833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.6666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4921250.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3280833.333d); + return true; + default: + break; + } + break; + case 15376: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -14.2666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -170.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 312234.65d); + return true; + default: + break; + } + break; + case 15377: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 41.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.2666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.0666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4921250.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3280833.3333d); + return true; + default: + break; + } + break; + case 15378: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -93.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.6166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15379: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 38.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 39.7833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 38.7166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1312333.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15380: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -98.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 38.566666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 37.2666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1312333.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1312333.3333d); + return true; + default: + break; + } + break; + case 15381: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 656166.6667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 26246666.6667d); + return true; + default: + break; + } + break; + case 15382: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -116.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.6667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 19685000.0d); + return true; + default: + break; + } + break; + case 15383: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.7500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -118.583333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2624666.6667d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 13123333.3333d); + return true; + default: + break; + } + break; + case 15384: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 492125.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15385: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 34.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 36.2333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 34.9333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1312333.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15386: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 32.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 34.7666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.3000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1312333.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1312333.3333d); + return true; + default: + break; + } + break; + case 15387: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -88.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 984250.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15388: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999941177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2296583.3333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15389: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 42.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.666666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999966667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 984250.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15390: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 41.0833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999375d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 328083.3333d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15391: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 30.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -92.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 32.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 31.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3280833.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15392: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 28.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.7000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 29.3000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3280833.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15393: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 27.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 26.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3280833.3333d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15394: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.6833333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.4166666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15395: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.3333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.4000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.8333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1968500.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15396: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 39.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 40.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1640416.6667d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 15397: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.568977d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -84.455955d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.122774d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 49.01518d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 15398: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.568977d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -83.248627d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.122774d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 49.01518d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 15399: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 9.54670833333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 138.168744444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 60000.0d); + return true; + default: + break; + } + break; + case 15400: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 13.4724663527781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 144.748750705556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 50000.0d); + return true; + default: + break; + } + break; + case 15914: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15915: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15916: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 15917: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1640416.67d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket16(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 16000: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Initial longitude", -180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Zone width", 6.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16003: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16004: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16005: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16006: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16007: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16008: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16009: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16010: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16011: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16012: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16013: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16014: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16015: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16016: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16017: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16018: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16019: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16020: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16021: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16022: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16023: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16024: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16025: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16026: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16027: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16028: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16029: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16030: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16033: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16034: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16035: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16036: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16037: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16038: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16039: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16040: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16041: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16042: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16043: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16044: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16045: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16046: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16047: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16048: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16049: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16050: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16051: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16052: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16053: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16054: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16055: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16056: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16057: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16058: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16059: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16060: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16061: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 16065: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16070: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16071: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 41500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16072: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 126.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 42500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16073: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 43500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16074: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 44500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16075: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 45500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16076: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 138.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 46500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16077: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 47500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16078: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 144.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 48500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16079: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 49500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16080: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16081: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 51500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16082: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 156.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 52500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16083: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 53500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16084: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 54500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16085: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 55500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16086: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 56500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16087: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 57500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16088: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 58500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16089: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 59500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16091: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 61500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16092: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 62500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16093: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 63500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16094: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 64500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16099: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16100: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Initial longitude", -180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Zone width", 6.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16104: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16105: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16106: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16107: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16108: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16109: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16110: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16111: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16112: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16113: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16114: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16115: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16116: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16117: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16118: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16119: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16120: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16121: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16122: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16123: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16124: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16125: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16126: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16127: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16128: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16129: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16130: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16132: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16133: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16134: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16135: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16136: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16137: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16138: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16139: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16140: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16141: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16142: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16143: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16144: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16145: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16146: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16147: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16148: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16149: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16150: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16151: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16152: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16153: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16154: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16155: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16156: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16157: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16158: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16159: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16160: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16161: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2000000.0d); + return true; + default: + break; + } + break; + case 16170: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16172: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 126.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16174: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16176: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 138.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16178: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 144.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16180: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16182: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 156.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16184: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16186: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16188: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16190: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16192: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16194: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16204: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16205: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16206: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16207: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16208: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16209: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 9500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16210: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 10500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16211: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 11500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16212: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 12500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16213: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 13500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16214: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 14500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16215: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 15500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16216: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 16500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16217: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 17500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16218: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 18500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16219: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 19500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16220: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16221: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 21500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16222: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 22500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16223: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 23500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16224: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 24500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16225: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 25500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16226: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 26500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16227: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 27500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16228: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 28500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16229: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 29500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16230: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 31500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 32500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16261: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16262: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16263: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16264: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16265: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16266: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16267: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16268: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16269: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 9500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16270: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 10500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16271: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 11500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16272: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 12500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16273: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 13500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16274: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 14500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16275: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 15500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16276: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 16500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16277: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 17500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16278: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 18500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16279: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 19500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16280: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 60.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16281: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 21500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16282: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 22500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16283: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 23500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16284: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 72.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 24500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16285: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 25500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16286: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 78.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 26500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16287: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 27500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16288: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 28500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16289: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 29500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16290: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16291: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 31500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16292: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 32500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16293: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 33500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16294: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 34500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16295: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 35500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16296: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 108.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 36500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16297: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 37500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16298: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 38500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16299: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 39500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16302: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16304: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16305: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16306: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16307: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16308: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16309: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16310: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16311: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16312: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16313: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16314: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16315: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16316: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16317: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16318: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16319: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16320: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16321: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16322: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16323: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16324: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16325: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16326: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16327: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16328: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16329: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16330: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16368: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16370: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16372: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16374: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16376: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16378: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16380: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 60.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16382: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16384: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 72.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16386: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 78.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16388: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16390: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16392: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16394: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16396: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 108.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16398: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16400: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16405: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 5.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16406: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16411: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16412: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16413: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16430: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16490: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16506: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 106.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16586: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 106.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16611: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16612: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16636: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16709: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 109.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16716: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 116.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16732: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 16907: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16908: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16909: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 9250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16910: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 10250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16911: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 11250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16912: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 12250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16913: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 13250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16914: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 14250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16915: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 15250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16916: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 16250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16917: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 17250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16918: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 18250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16919: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 19250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16920: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 60.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 20250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16921: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 21250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16922: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 22250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16923: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 23250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16924: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 72.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 24250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16925: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 25250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16926: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 78.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 26250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16927: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 27250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16928: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 28250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16929: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 29250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16930: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16931: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 31250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16932: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 32250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16933: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 33250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16934: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 34250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16935: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 35250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16936: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 108.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 36250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16937: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 37250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16938: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 38250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16939: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 39250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16940: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16941: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 41250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16942: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 126.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 42250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16943: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 43250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16944: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 44250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16945: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 45250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16946: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 138.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 46250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16947: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 47250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16948: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 144.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 48250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16949: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 49250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16950: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16951: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 51250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16952: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 156.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 52250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16953: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 53250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16954: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 54250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16955: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 55250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16956: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 56250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16957: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 57250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16958: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 58250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16959: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 59250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16960: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 60250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16961: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 61250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16962: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 62250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16963: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 63250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 16964: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 64250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket17(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 17001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17005: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -5.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17054: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17107: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17108: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17109: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17110: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17111: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17112: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 36.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17113: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 39.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17114: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17115: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17116: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17117: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17118: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17119: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17120: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 60.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17121: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17122: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17123: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17124: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 72.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17125: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17126: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 78.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17127: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17128: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17129: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17130: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17132: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17133: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17134: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17135: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17136: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 108.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17137: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17138: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17139: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17140: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17141: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17142: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 126.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17143: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17144: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17145: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17146: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 138.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17147: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17148: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 144.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17149: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17150: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17151: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17152: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 156.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17153: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17154: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17155: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17156: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17157: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17158: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17159: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17160: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17161: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -177.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17162: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17163: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17164: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17204: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -60.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -63.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17205: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -60.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -63.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17206: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -60.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -63.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17207: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17208: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17209: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17210: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17211: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17212: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17213: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 78.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17214: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17215: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17216: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17217: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 126.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17218: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 138.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17219: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17220: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -64.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -67.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17221: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17222: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17223: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -78.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17224: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17225: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17226: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17227: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17228: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17229: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17230: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 42.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 78.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17235: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 102.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17236: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17237: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 126.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17238: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 138.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17239: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17240: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17241: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 174.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -71.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17242: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17243: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17244: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17245: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17246: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17247: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17248: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17249: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17250: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17251: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17252: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17253: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17254: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17255: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 99.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17256: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17257: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17258: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17259: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 171.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -72.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -75.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17260: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17261: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -144.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17262: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17263: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17264: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -72.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17265: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17266: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17267: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17268: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17269: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17270: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 72.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17271: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17272: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17273: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 144.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17274: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 168.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17275: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17276: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17277: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17278: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17279: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17280: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17281: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17282: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17283: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 75.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17284: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17285: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17286: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17287: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17288: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17289: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17290: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17291: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17292: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17293: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -80.2386111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17294: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -78.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 162.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17295: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 180.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17296: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -150.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17297: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17298: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -40.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17299: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17300: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17321: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17322: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17323: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17324: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17325: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17326: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 14.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17327: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17328: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17329: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17330: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17331: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17332: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.2500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17333: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17334: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.3082777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17335: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.5582777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17336: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0582777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17337: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.3082777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17338: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 22.5582777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17339: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.3062500000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000006d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500025.141d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -667.282d); + return true; + default: + break; + } + break; + case 17340: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.5562666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000058d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500044.695d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -667.13d); + return true; + default: + break; + } + break; + case 17341: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.8062845294447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.00000561024d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500064.274d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -667.711d); + return true; + default: + break; + } + break; + case 17342: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0563000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000054d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500083.521d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -668.844d); + return true; + default: + break; + } + break; + case 17343: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.3063166666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000052d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500102.765d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -670.706d); + return true; + default: + break; + } + break; + case 17344: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 22.5563333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000049d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500121.846d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -672.557d); + return true; + default: + break; + } + break; + case 17348: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 105.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17349: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17350: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17351: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17352: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17353: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17354: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17355: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17356: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17357: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17358: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17359: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -32.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -28.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -36.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2000000.0d); + return true; + default: + break; + } + break; + case 17360: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 145.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -36.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -38.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 17361: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 145.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -36.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -38.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 2500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2500000.0d); + return true; + default: + break; + } + break; + case 17362: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 134.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -18.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -36.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17363: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -28.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 17364: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -33.25d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -30.75d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -35.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 9300000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4500000.0d); + return true; + default: + break; + } + break; + case 17365: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 132.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -18.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -36.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 17412: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17414: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 14.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17416: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17418: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17420: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17422: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 22.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17424: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17426: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 26.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17428: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17430: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 94.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17433: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 97.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17434: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17435: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 103.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17436: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 106.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17437: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 109.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17438: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 112.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17439: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 115.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17440: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 118.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17441: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 121.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17442: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 124.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17443: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17444: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 130.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17445: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 133.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17446: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 136.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17447: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 139.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1500000.0d); + return true; + default: + break; + } + break; + case 17449: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17450: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17451: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17452: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17453: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 135.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17454: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 141.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17455: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 147.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17456: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 153.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17457: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 159.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17458: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 165.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17515: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17517: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17519: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17521: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17523: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17525: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17527: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17529: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 29.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17531: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17533: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17611: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17613: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17615: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17617: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17619: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17621: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17623: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17625: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -22.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17701: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -53.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17702: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -56.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17703: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -58.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17704: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -61.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17705: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -64.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17706: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -67.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17707: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17708: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -73.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17709: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17710: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -79.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17711: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -82.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17712: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17713: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -84.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17714: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -87.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17715: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17716: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -93.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17717: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -96.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 304800.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17722: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -111.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17723: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17724: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17726: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -120.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17771: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 8.5d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 5621452.02d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 5990638.423d); + return true; + default: + break; + } + break; + case 17772: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 3714266.977d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 3402016.506d); + return true; + default: + break; + } + break; + case 17773: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 94.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 4340913.848d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 4812712.923d); + return true; + default: + break; + } + break; + case 17774: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 5837287.82d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 2121415.696d); + return true; + default: + break; + } + break; + case 17775: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -97.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 8264722.177d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 4867518.353d); + return true; + default: + break; + } + break; + case 17776: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -19.5d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 131.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 6988408.536d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 7654884.537d); + return true; + default: + break; + } + break; + case 17777: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -14.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -60.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 7257179.236d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 5592024.446d); + return true; + default: + break; + } + break; + case 17794: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -61.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17795: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -64.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17801: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 33.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17802: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 33.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 131.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17803: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 132.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17804: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 33.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 133.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17805: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 134.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17806: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 136.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17807: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 137.166666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17808: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 138.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17809: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 36.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 139.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17810: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 140.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17811: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 140.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17812: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 142.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17813: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 144.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17814: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 142.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17815: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17816: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 124.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17817: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 131.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17818: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 20.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 136.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17819: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 154.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 17901: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -36.8798652777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.764339361111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17902: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -37.7612498055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 176.46619725d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17903: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -38.624702777778d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.885636277778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17904: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -39.6509293055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 176.673680527778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17905: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -39.1357583055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.22801175d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17906: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -39.5124703888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 175.640036805556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17907: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -40.2419471388892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 175.488099611111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17908: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -40.9255326388892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 175.647349666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17909: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.3013196388892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.776623111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17910: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -40.7147590555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 172.6720465d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17911: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.2745447222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.299316805556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17912: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.289911527778d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 172.109028194445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17913: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.8108028611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.581260055556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17914: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -42.3336942777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.549771305556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17915: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -42.6891165833336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.010133388889d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17916: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.5444866666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.802074111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17917: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -42.8863223611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.9799935d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17918: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.1101281388892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.260925833334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17919: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.9778028888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.606267d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17920: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.5906375833336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 172.727193583334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17921: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.7487115555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.360748472222d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17922: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -44.4022203611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.057250833334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17923: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -44.7352679722225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 169.467755083334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17924: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.1329025833336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.398641194445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17925: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.5637261666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 167.738861777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17926: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.8161966111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.628595166667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17927: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.8615133611114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.282589111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 17928: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -46.6000096111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.342872d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300002.66d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 699999.58d); + return true; + default: + break; + } + break; + case 17931: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -36.8797222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.764166666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17932: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -37.7611111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 176.466111111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17933: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -38.6244444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 177.885555555556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17934: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -39.6508333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 176.673611111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17935: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -39.1355555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.227777777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17936: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -39.5122222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 175.64d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17937: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -40.2419444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 175.488055555556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17938: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -40.9252777777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 175.647222222222d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17939: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.3011111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 174.776388888889d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17940: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -40.7147222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 172.671944444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17941: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.2744444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.299166666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17942: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.2897222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 172.108888888889d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17943: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.8105555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.581111111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17944: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -42.3336111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.549722222222d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17945: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -42.6888888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.01d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17946: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.5444444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.801944444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17947: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -42.8861111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.979722222223d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17948: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.1100000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.260833333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17949: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.9777777777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.606111111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17950: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.5905555555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 172.726944444445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17951: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -43.7486111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.360555555556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17952: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -44.4019444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.057222222223d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17953: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -44.7350000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 169.4675d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17954: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.1327777777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.398611111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17955: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.5636111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 167.738611111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17956: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.8161111111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.628333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17957: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -45.8613888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 170.2825d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17958: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -46.6000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 168.342777777778d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17959: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -176.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 17960: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 166.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17961: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 169.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17962: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 179.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17963: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -178.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17964: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 173.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -37.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -44.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 3000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 7000000.0d); + return true; + default: + break; + } + break; + case 17965: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -176.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 17966: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 157.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -76.666666666667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -79.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket18(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 18001: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18002: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18003: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 34.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18004: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18005: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18006: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18007: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18008: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 450000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18009: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 750000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 18011: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999625544d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 18012: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999625769d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 18021: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999625544d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500135.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300090.0d); + return true; + default: + break; + } + break; + case 18022: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999625769d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500135.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300090.0d); + return true; + default: + break; + } + break; + case 18031: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -72.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18032: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -69.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18033: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18034: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18035: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -60.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18036: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -57.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18037: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18041: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18042: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18043: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 34.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18044: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18045: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 450000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18046: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 750000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18047: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 150000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18048: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 450000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18049: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 34.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 750000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18051: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5990472222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -77.0809166666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18052: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5990472222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.0809166666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18053: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5990472222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.0809166666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18054: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5990472222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -68.0809166666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18055: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.59620041666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -80.0775079166669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18056: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.59620041666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -77.0775079166669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18057: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.59620041666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.0775079166669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18058: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.59620041666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.0775079166669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18059: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.59620041666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -68.0775079166669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18063: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 22.3500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -81.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 23.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 21.7000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 280296.016d); + return true; + default: + break; + } + break; + case 18064: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 20.7166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -76.8333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 21.3000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 20.1333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 229126.939d); + return true; + default: + break; + } + break; + case 18065: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5962032222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -80.0775077694447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18066: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5962032222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -77.0775077694447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18067: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5962032222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -74.0775077694447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18068: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5962032222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.0775077694447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18069: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.5962032222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -68.0775077694447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18071: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1100000.0d); + return true; + default: + break; + } + break; + case 18072: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 615000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 810000.0d); + return true; + default: + break; + } + break; + case 18073: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 18074: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1200000.0d); + return true; + default: + break; + } + break; + case 18081: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 55.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999877341d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1200000.0d); + return true; + default: + break; + } + break; + case 18082: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99987742d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2200000.0d); + return true; + default: + break; + } + break; + case 18083: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 49.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999877499d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 3200000.0d); + return true; + default: + break; + } + break; + case 18084: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.85d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99994471d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 234.358d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4185861.369d); + return true; + default: + break; + } + break; + case 18085: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 46.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6600000.0d); + return true; + default: + break; + } + break; + case 18091: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 55.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999877341d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 18092: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99987742d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 18093: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 49.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999877499d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 200000.0d); + return true; + default: + break; + } + break; + case 18094: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.85d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99994471d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 234.358d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 185861.369d); + return true; + default: + break; + } + break; + case 18101: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 41.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 42.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1200000.0d); + return true; + default: + break; + } + break; + case 18102: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 43.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 43.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2200000.0d); + return true; + default: + break; + } + break; + case 18103: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 43.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 44.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3200000.0d); + return true; + default: + break; + } + break; + case 18104: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 45.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 4200000.0d); + return true; + default: + break; + } + break; + case 18105: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 46.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5200000.0d); + return true; + default: + break; + } + break; + case 18106: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 46.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 47.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6200000.0d); + return true; + default: + break; + } + break; + case 18107: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 48.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 47.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 48.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 7200000.0d); + return true; + default: + break; + } + break; + case 18108: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 49.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 48.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 49.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 8200000.0d); + return true; + default: + break; + } + break; + case 18109: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 50.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 3.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.25d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 50.75d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1700000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 9200000.0d); + return true; + default: + break; + } + break; + case 18110: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 68.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99846154d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2355500.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 2590000.0d); + return true; + default: + break; + } + break; + case 18111: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 68.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18112: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18113: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18114: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 19.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 80.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18116: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 12.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 80.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18121: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18122: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2520000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18131: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999625769d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 18132: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 33.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999615596d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 18134: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999616304d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400000.0d); + return true; + default: + break; + } + break; + case 18135: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 25.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999616437d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400000.0d); + return true; + default: + break; + } + break; + case 18141: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -39.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 175.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400000.0d); + return true; + default: + break; + } + break; + case 18142: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 171.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 18151: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 4.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 230738.26d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18152: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 8.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 670553.98d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18153: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1110369.7d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18161: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -6.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -80.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99983008d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 222000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1426834.743d); + return true; + default: + break; + } + break; + case 18162: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -9.50000000000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -76.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99932994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 720000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1039979.159d); + return true; + default: + break; + } + break; + case 18163: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -9.50000000000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -70.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99952992d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1324000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1040084.558d); + return true; + default: + break; + } + break; + case 18171: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 117.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18172: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 119.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18173: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 121.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18174: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 123.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18175: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 125.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18180: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18181: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999625544d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 18182: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 37.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999625769d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 18183: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18184: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 20.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18185: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18186: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 22.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18187: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18188: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18189: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18190: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 26.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18191: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18192: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18193: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18194: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18195: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18196: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 28.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18197: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 29.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18198: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 30.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18199: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 31.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18201: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.7340969444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.2120805555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 170251.555d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 126867.909d); + return true; + default: + break; + } + break; + case 18202: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.7340969444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.2120805555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 170251.555d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1126867.909d); + return true; + default: + break; + } + break; + case 18203: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.7340969444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.2120805555558d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 170251.555d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1126867.909d); + return true; + default: + break; + } + break; + case 18204: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 31.7343936111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 35.2045169444447d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0000067d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 219529.584d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 626907.39d); + return true; + default: + break; + } + break; + case 18205: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 33.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18211: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 16.8166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99992226d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 292209.579d); + return true; + default: + break; + } + break; + case 18212: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 14.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -90.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99989906d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 325992.681d); + return true; + default: + break; + } + break; + case 18221: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -4.66666666666695d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18222: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.33333333333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18223: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18224: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 2.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18225: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 6.16666666666694d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18226: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18227: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 14.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18228: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 58.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18231: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 68.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743195.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914398.5d); + return true; + default: + break; + } + break; + case 18232: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743195.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914398.5d); + return true; + default: + break; + } + break; + case 18233: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 19.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 80.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743195.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914398.5d); + return true; + default: + break; + } + break; + case 18234: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 12.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 80.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743195.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914398.5d); + return true; + default: + break; + } + break; + case 18235: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743195.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914398.5d); + return true; + default: + break; + } + break; + case 18236: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 68.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743196.4d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914398.8d); + return true; + default: + break; + } + break; + case 18237: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 74.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743196.4d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914398.8d); + return true; + default: + break; + } + break; + case 18238: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 26.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 90.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99878641d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2743185.69d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 914395.23d); + return true; + default: + break; + } + break; + case 18240: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18241: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18242: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18243: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18244: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18245: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18246: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18247: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18248: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18251: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 129.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 18252: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 127.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 18253: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 125.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 18260: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.6056177777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -52684.972d); + return true; + default: + break; + } + break; + case 18261: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.6056177777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 147315.028d); + return true; + default: + break; + } + break; + case 18262: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.6056177777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 447315.028d); + return true; + default: + break; + } + break; + case 18263: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -71.6056177777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", -17044.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -23139.97d); + return true; + default: + break; + } + break; + case 18275: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18276: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18277: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18278: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18280: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 50.6250000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4637000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 5467000.0d); + return true; + default: + break; + } + break; + case 18282: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.0019444444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.5027777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4603000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 5806000.0d); + return true; + default: + break; + } + break; + case 18283: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.5833333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.0083333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3501000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 5999000.0d); + return true; + default: + break; + } + break; + case 18284: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 51.6708333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.6722222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3703000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 5627000.0d); + return true; + default: + break; + } + break; + case 18285: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.9583333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999983d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 237000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -4700000.0d); + return true; + default: + break; + } + break; + case 18286: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.1666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.1666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999714d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 18300: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9993d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5300000.0d); + return true; + default: + break; + } + break; + case 18305: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999923d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 5500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18306: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999923d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 6500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18307: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999923d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 7500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18308: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999923d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 8500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18310: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18311: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 11.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18312: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18313: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18314: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18315: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18316: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18317: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 23.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18318: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18319: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 17.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9965d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18401: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 9.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18402: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18403: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 900000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18415: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 18425: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 70.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18426: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 67.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -32.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18427: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 64.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -40.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18428: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 61.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18432: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 79.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -64.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18433: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 76.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -64.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18434: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 73.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -52.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18435: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 70.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -52.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18436: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 67.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -52.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18437: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 64.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -52.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18441: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.11666666666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 41.5333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18442: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.11666666666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 44.5333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18443: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.11666666666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 47.5333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18444: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.11666666666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 50.5333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18446: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.13333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 50.7666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18447: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.13333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 53.7666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18448: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.13333333333361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 56.7666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 4300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18450: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.10000000000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 21.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18451: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.10000000000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 18452: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.10000000000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 27.9500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + private static bool TryGetConversionParameterBucket19(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter) + { + switch (conversionCode) + { + case 19838: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 53.3158204722225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 53.1301023611114d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 5000000.0d); + return true; + default: + break; + } + break; + case 19839: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 55.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19840: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", 75.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19841: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 46.9524055555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 7.43958333333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 90.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 90.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 0.0d); + return true; + default: + break; + } + break; + case 19842: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", 71.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19843: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19844: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -70.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 50.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 800000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 19845: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -5000000.0d); + return true; + default: + break; + } + break; + case 19848: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -25.0685526111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -130.112967111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 14200.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 15500.0d); + return true; + default: + break; + } + break; + case 19849: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -64.7500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 550000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 19851: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 16.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19852: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 16.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 45.9166666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 43.0833333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 19853: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6682583333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.13310833333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19854: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -55.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -37.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -54.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -54.7500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 19856: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -21.1166666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 55.5333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 160000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50000.0d); + return true; + default: + break; + } + break; + case 19857: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -112.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 62.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 70.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 19858: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 59.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -132.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 61.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 68.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 19859: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -17.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 178.75d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99985d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 4000000.0d); + return true; + default: + break; + } + break; + case 19860: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 18.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -77.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 750000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 650000.0d); + return true; + default: + break; + } + break; + case 19861: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", -21.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 49.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 21.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.9995d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 19862: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 50.7978150000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 4.35921583333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 150328.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 166262.0d); + return true; + default: + break; + } + break; + case 19863: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 21.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 114.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 18.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 24.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 19864: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 1.36666666666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 103.833333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 28001.642d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 38744.572d); + return true; + default: + break; + } + break; + case 19865: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", 70.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", -45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19866: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -70.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19869: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 30.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19870: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 62.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 19871: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 102.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 323.0257905d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 323.130102361111d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19872: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 102.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 323.0257905d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 323.130102361111d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 804670.24d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19873: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -22.2696917500003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 166.44242575d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -22.2446917500003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -22.2946917500003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.66d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1.02d); + return true; + default: + break; + } + break; + case 19874: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -22.2697222222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 166.4425d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -22.2447222222225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -22.2947222222225d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 8.313d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", -2.354d); + return true; + default: + break; + } + break; + case 19875: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -85.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 44.5d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 53.5d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 930000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6430000.0d); + return true; + default: + break; + } + break; + case 19876: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 18.0577900000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999425d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 100178.1808d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -6500614.7836d); + return true; + default: + break; + } + break; + case 19877: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 62.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -9.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 700000.0d); + return true; + default: + break; + } + break; + case 19878: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -16.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 179.333333333334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 1251331.8d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 1662888.5d); + return true; + default: + break; + } + break; + case 19879: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -18.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 178.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 544000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 704000.0d); + return true; + default: + break; + } + break; + case 19881: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9992d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19882: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9992d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19883: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19884: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 42.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19885: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.97254365833361d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.295241669445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 13227.851d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 8739.894d); + return true; + default: + break; + } + break; + case 19886: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.8590630222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.815410586111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", -1.769d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 133454.779d); + return true; + default: + break; + } + break; + case 19887: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.96467271388917d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.636371111111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19888: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 5.42151754166694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 100.344376963889d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", -23.414d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 62.283d); + return true; + default: + break; + } + break; + case 19889: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.97628520000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 103.070275625d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 19594.245d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 3371.895d); + return true; + default: + break; + } + break; + case 19890: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 3.68464905000028d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 101.389107913889d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", -34836.161d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 56464.049d); + return true; + default: + break; + } + break; + case 19891: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 3.76938808888917d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 102.368298983334d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", -7368.228d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 6485.858d); + return true; + default: + break; + } + break; + case 19892: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 2.68234763611139d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 101.974905041667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 3673.785d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", -4240.573d); + return true; + default: + break; + } + break; + case 19893: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 2.12167974444472d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 103.427936236111d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", -14810.562d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 8758.32d); + return true; + default: + break; + } + break; + case 19894: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 53.3158099500003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 53.1301023611114d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19895: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 102.25d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 323.025796466667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 323.130102361111d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 804671.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19896: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 22.3121333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.178555555556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 132033.92d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 62565.96d); + return true; + default: + break; + } + break; + case 19897: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 63.390675d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -91.8666666666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 77.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6200000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 3000000.0d); + return true; + default: + break; + } + break; + case 19899: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -20.1950694444447d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 57.5218277777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1000000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1000000.0d); + return true; + default: + break; + } + break; + case 19900: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19901: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 150000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5400000.0d); + return true; + default: + break; + } + break; + case 19902: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 4.3569397222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.8333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 51.1666666666669d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 150000.01256d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5400088.4378d); + return true; + default: + break; + } + break; + case 19903: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 55.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 6.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99950908d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 19904: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.66666666666695d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 274319.51d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19905: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 110.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.997d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 3900000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 900000.0d); + return true; + default: + break; + } + break; + case 19906: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 32.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 45.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9987864078d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 1166200.0d); + return true; + default: + break; + } + break; + case 19907: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 29.0262683333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 46.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9994d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 800000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19909: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 18.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -77.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 550000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400000.0d); + return true; + default: + break; + } + break; + case 19910: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 18.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -77.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 250000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 150000.0d); + return true; + default: + break; + } + break; + case 19911: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", -21.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 49.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 21.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 21.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.9995d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 400000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 800000.0d); + return true; + default: + break; + } + break; + case 19913: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.1561605555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 5.38763888888917d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999079d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19914: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.1561605555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 5.38763888888917d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999079d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 155000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 463000.0d); + return true; + default: + break; + } + break; + case 19916: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 49.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996012717d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -100000.0d); + return true; + default: + break; + } + break; + case 19917: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", -41.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 2510000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 6023150.0d); + return true; + default: + break; + } + break; + case 19919: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 24.4500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 51.216666666667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 19920: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 1.28764666666694d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 103.853002222223d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 30000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 30000.0d); + return true; + default: + break; + } + break; + case 19921: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 40.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9988085293d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 19922: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 46.9524055555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 7.43958333333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 90.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 90.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 600000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 200000.0d); + return true; + default: + break; + } + break; + case 19923: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 46.9524055555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 90.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 90.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 0.0d); + return true; + default: + break; + } + break; + case 19924: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 11.2521786111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -60.6860088888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 187500.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 180000.0d); + return true; + default: + break; + } + break; + case 19925: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.4416666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -61.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 430000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 325000.0d); + return true; + default: + break; + } + break; + case 19926: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 19927: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 45.9000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 25.3924658888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996667d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 19929: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.8082777777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19930: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19931: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 47.1443937222225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 19.0485717777781d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 90.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 90.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99993d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 650000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 200000.0d); + return true; + default: + break; + } + break; + case 19933: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 47.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999912d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 700000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 400000.0d); + return true; + default: + break; + } + break; + case 19934: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19936: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 19937: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 36.5964d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 7.83445d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 270.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 360.0d); + return true; + default: + break; + } + break; + case 19938: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 57.5175539305558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 59.3333333333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 58.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6375000.0d); + return true; + default: + break; + } + break; + case 19939: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19940: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 37.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996256d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 19941: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -54.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 5000000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 19942: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -62.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9995d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19943: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 13.1763888888892d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -59.5597222222225d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999986d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 30000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 75000.0d); + return true; + default: + break; + } + break; + case 19944: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 44.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -68.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 60.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 19945: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -66.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999912d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 19946: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 46.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -66.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999912d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 2500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 7500000.0d); + return true; + default: + break; + } + break; + case 19947: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 47.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 13.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 49.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 46.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 400000.0d); + return true; + default: + break; + } + break; + case 19948: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 34.6500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 37.3500000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996256d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 300000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 300000.0d); + return true; + default: + break; + } + break; + case 19949: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 38.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 43.5d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9995341d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19950: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 46.9524055555558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 7.43958333333361d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 90.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 90.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 1.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 2600000.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 1200000.0d); + return true; + default: + break; + } + break; + case 19951: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 27.5188288055558d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 52.603539166667d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 0.57166119444472d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 0.57166119444472d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.999895934d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 658377.437d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 3044969.194d); + return true; + default: + break; + } + break; + case 19952: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 42.5000000000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Co-latitude of cone axis", 30.2881397527781d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of pseudo standard parallel", 78.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor on pseudo standard parallel", 0.9999d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19953: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 25.3823611111114d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 50.7613888888892d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 100000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 19954: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -55.6833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19955: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -55.6833333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19956: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 53.3158204722225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 53.1301023611114d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 29352.4763d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 22014.3572d); + return true; + default: + break; + } + break; + case 19957: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 53.3158204722225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 53.1301023611114d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 1937263.44d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 1452947.58d); + return true; + default: + break; + } + break; + case 19958: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of projection centre", 4.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of projection centre", 115.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Azimuth at projection centre", 53.3158204722225d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Angle from Rectified to Skew Grid", 53.1301023611114d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Scale factor at projection centre", 0.99984d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Easting at projection centre", 590476.87d); + return true; + case 6: + parameter = new EpsgConversionParameterRecord("Northing at projection centre", 442857.65d); + return true; + default: + break; + } + break; + case 19959: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 4.66666666666695d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99975d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 900000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19960: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 47.2500000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -63.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999912d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 400000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 800000.0d); + return true; + default: + break; + } + break; + case 19961: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 90.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 4.36748666666694d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 51.1666672333336d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 49.8333339000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 150000.013d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 5400088.438d); + return true; + default: + break; + } + break; + case 19962: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.99982d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 750000.0d); + return true; + default: + break; + } + break; + case 19963: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 6.66666666666695d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19964: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 6.66666666666695d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -12.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 800000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 600000.0d); + return true; + default: + break; + } + break; + case 19966: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 49.8333333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 6.16666666666694d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 80000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 100000.0d); + return true; + default: + break; + } + break; + case 19967: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 15.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19969: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19971: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 173.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 1600000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 10000000.0d); + return true; + default: + break; + } + break; + case 19972: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.000035d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 250000.0d); + return true; + default: + break; + } + break; + case 19973: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 53.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 200000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 250000.0d); + return true; + default: + break; + } + break; + case 19974: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.13190611111139d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 180.598d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -86.99d); + return true; + default: + break; + } + break; + case 19975: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 10.4416666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -61.3333333333336d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 283800.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 214500.0d); + return true; + default: + break; + } + break; + case 19976: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 6.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -66.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 9.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 3.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 1000000.0d); + return true; + default: + break; + } + break; + case 19977: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 25.0895100000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 17.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 33.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 0.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 19978: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 22.3121333333336d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 114.178555555556d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 836694.05d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 819069.8d); + return true; + default: + break; + } + break; + case 19979: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 39.6666666666669d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 1.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19981: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -21.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 166.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -20.6666666666669d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -22.3333333333336d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 400000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 300000.0d); + return true; + default: + break; + } + break; + case 19983: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -67.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 140.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 300000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 200000.0d); + return true; + default: + break; + } + break; + case 19984: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 45.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -126.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 50.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 58.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 1000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 0.0d); + return true; + default: + break; + } + break; + case 19985: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 52.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 10.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 35.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 65.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 4000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 2800000.0d); + return true; + default: + break; + } + break; + case 19986: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 10.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 4321000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 3210000.0d); + return true; + default: + break; + } + break; + case 19987: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 65.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -19.0221250000003d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19988: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 65.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -18.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 500000.0d); + return true; + default: + break; + } + break; + case 19989: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", 65.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", -19.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", 64.2500000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", 65.7500000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 500000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 500000.0d); + return true; + default: + break; + } + break; + case 19990: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 24.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9996d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -6000000.0d); + return true; + default: + break; + } + break; + case 19991: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -8.50000000000028d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 50000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -7800000.0d); + return true; + default: + break; + } + break; + case 19992: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -71.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 0.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 0.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19993: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of standard parallel", -71.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of origin", 70.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 6000000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 6000000.0d); + return true; + default: + break; + } + break; + case 19994: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of false origin", -50.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of false origin", 70.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Latitude of 1st standard parallel", -68.5000000000003d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("Latitude of 2nd standard parallel", -74.5000000000003d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("Easting at false origin", 6000000.0d); + return true; + case 5: + parameter = new EpsgConversionParameterRecord("Northing at false origin", 6000000.0d); + return true; + default: + break; + } + break; + case 19995: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 37.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9998d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", -3000000.0d); + return true; + default: + break; + } + break; + case 19996: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 52.4186482777781d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 13.6272036666669d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False northing", 10000.0d); + return true; + default: + break; + } + break; + case 19997: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 0.0d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", 48.0d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 1.0d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 500000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 0.0d); + return true; + default: + break; + } + break; + case 19998: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 49.5000000000003d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.41666666666694d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.999997d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 47000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 50000.0d); + return true; + default: + break; + } + break; + case 19999: + switch (parameterIndex) + { + case 0: + parameter = new EpsgConversionParameterRecord("Latitude of natural origin", 49.225d); + return true; + case 1: + parameter = new EpsgConversionParameterRecord("Longitude of natural origin", -2.135d); + return true; + case 2: + parameter = new EpsgConversionParameterRecord("Scale factor at natural origin", 0.9999999d); + return true; + case 3: + parameter = new EpsgConversionParameterRecord("False easting", 40000.0d); + return true; + case 4: + parameter = new EpsgConversionParameterRecord("False northing", 70000.0d); + return true; + default: + break; + } + break; + default: + break; + } + + parameter = default; + return false; + } + + } +} diff --git a/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Operations.g.cs b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Operations.g.cs new file mode 100644 index 00000000..2be9c4b2 --- /dev/null +++ b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Operations.g.cs @@ -0,0 +1,24878 @@ +// +// Generated by tools\Generate-EpsgManagedData.ps1 +// Source: EPSG-v12_054-WKT.Zip +// +#pragma warning disable SA0001, SA1512, SA1518, SA1600, SA1614, SA1616, SA1633, SA1636 +using System; + +namespace ProjNet.Data.Generated +{ + internal static class EpsgGeneratedOperationsCatalog + { + internal static readonly EpsgOperationRecord[] Operations = new EpsgOperationRecord[] + { + new EpsgOperationRecord((EpsgOperationType)0, 1024, 4312, 4258, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 46.64d, 47.84d, 13.58d, 16.17d, 0, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1026, 4903, 4230, 10.0d, "Madrid to ED50 polynomial", "", 35.95d, 43.82d, -9.37d, 3.39d, 7, 9), + new EpsgOperationRecord((EpsgOperationType)0, 1027, 4903, 4230, 5.0d, "Madrid to ED50 polynomial", "", 39.96d, 43.82d, -9.37d, 3.39d, 16, 9), + new EpsgOperationRecord((EpsgOperationType)0, 1028, 4903, 4230, 5.0d, "Madrid to ED50 polynomial", "", 35.95d, 41.98d, -7.54d, 0.28d, 25, 9), + new EpsgOperationRecord((EpsgOperationType)0, 1035, 5800, 22192, 5.0d, "Transverse Mercator", "", -46.7d, -45.19d, -69.5d, -67.5d, 34, 9), + new EpsgOperationRecord((EpsgOperationType)0, 1041, 4300, 4258, 0.4d, "General polynomial of degree 6", "", 51.39d, 55.43d, -10.56d, -5.34d, 43, 38), + new EpsgOperationRecord((EpsgOperationType)0, 1042, 4300, 4326, 1.0d, "General polynomial of degree 6", "", 51.39d, 55.43d, -10.56d, -5.34d, 81, 38), + new EpsgOperationRecord((EpsgOperationType)0, 1044, 28992, 23031, 1.0d, "Oblique Stereographic", "", 50.75d, 53.7d, 3.2d, 7.22d, 119, 24), + new EpsgOperationRecord((EpsgOperationType)0, 1046, 28992, 23031, 1.0d, "Oblique Stereographic", "", 50.75d, 53.7d, 3.2d, 7.22d, 143, 24), + new EpsgOperationRecord((EpsgOperationType)0, 1048, 31300, 23031, 1.0d, "Lambert Conic Conformal (2SP Belgium)", "", 49.5d, 51.51d, 2.5d, 6.4d, 167, 23), + new EpsgOperationRecord((EpsgOperationType)0, 1050, 28992, 23095, 1.0d, "Oblique Stereographic", "", 50.75d, 53.7d, 3.2d, 7.22d, 190, 24), + new EpsgOperationRecord((EpsgOperationType)0, 1052, 4230, 4326, 5.0d, "Reversible polynomial of degree 13", "", 53.58d, 55.92d, 3.34d, 8.88d, 214, 48), + new EpsgOperationRecord((EpsgOperationType)0, 1055, 4204, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 28.53d, 30.09d, 46.54d, 48.48d, 262, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1056, 4204, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 28.53d, 30.09d, 46.54d, 48.48d, 265, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1057, 4204, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 29.1d, 30.09d, 46.54d, 48.42d, 272, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1058, 4204, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 28.53d, 29.45d, 46.54d, 48.48d, 279, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1059, 4246, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 28.53d, 30.09d, 46.54d, 48.48d, 286, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1060, 4318, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 28.53d, 30.09d, 46.54d, 48.48d, 289, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1061, 4319, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 29.17d, 29.45d, 47.78d, 48.16d, 292, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1062, 4319, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 29.17d, 29.45d, 47.78d, 48.16d, 295, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1063, 4676, 4678, 2.0d, "Geocentric translations (geog2D domain)", "", 13.92d, 22.5d, 100.09d, 107.64d, 302, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1064, 4677, 4678, 0.15d, "Geocentric translations (geog2D domain)", "", 13.92d, 22.5d, 100.09d, 107.64d, 305, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1065, 4678, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 13.92d, 22.5d, 100.09d, 107.64d, 308, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1066, 4289, 4258, 0.5d, "Molodensky-Badekas (CF geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 311, 10), + new EpsgOperationRecord((EpsgOperationType)0, 1067, 4263, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 3.24d, 5.54d, 4.41d, 6.29d, 321, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1068, 4675, 4152, 5.0d, "NADCON", "guhpgn.las", 13.18d, 13.7d, 144.58d, 145.01d, 324, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1069, 4675, 4326, 5.0d, "NADCON", "guhpgn.las", 13.18d, 13.7d, 144.58d, 145.01d, 324, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1070, 4675, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 13.18d, 13.7d, 144.58d, 145.01d, 324, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1071, 4281, 4141, 1.5d, "Geocentric translations (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 327, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1072, 28193, 2039, 3.0d, "Cassini-Soldner", "", 29.45d, 33.28d, 34.17d, 35.69d, 330, 11), + new EpsgOperationRecord((EpsgOperationType)0, 1073, 4141, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 341, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1074, 4281, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 344, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1075, 4230, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 31.35d, 43.45d, 28.03d, 41.47d, 351, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1078, 4181, 4258, 0.1d, "Molodensky-Badekas (CF geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 354, 10), + new EpsgOperationRecord((EpsgOperationType)0, 1079, 4181, 4326, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 364, 10), + new EpsgOperationRecord((EpsgOperationType)0, 1080, 4672, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", -44.64d, -43.3d, -177.25d, -175.54d, 374, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1081, 4673, 4326, 2.0d, "Coordinate Frame rotation (geog2D domain)", "", -44.64d, -43.3d, -177.25d, -175.54d, 377, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1082, 4673, 4167, 2.0d, "Coordinate Frame rotation (geog2D domain)", "", -44.64d, -43.3d, -177.25d, -175.54d, 384, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1083, 4242, 4322, 10.0d, "Geocentric translations (geog2D domain)", "", 17.64d, 18.58d, -78.43d, -76.17d, 391, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1084, 4242, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 17.64d, 18.58d, -78.43d, -76.17d, 394, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1085, 4242, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 17.64d, 18.58d, -78.43d, -76.17d, 397, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1087, 4230, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", 29.18d, 33.38d, 34.88d, 39.31d, 400, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1088, 4265, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 43.62d, 45.73d, 12.22d, 13.96d, 403, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1089, 4265, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 41.95d, 44.04d, 13.61d, 16.14d, 406, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1090, 4265, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 40.72d, 42.28d, 15.95d, 18.63d, 409, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1091, 4265, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 39.77d, 41.03d, 17.95d, 18.99d, 412, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1092, 4265, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 37.67d, 40.47d, 16.55d, 18.93d, 415, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1093, 4265, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 35.22d, 37.48d, 13.0d, 15.16d, 418, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1094, 4265, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 35.28d, 38.45d, 10.68d, 13.01d, 421, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1095, 4248, 4326, 15.0d, "Molodensky-Badekas (CF geog2D domain)", "", 0.64d, 12.25d, -73.38d, -59.8d, 424, 10), + new EpsgOperationRecord((EpsgOperationType)0, 1096, 4247, 4326, 15.0d, "Molodensky-Badekas (CF geog2D domain)", "", 0.64d, 12.25d, -73.38d, -59.8d, 434, 10), + new EpsgOperationRecord((EpsgOperationType)0, 1099, 4670, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 34.76d, 47.1d, 5.93d, 18.99d, 444, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1100, 4201, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", 3.4d, 22.24d, 21.82d, 47.99d, 447, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1101, 4201, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 9.39d, 15.09d, -5.53d, 2.4d, 450, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1102, 4201, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 1.65d, 13.09d, 8.45d, 16.21d, 453, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1103, 4201, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 3.4d, 14.89d, 32.99d, 47.99d, 456, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1104, 4201, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 10.14d, 25.01d, -12.25d, 4.26d, 459, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1105, 4201, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 12.29d, 16.7d, -17.59d, -11.36d, 462, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1106, 4201, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 3.49d, 22.24d, 21.82d, 38.66d, 465, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1107, 4205, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -1.71d, 12.03d, 40.98d, 51.47d, 468, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1108, 4202, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", -43.7d, -9.86d, 112.85d, 153.69d, 471, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1109, 4203, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", -38.53d, -9.37d, 109.23d, 153.61d, 474, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1110, 4204, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 25.53d, 26.34d, 50.39d, 50.85d, 477, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1111, 4204, 4326, 18.0d, "Geocentric translations (geog2D domain)", "", 16.37d, 32.16d, 34.51d, 55.67d, 480, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1112, 4289, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 483, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1113, 4209, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -30.66d, -8.19d, 19.99d, 35.93d, 490, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1114, 4209, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -26.88d, -17.78d, 19.99d, 29.38d, 493, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1116, 4209, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -30.66d, -28.57d, 27.01d, 29.46d, 496, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1117, 4209, 4326, 27.0d, "Geocentric translations (geog2D domain)", "", -17.14d, -9.37d, 32.68d, 35.93d, 499, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1118, 4209, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", -27.32d, -25.72d, 30.79d, 32.14d, 502, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1120, 4209, 4326, 41.0d, "Geocentric translations (geog2D domain)", "", -18.08d, -8.19d, 21.99d, 33.71d, 505, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1121, 4209, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", -22.42d, -15.61d, 25.23d, 33.08d, 508, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1122, 4210, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", -11.75d, 4.63d, 29.34d, 41.91d, 511, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1124, 4216, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 32.21d, 32.43d, -64.89d, -64.61d, 514, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1125, 4218, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -4.23d, 13.68d, -79.1d, -66.87d, 517, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1126, 4219, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -3.3d, -1.44d, 105.07d, 108.35d, 520, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1127, 4221, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", -54.93d, -21.78d, -73.59d, -53.65d, 523, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1128, 4222, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", -34.88d, -22.13d, 16.45d, 32.95d, 526, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1129, 4222, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", -34.88d, -22.13d, 16.45d, 32.95d, 529, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1130, 4223, 4326, 14.0d, "Geocentric translations (geog2D domain)", "", 30.23d, 38.41d, 7.49d, 13.67d, 532, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1131, 4224, 4326, 12.0d, "Geocentric translations (geog2D domain)", "", -22.0d, -19.29d, -62.57d, -57.81d, 535, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1132, 4225, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", -32.75d, -2.68d, -58.16d, -34.74d, 538, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1133, 4230, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 34.88d, 71.24d, -9.56d, 31.59d, 541, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1134, 4230, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 42.33d, 57.8d, -4.87d, 17.17d, 544, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1135, 4230, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 16.37d, 37.39d, 34.17d, 55.67d, 547, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1136, 4230, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", 32.88d, 36.21d, 29.95d, 35.2d, 550, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1137, 4230, 4326, 13.0d, "Geocentric translations (geog2D domain)", "", 25.71d, 31.68d, 24.7d, 30.0d, 553, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1138, 4230, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 49.11d, 60.9d, -10.56d, 1.84d, 556, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1139, 4230, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 57.9d, 71.24d, 4.39d, 31.59d, 559, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1140, 4230, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 34.88d, 41.75d, 19.57d, 28.3d, 562, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1141, 4154, 4326, 19.0d, "Geocentric translations (geog2D domain)", "", 23.34d, 39.78d, 44.03d, 63.34d, 565, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1142, 4230, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 38.82d, 41.31d, 8.08d, 9.89d, 568, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1143, 4230, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 36.59d, 38.35d, 12.36d, 15.71d, 571, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1144, 4230, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 35.74d, 36.05d, 14.27d, 14.63d, 574, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1145, 4230, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", 35.26d, 43.82d, -9.56d, 3.39d, 577, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1146, 4231, 4326, 0.8d, "Position Vector transformation (geog2D domain)", "", 51.03d, 62.0d, -5.05d, 10.86d, 580, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1147, 4230, 4231, 1.0d, "Position Vector transformation (geog2D domain)", "", 65.0d, 84.73d, -3.35d, 38.01d, 587, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1148, 4229, 4326, 11.0d, "Geocentric translations (geog2D domain)", "", 21.89d, 33.82d, 24.7d, 37.91d, 594, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1149, 4258, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 33.26d, 84.73d, -16.1d, 38.01d, 597, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1150, 4283, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", -60.55d, -8.47d, 93.41d, 173.34d, 600, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1151, 4272, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", -47.65d, -33.89d, 165.87d, 179.27d, 603, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1152, 4236, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", 21.87d, 25.34d, 119.25d, 122.06d, 606, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1153, 4239, 4326, 21.0d, "Geocentric translations (geog2D domain)", "", 5.63d, 20.46d, 97.34d, 105.64d, 609, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1154, 4240, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 5.63d, 20.46d, 97.34d, 105.64d, 612, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1155, 4144, 4326, 18.0d, "Geocentric translations (geog2D domain)", "", 20.52d, 26.64d, 88.01d, 92.67d, 615, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1156, 4146, 4326, 22.0d, "Geocentric translations (geog2D domain)", "", 8.02d, 35.51d, 68.13d, 97.42d, 618, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1157, 4244, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 5.86d, 9.88d, 79.64d, 81.95d, 621, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1158, 4245, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", 1.13d, 7.81d, 99.59d, 105.82d, 624, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1159, 4250, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 1.4d, 11.16d, -3.79d, 2.1d, 627, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1160, 4251, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", 4.29d, 8.52d, -11.52d, -7.36d, 630, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1161, 4253, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", 7.75d, 19.45d, 116.89d, 125.88d, 633, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1162, 4253, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 4.99d, 10.52d, 119.76d, 126.65d, 636, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1163, 4266, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -6.37d, 2.32d, 7.03d, 14.52d, 639, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1164, 4256, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -4.86d, -4.5d, 55.3d, 55.59d, 642, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1165, 4262, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 12.36d, 18.1d, 36.44d, 43.31d, 645, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1166, 4261, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 27.66d, 35.97d, -13.24d, -1.01d, 648, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1167, 4263, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 1.65d, 13.09d, 8.45d, 16.21d, 651, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1168, 4263, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", 1.92d, 13.9d, 2.66d, 14.65d, 654, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1169, 4265, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 38.82d, 41.31d, 8.08d, 9.89d, 657, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1170, 4267, 4326, 16.0d, "Geocentric translations (geog2D domain)", "", 13.0d, 23.25d, -85.01d, -59.37d, 660, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1171, 4267, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 7.98d, 18.49d, -92.29d, -82.53d, 663, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1172, 4267, 4326, 20.0d, "Geocentric translations (geog2D domain)", "", 40.0d, 83.17d, -141.01d, -44.0d, 666, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1173, 4267, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 24.41d, 49.38d, -124.79d, -66.91d, 669, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1174, 4267, 4326, 11.0d, "Geocentric translations (geog2D domain)", "", 24.41d, 49.38d, -97.22d, -66.91d, 672, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1175, 4267, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 25.83d, 49.05d, -124.79d, -89.64d, 675, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1176, 4267, 4326, 12.0d, "Geocentric translations (geog2D domain)", "", 54.34d, 71.4d, -168.26d, -129.99d, 678, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1177, 4267, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 20.86d, 27.29d, -79.04d, -72.68d, 681, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1178, 4267, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 23.9d, 24.19d, -74.6d, -74.37d, 684, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1179, 4267, 4326, 13.0d, "Geocentric translations (geog2D domain)", "", 48.25d, 60.01d, -139.04d, -109.98d, 687, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1180, 4267, 4326, 12.0d, "Geocentric translations (geog2D domain)", "", 41.67d, 60.01d, -102.0d, -74.35d, 690, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1181, 4267, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", 43.41d, 62.62d, -79.85d, -52.54d, 693, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1182, 4267, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 49.0d, 83.17d, -136.46d, -60.72d, 696, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1183, 4267, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 59.99d, 69.7d, -141.01d, -123.91d, 699, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1184, 4267, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 8.82d, 9.45d, -80.07d, -79.46d, 702, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1185, 4267, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 19.77d, 23.25d, -85.01d, -74.07d, 705, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1186, 4267, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 75.86d, 79.2d, -73.29d, -60.98d, 708, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1187, 4267, 4326, 12.0d, "Geocentric translations (geog2D domain)", "", 14.51d, 32.72d, -118.47d, -86.68d, 711, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1188, 4269, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", 23.81d, 86.46d, -172.54d, -47.74d, 714, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1189, 4270, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 20.12d, 20.74d, 58.58d, 59.01d, 717, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1190, 4270, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 24.63d, 28.57d, 47.95d, 50.81d, 720, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1191, 4270, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 22.63d, 26.27d, 51.5d, 57.13d, 723, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1192, 4271, 4326, 33.0d, "Geocentric translations (geog2D domain)", "", 11.08d, 11.41d, -60.9d, -60.44d, 726, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1193, 4275, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 41.31d, 51.14d, -4.87d, 9.63d, 729, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1194, 4312, 4326, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 46.64d, 47.84d, 13.58d, 16.17d, 732, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1195, 4277, 4326, 21.0d, "Geocentric translations (geog2D domain)", "", 49.79d, 60.94d, -8.82d, 1.92d, 739, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1196, 4277, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 49.81d, 55.85d, -6.5d, 1.84d, 742, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1197, 4277, 4326, 21.0d, "Geocentric translations (geog2D domain)", "", 49.81d, 55.85d, -6.5d, 1.84d, 745, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1198, 4277, 4326, 18.0d, "Geocentric translations (geog2D domain)", "", 54.57d, 60.9d, -8.74d, -0.65d, 748, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1199, 4277, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 51.28d, 53.48d, -5.34d, -2.65d, 751, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1200, 4282, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -6.91d, 3.72d, 8.84d, 18.65d, 754, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1201, 4248, 4326, 42.0d, "Geocentric translations (geog2D domain)", "", -43.5d, 12.25d, -81.41d, -56.47d, 757, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1202, 4248, 4326, 19.0d, "Geocentric translations (geog2D domain)", "", -22.91d, -9.67d, -69.66d, -57.52d, 760, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1203, 4248, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -21.51d, -17.5d, -70.49d, -68.18d, 763, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1204, 4248, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -38.99d, -74.48d, -71.38d, 766, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1205, 4248, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", -4.23d, 12.52d, -79.1d, -66.87d, 769, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1206, 4248, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -5.01d, 1.45d, -81.03d, -75.21d, 772, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1207, 4248, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", 1.18d, 10.7d, -61.39d, -55.77d, 775, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1208, 4248, 4326, 16.0d, "Geocentric translations (geog2D domain)", "", -18.35d, -0.03d, -81.41d, -68.67d, 778, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1209, 4248, 4326, 23.0d, "Geocentric translations (geog2D domain)", "", 0.64d, 12.25d, -73.38d, -59.8d, 781, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1210, 4694, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -58.41d, -21.78d, -73.59d, -52.63d, 784, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1225, 4292, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -52.51d, -51.16d, -59.98d, -57.6d, 787, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1226, 4293, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", -30.64d, -16.95d, 8.24d, 25.27d, 790, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1227, 4297, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -26.59d, -11.69d, 42.53d, 51.03d, 793, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1228, 4298, 4326, 19.0d, "Geocentric translations (geog2D domain)", "", 0.85d, 7.67d, 109.31d, 119.61d, 796, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1230, 4301, 4326, 29.0d, "Geocentric translations (geog2D domain)", "", 20.37d, 45.54d, 122.83d, 145.87d, 799, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1231, 4301, 4326, 13.0d, "Geocentric translations (geog2D domain)", "", 30.18d, 45.54d, 128.31d, 145.87d, 802, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1232, 4301, 4326, 13.0d, "Geocentric translations (geog2D domain)", "", 33.14d, 38.64d, 124.53d, 131.01d, 805, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1233, 4301, 4326, 29.0d, "Geocentric translations (geog2D domain)", "", 23.98d, 26.91d, 122.83d, 131.38d, 808, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1234, 4309, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -35.0d, -30.09d, -58.49d, -53.09d, 811, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1235, 4311, 4326, 11.0d, "Geocentric translations (geog2D domain)", "", 1.83d, 9.35d, -58.08d, -52.66d, 814, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1236, 4203, 4326, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", -38.53d, -9.37d, 109.23d, 153.61d, 817, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1237, 4322, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 824, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1238, 4322, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 831, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1239, 4324, 4322, 2.0d, "Position Vector transformation (geog2D domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 838, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1240, 4324, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 845, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1241, 4267, 4269, 0.15d, "NADCON", "conus.las", 23.81d, 49.38d, -129.17d, -65.69d, 852, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1242, 4237, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 852, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1243, 4267, 4269, 0.5d, "NADCON", "alaska.las", 47.88d, 74.71d, 167.65d, -129.99d, 855, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1244, 4740, 4326, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 39.87d, 85.19d, 18.92d, -168.97d, 855, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1245, 4230, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 30.23d, 38.41d, 7.49d, 13.67d, 862, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1246, 4255, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 29.4d, 38.48d, 60.5d, 74.92d, 865, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1247, 4145, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 23.64d, 37.07d, 60.86d, 77.83d, 868, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1248, 4238, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -10.98d, 5.97d, 95.16d, 141.01d, 871, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1249, 4267, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", 51.54d, 54.34d, -178.3d, -164.84d, 874, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1250, 4267, 4326, 18.0d, "Geocentric translations (geog2D domain)", "", 51.3d, 53.07d, 172.42d, 179.86d, 877, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1251, 4269, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 51.3d, 54.34d, 172.42d, -164.84d, 880, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1252, 4269, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", 15.56d, 25.58d, -163.74d, -151.27d, 883, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1253, 4307, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 18.97d, 37.14d, -8.67d, 11.99d, 886, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1254, 4284, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 41.19d, 81.91d, 19.58d, -168.97d, 889, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1255, 4307, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 31.99d, 37.14d, -2.95d, 9.09d, 892, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1256, 4232, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 16.59d, 26.42d, 51.99d, 59.91d, 895, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1257, 4200, 4740, 1.0d, "Geocentric translations (geog2D domain)", "", 39.87d, 85.19d, 18.92d, -168.97d, 898, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1260, 4804, 4257, 0.0d, "Longitude rotation", "", -6.54d, -1.88d, 118.71d, 120.78d, 901, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1262, 4806, 4265, 0.0d, "Longitude rotation", "", 34.76d, 47.1d, 5.93d, 18.99d, 902, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1264, 4809, 4215, 0.0d, "Longitude rotation", "", 49.5d, 51.51d, 2.5d, 6.4d, 903, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1265, 4810, 4297, 0.0d, "Longitude rotation", "", -25.64d, -11.89d, 43.18d, 50.56d, 904, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1266, 4811, 4304, 0.0d, "Longitude rotation", "", 31.99d, 37.14d, -2.95d, 9.09d, 905, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1267, 4284, 4326, 4.0d, "Coordinate Frame rotation (geog2D domain)", "", 41.19d, 81.91d, 19.58d, -168.97d, 906, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1271, 4293, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -30.64d, -16.95d, 8.24d, 25.27d, 913, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1272, 4121, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 34.88d, 41.75d, 19.57d, 28.3d, 916, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1274, 4284, 4669, 9.0d, "Coordinate Frame rotation (geog2D domain)", "", 53.89d, 56.45d, 20.86d, 26.82d, 919, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1275, 4230, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 926, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1276, 4275, 4230, 2.0d, "Geocentric translations (geog2D domain)", "", 41.31d, 51.14d, -4.87d, 9.63d, 929, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1277, 4275, 4322, 2.0d, "Geocentric translations (geog2D domain)", "", 41.31d, 51.14d, -4.87d, 9.63d, 932, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1278, 4202, 4283, 5.0d, "Geocentric translations (geog2D domain)", "", -43.7d, -9.86d, 112.85d, 153.69d, 935, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1279, 4203, 4283, 5.0d, "Geocentric translations (geog2D domain)", "", -38.53d, -9.37d, 109.23d, 153.61d, 938, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1280, 4203, 4283, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -38.53d, -9.37d, 109.23d, 153.61d, 941, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1281, 4200, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 39.87d, 85.19d, 18.92d, -168.97d, 948, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1283, 4669, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 53.89d, 56.45d, 19.02d, 26.82d, 955, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1284, 4210, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", -4.72d, 4.63d, 33.9d, 41.91d, 958, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1285, 4210, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", -11.75d, -0.99d, 29.34d, 40.48d, 961, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1290, 4284, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", 55.67d, 58.09d, 20.87d, 28.24d, 964, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1291, 4284, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 40.59d, 55.45d, 46.49d, 87.35d, 967, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1294, 4304, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 31.99d, 37.14d, -2.95d, 9.09d, 970, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1295, 4749, 4644, 0.05d, "NTv2", "RGNC1991_NEA74Noumea.gsb", -22.37d, -22.19d, 166.35d, 166.54d, 973, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1296, 4302, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 9.83d, 11.51d, -62.09d, -60.0d, 973, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1297, 4127, 4130, 30.0d, "Coordinate Frame rotation (geog2D domain)", "", -26.87d, -10.42d, 30.21d, 40.9d, 976, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1298, 4127, 4130, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -26.87d, -23.91d, 31.91d, 34.5d, 983, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1299, 4127, 4130, 4.0d, "Coordinate Frame rotation (geog2D domain)", "", -24.91d, -19.74d, 31.29d, 35.65d, 990, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1300, 4127, 4130, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -19.91d, -14.01d, 30.21d, 39.18d, 997, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1301, 4127, 4130, 10.0d, "Coordinate Frame rotation (geog2D domain)", "", -16.94d, -10.42d, 34.36d, 40.9d, 1004, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1302, 4130, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -27.71d, -10.09d, 30.21d, 43.03d, 1011, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1303, 4284, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", 41.15d, 46.97d, 48.9d, 53.15d, 1018, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1304, 4240, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 5.63d, 20.46d, 97.34d, 105.64d, 1025, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1305, 4301, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", 33.14d, 38.64d, 124.53d, 131.01d, 1028, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1307, 4271, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", 11.08d, 11.41d, -60.9d, -60.44d, 1031, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1309, 4314, 4258, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", 47.27d, 55.09d, 5.86d, 13.84d, 1034, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1311, 4230, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 47.42d, 63.89d, -16.1d, 10.86d, 1041, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1312, 4267, 4269, 1.0d, "NTv1", "NTv1_0.gsb", 40.0d, 83.17d, -141.01d, -44.0d, 1048, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1313, 4267, 4269, 1.5d, "NTv2", "NTv2_0.gsb", 40.0d, 83.17d, -141.01d, -44.0d, 1048, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1314, 4277, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", 49.79d, 60.94d, -8.82d, 1.92d, 1048, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1315, 4277, 4230, 2.0d, "Position Vector transformation (geog2D domain)", "", 49.79d, 60.94d, -8.82d, 1.92d, 1055, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1317, 4220, 4324, 10.0d, "Geocentric translations (geog2D domain)", "", -17.26d, -6.01d, 8.2d, 13.86d, 1062, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1318, 4220, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -8.59d, -7.75d, 12.58d, 13.4d, 1065, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1319, 4220, 4326, 25.0d, "Geocentric translations (geog2D domain)", "", -7.01d, -6.01d, 12.08d, 12.84d, 1068, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1320, 4220, 4326, 10.0d, "Position Vector transformation (geog2D domain)", "", -7.26d, -6.03d, 11.08d, 12.09d, 1071, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1321, 4220, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -17.26d, -6.01d, 8.2d, 13.86d, 1078, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1322, 4220, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", -7.34d, -6.66d, 11.74d, 12.5d, 1081, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1323, 4220, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", -10.09d, -9.41d, 12.66d, 13.39d, 1084, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1324, 4220, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", -10.09d, -6.03d, 10.83d, 13.39d, 1087, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1325, 4220, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", -7.01d, -6.01d, 12.08d, 12.84d, 1090, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1326, 4220, 4326, 10.0d, "Position Vector transformation (geog2D domain)", "", -8.34d, -6.03d, 11.08d, 12.75d, 1093, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1327, 4220, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -8.59d, -6.01d, 10.41d, 12.84d, 1100, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1330, 4259, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -6.04d, -5.05d, 10.53d, 12.37d, 1103, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1331, 4133, 4258, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", 57.52d, 59.75d, 21.74d, 28.2d, 1106, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1332, 4284, 4133, 9.0d, "Coordinate Frame rotation (geog2D domain)", "", 57.52d, 59.75d, 21.74d, 28.2d, 1113, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1333, 4133, 4326, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 57.52d, 59.75d, 21.74d, 28.2d, 1120, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1334, 4284, 4326, 9.0d, "Coordinate Frame rotation (geog2D domain)", "", 57.52d, 59.75d, 21.74d, 28.2d, 1127, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1437, 4124, 4258, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 54.96d, 69.07d, 10.03d, 24.17d, 1134, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1438, 4232, 4326, 25.0d, "Position Vector transformation (geog2D domain)", "", 16.59d, 26.42d, 51.99d, 59.91d, 1141, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1439, 4134, 4326, 0.5d, "Position Vector transformation (geog2D domain)", "", 16.59d, 26.58d, 51.99d, 59.91d, 1148, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1440, 4230, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 34.88d, 41.75d, 19.57d, 28.3d, 1155, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1441, 4601, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 16.94d, 17.22d, -61.95d, -61.61d, 1158, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1442, 4602, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 15.14d, 15.69d, -61.55d, -61.2d, 1161, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1443, 4603, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 11.94d, 12.29d, -61.84d, -61.54d, 1164, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1444, 4604, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 16.62d, 16.87d, -62.29d, -62.08d, 1167, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1445, 4605, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 17.06d, 17.46d, -62.92d, -62.5d, 1170, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1446, 4606, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 13.66d, 14.16d, -61.13d, -60.82d, 1173, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1447, 4600, 4326, 10.0d, "Geographic2D offsets", "", 18.11d, 18.33d, -63.22d, -62.92d, 1176, 2), + new EpsgOperationRecord((EpsgOperationType)0, 1448, 4237, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 1178, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1449, 4237, 4258, 0.4d, "Coordinate Frame rotation (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 1185, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1450, 4230, 4326, 0.5d, "Norway Offshore Interpolation", "ED50 to WGS 84 (15)", 62.0d, 65.01d, -0.49d, 5.01d, 1192, 2), + new EpsgOperationRecord((EpsgOperationType)0, 1451, 4609, 4269, 1.0d, "NTv1", "PQV4.DAC", 44.99d, 62.62d, -79.85d, -57.1d, 1194, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1454, 4135, 4269, 0.2d, "NADCON", "hawaii.las", 18.87d, 22.29d, -160.3d, -154.74d, 1194, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1455, 4136, 4269, 0.5d, "NADCON", "stlrnc.las", 62.89d, 63.84d, -171.97d, -168.59d, 1194, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1456, 4137, 4269, 0.5d, "NADCON", "stpaul.las", 57.06d, 57.28d, -170.51d, -170.04d, 1194, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1457, 4138, 4269, 0.5d, "NADCON", "stgeorge.las", 56.49d, 56.67d, -169.88d, -169.38d, 1194, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1458, 4202, 4283, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -35.93d, -35.12d, 148.76d, 149.4d, 1194, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1459, 4202, 4283, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -43.7d, -39.52d, 143.77d, 148.55d, 1201, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1460, 4202, 4283, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -39.2d, -28.15d, 140.96d, 153.69d, 1208, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1461, 4139, 4269, 0.05d, "NADCON", "prvi.las", 17.62d, 18.78d, -67.97d, -64.25d, 1215, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1462, 4267, 4269, 1.0d, "NTv1", "GS2783v1.QUE", 44.99d, 62.62d, -79.85d, -57.1d, 1215, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1463, 4608, 4269, 1.0d, "NTv2", "May76v20.gsb", 41.67d, 56.9d, -95.16d, -74.35d, 1215, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1464, 4202, 4283, 0.1d, "NTv2", "vic_0799.gsb", -39.2d, -33.98d, 140.96d, 150.04d, 1215, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1469, 4142, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", 5.15d, 5.54d, -4.22d, -3.85d, 1215, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1470, 4143, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 1.02d, 10.74d, -8.61d, -2.48d, 1218, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1474, 4269, 4152, 0.05d, "NADCON", "alhpgn.las", 30.14d, 35.02d, -88.48d, -84.89d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1475, 4269, 4152, 0.05d, "NADCON", "azhpgn.las", 31.33d, 37.01d, -114.81d, -109.04d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1476, 4269, 4152, 0.05d, "NADCON", "cnhpgn.las", 36.5d, 42.01d, -124.45d, -116.54d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1477, 4269, 4152, 0.05d, "NADCON", "cshpgn.las", 32.53d, 36.5d, -121.98d, -114.12d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1478, 4269, 4152, 0.05d, "NADCON", "cohpgn.las", 36.98d, 41.01d, -109.06d, -102.04d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1479, 4269, 4152, 0.05d, "NADCON", "gahpgn.las", 30.36d, 35.01d, -85.61d, -80.77d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1480, 4269, 4152, 0.05d, "NADCON", "flhpgn.las", 24.41d, 31.01d, -87.63d, -79.97d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1481, 4269, 4152, 0.05d, "NADCON", "emhpgn.las", 41.99d, 49.01d, -113.0d, -104.04d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1482, 4269, 4152, 0.05d, "NADCON", "wmhpgn.las", 41.99d, 49.01d, -117.24d, -113.0d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1483, 4269, 4152, 0.05d, "NADCON", "kyhpgn.las", 36.49d, 39.15d, -89.57d, -81.95d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1484, 4269, 4152, 0.05d, "NADCON", "lahpgn.las", 28.85d, 33.03d, -94.05d, -88.75d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1485, 4269, 4152, 0.05d, "NADCON", "mdhpgn.las", 37.97d, 39.85d, -79.49d, -74.97d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1486, 4269, 4152, 0.05d, "NADCON", "mehpgn.las", 43.04d, 47.47d, -71.09d, -66.91d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1487, 4269, 4152, 0.05d, "NADCON", "mihpgn.las", 41.69d, 48.32d, -90.42d, -82.13d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1488, 4269, 4152, 0.05d, "NADCON", "mshpgn.las", 30.01d, 35.01d, -91.65d, -88.09d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1489, 4269, 4152, 0.05d, "NADCON", "nbhpgn.las", 39.99d, 43.01d, -104.06d, -95.3d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1490, 4269, 4152, 0.05d, "NADCON", "nehpgn.las", 40.98d, 45.31d, -73.73d, -69.86d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1491, 4269, 4152, 0.05d, "NADCON", "nmhpgn.las", 31.33d, 37.0d, -109.06d, -102.99d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1492, 4269, 4152, 0.05d, "NADCON", "nyhpgn.las", 40.47d, 45.02d, -79.77d, -71.8d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1493, 4269, 4152, 0.05d, "NADCON", "ndhpgn.las", 45.93d, 49.01d, -104.07d, -96.55d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1494, 4269, 4152, 0.05d, "NADCON", "okhpgn.las", 33.62d, 37.01d, -103.0d, -94.42d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1495, 4269, 4152, 0.05d, "NADCON", "pvhpgn.las", 17.62d, 18.57d, -67.97d, -64.51d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1496, 4269, 4152, 0.05d, "NADCON", "sdhpgn.las", 42.48d, 45.95d, -104.07d, -96.43d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1497, 4269, 4152, 0.05d, "NADCON", "tnhpgn.las", 34.98d, 36.68d, -90.31d, -81.65d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1498, 4269, 4152, 0.05d, "NADCON", "ethpgn.las", 25.83d, 34.58d, -100.0d, -93.5d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1499, 4269, 4152, 0.05d, "NADCON", "wthpgn.las", 28.04d, 36.5d, -106.66d, -100.0d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1500, 4269, 4152, 0.05d, "NADCON", "vahpgn.las", 36.54d, 39.46d, -83.68d, -75.31d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1501, 4269, 4152, 0.05d, "NADCON", "wohpgn.las", 41.98d, 49.05d, -124.79d, -116.47d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1502, 4269, 4152, 0.05d, "NADCON", "wihpgn.las", 42.48d, 47.31d, -92.89d, -86.25d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1503, 4269, 4152, 0.05d, "NADCON", "wyhpgn.las", 40.99d, 45.01d, -111.06d, -104.05d, 1221, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1504, 4222, 4148, 15.0d, "Geocentric translations (geog2D domain)", "", -34.88d, -22.13d, 16.45d, 32.95d, 1221, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1505, 4148, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -50.32d, -22.13d, 13.33d, 42.85d, 1224, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1506, 4202, 4283, 0.1d, "NTv2", "tas_1098.gsb", -43.7d, -39.52d, 143.77d, 148.55d, 1227, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1507, 4202, 4283, 0.1d, "NTv2", "nt_0599.gsb", -26.01d, -10.86d, 128.99d, 138.0d, 1227, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1509, 4150, 4151, 0.1d, "Geocentric translations (geog2D domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 1227, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1511, 4151, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 1230, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1512, 4153, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 27.39d, 27.61d, 52.5d, 52.71d, 1233, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1513, 4132, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 27.3d, 28.2d, 51.8d, 53.01d, 1236, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1514, 4154, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 23.34d, 39.78d, 44.03d, 63.34d, 1239, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1516, 4247, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", 3.56d, 10.8d, -67.49d, -59.8d, 1246, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1517, 4315, 4326, 30.0d, "Geocentric translations (geog2D domain)", "", 7.19d, 12.68d, -15.13d, -7.65d, 1249, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1518, 4155, 4326, 25.0d, "Geocentric translations (geog2D domain)", "", 7.19d, 12.68d, -15.13d, -7.65d, 1252, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1520, 4269, 4152, 0.05d, "NADCON", "hihpgn.las", 18.87d, 22.29d, -160.3d, -154.74d, 1255, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1521, 4269, 4152, 0.05d, "NADCON", "inhpgn.las", 37.77d, 41.77d, -88.1d, -84.78d, 1255, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1522, 4269, 4152, 0.05d, "NADCON", "kshpgn.las", 36.99d, 40.01d, -102.06d, -94.58d, 1255, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1523, 4269, 4152, 0.05d, "NADCON", "nvhpgn.las", 34.99d, 42.0d, -120.0d, -114.03d, 1255, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1524, 4269, 4152, 0.05d, "NADCON", "ohhpgn.las", 38.4d, 42.33d, -84.83d, -80.51d, 1255, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1525, 4269, 4152, 0.05d, "NADCON", "uthpgn.las", 36.99d, 42.01d, -114.05d, -109.04d, 1255, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1526, 4269, 4152, 0.05d, "NADCON", "wvhpgn.las", 37.2d, 40.64d, -82.65d, -77.72d, 1255, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1527, 4221, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", -38.75d, -37.5d, -69.5d, -68.25d, 1255, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1528, 4160, 4221, 10.0d, "Geocentric translations (geog2D domain)", "", -38.75d, -37.5d, -69.5d, -68.25d, 1258, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1529, 4254, 4326, 0.5d, "Position Vector transformation (geog2D domain)", "", -55.11d, -52.59d, -68.64d, -63.73d, 1261, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1530, 4267, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", 18.83d, 25.51d, -87.01d, -73.57d, 1268, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1531, 4270, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 25.33d, 25.54d, 53.03d, 53.4d, 1271, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1532, 4266, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", -6.37d, 2.32d, 7.03d, 14.52d, 1274, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1533, 4144, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 9.48d, 17.87d, 93.94d, 99.66d, 1277, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1536, 4270, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 24.64d, 27.05d, 50.55d, 53.04d, 1280, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1537, 4240, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 6.74d, 8.16d, 102.16d, 103.05d, 1283, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1538, 4223, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 33.22d, 38.41d, 7.81d, 13.67d, 1286, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1539, 4164, 4163, 5.0d, "Geocentric translations (geog2D domain)", "", 12.54d, 19.0d, 43.37d, 53.14d, 1289, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1540, 4163, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 8.95d, 19.0d, 41.08d, 57.96d, 1292, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1541, 4131, 4324, 25.0d, "Geocentric translations (geog2D domain)", "", 7.99d, 11.15d, 106.54d, 110.0d, 1295, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1542, 4131, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 14.0d, 18.01d, 105.61d, 109.36d, 1298, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1543, 4131, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 8.57d, 8.83d, 106.48d, 106.8d, 1301, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1544, 4147, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 9.03d, 11.04d, 105.49d, 107.58d, 1304, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1545, 4229, 4322, 5.0d, "Geocentric translations (geog2D domain)", "", 21.89d, 33.82d, 24.7d, 37.91d, 1307, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1547, 4165, 4326, 25.0d, "Geocentric translations (geog2D domain)", "", 10.87d, 12.69d, -16.77d, -13.64d, 1310, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1550, 4208, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -9.8d, -8.39d, -39.04d, -37.09d, 1313, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1551, 4208, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -10.61d, -9.79d, -39.14d, -37.99d, 1316, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1552, 4208, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -12.27d, -10.6d, -39.07d, -37.98d, 1319, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1553, 4269, 4152, 0.05d, "NADCON", "ilhpgn.las", 36.97d, 42.51d, -91.52d, -87.02d, 1322, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1554, 4269, 4152, 0.05d, "NADCON", "njhpgn.las", 38.87d, 41.36d, -75.6d, -73.88d, 1322, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1555, 4158, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 9.99d, 10.9d, -61.98d, -60.85d, 1322, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1556, 4158, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", 9.99d, 10.9d, -61.98d, -60.85d, 1325, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1557, 4259, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -6.04d, -5.05d, 10.53d, 12.37d, 1328, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1558, 4166, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 33.14d, 38.64d, 124.53d, 131.01d, 1331, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1560, 4307, 4324, 8.0d, "Geocentric translations (geog2D domain)", "", 31.48d, 32.09d, 5.59d, 6.5d, 1334, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1561, 4285, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 24.55d, 26.2d, 50.69d, 51.68d, 1337, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1562, 4285, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 24.64d, 27.05d, 50.55d, 53.04d, 1340, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1563, 4285, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 24.55d, 26.2d, 50.69d, 51.68d, 1343, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1564, 4272, 4326, 4.0d, "Coordinate Frame rotation (geog2D domain)", "", -47.65d, -33.89d, 165.87d, 179.27d, 1346, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1565, 4167, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -55.95d, -25.88d, 160.6d, -171.2d, 1353, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1566, 4272, 4167, 5.0d, "Geocentric translations (geog2D domain)", "", -47.65d, -33.89d, 165.87d, 179.27d, 1356, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1568, 4272, 4167, 0.2d, "NTv2", "nzgd2kgrid0005.gsb", -47.33d, -34.1d, 166.37d, 178.63d, 1359, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1569, 4168, 4326, 25.0d, "Geocentric translations (geog2D domain)", "", 1.4d, 11.16d, -3.79d, 2.1d, 1359, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1570, 4168, 4324, 25.0d, "Geocentric translations (geog2D domain)", "", 1.4d, 6.06d, -3.79d, 2.1d, 1362, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1573, 4267, 4269, 1.5d, "NTv2", "NA27NA83.GSB", 44.99d, 62.62d, -79.85d, -57.1d, 1365, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1575, 4609, 4269, 1.5d, "NTv2", "CQ77NA83.GSB", 44.99d, 62.62d, -79.85d, -57.1d, 1365, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1577, 4169, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -14.43d, -14.11d, -170.88d, -169.38d, 1365, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1578, 4169, 4152, 5.0d, "NADCON", "wshpgn.las", -14.43d, -14.2d, -170.88d, -170.51d, 1368, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1579, 4169, 4152, 5.0d, "NADCON", "eshpgn.las", -14.31d, -14.11d, -169.73d, -169.38d, 1368, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1580, 4152, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -14.59d, 71.4d, 144.58d, -64.51d, 1368, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1581, 4170, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -59.87d, 16.75d, -113.21d, -26.0d, 1371, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1582, 4248, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", -14.43d, -13.56d, -68.96d, -67.79d, 1374, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1583, 4248, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", -21.71d, -21.09d, -63.44d, -62.95d, 1377, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1584, 4227, 4324, 5.0d, "Geocentric translations (geog2D domain)", "", 34.49d, 35.9d, 39.3d, 40.81d, 1380, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1586, 4227, 4326, 999.0d, "Position Vector transformation (geog2D domain)", "", 35.33d, 35.9d, 39.15d, 40.41d, 1383, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1587, 4227, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 35.79d, 36.5d, 40.5d, 41.39d, 1390, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1588, 4230, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 65.0d, 84.73d, -3.35d, 38.01d, 1393, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1589, 4230, 4258, 2.0d, "Norway Offshore Interpolation", "ED50 to ETRS89 (1)", 62.0d, 65.01d, -0.49d, 5.01d, 1400, 2), + new EpsgOperationRecord((EpsgOperationType)0, 1590, 4230, 4326, 0.5d, "Norway Offshore Interpolation", "ED50 to WGS 84 (21)", 62.0d, 65.01d, -0.49d, 5.01d, 1402, 2), + new EpsgOperationRecord((EpsgOperationType)0, 1592, 4298, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 4.01d, 6.31d, 112.37d, 115.37d, 1404, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1593, 4203, 4283, 0.1d, "NTv2", "wa_0700.gsb", -35.19d, -13.67d, 112.85d, 129.01d, 1407, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1594, 4202, 4283, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -43.7d, -39.52d, 143.77d, 148.55d, 1407, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1595, 4202, 4283, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -26.01d, -10.86d, 128.99d, 138.0d, 1414, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1596, 4202, 4283, 0.1d, "NTv2", "SEAust_21_06_00.gsb", -39.2d, -28.15d, 140.96d, 153.69d, 1421, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1597, 4218, 4326, 0.2d, "Geocentric translations (geog2D domain)", "", 4.75d, 5.68d, -73.0d, -72.25d, 1421, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1603, 4267, 4122, 1.5d, "Maritime Provinces polynomial interpolation", "TRNB2777.DAT", 44.56d, 48.07d, -69.05d, -63.7d, 1424, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1604, 4267, 4122, 1.5d, "Maritime Provinces polynomial interpolation", "TRNS2777.DAT", 43.41d, 47.08d, -66.28d, -59.73d, 1424, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1605, 4267, 4122, 1.5d, "Maritime Provinces polynomial interpolation", "TRPE2777.DAT", 45.9d, 47.09d, -64.49d, -61.9d, 1424, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1609, 4313, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 49.5d, 51.51d, 2.5d, 6.4d, 1424, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1610, 4313, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 49.5d, 51.51d, 2.5d, 6.4d, 1431, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1612, 4230, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 62.0d, 84.73d, -3.35d, 38.01d, 1434, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1613, 4230, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 56.08d, 62.01d, 1.37d, 10.81d, 1441, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1614, 4175, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", 6.88d, 10.0d, -13.35d, -10.26d, 1448, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1615, 4298, 4326, 100.0d, "Geocentric translations (geog2D domain)", "", 4.01d, 5.11d, 114.09d, 115.37d, 1451, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1616, 4134, 4322, 1.2d, "Position Vector transformation (geog2D domain)", "", 16.59d, 26.58d, 51.99d, 59.91d, 1454, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1617, 4134, 4326, 0.5d, "Position Vector transformation (geog2D domain)", "", 19.58d, 21.17d, 56.5d, 59.02d, 1461, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1618, 4312, 4326, 1.5d, "Position Vector transformation (geog2D domain)", "", 46.4d, 49.02d, 9.53d, 17.17d, 1468, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1619, 4312, 4258, 1.5d, "Position Vector transformation (geog2D domain)", "", 46.4d, 49.02d, 9.53d, 17.17d, 1475, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1622, 4156, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 48.58d, 51.06d, 12.09d, 18.86d, 1482, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1623, 4156, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 48.58d, 51.06d, 12.09d, 18.86d, 1489, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1626, 4230, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 54.5d, 57.81d, 7.98d, 15.28d, 1496, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1627, 4230, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 54.5d, 57.81d, 7.98d, 15.28d, 1503, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1628, 4230, 4258, 1.0d, "Geocentric translations (geog2D domain)", "", 36.0d, 36.16d, -5.42d, -4.89d, 1510, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1629, 4230, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 36.0d, 36.16d, -5.42d, -4.89d, 1513, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1630, 4230, 4258, 1.5d, "Position Vector transformation (geog2D domain)", "", 38.59d, 40.15d, 1.12d, 4.39d, 1516, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1631, 4230, 4326, 1.5d, "Position Vector transformation (geog2D domain)", "", 38.59d, 40.15d, 1.12d, 4.39d, 1523, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1632, 4230, 4258, 1.5d, "Position Vector transformation (geog2D domain)", "", 35.95d, 43.56d, -7.54d, 3.39d, 1530, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1633, 4230, 4326, 1.5d, "Position Vector transformation (geog2D domain)", "", 35.95d, 43.56d, -7.54d, 3.39d, 1537, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1634, 4230, 4258, 1.5d, "Position Vector transformation (geog2D domain)", "", 41.5d, 43.82d, -9.37d, -4.5d, 1544, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1635, 4230, 4326, 1.5d, "Position Vector transformation (geog2D domain)", "", 41.5d, 43.82d, -9.37d, -4.5d, 1551, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1638, 4123, 4258, 1.5d, "Position Vector transformation (geog2D domain)", "", 59.75d, 70.09d, 19.24d, 31.59d, 1558, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1639, 4123, 4326, 1.5d, "Position Vector transformation (geog2D domain)", "", 59.75d, 70.09d, 19.24d, 31.59d, 1565, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1641, 4299, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 51.39d, 55.43d, -10.56d, -5.34d, 1572, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1642, 4181, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 1579, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1643, 4181, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 1586, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1644, 4179, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 49.0d, 54.89d, 14.14d, 24.15d, 1593, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1645, 4179, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 49.0d, 54.89d, 14.14d, 24.15d, 1600, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1646, 4149, 11307, 1.5d, "Geocentric translations (geog2D domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 1607, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1647, 4150, 11307, 0.1d, "Geocentric translations (geog2D domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 1610, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1649, 4180, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 57.52d, 60.0d, 20.37d, 28.2d, 1613, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1650, 4230, 4258, 2.0d, "Geocentric translations (geog2D domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 1616, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1651, 4275, 4258, 2.0d, "Geocentric translations (geog2D domain)", "", 41.31d, 51.14d, -4.87d, 9.63d, 1619, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1652, 4313, 11063, 1.0d, "Position Vector transformation (geog2D domain)", "", 49.5d, 51.51d, 2.5d, 6.4d, 1622, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1653, 4273, 10875, 3.0d, "Position Vector transformation (geog2D domain)", "", 57.9d, 71.24d, 4.39d, 31.32d, 1629, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1654, 4273, 4326, 3.0d, "Position Vector transformation (geog2D domain)", "", 57.9d, 71.24d, 4.39d, 31.32d, 1636, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1655, 4207, 4258, 3.0d, "Position Vector transformation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 1643, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1656, 4207, 4326, 3.0d, "Position Vector transformation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 1650, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1657, 4274, 4258, 2.0d, "Position Vector transformation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 1657, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1658, 4274, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 1664, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1659, 4265, 4258, 4.0d, "Position Vector transformation (geog2D domain)", "", 37.86d, 47.1d, 6.62d, 18.58d, 1671, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1660, 4265, 4326, 4.0d, "Position Vector transformation (geog2D domain)", "", 37.86d, 47.1d, 6.62d, 18.58d, 1678, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1661, 4265, 4258, 4.0d, "Position Vector transformation (geog2D domain)", "", 38.82d, 41.31d, 8.08d, 9.89d, 1685, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1662, 4265, 4326, 4.0d, "Position Vector transformation (geog2D domain)", "", 38.82d, 41.31d, 8.08d, 9.89d, 1692, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1663, 4265, 4258, 4.0d, "Position Vector transformation (geog2D domain)", "", 36.59d, 38.35d, 12.36d, 15.71d, 1699, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1664, 4265, 4326, 4.0d, "Position Vector transformation (geog2D domain)", "", 36.59d, 38.35d, 12.36d, 15.71d, 1706, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1665, 4202, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -35.93d, -35.12d, 148.76d, 149.4d, 1713, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1666, 4202, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -39.2d, -28.15d, 140.96d, 153.69d, 1720, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1667, 4202, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -43.7d, -39.52d, 143.77d, 148.55d, 1727, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1668, 4202, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -26.01d, -10.86d, 128.99d, 138.0d, 1734, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1669, 4203, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -38.53d, -9.37d, 109.23d, 153.61d, 1741, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1670, 4272, 4326, 1.0d, "NTv2", "nzgd2kgrid0005.gsb", -47.33d, -34.1d, 166.37d, 178.63d, 1748, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1671, 4171, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 1748, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1672, 4289, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 1751, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1673, 4314, 4326, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", 47.27d, 55.09d, 5.86d, 13.84d, 1758, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1674, 4178, 4258, 2.0d, "Coordinate Frame rotation (geog2D domain)", "", 50.2d, 54.74d, 9.92d, 15.04d, 1765, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1675, 4178, 4326, 2.0d, "Coordinate Frame rotation (geog2D domain)", "", 50.2d, 54.74d, 9.92d, 15.04d, 1772, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1676, 4150, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 1779, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1678, 4173, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 51.39d, 55.43d, -10.56d, -5.34d, 1782, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1679, 4284, 4326, 9.0d, "Coordinate Frame rotation (geog2D domain)", "", 53.89d, 56.45d, 20.86d, 26.82d, 1785, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1680, 4124, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 54.96d, 69.07d, 10.03d, 24.17d, 1792, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1682, 4164, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 12.54d, 19.0d, 43.37d, 53.14d, 1799, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1683, 4127, 4326, 30.0d, "Coordinate Frame rotation (geog2D domain)", "", -26.87d, -10.42d, 30.21d, 40.9d, 1802, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1684, 4127, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -26.87d, -23.91d, 31.91d, 34.5d, 1809, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1685, 4127, 4326, 4.0d, "Coordinate Frame rotation (geog2D domain)", "", -24.91d, -19.74d, 31.29d, 35.65d, 1816, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1686, 4127, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -19.91d, -14.01d, 30.21d, 39.18d, 1823, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1687, 4127, 4326, 10.0d, "Coordinate Frame rotation (geog2D domain)", "", -16.94d, -10.42d, 34.36d, 40.9d, 1830, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1688, 4122, 4326, 1.5d, "NTv2", "NB7783v2.gsb", 44.56d, 48.07d, -69.05d, -63.7d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1689, 4122, 4326, 1.5d, "NTv2", "PE7783V2.gsb", 45.9d, 47.09d, -64.49d, -61.9d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1690, 4608, 4326, 2.0d, "NTv2", "May76v20.gsb", 41.67d, 56.9d, -95.16d, -74.35d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1691, 4609, 4326, 1.5d, "NTv2", "CQ77NA83.GSB", 44.99d, 62.62d, -79.85d, -57.1d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1692, 4267, 4326, 1.5d, "NTv2", "NA27SCRS.GSB", 44.99d, 62.62d, -79.85d, -57.1d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1693, 4267, 4326, 2.0d, "NTv2", "NTv2_0.gsb", 40.0d, 83.17d, -141.01d, -44.0d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1694, 4169, 4326, 5.0d, "NADCON", "wshpgn.las", -14.43d, -14.2d, -170.88d, -170.51d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1695, 4169, 4326, 5.0d, "NADCON", "eshpgn.las", -14.31d, -14.11d, -169.73d, -169.38d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1696, 4269, 4326, 1.5d, "NTv2", "NA83SCRS.GSB", 44.99d, 62.62d, -79.85d, -57.1d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1697, 4269, 4326, 1.5d, "NTv2", "SK83-98.gsb", 49.0d, 60.01d, -110.0d, -101.34d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1698, 4138, 4326, 1.5d, "NADCON", "stgeorge.las", 56.49d, 56.67d, -169.88d, -169.38d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1699, 4136, 4326, 1.5d, "NADCON", "stlrnc.las", 62.89d, 63.84d, -171.97d, -168.59d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1700, 4137, 4326, 1.5d, "NADCON", "stpaul.las", 57.06d, 57.28d, -170.51d, -170.04d, 1837, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1701, 4272, 4167, 4.0d, "Coordinate Frame rotation (geog2D domain)", "", -47.65d, -33.89d, 165.87d, 179.27d, 1837, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1702, 4269, 4326, 1.5d, "NTv2", "AB_CSRS.DAC", 48.99d, 60.0d, -120.0d, -109.98d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1703, 4267, 4326, 1.5d, "NTv2", "SK27-98.gsb", 49.0d, 60.01d, -110.0d, -101.34d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1704, 4269, 4152, 0.05d, "NADCON", "arhpgn.las", 33.01d, 36.5d, -94.62d, -89.64d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1705, 4269, 4152, 0.05d, "NADCON", "iahpgn.las", 40.36d, 43.51d, -96.65d, -90.14d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1706, 4269, 4152, 0.05d, "NADCON", "mnhpgn.las", 43.49d, 49.38d, -97.22d, -89.49d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1707, 4269, 4152, 0.05d, "NADCON", "mohpgn.las", 35.98d, 40.61d, -95.77d, -89.1d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1708, 4269, 4326, 2.0d, "NADCON", "arhpgn.las", 33.01d, 36.5d, -94.62d, -89.64d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1709, 4269, 4326, 2.0d, "NADCON", "iahpgn.las", 40.36d, 43.51d, -96.65d, -90.14d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1710, 4269, 4326, 2.0d, "NADCON", "mnhpgn.las", 43.49d, 49.38d, -97.22d, -89.49d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1711, 4269, 4326, 2.0d, "NADCON", "mohpgn.las", 35.98d, 40.61d, -95.77d, -89.1d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1712, 4269, 4326, 2.0d, "NADCON", "cohpgn.las", 36.98d, 41.01d, -109.06d, -102.04d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1713, 4269, 4326, 2.0d, "NADCON", "gahpgn.las", 30.36d, 35.01d, -85.61d, -80.77d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1714, 4269, 4326, 2.0d, "NADCON", "flhpgn.las", 24.41d, 31.01d, -87.63d, -79.97d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1715, 4269, 4326, 2.0d, "NADCON", "emhpgn.las", 41.99d, 49.01d, -113.0d, -104.04d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1716, 4269, 4326, 2.0d, "NADCON", "wmhpgn.las", 41.99d, 49.01d, -117.24d, -113.0d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1717, 4269, 4326, 2.0d, "NADCON", "alhpgn.las", 30.14d, 35.02d, -88.48d, -84.89d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1718, 4269, 4326, 2.0d, "NADCON", "kyhpgn.las", 36.49d, 39.15d, -89.57d, -81.95d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1719, 4269, 4326, 2.0d, "NADCON", "lahpgn.las", 28.85d, 33.03d, -94.05d, -88.75d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1720, 4269, 4326, 2.0d, "NADCON", "mdhpgn.las", 37.97d, 39.85d, -79.49d, -74.97d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1721, 4269, 4326, 2.0d, "NADCON", "mehpgn.las", 43.04d, 47.47d, -71.09d, -66.91d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1722, 4269, 4326, 2.0d, "NADCON", "mihpgn.las", 41.69d, 48.32d, -90.42d, -82.13d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1723, 4269, 4326, 2.0d, "NADCON", "mshpgn.las", 30.01d, 35.01d, -91.65d, -88.09d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1724, 4269, 4326, 2.0d, "NADCON", "nbhpgn.las", 39.99d, 43.01d, -104.06d, -95.3d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1725, 4269, 4326, 2.0d, "NADCON", "nehpgn.las", 40.98d, 45.31d, -73.73d, -69.86d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1726, 4269, 4326, 2.0d, "NADCON", "nmhpgn.las", 31.33d, 37.0d, -109.06d, -102.99d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1727, 4269, 4326, 2.0d, "NADCON", "nyhpgn.las", 40.47d, 45.02d, -79.77d, -71.8d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1728, 4269, 4326, 2.0d, "NADCON", "azhpgn.las", 31.33d, 37.01d, -114.81d, -109.04d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1729, 4269, 4326, 2.0d, "NADCON", "ndhpgn.las", 45.93d, 49.01d, -104.07d, -96.55d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1730, 4269, 4326, 2.0d, "NADCON", "okhpgn.las", 33.62d, 37.01d, -103.0d, -94.42d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1731, 4269, 4326, 2.0d, "NADCON", "pvhpgn.las", 17.62d, 18.57d, -67.97d, -64.51d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1732, 4269, 4326, 2.0d, "NADCON", "sdhpgn.las", 42.48d, 45.95d, -104.07d, -96.43d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1733, 4269, 4326, 2.0d, "NADCON", "tnhpgn.las", 34.98d, 36.68d, -90.31d, -81.65d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1734, 4269, 4326, 2.0d, "NADCON", "ethpgn.las", 25.83d, 34.58d, -100.0d, -93.5d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1735, 4269, 4326, 2.0d, "NADCON", "wthpgn.las", 28.04d, 36.5d, -106.66d, -100.0d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1736, 4269, 4326, 2.0d, "NADCON", "vahpgn.las", 36.54d, 39.46d, -83.68d, -75.31d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1737, 4269, 4326, 2.0d, "NADCON", "wohpgn.las", 41.98d, 49.05d, -124.79d, -116.47d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1738, 4269, 4326, 2.0d, "NADCON", "wihpgn.las", 42.48d, 47.31d, -92.89d, -86.25d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1739, 4269, 4326, 2.0d, "NADCON", "cnhpgn.las", 36.5d, 42.01d, -124.45d, -116.54d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1740, 4269, 4326, 2.0d, "NADCON", "wyhpgn.las", 40.99d, 45.01d, -111.06d, -104.05d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1741, 4269, 4326, 2.0d, "NADCON", "hihpgn.las", 18.87d, 22.29d, -160.3d, -154.74d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1742, 4269, 4326, 2.0d, "NADCON", "inhpgn.las", 37.77d, 41.77d, -88.1d, -84.78d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1743, 4269, 4326, 2.0d, "NADCON", "kshpgn.las", 36.99d, 40.01d, -102.06d, -94.58d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1744, 4269, 4326, 2.0d, "NADCON", "nvhpgn.las", 34.99d, 42.0d, -120.0d, -114.03d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1745, 4269, 4326, 2.0d, "NADCON", "ohhpgn.las", 38.4d, 42.33d, -84.83d, -80.51d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1746, 4269, 4326, 2.0d, "NADCON", "uthpgn.las", 36.99d, 42.01d, -114.05d, -109.04d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1747, 4269, 4326, 2.0d, "NADCON", "wvhpgn.las", 37.2d, 40.64d, -82.65d, -77.72d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1748, 4269, 4326, 2.0d, "NADCON", "ilhpgn.las", 36.97d, 42.51d, -91.52d, -87.02d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1749, 4269, 4326, 2.0d, "NADCON", "njhpgn.las", 38.87d, 41.36d, -75.6d, -73.88d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1750, 4269, 4326, 2.0d, "NADCON", "cshpgn.las", 32.53d, 36.5d, -121.98d, -114.12d, 1844, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1751, 4289, 4258, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 1844, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1753, 4149, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 1851, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1754, 4263, 4326, 5.0d, "Position Vector transformation (geog2D domain)", "", 4.22d, 6.95d, 4.35d, 9.45d, 1858, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1755, 4802, 4218, 0.0d, "Longitude rotation", "", -4.23d, 12.52d, -79.1d, -66.87d, 1865, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1756, 4803, 4207, 0.0d, "Longitude rotation", "", 36.95d, 42.16d, -9.56d, -6.19d, 1866, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1759, 4813, 4211, 0.0d, "Longitude rotation", "", -8.91d, 5.97d, 95.16d, 115.77d, 1867, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1760, 4814, 4308, 0.0d, "Longitude rotation", "", 55.28d, 69.07d, 10.93d, 24.17d, 1868, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1761, 4815, 4120, 0.0d, "Longitude rotation", "", 34.88d, 41.75d, 19.57d, 28.3d, 1869, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1762, 4817, 4273, 0.0d, "Longitude rotation", "", 57.9d, 71.24d, 4.39d, 31.32d, 1870, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1763, 4807, 4275, 0.0d, "Longitude rotation", "", 41.31d, 51.14d, -4.87d, 9.63d, 1871, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1764, 4807, 4275, 0.0d, "Longitude rotation", "", 41.31d, 51.14d, -4.87d, 9.63d, 1872, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1765, 4801, 4149, 0.0d, "Longitude rotation", "", 45.81d, 47.81d, 5.95d, 10.5d, 1873, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1766, 4149, 4326, 1.5d, "Geocentric translations (geog2D domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 1874, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1767, 4189, 4170, 0.02d, "Geocentric translations (geog2D domain)", "", 0.64d, 16.75d, -73.38d, -58.95d, 1877, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1768, 4189, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 0.64d, 16.75d, -73.38d, -58.95d, 1880, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1769, 4248, 4189, 15.0d, "Molodensky-Badekas (CF geog2D domain)", "", 0.64d, 12.25d, -73.38d, -59.8d, 1883, 10), + new EpsgOperationRecord((EpsgOperationType)0, 1771, 4247, 4189, 15.0d, "Molodensky-Badekas (CF geog2D domain)", "", 0.64d, 12.25d, -73.38d, -59.8d, 1893, 10), + new EpsgOperationRecord((EpsgOperationType)0, 1773, 4190, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -58.41d, -21.78d, -73.59d, -52.63d, 1903, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1774, 4190, 4170, 0.0d, "Geocentric translations (geog2D domain)", "", -58.41d, -21.78d, -73.59d, -52.63d, 1906, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1775, 4178, 4258, 0.1d, "Position Vector transformation (geog2D domain)", "", 50.2d, 54.74d, 9.92d, 15.04d, 1909, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1776, 4314, 4258, 3.0d, "Position Vector transformation (geog2D domain)", "", 47.27d, 55.09d, 5.86d, 13.84d, 1916, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1777, 4314, 4326, 3.0d, "Position Vector transformation (geog2D domain)", "", 47.27d, 55.09d, 5.86d, 13.84d, 1923, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1778, 4314, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 47.27d, 50.34d, 6.11d, 13.84d, 1930, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1779, 4314, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 50.33d, 52.34d, 5.86d, 12.03d, 1937, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1780, 4314, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 52.33d, 55.09d, 6.56d, 11.59d, 1944, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1783, 4230, 4258, 2.0d, "Position Vector transformation (geog2D domain)", "", 34.42d, 43.45d, 25.62d, 44.83d, 1951, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1784, 4230, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", 34.42d, 43.45d, 25.62d, 44.83d, 1958, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1796, 4193, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 2.16d, 4.99d, 8.45d, 10.4d, 1965, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1797, 4194, 4326, 48.0d, "Geocentric translations (geog2D domain)", "", 59.74d, 79.0d, -73.29d, -42.52d, 1968, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1798, 4194, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 59.74d, 79.0d, -73.29d, -42.52d, 1971, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1799, 4195, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 68.66d, 74.58d, -29.69d, -19.89d, 1978, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1800, 4196, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 65.52d, 65.91d, -38.86d, -36.81d, 1985, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1801, 4282, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", -6.91d, -3.55d, 8.84d, 12.34d, 1992, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1802, 4282, 4326, 0.15d, "Position Vector transformation (geog2D domain)", "", -6.91d, -3.55d, 8.84d, 12.34d, 1995, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1803, 4202, 4283, 0.5d, "NTv2", "A66 National (13.09.01).gsb", -43.7d, -9.86d, 112.85d, 153.69d, 2002, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1804, 4203, 4283, 0.1d, "NTv2", "National 84 (02.07.01).gsb", -38.53d, -9.37d, 109.23d, 153.61d, 2002, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1805, 4197, 4324, 5.0d, "Geocentric translations (geog2D domain)", "", 8.92d, 9.87d, 12.9d, 14.19d, 2002, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1806, 4198, 4324, 5.0d, "Geocentric translations (geog2D domain)", "", 11.7d, 12.77d, 14.17d, 15.09d, 2005, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1807, 4284, 4326, 10.0d, "Position Vector transformation (geog2D domain)", "", 37.89d, 42.59d, 44.77d, 51.73d, 2008, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1808, 4284, 4326, 5.0d, "Position Vector transformation (geog2D domain)", "", 37.89d, 43.59d, 39.99d, 51.73d, 2015, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1809, 4284, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", 38.31d, 40.33d, 48.93d, 50.4d, 2022, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1810, 4230, 4326, 15.0d, "Position Vector transformation (geog2D domain)", "", 25.71d, 31.68d, 24.7d, 30.0d, 2029, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1811, 4248, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -1.05d, 5.6d, -51.64d, -48.0d, 2036, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1812, 4240, 4326, 3.0d, "Position Vector transformation (geog2D domain)", "", 5.63d, 20.46d, 97.34d, 105.64d, 2039, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1813, 4211, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -6.89d, -4.07d, 105.77d, 110.01d, 2046, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1814, 4211, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -8.46d, -6.8d, 112.8d, 117.01d, 2049, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1815, 4307, 4326, 5.0d, "Position Vector transformation (geog2D domain)", "", 25.0d, 32.0d, 1.0d, 3.3d, 2052, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1816, 4307, 4326, 100.0d, "Geocentric translations (geog2D domain)", "", 27.5d, 28.3d, 8.83d, 9.92d, 2059, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1817, 4307, 4326, 100.0d, "Geocentric translations (geog2D domain)", "", 31.75d, 32.42d, 7.16d, 8.0d, 2062, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1818, 4263, 4326, 12.0d, "Position Vector transformation (geog2D domain)", "", 1.92d, 6.14d, 2.66d, 7.82d, 2065, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1820, 4263, 4326, 12.0d, "Geocentric translations (geog2D domain)", "", 3.25d, 5.54d, 4.01d, 6.96d, 2072, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1821, 4263, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 3.25d, 4.51d, 7.16d, 8.25d, 2075, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1822, 4263, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 4.22d, 6.31d, 3.83d, 5.17d, 2078, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1823, 4263, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 3.24d, 3.86d, 5.58d, 8.0d, 2081, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1824, 4263, 4326, 25.0d, "Geocentric translations (geog2D domain)", "", 8.78d, 11.63d, 9.41d, 12.13d, 2084, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1825, 4611, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 22.13d, 22.58d, 113.76d, 114.51d, 2087, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1826, 4612, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 17.09d, 46.05d, 122.38d, 157.65d, 2094, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1828, 4310, 4322, 25.0d, "Geocentric translations (geog2D domain)", "", 10.64d, 16.7d, -20.22d, -11.36d, 2097, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1829, 4237, 4258, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 2100, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1830, 4237, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 2107, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1831, 4237, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 2114, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1832, 4238, 4326, 25.0d, "Position Vector transformation (geog2D domain)", "", -10.98d, 5.97d, 95.16d, 141.01d, 2117, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1833, 4238, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -10.98d, 5.97d, 95.16d, 141.01d, 2124, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1837, 4257, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -6.54d, -1.88d, 118.71d, 120.78d, 2131, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1838, 4613, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -1.24d, 0.0d, 116.72d, 117.99d, 2134, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1839, 4213, 4324, 15.0d, "Geocentric translations (geog2D domain)", "", 12.8d, 16.7d, 7.81d, 14.9d, 2137, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1840, 4614, 4326, 0.0d, "Position Vector transformation (geog2D domain)", "", 24.55d, 26.2d, 50.69d, 51.68d, 2140, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1842, 4617, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 38.21d, 86.46d, -141.01d, -40.73d, 2147, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1851, 4122, 4326, 1.5d, "NTv2", "NS778301.gsb", 43.41d, 47.08d, -66.28d, -59.73d, 2150, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1852, 4298, 4326, 5.0d, "Position Vector transformation (geog2D domain)", "", 1.56d, 7.67d, 109.31d, 117.31d, 2150, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1853, 4230, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 53.75d, 55.76d, -12.5d, -9.49d, 2157, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1854, 4132, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 26.21d, 26.87d, 52.49d, 53.43d, 2160, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1855, 4132, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 29.16d, 29.39d, 50.22d, 50.42d, 2163, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1856, 4154, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 26.58d, 26.71d, 52.07d, 52.28d, 2166, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1857, 4154, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 26.21d, 26.87d, 52.49d, 53.43d, 2169, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1858, 4154, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 29.16d, 29.39d, 50.22d, 50.42d, 2172, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1859, 4159, 4326, 20.0d, "Geocentric translations (geog2D domain)", "", 27.32d, 27.67d, 18.37d, 18.72d, 2175, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1860, 4159, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 27.32d, 27.67d, 18.37d, 18.72d, 2178, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1861, 4159, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 29.61d, 30.07d, 17.13d, 17.51d, 2181, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1862, 4159, 4326, 0.5d, "Position Vector transformation (geog2D domain)", "", 29.61d, 30.07d, 17.13d, 17.51d, 2184, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1863, 4159, 4326, 6.0d, "Coordinate Frame rotation (geog2D domain)", "", 29.61d, 30.07d, 17.13d, 17.51d, 2191, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1864, 4618, 4326, 19.0d, "Geocentric translations (geog2D domain)", "", -45.0d, 12.52d, -81.41d, -34.74d, 2198, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1865, 4618, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", -52.43d, -21.78d, -73.59d, -53.65d, 2201, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1866, 4618, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", -22.91d, -9.67d, -69.66d, -57.52d, 2204, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1867, 4618, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", -33.78d, 4.44d, -60.58d, -34.74d, 2207, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1868, 4618, 4326, 21.0d, "Geocentric translations (geog2D domain)", "", -45.0d, -17.5d, -75.22d, -67.0d, 2210, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1869, 4618, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -4.23d, 12.52d, -79.1d, -66.87d, 2213, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1870, 4618, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", -5.01d, 1.45d, -81.03d, -75.21d, 2216, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1871, 4618, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -1.41d, 0.18d, -91.72d, -89.19d, 2219, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1872, 4618, 4326, 12.0d, "Geocentric translations (geog2D domain)", "", 1.18d, 8.58d, -61.39d, -56.47d, 2222, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1873, 4618, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", -27.59d, -19.29d, -62.65d, -54.24d, 2225, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1874, 4618, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", -18.35d, -0.03d, -81.41d, -68.67d, 2228, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1875, 4618, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 9.99d, 10.9d, -61.98d, -60.85d, 2231, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1876, 4618, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 0.64d, 12.25d, -73.38d, -59.8d, 2234, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1877, 4618, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -35.71d, 7.04d, -74.01d, -25.28d, 2237, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1879, 4619, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 54.96d, 69.07d, 10.03d, 24.17d, 2240, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1880, 4620, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 11.83d, 14.23d, -4.64d, 4.0d, 2243, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1881, 4816, 4223, 0.0d, "Longitude rotation", "", 30.23d, 37.4d, 7.49d, 11.59d, 2246, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1883, 4820, 4613, 0.0d, "Longitude rotation", "", -4.24d, 4.29d, 114.55d, 119.06d, 2247, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1884, 4818, 4156, 0.0d, "Longitude rotation", "", 47.73d, 51.06d, 12.09d, 22.56d, 2248, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1885, 4184, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 36.87d, 37.96d, -25.92d, -24.72d, 2249, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1886, 4183, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 38.32d, 39.14d, -28.9d, -26.97d, 2252, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1887, 4182, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 39.3d, 39.77d, -31.34d, -31.02d, 2255, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1888, 4615, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 32.35d, 33.15d, -17.31d, -16.23d, 2258, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1890, 4176, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -90.0d, -60.0d, 45.0d, 160.0d, 2261, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1891, 4120, 4121, 5.0d, "Geographic2D offsets", "", 34.88d, 41.75d, 19.57d, 28.3d, 2264, 2), + new EpsgOperationRecord((EpsgOperationType)0, 1892, 4254, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -55.96d, -51.99d, -74.83d, -66.33d, 2266, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1893, 4139, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 17.62d, 18.78d, -67.97d, -64.25d, 2269, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1895, 4124, 4619, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", 54.96d, 69.07d, 10.03d, 24.17d, 2272, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1896, 4124, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 54.96d, 69.07d, 10.03d, 24.17d, 2279, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1897, 4613, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -4.24d, 4.29d, 114.55d, 119.06d, 2286, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1898, 4613, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -4.24d, 0.0d, 114.55d, 117.99d, 2289, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1899, 4613, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -0.06d, 4.29d, 116.96d, 119.06d, 2292, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1900, 4152, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 24.41d, 49.38d, -124.79d, -66.91d, 2295, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1901, 4152, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 24.41d, 49.38d, -124.79d, -66.91d, 2302, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1902, 4193, 4324, 5.0d, "Geocentric translations (geog2D domain)", "", 2.16d, 4.99d, 8.45d, 10.4d, 2309, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1903, 4621, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 17.82d, 18.17d, -63.21d, -62.73d, 2312, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1904, 4622, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 15.8d, 16.55d, -61.85d, -60.97d, 2315, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1905, 4622, 4326, 0.1d, "Position Vector transformation (geog2D domain)", "", 15.8d, 16.55d, -61.85d, -60.97d, 2318, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1906, 4623, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 3.43d, 5.81d, -54.45d, -51.61d, 2325, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1908, 4623, 4624, 1.0d, "Position Vector transformation (geog2D domain)", "", 3.43d, 5.81d, -54.45d, -51.61d, 2328, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1909, 4625, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 14.35d, 14.93d, -61.29d, -60.76d, 2335, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1910, 4625, 4326, 0.1d, "Position Vector transformation (geog2D domain)", "", 14.35d, 14.93d, -61.29d, -60.76d, 2338, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1912, 4627, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -24.72d, -18.28d, 51.83d, 58.24d, 2345, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1913, 4629, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -16.96d, -16.17d, -151.91d, -150.89d, 2348, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1914, 4630, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -9.57d, -8.72d, -140.31d, -139.44d, 2351, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1916, 4632, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -13.05d, -12.61d, 44.98d, 45.35d, 2354, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1917, 4633, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -21.24d, -20.62d, 166.98d, 167.52d, 2357, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1921, 4636, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -66.78d, -66.1d, 139.44d, 141.5d, 2360, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1922, 4637, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -67.13d, -65.61d, 136.0d, 142.0d, 2363, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1923, 4638, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 46.69d, 47.19d, -56.48d, -56.07d, 2366, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1924, 4628, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -17.93d, -17.41d, -150.0d, -149.09d, 2369, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1926, 4626, 4627, 0.1d, "Position Vector transformation (geog2D domain)", "", -21.42d, -20.81d, 55.16d, 55.91d, 2372, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1927, 4633, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -21.24d, -20.62d, 166.98d, 167.52d, 2379, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1928, 4641, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -21.71d, -21.32d, 167.75d, 168.19d, 2386, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1931, 4643, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -19.85d, -19.5d, 163.54d, 163.75d, 2393, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1946, 4617, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 38.21d, 86.46d, -141.01d, -40.73d, 2400, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1947, 4122, 4267, 1.5d, "Maritime Provinces polynomial interpolation", "TRNB7727.DAT", 44.56d, 48.07d, -69.05d, -63.7d, 2407, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1948, 4122, 4267, 1.5d, "Maritime Provinces polynomial interpolation", "TRNS7727.DAT", 43.41d, 47.08d, -66.28d, -59.73d, 2407, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1949, 4122, 4267, 1.5d, "Maritime Provinces polynomial interpolation", "TRPE7727.DAT", 45.9d, 47.09d, -64.49d, -61.9d, 2407, 0), + new EpsgOperationRecord((EpsgOperationType)0, 1950, 4269, 4617, 2.0d, "Geocentric translations (geog2D domain)", "", 40.0d, 64.21d, -67.75d, -43.99d, 2407, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1951, 4658, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 63.34d, 66.59d, -24.63d, -13.38d, 2410, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1952, 4659, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 59.96d, 69.59d, -30.87d, -5.55d, 2413, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1953, 4300, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 51.39d, 55.43d, -10.56d, -5.34d, 2416, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1954, 4300, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 51.39d, 55.43d, -10.56d, -5.34d, 2423, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1955, 4188, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 53.96d, 55.36d, -8.18d, -5.34d, 2430, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1956, 4300, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 51.39d, 55.43d, -10.56d, -5.34d, 2437, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1957, 4660, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 70.75d, 71.24d, -9.17d, -7.87d, 2440, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1958, 4661, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 55.67d, 58.09d, 19.06d, 28.24d, 2447, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1959, 4607, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 12.54d, 13.44d, -61.52d, -61.07d, 2450, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1962, 4662, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -22.45d, -20.03d, 163.92d, 167.09d, 2453, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1963, 4662, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -22.45d, -20.03d, 163.92d, 167.09d, 2456, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1964, 4627, 4626, 0.1d, "Position Vector transformation (geog2D domain)", "", -21.42d, -20.81d, 55.16d, 55.91d, 2463, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1965, 4616, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 29.98d, 30.21d, -16.11d, -15.79d, 2470, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1966, 4663, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 32.58d, 33.15d, -17.31d, -16.23d, 2473, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1967, 4663, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 32.58d, 33.15d, -17.31d, -16.23d, 2476, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1968, 4664, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 37.65d, 37.96d, -25.92d, -25.08d, 2483, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1969, 4664, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 37.65d, 37.96d, -25.92d, -25.08d, 2486, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1970, 4664, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 36.87d, 37.96d, -25.92d, -24.72d, 2493, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1971, 4664, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 36.87d, 37.96d, -25.92d, -24.72d, 2496, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1972, 4665, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 38.57d, 38.86d, -27.44d, -26.97d, 2503, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1973, 4665, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 38.57d, 38.86d, -27.44d, -26.97d, 2506, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1974, 4665, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 38.46d, 38.7d, -28.9d, -28.54d, 2513, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1975, 4665, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 38.46d, 38.7d, -28.9d, -28.54d, 2516, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1976, 4665, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 38.32d, 38.61d, -28.61d, -27.98d, 2523, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1977, 4665, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 38.32d, 38.61d, -28.61d, -27.98d, 2526, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1978, 4665, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 38.48d, 38.8d, -28.37d, -27.71d, 2533, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1979, 4665, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 38.48d, 38.8d, -28.37d, -27.71d, 2536, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1980, 4665, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 38.32d, 39.14d, -28.9d, -26.97d, 2543, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1981, 4665, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 38.32d, 39.14d, -28.9d, -26.97d, 2546, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1982, 4182, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 39.3d, 39.77d, -31.34d, -31.02d, 2553, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1983, 4274, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2556, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1984, 4207, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2559, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1985, 4230, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2562, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1986, 4666, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2565, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1987, 4274, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2568, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1988, 4207, 4326, 2.0d, "Coordinate Frame rotation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2575, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1989, 4230, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2582, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1990, 4666, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2589, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1991, 4904, 4666, 0.0d, "Longitude rotation", "", 36.95d, 42.16d, -9.56d, -6.19d, 2596, 1), + new EpsgOperationRecord((EpsgOperationType)0, 1992, 4274, 4258, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2597, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1993, 4667, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 29.06d, 30.32d, 46.36d, 48.61d, 2604, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1994, 4657, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 63.34d, 66.59d, -24.63d, -13.38d, 2607, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1995, 4316, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 43.62d, 48.27d, 20.26d, 29.74d, 2610, 3), + new EpsgOperationRecord((EpsgOperationType)0, 1997, 4207, 4258, 2.0d, "Position Vector transformation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 2613, 7), + new EpsgOperationRecord((EpsgOperationType)0, 1998, 4230, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 53.58d, 55.92d, 3.34d, 8.88d, 2620, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3817, 3819, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 2627, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3830, 3824, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 17.36d, 26.96d, 114.32d, 123.61d, 2634, 3), + new EpsgOperationRecord((EpsgOperationType)0, 3858, 4979, 3855, 0.113d, "Geographic3D to GravityRelatedHeight (EGM2008)", "Und_min2.5x2.5_egm2008_isw=82_WGS84_TideFree", -90.0d, 90.0d, -180.0d, 180.0d, 2637, 0), + new EpsgOperationRecord((EpsgOperationType)0, 3859, 4979, 3855, 0.11d, "Geographic3D to GravityRelatedHeight (EGM2008)", "Und_min1x1_egm2008_isw=82_WGS84_TideFree", -90.0d, 90.0d, -180.0d, 180.0d, 2637, 0), + new EpsgOperationRecord((EpsgOperationType)0, 3894, 3889, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 29.06d, 37.39d, 38.79d, 48.75d, 2637, 3), + new EpsgOperationRecord((EpsgOperationType)0, 3895, 4805, 4312, 0.0d, "Longitude rotation", "", 46.4d, 49.02d, 9.53d, 17.17d, 2640, 1), + new EpsgOperationRecord((EpsgOperationType)0, 3904, 4230, 4326, 5.0d, "Position Vector transformation (geog2D domain)", "", 51.44d, 55.77d, 2.53d, 6.37d, 2641, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3905, 4231, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 34.88d, 84.73d, -10.56d, 38.01d, 2648, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3913, 4805, 3906, 1.0d, "Longitude rotation", "", 40.85d, 46.88d, 13.38d, 23.04d, 2655, 1), + new EpsgOperationRecord((EpsgOperationType)0, 3914, 3906, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 2656, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3915, 3906, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 2663, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3916, 3906, 4765, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 2670, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3917, 3906, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 2677, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3918, 3906, 4765, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 45.44d, 46.53d, 13.38d, 14.58d, 2684, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3919, 3906, 4765, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 46.14d, 46.88d, 14.54d, 16.61d, 2691, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3921, 3906, 4765, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 45.42d, 46.22d, 14.55d, 15.73d, 2698, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3922, 3906, 4765, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 45.42d, 45.77d, 14.53d, 15.36d, 2705, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3923, 3906, 4765, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 45.7d, 46.12d, 14.47d, 15.73d, 2712, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3924, 3906, 4765, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 46.1d, 46.76d, 14.74d, 16.27d, 2719, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3925, 3906, 4765, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 46.47d, 46.88d, 15.96d, 16.61d, 2726, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3926, 3906, 4765, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 46.05d, 46.53d, 13.38d, 14.82d, 2733, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3927, 3906, 4765, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 45.44d, 46.08d, 13.47d, 14.58d, 2740, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3928, 3906, 4765, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 45.91d, 46.31d, 14.21d, 15.28d, 2747, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3929, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.16d, 46.49d, 13.38d, 13.87d, 2754, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3930, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.77d, 46.18d, 13.47d, 14.11d, 2768, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3931, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.64d, 45.9d, 13.57d, 14.12d, 2782, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3932, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.44d, 45.73d, 13.5d, 14.24d, 2796, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3933, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.09d, 46.53d, 13.7d, 14.18d, 2810, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3934, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.14d, 46.45d, 14.01d, 14.61d, 2824, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3935, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.86d, 46.21d, 13.95d, 14.52d, 2838, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3936, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.47d, 45.88d, 14.08d, 14.59d, 2852, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3937, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.21d, 46.56d, 14.55d, 15.0d, 2866, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3938, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.85d, 46.32d, 14.45d, 15.01d, 2880, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3939, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.66d, 45.89d, 14.57d, 15.08d, 2894, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3940, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.46d, 45.76d, 14.6d, 15.12d, 2908, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3941, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.46d, 46.66d, 14.83d, 15.51d, 2922, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3951, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.28d, 46.51d, 14.9d, 15.26d, 2936, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3952, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.0d, 46.32d, 14.84d, 15.35d, 2950, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3953, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.81d, 46.01d, 14.97d, 15.43d, 2964, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3954, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.66d, 45.87d, 15.06d, 15.47d, 2978, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3955, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.42d, 45.74d, 15.06d, 15.36d, 2992, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3956, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.28d, 46.58d, 15.15d, 15.54d, 3006, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3957, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.14d, 46.46d, 15.31d, 16.0d, 3020, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3958, 3912, 3794, 0.2d, "Transverse Mercator", "", 45.81d, 46.17d, 15.2d, 15.73d, 3034, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3959, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.33d, 46.74d, 15.44d, 16.0d, 3048, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3960, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.29d, 46.76d, 15.9d, 16.3d, 3062, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3961, 3912, 3794, 0.2d, "Transverse Mercator", "", 46.47d, 46.88d, 15.98d, 16.61d, 3076, 14), + new EpsgOperationRecord((EpsgOperationType)0, 3962, 3906, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 40.85d, 46.88d, 13.38d, 23.04d, 3090, 3), + new EpsgOperationRecord((EpsgOperationType)0, 3963, 3906, 4761, 1.0d, "Position Vector transformation (geog2D domain)", "", 42.34d, 46.54d, 13.43d, 19.43d, 3093, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3964, 3906, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 42.34d, 46.54d, 13.43d, 19.43d, 3100, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3965, 3906, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 41.79d, 43.56d, 18.45d, 20.38d, 3107, 3), + new EpsgOperationRecord((EpsgOperationType)0, 3971, 4248, 4170, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", -5.01d, 1.45d, -81.03d, -75.21d, 3110, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3972, 4224, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -15.94d, -15.37d, -48.1d, -47.1d, 3117, 3), + new EpsgOperationRecord((EpsgOperationType)0, 3990, 4248, 4326, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", -5.01d, 1.45d, -81.03d, -75.21d, 3120, 7), + new EpsgOperationRecord((EpsgOperationType)0, 3998, 4210, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", -4.45d, -2.3d, 28.98d, 30.86d, 3127, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4064, 4046, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -13.46d, -3.41d, 11.79d, 29.81d, 3130, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4065, 4695, 4046, 1.5d, "Geocentric translations (geog2D domain)", "", -12.01d, -11.13d, 26.38d, 27.75d, 3133, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4066, 4695, 4326, 1.5d, "Geocentric translations (geog2D domain)", "", -12.01d, -11.13d, 26.38d, 27.75d, 3136, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4067, 4695, 4046, 0.5d, "Molodensky-Badekas (CF geog2D domain)", "", -12.01d, -11.13d, 26.38d, 27.75d, 3139, 10), + new EpsgOperationRecord((EpsgOperationType)0, 4068, 4695, 4326, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", -12.01d, -11.13d, 26.38d, 27.75d, 3149, 10), + new EpsgOperationRecord((EpsgOperationType)0, 4069, 4224, 4674, 5.0d, "Geocentric translations (geog2D domain)", "", -15.94d, -15.37d, -48.1d, -47.1d, 3159, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4072, 3392, 3891, 3.0d, "Transverse Mercator", "", 29.06d, 32.51d, 43.98d, 48.61d, 3162, 12), + new EpsgOperationRecord((EpsgOperationType)0, 4077, 4075, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 3174, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4078, 4231, 4258, 0.3d, "Position Vector transformation (geog2D domain)", "", 34.88d, 84.73d, -10.56d, 38.01d, 3177, 7), + new EpsgOperationRecord((EpsgOperationType)0, 4084, 4081, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 24.6d, 32.76d, -21.93d, -11.75d, 3184, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4290, 4475, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -13.05d, -12.61d, 44.98d, 45.35d, 3187, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4441, 4440, 5767, 0.03d, "Vertical Offset", "", -36.41d, -34.36d, 172.61d, 174.83d, 3190, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4442, 4440, 5759, 0.05d, "Vertical Offset", "", -37.67d, -36.12d, 174.0d, 176.17d, 3191, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4443, 4440, 5764, 0.06d, "Vertical Offset", "", -40.59d, -37.52d, 174.57d, 177.26d, 3192, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4444, 4440, 5766, 0.07d, "Vertical Offset", "", -42.44d, -40.44d, 171.82d, 174.46d, 3193, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4445, 4440, 5762, 0.02d, "Vertical Offset", "", -39.04d, -37.49d, 176.41d, 178.63d, 3194, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4446, 4440, 5765, 0.05d, "Vertical Offset", "", -40.57d, -38.87d, 175.8d, 178.07d, 3195, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4447, 4440, 5769, 0.05d, "Vertical Offset", "", -39.92d, -38.41d, 173.68d, 174.95d, 3196, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4448, 4440, 5770, 0.04d, "Vertical Offset", "", -41.67d, -40.12d, 174.52d, 176.55d, 3197, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4449, 4440, 5763, 0.09d, "Vertical Offset", "", -44.92d, -41.6d, 168.95d, 173.77d, 3198, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4450, 4440, 5761, 0.07d, "Vertical Offset", "", -46.4d, -43.82d, 167.73d, 171.28d, 3199, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4451, 4440, 5760, 0.05d, "Vertical Offset", "", -46.71d, -46.26d, 168.01d, 168.86d, 3200, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4452, 4440, 5772, 0.15d, "Vertical Offset", "", -47.33d, -46.63d, 167.29d, 168.34d, 3201, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4453, 4440, 4458, 0.04d, "Vertical Offset", "", -46.73d, -44.52d, 166.37d, 169.95d, 3202, 1), + new EpsgOperationRecord((EpsgOperationType)0, 4459, 4959, 4440, 0.1d, "Geographic3D to GravityRelatedHeight (NZgeoid)", "nzgeoid09.sid", -55.95d, -25.88d, 160.6d, -171.2d, 3203, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4461, 4152, 4759, 0.1d, "Geocentric translations (geog2D domain)", "", 24.41d, 49.38d, -124.79d, -66.91d, 3203, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4476, 4470, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -14.49d, -11.33d, 43.68d, 46.7d, 3206, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4477, 4463, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 43.41d, 47.37d, -57.1d, -55.9d, 3209, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4478, 4475, 4470, 0.1d, "Geocentric translations (geog2D domain)", "", -13.05d, -12.61d, 44.98d, 45.35d, 3212, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4560, 4558, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 14.08d, 18.54d, -63.66d, -57.52d, 3215, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4561, 4557, 5756, 998.0d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggm00.txt", 14.35d, 14.93d, -61.29d, -60.76d, 3218, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4562, 4557, 5757, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggg00.txt", 15.88d, 16.55d, -61.85d, -61.15d, 3218, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4563, 4557, 5617, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggg00_mg.txt", 15.8d, 16.05d, -61.39d, -61.13d, 3218, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4564, 4557, 5620, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggg00_sm.txt", 18.01d, 18.17d, -63.21d, -62.96d, 3218, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4565, 4557, 5616, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggg00_ls.txt", 15.8d, 15.94d, -61.68d, -61.52d, 3218, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4566, 4557, 5618, 0.5d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggg00_ld.txt", 16.26d, 16.38d, -61.13d, -60.97d, 3218, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4567, 4557, 5619, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggg00_sb.txt", 17.82d, 17.98d, -62.92d, -62.73d, 3218, 0), + new EpsgOperationRecord((EpsgOperationType)0, 4649, 31467, 4647, 0.05d, "Transverse Mercator", "shTransCom.dll", 53.37d, 55.09d, 7.8d, 10.5d, 3218, 10), + new EpsgOperationRecord((EpsgOperationType)0, 4650, 31468, 4647, 0.05d, "Transverse Mercator", "shTransCom.dll", 53.36d, 54.59d, 10.49d, 11.4d, 3228, 10), + new EpsgOperationRecord((EpsgOperationType)0, 4651, 5701, 5730, 0.1d, "Vertical Offset and Slope", "", 49.93d, 58.71d, -7.06d, 1.8d, 3238, 6), + new EpsgOperationRecord((EpsgOperationType)0, 4827, 4156, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 3244, 7), + new EpsgOperationRecord((EpsgOperationType)0, 4829, 4156, 4258, 0.5d, "Molodensky-Badekas (CF geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 3251, 10), + new EpsgOperationRecord((EpsgOperationType)0, 4830, 4289, 4258, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 3261, 7), + new EpsgOperationRecord((EpsgOperationType)0, 4831, 4289, 4258, 0.5d, "Molodensky-Badekas (CF geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 3268, 10), + new EpsgOperationRecord((EpsgOperationType)0, 4832, 4483, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 12.1d, 32.72d, -122.19d, -84.64d, 3278, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4833, 4289, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 3281, 7), + new EpsgOperationRecord((EpsgOperationType)0, 4834, 4224, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -15.94d, -15.37d, -48.1d, -47.1d, 3288, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4836, 4156, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 3291, 7), + new EpsgOperationRecord((EpsgOperationType)0, 4840, 4624, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 2.11d, 8.88d, -54.61d, -49.45d, 3298, 3), + new EpsgOperationRecord((EpsgOperationType)0, 4905, 5013, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 29.24d, 43.07d, -35.58d, -12.48d, 3301, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5021, 4663, 5013, 2.0d, "Geocentric translations (geog2D domain)", "", 32.35d, 33.15d, -17.31d, -16.23d, 3304, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5022, 4663, 5013, 1.0d, "Position Vector transformation (geog2D domain)", "", 32.58d, 32.93d, -17.31d, -16.66d, 3307, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5023, 4663, 5013, 0.2d, "Geocentric translations (geog2D domain)", "", 32.96d, 33.15d, -16.44d, -16.23d, 3314, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5024, 4664, 5013, 2.0d, "Geocentric translations (geog2D domain)", "", 36.87d, 37.96d, -25.92d, -24.72d, 3317, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5025, 4664, 5013, 0.3d, "Geocentric translations (geog2D domain)", "", 37.65d, 37.96d, -25.92d, -25.08d, 3320, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5026, 4664, 5013, 0.1d, "Geocentric translations (geog2D domain)", "", 36.87d, 37.08d, -25.26d, -24.96d, 3323, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5027, 4665, 5013, 2.0d, "Geocentric translations (geog2D domain)", "", 38.32d, 39.14d, -28.9d, -26.97d, 3326, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5028, 4665, 5013, 0.5d, "Geocentric translations (geog2D domain)", "", 38.46d, 38.7d, -28.9d, -28.54d, 3329, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5029, 4665, 5013, 0.2d, "Geocentric translations (geog2D domain)", "", 38.97d, 39.14d, -28.13d, -27.88d, 3332, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5030, 4665, 5013, 1.0d, "Geocentric translations (geog2D domain)", "", 38.32d, 38.61d, -28.61d, -27.98d, 3335, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5031, 4665, 5013, 0.8d, "Geocentric translations (geog2D domain)", "", 38.48d, 38.8d, -28.37d, -27.71d, 3338, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5032, 4665, 5013, 0.6d, "Geocentric translations (geog2D domain)", "", 38.57d, 38.86d, -27.44d, -26.97d, 3341, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5033, 4182, 5013, 0.5d, "Geocentric translations (geog2D domain)", "", 39.3d, 39.77d, -31.34d, -31.02d, 3344, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5034, 4182, 5013, 0.2d, "Geocentric translations (geog2D domain)", "", 39.3d, 39.58d, -31.34d, -31.07d, 3347, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5035, 4182, 5013, 0.3d, "Geocentric translations (geog2D domain)", "", 39.63d, 39.77d, -31.18d, -31.02d, 3350, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5036, 4274, 11108, 3.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 3353, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5037, 4274, 11108, 2.0d, "Position Vector transformation (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 3356, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5038, 4207, 11108, 2.5d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 3363, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5039, 4666, 4258, 5.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 3366, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5040, 4230, 4258, 5.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 3369, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5043, 4200, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 39.87d, 85.19d, 18.92d, -168.97d, 3372, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5044, 4284, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", 41.19d, 81.91d, 19.58d, -168.97d, 3379, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5050, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -35.71d, -22.66d, -53.38d, -40.2d, 3386, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5051, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -35.71d, -22.66d, -53.38d, -40.2d, 3389, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5052, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -25.91d, -20.45d, -42.04d, -37.11d, 3392, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5053, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -25.91d, -20.45d, -42.04d, -37.11d, 3395, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5054, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -22.04d, -17.59d, -40.37d, -35.18d, 3398, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5055, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -22.04d, -17.59d, -40.37d, -35.18d, 3401, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5056, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -17.7d, -13.01d, -39.22d, -34.6d, 3404, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5057, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -17.7d, -13.01d, -39.22d, -34.6d, 3407, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5058, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -13.57d, -11.18d, -39.09d, -35.31d, 3410, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5059, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -13.57d, -11.18d, -39.09d, -35.31d, 3413, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5060, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -12.27d, -8.39d, -39.14d, -37.09d, 3416, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5061, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -12.27d, -8.39d, -39.14d, -37.09d, 3419, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5062, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -13.58d, -8.73d, -37.34d, -32.01d, 3422, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5063, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -13.58d, -8.73d, -37.34d, -32.01d, 3425, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5064, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -10.17d, -4.6d, -35.1d, -29.13d, 3428, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5065, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -10.17d, -4.6d, -35.1d, -29.13d, 3431, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5066, 4208, 4674, 0.5d, "Geocentric translations (geog2D domain)", "", -6.5d, 4.26d, -44.79d, -26.0d, 3434, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5067, 4208, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -6.5d, 4.26d, -44.79d, -26.0d, 3437, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5073, 31468, 25832, 0.02d, "Transverse Mercator", "gntrans.dll", 51.55d, 53.38d, 10.5d, 11.59d, 3440, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5074, 31467, 25832, 0.02d, "Transverse Mercator", "gntrans.dll", 51.28d, 53.95d, 7.5d, 10.51d, 3450, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5075, 31466, 25832, 0.02d, "Transverse Mercator", "gntrans.dll", 52.23d, 53.81d, 6.56d, 7.51d, 3460, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5077, 4743, 3889, 0.3d, "Geocentric translations (geog2D domain)", "", 29.06d, 37.39d, 38.79d, 48.61d, 3470, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5078, 4743, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 29.06d, 37.39d, 38.79d, 48.61d, 3473, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5133, 5132, 4301, 0.0d, "Longitude rotation", "", 20.37d, 45.54d, 122.83d, 154.05d, 3476, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5134, 5132, 4162, 0.0d, "Longitude rotation", "", 33.14d, 38.64d, 124.53d, 131.01d, 3477, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5166, 23031, 25831, 0.05d, "Transverse Mercator", "", 40.49d, 42.86d, 0.16d, 3.39d, 3478, 14), + new EpsgOperationRecord((EpsgOperationType)0, 5189, 4162, 4737, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 33.14d, 38.64d, 124.53d, 131.01d, 3492, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5191, 4162, 4326, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 33.14d, 38.64d, 124.53d, 131.01d, 3502, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5194, 4756, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 9.35d, 11.04d, 104.24d, 107.11d, 3512, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5196, 5610, 5730, 0.1d, "Vertical Offset and Slope", "", 42.34d, 46.54d, 13.43d, 19.43d, 3519, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5197, 5610, 5621, 0.1d, "Vertical Offset and Slope", "", 42.34d, 46.54d, 13.43d, 19.43d, 3525, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5198, 5710, 5730, 0.1d, "Vertical Offset and Slope", "", 49.5d, 51.51d, 2.5d, 6.4d, 3531, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5199, 5710, 5621, 0.1d, "Vertical Offset and Slope", "", 49.5d, 51.51d, 2.5d, 6.4d, 3537, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5200, 5786, 5621, 0.1d, "Vertical Offset and Slope", "", 41.24d, 44.23d, 22.36d, 28.68d, 3543, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5201, 8357, 5730, 0.1d, "Vertical Offset and Slope", "", 48.58d, 51.06d, 12.09d, 18.86d, 3549, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5202, 8357, 5621, 0.1d, "Vertical Offset and Slope", "", 48.58d, 51.06d, 12.09d, 18.86d, 3555, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5203, 5705, 5621, 0.1d, "Vertical Offset and Slope", "", 57.52d, 59.75d, 21.74d, 28.2d, 3561, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5204, 5705, 5621, 0.1d, "Vertical Offset and Slope", "", 53.89d, 56.45d, 20.86d, 26.82d, 3567, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5205, 5781, 5730, 0.1d, "Vertical Offset and Slope", "", 43.62d, 48.27d, 20.26d, 29.74d, 3573, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5206, 5781, 5621, 0.1d, "Vertical Offset and Slope", "", 43.62d, 48.27d, 20.26d, 29.74d, 3579, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5207, 5728, 5621, 0.1d, "Vertical Offset and Slope", "", 45.81d, 47.81d, 5.95d, 10.5d, 3585, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5208, 5613, 5621, 0.1d, "Vertical Offset and Slope", "", 55.28d, 69.07d, 10.93d, 24.17d, 3591, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5209, 5705, 5730, 0.1d, "Vertical Offset and Slope", "", 55.67d, 58.09d, 20.87d, 28.24d, 3597, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5210, 5705, 5621, 0.1d, "Vertical Offset and Slope", "", 55.67d, 58.09d, 20.87d, 28.24d, 3603, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5211, 5783, 5621, 0.1d, "Vertical Offset and Slope", "", 47.27d, 55.09d, 5.86d, 15.04d, 3609, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5212, 5784, 5621, 0.1d, "Vertical Offset and Slope", "", 47.27d, 55.09d, 5.86d, 13.84d, 3615, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5213, 5214, 5730, 0.1d, "Vertical Offset and Slope", "", 37.86d, 47.1d, 6.62d, 18.58d, 3621, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5215, 5214, 5621, 0.1d, "Vertical Offset and Slope", "", 37.86d, 47.1d, 6.62d, 18.58d, 3627, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5216, 5214, 5730, 0.1d, "Vertical Offset and Slope", "", 36.59d, 38.35d, 12.36d, 15.71d, 3633, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5217, 5214, 5621, 0.1d, "Vertical Offset and Slope", "", 36.59d, 38.35d, 12.36d, 15.71d, 3639, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5226, 5228, 11070, 0.0d, "Coordinate Frame rotation (geog2D domain)", "", 48.58d, 51.06d, 12.09d, 18.86d, 3645, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5227, 5228, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 48.58d, 51.06d, 12.09d, 18.86d, 3652, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5236, 5233, 4326, 14.0d, "Coordinate Frame rotation (geog2D domain)", "", 5.86d, 9.88d, 79.64d, 81.95d, 3659, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5238, 5229, 5228, 0.0d, "Longitude rotation", "", 48.58d, 51.06d, 12.09d, 18.86d, 3666, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5239, 4156, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 48.58d, 51.06d, 12.09d, 18.86d, 3667, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5241, 4156, 5228, 0.0d, "Geographic2D offsets", "", 48.58d, 51.06d, 12.09d, 18.86d, 3674, 2), + new EpsgOperationRecord((EpsgOperationType)0, 5249, 4298, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 4.01d, 6.31d, 112.37d, 115.37d, 3676, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5260, 5252, 4258, 0.1d, "Position Vector transformation (geog2D domain)", "", 34.42d, 43.45d, 25.62d, 44.83d, 3683, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5261, 5252, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 34.42d, 43.45d, 25.62d, 44.83d, 3690, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5267, 5264, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 26.7d, 28.33d, 88.74d, 92.13d, 3693, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5327, 5324, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 59.96d, 69.59d, -30.87d, -5.55d, 3696, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5334, 4937, 5732, 0.03d, "Geographic3D to GravityRelatedHeight (OSGM02-Ire)", "OSGM02_NI.txt", 53.96d, 55.36d, -8.18d, -5.34d, 3699, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5335, 4937, 5731, 0.04d, "Geographic3D to GravityRelatedHeight (OSGM02-Ire)", "OSGM02_RoI.txt", 51.39d, 55.43d, -10.56d, -5.34d, 3699, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5338, 4277, 4258, 0.03d, "NTv2", "OSTN02_NTv2.gsb", 49.81d, 60.93d, -8.69d, 1.91d, 3699, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5339, 4277, 4326, 1.0d, "NTv2", "OSTN02_NTv2.gsb", 49.81d, 60.93d, -8.69d, 1.91d, 3699, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5350, 4221, 5340, 5.0d, "Geocentric translations (geog2D domain)", "", -52.43d, -21.78d, -73.59d, -53.65d, 3699, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5351, 5340, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -58.41d, -21.78d, -73.59d, -52.63d, 3702, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5374, 5354, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -22.91d, -9.67d, -69.66d, -57.52d, 3705, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5376, 5365, 4326, 1.5d, "Geocentric translations (geog2D domain)", "", 2.15d, 11.77d, -90.45d, -81.43d, 3708, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5377, 5371, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 5.0d, 12.51d, -84.32d, -77.04d, 3711, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5378, 5373, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -21.05d, -0.03d, -84.68d, -68.67d, 3714, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5384, 5381, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -37.77d, -30.09d, -58.49d, -50.01d, 3717, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5385, 4309, 5381, 1.5d, "Position Vector transformation (geog2D domain)", "", -35.0d, -30.09d, -58.49d, -53.09d, 3720, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5386, 4309, 4326, 1.5d, "Position Vector transformation (geog2D domain)", "", -35.0d, -30.09d, -58.49d, -53.09d, 3727, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5395, 5393, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 9.97d, 14.44d, -91.43d, -87.65d, 3734, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5403, 5734, 5706, 0.0d, "Height Depth Reversal", "", 37.89d, 42.59d, 48.66d, 51.73d, 3737, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5415, 5778, 5730, 0.1d, "Vertical Offset and Slope", "", 46.4d, 49.02d, 9.53d, 17.17d, 3738, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5416, 5786, 5730, 0.1d, "Vertical Offset and Slope", "", 41.24d, 44.23d, 22.36d, 28.68d, 3744, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5417, 5733, 5730, 0.1d, "Vertical Offset and Slope", "", 54.5d, 57.81d, 7.98d, 15.28d, 3750, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5419, 5720, 5730, 0.1d, "Vertical Offset", "", 42.33d, 51.14d, -4.87d, 8.23d, 3756, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5420, 5783, 5730, 0.1d, "Vertical Offset and Slope", "", 47.27d, 55.09d, 5.86d, 15.04d, 3757, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5421, 5784, 5730, 0.1d, "Vertical Offset and Slope", "", 47.27d, 55.09d, 5.86d, 13.84d, 3763, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5422, 5785, 5730, 0.1d, "Vertical Offset and Slope", "", 50.2d, 54.74d, 9.92d, 15.04d, 3769, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5424, 5787, 5730, 0.1d, "Vertical Offset and Slope", "", 45.74d, 48.58d, 16.11d, 22.9d, 3775, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5425, 5709, 5730, 0.1d, "Vertical Offset", "", 50.75d, 53.7d, 3.2d, 7.22d, 3781, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5426, 5776, 5730, 0.1d, "Vertical Offset and Slope", "", 57.9d, 71.24d, 4.39d, 31.32d, 3782, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5427, 5780, 5730, 0.1d, "Vertical Offset", "", 36.95d, 42.16d, -9.56d, -6.19d, 3788, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5428, 5779, 5730, 0.1d, "Vertical Offset and Slope", "", 45.42d, 46.88d, 13.38d, 16.61d, 3789, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5429, 5782, 5730, 0.1d, "Vertical Offset and Slope", "", 35.95d, 43.82d, -9.37d, 3.39d, 3795, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5430, 5718, 5730, 0.1d, "Vertical Offset and Slope", "", 55.28d, 69.07d, 10.93d, 24.17d, 3801, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5431, 5728, 5730, 0.1d, "Vertical Offset and Slope", "", 45.81d, 47.81d, 5.95d, 10.5d, 3807, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5432, 5717, 5730, 0.1d, "Vertical Offset", "", 59.75d, 70.09d, 19.24d, 31.59d, 3813, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5435, 8357, 5730, 0.1d, "Vertical Offset and Slope", "", 47.73d, 49.61d, 16.84d, 22.56d, 3814, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5436, 5705, 5730, 0.1d, "Vertical Offset and Slope", "", 57.52d, 59.75d, 21.74d, 28.2d, 3820, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5437, 5705, 5730, 0.1d, "Vertical Offset and Slope", "", 53.89d, 56.45d, 20.86d, 26.82d, 3826, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5438, 5705, 5611, 0.0d, "Vertical Offset", "", 37.35d, 46.97d, 46.95d, 53.93d, 3832, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5440, 5612, 5706, 0.0d, "Height Depth Reversal", "", 37.35d, 46.97d, 46.95d, 53.93d, 3833, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5443, 5705, 5797, 0.0d, "Vertical Offset", "", 37.89d, 42.59d, 48.66d, 51.73d, 3834, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5445, 5612, 5734, 0.0d, "Height Depth Reversal", "", 37.89d, 42.59d, 48.66d, 51.73d, 3835, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5447, 5705, 5735, 0.0d, "Vertical Offset", "", 41.04d, 43.59d, 39.99d, 46.72d, 3836, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5450, 5790, 5788, 0.1d, "Vertical Offset", "", 28.53d, 30.09d, 46.54d, 48.48d, 3837, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5452, 5732, 5731, 0.01d, "Vertical Offset", "", 53.96d, 55.36d, -8.18d, -5.34d, 3838, 1), + new EpsgOperationRecord((EpsgOperationType)0, 5470, 5451, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 7.98d, 11.22d, -85.97d, -82.53d, 3839, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5483, 4181, 4258, 0.0d, "Molodensky-Badekas (CF geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 3842, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5484, 4181, 4326, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 3852, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5485, 4181, 4258, 0.0d, "Coordinate Frame rotation (geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 3862, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5486, 4181, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 3869, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5491, 4625, 5489, 0.1d, "Position Vector transformation (geog2D domain)", "", 14.35d, 14.93d, -61.29d, -60.76d, 3876, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5492, 4622, 5489, 10.0d, "Position Vector transformation (geog2D domain)", "", 15.8d, 16.55d, -61.85d, -60.97d, 3883, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5493, 4621, 5489, 10.0d, "Position Vector transformation (geog2D domain)", "", 17.82d, 18.17d, -63.21d, -62.73d, 3890, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5494, 4558, 5489, 0.1d, "Position Vector transformation (geog2D domain)", "", 14.08d, 16.36d, -62.82d, -57.52d, 3897, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5495, 4558, 5489, 0.1d, "Position Vector transformation (geog2D domain)", "", 15.8d, 16.55d, -61.85d, -60.97d, 3904, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5496, 4558, 5489, 0.1d, "Position Vector transformation (geog2D domain)", "", 17.82d, 18.17d, -63.21d, -62.73d, 3911, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5497, 5340, 4674, 0.1d, "Geocentric translations (geog2D domain)", "", -58.41d, -21.78d, -73.59d, -52.63d, 3918, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5501, 5489, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 14.08d, 18.54d, -63.66d, -57.52d, 3921, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5502, 5488, 5756, 998.0d, "Geographic3D to GravityRelatedHeight (IGN1997)", "gg10_mart.txt", 14.35d, 14.93d, -61.29d, -60.76d, 3924, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5503, 5488, 5757, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "gg10_gtbt.txt", 15.88d, 16.55d, -61.85d, -61.15d, 3924, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5504, 5488, 5617, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "gg10_mg.txt", 15.8d, 16.05d, -61.39d, -61.13d, 3924, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5505, 5488, 5620, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "gg10_sm.txt", 18.01d, 18.17d, -63.21d, -62.96d, 3924, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5506, 5488, 5616, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "gg10_ls.txt", 15.8d, 15.94d, -61.68d, -61.52d, 3924, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5507, 5488, 5618, 0.5d, "Geographic3D to GravityRelatedHeight (IGN1997)", "gg10_ld.txt", 16.26d, 16.38d, -61.13d, -60.97d, 3924, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5508, 5488, 5619, 0.2d, "Geographic3D to GravityRelatedHeight (IGN1997)", "gg10_sb.txt", 17.82d, 17.98d, -62.92d, -62.73d, 3924, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5521, 4646, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -11.99d, -11.31d, 43.16d, 43.55d, 3924, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5525, 5524, 4674, 2.0d, "NTv2", "CA61_003.gsb", -27.5d, -14.99d, -58.16d, -38.82d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5526, 4225, 4674, 2.0d, "NTv2", "CA7072_003.gsb", -32.75d, -2.68d, -58.16d, -34.74d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5528, 4618, 4674, 1.0d, "NTv2", "SAD69_003.gsb", -33.78d, 4.44d, -60.58d, -34.74d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5529, 5527, 4674, 0.5d, "NTv2", "SAD96_003.gsb", -33.78d, 4.44d, -60.58d, -34.74d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5540, 5524, 4326, 2.0d, "NTv2", "CA61_003.gsb", -27.5d, -14.99d, -58.16d, -38.82d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5541, 4225, 4326, 2.0d, "NTv2", "CA7072_003.gsb", -32.75d, -2.68d, -58.16d, -34.74d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5542, 4618, 4326, 2.0d, "NTv2", "SAD69_003.gsb", -33.78d, 4.44d, -60.58d, -34.74d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5543, 5527, 4326, 1.0d, "NTv2", "SAD96_003.gsb", -33.78d, 4.44d, -60.58d, -34.74d, 3927, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5553, 5546, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -14.75d, 2.58d, 139.2d, 162.81d, 3927, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5557, 5778, 5621, 0.1d, "Vertical Offset and Slope", "", 46.4d, 49.02d, 9.53d, 17.17d, 3930, 6), + new EpsgOperationRecord((EpsgOperationType)0, 5585, 4023, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 45.44d, 48.47d, 26.63d, 30.13d, 3936, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5586, 4284, 5561, 3.5d, "Geocentric translations (geog2D domain)", "", 43.18d, 52.38d, 22.15d, 40.18d, 3939, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5590, 5561, 4326, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", 43.18d, 52.38d, 22.15d, 40.18d, 3942, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5599, 5593, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 54.33d, 54.83d, 10.66d, 12.01d, 3949, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5622, 4277, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", 50.53d, 50.8d, -2.2d, -1.68d, 3952, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5626, 5592, 5597, 0.1d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "fehmarn_geoid10.gri", 54.42d, 54.76d, 11.17d, 11.51d, 3955, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5630, 4307, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 26.06d, 27.51d, 1.24d, 2.92d, 3955, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5656, 4939, 5711, 0.15d, "Geographic3D to GravityRelatedHeight (AUSGeoid v2)", "AUSGeoid09_V1.01.gsb", -39.2d, -10.65d, 112.85d, 153.69d, 3958, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5657, 4939, 5712, 0.03d, "Geographic3D to GravityRelatedHeight (AUSGeoid v2)", "AUSGeoid09_GDA94_V1.01_DOV_windows.gsb", -43.7d, -40.24d, 144.55d, 148.44d, 3958, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5660, 4307, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 18.97d, 38.8d, -8.67d, 11.99d, 3958, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5661, 4230, 11134, 0.05d, "NTv2", "100800401.gsb", 40.49d, 42.86d, 0.16d, 3.39d, 3965, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5662, 4202, 5546, 2.0d, "Geocentric translations (geog2D domain)", "", -8.28d, -5.59d, 142.24d, 144.75d, 3965, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5686, 5682, 25832, 0.05d, "Transverse Mercator", "gntrans.dll", 49.11d, 53.81d, 5.86d, 7.51d, 3968, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5687, 5683, 25832, 0.05d, "Transverse Mercator", "gntrans.dll", 47.27d, 55.09d, 7.5d, 10.51d, 3978, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5688, 5684, 25832, 0.1d, "Transverse Mercator", "gntrans.dll", 47.39d, 54.59d, 10.5d, 12.0d, 3988, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5689, 5684, 25833, 0.1d, "Transverse Mercator", "gntrans.dll", 47.46d, 54.74d, 12.0d, 13.51d, 3998, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5690, 5685, 25833, 0.1d, "Transverse Mercator", "gntrans.dll", 48.51d, 54.72d, 13.5d, 15.04d, 4008, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5691, 5682, 5676, 0.05d, "Transverse Mercator", "gntrans.dll", 49.11d, 53.81d, 5.86d, 7.51d, 4018, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5692, 5683, 5676, 0.05d, "Transverse Mercator", "gntrans.dll", 47.27d, 55.09d, 7.5d, 10.51d, 4028, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5693, 5684, 5678, 0.05d, "Transverse Mercator", "gntrans.dll", 47.39d, 54.59d, 10.5d, 13.51d, 4038, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5694, 5685, 5679, 0.05d, "Transverse Mercator", "gntrans.dll", 48.51d, 48.98d, 13.5d, 13.84d, 4048, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5695, 5683, 5673, 0.1d, "Transverse Mercator", "gntrans.dll", 50.35d, 51.56d, 9.92d, 10.5d, 4058, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5696, 5684, 5674, 0.1d, "Transverse Mercator", "gntrans.dll", 50.2d, 54.74d, 10.5d, 13.51d, 4068, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5697, 5685, 5675, 0.1d, "Transverse Mercator", "gntrans.dll", 50.62d, 54.72d, 13.5d, 15.04d, 4078, 10), + new EpsgOperationRecord((EpsgOperationType)0, 5826, 5681, 4258, 0.0d, "Coordinate Frame rotation (geog2D domain)", "", 47.27d, 55.09d, 5.86d, 15.04d, 4088, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5827, 4202, 4283, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", -35.93d, -35.12d, 148.76d, 149.4d, 4095, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5840, 5561, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 43.18d, 52.38d, 22.15d, 40.18d, 4102, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5841, 4202, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -8.28d, -5.59d, 142.24d, 144.75d, 4105, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5878, 4298, 5246, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 4.01d, 6.31d, 112.37d, 115.37d, 4108, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5881, 5527, 4674, 5.0d, "Geocentric translations (geog2D domain)", "", -35.71d, 7.04d, -74.01d, -25.28d, 4115, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5882, 4618, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -35.71d, 7.04d, -74.01d, -25.28d, 4118, 3), + new EpsgOperationRecord((EpsgOperationType)0, 5888, 4632, 4470, 0.3d, "Position Vector transformation (geog2D domain)", "", -13.05d, -12.61d, 44.98d, 45.35d, 4121, 7), + new EpsgOperationRecord((EpsgOperationType)0, 5891, 4312, 4258, 0.15d, "NTv2", "AT_GIS_GRID.gsb", 46.4d, 49.02d, 9.53d, 17.17d, 4128, 0), + new EpsgOperationRecord((EpsgOperationType)0, 5900, 4896, 8397, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 4128, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6136, 4723, 6135, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", 19.21d, 19.41d, -81.46d, -81.04d, 4143, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6137, 4726, 6135, 0.15d, "Coordinate Frame rotation (geog2D domain)", "", 19.63d, 19.78d, -80.14d, -79.69d, 4150, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6138, 6134, 6130, 0.03d, "Geographic3D to GravityRelatedHeight (CI)", "GCGM0811.TXT", 19.21d, 19.41d, -81.46d, -81.04d, 4157, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6139, 6134, 6131, 0.03d, "Geographic3D to GravityRelatedHeight (CI)", "LCGM0811.TXT", 19.63d, 19.74d, -80.14d, -79.93d, 4157, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6140, 6134, 6132, 0.03d, "Geographic3D to GravityRelatedHeight (CI)", "CBGM0811.TXT", 19.66d, 19.78d, -79.92d, -79.69d, 4157, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6142, 4723, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 19.21d, 19.41d, -81.46d, -81.04d, 4157, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6143, 4726, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 19.63d, 19.78d, -80.14d, -79.69d, 4164, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6177, 6135, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 17.58d, 20.68d, -83.6d, -78.72d, 4171, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6188, 4207, 11108, 0.1d, "NTv2", "DLx_ETRS89_geo.gsb", 36.95d, 42.16d, -9.56d, -6.19d, 4174, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6189, 4274, 11108, 0.1d, "NTv2", "D73_ETRS89_geo.gsb", 36.95d, 42.16d, -9.56d, -6.19d, 4174, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6191, 4225, 4618, 5.0d, "Geocentric translations (geog2D domain)", "", -32.75d, -2.68d, -58.16d, -34.74d, 4174, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6192, 4225, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -32.75d, -2.68d, -58.16d, -34.74d, 4177, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6193, 4225, 4674, 5.0d, "Geocentric translations (geog2D domain)", "", -32.75d, -2.68d, -58.16d, -34.74d, 4180, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6194, 4225, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -32.75d, -2.68d, -58.16d, -34.74d, 4183, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6195, 5527, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -35.71d, 7.04d, -74.01d, -25.28d, 4186, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6196, 4263, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 5.56d, 5.74d, 6.72d, 6.97d, 4189, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6205, 3906, 11099, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 40.85d, 42.36d, 20.45d, 23.04d, 4192, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6206, 3906, 4326, 2.0d, "Coordinate Frame rotation (geog2D domain)", "", 40.85d, 42.36d, 20.45d, 23.04d, 4199, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6208, 6207, 4326, 0.3d, "Geocentric translations (geog2D domain)", "", 26.34d, 30.43d, 80.06d, 88.21d, 4206, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6276, 5332, 4938, 0.03d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4209, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6277, 4896, 4938, 0.03d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4224, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6278, 4919, 4938, 0.06d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4239, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6279, 4918, 4938, 0.18d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4254, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6280, 4917, 4938, 0.11d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4269, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6281, 4910, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4284, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6283, 4912, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4299, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6284, 4913, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4314, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6285, 4914, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4329, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6286, 4915, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4344, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6287, 4916, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4359, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6288, 4917, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4374, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6289, 4918, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4389, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6291, 4910, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4404, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6292, 4911, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4419, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6293, 4912, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4434, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6294, 4913, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4449, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6295, 4914, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4464, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6296, 4915, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4479, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6297, 4916, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4494, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6298, 4917, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4509, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6299, 4918, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4524, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6300, 4919, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4539, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6302, 4919, 4896, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4554, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6303, 23031, 28992, 1.0d, "Transverse Mercator", "", 50.75d, 53.7d, 3.2d, 7.22d, 4569, 24), + new EpsgOperationRecord((EpsgOperationType)0, 6304, 23031, 28992, 1.0d, "Transverse Mercator", "", 50.75d, 53.7d, 3.2d, 7.22d, 4593, 24), + new EpsgOperationRecord((EpsgOperationType)0, 6305, 23031, 31300, 1.0d, "Transverse Mercator", "", 49.5d, 51.51d, 2.5d, 6.4d, 4617, 23), + new EpsgOperationRecord((EpsgOperationType)0, 6306, 23095, 28992, 1.0d, "Transverse Mercator", "", 50.75d, 53.7d, 3.2d, 7.22d, 4640, 24), + new EpsgOperationRecord((EpsgOperationType)0, 6313, 4917, 4938, 0.1d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4664, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6315, 4919, 4938, 0.1d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4679, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6326, 6319, 5703, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2012bu0.bin", 24.41d, 49.38d, -124.79d, -66.91d, 4694, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6327, 6319, 5703, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2012ba0.bin", 51.3d, 71.4d, 172.42d, -129.99d, 4694, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6373, 6365, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 12.1d, 32.72d, -122.19d, -84.64d, 4694, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6389, 4896, 5332, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 4697, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6392, 4918, 4938, 0.1d, "Time-dependent Coordinate Frame rotation (geocen)", "", -47.2d, -8.88d, 109.23d, 163.2d, 4712, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6698, 4612, 6668, 1.0d, "Geocentric translations (geog2D domain)", "", 17.09d, 46.05d, 122.38d, 157.65d, 4727, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6699, 6694, 6695, 0.01d, "Vertical Offset", "", 30.94d, 45.54d, 129.3d, 145.87d, 4730, 1), + new EpsgOperationRecord((EpsgOperationType)0, 6701, 5246, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 4.01d, 6.31d, 112.37d, 115.37d, 4731, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6711, 6706, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 34.76d, 47.1d, 5.93d, 18.99d, 4734, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6712, 4301, 4612, 0.2d, "NTv2", "tky2jgd.gsb", 20.37d, 45.54d, 122.83d, 154.05d, 4737, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6713, 4612, 6668, 0.2d, "NTv2", "touhokutaiheiyouoki2011.gsb", 34.84d, 41.58d, 135.42d, 142.14d, 4737, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6724, 6715, 28348, 5.0d, "Transverse Mercator", "", -10.63d, -10.36d, 105.48d, 105.77d, 4737, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6740, 4301, 6668, 0.2d, "NTv2", "tky2jgd.gsb", 20.37d, 45.54d, 122.83d, 154.05d, 4744, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6864, 4917, 6781, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 14.92d, 74.71d, 167.65d, -63.88d, 4744, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6865, 4918, 6781, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 14.92d, 74.71d, 167.65d, -63.88d, 4759, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6866, 4919, 6781, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 14.92d, 74.71d, 167.65d, -63.88d, 4774, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6872, 4143, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 1.02d, 5.19d, -7.55d, -3.11d, 4789, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6873, 4297, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", -26.59d, -11.69d, 42.53d, 51.03d, 4792, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6888, 5451, 4267, 9.0d, "Geocentric translations (geog2D domain)", "", 7.98d, 17.83d, -92.29d, -82.53d, 4795, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6889, 5451, 4326, 5.0d, "Molodensky-Badekas (PV geog2D domain)", "", 7.98d, 11.22d, -85.97d, -82.53d, 4798, 10), + new EpsgOperationRecord((EpsgOperationType)0, 6890, 5451, 5365, 8.0d, "Geocentric translations (geog2D domain)", "", 7.98d, 11.22d, -85.97d, -82.53d, 4808, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6891, 5451, 4326, 14.0d, "Geocentric translations (geog2D domain)", "", 7.98d, 17.83d, -92.29d, -82.53d, 4811, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6895, 4752, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -18.32d, -17.25d, 177.19d, 178.75d, 4814, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6896, 4168, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 4.67d, 11.16d, -3.25d, 1.23d, 4817, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6897, 4606, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 13.66d, 14.16d, -61.13d, -60.82d, 4820, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6898, 4207, 4326, 43.0d, "Geocentric translations (geog2D domain)", "", 36.95d, 42.16d, -9.56d, -6.19d, 4823, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6899, 4284, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 57.52d, 59.75d, 21.74d, 28.2d, 4826, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6900, 4129, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", -26.87d, -19.84d, 31.29d, 35.65d, 4829, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6901, 4127, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", -26.87d, -10.42d, 30.21d, 40.9d, 4832, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6902, 4298, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 4.01d, 5.11d, 114.09d, 115.37d, 4835, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6903, 4310, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 10.64d, 16.7d, -20.22d, -11.36d, 4838, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6904, 4209, 4326, 29.0d, "Geocentric translations (geog2D domain)", "", -17.14d, -9.37d, 32.68d, 35.93d, 4841, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6905, 4202, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", -43.7d, -9.86d, 112.85d, 153.69d, 4844, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6906, 4209, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", -22.42d, -15.61d, 25.23d, 33.08d, 4847, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6907, 4713, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", 10.94d, 12.72d, 41.75d, 43.48d, 4850, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6908, 4232, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 16.59d, 26.42d, 51.99d, 59.91d, 4853, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6909, 4658, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 63.34d, 66.59d, -24.63d, -13.38d, 4856, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6910, 6881, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 12.54d, 19.0d, 43.37d, 53.14d, 4859, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6911, 6882, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", 33.06d, 34.65d, 35.04d, 36.63d, 4862, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6912, 6883, 4326, 42.0d, "Geocentric translations (geog2D domain)", "", 3.14d, 3.82d, 8.37d, 9.02d, 4865, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6913, 6894, 4326, 43.0d, "Geocentric translations (geog2D domain)", "", 13.05d, 13.83d, -16.88d, -13.79d, 4868, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6914, 6892, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -4.86d, -3.66d, 55.15d, 56.01d, 4871, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6918, 32631, 32764, 0.0d, "Transverse Mercator", "", 89.99d, 90.0d, 179.99d, 180.0d, 4874, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6919, 32066, 32765, 0.0d, "Transverse Mercator", "", 89.99d, 90.0d, 179.99d, 180.0d, 4889, 15), + new EpsgOperationRecord((EpsgOperationType)0, 6926, 6892, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -4.86d, -3.66d, 55.15d, 56.01d, 4904, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6935, 6934, 3887, 0.05d, "Molodensky-Badekas (PV geocentric domain)", "", 29.06d, 37.39d, 38.79d, 48.75d, 4911, 10), + new EpsgOperationRecord((EpsgOperationType)0, 6936, 6934, 3887, 0.05d, "Position Vector transformation (geocentric domain)", "", 29.06d, 37.39d, 38.79d, 48.75d, 4921, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6937, 4202, 5546, 1.0d, "Position Vector transformation (geog2D domain)", "", -10.76d, -2.53d, 140.85d, 150.96d, 4928, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6938, 4202, 5546, 4.0d, "Geocentric translations (geog2D domain)", "", -10.76d, -2.53d, 140.85d, 150.96d, 4935, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6939, 4202, 5546, 1.0d, "Position Vector transformation (geog2D domain)", "", -8.28d, -5.59d, 142.24d, 144.75d, 4938, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6940, 4202, 5546, 2.0d, "Geocentric translations (geog2D domain)", "", -8.28d, -5.59d, 142.24d, 144.75d, 4945, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6941, 4202, 5546, 0.5d, "Position Vector transformation (geog2D domain)", "", -6.6d, -5.05d, 140.89d, 141.54d, 4948, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6942, 4202, 5546, 2.5d, "Geocentric translations (geog2D domain)", "", -6.6d, -5.05d, 140.89d, 141.54d, 4955, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6943, 4202, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -10.76d, -2.53d, 140.85d, 150.96d, 4958, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6944, 4202, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", -8.28d, -5.59d, 142.24d, 144.75d, 4961, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6945, 4202, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", -6.6d, -5.05d, 140.89d, 141.54d, 4964, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6946, 4300, 4258, 0.41d, "NTv2", "tm75_etrs89.gsb", 51.39d, 55.43d, -10.56d, -5.34d, 4967, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6947, 4300, 4326, 1.0d, "NTv2", "tm75_etrs89.gsb", 51.39d, 55.43d, -10.56d, -5.34d, 4967, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6948, 4745, 4258, 0.03d, "NTv2", "NTv2_SN.gsb", 50.2d, 51.66d, 11.89d, 15.04d, 4967, 0), + new EpsgOperationRecord((EpsgOperationType)0, 6949, 4248, 5360, 5.0d, "Geocentric translations (geog2D domain)", "", -26.0d, -17.5d, -70.79d, -67.0d, 4967, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6950, 4248, 5360, 5.0d, "Geocentric translations (geog2D domain)", "", -36.0d, -26.0d, -72.87d, -68.28d, 4970, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6951, 4248, 5360, 5.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -35.99d, -74.48d, -70.39d, 4973, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6960, 4756, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 8.33d, 23.4d, 102.14d, 109.53d, 4976, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6968, 4618, 5360, 5.0d, "Geocentric translations (geog2D domain)", "", -36.0d, -31.99d, -72.87d, -69.77d, 4983, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6970, 4618, 5360, 5.0d, "Geocentric translations (geog2D domain)", "", -55.96d, -51.99d, -74.83d, -66.33d, 4986, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6971, 4248, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", -26.0d, -17.5d, -70.79d, -67.0d, 4989, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6972, 4248, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", -36.0d, -26.0d, -72.87d, -68.28d, 4992, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6973, 4248, 4326, 17.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -35.99d, -74.48d, -70.39d, 4995, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6974, 4618, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", -32.0d, -17.5d, -71.77d, -67.0d, 4998, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6975, 4618, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", -36.0d, -31.99d, -72.87d, -69.77d, 5001, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6976, 4618, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -35.99d, -74.48d, -70.39d, 5004, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6977, 4618, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", -55.96d, -51.99d, -74.83d, -66.33d, 5007, 3), + new EpsgOperationRecord((EpsgOperationType)0, 6992, 7136, 7139, 0.05d, "Coordinate Frame rotation (geog2D domain)", "", 29.45d, 33.53d, 32.99d, 35.69d, 5010, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6993, 7139, 6990, 0.0d, "Coordinate Frame rotation (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 5017, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6998, 4270, 4326, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", 22.63d, 25.64d, 51.5d, 56.03d, 5024, 7), + new EpsgOperationRecord((EpsgOperationType)0, 6999, 4270, 4326, 0.15d, "Coordinate Frame rotation (geog2D domain)", "", 22.63d, 24.95d, 51.56d, 56.03d, 5031, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7000, 4289, 4258, 0.001d, "NTv2", "rdtrans2008.gsb", 50.75d, 53.7d, 3.2d, 7.22d, 5038, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7002, 4270, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 24.24d, 24.64d, 54.2d, 54.71d, 5038, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7003, 4270, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 22.76d, 24.32d, 51.56d, 54.01d, 5045, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7004, 4270, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 22.63d, 24.95d, 53.99d, 56.03d, 5052, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7008, 7005, 3391, 5.0d, "Transverse Mercator", "", 36.19d, 36.75d, 41.27d, 42.0d, 5059, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7009, 7006, 3392, 5.0d, "Transverse Mercator", "", 36.19d, 37.39d, 42.0d, 43.89d, 5071, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7010, 7006, 3392, 5.0d, "Transverse Mercator", "", 36.22d, 37.33d, 43.87d, 45.33d, 5083, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7011, 7006, 3392, 5.0d, "Transverse Mercator", "", 36.22d, 37.33d, 43.87d, 45.33d, 5095, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7012, 7006, 3392, 5.0d, "Transverse Mercator", "", 34.57d, 36.22d, 41.09d, 42.82d, 5107, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7013, 7006, 3392, 5.0d, "Transverse Mercator", "", 34.59d, 36.24d, 42.77d, 45.0d, 5119, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7014, 7006, 3392, 5.0d, "Transverse Mercator", "", 34.6d, 36.24d, 45.0d, 46.35d, 5131, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7015, 7005, 3391, 5.0d, "Transverse Mercator", "", 32.98d, 33.99d, 38.79d, 40.09d, 5143, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7016, 7005, 3391, 5.0d, "Transverse Mercator", "", 32.95d, 34.6d, 40.07d, 42.0d, 5155, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7017, 7006, 3392, 5.0d, "Transverse Mercator", "", 32.95d, 34.61d, 42.0d, 43.93d, 5167, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7018, 7006, 3392, 5.0d, "Transverse Mercator", "", 32.98d, 34.62d, 43.9d, 46.2d, 5179, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7019, 7005, 3391, 5.0d, "Transverse Mercator", "", 31.32d, 32.99d, 40.05d, 42.0d, 5191, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7020, 7005, 3391, 5.0d, "Transverse Mercator", "", 32.0d, 32.99d, 38.92d, 40.08d, 5203, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7021, 7005, 3391, 5.0d, "Transverse Mercator", "", 31.32d, 32.99d, 40.05d, 42.0d, 5215, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7022, 7006, 3392, 5.0d, "Transverse Mercator", "", 31.32d, 32.99d, 42.0d, 43.95d, 5227, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7023, 7006, 3392, 5.0d, "Transverse Mercator", "", 31.36d, 32.99d, 43.92d, 46.08d, 5239, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7024, 7006, 3392, 5.0d, "Transverse Mercator", "", 31.33d, 32.99d, 46.05d, 47.87d, 5251, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7025, 7006, 3392, 5.0d, "Transverse Mercator", "", 31.33d, 32.99d, 46.05d, 47.87d, 5263, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7026, 7006, 3392, 5.0d, "Transverse Mercator", "", 29.75d, 31.37d, 42.0d, 43.97d, 5275, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7027, 7006, 3392, 5.0d, "Transverse Mercator", "", 29.73d, 31.37d, 43.94d, 46.06d, 5287, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7028, 7006, 3392, 5.0d, "Transverse Mercator", "", 29.72d, 31.37d, 46.03d, 48.0d, 5299, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7029, 7006, 3392, 5.0d, "Transverse Mercator", "", 29.75d, 31.37d, 42.0d, 43.97d, 5311, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7030, 7006, 3392, 5.0d, "Transverse Mercator", "", 29.73d, 31.37d, 43.94d, 46.06d, 5323, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7031, 7006, 3392, 5.0d, "Transverse Mercator", "", 29.09d, 29.75d, 43.99d, 46.04d, 5335, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7032, 7006, 3392, 5.0d, "Transverse Mercator", "", 29.06d, 29.74d, 46.02d, 47.02d, 5347, 12), + new EpsgOperationRecord((EpsgOperationType)0, 7033, 4744, 4326, 30.0d, "Geocentric translations (geog2D domain)", "", 29.06d, 37.39d, 38.79d, 48.61d, 5359, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7083, 4637, 7073, 0.5d, "Geocentric translations (geog2D domain)", "", -66.78d, -66.1d, 139.44d, 141.5d, 5362, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7140, 7136, 6983, 0.0d, "Coordinate Frame rotation (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 5365, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7377, 7371, 4978, 0.1d, "Coordinate Frame rotation (geocentric domain)", "", 14.33d, 26.74d, 51.99d, 63.38d, 5372, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7442, 4307, 4326, 100.0d, "Geocentric translations (geog2D domain)", "", 27.4d, 28.1d, 7.66d, 8.27d, 5379, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7443, 7373, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 14.33d, 26.74d, 51.99d, 63.38d, 5382, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7448, 4618, 5360, 5.0d, "Geocentric translations (geog2D domain)", "", -32.0d, -17.5d, -71.77d, -67.0d, 5385, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7449, 4618, 5360, 5.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -35.99d, -74.48d, -70.39d, 5388, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7646, 6319, 6641, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2012bp0.bin", 17.87d, 18.57d, -67.97d, -65.19d, 5391, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7647, 6319, 6642, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2012bp0.bin", 17.62d, 18.44d, -65.09d, -64.51d, 5391, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7648, 6324, 6644, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2012bg0.bin", 13.18d, 13.7d, 144.58d, 145.01d, 5391, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7649, 6324, 6640, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2012bg0.bin", 14.06d, 15.35d, 145.06d, 145.89d, 5391, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7650, 6321, 6643, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2012bs0.bin", -14.43d, -14.2d, -170.88d, -170.51d, 5391, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7653, 5773, 7651, 0.0d, "Vertical Offset", "", -8.28d, -5.59d, 142.24d, 144.75d, 5391, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7654, 3855, 7652, 0.0d, "Vertical Offset", "", -9.35d, -5.0d, 140.85d, 144.01d, 5392, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7655, 5545, 7447, 0.2d, "Geographic3D to GravityRelatedHeight (PNG)", "PNG08.DAT", -12.0d, 0.01d, 140.0d, 158.01d, 5393, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7666, 7664, 5332, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5393, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7667, 7662, 7664, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5400, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7668, 7660, 7664, 0.02d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5407, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7669, 7662, 5332, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5414, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7670, 7660, 4919, 0.02d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5421, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7672, 7656, 4914, 0.2d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5428, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7673, 4149, 4151, 0.25d, "NTv2", "CHENyx06_ETRS.gsb", 45.81d, 47.81d, 5.95d, 10.5d, 5435, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7674, 4149, 11307, 0.25d, "NTv2", "CHENyx06_ETRS.gsb", 45.81d, 47.81d, 5.95d, 10.5d, 5435, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7675, 3906, 8685, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 5435, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7676, 3906, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 5442, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7697, 4229, 4326, 1.2d, "Molodensky-Badekas (CF geog2D domain)", "", 21.89d, 33.82d, 24.7d, 37.91d, 5449, 10), + new EpsgOperationRecord((EpsgOperationType)0, 7698, 4267, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 7.15d, 9.68d, -83.04d, -77.19d, 5459, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7701, 7700, 5621, 0.1d, "Vertical Offset and Slope", "", 55.67d, 58.09d, 20.87d, 28.24d, 5466, 6), + new EpsgOperationRecord((EpsgOperationType)0, 7702, 4922, 7677, 0.17d, "Time-specific Coordinate Frame rotation (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5472, 8), + new EpsgOperationRecord((EpsgOperationType)0, 7703, 7677, 7679, 0.07d, "Time-specific Coordinate Frame rotation (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5480, 8), + new EpsgOperationRecord((EpsgOperationType)0, 7704, 4922, 7679, 0.2d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5488, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7705, 7681, 7679, 0.03d, "Time-specific Coordinate Frame rotation (geocen)", "", 39.87d, 85.19d, 18.92d, -168.97d, 5495, 8), + new EpsgOperationRecord((EpsgOperationType)0, 7709, 4277, 11009, 0.03d, "NTv2", "OSTN15_NTv2_OSGBtoETRS.gsb", 49.75d, 61.01d, -9.01d, 2.01d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7710, 4277, 4326, 1.0d, "NTv2", "OSTN15_NTv2_OSGBtoETRS.gsb", 49.75d, 61.01d, -9.01d, 2.01d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7711, 11008, 5701, 0.008d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.93d, 58.71d, -7.06d, 1.8d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7712, 11008, 5740, 0.017d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 58.72d, 59.41d, -3.48d, -2.34d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7713, 4937, 7707, 0.02d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.75d, 61.01d, -9.01d, 2.01d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7714, 11008, 5742, 0.018d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 59.83d, 60.87d, -1.78d, -0.67d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7715, 11008, 5746, 0.011d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 56.76d, 58.54d, -7.72d, -6.1d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7716, 11008, 5749, 0.01d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.86d, 49.99d, -6.41d, -6.23d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7717, 4937, 5750, 0.03d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 54.02d, 54.44d, -4.87d, -4.27d, 5503, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7720, 6311, 4258, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", 34.59d, 35.74d, 32.2d, 34.65d, 5503, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7721, 6311, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 34.59d, 35.74d, 32.2d, 34.65d, 5510, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7788, 4149, 4326, 1.5d, "NTv2", "CHENyx06_ETRS.gsb", 45.81d, 47.81d, 5.95d, 10.5d, 5517, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7790, 5332, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5517, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7806, 4178, 7798, 5.0d, "Molodensky-Badekas (PV geog2D domain)", "", 41.24d, 44.23d, 22.36d, 28.68d, 5532, 10), + new EpsgOperationRecord((EpsgOperationType)0, 7807, 5332, 6317, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 14.92d, 74.71d, 167.65d, -63.88d, 5542, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7808, 5332, 6320, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", -17.56d, 31.8d, 157.47d, -151.27d, 5557, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7809, 5332, 6323, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 1.64d, 23.9d, 129.48d, 149.55d, 5572, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7814, 4911, 4919, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5587, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7817, 5558, 4919, 0.0d, "Geocentric translations (geocentric domain)", "", 43.18d, 52.38d, 22.15d, 40.18d, 5602, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7833, 4191, 11047, 0.2d, "Coordinate Frame rotation (geog2D domain)", "", 39.64d, 42.67d, 19.22d, 21.06d, 5605, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7834, 4191, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 39.64d, 42.67d, 19.22d, 21.06d, 5612, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7835, 4179, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 41.3d, 42.67d, 19.14d, 20.63d, 5619, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7836, 4179, 4191, 1.0d, "Geocentric translations (geog2D domain)", "", 39.64d, 42.67d, 19.22d, 21.06d, 5622, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7838, 7837, 5621, 0.1d, "Vertical Offset and Slope", "", 47.27d, 55.09d, 5.86d, 15.04d, 5625, 6), + new EpsgOperationRecord((EpsgOperationType)0, 7840, 4959, 7839, 0.1d, "Geographic3D to GravityRelatedHeight (NZgeoid)", "New_Zealand_Quasigeoid_2016.csv", -55.95d, -25.88d, 160.6d, -171.2d, 5631, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7860, 7839, 5759, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "auckland-1946-to-nzvd2016-conversion.csv", -37.67d, -36.12d, 174.0d, 176.17d, 5631, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7861, 7839, 5760, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "bluff-1955-to-nzvd2016-conversion.csv", -46.71d, -46.26d, 168.01d, 168.86d, 5632, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7862, 7839, 5761, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "dunedin-1958-to-nzvd2016-conversion.csv", -46.4d, -43.82d, 167.73d, 171.28d, 5633, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7863, 7839, 4458, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "dunedin-bluff-1960-to-nzvd2016-conversion.csv", -46.73d, -44.52d, 166.37d, 169.95d, 5634, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7864, 7839, 5762, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "gisborne-1926-to-nzvd2016-conversion.csv", -39.04d, -37.49d, 176.41d, 178.63d, 5635, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7865, 7839, 5763, 0.01d, "Vertical Offset by Grid Interpolation (NZLVD)", "lyttelton-1937-to-nzvd2016-conversion.csv", -44.92d, -41.6d, 168.95d, 173.77d, 5636, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7866, 7839, 5764, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "moturiki-1953-to-nzvd2016-conversion.csv", -40.59d, -37.52d, 174.57d, 177.26d, 5637, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7867, 7839, 5765, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "napier-1962-to-nzvd2016-conversion.csv", -40.57d, -38.87d, 175.8d, 178.07d, 5638, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7868, 7839, 5766, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "nelson-1955-to-nzvd2016-conversion.csv", -42.44d, -40.44d, 171.82d, 174.46d, 5639, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7869, 7839, 5767, 0.01d, "Vertical Offset by Grid Interpolation (NZLVD)", "onetreepoint-1964-to-nzvd2016-conversion.csv", -36.41d, -34.36d, 172.61d, 174.83d, 5640, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7870, 7839, 5772, 0.18d, "Vertical Offset by Grid Interpolation (NZLVD)", "stewartisland-1977-to-nzvd2016-conversion.csv", -47.33d, -46.63d, 167.29d, 168.34d, 5641, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7871, 7839, 5769, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "taranaki-1970-to-nzvd2016-conversion.csv", -39.92d, -38.41d, 173.68d, 174.95d, 5642, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7872, 7839, 5770, 0.02d, "Vertical Offset by Grid Interpolation (NZLVD)", "wellington-1953-to-nzvd2016-conversion.csv", -41.67d, -40.12d, 174.52d, 176.55d, 5643, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7873, 5773, 7832, 0.0d, "Vertical Offset", "", -10.42d, -6.67d, 144.4d, 149.67d, 5644, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7874, 3855, 7841, 0.0d, "Vertical Offset", "", -10.42d, -6.67d, 144.4d, 149.67d, 5645, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7891, 7885, 7890, 0.0d, "Geographic3D to GravityRelatedHeight (EGM2008)", "Und_min2.5x2.5_egm2008_isw=82_WGS84_TideFree.gz", -16.08d, -15.85d, -5.85d, -5.59d, 5646, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7892, 7886, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 5646, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7893, 4710, 7886, 0.15d, "Geocentric translations (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 5649, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7894, 4710, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 5652, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7895, 4710, 7886, 0.1d, "Position Vector transformation (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 5655, 7), + new EpsgOperationRecord((EpsgOperationType)0, 7897, 7881, 7886, 0.05d, "Geocentric translations (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 5662, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7898, 7881, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 5665, 3), + new EpsgOperationRecord((EpsgOperationType)0, 7913, 4258, 27700, 0.2d, "Transverse Mercator", "ostn97.txt", 49.81d, 60.93d, -8.69d, 1.91d, 5668, 5), + new EpsgOperationRecord((EpsgOperationType)0, 7932, 4911, 7914, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5673, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7933, 4912, 7916, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5688, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7934, 4913, 7918, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5703, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7935, 4914, 7920, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5718, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7936, 4915, 7922, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5733, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7937, 4916, 7924, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5748, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7938, 4917, 7926, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5763, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7939, 4918, 7928, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5778, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7940, 4919, 7930, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5793, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7941, 4919, 7930, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5808, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7942, 4911, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5823, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7943, 4912, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5838, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7944, 4913, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5853, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7945, 4914, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5868, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7946, 4915, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5883, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7947, 4916, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5898, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7948, 4917, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5913, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7949, 4918, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5928, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7950, 4896, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5943, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7951, 5332, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 5958, 15), + new EpsgOperationRecord((EpsgOperationType)0, 7952, 4258, 27700, 0.0d, "Transverse Mercator", "OSTN02_OSGM02_GB.txt", 49.81d, 60.93d, -8.69d, 1.91d, 5973, 5), + new EpsgOperationRecord((EpsgOperationType)0, 7953, 11009, 27700, 0.0d, "Transverse Mercator", "OSTN15_OSGM15_GB.txt", 49.75d, 61.01d, -9.01d, 2.01d, 5978, 5), + new EpsgOperationRecord((EpsgOperationType)0, 7958, 4943, 5732, 0.014d, "Geographic3D to GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Belfast.gri", 53.96d, 55.36d, -8.18d, -5.34d, 5983, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7959, 4943, 5731, 0.023d, "Geographic3D to GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Malin.gri", 51.39d, 55.43d, -10.56d, -5.34d, 5983, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7960, 7679, 5332, 0.004d, "Time-specific Coordinate Frame rotation (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5983, 8), + new EpsgOperationRecord((EpsgOperationType)0, 7961, 7660, 7677, 0.17d, "Time-specific Coordinate Frame rotation (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 5991, 8), + new EpsgOperationRecord((EpsgOperationType)0, 7964, 7962, 5731, 0.1d, "Change of Vertical Unit", "", 51.39d, 55.43d, -10.56d, -5.34d, 5999, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7966, 7962, 5732, 0.1d, "Change of Vertical Unit", "", 53.96d, 55.36d, -8.18d, -5.34d, 6000, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7969, 7968, 5703, 0.02d, "Change of Vertical Unit", "vertconw.94", 31.33d, 49.05d, -124.79d, -107.0d, 6001, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7970, 7968, 5703, 0.02d, "Change of Vertical Unit", "vertconc.94", 25.83d, 49.38d, -107.0d, -89.0d, 6001, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7971, 7968, 5703, 0.02d, "Change of Vertical Unit", "vertcone.94", 24.41d, 48.32d, -89.0d, -66.91d, 6001, 0), + new EpsgOperationRecord((EpsgOperationType)0, 7977, 7976, 5739, 0.0d, "Height Depth Reversal", "", 22.13d, 22.58d, 113.76d, 114.51d, 6001, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7980, 5790, 7979, 0.1d, "Vertical Offset", "", 28.53d, 30.09d, 46.54d, 48.48d, 6002, 1), + new EpsgOperationRecord((EpsgOperationType)0, 7981, 5788, 7979, 0.1d, "Vertical Offset", "", 28.53d, 30.09d, 46.54d, 48.48d, 6003, 1), + new EpsgOperationRecord((EpsgOperationType)0, 8037, 4979, 5714, 0.5d, "Geographic3D to GravityRelatedHeight (EGM2008)", "Und_min1x1_egm2008_isw=82_WGS84_TideFree.gz", -90.0d, 90.0d, -180.0d, 180.0d, 6004, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8048, 4283, 7844, 0.01d, "Coordinate Frame rotation (geog2D domain)", "", -60.55d, -8.47d, 93.41d, 173.34d, 6004, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8049, 7789, 7842, 0.03d, "Time-dependent Coordinate Frame rotation (geocen)", "", -60.55d, -8.47d, 93.41d, 173.34d, 6011, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8069, 4910, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6026, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8070, 4911, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6041, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8071, 4912, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6056, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8072, 4913, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6071, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8073, 4914, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6086, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8074, 4915, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6101, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8075, 4916, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6116, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8076, 4917, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6131, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8077, 4918, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6146, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8078, 4919, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6161, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8079, 4896, 7789, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6176, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8256, 4914, 8230, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6191, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8257, 4915, 8230, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6206, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8258, 4916, 8230, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6221, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8259, 4917, 8233, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6236, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8260, 4918, 8238, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6251, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8261, 4919, 8242, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6266, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8264, 5332, 8250, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6281, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8265, 7789, 8253, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 6296, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8268, 4909, 8266, 0.1d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "gr2000g.gri", 59.0d, 84.01d, -75.0d, -10.0d, 6311, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8269, 4909, 8267, 0.1d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "ggeoid16.gri", 58.0d, 85.01d, -75.0d, -6.99d, 6311, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8270, 4638, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 46.69d, 47.19d, -56.48d, -56.07d, 6311, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8361, 11075, 11312, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "Slovakia_ETRS89h_to_Baltic1957.gtx", 47.73d, 49.61d, 16.84d, 22.56d, 6314, 1), + new EpsgOperationRecord((EpsgOperationType)0, 8362, 11075, 11314, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "Slovakia_ETRS89h_to_EVRF2007.gtx", 47.73d, 49.61d, 16.84d, 22.56d, 6315, 1), + new EpsgOperationRecord((EpsgOperationType)0, 8364, 8351, 4156, 0.05d, "NADCON", "Slovakia_JTSK03_to_JTSK.LAS", 47.73d, 49.61d, 16.84d, 22.56d, 6316, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8365, 11076, 8351, 0.001d, "Coordinate Frame rotation (geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 6316, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8366, 7789, 8401, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6323, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8367, 8351, 11076, 0.001d, "Coordinate Frame rotation (geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 6338, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8368, 8351, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 6345, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8369, 4313, 11215, 0.01d, "NTv2", "bd72lb72_etrs89lb08.gsb", 49.5d, 51.51d, 2.5d, 6.4d, 6352, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8371, 9776, 5720, 0.02d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAF09.mnt", 42.33d, 51.14d, -4.87d, 8.23d, 6352, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8372, 9776, 5721, 0.05d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAC09.mnt", 41.31d, 43.07d, 8.5d, 9.63d, 6352, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8405, 7789, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6352, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8435, 8431, 8428, 999.0d, "Molodensky-Badekas (CF geog2D domain)", "", 22.06d, 22.23d, 113.52d, 113.68d, 6367, 10), + new EpsgOperationRecord((EpsgOperationType)0, 8436, 8431, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 22.06d, 22.23d, 113.52d, 113.68d, 6377, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8438, 8428, 4326, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 22.06d, 22.23d, 113.52d, 113.68d, 6380, 10), + new EpsgOperationRecord((EpsgOperationType)0, 8439, 8427, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 22.13d, 22.58d, 113.76d, 114.51d, 6390, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8444, 4283, 7844, 0.05d, "NTv2", "GDA94_GDA2020_conformal_christmas_island.gsb", -10.63d, -10.36d, 105.48d, 105.77d, 6393, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8445, 4283, 7844, 0.05d, "NTv2", "GDA94_GDA2020_conformal_cocos_island.gsb", -12.27d, -11.76d, 96.76d, 96.99d, 6393, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8446, 4283, 7844, 0.05d, "NTv2", "GDA94_GDA2020_conformal.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 6393, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8447, 4283, 7844, 0.05d, "NTv2", "GDA94_GDA2020_conformal_and_distortion.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 6393, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8448, 7842, 7664, 0.2d, "Time-dependent Coordinate Frame rotation (geocen)", "", -60.55d, -8.47d, 93.41d, 173.34d, 6393, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8450, 7844, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", -60.55d, -8.47d, 93.41d, 173.34d, 6408, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8451, 7843, 5711, 0.15d, "Geographic3D to GravityRelatedHeight (AUSGeoid v2)", "AUSGeoid2020_20180201.gsb", -43.7d, -9.86d, 96.76d, 153.69d, 6411, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8452, 4211, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", -8.91d, 5.97d, 95.16d, 115.77d, 6411, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8546, 4138, 4269, 0.15d, "NADCON5 (2D)", "nadcon5.sg1952.nad83_1986.stgeorge.lat.trn.20160901.b", 56.49d, 56.67d, -169.88d, -169.38d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8547, 4136, 4269, 0.5d, "NADCON5 (2D)", "nadcon5.sl1952.nad83_1986.stlawrence.lat.trn.20160901.b", 62.89d, 63.84d, -171.97d, -168.59d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8548, 4137, 4269, 0.5d, "NADCON5 (2D)", "nadcon5.sp1952.nad83_1986.stpaul.lat.trn.20160901.b", 57.06d, 57.28d, -170.51d, -170.04d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8549, 4267, 4269, 0.5d, "NADCON5 (2D)", "nadcon5.nad27.nad83_1986.alaska.lat.trn.20160901.b", 51.3d, 71.4d, 172.42d, -129.99d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8550, 4269, 4152, 0.15d, "NADCON5 (2D)", "nadcon5.nad83_1986.nad83_1992.alaska.lat.trn.20160901.b", 47.88d, 74.71d, 167.65d, -129.99d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8551, 4152, 4759, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_1992.nad83_2007.alaska.lat.trn.20160901.b", 47.88d, 74.71d, 167.65d, -129.99d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8552, 4759, 6318, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_2007.nad83_2011.alaska.lat.trn.20160901.b", 51.3d, 71.4d, 172.42d, -129.99d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8555, 4267, 4269, 0.15d, "NADCON5 (2D)", "nadcon5.nad27.nad83_1986.conus.lat.trn.20160901.b", 23.82d, 49.38d, -124.79d, -66.91d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8556, 4269, 4152, 0.05d, "NADCON5 (2D)", "nadcon5.nad83_1986.nad83_harn.conus.lat.trn.20160901.b", 23.82d, 49.38d, -124.79d, -66.91d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8559, 4759, 6318, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_2007.nad83_2011.conus.lat.trn.20160901.b", 23.82d, 49.38d, -124.79d, -66.91d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8561, 4135, 4269, 0.2d, "NADCON5 (2D)", "nadcon5.ohd.nad83_1986.hawaii.lat.trn.20160901.b", 18.87d, 22.29d, -160.3d, -154.74d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8660, 4269, 4152, 0.05d, "NADCON5 (2D)", "nadcon5.nad83_1986.nad83_1993.hawaii.lat.trn.20160901.b", 18.87d, 22.29d, -160.3d, -154.74d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8661, 4152, 6322, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_1993.nad83_pa11.hawaii.lat.trn.20160901.b", 18.87d, 22.29d, -160.3d, -154.74d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8662, 4169, 4152, 5.0d, "NADCON5 (2D)", "nadcon5.as62.nad83_1993.as.lat.trn.20160901.b", -14.43d, -14.11d, -170.88d, -169.38d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8665, 4675, 4152, 5.0d, "NADCON5 (2D)", "nadcon5.gu63.nad83_1993.guamcnmi.lat.trn.20160901.b", 13.18d, 20.61d, 144.58d, 146.12d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8668, 4139, 4269, 0.15d, "NADCON5 (2D)", "nadcon5.pr40.nad83_1986.prvi.lat.trn.20160901.b", 17.62d, 18.57d, -67.97d, -64.51d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8669, 4269, 4152, 0.15d, "NADCON5 (2D)", "nadcon5.nad83_1986.nad83_1993.prvi.lat.trn.20160901.b", 17.62d, 18.57d, -67.97d, -64.51d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8673, 4759, 6318, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_2007.nad83_2011.prvi.lat.trn.20160901.b", 14.92d, 21.86d, -68.49d, -63.88d, 6414, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8674, 4247, 4248, 0.0d, "Geocentric translations (geog2D domain)", "", 0.64d, 12.25d, -73.38d, -59.8d, 6414, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8680, 3906, 10328, 1.0d, "Position Vector transformation (geog2D domain)", "", 42.56d, 45.27d, 15.74d, 19.62d, 6417, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8688, 3906, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 6424, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8689, 3906, 4765, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 6431, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8695, 4220, 8694, 5.8d, "Coordinate Frame rotation (geog2D domain)", "", -8.59d, -6.01d, 10.41d, 12.84d, 6438, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8696, 4220, 8694, 4.2d, "Coordinate Frame rotation (geog2D domain)", "", -10.09d, -6.03d, 10.83d, 13.39d, 6445, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8819, 8699, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -18.02d, -4.38d, 8.2d, 24.09d, 6452, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8822, 8818, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 16.29d, 32.16d, 34.44d, 55.67d, 6455, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8823, 3906, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 42.56d, 45.27d, 15.74d, 19.62d, 6458, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8824, 4204, 8818, 5.0d, "Coordinate Frame rotation (geog2D domain)", "", 16.37d, 32.16d, 34.51d, 55.67d, 6465, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8827, 8694, 8699, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -18.02d, -4.38d, 8.2d, 24.09d, 6472, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8828, 4687, 4326, 0.5d, "Position Vector transformation (geog2D domain)", "", -31.24d, -4.52d, -158.13d, -131.97d, 6479, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8829, 4690, 4687, 0.5d, "Position Vector transformation (geog2D domain)", "", -17.93d, -17.44d, -149.7d, -149.09d, 6486, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8830, 4690, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -17.93d, -17.44d, -149.7d, -149.09d, 6493, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8831, 4691, 4687, 0.5d, "Position Vector transformation (geog2D domain)", "", -17.63d, -17.41d, -150.0d, -149.73d, 6500, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8832, 4691, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -17.63d, -17.41d, -150.0d, -149.73d, 6507, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8833, 4629, 4687, 0.5d, "Position Vector transformation (geog2D domain)", "", -16.96d, -16.17d, -151.91d, -150.89d, 6514, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8834, 4629, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -16.96d, -16.17d, -151.91d, -150.89d, 6521, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8835, 4688, 4687, 2.0d, "Position Vector transformation (geog2D domain)", "", -10.6d, -10.36d, -138.75d, -138.54d, 6528, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8842, 4688, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -10.6d, -10.36d, -138.75d, -138.54d, 6535, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8843, 4689, 4687, 0.5d, "Position Vector transformation (geog2D domain)", "", -9.89d, -9.64d, -139.23d, -138.75d, 6542, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8844, 4689, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -9.89d, -9.64d, -139.23d, -138.75d, 6549, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8845, 4689, 4687, 2.0d, "Position Vector transformation (geog2D domain)", "", -10.08d, -9.86d, -139.19d, -138.98d, 6556, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8846, 4689, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -10.08d, -9.86d, -139.19d, -138.98d, 6563, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8847, 4630, 4687, 0.5d, "Position Vector transformation (geog2D domain)", "", -9.01d, -8.72d, -140.31d, -139.96d, 6570, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8848, 4630, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -9.01d, -8.72d, -140.31d, -139.96d, 6577, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8849, 4630, 4687, 2.0d, "Position Vector transformation (geog2D domain)", "", -9.0d, -8.81d, -139.66d, -139.44d, 6584, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8850, 4630, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -9.0d, -8.81d, -139.66d, -139.44d, 6591, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8851, 4630, 4687, 0.5d, "Position Vector transformation (geog2D domain)", "", -9.57d, -9.27d, -140.21d, -139.95d, 6598, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8852, 4630, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -9.57d, -9.27d, -140.21d, -139.95d, 6605, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8853, 4692, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", -16.57d, -16.34d, -152.39d, -152.14d, 6612, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8861, 4152, 8860, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_harn.nad83_fbn.conus.lat.trn.20160901.b", 23.82d, 49.38d, -124.79d, -66.91d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8862, 8860, 4759, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_fbn.nad83_2007.conus.lat.trn.20160901.b", 23.82d, 49.38d, -124.79d, -66.91d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8863, 4152, 8860, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_1993.nad83_2002.as.lat.trn.20160901.b", -14.59d, -14.11d, -170.88d, -168.09d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8864, 8860, 6322, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_2002.nad83_pa11.as.lat.trn.20160901.b", -14.59d, -14.11d, -170.88d, -168.09d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8865, 4152, 8860, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_1993.nad83_2002.guamcnmi.lat.trn.20160901.b", 13.18d, 20.61d, 144.58d, 146.12d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8866, 8860, 6325, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_2002.nad83_ma11.guamcnmi.lat.trn.20160901.b", 13.18d, 20.61d, 144.58d, 146.12d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8867, 8545, 8860, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_1997.nad83_2002.prvi.lat.trn.20160901.b", 17.62d, 18.57d, -67.97d, -64.51d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8868, 8860, 4759, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_2002.nad83_2007.prvi.lat.trn.20160901.b", 17.62d, 18.57d, -67.97d, -64.51d, 6619, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8869, 5332, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6619, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8870, 4896, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6634, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8871, 4919, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6649, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8872, 4918, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6664, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8873, 4917, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6679, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8874, 4916, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6694, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8875, 4915, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6709, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8876, 4914, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6724, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8877, 4913, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6739, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8878, 4912, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6754, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8879, 4911, 8401, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6769, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8880, 7789, 8401, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 6784, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8882, 8694, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -18.02d, -4.38d, 8.2d, 24.09d, 6799, 7), + new EpsgOperationRecord((EpsgOperationType)0, 8883, 4220, 8699, 3.0d, "Geocentric translations (geog2D domain)", "", -10.09d, -6.03d, 10.83d, 13.39d, 6806, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8884, 4220, 8699, 5.0d, "Geocentric translations (geog2D domain)", "", -8.59d, -6.01d, 10.41d, 12.84d, 6809, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8885, 9776, 5720, 0.01d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAF18.mnt", 42.33d, 51.14d, -4.87d, 8.23d, 6812, 0), + new EpsgOperationRecord((EpsgOperationType)0, 8886, 4757, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 1.13d, 1.47d, 103.59d, 104.07d, 6812, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8887, 7842, 7815, 3.0d, "Geocentric translations (geocentric domain)", "", -60.55d, -8.47d, 93.41d, 173.34d, 6815, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8890, 7798, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 41.24d, 44.23d, 22.36d, 31.35d, 6818, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8894, 8685, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 6821, 3), + new EpsgOperationRecord((EpsgOperationType)0, 8952, 4918, 8915, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6824, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8953, 4919, 8917, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6832, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8954, 4919, 8919, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6840, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8955, 4919, 8921, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6848, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8956, 4919, 8923, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6856, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8957, 4919, 8925, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6864, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8958, 4919, 8927, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6872, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8959, 9010, 8929, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6880, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8960, 9010, 8931, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6888, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8961, 9010, 8933, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6896, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8962, 5332, 8935, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6904, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8963, 5332, 8937, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6912, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8964, 9015, 8939, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6920, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8965, 9015, 8941, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6928, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8966, 9015, 8943, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6936, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8967, 8227, 8945, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 6944, 8), + new EpsgOperationRecord((EpsgOperationType)0, 8970, 7789, 6317, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 14.92d, 74.71d, 167.65d, -63.88d, 6952, 15), + new EpsgOperationRecord((EpsgOperationType)0, 8971, 4269, 6318, 1.0d, "Geocentric translations (geog2D domain)", "", 23.82d, 30.25d, -97.22d, -81.17d, 6967, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9020, 4910, 4911, 0.01d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6970, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9021, 4911, 4912, 0.01d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6977, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9022, 4912, 4913, 0.007d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6984, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9023, 4913, 4914, 0.005d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6991, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9024, 4914, 4915, 0.003d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 6998, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9025, 4915, 4916, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7013, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9026, 4916, 4917, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7028, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9027, 4917, 4918, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7036, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9028, 4918, 9001, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7044, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9029, 4919, 9004, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7052, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9030, 4896, 9010, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7060, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9031, 5332, 6934, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7068, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9032, 7789, 8227, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7076, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9033, 9001, 9004, 0.007d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7084, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9034, 9004, 9007, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7099, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9035, 9007, 9010, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7107, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9036, 9010, 6934, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7122, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9037, 6934, 9015, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7137, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9038, 9015, 8227, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7145, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9041, 5638, 3035, 1.0d, "Lambert Azimuthal Equal Area", "", 59.96d, 69.59d, -30.87d, -5.55d, 7160, 10), + new EpsgOperationRecord((EpsgOperationType)0, 9042, 5639, 3034, 1.0d, "Lambert Conic Conformal (2SP)", "", 59.96d, 69.59d, -30.87d, -5.55d, 7170, 14), + new EpsgOperationRecord((EpsgOperationType)0, 9043, 9039, 3035, 1.0d, "Lambert Azimuthal Equal Area", "", 59.96d, 69.59d, -30.87d, -5.55d, 7184, 10), + new EpsgOperationRecord((EpsgOperationType)0, 9044, 9040, 3034, 1.0d, "Lambert Conic Conformal (2SP)", "", 59.96d, 69.59d, -30.87d, -5.55d, 7194, 14), + new EpsgOperationRecord((EpsgOperationType)0, 9045, 5633, 3035, 1.0d, "Lambert Azimuthal Equal Area", "", 29.24d, 43.07d, -35.58d, -12.48d, 7208, 10), + new EpsgOperationRecord((EpsgOperationType)0, 9046, 5632, 3034, 1.0d, "Lambert Conic Conformal (2SP)", "", 29.24d, 43.07d, -35.58d, -12.48d, 7218, 14), + new EpsgOperationRecord((EpsgOperationType)0, 9047, 5635, 3035, 1.0d, "Lambert Azimuthal Equal Area", "", 24.6d, 32.76d, -21.93d, -11.75d, 7232, 10), + new EpsgOperationRecord((EpsgOperationType)0, 9048, 5634, 3034, 1.0d, "Lambert Conic Conformal (2SP)", "", 24.6d, 32.76d, -21.93d, -11.75d, 7242, 14), + new EpsgOperationRecord((EpsgOperationType)0, 9049, 5636, 3035, 1.0d, "Lambert Azimuthal Equal Area", "", 34.42d, 43.45d, 25.62d, 44.83d, 7256, 10), + new EpsgOperationRecord((EpsgOperationType)0, 9050, 5637, 3034, 1.0d, "Lambert Conic Conformal (2SP)", "", 34.42d, 43.45d, 25.62d, 44.83d, 7266, 14), + new EpsgOperationRecord((EpsgOperationType)0, 9051, 4916, 4974, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 16.75d, -113.21d, -26.0d, 7280, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9052, 4919, 4988, 0.01d, "Time-specific Position Vector transform (geocen)", "", -59.87d, 32.72d, -122.19d, -25.28d, 7288, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9076, 7658, 4916, 0.1d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7296, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9077, 4919, 9070, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 1.64d, 23.9d, 129.48d, 149.55d, 7303, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9078, 4919, 9073, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", -17.56d, 31.8d, 157.47d, -151.27d, 7318, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9079, 4918, 4917, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -55.95d, -25.88d, 160.6d, -171.2d, 7333, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9080, 4919, 4917, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -55.95d, -25.88d, 160.6d, -171.2d, 7348, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9081, 4896, 4917, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -55.95d, -25.88d, 160.6d, -171.2d, 7363, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9082, 5332, 4917, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -55.95d, -25.88d, 160.6d, -171.2d, 7378, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9083, 7789, 4917, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -55.95d, -25.88d, 160.6d, -171.2d, 7393, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9084, 7907, 4959, 0.02d, "New Zealand Deformation Model", "nzgd2000_deformation_20000101_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9085, 7907, 4959, 0.02d, "New Zealand Deformation Model", "nzgd2000_deformation_20130801_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9086, 7907, 4959, 0.02d, "New Zealand Deformation Model", "nzgd2000_deformation_20140201_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9087, 7907, 4959, 0.02d, "New Zealand Deformation Model", "nzgd2000_deformation_20150101_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9088, 7907, 4959, 0.02d, "New Zealand Deformation Model", "nzgd2000_deformation_20160701_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9089, 7907, 4959, 0.02d, "New Zealand Deformation Model", "nzgd2000_deformation_20171201_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9090, 7907, 4959, 0.02d, "New Zealand Deformation Model", "nzgd2000_deformation_20180701_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9105, 4122, 4269, 0.5d, "NTv2", "GS7783.GSB", 43.41d, 47.08d, -66.28d, -59.73d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9106, 4122, 8252, 0.06d, "NTv2", "NS778302.gsb", 43.41d, 47.08d, -66.28d, -59.73d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9107, 4267, 8240, 1.5d, "NTv2", "ON27CSv1.GSB", 41.67d, 56.9d, -95.16d, -74.35d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9108, 4267, 8240, 1.0d, "NTv2", "TO27CSv1.GSB", 43.58d, 43.86d, -79.64d, -79.11d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9109, 4608, 8240, 1.0d, "NTv2", "ON76CSv1.GSB", 41.67d, 56.9d, -95.16d, -74.35d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9110, 4269, 8240, 0.1d, "NTv2", "ON83CSv1.GSB", 41.67d, 56.9d, -95.16d, -74.35d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9111, 4267, 4269, 1.5d, "NTv2", "SK27-83.gsb", 49.0d, 60.01d, -110.0d, -101.34d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9112, 4267, 8237, 1.5d, "NTv2", "BC_27_98.GSB", 48.25d, 60.01d, -139.04d, -114.08d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9113, 4267, 8240, 1.5d, "NTv2", "CRD27_00.GSB", 48.25d, 49.06d, -124.52d, -123.0d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9114, 4267, 8240, 1.5d, "NTv2", "NVI27_05.GSB", 48.48d, 50.93d, -128.5d, -123.49d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9115, 4267, 8246, 1.5d, "NTv2", "BC_27_05.GSB", 48.99d, 60.01d, -138.07d, -114.33d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9116, 4269, 8237, 0.1d, "NTv2", "BC_93_98.GSB", 48.25d, 60.01d, -139.04d, -114.08d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9117, 4269, 8240, 0.1d, "NTv2", "CRD93_00.GSB", 48.25d, 49.06d, -124.52d, -123.0d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9118, 4269, 8240, 0.1d, "NTv2", "NVI93_05.GSB", 48.48d, 50.93d, -128.5d, -123.49d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9119, 4269, 8246, 0.1d, "NTv2", "BC_93_05.GSB", 48.99d, 60.01d, -138.07d, -114.33d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9120, 8237, 8240, 0.1d, "NTv2", "CRD98_00.GSB", 48.25d, 49.06d, -124.52d, -123.0d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9121, 8237, 8240, 0.1d, "NTv2", "NVI98_05.GSB", 48.48d, 50.93d, -128.5d, -123.49d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9124, 7911, 6647, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013i08.byn", 38.21d, 86.46d, -141.01d, -40.73d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9125, 7911, 9245, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013ai08.byn", 38.21d, 86.46d, -141.01d, -40.73d, 7408, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9126, 8233, 6781, 0.0d, "Position Vector transformation (geocentric domain)", "", 14.92d, 86.46d, 167.65d, -47.74d, 7408, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9127, 8238, 6781, 0.0d, "Position Vector transformation (geocentric domain)", "", 14.92d, 86.46d, 167.65d, -47.74d, 7415, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9128, 8242, 6781, 0.0d, "Position Vector transformation (geocentric domain)", "", 14.92d, 86.46d, 167.65d, -47.74d, 7422, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9129, 8250, 6317, 0.0d, "Position Vector transformation (geocentric domain)", "", 14.92d, 86.46d, 167.65d, -47.74d, 7429, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9131, 5488, 9130, 0.2d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RALD2016.mnt", 16.26d, 16.38d, -61.13d, -60.97d, 7436, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9132, 4557, 9130, 0.2d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RALDW842016.mnt", 16.26d, 16.38d, -61.13d, -60.97d, 7436, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9133, 5488, 5757, 0.05d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAGTBT2016.mnt", 15.88d, 16.55d, -61.85d, -61.15d, 7436, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9134, 5488, 5616, 0.1d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RALS2016.mnt", 15.8d, 15.94d, -61.68d, -61.52d, 7436, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9135, 5488, 5617, 0.1d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAMG2016.mnt", 15.8d, 16.05d, -61.39d, -61.13d, 7436, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9136, 5488, 5756, 0.05d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAMART2016.mnt", 14.35d, 14.93d, -61.29d, -60.76d, 7436, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9137, 4466, 5792, 0.2d, "Geographic3D to GravityRelatedHeight (IGN2009)", "GGSPM06v1.mnt", 46.69d, 47.19d, -56.48d, -56.07d, 7436, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9142, 3906, 9140, 1.0d, "Position Vector transformation (geog2D domain)", "", 41.85d, 43.25d, 19.97d, 21.8d, 7436, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9143, 3906, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 41.85d, 43.25d, 19.97d, 21.8d, 7443, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9144, 9140, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 41.85d, 43.25d, 19.97d, 21.8d, 7450, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9145, 7815, 4912, 1.0d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7453, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9160, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u01.bin", 41.0d, 49.05d, -124.79d, -112.0d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9161, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u02.bin", 41.0d, 49.38d, -112.0d, -95.0d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9162, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u03.bin", 41.0d, 49.37d, -95.0d, -77.99d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9163, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u04.bin", 41.0d, 47.47d, -78.0d, -66.91d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9164, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u05.bin", 31.64d, 41.0d, -124.45d, -112.0d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9165, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u06.bin", 25.83d, 41.01d, -112.0d, -94.99d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9166, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u07.bin", 24.41d, 41.01d, -95.0d, -77.99d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9167, 4957, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g1999u08.bin", 33.84d, 41.01d, -78.0d, -71.9d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9168, 8542, 5703, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "geoid03_conus.bin", 24.41d, 49.38d, -124.79d, -66.91d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9169, 4957, 5703, 0.02d, "Geographic3D to GravityRelatedHeight (NGS bin)", "geoid06_ak.bin", 51.3d, 71.4d, 172.42d, -129.99d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9170, 8542, 6643, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2009s01.bin", -14.43d, -14.2d, -170.88d, -170.51d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9171, 8542, 6644, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2009g01.bin", 13.18d, 13.7d, 144.58d, 145.01d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9172, 8542, 6640, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2009g01.bin", 14.06d, 15.35d, 145.06d, 145.89d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9173, 4893, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "geoid09_conus.bin", 24.41d, 49.38d, -124.79d, -66.91d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9174, 4893, 5703, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "geoid09_ak.bin", 51.3d, 71.4d, 172.42d, -129.99d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9175, 4893, 6641, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2009p01.bin", 17.87d, 18.57d, -67.97d, -65.19d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9176, 4893, 6642, 0.05d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2009p01.bin", 17.62d, 18.44d, -65.09d, -64.51d, 7460, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9177, 5358, 4919, 0.1d, "Time-specific Position Vector transform (geocen)", "", -59.87d, -17.5d, -113.21d, -65.72d, 7460, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9179, 9146, 9015, 0.1d, "Time-specific Position Vector transform (geocen)", "", -59.87d, -17.5d, -113.21d, -65.72d, 7468, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9180, 9151, 9015, 0.1d, "Time-specific Position Vector transform (geocen)", "", -59.87d, -17.5d, -113.21d, -65.72d, 7476, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9181, 4152, 8545, 0.05d, "NADCON5 (3D)", "nadcon5.nad83_1993.nad83_1997.prvi.lat.trn.20160901.b", 17.62d, 18.57d, -67.97d, -64.51d, 7484, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9185, 4202, 7844, 0.05d, "Coordinate Frame rotation (geog2D domain)", "", -35.93d, -35.12d, 148.76d, 149.4d, 7484, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9186, 8999, 6990, 0.05d, "Coordinate Frame rotation (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 7491, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9187, 5488, 5619, 0.1d, "Geographic3D to GravityRelatedHeight (IGN2009)", "gg10_sbv2.mnt", 17.82d, 17.98d, -62.92d, -62.73d, 7498, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9188, 5488, 5620, 0.1d, "Geographic3D to GravityRelatedHeight (IGN2009)", "gg10_smv2.mnt", 18.01d, 18.17d, -63.21d, -62.96d, 7498, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9189, 4326, 6990, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 7498, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9224, 4230, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 53.49d, 55.92d, 3.34d, 8.88d, 7505, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9225, 4978, 4936, 0.1d, "Time-specific Position Vector transform (geocen)", "", 53.49d, 55.92d, 3.34d, 8.88d, 7512, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9226, 7886, 4710, 0.1d, "Position Vector transformation (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 7520, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9227, 4896, 8247, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 7527, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9228, 4466, 5792, 0.05d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RASPM2018.mnt", 46.69d, 47.19d, -56.48d, -56.07d, 7542, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9229, 6319, 5703, 0.015d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2018u0.bin", 24.41d, 49.38d, -124.79d, -66.91d, 7542, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9230, 6319, 6641, 0.015d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2018p0.bin", 17.87d, 18.57d, -67.97d, -65.19d, 7542, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9231, 6319, 6642, 0.015d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2018p0.bin", 17.62d, 18.44d, -65.09d, -64.51d, 7542, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9232, 4659, 8086, 0.05d, "NTv2", "ISN93_ISN2016.gsb", 63.34d, 66.59d, -24.63d, -13.38d, 7542, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9233, 5324, 8086, 0.05d, "NTv2", "ISN2004_ISN2016.gsb", 63.34d, 66.59d, -24.63d, -13.38d, 7542, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9234, 4145, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", 24.16d, 28.61d, 68.27d, 71.14d, 7542, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9235, 4122, 8240, 1.5d, "NTv2", "NS778301.gsb", 43.41d, 47.08d, -66.28d, -59.73d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9236, 4122, 8237, 1.5d, "NTv2", "PE7783V2.gsb", 45.9d, 47.09d, -64.49d, -61.9d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9237, 4122, 8237, 1.5d, "NTv2", "NB7783v2.gsb", 44.56d, 48.07d, -69.05d, -63.7d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9238, 4267, 8237, 0.8d, "NTv2", "NB2783v2.gsb", 44.56d, 48.07d, -69.05d, -63.7d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9239, 4267, 8237, 1.5d, "NTv2", "QUE27-98.gsb", 44.99d, 62.62d, -79.85d, -57.1d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9240, 4609, 8237, 1.5d, "NTv2", "CGQ77-98.gsb", 44.99d, 62.62d, -79.85d, -57.1d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9241, 4269, 8237, 1.5d, "NTv2", "NAD83-98.gsb", 44.99d, 62.62d, -79.85d, -57.1d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9244, 4269, 8246, 1.5d, "NTv2", "AB_CSRS.DAC", 48.99d, 60.0d, -120.0d, -109.98d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9246, 8251, 6647, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013n83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9247, 8251, 9245, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9256, 5342, 9255, 0.05d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "GEOIDE-Ar16.gri", -55.11d, -21.78d, -73.59d, -53.65d, 7549, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9257, 4160, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", -36.37d, -31.96d, -69.4d, -66.42d, 7549, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9258, 4160, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", -40.17d, -34.26d, -71.19d, -66.52d, 7552, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9259, 4161, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", -50.34d, -42.49d, -73.59d, -65.47d, 7555, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9260, 9248, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -52.43d, -50.33d, -73.28d, -68.3d, 7558, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9261, 9251, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", -55.11d, -52.59d, -68.64d, -63.73d, 7561, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9262, 9253, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", -55.11d, -52.59d, -68.64d, -63.73d, 7564, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9263, 4254, 4326, 2.5d, "Geocentric translations (geog2D domain)", "", -55.11d, -52.59d, -68.64d, -63.73d, 7567, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9264, 5340, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", -58.41d, -21.78d, -73.59d, -52.63d, 7570, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9275, 5778, 9274, 0.05d, "Vertical Offset by Grid Interpolation (BEV AT)", "GV_HoehenGrid_V1.csv", 46.4d, 49.02d, 9.53d, 17.17d, 7573, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9276, 11056, 9274, 0.05d, "Geographic3D to GravityRelatedHeight (BEV AT)", "GEOID_GRS80_Oesterreich.csv", 46.4d, 49.02d, 9.53d, 17.17d, 7574, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9277, 9267, 9274, 0.05d, "Geographic3D to GravityRelatedHeight (BEV AT)", "GEOID_BESSEL_Oesterreich.csv", 46.4d, 49.02d, 9.53d, 17.17d, 7575, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9280, 7910, 9279, 0.07d, "Geographic3D to GravityRelatedHeight (txt)", "SAGEOID2010.dat", -34.88d, -22.13d, 16.45d, 32.95d, 7576, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9281, 4289, 11037, 0.25d, "Coordinate Frame rotation (geog2D domain)", "", 50.75d, 55.77d, 2.53d, 7.22d, 7576, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9282, 4289, 11037, 0.001d, "NTv2", "rdtrans2018.gsb", 50.75d, 55.77d, 2.53d, 7.22d, 7583, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9283, 11036, 5709, 0.01d, "Geographic3D to GravityRelatedHeight (gtx)", "nlgeo2018.gtx", 50.75d, 55.77d, 2.53d, 7.22d, 7583, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9291, 8086, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 59.96d, 69.59d, -30.87d, -5.55d, 7583, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9298, 9292, 4978, 0.1d, "Coordinate Frame rotation (geocentric domain)", "", 14.33d, 26.74d, 51.99d, 63.38d, 7586, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9302, 9299, 11009, 0.0d, "NTv2", "HS2TN15_NTv2.gsb", 51.45d, 53.3d, -2.75d, 0.0d, 7593, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9304, 11008, 9303, 0.001d, "Geographic3D to GravityRelatedHeight (EGM)", "HS2GM15W.grd", 51.45d, 53.3d, -2.75d, 0.0d, 7593, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9305, 9469, 9471, 0.0d, "Geographic3D to GravityRelatedHeight (gtx)", "INAGEOID2020v1.gtx", -13.95d, 7.79d, 92.01d, 141.46d, 7593, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9310, 4314, 4258, 0.01d, "NTv2", "SeTa2016.gsb", 49.11d, 49.64d, 6.35d, 7.41d, 7593, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9312, 7839, 5759, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "auckht1946-nzvd2016.gtx", -37.67d, -36.12d, 174.0d, 176.17d, 7593, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9313, 7839, 5760, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "blufht1955-nzvd2016.gtx", -46.71d, -46.26d, 168.01d, 168.86d, 7594, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9314, 7839, 5761, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "duneht1958-nzvd2016.gtx", -46.4d, -43.82d, 167.73d, 171.28d, 7595, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9315, 7839, 4458, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "dublht1960-nzvd2016.gtx", -46.73d, -44.52d, 166.37d, 169.95d, 7596, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9316, 7839, 5762, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "gisbht1926-nzvd2016.gtx", -39.04d, -37.49d, 176.41d, 178.63d, 7597, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9317, 7839, 5763, 0.01d, "Vertical Offset by Grid Interpolation (gtx)", "lyttht1937-nzvd2016.gtx", -44.92d, -41.6d, 168.95d, 173.77d, 7598, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9318, 7839, 5764, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "motuht1953-nzvd2016.gtx", -40.59d, -37.52d, 174.57d, 177.26d, 7599, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9319, 7839, 5765, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "napiht1962-nzvd2016.gtx", -40.57d, -38.87d, 175.8d, 178.07d, 7600, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9320, 7839, 5766, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "nelsht1955-nzvd2016.gtx", -42.44d, -40.44d, 171.82d, 174.46d, 7601, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9321, 7839, 5767, 0.01d, "Vertical Offset by Grid Interpolation (gtx)", "ontpht1964-nzvd2016.gtx", -36.41d, -34.36d, 172.61d, 174.83d, 7602, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9322, 7839, 5772, 0.18d, "Vertical Offset by Grid Interpolation (gtx)", "stisht1977-nzvd2016.gtx", -47.33d, -46.63d, 167.29d, 168.34d, 7603, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9323, 7839, 5769, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "taraht1970-nzvd2016.gtx", -39.92d, -38.41d, 173.68d, 174.95d, 7604, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9324, 7839, 5770, 0.02d, "Vertical Offset by Grid Interpolation (gtx)", "wellht1953-nzvd2016.gtx", -41.67d, -40.12d, 174.52d, 176.55d, 7605, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9325, 4959, 4440, 0.1d, "Geographic3D to GravityRelatedHeight (gtx)", "nzgeoid2009.gtx", -55.95d, -25.88d, 160.6d, -171.2d, 7606, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9326, 4959, 7839, 0.1d, "Geographic3D to GravityRelatedHeight (gtx)", "nzgeoid2016.gtx", -55.95d, -25.88d, 160.6d, -171.2d, 7606, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9327, 4275, 4171, 1.0d, "Geocentric translations (geog2D domain) by grid (IGN)", "gr3df97a.txt", 41.31d, 51.14d, -4.87d, 9.63d, 7606, 2), + new EpsgOperationRecord((EpsgOperationType)0, 9328, 4644, 4749, 0.05d, "Geocentric translations (geog2D domain) by grid (IGN)", "gr3dnc03a.mnt", -22.37d, -22.19d, 166.35d, 166.54d, 7608, 2), + new EpsgOperationRecord((EpsgOperationType)0, 9329, 4662, 4749, 0.1d, "Geocentric translations (geog2D domain) by grid (IGN)", "gr3dnc01b.mnt", -22.45d, -20.03d, 163.92d, 167.09d, 7610, 2), + new EpsgOperationRecord((EpsgOperationType)0, 9330, 4662, 4749, 0.05d, "Geocentric translations (geog2D domain) by grid (IGN)", "gr3dnc02b.mnt", -22.37d, -22.19d, 166.35d, 166.54d, 7612, 2), + new EpsgOperationRecord((EpsgOperationType)0, 9334, 7789, 9331, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", 16.29d, 32.16d, 34.44d, 55.67d, 7614, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9338, 4314, 4258, 0.1d, "NTv2", "BWTA2017.gsb", 47.54d, 49.8d, 7.51d, 10.5d, 7629, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9342, 4248, 9148, 5.0d, "Geocentric translations (geog2D domain)", "", -26.0d, -17.5d, -70.79d, -67.0d, 7629, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9343, 4248, 9148, 5.0d, "Geocentric translations (geog2D domain)", "", -36.0d, -26.0d, -72.87d, -68.28d, 7632, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9344, 4248, 9148, 5.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -35.99d, -74.48d, -70.39d, 7635, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9345, 4248, 9153, 5.0d, "Geocentric translations (geog2D domain)", "", -26.0d, -17.5d, -70.79d, -67.0d, 7638, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9346, 4248, 9153, 5.0d, "Geocentric translations (geog2D domain)", "", -36.0d, -26.0d, -72.87d, -68.28d, 7641, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9347, 4248, 9153, 5.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -35.99d, -74.48d, -70.39d, 7644, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9349, 4618, 9148, 5.0d, "Geocentric translations (geog2D domain)", "", -55.96d, -51.99d, -74.83d, -66.33d, 7647, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9350, 4618, 9153, 5.0d, "Geocentric translations (geog2D domain)", "", -55.96d, -51.99d, -74.83d, -66.33d, 7650, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9352, 4907, 9351, 0.03d, "Geographic3D to GravityRelatedHeight (IGN2009)", "Ranc08_Circe.mnt", -22.73d, -19.5d, 163.54d, 168.19d, 7653, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9355, 9332, 9335, 0.1d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "KSA-GEOID17.gra", 16.37d, 32.16d, 34.51d, 55.67d, 7653, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9361, 8818, 9333, 0.1d, "Position Vector transformation (geog2D domain)", "", 16.29d, 32.16d, 34.44d, 55.67d, 7653, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9362, 4204, 9333, 2.0d, "Geocentric translations (geog2D domain)", "", 16.37d, 32.16d, 34.51d, 55.67d, 7660, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9363, 4204, 9333, 0.1d, "Geocentric translations (geog2D domain) by grid (IGN)", "ARAMCO_AAA-KSAGRF_6.tac", 16.37d, 32.16d, 34.51d, 55.67d, 7663, 2), + new EpsgOperationRecord((EpsgOperationType)0, 9365, 11009, 9364, 0.0d, "NTv2", "TN15-ETRS89-to-TPEN11-IRF.gsb", 53.32d, 53.9d, -3.14d, -1.34d, 7665, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9369, 11009, 9372, 0.0d, "NTv2", "TN15-ETRS89-to-MML07-IRF.gsb", 51.46d, 53.42d, -1.89d, 0.16d, 7665, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9371, 8881, 5778, 0.0d, "Vertical Offset", "", 48.12d, 48.34d, 16.18d, 16.59d, 7665, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9381, 7789, 9378, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7666, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9382, 8227, 9378, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7674, 8), + new EpsgOperationRecord((EpsgOperationType)0, 9383, 9333, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 16.29d, 32.16d, 34.44d, 55.67d, 7682, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9386, 11009, 9384, 0.0d, "NTv2", "TN15-ETRS89-to-AbInvA96_2020-IRF.gsb", 57.1d, 57.71d, -4.31d, -2.1d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9408, 4230, 11134, 0.2d, "NTv2", "PENR2009.gsb", 35.84d, 43.82d, -9.37d, 3.39d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9409, 4230, 11134, 0.2d, "NTv2", "BALR2009.gsb", 38.59d, 40.15d, 1.12d, 4.39d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9410, 11130, 5782, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.95d, 43.82d, -9.37d, 3.39d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9411, 11130, 9392, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 39.07d, 40.02d, 2.23d, 3.55d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9412, 11130, 9393, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 39.75d, 40.15d, 3.73d, 4.39d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9413, 4937, 9394, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 38.77d, 39.17d, 1.12d, 1.68d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9414, 11130, 9402, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.84d, 35.97d, -5.4d, -5.24d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9415, 4080, 9395, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 28.78d, 29.47d, -13.95d, -13.37d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9416, 4080, 9396, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.99d, 28.81d, -14.58d, -13.75d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9417, 4080, 9397, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.68d, 28.23d, -15.88d, -15.31d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9418, 4080, 9398, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.93d, 28.63d, -16.96d, -16.08d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9419, 4080, 9399, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.95d, 28.26d, -17.39d, -17.03d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9420, 4080, 9400, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 28.4d, 28.9d, -18.06d, -17.66d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9421, 4080, 9401, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.58d, 27.9d, -18.22d, -17.83d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9454, 11009, 9453, 0.0d, "NTv2", "TN15-ETRS89-to-GBK19-IRF.gsb", 55.55d, 55.95d, -4.65d, -4.05d, 7689, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9459, 9307, 7842, 0.03d, "Time-dependent Coordinate Frame rotation (geocen)", "", -60.55d, -8.47d, 93.41d, 173.34d, 7689, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9460, 7789, 9307, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -60.55d, -8.47d, 93.41d, 173.34d, 7704, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9466, 7843, 9463, 0.15d, "Geog3D to Geog2D+GravityRelatedHeight (AUSGeoidv2)", "AUSGeoid2020_20180201.gsb", -43.7d, -9.86d, 96.76d, 153.69d, 7711, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9467, 4939, 9464, 0.15d, "Geog3D to Geog2D+GravityRelatedHeight (AUSGeoidv2)", "AUSGeoid09_V1.01.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 7712, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9472, 4755, 9470, 0.2d, "Coordinate Frame rotation (geog2D domain)", "", -13.95d, 7.79d, 92.01d, 141.46d, 7713, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9484, 10874, 5776, 0.02d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "href2008a.bin", 57.9d, 71.24d, 4.39d, 31.32d, 7720, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9485, 10874, 5941, 0.02d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "HREF2018B_NN2000_EUREF89.bin", 57.9d, 71.24d, 4.39d, 31.32d, 7720, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9486, 3906, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 7720, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9495, 3906, 8685, 0.46d, "Coordinate Frame rotation (geog2D domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 7727, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9496, 3906, 8685, 0.03d, "NTv2", "MGI1901_TO_SRBETRS89_NTv2.gsb", 42.23d, 46.19d, 18.81d, 23.01d, 7734, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9550, 4269, 8252, 0.1d, "NTv2", "NLCSRSV4A.GSB", 46.56d, 51.68d, -59.48d, -52.54d, 7734, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9551, 5775, 9389, 0.02d, "Vertical Offset", "", 35.81d, 42.15d, 25.62d, 44.83d, 7734, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9552, 5775, 9390, 0.02d, "Vertical Offset", "", 35.81d, 42.15d, 25.62d, 44.83d, 7735, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9553, 5780, 9389, 0.028d, "Vertical Offset by Grid Interpolation (asc)", "pt_2019z.asc", 36.95d, 42.16d, -9.56d, -6.19d, 7736, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9554, 5780, 9390, 0.024d, "Vertical Offset by Grid Interpolation (asc)", "pt_2019m.asc", 36.95d, 42.16d, -9.56d, -6.19d, 7737, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9555, 7837, 9389, 0.02d, "Vertical Offset by Grid Interpolation (asc)", "de_2019z.asc", 47.27d, 55.09d, 5.86d, 15.04d, 7738, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9556, 7837, 9390, 0.008d, "Vertical Offset by Grid Interpolation (asc)", "de_2019m.asc", 47.27d, 55.09d, 5.86d, 15.04d, 7739, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9557, 5778, 9389, 0.136d, "Vertical Offset by Grid Interpolation (asc)", "at_2019z.asc", 46.4d, 49.02d, 9.53d, 17.17d, 7740, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9558, 5778, 9390, 0.13d, "Vertical Offset by Grid Interpolation (asc)", "at_2019m.asc", 46.4d, 49.02d, 9.53d, 17.17d, 7741, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9561, 5701, 9389, 0.024d, "Vertical Offset by Grid Interpolation (asc)", "gb_2019z.asc", 49.93d, 58.71d, -7.06d, 1.8d, 7742, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9562, 5701, 9390, 0.02d, "Vertical Offset", "", 49.93d, 58.71d, -7.06d, 1.8d, 7743, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9563, 5710, 9389, 0.042d, "Vertical Offset by Grid Interpolation (asc)", "be_2019z.asc", 49.5d, 51.51d, 2.5d, 6.4d, 7744, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9564, 5710, 9390, 0.04d, "Vertical Offset by Grid Interpolation (asc)", "be_2019m.asc", 49.5d, 51.51d, 2.5d, 6.4d, 7745, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9565, 8690, 9389, 0.006d, "Vertical Offset by Grid Interpolation (asc)", "si_2019z.asc", 45.42d, 46.88d, 13.38d, 16.61d, 7746, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9566, 8690, 9390, 0.008d, "Vertical Offset by Grid Interpolation (asc)", "si_2019m.asc", 45.42d, 46.88d, 13.38d, 16.61d, 7747, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9567, 5195, 9389, 0.042d, "Vertical Offset by Grid Interpolation (asc)", "mk_2019z.asc", 40.85d, 42.36d, 20.45d, 23.04d, 7748, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9568, 5195, 9390, 0.044d, "Vertical Offset by Grid Interpolation (asc)", "mk_2019m.asc", 40.85d, 42.36d, 20.45d, 23.04d, 7749, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9569, 5195, 9389, 0.012d, "Vertical Offset by Grid Interpolation (asc)", "ba_2019z.asc", 42.56d, 45.27d, 15.74d, 19.62d, 7750, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9570, 5195, 9390, 0.01d, "Vertical Offset by Grid Interpolation (asc)", "ba_2019m.asc", 42.56d, 45.27d, 15.74d, 19.62d, 7751, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9571, 5786, 9389, 0.048d, "Vertical Offset by Grid Interpolation (asc)", "bgalt_2019z.asc", 41.24d, 44.23d, 22.36d, 28.68d, 7752, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9572, 5786, 9390, 0.042d, "Vertical Offset by Grid Interpolation (asc)", "bgalt_2019m.asc", 41.24d, 44.23d, 22.36d, 28.68d, 7753, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9573, 3900, 9389, 0.004d, "Vertical Offset by Grid Interpolation (asc)", "fi_2019z.asc", 59.75d, 70.09d, 19.24d, 31.59d, 7754, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9574, 3900, 9390, 0.024d, "Vertical Offset by Grid Interpolation (asc)", "fi_2019m.asc", 59.75d, 70.09d, 19.24d, 31.59d, 7755, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9575, 5720, 9389, 0.108d, "Vertical Offset by Grid Interpolation (asc)", "fr_2019z.asc", 42.33d, 51.14d, -4.87d, 8.23d, 7756, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9576, 5720, 9390, 0.086d, "Vertical Offset by Grid Interpolation (asc)", "fr_2019m.asc", 42.33d, 51.14d, -4.87d, 8.23d, 7757, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9577, 5787, 9389, 0.006d, "Vertical Offset by Grid Interpolation (asc)", "hu_2019z.asc", 45.74d, 48.58d, 16.11d, 22.9d, 7758, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9578, 5787, 9390, 0.01d, "Vertical Offset by Grid Interpolation (asc)", "hu_2019m.asc", 45.74d, 48.58d, 16.11d, 22.9d, 7759, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9579, 7700, 9389, 0.006d, "Vertical Offset by Grid Interpolation (asc)", "lv_2019z.asc", 55.67d, 58.09d, 20.87d, 28.24d, 7760, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9580, 7700, 9390, 0.008d, "Vertical Offset by Grid Interpolation (asc)", "lv_2019m.asc", 55.67d, 58.09d, 20.87d, 28.24d, 7761, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9581, 5709, 9389, 0.016d, "Vertical Offset by Grid Interpolation (asc)", "nl_2019z.asc", 50.75d, 53.7d, 3.2d, 7.22d, 7762, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9582, 5709, 9390, 0.012d, "Vertical Offset by Grid Interpolation (asc)", "nl_2019m.asc", 50.75d, 53.7d, 3.2d, 7.22d, 7763, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9583, 5781, 9389, 0.12d, "Vertical Offset by Grid Interpolation (asc)", "ro_2019z.asc", 43.62d, 48.27d, 20.26d, 29.74d, 7764, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9584, 11008, 9428, 0.011d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 56.76d, 58.54d, -7.72d, -6.1d, 7765, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9585, 11008, 9430, 0.01d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.86d, 49.99d, -6.41d, -6.23d, 7766, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9586, 11008, 9426, 0.017d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 58.72d, 59.41d, -3.48d, -2.34d, 7767, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9587, 11008, 9424, 0.008d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.93d, 58.71d, -7.06d, 1.8d, 7768, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9588, 4937, 9425, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.75d, 61.01d, -9.01d, 2.01d, 7769, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9589, 11008, 9427, 0.018d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 59.83d, 60.87d, -1.78d, -0.67d, 7770, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9590, 4937, 9429, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 54.02d, 54.44d, -4.87d, -4.27d, 7771, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9591, 4943, 9449, 0.023d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Malin.gri", 51.39d, 55.43d, -10.56d, -5.34d, 7772, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9592, 4943, 9450, 0.014d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Belfast.gri", 53.96d, 55.36d, -8.18d, -5.34d, 7773, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9593, 10874, 5942, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "HREF2018B_NN2000_EUREF89.bin", 57.9d, 71.24d, 4.39d, 31.32d, 7774, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9594, 10874, 6144, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "href2008a.bin", 57.9d, 71.24d, 4.39d, 31.32d, 7775, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9595, 6319, 6349, 0.015d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2018u0.bin", 24.41d, 49.38d, -124.79d, -66.91d, 7776, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9596, 6319, 6349, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2012ba0.bin", 51.3d, 71.4d, 172.42d, -129.99d, 7777, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9597, 11036, 9286, 0.01d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "nlgeo2018.gtx", 50.75d, 55.77d, 2.53d, 7.22d, 7778, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9598, 4909, 8349, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "gr2000g.gri", 59.0d, 84.01d, -75.0d, -10.0d, 7779, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9599, 4909, 8350, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "ggeoid16.gri", 58.0d, 85.01d, -75.0d, -6.99d, 7780, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9600, 11056, 9500, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (BEV AT)", "GEOID_GRS80_Oesterreich.csv", 46.4d, 49.02d, 9.53d, 17.17d, 7781, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9601, 9267, 9501, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (BEV AT)", "GEOID_BESSEL_Oesterreich.csv", 46.4d, 49.02d, 9.53d, 17.17d, 7782, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9602, 6134, 9502, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (CI)", "CBGM0811.TXT", 19.66d, 19.78d, -79.92d, -79.69d, 7783, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9603, 6134, 9503, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (CI)", "GCGM0811.TXT", 19.21d, 19.41d, -81.46d, -81.04d, 7784, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9604, 6134, 9504, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (CI)", "LCGM0811.TXT", 19.63d, 19.74d, -80.14d, -79.93d, 7785, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9605, 11130, 9505, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.95d, 43.82d, -9.37d, 3.39d, 7786, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9606, 11130, 9506, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.84d, 35.97d, -5.4d, -5.24d, 7787, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9607, 11130, 9507, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 38.77d, 39.17d, 1.12d, 1.68d, 7788, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9608, 11130, 9508, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 39.07d, 40.02d, 2.23d, 3.55d, 7789, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9609, 11130, 9509, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 39.75d, 40.15d, 3.73d, 4.39d, 7790, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9610, 4080, 9510, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.58d, 27.9d, -18.22d, -17.83d, 7791, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9611, 4080, 9511, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.99d, 28.81d, -14.58d, -13.75d, 7792, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9612, 4080, 9512, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.68d, 28.23d, -15.88d, -15.31d, 7793, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9613, 4080, 9513, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.95d, 28.26d, -17.39d, -17.03d, 7794, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9614, 4080, 9514, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 28.4d, 28.9d, -18.06d, -17.66d, 7795, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9615, 4080, 9515, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 28.78d, 29.47d, -13.95d, -13.37d, 7796, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9616, 4080, 9516, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP_Canarias.txt", 27.93d, 28.63d, -16.96d, -16.08d, 7797, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9618, 4979, 9518, 0.11d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "Und_min1x1_egm2008_isw=82_WGS84_TideFree", -90.0d, 90.0d, -180.0d, 180.0d, 7798, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9619, 5592, 9519, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "fehmarn_geoid10.gri", 54.42d, 54.76d, 11.17d, 11.51d, 7799, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9620, 9332, 9520, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "KSA-GEOID17.gra", 16.37d, 32.16d, 34.51d, 55.67d, 7800, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9621, 5342, 9521, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "GEOIDE-Ar16.gri", -55.11d, -21.78d, -73.59d, -53.65d, 7801, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9622, 6319, 9522, 0.015d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2018p0.bin", 17.87d, 18.57d, -67.97d, -65.19d, 7802, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9623, 6319, 9523, 0.015d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2018p0.bin", 17.62d, 18.44d, -65.09d, -64.51d, 7803, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9624, 6324, 9524, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2012bg0.bin", 13.18d, 13.7d, 144.58d, 145.01d, 7804, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9625, 6324, 9525, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2012bg0.bin", 14.06d, 15.35d, 145.06d, 145.89d, 7805, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9626, 6321, 9526, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2012bs0.bin", -14.43d, -14.2d, -170.88d, -170.51d, 7806, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9627, 4959, 9527, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "nzgeoid2009.gtx", -55.95d, -25.88d, 160.6d, -171.2d, 7807, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9628, 4959, 9528, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "nzgeoid2016.gtx", -55.95d, -25.88d, 160.6d, -171.2d, 7808, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9629, 9469, 9529, 0.0d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "INAGEOID2020v1.gtx", -13.95d, 7.79d, 92.01d, 141.46d, 7809, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9630, 4967, 9530, 998.0d, "Geog3D to Geog2D+GravityRelatedHeight (IGN1997)", "ggguy00.txt", 2.11d, 5.81d, -54.61d, -51.61d, 7810, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9631, 5488, 9531, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAGTBT2016.mnt", 15.88d, 16.55d, -61.85d, -61.15d, 7811, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9632, 5488, 9532, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RALS2016.mnt", 15.8d, 15.94d, -61.68d, -61.52d, 7812, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9633, 5488, 9533, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAMG2016.mnt", 15.8d, 16.05d, -61.39d, -61.13d, 7813, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9634, 5488, 9534, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "gg10_sbv2.mnt", 17.82d, 17.98d, -62.92d, -62.73d, 7814, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9635, 5488, 9535, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "gg10_smv2.mnt", 18.01d, 18.17d, -63.21d, -62.96d, 7815, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9636, 5488, 9536, 0.2d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RALD2016.mnt", 16.26d, 16.38d, -61.13d, -60.97d, 7816, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9637, 5488, 9537, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAMART2016.mnt", 14.35d, 14.93d, -61.29d, -60.76d, 7817, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9638, 9776, 9538, 0.01d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAF18.mnt", 42.33d, 51.14d, -4.87d, 8.23d, 7818, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9639, 9776, 9539, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAC09.mnt", 41.31d, 43.07d, 8.5d, 9.63d, 7819, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9640, 4907, 9540, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "Ranc08_Circe.mnt", -22.73d, -19.5d, 163.54d, 168.19d, 7820, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9641, 4466, 9541, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RASPM2018.mnt", 46.69d, 47.19d, -56.48d, -56.07d, 7821, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9642, 4557, 9542, 0.2d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RALDW842016.mnt", 16.26d, 16.38d, -61.13d, -60.97d, 7822, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9643, 7910, 9543, 0.07d, "Geog3D to Geog2D+GravityRelatedHeight (txt)", "SAGEOID2010.dat", -34.88d, -22.13d, 16.45d, 32.95d, 7823, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9644, 8251, 9544, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 7824, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9645, 5781, 9390, 0.02d, "Vertical Offset by Grid Interpolation (asc)", "ro_2019m.asc", 43.62d, 48.27d, 20.26d, 29.74d, 7825, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9646, 5782, 9389, 0.082d, "Vertical Offset by Grid Interpolation (asc)", "es_2019z.asc", 35.95d, 43.82d, -9.37d, 3.39d, 7826, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9647, 5782, 9390, 0.078d, "Vertical Offset by Grid Interpolation (asc)", "es_2019m.asc", 35.95d, 43.82d, -9.37d, 3.39d, 7827, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9648, 5613, 9389, 0.006d, "Vertical Offset by Grid Interpolation (asc)", "se_2019z.asc", 55.28d, 69.07d, 10.93d, 24.17d, 7828, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9649, 5613, 9390, 0.032d, "Vertical Offset by Grid Interpolation (asc)", "se_2019m.asc", 55.28d, 69.07d, 10.93d, 24.17d, 7829, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9652, 9650, 9389, 0.02d, "Vertical Offset by Grid Interpolation (asc)", "pl86_2019z.asc", 49.0d, 54.89d, 14.14d, 24.15d, 7830, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9653, 9650, 9390, 0.02d, "Vertical Offset by Grid Interpolation (asc)", "pl86_2019m.asc", 49.0d, 54.89d, 14.14d, 24.15d, 7831, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9654, 9651, 9389, 0.006d, "Vertical Offset by Grid Interpolation (asc)", "pl07_2019z.asc", 49.0d, 54.89d, 14.14d, 24.15d, 7832, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9655, 9651, 9390, 0.012d, "Vertical Offset by Grid Interpolation (asc)", "pl07_2019m.asc", 49.0d, 54.89d, 14.14d, 24.15d, 7833, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9662, 9650, 9651, 0.04d, "Vertical Offset by Grid Interpolation (PL txt)", "gugik-evrf2007.txt", 49.0d, 54.89d, 14.14d, 24.15d, 7834, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9664, 9663, 9389, 0.002d, "Vertical Offset by Grid Interpolation (asc)", "ee_2019z.asc", 57.52d, 59.75d, 21.74d, 28.2d, 7835, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9665, 9663, 9390, 0.004d, "Vertical Offset by Grid Interpolation (asc)", "ee_2019m.asc", 57.52d, 59.75d, 21.74d, 28.2d, 7836, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9667, 9666, 9389, 0.01d, "Vertical Offset by Grid Interpolation (asc)", "lt_2019z.asc", 53.89d, 56.45d, 20.86d, 26.82d, 7837, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9668, 9666, 9390, 0.014d, "Vertical Offset by Grid Interpolation (asc)", "lt_2019m.asc", 53.89d, 56.45d, 20.86d, 26.82d, 7838, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9670, 9669, 9389, 0.036d, "Vertical Offset by Grid Interpolation (asc)", "bgneu_2019z.asc", 41.24d, 44.23d, 22.36d, 28.68d, 7839, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9671, 9669, 9390, 0.032d, "Vertical Offset by Grid Interpolation (asc)", "bgneu_2019m.asc", 41.24d, 44.23d, 22.36d, 28.68d, 7840, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9676, 4141, 4326, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 29.45d, 33.28d, 34.17d, 35.69d, 7841, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9679, 4682, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 18.56d, 26.64d, 88.01d, 92.67d, 7848, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9682, 7789, 4938, 0.035d, "Time-dependent Coordinate Frame rotation (geocen)", "", -60.55d, -8.47d, 93.41d, 173.34d, 7851, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9684, 9307, 4938, 0.035d, "Time-dependent Coordinate Frame rotation (geocen)", "", -60.55d, -8.47d, 93.41d, 173.34d, 7866, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9686, 4938, 7664, 0.25d, "Time-dependent Coordinate Frame rotation (geocen)", "", -60.55d, -8.47d, 93.41d, 173.34d, 7881, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9688, 4283, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -60.55d, -8.47d, 93.41d, 173.34d, 7896, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9689, 4283, 4326, 3.0d, "NTv2", "GDA94_GDA2020_conformal_and_distortion.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 7903, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9690, 4326, 7844, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -60.55d, -8.47d, 93.41d, 173.34d, 7903, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9691, 4326, 7844, 3.0d, "NTv2", "GDA94_GDA2020_conformal_and_distortion.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 7910, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9692, 7843, 9458, 0.1d, "Geographic3D to GravityRelatedHeight (AUSGeoid v2)", "AGQG_20201120.gsb", -60.55d, -8.47d, 93.41d, 173.34d, 7910, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9693, 7843, 9462, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (AUSGeoidv2)", "AGQG_20201120.gsb", -60.55d, -8.47d, 93.41d, 173.34d, 7910, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9704, 4979, 9518, 0.113d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "Und_min2.5x2.5_egm2008_isw=82_WGS84_TideFree", -90.0d, 90.0d, -180.0d, 180.0d, 7911, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9706, 4979, 9705, 0.5d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "Und_min1x1_egm2008_isw=82_WGS84_TideFree.gz", -90.0d, 90.0d, -180.0d, 180.0d, 7912, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9708, 4979, 9707, 1.0d, "Geog3D to Geog2D+GravityRelatedHeight (EGM)", "WW15MGH.GRD", -90.0d, 90.0d, -180.0d, 180.0d, 7913, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9710, 9389, 9390, 0.0d, "zero-tide height to mean-tide height (EVRF2019)", "", 35.95d, 77.07d, -9.56d, 69.15d, 7914, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9717, 9701, 9650, 0.03d, "Geographic3D to GravityRelatedHeight (PL txt)", "gugik-geoid2011-PL-KRON86-NH.txt", 49.0d, 54.89d, 14.14d, 24.15d, 7914, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9718, 9701, 9656, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (PL txt)", "gugik-geoid2011-PL-KRON86-NH.txt", 49.0d, 54.89d, 14.14d, 24.15d, 7914, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9719, 9701, 9651, 0.03d, "Geographic3D to GravityRelatedHeight (PL txt)", "gugik-geoid2011-PL-EVRF2007-NH.txt", 49.0d, 54.89d, 14.14d, 24.15d, 7915, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9720, 9701, 9657, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (PL txt)", "gugik-geoid2011-PL-EVRF2007-NH.txt", 49.0d, 54.89d, 14.14d, 24.15d, 7915, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9726, 5214, 9721, 0.01d, "Vertical Offset", "", 36.59d, 38.35d, 12.36d, 15.71d, 7916, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9727, 6705, 5214, 0.035d, "Geographic3D to GravityRelatedHeight (ITAL2005)", "geo_igm_mar06.grd", 36.59d, 47.1d, 6.62d, 18.58d, 7917, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9728, 6705, 9722, 0.035d, "Geographic3D to GravityRelatedHeight (ITAL2005)", "geo_igm_mar06.grd", 38.82d, 41.31d, 8.08d, 9.89d, 7917, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9729, 6705, 9723, 0.035d, "Geog3D to Geog2D+GravityRelatedHeight (ITAL2005)", "geo_igm_mar06.grd", 36.59d, 47.1d, 6.62d, 18.58d, 7917, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9730, 6705, 9725, 0.035d, "Geog3D to Geog2D+GravityRelatedHeight (ITAL2005)", "geo_igm_mar06.grd", 38.82d, 41.31d, 8.08d, 9.89d, 7918, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9732, 4265, 4230, 0.1d, "NTv2", "35160622_47161840_R40_E50.gsb", 35.26d, 47.1d, 6.36d, 18.67d, 7919, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9733, 4265, 4670, 0.1d, "NTv2", "35160622_47161840_R40_F89.gsb", 35.26d, 47.1d, 6.36d, 18.67d, 7919, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9734, 4265, 6706, 0.1d, "NTv2", "35160622_47161840_R40_F00.gsb", 35.26d, 47.1d, 6.36d, 18.67d, 7919, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9735, 4230, 4670, 0.2d, "NTv2", "35160622_47161840_E50_F89.gsb", 35.26d, 47.1d, 6.36d, 18.67d, 7919, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9736, 4230, 6706, 0.2d, "NTv2", "35160622_47161840_E50_F00.gsb", 35.26d, 47.1d, 6.36d, 18.67d, 7919, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9737, 4670, 6706, 0.01d, "NTv2", "35160622_47161840_F89_F00.gsb", 35.26d, 47.1d, 6.36d, 18.67d, 7919, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9740, 11009, 9739, 0.0d, "NTv2", "TN15-ETRS89-to-EOS21-IRF.gsb", 55.55d, 57.21d, -3.56d, -1.94d, 7919, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9743, 9403, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 27.58d, 29.47d, -18.22d, -13.37d, 7919, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9744, 8357, 9390, 0.01d, "Vertical Offset and Slope", "", 48.58d, 51.06d, 12.09d, 18.86d, 7922, 6), + new EpsgOperationRecord((EpsgOperationType)0, 9745, 8357, 9389, 0.01d, "Vertical Offset and Slope", "", 48.58d, 51.06d, 12.09d, 18.86d, 7928, 6), + new EpsgOperationRecord((EpsgOperationType)0, 9751, 5365, 8907, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 2.15d, 11.77d, -90.45d, -81.43d, 7934, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9752, 5365, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 2.15d, 11.77d, -90.45d, -81.43d, 7941, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9756, 7664, 9753, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7948, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9757, 9753, 7789, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 7955, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9759, 11009, 9758, 0.0d, "NTv2", "TN15-ETRS89-to-ECML14_NB-IRF.gsb", 54.85d, 55.3d, -1.9d, -1.3d, 7962, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9764, 11009, 9763, 0.0d, "NTv2", "TN15-ETRS89-to-EWR2-IRF.gsb", 51.7d, 52.24d, -1.43d, -0.36d, 7962, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9768, 7686, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 39.19d, 43.22d, 69.24d, 80.29d, 7962, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9769, 8900, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -15.94d, -9.84d, 179.49d, -174.27d, 7965, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9770, 7073, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -67.13d, -20.91d, 37.98d, 142.0d, 7968, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9771, 5886, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -25.68d, -14.14d, -179.08d, -171.28d, 7971, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9772, 9702, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 49.0d, 55.93d, 14.14d, 24.15d, 7974, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9773, 7683, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 39.87d, 85.19d, 18.92d, -168.97d, 7977, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9774, 6318, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 14.92d, 74.71d, 167.65d, -63.88d, 7980, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9786, 9781, 5720, 0.01d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAF18b.mnt", 42.33d, 51.14d, -4.87d, 8.23d, 7983, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9787, 9781, 9785, 0.01d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAF18b.mnt", 42.33d, 51.14d, -4.87d, 8.23d, 7983, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9788, 9777, 9782, 0.005d, "Position Vector transformation (geog2D domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 7984, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9791, 9777, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 7991, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9792, 9782, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 7994, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9795, 4269, 8255, 0.1d, "NTv2", "ABCSRSV7.GSB", 48.99d, 60.0d, -120.0d, -109.98d, 7997, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9867, 11009, 9866, 0.0d, "NTv2", "TN15-ETRS89-to-MRH21-IRF.gsb", 51.35d, 53.26d, -3.27d, -0.36d, 7997, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9876, 9781, 5720, 0.01d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAF20.tac", 42.33d, 51.14d, -4.87d, 8.23d, 7997, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9877, 9781, 9785, 0.01d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAF20.tac", 42.33d, 51.14d, -4.87d, 8.23d, 7997, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9878, 11009, 9871, 0.0d, "NTv2", "TN15-ETRS89-to-MOLDOR11-IRF.gsb", 53.25d, 53.55d, -2.4d, -1.39d, 7998, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9882, 4171, 9777, 0.05d, "Geocentric translations (geog2D domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 7998, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9884, 10874, 9672, 999.0d, "Geographic3D to Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2021a.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8001, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9885, 10874, 9883, 999.0d, "Geog3D to Geog2D+Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2021a.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8001, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9886, 4267, 8237, 1.5d, "NTv2", "SK27-98.gsb", 49.0d, 60.01d, -110.0d, -101.34d, 8002, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9887, 4269, 8237, 1.5d, "NTv2", "SK83-98.gsb", 49.0d, 60.01d, -110.0d, -101.34d, 8002, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9888, 4275, 9777, 1.0d, "Geocentric translations (geog2D domain) by grid (IGN)", "gr3df97a.txt", 41.31d, 51.14d, -4.87d, 9.63d, 8002, 2), + new EpsgOperationRecord((EpsgOperationType)0, 9889, 4275, 9782, 1.0d, "Geocentric translations (geog2D domain) by grid (IGN)", "gr3df97a.txt", 41.31d, 51.14d, -4.87d, 9.63d, 8004, 2), + new EpsgOperationRecord((EpsgOperationType)0, 9890, 9777, 4275, 1.0d, "NTv2", "rgf93_ntf.gsb", 41.31d, 51.14d, -4.87d, 9.63d, 8006, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9891, 9782, 4275, 1.0d, "NTv2", "rgf93_ntf.gsb", 41.31d, 51.14d, -4.87d, 9.63d, 8006, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9896, 4612, 4326, 1.0d, "NTv2", "touhokutaiheiyouoki2011.gsb", 34.84d, 41.58d, 135.42d, 142.14d, 8006, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9898, 4181, 4258, 0.0d, "Molodensky-Badekas (CF geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 8006, 10), + new EpsgOperationRecord((EpsgOperationType)0, 9899, 4181, 4258, 0.0d, "Coordinate Frame rotation (geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 8016, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9900, 5214, 9389, 0.124d, "Vertical Offset by Grid Interpolation (asc)", "it_2019z.asc", 39.65d, 47.1d, 6.62d, 16.53d, 8023, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9902, 5705, 9389, 0.068d, "Vertical Offset by Grid Interpolation (asc)", "ua_2019z.asc", 44.32d, 52.38d, 22.15d, 40.18d, 8024, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9903, 5705, 9390, 0.064d, "Vertical Offset by Grid Interpolation (asc)", "ua_2019m.asc", 44.32d, 52.38d, 22.15d, 40.18d, 8025, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9904, 4220, 8699, 8.0d, "Geocentric translations (geog2D domain)", "", -10.09d, -9.41d, 12.66d, 13.39d, 8026, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9905, 4220, 8699, 10.0d, "Position Vector transformation (geog2D domain)", "", -8.34d, -6.03d, 11.08d, 12.75d, 8029, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9906, 4259, 8699, 5.0d, "Geocentric translations (geog2D domain)", "", -6.04d, -5.05d, 10.53d, 12.37d, 8036, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9908, 11214, 5710, 0.02d, "Geographic3D to GravityRelatedHeight (txt)", "hBG18.dat", 49.5d, 51.51d, 2.5d, 6.4d, 8039, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9909, 11214, 9907, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (txt)", "hBG18.dat", 49.5d, 51.51d, 2.5d, 6.4d, 8039, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9910, 4312, 11057, 0.14d, "NTv2", "AT_GIS_GRID_2021_09_28.gsb", 46.4d, 49.02d, 9.53d, 17.17d, 8040, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9913, 4611, 8427, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 22.13d, 22.58d, 113.76d, 114.51d, 8040, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9914, 4937, 9451, 0.02d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.75d, 61.01d, -9.01d, 2.01d, 8047, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9915, 4937, 9452, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB)", "OSTN15_OSGM15_GB.txt", 49.75d, 61.01d, -9.01d, 2.01d, 8047, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9916, 4937, 9451, 0.014d, "Geographic3D to GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Belfast.gri", 53.96d, 55.36d, -8.18d, -5.34d, 8048, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9917, 4937, 9452, 0.014d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Belfast.gri", 53.96d, 55.36d, -8.18d, -5.34d, 8048, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9918, 4937, 9451, 0.023d, "Geographic3D to GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Malin.gri", 51.39d, 55.43d, -10.56d, -5.34d, 8049, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9919, 4937, 9452, 0.023d, "Geog3D to Geog2D+GravityRelatedHeight (OSGM15-Ire)", "OSGM15_Malin.gri", 51.39d, 55.43d, -10.56d, -5.34d, 8049, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9921, 5214, 9390, 0.108d, "Vertical Offset by Grid Interpolation (asc)", "it_2019m.asc", 39.65d, 47.1d, 6.62d, 16.53d, 8050, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9925, 4937, 7837, 0.1d, "Geographic3D to GravityRelatedHeight (txt)", "GCG2016.txt", 47.27d, 55.09d, 5.86d, 15.04d, 8051, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9926, 4937, 9924, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (txt)", "GCG2016.txt", 47.27d, 55.09d, 5.86d, 15.04d, 8051, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9936, 6668, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 17.09d, 46.05d, 122.38d, 157.65d, 8052, 3), + new EpsgOperationRecord((EpsgOperationType)0, 9937, 4181, 11393, 0.0d, "Molodensky-Badekas (CF geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 8055, 10), + new EpsgOperationRecord((EpsgOperationType)0, 9938, 4181, 4258, 0.0d, "Coordinate Frame rotation (geog2D domain)", "", 49.44d, 50.19d, 5.73d, 6.53d, 8065, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9940, 4314, 4258, 0.1d, "NTv2", "HeTa2010.gsb", 49.39d, 51.66d, 7.77d, 10.24d, 8072, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9941, 11009, 9939, 0.0d, "NTv2", "TN15-ETRS89-to-EBBWV14-IRF.gsb", 51.5d, 51.85d, -3.3d, -2.89d, 8072, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9954, 4945, 8089, 0.05d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "Icegeoid_ISN93.gri", 63.24d, 66.63d, -24.63d, -13.38d, 8072, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9955, 4945, 9948, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "Icegeoid_ISN93.gri", 63.24d, 66.63d, -24.63d, -13.38d, 8072, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9956, 5323, 8089, 0.05d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "Icegeoid_ISN2004.gri", 63.24d, 66.63d, -24.63d, -13.38d, 8073, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9957, 5323, 9949, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "Icegeoid_ISN2004.gri", 63.24d, 66.63d, -24.63d, -13.38d, 8073, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9958, 8085, 8089, 0.05d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "Icegeoid_ISN2016.gri", 63.24d, 66.63d, -24.63d, -13.38d, 8074, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9959, 8085, 9950, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "Icegeoid_ISN2016.gri", 63.24d, 66.63d, -24.63d, -13.38d, 8074, 1), + new EpsgOperationRecord((EpsgOperationType)0, 9960, 7815, 7656, 0.7d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8075, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9961, 7656, 7658, 0.04d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8082, 7), + new EpsgOperationRecord((EpsgOperationType)0, 9962, 7658, 7660, 0.03d, "Time-dependent Coordinate Frame rotation (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8089, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9963, 7660, 7662, 0.02d, "Time-dependent Coordinate Frame rotation (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8104, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9965, 11009, 9964, 0.0d, "NTv2", "TN15-ETRS89-to-HULLEE13-IRF.gsb", 53.6d, 53.9d, -1.7d, -0.27d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9970, 11009, 9969, 0.0d, "NTv2", "TN15-ETRS89-to-SCM22-IRF.gsb", 55.7d, 57.55d, -4.4d, -3.3d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9975, 11009, 9974, 0.0d, "NTv2", "TN15-ETRS89-to-FNL22-IRF.gsb", 57.4d, 58.64d, -4.6d, -3.0d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9979, 4170, 4674, 0.006d, "NTv2", "SIRGAS1995-to-SIRGAS2000.gsb", -59.87d, 16.75d, -113.21d, -26.0d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9980, 4674, 8987, 0.02d, "NTv2", "SIRGAS2000-to-SIRGAS-CONSIR17P01.gsb", -59.87d, 32.72d, -122.19d, -25.28d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9983, 8239, 5713, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_1997.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9984, 8235, 5713, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_1997.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9985, 8244, 5713, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2002v70.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9986, 8251, 5713, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2010v70.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9987, 8254, 5713, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2010v70.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8119, 0), + new EpsgOperationRecord((EpsgOperationType)0, 9991, 7789, 9988, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8119, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9992, 5332, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8134, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9993, 4896, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8149, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9994, 4919, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8164, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9995, 4918, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8179, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9996, 4917, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8194, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9997, 4916, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8209, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9998, 4915, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8224, 15), + new EpsgOperationRecord((EpsgOperationType)0, 9999, 4914, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8239, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10000, 4965, 5720, 0.5d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggf97a.txt", 42.33d, 51.14d, -4.87d, 8.23d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10001, 4937, 5720, 0.5d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggf97a.txt", 42.33d, 51.14d, -4.87d, 8.23d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10002, 4965, 5721, 0.5d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggf97a_corse.txt", 41.31d, 43.07d, 8.5d, 9.63d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10003, 4937, 5721, 0.5d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggf97a_corse.txt", 41.31d, 43.07d, 8.5d, 9.63d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10011, 4967, 5755, 998.0d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggguy00.txt", 2.11d, 5.81d, -54.61d, -51.61d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10012, 4971, 5758, 0.1d, "Geographic3D to GravityRelatedHeight (IGN1997)", "ggr99.txt", -21.42d, -20.81d, 55.16d, 55.91d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10021, 4937, 5701, 0.02d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 49.93d, 58.71d, -7.06d, 1.8d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10023, 4937, 5750, 0.02d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 54.02d, 54.44d, -4.87d, -4.27d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10024, 4937, 5741, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 59.45d, 59.6d, -1.76d, -1.5d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10025, 4937, 5748, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 58.21d, 58.35d, -7.75d, -7.46d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10026, 4937, 5743, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 60.06d, 60.2d, -2.21d, -1.95d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10027, 4937, 5742, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 59.83d, 60.87d, -1.78d, -0.67d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10029, 4937, 5740, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 58.72d, 59.41d, -3.48d, -2.34d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10030, 4937, 5745, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 59.07d, 59.19d, -5.92d, -5.73d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10031, 4937, 5747, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 57.74d, 57.93d, -8.74d, -8.41d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10032, 4937, 5749, 0.0d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 49.86d, 49.99d, -6.41d, -6.23d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10033, 4937, 5746, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 56.76d, 58.54d, -7.72d, -6.1d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10034, 4937, 5744, 0.05d, "Geographic3D to GravityRelatedHeight (OSGM-GB)", "OSTN02_OSGM02_GB.txt", 59.05d, 59.13d, -4.5d, -4.3d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10035, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SC52_DAT.htm", -12.0d, -11.07d, 131.02d, 132.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10036, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SC53_DAT.htm", -12.0d, -10.92d, 132.0d, 136.83d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10037, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SC54_DAT.htm", -12.0d, -10.65d, 141.77d, 143.31d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10038, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SD51_DAT.htm", -16.0d, -13.87d, 124.17d, 126.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10039, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SD52_DAT.htm", -16.0d, -12.0d, 126.0d, 132.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10040, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SD53_DAT.htm", -16.0d, -12.0d, 132.0d, 137.28d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10041, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SD54_DAT.htm", -16.0d, -12.0d, 141.34d, 144.01d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10042, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SD55_DAT.htm", -16.0d, -14.01d, 144.0d, 145.49d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10043, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SE50_DAT.htm", -20.0d, -19.88d, 118.94d, 120.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10044, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SE51_DAT.htm", -20.0d, -16.0d, 120.0d, 126.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10045, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SE52_DAT.htm", -20.0d, -16.0d, 126.0d, 132.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10046, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SE53_DAT.htm", -20.0d, -16.0d, 132.0d, 138.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10047, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SE54_DAT.htm", -20.0d, -16.0d, 138.0d, 144.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10048, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SE55_DAT.htm", -20.0d, -16.0d, 144.0d, 148.97d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10049, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF49_DAT.htm", -24.0d, -21.8d, 113.39d, 114.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10050, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF50_DAT.htm", -24.0d, -20.0d, 114.0d, 120.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10051, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF51_DAT.htm", -24.0d, -20.0d, 120.0d, 126.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10052, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF52_DAT.htm", -24.0d, -20.0d, 126.0d, 132.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10053, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF53_DAT.htm", -24.0d, -20.0d, 132.0d, 138.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10054, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF54_DAT.htm", -24.0d, -20.0d, 138.0d, 144.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10055, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF55_DAT.htm", -24.0d, -20.0d, 144.0d, 150.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10056, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SF56_DAT.htm", -24.0d, -22.0d, 150.0d, 151.82d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10057, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG49_DAT.htm", -27.44d, -24.0d, 112.85d, 114.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10058, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG50_DAT.htm", -28.0d, -24.0d, 114.0d, 120.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10059, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG51_DAT.htm", -28.0d, -24.0d, 120.0d, 126.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10060, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG52_DAT.htm", -28.0d, -24.0d, 126.0d, 132.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10061, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG53_DAT.htm", -28.0d, -24.0d, 132.0d, 138.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10062, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG54_DAT.htm", -28.0d, -24.0d, 138.0d, 144.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10063, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG55_DAT.htm", -28.0d, -24.0d, 144.0d, 150.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10064, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SG56_DAT.htm", -28.0d, -24.0d, 150.0d, 153.6d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10066, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SH50_DAT.htm", -32.0d, -28.0d, 114.07d, 120.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10067, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SH51_DAT.htm", -32.0d, -28.0d, 120.0d, 126.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10068, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SH52_DAT.htm", -32.37d, -28.0d, 126.0d, 132.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10069, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SH53_DAT.htm", -32.0d, -28.0d, 132.0d, 138.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10070, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SH54_DAT.htm", -32.0d, -28.0d, 138.0d, 144.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10071, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SH55_DAT.htm", -32.0d, -28.0d, 144.0d, 150.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10072, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SH56_DAT.htm", -32.0d, -28.0d, 150.0d, 153.69d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10073, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SI50_DAT.htm", -35.19d, -32.0d, 114.89d, 120.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10074, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SI51_DAT.htm", -34.16d, -32.0d, 120.0d, 126.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10075, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SI53_DAT.htm", -36.0d, -32.0d, 132.08d, 138.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10076, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SI54_DAT.htm", -36.0d, -32.0d, 138.0d, 144.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10077, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SI55_DAT.htm", -36.0d, -32.0d, 144.0d, 150.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10078, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SI56_DAT.htm", -36.0d, -32.0d, 150.0d, 152.66d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10079, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SJ53_DAT.htm", -36.14d, -36.0d, 136.57d, 137.68d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10080, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SJ54_DAT.htm", -38.91d, -36.0d, 139.38d, 144.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10081, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SJ55_DAT.htm", -39.2d, -36.0d, 144.0d, 150.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10082, 4939, 5711, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SJ56_DAT.htm", -37.57d, -36.0d, 150.0d, 150.22d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10083, 4939, 5712, 0.4d, "Geographic3D to GravityRelatedHeight (AUSGeoid98)", "SK55_DAT.htm", -43.7d, -40.24d, 144.55d, 148.44d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10084, 4979, 5773, 1.0d, "Geographic3D to GravityRelatedHeight (EGM)", "WW15MGH.GRD", -90.0d, 90.0d, -180.0d, 180.0d, 8254, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10085, 4302, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", 9.83d, 11.51d, -62.09d, -60.0d, 8254, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10086, 4242, 4322, 15.0d, "Geocentric translations (geog2D domain)", "", 17.64d, 18.58d, -78.43d, -76.17d, 8257, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10087, 24100, 24200, 1.5d, "Lambert Conic Conformal (1SP)", "", 17.64d, 18.58d, -78.43d, -76.17d, 8260, 16), + new EpsgOperationRecord((EpsgOperationType)0, 10089, 4208, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -28.41d, -22.66d, -48.8d, -40.2d, 8276, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10090, 4208, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -25.91d, -20.45d, -42.04d, -37.11d, 8279, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10091, 4208, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -22.04d, -17.59d, -40.37d, -35.18d, 8282, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10092, 4208, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -35.71d, -28.11d, -53.38d, -44.71d, 8285, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10093, 4208, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", -34.0d, -18.0d, -53.38d, -35.19d, 8288, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10098, 4123, 10690, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 59.75d, 70.09d, 19.24d, 31.59d, 8291, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10099, 4123, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 59.75d, 70.09d, 19.24d, 31.59d, 8298, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10100, 4913, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8305, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10103, 4912, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8320, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10104, 4911, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8335, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10105, 4910, 9988, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8350, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10106, 10874, 20000, 1.0d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "arcgp-2006-sk.bin", 76.16d, 81.17d, -3.35d, 38.01d, 8365, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10107, 10874, 20001, 1.0d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "arcgp-2006-sk.bin", 76.16d, 81.17d, -3.35d, 38.01d, 8365, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10108, 11009, 20033, 0.0d, "NTv2", "TN15-ETRS89-to-MWC18-IRF.gsb", 53.09d, 53.65d, -3.15d, -2.1d, 8366, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10109, 8254, 9245, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 8366, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10110, 8244, 20034, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 8366, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10111, 8239, 20035, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 8366, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10112, 8235, 20035, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 8366, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10128, 8244, 20037, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 8366, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10129, 8239, 20038, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 8367, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10130, 10874, 9672, 0.5d, "Geographic3D to Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2021b.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8368, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10133, 10874, 9883, 0.5d, "Geog3D to Geog2D+Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2021b.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8368, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10134, 20039, 7789, 0.1d, "Time-specific Position Vector transform (geocen)", "", -59.87d, -17.5d, -113.21d, -65.72d, 8369, 8), + new EpsgOperationRecord((EpsgOperationType)0, 10135, 4248, 20041, 5.0d, "Geocentric translations (geog2D domain)", "", -26.0d, -17.5d, -70.79d, -67.0d, 8377, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10136, 4248, 20041, 5.0d, "Geocentric translations (geog2D domain)", "", -36.0d, -26.0d, -72.87d, -68.28d, 8380, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10137, 4248, 20041, 5.0d, "Geocentric translations (geog2D domain)", "", -43.5d, -35.99d, -74.48d, -70.39d, 8383, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10138, 4618, 20041, 5.0d, "Geocentric translations (geog2D domain)", "", -55.96d, -51.99d, -74.83d, -66.33d, 8386, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10139, 4910, 4911, 0.01d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8389, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10140, 4911, 4912, 0.01d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8396, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10141, 4912, 4913, 0.007d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8403, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10142, 4913, 4914, 0.005d, "Position Vector transformation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8410, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10143, 4915, 4916, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8417, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10144, 9469, 20036, 0.0d, "Geographic3D to GravityRelatedHeight (gtx)", "INAGEOID2020v2.gtx", -13.95d, 7.79d, 92.01d, 141.46d, 8432, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10145, 9469, 20043, 0.0d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "INAGEOID2020v2.gtx", -13.95d, 7.79d, 92.01d, 141.46d, 8432, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10149, 20046, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -4.23d, 15.51d, -84.77d, -66.87d, 8433, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10152, 4937, 10156, 0.3d, "Geog3D to Geog2D+Depth (txt)", "VORF-UK08_ETRF_to_MSL.vrf", 47.42d, 63.89d, -16.1d, 3.4d, 8436, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10153, 4937, 10157, 0.3d, "Geog3D to Geog2D+Depth (txt)", "VORF-UK08_ETRF_to_CD.vrf", 47.42d, 63.89d, -16.1d, 3.4d, 8437, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10154, 4937, 10150, 0.3d, "Geographic3D to Depth (txt)", "VORF-UK08_ETRF_to_MSL.vrf", 47.42d, 63.89d, -16.1d, 3.4d, 8438, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10155, 4937, 10151, 0.3d, "Geographic3D to Depth (txt)", "VORF-UK08_ETRF_to_CD.vrf", 47.42d, 63.89d, -16.1d, 3.4d, 8438, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10161, 4258, 10158, 0.03d, "NTv2", "s34j_2022.gsb", 54.67d, 57.8d, 8.0d, 11.29d, 8438, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10179, 9988, 10176, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8438, 8), + new EpsgOperationRecord((EpsgOperationType)0, 10180, 9378, 10176, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 8446, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10181, 11009, 10175, 0.0d, "NTv2", "TN15-ETRS89-to-DoPw22-IRF.gsb", 52.45d, 53.01d, -4.51d, -3.8d, 8461, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10186, 11009, 10185, 0.0d, "NTv2", "TN15-ETRS89-to-ShAb07-IRF.gsb", 52.37d, 52.77d, -4.16d, -2.6d, 8461, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10192, 11009, 10191, 0.0d, "NTv2", "TN15-ETRS89-to-CNH22-IRF.gsb", 53.02d, 53.46d, -4.71d, -2.28d, 8461, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10197, 11009, 10196, 0.0d, "NTv2", "TN15-ETRS89-to-CWS13-IRF.gsb", 52.5d, 53.26d, -3.16d, -2.65d, 8461, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10205, 11009, 10204, 0.0d, "NTv2", "TN15-ETRS89-to-DIBA15-IRF.gsb", 51.57d, 52.11d, -1.46d, -1.15d, 8461, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10210, 11009, 10209, 0.0d, "NTv2", "TN15-ETRS89-to-GWPBS22-IRF.gsb", 51.25d, 52.06d, -4.26d, -0.1d, 8461, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10215, 11009, 10214, 0.0d, "NTv2", "TN15-ETRS89-to-GWWAB22-IRF.gsb", 51.35d, 51.81d, -3.6d, -3.12d, 8461, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10216, 3114, 11114, 0.3d, "Transverse Mercator", "", 1.23d, 2.48d, -79.1d, -78.58d, 8461, 12), + new EpsgOperationRecord((EpsgOperationType)0, 10220, 11009, 10219, 0.0d, "NTv2", "TN15-ETRS89-to-GWWWA22-IRF.gsb", 51.55d, 52.06d, -5.16d, -3.6d, 8473, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10221, 3115, 11115, 0.3d, "Transverse Mercator", "", 0.03d, 10.21d, -78.59d, -75.58d, 8473, 12), + new EpsgOperationRecord((EpsgOperationType)0, 10225, 11009, 10224, 0.0d, "NTv2", "TN15-ETRS89-to-MALS09-IRF.gsb", 51.45d, 52.91d, -2.26d, -0.05d, 8485, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10230, 11009, 10229, 0.0d, "NTv2", "TN15-ETRS89-to-OxWo08-IRF.gsb", 51.65d, 52.26d, -2.31d, -1.15d, 8485, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10238, 11009, 10237, 0.0d, "NTv2", "TN15-ETRS89-to-SYC20-IRF.gsb", 52.65d, 53.16d, -2.91d, -2.3d, 8485, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10242, 3116, 11116, 0.3d, "Transverse Mercator", "", -2.51d, 11.82d, -75.59d, -72.58d, 8485, 12), + new EpsgOperationRecord((EpsgOperationType)0, 10243, 3117, 11117, 0.3d, "Transverse Mercator", "", -4.23d, 12.52d, -72.59d, -69.58d, 8497, 12), + new EpsgOperationRecord((EpsgOperationType)0, 10244, 3118, 11118, 0.3d, "Transverse Mercator", "", -2.25d, 6.31d, -69.59d, -66.87d, 8509, 12), + new EpsgOperationRecord((EpsgOperationType)0, 10247, 4883, 8690, 0.1d, "Geographic3D to GravityRelatedHeight (ISG)", "https://isgeoid.polimi.it/Geoid/Europe/Slovenia/public/Slovenia_2016_SLO_VRP2016_Koper_hybrQ_20221122.isg", 45.42d, 46.88d, 13.38d, 16.61d, 8521, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10248, 4883, 10245, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (ISG)", "https://isgeoid.polimi.it/Geoid/Europe/Slovenia/public/Slovenia_2016_SLO_VRP2016_Koper_hybrQ_20221122.isg", 45.42d, 46.88d, 13.38d, 16.61d, 8521, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10251, 4258, 10249, 0.03d, "NTv2", "s34s_2022.gsb", 54.51d, 56.79d, 10.79d, 12.87d, 8522, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10255, 4258, 10252, 0.03d, "NTv2", "s45b_2022.gsb", 54.94d, 55.38d, 14.59d, 15.25d, 8522, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10259, 4258, 10256, 0.5d, "NTv2", "gs_2022.gsb", 54.51d, 57.8d, 8.0d, 12.87d, 8522, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10263, 4258, 10260, 0.5d, "NTv2", "gsb_2022.gsb", 54.94d, 55.38d, 14.59d, 15.25d, 8522, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10264, 10299, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 18.97d, 38.8d, -8.67d, 11.99d, 8522, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10267, 4258, 10265, 0.5d, "NTv2", "kk_2022.gsb", 55.51d, 55.82d, 12.23d, 12.73d, 8525, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10271, 4258, 10268, 1.0d, "NTv2", "os_2022.gsb", 54.8d, 55.47d, 8.37d, 10.16d, 8525, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10273, 11009, 10272, 0.0d, "NTv2", "TN15-ETRS89-to-SMITB20-IRF.gsb", 50.65d, 50.86d, -4.11d, -3.6d, 8525, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10278, 11009, 10277, 0.0d, "NTv2", "TN15-ETRS89-to-RBEPP12-IRF.gsb", 50.05d, 51.71d, -5.63d, -0.85d, 8525, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10292, 10282, 7930, 0.1d, "Position Vector transformation (geocentric domain)", "", 47.27d, 55.92d, 3.34d, 15.04d, 8525, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10294, 10283, 7837, 0.02d, "Geographic3D to GravityRelatedHeight (txt)", "GCG2016.txt", 47.27d, 55.92d, 3.34d, 15.04d, 8532, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10295, 10283, 10293, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (txt)", "GCG2016.txt", 47.27d, 55.09d, 5.86d, 15.04d, 8532, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10296, 4307, 4326, 1.0d, "Position Vector transformation (geog2D domain)", "", 18.97d, 38.8d, -8.67d, 11.99d, 8533, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10319, 10311, 9351, 0.03d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RANC15.tac", -22.73d, -19.5d, 163.54d, 168.19d, 8540, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10320, 10311, 10318, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RANC15.tac", -22.73d, -19.5d, 163.54d, 168.19d, 8540, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10321, 10307, 10312, 1.0d, "Geocentric translations (geog2D domain)", "", -22.73d, -19.5d, 163.54d, 168.19d, 8541, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10322, 10307, 10312, 0.03d, "Geocentric translations (geog2D domain) by grid (IGN)", "gr3dncl08.tac", -22.73d, -19.5d, 163.54d, 168.19d, 8544, 2), + new EpsgOperationRecord((EpsgOperationType)0, 10324, 10310, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -26.45d, -14.83d, 156.25d, 174.28d, 8546, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10333, 10328, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 42.56d, 45.27d, 15.74d, 19.62d, 8549, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10334, 9988, 6317, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 14.92d, 74.71d, 167.65d, -63.88d, 8552, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10335, 7789, 6320, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", -17.56d, 31.8d, 157.47d, -151.27d, 8567, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10336, 9988, 6320, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", -17.56d, 31.8d, 157.47d, -151.27d, 8582, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10337, 7789, 6323, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 1.64d, 23.9d, 129.48d, 149.55d, 8597, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10338, 9988, 6323, 0.0d, "Time-dependent Coordinate Frame rotation (geocen)", "", 1.64d, 23.9d, 129.48d, 149.55d, 8612, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10339, 4307, 10299, 5.0d, "Position Vector transformation (geog2D domain)", "", 25.0d, 32.0d, 1.0d, 3.3d, 8627, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10340, 4307, 10299, 100.0d, "Geocentric translations (geog2D domain)", "", 27.5d, 28.3d, 8.83d, 9.92d, 8634, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10341, 4307, 10299, 100.0d, "Geocentric translations (geog2D domain)", "", 31.75d, 32.42d, 7.16d, 8.0d, 8637, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10342, 4307, 10299, 5.0d, "Geocentric translations (geog2D domain)", "", 29.25d, 31.0d, 0.0d, 1.25d, 8640, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10343, 4307, 10299, 5.0d, "Geocentric translations (geog2D domain)", "", 26.06d, 27.51d, 1.24d, 2.92d, 8643, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10344, 4307, 10299, 100.0d, "Geocentric translations (geog2D domain)", "", 27.4d, 28.1d, 7.66d, 8.27d, 8646, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10347, 9701, 9651, 0.02d, "Geographic3D to GravityRelatedHeight (PL txt)", "Model_quasi-geoidy-PL-geoid2021-PL-EVRF2007-NH.txt", 49.0d, 54.89d, 14.14d, 24.15d, 8649, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10348, 9701, 9657, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (PL txt)", "Model_quasi-geoidy-PL-geoid2021-PL-EVRF2007-NH.txt", 49.0d, 54.89d, 14.14d, 24.15d, 8649, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10350, 11036, 9287, 0.1d, "Geographic3D to Depth (gtx)", "nllat2018.gtx", 51.32d, 55.77d, 2.53d, 7.21d, 8650, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10351, 11036, 9289, 0.1d, "Geog3D to Geog2D+Depth (gtx)", "nllat2018.gtx", 51.32d, 55.77d, 2.53d, 7.21d, 8650, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10358, 11130, 10352, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 38.59d, 38.86d, 1.31d, 1.65d, 8651, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10359, 11130, 10355, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 38.59d, 38.86d, 1.31d, 1.65d, 8651, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10360, 11130, 10353, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.88d, 36.0d, -3.1d, -2.96d, 8652, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10361, 11130, 10356, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.88d, 36.0d, -3.1d, -2.96d, 8652, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10362, 11130, 10354, 0.05d, "Geographic3D to GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.26d, 35.38d, -2.98d, -2.88d, 8653, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10363, 11130, 10357, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "EGM08_REDNAP.txt", 35.26d, 35.38d, -2.98d, -2.88d, 8653, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10366, 4927, 5193, 0.035d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "KNGeoid14.gri", 33.14d, 38.64d, 124.53d, 131.01d, 8654, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10367, 4927, 10365, 0.035d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "KNGeoid14.gri", 33.14d, 38.64d, 124.53d, 131.01d, 8654, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10368, 4927, 5193, 0.024d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "KNGeoid18.gri", 33.14d, 38.64d, 124.53d, 131.01d, 8655, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10369, 4927, 10365, 0.024d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "KNGeoid18.gri", 33.14d, 38.64d, 124.53d, 131.01d, 8655, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10380, 10364, 10349, 0.0d, "Height Depth Reversal", "", 36.9d, 41.88d, -9.57d, -7.39d, 8656, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10381, 10364, 10349, 0.0d, "Height Depth Reversal", "", 38.58d, 38.96d, -9.46d, -8.92d, 8657, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10382, 10370, 10349, 0.0d, "Height Depth Reversal", "", 32.35d, 32.93d, -17.33d, -16.4d, 8658, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10383, 10371, 10349, 0.0d, "Height Depth Reversal", "", 32.97d, 33.16d, -16.46d, -16.23d, 8659, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10384, 10372, 10349, 0.0d, "Height Depth Reversal", "", 39.32d, 39.78d, -31.34d, -31.01d, 8660, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10385, 10373, 10349, 0.0d, "Height Depth Reversal", "", 38.46d, 38.7d, -28.91d, -28.53d, 8661, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10386, 10374, 10349, 0.0d, "Height Depth Reversal", "", 38.33d, 38.62d, -28.61d, -27.96d, 8662, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10387, 10375, 10349, 0.0d, "Height Depth Reversal", "", 38.48d, 38.81d, -28.39d, -27.68d, 8663, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10388, 10376, 10349, 0.0d, "Height Depth Reversal", "", 38.95d, 39.15d, -28.14d, -27.88d, 8664, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10389, 10377, 10349, 0.0d, "Height Depth Reversal", "", 38.58d, 38.86d, -27.45d, -26.97d, 8665, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10390, 10378, 10349, 0.0d, "Height Depth Reversal", "", 37.65d, 37.97d, -25.92d, -25.07d, 8666, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10391, 10379, 10349, 0.0d, "Height Depth Reversal", "", 36.87d, 37.33d, -25.25d, -24.71d, 8667, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10415, 9988, 10412, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 38.21d, 86.46d, -141.01d, -40.73d, 8668, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10416, 8253, 6317, 0.0d, "Position Vector transformation (geocentric domain)", "", 14.92d, 86.46d, 167.65d, -47.74d, 8683, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10417, 10413, 9245, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "CGG2013an83.byn", 38.21d, 86.46d, -141.01d, -40.73d, 8690, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10418, 10413, 5713, 0.05d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2010v70.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8690, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10419, 10412, 6317, 0.0d, "Position Vector transformation (geocentric domain)", "", 14.92d, 86.46d, 167.65d, -47.74d, 8690, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10466, 11036, 9288, 0.3d, "Geographic3D to Depth (gtx)", "nlgeo2018.gtx", 51.32d, 55.77d, 2.53d, 7.21d, 8697, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10467, 11036, 9290, 0.3d, "Geog3D to Geog2D+Depth (gtx)", "nlgeo2018.gtx", 51.32d, 55.77d, 2.53d, 7.21d, 8697, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10469, 11009, 10468, 0.0d, "NTv2", "TN15-ETRS89-to-COV23-IRF.gsb", 52.3d, 52.5d, -1.85d, -1.3d, 8698, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10478, 10475, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 46.45d, 47.33d, 11.04d, 11.91d, 8698, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10489, 4937, 10483, 0.06d, "Geographic3D to GravityRelatedHeight (gtg)", "dvr90_2002.tif", 54.5d, 57.81d, 7.98d, 15.28d, 8701, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10490, 4937, 10486, 0.06d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "dvr90_2002.tif", 54.5d, 57.81d, 7.98d, 15.28d, 8701, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10491, 4937, 10484, 0.03d, "Geographic3D to GravityRelatedHeight (gtg)", "dvr90_2013.tif", 54.5d, 57.81d, 7.98d, 15.28d, 8702, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10492, 4937, 10487, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "dvr90_2013.tif", 54.5d, 57.81d, 7.98d, 15.28d, 8702, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10493, 4937, 10485, 0.01d, "Geographic3D to GravityRelatedHeight (gtg)", "dvr90_2023.tif", 54.5d, 57.81d, 7.98d, 15.28d, 8703, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10494, 4937, 10488, 0.01d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "dvr90_2023.tif", 54.5d, 57.81d, 7.98d, 15.28d, 8703, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10504, 10874, 9672, 0.5d, "Geographic3D to Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2023a.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8704, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10505, 10874, 9883, 0.5d, "Geog3D to Geog2D+Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2023a.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8704, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10506, 9781, 5721, 0.02d, "Geographic3D to GravityRelatedHeight (IGN2009)", "RAC23.mnt", 41.31d, 43.07d, 8.5d, 9.63d, 8705, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10508, 9781, 10507, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "RAC23.mnt", 41.31d, 43.07d, 8.5d, 9.63d, 8705, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10509, 10874, 9672, 0.5d, "Geographic3D to Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2023b.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8706, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10510, 10874, 9883, 0.5d, "Geog3D to Geog2D+Depth (Gravsoft)", "ChartDatum_above_Ellipsoid_EUREF89_v2023b.bin", 57.75d, 71.39d, 4.08d, 31.77d, 8706, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10511, 8233, 8238, 0.01d, "Position Vector transformation (geocentric domain)", "", 38.21d, 86.46d, -141.01d, -40.73d, 8707, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10512, 8250, 8253, 0.01d, "Position Vector transformation (geocentric domain)", "", 38.21d, 86.46d, -141.01d, -40.73d, 8714, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10513, 8250, 10412, 0.01d, "Position Vector transformation (geocentric domain)", "", 38.21d, 86.46d, -141.01d, -40.73d, 8721, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10514, 8253, 10412, 0.01d, "Position Vector transformation (geocentric domain)", "", 38.21d, 86.46d, -141.01d, -40.73d, 8728, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10517, 10516, 8162, 0.1d, "Transverse Mercator", "", 44.07d, 44.6d, -91.17d, -90.31d, 8735, 12), + new EpsgOperationRecord((EpsgOperationType)0, 10518, 5713, 20035, 0.05d, "Vertical change by geoid grid difference (NRCan)", "HT2_1997_CGG2013a.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8747, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10519, 5713, 20034, 0.05d, "Vertical change by geoid grid difference (NRCan)", "HT2_2002v70_CGG2013a.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8748, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10520, 5713, 9245, 0.05d, "Vertical change by geoid grid difference (NRCan)", "HT2_2010v70_CGG2013a.byn", 41.67d, 69.81d, -141.01d, -52.54d, 8749, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10521, 8235, 8244, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v6VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8750, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10522, 8235, 8244, 0.015d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8751, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10523, 8235, 8251, 0.03d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v6VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8752, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10524, 8235, 8251, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8753, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10525, 8235, 8254, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8754, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10526, 8235, 10413, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8755, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10527, 8239, 8244, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v6VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8756, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10528, 8239, 8244, 0.015d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8757, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10529, 8239, 8251, 0.03d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v6VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8758, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10530, 8239, 8251, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8759, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10534, 8239, 8254, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8760, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10535, 8239, 10413, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8761, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10536, 8244, 8251, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v6VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8762, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10537, 8244, 8251, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8763, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10538, 8244, 8254, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8764, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10539, 8244, 10413, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8765, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10540, 20035, 20034, 0.05d, "Vertical Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8766, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10541, 20035, 9245, 0.05d, "Vertical Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8767, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10542, 20034, 9245, 0.05d, "Vertical Offset using NEU velocity grid (NTv2_Vel)", "NAD83v70VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 8768, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10543, 4742, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 0.85d, 7.81d, 98.02d, 119.61d, 8769, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10544, 11107, 5780, 0.04d, "Geographic3D to GravityRelatedHeight (txt)", "GeodPT08.dat", 36.95d, 42.16d, -9.56d, -6.19d, 8772, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10546, 11107, 10545, 0.04d, "Geog3D to Geog2D+GravityRelatedHeight (txt)", "GeodPT08.dat", 36.95d, 42.16d, -9.56d, -6.19d, 8772, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10557, 4937, 10547, 0.5d, "Geographic3D to Depth (gtg)", "dkmsl_2022.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8773, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10558, 4937, 10553, 0.5d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "dkmsl_2022.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8773, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10559, 4937, 10548, 0.5d, "Geographic3D to Depth (gtg)", "dklat_2022.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8774, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10560, 4937, 10554, 0.5d, "Geog3D to Geog2D+Depth (gtg)", "dklat_2022.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8774, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10561, 4937, 10549, 0.1d, "Geographic3D to Depth (gtg)", "dkmsl_2023.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8775, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10562, 4937, 10555, 0.1d, "Geog3D to Geog2D+Depth (gtg)", "dkmsl_2023.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8775, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10563, 4937, 10550, 0.1d, "Geographic3D to Depth (gtg)", "dklat_2023.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8776, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10564, 4937, 10556, 0.1d, "Geog3D to Geog2D+Depth (gtg)", "dklat_2023.tif", 54.36d, 58.27d, 3.25d, 16.51d, 8776, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10566, 10565, 8267, 0.02d, "Vertical Offset by Grid Interpolation (gtg)", "gllmsl_2022.tif", 59.74d, 83.67d, -73.29d, -11.81d, 8777, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10567, 11069, 8357, 0.03d, "Geographic3D to GravityRelatedHeight (gtx)", "CR2005_GTX.gtx", 48.58d, 51.06d, 12.09d, 18.86d, 8778, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10568, 11069, 11311, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "CR2005_GTX.gtx", 48.58d, 51.06d, 12.09d, 18.86d, 8778, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10572, 9988, 10569, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8779, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10573, 9988, 10569, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8794, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10574, 7789, 10569, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8809, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10575, 5332, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8824, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10576, 4896, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8839, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10577, 4919, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8854, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10578, 4918, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8869, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10579, 4917, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8884, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10580, 4916, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8899, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10581, 4915, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8914, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10582, 4914, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8929, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10583, 4913, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8944, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10584, 4912, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8959, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10585, 4911, 10569, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8974, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10586, 9988, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 8989, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10587, 9988, 8401, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 9004, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10589, 9000, 4613, 0.2d, "Coordinate Frame rotation (geog2D domain)", "", -1.24d, 0.0d, 116.72d, 117.99d, 9019, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10590, 8235, 10588, 0.01d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_1997.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9026, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10607, 9753, 10604, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 9026, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10608, 10604, 9988, 0.01d, "Coordinate Frame rotation (geocentric domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 9033, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10609, 8239, 10588, 0.0d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_1997.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9040, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10610, 8244, 10588, 0.02d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2002v70.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9040, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10612, 8254, 10588, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2010v70.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9040, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10613, 10413, 10588, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2010v70.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9040, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10614, 7885, 9517, 0.0d, "Geog3D to Geog2D+GravityRelatedHeight (EGM2008)", "Und_min2.5x2.5_egm2008_isw=82_WGS84_TideFree.gz", -16.08d, -15.85d, -5.85d, -5.59d, 9040, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10615, 8237, 8246, 0.1d, "NTv2", "BC_98_05.GSB", 48.99d, 60.01d, -139.04d, -114.08d, 9041, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10617, 10588, 20035, 0.03d, "Vertical change by geoid grid difference (NRCan)", "HT2_1997_CGG2013a.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9041, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10618, 10588, 20034, 0.02d, "Vertical change by geoid grid difference (NRCan)", "HT2_2002v70_CGG2013a.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9042, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10619, 10588, 9245, 0.0d, "Vertical change by geoid grid difference (NRCan)", "HT2_2010v70_CGG2013a.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9043, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10620, 5713, 10588, 0.05d, "Vertical Offset", "", 41.67d, 69.81d, -141.01d, -52.54d, 9044, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10624, 11009, 10623, 0.0d, "NTv2", "TN15-ETRS89-to-ECML14-IRF.gsb", 51.45d, 56.1d, -3.45d, 0.05d, 9045, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10629, 11009, 10628, 0.0d, "NTv2", "TN15-ETRS89-to-WC05-IRF.gsb", 51.4d, 55.92d, -4.33d, -0.04d, 9045, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10646, 10635, 10638, 0.05d, "Coordinate Frame rotation full matrix (geog3D)", "", 17.56d, 17.71d, -63.31d, -63.16d, 9045, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10647, 10637, 7789, 0.05d, "Time-dependent Coordinate Frame rotation (geocen)", "", 17.56d, 17.71d, -63.31d, -63.16d, 9052, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10648, 10639, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 17.56d, 17.71d, -63.31d, -63.16d, 9067, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10653, 4909, 10649, 0.05d, "Geographic3D to Depth (gtg)", "glmsl_2023.tif", 56.38d, 87.03d, -75.0d, 7.99d, 9070, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10654, 4909, 10651, 0.05d, "Geog3D to Geog2D+Depth (gtg)", "glmsl_2023.tif", 56.38d, 87.03d, -75.0d, 7.99d, 9070, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10655, 4909, 10650, 0.1d, "Geographic3D to Depth (gtg)", "gllat_2023.tif", 56.38d, 87.03d, -75.0d, 7.99d, 9071, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10656, 4909, 10652, 0.1d, "Geog3D to Geog2D+Depth (gtg)", "gllat_2023.tif", 56.38d, 87.03d, -75.0d, 7.99d, 9071, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10657, 10635, 10642, 0.2d, "Geographic3D to GravityRelatedHeight", "", 17.56d, 17.71d, -63.31d, -63.16d, 9072, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10658, 10635, 10643, 0.2d, "Geog3D to Geog2D+GravityRelatedHeight", "", 17.56d, 17.71d, -63.31d, -63.16d, 9073, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10661, 11162, 5787, 0.05d, "Geographic3D to GravityRelatedHeight (gtg)", "hu_sgo_vitel2014.tif", 45.74d, 48.58d, 16.11d, 22.9d, 9074, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10662, 11162, 10659, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "hu_sgo_vitel2014.tif", 45.74d, 48.58d, 16.11d, 22.9d, 9074, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10663, 4237, 11163, 0.01d, "NTv2", "hu_sgo_hd72corr.gsb", 45.74d, 48.58d, 16.11d, 22.9d, 9075, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10666, 11162, 5787, 0.06d, "Geographic3D to GravityRelatedHeight (gtg)", "hu_bme_geoid2014.tif", 45.74d, 48.58d, 16.11d, 22.9d, 9075, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10667, 11162, 10659, 0.06d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "hu_bme_geoid2014.tif", 45.74d, 48.58d, 16.11d, 22.9d, 9075, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10668, 4237, 11163, 0.015d, "NTv2", "hu_bme_hd72corr.gsb", 45.74d, 48.58d, 16.11d, 22.9d, 9076, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10676, 10636, 4326, 1.0d, "Coordinate Frame rotation full matrix (geog2D)", "", 17.56d, 17.71d, -63.31d, -63.16d, 9076, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10677, 4746, 4258, 0.3d, "NTv2", "de_tlbg_thuringen_NTv2gridTH.gsb", 50.2d, 51.65d, 9.87d, 12.66d, 9083, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10680, 4937, 10678, 0.05d, "Geographic3D to Depth (txt)", "BSCD2000.txt", 53.88d, 65.92d, 8.5d, 30.23d, 9083, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10681, 4937, 10679, 0.05d, "Geog3D to Geog2D+Depth (txt)", "BSCD2000.txt", 53.88d, 65.92d, 8.5d, 30.23d, 9083, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10682, 4470, 10671, 0.1d, "Geocentric translations (geog2D domain)", "", -14.49d, -11.33d, 43.68d, 46.7d, 9084, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10683, 4470, 10671, 0.05d, "Geocentric translations (geog2D domain) by grid (IGN)", "RGM04versRGM23.txt", -14.49d, -11.33d, 43.68d, 46.7d, 9087, 2), + new EpsgOperationRecord((EpsgOperationType)0, 10684, 10671, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -14.49d, -11.33d, 43.68d, 46.7d, 9089, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10685, 5779, 8690, 0.02d, "Vertical Offset by Grid Interpolation (asc)", "SLO-VTP2024.xyz", 45.42d, 46.88d, 13.38d, 16.61d, 9092, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10693, 10689, 5717, 0.03d, "Geographic3D to GravityRelatedHeight (gtg)", "fi_nls_fin2000.tif", 59.75d, 70.09d, 19.24d, 31.59d, 9093, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10694, 10689, 10691, 0.03d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "fi_nls_fin2000.tif", 59.75d, 70.09d, 19.24d, 31.59d, 9093, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10695, 10689, 3900, 0.02d, "Geographic3D to GravityRelatedHeight (gtg)", "fi_nls_fin2005n00.tif", 59.75d, 70.09d, 19.24d, 31.59d, 9094, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10696, 10689, 10692, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "fi_nls_fin2005n00.tif", 59.75d, 70.09d, 19.24d, 31.59d, 9094, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10697, 10689, 3900, 0.014d, "Geographic3D to GravityRelatedHeight (gtg)", "fi_nls_fin2023n2000.tif", 59.75d, 70.09d, 19.24d, 31.59d, 9095, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10698, 10689, 10692, 0.014d, "Geog3D to Geog2D+GravityRelatedHeight (gtg)", "fi_nls_fin2023n2000.tif", 59.75d, 70.09d, 19.24d, 31.59d, 9095, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10701, 10690, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 58.84d, 70.09d, 19.08d, 31.59d, 9096, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10703, 2393, 3067, 0.03d, "Transverse Mercator", "fi_nls_ykj_etrs35fin.json", 59.75d, 70.09d, 19.24d, 31.59d, 9099, 10), + new EpsgOperationRecord((EpsgOperationType)0, 10704, 8675, 5717, 999.0d, "Vertical Offset by TIN Interpolation (JSON)", "fi_nls_n43_n60.json", 59.75d, 66.73d, 20.95d, 31.59d, 9109, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10705, 5717, 3900, 0.005d, "Vertical Offset by TIN Interpolation (JSON)", "fi_nls_n60_n2000.json", 59.75d, 70.09d, 19.24d, 31.59d, 9110, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10708, 8235, 8244, 0.015d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9111, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10709, 8235, 8251, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9112, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10710, 8235, 8254, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9113, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10711, 8235, 10413, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9114, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10712, 8239, 8244, 0.015d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9115, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10713, 8239, 8251, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9116, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10714, 8239, 8254, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9117, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10715, 8239, 10413, 0.025d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9118, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10716, 8244, 8251, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9119, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10717, 8244, 8254, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9120, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10718, 8244, 10413, 0.02d, "Geographic3D Offset using NEU velocity grid (NTv2_Vel)", "NAD83v80VG.gvb", 41.67d, 83.17d, -141.01d, -52.54d, 9121, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10748, 10737, 7789, 0.05d, "Time-dependent Coordinate Frame rotation (geocen)", "", 17.41d, 17.58d, -63.05d, -62.88d, 9122, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10749, 10739, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 17.41d, 17.58d, -63.05d, -62.88d, 9137, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10750, 10735, 10738, 0.05d, "Coordinate Frame rotation full matrix (geog3D)", "", 17.41d, 17.58d, -63.05d, -62.88d, 9140, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10752, 10735, 10740, 0.2d, "Geographic3D to GravityRelatedHeight", "", 17.41d, 17.58d, -63.05d, -62.88d, 9147, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10753, 10735, 10741, 0.2d, "Geog3D to Geog2D+GravityRelatedHeight", "", 17.41d, 17.58d, -63.05d, -62.88d, 9148, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10766, 10758, 10762, 0.05d, "Coordinate Frame rotation full matrix (geog2D)", "", 11.97d, 12.36d, -68.47d, -68.14d, 9149, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10767, 10761, 10763, 0.25d, "Geographic3D to GravityRelatedHeight (gtx)", "bongeo2004.gtx", 11.97d, 12.36d, -68.47d, -68.14d, 9156, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10768, 10761, 10765, 0.25d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "bongeo2004.gtx", 11.97d, 12.36d, -68.47d, -68.14d, 9156, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10769, 10758, 4326, 1.0d, "Coordinate Frame rotation full matrix (geog2D)", "", 11.97d, 12.36d, -68.47d, -68.14d, 9157, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10770, 10762, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 11.97d, 12.36d, -68.47d, -68.14d, 9164, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10771, 10725, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 37.18d, 45.58d, 55.99d, 73.17d, 9167, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10775, 9332, 9335, 0.02d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "KSA-GEOID21.gra", 16.37d, 32.16d, 34.51d, 55.67d, 9170, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10776, 9332, 9520, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "KSA-GEOID21.gra", 16.37d, 32.16d, 34.51d, 55.67d, 9170, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10777, 9988, 9331, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", 16.29d, 32.16d, 34.44d, 55.67d, 9171, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10782, 9988, 10779, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 9186, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10786, 10779, 10783, 0.0d, "Time-specific Position Vector transform (geocen)", "", -90.0d, 90.0d, -180.0d, 180.0d, 9201, 8), + new EpsgOperationRecord((EpsgOperationType)0, 10787, 10176, 10783, 0.001d, "Time-dependent Position Vector tfm (geocentric)", "", -90.0d, 90.0d, -180.0d, 180.0d, 9209, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10796, 4210, 10791, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -1.48d, 4.23d, 29.57d, 35.01d, 9224, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10797, 10791, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -1.48d, 4.23d, 29.57d, 35.01d, 9231, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10803, 10800, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 1.02d, 8.52d, -13.59d, -7.36d, 9234, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10804, 10736, 4326, 1.0d, "Coordinate Frame rotation full matrix (geog2D)", "", 17.41d, 17.58d, -63.05d, -62.88d, 9237, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10809, 8401, 10805, 0.003d, "Geocentric translations using NEU velocity grid (gtg)", "NKG_RF17vel.tif", 53.89d, 71.39d, 3.24d, 31.77d, 9244, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10810, 10805, 10688, 0.002d, "Position Vector (geocen) & Geocen translations NEU velocities (gtg)", "NKG_RF17vel.tif", 58.84d, 70.09d, 19.08d, 31.59d, 9247, 10), + new EpsgOperationRecord((EpsgOperationType)0, 10811, 10805, 10873, 0.005d, "Geocen translations by grid (gtg) & Geocen translations NEU velocities (gtg)", "no_kv_NKGETRF14_EPSG7922_2000.tif", 57.9d, 71.24d, 4.39d, 31.32d, 9257, 4), + new EpsgOperationRecord((EpsgOperationType)0, 10812, 10805, 4950, 0.008d, "Position Vector (geocen) & Geocen translations NEU velocities (gtg)", "NKG_RF17vel.tif", 53.89d, 56.45d, 19.02d, 26.82d, 9261, 10), + new EpsgOperationRecord((EpsgOperationType)0, 10813, 10805, 4976, 0.001d, "Position Vector (geocen) & Geocen translations NEU velocities (gtg)", "NKG_RF17vel.tif", 54.96d, 69.07d, 10.03d, 24.17d, 9271, 10), + new EpsgOperationRecord((EpsgOperationType)0, 10814, 10805, 4934, 0.002d, "Position Vector (geocen) & Geocen translations NEU velocities (gtg)", "NKG_RF17vel.tif", 57.52d, 60.0d, 20.37d, 28.2d, 9281, 10), + new EpsgOperationRecord((EpsgOperationType)0, 10821, 8251, 10588, 0.03d, "Geographic3D to GravityRelatedHeight (NRCan byn)", "HT2_2010v70.byn", 41.0d, 84.0d, -141.01d, -48.0d, 9291, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10822, 5728, 9389, 0.146d, "Vertical Offset by Grid Interpolation (asc)", "ch_2019z.asc", 45.81d, 47.81d, 5.95d, 10.5d, 9291, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10823, 5728, 9390, 0.142d, "Vertical Offset by Grid Interpolation (asc)", "ch_2019m.asc", 45.81d, 47.81d, 5.95d, 10.5d, 9292, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10827, 4949, 7700, 0.04d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "LV'14.gri", 55.67d, 58.09d, 20.87d, 28.24d, 9293, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10828, 4949, 10826, 0.04d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "LV'14.gri", 55.67d, 58.09d, 20.87d, 28.24d, 9293, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10834, 10829, 4978, 0.03d, "Coordinate Frame rotation (geocentric domain)", "", 41.04d, 43.59d, 38.97d, 46.72d, 9294, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10835, 4284, 10831, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", 41.04d, 43.59d, 39.99d, 46.72d, 9301, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10838, 4661, 10305, 0.02d, "NTv2", "LKS92to2020NTv2.gsb", 55.67d, 58.09d, 19.06d, 28.24d, 9308, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10840, 10305, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 55.67d, 58.09d, 19.06d, 28.24d, 9308, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10841, 5488, 5620, 0.05d, "Geographic3D to GravityRelatedHeight (IGN2009)", "GG23SM.tac", 18.01d, 18.17d, -63.21d, -62.96d, 9311, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10848, 7789, 10798, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 1.02d, 8.52d, -13.59d, -7.36d, 9311, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10850, 11009, 10849, 0.0d, "NTv2", "TN15-ETRS89-to-EWR3-IRF.gsb", 51.7d, 52.33d, -1.43d, 0.27d, 9326, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10853, 9988, 5544, 0.15d, "Time-dependent Coordinate Frame rotation (geocen)", "", -12.0d, -4.98d, 139.2d, 149.67d, 9326, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10854, 10875, 4273, 0.1d, "Geographic2D Offsets by TIN Interpolation (JSON)", "no_kv_ETRS89NO_NGO48_TIN.json", 57.9d, 71.24d, 4.39d, 31.32d, 9341, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10855, 10283, 9927, 0.02d, "Geographic3D to GravityRelatedHeight (txt)", "GCG2016.txt", 47.27d, 55.09d, 5.86d, 15.04d, 9341, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10858, 5799, 9389, 0.012d, "Vertical Offset by Grid Interpolation (gtg)", "dvr90_evrf2019.tif", 54.51d, 57.8d, 8.0d, 12.87d, 9341, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10859, 5799, 9390, 0.018d, "Vertical Offset by Grid Interpolation (gtg)", "dvr90_evrf2019_mean_tide.tif", 54.51d, 57.8d, 8.0d, 12.87d, 9342, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10861, 11009, 10860, 0.0d, "NTv2", "TN15-ETRS89-to-WSPG-IRF.gsb", 54.35d, 54.7d, -1.25d, -0.45d, 9343, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10866, 6310, 7446, 0.043d, "Geographic3D to GravityRelatedHeight (txt)", "SEPARATION.TXT", 34.59d, 35.74d, 32.2d, 34.65d, 9343, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10867, 6310, 10865, 0.043d, "Geog3D to Geog2D+GravityRelatedHeight (txt)", "SEPARATION.TXT", 34.59d, 35.74d, 32.2d, 34.65d, 9343, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10887, 5488, 9535, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "GG23SM.tac", 18.01d, 18.17d, -63.21d, -62.96d, 9344, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10888, 8401, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 9345, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10893, 10805, 10890, 0.001d, "Position Vector (geocen) & Geocen translations NEU velocities (gtg)", "NKG_RF17vel.tif", 54.36d, 58.27d, 3.24d, 16.51d, 9360, 10), + new EpsgOperationRecord((EpsgOperationType)0, 10896, 5488, 5619, 0.05d, "Geographic3D to GravityRelatedHeight (IGN2009)", "GG23SB.tac", 17.82d, 17.98d, -62.92d, -62.73d, 9370, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10897, 5488, 9534, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "GG23SB.tac", 17.82d, 17.98d, -62.92d, -62.73d, 9370, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10905, 10283, 10904, 0.04d, "Coordinate Frame rotation (geog3D to compound)", "", 52.11d, 52.16d, 10.6d, 10.7d, 9371, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10907, 7926, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 9378, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10927, 10909, 10918, 0.015d, "Geographic3D to GravityRelatedHeight (NGS bin)", "g2018u0.bin", 32.53d, 42.01d, -124.45d, -114.12d, 9393, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10928, 10909, 10920, 0.015d, "Geog3D to Geog2D+GravityRelatedHeight (NGS bin)", "g2018u0.bin", 32.53d, 42.01d, -124.45d, -114.12d, 9393, 1), + new EpsgOperationRecord((EpsgOperationType)0, 10930, 10910, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 32.53d, 42.01d, -124.45d, -114.12d, 9394, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10935, 7922, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 9397, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10953, 10950, 10908, 0.0d, "Coordinate Frame rotation (geocentric domain)", "", 32.53d, 42.01d, -124.45d, -114.12d, 9412, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10960, 9988, 10957, 0.1d, "Time-dependent Coordinate Frame rotation (geocen)", "", 56.38d, 87.03d, -75.0d, 7.99d, 9419, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10961, 4196, 4747, 0.4d, "Coordinate Frame rotation (geog2D domain)", "", 65.52d, 65.91d, -38.86d, -36.81d, 9434, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10963, 4195, 4747, 10.0d, "Coordinate Frame rotation (geog2D domain)", "", 68.66d, 74.58d, -29.69d, -19.89d, 9441, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10964, 4284, 10941, 0.1d, "NTv2", "qazgrid_kz.gsb", 40.59d, 55.45d, 46.49d, 87.35d, 9448, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10965, 10941, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 40.59d, 55.45d, 46.49d, 87.35d, 9448, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10969, 10968, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 23.81d, 86.46d, 167.65d, -40.73d, 9451, 3), + new EpsgOperationRecord((EpsgOperationType)0, 10988, 7914, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 9454, 15), + new EpsgOperationRecord((EpsgOperationType)0, 10990, 11008, 10989, 0.0d, "Geographic3D to GravityRelatedHeight (EGM)", "LSGGM2025.grd", 51.1d, 52.0d, -1.33d, 0.8d, 9469, 0), + new EpsgOperationRecord((EpsgOperationType)0, 10996, 11007, 10991, 0.0d, "Coordinate Frame rotation (geocentric domain)", "", 51.1d, 52.0d, -1.33d, 0.8d, 9469, 7), + new EpsgOperationRecord((EpsgOperationType)0, 10998, 11008, 10997, 0.0d, "Geog3D to Geog2D+GravityRelatedHeight (EGM)", "LSGGM2025.grd", 51.1d, 52.0d, -1.33d, 0.8d, 9476, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11003, 10874, 10999, 1.0d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "Href_Svalbard_EUREF89_EGG2015_2024.bin", 74.3208d, 81.8504d, 6.49005d, 33.50985d, 9477, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11004, 10874, 11000, 1.0d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "Href_Svalbard_EUREF89_EGG2015_2024.bin", 74.3208d, 81.8504d, 6.49005d, 33.50985d, 9477, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11010, 11007, 7928, 0.1d, "Geocentric translations (geocentric domain)", "", 49.79d, 60.94d, -8.82d, 1.92d, 9478, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11011, 11009, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 49.79d, 60.94d, -8.82d, 1.92d, 9481, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11028, 10875, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 56.08d, 84.73d, -3.35d, 38.01d, 9484, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11034, 9468, 11029, 0.1d, "Time-dep Coordinate Frame (geocen) & Geocen translations by XYZ vel (INADEFORM)", "v3_dm_grd01_xyz.dat", -13.95d, 7.79d, 92.01d, 141.46d, 9487, 18), + new EpsgOperationRecord((EpsgOperationType)0, 11038, 11035, 7930, 0.03d, "Geocentric translations (geocentric domain)", "", 50.75d, 55.77d, 2.53d, 7.22d, 9505, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11039, 9988, 11035, 0.04d, "Time-dependent Position Vector tfm (geocentric)", "", 50.75d, 55.77d, 2.53d, 7.22d, 9508, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11040, 11037, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 50.75d, 55.77d, 2.53d, 7.22d, 9523, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11044, 11041, 8905, 0.04d, "Molodensky-Badekas (PV geocentric domain)", "", 2.15d, 11.77d, -90.45d, -81.43d, 9526, 10), + new EpsgOperationRecord((EpsgOperationType)0, 11048, 11045, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 39.63d, 42.67d, 18.46d, 21.06d, 9536, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11049, 9988, 11045, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 39.63d, 42.67d, 18.46d, 21.06d, 9539, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11050, 11047, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 39.63d, 42.67d, 18.46d, 21.06d, 9554, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11054, 11051, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 39.63d, 42.67d, 18.46d, 21.06d, 9557, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11058, 11055, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 46.4d, 49.02d, 9.53d, 17.17d, 9560, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11059, 9988, 11055, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 46.4d, 49.02d, 9.53d, 17.17d, 9563, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11060, 11057, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 46.4d, 49.02d, 9.53d, 17.17d, 9578, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11064, 11061, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 49.5d, 51.88d, 2.23d, 6.4d, 9581, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11065, 9988, 11061, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 49.5d, 51.88d, 2.23d, 6.4d, 9584, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11067, 11063, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 49.5d, 51.88d, 2.23d, 6.4d, 9599, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11071, 11068, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 48.58d, 51.06d, 12.09d, 18.86d, 9602, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11072, 9988, 11068, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 48.58d, 51.06d, 12.09d, 18.86d, 9605, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11073, 11070, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 48.58d, 51.06d, 12.09d, 18.86d, 9620, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11080, 11074, 4919, 0.1d, "Geocentric translations (geocentric domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 9623, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11081, 11077, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 9626, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11082, 9988, 11077, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 47.73d, 49.61d, 16.84d, 22.56d, 9629, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11083, 11076, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 9644, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11084, 11079, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 47.73d, 49.61d, 16.84d, 22.56d, 9647, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11088, 11085, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 59.94d, 65.7d, -13.91d, -0.48d, 9650, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11089, 9988, 11085, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 59.94d, 65.7d, -13.91d, -0.48d, 9653, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11090, 11087, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 59.94d, 65.7d, -13.91d, -0.48d, 9668, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11094, 11091, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 33.26d, 41.75d, 18.26d, 30.23d, 9671, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11095, 9988, 11091, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 41.75d, 18.26d, 30.23d, 9674, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11096, 11093, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 33.26d, 41.75d, 18.26d, 30.23d, 9689, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11100, 11097, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 40.85d, 42.36d, 20.45d, 23.04d, 9692, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11104, 9988, 11097, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 40.85d, 42.36d, 20.45d, 23.04d, 9695, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11105, 11099, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 40.85d, 42.36d, 20.45d, 23.04d, 9710, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11109, 11106, 7928, 0.1d, "Geocentric translations (geocentric domain)", "", 34.91d, 42.16d, -13.87d, -6.19d, 9713, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11111, 11108, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 34.91d, 42.16d, -13.87d, -6.19d, 9716, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11121, 11086, 5317, 0.02d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "fo_geoid2012a.gri", 61.33d, 62.41d, -7.49d, -6.33d, 9719, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11122, 11086, 11120, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "fo_geoid2012a.gri", 61.33d, 62.41d, -7.49d, -6.33d, 9719, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11123, 11112, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 43.44d, 48.27d, 20.26d, 31.41d, 9720, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11124, 9988, 11112, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 43.44d, 48.27d, 20.26d, 31.41d, 9723, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11125, 11119, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 43.44d, 48.27d, 20.26d, 31.41d, 9738, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11135, 11129, 7926, 0.1d, "Geocentric translations (geocentric domain)", "", 35.26d, 46.26d, -13.86d, 6.3d, 9741, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11136, 11126, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 35.26d, 46.26d, -13.86d, 6.3d, 9744, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11137, 11134, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 35.26d, 46.26d, -13.86d, 6.3d, 9747, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11138, 11128, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 35.26d, 46.26d, -13.86d, 6.3d, 9750, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11139, 9988, 11126, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 35.26d, 46.26d, -13.86d, 6.3d, 9753, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11149, 4964, 7914, 0.1d, "Geocentric translations (geocentric domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 9768, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11150, 9775, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 9771, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11151, 9780, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 41.15d, 51.56d, -9.86d, 10.38d, 9774, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11152, 4882, 7930, 0.1d, "Position Vector transformation (geocentric domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 9777, 7), + new EpsgOperationRecord((EpsgOperationType)0, 11153, 11007, 7930, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 49.79d, 60.94d, -8.82d, 1.92d, 9784, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11154, 11106, 7930, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 34.91d, 42.16d, -13.87d, -6.19d, 9799, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11155, 11129, 7930, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 35.26d, 46.26d, -13.86d, 6.3d, 9814, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11156, 5776, 5941, 0.03d, "Vertical Offset by Grid Interpolation (gtx)", "NNTrans2018B.gtx", 57.9d, 71.24d, 4.39d, 31.32d, 9829, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11159, 10670, 11157, 0.1d, "Geographic3D to GravityRelatedHeight (IGN2009)", "GGM23V2.tac", -13.05d, -12.61d, 44.98d, 45.35d, 9830, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11160, 10670, 11158, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "GGM23V2.tac", -13.05d, -12.61d, 44.98d, 45.35d, 9830, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11164, 11161, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 9831, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11165, 9988, 11161, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 45.74d, 48.58d, 16.11d, 22.9d, 9834, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11166, 11163, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 9849, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11167, 6704, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 34.76d, 47.1d, 5.93d, 18.99d, 9852, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11168, 4982, 7914, 0.15d, "Geocentric translations (geocentric domain)", "", 34.76d, 47.1d, 5.93d, 18.99d, 9855, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11182, 10326, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 42.56d, 45.27d, 15.74d, 19.62d, 9858, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11183, 9988, 10326, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 42.56d, 45.27d, 15.74d, 19.62d, 9861, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11184, 9988, 6704, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 34.76d, 47.1d, 5.93d, 18.99d, 9876, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11185, 7796, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 41.24d, 44.23d, 22.36d, 31.35d, 9891, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11186, 9988, 7796, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 41.24d, 44.23d, 22.36d, 31.35d, 9894, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11190, 11187, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 41.62d, 46.54d, 13.0d, 19.43d, 9909, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11191, 11189, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 41.62d, 46.54d, 13.0d, 19.43d, 9912, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11192, 4888, 7914, 0.1d, "Geocentric translations (geocentric domain)", "", 41.62d, 46.54d, 13.0d, 19.43d, 9915, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11193, 9988, 11187, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 41.62d, 46.54d, 13.0d, 19.43d, 9918, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11195, 9138, 7928, 0.1d, "Geocentric translations (geocentric domain)", "", 41.85d, 43.25d, 19.97d, 21.8d, 9933, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11200, 11197, 10569, 0.1d, "Position Vector transformation (geocentric domain)", "", 47.27d, 55.92d, 3.34d, 15.04d, 9936, 7), + new EpsgOperationRecord((EpsgOperationType)0, 11203, 10284, 11199, 0.01d, "NTv2", "R16_to_R25.gsb", 47.27d, 55.92d, 3.34d, 15.04d, 9943, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11204, 9988, 11197, 0.0d, "Time-dependent Position Vector tfm (geocentric)", "", 47.27d, 55.92d, 3.34d, 15.04d, 9943, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11205, 4000, 7928, 0.1d, "Geocentric translations (geocentric domain)", "", 45.44d, 48.47d, 26.63d, 30.13d, 9958, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11207, 9700, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 49.0d, 55.93d, 14.14d, 24.15d, 9961, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11208, 9988, 9700, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 9964, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11209, 4073, 7914, 0.1d, "Geocentric translations (geocentric domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 9979, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11210, 8683, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 42.23d, 46.19d, 18.81d, 23.01d, 9982, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11211, 9988, 8683, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 42.23d, 46.19d, 18.81d, 23.01d, 9985, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11212, 9988, 4882, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 45.42d, 46.88d, 13.38d, 16.61d, 10000, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11216, 11213, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 49.5d, 51.88d, 2.23d, 6.4d, 10015, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11217, 9988, 11213, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 49.5d, 51.88d, 2.23d, 6.4d, 10018, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11218, 11215, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 49.5d, 51.88d, 2.23d, 6.4d, 10033, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11220, 4942, 7930, 0.1d, "Geocentric translations (geocentric domain)", "", 51.39d, 55.43d, -10.56d, -5.34d, 10036, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11221, 9988, 4942, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 51.39d, 55.43d, -10.56d, -5.34d, 10039, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11227, 11226, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 26.7d, 28.33d, 88.74d, 92.13d, 10054, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11228, 7928, 7930, 0.01d, "Time-dependent Position Vector tfm (geocentric)", "", 33.26d, 84.73d, -16.1d, 38.01d, 10057, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11229, 4194, 4747, 8.0d, "Coordinate Frame rotation (geog2D domain)", "", 59.74d, 79.0d, -73.29d, -42.52d, 10072, 7), + new EpsgOperationRecord((EpsgOperationType)0, 11273, 11030, 20036, 0.1d, "Geographic3D to GravityRelatedHeight (gtx)", "INAGEOID2020v2.gtx", -13.95d, 7.79d, 92.01d, 141.46d, 10079, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11275, 11030, 11274, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "INAGEOID2020v2.gtx", -13.95d, 7.79d, 92.01d, 141.46d, 10079, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11286, 9988, 9780, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 41.15d, 51.56d, -9.86d, 10.38d, 10080, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11308, 11222, 7914, 0.1d, "Geocentric translations (geocentric domain)", "", 45.81d, 47.81d, 5.95d, 10.5d, 10095, 3), + new EpsgOperationRecord((EpsgOperationType)0, 11309, 11222, 7930, 0.1d, "Time-dependent Position Vector tfm (geocentric)", "", 45.81d, 47.81d, 5.95d, 10.5d, 10098, 15), + new EpsgOperationRecord((EpsgOperationType)0, 11316, 10805, 10303, 0.002d, "Position Vector (geocen) & Geocen translations NEU velocities (gtg)", "NKG_RF17vel.tif", 55.67d, 58.09d, 19.06d, 28.24d, 10113, 10), + new EpsgOperationRecord((EpsgOperationType)0, 11337, 5264, 11226, 0.1d, "NTv2", "d03tod23.gsb", 26.7d, 28.33d, 88.74d, 92.13d, 10123, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11339, 11225, 11338, 0.1d, "Geographic3D to GravityRelatedHeight (gtx)", "drukgeoid2022.gtx", 26.7d, 28.33d, 88.74d, 92.13d, 10123, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11384, 11225, 11383, 0.1d, "Geog3D to Geog2D+GravityRelatedHeight (gtx)", "drukgeoid2022.gtx", 26.7d, 28.33d, 88.74d, 92.13d, 10123, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11386, 6364, 5703, 1.0d, "Geographic3D to GravityRelatedHeight (txt)", "GGM10.txt", 14.51d, 32.72d, -118.47d, -86.68d, 10124, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11387, 6364, 11385, 1.0d, "Geog3D to Geog2D+GravityRelatedHeight (txt)", "GGM10.txt", 14.51d, 32.72d, -118.47d, -86.68d, 10124, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11388, 27700, 11378, 0.0d, "Transverse Mercator", "", 51.42d, 51.52d, -0.56d, -0.37d, 10125, 9), + new EpsgOperationRecord((EpsgOperationType)0, 11395, 10874, 11394, 0.02d, "Geographic3D to GravityRelatedHeight (Gravsoft)", "HREF2025A_NN2000_EUREF89.bin", 57.9d, 71.24d, 4.39d, 31.32d, 10134, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11396, 10874, 11399, 0.02d, "Geog3D to Geog2D+GravityRelatedHeight (Gravsoft)", "HREF2025A_NN2000_EUREF89.bin", 57.9d, 71.24d, 4.39d, 31.32d, 10134, 1), + new EpsgOperationRecord((EpsgOperationType)0, 11397, 4289, 11037, 0.0d, "NTv2 & Coordinate Frame rotation (geocentric domain)", "rdcorr2018.gsb", 50.75d, 55.77d, 2.53d, 7.22d, 10135, 7), + new EpsgOperationRecord((EpsgOperationType)0, 11433, 4690, 4687, 0.1d, "NTv2", "gr3dpf25.gsb", -17.93d, -17.44d, -149.7d, -149.09d, 10142, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11448, 8899, 11446, 0.05d, "Geographic3D to GravityRelatedHeight (IGN2009)", "ggfutuna2022v2.tac", -14.42d, -14.18d, -178.24d, -177.94d, 10142, 0), + new EpsgOperationRecord((EpsgOperationType)0, 11449, 8899, 11447, 0.05d, "Geog3D to Geog2D+GravityRelatedHeight (IGN2009)", "ggfutuna2022v2.tac", -14.42d, -14.18d, -178.24d, -177.94d, 10142, 1), + new EpsgOperationRecord((EpsgOperationType)0, 15483, 4301, 4612, 9.0d, "Geocentric translations (geog2D domain)", "", 20.37d, 45.54d, 122.83d, 154.05d, 10143, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15484, 4301, 4326, 9.0d, "Geocentric translations (geog2D domain)", "", 20.37d, 45.54d, 122.83d, 154.05d, 10146, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15485, 4618, 4674, 5.0d, "Geocentric translations (geog2D domain)", "", -35.71d, 7.04d, -74.01d, -25.28d, 10149, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15486, 4149, 4150, 0.2d, "NTv2", "CHENyx06a.gsb", 45.81d, 47.81d, 5.95d, 10.5d, 10152, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15487, 3828, 3826, 7.0d, "Transverse Mercator", "", 21.87d, 25.34d, 119.99d, 122.06d, 10152, 12), + new EpsgOperationRecord((EpsgOperationType)0, 15493, 4263, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 3.99d, 5.01d, 5.99d, 8.01d, 10164, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15494, 4145, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", 25.88d, 27.67d, 68.24d, 69.3d, 10167, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15495, 4168, 4326, 25.0d, "Position Vector transformation (geog2D domain)", "", 1.4d, 6.06d, -3.79d, 2.1d, 10170, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15496, 4179, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 43.44d, 48.27d, 20.26d, 31.41d, 10177, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15497, 4179, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 43.44d, 48.27d, 20.26d, 31.41d, 10180, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15596, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 45.33d, 45.54d, 142.0d, 142.27d, 10183, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15597, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 44.66d, 45.34d, 141.5d, 142.0d, 10186, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15598, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 44.66d, 45.34d, 142.0d, 142.97d, 10189, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15599, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.99d, 44.67d, 141.58d, 142.0d, 10192, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15600, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.99d, 44.67d, 142.0d, 143.0d, 10195, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15601, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.99d, 44.65d, 143.0d, 144.0d, 10198, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15602, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.99d, 44.19d, 144.0d, 145.0d, 10201, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15603, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.33d, 44.0d, 141.26d, 142.0d, 10204, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15604, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.33d, 44.0d, 142.0d, 143.0d, 10207, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15605, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.33d, 44.0d, 143.0d, 144.0d, 10210, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15606, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.33d, 44.0d, 144.0d, 145.0d, 10213, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15607, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 43.33d, 44.4d, 145.0d, 145.87d, 10216, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15608, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.66d, 43.42d, 140.0d, 141.0d, 10219, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15609, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.66d, 43.34d, 141.0d, 142.0d, 10222, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15610, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.66d, 43.34d, 142.0d, 143.0d, 10225, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15611, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.66d, 43.34d, 143.0d, 144.0d, 10228, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15612, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.84d, 43.34d, 144.0d, 145.0d, 10231, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15613, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.93d, 43.34d, 145.0d, 145.87d, 10234, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15614, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.05d, 42.73d, 139.7d, 140.0d, 10237, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15615, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 41.99d, 42.67d, 140.0d, 141.0d, 10240, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15616, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.24d, 42.67d, 141.0d, 142.0d, 10243, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15617, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 42.02d, 42.67d, 142.0d, 143.0d, 10246, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15618, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 41.87d, 42.67d, 143.0d, 143.76d, 10249, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15619, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 41.33d, 42.0d, 139.91d, 141.0d, 10252, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15620, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 41.33d, 41.96d, 141.0d, 141.53d, 10255, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15621, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 40.66d, 41.34d, 140.0d, 141.0d, 10258, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15622, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 40.66d, 41.34d, 141.0d, 141.53d, 10261, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15623, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 39.99d, 40.8d, 139.63d, 140.0d, 10264, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15624, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 39.99d, 40.67d, 140.0d, 141.0d, 10267, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15625, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 39.99d, 40.67d, 141.0d, 142.0d, 10270, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15626, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 39.33d, 40.0d, 139.63d, 140.0d, 10273, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15627, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 39.33d, 40.0d, 140.0d, 141.0d, 10276, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15628, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 39.33d, 40.0d, 141.0d, 142.14d, 10279, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15629, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 38.66d, 39.34d, 139.55d, 140.0d, 10282, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15630, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 38.66d, 39.34d, 140.0d, 141.0d, 10285, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15631, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 38.66d, 39.34d, 141.0d, 141.99d, 10288, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15632, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.99d, 38.67d, 139.11d, 140.0d, 10291, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15633, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.99d, 38.67d, 140.0d, 141.0d, 10294, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15634, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 38.08d, 38.67d, 141.0d, 141.62d, 10297, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15635, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.33d, 37.47d, 136.67d, 137.0d, 10300, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15636, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.33d, 37.58d, 137.0d, 137.43d, 10303, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15637, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.33d, 37.97d, 138.39d, 139.0d, 10306, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15638, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.33d, 38.0d, 139.0d, 140.0d, 10309, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15639, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.33d, 38.0d, 140.0d, 141.0d, 10312, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15640, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 37.33d, 37.87d, 141.0d, 141.11d, 10315, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15641, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 36.66d, 37.34d, 136.58d, 137.0d, 10318, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15642, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 36.66d, 37.34d, 137.0d, 138.0d, 10321, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15643, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 36.66d, 37.34d, 138.0d, 139.0d, 10324, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15644, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 36.66d, 37.34d, 139.0d, 140.0d, 10327, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15645, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 36.66d, 37.34d, 140.0d, 141.1d, 10330, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15646, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.99d, 36.67d, 135.9d, 137.0d, 10333, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15647, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.99d, 36.67d, 137.0d, 138.0d, 10336, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15648, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.99d, 36.67d, 138.0d, 139.0d, 10339, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15649, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.99d, 36.67d, 139.0d, 140.0d, 10342, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15650, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.99d, 36.67d, 140.0d, 140.77d, 10345, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15651, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 35.58d, 132.56d, 133.0d, 10348, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15652, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 35.64d, 133.0d, 134.0d, 10351, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15653, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 35.73d, 134.0d, 135.0d, 10354, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15654, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 36.0d, 135.0d, 136.0d, 10357, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15655, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 36.0d, 136.0d, 137.0d, 10360, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15656, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 36.0d, 137.0d, 138.0d, 10363, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15657, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 36.0d, 138.0d, 139.0d, 10366, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15658, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 36.0d, 139.0d, 140.0d, 10369, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15659, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 35.33d, 36.0d, 140.0d, 140.9d, 10372, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15660, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 132.0d, 133.0d, 10375, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15661, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 133.0d, 134.0d, 10378, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15662, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 134.0d, 135.0d, 10381, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15663, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 135.0d, 136.0d, 10384, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15664, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 136.0d, 137.0d, 10387, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15665, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 137.0d, 138.0d, 10390, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15666, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 138.0d, 139.0d, 10393, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15667, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.66d, 35.34d, 139.0d, 140.0d, 10396, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15668, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.87d, 35.34d, 140.0d, 140.48d, 10399, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15669, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.99d, 34.48d, 130.81d, 131.0d, 10402, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15670, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.99d, 34.9d, 131.0d, 132.0d, 10405, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15671, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.99d, 34.67d, 132.0d, 133.0d, 10408, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15672, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.99d, 34.67d, 133.0d, 134.0d, 10411, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15673, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.99d, 34.67d, 134.0d, 135.0d, 10414, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15674, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.99d, 34.67d, 135.0d, 136.0d, 10417, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15675, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.99d, 34.67d, 136.0d, 137.0d, 10420, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15676, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.51d, 34.67d, 137.0d, 138.0d, 10423, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15677, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 34.54d, 34.67d, 138.0d, 139.0d, 10426, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15678, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.33d, 33.59d, 129.38d, 130.0d, 10429, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15679, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.33d, 34.0d, 130.0d, 131.0d, 10432, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15680, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.33d, 34.0d, 131.0d, 132.0d, 10435, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15681, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.33d, 34.0d, 132.0d, 133.0d, 10438, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15682, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.33d, 34.0d, 133.0d, 134.0d, 10441, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15683, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.33d, 34.0d, 134.0d, 134.81d, 10444, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15684, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.4d, 34.0d, 135.0d, 136.0d, 10447, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15685, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.54d, 34.0d, 136.0d, 136.34d, 10450, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15686, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 32.51d, 33.34d, 129.3d, 130.0d, 10453, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15687, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 32.66d, 33.34d, 130.0d, 131.0d, 10456, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15688, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 32.66d, 33.34d, 131.0d, 132.0d, 10459, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15689, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 32.69d, 33.34d, 132.0d, 133.0d, 10462, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15690, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 32.7d, 33.34d, 133.0d, 134.0d, 10465, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15691, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 33.19d, 33.34d, 134.0d, 134.27d, 10468, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15692, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 31.99d, 32.67d, 129.89d, 131.0d, 10471, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15693, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 31.99d, 32.67d, 131.0d, 131.91d, 10474, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15694, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 31.33d, 32.0d, 130.1d, 131.0d, 10477, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15695, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 31.33d, 32.0d, 131.0d, 131.55d, 10480, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15696, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 30.94d, 31.34d, 130.14d, 131.19d, 10483, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15697, 7414, 4979, 1.0d, "Geographic2D with Height Offsets", "", 45.33d, 45.54d, 141.56d, 142.0d, 10486, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15699, 4267, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 17.85d, 20.89d, -94.79d, -89.75d, 10489, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15701, 4145, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 21.05d, 25.39d, 64.0d, 68.24d, 10492, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15702, 4145, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", 24.0d, 25.64d, 67.74d, 69.87d, 10495, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15703, 4145, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", 24.69d, 25.76d, 66.83d, 68.0d, 10498, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15705, 4263, 4326, 8.0d, "Position Vector transformation (geog2D domain)", "", 3.25d, 4.23d, 5.02d, 7.31d, 10501, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15706, 4263, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 1.92d, 6.14d, 2.66d, 7.82d, 10508, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15707, 4159, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 29.1d, 29.8d, 20.8d, 21.4d, 10511, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15708, 4683, 4326, 0.05d, "Coordinate Frame rotation (geog2D domain)", "", 3.0d, 22.18d, 116.04d, 129.95d, 10514, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15709, 4680, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 17.89d, 18.25d, -16.11d, -15.83d, 10521, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15710, 4208, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -25.91d, -20.45d, -42.04d, -37.11d, 10524, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15711, 4208, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -28.41d, -22.66d, -48.8d, -40.2d, 10527, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15712, 4208, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -22.04d, -17.59d, -40.37d, -35.18d, 10530, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15713, 4684, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -0.69d, 7.08d, 72.81d, 73.69d, 10533, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15714, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 9.8d, 12.52d, -73.0d, -71.06d, 10536, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15715, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 9.8d, 12.52d, -73.0d, -71.06d, 10543, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15716, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 9.39d, 11.59d, -76.08d, -73.0d, 10550, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15717, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 9.39d, 11.59d, -76.08d, -73.0d, 10557, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15718, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 8.0d, 9.4d, -77.48d, -74.39d, 10564, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15719, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 8.0d, 9.4d, -77.48d, -74.39d, 10571, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15720, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 5.0d, 9.4d, -74.4d, -71.99d, 10578, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15721, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 5.0d, 9.4d, -74.4d, -71.99d, 10585, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15722, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 5.0d, 8.01d, -77.92d, -74.39d, 10592, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15723, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 5.0d, 8.01d, -77.92d, -74.39d, 10599, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15724, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 3.0d, 5.01d, -77.68d, -74.39d, 10606, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15725, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 3.0d, 5.01d, -77.68d, -74.39d, 10613, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15726, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -1.13d, 3.01d, -79.1d, -74.0d, 10620, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15727, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -1.13d, 3.01d, -79.1d, -74.0d, 10627, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15728, 4218, 4686, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -4.23d, 7.1d, -74.4d, -66.87d, 10634, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15729, 4218, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", -4.23d, 7.1d, -74.4d, -66.87d, 10641, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15730, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 9.8d, 12.52d, -73.0d, -71.06d, 10648, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15731, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 9.39d, 11.59d, -76.08d, -73.0d, 10658, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15732, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 8.0d, 9.4d, -77.48d, -74.39d, 10668, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15733, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 5.0d, 9.4d, -74.4d, -71.99d, 10678, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15734, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 5.0d, 8.01d, -77.92d, -74.39d, 10688, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15735, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", 3.0d, 5.01d, -77.68d, -74.39d, 10698, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15736, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", -1.13d, 3.01d, -79.1d, -74.0d, 10708, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15737, 4218, 4686, 1.0d, "Molodensky-Badekas (CF geog2D domain)", "", -4.23d, 7.1d, -74.4d, -66.87d, 10718, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15738, 4686, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -4.23d, 15.51d, -84.77d, -66.87d, 10728, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15739, 4289, 4258, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 10731, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15740, 4289, 4258, 0.5d, "Molodensky-Badekas (CF geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 10738, 10), + new EpsgOperationRecord((EpsgOperationType)0, 15741, 4227, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 34.49d, 35.9d, 39.3d, 40.81d, 10748, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15742, 4227, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 32.31d, 37.3d, 35.61d, 42.38d, 10751, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15743, 4227, 4326, 0.5d, "Position Vector transformation (geog2D domain)", "", 34.49d, 35.9d, 39.3d, 40.81d, 10754, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15745, 4154, 4326, 0.2d, "Geocentric translations (geog2D domain)", "", 26.46d, 26.64d, 52.22d, 52.41d, 10761, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15746, 4693, 4326, 0.2d, "Geocentric translations (geog2D domain)", "", 27.63d, 27.81d, 52.09d, 52.26d, 10764, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15747, 5817, 3307, 0.0d, "Transverse Mercator", "", 27.63d, 27.81d, 52.09d, 52.26d, 10767, 9), + new EpsgOperationRecord((EpsgOperationType)0, 15750, 4605, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 17.06d, 17.46d, -62.92d, -62.5d, 10776, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15751, 4626, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -21.42d, -20.81d, 55.16d, 55.91d, 10779, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15752, 4668, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 34.88d, 84.73d, -10.56d, 38.01d, 10782, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15753, 4230, 4231, 1.0d, "Reversible polynomial of degree 4", "", 51.03d, 62.0d, -5.05d, 10.86d, 10785, 33), + new EpsgOperationRecord((EpsgOperationType)0, 15754, 4208, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -28.41d, -17.59d, -48.8d, -35.18d, 10818, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15755, 4263, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", 5.05d, 5.36d, 6.53d, 6.84d, 10821, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15759, 4692, 4687, 0.5d, "Geocentric translations (geog2D domain)", "", -16.57d, -16.34d, -152.39d, -152.14d, 10824, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15778, 4159, 4326, 0.5d, "Geocentric translations (geog2D domain)", "", 27.5d, 28.07d, 21.25d, 21.59d, 10827, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15779, 4682, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 18.56d, 26.64d, 88.01d, 92.67d, 10830, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15781, 4979, 5798, 1.0d, "Geographic3D to GravityRelatedHeight (EGM)", "DIRACC.DAT", -90.0d, 90.0d, -180.0d, 180.0d, 10833, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15782, 4221, 4694, 5.0d, "Geocentric translations (geog2D domain)", "", -52.43d, -21.78d, -73.59d, -53.65d, 10833, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15783, 4641, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -21.71d, -21.32d, 167.75d, 168.19d, 10836, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15784, 4699, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -20.57d, -19.94d, 57.25d, 57.85d, 10839, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15785, 4203, 4326, 2.9d, "NTv2", "National 84 (02.07.01).gsb", -38.53d, -9.37d, 109.23d, 153.61d, 10842, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15786, 4202, 4326, 2.9d, "NTv2", "A66 National (13.09.01).gsb", -43.7d, -9.86d, 112.85d, 153.69d, 10842, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15787, 4701, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -6.04d, -4.28d, 12.17d, 16.28d, 10842, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15788, 4202, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -43.7d, -9.86d, 112.85d, 153.69d, 10845, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15789, 4203, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -38.53d, -9.37d, 109.23d, 153.61d, 10848, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15790, 4705, 4324, 10.0d, "Geocentric translations (geog2D domain)", "", -6.04d, -5.05d, 10.53d, 12.37d, 10851, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15791, 4259, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -6.04d, -5.05d, 10.53d, 12.37d, 10854, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15792, 4706, 4324, 5.0d, "Geocentric translations (geog2D domain)", "", 27.19d, 30.01d, 32.34d, 34.27d, 10857, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15793, 4212, 4326, 3.0d, "Geocentric translations (geog2D domain)", "", 13.0d, 13.39d, -59.71d, -59.37d, 10860, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15794, 4708, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -12.27d, -11.76d, 96.76d, 96.99d, 10863, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15795, 4707, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 23.69d, 23.93d, -166.36d, -166.03d, 10866, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15796, 4709, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 24.67d, 24.89d, 141.2d, 141.42d, 10869, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15797, 4712, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -8.03d, -7.83d, -14.46d, -14.24d, 10872, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15798, 4710, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -16.08d, -15.85d, -5.85d, -5.59d, 10875, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15799, 4711, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 24.22d, 24.35d, 153.91d, 154.05d, 10878, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15800, 4713, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 10.94d, 12.72d, 41.75d, 44.15d, 10881, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15801, 4714, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", -20.31d, -17.37d, 168.09d, 169.95d, 10884, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15802, 4715, 4326, 999.0d, "Geocentric translations (geog2D domain)", "", -77.94d, -77.17d, 165.73d, 167.43d, 10887, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15803, 4716, 4326, 26.0d, "Geocentric translations (geog2D domain)", "", -4.76d, -2.68d, -174.6d, -170.66d, 10890, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15804, 4717, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 20.86d, 30.83d, -82.33d, -72.68d, 10893, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15805, 4718, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -8.86d, -7.52d, 156.44d, 158.2d, 10896, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15806, 4719, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -27.25d, -27.01d, -109.51d, -109.16d, 10899, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15807, 4718, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -9.98d, -9.2d, 159.55d, 160.88d, 10902, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15808, 4724, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -7.49d, -7.18d, 72.3d, 72.55d, 10905, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15809, 4725, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 16.67d, 16.79d, -169.59d, -169.47d, 10908, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15810, 4735, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 5.21d, 5.43d, 162.85d, 163.1d, 10911, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15811, 4601, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 16.94d, 17.22d, -61.95d, -61.61d, 10914, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15812, 4736, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", -63.08d, -62.82d, -60.89d, -60.35d, 10917, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15813, 4722, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -54.95d, -53.93d, -38.08d, -35.74d, 10920, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15814, 4726, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 19.63d, 19.78d, -80.14d, -79.69d, 10923, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15815, 4728, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 27.58d, 28.9d, -18.22d, -16.08d, 10926, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15816, 4734, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -40.42d, -37.0d, -12.76d, -9.8d, 10929, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15817, 4727, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 28.13d, 28.28d, -177.45d, -177.31d, 10932, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15818, 4727, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 28.13d, 28.28d, -177.45d, -177.31d, 10935, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15819, 4729, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -25.14d, -25.0d, -130.16d, -130.01d, 10938, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15820, 4730, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -17.32d, -14.57d, 166.47d, 168.71d, 10941, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15822, 4732, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 8.66d, 19.38d, 162.27d, 167.82d, 10944, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15823, 4733, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 19.22d, 19.38d, 166.55d, 166.72d, 10947, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15824, 4135, 4326, 38.0d, "Geocentric translations (geog2D domain)", "", 18.87d, 22.29d, -160.3d, -154.74d, 10950, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15825, 4135, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 18.87d, 20.33d, -156.1d, -154.74d, 10953, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15826, 4135, 4326, 35.0d, "Geocentric translations (geog2D domain)", "", 21.81d, 22.29d, -159.85d, -159.23d, 10956, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15827, 4135, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", 20.45d, 21.26d, -157.36d, -155.93d, 10959, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15828, 4135, 4326, 14.0d, "Geocentric translations (geog2D domain)", "", 21.2d, 21.75d, -158.33d, -157.61d, 10962, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15829, 4726, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 19.63d, 19.78d, -80.14d, -79.69d, 10965, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15830, 4723, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 19.21d, 19.41d, -81.46d, -81.04d, 10968, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15831, 4737, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 28.6d, 40.27d, 122.71d, 134.28d, 10971, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15833, 4687, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -31.24d, -4.52d, -158.13d, -131.97d, 10974, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15834, 4269, 4152, 0.05d, "NADCON", "nchpgn.las", 33.83d, 36.59d, -84.33d, -75.38d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15835, 4269, 4326, 2.0d, "NADCON", "nchpgn.las", 33.83d, 36.59d, -84.33d, -75.38d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15836, 4269, 4152, 0.05d, "NADCON", "schpgn.las", 32.05d, 35.21d, -83.36d, -78.52d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15837, 4269, 4326, 2.0d, "NADCON", "schpgn.las", 32.05d, 35.21d, -83.36d, -78.52d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15838, 4269, 4152, 0.05d, "NADCON", "pahpgn.las", 39.71d, 42.53d, -80.53d, -74.7d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15839, 4269, 4326, 2.0d, "NADCON", "pahpgn.las", 39.71d, 42.53d, -80.53d, -74.7d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15840, 4135, 4326, 2.0d, "NADCON", "hawaii.las", 18.87d, 22.29d, -160.3d, -154.74d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15841, 4139, 4326, 2.0d, "NADCON", "prvi.las", 17.87d, 18.57d, -67.97d, -65.19d, 10977, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15842, 4739, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 22.13d, 22.58d, 113.76d, 114.51d, 10977, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15843, 4740, 4326, 1.5d, "Coordinate Frame rotation (geog2D domain)", "", -90.0d, 90.0d, -180.0d, 180.0d, 10980, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15844, 4284, 4740, 4.0d, "Coordinate Frame rotation (geog2D domain)", "", 35.14d, 81.91d, 19.57d, -168.97d, 10987, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15846, 4706, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 27.19d, 30.01d, 32.34d, 34.27d, 10994, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15847, 4639, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -13.42d, -13.17d, -176.29d, -176.11d, 10997, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15848, 4642, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -22.73d, -22.49d, 167.36d, 167.61d, 11000, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15849, 4213, 4326, 15.0d, "Geocentric translations (geog2D domain)", "", 12.8d, 16.7d, 7.81d, 14.9d, 11003, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15850, 4698, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", -49.78d, -48.6d, 68.69d, 70.62d, 11006, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15851, 4267, 4326, 5.0d, "NADCON", "conus.las", 23.81d, 49.38d, -129.17d, -65.69d, 11009, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15852, 4267, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 23.82d, 30.25d, -87.25d, -81.17d, 11009, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15853, 4267, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 25.61d, 30.23d, -95.0d, -87.25d, 11012, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15854, 4267, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 25.97d, 28.97d, -97.22d, -95.0d, 11015, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15855, 4267, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 21.51d, 22.75d, -98.1d, -96.89d, 11018, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15856, 4267, 4326, 8.0d, "Geocentric translations (geog2D domain)", "", 23.82d, 30.25d, -97.22d, -81.17d, 11021, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15857, 3367, 3343, 40.0d, "Transverse Mercator", "", 14.72d, 23.46d, -17.08d, -12.0d, 11024, 16), + new EpsgOperationRecord((EpsgOperationType)0, 15858, 3368, 3344, 40.0d, "Transverse Mercator", "", 14.75d, 27.3d, -12.0d, -6.0d, 11040, 16), + new EpsgOperationRecord((EpsgOperationType)0, 15859, 3369, 3345, 40.0d, "Transverse Mercator", "", 15.49d, 25.74d, -6.0d, -4.8d, 11056, 16), + new EpsgOperationRecord((EpsgOperationType)0, 15860, 4702, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 14.72d, 27.3d, -20.04d, -4.8d, 11072, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15861, 3367, 32628, 40.0d, "Transverse Mercator", "", 14.72d, 23.46d, -17.08d, -12.0d, 11075, 16), + new EpsgOperationRecord((EpsgOperationType)0, 15862, 3368, 32629, 1.0d, "Transverse Mercator", "", 14.75d, 27.3d, -12.0d, -6.0d, 11091, 16), + new EpsgOperationRecord((EpsgOperationType)0, 15863, 3369, 32630, 1.0d, "Transverse Mercator", "", 15.49d, 25.74d, -6.0d, -4.8d, 11107, 16), + new EpsgOperationRecord((EpsgOperationType)0, 15864, 4267, 4326, 5.0d, "NADCON", "alaska.las", 47.88d, 74.71d, 167.65d, -129.99d, 11123, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15865, 4284, 4326, 4.5d, "Coordinate Frame rotation (geog2D domain)", "", 35.14d, 81.91d, 19.57d, -168.97d, 11123, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15866, 4741, 4230, 0.0d, "Geocentric translations (geog2D domain)", "", 61.33d, 62.41d, -7.49d, -6.33d, 11130, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15867, 4746, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 50.2d, 51.65d, 9.87d, 12.66d, 11133, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15868, 4745, 4258, 1.0d, "Position Vector transformation (geog2D domain)", "", 50.2d, 51.66d, 11.89d, 15.04d, 11140, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15869, 4314, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", 50.2d, 54.74d, 9.92d, 15.04d, 11147, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15870, 4679, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 19.37d, 21.34d, -17.08d, -15.88d, 11154, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15872, 4743, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 29.87d, 31.09d, 46.46d, 48.61d, 11157, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15873, 4192, 4326, 10.0d, "Geocentric translations (geog2D domain)", "", 2.16d, 4.99d, 8.45d, 10.4d, 11160, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15874, 4307, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 29.25d, 31.0d, 0.0d, 1.25d, 11163, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15875, 4721, 4326, 7.0d, "Geocentric translations (geog2D domain)", "", -19.22d, -16.1d, 176.81d, -179.77d, 11166, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15876, 4720, 4326, 2.0d, "Position Vector transformation (geog2D domain)", "", -20.81d, -12.42d, 176.81d, -178.15d, 11169, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15877, 4720, 4326, 40.0d, "Coordinate Frame rotation (geog2D domain)", "", -19.22d, -16.1d, 176.81d, -179.77d, 11176, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15878, 4748, 4326, 50.0d, "Geocentric translations (geog2D domain)", "", -17.07d, -16.1d, 178.42d, -179.77d, 11183, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15879, 4747, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 56.38d, 87.03d, -75.0d, 7.99d, 11186, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15880, 4749, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -26.45d, -14.83d, 156.25d, 174.28d, 11189, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15881, 4750, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -20.77d, -20.34d, 166.44d, 166.71d, 11192, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15882, 4662, 4749, 2.0d, "Geocentric translations (geog2D domain)", "", -22.45d, -20.03d, 163.92d, 167.09d, 11195, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15883, 4633, 4749, 1.0d, "Geocentric translations (geog2D domain)", "", -21.24d, -20.62d, 166.98d, 167.52d, 11198, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15884, 4641, 4749, 2.0d, "Geocentric translations (geog2D domain)", "", -21.71d, -21.32d, 167.75d, 168.19d, 11201, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15885, 4750, 4749, 0.5d, "Geocentric translations (geog2D domain)", "", -20.77d, -20.34d, 166.44d, 166.71d, 11204, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15886, 4644, 4749, 1.0d, "Geocentric translations (geog2D domain)", "", -22.37d, -22.19d, 166.35d, 166.54d, 11207, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15887, 4662, 4749, 0.3d, "Coordinate Frame rotation (geog2D domain)", "", -22.45d, -20.03d, 163.92d, 167.09d, 11210, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15888, 4662, 4749, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", -22.37d, -22.19d, 166.35d, 166.54d, 11217, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15889, 4644, 4749, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", -22.37d, -22.19d, 166.35d, 166.54d, 11224, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15890, 4633, 4749, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", -21.24d, -20.62d, 166.98d, 167.52d, 11231, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15891, 4641, 4749, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", -21.71d, -21.32d, 167.75d, 168.19d, 11238, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15892, 4750, 4749, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", -20.77d, -20.34d, 166.44d, 166.71d, 11245, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15893, 4642, 4749, 0.1d, "Coordinate Frame rotation (geog2D domain)", "", -22.73d, -22.49d, 167.36d, 167.61d, 11252, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15894, 4674, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -59.87d, 32.72d, -122.19d, -25.28d, 11259, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15896, 4751, 4245, 0.0d, "Geocentric translations (geog2D domain)", "", 1.13d, 6.72d, 99.59d, 104.6d, 11262, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15897, 4752, 4326, 44.0d, "Geocentric translations (geog2D domain)", "", -18.32d, -17.25d, 177.19d, 178.75d, 11265, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15899, 4195, 4747, 12.0d, "Position Vector transformation (geog2D domain)", "", 68.66d, 74.58d, -29.69d, -19.89d, 11268, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15900, 4196, 4747, 1.0d, "Position Vector transformation (geog2D domain)", "", 65.52d, 65.91d, -38.86d, -36.81d, 11275, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15901, 4641, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -21.71d, -21.32d, 167.75d, 168.19d, 11282, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15902, 4633, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -21.24d, -20.62d, 166.98d, 167.52d, 11285, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15903, 4662, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", -22.45d, -20.03d, 163.92d, 167.09d, 11288, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15904, 4644, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -22.37d, -22.19d, 166.35d, 166.54d, 11291, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15908, 4754, 4326, 0.1d, "Geocentric translations (geog2D domain)", "", 19.5d, 35.23d, 9.31d, 26.21d, 11294, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15909, 4159, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 19.5d, 33.23d, 9.31d, 25.21d, 11297, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15911, 4238, 4755, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -10.98d, 5.97d, 95.16d, 141.01d, 11300, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15912, 4755, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -13.95d, 7.79d, 92.01d, 141.46d, 11307, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15913, 4267, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 20.87d, 23.01d, -94.33d, -88.67d, 11310, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15918, 4214, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 35.0d, 39.0d, 107.0d, 110.01d, 11313, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15919, 4214, 4326, 15.0d, "Position Vector transformation (geog2D domain)", "", 31.23d, 37.4d, 119.23d, 125.06d, 11316, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15920, 4214, 4326, 15.0d, "Position Vector transformation (geog2D domain)", "", 18.31d, 22.89d, 110.13d, 116.76d, 11323, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15921, 4214, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 37.0d, 41.99d, 77.45d, 88.0d, 11330, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15922, 24500, 3414, 2.0d, "Cassini-Soldner", "", 1.13d, 1.47d, 103.59d, 104.07d, 11333, 11), + new EpsgOperationRecord((EpsgOperationType)0, 15923, 4159, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 32.0d, 32.8d, 22.49d, 23.0d, 11344, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15924, 4159, 4754, 5.0d, "Geocentric translations (geog2D domain)", "", 19.5d, 33.23d, 9.31d, 25.21d, 11347, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15925, 4758, 4326, 0.0d, "Geocentric translations (geog2D domain)", "", 14.08d, 19.36d, -80.6d, -74.51d, 11350, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15926, 4242, 4758, 0.5d, "Coordinate Frame rotation (geog2D domain)", "", 17.64d, 18.58d, -78.43d, -76.17d, 11353, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15927, 4242, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 17.64d, 18.58d, -78.43d, -76.17d, 11360, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15928, 4313, 11063, 0.2d, "Coordinate Frame rotation (geog2D domain)", "", 49.5d, 51.51d, 2.5d, 6.4d, 11367, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15929, 4313, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 49.5d, 51.51d, 2.5d, 6.4d, 11374, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15931, 4759, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 14.92d, 74.71d, 167.65d, -63.88d, 11381, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15932, 4230, 4258, 0.2d, "NTv2", "SPED2ETV2.gsb", 35.84d, 43.82d, -9.37d, 4.39d, 11384, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15933, 4230, 4326, 1.0d, "NTv2", "SPED2ETV2.gsb", 35.84d, 43.82d, -9.37d, 4.39d, 11384, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15934, 4289, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 50.75d, 53.7d, 3.2d, 7.22d, 11384, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15935, 4214, 4326, 10.0d, "Position Vector transformation (geog2D domain)", "", 17.81d, 21.69d, 107.15d, 110.17d, 11391, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15936, 4214, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 35.0d, 39.0d, 107.0d, 110.01d, 11398, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15937, 4270, 4326, 2.0d, "Geocentric translations (geog2D domain)", "", 24.0d, 25.64d, 51.5d, 54.85d, 11401, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15938, 4270, 4326, 5.0d, "Position Vector transformation (geog2D domain)", "", 24.0d, 25.64d, 51.5d, 54.85d, 11404, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15948, 4314, 4258, 0.9d, "NTv2", "BETA2007.gsb", 47.27d, 55.09d, 5.86d, 15.04d, 11411, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15949, 4314, 4326, 1.0d, "NTv2", "BETA2007.gsb", 47.27d, 55.09d, 5.86d, 15.04d, 11411, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15950, 3140, 3460, 1.0d, "Cassini-Soldner", "", -18.32d, -17.25d, 177.19d, 178.75d, 11411, 27), + new EpsgOperationRecord((EpsgOperationType)0, 15951, 3139, 3460, 1.0d, "Hyperbolic Cassini-Soldner", "", -17.07d, -16.1d, 178.42d, -179.77d, 11438, 27), + new EpsgOperationRecord((EpsgOperationType)0, 15952, 4270, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 24.94d, 25.8d, 54.06d, 55.3d, 11465, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15953, 4270, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 24.85d, 25.34d, 54.84d, 55.55d, 11468, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15954, 4745, 4326, 1.0d, "NTv2", "BETA2007.gsb", 50.2d, 51.66d, 11.89d, 15.04d, 11471, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15955, 4746, 4326, 1.0d, "NTv2", "BETA2007.gsb", 50.2d, 51.65d, 9.87d, 12.66d, 11471, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15957, 4194, 4747, 10.0d, "Position Vector transformation (geog2D domain)", "", 59.74d, 79.0d, -73.29d, -42.52d, 11471, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15958, 4171, 4275, 1.0d, "NTv2", "rgf93_ntf.gsb", 41.31d, 51.14d, -4.87d, 9.63d, 11478, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15959, 4258, 4275, 1.0d, "NTv2", "rgf93_ntf.gsb", 41.31d, 51.14d, -4.87d, 9.63d, 11478, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15960, 4326, 4275, 1.0d, "NTv2", "rgf93_ntf.gsb", 41.31d, 51.14d, -4.87d, 9.63d, 11478, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15962, 4749, 4662, 0.1d, "NTv2", "RGNC1991_IGN72GrandeTerre.gsb", -22.45d, -20.03d, 163.92d, 167.09d, 11478, 0), + new EpsgOperationRecord((EpsgOperationType)0, 15964, 4230, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 34.91d, 41.88d, -13.87d, -7.24d, 11478, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15965, 4156, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 47.73d, 51.06d, 12.09d, 22.56d, 11481, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15967, 4761, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 41.62d, 46.54d, 13.0d, 19.43d, 11484, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15969, 4216, 4762, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 32.21d, 32.43d, -64.89d, -64.61d, 11487, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15970, 4216, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 32.21d, 32.43d, -64.89d, -64.61d, 11494, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15971, 4762, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 28.91d, 35.73d, -68.83d, -60.7d, 11501, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15972, 4763, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -25.14d, -25.0d, -130.16d, -130.01d, 11504, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15974, 4764, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", -90.0d, -59.99d, 144.99d, -144.99d, 11507, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15975, 4272, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", -47.65d, -33.89d, 165.87d, 179.27d, 11510, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15976, 4765, 4326, 1.0d, "Geocentric translations (geog2D domain)", "", 45.42d, 46.88d, 13.38d, 16.61d, 11513, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15978, 4267, 4326, 1.0d, "Coordinate Frame rotation (geog2D domain)", "", 18.83d, 25.51d, -87.01d, -73.57d, 11516, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15979, 4202, 4283, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -47.2d, -8.88d, 109.23d, 163.2d, 11523, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15980, 4202, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", -47.2d, -8.88d, 109.23d, 163.2d, 11530, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15993, 4179, 4258, 10.0d, "Coordinate Frame rotation (geog2D domain)", "", 43.44d, 48.27d, 20.26d, 31.41d, 11537, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15994, 4179, 11119, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", 43.44d, 48.27d, 20.26d, 31.41d, 11544, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15995, 4179, 4326, 3.0d, "Coordinate Frame rotation (geog2D domain)", "", 43.44d, 48.27d, 20.26d, 31.41d, 11551, 7), + new EpsgOperationRecord((EpsgOperationType)0, 15996, 4178, 4326, 4.0d, "Geocentric translations (geog2D domain)", "", 45.74d, 48.58d, 16.11d, 22.9d, 11558, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15997, 4179, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 49.0d, 54.89d, 14.14d, 24.15d, 11561, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15998, 4178, 4326, 5.0d, "Geocentric translations (geog2D domain)", "", 47.73d, 51.06d, 12.09d, 22.56d, 11564, 3), + new EpsgOperationRecord((EpsgOperationType)0, 15999, 4179, 4326, 6.0d, "Geocentric translations (geog2D domain)", "", 39.64d, 42.67d, 19.22d, 21.06d, 11567, 3), + new EpsgOperationRecord((EpsgOperationType)1, 3896, 4805, 4326, 0.0d, "", "", 46.4d, 49.02d, 9.53d, 17.17d, 11570, 8), + new EpsgOperationRecord((EpsgOperationType)1, 3966, 4805, 4326, 1.0d, "", "", 40.85d, 46.88d, 13.38d, 23.04d, 11578, 4), + new EpsgOperationRecord((EpsgOperationType)1, 4435, 4139, 4152, 0.05d, "", "prvi.las", 17.62d, 18.57d, -67.97d, -64.51d, 11582, 0), + new EpsgOperationRecord((EpsgOperationType)1, 4837, 4289, 4230, 1.0d, "", "", 50.75d, 55.77d, 2.53d, 7.22d, 11582, 14), + new EpsgOperationRecord((EpsgOperationType)1, 5190, 5132, 4737, 0.0d, "", "", 33.14d, 38.64d, 124.53d, 131.01d, 11596, 11), + new EpsgOperationRecord((EpsgOperationType)1, 5192, 5132, 4326, 0.0d, "", "", 33.14d, 38.64d, 124.53d, 131.01d, 11607, 11), + new EpsgOperationRecord((EpsgOperationType)1, 5230, 4818, 4326, 0.0d, "", "", 47.73d, 49.61d, 16.84d, 22.56d, 11618, 8), + new EpsgOperationRecord((EpsgOperationType)1, 5240, 5229, 4326, 0.0d, "", "", 48.58d, 51.06d, 12.09d, 18.86d, 11626, 8), + new EpsgOperationRecord((EpsgOperationType)1, 5242, 4818, 4326, 0.0d, "", "", 48.58d, 51.06d, 12.09d, 18.86d, 11634, 8), + new EpsgOperationRecord((EpsgOperationType)1, 5838, 4803, 4326, 0.0d, "", "", 36.95d, 42.16d, -9.56d, -6.19d, 11642, 8), + new EpsgOperationRecord((EpsgOperationType)1, 6714, 4301, 6668, 0.2d, "", "tky2jgd.gsb", 34.84d, 41.58d, 135.42d, 142.14d, 11650, 0), + new EpsgOperationRecord((EpsgOperationType)1, 6739, 4267, 4152, 0.15d, "", "conus.las", 42.48d, 45.95d, -104.07d, -96.43d, 11650, 0), + new EpsgOperationRecord((EpsgOperationType)1, 6874, 4810, 4326, 0.0d, "", "", -25.64d, -11.89d, 43.18d, 50.56d, 11650, 4), + new EpsgOperationRecord((EpsgOperationType)1, 7811, 4807, 4171, 0.0d, "", "rgf93_ntf.gsb", 41.31d, 51.14d, -4.87d, 9.63d, 11654, 1), + new EpsgOperationRecord((EpsgOperationType)1, 7965, 5754, 5731, 0.1d, "", "", 51.39d, 55.43d, -10.56d, -5.34d, 11655, 1), + new EpsgOperationRecord((EpsgOperationType)1, 7967, 5754, 5732, 0.1d, "", "", 53.96d, 55.36d, -8.18d, -5.34d, 11656, 1), + new EpsgOperationRecord((EpsgOperationType)1, 7973, 5702, 5703, 0.02d, "", "vertconw.94", 31.33d, 49.05d, -124.79d, -107.0d, 11657, 0), + new EpsgOperationRecord((EpsgOperationType)1, 7974, 5702, 5703, 0.02d, "", "vertconc.94", 25.83d, 49.38d, -107.0d, -89.0d, 11657, 0), + new EpsgOperationRecord((EpsgOperationType)1, 7975, 5702, 5703, 0.02d, "", "vertcone.94", 24.41d, 48.32d, -89.0d, -66.91d, 11657, 0), + new EpsgOperationRecord((EpsgOperationType)1, 7983, 5738, 5739, 0.0d, "", "", 22.13d, 22.58d, 113.76d, 114.51d, 11657, 1), + new EpsgOperationRecord((EpsgOperationType)1, 7986, 5790, 5789, 0.1d, "", "", 28.53d, 30.09d, 46.54d, 48.48d, 11658, 1), + new EpsgOperationRecord((EpsgOperationType)1, 8047, 4230, 4326, 1.0d, "", "", 65.0d, 84.73d, -3.35d, 38.01d, 11659, 14), + new EpsgOperationRecord((EpsgOperationType)1, 8094, 4807, 4326, 0.0d, "", "", 41.31d, 51.14d, -4.87d, 9.63d, 11673, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8174, 4802, 4326, 0.0d, "", "", -4.23d, 12.52d, -79.1d, -66.87d, 11677, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8175, 4806, 4326, 0.0d, "", "", 38.82d, 41.31d, 8.08d, 9.89d, 11681, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8176, 4810, 4326, 0.0d, "", "", -25.64d, -11.89d, 43.18d, 50.56d, 11685, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8178, 4813, 4326, 0.0d, "", "", -8.91d, 5.97d, 95.16d, 115.77d, 11689, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8186, 4807, 4230, 0.0d, "", "", 41.31d, 51.14d, -4.87d, 9.63d, 11693, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8188, 4807, 4322, 0.0d, "", "", 41.31d, 51.14d, -4.87d, 9.63d, 11697, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8211, 4811, 4326, 0.0d, "", "", 31.99d, 37.14d, -2.95d, 9.09d, 11701, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8241, 4903, 4326, 10.0d, "", "", 35.95d, 43.82d, -9.37d, 3.39d, 11705, 12), + new EpsgOperationRecord((EpsgOperationType)1, 8363, 11312, 11314, 0.03d, "", "Slovakia_ETRS89h_to_Baltic1957.gtx", 47.73d, 49.61d, 16.84d, 22.56d, 11717, 2), + new EpsgOperationRecord((EpsgOperationType)1, 8442, 11076, 4156, 0.001d, "", "Slovakia_JTSK03_to_JTSK.LAS", 47.73d, 49.61d, 16.84d, 22.56d, 11719, 7), + new EpsgOperationRecord((EpsgOperationType)1, 8443, 4156, 11076, 0.05d, "", "Slovakia_JTSK03_to_JTSK.LAS", 47.73d, 49.61d, 16.84d, 22.56d, 11726, 7), + new EpsgOperationRecord((EpsgOperationType)1, 8460, 4267, 4152, 0.15d, "", "conus.las", 30.14d, 35.02d, -88.48d, -84.89d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8461, 4267, 4152, 0.15d, "", "conus.las", 31.33d, 37.01d, -114.81d, -109.04d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8462, 4267, 4152, 0.15d, "", "conus.las", 36.5d, 42.01d, -124.45d, -116.54d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8463, 4267, 4152, 0.15d, "", "conus.las", 32.53d, 36.5d, -121.98d, -114.12d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8464, 4267, 4152, 0.15d, "", "conus.las", 36.98d, 41.01d, -109.06d, -102.04d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8465, 4267, 4152, 0.15d, "", "conus.las", 30.36d, 35.01d, -85.61d, -80.77d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8466, 4267, 4152, 0.15d, "", "conus.las", 24.41d, 31.01d, -87.63d, -79.97d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8467, 4267, 4152, 0.15d, "", "conus.las", 41.99d, 49.01d, -113.0d, -104.04d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8468, 4267, 4152, 0.15d, "", "conus.las", 41.99d, 49.01d, -117.24d, -113.0d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8469, 4267, 4152, 0.15d, "", "conus.las", 36.49d, 39.15d, -89.57d, -81.95d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8470, 4267, 4152, 0.15d, "", "conus.las", 28.85d, 33.03d, -94.05d, -88.75d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8471, 4267, 4152, 0.15d, "", "conus.las", 37.97d, 39.85d, -79.49d, -74.97d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8472, 4267, 4152, 0.15d, "", "conus.las", 43.04d, 47.47d, -71.09d, -66.91d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8473, 4267, 4152, 0.15d, "", "conus.las", 41.69d, 48.32d, -90.42d, -82.13d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8474, 4267, 4152, 0.15d, "", "conus.las", 30.01d, 35.01d, -91.65d, -88.09d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8475, 4267, 4152, 0.15d, "", "conus.las", 39.99d, 43.01d, -104.06d, -95.3d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8476, 4267, 4152, 0.15d, "", "conus.las", 40.98d, 45.31d, -73.73d, -69.86d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8477, 4267, 4152, 0.15d, "", "conus.las", 31.33d, 37.0d, -109.06d, -102.99d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8478, 4267, 4152, 0.15d, "", "conus.las", 40.47d, 45.02d, -79.77d, -71.8d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8479, 4267, 4152, 0.15d, "", "conus.las", 45.93d, 49.01d, -104.07d, -96.55d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8480, 4267, 4152, 0.15d, "", "conus.las", 33.62d, 37.01d, -103.0d, -94.42d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8483, 4267, 4152, 0.15d, "", "conus.las", 34.98d, 36.68d, -90.31d, -81.65d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8484, 4267, 4152, 0.15d, "", "conus.las", 25.83d, 34.58d, -100.0d, -93.5d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8485, 4267, 4152, 0.15d, "", "conus.las", 28.04d, 36.5d, -106.66d, -100.0d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8486, 4267, 4152, 0.15d, "", "conus.las", 36.54d, 39.46d, -83.68d, -75.31d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8487, 4267, 4152, 0.15d, "", "conus.las", 41.98d, 49.05d, -124.79d, -116.47d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8488, 4267, 4152, 0.15d, "", "conus.las", 42.48d, 47.31d, -92.89d, -86.25d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8489, 4267, 4152, 0.15d, "", "conus.las", 40.99d, 45.01d, -111.06d, -104.05d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8508, 4135, 4152, 0.2d, "", "hawaii.las", 18.87d, 22.29d, -160.3d, -154.74d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8509, 4267, 4152, 0.15d, "", "conus.las", 37.77d, 41.77d, -88.1d, -84.78d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8510, 4267, 4152, 0.15d, "", "conus.las", 36.99d, 40.01d, -102.06d, -94.58d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8511, 4267, 4152, 0.15d, "", "conus.las", 34.99d, 42.0d, -120.0d, -114.03d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8512, 4267, 4152, 0.15d, "", "conus.las", 38.4d, 42.33d, -84.83d, -80.51d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8513, 4267, 4152, 0.15d, "", "conus.las", 36.99d, 42.01d, -114.05d, -109.04d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8514, 4267, 4152, 0.15d, "", "conus.las", 37.2d, 40.64d, -82.65d, -77.72d, 11733, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8517, 4160, 4326, 10.0d, "", "", -38.75d, -37.5d, -69.5d, -68.25d, 11733, 6), + new EpsgOperationRecord((EpsgOperationType)1, 8532, 4131, 4326, 25.0d, "", "", 7.99d, 11.15d, 106.54d, 110.0d, 11739, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8537, 4229, 4326, 5.0d, "", "", 21.89d, 33.82d, 24.7d, 37.91d, 11749, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8553, 4267, 4152, 0.15d, "", "conus.las", 36.97d, 42.51d, -91.52d, -87.02d, 11759, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8554, 4267, 4152, 0.15d, "", "conus.las", 38.87d, 41.36d, -75.6d, -73.88d, 11759, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8562, 4307, 4326, 8.0d, "", "", 31.48d, 32.09d, 5.59d, 6.5d, 11759, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8568, 4227, 4326, 5.0d, "", "", 34.49d, 35.9d, 39.3d, 40.81d, 11769, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8569, 4230, 4326, 1.0d, "", "", 65.0d, 84.73d, -3.35d, 38.01d, 11779, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8571, 4168, 4326, 25.0d, "", "", 1.4d, 6.06d, -3.79d, 2.1d, 11789, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8581, 4134, 4326, 1.2d, "", "", 16.59d, 26.58d, 51.99d, 59.91d, 11799, 14), + new EpsgOperationRecord((EpsgOperationType)1, 8582, 4135, 4326, 0.2d, "", "hawaii.las", 18.87d, 22.29d, -160.3d, -154.74d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8583, 4139, 4326, 0.05d, "", "prvi.las", 17.62d, 18.57d, -67.97d, -64.51d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8585, 4267, 4326, 1.5d, "", "NTv2_0.gsb", 48.99d, 60.0d, -120.0d, -109.98d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8586, 4267, 4152, 0.15d, "", "conus.las", 33.01d, 36.5d, -94.62d, -89.64d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8587, 4267, 4152, 0.15d, "", "conus.las", 40.36d, 43.51d, -96.65d, -90.14d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8588, 4267, 4152, 0.15d, "", "conus.las", 43.49d, 49.38d, -97.22d, -89.49d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8589, 4267, 4152, 0.15d, "", "conus.las", 35.98d, 40.61d, -95.77d, -89.1d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8590, 4267, 4326, 0.15d, "", "conus.las", 30.14d, 35.02d, -88.48d, -84.89d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8591, 4267, 4326, 0.15d, "", "conus.las", 31.33d, 37.01d, -114.81d, -109.04d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8592, 4267, 4326, 0.15d, "", "conus.las", 33.01d, 36.5d, -94.62d, -89.64d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8593, 4267, 4326, 0.15d, "", "conus.las", 36.5d, 42.01d, -124.45d, -116.54d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8594, 4267, 4326, 0.15d, "", "conus.las", 32.53d, 36.5d, -121.98d, -114.12d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8595, 4267, 4326, 0.15d, "", "conus.las", 36.98d, 41.01d, -109.06d, -102.04d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8596, 4267, 4326, 0.15d, "", "conus.las", 24.41d, 31.01d, -87.63d, -79.97d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8597, 4267, 4326, 0.15d, "", "conus.las", 30.36d, 35.01d, -85.61d, -80.77d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8598, 4267, 4326, 0.15d, "", "conus.las", 36.97d, 42.51d, -91.52d, -87.02d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8599, 4267, 4326, 0.15d, "", "conus.las", 37.77d, 41.77d, -88.1d, -84.78d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8600, 4267, 4326, 0.15d, "", "conus.las", 40.36d, 43.51d, -96.65d, -90.14d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8601, 4267, 4326, 0.15d, "", "conus.las", 36.99d, 40.01d, -102.06d, -94.58d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8602, 4267, 4326, 0.15d, "", "conus.las", 36.49d, 39.15d, -89.57d, -81.95d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8603, 4267, 4326, 0.15d, "", "conus.las", 28.85d, 33.03d, -94.05d, -88.75d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8604, 4267, 4326, 0.15d, "", "conus.las", 43.04d, 47.47d, -71.09d, -66.91d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8605, 4267, 4326, 0.15d, "", "conus.las", 37.97d, 39.85d, -79.49d, -74.97d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8606, 4267, 4326, 0.15d, "", "conus.las", 40.98d, 45.31d, -73.73d, -69.86d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8607, 4267, 4326, 0.15d, "", "conus.las", 41.69d, 48.32d, -90.42d, -82.13d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8608, 4267, 4326, 0.15d, "", "conus.las", 43.49d, 49.38d, -97.22d, -89.49d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8609, 4267, 4326, 0.15d, "", "conus.las", 30.01d, 35.01d, -91.65d, -88.09d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8610, 4267, 4326, 0.15d, "", "conus.las", 35.98d, 40.61d, -95.77d, -89.1d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8611, 4267, 4326, 0.15d, "", "conus.las", 41.99d, 49.01d, -113.0d, -104.04d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8612, 4267, 4326, 0.15d, "", "conus.las", 41.99d, 49.01d, -117.24d, -113.0d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8613, 4267, 4326, 0.15d, "", "conus.las", 39.99d, 43.01d, -104.06d, -95.3d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8614, 4267, 4326, 0.15d, "", "conus.las", 34.99d, 42.0d, -120.0d, -114.03d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8615, 4267, 4326, 0.15d, "", "conus.las", 38.87d, 41.36d, -75.6d, -73.88d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8616, 4267, 4326, 0.15d, "", "conus.las", 31.33d, 37.0d, -109.06d, -102.99d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8617, 4267, 4326, 0.15d, "", "conus.las", 40.47d, 45.02d, -79.77d, -71.8d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8618, 4267, 4326, 0.15d, "", "conus.las", 45.93d, 49.01d, -104.07d, -96.55d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8619, 4267, 4326, 0.15d, "", "conus.las", 38.4d, 42.33d, -84.83d, -80.51d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8620, 4267, 4326, 0.15d, "", "conus.las", 33.62d, 37.01d, -103.0d, -94.42d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8621, 4267, 4326, 0.15d, "", "conus.las", 41.98d, 49.05d, -124.79d, -116.47d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8622, 4267, 4326, 0.15d, "", "conus.las", 42.48d, 45.95d, -104.07d, -96.43d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8623, 4267, 4326, 0.15d, "", "conus.las", 34.98d, 36.68d, -90.31d, -81.65d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8624, 4267, 4326, 0.15d, "", "conus.las", 25.83d, 34.58d, -100.0d, -93.5d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8625, 4267, 4326, 0.15d, "", "conus.las", 28.04d, 36.5d, -106.66d, -100.0d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8626, 4267, 4326, 0.15d, "", "conus.las", 36.99d, 42.01d, -114.05d, -109.04d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8627, 4267, 4326, 0.15d, "", "conus.las", 36.54d, 39.46d, -83.68d, -75.31d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8628, 4267, 4326, 0.15d, "", "conus.las", 37.2d, 40.64d, -82.65d, -77.72d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8629, 4267, 4326, 0.15d, "", "conus.las", 42.48d, 47.31d, -92.89d, -86.25d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8630, 4267, 4326, 0.15d, "", "conus.las", 40.99d, 45.01d, -111.06d, -104.05d, 11813, 0), + new EpsgOperationRecord((EpsgOperationType)1, 8631, 4197, 4326, 5.0d, "", "", 8.92d, 9.87d, 12.9d, 14.19d, 11813, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8632, 4198, 4326, 5.0d, "", "", 11.7d, 12.77d, 14.17d, 15.09d, 11823, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8633, 4310, 4326, 25.0d, "", "", 10.64d, 16.7d, -20.22d, -11.36d, 11833, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8634, 4213, 4326, 15.0d, "", "", 12.8d, 16.7d, 7.81d, 14.9d, 11843, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8636, 4816, 4326, 0.0d, "", "", 30.23d, 37.4d, 7.49d, 11.59d, 11853, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8638, 4804, 4326, 0.0d, "", "", -6.54d, -1.88d, 118.71d, 120.78d, 11857, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8639, 4817, 4326, 0.0d, "", "", 57.9d, 71.24d, 4.39d, 31.32d, 11861, 8), + new EpsgOperationRecord((EpsgOperationType)1, 8641, 4820, 4326, 0.0d, "", "", -4.24d, 4.29d, 114.55d, 119.06d, 11869, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8642, 4818, 4326, 0.0d, "", "", 48.58d, 51.06d, 12.09d, 18.86d, 11873, 8), + new EpsgOperationRecord((EpsgOperationType)1, 8643, 4120, 4326, 5.0d, "", "", 34.88d, 41.75d, 19.57d, 28.3d, 11881, 5), + new EpsgOperationRecord((EpsgOperationType)1, 8644, 4815, 4326, 0.0d, "", "", 34.88d, 41.75d, 19.57d, 28.3d, 11886, 6), + new EpsgOperationRecord((EpsgOperationType)1, 8647, 4267, 4326, 1.5d, "", "NTv2_0.gsb", 40.0d, 64.21d, -67.75d, -43.99d, 11892, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8648, 4904, 4326, 0.0d, "", "", 36.95d, 42.16d, -9.56d, -6.19d, 11902, 4), + new EpsgOperationRecord((EpsgOperationType)1, 8649, 4904, 4326, 0.0d, "", "", 36.95d, 42.16d, -9.56d, -6.19d, 11906, 8), + new EpsgOperationRecord((EpsgOperationType)1, 8650, 4281, 4326, 1.5d, "", "", 29.45d, 33.28d, 34.17d, 35.69d, 11914, 6), + new EpsgOperationRecord((EpsgOperationType)1, 8651, 4676, 4326, 2.0d, "", "", 13.92d, 22.5d, 100.09d, 107.64d, 11920, 6), + new EpsgOperationRecord((EpsgOperationType)1, 8652, 4677, 4326, 0.15d, "", "", 13.92d, 22.5d, 100.09d, 107.64d, 11926, 6), + new EpsgOperationRecord((EpsgOperationType)1, 8653, 4230, 4326, 1.0d, "", "", 51.03d, 62.0d, -5.05d, 10.86d, 11932, 40), + new EpsgOperationRecord((EpsgOperationType)1, 8654, 4230, 4258, 1.0d, "", "", 51.03d, 62.0d, -5.05d, 10.86d, 11972, 43), + new EpsgOperationRecord((EpsgOperationType)1, 8655, 4193, 4326, 5.0d, "", "", 2.16d, 4.99d, 8.45d, 10.4d, 12015, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8656, 4705, 4326, 10.0d, "", "", -6.04d, -5.05d, 10.53d, 12.37d, 12025, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8657, 4706, 4326, 5.0d, "", "", 27.19d, 30.01d, 32.34d, 34.27d, 12035, 10), + new EpsgOperationRecord((EpsgOperationType)1, 8659, 4751, 4326, 0.0d, "", "", 1.13d, 6.72d, 99.59d, 104.6d, 12045, 6), + new EpsgOperationRecord((EpsgOperationType)1, 9091, 4918, 4959, 0.01d, "", "nzgd2000_deformation_20000101_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12051, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9092, 4919, 4959, 0.01d, "", "nzgd2000_deformation_20000101_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12066, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9093, 4896, 4959, 0.01d, "", "nzgd2000_deformation_20000101_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12081, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9094, 5332, 4959, 0.01d, "", "nzgd2000_deformation_20000101_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12096, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9095, 5332, 4959, 0.01d, "", "nzgd2000_deformation_20130801_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12111, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9096, 5332, 4959, 0.01d, "", "nzgd2000_deformation_20140201_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12126, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9097, 5332, 4959, 0.01d, "", "nzgd2000_deformation_20150101_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12141, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9098, 5332, 4959, 0.01d, "", "nzgd2000_deformation_20160701_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12156, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9099, 5332, 4959, 0.01d, "", "nzgd2000_deformation_20171201_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12171, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9100, 7789, 4959, 0.01d, "", "nzgd2000_deformation_20160701_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12186, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9101, 7789, 4959, 0.01d, "", "nzgd2000_deformation_20171201_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12201, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9102, 7789, 4959, 0.01d, "", "nzgd2000_deformation_20180701_full.zip", -47.65d, -33.89d, 165.87d, 179.27d, 12216, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9103, 4267, 7789, 0.15d, "", "conus.las", 23.82d, 30.25d, -97.22d, -81.17d, 12231, 33), + new EpsgOperationRecord((EpsgOperationType)1, 9104, 4267, 7789, 0.15d, "", "nadcon5.nad27.nad83_1986.conus.lat.trn.20160901.b", 23.82d, 30.25d, -97.22d, -81.17d, 12264, 30), + new EpsgOperationRecord((EpsgOperationType)1, 9336, 4267, 8246, 1.5d, "", "NTv2_0.gsb", 48.99d, 60.0d, -120.0d, -109.98d, 12294, 0), + new EpsgOperationRecord((EpsgOperationType)1, 9337, 4807, 4171, 0.0d, "", "gr3df97a.txt", 41.31d, 51.14d, -4.87d, 9.63d, 12294, 3), + new EpsgOperationRecord((EpsgOperationType)1, 9499, 11056, 5778, 0.05d, "", "GEOID_GRS80_Oesterreich.csv", 46.4d, 49.02d, 9.53d, 17.17d, 12297, 2), + new EpsgOperationRecord((EpsgOperationType)1, 9683, 9000, 4283, 0.03d, "", "GDA94_GDA2020_conformal_and_distortion.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 12299, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9685, 9309, 4283, 0.03d, "", "GDA94_GDA2020_conformal_and_distortion.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 12314, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9687, 4283, 9057, 0.05d, "", "GDA94_GDA2020_conformal_and_distortion.gsb", -43.7d, -9.86d, 112.85d, 153.69d, 12329, 15), + new EpsgOperationRecord((EpsgOperationType)1, 9750, 6705, 9721, 0.035d, "", "geo_igm_mar06.grd", 36.59d, 38.35d, 12.36d, 15.71d, 12344, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10392, 5780, 10349, 0.0d, "", "", 36.9d, 41.88d, -9.57d, -7.39d, 12345, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10393, 5780, 10349, 0.0d, "", "", 38.58d, 38.96d, -9.46d, -8.92d, 12346, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10394, 6178, 10349, 0.0d, "", "", 32.35d, 32.93d, -17.33d, -16.4d, 12347, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10395, 6179, 10349, 0.0d, "", "", 32.97d, 33.16d, -16.46d, -16.23d, 12348, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10396, 6185, 10349, 0.0d, "", "", 39.32d, 39.78d, -31.34d, -31.01d, 12349, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10397, 6181, 10349, 0.0d, "", "", 38.46d, 38.7d, -28.91d, -28.53d, 12350, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10398, 6182, 10349, 0.0d, "", "", 38.33d, 38.62d, -28.61d, -27.96d, 12351, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10399, 6180, 10349, 0.0d, "", "", 38.48d, 38.81d, -28.39d, -27.68d, 12352, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10400, 6183, 10349, 0.0d, "", "", 38.95d, 39.15d, -28.14d, -27.88d, 12353, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10409, 6184, 10349, 0.0d, "", "", 38.58d, 38.86d, -27.45d, -26.97d, 12354, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10410, 6187, 10349, 0.0d, "", "", 37.65d, 37.97d, -25.92d, -25.07d, 12355, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10411, 6186, 10349, 0.0d, "", "", 36.87d, 37.33d, -25.25d, -24.71d, 12356, 1), + new EpsgOperationRecord((EpsgOperationType)1, 10495, 10486, 10487, 0.06d, "", "dvr90_2002.tif", 54.5d, 57.81d, 7.98d, 15.28d, 12357, 2), + new EpsgOperationRecord((EpsgOperationType)1, 10496, 10487, 10488, 0.03d, "", "dvr90_2013.tif", 54.36d, 58.27d, 3.24d, 16.51d, 12359, 2), + new EpsgOperationRecord((EpsgOperationType)1, 10616, 9529, 20043, 0.0d, "", "INAGEOID2020v1.gtx", -13.95d, 7.79d, 92.01d, 141.46d, 12361, 2), + new EpsgOperationRecord((EpsgOperationType)1, 10675, 10638, 10642, 0.05d, "", "", 17.56d, 17.71d, -63.31d, -63.16d, 12363, 8), + new EpsgOperationRecord((EpsgOperationType)1, 10754, 10738, 10740, 0.05d, "", "", 17.41d, 17.58d, -63.05d, -62.88d, 12371, 8), + new EpsgOperationRecord((EpsgOperationType)1, 10755, 10638, 10643, 0.05d, "", "", 17.56d, 17.71d, -63.31d, -63.16d, 12379, 8), + new EpsgOperationRecord((EpsgOperationType)1, 10756, 10738, 10741, 0.05d, "", "", 17.41d, 17.58d, -63.05d, -62.88d, 12387, 8), + new EpsgOperationRecord((EpsgOperationType)1, 10778, 4123, 10690, 0.03d, "", "fi_nls_ykj_etrs35fin.json", 59.75d, 70.09d, 19.24d, 31.59d, 12395, 20), + new EpsgOperationRecord((EpsgOperationType)1, 10815, 7789, 10805, 0.0d, "", "NKG_RF17vel.tif", 53.89d, 71.39d, 3.24d, 31.77d, 12415, 18), + new EpsgOperationRecord((EpsgOperationType)1, 10816, 7789, 10688, 0.0d, "", "NKG_RF17vel.tif", 58.84d, 70.09d, 19.08d, 31.59d, 12433, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10817, 7789, 10873, 0.0d, "", "NKG_RF17vel.tif", 57.9d, 71.24d, 4.39d, 31.32d, 12461, 22), + new EpsgOperationRecord((EpsgOperationType)1, 10818, 7789, 4950, 0.0d, "", "NKG_RF17vel.tif", 53.89d, 56.45d, 19.02d, 26.82d, 12483, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10824, 7789, 4976, 0.0d, "", "NKG_RF17vel.tif", 54.96d, 69.07d, 10.03d, 24.17d, 12511, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10825, 7789, 4934, 0.0d, "", "NKG_RF17vel.tif", 57.52d, 60.0d, 20.37d, 28.2d, 12539, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10868, 9988, 4934, 0.001d, "", "NKG_RF17vel.tif", 57.52d, 60.0d, 20.37d, 28.2d, 12567, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10869, 9988, 10688, 0.001d, "", "NKG_RF17vel.tif", 58.84d, 70.09d, 19.08d, 31.59d, 12595, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10870, 9988, 10873, 0.001d, "", "NKG_RF17vel.tif", 57.9d, 71.24d, 4.39d, 31.32d, 12623, 22), + new EpsgOperationRecord((EpsgOperationType)1, 10871, 9988, 4950, 0.001d, "", "NKG_RF17vel.tif", 53.89d, 56.45d, 19.02d, 26.82d, 12645, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10872, 9988, 4976, 0.001d, "", "NKG_RF17vel.tif", 54.96d, 69.07d, 10.03d, 24.17d, 12673, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10894, 7789, 10890, 0.0d, "", "NKG_RF17vel.tif", 54.36d, 58.27d, 3.24d, 16.51d, 12701, 28), + new EpsgOperationRecord((EpsgOperationType)1, 10895, 9988, 10890, 0.001d, "", "NKG_RF17vel.tif", 54.36d, 58.27d, 3.24d, 16.51d, 12729, 28), + new EpsgOperationRecord((EpsgOperationType)1, 11005, 20000, 10999, 1.0d, "", "arcgp-2006-sk.bin", 74.3208d, 81.8504d, 6.49005d, 33.50985d, 12757, 2), + new EpsgOperationRecord((EpsgOperationType)1, 11066, 9988, 11007, 0.01d, "", "", 49.79d, 60.94d, -8.82d, 1.92d, 12759, 33), + new EpsgOperationRecord((EpsgOperationType)1, 11110, 9988, 11106, 0.01d, "", "", 34.91d, 42.16d, -13.87d, -6.19d, 12792, 33), + new EpsgOperationRecord((EpsgOperationType)1, 11140, 9988, 11129, 0.01d, "", "", 35.26d, 46.26d, -13.86d, 6.3d, 12825, 33), + new EpsgOperationRecord((EpsgOperationType)1, 11194, 9988, 4888, 0.01d, "", "", 41.62d, 46.54d, 13.0d, 19.43d, 12858, 33), + new EpsgOperationRecord((EpsgOperationType)1, 11196, 9988, 9138, 0.01d, "", "", 41.85d, 43.25d, 19.97d, 21.8d, 12891, 33), + new EpsgOperationRecord((EpsgOperationType)1, 11230, 4897, 11029, 0.2d, "", "v3_dm_grd01_xyz.dat", -13.95d, 7.79d, 92.01d, 141.46d, 12924, 25), + new EpsgOperationRecord((EpsgOperationType)1, 11285, 9988, 4000, 0.01d, "", "", 45.44d, 48.47d, 26.63d, 30.13d, 12949, 33), + new EpsgOperationRecord((EpsgOperationType)1, 11310, 9988, 11222, 0.01d, "", "", 45.81d, 47.81d, 5.95d, 10.5d, 12982, 33), + new EpsgOperationRecord((EpsgOperationType)1, 11315, 9988, 10303, 0.001d, "", "NKG_RF17vel.tif", 55.67d, 58.09d, 19.06d, 28.24d, 13015, 28), + new EpsgOperationRecord((EpsgOperationType)1, 11398, 5941, 11394, 0.02d, "", "HREF2018B_NN2000_EUREF89.bin", 57.9d, 71.24d, 4.39d, 31.32d, 13043, 2), + }; + + internal static readonly EpsgOperationParameterRecord[] OperationParameters = new EpsgOperationParameterRecord[] + { + new EpsgOperationParameterRecord(1024, "X-axis translation", 601.705d), + new EpsgOperationParameterRecord(1024, "Y-axis translation", 84.263d), + new EpsgOperationParameterRecord(1024, "Z-axis translation", 485.227d), + new EpsgOperationParameterRecord(1024, "X-axis rotation", -4.7354d), + new EpsgOperationParameterRecord(1024, "Y-axis rotation", -1.3145d), + new EpsgOperationParameterRecord(1024, "Z-axis rotation", -5.393d), + new EpsgOperationParameterRecord(1024, "Scale difference", -2.3887d), + new EpsgOperationParameterRecord(1026, "A0", 8.4386918d), + new EpsgOperationParameterRecord(1026, "A1", -0.0972d), + new EpsgOperationParameterRecord(1026, "A2", -0.03672d), + new EpsgOperationParameterRecord(1026, "A3", 4.06e-05d), + new EpsgOperationParameterRecord(1026, "B00", -13276.58d), + new EpsgOperationParameterRecord(1026, "B0", 2.6620443d), + new EpsgOperationParameterRecord(1026, "B1", 0.07992d), + new EpsgOperationParameterRecord(1026, "B2", -0.0036d), + new EpsgOperationParameterRecord(1026, "B3", -1.09e-05d), + new EpsgOperationParameterRecord(1027, "A0", 11.328779d), + new EpsgOperationParameterRecord(1027, "A1", -0.1674d), + new EpsgOperationParameterRecord(1027, "A2", -0.03852d), + new EpsgOperationParameterRecord(1027, "A3", 3.79e-05d), + new EpsgOperationParameterRecord(1027, "B00", -13276.58d), + new EpsgOperationParameterRecord(1027, "B0", 2.5079425d), + new EpsgOperationParameterRecord(1027, "B1", 0.08352d), + new EpsgOperationParameterRecord(1027, "B2", -0.00864d), + new EpsgOperationParameterRecord(1027, "B3", -3.8e-06d), + new EpsgOperationParameterRecord(1028, "A0", 6.2280987d), + new EpsgOperationParameterRecord(1028, "A1", -0.03924d), + new EpsgOperationParameterRecord(1028, "A2", -0.03276d), + new EpsgOperationParameterRecord(1028, "A3", 2.84e-05d), + new EpsgOperationParameterRecord(1028, "B00", -13276.58d), + new EpsgOperationParameterRecord(1028, "B0", 2.9368989d), + new EpsgOperationParameterRecord(1028, "B1", 0.07272d), + new EpsgOperationParameterRecord(1028, "B2", 0.00216d), + new EpsgOperationParameterRecord(1028, "B3", -1.79e-05d), + new EpsgOperationParameterRecord(1035, "Latitude of natural origin", -90.0d), + new EpsgOperationParameterRecord(1035, "Longitude of natural origin", -69.0d), + new EpsgOperationParameterRecord(1035, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(1035, "False easting", 2500000.0d), + new EpsgOperationParameterRecord(1035, "False northing", 0.0d), + new EpsgOperationParameterRecord(1035, "Ordinate 1 of evaluation point in target CRS", 2610200.48d), + new EpsgOperationParameterRecord(1035, "Ordinate 2 of evaluation point in target CRS", 4905282.73d), + new EpsgOperationParameterRecord(1035, "Scale factor for source CRS axes", 1.0d), + new EpsgOperationParameterRecord(1035, "Rotation angle of source CRS axes", 271.091666666667d), + new EpsgOperationParameterRecord(1041, "Ordinate 1 of evaluation point in source CRS", 53.5d), + new EpsgOperationParameterRecord(1041, "Ordinate 2 of evaluation point in source CRS", -7.7d), + new EpsgOperationParameterRecord(1041, "Ordinate 1 of evaluation point in target CRS", 53.5d), + new EpsgOperationParameterRecord(1041, "Ordinate 2 of evaluation point in target CRS", -7.7d), + new EpsgOperationParameterRecord(1041, "Scaling factor for source CRS coord differences", 0.1d), + new EpsgOperationParameterRecord(1041, "Scaling factor for target CRS coord differences", 3600.0d), + new EpsgOperationParameterRecord(1041, "A0", 0.763d), + new EpsgOperationParameterRecord(1041, "Au1v0", -4.487d), + new EpsgOperationParameterRecord(1041, "Au0v1", 0.123d), + new EpsgOperationParameterRecord(1041, "Au2v0", 0.215d), + new EpsgOperationParameterRecord(1041, "Au1v1", -0.515d), + new EpsgOperationParameterRecord(1041, "Au0v2", 0.183d), + new EpsgOperationParameterRecord(1041, "Au3v0", -0.265d), + new EpsgOperationParameterRecord(1041, "Au2v1", -0.57d), + new EpsgOperationParameterRecord(1041, "Au1v2", 0.414d), + new EpsgOperationParameterRecord(1041, "Au0v3", -0.374d), + new EpsgOperationParameterRecord(1041, "Au3v1", 2.852d), + new EpsgOperationParameterRecord(1041, "Au2v2", 5.703d), + new EpsgOperationParameterRecord(1041, "Au1v3", 13.11d), + new EpsgOperationParameterRecord(1041, "Au3v2", -61.678d), + new EpsgOperationParameterRecord(1041, "Au2v3", 113.743d), + new EpsgOperationParameterRecord(1041, "Au3v3", -265.898d), + new EpsgOperationParameterRecord(1041, "B0", -2.81d), + new EpsgOperationParameterRecord(1041, "Bu1v0", -0.341d), + new EpsgOperationParameterRecord(1041, "Bu0v1", -4.68d), + new EpsgOperationParameterRecord(1041, "Bu2v0", 1.196d), + new EpsgOperationParameterRecord(1041, "Bu1v1", -0.119d), + new EpsgOperationParameterRecord(1041, "Bu0v2", 0.17d), + new EpsgOperationParameterRecord(1041, "Bu3v0", -0.887d), + new EpsgOperationParameterRecord(1041, "Bu2v1", 4.877d), + new EpsgOperationParameterRecord(1041, "Bu1v2", 3.913d), + new EpsgOperationParameterRecord(1041, "Bu0v3", 2.163d), + new EpsgOperationParameterRecord(1041, "Bu3v1", -46.666d), + new EpsgOperationParameterRecord(1041, "Bu2v2", -27.795d), + new EpsgOperationParameterRecord(1041, "Bu1v3", 18.867d), + new EpsgOperationParameterRecord(1041, "Bu3v2", -95.377d), + new EpsgOperationParameterRecord(1041, "Bu2v3", -284.294d), + new EpsgOperationParameterRecord(1041, "Bu3v3", -853.95d), + new EpsgOperationParameterRecord(1042, "Ordinate 1 of evaluation point in source CRS", 53.5d), + new EpsgOperationParameterRecord(1042, "Ordinate 2 of evaluation point in source CRS", -7.7d), + new EpsgOperationParameterRecord(1042, "Ordinate 1 of evaluation point in target CRS", 53.5d), + new EpsgOperationParameterRecord(1042, "Ordinate 2 of evaluation point in target CRS", -7.7d), + new EpsgOperationParameterRecord(1042, "Scaling factor for source CRS coord differences", 0.1d), + new EpsgOperationParameterRecord(1042, "Scaling factor for target CRS coord differences", 3600.0d), + new EpsgOperationParameterRecord(1042, "A0", 0.763d), + new EpsgOperationParameterRecord(1042, "Au1v0", -4.487d), + new EpsgOperationParameterRecord(1042, "Au0v1", 0.123d), + new EpsgOperationParameterRecord(1042, "Au2v0", 0.215d), + new EpsgOperationParameterRecord(1042, "Au1v1", -0.515d), + new EpsgOperationParameterRecord(1042, "Au0v2", 0.183d), + new EpsgOperationParameterRecord(1042, "Au3v0", -0.265d), + new EpsgOperationParameterRecord(1042, "Au2v1", -0.57d), + new EpsgOperationParameterRecord(1042, "Au1v2", 0.414d), + new EpsgOperationParameterRecord(1042, "Au0v3", -0.374d), + new EpsgOperationParameterRecord(1042, "Au3v1", 2.852d), + new EpsgOperationParameterRecord(1042, "Au2v2", 5.703d), + new EpsgOperationParameterRecord(1042, "Au1v3", 13.11d), + new EpsgOperationParameterRecord(1042, "Au3v2", -61.678d), + new EpsgOperationParameterRecord(1042, "Au2v3", 113.743d), + new EpsgOperationParameterRecord(1042, "Au3v3", -265.898d), + new EpsgOperationParameterRecord(1042, "B0", -2.81d), + new EpsgOperationParameterRecord(1042, "Bu1v0", -0.341d), + new EpsgOperationParameterRecord(1042, "Bu0v1", -4.68d), + new EpsgOperationParameterRecord(1042, "Bu2v0", 1.196d), + new EpsgOperationParameterRecord(1042, "Bu1v1", -0.119d), + new EpsgOperationParameterRecord(1042, "Bu0v2", 0.17d), + new EpsgOperationParameterRecord(1042, "Bu3v0", -0.887d), + new EpsgOperationParameterRecord(1042, "Bu2v1", 4.877d), + new EpsgOperationParameterRecord(1042, "Bu1v2", 3.913d), + new EpsgOperationParameterRecord(1042, "Bu0v3", 2.163d), + new EpsgOperationParameterRecord(1042, "Bu3v1", -46.666d), + new EpsgOperationParameterRecord(1042, "Bu2v2", -27.795d), + new EpsgOperationParameterRecord(1042, "Bu1v3", 18.867d), + new EpsgOperationParameterRecord(1042, "Bu3v2", -95.377d), + new EpsgOperationParameterRecord(1042, "Bu2v3", -284.294d), + new EpsgOperationParameterRecord(1042, "Bu3v3", -853.95d), + new EpsgOperationParameterRecord(1044, "Latitude of natural origin", 52.1561605555558d), + new EpsgOperationParameterRecord(1044, "Longitude of natural origin", 5.38763888888917d), + new EpsgOperationParameterRecord(1044, "Scale factor at natural origin", 0.9999079d), + new EpsgOperationParameterRecord(1044, "False easting", 155000.0d), + new EpsgOperationParameterRecord(1044, "False northing", 463000.0d), + new EpsgOperationParameterRecord(1044, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(1044, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(1044, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(1044, "False easting", 500000.0d), + new EpsgOperationParameterRecord(1044, "False northing", 0.0d), + new EpsgOperationParameterRecord(1044, "Ordinate 1 of evaluation point in source CRS", 155000.0d), + new EpsgOperationParameterRecord(1044, "Ordinate 2 of evaluation point in source CRS", 463000.0d), + new EpsgOperationParameterRecord(1044, "Ordinate 1 of evaluation point in target CRS", 663395.607d), + new EpsgOperationParameterRecord(1044, "Ordinate 2 of evaluation point in target CRS", 5781194.38d), + new EpsgOperationParameterRecord(1044, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(1044, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(1044, "A1", -51.681d), + new EpsgOperationParameterRecord(1044, "A2", 3290.525d), + new EpsgOperationParameterRecord(1044, "A3", 20.172d), + new EpsgOperationParameterRecord(1044, "A4", 1.133d), + new EpsgOperationParameterRecord(1044, "A5", 2.075d), + new EpsgOperationParameterRecord(1044, "A6", 0.251d), + new EpsgOperationParameterRecord(1044, "A7", 0.075d), + new EpsgOperationParameterRecord(1044, "A8", -0.012d), + new EpsgOperationParameterRecord(1046, "Latitude of natural origin", 52.1561605555558d), + new EpsgOperationParameterRecord(1046, "Longitude of natural origin", 5.38763888888917d), + new EpsgOperationParameterRecord(1046, "Scale factor at natural origin", 0.9999079d), + new EpsgOperationParameterRecord(1046, "False easting", 155000.0d), + new EpsgOperationParameterRecord(1046, "False northing", 463000.0d), + new EpsgOperationParameterRecord(1046, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(1046, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(1046, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(1046, "False easting", 500000.0d), + new EpsgOperationParameterRecord(1046, "False northing", 0.0d), + new EpsgOperationParameterRecord(1046, "Ordinate 1 of evaluation point in source CRS", 155000.0d), + new EpsgOperationParameterRecord(1046, "Ordinate 2 of evaluation point in source CRS", 463000.0d), + new EpsgOperationParameterRecord(1046, "Ordinate 1 of evaluation point in target CRS", 663395.563d), + new EpsgOperationParameterRecord(1046, "Ordinate 2 of evaluation point in target CRS", 5781194.442d), + new EpsgOperationParameterRecord(1046, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(1046, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(1046, "A1", -51.718d), + new EpsgOperationParameterRecord(1046, "A2", 3290.521d), + new EpsgOperationParameterRecord(1046, "A3", 20.154d), + new EpsgOperationParameterRecord(1046, "A4", 1.152d), + new EpsgOperationParameterRecord(1046, "A5", 2.061d), + new EpsgOperationParameterRecord(1046, "A6", 0.238d), + new EpsgOperationParameterRecord(1046, "A7", 0.058d), + new EpsgOperationParameterRecord(1046, "A8", -0.013d), + new EpsgOperationParameterRecord(1048, "Latitude of false origin", 90.0d), + new EpsgOperationParameterRecord(1048, "Longitude of false origin", 4.3569397222225d), + new EpsgOperationParameterRecord(1048, "Latitude of 1st standard parallel", 49.8333333333336d), + new EpsgOperationParameterRecord(1048, "Latitude of 2nd standard parallel", 51.1666666666669d), + new EpsgOperationParameterRecord(1048, "Easting at false origin", 150000.01256d), + new EpsgOperationParameterRecord(1048, "Northing at false origin", 5400088.4378d), + new EpsgOperationParameterRecord(1048, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(1048, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(1048, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(1048, "False easting", 500000.0d), + new EpsgOperationParameterRecord(1048, "False northing", 0.0d), + new EpsgOperationParameterRecord(1048, "Ordinate 1 of evaluation point in source CRS", 0.0d), + new EpsgOperationParameterRecord(1048, "Ordinate 2 of evaluation point in source CRS", 0.0d), + new EpsgOperationParameterRecord(1048, "Ordinate 1 of evaluation point in target CRS", 449681.702d), + new EpsgOperationParameterRecord(1048, "Ordinate 2 of evaluation point in target CRS", 5460505.326d), + new EpsgOperationParameterRecord(1048, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(1048, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(1048, "A1", -71.3747d), + new EpsgOperationParameterRecord(1048, "A2", 1858.8407d), + new EpsgOperationParameterRecord(1048, "A3", -5.4504d), + new EpsgOperationParameterRecord(1048, "A4", -16.9681d), + new EpsgOperationParameterRecord(1048, "A5", 4.0783d), + new EpsgOperationParameterRecord(1048, "A6", 0.2193d), + new EpsgOperationParameterRecord(1050, "Latitude of natural origin", 52.1561605555558d), + new EpsgOperationParameterRecord(1050, "Longitude of natural origin", 5.38763888888917d), + new EpsgOperationParameterRecord(1050, "Scale factor at natural origin", 0.9999079d), + new EpsgOperationParameterRecord(1050, "False easting", 155000.0d), + new EpsgOperationParameterRecord(1050, "False northing", 463000.0d), + new EpsgOperationParameterRecord(1050, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(1050, "Longitude of natural origin", 5.0d), + new EpsgOperationParameterRecord(1050, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(1050, "False easting", 500000.0d), + new EpsgOperationParameterRecord(1050, "False northing", 0.0d), + new EpsgOperationParameterRecord(1050, "Ordinate 1 of evaluation point in source CRS", 155000.0d), + new EpsgOperationParameterRecord(1050, "Ordinate 2 of evaluation point in source CRS", 463000.0d), + new EpsgOperationParameterRecord(1050, "Ordinate 1 of evaluation point in target CRS", 526577.124d), + new EpsgOperationParameterRecord(1050, "Ordinate 2 of evaluation point in target CRS", 5778575.474d), + new EpsgOperationParameterRecord(1050, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(1050, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(1050, "A1", 99969.1014d), + new EpsgOperationParameterRecord(1050, "A2", 533.6385d), + new EpsgOperationParameterRecord(1050, "A3", 3.3943d), + new EpsgOperationParameterRecord(1050, "A4", -0.1391d), + new EpsgOperationParameterRecord(1050, "A5", 2.0658d), + new EpsgOperationParameterRecord(1050, "A6", 0.0677d), + new EpsgOperationParameterRecord(1050, "A7", 0.0561d), + new EpsgOperationParameterRecord(1050, "A8", -0.0148d), + new EpsgOperationParameterRecord(1052, "Ordinate 1 of evaluation point", 52.0d), + new EpsgOperationParameterRecord(1052, "Ordinate 2 of evaluation point", 10.0d), + new EpsgOperationParameterRecord(1052, "Scaling factor for coord differences", 0.05235988d), + new EpsgOperationParameterRecord(1052, "A0", -2.65261d), + new EpsgOperationParameterRecord(1052, "Au1v0", 2.06392d), + new EpsgOperationParameterRecord(1052, "Au0v1", 0.77921d), + new EpsgOperationParameterRecord(1052, "Au2v0", 0.26743d), + new EpsgOperationParameterRecord(1052, "Au1v1", 0.10706d), + new EpsgOperationParameterRecord(1052, "Au3v0", 0.76407d), + new EpsgOperationParameterRecord(1052, "Au2v1", -0.9543d), + new EpsgOperationParameterRecord(1052, "Au4v0", 0.17197d), + new EpsgOperationParameterRecord(1052, "Au4v1", 1.04974d), + new EpsgOperationParameterRecord(1052, "Au5v2", -0.22899d), + new EpsgOperationParameterRecord(1052, "Au0v8", -0.05401d), + new EpsgOperationParameterRecord(1052, "Au9v0", -0.78909d), + new EpsgOperationParameterRecord(1052, "Au2v7", -0.10572d), + new EpsgOperationParameterRecord(1052, "Au1v9", 0.05283d), + new EpsgOperationParameterRecord(1052, "Au3v9", 0.02445d), + new EpsgOperationParameterRecord(1052, "B0", -4.13447d), + new EpsgOperationParameterRecord(1052, "Bu1v0", -1.50572d), + new EpsgOperationParameterRecord(1052, "Bu0v1", 1.94075d), + new EpsgOperationParameterRecord(1052, "Bu2v0", -1.376d), + new EpsgOperationParameterRecord(1052, "Bu1v1", 1.98425d), + new EpsgOperationParameterRecord(1052, "Bu0v2", 0.30068d), + new EpsgOperationParameterRecord(1052, "Bu3v0", -2.31939d), + new EpsgOperationParameterRecord(1052, "Bu4v0", -1.70401d), + new EpsgOperationParameterRecord(1052, "Bu1v3", -5.48711d), + new EpsgOperationParameterRecord(1052, "Bu5v0", 7.41956d), + new EpsgOperationParameterRecord(1052, "Bu2v3", -1.61351d), + new EpsgOperationParameterRecord(1052, "Bu1v4", 5.92933d), + new EpsgOperationParameterRecord(1052, "Bu0v5", -1.97974d), + new EpsgOperationParameterRecord(1052, "Bu6v0", 1.57701d), + new EpsgOperationParameterRecord(1052, "Bu3v3", -6.52522d), + new EpsgOperationParameterRecord(1052, "Bu2v4", 16.85976d), + new EpsgOperationParameterRecord(1052, "Bu1v5", -1.79701d), + new EpsgOperationParameterRecord(1052, "Bu7v0", -3.08344d), + new EpsgOperationParameterRecord(1052, "Bu6v1", -14.32516d), + new EpsgOperationParameterRecord(1052, "Bu4v4", 4.49096d), + new EpsgOperationParameterRecord(1052, "Bu8v1", 9.9875d), + new EpsgOperationParameterRecord(1052, "Bu7v2", 7.80215d), + new EpsgOperationParameterRecord(1052, "Bu2v7", -2.26917d), + new EpsgOperationParameterRecord(1052, "Bu0v9", 0.16438d), + new EpsgOperationParameterRecord(1052, "Bu4v6", -17.45428d), + new EpsgOperationParameterRecord(1052, "Bu9v2", -8.25844d), + new EpsgOperationParameterRecord(1052, "Bu8v3", 5.28734d), + new EpsgOperationParameterRecord(1052, "Bu5v7", 8.87141d), + new EpsgOperationParameterRecord(1052, "Bu9v4", -3.48015d), + new EpsgOperationParameterRecord(1052, "Bu4v9", 0.71041d), + new EpsgOperationParameterRecord(1055, "X-axis translation", -145.7d), + new EpsgOperationParameterRecord(1055, "Y-axis translation", -249.1d), + new EpsgOperationParameterRecord(1055, "Z-axis translation", 1.5d), + new EpsgOperationParameterRecord(1056, "X-axis translation", -85.645d), + new EpsgOperationParameterRecord(1056, "Y-axis translation", -273.077d), + new EpsgOperationParameterRecord(1056, "Z-axis translation", -79.708d), + new EpsgOperationParameterRecord(1056, "X-axis rotation", -2.289d), + new EpsgOperationParameterRecord(1056, "Y-axis rotation", 1.421d), + new EpsgOperationParameterRecord(1056, "Z-axis rotation", -2.532d), + new EpsgOperationParameterRecord(1056, "Scale difference", 3.194d), + new EpsgOperationParameterRecord(1057, "X-axis translation", -202.234d), + new EpsgOperationParameterRecord(1057, "Y-axis translation", -168.351d), + new EpsgOperationParameterRecord(1057, "Z-axis translation", -63.51d), + new EpsgOperationParameterRecord(1057, "X-axis rotation", -3.545d), + new EpsgOperationParameterRecord(1057, "Y-axis rotation", -0.659d), + new EpsgOperationParameterRecord(1057, "Z-axis rotation", 1.945d), + new EpsgOperationParameterRecord(1057, "Scale difference", 2.1d), + new EpsgOperationParameterRecord(1058, "X-axis translation", -18.944d), + new EpsgOperationParameterRecord(1058, "Y-axis translation", -379.364d), + new EpsgOperationParameterRecord(1058, "Z-axis translation", -24.063d), + new EpsgOperationParameterRecord(1058, "X-axis rotation", -0.04d), + new EpsgOperationParameterRecord(1058, "Y-axis rotation", 0.764d), + new EpsgOperationParameterRecord(1058, "Z-axis rotation", -6.431d), + new EpsgOperationParameterRecord(1058, "Scale difference", 3.657d), + new EpsgOperationParameterRecord(1059, "X-axis translation", -294.7d), + new EpsgOperationParameterRecord(1059, "Y-axis translation", -200.1d), + new EpsgOperationParameterRecord(1059, "Z-axis translation", 525.5d), + new EpsgOperationParameterRecord(1060, "X-axis translation", -3.2d), + new EpsgOperationParameterRecord(1060, "Y-axis translation", -5.7d), + new EpsgOperationParameterRecord(1060, "Z-axis translation", 2.8d), + new EpsgOperationParameterRecord(1061, "X-axis translation", -20.8d), + new EpsgOperationParameterRecord(1061, "Y-axis translation", 11.3d), + new EpsgOperationParameterRecord(1061, "Z-axis translation", 2.4d), + new EpsgOperationParameterRecord(1062, "X-axis translation", 226.702d), + new EpsgOperationParameterRecord(1062, "Y-axis translation", -193.337d), + new EpsgOperationParameterRecord(1062, "Z-axis translation", -35.371d), + new EpsgOperationParameterRecord(1062, "X-axis rotation", 2.229d), + new EpsgOperationParameterRecord(1062, "Y-axis rotation", 4.391d), + new EpsgOperationParameterRecord(1062, "Z-axis rotation", -9.238d), + new EpsgOperationParameterRecord(1062, "Scale difference", 0.9798d), + new EpsgOperationParameterRecord(1063, "X-axis translation", -2.227d), + new EpsgOperationParameterRecord(1063, "Y-axis translation", 6.524d), + new EpsgOperationParameterRecord(1063, "Z-axis translation", 2.178d), + new EpsgOperationParameterRecord(1064, "X-axis translation", -0.652d), + new EpsgOperationParameterRecord(1064, "Y-axis translation", 1.619d), + new EpsgOperationParameterRecord(1064, "Z-axis translation", 0.213d), + new EpsgOperationParameterRecord(1065, "X-axis translation", 44.585d), + new EpsgOperationParameterRecord(1065, "Y-axis translation", -131.212d), + new EpsgOperationParameterRecord(1065, "Z-axis translation", -39.544d), + new EpsgOperationParameterRecord(1066, "X-axis translation", 593.032d), + new EpsgOperationParameterRecord(1066, "Y-axis translation", 26.0d), + new EpsgOperationParameterRecord(1066, "Z-axis translation", 478.741d), + new EpsgOperationParameterRecord(1066, "X-axis rotation", 1.9848d), + new EpsgOperationParameterRecord(1066, "Y-axis rotation", -1.7439d), + new EpsgOperationParameterRecord(1066, "Z-axis rotation", 9.0587d), + new EpsgOperationParameterRecord(1066, "Scale difference", 4.0772d), + new EpsgOperationParameterRecord(1066, "Ordinate 1 of evaluation point", 3903453.148d), + new EpsgOperationParameterRecord(1066, "Ordinate 2 of evaluation point", 368135.313d), + new EpsgOperationParameterRecord(1066, "Ordinate 3 of evaluation point", 5012970.306d), + new EpsgOperationParameterRecord(1067, "X-axis translation", -92.1d), + new EpsgOperationParameterRecord(1067, "Y-axis translation", -89.9d), + new EpsgOperationParameterRecord(1067, "Z-axis translation", 114.9d), + new EpsgOperationParameterRecord(1070, "X-axis translation", -100.0d), + new EpsgOperationParameterRecord(1070, "Y-axis translation", -248.0d), + new EpsgOperationParameterRecord(1070, "Z-axis translation", 259.0d), + new EpsgOperationParameterRecord(1071, "X-axis translation", -181.0d), + new EpsgOperationParameterRecord(1071, "Y-axis translation", -122.0d), + new EpsgOperationParameterRecord(1071, "Z-axis translation", 225.0d), + new EpsgOperationParameterRecord(1072, "Latitude of natural origin", 31.7340969444447d), + new EpsgOperationParameterRecord(1072, "Longitude of natural origin", 35.2120805555558d), + new EpsgOperationParameterRecord(1072, "False easting", 170251.555d), + new EpsgOperationParameterRecord(1072, "False northing", 1126867.909d), + new EpsgOperationParameterRecord(1072, "Latitude of natural origin", 31.7343936111114d), + new EpsgOperationParameterRecord(1072, "Longitude of natural origin", 35.2045169444447d), + new EpsgOperationParameterRecord(1072, "Scale factor at natural origin", 1.0000067d), + new EpsgOperationParameterRecord(1072, "False easting", 219529.584d), + new EpsgOperationParameterRecord(1072, "False northing", 626907.39d), + new EpsgOperationParameterRecord(1072, "Easting offset", 50000.0d), + new EpsgOperationParameterRecord(1072, "Northing offset", -500000.0d), + new EpsgOperationParameterRecord(1073, "X-axis translation", -48.0d), + new EpsgOperationParameterRecord(1073, "Y-axis translation", 55.0d), + new EpsgOperationParameterRecord(1073, "Z-axis translation", 52.0d), + new EpsgOperationParameterRecord(1074, "X-axis translation", -275.7224d), + new EpsgOperationParameterRecord(1074, "Y-axis translation", 94.7824d), + new EpsgOperationParameterRecord(1074, "Z-axis translation", 340.8944d), + new EpsgOperationParameterRecord(1074, "X-axis rotation", -8.001d), + new EpsgOperationParameterRecord(1074, "Y-axis rotation", -4.42d), + new EpsgOperationParameterRecord(1074, "Z-axis rotation", -11.821d), + new EpsgOperationParameterRecord(1074, "Scale difference", 1.0d), + new EpsgOperationParameterRecord(1075, "X-axis translation", -89.05d), + new EpsgOperationParameterRecord(1075, "Y-axis translation", -87.03d), + new EpsgOperationParameterRecord(1075, "Z-axis translation", -124.56d), + new EpsgOperationParameterRecord(1078, "X-axis translation", -265.983d), + new EpsgOperationParameterRecord(1078, "Y-axis translation", 76.918d), + new EpsgOperationParameterRecord(1078, "Z-axis translation", 20.182d), + new EpsgOperationParameterRecord(1078, "X-axis rotation", 0.4099d), + new EpsgOperationParameterRecord(1078, "Y-axis rotation", 2.9332d), + new EpsgOperationParameterRecord(1078, "Z-axis rotation", -2.6881d), + new EpsgOperationParameterRecord(1078, "Scale difference", 0.43d), + new EpsgOperationParameterRecord(1078, "Ordinate 1 of evaluation point", 4098647.674d), + new EpsgOperationParameterRecord(1078, "Ordinate 2 of evaluation point", 442843.139d), + new EpsgOperationParameterRecord(1078, "Ordinate 3 of evaluation point", 4851251.093d), + new EpsgOperationParameterRecord(1079, "X-axis translation", -265.983d), + new EpsgOperationParameterRecord(1079, "Y-axis translation", 76.918d), + new EpsgOperationParameterRecord(1079, "Z-axis translation", 20.182d), + new EpsgOperationParameterRecord(1079, "X-axis rotation", 0.4099d), + new EpsgOperationParameterRecord(1079, "Y-axis rotation", 2.9332d), + new EpsgOperationParameterRecord(1079, "Z-axis rotation", -2.6881d), + new EpsgOperationParameterRecord(1079, "Scale difference", 0.43d), + new EpsgOperationParameterRecord(1079, "Ordinate 1 of evaluation point", 4098647.674d), + new EpsgOperationParameterRecord(1079, "Ordinate 2 of evaluation point", 442843.139d), + new EpsgOperationParameterRecord(1079, "Ordinate 3 of evaluation point", 4851251.093d), + new EpsgOperationParameterRecord(1080, "X-axis translation", 175.0d), + new EpsgOperationParameterRecord(1080, "Y-axis translation", -38.0d), + new EpsgOperationParameterRecord(1080, "Z-axis translation", 113.0d), + new EpsgOperationParameterRecord(1081, "X-axis translation", 174.05d), + new EpsgOperationParameterRecord(1081, "Y-axis translation", -25.49d), + new EpsgOperationParameterRecord(1081, "Z-axis translation", 112.57d), + new EpsgOperationParameterRecord(1081, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1081, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1081, "Z-axis rotation", -0.554d), + new EpsgOperationParameterRecord(1081, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(1082, "X-axis translation", 174.05d), + new EpsgOperationParameterRecord(1082, "Y-axis translation", -25.49d), + new EpsgOperationParameterRecord(1082, "Z-axis translation", 112.57d), + new EpsgOperationParameterRecord(1082, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1082, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1082, "Z-axis rotation", -0.554d), + new EpsgOperationParameterRecord(1082, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(1083, "X-axis translation", 50.0d), + new EpsgOperationParameterRecord(1083, "Y-axis translation", 212.0d), + new EpsgOperationParameterRecord(1083, "Z-axis translation", 381.0d), + new EpsgOperationParameterRecord(1084, "X-axis translation", 70.0d), + new EpsgOperationParameterRecord(1084, "Y-axis translation", 207.0d), + new EpsgOperationParameterRecord(1084, "Z-axis translation", 389.5d), + new EpsgOperationParameterRecord(1085, "X-axis translation", 65.334d), + new EpsgOperationParameterRecord(1085, "Y-axis translation", 212.46d), + new EpsgOperationParameterRecord(1085, "Z-axis translation", 387.63d), + new EpsgOperationParameterRecord(1087, "X-axis translation", -112.0d), + new EpsgOperationParameterRecord(1087, "Y-axis translation", -110.3d), + new EpsgOperationParameterRecord(1087, "Z-axis translation", -140.2d), + new EpsgOperationParameterRecord(1088, "X-axis translation", -223.7d), + new EpsgOperationParameterRecord(1088, "Y-axis translation", -67.38d), + new EpsgOperationParameterRecord(1088, "Z-axis translation", 1.34d), + new EpsgOperationParameterRecord(1089, "X-axis translation", -225.4d), + new EpsgOperationParameterRecord(1089, "Y-axis translation", -67.7d), + new EpsgOperationParameterRecord(1089, "Z-axis translation", 7.85d), + new EpsgOperationParameterRecord(1090, "X-axis translation", -227.1d), + new EpsgOperationParameterRecord(1090, "Y-axis translation", -68.1d), + new EpsgOperationParameterRecord(1090, "Z-axis translation", 14.4d), + new EpsgOperationParameterRecord(1091, "X-axis translation", -231.61d), + new EpsgOperationParameterRecord(1091, "Y-axis translation", -68.21d), + new EpsgOperationParameterRecord(1091, "Z-axis translation", 13.93d), + new EpsgOperationParameterRecord(1092, "X-axis translation", -225.06d), + new EpsgOperationParameterRecord(1092, "Y-axis translation", -67.37d), + new EpsgOperationParameterRecord(1092, "Z-axis translation", 14.61d), + new EpsgOperationParameterRecord(1093, "X-axis translation", -229.08d), + new EpsgOperationParameterRecord(1093, "Y-axis translation", -65.73d), + new EpsgOperationParameterRecord(1093, "Z-axis translation", 20.21d), + new EpsgOperationParameterRecord(1094, "X-axis translation", -230.47d), + new EpsgOperationParameterRecord(1094, "Y-axis translation", -56.08d), + new EpsgOperationParameterRecord(1094, "Z-axis translation", 22.43d), + new EpsgOperationParameterRecord(1095, "X-axis translation", -270.933d), + new EpsgOperationParameterRecord(1095, "Y-axis translation", 115.599d), + new EpsgOperationParameterRecord(1095, "Z-axis translation", -360.226d), + new EpsgOperationParameterRecord(1095, "X-axis rotation", -5.266d), + new EpsgOperationParameterRecord(1095, "Y-axis rotation", -1.238d), + new EpsgOperationParameterRecord(1095, "Z-axis rotation", 2.381d), + new EpsgOperationParameterRecord(1095, "Scale difference", -5.109d), + new EpsgOperationParameterRecord(1095, "Ordinate 1 of evaluation point", 2464351.59d), + new EpsgOperationParameterRecord(1095, "Ordinate 2 of evaluation point", -5783466.61d), + new EpsgOperationParameterRecord(1095, "Ordinate 3 of evaluation point", 974809.81d), + new EpsgOperationParameterRecord(1096, "X-axis translation", -270.933d), + new EpsgOperationParameterRecord(1096, "Y-axis translation", 115.599d), + new EpsgOperationParameterRecord(1096, "Z-axis translation", -360.226d), + new EpsgOperationParameterRecord(1096, "X-axis rotation", -5.266d), + new EpsgOperationParameterRecord(1096, "Y-axis rotation", -1.238d), + new EpsgOperationParameterRecord(1096, "Z-axis rotation", 2.381d), + new EpsgOperationParameterRecord(1096, "Scale difference", -5.109d), + new EpsgOperationParameterRecord(1096, "Ordinate 1 of evaluation point", 2464351.59d), + new EpsgOperationParameterRecord(1096, "Ordinate 2 of evaluation point", -5783466.61d), + new EpsgOperationParameterRecord(1096, "Ordinate 3 of evaluation point", 974809.81d), + new EpsgOperationParameterRecord(1099, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1099, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1099, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1100, "X-axis translation", -166.0d), + new EpsgOperationParameterRecord(1100, "Y-axis translation", -15.0d), + new EpsgOperationParameterRecord(1100, "Z-axis translation", 204.0d), + new EpsgOperationParameterRecord(1101, "X-axis translation", -118.0d), + new EpsgOperationParameterRecord(1101, "Y-axis translation", -14.0d), + new EpsgOperationParameterRecord(1101, "Z-axis translation", 218.0d), + new EpsgOperationParameterRecord(1102, "X-axis translation", -134.0d), + new EpsgOperationParameterRecord(1102, "Y-axis translation", -2.0d), + new EpsgOperationParameterRecord(1102, "Z-axis translation", 210.0d), + new EpsgOperationParameterRecord(1103, "X-axis translation", -165.0d), + new EpsgOperationParameterRecord(1103, "Y-axis translation", -11.0d), + new EpsgOperationParameterRecord(1103, "Z-axis translation", 206.0d), + new EpsgOperationParameterRecord(1104, "X-axis translation", -123.0d), + new EpsgOperationParameterRecord(1104, "Y-axis translation", -20.0d), + new EpsgOperationParameterRecord(1104, "Z-axis translation", 220.0d), + new EpsgOperationParameterRecord(1105, "X-axis translation", -128.0d), + new EpsgOperationParameterRecord(1105, "Y-axis translation", -18.0d), + new EpsgOperationParameterRecord(1105, "Z-axis translation", 224.0d), + new EpsgOperationParameterRecord(1106, "X-axis translation", -161.0d), + new EpsgOperationParameterRecord(1106, "Y-axis translation", -14.0d), + new EpsgOperationParameterRecord(1106, "Z-axis translation", 205.0d), + new EpsgOperationParameterRecord(1107, "X-axis translation", -43.0d), + new EpsgOperationParameterRecord(1107, "Y-axis translation", -163.0d), + new EpsgOperationParameterRecord(1107, "Z-axis translation", 45.0d), + new EpsgOperationParameterRecord(1108, "X-axis translation", -133.0d), + new EpsgOperationParameterRecord(1108, "Y-axis translation", -48.0d), + new EpsgOperationParameterRecord(1108, "Z-axis translation", 148.0d), + new EpsgOperationParameterRecord(1109, "X-axis translation", -134.0d), + new EpsgOperationParameterRecord(1109, "Y-axis translation", -48.0d), + new EpsgOperationParameterRecord(1109, "Z-axis translation", 149.0d), + new EpsgOperationParameterRecord(1110, "X-axis translation", -150.0d), + new EpsgOperationParameterRecord(1110, "Y-axis translation", -250.0d), + new EpsgOperationParameterRecord(1110, "Z-axis translation", -1.0d), + new EpsgOperationParameterRecord(1111, "X-axis translation", -143.0d), + new EpsgOperationParameterRecord(1111, "Y-axis translation", -236.0d), + new EpsgOperationParameterRecord(1111, "Z-axis translation", 7.0d), + new EpsgOperationParameterRecord(1112, "X-axis translation", 593.16d), + new EpsgOperationParameterRecord(1112, "Y-axis translation", 26.15d), + new EpsgOperationParameterRecord(1112, "Z-axis translation", 478.54d), + new EpsgOperationParameterRecord(1112, "X-axis rotation", -6.3239d), + new EpsgOperationParameterRecord(1112, "Y-axis rotation", -0.5008d), + new EpsgOperationParameterRecord(1112, "Z-axis rotation", -5.5487d), + new EpsgOperationParameterRecord(1112, "Scale difference", 4.0775d), + new EpsgOperationParameterRecord(1113, "X-axis translation", -143.0d), + new EpsgOperationParameterRecord(1113, "Y-axis translation", -90.0d), + new EpsgOperationParameterRecord(1113, "Z-axis translation", -294.0d), + new EpsgOperationParameterRecord(1114, "X-axis translation", -138.0d), + new EpsgOperationParameterRecord(1114, "Y-axis translation", -105.0d), + new EpsgOperationParameterRecord(1114, "Z-axis translation", -289.0d), + new EpsgOperationParameterRecord(1116, "X-axis translation", -125.0d), + new EpsgOperationParameterRecord(1116, "Y-axis translation", -108.0d), + new EpsgOperationParameterRecord(1116, "Z-axis translation", -295.0d), + new EpsgOperationParameterRecord(1117, "X-axis translation", -161.0d), + new EpsgOperationParameterRecord(1117, "Y-axis translation", -73.0d), + new EpsgOperationParameterRecord(1117, "Z-axis translation", -317.0d), + new EpsgOperationParameterRecord(1118, "X-axis translation", -134.0d), + new EpsgOperationParameterRecord(1118, "Y-axis translation", -105.0d), + new EpsgOperationParameterRecord(1118, "Z-axis translation", -295.0d), + new EpsgOperationParameterRecord(1120, "X-axis translation", -147.0d), + new EpsgOperationParameterRecord(1120, "Y-axis translation", -74.0d), + new EpsgOperationParameterRecord(1120, "Z-axis translation", -283.0d), + new EpsgOperationParameterRecord(1121, "X-axis translation", -142.0d), + new EpsgOperationParameterRecord(1121, "Y-axis translation", -96.0d), + new EpsgOperationParameterRecord(1121, "Z-axis translation", -293.0d), + new EpsgOperationParameterRecord(1122, "X-axis translation", -160.0d), + new EpsgOperationParameterRecord(1122, "Y-axis translation", -6.0d), + new EpsgOperationParameterRecord(1122, "Z-axis translation", -302.0d), + new EpsgOperationParameterRecord(1124, "X-axis translation", -73.0d), + new EpsgOperationParameterRecord(1124, "Y-axis translation", 213.0d), + new EpsgOperationParameterRecord(1124, "Z-axis translation", 296.0d), + new EpsgOperationParameterRecord(1125, "X-axis translation", 307.0d), + new EpsgOperationParameterRecord(1125, "Y-axis translation", 304.0d), + new EpsgOperationParameterRecord(1125, "Z-axis translation", -318.0d), + new EpsgOperationParameterRecord(1126, "X-axis translation", -384.0d), + new EpsgOperationParameterRecord(1126, "Y-axis translation", 664.0d), + new EpsgOperationParameterRecord(1126, "Z-axis translation", -48.0d), + new EpsgOperationParameterRecord(1127, "X-axis translation", -148.0d), + new EpsgOperationParameterRecord(1127, "Y-axis translation", 136.0d), + new EpsgOperationParameterRecord(1127, "Z-axis translation", 90.0d), + new EpsgOperationParameterRecord(1128, "X-axis translation", -136.0d), + new EpsgOperationParameterRecord(1128, "Y-axis translation", -108.0d), + new EpsgOperationParameterRecord(1128, "Z-axis translation", -292.0d), + new EpsgOperationParameterRecord(1129, "X-axis translation", -134.73d), + new EpsgOperationParameterRecord(1129, "Y-axis translation", -110.92d), + new EpsgOperationParameterRecord(1129, "Z-axis translation", -292.66d), + new EpsgOperationParameterRecord(1130, "X-axis translation", -263.0d), + new EpsgOperationParameterRecord(1130, "Y-axis translation", 6.0d), + new EpsgOperationParameterRecord(1130, "Z-axis translation", 431.0d), + new EpsgOperationParameterRecord(1131, "X-axis translation", -134.0d), + new EpsgOperationParameterRecord(1131, "Y-axis translation", 229.0d), + new EpsgOperationParameterRecord(1131, "Z-axis translation", -29.0d), + new EpsgOperationParameterRecord(1132, "X-axis translation", -206.0d), + new EpsgOperationParameterRecord(1132, "Y-axis translation", 172.0d), + new EpsgOperationParameterRecord(1132, "Z-axis translation", -6.0d), + new EpsgOperationParameterRecord(1133, "X-axis translation", -87.0d), + new EpsgOperationParameterRecord(1133, "Y-axis translation", -98.0d), + new EpsgOperationParameterRecord(1133, "Z-axis translation", -121.0d), + new EpsgOperationParameterRecord(1134, "X-axis translation", -87.0d), + new EpsgOperationParameterRecord(1134, "Y-axis translation", -96.0d), + new EpsgOperationParameterRecord(1134, "Z-axis translation", -120.0d), + new EpsgOperationParameterRecord(1135, "X-axis translation", -103.0d), + new EpsgOperationParameterRecord(1135, "Y-axis translation", -106.0d), + new EpsgOperationParameterRecord(1135, "Z-axis translation", -141.0d), + new EpsgOperationParameterRecord(1136, "X-axis translation", -104.0d), + new EpsgOperationParameterRecord(1136, "Y-axis translation", -101.0d), + new EpsgOperationParameterRecord(1136, "Z-axis translation", -140.0d), + new EpsgOperationParameterRecord(1137, "X-axis translation", -130.0d), + new EpsgOperationParameterRecord(1137, "Y-axis translation", -117.0d), + new EpsgOperationParameterRecord(1137, "Z-axis translation", -151.0d), + new EpsgOperationParameterRecord(1138, "X-axis translation", -86.0d), + new EpsgOperationParameterRecord(1138, "Y-axis translation", -96.0d), + new EpsgOperationParameterRecord(1138, "Z-axis translation", -120.0d), + new EpsgOperationParameterRecord(1139, "X-axis translation", -87.0d), + new EpsgOperationParameterRecord(1139, "Y-axis translation", -95.0d), + new EpsgOperationParameterRecord(1139, "Z-axis translation", -120.0d), + new EpsgOperationParameterRecord(1140, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(1140, "Y-axis translation", -95.0d), + new EpsgOperationParameterRecord(1140, "Z-axis translation", -130.0d), + new EpsgOperationParameterRecord(1141, "X-axis translation", -117.0d), + new EpsgOperationParameterRecord(1141, "Y-axis translation", -132.0d), + new EpsgOperationParameterRecord(1141, "Z-axis translation", -164.0d), + new EpsgOperationParameterRecord(1142, "X-axis translation", -97.0d), + new EpsgOperationParameterRecord(1142, "Y-axis translation", -103.0d), + new EpsgOperationParameterRecord(1142, "Z-axis translation", -120.0d), + new EpsgOperationParameterRecord(1143, "X-axis translation", -97.0d), + new EpsgOperationParameterRecord(1143, "Y-axis translation", -88.0d), + new EpsgOperationParameterRecord(1143, "Z-axis translation", -135.0d), + new EpsgOperationParameterRecord(1144, "X-axis translation", -107.0d), + new EpsgOperationParameterRecord(1144, "Y-axis translation", -88.0d), + new EpsgOperationParameterRecord(1144, "Z-axis translation", -149.0d), + new EpsgOperationParameterRecord(1145, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(1145, "Y-axis translation", -107.0d), + new EpsgOperationParameterRecord(1145, "Z-axis translation", -120.0d), + new EpsgOperationParameterRecord(1146, "X-axis translation", -82.981d), + new EpsgOperationParameterRecord(1146, "Y-axis translation", -99.719d), + new EpsgOperationParameterRecord(1146, "Z-axis translation", -110.709d), + new EpsgOperationParameterRecord(1146, "X-axis rotation", -0.5076d), + new EpsgOperationParameterRecord(1146, "Y-axis rotation", 0.1503d), + new EpsgOperationParameterRecord(1146, "Z-axis rotation", 0.3898d), + new EpsgOperationParameterRecord(1146, "Scale difference", -0.3143d), + new EpsgOperationParameterRecord(1147, "X-axis translation", -1.51d), + new EpsgOperationParameterRecord(1147, "Y-axis translation", -0.84d), + new EpsgOperationParameterRecord(1147, "Z-axis translation", -3.5d), + new EpsgOperationParameterRecord(1147, "X-axis rotation", -1.893d), + new EpsgOperationParameterRecord(1147, "Y-axis rotation", -0.687d), + new EpsgOperationParameterRecord(1147, "Z-axis rotation", -2.764d), + new EpsgOperationParameterRecord(1147, "Scale difference", 0.609d), + new EpsgOperationParameterRecord(1148, "X-axis translation", -130.0d), + new EpsgOperationParameterRecord(1148, "Y-axis translation", 110.0d), + new EpsgOperationParameterRecord(1148, "Z-axis translation", -13.0d), + new EpsgOperationParameterRecord(1149, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1149, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1149, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1150, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1150, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1150, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1151, "X-axis translation", 84.0d), + new EpsgOperationParameterRecord(1151, "Y-axis translation", -22.0d), + new EpsgOperationParameterRecord(1151, "Z-axis translation", 209.0d), + new EpsgOperationParameterRecord(1152, "X-axis translation", -637.0d), + new EpsgOperationParameterRecord(1152, "Y-axis translation", -549.0d), + new EpsgOperationParameterRecord(1152, "Z-axis translation", -203.0d), + new EpsgOperationParameterRecord(1153, "X-axis translation", 217.0d), + new EpsgOperationParameterRecord(1153, "Y-axis translation", 823.0d), + new EpsgOperationParameterRecord(1153, "Z-axis translation", 299.0d), + new EpsgOperationParameterRecord(1154, "X-axis translation", 209.0d), + new EpsgOperationParameterRecord(1154, "Y-axis translation", 818.0d), + new EpsgOperationParameterRecord(1154, "Z-axis translation", 290.0d), + new EpsgOperationParameterRecord(1155, "X-axis translation", 282.0d), + new EpsgOperationParameterRecord(1155, "Y-axis translation", 726.0d), + new EpsgOperationParameterRecord(1155, "Z-axis translation", 254.0d), + new EpsgOperationParameterRecord(1156, "X-axis translation", 295.0d), + new EpsgOperationParameterRecord(1156, "Y-axis translation", 736.0d), + new EpsgOperationParameterRecord(1156, "Z-axis translation", 257.0d), + new EpsgOperationParameterRecord(1157, "X-axis translation", -97.0d), + new EpsgOperationParameterRecord(1157, "Y-axis translation", 787.0d), + new EpsgOperationParameterRecord(1157, "Z-axis translation", 86.0d), + new EpsgOperationParameterRecord(1158, "X-axis translation", -11.0d), + new EpsgOperationParameterRecord(1158, "Y-axis translation", 851.0d), + new EpsgOperationParameterRecord(1158, "Z-axis translation", 5.0d), + new EpsgOperationParameterRecord(1159, "X-axis translation", -130.0d), + new EpsgOperationParameterRecord(1159, "Y-axis translation", 29.0d), + new EpsgOperationParameterRecord(1159, "Z-axis translation", 364.0d), + new EpsgOperationParameterRecord(1160, "X-axis translation", -90.0d), + new EpsgOperationParameterRecord(1160, "Y-axis translation", 40.0d), + new EpsgOperationParameterRecord(1160, "Z-axis translation", 88.0d), + new EpsgOperationParameterRecord(1161, "X-axis translation", -133.0d), + new EpsgOperationParameterRecord(1161, "Y-axis translation", -77.0d), + new EpsgOperationParameterRecord(1161, "Z-axis translation", -51.0d), + new EpsgOperationParameterRecord(1162, "X-axis translation", -133.0d), + new EpsgOperationParameterRecord(1162, "Y-axis translation", -79.0d), + new EpsgOperationParameterRecord(1162, "Z-axis translation", -72.0d), + new EpsgOperationParameterRecord(1163, "X-axis translation", -74.0d), + new EpsgOperationParameterRecord(1163, "Y-axis translation", -130.0d), + new EpsgOperationParameterRecord(1163, "Z-axis translation", 42.0d), + new EpsgOperationParameterRecord(1164, "X-axis translation", 41.0d), + new EpsgOperationParameterRecord(1164, "Y-axis translation", -220.0d), + new EpsgOperationParameterRecord(1164, "Z-axis translation", -134.0d), + new EpsgOperationParameterRecord(1165, "X-axis translation", 639.0d), + new EpsgOperationParameterRecord(1165, "Y-axis translation", 405.0d), + new EpsgOperationParameterRecord(1165, "Z-axis translation", 60.0d), + new EpsgOperationParameterRecord(1166, "X-axis translation", 31.0d), + new EpsgOperationParameterRecord(1166, "Y-axis translation", 146.0d), + new EpsgOperationParameterRecord(1166, "Z-axis translation", 47.0d), + new EpsgOperationParameterRecord(1167, "X-axis translation", -81.0d), + new EpsgOperationParameterRecord(1167, "Y-axis translation", -84.0d), + new EpsgOperationParameterRecord(1167, "Z-axis translation", 115.0d), + new EpsgOperationParameterRecord(1168, "X-axis translation", -92.0d), + new EpsgOperationParameterRecord(1168, "Y-axis translation", -93.0d), + new EpsgOperationParameterRecord(1168, "Z-axis translation", 122.0d), + new EpsgOperationParameterRecord(1169, "X-axis translation", -225.0d), + new EpsgOperationParameterRecord(1169, "Y-axis translation", -65.0d), + new EpsgOperationParameterRecord(1169, "Z-axis translation", 9.0d), + new EpsgOperationParameterRecord(1170, "X-axis translation", -3.0d), + new EpsgOperationParameterRecord(1170, "Y-axis translation", 142.0d), + new EpsgOperationParameterRecord(1170, "Z-axis translation", 183.0d), + new EpsgOperationParameterRecord(1171, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1171, "Y-axis translation", 125.0d), + new EpsgOperationParameterRecord(1171, "Z-axis translation", 194.0d), + new EpsgOperationParameterRecord(1172, "X-axis translation", -10.0d), + new EpsgOperationParameterRecord(1172, "Y-axis translation", 158.0d), + new EpsgOperationParameterRecord(1172, "Z-axis translation", 187.0d), + new EpsgOperationParameterRecord(1173, "X-axis translation", -8.0d), + new EpsgOperationParameterRecord(1173, "Y-axis translation", 160.0d), + new EpsgOperationParameterRecord(1173, "Z-axis translation", 176.0d), + new EpsgOperationParameterRecord(1174, "X-axis translation", -9.0d), + new EpsgOperationParameterRecord(1174, "Y-axis translation", 161.0d), + new EpsgOperationParameterRecord(1174, "Z-axis translation", 179.0d), + new EpsgOperationParameterRecord(1175, "X-axis translation", -8.0d), + new EpsgOperationParameterRecord(1175, "Y-axis translation", 159.0d), + new EpsgOperationParameterRecord(1175, "Z-axis translation", 175.0d), + new EpsgOperationParameterRecord(1176, "X-axis translation", -5.0d), + new EpsgOperationParameterRecord(1176, "Y-axis translation", 135.0d), + new EpsgOperationParameterRecord(1176, "Z-axis translation", 172.0d), + new EpsgOperationParameterRecord(1177, "X-axis translation", -4.0d), + new EpsgOperationParameterRecord(1177, "Y-axis translation", 154.0d), + new EpsgOperationParameterRecord(1177, "Z-axis translation", 178.0d), + new EpsgOperationParameterRecord(1178, "X-axis translation", 1.0d), + new EpsgOperationParameterRecord(1178, "Y-axis translation", 140.0d), + new EpsgOperationParameterRecord(1178, "Z-axis translation", 165.0d), + new EpsgOperationParameterRecord(1179, "X-axis translation", -7.0d), + new EpsgOperationParameterRecord(1179, "Y-axis translation", 162.0d), + new EpsgOperationParameterRecord(1179, "Z-axis translation", 188.0d), + new EpsgOperationParameterRecord(1180, "X-axis translation", -9.0d), + new EpsgOperationParameterRecord(1180, "Y-axis translation", 157.0d), + new EpsgOperationParameterRecord(1180, "Z-axis translation", 184.0d), + new EpsgOperationParameterRecord(1181, "X-axis translation", -22.0d), + new EpsgOperationParameterRecord(1181, "Y-axis translation", 160.0d), + new EpsgOperationParameterRecord(1181, "Z-axis translation", 190.0d), + new EpsgOperationParameterRecord(1182, "X-axis translation", 4.0d), + new EpsgOperationParameterRecord(1182, "Y-axis translation", 159.0d), + new EpsgOperationParameterRecord(1182, "Z-axis translation", 188.0d), + new EpsgOperationParameterRecord(1183, "X-axis translation", -7.0d), + new EpsgOperationParameterRecord(1183, "Y-axis translation", 139.0d), + new EpsgOperationParameterRecord(1183, "Z-axis translation", 181.0d), + new EpsgOperationParameterRecord(1184, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1184, "Y-axis translation", 125.0d), + new EpsgOperationParameterRecord(1184, "Z-axis translation", 201.0d), + new EpsgOperationParameterRecord(1185, "X-axis translation", -9.0d), + new EpsgOperationParameterRecord(1185, "Y-axis translation", 152.0d), + new EpsgOperationParameterRecord(1185, "Z-axis translation", 178.0d), + new EpsgOperationParameterRecord(1186, "X-axis translation", 11.0d), + new EpsgOperationParameterRecord(1186, "Y-axis translation", 114.0d), + new EpsgOperationParameterRecord(1186, "Z-axis translation", 195.0d), + new EpsgOperationParameterRecord(1187, "X-axis translation", -12.0d), + new EpsgOperationParameterRecord(1187, "Y-axis translation", 130.0d), + new EpsgOperationParameterRecord(1187, "Z-axis translation", 190.0d), + new EpsgOperationParameterRecord(1188, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1188, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1188, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1189, "X-axis translation", -247.0d), + new EpsgOperationParameterRecord(1189, "Y-axis translation", -148.0d), + new EpsgOperationParameterRecord(1189, "Z-axis translation", 369.0d), + new EpsgOperationParameterRecord(1190, "X-axis translation", -243.0d), + new EpsgOperationParameterRecord(1190, "Y-axis translation", -192.0d), + new EpsgOperationParameterRecord(1190, "Z-axis translation", 477.0d), + new EpsgOperationParameterRecord(1191, "X-axis translation", -249.0d), + new EpsgOperationParameterRecord(1191, "Y-axis translation", -156.0d), + new EpsgOperationParameterRecord(1191, "Z-axis translation", 381.0d), + new EpsgOperationParameterRecord(1192, "X-axis translation", -10.0d), + new EpsgOperationParameterRecord(1192, "Y-axis translation", 375.0d), + new EpsgOperationParameterRecord(1192, "Z-axis translation", 165.0d), + new EpsgOperationParameterRecord(1193, "X-axis translation", -168.0d), + new EpsgOperationParameterRecord(1193, "Y-axis translation", -60.0d), + new EpsgOperationParameterRecord(1193, "Z-axis translation", 320.0d), + new EpsgOperationParameterRecord(1194, "X-axis translation", 601.705d), + new EpsgOperationParameterRecord(1194, "Y-axis translation", 84.263d), + new EpsgOperationParameterRecord(1194, "Z-axis translation", 485.227d), + new EpsgOperationParameterRecord(1194, "X-axis rotation", -4.7354d), + new EpsgOperationParameterRecord(1194, "Y-axis rotation", -1.3145d), + new EpsgOperationParameterRecord(1194, "Z-axis rotation", -5.393d), + new EpsgOperationParameterRecord(1194, "Scale difference", -2.3887d), + new EpsgOperationParameterRecord(1195, "X-axis translation", 375.0d), + new EpsgOperationParameterRecord(1195, "Y-axis translation", -111.0d), + new EpsgOperationParameterRecord(1195, "Z-axis translation", 431.0d), + new EpsgOperationParameterRecord(1196, "X-axis translation", 371.0d), + new EpsgOperationParameterRecord(1196, "Y-axis translation", -112.0d), + new EpsgOperationParameterRecord(1196, "Z-axis translation", 434.0d), + new EpsgOperationParameterRecord(1197, "X-axis translation", 371.0d), + new EpsgOperationParameterRecord(1197, "Y-axis translation", -111.0d), + new EpsgOperationParameterRecord(1197, "Z-axis translation", 434.0d), + new EpsgOperationParameterRecord(1198, "X-axis translation", 384.0d), + new EpsgOperationParameterRecord(1198, "Y-axis translation", -111.0d), + new EpsgOperationParameterRecord(1198, "Z-axis translation", 425.0d), + new EpsgOperationParameterRecord(1199, "X-axis translation", 370.0d), + new EpsgOperationParameterRecord(1199, "Y-axis translation", -108.0d), + new EpsgOperationParameterRecord(1199, "Z-axis translation", 434.0d), + new EpsgOperationParameterRecord(1200, "X-axis translation", -148.0d), + new EpsgOperationParameterRecord(1200, "Y-axis translation", 51.0d), + new EpsgOperationParameterRecord(1200, "Z-axis translation", -291.0d), + new EpsgOperationParameterRecord(1201, "X-axis translation", -288.0d), + new EpsgOperationParameterRecord(1201, "Y-axis translation", 175.0d), + new EpsgOperationParameterRecord(1201, "Z-axis translation", -376.0d), + new EpsgOperationParameterRecord(1202, "X-axis translation", -270.0d), + new EpsgOperationParameterRecord(1202, "Y-axis translation", 188.0d), + new EpsgOperationParameterRecord(1202, "Z-axis translation", -388.0d), + new EpsgOperationParameterRecord(1203, "X-axis translation", -270.0d), + new EpsgOperationParameterRecord(1203, "Y-axis translation", 183.0d), + new EpsgOperationParameterRecord(1203, "Z-axis translation", -390.0d), + new EpsgOperationParameterRecord(1204, "X-axis translation", -305.0d), + new EpsgOperationParameterRecord(1204, "Y-axis translation", 243.0d), + new EpsgOperationParameterRecord(1204, "Z-axis translation", -442.0d), + new EpsgOperationParameterRecord(1205, "X-axis translation", -282.0d), + new EpsgOperationParameterRecord(1205, "Y-axis translation", 169.0d), + new EpsgOperationParameterRecord(1205, "Z-axis translation", -371.0d), + new EpsgOperationParameterRecord(1206, "X-axis translation", -278.0d), + new EpsgOperationParameterRecord(1206, "Y-axis translation", 171.0d), + new EpsgOperationParameterRecord(1206, "Z-axis translation", -367.0d), + new EpsgOperationParameterRecord(1207, "X-axis translation", -298.0d), + new EpsgOperationParameterRecord(1207, "Y-axis translation", 159.0d), + new EpsgOperationParameterRecord(1207, "Z-axis translation", -369.0d), + new EpsgOperationParameterRecord(1208, "X-axis translation", -279.0d), + new EpsgOperationParameterRecord(1208, "Y-axis translation", 175.0d), + new EpsgOperationParameterRecord(1208, "Z-axis translation", -379.0d), + new EpsgOperationParameterRecord(1209, "X-axis translation", -295.0d), + new EpsgOperationParameterRecord(1209, "Y-axis translation", 173.0d), + new EpsgOperationParameterRecord(1209, "Z-axis translation", -371.0d), + new EpsgOperationParameterRecord(1210, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1210, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1210, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1225, "X-axis translation", -355.0d), + new EpsgOperationParameterRecord(1225, "Y-axis translation", 21.0d), + new EpsgOperationParameterRecord(1225, "Z-axis translation", 72.0d), + new EpsgOperationParameterRecord(1226, "X-axis translation", 616.0d), + new EpsgOperationParameterRecord(1226, "Y-axis translation", 97.0d), + new EpsgOperationParameterRecord(1226, "Z-axis translation", -251.0d), + new EpsgOperationParameterRecord(1227, "X-axis translation", -189.0d), + new EpsgOperationParameterRecord(1227, "Y-axis translation", -242.0d), + new EpsgOperationParameterRecord(1227, "Z-axis translation", -91.0d), + new EpsgOperationParameterRecord(1228, "X-axis translation", -679.0d), + new EpsgOperationParameterRecord(1228, "Y-axis translation", 669.0d), + new EpsgOperationParameterRecord(1228, "Z-axis translation", -48.0d), + new EpsgOperationParameterRecord(1230, "X-axis translation", -148.0d), + new EpsgOperationParameterRecord(1230, "Y-axis translation", 507.0d), + new EpsgOperationParameterRecord(1230, "Z-axis translation", 685.0d), + new EpsgOperationParameterRecord(1231, "X-axis translation", -148.0d), + new EpsgOperationParameterRecord(1231, "Y-axis translation", 507.0d), + new EpsgOperationParameterRecord(1231, "Z-axis translation", 685.0d), + new EpsgOperationParameterRecord(1232, "X-axis translation", -146.0d), + new EpsgOperationParameterRecord(1232, "Y-axis translation", 507.0d), + new EpsgOperationParameterRecord(1232, "Z-axis translation", 687.0d), + new EpsgOperationParameterRecord(1233, "X-axis translation", -158.0d), + new EpsgOperationParameterRecord(1233, "Y-axis translation", 507.0d), + new EpsgOperationParameterRecord(1233, "Z-axis translation", 676.0d), + new EpsgOperationParameterRecord(1234, "X-axis translation", -155.0d), + new EpsgOperationParameterRecord(1234, "Y-axis translation", 171.0d), + new EpsgOperationParameterRecord(1234, "Z-axis translation", 37.0d), + new EpsgOperationParameterRecord(1235, "X-axis translation", -265.0d), + new EpsgOperationParameterRecord(1235, "Y-axis translation", 120.0d), + new EpsgOperationParameterRecord(1235, "Z-axis translation", -358.0d), + new EpsgOperationParameterRecord(1236, "X-axis translation", -116.0d), + new EpsgOperationParameterRecord(1236, "Y-axis translation", -50.47d), + new EpsgOperationParameterRecord(1236, "Z-axis translation", 141.69d), + new EpsgOperationParameterRecord(1236, "X-axis rotation", -0.23d), + new EpsgOperationParameterRecord(1236, "Y-axis rotation", -0.39d), + new EpsgOperationParameterRecord(1236, "Z-axis rotation", -0.344d), + new EpsgOperationParameterRecord(1236, "Scale difference", 0.0983d), + new EpsgOperationParameterRecord(1237, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1237, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1237, "Z-axis translation", 4.5d), + new EpsgOperationParameterRecord(1237, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1237, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1237, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(1237, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(1238, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1238, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1238, "Z-axis translation", 4.5d), + new EpsgOperationParameterRecord(1238, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1238, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1238, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(1238, "Scale difference", 0.219d), + new EpsgOperationParameterRecord(1239, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1239, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1239, "Z-axis translation", -2.6d), + new EpsgOperationParameterRecord(1239, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1239, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1239, "Z-axis rotation", 0.26d), + new EpsgOperationParameterRecord(1239, "Scale difference", -0.6063d), + new EpsgOperationParameterRecord(1240, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1240, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1240, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(1240, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1240, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1240, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(1240, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(1242, "X-axis translation", 52.17d), + new EpsgOperationParameterRecord(1242, "Y-axis translation", -71.82d), + new EpsgOperationParameterRecord(1242, "Z-axis translation", -14.9d), + new EpsgOperationParameterRecord(1244, "X-axis translation", -1.08d), + new EpsgOperationParameterRecord(1244, "Y-axis translation", -0.27d), + new EpsgOperationParameterRecord(1244, "Z-axis translation", -0.9d), + new EpsgOperationParameterRecord(1244, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1244, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1244, "Z-axis rotation", -0.16d), + new EpsgOperationParameterRecord(1244, "Scale difference", -0.12d), + new EpsgOperationParameterRecord(1245, "X-axis translation", -112.0d), + new EpsgOperationParameterRecord(1245, "Y-axis translation", -77.0d), + new EpsgOperationParameterRecord(1245, "Z-axis translation", -145.0d), + new EpsgOperationParameterRecord(1246, "X-axis translation", -333.0d), + new EpsgOperationParameterRecord(1246, "Y-axis translation", -222.0d), + new EpsgOperationParameterRecord(1246, "Z-axis translation", 114.0d), + new EpsgOperationParameterRecord(1247, "X-axis translation", 283.0d), + new EpsgOperationParameterRecord(1247, "Y-axis translation", 682.0d), + new EpsgOperationParameterRecord(1247, "Z-axis translation", 231.0d), + new EpsgOperationParameterRecord(1248, "X-axis translation", -24.0d), + new EpsgOperationParameterRecord(1248, "Y-axis translation", -15.0d), + new EpsgOperationParameterRecord(1248, "Z-axis translation", 5.0d), + new EpsgOperationParameterRecord(1249, "X-axis translation", -2.0d), + new EpsgOperationParameterRecord(1249, "Y-axis translation", 152.0d), + new EpsgOperationParameterRecord(1249, "Z-axis translation", 149.0d), + new EpsgOperationParameterRecord(1250, "X-axis translation", 2.0d), + new EpsgOperationParameterRecord(1250, "Y-axis translation", 204.0d), + new EpsgOperationParameterRecord(1250, "Z-axis translation", 105.0d), + new EpsgOperationParameterRecord(1251, "X-axis translation", -2.0d), + new EpsgOperationParameterRecord(1251, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1251, "Z-axis translation", 4.0d), + new EpsgOperationParameterRecord(1252, "X-axis translation", 1.0d), + new EpsgOperationParameterRecord(1252, "Y-axis translation", 1.0d), + new EpsgOperationParameterRecord(1252, "Z-axis translation", -1.0d), + new EpsgOperationParameterRecord(1253, "X-axis translation", -186.0d), + new EpsgOperationParameterRecord(1253, "Y-axis translation", -93.0d), + new EpsgOperationParameterRecord(1253, "Z-axis translation", 310.0d), + new EpsgOperationParameterRecord(1254, "X-axis translation", 28.0d), + new EpsgOperationParameterRecord(1254, "Y-axis translation", -130.0d), + new EpsgOperationParameterRecord(1254, "Z-axis translation", -95.0d), + new EpsgOperationParameterRecord(1255, "X-axis translation", -123.0d), + new EpsgOperationParameterRecord(1255, "Y-axis translation", -206.0d), + new EpsgOperationParameterRecord(1255, "Z-axis translation", 219.0d), + new EpsgOperationParameterRecord(1256, "X-axis translation", -346.0d), + new EpsgOperationParameterRecord(1256, "Y-axis translation", -1.0d), + new EpsgOperationParameterRecord(1256, "Z-axis translation", 224.0d), + new EpsgOperationParameterRecord(1257, "X-axis translation", 25.9d), + new EpsgOperationParameterRecord(1257, "Y-axis translation", -130.94d), + new EpsgOperationParameterRecord(1257, "Z-axis translation", -81.76d), + new EpsgOperationParameterRecord(1260, "Longitude offset", 106.807719444445d), + new EpsgOperationParameterRecord(1262, "Longitude offset", 12.4523333333336d), + new EpsgOperationParameterRecord(1264, "Longitude offset", 4.36797500000028d), + new EpsgOperationParameterRecord(1265, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(1266, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(1267, "X-axis translation", 23.92d), + new EpsgOperationParameterRecord(1267, "Y-axis translation", -141.27d), + new EpsgOperationParameterRecord(1267, "Z-axis translation", -80.9d), + new EpsgOperationParameterRecord(1267, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1267, "Y-axis rotation", -0.35d), + new EpsgOperationParameterRecord(1267, "Z-axis rotation", -0.82d), + new EpsgOperationParameterRecord(1267, "Scale difference", -0.12d), + new EpsgOperationParameterRecord(1271, "X-axis translation", 615.64d), + new EpsgOperationParameterRecord(1271, "Y-axis translation", 102.08d), + new EpsgOperationParameterRecord(1271, "Z-axis translation", -255.81d), + new EpsgOperationParameterRecord(1272, "X-axis translation", -199.87d), + new EpsgOperationParameterRecord(1272, "Y-axis translation", 74.79d), + new EpsgOperationParameterRecord(1272, "Z-axis translation", 246.62d), + new EpsgOperationParameterRecord(1274, "X-axis translation", -40.595d), + new EpsgOperationParameterRecord(1274, "Y-axis translation", -18.55d), + new EpsgOperationParameterRecord(1274, "Z-axis translation", -69.339d), + new EpsgOperationParameterRecord(1274, "X-axis rotation", -2.508d), + new EpsgOperationParameterRecord(1274, "Y-axis rotation", -1.832d), + new EpsgOperationParameterRecord(1274, "Z-axis rotation", 2.611d), + new EpsgOperationParameterRecord(1274, "Scale difference", -4.299d), + new EpsgOperationParameterRecord(1275, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(1275, "Y-axis translation", -97.0d), + new EpsgOperationParameterRecord(1275, "Z-axis translation", -117.0d), + new EpsgOperationParameterRecord(1276, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(1276, "Y-axis translation", 37.0d), + new EpsgOperationParameterRecord(1276, "Z-axis translation", 437.0d), + new EpsgOperationParameterRecord(1277, "X-axis translation", -168.0d), + new EpsgOperationParameterRecord(1277, "Y-axis translation", -72.0d), + new EpsgOperationParameterRecord(1277, "Z-axis translation", 314.0d), + new EpsgOperationParameterRecord(1278, "X-axis translation", -127.8d), + new EpsgOperationParameterRecord(1278, "Y-axis translation", -52.3d), + new EpsgOperationParameterRecord(1278, "Z-axis translation", 152.9d), + new EpsgOperationParameterRecord(1279, "X-axis translation", -128.5d), + new EpsgOperationParameterRecord(1279, "Y-axis translation", -53.0d), + new EpsgOperationParameterRecord(1279, "Z-axis translation", 153.4d), + new EpsgOperationParameterRecord(1280, "X-axis translation", -117.763d), + new EpsgOperationParameterRecord(1280, "Y-axis translation", -51.51d), + new EpsgOperationParameterRecord(1280, "Z-axis translation", 139.061d), + new EpsgOperationParameterRecord(1280, "X-axis rotation", -0.292d), + new EpsgOperationParameterRecord(1280, "Y-axis rotation", -0.443d), + new EpsgOperationParameterRecord(1280, "Z-axis rotation", -0.277d), + new EpsgOperationParameterRecord(1280, "Scale difference", -0.191d), + new EpsgOperationParameterRecord(1281, "X-axis translation", 24.82d), + new EpsgOperationParameterRecord(1281, "Y-axis translation", -131.21d), + new EpsgOperationParameterRecord(1281, "Z-axis translation", -82.66d), + new EpsgOperationParameterRecord(1281, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1281, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1281, "Z-axis rotation", -0.16d), + new EpsgOperationParameterRecord(1281, "Scale difference", -0.12d), + new EpsgOperationParameterRecord(1283, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1283, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1283, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1284, "X-axis translation", -157.0d), + new EpsgOperationParameterRecord(1284, "Y-axis translation", -2.0d), + new EpsgOperationParameterRecord(1284, "Z-axis translation", -299.0d), + new EpsgOperationParameterRecord(1285, "X-axis translation", -175.0d), + new EpsgOperationParameterRecord(1285, "Y-axis translation", -23.0d), + new EpsgOperationParameterRecord(1285, "Z-axis translation", -303.0d), + new EpsgOperationParameterRecord(1290, "X-axis translation", 24.0d), + new EpsgOperationParameterRecord(1290, "Y-axis translation", -124.0d), + new EpsgOperationParameterRecord(1290, "Z-axis translation", -82.0d), + new EpsgOperationParameterRecord(1291, "X-axis translation", 15.0d), + new EpsgOperationParameterRecord(1291, "Y-axis translation", -130.0d), + new EpsgOperationParameterRecord(1291, "Z-axis translation", -84.0d), + new EpsgOperationParameterRecord(1294, "X-axis translation", -73.0d), + new EpsgOperationParameterRecord(1294, "Y-axis translation", -247.0d), + new EpsgOperationParameterRecord(1294, "Z-axis translation", 227.0d), + new EpsgOperationParameterRecord(1296, "X-axis translation", -61.702d), + new EpsgOperationParameterRecord(1296, "Y-axis translation", 284.488d), + new EpsgOperationParameterRecord(1296, "Z-axis translation", 472.052d), + new EpsgOperationParameterRecord(1297, "X-axis translation", -115.064d), + new EpsgOperationParameterRecord(1297, "Y-axis translation", -87.39d), + new EpsgOperationParameterRecord(1297, "Z-axis translation", -101.716d), + new EpsgOperationParameterRecord(1297, "X-axis rotation", 0.058d), + new EpsgOperationParameterRecord(1297, "Y-axis rotation", -4.001d), + new EpsgOperationParameterRecord(1297, "Z-axis rotation", 2.062d), + new EpsgOperationParameterRecord(1297, "Scale difference", 9.366d), + new EpsgOperationParameterRecord(1298, "X-axis translation", -82.875d), + new EpsgOperationParameterRecord(1298, "Y-axis translation", -57.097d), + new EpsgOperationParameterRecord(1298, "Z-axis translation", -156.768d), + new EpsgOperationParameterRecord(1298, "X-axis rotation", 2.158d), + new EpsgOperationParameterRecord(1298, "Y-axis rotation", -1.524d), + new EpsgOperationParameterRecord(1298, "Z-axis rotation", 0.982d), + new EpsgOperationParameterRecord(1298, "Scale difference", -0.359d), + new EpsgOperationParameterRecord(1299, "X-axis translation", -138.527d), + new EpsgOperationParameterRecord(1299, "Y-axis translation", -91.999d), + new EpsgOperationParameterRecord(1299, "Z-axis translation", -114.591d), + new EpsgOperationParameterRecord(1299, "X-axis rotation", 0.14d), + new EpsgOperationParameterRecord(1299, "Y-axis rotation", -3.363d), + new EpsgOperationParameterRecord(1299, "Z-axis rotation", 2.217d), + new EpsgOperationParameterRecord(1299, "Scale difference", 11.748d), + new EpsgOperationParameterRecord(1300, "X-axis translation", -73.472d), + new EpsgOperationParameterRecord(1300, "Y-axis translation", -51.66d), + new EpsgOperationParameterRecord(1300, "Z-axis translation", -112.482d), + new EpsgOperationParameterRecord(1300, "X-axis rotation", -0.953d), + new EpsgOperationParameterRecord(1300, "Y-axis rotation", -4.6d), + new EpsgOperationParameterRecord(1300, "Z-axis rotation", 2.368d), + new EpsgOperationParameterRecord(1300, "Scale difference", 0.586d), + new EpsgOperationParameterRecord(1301, "X-axis translation", 219.315d), + new EpsgOperationParameterRecord(1301, "Y-axis translation", 168.975d), + new EpsgOperationParameterRecord(1301, "Z-axis translation", -166.145d), + new EpsgOperationParameterRecord(1301, "X-axis rotation", -0.198d), + new EpsgOperationParameterRecord(1301, "Y-axis rotation", -5.926d), + new EpsgOperationParameterRecord(1301, "Z-axis rotation", 2.356d), + new EpsgOperationParameterRecord(1301, "Scale difference", -57.104d), + new EpsgOperationParameterRecord(1302, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1302, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1302, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1302, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1302, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1302, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1302, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(1303, "X-axis translation", 43.822d), + new EpsgOperationParameterRecord(1303, "Y-axis translation", -108.842d), + new EpsgOperationParameterRecord(1303, "Z-axis translation", -119.585d), + new EpsgOperationParameterRecord(1303, "X-axis rotation", 1.455d), + new EpsgOperationParameterRecord(1303, "Y-axis rotation", -0.761d), + new EpsgOperationParameterRecord(1303, "Z-axis rotation", 0.737d), + new EpsgOperationParameterRecord(1303, "Scale difference", 0.549d), + new EpsgOperationParameterRecord(1304, "X-axis translation", 210.0d), + new EpsgOperationParameterRecord(1304, "Y-axis translation", 814.0d), + new EpsgOperationParameterRecord(1304, "Z-axis translation", 289.0d), + new EpsgOperationParameterRecord(1305, "X-axis translation", -147.0d), + new EpsgOperationParameterRecord(1305, "Y-axis translation", 506.0d), + new EpsgOperationParameterRecord(1305, "Z-axis translation", 687.0d), + new EpsgOperationParameterRecord(1307, "X-axis translation", -2.0d), + new EpsgOperationParameterRecord(1307, "Y-axis translation", 374.0d), + new EpsgOperationParameterRecord(1307, "Z-axis translation", 172.0d), + new EpsgOperationParameterRecord(1309, "X-axis translation", 582.0d), + new EpsgOperationParameterRecord(1309, "Y-axis translation", 105.0d), + new EpsgOperationParameterRecord(1309, "Z-axis translation", 414.0d), + new EpsgOperationParameterRecord(1309, "X-axis rotation", -1.04d), + new EpsgOperationParameterRecord(1309, "Y-axis rotation", -0.35d), + new EpsgOperationParameterRecord(1309, "Z-axis rotation", 3.08d), + new EpsgOperationParameterRecord(1309, "Scale difference", 8.3d), + new EpsgOperationParameterRecord(1311, "X-axis translation", -89.5d), + new EpsgOperationParameterRecord(1311, "Y-axis translation", -93.8d), + new EpsgOperationParameterRecord(1311, "Z-axis translation", -123.1d), + new EpsgOperationParameterRecord(1311, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1311, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1311, "Z-axis rotation", -0.156d), + new EpsgOperationParameterRecord(1311, "Scale difference", 1.2d), + new EpsgOperationParameterRecord(1314, "X-axis translation", 446.448d), + new EpsgOperationParameterRecord(1314, "Y-axis translation", -125.157d), + new EpsgOperationParameterRecord(1314, "Z-axis translation", 542.06d), + new EpsgOperationParameterRecord(1314, "X-axis rotation", 0.15d), + new EpsgOperationParameterRecord(1314, "Y-axis rotation", 0.247d), + new EpsgOperationParameterRecord(1314, "Z-axis rotation", 0.842d), + new EpsgOperationParameterRecord(1314, "Scale difference", -20.489d), + new EpsgOperationParameterRecord(1315, "X-axis translation", 535.948d), + new EpsgOperationParameterRecord(1315, "Y-axis translation", -31.357d), + new EpsgOperationParameterRecord(1315, "Z-axis translation", 665.16d), + new EpsgOperationParameterRecord(1315, "X-axis rotation", 0.15d), + new EpsgOperationParameterRecord(1315, "Y-axis rotation", 0.247d), + new EpsgOperationParameterRecord(1315, "Z-axis rotation", 0.998d), + new EpsgOperationParameterRecord(1315, "Scale difference", -21.689d), + new EpsgOperationParameterRecord(1317, "X-axis translation", -37.2d), + new EpsgOperationParameterRecord(1317, "Y-axis translation", -370.6d), + new EpsgOperationParameterRecord(1317, "Z-axis translation", -228.5d), + new EpsgOperationParameterRecord(1318, "X-axis translation", -42.01d), + new EpsgOperationParameterRecord(1318, "Y-axis translation", -332.21d), + new EpsgOperationParameterRecord(1318, "Z-axis translation", -229.75d), + new EpsgOperationParameterRecord(1319, "X-axis translation", -40.0d), + new EpsgOperationParameterRecord(1319, "Y-axis translation", -354.0d), + new EpsgOperationParameterRecord(1319, "Z-axis translation", -224.0d), + new EpsgOperationParameterRecord(1320, "X-axis translation", -37.2d), + new EpsgOperationParameterRecord(1320, "Y-axis translation", -370.6d), + new EpsgOperationParameterRecord(1320, "Z-axis translation", -224.0d), + new EpsgOperationParameterRecord(1320, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1320, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1320, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(1320, "Scale difference", 0.219d), + new EpsgOperationParameterRecord(1321, "X-axis translation", -41.8d), + new EpsgOperationParameterRecord(1321, "Y-axis translation", -342.2d), + new EpsgOperationParameterRecord(1321, "Z-axis translation", -228.2d), + new EpsgOperationParameterRecord(1322, "X-axis translation", -55.5d), + new EpsgOperationParameterRecord(1322, "Y-axis translation", -348.0d), + new EpsgOperationParameterRecord(1322, "Z-axis translation", -229.2d), + new EpsgOperationParameterRecord(1323, "X-axis translation", -43.0d), + new EpsgOperationParameterRecord(1323, "Y-axis translation", -337.0d), + new EpsgOperationParameterRecord(1323, "Z-axis translation", -233.0d), + new EpsgOperationParameterRecord(1324, "X-axis translation", -48.0d), + new EpsgOperationParameterRecord(1324, "Y-axis translation", -345.0d), + new EpsgOperationParameterRecord(1324, "Z-axis translation", -231.0d), + new EpsgOperationParameterRecord(1325, "X-axis translation", -48.6d), + new EpsgOperationParameterRecord(1325, "Y-axis translation", -345.1d), + new EpsgOperationParameterRecord(1325, "Z-axis translation", -230.8d), + new EpsgOperationParameterRecord(1326, "X-axis translation", -41.057d), + new EpsgOperationParameterRecord(1326, "Y-axis translation", -374.564d), + new EpsgOperationParameterRecord(1326, "Z-axis translation", -226.287d), + new EpsgOperationParameterRecord(1326, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1326, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1326, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(1326, "Scale difference", 0.219d), + new EpsgOperationParameterRecord(1327, "X-axis translation", -50.9d), + new EpsgOperationParameterRecord(1327, "Y-axis translation", -347.6d), + new EpsgOperationParameterRecord(1327, "Z-axis translation", -231.0d), + new EpsgOperationParameterRecord(1330, "X-axis translation", -252.95d), + new EpsgOperationParameterRecord(1330, "Y-axis translation", -4.11d), + new EpsgOperationParameterRecord(1330, "Z-axis translation", -96.38d), + new EpsgOperationParameterRecord(1331, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1331, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1331, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1331, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1331, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1331, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1331, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(1332, "X-axis translation", 21.53219d), + new EpsgOperationParameterRecord(1332, "Y-axis translation", -97.00027d), + new EpsgOperationParameterRecord(1332, "Z-axis translation", -60.74046d), + new EpsgOperationParameterRecord(1332, "X-axis rotation", -0.99548d), + new EpsgOperationParameterRecord(1332, "Y-axis rotation", -0.58147d), + new EpsgOperationParameterRecord(1332, "Z-axis rotation", -0.2418d), + new EpsgOperationParameterRecord(1332, "Scale difference", -4.5981d), + new EpsgOperationParameterRecord(1333, "X-axis translation", 0.055d), + new EpsgOperationParameterRecord(1333, "Y-axis translation", -0.541d), + new EpsgOperationParameterRecord(1333, "Z-axis translation", -0.185d), + new EpsgOperationParameterRecord(1333, "X-axis rotation", -0.0183d), + new EpsgOperationParameterRecord(1333, "Y-axis rotation", 0.0003d), + new EpsgOperationParameterRecord(1333, "Z-axis rotation", 0.007d), + new EpsgOperationParameterRecord(1333, "Scale difference", -0.014d), + new EpsgOperationParameterRecord(1334, "X-axis translation", 21.58719d), + new EpsgOperationParameterRecord(1334, "Y-axis translation", -97.54127d), + new EpsgOperationParameterRecord(1334, "Z-axis translation", -60.92546d), + new EpsgOperationParameterRecord(1334, "X-axis rotation", -1.01378d), + new EpsgOperationParameterRecord(1334, "Y-axis rotation", -0.58117d), + new EpsgOperationParameterRecord(1334, "Z-axis rotation", -0.2348d), + new EpsgOperationParameterRecord(1334, "Scale difference", -4.6121d), + new EpsgOperationParameterRecord(1437, "X-axis translation", 419.3836d), + new EpsgOperationParameterRecord(1437, "Y-axis translation", 99.3335d), + new EpsgOperationParameterRecord(1437, "Z-axis translation", 591.3451d), + new EpsgOperationParameterRecord(1437, "X-axis rotation", -0.850389d), + new EpsgOperationParameterRecord(1437, "Y-axis rotation", -1.817277d), + new EpsgOperationParameterRecord(1437, "Z-axis rotation", 7.862238d), + new EpsgOperationParameterRecord(1437, "Scale difference", -0.99496d), + new EpsgOperationParameterRecord(1438, "X-axis translation", -333.102d), + new EpsgOperationParameterRecord(1438, "Y-axis translation", -11.02d), + new EpsgOperationParameterRecord(1438, "Z-axis translation", 230.69d), + new EpsgOperationParameterRecord(1438, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1438, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1438, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(1438, "Scale difference", 0.219d), + new EpsgOperationParameterRecord(1439, "X-axis translation", -180.624d), + new EpsgOperationParameterRecord(1439, "Y-axis translation", -225.516d), + new EpsgOperationParameterRecord(1439, "Z-axis translation", 173.919d), + new EpsgOperationParameterRecord(1439, "X-axis rotation", -0.81d), + new EpsgOperationParameterRecord(1439, "Y-axis rotation", -1.898d), + new EpsgOperationParameterRecord(1439, "Z-axis rotation", 8.336d), + new EpsgOperationParameterRecord(1439, "Scale difference", 16.71006d), + new EpsgOperationParameterRecord(1440, "X-axis translation", -86.0d), + new EpsgOperationParameterRecord(1440, "Y-axis translation", -92.2d), + new EpsgOperationParameterRecord(1440, "Z-axis translation", -127.5d), + new EpsgOperationParameterRecord(1441, "X-axis translation", -255.0d), + new EpsgOperationParameterRecord(1441, "Y-axis translation", -15.0d), + new EpsgOperationParameterRecord(1441, "Z-axis translation", 71.0d), + new EpsgOperationParameterRecord(1442, "X-axis translation", 725.0d), + new EpsgOperationParameterRecord(1442, "Y-axis translation", 685.0d), + new EpsgOperationParameterRecord(1442, "Z-axis translation", 536.0d), + new EpsgOperationParameterRecord(1443, "X-axis translation", 72.0d), + new EpsgOperationParameterRecord(1443, "Y-axis translation", 213.7d), + new EpsgOperationParameterRecord(1443, "Z-axis translation", 93.0d), + new EpsgOperationParameterRecord(1444, "X-axis translation", 174.0d), + new EpsgOperationParameterRecord(1444, "Y-axis translation", 359.0d), + new EpsgOperationParameterRecord(1444, "Z-axis translation", 365.0d), + new EpsgOperationParameterRecord(1445, "X-axis translation", 9.0d), + new EpsgOperationParameterRecord(1445, "Y-axis translation", 183.0d), + new EpsgOperationParameterRecord(1445, "Z-axis translation", 236.0d), + new EpsgOperationParameterRecord(1446, "X-axis translation", -149.0d), + new EpsgOperationParameterRecord(1446, "Y-axis translation", 128.0d), + new EpsgOperationParameterRecord(1446, "Z-axis translation", 296.0d), + new EpsgOperationParameterRecord(1447, "Latitude offset", -18.0d), + new EpsgOperationParameterRecord(1447, "Longitude offset", 4.4d), + new EpsgOperationParameterRecord(1448, "X-axis translation", 52.684d), + new EpsgOperationParameterRecord(1448, "Y-axis translation", -71.194d), + new EpsgOperationParameterRecord(1448, "Z-axis translation", -13.975d), + new EpsgOperationParameterRecord(1448, "X-axis rotation", 0.312d), + new EpsgOperationParameterRecord(1448, "Y-axis rotation", 0.1063d), + new EpsgOperationParameterRecord(1448, "Z-axis rotation", 0.3729d), + new EpsgOperationParameterRecord(1448, "Scale difference", 1.0191d), + new EpsgOperationParameterRecord(1449, "X-axis translation", 52.684d), + new EpsgOperationParameterRecord(1449, "Y-axis translation", -71.194d), + new EpsgOperationParameterRecord(1449, "Z-axis translation", -13.975d), + new EpsgOperationParameterRecord(1449, "X-axis rotation", 0.312d), + new EpsgOperationParameterRecord(1449, "Y-axis rotation", 0.1063d), + new EpsgOperationParameterRecord(1449, "Z-axis rotation", 0.3729d), + new EpsgOperationParameterRecord(1449, "Scale difference", 1.0191d), + new EpsgOperationParameterRecord(1450, "EPSG code for Coord. Op. for northern boundary", 8047.0d), + new EpsgOperationParameterRecord(1450, "EPSG code for Coord. Op. for southern boundary", 8046.0d), + new EpsgOperationParameterRecord(1458, "X-axis translation", -129.193d), + new EpsgOperationParameterRecord(1458, "Y-axis translation", -41.212d), + new EpsgOperationParameterRecord(1458, "Z-axis translation", 130.73d), + new EpsgOperationParameterRecord(1458, "X-axis rotation", -0.246d), + new EpsgOperationParameterRecord(1458, "Y-axis rotation", -0.374d), + new EpsgOperationParameterRecord(1458, "Z-axis rotation", -0.329d), + new EpsgOperationParameterRecord(1458, "Scale difference", -2.955d), + new EpsgOperationParameterRecord(1459, "X-axis translation", -120.695d), + new EpsgOperationParameterRecord(1459, "Y-axis translation", -62.73d), + new EpsgOperationParameterRecord(1459, "Z-axis translation", 165.46d), + new EpsgOperationParameterRecord(1459, "X-axis rotation", -0.109d), + new EpsgOperationParameterRecord(1459, "Y-axis rotation", 0.141d), + new EpsgOperationParameterRecord(1459, "Z-axis rotation", 0.116d), + new EpsgOperationParameterRecord(1459, "Scale difference", 2.733d), + new EpsgOperationParameterRecord(1460, "X-axis translation", -119.353d), + new EpsgOperationParameterRecord(1460, "Y-axis translation", -48.301d), + new EpsgOperationParameterRecord(1460, "Z-axis translation", 139.484d), + new EpsgOperationParameterRecord(1460, "X-axis rotation", -0.415d), + new EpsgOperationParameterRecord(1460, "Y-axis rotation", -0.26d), + new EpsgOperationParameterRecord(1460, "Z-axis rotation", -0.437d), + new EpsgOperationParameterRecord(1460, "Scale difference", -0.613d), + new EpsgOperationParameterRecord(1469, "X-axis translation", -125.0d), + new EpsgOperationParameterRecord(1469, "Y-axis translation", 53.0d), + new EpsgOperationParameterRecord(1469, "Z-axis translation", 467.0d), + new EpsgOperationParameterRecord(1470, "X-axis translation", -124.76d), + new EpsgOperationParameterRecord(1470, "Y-axis translation", 53.0d), + new EpsgOperationParameterRecord(1470, "Z-axis translation", 466.79d), + new EpsgOperationParameterRecord(1504, "X-axis translation", -134.73d), + new EpsgOperationParameterRecord(1504, "Y-axis translation", -110.92d), + new EpsgOperationParameterRecord(1504, "Z-axis translation", -292.66d), + new EpsgOperationParameterRecord(1505, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1505, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1505, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1509, "X-axis translation", 674.374d), + new EpsgOperationParameterRecord(1509, "Y-axis translation", 15.056d), + new EpsgOperationParameterRecord(1509, "Z-axis translation", 405.346d), + new EpsgOperationParameterRecord(1511, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1511, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1511, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1512, "X-axis translation", -133.63d), + new EpsgOperationParameterRecord(1512, "Y-axis translation", -157.5d), + new EpsgOperationParameterRecord(1512, "Z-axis translation", -158.62d), + new EpsgOperationParameterRecord(1513, "X-axis translation", -241.54d), + new EpsgOperationParameterRecord(1513, "Y-axis translation", -163.64d), + new EpsgOperationParameterRecord(1513, "Z-axis translation", 396.06d), + new EpsgOperationParameterRecord(1514, "X-axis translation", -110.33d), + new EpsgOperationParameterRecord(1514, "Y-axis translation", -97.73d), + new EpsgOperationParameterRecord(1514, "Z-axis translation", -119.85d), + new EpsgOperationParameterRecord(1514, "X-axis rotation", 0.3423d), + new EpsgOperationParameterRecord(1514, "Y-axis rotation", 1.1634d), + new EpsgOperationParameterRecord(1514, "Z-axis rotation", 0.2715d), + new EpsgOperationParameterRecord(1514, "Scale difference", 0.063d), + new EpsgOperationParameterRecord(1516, "X-axis translation", -273.5d), + new EpsgOperationParameterRecord(1516, "Y-axis translation", 110.6d), + new EpsgOperationParameterRecord(1516, "Z-axis translation", -357.9d), + new EpsgOperationParameterRecord(1517, "X-axis translation", -23.0d), + new EpsgOperationParameterRecord(1517, "Y-axis translation", 259.0d), + new EpsgOperationParameterRecord(1517, "Z-axis translation", -9.0d), + new EpsgOperationParameterRecord(1518, "X-axis translation", -83.0d), + new EpsgOperationParameterRecord(1518, "Y-axis translation", 37.0d), + new EpsgOperationParameterRecord(1518, "Z-axis translation", 124.0d), + new EpsgOperationParameterRecord(1527, "X-axis translation", -154.5d), + new EpsgOperationParameterRecord(1527, "Y-axis translation", 150.7d), + new EpsgOperationParameterRecord(1527, "Z-axis translation", 100.4d), + new EpsgOperationParameterRecord(1528, "X-axis translation", 160.0d), + new EpsgOperationParameterRecord(1528, "Y-axis translation", 26.0d), + new EpsgOperationParameterRecord(1528, "Z-axis translation", 41.0d), + new EpsgOperationParameterRecord(1529, "X-axis translation", 18.38d), + new EpsgOperationParameterRecord(1529, "Y-axis translation", 192.45d), + new EpsgOperationParameterRecord(1529, "Z-axis translation", 96.82d), + new EpsgOperationParameterRecord(1529, "X-axis rotation", 0.056d), + new EpsgOperationParameterRecord(1529, "Y-axis rotation", -0.142d), + new EpsgOperationParameterRecord(1529, "Z-axis rotation", -0.2d), + new EpsgOperationParameterRecord(1529, "Scale difference", -0.0013d), + new EpsgOperationParameterRecord(1530, "X-axis translation", -4.2d), + new EpsgOperationParameterRecord(1530, "Y-axis translation", 135.4d), + new EpsgOperationParameterRecord(1530, "Z-axis translation", 181.9d), + new EpsgOperationParameterRecord(1531, "X-axis translation", -245.0d), + new EpsgOperationParameterRecord(1531, "Y-axis translation", -153.9d), + new EpsgOperationParameterRecord(1531, "Z-axis translation", 382.8d), + new EpsgOperationParameterRecord(1532, "X-axis translation", -80.7d), + new EpsgOperationParameterRecord(1532, "Y-axis translation", -132.5d), + new EpsgOperationParameterRecord(1532, "Z-axis translation", 41.1d), + new EpsgOperationParameterRecord(1533, "X-axis translation", 214.0d), + new EpsgOperationParameterRecord(1533, "Y-axis translation", 804.0d), + new EpsgOperationParameterRecord(1533, "Z-axis translation", 268.0d), + new EpsgOperationParameterRecord(1536, "X-axis translation", -250.2d), + new EpsgOperationParameterRecord(1536, "Y-axis translation", -153.09d), + new EpsgOperationParameterRecord(1536, "Z-axis translation", 391.7d), + new EpsgOperationParameterRecord(1537, "X-axis translation", 204.64d), + new EpsgOperationParameterRecord(1537, "Y-axis translation", 834.74d), + new EpsgOperationParameterRecord(1537, "Z-axis translation", 293.8d), + new EpsgOperationParameterRecord(1538, "X-axis translation", -260.1d), + new EpsgOperationParameterRecord(1538, "Y-axis translation", 5.5d), + new EpsgOperationParameterRecord(1538, "Z-axis translation", 432.2d), + new EpsgOperationParameterRecord(1539, "X-axis translation", -76.0d), + new EpsgOperationParameterRecord(1539, "Y-axis translation", -138.0d), + new EpsgOperationParameterRecord(1539, "Z-axis translation", 67.0d), + new EpsgOperationParameterRecord(1540, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1540, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1540, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1541, "X-axis translation", 199.0d), + new EpsgOperationParameterRecord(1541, "Y-axis translation", 931.0d), + new EpsgOperationParameterRecord(1541, "Z-axis translation", 317.0d), + new EpsgOperationParameterRecord(1542, "X-axis translation", 198.0d), + new EpsgOperationParameterRecord(1542, "Y-axis translation", 881.0d), + new EpsgOperationParameterRecord(1542, "Z-axis translation", 317.0d), + new EpsgOperationParameterRecord(1543, "X-axis translation", 182.0d), + new EpsgOperationParameterRecord(1543, "Y-axis translation", 915.0d), + new EpsgOperationParameterRecord(1543, "Z-axis translation", 344.0d), + new EpsgOperationParameterRecord(1544, "X-axis translation", -17.51d), + new EpsgOperationParameterRecord(1544, "Y-axis translation", -108.32d), + new EpsgOperationParameterRecord(1544, "Z-axis translation", -62.39d), + new EpsgOperationParameterRecord(1545, "X-axis translation", -121.8d), + new EpsgOperationParameterRecord(1545, "Y-axis translation", 98.1d), + new EpsgOperationParameterRecord(1545, "Z-axis translation", -15.2d), + new EpsgOperationParameterRecord(1547, "X-axis translation", -173.0d), + new EpsgOperationParameterRecord(1547, "Y-axis translation", 253.0d), + new EpsgOperationParameterRecord(1547, "Z-axis translation", 27.0d), + new EpsgOperationParameterRecord(1550, "X-axis translation", -139.62d), + new EpsgOperationParameterRecord(1550, "Y-axis translation", 290.53d), + new EpsgOperationParameterRecord(1550, "Z-axis translation", -150.29d), + new EpsgOperationParameterRecord(1551, "X-axis translation", -141.15d), + new EpsgOperationParameterRecord(1551, "Y-axis translation", 293.44d), + new EpsgOperationParameterRecord(1551, "Z-axis translation", -150.56d), + new EpsgOperationParameterRecord(1552, "X-axis translation", -142.48d), + new EpsgOperationParameterRecord(1552, "Y-axis translation", 296.03d), + new EpsgOperationParameterRecord(1552, "Z-axis translation", -149.74d), + new EpsgOperationParameterRecord(1555, "X-axis translation", -0.465d), + new EpsgOperationParameterRecord(1555, "Y-axis translation", 372.095d), + new EpsgOperationParameterRecord(1555, "Z-axis translation", 171.736d), + new EpsgOperationParameterRecord(1556, "X-axis translation", -2.0d), + new EpsgOperationParameterRecord(1556, "Y-axis translation", 374.0d), + new EpsgOperationParameterRecord(1556, "Z-axis translation", 172.0d), + new EpsgOperationParameterRecord(1557, "X-axis translation", -254.1d), + new EpsgOperationParameterRecord(1557, "Y-axis translation", -5.36d), + new EpsgOperationParameterRecord(1557, "Z-axis translation", -100.29d), + new EpsgOperationParameterRecord(1558, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1558, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1558, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1560, "X-axis translation", -156.5d), + new EpsgOperationParameterRecord(1560, "Y-axis translation", -87.2d), + new EpsgOperationParameterRecord(1560, "Z-axis translation", 285.9d), + new EpsgOperationParameterRecord(1561, "X-axis translation", -128.0d), + new EpsgOperationParameterRecord(1561, "Y-axis translation", -283.0d), + new EpsgOperationParameterRecord(1561, "Z-axis translation", 22.0d), + new EpsgOperationParameterRecord(1562, "X-axis translation", -128.16d), + new EpsgOperationParameterRecord(1562, "Y-axis translation", -282.42d), + new EpsgOperationParameterRecord(1562, "Z-axis translation", 21.93d), + new EpsgOperationParameterRecord(1563, "X-axis translation", -128.033d), + new EpsgOperationParameterRecord(1563, "Y-axis translation", -283.697d), + new EpsgOperationParameterRecord(1563, "Z-axis translation", 21.052d), + new EpsgOperationParameterRecord(1564, "X-axis translation", 59.47d), + new EpsgOperationParameterRecord(1564, "Y-axis translation", -5.04d), + new EpsgOperationParameterRecord(1564, "Z-axis translation", 187.44d), + new EpsgOperationParameterRecord(1564, "X-axis rotation", -0.47d), + new EpsgOperationParameterRecord(1564, "Y-axis rotation", 0.1d), + new EpsgOperationParameterRecord(1564, "Z-axis rotation", -1.024d), + new EpsgOperationParameterRecord(1564, "Scale difference", -4.5993d), + new EpsgOperationParameterRecord(1565, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1565, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1565, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1566, "X-axis translation", 54.4d), + new EpsgOperationParameterRecord(1566, "Y-axis translation", -20.1d), + new EpsgOperationParameterRecord(1566, "Z-axis translation", 183.1d), + new EpsgOperationParameterRecord(1569, "X-axis translation", -199.0d), + new EpsgOperationParameterRecord(1569, "Y-axis translation", 32.0d), + new EpsgOperationParameterRecord(1569, "Z-axis translation", 322.0d), + new EpsgOperationParameterRecord(1570, "X-axis translation", -171.16d), + new EpsgOperationParameterRecord(1570, "Y-axis translation", 17.29d), + new EpsgOperationParameterRecord(1570, "Z-axis translation", 323.31d), + new EpsgOperationParameterRecord(1577, "X-axis translation", -115.0d), + new EpsgOperationParameterRecord(1577, "Y-axis translation", 118.0d), + new EpsgOperationParameterRecord(1577, "Z-axis translation", 426.0d), + new EpsgOperationParameterRecord(1580, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1580, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1580, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1581, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1581, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1581, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1582, "X-axis translation", -259.73d), + new EpsgOperationParameterRecord(1582, "Y-axis translation", 173.12d), + new EpsgOperationParameterRecord(1582, "Z-axis translation", -398.27d), + new EpsgOperationParameterRecord(1583, "X-axis translation", -307.7d), + new EpsgOperationParameterRecord(1583, "Y-axis translation", 265.3d), + new EpsgOperationParameterRecord(1583, "Z-axis translation", -363.5d), + new EpsgOperationParameterRecord(1584, "X-axis translation", -174.6d), + new EpsgOperationParameterRecord(1584, "Y-axis translation", -3.1d), + new EpsgOperationParameterRecord(1584, "Z-axis translation", 236.2d), + new EpsgOperationParameterRecord(1586, "X-axis translation", -175.09d), + new EpsgOperationParameterRecord(1586, "Y-axis translation", 1.218d), + new EpsgOperationParameterRecord(1586, "Z-axis translation", 238.831d), + new EpsgOperationParameterRecord(1586, "X-axis rotation", -0.047d), + new EpsgOperationParameterRecord(1586, "Y-axis rotation", 0.019d), + new EpsgOperationParameterRecord(1586, "Z-axis rotation", 0.808d), + new EpsgOperationParameterRecord(1586, "Scale difference", 0.1698d), + new EpsgOperationParameterRecord(1587, "X-axis translation", -191.77d), + new EpsgOperationParameterRecord(1587, "Y-axis translation", 15.01d), + new EpsgOperationParameterRecord(1587, "Z-axis translation", 235.07d), + new EpsgOperationParameterRecord(1588, "X-axis translation", -116.641d), + new EpsgOperationParameterRecord(1588, "Y-axis translation", -56.931d), + new EpsgOperationParameterRecord(1588, "Z-axis translation", -110.559d), + new EpsgOperationParameterRecord(1588, "X-axis rotation", 4.327d), + new EpsgOperationParameterRecord(1588, "Y-axis rotation", 4.464d), + new EpsgOperationParameterRecord(1588, "Z-axis rotation", -4.444d), + new EpsgOperationParameterRecord(1588, "Scale difference", -3.52d), + new EpsgOperationParameterRecord(1589, "EPSG code for Coord. Op. for northern boundary", 1588.0d), + new EpsgOperationParameterRecord(1589, "EPSG code for Coord. Op. for southern boundary", 8570.0d), + new EpsgOperationParameterRecord(1590, "EPSG code for Coord. Op. for northern boundary", 8569.0d), + new EpsgOperationParameterRecord(1590, "EPSG code for Coord. Op. for southern boundary", 8046.0d), + new EpsgOperationParameterRecord(1592, "X-axis translation", -678.0d), + new EpsgOperationParameterRecord(1592, "Y-axis translation", 670.0d), + new EpsgOperationParameterRecord(1592, "Z-axis translation", -48.0d), + new EpsgOperationParameterRecord(1594, "X-axis translation", -120.271d), + new EpsgOperationParameterRecord(1594, "Y-axis translation", -64.543d), + new EpsgOperationParameterRecord(1594, "Z-axis translation", 161.632d), + new EpsgOperationParameterRecord(1594, "X-axis rotation", -0.217d), + new EpsgOperationParameterRecord(1594, "Y-axis rotation", 0.067d), + new EpsgOperationParameterRecord(1594, "Z-axis rotation", 0.129d), + new EpsgOperationParameterRecord(1594, "Scale difference", 2.499d), + new EpsgOperationParameterRecord(1595, "X-axis translation", -124.133d), + new EpsgOperationParameterRecord(1595, "Y-axis translation", -42.003d), + new EpsgOperationParameterRecord(1595, "Z-axis translation", 137.4d), + new EpsgOperationParameterRecord(1595, "X-axis rotation", 0.008d), + new EpsgOperationParameterRecord(1595, "Y-axis rotation", -0.557d), + new EpsgOperationParameterRecord(1595, "Z-axis rotation", -0.178d), + new EpsgOperationParameterRecord(1595, "Scale difference", -1.854d), + new EpsgOperationParameterRecord(1597, "X-axis translation", 304.5d), + new EpsgOperationParameterRecord(1597, "Y-axis translation", 306.5d), + new EpsgOperationParameterRecord(1597, "Z-axis translation", -318.1d), + new EpsgOperationParameterRecord(1609, "X-axis translation", -99.059d), + new EpsgOperationParameterRecord(1609, "Y-axis translation", 53.322d), + new EpsgOperationParameterRecord(1609, "Z-axis translation", -112.486d), + new EpsgOperationParameterRecord(1609, "X-axis rotation", -0.419d), + new EpsgOperationParameterRecord(1609, "Y-axis rotation", 0.83d), + new EpsgOperationParameterRecord(1609, "Z-axis rotation", -1.885d), + new EpsgOperationParameterRecord(1609, "Scale difference", -1.0d), + new EpsgOperationParameterRecord(1610, "X-axis translation", -125.8d), + new EpsgOperationParameterRecord(1610, "Y-axis translation", 79.9d), + new EpsgOperationParameterRecord(1610, "Z-axis translation", -100.5d), + new EpsgOperationParameterRecord(1612, "X-axis translation", -116.641d), + new EpsgOperationParameterRecord(1612, "Y-axis translation", -56.931d), + new EpsgOperationParameterRecord(1612, "Z-axis translation", -110.559d), + new EpsgOperationParameterRecord(1612, "X-axis rotation", 0.893d), + new EpsgOperationParameterRecord(1612, "Y-axis rotation", 0.921d), + new EpsgOperationParameterRecord(1612, "Z-axis rotation", -0.917d), + new EpsgOperationParameterRecord(1612, "Scale difference", -3.52d), + new EpsgOperationParameterRecord(1613, "X-axis translation", -90.365d), + new EpsgOperationParameterRecord(1613, "Y-axis translation", -101.13d), + new EpsgOperationParameterRecord(1613, "Z-axis translation", -123.384d), + new EpsgOperationParameterRecord(1613, "X-axis rotation", 0.333d), + new EpsgOperationParameterRecord(1613, "Y-axis rotation", 0.077d), + new EpsgOperationParameterRecord(1613, "Z-axis rotation", 0.894d), + new EpsgOperationParameterRecord(1613, "Scale difference", 1.994d), + new EpsgOperationParameterRecord(1614, "X-axis translation", -88.0d), + new EpsgOperationParameterRecord(1614, "Y-axis translation", 4.0d), + new EpsgOperationParameterRecord(1614, "Z-axis translation", 101.0d), + new EpsgOperationParameterRecord(1615, "X-axis translation", -726.282d), + new EpsgOperationParameterRecord(1615, "Y-axis translation", 703.611d), + new EpsgOperationParameterRecord(1615, "Z-axis translation", -48.999d), + new EpsgOperationParameterRecord(1616, "X-axis translation", -182.046d), + new EpsgOperationParameterRecord(1616, "Y-axis translation", -225.604d), + new EpsgOperationParameterRecord(1616, "Z-axis translation", 168.884d), + new EpsgOperationParameterRecord(1616, "X-axis rotation", -0.616d), + new EpsgOperationParameterRecord(1616, "Y-axis rotation", -1.655d), + new EpsgOperationParameterRecord(1616, "Z-axis rotation", 7.824d), + new EpsgOperationParameterRecord(1616, "Scale difference", 16.641d), + new EpsgOperationParameterRecord(1617, "X-axis translation", -191.808d), + new EpsgOperationParameterRecord(1617, "Y-axis translation", -250.512d), + new EpsgOperationParameterRecord(1617, "Z-axis translation", 167.861d), + new EpsgOperationParameterRecord(1617, "X-axis rotation", -0.792d), + new EpsgOperationParameterRecord(1617, "Y-axis rotation", -1.653d), + new EpsgOperationParameterRecord(1617, "Z-axis rotation", 8.558d), + new EpsgOperationParameterRecord(1617, "Scale difference", 20.703d), + new EpsgOperationParameterRecord(1618, "X-axis translation", 577.326d), + new EpsgOperationParameterRecord(1618, "Y-axis translation", 90.129d), + new EpsgOperationParameterRecord(1618, "Z-axis translation", 463.919d), + new EpsgOperationParameterRecord(1618, "X-axis rotation", 5.137d), + new EpsgOperationParameterRecord(1618, "Y-axis rotation", 1.474d), + new EpsgOperationParameterRecord(1618, "Z-axis rotation", 5.297d), + new EpsgOperationParameterRecord(1618, "Scale difference", 2.4232d), + new EpsgOperationParameterRecord(1619, "X-axis translation", 577.326d), + new EpsgOperationParameterRecord(1619, "Y-axis translation", 90.129d), + new EpsgOperationParameterRecord(1619, "Z-axis translation", 463.919d), + new EpsgOperationParameterRecord(1619, "X-axis rotation", 5.137d), + new EpsgOperationParameterRecord(1619, "Y-axis rotation", 1.474d), + new EpsgOperationParameterRecord(1619, "Z-axis rotation", 5.297d), + new EpsgOperationParameterRecord(1619, "Scale difference", 2.4232d), + new EpsgOperationParameterRecord(1622, "X-axis translation", 570.8d), + new EpsgOperationParameterRecord(1622, "Y-axis translation", 85.7d), + new EpsgOperationParameterRecord(1622, "Z-axis translation", 462.8d), + new EpsgOperationParameterRecord(1622, "X-axis rotation", 4.998d), + new EpsgOperationParameterRecord(1622, "Y-axis rotation", 1.587d), + new EpsgOperationParameterRecord(1622, "Z-axis rotation", 5.261d), + new EpsgOperationParameterRecord(1622, "Scale difference", 3.56d), + new EpsgOperationParameterRecord(1623, "X-axis translation", 570.8d), + new EpsgOperationParameterRecord(1623, "Y-axis translation", 85.7d), + new EpsgOperationParameterRecord(1623, "Z-axis translation", 462.8d), + new EpsgOperationParameterRecord(1623, "X-axis rotation", 4.998d), + new EpsgOperationParameterRecord(1623, "Y-axis rotation", 1.587d), + new EpsgOperationParameterRecord(1623, "Z-axis rotation", 5.261d), + new EpsgOperationParameterRecord(1623, "Scale difference", 3.56d), + new EpsgOperationParameterRecord(1626, "X-axis translation", -81.1d), + new EpsgOperationParameterRecord(1626, "Y-axis translation", -89.4d), + new EpsgOperationParameterRecord(1626, "Z-axis translation", -115.8d), + new EpsgOperationParameterRecord(1626, "X-axis rotation", 0.485d), + new EpsgOperationParameterRecord(1626, "Y-axis rotation", 0.024d), + new EpsgOperationParameterRecord(1626, "Z-axis rotation", 0.413d), + new EpsgOperationParameterRecord(1626, "Scale difference", -0.54d), + new EpsgOperationParameterRecord(1627, "X-axis translation", -81.1d), + new EpsgOperationParameterRecord(1627, "Y-axis translation", -89.4d), + new EpsgOperationParameterRecord(1627, "Z-axis translation", -115.8d), + new EpsgOperationParameterRecord(1627, "X-axis rotation", 0.485d), + new EpsgOperationParameterRecord(1627, "Y-axis rotation", 0.024d), + new EpsgOperationParameterRecord(1627, "Z-axis rotation", 0.413d), + new EpsgOperationParameterRecord(1627, "Scale difference", -0.54d), + new EpsgOperationParameterRecord(1628, "X-axis translation", -116.8d), + new EpsgOperationParameterRecord(1628, "Y-axis translation", -106.4d), + new EpsgOperationParameterRecord(1628, "Z-axis translation", -154.4d), + new EpsgOperationParameterRecord(1629, "X-axis translation", -116.8d), + new EpsgOperationParameterRecord(1629, "Y-axis translation", -106.4d), + new EpsgOperationParameterRecord(1629, "Z-axis translation", -154.4d), + new EpsgOperationParameterRecord(1630, "X-axis translation", -181.5d), + new EpsgOperationParameterRecord(1630, "Y-axis translation", -90.3d), + new EpsgOperationParameterRecord(1630, "Z-axis translation", -187.2d), + new EpsgOperationParameterRecord(1630, "X-axis rotation", 0.144d), + new EpsgOperationParameterRecord(1630, "Y-axis rotation", 0.492d), + new EpsgOperationParameterRecord(1630, "Z-axis rotation", -0.394d), + new EpsgOperationParameterRecord(1630, "Scale difference", 17.57d), + new EpsgOperationParameterRecord(1631, "X-axis translation", -181.5d), + new EpsgOperationParameterRecord(1631, "Y-axis translation", -90.3d), + new EpsgOperationParameterRecord(1631, "Z-axis translation", -187.2d), + new EpsgOperationParameterRecord(1631, "X-axis rotation", 0.144d), + new EpsgOperationParameterRecord(1631, "Y-axis rotation", 0.492d), + new EpsgOperationParameterRecord(1631, "Z-axis rotation", -0.394d), + new EpsgOperationParameterRecord(1631, "Scale difference", 17.57d), + new EpsgOperationParameterRecord(1632, "X-axis translation", -131.0d), + new EpsgOperationParameterRecord(1632, "Y-axis translation", -100.3d), + new EpsgOperationParameterRecord(1632, "Z-axis translation", -163.4d), + new EpsgOperationParameterRecord(1632, "X-axis rotation", -1.244d), + new EpsgOperationParameterRecord(1632, "Y-axis rotation", -0.02d), + new EpsgOperationParameterRecord(1632, "Z-axis rotation", -1.144d), + new EpsgOperationParameterRecord(1632, "Scale difference", 9.39d), + new EpsgOperationParameterRecord(1633, "X-axis translation", -131.0d), + new EpsgOperationParameterRecord(1633, "Y-axis translation", -100.3d), + new EpsgOperationParameterRecord(1633, "Z-axis translation", -163.4d), + new EpsgOperationParameterRecord(1633, "X-axis rotation", -1.244d), + new EpsgOperationParameterRecord(1633, "Y-axis rotation", -0.02d), + new EpsgOperationParameterRecord(1633, "Z-axis rotation", -1.144d), + new EpsgOperationParameterRecord(1633, "Scale difference", 9.39d), + new EpsgOperationParameterRecord(1634, "X-axis translation", -178.4d), + new EpsgOperationParameterRecord(1634, "Y-axis translation", -83.2d), + new EpsgOperationParameterRecord(1634, "Z-axis translation", -221.3d), + new EpsgOperationParameterRecord(1634, "X-axis rotation", 0.54d), + new EpsgOperationParameterRecord(1634, "Y-axis rotation", -0.532d), + new EpsgOperationParameterRecord(1634, "Z-axis rotation", -0.126d), + new EpsgOperationParameterRecord(1634, "Scale difference", 21.2d), + new EpsgOperationParameterRecord(1635, "X-axis translation", -178.4d), + new EpsgOperationParameterRecord(1635, "Y-axis translation", -83.2d), + new EpsgOperationParameterRecord(1635, "Z-axis translation", -221.3d), + new EpsgOperationParameterRecord(1635, "X-axis rotation", 0.54d), + new EpsgOperationParameterRecord(1635, "Y-axis rotation", -0.532d), + new EpsgOperationParameterRecord(1635, "Z-axis rotation", -0.126d), + new EpsgOperationParameterRecord(1635, "Scale difference", 21.2d), + new EpsgOperationParameterRecord(1638, "X-axis translation", -90.7d), + new EpsgOperationParameterRecord(1638, "Y-axis translation", -106.1d), + new EpsgOperationParameterRecord(1638, "Z-axis translation", -119.2d), + new EpsgOperationParameterRecord(1638, "X-axis rotation", 4.09d), + new EpsgOperationParameterRecord(1638, "Y-axis rotation", 0.218d), + new EpsgOperationParameterRecord(1638, "Z-axis rotation", -1.05d), + new EpsgOperationParameterRecord(1638, "Scale difference", 1.37d), + new EpsgOperationParameterRecord(1639, "X-axis translation", -90.7d), + new EpsgOperationParameterRecord(1639, "Y-axis translation", -106.1d), + new EpsgOperationParameterRecord(1639, "Z-axis translation", -119.2d), + new EpsgOperationParameterRecord(1639, "X-axis rotation", 4.09d), + new EpsgOperationParameterRecord(1639, "Y-axis rotation", 0.218d), + new EpsgOperationParameterRecord(1639, "Z-axis rotation", -1.05d), + new EpsgOperationParameterRecord(1639, "Scale difference", 1.37d), + new EpsgOperationParameterRecord(1641, "X-axis translation", 482.5d), + new EpsgOperationParameterRecord(1641, "Y-axis translation", -130.6d), + new EpsgOperationParameterRecord(1641, "Z-axis translation", 564.6d), + new EpsgOperationParameterRecord(1641, "X-axis rotation", -1.042d), + new EpsgOperationParameterRecord(1641, "Y-axis rotation", -0.214d), + new EpsgOperationParameterRecord(1641, "Z-axis rotation", -0.631d), + new EpsgOperationParameterRecord(1641, "Scale difference", 8.15d), + new EpsgOperationParameterRecord(1642, "X-axis translation", -193.0d), + new EpsgOperationParameterRecord(1642, "Y-axis translation", 13.7d), + new EpsgOperationParameterRecord(1642, "Z-axis translation", -39.3d), + new EpsgOperationParameterRecord(1642, "X-axis rotation", -0.41d), + new EpsgOperationParameterRecord(1642, "Y-axis rotation", -2.933d), + new EpsgOperationParameterRecord(1642, "Z-axis rotation", 2.688d), + new EpsgOperationParameterRecord(1642, "Scale difference", 0.43d), + new EpsgOperationParameterRecord(1643, "X-axis translation", -193.0d), + new EpsgOperationParameterRecord(1643, "Y-axis translation", 13.7d), + new EpsgOperationParameterRecord(1643, "Z-axis translation", -39.3d), + new EpsgOperationParameterRecord(1643, "X-axis rotation", -0.41d), + new EpsgOperationParameterRecord(1643, "Y-axis rotation", -2.933d), + new EpsgOperationParameterRecord(1643, "Z-axis rotation", 2.688d), + new EpsgOperationParameterRecord(1643, "Scale difference", 0.43d), + new EpsgOperationParameterRecord(1644, "X-axis translation", 33.4d), + new EpsgOperationParameterRecord(1644, "Y-axis translation", -146.6d), + new EpsgOperationParameterRecord(1644, "Z-axis translation", -76.3d), + new EpsgOperationParameterRecord(1644, "X-axis rotation", -0.359d), + new EpsgOperationParameterRecord(1644, "Y-axis rotation", -0.053d), + new EpsgOperationParameterRecord(1644, "Z-axis rotation", 0.844d), + new EpsgOperationParameterRecord(1644, "Scale difference", -0.84d), + new EpsgOperationParameterRecord(1645, "X-axis translation", 33.4d), + new EpsgOperationParameterRecord(1645, "Y-axis translation", -146.6d), + new EpsgOperationParameterRecord(1645, "Z-axis translation", -76.3d), + new EpsgOperationParameterRecord(1645, "X-axis rotation", -0.359d), + new EpsgOperationParameterRecord(1645, "Y-axis rotation", -0.053d), + new EpsgOperationParameterRecord(1645, "Z-axis rotation", 0.844d), + new EpsgOperationParameterRecord(1645, "Scale difference", -0.84d), + new EpsgOperationParameterRecord(1646, "X-axis translation", 674.374d), + new EpsgOperationParameterRecord(1646, "Y-axis translation", 15.056d), + new EpsgOperationParameterRecord(1646, "Z-axis translation", 405.346d), + new EpsgOperationParameterRecord(1647, "X-axis translation", 674.374d), + new EpsgOperationParameterRecord(1647, "Y-axis translation", 15.056d), + new EpsgOperationParameterRecord(1647, "Z-axis translation", 405.346d), + new EpsgOperationParameterRecord(1649, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1649, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1649, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1650, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(1650, "Y-axis translation", -97.0d), + new EpsgOperationParameterRecord(1650, "Z-axis translation", -117.0d), + new EpsgOperationParameterRecord(1651, "X-axis translation", -168.0d), + new EpsgOperationParameterRecord(1651, "Y-axis translation", -60.0d), + new EpsgOperationParameterRecord(1651, "Z-axis translation", 320.0d), + new EpsgOperationParameterRecord(1652, "X-axis translation", -99.1d), + new EpsgOperationParameterRecord(1652, "Y-axis translation", 53.3d), + new EpsgOperationParameterRecord(1652, "Z-axis translation", -112.5d), + new EpsgOperationParameterRecord(1652, "X-axis rotation", 0.419d), + new EpsgOperationParameterRecord(1652, "Y-axis rotation", -0.83d), + new EpsgOperationParameterRecord(1652, "Z-axis rotation", 1.885d), + new EpsgOperationParameterRecord(1652, "Scale difference", -1.0d), + new EpsgOperationParameterRecord(1653, "X-axis translation", 278.3d), + new EpsgOperationParameterRecord(1653, "Y-axis translation", 93.0d), + new EpsgOperationParameterRecord(1653, "Z-axis translation", 474.5d), + new EpsgOperationParameterRecord(1653, "X-axis rotation", 7.889d), + new EpsgOperationParameterRecord(1653, "Y-axis rotation", 0.05d), + new EpsgOperationParameterRecord(1653, "Z-axis rotation", -6.61d), + new EpsgOperationParameterRecord(1653, "Scale difference", 6.21d), + new EpsgOperationParameterRecord(1654, "X-axis translation", 278.3d), + new EpsgOperationParameterRecord(1654, "Y-axis translation", 93.0d), + new EpsgOperationParameterRecord(1654, "Z-axis translation", 474.5d), + new EpsgOperationParameterRecord(1654, "X-axis rotation", 7.889d), + new EpsgOperationParameterRecord(1654, "Y-axis rotation", 0.05d), + new EpsgOperationParameterRecord(1654, "Z-axis rotation", -6.61d), + new EpsgOperationParameterRecord(1654, "Scale difference", 6.21d), + new EpsgOperationParameterRecord(1655, "X-axis translation", -280.9d), + new EpsgOperationParameterRecord(1655, "Y-axis translation", -89.8d), + new EpsgOperationParameterRecord(1655, "Z-axis translation", 130.2d), + new EpsgOperationParameterRecord(1655, "X-axis rotation", -1.721d), + new EpsgOperationParameterRecord(1655, "Y-axis rotation", 0.355d), + new EpsgOperationParameterRecord(1655, "Z-axis rotation", -0.371d), + new EpsgOperationParameterRecord(1655, "Scale difference", -5.92d), + new EpsgOperationParameterRecord(1656, "X-axis translation", -280.9d), + new EpsgOperationParameterRecord(1656, "Y-axis translation", -89.8d), + new EpsgOperationParameterRecord(1656, "Z-axis translation", 130.2d), + new EpsgOperationParameterRecord(1656, "X-axis rotation", -1.721d), + new EpsgOperationParameterRecord(1656, "Y-axis rotation", 0.355d), + new EpsgOperationParameterRecord(1656, "Z-axis rotation", -0.371d), + new EpsgOperationParameterRecord(1656, "Scale difference", -5.92d), + new EpsgOperationParameterRecord(1657, "X-axis translation", -238.2d), + new EpsgOperationParameterRecord(1657, "Y-axis translation", 85.2d), + new EpsgOperationParameterRecord(1657, "Z-axis translation", 29.9d), + new EpsgOperationParameterRecord(1657, "X-axis rotation", 0.166d), + new EpsgOperationParameterRecord(1657, "Y-axis rotation", 0.046d), + new EpsgOperationParameterRecord(1657, "Z-axis rotation", 1.248d), + new EpsgOperationParameterRecord(1657, "Scale difference", 2.03d), + new EpsgOperationParameterRecord(1658, "X-axis translation", -238.2d), + new EpsgOperationParameterRecord(1658, "Y-axis translation", 85.2d), + new EpsgOperationParameterRecord(1658, "Z-axis translation", 29.9d), + new EpsgOperationParameterRecord(1658, "X-axis rotation", 0.166d), + new EpsgOperationParameterRecord(1658, "Y-axis rotation", 0.046d), + new EpsgOperationParameterRecord(1658, "Z-axis rotation", 1.248d), + new EpsgOperationParameterRecord(1658, "Scale difference", 2.03d), + new EpsgOperationParameterRecord(1659, "X-axis translation", -104.1d), + new EpsgOperationParameterRecord(1659, "Y-axis translation", -49.1d), + new EpsgOperationParameterRecord(1659, "Z-axis translation", -9.9d), + new EpsgOperationParameterRecord(1659, "X-axis rotation", 0.971d), + new EpsgOperationParameterRecord(1659, "Y-axis rotation", -2.917d), + new EpsgOperationParameterRecord(1659, "Z-axis rotation", 0.714d), + new EpsgOperationParameterRecord(1659, "Scale difference", -11.68d), + new EpsgOperationParameterRecord(1660, "X-axis translation", -104.1d), + new EpsgOperationParameterRecord(1660, "Y-axis translation", -49.1d), + new EpsgOperationParameterRecord(1660, "Z-axis translation", -9.9d), + new EpsgOperationParameterRecord(1660, "X-axis rotation", 0.971d), + new EpsgOperationParameterRecord(1660, "Y-axis rotation", -2.917d), + new EpsgOperationParameterRecord(1660, "Z-axis rotation", 0.714d), + new EpsgOperationParameterRecord(1660, "Scale difference", -11.68d), + new EpsgOperationParameterRecord(1661, "X-axis translation", -168.6d), + new EpsgOperationParameterRecord(1661, "Y-axis translation", -34.0d), + new EpsgOperationParameterRecord(1661, "Z-axis translation", 38.6d), + new EpsgOperationParameterRecord(1661, "X-axis rotation", -0.374d), + new EpsgOperationParameterRecord(1661, "Y-axis rotation", -0.679d), + new EpsgOperationParameterRecord(1661, "Z-axis rotation", -1.379d), + new EpsgOperationParameterRecord(1661, "Scale difference", -9.48d), + new EpsgOperationParameterRecord(1662, "X-axis translation", -168.6d), + new EpsgOperationParameterRecord(1662, "Y-axis translation", -34.0d), + new EpsgOperationParameterRecord(1662, "Z-axis translation", 38.6d), + new EpsgOperationParameterRecord(1662, "X-axis rotation", -0.374d), + new EpsgOperationParameterRecord(1662, "Y-axis rotation", -0.679d), + new EpsgOperationParameterRecord(1662, "Z-axis rotation", -1.379d), + new EpsgOperationParameterRecord(1662, "Scale difference", -9.48d), + new EpsgOperationParameterRecord(1663, "X-axis translation", -50.2d), + new EpsgOperationParameterRecord(1663, "Y-axis translation", -50.4d), + new EpsgOperationParameterRecord(1663, "Z-axis translation", 84.8d), + new EpsgOperationParameterRecord(1663, "X-axis rotation", -0.69d), + new EpsgOperationParameterRecord(1663, "Y-axis rotation", -2.012d), + new EpsgOperationParameterRecord(1663, "Z-axis rotation", 0.459d), + new EpsgOperationParameterRecord(1663, "Scale difference", -28.08d), + new EpsgOperationParameterRecord(1664, "X-axis translation", -50.2d), + new EpsgOperationParameterRecord(1664, "Y-axis translation", -50.4d), + new EpsgOperationParameterRecord(1664, "Z-axis translation", 84.8d), + new EpsgOperationParameterRecord(1664, "X-axis rotation", -0.69d), + new EpsgOperationParameterRecord(1664, "Y-axis rotation", -2.012d), + new EpsgOperationParameterRecord(1664, "Z-axis rotation", 0.459d), + new EpsgOperationParameterRecord(1664, "Scale difference", -28.08d), + new EpsgOperationParameterRecord(1665, "X-axis translation", -129.193d), + new EpsgOperationParameterRecord(1665, "Y-axis translation", -41.212d), + new EpsgOperationParameterRecord(1665, "Z-axis translation", 130.73d), + new EpsgOperationParameterRecord(1665, "X-axis rotation", -0.246d), + new EpsgOperationParameterRecord(1665, "Y-axis rotation", -0.374d), + new EpsgOperationParameterRecord(1665, "Z-axis rotation", -0.329d), + new EpsgOperationParameterRecord(1665, "Scale difference", -2.955d), + new EpsgOperationParameterRecord(1666, "X-axis translation", -119.353d), + new EpsgOperationParameterRecord(1666, "Y-axis translation", -48.301d), + new EpsgOperationParameterRecord(1666, "Z-axis translation", 139.484d), + new EpsgOperationParameterRecord(1666, "X-axis rotation", -0.415d), + new EpsgOperationParameterRecord(1666, "Y-axis rotation", -0.26d), + new EpsgOperationParameterRecord(1666, "Z-axis rotation", -0.437d), + new EpsgOperationParameterRecord(1666, "Scale difference", -0.613d), + new EpsgOperationParameterRecord(1667, "X-axis translation", -120.271d), + new EpsgOperationParameterRecord(1667, "Y-axis translation", -64.543d), + new EpsgOperationParameterRecord(1667, "Z-axis translation", 161.632d), + new EpsgOperationParameterRecord(1667, "X-axis rotation", -0.217d), + new EpsgOperationParameterRecord(1667, "Y-axis rotation", 0.067d), + new EpsgOperationParameterRecord(1667, "Z-axis rotation", 0.129d), + new EpsgOperationParameterRecord(1667, "Scale difference", 2.499d), + new EpsgOperationParameterRecord(1668, "X-axis translation", -124.133d), + new EpsgOperationParameterRecord(1668, "Y-axis translation", -42.003d), + new EpsgOperationParameterRecord(1668, "Z-axis translation", 137.4d), + new EpsgOperationParameterRecord(1668, "X-axis rotation", 0.008d), + new EpsgOperationParameterRecord(1668, "Y-axis rotation", -0.557d), + new EpsgOperationParameterRecord(1668, "Z-axis rotation", -0.178d), + new EpsgOperationParameterRecord(1668, "Scale difference", -1.854d), + new EpsgOperationParameterRecord(1669, "X-axis translation", -117.763d), + new EpsgOperationParameterRecord(1669, "Y-axis translation", -51.51d), + new EpsgOperationParameterRecord(1669, "Z-axis translation", 139.061d), + new EpsgOperationParameterRecord(1669, "X-axis rotation", -0.292d), + new EpsgOperationParameterRecord(1669, "Y-axis rotation", -0.443d), + new EpsgOperationParameterRecord(1669, "Z-axis rotation", -0.277d), + new EpsgOperationParameterRecord(1669, "Scale difference", -0.191d), + new EpsgOperationParameterRecord(1671, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1671, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1671, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1672, "X-axis translation", 565.04d), + new EpsgOperationParameterRecord(1672, "Y-axis translation", 49.91d), + new EpsgOperationParameterRecord(1672, "Z-axis translation", 465.84d), + new EpsgOperationParameterRecord(1672, "X-axis rotation", 1.9848d), + new EpsgOperationParameterRecord(1672, "Y-axis rotation", -1.7439d), + new EpsgOperationParameterRecord(1672, "Z-axis rotation", 9.0587d), + new EpsgOperationParameterRecord(1672, "Scale difference", 4.0772d), + new EpsgOperationParameterRecord(1673, "X-axis translation", 582.0d), + new EpsgOperationParameterRecord(1673, "Y-axis translation", 105.0d), + new EpsgOperationParameterRecord(1673, "Z-axis translation", 414.0d), + new EpsgOperationParameterRecord(1673, "X-axis rotation", -1.04d), + new EpsgOperationParameterRecord(1673, "Y-axis rotation", -0.35d), + new EpsgOperationParameterRecord(1673, "Z-axis rotation", 3.08d), + new EpsgOperationParameterRecord(1673, "Scale difference", 8.3d), + new EpsgOperationParameterRecord(1674, "X-axis translation", 24.0d), + new EpsgOperationParameterRecord(1674, "Y-axis translation", -123.0d), + new EpsgOperationParameterRecord(1674, "Z-axis translation", -94.0d), + new EpsgOperationParameterRecord(1674, "X-axis rotation", -0.02d), + new EpsgOperationParameterRecord(1674, "Y-axis rotation", 0.25d), + new EpsgOperationParameterRecord(1674, "Z-axis rotation", 0.13d), + new EpsgOperationParameterRecord(1674, "Scale difference", 1.1d), + new EpsgOperationParameterRecord(1675, "X-axis translation", 24.0d), + new EpsgOperationParameterRecord(1675, "Y-axis translation", -123.0d), + new EpsgOperationParameterRecord(1675, "Z-axis translation", -94.0d), + new EpsgOperationParameterRecord(1675, "X-axis rotation", -0.02d), + new EpsgOperationParameterRecord(1675, "Y-axis rotation", 0.25d), + new EpsgOperationParameterRecord(1675, "Z-axis rotation", 0.13d), + new EpsgOperationParameterRecord(1675, "Scale difference", 1.1d), + new EpsgOperationParameterRecord(1676, "X-axis translation", 674.374d), + new EpsgOperationParameterRecord(1676, "Y-axis translation", 15.056d), + new EpsgOperationParameterRecord(1676, "Z-axis translation", 405.346d), + new EpsgOperationParameterRecord(1678, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1678, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1678, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1679, "X-axis translation", -40.595d), + new EpsgOperationParameterRecord(1679, "Y-axis translation", -18.55d), + new EpsgOperationParameterRecord(1679, "Z-axis translation", -69.339d), + new EpsgOperationParameterRecord(1679, "X-axis rotation", -2.508d), + new EpsgOperationParameterRecord(1679, "Y-axis rotation", -1.832d), + new EpsgOperationParameterRecord(1679, "Z-axis rotation", 2.611d), + new EpsgOperationParameterRecord(1679, "Scale difference", -4.299d), + new EpsgOperationParameterRecord(1680, "X-axis translation", 419.3836d), + new EpsgOperationParameterRecord(1680, "Y-axis translation", 99.3335d), + new EpsgOperationParameterRecord(1680, "Z-axis translation", 591.3451d), + new EpsgOperationParameterRecord(1680, "X-axis rotation", -0.850389d), + new EpsgOperationParameterRecord(1680, "Y-axis rotation", -1.817277d), + new EpsgOperationParameterRecord(1680, "Z-axis rotation", 7.862238d), + new EpsgOperationParameterRecord(1680, "Scale difference", -0.99496d), + new EpsgOperationParameterRecord(1682, "X-axis translation", -76.0d), + new EpsgOperationParameterRecord(1682, "Y-axis translation", -138.0d), + new EpsgOperationParameterRecord(1682, "Z-axis translation", 67.0d), + new EpsgOperationParameterRecord(1683, "X-axis translation", -115.064d), + new EpsgOperationParameterRecord(1683, "Y-axis translation", -87.39d), + new EpsgOperationParameterRecord(1683, "Z-axis translation", -101.716d), + new EpsgOperationParameterRecord(1683, "X-axis rotation", 0.058d), + new EpsgOperationParameterRecord(1683, "Y-axis rotation", -4.001d), + new EpsgOperationParameterRecord(1683, "Z-axis rotation", 2.062d), + new EpsgOperationParameterRecord(1683, "Scale difference", 9.366d), + new EpsgOperationParameterRecord(1684, "X-axis translation", -82.875d), + new EpsgOperationParameterRecord(1684, "Y-axis translation", -57.097d), + new EpsgOperationParameterRecord(1684, "Z-axis translation", -156.768d), + new EpsgOperationParameterRecord(1684, "X-axis rotation", 2.158d), + new EpsgOperationParameterRecord(1684, "Y-axis rotation", -1.524d), + new EpsgOperationParameterRecord(1684, "Z-axis rotation", 0.982d), + new EpsgOperationParameterRecord(1684, "Scale difference", -0.359d), + new EpsgOperationParameterRecord(1685, "X-axis translation", -138.527d), + new EpsgOperationParameterRecord(1685, "Y-axis translation", -91.999d), + new EpsgOperationParameterRecord(1685, "Z-axis translation", -114.591d), + new EpsgOperationParameterRecord(1685, "X-axis rotation", 0.14d), + new EpsgOperationParameterRecord(1685, "Y-axis rotation", -3.363d), + new EpsgOperationParameterRecord(1685, "Z-axis rotation", 2.217d), + new EpsgOperationParameterRecord(1685, "Scale difference", 11.748d), + new EpsgOperationParameterRecord(1686, "X-axis translation", -73.472d), + new EpsgOperationParameterRecord(1686, "Y-axis translation", -51.66d), + new EpsgOperationParameterRecord(1686, "Z-axis translation", -112.482d), + new EpsgOperationParameterRecord(1686, "X-axis rotation", -0.953d), + new EpsgOperationParameterRecord(1686, "Y-axis rotation", -4.6d), + new EpsgOperationParameterRecord(1686, "Z-axis rotation", 2.368d), + new EpsgOperationParameterRecord(1686, "Scale difference", 0.586d), + new EpsgOperationParameterRecord(1687, "X-axis translation", 219.315d), + new EpsgOperationParameterRecord(1687, "Y-axis translation", 168.975d), + new EpsgOperationParameterRecord(1687, "Z-axis translation", -166.145d), + new EpsgOperationParameterRecord(1687, "X-axis rotation", -0.198d), + new EpsgOperationParameterRecord(1687, "Y-axis rotation", -5.926d), + new EpsgOperationParameterRecord(1687, "Z-axis rotation", 2.356d), + new EpsgOperationParameterRecord(1687, "Scale difference", -57.104d), + new EpsgOperationParameterRecord(1701, "X-axis translation", 59.47d), + new EpsgOperationParameterRecord(1701, "Y-axis translation", -5.04d), + new EpsgOperationParameterRecord(1701, "Z-axis translation", 187.44d), + new EpsgOperationParameterRecord(1701, "X-axis rotation", -0.47d), + new EpsgOperationParameterRecord(1701, "Y-axis rotation", 0.1d), + new EpsgOperationParameterRecord(1701, "Z-axis rotation", -1.024d), + new EpsgOperationParameterRecord(1701, "Scale difference", -4.5993d), + new EpsgOperationParameterRecord(1751, "X-axis translation", 565.04d), + new EpsgOperationParameterRecord(1751, "Y-axis translation", 49.91d), + new EpsgOperationParameterRecord(1751, "Z-axis translation", 465.84d), + new EpsgOperationParameterRecord(1751, "X-axis rotation", 1.9848d), + new EpsgOperationParameterRecord(1751, "Y-axis rotation", -1.7439d), + new EpsgOperationParameterRecord(1751, "Z-axis rotation", 9.0587d), + new EpsgOperationParameterRecord(1751, "Scale difference", 4.0772d), + new EpsgOperationParameterRecord(1753, "X-axis translation", 660.077d), + new EpsgOperationParameterRecord(1753, "Y-axis translation", 13.551d), + new EpsgOperationParameterRecord(1753, "Z-axis translation", 369.344d), + new EpsgOperationParameterRecord(1753, "X-axis rotation", 2.484d), + new EpsgOperationParameterRecord(1753, "Y-axis rotation", 1.783d), + new EpsgOperationParameterRecord(1753, "Z-axis rotation", 2.939d), + new EpsgOperationParameterRecord(1753, "Scale difference", 5.66d), + new EpsgOperationParameterRecord(1754, "X-axis translation", -111.92d), + new EpsgOperationParameterRecord(1754, "Y-axis translation", -87.85d), + new EpsgOperationParameterRecord(1754, "Z-axis translation", 114.5d), + new EpsgOperationParameterRecord(1754, "X-axis rotation", 1.875d), + new EpsgOperationParameterRecord(1754, "Y-axis rotation", 0.202d), + new EpsgOperationParameterRecord(1754, "Z-axis rotation", 0.219d), + new EpsgOperationParameterRecord(1754, "Scale difference", 0.032d), + new EpsgOperationParameterRecord(1755, "Longitude offset", -74.0809166666669d), + new EpsgOperationParameterRecord(1756, "Longitude offset", -9.13190611111139d), + new EpsgOperationParameterRecord(1759, "Longitude offset", 106.807719444445d), + new EpsgOperationParameterRecord(1760, "Longitude offset", 18.0582777777781d), + new EpsgOperationParameterRecord(1761, "Longitude offset", 23.7163375000003d), + new EpsgOperationParameterRecord(1762, "Longitude offset", 10.7229166666669d), + new EpsgOperationParameterRecord(1763, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(1764, "Longitude offset", 2.33720833333361d), + new EpsgOperationParameterRecord(1765, "Longitude offset", 7.43958333333361d), + new EpsgOperationParameterRecord(1766, "X-axis translation", 674.374d), + new EpsgOperationParameterRecord(1766, "Y-axis translation", 15.056d), + new EpsgOperationParameterRecord(1766, "Z-axis translation", 405.346d), + new EpsgOperationParameterRecord(1767, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1767, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1767, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1768, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1768, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1768, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1769, "X-axis translation", -270.933d), + new EpsgOperationParameterRecord(1769, "Y-axis translation", 115.599d), + new EpsgOperationParameterRecord(1769, "Z-axis translation", -360.226d), + new EpsgOperationParameterRecord(1769, "X-axis rotation", -5.266d), + new EpsgOperationParameterRecord(1769, "Y-axis rotation", -1.238d), + new EpsgOperationParameterRecord(1769, "Z-axis rotation", 2.381d), + new EpsgOperationParameterRecord(1769, "Scale difference", -5.109d), + new EpsgOperationParameterRecord(1769, "Ordinate 1 of evaluation point", 2464351.59d), + new EpsgOperationParameterRecord(1769, "Ordinate 2 of evaluation point", -5783466.61d), + new EpsgOperationParameterRecord(1769, "Ordinate 3 of evaluation point", 974809.81d), + new EpsgOperationParameterRecord(1771, "X-axis translation", -270.933d), + new EpsgOperationParameterRecord(1771, "Y-axis translation", 115.599d), + new EpsgOperationParameterRecord(1771, "Z-axis translation", -360.226d), + new EpsgOperationParameterRecord(1771, "X-axis rotation", -5.266d), + new EpsgOperationParameterRecord(1771, "Y-axis rotation", -1.238d), + new EpsgOperationParameterRecord(1771, "Z-axis rotation", 2.381d), + new EpsgOperationParameterRecord(1771, "Scale difference", -5.109d), + new EpsgOperationParameterRecord(1771, "Ordinate 1 of evaluation point", 2464351.59d), + new EpsgOperationParameterRecord(1771, "Ordinate 2 of evaluation point", -5783466.61d), + new EpsgOperationParameterRecord(1771, "Ordinate 3 of evaluation point", 974809.81d), + new EpsgOperationParameterRecord(1773, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1773, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1773, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1774, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1774, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1774, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1775, "X-axis translation", 24.9d), + new EpsgOperationParameterRecord(1775, "Y-axis translation", -126.4d), + new EpsgOperationParameterRecord(1775, "Z-axis translation", -93.2d), + new EpsgOperationParameterRecord(1775, "X-axis rotation", -0.063d), + new EpsgOperationParameterRecord(1775, "Y-axis rotation", -0.247d), + new EpsgOperationParameterRecord(1775, "Z-axis rotation", -0.041d), + new EpsgOperationParameterRecord(1775, "Scale difference", 1.01d), + new EpsgOperationParameterRecord(1776, "X-axis translation", 598.1d), + new EpsgOperationParameterRecord(1776, "Y-axis translation", 73.7d), + new EpsgOperationParameterRecord(1776, "Z-axis translation", 418.2d), + new EpsgOperationParameterRecord(1776, "X-axis rotation", 0.202d), + new EpsgOperationParameterRecord(1776, "Y-axis rotation", 0.045d), + new EpsgOperationParameterRecord(1776, "Z-axis rotation", -2.455d), + new EpsgOperationParameterRecord(1776, "Scale difference", 6.7d), + new EpsgOperationParameterRecord(1777, "X-axis translation", 598.1d), + new EpsgOperationParameterRecord(1777, "Y-axis translation", 73.7d), + new EpsgOperationParameterRecord(1777, "Z-axis translation", 418.2d), + new EpsgOperationParameterRecord(1777, "X-axis rotation", 0.202d), + new EpsgOperationParameterRecord(1777, "Y-axis rotation", 0.045d), + new EpsgOperationParameterRecord(1777, "Z-axis rotation", -2.455d), + new EpsgOperationParameterRecord(1777, "Scale difference", 6.7d), + new EpsgOperationParameterRecord(1778, "X-axis translation", 597.1d), + new EpsgOperationParameterRecord(1778, "Y-axis translation", 71.4d), + new EpsgOperationParameterRecord(1778, "Z-axis translation", 412.1d), + new EpsgOperationParameterRecord(1778, "X-axis rotation", 0.894d), + new EpsgOperationParameterRecord(1778, "Y-axis rotation", 0.068d), + new EpsgOperationParameterRecord(1778, "Z-axis rotation", -1.563d), + new EpsgOperationParameterRecord(1778, "Scale difference", 7.58d), + new EpsgOperationParameterRecord(1779, "X-axis translation", 584.8d), + new EpsgOperationParameterRecord(1779, "Y-axis translation", 67.0d), + new EpsgOperationParameterRecord(1779, "Z-axis translation", 400.3d), + new EpsgOperationParameterRecord(1779, "X-axis rotation", 0.105d), + new EpsgOperationParameterRecord(1779, "Y-axis rotation", 0.013d), + new EpsgOperationParameterRecord(1779, "Z-axis rotation", -2.378d), + new EpsgOperationParameterRecord(1779, "Scale difference", 10.29d), + new EpsgOperationParameterRecord(1780, "X-axis translation", 590.5d), + new EpsgOperationParameterRecord(1780, "Y-axis translation", 69.5d), + new EpsgOperationParameterRecord(1780, "Z-axis translation", 411.6d), + new EpsgOperationParameterRecord(1780, "X-axis rotation", -0.796d), + new EpsgOperationParameterRecord(1780, "Y-axis rotation", -0.052d), + new EpsgOperationParameterRecord(1780, "Z-axis rotation", -3.601d), + new EpsgOperationParameterRecord(1780, "Scale difference", 8.3d), + new EpsgOperationParameterRecord(1783, "X-axis translation", -84.1d), + new EpsgOperationParameterRecord(1783, "Y-axis translation", -101.8d), + new EpsgOperationParameterRecord(1783, "Z-axis translation", -129.7d), + new EpsgOperationParameterRecord(1783, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1783, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1783, "Z-axis rotation", 0.468d), + new EpsgOperationParameterRecord(1783, "Scale difference", 1.05d), + new EpsgOperationParameterRecord(1784, "X-axis translation", -84.1d), + new EpsgOperationParameterRecord(1784, "Y-axis translation", -101.8d), + new EpsgOperationParameterRecord(1784, "Z-axis translation", -129.7d), + new EpsgOperationParameterRecord(1784, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1784, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1784, "Z-axis rotation", 0.468d), + new EpsgOperationParameterRecord(1784, "Scale difference", 1.05d), + new EpsgOperationParameterRecord(1796, "X-axis translation", -70.9d), + new EpsgOperationParameterRecord(1796, "Y-axis translation", -151.8d), + new EpsgOperationParameterRecord(1796, "Z-axis translation", -41.4d), + new EpsgOperationParameterRecord(1797, "X-axis translation", 164.0d), + new EpsgOperationParameterRecord(1797, "Y-axis translation", 138.0d), + new EpsgOperationParameterRecord(1797, "Z-axis translation", -189.0d), + new EpsgOperationParameterRecord(1798, "X-axis translation", 163.511d), + new EpsgOperationParameterRecord(1798, "Y-axis translation", 127.533d), + new EpsgOperationParameterRecord(1798, "Z-axis translation", -159.789d), + new EpsgOperationParameterRecord(1798, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1798, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1798, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(1798, "Scale difference", -0.6d), + new EpsgOperationParameterRecord(1799, "X-axis translation", 105.0d), + new EpsgOperationParameterRecord(1799, "Y-axis translation", 326.0d), + new EpsgOperationParameterRecord(1799, "Z-axis translation", -102.5d), + new EpsgOperationParameterRecord(1799, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1799, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1799, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(1799, "Scale difference", -0.6d), + new EpsgOperationParameterRecord(1800, "X-axis translation", -45.0d), + new EpsgOperationParameterRecord(1800, "Y-axis translation", 417.0d), + new EpsgOperationParameterRecord(1800, "Z-axis translation", -3.5d), + new EpsgOperationParameterRecord(1800, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1800, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1800, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(1800, "Scale difference", -0.6d), + new EpsgOperationParameterRecord(1801, "X-axis translation", -145.0d), + new EpsgOperationParameterRecord(1801, "Y-axis translation", 52.7d), + new EpsgOperationParameterRecord(1801, "Z-axis translation", -291.6d), + new EpsgOperationParameterRecord(1802, "X-axis translation", -178.3d), + new EpsgOperationParameterRecord(1802, "Y-axis translation", -316.7d), + new EpsgOperationParameterRecord(1802, "Z-axis translation", -131.5d), + new EpsgOperationParameterRecord(1802, "X-axis rotation", 5.278d), + new EpsgOperationParameterRecord(1802, "Y-axis rotation", 6.077d), + new EpsgOperationParameterRecord(1802, "Z-axis rotation", 10.979d), + new EpsgOperationParameterRecord(1802, "Scale difference", 19.166d), + new EpsgOperationParameterRecord(1805, "X-axis translation", -56.1d), + new EpsgOperationParameterRecord(1805, "Y-axis translation", -167.8d), + new EpsgOperationParameterRecord(1805, "Z-axis translation", 13.1d), + new EpsgOperationParameterRecord(1806, "X-axis translation", -104.4d), + new EpsgOperationParameterRecord(1806, "Y-axis translation", -136.6d), + new EpsgOperationParameterRecord(1806, "Z-axis translation", 201.2d), + new EpsgOperationParameterRecord(1807, "X-axis translation", 27.0d), + new EpsgOperationParameterRecord(1807, "Y-axis translation", -135.0d), + new EpsgOperationParameterRecord(1807, "Z-axis translation", -84.5d), + new EpsgOperationParameterRecord(1807, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1807, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1807, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(1807, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(1808, "X-axis translation", 686.1d), + new EpsgOperationParameterRecord(1808, "Y-axis translation", -123.5d), + new EpsgOperationParameterRecord(1808, "Z-axis translation", -574.4d), + new EpsgOperationParameterRecord(1808, "X-axis rotation", 8.045d), + new EpsgOperationParameterRecord(1808, "Y-axis rotation", -23.366d), + new EpsgOperationParameterRecord(1808, "Z-axis rotation", 10.791d), + new EpsgOperationParameterRecord(1808, "Scale difference", -2.926d), + new EpsgOperationParameterRecord(1809, "X-axis translation", 926.4d), + new EpsgOperationParameterRecord(1809, "Y-axis translation", -715.9d), + new EpsgOperationParameterRecord(1809, "Z-axis translation", -186.4d), + new EpsgOperationParameterRecord(1809, "X-axis rotation", -10.364d), + new EpsgOperationParameterRecord(1809, "Y-axis rotation", -20.78d), + new EpsgOperationParameterRecord(1809, "Z-axis rotation", 26.452d), + new EpsgOperationParameterRecord(1809, "Scale difference", -7.224d), + new EpsgOperationParameterRecord(1810, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(1810, "Y-axis translation", -103.0d), + new EpsgOperationParameterRecord(1810, "Z-axis translation", -122.5d), + new EpsgOperationParameterRecord(1810, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1810, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1810, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(1810, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(1811, "X-axis translation", -291.87d), + new EpsgOperationParameterRecord(1811, "Y-axis translation", 106.37d), + new EpsgOperationParameterRecord(1811, "Z-axis translation", -364.52d), + new EpsgOperationParameterRecord(1812, "X-axis translation", 293.0d), + new EpsgOperationParameterRecord(1812, "Y-axis translation", 836.0d), + new EpsgOperationParameterRecord(1812, "Z-axis translation", 318.0d), + new EpsgOperationParameterRecord(1812, "X-axis rotation", 0.5d), + new EpsgOperationParameterRecord(1812, "Y-axis rotation", 1.6d), + new EpsgOperationParameterRecord(1812, "Z-axis rotation", -2.8d), + new EpsgOperationParameterRecord(1812, "Scale difference", 2.1d), + new EpsgOperationParameterRecord(1813, "X-axis translation", -378.873d), + new EpsgOperationParameterRecord(1813, "Y-axis translation", 676.002d), + new EpsgOperationParameterRecord(1813, "Z-axis translation", -46.255d), + new EpsgOperationParameterRecord(1814, "X-axis translation", -377.7d), + new EpsgOperationParameterRecord(1814, "Y-axis translation", 675.1d), + new EpsgOperationParameterRecord(1814, "Z-axis translation", -52.2d), + new EpsgOperationParameterRecord(1815, "X-axis translation", -152.9d), + new EpsgOperationParameterRecord(1815, "Y-axis translation", 43.8d), + new EpsgOperationParameterRecord(1815, "Z-axis translation", 358.3d), + new EpsgOperationParameterRecord(1815, "X-axis rotation", 2.714d), + new EpsgOperationParameterRecord(1815, "Y-axis rotation", 1.386d), + new EpsgOperationParameterRecord(1815, "Z-axis rotation", -2.788d), + new EpsgOperationParameterRecord(1815, "Scale difference", -6.743d), + new EpsgOperationParameterRecord(1816, "X-axis translation", -95.7d), + new EpsgOperationParameterRecord(1816, "Y-axis translation", 10.2d), + new EpsgOperationParameterRecord(1816, "Z-axis translation", 158.9d), + new EpsgOperationParameterRecord(1817, "X-axis translation", -165.914d), + new EpsgOperationParameterRecord(1817, "Y-axis translation", -70.607d), + new EpsgOperationParameterRecord(1817, "Z-axis translation", 305.009d), + new EpsgOperationParameterRecord(1818, "X-axis translation", -89.0d), + new EpsgOperationParameterRecord(1818, "Y-axis translation", -112.0d), + new EpsgOperationParameterRecord(1818, "Z-axis translation", 125.9d), + new EpsgOperationParameterRecord(1818, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1818, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1818, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(1818, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(1820, "X-axis translation", -93.2d), + new EpsgOperationParameterRecord(1820, "Y-axis translation", -93.31d), + new EpsgOperationParameterRecord(1820, "Z-axis translation", 121.156d), + new EpsgOperationParameterRecord(1821, "X-axis translation", -88.98d), + new EpsgOperationParameterRecord(1821, "Y-axis translation", -83.23d), + new EpsgOperationParameterRecord(1821, "Z-axis translation", 113.55d), + new EpsgOperationParameterRecord(1822, "X-axis translation", -92.726d), + new EpsgOperationParameterRecord(1822, "Y-axis translation", -90.304d), + new EpsgOperationParameterRecord(1822, "Z-axis translation", 115.735d), + new EpsgOperationParameterRecord(1823, "X-axis translation", -93.134d), + new EpsgOperationParameterRecord(1823, "Y-axis translation", -86.647d), + new EpsgOperationParameterRecord(1823, "Z-axis translation", 114.196d), + new EpsgOperationParameterRecord(1824, "X-axis translation", -93.0d), + new EpsgOperationParameterRecord(1824, "Y-axis translation", -94.0d), + new EpsgOperationParameterRecord(1824, "Z-axis translation", 124.0d), + new EpsgOperationParameterRecord(1825, "X-axis translation", -162.619d), + new EpsgOperationParameterRecord(1825, "Y-axis translation", -276.959d), + new EpsgOperationParameterRecord(1825, "Z-axis translation", -161.764d), + new EpsgOperationParameterRecord(1825, "X-axis rotation", -0.067753d), + new EpsgOperationParameterRecord(1825, "Y-axis rotation", 2.243648d), + new EpsgOperationParameterRecord(1825, "Z-axis rotation", 1.158828d), + new EpsgOperationParameterRecord(1825, "Scale difference", -1.094246d), + new EpsgOperationParameterRecord(1826, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1826, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1826, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1828, "X-axis translation", -37.0d), + new EpsgOperationParameterRecord(1828, "Y-axis translation", 157.0d), + new EpsgOperationParameterRecord(1828, "Z-axis translation", 85.0d), + new EpsgOperationParameterRecord(1829, "X-axis translation", 56.0d), + new EpsgOperationParameterRecord(1829, "Y-axis translation", -75.77d), + new EpsgOperationParameterRecord(1829, "Z-axis translation", -15.31d), + new EpsgOperationParameterRecord(1829, "X-axis rotation", 0.37d), + new EpsgOperationParameterRecord(1829, "Y-axis rotation", 0.2d), + new EpsgOperationParameterRecord(1829, "Z-axis rotation", 0.21d), + new EpsgOperationParameterRecord(1829, "Scale difference", 1.01d), + new EpsgOperationParameterRecord(1830, "X-axis translation", 56.0d), + new EpsgOperationParameterRecord(1830, "Y-axis translation", -75.77d), + new EpsgOperationParameterRecord(1830, "Z-axis translation", -15.31d), + new EpsgOperationParameterRecord(1830, "X-axis rotation", 0.37d), + new EpsgOperationParameterRecord(1830, "Y-axis rotation", 0.2d), + new EpsgOperationParameterRecord(1830, "Z-axis rotation", 0.21d), + new EpsgOperationParameterRecord(1830, "Scale difference", 1.01d), + new EpsgOperationParameterRecord(1831, "X-axis translation", 57.01d), + new EpsgOperationParameterRecord(1831, "Y-axis translation", -69.97d), + new EpsgOperationParameterRecord(1831, "Z-axis translation", -9.29d), + new EpsgOperationParameterRecord(1832, "X-axis translation", 2.691d), + new EpsgOperationParameterRecord(1832, "Y-axis translation", -14.757d), + new EpsgOperationParameterRecord(1832, "Z-axis translation", 4.724d), + new EpsgOperationParameterRecord(1832, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1832, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1832, "Z-axis rotation", 0.774d), + new EpsgOperationParameterRecord(1832, "Scale difference", -0.6d), + new EpsgOperationParameterRecord(1833, "X-axis translation", -1.977d), + new EpsgOperationParameterRecord(1833, "Y-axis translation", -13.06d), + new EpsgOperationParameterRecord(1833, "Z-axis translation", -9.993d), + new EpsgOperationParameterRecord(1833, "X-axis rotation", -0.364d), + new EpsgOperationParameterRecord(1833, "Y-axis rotation", -0.254d), + new EpsgOperationParameterRecord(1833, "Z-axis rotation", -0.689d), + new EpsgOperationParameterRecord(1833, "Scale difference", -1.037d), + new EpsgOperationParameterRecord(1837, "X-axis translation", -587.8d), + new EpsgOperationParameterRecord(1837, "Y-axis translation", 519.75d), + new EpsgOperationParameterRecord(1837, "Z-axis translation", 145.76d), + new EpsgOperationParameterRecord(1838, "X-axis translation", -404.78d), + new EpsgOperationParameterRecord(1838, "Y-axis translation", 685.68d), + new EpsgOperationParameterRecord(1838, "Z-axis translation", 45.47d), + new EpsgOperationParameterRecord(1839, "X-axis translation", -101.0d), + new EpsgOperationParameterRecord(1839, "Y-axis translation", -111.0d), + new EpsgOperationParameterRecord(1839, "Z-axis translation", 187.0d), + new EpsgOperationParameterRecord(1840, "X-axis translation", -119.4248d), + new EpsgOperationParameterRecord(1840, "Y-axis translation", -303.65872d), + new EpsgOperationParameterRecord(1840, "Z-axis translation", -11.00061d), + new EpsgOperationParameterRecord(1840, "X-axis rotation", 1.164298d), + new EpsgOperationParameterRecord(1840, "Y-axis rotation", 0.174458d), + new EpsgOperationParameterRecord(1840, "Z-axis rotation", 1.096259d), + new EpsgOperationParameterRecord(1840, "Scale difference", 3.657065d), + new EpsgOperationParameterRecord(1842, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1842, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1842, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1852, "X-axis translation", -533.4d), + new EpsgOperationParameterRecord(1852, "Y-axis translation", 669.2d), + new EpsgOperationParameterRecord(1852, "Z-axis translation", -52.5d), + new EpsgOperationParameterRecord(1852, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1852, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(1852, "Z-axis rotation", 4.28d), + new EpsgOperationParameterRecord(1852, "Scale difference", 9.4d), + new EpsgOperationParameterRecord(1853, "X-axis translation", -82.31d), + new EpsgOperationParameterRecord(1853, "Y-axis translation", -95.23d), + new EpsgOperationParameterRecord(1853, "Z-axis translation", -114.96d), + new EpsgOperationParameterRecord(1854, "X-axis translation", -239.1d), + new EpsgOperationParameterRecord(1854, "Y-axis translation", -170.02d), + new EpsgOperationParameterRecord(1854, "Z-axis translation", 397.5d), + new EpsgOperationParameterRecord(1855, "X-axis translation", -244.72d), + new EpsgOperationParameterRecord(1855, "Y-axis translation", -162.773d), + new EpsgOperationParameterRecord(1855, "Z-axis translation", 400.75d), + new EpsgOperationParameterRecord(1856, "X-axis translation", -122.89d), + new EpsgOperationParameterRecord(1856, "Y-axis translation", -159.08d), + new EpsgOperationParameterRecord(1856, "Z-axis translation", -168.74d), + new EpsgOperationParameterRecord(1857, "X-axis translation", -84.78d), + new EpsgOperationParameterRecord(1857, "Y-axis translation", -107.55d), + new EpsgOperationParameterRecord(1857, "Z-axis translation", -137.25d), + new EpsgOperationParameterRecord(1858, "X-axis translation", -123.92d), + new EpsgOperationParameterRecord(1858, "Y-axis translation", -155.515d), + new EpsgOperationParameterRecord(1858, "Z-axis translation", -157.721d), + new EpsgOperationParameterRecord(1859, "X-axis translation", -69.06d), + new EpsgOperationParameterRecord(1859, "Y-axis translation", -90.71d), + new EpsgOperationParameterRecord(1859, "Z-axis translation", -142.56d), + new EpsgOperationParameterRecord(1860, "X-axis translation", -113.997d), + new EpsgOperationParameterRecord(1860, "Y-axis translation", -97.076d), + new EpsgOperationParameterRecord(1860, "Z-axis translation", -152.312d), + new EpsgOperationParameterRecord(1861, "X-axis translation", -114.5d), + new EpsgOperationParameterRecord(1861, "Y-axis translation", -96.1d), + new EpsgOperationParameterRecord(1861, "Z-axis translation", -151.9d), + new EpsgOperationParameterRecord(1862, "X-axis translation", -194.513d), + new EpsgOperationParameterRecord(1862, "Y-axis translation", -63.978d), + new EpsgOperationParameterRecord(1862, "Z-axis translation", -25.759d), + new EpsgOperationParameterRecord(1862, "X-axis rotation", -3.4027d), + new EpsgOperationParameterRecord(1862, "Y-axis rotation", 3.756d), + new EpsgOperationParameterRecord(1862, "Z-axis rotation", -3.352d), + new EpsgOperationParameterRecord(1862, "Scale difference", -0.9175d), + new EpsgOperationParameterRecord(1863, "X-axis translation", -389.691d), + new EpsgOperationParameterRecord(1863, "Y-axis translation", 64.502d), + new EpsgOperationParameterRecord(1863, "Z-axis translation", 210.209d), + new EpsgOperationParameterRecord(1863, "X-axis rotation", -0.086d), + new EpsgOperationParameterRecord(1863, "Y-axis rotation", -14.314d), + new EpsgOperationParameterRecord(1863, "Z-axis rotation", 6.39d), + new EpsgOperationParameterRecord(1863, "Scale difference", 0.9264d), + new EpsgOperationParameterRecord(1864, "X-axis translation", -57.0d), + new EpsgOperationParameterRecord(1864, "Y-axis translation", 1.0d), + new EpsgOperationParameterRecord(1864, "Z-axis translation", -41.0d), + new EpsgOperationParameterRecord(1865, "X-axis translation", -62.0d), + new EpsgOperationParameterRecord(1865, "Y-axis translation", -1.0d), + new EpsgOperationParameterRecord(1865, "Z-axis translation", -37.0d), + new EpsgOperationParameterRecord(1866, "X-axis translation", -61.0d), + new EpsgOperationParameterRecord(1866, "Y-axis translation", 2.0d), + new EpsgOperationParameterRecord(1866, "Z-axis translation", -48.0d), + new EpsgOperationParameterRecord(1867, "X-axis translation", -60.0d), + new EpsgOperationParameterRecord(1867, "Y-axis translation", -2.0d), + new EpsgOperationParameterRecord(1867, "Z-axis translation", -41.0d), + new EpsgOperationParameterRecord(1868, "X-axis translation", -75.0d), + new EpsgOperationParameterRecord(1868, "Y-axis translation", -1.0d), + new EpsgOperationParameterRecord(1868, "Z-axis translation", -44.0d), + new EpsgOperationParameterRecord(1869, "X-axis translation", -44.0d), + new EpsgOperationParameterRecord(1869, "Y-axis translation", 6.0d), + new EpsgOperationParameterRecord(1869, "Z-axis translation", -36.0d), + new EpsgOperationParameterRecord(1870, "X-axis translation", -48.0d), + new EpsgOperationParameterRecord(1870, "Y-axis translation", 3.0d), + new EpsgOperationParameterRecord(1870, "Z-axis translation", -44.0d), + new EpsgOperationParameterRecord(1871, "X-axis translation", -47.0d), + new EpsgOperationParameterRecord(1871, "Y-axis translation", 26.0d), + new EpsgOperationParameterRecord(1871, "Z-axis translation", -42.0d), + new EpsgOperationParameterRecord(1872, "X-axis translation", -53.0d), + new EpsgOperationParameterRecord(1872, "Y-axis translation", 3.0d), + new EpsgOperationParameterRecord(1872, "Z-axis translation", -47.0d), + new EpsgOperationParameterRecord(1873, "X-axis translation", -61.0d), + new EpsgOperationParameterRecord(1873, "Y-axis translation", 2.0d), + new EpsgOperationParameterRecord(1873, "Z-axis translation", -33.0d), + new EpsgOperationParameterRecord(1874, "X-axis translation", -58.0d), + new EpsgOperationParameterRecord(1874, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1874, "Z-axis translation", -44.0d), + new EpsgOperationParameterRecord(1875, "X-axis translation", -45.0d), + new EpsgOperationParameterRecord(1875, "Y-axis translation", 12.0d), + new EpsgOperationParameterRecord(1875, "Z-axis translation", -33.0d), + new EpsgOperationParameterRecord(1876, "X-axis translation", -45.0d), + new EpsgOperationParameterRecord(1876, "Y-axis translation", 8.0d), + new EpsgOperationParameterRecord(1876, "Z-axis translation", -33.0d), + new EpsgOperationParameterRecord(1877, "X-axis translation", -66.87d), + new EpsgOperationParameterRecord(1877, "Y-axis translation", 4.37d), + new EpsgOperationParameterRecord(1877, "Z-axis translation", -38.52d), + new EpsgOperationParameterRecord(1879, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1879, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1879, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1880, "X-axis translation", -106.0d), + new EpsgOperationParameterRecord(1880, "Y-axis translation", -129.0d), + new EpsgOperationParameterRecord(1880, "Z-axis translation", 165.0d), + new EpsgOperationParameterRecord(1881, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(1883, "Longitude offset", 106.807719444445d), + new EpsgOperationParameterRecord(1884, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(1885, "X-axis translation", -203.0d), + new EpsgOperationParameterRecord(1885, "Y-axis translation", 141.0d), + new EpsgOperationParameterRecord(1885, "Z-axis translation", 53.0d), + new EpsgOperationParameterRecord(1886, "X-axis translation", -104.0d), + new EpsgOperationParameterRecord(1886, "Y-axis translation", 167.0d), + new EpsgOperationParameterRecord(1886, "Z-axis translation", -38.0d), + new EpsgOperationParameterRecord(1887, "X-axis translation", -425.0d), + new EpsgOperationParameterRecord(1887, "Y-axis translation", -169.0d), + new EpsgOperationParameterRecord(1887, "Z-axis translation", 81.0d), + new EpsgOperationParameterRecord(1888, "X-axis translation", -499.0d), + new EpsgOperationParameterRecord(1888, "Y-axis translation", -249.0d), + new EpsgOperationParameterRecord(1888, "Z-axis translation", 314.0d), + new EpsgOperationParameterRecord(1890, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1890, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1890, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1891, "Latitude offset", -5.86d), + new EpsgOperationParameterRecord(1891, "Longitude offset", 0.28d), + new EpsgOperationParameterRecord(1892, "X-axis translation", 16.0d), + new EpsgOperationParameterRecord(1892, "Y-axis translation", 196.0d), + new EpsgOperationParameterRecord(1892, "Z-axis translation", 93.0d), + new EpsgOperationParameterRecord(1893, "X-axis translation", 11.0d), + new EpsgOperationParameterRecord(1893, "Y-axis translation", 72.0d), + new EpsgOperationParameterRecord(1893, "Z-axis translation", -101.0d), + new EpsgOperationParameterRecord(1895, "X-axis translation", 414.1d), + new EpsgOperationParameterRecord(1895, "Y-axis translation", 41.3d), + new EpsgOperationParameterRecord(1895, "Z-axis translation", 603.1d), + new EpsgOperationParameterRecord(1895, "X-axis rotation", 0.855d), + new EpsgOperationParameterRecord(1895, "Y-axis rotation", -2.141d), + new EpsgOperationParameterRecord(1895, "Z-axis rotation", 7.023d), + new EpsgOperationParameterRecord(1895, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(1896, "X-axis translation", 414.1d), + new EpsgOperationParameterRecord(1896, "Y-axis translation", 41.3d), + new EpsgOperationParameterRecord(1896, "Z-axis translation", 603.1d), + new EpsgOperationParameterRecord(1896, "X-axis rotation", 0.855d), + new EpsgOperationParameterRecord(1896, "Y-axis rotation", -2.141d), + new EpsgOperationParameterRecord(1896, "Z-axis rotation", 7.023d), + new EpsgOperationParameterRecord(1896, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(1897, "X-axis translation", -403.0d), + new EpsgOperationParameterRecord(1897, "Y-axis translation", 684.0d), + new EpsgOperationParameterRecord(1897, "Z-axis translation", 41.0d), + new EpsgOperationParameterRecord(1898, "X-axis translation", -387.06d), + new EpsgOperationParameterRecord(1898, "Y-axis translation", 636.53d), + new EpsgOperationParameterRecord(1898, "Z-axis translation", 46.29d), + new EpsgOperationParameterRecord(1899, "X-axis translation", -403.4d), + new EpsgOperationParameterRecord(1899, "Y-axis translation", 681.12d), + new EpsgOperationParameterRecord(1899, "Z-axis translation", 46.56d), + new EpsgOperationParameterRecord(1900, "X-axis translation", -0.9738d), + new EpsgOperationParameterRecord(1900, "Y-axis translation", 1.9453d), + new EpsgOperationParameterRecord(1900, "Z-axis translation", 0.5486d), + new EpsgOperationParameterRecord(1900, "X-axis rotation", -1.3357e-07d), + new EpsgOperationParameterRecord(1900, "Y-axis rotation", -4.872e-08d), + new EpsgOperationParameterRecord(1900, "Z-axis rotation", -5.507e-08d), + new EpsgOperationParameterRecord(1900, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(1901, "X-axis translation", -0.991d), + new EpsgOperationParameterRecord(1901, "Y-axis translation", 1.9072d), + new EpsgOperationParameterRecord(1901, "Z-axis translation", 0.5129d), + new EpsgOperationParameterRecord(1901, "X-axis rotation", -1.25033e-07d), + new EpsgOperationParameterRecord(1901, "Y-axis rotation", -4.6785e-08d), + new EpsgOperationParameterRecord(1901, "Z-axis rotation", -5.6529e-08d), + new EpsgOperationParameterRecord(1901, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(1902, "X-axis translation", -56.7d), + new EpsgOperationParameterRecord(1902, "Y-axis translation", -171.8d), + new EpsgOperationParameterRecord(1902, "Z-axis translation", -40.6d), + new EpsgOperationParameterRecord(1903, "X-axis translation", 137.0d), + new EpsgOperationParameterRecord(1903, "Y-axis translation", 248.0d), + new EpsgOperationParameterRecord(1903, "Z-axis translation", -430.0d), + new EpsgOperationParameterRecord(1904, "X-axis translation", -467.0d), + new EpsgOperationParameterRecord(1904, "Y-axis translation", -16.0d), + new EpsgOperationParameterRecord(1904, "Z-axis translation", -300.0d), + new EpsgOperationParameterRecord(1905, "X-axis translation", -472.29d), + new EpsgOperationParameterRecord(1905, "Y-axis translation", -5.63d), + new EpsgOperationParameterRecord(1905, "Z-axis translation", -304.12d), + new EpsgOperationParameterRecord(1905, "X-axis rotation", 0.4362d), + new EpsgOperationParameterRecord(1905, "Y-axis rotation", -0.8374d), + new EpsgOperationParameterRecord(1905, "Z-axis rotation", 0.2563d), + new EpsgOperationParameterRecord(1905, "Scale difference", 1.8984d), + new EpsgOperationParameterRecord(1906, "X-axis translation", -186.0d), + new EpsgOperationParameterRecord(1906, "Y-axis translation", 230.0d), + new EpsgOperationParameterRecord(1906, "Z-axis translation", 110.0d), + new EpsgOperationParameterRecord(1908, "X-axis translation", -193.066d), + new EpsgOperationParameterRecord(1908, "Y-axis translation", 236.993d), + new EpsgOperationParameterRecord(1908, "Z-axis translation", 105.447d), + new EpsgOperationParameterRecord(1908, "X-axis rotation", 0.4814d), + new EpsgOperationParameterRecord(1908, "Y-axis rotation", -0.8074d), + new EpsgOperationParameterRecord(1908, "Z-axis rotation", 0.1276d), + new EpsgOperationParameterRecord(1908, "Scale difference", 1.5649d), + new EpsgOperationParameterRecord(1909, "X-axis translation", 186.0d), + new EpsgOperationParameterRecord(1909, "Y-axis translation", 482.0d), + new EpsgOperationParameterRecord(1909, "Z-axis translation", 151.0d), + new EpsgOperationParameterRecord(1910, "X-axis translation", 126.93d), + new EpsgOperationParameterRecord(1910, "Y-axis translation", 547.94d), + new EpsgOperationParameterRecord(1910, "Z-axis translation", 130.41d), + new EpsgOperationParameterRecord(1910, "X-axis rotation", -2.7867d), + new EpsgOperationParameterRecord(1910, "Y-axis rotation", 5.1612d), + new EpsgOperationParameterRecord(1910, "Z-axis rotation", -0.8584d), + new EpsgOperationParameterRecord(1910, "Scale difference", 13.8227d), + new EpsgOperationParameterRecord(1912, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1912, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1912, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1913, "X-axis translation", 65.0d), + new EpsgOperationParameterRecord(1913, "Y-axis translation", 342.0d), + new EpsgOperationParameterRecord(1913, "Z-axis translation", 77.0d), + new EpsgOperationParameterRecord(1914, "X-axis translation", 84.0d), + new EpsgOperationParameterRecord(1914, "Y-axis translation", 274.0d), + new EpsgOperationParameterRecord(1914, "Z-axis translation", 65.0d), + new EpsgOperationParameterRecord(1916, "X-axis translation", -382.0d), + new EpsgOperationParameterRecord(1916, "Y-axis translation", -59.0d), + new EpsgOperationParameterRecord(1916, "Z-axis translation", -262.0d), + new EpsgOperationParameterRecord(1917, "X-axis translation", 336.0d), + new EpsgOperationParameterRecord(1917, "Y-axis translation", 223.0d), + new EpsgOperationParameterRecord(1917, "Z-axis translation", -231.0d), + new EpsgOperationParameterRecord(1921, "X-axis translation", 365.0d), + new EpsgOperationParameterRecord(1921, "Y-axis translation", 194.0d), + new EpsgOperationParameterRecord(1921, "Z-axis translation", 166.0d), + new EpsgOperationParameterRecord(1922, "X-axis translation", 325.0d), + new EpsgOperationParameterRecord(1922, "Y-axis translation", 154.0d), + new EpsgOperationParameterRecord(1922, "Z-axis translation", 172.0d), + new EpsgOperationParameterRecord(1923, "X-axis translation", 30.0d), + new EpsgOperationParameterRecord(1923, "Y-axis translation", 430.0d), + new EpsgOperationParameterRecord(1923, "Z-axis translation", 368.0d), + new EpsgOperationParameterRecord(1924, "X-axis translation", 162.0d), + new EpsgOperationParameterRecord(1924, "Y-axis translation", 117.0d), + new EpsgOperationParameterRecord(1924, "Z-axis translation", 154.0d), + new EpsgOperationParameterRecord(1926, "X-axis translation", 789.524d), + new EpsgOperationParameterRecord(1926, "Y-axis translation", -626.486d), + new EpsgOperationParameterRecord(1926, "Z-axis translation", -89.904d), + new EpsgOperationParameterRecord(1926, "X-axis rotation", 0.6006d), + new EpsgOperationParameterRecord(1926, "Y-axis rotation", 76.7946d), + new EpsgOperationParameterRecord(1926, "Z-axis rotation", -10.5788d), + new EpsgOperationParameterRecord(1926, "Scale difference", -32.3241d), + new EpsgOperationParameterRecord(1927, "X-axis translation", 137.092d), + new EpsgOperationParameterRecord(1927, "Y-axis translation", 131.66d), + new EpsgOperationParameterRecord(1927, "Z-axis translation", 91.475d), + new EpsgOperationParameterRecord(1927, "X-axis rotation", -1.9436d), + new EpsgOperationParameterRecord(1927, "Y-axis rotation", -11.5993d), + new EpsgOperationParameterRecord(1927, "Z-axis rotation", -4.3321d), + new EpsgOperationParameterRecord(1927, "Scale difference", -7.4824d), + new EpsgOperationParameterRecord(1928, "X-axis translation", -408.809d), + new EpsgOperationParameterRecord(1928, "Y-axis translation", 366.856d), + new EpsgOperationParameterRecord(1928, "Z-axis translation", -412.987d), + new EpsgOperationParameterRecord(1928, "X-axis rotation", 1.8842d), + new EpsgOperationParameterRecord(1928, "Y-axis rotation", -0.5308d), + new EpsgOperationParameterRecord(1928, "Z-axis rotation", 2.1655d), + new EpsgOperationParameterRecord(1928, "Scale difference", -121.0993d), + new EpsgOperationParameterRecord(1931, "X-axis translation", -480.26d), + new EpsgOperationParameterRecord(1931, "Y-axis translation", -438.32d), + new EpsgOperationParameterRecord(1931, "Z-axis translation", -643.429d), + new EpsgOperationParameterRecord(1931, "X-axis rotation", 16.3119d), + new EpsgOperationParameterRecord(1931, "Y-axis rotation", 20.1721d), + new EpsgOperationParameterRecord(1931, "Z-axis rotation", -4.0349d), + new EpsgOperationParameterRecord(1931, "Scale difference", -111.7002d), + new EpsgOperationParameterRecord(1946, "X-axis translation", -0.991d), + new EpsgOperationParameterRecord(1946, "Y-axis translation", 1.9072d), + new EpsgOperationParameterRecord(1946, "Z-axis translation", 0.5129d), + new EpsgOperationParameterRecord(1946, "X-axis rotation", -1.25033e-07d), + new EpsgOperationParameterRecord(1946, "Y-axis rotation", -4.6785e-08d), + new EpsgOperationParameterRecord(1946, "Z-axis rotation", -5.6529e-08d), + new EpsgOperationParameterRecord(1946, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(1950, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1950, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1950, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1951, "X-axis translation", -73.0d), + new EpsgOperationParameterRecord(1951, "Y-axis translation", 46.0d), + new EpsgOperationParameterRecord(1951, "Z-axis translation", -86.0d), + new EpsgOperationParameterRecord(1952, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1952, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1952, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1953, "X-axis translation", 482.5d), + new EpsgOperationParameterRecord(1953, "Y-axis translation", -130.6d), + new EpsgOperationParameterRecord(1953, "Z-axis translation", 564.6d), + new EpsgOperationParameterRecord(1953, "X-axis rotation", -1.042d), + new EpsgOperationParameterRecord(1953, "Y-axis rotation", -0.214d), + new EpsgOperationParameterRecord(1953, "Z-axis rotation", -0.631d), + new EpsgOperationParameterRecord(1953, "Scale difference", 8.15d), + new EpsgOperationParameterRecord(1954, "X-axis translation", 482.5d), + new EpsgOperationParameterRecord(1954, "Y-axis translation", -130.6d), + new EpsgOperationParameterRecord(1954, "Z-axis translation", 564.6d), + new EpsgOperationParameterRecord(1954, "X-axis rotation", -1.042d), + new EpsgOperationParameterRecord(1954, "Y-axis rotation", -0.214d), + new EpsgOperationParameterRecord(1954, "Z-axis rotation", -0.631d), + new EpsgOperationParameterRecord(1954, "Scale difference", 8.15d), + new EpsgOperationParameterRecord(1955, "X-axis translation", 482.5d), + new EpsgOperationParameterRecord(1955, "Y-axis translation", -130.6d), + new EpsgOperationParameterRecord(1955, "Z-axis translation", 564.6d), + new EpsgOperationParameterRecord(1955, "X-axis rotation", -1.042d), + new EpsgOperationParameterRecord(1955, "Y-axis rotation", -0.214d), + new EpsgOperationParameterRecord(1955, "Z-axis rotation", -0.631d), + new EpsgOperationParameterRecord(1955, "Scale difference", 8.15d), + new EpsgOperationParameterRecord(1956, "X-axis translation", 506.0d), + new EpsgOperationParameterRecord(1956, "Y-axis translation", -122.0d), + new EpsgOperationParameterRecord(1956, "Z-axis translation", 611.0d), + new EpsgOperationParameterRecord(1957, "X-axis translation", 982.6087d), + new EpsgOperationParameterRecord(1957, "Y-axis translation", 552.753d), + new EpsgOperationParameterRecord(1957, "Z-axis translation", -540.873d), + new EpsgOperationParameterRecord(1957, "X-axis rotation", 32.39344d), + new EpsgOperationParameterRecord(1957, "Y-axis rotation", -153.25684d), + new EpsgOperationParameterRecord(1957, "Z-axis rotation", -96.2266d), + new EpsgOperationParameterRecord(1957, "Scale difference", 16.805d), + new EpsgOperationParameterRecord(1958, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1958, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1958, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1959, "X-axis translation", 195.671d), + new EpsgOperationParameterRecord(1959, "Y-axis translation", 332.517d), + new EpsgOperationParameterRecord(1959, "Z-axis translation", 274.607d), + new EpsgOperationParameterRecord(1962, "X-axis translation", -13.0d), + new EpsgOperationParameterRecord(1962, "Y-axis translation", -348.0d), + new EpsgOperationParameterRecord(1962, "Z-axis translation", 292.0d), + new EpsgOperationParameterRecord(1963, "X-axis translation", 97.295d), + new EpsgOperationParameterRecord(1963, "Y-axis translation", -263.247d), + new EpsgOperationParameterRecord(1963, "Z-axis translation", 310.882d), + new EpsgOperationParameterRecord(1963, "X-axis rotation", -1.5999d), + new EpsgOperationParameterRecord(1963, "Y-axis rotation", 0.8386d), + new EpsgOperationParameterRecord(1963, "Z-axis rotation", 3.1409d), + new EpsgOperationParameterRecord(1963, "Scale difference", 13.3259d), + new EpsgOperationParameterRecord(1964, "X-axis translation", -789.99d), + new EpsgOperationParameterRecord(1964, "Y-axis translation", 627.333d), + new EpsgOperationParameterRecord(1964, "Z-axis translation", 89.685d), + new EpsgOperationParameterRecord(1964, "X-axis rotation", -0.6072d), + new EpsgOperationParameterRecord(1964, "Y-axis rotation", -76.8019d), + new EpsgOperationParameterRecord(1964, "Z-axis rotation", 10.568d), + new EpsgOperationParameterRecord(1964, "Scale difference", 32.2083d), + new EpsgOperationParameterRecord(1965, "X-axis translation", -289.0d), + new EpsgOperationParameterRecord(1965, "Y-axis translation", -124.0d), + new EpsgOperationParameterRecord(1965, "Z-axis translation", 60.0d), + new EpsgOperationParameterRecord(1966, "X-axis translation", -502.862d), + new EpsgOperationParameterRecord(1966, "Y-axis translation", -247.438d), + new EpsgOperationParameterRecord(1966, "Z-axis translation", 312.724d), + new EpsgOperationParameterRecord(1967, "X-axis translation", -210.502d), + new EpsgOperationParameterRecord(1967, "Y-axis translation", -66.902d), + new EpsgOperationParameterRecord(1967, "Z-axis translation", -48.476d), + new EpsgOperationParameterRecord(1967, "X-axis rotation", -2.094d), + new EpsgOperationParameterRecord(1967, "Y-axis rotation", 15.067d), + new EpsgOperationParameterRecord(1967, "Z-axis rotation", 5.817d), + new EpsgOperationParameterRecord(1967, "Scale difference", 0.485d), + new EpsgOperationParameterRecord(1968, "X-axis translation", -204.633d), + new EpsgOperationParameterRecord(1968, "Y-axis translation", 140.216d), + new EpsgOperationParameterRecord(1968, "Z-axis translation", 55.199d), + new EpsgOperationParameterRecord(1969, "X-axis translation", -211.939d), + new EpsgOperationParameterRecord(1969, "Y-axis translation", 137.626d), + new EpsgOperationParameterRecord(1969, "Z-axis translation", 58.3d), + new EpsgOperationParameterRecord(1969, "X-axis rotation", 0.089d), + new EpsgOperationParameterRecord(1969, "Y-axis rotation", -0.251d), + new EpsgOperationParameterRecord(1969, "Z-axis rotation", -0.079d), + new EpsgOperationParameterRecord(1969, "Scale difference", 0.384d), + new EpsgOperationParameterRecord(1970, "X-axis translation", -204.619d), + new EpsgOperationParameterRecord(1970, "Y-axis translation", 140.176d), + new EpsgOperationParameterRecord(1970, "Z-axis translation", 55.226d), + new EpsgOperationParameterRecord(1971, "X-axis translation", -208.719d), + new EpsgOperationParameterRecord(1971, "Y-axis translation", 129.685d), + new EpsgOperationParameterRecord(1971, "Z-axis translation", 52.092d), + new EpsgOperationParameterRecord(1971, "X-axis rotation", 0.195d), + new EpsgOperationParameterRecord(1971, "Y-axis rotation", 0.014d), + new EpsgOperationParameterRecord(1971, "Z-axis rotation", -0.327d), + new EpsgOperationParameterRecord(1971, "Scale difference", 0.198d), + new EpsgOperationParameterRecord(1972, "X-axis translation", -106.301d), + new EpsgOperationParameterRecord(1972, "Y-axis translation", 166.27d), + new EpsgOperationParameterRecord(1972, "Z-axis translation", -37.916d), + new EpsgOperationParameterRecord(1973, "X-axis translation", -105.854d), + new EpsgOperationParameterRecord(1973, "Y-axis translation", 165.589d), + new EpsgOperationParameterRecord(1973, "Z-axis translation", -38.312d), + new EpsgOperationParameterRecord(1973, "X-axis rotation", 0.003d), + new EpsgOperationParameterRecord(1973, "Y-axis rotation", 0.026d), + new EpsgOperationParameterRecord(1973, "Z-axis rotation", -0.024d), + new EpsgOperationParameterRecord(1973, "Scale difference", -0.048d), + new EpsgOperationParameterRecord(1974, "X-axis translation", -106.248d), + new EpsgOperationParameterRecord(1974, "Y-axis translation", 166.244d), + new EpsgOperationParameterRecord(1974, "Z-axis translation", -37.845d), + new EpsgOperationParameterRecord(1975, "X-axis translation", -104.0d), + new EpsgOperationParameterRecord(1975, "Y-axis translation", 162.924d), + new EpsgOperationParameterRecord(1975, "Z-axis translation", -38.882d), + new EpsgOperationParameterRecord(1975, "X-axis rotation", 0.075d), + new EpsgOperationParameterRecord(1975, "Y-axis rotation", 0.071d), + new EpsgOperationParameterRecord(1975, "Z-axis rotation", -0.051d), + new EpsgOperationParameterRecord(1975, "Scale difference", -0.338d), + new EpsgOperationParameterRecord(1976, "X-axis translation", -106.044d), + new EpsgOperationParameterRecord(1976, "Y-axis translation", 166.655d), + new EpsgOperationParameterRecord(1976, "Z-axis translation", -37.876d), + new EpsgOperationParameterRecord(1977, "X-axis translation", -95.323d), + new EpsgOperationParameterRecord(1977, "Y-axis translation", 166.098d), + new EpsgOperationParameterRecord(1977, "Z-axis translation", -69.942d), + new EpsgOperationParameterRecord(1977, "X-axis rotation", 0.215d), + new EpsgOperationParameterRecord(1977, "Y-axis rotation", 1.031d), + new EpsgOperationParameterRecord(1977, "Z-axis rotation", -0.047d), + new EpsgOperationParameterRecord(1977, "Scale difference", 1.922d), + new EpsgOperationParameterRecord(1978, "X-axis translation", -106.253d), + new EpsgOperationParameterRecord(1978, "Y-axis translation", 166.239d), + new EpsgOperationParameterRecord(1978, "Z-axis translation", -37.854d), + new EpsgOperationParameterRecord(1979, "X-axis translation", -100.306d), + new EpsgOperationParameterRecord(1979, "Y-axis translation", 161.246d), + new EpsgOperationParameterRecord(1979, "Z-axis translation", -48.761d), + new EpsgOperationParameterRecord(1979, "X-axis rotation", 0.192d), + new EpsgOperationParameterRecord(1979, "Y-axis rotation", 0.385d), + new EpsgOperationParameterRecord(1979, "Z-axis rotation", -0.076d), + new EpsgOperationParameterRecord(1979, "Scale difference", 0.131d), + new EpsgOperationParameterRecord(1980, "X-axis translation", -106.226d), + new EpsgOperationParameterRecord(1980, "Y-axis translation", 166.366d), + new EpsgOperationParameterRecord(1980, "Z-axis translation", -37.893d), + new EpsgOperationParameterRecord(1981, "X-axis translation", -103.088d), + new EpsgOperationParameterRecord(1981, "Y-axis translation", 162.481d), + new EpsgOperationParameterRecord(1981, "Z-axis translation", -28.276d), + new EpsgOperationParameterRecord(1981, "X-axis rotation", -0.167d), + new EpsgOperationParameterRecord(1981, "Y-axis rotation", -0.082d), + new EpsgOperationParameterRecord(1981, "Z-axis rotation", -0.168d), + new EpsgOperationParameterRecord(1981, "Scale difference", -1.504d), + new EpsgOperationParameterRecord(1982, "X-axis translation", -422.651d), + new EpsgOperationParameterRecord(1982, "Y-axis translation", -172.995d), + new EpsgOperationParameterRecord(1982, "Z-axis translation", 84.02d), + new EpsgOperationParameterRecord(1983, "X-axis translation", -223.237d), + new EpsgOperationParameterRecord(1983, "Y-axis translation", 110.193d), + new EpsgOperationParameterRecord(1983, "Z-axis translation", 36.649d), + new EpsgOperationParameterRecord(1984, "X-axis translation", -304.046d), + new EpsgOperationParameterRecord(1984, "Y-axis translation", -60.576d), + new EpsgOperationParameterRecord(1984, "Z-axis translation", 103.64d), + new EpsgOperationParameterRecord(1985, "X-axis translation", -87.987d), + new EpsgOperationParameterRecord(1985, "Y-axis translation", -108.639d), + new EpsgOperationParameterRecord(1985, "Z-axis translation", -121.593d), + new EpsgOperationParameterRecord(1986, "X-axis translation", 508.088d), + new EpsgOperationParameterRecord(1986, "Y-axis translation", -191.042d), + new EpsgOperationParameterRecord(1986, "Z-axis translation", 565.223d), + new EpsgOperationParameterRecord(1987, "X-axis translation", -239.749d), + new EpsgOperationParameterRecord(1987, "Y-axis translation", 88.181d), + new EpsgOperationParameterRecord(1987, "Z-axis translation", 30.488d), + new EpsgOperationParameterRecord(1987, "X-axis rotation", -0.263d), + new EpsgOperationParameterRecord(1987, "Y-axis rotation", -0.082d), + new EpsgOperationParameterRecord(1987, "Z-axis rotation", -1.211d), + new EpsgOperationParameterRecord(1987, "Scale difference", 2.229d), + new EpsgOperationParameterRecord(1988, "X-axis translation", -288.885d), + new EpsgOperationParameterRecord(1988, "Y-axis translation", -91.744d), + new EpsgOperationParameterRecord(1988, "Z-axis translation", 126.244d), + new EpsgOperationParameterRecord(1988, "X-axis rotation", 1.691d), + new EpsgOperationParameterRecord(1988, "Y-axis rotation", -0.41d), + new EpsgOperationParameterRecord(1988, "Z-axis rotation", 0.211d), + new EpsgOperationParameterRecord(1988, "Scale difference", -4.598d), + new EpsgOperationParameterRecord(1989, "X-axis translation", -74.292d), + new EpsgOperationParameterRecord(1989, "Y-axis translation", -135.889d), + new EpsgOperationParameterRecord(1989, "Z-axis translation", -104.967d), + new EpsgOperationParameterRecord(1989, "X-axis rotation", 0.524d), + new EpsgOperationParameterRecord(1989, "Y-axis rotation", 0.136d), + new EpsgOperationParameterRecord(1989, "Z-axis rotation", -0.61d), + new EpsgOperationParameterRecord(1989, "Scale difference", -3.761d), + new EpsgOperationParameterRecord(1990, "X-axis translation", 631.392d), + new EpsgOperationParameterRecord(1990, "Y-axis translation", -66.551d), + new EpsgOperationParameterRecord(1990, "Z-axis translation", 481.442d), + new EpsgOperationParameterRecord(1990, "X-axis rotation", -1.09d), + new EpsgOperationParameterRecord(1990, "Y-axis rotation", 4.445d), + new EpsgOperationParameterRecord(1990, "Z-axis rotation", 4.487d), + new EpsgOperationParameterRecord(1990, "Scale difference", -4.43d), + new EpsgOperationParameterRecord(1991, "Longitude offset", -9.13190611111139d), + new EpsgOperationParameterRecord(1992, "X-axis translation", -231.034d), + new EpsgOperationParameterRecord(1992, "Y-axis translation", 102.615d), + new EpsgOperationParameterRecord(1992, "Z-axis translation", 26.836d), + new EpsgOperationParameterRecord(1992, "X-axis rotation", -0.615d), + new EpsgOperationParameterRecord(1992, "Y-axis rotation", 0.198d), + new EpsgOperationParameterRecord(1992, "Z-axis rotation", -0.881d), + new EpsgOperationParameterRecord(1992, "Scale difference", 1.786d), + new EpsgOperationParameterRecord(1993, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(1993, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(1993, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(1994, "X-axis translation", -28.0d), + new EpsgOperationParameterRecord(1994, "Y-axis translation", 199.0d), + new EpsgOperationParameterRecord(1994, "Z-axis translation", 5.0d), + new EpsgOperationParameterRecord(1995, "X-axis translation", 103.25d), + new EpsgOperationParameterRecord(1995, "Y-axis translation", -100.4d), + new EpsgOperationParameterRecord(1995, "Z-axis translation", -307.19d), + new EpsgOperationParameterRecord(1997, "X-axis translation", -282.1d), + new EpsgOperationParameterRecord(1997, "Y-axis translation", -72.2d), + new EpsgOperationParameterRecord(1997, "Z-axis translation", 120.0d), + new EpsgOperationParameterRecord(1997, "X-axis rotation", -1.529d), + new EpsgOperationParameterRecord(1997, "Y-axis rotation", 0.145d), + new EpsgOperationParameterRecord(1997, "Z-axis rotation", -0.89d), + new EpsgOperationParameterRecord(1997, "Scale difference", -4.46d), + new EpsgOperationParameterRecord(1998, "X-axis translation", -157.89d), + new EpsgOperationParameterRecord(1998, "Y-axis translation", -17.16d), + new EpsgOperationParameterRecord(1998, "Z-axis translation", -78.41d), + new EpsgOperationParameterRecord(1998, "X-axis rotation", 2.118d), + new EpsgOperationParameterRecord(1998, "Y-axis rotation", 2.697d), + new EpsgOperationParameterRecord(1998, "Z-axis rotation", -1.434d), + new EpsgOperationParameterRecord(1998, "Scale difference", -5.38d), + new EpsgOperationParameterRecord(3817, "X-axis translation", 595.48d), + new EpsgOperationParameterRecord(3817, "Y-axis translation", 121.69d), + new EpsgOperationParameterRecord(3817, "Z-axis translation", 515.35d), + new EpsgOperationParameterRecord(3817, "X-axis rotation", -4.115d), + new EpsgOperationParameterRecord(3817, "Y-axis rotation", 2.9383d), + new EpsgOperationParameterRecord(3817, "Z-axis rotation", -0.853d), + new EpsgOperationParameterRecord(3817, "Scale difference", -3.408d), + new EpsgOperationParameterRecord(3830, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(3830, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(3830, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(3894, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(3894, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(3894, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(3895, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(3904, "X-axis translation", -83.11d), + new EpsgOperationParameterRecord(3904, "Y-axis translation", -97.38d), + new EpsgOperationParameterRecord(3904, "Z-axis translation", -117.22d), + new EpsgOperationParameterRecord(3904, "X-axis rotation", 0.0276d), + new EpsgOperationParameterRecord(3904, "Y-axis rotation", -0.2167d), + new EpsgOperationParameterRecord(3904, "Z-axis rotation", 0.2147d), + new EpsgOperationParameterRecord(3904, "Scale difference", 0.1218d), + new EpsgOperationParameterRecord(3905, "X-axis translation", -83.11d), + new EpsgOperationParameterRecord(3905, "Y-axis translation", -97.38d), + new EpsgOperationParameterRecord(3905, "Z-axis translation", -117.22d), + new EpsgOperationParameterRecord(3905, "X-axis rotation", 0.0276d), + new EpsgOperationParameterRecord(3905, "Y-axis rotation", -0.2167d), + new EpsgOperationParameterRecord(3905, "Z-axis rotation", 0.2147d), + new EpsgOperationParameterRecord(3905, "Scale difference", 0.1218d), + new EpsgOperationParameterRecord(3913, "Longitude offset", -17.6627833333336d), + new EpsgOperationParameterRecord(3914, "X-axis translation", 426.9d), + new EpsgOperationParameterRecord(3914, "Y-axis translation", 142.6d), + new EpsgOperationParameterRecord(3914, "Z-axis translation", 460.1d), + new EpsgOperationParameterRecord(3914, "X-axis rotation", 4.91d), + new EpsgOperationParameterRecord(3914, "Y-axis rotation", 4.49d), + new EpsgOperationParameterRecord(3914, "Z-axis rotation", -12.42d), + new EpsgOperationParameterRecord(3914, "Scale difference", 17.1d), + new EpsgOperationParameterRecord(3915, "X-axis translation", 426.9d), + new EpsgOperationParameterRecord(3915, "Y-axis translation", 142.6d), + new EpsgOperationParameterRecord(3915, "Z-axis translation", 460.1d), + new EpsgOperationParameterRecord(3915, "X-axis rotation", 4.91d), + new EpsgOperationParameterRecord(3915, "Y-axis rotation", 4.49d), + new EpsgOperationParameterRecord(3915, "Z-axis rotation", -12.42d), + new EpsgOperationParameterRecord(3915, "Scale difference", 17.1d), + new EpsgOperationParameterRecord(3916, "X-axis translation", 409.545d), + new EpsgOperationParameterRecord(3916, "Y-axis translation", 72.164d), + new EpsgOperationParameterRecord(3916, "Z-axis translation", 486.872d), + new EpsgOperationParameterRecord(3916, "X-axis rotation", -3.085957d), + new EpsgOperationParameterRecord(3916, "Y-axis rotation", -5.46911d), + new EpsgOperationParameterRecord(3916, "Z-axis rotation", 11.020289d), + new EpsgOperationParameterRecord(3916, "Scale difference", 17.919665d), + new EpsgOperationParameterRecord(3917, "X-axis translation", 409.545d), + new EpsgOperationParameterRecord(3917, "Y-axis translation", 72.164d), + new EpsgOperationParameterRecord(3917, "Z-axis translation", 486.872d), + new EpsgOperationParameterRecord(3917, "X-axis rotation", -3.085957d), + new EpsgOperationParameterRecord(3917, "Y-axis rotation", -5.46911d), + new EpsgOperationParameterRecord(3917, "Z-axis rotation", 11.020289d), + new EpsgOperationParameterRecord(3917, "Scale difference", 17.919665d), + new EpsgOperationParameterRecord(3918, "X-axis translation", 315.393d), + new EpsgOperationParameterRecord(3918, "Y-axis translation", 186.223d), + new EpsgOperationParameterRecord(3918, "Z-axis translation", 499.609d), + new EpsgOperationParameterRecord(3918, "X-axis rotation", -6.445954d), + new EpsgOperationParameterRecord(3918, "Y-axis rotation", -8.131631d), + new EpsgOperationParameterRecord(3918, "Z-axis rotation", 13.208641d), + new EpsgOperationParameterRecord(3918, "Scale difference", 23.449046d), + new EpsgOperationParameterRecord(3919, "X-axis translation", 464.939d), + new EpsgOperationParameterRecord(3919, "Y-axis translation", -21.478d), + new EpsgOperationParameterRecord(3919, "Z-axis translation", 504.497d), + new EpsgOperationParameterRecord(3919, "X-axis rotation", 0.403d), + new EpsgOperationParameterRecord(3919, "Y-axis rotation", -4.228747d), + new EpsgOperationParameterRecord(3919, "Z-axis rotation", 9.954942d), + new EpsgOperationParameterRecord(3919, "Scale difference", 12.795378d), + new EpsgOperationParameterRecord(3921, "X-axis translation", 459.968d), + new EpsgOperationParameterRecord(3921, "Y-axis translation", 82.193d), + new EpsgOperationParameterRecord(3921, "Z-axis translation", 458.756d), + new EpsgOperationParameterRecord(3921, "X-axis rotation", -3.565234d), + new EpsgOperationParameterRecord(3921, "Y-axis rotation", -3.700593d), + new EpsgOperationParameterRecord(3921, "Z-axis rotation", 10.860523d), + new EpsgOperationParameterRecord(3921, "Scale difference", 15.507563d), + new EpsgOperationParameterRecord(3922, "X-axis translation", 427.914d), + new EpsgOperationParameterRecord(3922, "Y-axis translation", 105.528d), + new EpsgOperationParameterRecord(3922, "Z-axis translation", 510.908d), + new EpsgOperationParameterRecord(3922, "X-axis rotation", -4.992523d), + new EpsgOperationParameterRecord(3922, "Y-axis rotation", -5.898813d), + new EpsgOperationParameterRecord(3922, "Z-axis rotation", 10.306673d), + new EpsgOperationParameterRecord(3922, "Scale difference", 12.431493d), + new EpsgOperationParameterRecord(3923, "X-axis translation", 468.63d), + new EpsgOperationParameterRecord(3923, "Y-axis translation", 81.389d), + new EpsgOperationParameterRecord(3923, "Z-axis translation", 445.221d), + new EpsgOperationParameterRecord(3923, "X-axis rotation", -3.839242d), + new EpsgOperationParameterRecord(3923, "Y-axis rotation", -3.262525d), + new EpsgOperationParameterRecord(3923, "Z-axis rotation", 10.566866d), + new EpsgOperationParameterRecord(3923, "Scale difference", 16.132726d), + new EpsgOperationParameterRecord(3924, "X-axis translation", 439.5d), + new EpsgOperationParameterRecord(3924, "Y-axis translation", -11.77d), + new EpsgOperationParameterRecord(3924, "Z-axis translation", 494.976d), + new EpsgOperationParameterRecord(3924, "X-axis rotation", -0.026585d), + new EpsgOperationParameterRecord(3924, "Y-axis rotation", -4.65641d), + new EpsgOperationParameterRecord(3924, "Z-axis rotation", 10.155824d), + new EpsgOperationParameterRecord(3924, "Scale difference", 16.270002d), + new EpsgOperationParameterRecord(3925, "X-axis translation", 524.442d), + new EpsgOperationParameterRecord(3925, "Y-axis translation", 3.275d), + new EpsgOperationParameterRecord(3925, "Z-axis translation", 519.002d), + new EpsgOperationParameterRecord(3925, "X-axis rotation", 0.013287d), + new EpsgOperationParameterRecord(3925, "Y-axis rotation", -3.119714d), + new EpsgOperationParameterRecord(3925, "Z-axis rotation", 10.232693d), + new EpsgOperationParameterRecord(3925, "Scale difference", 4.184981d), + new EpsgOperationParameterRecord(3926, "X-axis translation", 281.529d), + new EpsgOperationParameterRecord(3926, "Y-axis translation", 45.963d), + new EpsgOperationParameterRecord(3926, "Z-axis translation", 537.515d), + new EpsgOperationParameterRecord(3926, "X-axis rotation", -2.570437d), + new EpsgOperationParameterRecord(3926, "Y-axis rotation", -9.648271d), + new EpsgOperationParameterRecord(3926, "Z-axis rotation", 10.759507d), + new EpsgOperationParameterRecord(3926, "Scale difference", 26.465548d), + new EpsgOperationParameterRecord(3927, "X-axis translation", 355.845d), + new EpsgOperationParameterRecord(3927, "Y-axis translation", 274.282d), + new EpsgOperationParameterRecord(3927, "Z-axis translation", 462.979d), + new EpsgOperationParameterRecord(3927, "X-axis rotation", -9.086933d), + new EpsgOperationParameterRecord(3927, "Y-axis rotation", -6.491055d), + new EpsgOperationParameterRecord(3927, "Z-axis rotation", 14.502181d), + new EpsgOperationParameterRecord(3927, "Scale difference", 20.888647d), + new EpsgOperationParameterRecord(3928, "X-axis translation", 400.629d), + new EpsgOperationParameterRecord(3928, "Y-axis translation", 90.651d), + new EpsgOperationParameterRecord(3928, "Z-axis translation", 472.249d), + new EpsgOperationParameterRecord(3928, "X-axis rotation", -3.261138d), + new EpsgOperationParameterRecord(3928, "Y-axis rotation", -5.263404d), + new EpsgOperationParameterRecord(3928, "Z-axis rotation", 11.83739d), + new EpsgOperationParameterRecord(3928, "Scale difference", 20.022676d), + new EpsgOperationParameterRecord(3929, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3929, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3929, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3929, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3929, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3929, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3929, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3929, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3929, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3929, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3929, "Ordinate 1 of evaluation point in target CRS", -378.752d), + new EpsgOperationParameterRecord(3929, "Ordinate 2 of evaluation point in target CRS", 493.395d), + new EpsgOperationParameterRecord(3929, "Scale factor for source CRS axes", 1.0000126775d), + new EpsgOperationParameterRecord(3929, "Rotation angle of source CRS axes", 0.000984675d), + new EpsgOperationParameterRecord(3930, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3930, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3930, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3930, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3930, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3930, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3930, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3930, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3930, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3930, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3930, "Ordinate 1 of evaluation point in target CRS", -380.322d), + new EpsgOperationParameterRecord(3930, "Ordinate 2 of evaluation point in target CRS", 494.216d), + new EpsgOperationParameterRecord(3930, "Scale factor for source CRS axes", 1.000015768d), + new EpsgOperationParameterRecord(3930, "Rotation angle of source CRS axes", 0.0012011048d), + new EpsgOperationParameterRecord(3931, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3931, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3931, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3931, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3931, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3931, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3931, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3931, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3931, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3931, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3931, "Ordinate 1 of evaluation point in target CRS", -382.19d), + new EpsgOperationParameterRecord(3931, "Ordinate 2 of evaluation point in target CRS", 492.412d), + new EpsgOperationParameterRecord(3931, "Scale factor for source CRS axes", 1.0000210585d), + new EpsgOperationParameterRecord(3931, "Rotation angle of source CRS axes", 0.0009919079d), + new EpsgOperationParameterRecord(3932, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3932, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3932, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3932, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3932, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3932, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3932, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3932, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3932, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3932, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3932, "Ordinate 1 of evaluation point in target CRS", -377.487d), + new EpsgOperationParameterRecord(3932, "Ordinate 2 of evaluation point in target CRS", 492.209d), + new EpsgOperationParameterRecord(3932, "Scale factor for source CRS axes", 1.0000103303d), + new EpsgOperationParameterRecord(3932, "Rotation angle of source CRS axes", 0.0008612384d), + new EpsgOperationParameterRecord(3933, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3933, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3933, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3933, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3933, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3933, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3933, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3933, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3933, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3933, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3933, "Ordinate 1 of evaluation point in target CRS", -383.677d), + new EpsgOperationParameterRecord(3933, "Ordinate 2 of evaluation point in target CRS", 493.408d), + new EpsgOperationParameterRecord(3933, "Scale factor for source CRS axes", 1.000023822d), + new EpsgOperationParameterRecord(3933, "Rotation angle of source CRS axes", 0.0011916674d), + new EpsgOperationParameterRecord(3934, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3934, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3934, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3934, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3934, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3934, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3934, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3934, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3934, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3934, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3934, "Ordinate 1 of evaluation point in target CRS", -379.867d), + new EpsgOperationParameterRecord(3934, "Ordinate 2 of evaluation point in target CRS", 496.342d), + new EpsgOperationParameterRecord(3934, "Scale factor for source CRS axes", 1.0000139529d), + new EpsgOperationParameterRecord(3934, "Rotation angle of source CRS axes", 0.001410572d), + new EpsgOperationParameterRecord(3935, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3935, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3935, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3935, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3935, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3935, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3935, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3935, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3935, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3935, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3935, "Ordinate 1 of evaluation point in target CRS", -378.706d), + new EpsgOperationParameterRecord(3935, "Ordinate 2 of evaluation point in target CRS", 493.722d), + new EpsgOperationParameterRecord(3935, "Scale factor for source CRS axes", 1.0000128479d), + new EpsgOperationParameterRecord(3935, "Rotation angle of source CRS axes", 0.0010638143d), + new EpsgOperationParameterRecord(3936, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3936, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3936, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3936, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3936, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3936, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3936, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3936, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3936, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3936, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3936, "Ordinate 1 of evaluation point in target CRS", -376.275d), + new EpsgOperationParameterRecord(3936, "Ordinate 2 of evaluation point in target CRS", 493.231d), + new EpsgOperationParameterRecord(3936, "Scale factor for source CRS axes", 1.0000076601d), + new EpsgOperationParameterRecord(3936, "Rotation angle of source CRS axes", 0.000972004d), + new EpsgOperationParameterRecord(3937, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3937, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3937, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3937, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3937, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3937, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3937, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3937, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3937, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3937, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3937, "Ordinate 1 of evaluation point in target CRS", -379.883d), + new EpsgOperationParameterRecord(3937, "Ordinate 2 of evaluation point in target CRS", 497.465d), + new EpsgOperationParameterRecord(3937, "Scale factor for source CRS axes", 1.0000132379d), + new EpsgOperationParameterRecord(3937, "Rotation angle of source CRS axes", 0.0015286859d), + new EpsgOperationParameterRecord(3938, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3938, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3938, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3938, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3938, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3938, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3938, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3938, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3938, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3938, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3938, "Ordinate 1 of evaluation point in target CRS", -380.127d), + new EpsgOperationParameterRecord(3938, "Ordinate 2 of evaluation point in target CRS", 497.52d), + new EpsgOperationParameterRecord(3938, "Scale factor for source CRS axes", 1.0000138184d), + new EpsgOperationParameterRecord(3938, "Rotation angle of source CRS axes", 0.001539718d), + new EpsgOperationParameterRecord(3939, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3939, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3939, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3939, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3939, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3939, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3939, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3939, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3939, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3939, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3939, "Ordinate 1 of evaluation point in target CRS", -377.965d), + new EpsgOperationParameterRecord(3939, "Ordinate 2 of evaluation point in target CRS", 499.354d), + new EpsgOperationParameterRecord(3939, "Scale factor for source CRS axes", 1.0000089352d), + new EpsgOperationParameterRecord(3939, "Rotation angle of source CRS axes", 0.0017109424d), + new EpsgOperationParameterRecord(3940, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3940, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3940, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3940, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3940, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3940, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3940, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3940, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3940, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3940, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3940, "Ordinate 1 of evaluation point in target CRS", -384.455d), + new EpsgOperationParameterRecord(3940, "Ordinate 2 of evaluation point in target CRS", 487.979d), + new EpsgOperationParameterRecord(3940, "Scale factor for source CRS axes", 1.0000246653d), + new EpsgOperationParameterRecord(3940, "Rotation angle of source CRS axes", 0.0004151044d), + new EpsgOperationParameterRecord(3941, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3941, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3941, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3941, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3941, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3941, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3941, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3941, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3941, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3941, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3941, "Ordinate 1 of evaluation point in target CRS", -384.415d), + new EpsgOperationParameterRecord(3941, "Ordinate 2 of evaluation point in target CRS", 502.308d), + new EpsgOperationParameterRecord(3941, "Scale factor for source CRS axes", 1.0000190161d), + new EpsgOperationParameterRecord(3941, "Rotation angle of source CRS axes", 0.0021913792d), + new EpsgOperationParameterRecord(3951, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3951, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3951, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3951, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3951, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3951, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3951, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3951, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3951, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3951, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3951, "Ordinate 1 of evaluation point in target CRS", -380.84d), + new EpsgOperationParameterRecord(3951, "Ordinate 2 of evaluation point in target CRS", 495.612d), + new EpsgOperationParameterRecord(3951, "Scale factor for source CRS axes", 1.0000158378d), + new EpsgOperationParameterRecord(3951, "Rotation angle of source CRS axes", 0.0013669828d), + new EpsgOperationParameterRecord(3952, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3952, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3952, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3952, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3952, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3952, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3952, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3952, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3952, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3952, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3952, "Ordinate 1 of evaluation point in target CRS", -377.812d), + new EpsgOperationParameterRecord(3952, "Ordinate 2 of evaluation point in target CRS", 496.076d), + new EpsgOperationParameterRecord(3952, "Scale factor for source CRS axes", 1.0000100475d), + new EpsgOperationParameterRecord(3952, "Rotation angle of source CRS axes", 0.0013339461d), + new EpsgOperationParameterRecord(3953, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3953, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3953, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3953, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3953, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3953, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3953, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3953, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3953, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3953, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3953, "Ordinate 1 of evaluation point in target CRS", -379.658d), + new EpsgOperationParameterRecord(3953, "Ordinate 2 of evaluation point in target CRS", 493.837d), + new EpsgOperationParameterRecord(3953, "Scale factor for source CRS axes", 1.0000140367d), + new EpsgOperationParameterRecord(3953, "Rotation angle of source CRS axes", 0.0011164461d), + new EpsgOperationParameterRecord(3954, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3954, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3954, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3954, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3954, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3954, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3954, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3954, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3954, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3954, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3954, "Ordinate 1 of evaluation point in target CRS", -382.138d), + new EpsgOperationParameterRecord(3954, "Ordinate 2 of evaluation point in target CRS", 496.819d), + new EpsgOperationParameterRecord(3954, "Scale factor for source CRS axes", 1.0000177148d), + new EpsgOperationParameterRecord(3954, "Rotation angle of source CRS axes", 0.0014400562d), + new EpsgOperationParameterRecord(3955, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3955, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3955, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3955, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3955, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3955, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3955, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3955, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3955, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3955, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3955, "Ordinate 1 of evaluation point in target CRS", -375.995d), + new EpsgOperationParameterRecord(3955, "Ordinate 2 of evaluation point in target CRS", 490.833d), + new EpsgOperationParameterRecord(3955, "Scale factor for source CRS axes", 1.0000073161d), + new EpsgOperationParameterRecord(3955, "Rotation angle of source CRS axes", 0.0006771071d), + new EpsgOperationParameterRecord(3956, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3956, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3956, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3956, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3956, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3956, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3956, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3956, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3956, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3956, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3956, "Ordinate 1 of evaluation point in target CRS", -381.28d), + new EpsgOperationParameterRecord(3956, "Ordinate 2 of evaluation point in target CRS", 505.983d), + new EpsgOperationParameterRecord(3956, "Scale factor for source CRS axes", 1.0000118449d), + new EpsgOperationParameterRecord(3956, "Rotation angle of source CRS axes", 0.0024426318d), + new EpsgOperationParameterRecord(3957, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3957, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3957, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3957, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3957, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3957, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3957, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3957, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3957, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3957, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3957, "Ordinate 1 of evaluation point in target CRS", -374.256d), + new EpsgOperationParameterRecord(3957, "Ordinate 2 of evaluation point in target CRS", 501.885d), + new EpsgOperationParameterRecord(3957, "Scale factor for source CRS axes", 1.0000013456d), + new EpsgOperationParameterRecord(3957, "Rotation angle of source CRS axes", 0.0018316181d), + new EpsgOperationParameterRecord(3958, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3958, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3958, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3958, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3958, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3958, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3958, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3958, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3958, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3958, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3958, "Ordinate 1 of evaluation point in target CRS", -371.329d), + new EpsgOperationParameterRecord(3958, "Ordinate 2 of evaluation point in target CRS", 496.151d), + new EpsgOperationParameterRecord(3958, "Scale factor for source CRS axes", 0.9999980009d), + new EpsgOperationParameterRecord(3958, "Rotation angle of source CRS axes", 0.0012073334d), + new EpsgOperationParameterRecord(3959, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3959, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3959, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3959, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3959, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3959, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3959, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3959, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3959, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3959, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3959, "Ordinate 1 of evaluation point in target CRS", -372.573d), + new EpsgOperationParameterRecord(3959, "Ordinate 2 of evaluation point in target CRS", 505.578d), + new EpsgOperationParameterRecord(3959, "Scale factor for source CRS axes", 0.9999970275d), + new EpsgOperationParameterRecord(3959, "Rotation angle of source CRS axes", 0.002148407d), + new EpsgOperationParameterRecord(3960, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3960, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3960, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3960, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3960, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3960, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3960, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3960, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3960, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3960, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3960, "Ordinate 1 of evaluation point in target CRS", -371.849d), + new EpsgOperationParameterRecord(3960, "Ordinate 2 of evaluation point in target CRS", 503.318d), + new EpsgOperationParameterRecord(3960, "Scale factor for source CRS axes", 0.9999967297d), + new EpsgOperationParameterRecord(3960, "Rotation angle of source CRS axes", 0.0019225985d), + new EpsgOperationParameterRecord(3961, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3961, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3961, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3961, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3961, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3961, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(3961, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(3961, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(3961, "False easting", 500000.0d), + new EpsgOperationParameterRecord(3961, "False northing", -5000000.0d), + new EpsgOperationParameterRecord(3961, "Ordinate 1 of evaluation point in target CRS", -371.498d), + new EpsgOperationParameterRecord(3961, "Ordinate 2 of evaluation point in target CRS", 504.461d), + new EpsgOperationParameterRecord(3961, "Scale factor for source CRS axes", 0.9999956084d), + new EpsgOperationParameterRecord(3961, "Rotation angle of source CRS axes", 0.0020157601d), + new EpsgOperationParameterRecord(3962, "X-axis translation", 682.0d), + new EpsgOperationParameterRecord(3962, "Y-axis translation", -203.0d), + new EpsgOperationParameterRecord(3962, "Z-axis translation", 480.0d), + new EpsgOperationParameterRecord(3963, "X-axis translation", 551.7d), + new EpsgOperationParameterRecord(3963, "Y-axis translation", 162.9d), + new EpsgOperationParameterRecord(3963, "Z-axis translation", 467.9d), + new EpsgOperationParameterRecord(3963, "X-axis rotation", 6.04d), + new EpsgOperationParameterRecord(3963, "Y-axis rotation", 1.96d), + new EpsgOperationParameterRecord(3963, "Z-axis rotation", -11.38d), + new EpsgOperationParameterRecord(3963, "Scale difference", -4.82d), + new EpsgOperationParameterRecord(3964, "X-axis translation", 551.7d), + new EpsgOperationParameterRecord(3964, "Y-axis translation", 162.9d), + new EpsgOperationParameterRecord(3964, "Z-axis translation", 467.9d), + new EpsgOperationParameterRecord(3964, "X-axis rotation", 6.04d), + new EpsgOperationParameterRecord(3964, "Y-axis rotation", 1.96d), + new EpsgOperationParameterRecord(3964, "Z-axis rotation", -11.38d), + new EpsgOperationParameterRecord(3964, "Scale difference", -4.82d), + new EpsgOperationParameterRecord(3965, "X-axis translation", 695.5d), + new EpsgOperationParameterRecord(3965, "Y-axis translation", -216.6d), + new EpsgOperationParameterRecord(3965, "Z-axis translation", 491.1d), + new EpsgOperationParameterRecord(3971, "X-axis translation", -60.31d), + new EpsgOperationParameterRecord(3971, "Y-axis translation", 245.935d), + new EpsgOperationParameterRecord(3971, "Z-axis translation", 31.008d), + new EpsgOperationParameterRecord(3971, "X-axis rotation", -12.324d), + new EpsgOperationParameterRecord(3971, "Y-axis rotation", -3.755d), + new EpsgOperationParameterRecord(3971, "Z-axis rotation", 7.37d), + new EpsgOperationParameterRecord(3971, "Scale difference", 0.447d), + new EpsgOperationParameterRecord(3972, "X-axis translation", -143.87d), + new EpsgOperationParameterRecord(3972, "Y-axis translation", 243.37d), + new EpsgOperationParameterRecord(3972, "Z-axis translation", -33.52d), + new EpsgOperationParameterRecord(3990, "X-axis translation", -60.31d), + new EpsgOperationParameterRecord(3990, "Y-axis translation", 245.935d), + new EpsgOperationParameterRecord(3990, "Z-axis translation", 31.008d), + new EpsgOperationParameterRecord(3990, "X-axis rotation", -12.324d), + new EpsgOperationParameterRecord(3990, "Y-axis rotation", -3.755d), + new EpsgOperationParameterRecord(3990, "Z-axis rotation", 7.37d), + new EpsgOperationParameterRecord(3990, "Scale difference", 0.447d), + new EpsgOperationParameterRecord(3998, "X-axis translation", -153.0d), + new EpsgOperationParameterRecord(3998, "Y-axis translation", -5.0d), + new EpsgOperationParameterRecord(3998, "Z-axis translation", -292.0d), + new EpsgOperationParameterRecord(4064, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4064, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4064, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4065, "X-axis translation", -103.746d), + new EpsgOperationParameterRecord(4065, "Y-axis translation", -9.614d), + new EpsgOperationParameterRecord(4065, "Z-axis translation", -255.95d), + new EpsgOperationParameterRecord(4066, "X-axis translation", -103.746d), + new EpsgOperationParameterRecord(4066, "Y-axis translation", -9.614d), + new EpsgOperationParameterRecord(4066, "Z-axis translation", -255.95d), + new EpsgOperationParameterRecord(4067, "X-axis translation", -102.283d), + new EpsgOperationParameterRecord(4067, "Y-axis translation", -10.277d), + new EpsgOperationParameterRecord(4067, "Z-axis translation", -257.396d), + new EpsgOperationParameterRecord(4067, "X-axis rotation", -3.976d), + new EpsgOperationParameterRecord(4067, "Y-axis rotation", -0.002d), + new EpsgOperationParameterRecord(4067, "Z-axis rotation", -6.203d), + new EpsgOperationParameterRecord(4067, "Scale difference", 12.315d), + new EpsgOperationParameterRecord(4067, "Ordinate 1 of evaluation point", 5580868.818d), + new EpsgOperationParameterRecord(4067, "Ordinate 2 of evaluation point", 2826402.46d), + new EpsgOperationParameterRecord(4067, "Ordinate 3 of evaluation point", -1243557.996d), + new EpsgOperationParameterRecord(4068, "X-axis translation", -102.283d), + new EpsgOperationParameterRecord(4068, "Y-axis translation", -10.277d), + new EpsgOperationParameterRecord(4068, "Z-axis translation", -257.396d), + new EpsgOperationParameterRecord(4068, "X-axis rotation", -3.976d), + new EpsgOperationParameterRecord(4068, "Y-axis rotation", -0.002d), + new EpsgOperationParameterRecord(4068, "Z-axis rotation", -6.203d), + new EpsgOperationParameterRecord(4068, "Scale difference", 12.315d), + new EpsgOperationParameterRecord(4068, "Ordinate 1 of evaluation point", 5580868.818d), + new EpsgOperationParameterRecord(4068, "Ordinate 2 of evaluation point", 2826402.46d), + new EpsgOperationParameterRecord(4068, "Ordinate 3 of evaluation point", -1243557.996d), + new EpsgOperationParameterRecord(4069, "X-axis translation", -144.35d), + new EpsgOperationParameterRecord(4069, "Y-axis translation", 242.88d), + new EpsgOperationParameterRecord(4069, "Z-axis translation", -33.2d), + new EpsgOperationParameterRecord(4072, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(4072, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(4072, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(4072, "False easting", 500000.0d), + new EpsgOperationParameterRecord(4072, "False northing", 0.0d), + new EpsgOperationParameterRecord(4072, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(4072, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(4072, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(4072, "False easting", 500000.0d), + new EpsgOperationParameterRecord(4072, "False northing", 0.0d), + new EpsgOperationParameterRecord(4072, "Easting offset", -287.54d), + new EpsgOperationParameterRecord(4072, "Northing offset", 278.25d), + new EpsgOperationParameterRecord(4077, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4077, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4077, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4078, "X-axis translation", -83.11d), + new EpsgOperationParameterRecord(4078, "Y-axis translation", -97.38d), + new EpsgOperationParameterRecord(4078, "Z-axis translation", -117.22d), + new EpsgOperationParameterRecord(4078, "X-axis rotation", 0.0276d), + new EpsgOperationParameterRecord(4078, "Y-axis rotation", -0.2167d), + new EpsgOperationParameterRecord(4078, "Z-axis rotation", 0.2147d), + new EpsgOperationParameterRecord(4078, "Scale difference", 0.1218d), + new EpsgOperationParameterRecord(4084, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4084, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4084, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4290, "X-axis translation", -381.788d), + new EpsgOperationParameterRecord(4290, "Y-axis translation", -57.501d), + new EpsgOperationParameterRecord(4290, "Z-axis translation", -256.673d), + new EpsgOperationParameterRecord(4441, "Vertical Offset", 0.06d), + new EpsgOperationParameterRecord(4442, "Vertical Offset", 0.34d), + new EpsgOperationParameterRecord(4443, "Vertical Offset", 0.24d), + new EpsgOperationParameterRecord(4444, "Vertical Offset", 0.29d), + new EpsgOperationParameterRecord(4445, "Vertical Offset", 0.34d), + new EpsgOperationParameterRecord(4446, "Vertical Offset", 0.2d), + new EpsgOperationParameterRecord(4447, "Vertical Offset", 0.32d), + new EpsgOperationParameterRecord(4448, "Vertical Offset", 0.44d), + new EpsgOperationParameterRecord(4449, "Vertical Offset", 0.47d), + new EpsgOperationParameterRecord(4450, "Vertical Offset", 0.49d), + new EpsgOperationParameterRecord(4451, "Vertical Offset", 0.36d), + new EpsgOperationParameterRecord(4452, "Vertical Offset", 0.39d), + new EpsgOperationParameterRecord(4453, "Vertical Offset", 0.38d), + new EpsgOperationParameterRecord(4461, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4461, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4461, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4476, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4476, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4476, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4477, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4477, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4477, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4478, "X-axis translation", -381.788d), + new EpsgOperationParameterRecord(4478, "Y-axis translation", -57.501d), + new EpsgOperationParameterRecord(4478, "Z-axis translation", -256.673d), + new EpsgOperationParameterRecord(4560, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4560, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4560, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4649, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(4649, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(4649, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(4649, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(4649, "False northing", 0.0d), + new EpsgOperationParameterRecord(4649, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(4649, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(4649, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(4649, "False easting", 32500000.0d), + new EpsgOperationParameterRecord(4649, "False northing", 0.0d), + new EpsgOperationParameterRecord(4650, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(4650, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(4650, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(4650, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(4650, "False northing", 0.0d), + new EpsgOperationParameterRecord(4650, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(4650, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(4650, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(4650, "False easting", 32500000.0d), + new EpsgOperationParameterRecord(4650, "False northing", 0.0d), + new EpsgOperationParameterRecord(4651, "Ordinate 1 of evaluation point", 54.5833333333336d), + new EpsgOperationParameterRecord(4651, "Ordinate 2 of evaluation point", -2.25000000000028d), + new EpsgOperationParameterRecord(4651, "Vertical Offset", 0.07d), + new EpsgOperationParameterRecord(4651, "Inclination in latitude", 0.044d), + new EpsgOperationParameterRecord(4651, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(4651, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(4827, "X-axis translation", 485.0d), + new EpsgOperationParameterRecord(4827, "Y-axis translation", 169.5d), + new EpsgOperationParameterRecord(4827, "Z-axis translation", 483.8d), + new EpsgOperationParameterRecord(4827, "X-axis rotation", 7.786d), + new EpsgOperationParameterRecord(4827, "Y-axis rotation", 4.398d), + new EpsgOperationParameterRecord(4827, "Z-axis rotation", 4.103d), + new EpsgOperationParameterRecord(4827, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(4829, "X-axis translation", 558.7d), + new EpsgOperationParameterRecord(4829, "Y-axis translation", 68.8d), + new EpsgOperationParameterRecord(4829, "Z-axis translation", 452.2d), + new EpsgOperationParameterRecord(4829, "X-axis rotation", -8.025d), + new EpsgOperationParameterRecord(4829, "Y-axis rotation", -4.105d), + new EpsgOperationParameterRecord(4829, "Z-axis rotation", -4.295d), + new EpsgOperationParameterRecord(4829, "Scale difference", 5.74d), + new EpsgOperationParameterRecord(4829, "Ordinate 1 of evaluation point", 3977358.114d), + new EpsgOperationParameterRecord(4829, "Ordinate 2 of evaluation point", 1407223.203d), + new EpsgOperationParameterRecord(4829, "Ordinate 3 of evaluation point", 4765441.589d), + new EpsgOperationParameterRecord(4830, "X-axis translation", 565.4171d), + new EpsgOperationParameterRecord(4830, "Y-axis translation", 50.3319d), + new EpsgOperationParameterRecord(4830, "Z-axis translation", 465.5524d), + new EpsgOperationParameterRecord(4830, "X-axis rotation", 1.9342d), + new EpsgOperationParameterRecord(4830, "Y-axis rotation", -1.6677d), + new EpsgOperationParameterRecord(4830, "Z-axis rotation", 9.1019d), + new EpsgOperationParameterRecord(4830, "Scale difference", 4.0725d), + new EpsgOperationParameterRecord(4831, "X-axis translation", 593.0248d), + new EpsgOperationParameterRecord(4831, "Y-axis translation", 25.9984d), + new EpsgOperationParameterRecord(4831, "Z-axis translation", 478.7459d), + new EpsgOperationParameterRecord(4831, "X-axis rotation", 1.9342d), + new EpsgOperationParameterRecord(4831, "Y-axis rotation", -1.6677d), + new EpsgOperationParameterRecord(4831, "Z-axis rotation", 9.1019d), + new EpsgOperationParameterRecord(4831, "Scale difference", 4.0725d), + new EpsgOperationParameterRecord(4831, "Ordinate 1 of evaluation point", 3903453.1482d), + new EpsgOperationParameterRecord(4831, "Ordinate 2 of evaluation point", 368135.3134d), + new EpsgOperationParameterRecord(4831, "Ordinate 3 of evaluation point", 5012970.3051d), + new EpsgOperationParameterRecord(4832, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4832, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4832, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4833, "X-axis translation", 565.4171d), + new EpsgOperationParameterRecord(4833, "Y-axis translation", 50.3319d), + new EpsgOperationParameterRecord(4833, "Z-axis translation", 465.5524d), + new EpsgOperationParameterRecord(4833, "X-axis rotation", 1.9342d), + new EpsgOperationParameterRecord(4833, "Y-axis rotation", -1.6677d), + new EpsgOperationParameterRecord(4833, "Z-axis rotation", 9.1019d), + new EpsgOperationParameterRecord(4833, "Scale difference", 4.0725d), + new EpsgOperationParameterRecord(4834, "X-axis translation", -144.35d), + new EpsgOperationParameterRecord(4834, "Y-axis translation", 242.88d), + new EpsgOperationParameterRecord(4834, "Z-axis translation", -33.2d), + new EpsgOperationParameterRecord(4836, "X-axis translation", 485.0d), + new EpsgOperationParameterRecord(4836, "Y-axis translation", 169.5d), + new EpsgOperationParameterRecord(4836, "Z-axis translation", 483.8d), + new EpsgOperationParameterRecord(4836, "X-axis rotation", 7.786d), + new EpsgOperationParameterRecord(4836, "Y-axis rotation", 4.398d), + new EpsgOperationParameterRecord(4836, "Z-axis rotation", 4.103d), + new EpsgOperationParameterRecord(4836, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(4840, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4840, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4840, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(4905, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(4905, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(4905, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5021, "X-axis translation", -503.229d), + new EpsgOperationParameterRecord(5021, "Y-axis translation", -247.375d), + new EpsgOperationParameterRecord(5021, "Z-axis translation", 312.582d), + new EpsgOperationParameterRecord(5022, "X-axis translation", -303.956d), + new EpsgOperationParameterRecord(5022, "Y-axis translation", 224.556d), + new EpsgOperationParameterRecord(5022, "Z-axis translation", 214.306d), + new EpsgOperationParameterRecord(5022, "X-axis rotation", 9.405d), + new EpsgOperationParameterRecord(5022, "Y-axis rotation", -6.626d), + new EpsgOperationParameterRecord(5022, "Z-axis rotation", -12.583d), + new EpsgOperationParameterRecord(5022, "Scale difference", 1.327d), + new EpsgOperationParameterRecord(5023, "X-axis translation", -503.3d), + new EpsgOperationParameterRecord(5023, "Y-axis translation", -247.574d), + new EpsgOperationParameterRecord(5023, "Z-axis translation", 313.025d), + new EpsgOperationParameterRecord(5024, "X-axis translation", -204.926d), + new EpsgOperationParameterRecord(5024, "Y-axis translation", 140.353d), + new EpsgOperationParameterRecord(5024, "Z-axis translation", 55.063d), + new EpsgOperationParameterRecord(5025, "X-axis translation", -204.519d), + new EpsgOperationParameterRecord(5025, "Y-axis translation", 140.159d), + new EpsgOperationParameterRecord(5025, "Z-axis translation", 55.404d), + new EpsgOperationParameterRecord(5026, "X-axis translation", -205.808d), + new EpsgOperationParameterRecord(5026, "Y-axis translation", 140.771d), + new EpsgOperationParameterRecord(5026, "Z-axis translation", 54.326d), + new EpsgOperationParameterRecord(5027, "X-axis translation", -105.679d), + new EpsgOperationParameterRecord(5027, "Y-axis translation", 166.1d), + new EpsgOperationParameterRecord(5027, "Z-axis translation", -37.322d), + new EpsgOperationParameterRecord(5028, "X-axis translation", -105.377d), + new EpsgOperationParameterRecord(5028, "Y-axis translation", 165.769d), + new EpsgOperationParameterRecord(5028, "Z-axis translation", -36.965d), + new EpsgOperationParameterRecord(5029, "X-axis translation", -105.359d), + new EpsgOperationParameterRecord(5029, "Y-axis translation", 165.804d), + new EpsgOperationParameterRecord(5029, "Z-axis translation", -37.05d), + new EpsgOperationParameterRecord(5030, "X-axis translation", -105.531d), + new EpsgOperationParameterRecord(5030, "Y-axis translation", 166.39d), + new EpsgOperationParameterRecord(5030, "Z-axis translation", -37.326d), + new EpsgOperationParameterRecord(5031, "X-axis translation", -105.756d), + new EpsgOperationParameterRecord(5031, "Y-axis translation", 165.972d), + new EpsgOperationParameterRecord(5031, "Z-axis translation", -37.313d), + new EpsgOperationParameterRecord(5032, "X-axis translation", -106.235d), + new EpsgOperationParameterRecord(5032, "Y-axis translation", 166.236d), + new EpsgOperationParameterRecord(5032, "Z-axis translation", -37.768d), + new EpsgOperationParameterRecord(5033, "X-axis translation", -423.058d), + new EpsgOperationParameterRecord(5033, "Y-axis translation", -172.868d), + new EpsgOperationParameterRecord(5033, "Z-axis translation", 83.772d), + new EpsgOperationParameterRecord(5034, "X-axis translation", -423.053d), + new EpsgOperationParameterRecord(5034, "Y-axis translation", -172.871d), + new EpsgOperationParameterRecord(5034, "Z-axis translation", 83.771d), + new EpsgOperationParameterRecord(5035, "X-axis translation", -423.024d), + new EpsgOperationParameterRecord(5035, "Y-axis translation", -172.923d), + new EpsgOperationParameterRecord(5035, "Z-axis translation", 83.83d), + new EpsgOperationParameterRecord(5036, "X-axis translation", -223.15d), + new EpsgOperationParameterRecord(5036, "Y-axis translation", 110.132d), + new EpsgOperationParameterRecord(5036, "Z-axis translation", 36.711d), + new EpsgOperationParameterRecord(5037, "X-axis translation", -230.994d), + new EpsgOperationParameterRecord(5037, "Y-axis translation", 102.591d), + new EpsgOperationParameterRecord(5037, "Z-axis translation", 25.199d), + new EpsgOperationParameterRecord(5037, "X-axis rotation", 0.633d), + new EpsgOperationParameterRecord(5037, "Y-axis rotation", -0.239d), + new EpsgOperationParameterRecord(5037, "Z-axis rotation", 0.9d), + new EpsgOperationParameterRecord(5037, "Scale difference", 1.95d), + new EpsgOperationParameterRecord(5038, "X-axis translation", -303.861d), + new EpsgOperationParameterRecord(5038, "Y-axis translation", -60.693d), + new EpsgOperationParameterRecord(5038, "Z-axis translation", 103.607d), + new EpsgOperationParameterRecord(5039, "X-axis translation", 508.088d), + new EpsgOperationParameterRecord(5039, "Y-axis translation", -191.042d), + new EpsgOperationParameterRecord(5039, "Z-axis translation", 565.223d), + new EpsgOperationParameterRecord(5040, "X-axis translation", -87.987d), + new EpsgOperationParameterRecord(5040, "Y-axis translation", -108.639d), + new EpsgOperationParameterRecord(5040, "Z-axis translation", -121.593d), + new EpsgOperationParameterRecord(5043, "X-axis translation", 24.47d), + new EpsgOperationParameterRecord(5043, "Y-axis translation", -130.89d), + new EpsgOperationParameterRecord(5043, "Z-axis translation", -81.56d), + new EpsgOperationParameterRecord(5043, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(5043, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(5043, "Z-axis rotation", -0.13d), + new EpsgOperationParameterRecord(5043, "Scale difference", -0.22d), + new EpsgOperationParameterRecord(5044, "X-axis translation", 23.57d), + new EpsgOperationParameterRecord(5044, "Y-axis translation", -140.95d), + new EpsgOperationParameterRecord(5044, "Z-axis translation", -79.8d), + new EpsgOperationParameterRecord(5044, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(5044, "Y-axis rotation", -0.35d), + new EpsgOperationParameterRecord(5044, "Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(5044, "Scale difference", -0.22d), + new EpsgOperationParameterRecord(5050, "X-axis translation", -157.84d), + new EpsgOperationParameterRecord(5050, "Y-axis translation", 308.54d), + new EpsgOperationParameterRecord(5050, "Z-axis translation", -146.6d), + new EpsgOperationParameterRecord(5051, "X-axis translation", -157.84d), + new EpsgOperationParameterRecord(5051, "Y-axis translation", 308.54d), + new EpsgOperationParameterRecord(5051, "Z-axis translation", -146.6d), + new EpsgOperationParameterRecord(5052, "X-axis translation", -160.31d), + new EpsgOperationParameterRecord(5052, "Y-axis translation", 314.82d), + new EpsgOperationParameterRecord(5052, "Z-axis translation", -142.25d), + new EpsgOperationParameterRecord(5053, "X-axis translation", -160.31d), + new EpsgOperationParameterRecord(5053, "Y-axis translation", 314.82d), + new EpsgOperationParameterRecord(5053, "Z-axis translation", -142.25d), + new EpsgOperationParameterRecord(5054, "X-axis translation", -161.11d), + new EpsgOperationParameterRecord(5054, "Y-axis translation", 310.25d), + new EpsgOperationParameterRecord(5054, "Z-axis translation", -144.64d), + new EpsgOperationParameterRecord(5055, "X-axis translation", -161.11d), + new EpsgOperationParameterRecord(5055, "Y-axis translation", 310.25d), + new EpsgOperationParameterRecord(5055, "Z-axis translation", -144.64d), + new EpsgOperationParameterRecord(5056, "X-axis translation", -160.4d), + new EpsgOperationParameterRecord(5056, "Y-axis translation", 302.29d), + new EpsgOperationParameterRecord(5056, "Z-axis translation", -144.19d), + new EpsgOperationParameterRecord(5057, "X-axis translation", -160.4d), + new EpsgOperationParameterRecord(5057, "Y-axis translation", 302.29d), + new EpsgOperationParameterRecord(5057, "Z-axis translation", -144.19d), + new EpsgOperationParameterRecord(5058, "X-axis translation", -153.54d), + new EpsgOperationParameterRecord(5058, "Y-axis translation", 302.33d), + new EpsgOperationParameterRecord(5058, "Z-axis translation", -152.37d), + new EpsgOperationParameterRecord(5059, "X-axis translation", -153.54d), + new EpsgOperationParameterRecord(5059, "Y-axis translation", 302.33d), + new EpsgOperationParameterRecord(5059, "Z-axis translation", -152.37d), + new EpsgOperationParameterRecord(5060, "X-axis translation", -151.5d), + new EpsgOperationParameterRecord(5060, "Y-axis translation", 300.09d), + new EpsgOperationParameterRecord(5060, "Z-axis translation", -151.15d), + new EpsgOperationParameterRecord(5061, "X-axis translation", -151.5d), + new EpsgOperationParameterRecord(5061, "Y-axis translation", 300.09d), + new EpsgOperationParameterRecord(5061, "Z-axis translation", -151.15d), + new EpsgOperationParameterRecord(5062, "X-axis translation", -156.8d), + new EpsgOperationParameterRecord(5062, "Y-axis translation", 298.41d), + new EpsgOperationParameterRecord(5062, "Z-axis translation", -147.41d), + new EpsgOperationParameterRecord(5063, "X-axis translation", -156.8d), + new EpsgOperationParameterRecord(5063, "Y-axis translation", 298.41d), + new EpsgOperationParameterRecord(5063, "Z-axis translation", -147.41d), + new EpsgOperationParameterRecord(5064, "X-axis translation", -157.4d), + new EpsgOperationParameterRecord(5064, "Y-axis translation", 295.05d), + new EpsgOperationParameterRecord(5064, "Z-axis translation", -150.19d), + new EpsgOperationParameterRecord(5065, "X-axis translation", -157.4d), + new EpsgOperationParameterRecord(5065, "Y-axis translation", 295.05d), + new EpsgOperationParameterRecord(5065, "Z-axis translation", -150.19d), + new EpsgOperationParameterRecord(5066, "X-axis translation", -151.99d), + new EpsgOperationParameterRecord(5066, "Y-axis translation", 287.04d), + new EpsgOperationParameterRecord(5066, "Z-axis translation", -147.45d), + new EpsgOperationParameterRecord(5067, "X-axis translation", -151.99d), + new EpsgOperationParameterRecord(5067, "Y-axis translation", 287.04d), + new EpsgOperationParameterRecord(5067, "Z-axis translation", -147.45d), + new EpsgOperationParameterRecord(5073, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5073, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(5073, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5073, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(5073, "False northing", 0.0d), + new EpsgOperationParameterRecord(5073, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5073, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5073, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5073, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5073, "False northing", 0.0d), + new EpsgOperationParameterRecord(5074, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5074, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5074, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5074, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(5074, "False northing", 0.0d), + new EpsgOperationParameterRecord(5074, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5074, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5074, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5074, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5074, "False northing", 0.0d), + new EpsgOperationParameterRecord(5075, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5075, "Longitude of natural origin", 6.0d), + new EpsgOperationParameterRecord(5075, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5075, "False easting", 2500000.0d), + new EpsgOperationParameterRecord(5075, "False northing", 0.0d), + new EpsgOperationParameterRecord(5075, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5075, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5075, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5075, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5075, "False northing", 0.0d), + new EpsgOperationParameterRecord(5077, "X-axis translation", 70.995d), + new EpsgOperationParameterRecord(5077, "Y-axis translation", -335.916d), + new EpsgOperationParameterRecord(5077, "Z-axis translation", 262.898d), + new EpsgOperationParameterRecord(5078, "X-axis translation", 70.995d), + new EpsgOperationParameterRecord(5078, "Y-axis translation", -335.916d), + new EpsgOperationParameterRecord(5078, "Z-axis translation", 262.898d), + new EpsgOperationParameterRecord(5133, "Longitude offset", 10.405d), + new EpsgOperationParameterRecord(5134, "Longitude offset", 10.405d), + new EpsgOperationParameterRecord(5166, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5166, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(5166, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5166, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5166, "False northing", 0.0d), + new EpsgOperationParameterRecord(5166, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5166, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(5166, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5166, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5166, "False northing", 0.0d), + new EpsgOperationParameterRecord(5166, "Ordinate 1 of evaluation point in target CRS", -129.549d), + new EpsgOperationParameterRecord(5166, "Ordinate 2 of evaluation point in target CRS", -208.185d), + new EpsgOperationParameterRecord(5166, "Scale factor for source CRS axes", 1.0000015504d), + new EpsgOperationParameterRecord(5166, "Rotation angle of source CRS axes", 1.56504d), + new EpsgOperationParameterRecord(5189, "X-axis translation", -145.907d), + new EpsgOperationParameterRecord(5189, "Y-axis translation", 505.034d), + new EpsgOperationParameterRecord(5189, "Z-axis translation", 685.756d), + new EpsgOperationParameterRecord(5189, "X-axis rotation", -1.162d), + new EpsgOperationParameterRecord(5189, "Y-axis rotation", 2.347d), + new EpsgOperationParameterRecord(5189, "Z-axis rotation", 1.592d), + new EpsgOperationParameterRecord(5189, "Scale difference", 6.342d), + new EpsgOperationParameterRecord(5189, "Ordinate 1 of evaluation point", -3159521.31d), + new EpsgOperationParameterRecord(5189, "Ordinate 2 of evaluation point", 4068151.32d), + new EpsgOperationParameterRecord(5189, "Ordinate 3 of evaluation point", 3748113.85d), + new EpsgOperationParameterRecord(5191, "X-axis translation", -145.907d), + new EpsgOperationParameterRecord(5191, "Y-axis translation", 505.034d), + new EpsgOperationParameterRecord(5191, "Z-axis translation", 685.756d), + new EpsgOperationParameterRecord(5191, "X-axis rotation", -1.162d), + new EpsgOperationParameterRecord(5191, "Y-axis rotation", 2.347d), + new EpsgOperationParameterRecord(5191, "Z-axis rotation", 1.592d), + new EpsgOperationParameterRecord(5191, "Scale difference", 6.342d), + new EpsgOperationParameterRecord(5191, "Ordinate 1 of evaluation point", -3159521.31d), + new EpsgOperationParameterRecord(5191, "Ordinate 2 of evaluation point", 4068151.32d), + new EpsgOperationParameterRecord(5191, "Ordinate 3 of evaluation point", 3748113.85d), + new EpsgOperationParameterRecord(5194, "X-axis translation", -192.873d), + new EpsgOperationParameterRecord(5194, "Y-axis translation", -39.382d), + new EpsgOperationParameterRecord(5194, "Z-axis translation", -111.202d), + new EpsgOperationParameterRecord(5194, "X-axis rotation", 0.00205d), + new EpsgOperationParameterRecord(5194, "Y-axis rotation", 0.0005d), + new EpsgOperationParameterRecord(5194, "Z-axis rotation", -0.00335d), + new EpsgOperationParameterRecord(5194, "Scale difference", 0.0188d), + new EpsgOperationParameterRecord(5196, "Ordinate 1 of evaluation point", 45.3500000000003d), + new EpsgOperationParameterRecord(5196, "Ordinate 2 of evaluation point", 16.3666666666669d), + new EpsgOperationParameterRecord(5196, "Vertical Offset", -0.343d), + new EpsgOperationParameterRecord(5196, "Inclination in latitude", -0.007d), + new EpsgOperationParameterRecord(5196, "Inclination in longitude", -0.016d), + new EpsgOperationParameterRecord(5196, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5197, "Ordinate 1 of evaluation point", 45.3500000000003d), + new EpsgOperationParameterRecord(5197, "Ordinate 2 of evaluation point", 16.3666666666669d), + new EpsgOperationParameterRecord(5197, "Vertical Offset", -0.313d), + new EpsgOperationParameterRecord(5197, "Inclination in latitude", -0.016d), + new EpsgOperationParameterRecord(5197, "Inclination in longitude", -0.018d), + new EpsgOperationParameterRecord(5197, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5198, "Ordinate 1 of evaluation point", 50.7166666666669d), + new EpsgOperationParameterRecord(5198, "Ordinate 2 of evaluation point", 4.76666666666694d), + new EpsgOperationParameterRecord(5198, "Vertical Offset", -2.311d), + new EpsgOperationParameterRecord(5198, "Inclination in latitude", -0.016d), + new EpsgOperationParameterRecord(5198, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5198, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5199, "Ordinate 1 of evaluation point", 50.7166666666669d), + new EpsgOperationParameterRecord(5199, "Ordinate 2 of evaluation point", 4.76666666666694d), + new EpsgOperationParameterRecord(5199, "Vertical Offset", -2.317d), + new EpsgOperationParameterRecord(5199, "Inclination in latitude", -0.031d), + new EpsgOperationParameterRecord(5199, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5199, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5200, "Ordinate 1 of evaluation point", 42.6250000000003d), + new EpsgOperationParameterRecord(5200, "Ordinate 2 of evaluation point", 25.3766666666669d), + new EpsgOperationParameterRecord(5200, "Vertical Offset", 0.228d), + new EpsgOperationParameterRecord(5200, "Inclination in latitude", -0.009d), + new EpsgOperationParameterRecord(5200, "Inclination in longitude", -0.003d), + new EpsgOperationParameterRecord(5200, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5201, "Ordinate 1 of evaluation point", 49.9166666666669d), + new EpsgOperationParameterRecord(5201, "Ordinate 2 of evaluation point", 15.2500000000003d), + new EpsgOperationParameterRecord(5201, "Vertical Offset", 0.116d), + new EpsgOperationParameterRecord(5201, "Inclination in latitude", 0.036d), + new EpsgOperationParameterRecord(5201, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5201, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5202, "Ordinate 1 of evaluation point", 49.9166666666669d), + new EpsgOperationParameterRecord(5202, "Ordinate 2 of evaluation point", 15.2500000000003d), + new EpsgOperationParameterRecord(5202, "Vertical Offset", 0.13d), + new EpsgOperationParameterRecord(5202, "Inclination in latitude", 0.026d), + new EpsgOperationParameterRecord(5202, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5202, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5203, "Ordinate 1 of evaluation point", 58.7000000000003d), + new EpsgOperationParameterRecord(5203, "Ordinate 2 of evaluation point", 25.8666666666669d), + new EpsgOperationParameterRecord(5203, "Vertical Offset", 0.195d), + new EpsgOperationParameterRecord(5203, "Inclination in latitude", 0.009d), + new EpsgOperationParameterRecord(5203, "Inclination in longitude", -0.013d), + new EpsgOperationParameterRecord(5203, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5204, "Ordinate 1 of evaluation point", 55.3000000000003d), + new EpsgOperationParameterRecord(5204, "Ordinate 2 of evaluation point", 24.0166666666669d), + new EpsgOperationParameterRecord(5204, "Vertical Offset", 0.121d), + new EpsgOperationParameterRecord(5204, "Inclination in latitude", 0.053d), + new EpsgOperationParameterRecord(5204, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5204, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5205, "Ordinate 1 of evaluation point", 46.0166666666669d), + new EpsgOperationParameterRecord(5205, "Ordinate 2 of evaluation point", 24.8166666666669d), + new EpsgOperationParameterRecord(5205, "Vertical Offset", 0.028d), + new EpsgOperationParameterRecord(5205, "Inclination in latitude", 0.002d), + new EpsgOperationParameterRecord(5205, "Inclination in longitude", 0.002d), + new EpsgOperationParameterRecord(5205, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5206, "Ordinate 1 of evaluation point", 46.0166666666669d), + new EpsgOperationParameterRecord(5206, "Ordinate 2 of evaluation point", 24.8166666666669d), + new EpsgOperationParameterRecord(5206, "Vertical Offset", 0.062d), + new EpsgOperationParameterRecord(5206, "Inclination in latitude", -0.005d), + new EpsgOperationParameterRecord(5206, "Inclination in longitude", 0.008d), + new EpsgOperationParameterRecord(5206, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5207, "Ordinate 1 of evaluation point", 46.9166666666669d), + new EpsgOperationParameterRecord(5207, "Ordinate 2 of evaluation point", 8.18333333333361d), + new EpsgOperationParameterRecord(5207, "Vertical Offset", -0.225d), + new EpsgOperationParameterRecord(5207, "Inclination in latitude", -0.221d), + new EpsgOperationParameterRecord(5207, "Inclination in longitude", -0.033d), + new EpsgOperationParameterRecord(5207, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5208, "Ordinate 1 of evaluation point", 61.9000000000003d), + new EpsgOperationParameterRecord(5208, "Ordinate 2 of evaluation point", 15.8000000000003d), + new EpsgOperationParameterRecord(5208, "Vertical Offset", -0.008d), + new EpsgOperationParameterRecord(5208, "Inclination in latitude", -0.0006d), + new EpsgOperationParameterRecord(5208, "Inclination in longitude", -0.0003d), + new EpsgOperationParameterRecord(5208, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5209, "Ordinate 1 of evaluation point", 56.9666666666669d), + new EpsgOperationParameterRecord(5209, "Ordinate 2 of evaluation point", 24.8833333333336d), + new EpsgOperationParameterRecord(5209, "Vertical Offset", 0.105d), + new EpsgOperationParameterRecord(5209, "Inclination in latitude", 0.0d), + new EpsgOperationParameterRecord(5209, "Inclination in longitude", 0.004d), + new EpsgOperationParameterRecord(5209, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5210, "Ordinate 1 of evaluation point", 56.9666666666669d), + new EpsgOperationParameterRecord(5210, "Ordinate 2 of evaluation point", 24.8833333333336d), + new EpsgOperationParameterRecord(5210, "Vertical Offset", 0.154d), + new EpsgOperationParameterRecord(5210, "Inclination in latitude", 0.016d), + new EpsgOperationParameterRecord(5210, "Inclination in longitude", -0.012d), + new EpsgOperationParameterRecord(5210, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5211, "Ordinate 1 of evaluation point", 51.0500000000003d), + new EpsgOperationParameterRecord(5211, "Ordinate 2 of evaluation point", 10.2166666666669d), + new EpsgOperationParameterRecord(5211, "Vertical Offset", 0.015d), + new EpsgOperationParameterRecord(5211, "Inclination in latitude", -0.01d), + new EpsgOperationParameterRecord(5211, "Inclination in longitude", 0.002d), + new EpsgOperationParameterRecord(5211, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5212, "Ordinate 1 of evaluation point", 51.0500000000003d), + new EpsgOperationParameterRecord(5212, "Ordinate 2 of evaluation point", 8.66666666666695d), + new EpsgOperationParameterRecord(5212, "Vertical Offset", 0.017d), + new EpsgOperationParameterRecord(5212, "Inclination in latitude", -0.011d), + new EpsgOperationParameterRecord(5212, "Inclination in longitude", 0.005d), + new EpsgOperationParameterRecord(5212, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5213, "Ordinate 1 of evaluation point", 42.5833333333336d), + new EpsgOperationParameterRecord(5213, "Ordinate 2 of evaluation point", 12.9666666666669d), + new EpsgOperationParameterRecord(5213, "Vertical Offset", -0.309d), + new EpsgOperationParameterRecord(5213, "Inclination in latitude", -0.03d), + new EpsgOperationParameterRecord(5213, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5213, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5215, "Ordinate 1 of evaluation point", 42.5833333333336d), + new EpsgOperationParameterRecord(5215, "Ordinate 2 of evaluation point", 12.9666666666669d), + new EpsgOperationParameterRecord(5215, "Vertical Offset", -0.259d), + new EpsgOperationParameterRecord(5215, "Inclination in latitude", -0.036d), + new EpsgOperationParameterRecord(5215, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5215, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5216, "Ordinate 1 of evaluation point", 37.5000000000003d), + new EpsgOperationParameterRecord(5216, "Ordinate 2 of evaluation point", 14.3000000000003d), + new EpsgOperationParameterRecord(5216, "Vertical Offset", -0.402d), + new EpsgOperationParameterRecord(5216, "Inclination in latitude", -0.079d), + new EpsgOperationParameterRecord(5216, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5216, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5217, "Ordinate 1 of evaluation point", 37.5000000000003d), + new EpsgOperationParameterRecord(5217, "Ordinate 2 of evaluation point", 14.3000000000003d), + new EpsgOperationParameterRecord(5217, "Vertical Offset", -0.333d), + new EpsgOperationParameterRecord(5217, "Inclination in latitude", -0.051d), + new EpsgOperationParameterRecord(5217, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5217, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5226, "X-axis translation", 572.213d), + new EpsgOperationParameterRecord(5226, "Y-axis translation", 85.334d), + new EpsgOperationParameterRecord(5226, "Z-axis translation", 461.94d), + new EpsgOperationParameterRecord(5226, "X-axis rotation", -4.9732d), + new EpsgOperationParameterRecord(5226, "Y-axis rotation", -1.529d), + new EpsgOperationParameterRecord(5226, "Z-axis rotation", -5.2484d), + new EpsgOperationParameterRecord(5226, "Scale difference", 3.5378d), + new EpsgOperationParameterRecord(5227, "X-axis translation", 572.213d), + new EpsgOperationParameterRecord(5227, "Y-axis translation", 85.334d), + new EpsgOperationParameterRecord(5227, "Z-axis translation", 461.94d), + new EpsgOperationParameterRecord(5227, "X-axis rotation", -4.9732d), + new EpsgOperationParameterRecord(5227, "Y-axis rotation", -1.529d), + new EpsgOperationParameterRecord(5227, "Z-axis rotation", -5.2484d), + new EpsgOperationParameterRecord(5227, "Scale difference", 3.5378d), + new EpsgOperationParameterRecord(5236, "X-axis translation", -0.293d), + new EpsgOperationParameterRecord(5236, "Y-axis translation", 766.95d), + new EpsgOperationParameterRecord(5236, "Z-axis translation", 87.713d), + new EpsgOperationParameterRecord(5236, "X-axis rotation", -0.195704d), + new EpsgOperationParameterRecord(5236, "Y-axis rotation", -1.695068d), + new EpsgOperationParameterRecord(5236, "Z-axis rotation", -3.473016d), + new EpsgOperationParameterRecord(5236, "Scale difference", -0.039338d), + new EpsgOperationParameterRecord(5238, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(5239, "X-axis translation", 572.213d), + new EpsgOperationParameterRecord(5239, "Y-axis translation", 85.334d), + new EpsgOperationParameterRecord(5239, "Z-axis translation", 461.94d), + new EpsgOperationParameterRecord(5239, "X-axis rotation", -4.9732d), + new EpsgOperationParameterRecord(5239, "Y-axis rotation", -1.529d), + new EpsgOperationParameterRecord(5239, "Z-axis rotation", -5.2484d), + new EpsgOperationParameterRecord(5239, "Scale difference", 3.5378d), + new EpsgOperationParameterRecord(5241, "Latitude offset", 0.0d), + new EpsgOperationParameterRecord(5241, "Longitude offset", 0.0d), + new EpsgOperationParameterRecord(5249, "X-axis translation", -689.5937d), + new EpsgOperationParameterRecord(5249, "Y-axis translation", 623.84046d), + new EpsgOperationParameterRecord(5249, "Z-axis translation", -65.93566d), + new EpsgOperationParameterRecord(5249, "X-axis rotation", 0.02331d), + new EpsgOperationParameterRecord(5249, "Y-axis rotation", -1.17094d), + new EpsgOperationParameterRecord(5249, "Z-axis rotation", 0.80054d), + new EpsgOperationParameterRecord(5249, "Scale difference", 5.88536d), + new EpsgOperationParameterRecord(5260, "X-axis translation", 0.023d), + new EpsgOperationParameterRecord(5260, "Y-axis translation", 0.036d), + new EpsgOperationParameterRecord(5260, "Z-axis translation", -0.068d), + new EpsgOperationParameterRecord(5260, "X-axis rotation", 0.00176d), + new EpsgOperationParameterRecord(5260, "Y-axis rotation", 0.00912d), + new EpsgOperationParameterRecord(5260, "Z-axis rotation", -0.01136d), + new EpsgOperationParameterRecord(5260, "Scale difference", 0.00439d), + new EpsgOperationParameterRecord(5261, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5261, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5261, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5267, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5267, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5267, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5327, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5327, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5327, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5350, "X-axis translation", -148.0d), + new EpsgOperationParameterRecord(5350, "Y-axis translation", 136.0d), + new EpsgOperationParameterRecord(5350, "Z-axis translation", 90.0d), + new EpsgOperationParameterRecord(5351, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5351, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5351, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5374, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5374, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5374, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5376, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5376, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5376, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5377, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5377, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5377, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5378, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5378, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5378, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5384, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5384, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5384, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5385, "X-axis translation", -124.45d), + new EpsgOperationParameterRecord(5385, "Y-axis translation", 183.74d), + new EpsgOperationParameterRecord(5385, "Z-axis translation", 44.64d), + new EpsgOperationParameterRecord(5385, "X-axis rotation", -0.4384d), + new EpsgOperationParameterRecord(5385, "Y-axis rotation", 0.5446d), + new EpsgOperationParameterRecord(5385, "Z-axis rotation", -0.9706d), + new EpsgOperationParameterRecord(5385, "Scale difference", -2.1365d), + new EpsgOperationParameterRecord(5386, "X-axis translation", -124.45d), + new EpsgOperationParameterRecord(5386, "Y-axis translation", 183.74d), + new EpsgOperationParameterRecord(5386, "Z-axis translation", 44.64d), + new EpsgOperationParameterRecord(5386, "X-axis rotation", -0.4384d), + new EpsgOperationParameterRecord(5386, "Y-axis rotation", 0.5446d), + new EpsgOperationParameterRecord(5386, "Z-axis rotation", -0.9706d), + new EpsgOperationParameterRecord(5386, "Scale difference", -2.1365d), + new EpsgOperationParameterRecord(5395, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5395, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5395, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5403, "Vertical Offset", -1.7d), + new EpsgOperationParameterRecord(5415, "Ordinate 1 of evaluation point", 47.5333333333336d), + new EpsgOperationParameterRecord(5415, "Ordinate 2 of evaluation point", 14.4500000000003d), + new EpsgOperationParameterRecord(5415, "Vertical Offset", -0.356d), + new EpsgOperationParameterRecord(5415, "Inclination in latitude", -0.057d), + new EpsgOperationParameterRecord(5415, "Inclination in longitude", -0.058d), + new EpsgOperationParameterRecord(5415, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5416, "Ordinate 1 of evaluation point", 42.6250000000003d), + new EpsgOperationParameterRecord(5416, "Ordinate 2 of evaluation point", 25.3766666666669d), + new EpsgOperationParameterRecord(5416, "Vertical Offset", 0.182d), + new EpsgOperationParameterRecord(5416, "Inclination in latitude", 0.001d), + new EpsgOperationParameterRecord(5416, "Inclination in longitude", -0.004d), + new EpsgOperationParameterRecord(5416, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5417, "Ordinate 1 of evaluation point", 56.0333333333336d), + new EpsgOperationParameterRecord(5417, "Ordinate 2 of evaluation point", 9.23333333333361d), + new EpsgOperationParameterRecord(5417, "Vertical Offset", 0.011d), + new EpsgOperationParameterRecord(5417, "Inclination in latitude", 0.003d), + new EpsgOperationParameterRecord(5417, "Inclination in longitude", 0.011d), + new EpsgOperationParameterRecord(5417, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5419, "Vertical Offset", -0.486d), + new EpsgOperationParameterRecord(5420, "Ordinate 1 of evaluation point", 51.0500000000003d), + new EpsgOperationParameterRecord(5420, "Ordinate 2 of evaluation point", 10.2166666666669d), + new EpsgOperationParameterRecord(5420, "Vertical Offset", 0.014d), + new EpsgOperationParameterRecord(5420, "Inclination in latitude", -0.001d), + new EpsgOperationParameterRecord(5420, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5420, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5421, "Ordinate 1 of evaluation point", 51.0500000000003d), + new EpsgOperationParameterRecord(5421, "Ordinate 2 of evaluation point", 8.66666666666695d), + new EpsgOperationParameterRecord(5421, "Vertical Offset", 0.017d), + new EpsgOperationParameterRecord(5421, "Inclination in latitude", -0.002d), + new EpsgOperationParameterRecord(5421, "Inclination in longitude", 0.003d), + new EpsgOperationParameterRecord(5421, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5422, "Ordinate 1 of evaluation point", 52.5333333333336d), + new EpsgOperationParameterRecord(5422, "Ordinate 2 of evaluation point", 13.1666666666669d), + new EpsgOperationParameterRecord(5422, "Vertical Offset", 0.157d), + new EpsgOperationParameterRecord(5422, "Inclination in latitude", 0.007d), + new EpsgOperationParameterRecord(5422, "Inclination in longitude", 0.005d), + new EpsgOperationParameterRecord(5422, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5424, "Ordinate 1 of evaluation point", 46.9833333333336d), + new EpsgOperationParameterRecord(5424, "Ordinate 2 of evaluation point", 19.5833333333336d), + new EpsgOperationParameterRecord(5424, "Vertical Offset", 0.14d), + new EpsgOperationParameterRecord(5424, "Inclination in latitude", 0.008d), + new EpsgOperationParameterRecord(5424, "Inclination in longitude", -0.002d), + new EpsgOperationParameterRecord(5424, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5425, "Vertical Offset", -0.005d), + new EpsgOperationParameterRecord(5426, "Ordinate 1 of evaluation point", 62.9333333333336d), + new EpsgOperationParameterRecord(5426, "Ordinate 2 of evaluation point", 11.1666666666669d), + new EpsgOperationParameterRecord(5426, "Vertical Offset", -0.001d), + new EpsgOperationParameterRecord(5426, "Inclination in latitude", -0.01d), + new EpsgOperationParameterRecord(5426, "Inclination in longitude", 0.034d), + new EpsgOperationParameterRecord(5426, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5427, "Vertical Offset", -0.315d), + new EpsgOperationParameterRecord(5428, "Ordinate 1 of evaluation point", 46.0d), + new EpsgOperationParameterRecord(5428, "Ordinate 2 of evaluation point", 15.0d), + new EpsgOperationParameterRecord(5428, "Vertical Offset", -0.411d), + new EpsgOperationParameterRecord(5428, "Inclination in latitude", -0.033d), + new EpsgOperationParameterRecord(5428, "Inclination in longitude", 0.008d), + new EpsgOperationParameterRecord(5428, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5429, "Ordinate 1 of evaluation point", 40.7722222222225d), + new EpsgOperationParameterRecord(5429, "Ordinate 2 of evaluation point", -3.6597222222225d), + new EpsgOperationParameterRecord(5429, "Vertical Offset", -0.486d), + new EpsgOperationParameterRecord(5429, "Inclination in latitude", -0.003d), + new EpsgOperationParameterRecord(5429, "Inclination in longitude", 0.006d), + new EpsgOperationParameterRecord(5429, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5430, "Ordinate 1 of evaluation point", 64.0d), + new EpsgOperationParameterRecord(5430, "Ordinate 2 of evaluation point", 16.2333333333336d), + new EpsgOperationParameterRecord(5430, "Vertical Offset", 0.005d), + new EpsgOperationParameterRecord(5430, "Inclination in latitude", -0.012d), + new EpsgOperationParameterRecord(5430, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5430, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5431, "Ordinate 1 of evaluation point", 46.9166666666669d), + new EpsgOperationParameterRecord(5431, "Ordinate 2 of evaluation point", 8.18333333333361d), + new EpsgOperationParameterRecord(5431, "Vertical Offset", -0.245d), + new EpsgOperationParameterRecord(5431, "Inclination in latitude", -0.21d), + new EpsgOperationParameterRecord(5431, "Inclination in longitude", -0.032d), + new EpsgOperationParameterRecord(5431, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5432, "Vertical Offset", 0.213d), + new EpsgOperationParameterRecord(5435, "Ordinate 1 of evaluation point", 48.6333333333336d), + new EpsgOperationParameterRecord(5435, "Ordinate 2 of evaluation point", 19.2500000000003d), + new EpsgOperationParameterRecord(5435, "Vertical Offset", 0.122d), + new EpsgOperationParameterRecord(5435, "Inclination in latitude", 0.02d), + new EpsgOperationParameterRecord(5435, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(5435, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5436, "Ordinate 1 of evaluation point", 58.7000000000003d), + new EpsgOperationParameterRecord(5436, "Ordinate 2 of evaluation point", 25.8666666666669d), + new EpsgOperationParameterRecord(5436, "Vertical Offset", 0.133d), + new EpsgOperationParameterRecord(5436, "Inclination in latitude", -0.014d), + new EpsgOperationParameterRecord(5436, "Inclination in longitude", 0.005d), + new EpsgOperationParameterRecord(5436, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5437, "Ordinate 1 of evaluation point", 55.3000000000003d), + new EpsgOperationParameterRecord(5437, "Ordinate 2 of evaluation point", 24.0166666666669d), + new EpsgOperationParameterRecord(5437, "Vertical Offset", 0.102d), + new EpsgOperationParameterRecord(5437, "Inclination in latitude", 0.0d), + new EpsgOperationParameterRecord(5437, "Inclination in longitude", 0.002d), + new EpsgOperationParameterRecord(5437, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5438, "Vertical Offset", 28.0d), + new EpsgOperationParameterRecord(5440, "Vertical Offset", -28.0d), + new EpsgOperationParameterRecord(5443, "Vertical Offset", 26.3d), + new EpsgOperationParameterRecord(5445, "Vertical Offset", -26.3d), + new EpsgOperationParameterRecord(5447, "Vertical Offset", 0.4d), + new EpsgOperationParameterRecord(5450, "Vertical Offset", -0.49d), + new EpsgOperationParameterRecord(5452, "Vertical Offset", -0.037d), + new EpsgOperationParameterRecord(5470, "X-axis translation", 213.11d), + new EpsgOperationParameterRecord(5470, "Y-axis translation", 9.37d), + new EpsgOperationParameterRecord(5470, "Z-axis translation", -74.95d), + new EpsgOperationParameterRecord(5483, "X-axis translation", -265.8867d), + new EpsgOperationParameterRecord(5483, "Y-axis translation", 76.9851d), + new EpsgOperationParameterRecord(5483, "Z-axis translation", 20.2667d), + new EpsgOperationParameterRecord(5483, "X-axis rotation", 0.33746d), + new EpsgOperationParameterRecord(5483, "Y-axis rotation", 3.09264d), + new EpsgOperationParameterRecord(5483, "Z-axis rotation", -2.53861d), + new EpsgOperationParameterRecord(5483, "Scale difference", 0.4598d), + new EpsgOperationParameterRecord(5483, "Ordinate 1 of evaluation point", 4103620.3943d), + new EpsgOperationParameterRecord(5483, "Ordinate 2 of evaluation point", 440486.4235d), + new EpsgOperationParameterRecord(5483, "Ordinate 3 of evaluation point", 4846923.4558d), + new EpsgOperationParameterRecord(5484, "X-axis translation", -265.8867d), + new EpsgOperationParameterRecord(5484, "Y-axis translation", 76.9851d), + new EpsgOperationParameterRecord(5484, "Z-axis translation", 20.2667d), + new EpsgOperationParameterRecord(5484, "X-axis rotation", 0.33746d), + new EpsgOperationParameterRecord(5484, "Y-axis rotation", 3.09264d), + new EpsgOperationParameterRecord(5484, "Z-axis rotation", -2.53861d), + new EpsgOperationParameterRecord(5484, "Scale difference", 0.4598d), + new EpsgOperationParameterRecord(5484, "Ordinate 1 of evaluation point", 4103620.3943d), + new EpsgOperationParameterRecord(5484, "Ordinate 2 of evaluation point", 440486.4235d), + new EpsgOperationParameterRecord(5484, "Ordinate 3 of evaluation point", 4846923.4558d), + new EpsgOperationParameterRecord(5485, "X-axis translation", -189.6806d), + new EpsgOperationParameterRecord(5485, "Y-axis translation", 18.3463d), + new EpsgOperationParameterRecord(5485, "Z-axis translation", -42.7695d), + new EpsgOperationParameterRecord(5485, "X-axis rotation", 0.33746d), + new EpsgOperationParameterRecord(5485, "Y-axis rotation", 3.09264d), + new EpsgOperationParameterRecord(5485, "Z-axis rotation", -2.53861d), + new EpsgOperationParameterRecord(5485, "Scale difference", 0.4598d), + new EpsgOperationParameterRecord(5486, "X-axis translation", -189.6806d), + new EpsgOperationParameterRecord(5486, "Y-axis translation", 18.3463d), + new EpsgOperationParameterRecord(5486, "Z-axis translation", -42.7695d), + new EpsgOperationParameterRecord(5486, "X-axis rotation", 0.33746d), + new EpsgOperationParameterRecord(5486, "Y-axis rotation", 3.09264d), + new EpsgOperationParameterRecord(5486, "Z-axis rotation", -2.53861d), + new EpsgOperationParameterRecord(5486, "Scale difference", 0.4598d), + new EpsgOperationParameterRecord(5491, "X-axis translation", 127.744d), + new EpsgOperationParameterRecord(5491, "Y-axis translation", 547.069d), + new EpsgOperationParameterRecord(5491, "Z-axis translation", 118.359d), + new EpsgOperationParameterRecord(5491, "X-axis rotation", -3.1116d), + new EpsgOperationParameterRecord(5491, "Y-axis rotation", 4.9509d), + new EpsgOperationParameterRecord(5491, "Z-axis rotation", -0.8837d), + new EpsgOperationParameterRecord(5491, "Scale difference", 14.1012d), + new EpsgOperationParameterRecord(5492, "X-axis translation", -471.06d), + new EpsgOperationParameterRecord(5492, "Y-axis translation", -3.212d), + new EpsgOperationParameterRecord(5492, "Z-axis translation", -305.843d), + new EpsgOperationParameterRecord(5492, "X-axis rotation", 0.4752d), + new EpsgOperationParameterRecord(5492, "Y-axis rotation", -0.9978d), + new EpsgOperationParameterRecord(5492, "Z-axis rotation", 0.2068d), + new EpsgOperationParameterRecord(5492, "Scale difference", 2.1353d), + new EpsgOperationParameterRecord(5493, "X-axis translation", 151.613d), + new EpsgOperationParameterRecord(5493, "Y-axis translation", 253.832d), + new EpsgOperationParameterRecord(5493, "Z-axis translation", -429.084d), + new EpsgOperationParameterRecord(5493, "X-axis rotation", -0.0506d), + new EpsgOperationParameterRecord(5493, "Y-axis rotation", 0.0958d), + new EpsgOperationParameterRecord(5493, "Z-axis rotation", -0.5974d), + new EpsgOperationParameterRecord(5493, "Scale difference", -0.3971d), + new EpsgOperationParameterRecord(5494, "X-axis translation", 0.7696d), + new EpsgOperationParameterRecord(5494, "Y-axis translation", -0.8692d), + new EpsgOperationParameterRecord(5494, "Z-axis translation", -12.0631d), + new EpsgOperationParameterRecord(5494, "X-axis rotation", -0.32511d), + new EpsgOperationParameterRecord(5494, "Y-axis rotation", -0.21041d), + new EpsgOperationParameterRecord(5494, "Z-axis rotation", -0.0239d), + new EpsgOperationParameterRecord(5494, "Scale difference", 0.2829d), + new EpsgOperationParameterRecord(5495, "X-axis translation", 1.2239d), + new EpsgOperationParameterRecord(5495, "Y-axis translation", 2.4156d), + new EpsgOperationParameterRecord(5495, "Z-axis translation", -1.7598d), + new EpsgOperationParameterRecord(5495, "X-axis rotation", 0.038d), + new EpsgOperationParameterRecord(5495, "Y-axis rotation", -0.16101d), + new EpsgOperationParameterRecord(5495, "Z-axis rotation", -0.04925d), + new EpsgOperationParameterRecord(5495, "Scale difference", 0.2387d), + new EpsgOperationParameterRecord(5496, "X-axis translation", 14.6642d), + new EpsgOperationParameterRecord(5496, "Y-axis translation", 5.2493d), + new EpsgOperationParameterRecord(5496, "Z-axis translation", 0.1981d), + new EpsgOperationParameterRecord(5496, "X-axis rotation", -0.06838d), + new EpsgOperationParameterRecord(5496, "Y-axis rotation", 0.09141d), + new EpsgOperationParameterRecord(5496, "Z-axis rotation", -0.58131d), + new EpsgOperationParameterRecord(5496, "Scale difference", -0.4067d), + new EpsgOperationParameterRecord(5497, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5497, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5497, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5501, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5501, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5501, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5521, "X-axis translation", -963.0d), + new EpsgOperationParameterRecord(5521, "Y-axis translation", 510.0d), + new EpsgOperationParameterRecord(5521, "Z-axis translation", -359.0d), + new EpsgOperationParameterRecord(5553, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5553, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5553, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5557, "Ordinate 1 of evaluation point", 47.5333333333336d), + new EpsgOperationParameterRecord(5557, "Ordinate 2 of evaluation point", 14.4500000000003d), + new EpsgOperationParameterRecord(5557, "Vertical Offset", -0.335d), + new EpsgOperationParameterRecord(5557, "Inclination in latitude", -0.065d), + new EpsgOperationParameterRecord(5557, "Inclination in longitude", -0.06d), + new EpsgOperationParameterRecord(5557, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(5585, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5585, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5585, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5586, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5586, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5586, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5590, "X-axis translation", 25.0d), + new EpsgOperationParameterRecord(5590, "Y-axis translation", -141.0d), + new EpsgOperationParameterRecord(5590, "Z-axis translation", -78.5d), + new EpsgOperationParameterRecord(5590, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(5590, "Y-axis rotation", -0.35d), + new EpsgOperationParameterRecord(5590, "Z-axis rotation", -0.736d), + new EpsgOperationParameterRecord(5590, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(5599, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5599, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5599, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5622, "X-axis translation", 370.936d), + new EpsgOperationParameterRecord(5622, "Y-axis translation", -108.938d), + new EpsgOperationParameterRecord(5622, "Z-axis translation", 435.682d), + new EpsgOperationParameterRecord(5630, "X-axis translation", -168.52d), + new EpsgOperationParameterRecord(5630, "Y-axis translation", -72.05d), + new EpsgOperationParameterRecord(5630, "Z-axis translation", 304.3d), + new EpsgOperationParameterRecord(5660, "X-axis translation", -209.3622d), + new EpsgOperationParameterRecord(5660, "Y-axis translation", -87.8162d), + new EpsgOperationParameterRecord(5660, "Z-axis translation", 404.6198d), + new EpsgOperationParameterRecord(5660, "X-axis rotation", 0.0046d), + new EpsgOperationParameterRecord(5660, "Y-axis rotation", 3.4784d), + new EpsgOperationParameterRecord(5660, "Z-axis rotation", 0.5805d), + new EpsgOperationParameterRecord(5660, "Scale difference", -1.4547d), + new EpsgOperationParameterRecord(5662, "X-axis translation", -124.0d), + new EpsgOperationParameterRecord(5662, "Y-axis translation", -60.0d), + new EpsgOperationParameterRecord(5662, "Z-axis translation", 153.0d), + new EpsgOperationParameterRecord(5686, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5686, "Longitude of natural origin", 6.0d), + new EpsgOperationParameterRecord(5686, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5686, "False easting", 2500000.0d), + new EpsgOperationParameterRecord(5686, "False northing", 0.0d), + new EpsgOperationParameterRecord(5686, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5686, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5686, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5686, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5686, "False northing", 0.0d), + new EpsgOperationParameterRecord(5687, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5687, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5687, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5687, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(5687, "False northing", 0.0d), + new EpsgOperationParameterRecord(5687, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5687, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5687, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5687, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5687, "False northing", 0.0d), + new EpsgOperationParameterRecord(5688, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5688, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(5688, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5688, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(5688, "False northing", 0.0d), + new EpsgOperationParameterRecord(5688, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5688, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5688, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5688, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5688, "False northing", 0.0d), + new EpsgOperationParameterRecord(5689, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5689, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(5689, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5689, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(5689, "False northing", 0.0d), + new EpsgOperationParameterRecord(5689, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5689, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(5689, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5689, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5689, "False northing", 0.0d), + new EpsgOperationParameterRecord(5690, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5690, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(5690, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5690, "False easting", 5500000.0d), + new EpsgOperationParameterRecord(5690, "False northing", 0.0d), + new EpsgOperationParameterRecord(5690, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5690, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(5690, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(5690, "False easting", 500000.0d), + new EpsgOperationParameterRecord(5690, "False northing", 0.0d), + new EpsgOperationParameterRecord(5691, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5691, "Longitude of natural origin", 6.0d), + new EpsgOperationParameterRecord(5691, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5691, "False easting", 2500000.0d), + new EpsgOperationParameterRecord(5691, "False northing", 0.0d), + new EpsgOperationParameterRecord(5691, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5691, "Longitude of natural origin", 6.0d), + new EpsgOperationParameterRecord(5691, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5691, "False easting", 2500000.0d), + new EpsgOperationParameterRecord(5691, "False northing", 0.0d), + new EpsgOperationParameterRecord(5692, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5692, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5692, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5692, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(5692, "False northing", 0.0d), + new EpsgOperationParameterRecord(5692, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5692, "Longitude of natural origin", 6.0d), + new EpsgOperationParameterRecord(5692, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5692, "False easting", 2500000.0d), + new EpsgOperationParameterRecord(5692, "False northing", 0.0d), + new EpsgOperationParameterRecord(5693, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5693, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(5693, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5693, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(5693, "False northing", 0.0d), + new EpsgOperationParameterRecord(5693, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5693, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(5693, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5693, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(5693, "False northing", 0.0d), + new EpsgOperationParameterRecord(5694, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5694, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(5694, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5694, "False easting", 5500000.0d), + new EpsgOperationParameterRecord(5694, "False northing", 0.0d), + new EpsgOperationParameterRecord(5694, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5694, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(5694, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5694, "False easting", 5500000.0d), + new EpsgOperationParameterRecord(5694, "False northing", 0.0d), + new EpsgOperationParameterRecord(5695, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5695, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5695, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5695, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(5695, "False northing", 0.0d), + new EpsgOperationParameterRecord(5695, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5695, "Longitude of natural origin", 9.0d), + new EpsgOperationParameterRecord(5695, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5695, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(5695, "False northing", 0.0d), + new EpsgOperationParameterRecord(5696, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5696, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(5696, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5696, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(5696, "False northing", 0.0d), + new EpsgOperationParameterRecord(5696, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5696, "Longitude of natural origin", 12.0d), + new EpsgOperationParameterRecord(5696, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5696, "False easting", 4500000.0d), + new EpsgOperationParameterRecord(5696, "False northing", 0.0d), + new EpsgOperationParameterRecord(5697, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5697, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(5697, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5697, "False easting", 5500000.0d), + new EpsgOperationParameterRecord(5697, "False northing", 0.0d), + new EpsgOperationParameterRecord(5697, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(5697, "Longitude of natural origin", 15.0d), + new EpsgOperationParameterRecord(5697, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(5697, "False easting", 5500000.0d), + new EpsgOperationParameterRecord(5697, "False northing", 0.0d), + new EpsgOperationParameterRecord(5826, "X-axis translation", 584.9636d), + new EpsgOperationParameterRecord(5826, "Y-axis translation", 107.7175d), + new EpsgOperationParameterRecord(5826, "Z-axis translation", 413.8067d), + new EpsgOperationParameterRecord(5826, "X-axis rotation", -1.1155d), + new EpsgOperationParameterRecord(5826, "Y-axis rotation", -0.2824d), + new EpsgOperationParameterRecord(5826, "Z-axis rotation", 3.1384d), + new EpsgOperationParameterRecord(5826, "Scale difference", 7.9922d), + new EpsgOperationParameterRecord(5827, "X-axis translation", -129.164d), + new EpsgOperationParameterRecord(5827, "Y-axis translation", -41.188d), + new EpsgOperationParameterRecord(5827, "Z-axis translation", 130.718d), + new EpsgOperationParameterRecord(5827, "X-axis rotation", -0.246d), + new EpsgOperationParameterRecord(5827, "Y-axis rotation", -0.374d), + new EpsgOperationParameterRecord(5827, "Z-axis rotation", -0.329d), + new EpsgOperationParameterRecord(5827, "Scale difference", -2.955d), + new EpsgOperationParameterRecord(5840, "X-axis translation", 24.0d), + new EpsgOperationParameterRecord(5840, "Y-axis translation", -121.0d), + new EpsgOperationParameterRecord(5840, "Z-axis translation", -76.0d), + new EpsgOperationParameterRecord(5841, "X-axis translation", -124.0d), + new EpsgOperationParameterRecord(5841, "Y-axis translation", -60.0d), + new EpsgOperationParameterRecord(5841, "Z-axis translation", 154.0d), + new EpsgOperationParameterRecord(5878, "X-axis translation", -689.5937d), + new EpsgOperationParameterRecord(5878, "Y-axis translation", 623.84046d), + new EpsgOperationParameterRecord(5878, "Z-axis translation", -65.93566d), + new EpsgOperationParameterRecord(5878, "X-axis rotation", 0.02331d), + new EpsgOperationParameterRecord(5878, "Y-axis rotation", -1.17094d), + new EpsgOperationParameterRecord(5878, "Z-axis rotation", 0.80054d), + new EpsgOperationParameterRecord(5878, "Scale difference", 5.88536d), + new EpsgOperationParameterRecord(5881, "X-axis translation", -67.35d), + new EpsgOperationParameterRecord(5881, "Y-axis translation", 3.88d), + new EpsgOperationParameterRecord(5881, "Z-axis translation", -38.22d), + new EpsgOperationParameterRecord(5882, "X-axis translation", -67.35d), + new EpsgOperationParameterRecord(5882, "Y-axis translation", 3.88d), + new EpsgOperationParameterRecord(5882, "Z-axis translation", -38.22d), + new EpsgOperationParameterRecord(5888, "X-axis translation", -599.928d), + new EpsgOperationParameterRecord(5888, "Y-axis translation", -275.552d), + new EpsgOperationParameterRecord(5888, "Z-axis translation", -195.665d), + new EpsgOperationParameterRecord(5888, "X-axis rotation", -0.0835d), + new EpsgOperationParameterRecord(5888, "Y-axis rotation", -0.4715d), + new EpsgOperationParameterRecord(5888, "Z-axis rotation", 0.0602d), + new EpsgOperationParameterRecord(5888, "Scale difference", 49.2814d), + new EpsgOperationParameterRecord(5900, "X-axis translation", 56.0d), + new EpsgOperationParameterRecord(5900, "Y-axis translation", 48.0d), + new EpsgOperationParameterRecord(5900, "Z-axis translation", -37.0d), + new EpsgOperationParameterRecord(5900, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(5900, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(5900, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(5900, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(5900, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(5900, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(5900, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(5900, "Rate of change of X-axis rotation", 0.054d), + new EpsgOperationParameterRecord(5900, "Rate of change of Y-axis rotation", 0.518d), + new EpsgOperationParameterRecord(5900, "Rate of change of Z-axis rotation", -0.781d), + new EpsgOperationParameterRecord(5900, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(5900, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(6136, "X-axis translation", -179.483d), + new EpsgOperationParameterRecord(6136, "Y-axis translation", -69.379d), + new EpsgOperationParameterRecord(6136, "Z-axis translation", -27.584d), + new EpsgOperationParameterRecord(6136, "X-axis rotation", 7.862d), + new EpsgOperationParameterRecord(6136, "Y-axis rotation", -8.163d), + new EpsgOperationParameterRecord(6136, "Z-axis rotation", -6.042d), + new EpsgOperationParameterRecord(6136, "Scale difference", -13.925d), + new EpsgOperationParameterRecord(6137, "X-axis translation", 8.853d), + new EpsgOperationParameterRecord(6137, "Y-axis translation", -52.644d), + new EpsgOperationParameterRecord(6137, "Z-axis translation", 180.304d), + new EpsgOperationParameterRecord(6137, "X-axis rotation", 0.393d), + new EpsgOperationParameterRecord(6137, "Y-axis rotation", 2.323d), + new EpsgOperationParameterRecord(6137, "Z-axis rotation", -2.96d), + new EpsgOperationParameterRecord(6137, "Scale difference", -24.081d), + new EpsgOperationParameterRecord(6142, "X-axis translation", -179.483d), + new EpsgOperationParameterRecord(6142, "Y-axis translation", -69.379d), + new EpsgOperationParameterRecord(6142, "Z-axis translation", -27.584d), + new EpsgOperationParameterRecord(6142, "X-axis rotation", 7.862d), + new EpsgOperationParameterRecord(6142, "Y-axis rotation", -8.163d), + new EpsgOperationParameterRecord(6142, "Z-axis rotation", -6.042d), + new EpsgOperationParameterRecord(6142, "Scale difference", -13.925d), + new EpsgOperationParameterRecord(6143, "X-axis translation", 8.853d), + new EpsgOperationParameterRecord(6143, "Y-axis translation", -52.644d), + new EpsgOperationParameterRecord(6143, "Z-axis translation", 180.304d), + new EpsgOperationParameterRecord(6143, "X-axis rotation", 0.393d), + new EpsgOperationParameterRecord(6143, "Y-axis rotation", 2.323d), + new EpsgOperationParameterRecord(6143, "Z-axis rotation", -2.96d), + new EpsgOperationParameterRecord(6143, "Scale difference", -24.081d), + new EpsgOperationParameterRecord(6177, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6177, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6177, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(6191, "X-axis translation", -138.7d), + new EpsgOperationParameterRecord(6191, "Y-axis translation", 164.4d), + new EpsgOperationParameterRecord(6191, "Z-axis translation", 34.4d), + new EpsgOperationParameterRecord(6192, "X-axis translation", -205.57d), + new EpsgOperationParameterRecord(6192, "Y-axis translation", 168.77d), + new EpsgOperationParameterRecord(6192, "Z-axis translation", -4.12d), + new EpsgOperationParameterRecord(6193, "X-axis translation", -206.05d), + new EpsgOperationParameterRecord(6193, "Y-axis translation", 168.28d), + new EpsgOperationParameterRecord(6193, "Z-axis translation", -3.82d), + new EpsgOperationParameterRecord(6194, "X-axis translation", -206.05d), + new EpsgOperationParameterRecord(6194, "Y-axis translation", 168.28d), + new EpsgOperationParameterRecord(6194, "Z-axis translation", -3.82d), + new EpsgOperationParameterRecord(6195, "X-axis translation", -67.35d), + new EpsgOperationParameterRecord(6195, "Y-axis translation", 3.88d), + new EpsgOperationParameterRecord(6195, "Z-axis translation", -38.22d), + new EpsgOperationParameterRecord(6196, "X-axis translation", -93.179d), + new EpsgOperationParameterRecord(6196, "Y-axis translation", -87.124d), + new EpsgOperationParameterRecord(6196, "Z-axis translation", 114.338d), + new EpsgOperationParameterRecord(6205, "X-axis translation", 517.4399d), + new EpsgOperationParameterRecord(6205, "Y-axis translation", 228.7318d), + new EpsgOperationParameterRecord(6205, "Z-axis translation", 579.7954d), + new EpsgOperationParameterRecord(6205, "X-axis rotation", -4.045d), + new EpsgOperationParameterRecord(6205, "Y-axis rotation", -4.304d), + new EpsgOperationParameterRecord(6205, "Z-axis rotation", 15.612d), + new EpsgOperationParameterRecord(6205, "Scale difference", -8.312d), + new EpsgOperationParameterRecord(6206, "X-axis translation", 521.748d), + new EpsgOperationParameterRecord(6206, "Y-axis translation", 229.489d), + new EpsgOperationParameterRecord(6206, "Z-axis translation", 590.921d), + new EpsgOperationParameterRecord(6206, "X-axis rotation", -4.029d), + new EpsgOperationParameterRecord(6206, "Y-axis rotation", -4.488d), + new EpsgOperationParameterRecord(6206, "Z-axis rotation", 15.521d), + new EpsgOperationParameterRecord(6206, "Scale difference", -9.78d), + new EpsgOperationParameterRecord(6208, "X-axis translation", 293.17d), + new EpsgOperationParameterRecord(6208, "Y-axis translation", 726.18d), + new EpsgOperationParameterRecord(6208, "Z-axis translation", 245.36d), + new EpsgOperationParameterRecord(6276, "X-axis translation", -84.68d), + new EpsgOperationParameterRecord(6276, "Y-axis translation", -19.42d), + new EpsgOperationParameterRecord(6276, "Z-axis translation", 32.01d), + new EpsgOperationParameterRecord(6276, "X-axis rotation", -0.4254d), + new EpsgOperationParameterRecord(6276, "Y-axis rotation", 2.2578d), + new EpsgOperationParameterRecord(6276, "Z-axis rotation", 2.4015d), + new EpsgOperationParameterRecord(6276, "Scale difference", 9.71d), + new EpsgOperationParameterRecord(6276, "Rate of change of X-axis translation", 1.42d), + new EpsgOperationParameterRecord(6276, "Rate of change of Y-axis translation", 1.34d), + new EpsgOperationParameterRecord(6276, "Rate of change of Z-axis translation", 0.9d), + new EpsgOperationParameterRecord(6276, "Rate of change of X-axis rotation", 1.5461d), + new EpsgOperationParameterRecord(6276, "Rate of change of Y-axis rotation", 1.182d), + new EpsgOperationParameterRecord(6276, "Rate of change of Z-axis rotation", 1.1551d), + new EpsgOperationParameterRecord(6276, "Rate of change of scale difference", 0.109d), + new EpsgOperationParameterRecord(6276, "Parameter reference epoch", 1994.0d), + new EpsgOperationParameterRecord(6277, "X-axis translation", -79.73d), + new EpsgOperationParameterRecord(6277, "Y-axis translation", -6.86d), + new EpsgOperationParameterRecord(6277, "Z-axis translation", 38.03d), + new EpsgOperationParameterRecord(6277, "X-axis rotation", -0.0351d), + new EpsgOperationParameterRecord(6277, "Y-axis rotation", 2.1211d), + new EpsgOperationParameterRecord(6277, "Z-axis rotation", 2.1411d), + new EpsgOperationParameterRecord(6277, "Scale difference", 6.636d), + new EpsgOperationParameterRecord(6277, "Rate of change of X-axis translation", 2.25d), + new EpsgOperationParameterRecord(6277, "Rate of change of Y-axis translation", -0.62d), + new EpsgOperationParameterRecord(6277, "Rate of change of Z-axis translation", -0.56d), + new EpsgOperationParameterRecord(6277, "Rate of change of X-axis rotation", 1.4707d), + new EpsgOperationParameterRecord(6277, "Rate of change of Y-axis rotation", 1.1443d), + new EpsgOperationParameterRecord(6277, "Rate of change of Z-axis rotation", 1.1701d), + new EpsgOperationParameterRecord(6277, "Rate of change of scale difference", 0.294d), + new EpsgOperationParameterRecord(6277, "Parameter reference epoch", 1994.0d), + new EpsgOperationParameterRecord(6278, "X-axis translation", -45.91d), + new EpsgOperationParameterRecord(6278, "Y-axis translation", -29.85d), + new EpsgOperationParameterRecord(6278, "Z-axis translation", -20.37d), + new EpsgOperationParameterRecord(6278, "X-axis rotation", -1.6705d), + new EpsgOperationParameterRecord(6278, "Y-axis rotation", 0.4594d), + new EpsgOperationParameterRecord(6278, "Z-axis rotation", 1.9356d), + new EpsgOperationParameterRecord(6278, "Scale difference", 7.07d), + new EpsgOperationParameterRecord(6278, "Rate of change of X-axis translation", -4.66d), + new EpsgOperationParameterRecord(6278, "Rate of change of Y-axis translation", 3.55d), + new EpsgOperationParameterRecord(6278, "Rate of change of Z-axis translation", 11.24d), + new EpsgOperationParameterRecord(6278, "Rate of change of X-axis rotation", 1.7454d), + new EpsgOperationParameterRecord(6278, "Rate of change of Y-axis rotation", 1.4868d), + new EpsgOperationParameterRecord(6278, "Rate of change of Z-axis rotation", 1.224d), + new EpsgOperationParameterRecord(6278, "Rate of change of scale difference", 0.249d), + new EpsgOperationParameterRecord(6278, "Parameter reference epoch", 1994.0d), + new EpsgOperationParameterRecord(6279, "X-axis translation", -14.63d), + new EpsgOperationParameterRecord(6279, "Y-axis translation", -27.62d), + new EpsgOperationParameterRecord(6279, "Z-axis translation", -25.32d), + new EpsgOperationParameterRecord(6279, "X-axis rotation", -1.7893d), + new EpsgOperationParameterRecord(6279, "Y-axis rotation", -0.6047d), + new EpsgOperationParameterRecord(6279, "Z-axis rotation", 0.9962d), + new EpsgOperationParameterRecord(6279, "Scale difference", 6.695d), + new EpsgOperationParameterRecord(6279, "Rate of change of X-axis translation", -8.6d), + new EpsgOperationParameterRecord(6279, "Rate of change of Y-axis translation", 0.36d), + new EpsgOperationParameterRecord(6279, "Rate of change of Z-axis translation", 11.25d), + new EpsgOperationParameterRecord(6279, "Rate of change of X-axis rotation", 1.6394d), + new EpsgOperationParameterRecord(6279, "Rate of change of Y-axis rotation", 1.5198d), + new EpsgOperationParameterRecord(6279, "Rate of change of Z-axis rotation", 1.3801d), + new EpsgOperationParameterRecord(6279, "Rate of change of scale difference", 0.007d), + new EpsgOperationParameterRecord(6279, "Parameter reference epoch", 1994.0d), + new EpsgOperationParameterRecord(6280, "X-axis translation", 24.54d), + new EpsgOperationParameterRecord(6280, "Y-axis translation", -36.43d), + new EpsgOperationParameterRecord(6280, "Z-axis translation", -68.12d), + new EpsgOperationParameterRecord(6280, "X-axis rotation", -2.7359d), + new EpsgOperationParameterRecord(6280, "Y-axis rotation", -2.0431d), + new EpsgOperationParameterRecord(6280, "Z-axis rotation", 0.3731d), + new EpsgOperationParameterRecord(6280, "Scale difference", 6.901d), + new EpsgOperationParameterRecord(6280, "Rate of change of X-axis translation", -21.8d), + new EpsgOperationParameterRecord(6280, "Rate of change of Y-axis translation", 4.71d), + new EpsgOperationParameterRecord(6280, "Rate of change of Z-axis translation", 26.27d), + new EpsgOperationParameterRecord(6280, "Rate of change of X-axis rotation", 2.0203d), + new EpsgOperationParameterRecord(6280, "Rate of change of Y-axis rotation", 2.1735d), + new EpsgOperationParameterRecord(6280, "Rate of change of Z-axis rotation", 1.629d), + new EpsgOperationParameterRecord(6280, "Rate of change of scale difference", 0.388d), + new EpsgOperationParameterRecord(6280, "Parameter reference epoch", 1994.0d), + new EpsgOperationParameterRecord(6281, "X-axis translation", -2.47d), + new EpsgOperationParameterRecord(6281, "Y-axis translation", -1.15d), + new EpsgOperationParameterRecord(6281, "Z-axis translation", 9.79d), + new EpsgOperationParameterRecord(6281, "X-axis rotation", -0.1d), + new EpsgOperationParameterRecord(6281, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6281, "Z-axis rotation", 0.18d), + new EpsgOperationParameterRecord(6281, "Scale difference", -8.95d), + new EpsgOperationParameterRecord(6281, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6281, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(6281, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(6281, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6281, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6281, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6281, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6281, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(6283, "X-axis translation", -2.47d), + new EpsgOperationParameterRecord(6283, "Y-axis translation", -2.35d), + new EpsgOperationParameterRecord(6283, "Z-axis translation", 3.59d), + new EpsgOperationParameterRecord(6283, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6283, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6283, "Z-axis rotation", 0.18d), + new EpsgOperationParameterRecord(6283, "Scale difference", -2.45d), + new EpsgOperationParameterRecord(6283, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6283, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(6283, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(6283, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6283, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6283, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6283, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6283, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(6284, "X-axis translation", -2.67d), + new EpsgOperationParameterRecord(6284, "Y-axis translation", -2.75d), + new EpsgOperationParameterRecord(6284, "Z-axis translation", 1.99d), + new EpsgOperationParameterRecord(6284, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6284, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6284, "Z-axis rotation", 0.18d), + new EpsgOperationParameterRecord(6284, "Scale difference", -2.15d), + new EpsgOperationParameterRecord(6284, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6284, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(6284, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(6284, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6284, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6284, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6284, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6284, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(6285, "X-axis translation", -1.47d), + new EpsgOperationParameterRecord(6285, "Y-axis translation", -1.35d), + new EpsgOperationParameterRecord(6285, "Z-axis translation", 1.39d), + new EpsgOperationParameterRecord(6285, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6285, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6285, "Z-axis rotation", 0.18d), + new EpsgOperationParameterRecord(6285, "Scale difference", -0.75d), + new EpsgOperationParameterRecord(6285, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6285, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(6285, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(6285, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6285, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6285, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6285, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6285, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(6286, "X-axis translation", -1.27d), + new EpsgOperationParameterRecord(6286, "Y-axis translation", -0.65d), + new EpsgOperationParameterRecord(6286, "Z-axis translation", 2.09d), + new EpsgOperationParameterRecord(6286, "X-axis rotation", 0.39d), + new EpsgOperationParameterRecord(6286, "Y-axis rotation", -0.8d), + new EpsgOperationParameterRecord(6286, "Z-axis rotation", 1.14d), + new EpsgOperationParameterRecord(6286, "Scale difference", -1.95d), + new EpsgOperationParameterRecord(6286, "Rate of change of X-axis translation", 0.29d), + new EpsgOperationParameterRecord(6286, "Rate of change of Y-axis translation", 0.02d), + new EpsgOperationParameterRecord(6286, "Rate of change of Z-axis translation", 0.06d), + new EpsgOperationParameterRecord(6286, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(6286, "Rate of change of Y-axis rotation", 0.19d), + new EpsgOperationParameterRecord(6286, "Rate of change of Z-axis rotation", -0.07d), + new EpsgOperationParameterRecord(6286, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6286, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(6287, "X-axis translation", -0.67d), + new EpsgOperationParameterRecord(6287, "Y-axis translation", -0.61d), + new EpsgOperationParameterRecord(6287, "Z-axis translation", 1.85d), + new EpsgOperationParameterRecord(6287, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6287, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6287, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6287, "Scale difference", -1.55d), + new EpsgOperationParameterRecord(6287, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6287, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(6287, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(6287, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6287, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6287, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6287, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6287, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(6288, "X-axis translation", -0.67d), + new EpsgOperationParameterRecord(6288, "Y-axis translation", -0.61d), + new EpsgOperationParameterRecord(6288, "Z-axis translation", 1.85d), + new EpsgOperationParameterRecord(6288, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6288, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6288, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6288, "Scale difference", -1.55d), + new EpsgOperationParameterRecord(6288, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6288, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(6288, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(6288, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6288, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6288, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6288, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6288, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(6289, "X-axis translation", -0.67d), + new EpsgOperationParameterRecord(6289, "Y-axis translation", -0.61d), + new EpsgOperationParameterRecord(6289, "Z-axis translation", 1.85d), + new EpsgOperationParameterRecord(6289, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6289, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6289, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6289, "Scale difference", -1.55d), + new EpsgOperationParameterRecord(6289, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6289, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(6289, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(6289, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6289, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6289, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6289, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(6289, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(6291, "X-axis translation", -22.8d), + new EpsgOperationParameterRecord(6291, "Y-axis translation", -2.6d), + new EpsgOperationParameterRecord(6291, "Z-axis translation", 125.2d), + new EpsgOperationParameterRecord(6291, "X-axis rotation", -0.1d), + new EpsgOperationParameterRecord(6291, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6291, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6291, "Scale difference", -10.41d), + new EpsgOperationParameterRecord(6291, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6291, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6291, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6291, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6291, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6291, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6291, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6291, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6292, "X-axis translation", -27.8d), + new EpsgOperationParameterRecord(6292, "Y-axis translation", -38.6d), + new EpsgOperationParameterRecord(6292, "Z-axis translation", 101.2d), + new EpsgOperationParameterRecord(6292, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6292, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6292, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6292, "Scale difference", -7.31d), + new EpsgOperationParameterRecord(6292, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6292, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6292, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6292, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6292, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6292, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6292, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6292, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6293, "X-axis translation", -22.8d), + new EpsgOperationParameterRecord(6293, "Y-axis translation", -14.6d), + new EpsgOperationParameterRecord(6293, "Z-axis translation", 63.2d), + new EpsgOperationParameterRecord(6293, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6293, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6293, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6293, "Scale difference", -3.91d), + new EpsgOperationParameterRecord(6293, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6293, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6293, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6293, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6293, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6293, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6293, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6293, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6294, "X-axis translation", -24.8d), + new EpsgOperationParameterRecord(6294, "Y-axis translation", -18.6d), + new EpsgOperationParameterRecord(6294, "Z-axis translation", 47.2d), + new EpsgOperationParameterRecord(6294, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6294, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6294, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6294, "Scale difference", -3.61d), + new EpsgOperationParameterRecord(6294, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6294, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6294, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6294, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6294, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6294, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6294, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6294, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6295, "X-axis translation", -12.8d), + new EpsgOperationParameterRecord(6295, "Y-axis translation", -4.6d), + new EpsgOperationParameterRecord(6295, "Z-axis translation", 41.2d), + new EpsgOperationParameterRecord(6295, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6295, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6295, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6295, "Scale difference", -2.21d), + new EpsgOperationParameterRecord(6295, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6295, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6295, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6295, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6295, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6295, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6295, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6295, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6296, "X-axis translation", 24.0d), + new EpsgOperationParameterRecord(6296, "Y-axis translation", -2.4d), + new EpsgOperationParameterRecord(6296, "Z-axis translation", 38.6d), + new EpsgOperationParameterRecord(6296, "X-axis rotation", 1.71d), + new EpsgOperationParameterRecord(6296, "Y-axis rotation", 1.48d), + new EpsgOperationParameterRecord(6296, "Z-axis rotation", 0.3d), + new EpsgOperationParameterRecord(6296, "Scale difference", -3.41d), + new EpsgOperationParameterRecord(6296, "Rate of change of X-axis translation", 2.8d), + new EpsgOperationParameterRecord(6296, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(6296, "Rate of change of Z-axis translation", 2.4d), + new EpsgOperationParameterRecord(6296, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(6296, "Rate of change of Y-axis rotation", 0.19d), + new EpsgOperationParameterRecord(6296, "Rate of change of Z-axis rotation", -0.07d), + new EpsgOperationParameterRecord(6296, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6296, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6297, "X-axis translation", -4.8d), + new EpsgOperationParameterRecord(6297, "Y-axis translation", -2.6d), + new EpsgOperationParameterRecord(6297, "Z-axis translation", 33.2d), + new EpsgOperationParameterRecord(6297, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6297, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6297, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6297, "Scale difference", -2.92d), + new EpsgOperationParameterRecord(6297, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6297, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6297, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6297, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6297, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6297, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6297, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6297, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6298, "X-axis translation", -4.8d), + new EpsgOperationParameterRecord(6298, "Y-axis translation", -2.6d), + new EpsgOperationParameterRecord(6298, "Z-axis translation", 33.2d), + new EpsgOperationParameterRecord(6298, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6298, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6298, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6298, "Scale difference", -2.92d), + new EpsgOperationParameterRecord(6298, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6298, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6298, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6298, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6298, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6298, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6298, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6298, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6299, "X-axis translation", -4.8d), + new EpsgOperationParameterRecord(6299, "Y-axis translation", -2.6d), + new EpsgOperationParameterRecord(6299, "Z-axis translation", 33.2d), + new EpsgOperationParameterRecord(6299, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6299, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6299, "Z-axis rotation", -0.06d), + new EpsgOperationParameterRecord(6299, "Scale difference", -2.92d), + new EpsgOperationParameterRecord(6299, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6299, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(6299, "Rate of change of Z-axis translation", 3.2d), + new EpsgOperationParameterRecord(6299, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6299, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6299, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(6299, "Rate of change of scale difference", -0.09d), + new EpsgOperationParameterRecord(6299, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6300, "X-axis translation", 1.9d), + new EpsgOperationParameterRecord(6300, "Y-axis translation", 1.7d), + new EpsgOperationParameterRecord(6300, "Z-axis translation", 10.5d), + new EpsgOperationParameterRecord(6300, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6300, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6300, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6300, "Scale difference", -1.34d), + new EpsgOperationParameterRecord(6300, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6300, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(6300, "Rate of change of Z-axis translation", 1.8d), + new EpsgOperationParameterRecord(6300, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6300, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6300, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6300, "Rate of change of scale difference", -0.08d), + new EpsgOperationParameterRecord(6300, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6302, "X-axis translation", -0.1d), + new EpsgOperationParameterRecord(6302, "Y-axis translation", 0.8d), + new EpsgOperationParameterRecord(6302, "Z-axis translation", 5.8d), + new EpsgOperationParameterRecord(6302, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6302, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6302, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6302, "Scale difference", -0.4d), + new EpsgOperationParameterRecord(6302, "Rate of change of X-axis translation", 0.2d), + new EpsgOperationParameterRecord(6302, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(6302, "Rate of change of Z-axis translation", 1.8d), + new EpsgOperationParameterRecord(6302, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6302, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6302, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6302, "Rate of change of scale difference", -0.08d), + new EpsgOperationParameterRecord(6302, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6303, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(6303, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(6303, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(6303, "False easting", 500000.0d), + new EpsgOperationParameterRecord(6303, "False northing", 0.0d), + new EpsgOperationParameterRecord(6303, "Latitude of natural origin", 52.1561605555558d), + new EpsgOperationParameterRecord(6303, "Longitude of natural origin", 5.38763888888917d), + new EpsgOperationParameterRecord(6303, "Scale factor at natural origin", 0.9999079d), + new EpsgOperationParameterRecord(6303, "False easting", 155000.0d), + new EpsgOperationParameterRecord(6303, "False northing", 463000.0d), + new EpsgOperationParameterRecord(6303, "Ordinate 1 of evaluation point in source CRS", 663395.607d), + new EpsgOperationParameterRecord(6303, "Ordinate 2 of evaluation point in source CRS", 5781194.38d), + new EpsgOperationParameterRecord(6303, "Ordinate 1 of evaluation point in target CRS", 155000.0d), + new EpsgOperationParameterRecord(6303, "Ordinate 2 of evaluation point in target CRS", 463000.0d), + new EpsgOperationParameterRecord(6303, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(6303, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(6303, "A1", -56.619d), + new EpsgOperationParameterRecord(6303, "A2", -3290.362d), + new EpsgOperationParameterRecord(6303, "A3", -20.184d), + new EpsgOperationParameterRecord(6303, "A4", 0.861d), + new EpsgOperationParameterRecord(6303, "A5", -2.082d), + new EpsgOperationParameterRecord(6303, "A6", 0.023d), + new EpsgOperationParameterRecord(6303, "A7", -0.07d), + new EpsgOperationParameterRecord(6303, "A8", 0.025d), + new EpsgOperationParameterRecord(6304, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(6304, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(6304, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(6304, "False easting", 500000.0d), + new EpsgOperationParameterRecord(6304, "False northing", 0.0d), + new EpsgOperationParameterRecord(6304, "Latitude of natural origin", 52.1561605555558d), + new EpsgOperationParameterRecord(6304, "Longitude of natural origin", 5.38763888888917d), + new EpsgOperationParameterRecord(6304, "Scale factor at natural origin", 0.9999079d), + new EpsgOperationParameterRecord(6304, "False easting", 155000.0d), + new EpsgOperationParameterRecord(6304, "False northing", 463000.0d), + new EpsgOperationParameterRecord(6304, "Ordinate 1 of evaluation point in source CRS", 663395.563d), + new EpsgOperationParameterRecord(6304, "Ordinate 2 of evaluation point in source CRS", 5781194.442d), + new EpsgOperationParameterRecord(6304, "Ordinate 1 of evaluation point in target CRS", 155000.0d), + new EpsgOperationParameterRecord(6304, "Ordinate 2 of evaluation point in target CRS", 463000.0d), + new EpsgOperationParameterRecord(6304, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(6304, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(6304, "A1", -99943.4175d), + new EpsgOperationParameterRecord(6304, "A2", 3290.3612d), + new EpsgOperationParameterRecord(6304, "A3", 20.1673d), + new EpsgOperationParameterRecord(6304, "A4", -0.8387d), + new EpsgOperationParameterRecord(6304, "A5", 2.0651d), + new EpsgOperationParameterRecord(6304, "A6", -0.0334d), + new EpsgOperationParameterRecord(6304, "A7", 0.0523d), + new EpsgOperationParameterRecord(6304, "A8", -0.23d), + new EpsgOperationParameterRecord(6305, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(6305, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(6305, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(6305, "False easting", 500000.0d), + new EpsgOperationParameterRecord(6305, "False northing", 0.0d), + new EpsgOperationParameterRecord(6305, "Latitude of false origin", 90.0d), + new EpsgOperationParameterRecord(6305, "Longitude of false origin", 4.3569397222225d), + new EpsgOperationParameterRecord(6305, "Latitude of 1st standard parallel", 49.8333333333336d), + new EpsgOperationParameterRecord(6305, "Latitude of 2nd standard parallel", 51.1666666666669d), + new EpsgOperationParameterRecord(6305, "Easting at false origin", 150000.01256d), + new EpsgOperationParameterRecord(6305, "Northing at false origin", 5400088.4378d), + new EpsgOperationParameterRecord(6305, "Ordinate 1 of evaluation point in source CRS", 500000.0d), + new EpsgOperationParameterRecord(6305, "Ordinate 2 of evaluation point in source CRS", 5500000.0d), + new EpsgOperationParameterRecord(6305, "Ordinate 1 of evaluation point in target CRS", 448933.793d), + new EpsgOperationParameterRecord(6305, "Ordinate 2 of evaluation point in target CRS", 5461423.984d), + new EpsgOperationParameterRecord(6305, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(6305, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(6305, "A1", -28.7827d), + new EpsgOperationParameterRecord(6305, "A2", 1843.8236d), + new EpsgOperationParameterRecord(6305, "A3", -0.0864d), + new EpsgOperationParameterRecord(6305, "A4", -11.9065d), + new EpsgOperationParameterRecord(6305, "A5", 4.0793d), + new EpsgOperationParameterRecord(6305, "A6", -0.0809d), + new EpsgOperationParameterRecord(6306, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(6306, "Longitude of natural origin", 5.0d), + new EpsgOperationParameterRecord(6306, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(6306, "False easting", 500000.0d), + new EpsgOperationParameterRecord(6306, "False northing", 0.0d), + new EpsgOperationParameterRecord(6306, "Latitude of natural origin", 52.1561605555558d), + new EpsgOperationParameterRecord(6306, "Longitude of natural origin", 5.38763888888917d), + new EpsgOperationParameterRecord(6306, "Scale factor at natural origin", 0.9999079d), + new EpsgOperationParameterRecord(6306, "False easting", 155000.0d), + new EpsgOperationParameterRecord(6306, "False northing", 463000.0d), + new EpsgOperationParameterRecord(6306, "Ordinate 1 of evaluation point in source CRS", 526577.124d), + new EpsgOperationParameterRecord(6306, "Ordinate 2 of evaluation point in source CRS", 5778575.474d), + new EpsgOperationParameterRecord(6306, "Ordinate 1 of evaluation point in target CRS", 155000.0d), + new EpsgOperationParameterRecord(6306, "Ordinate 2 of evaluation point in target CRS", 463000.0d), + new EpsgOperationParameterRecord(6306, "Scaling factor for source CRS coord differences", 1e-05d), + new EpsgOperationParameterRecord(6306, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(6306, "A1", -100028.0577d), + new EpsgOperationParameterRecord(6306, "A2", 533.9532d), + new EpsgOperationParameterRecord(6306, "A3", 3.3943d), + new EpsgOperationParameterRecord(6306, "A4", -0.1935d), + new EpsgOperationParameterRecord(6306, "A5", 2.0687d), + new EpsgOperationParameterRecord(6306, "A6", 0.0235d), + new EpsgOperationParameterRecord(6306, "A7", 0.0554d), + new EpsgOperationParameterRecord(6306, "A8", -0.0167d), + new EpsgOperationParameterRecord(6313, "X-axis translation", -0.014d), + new EpsgOperationParameterRecord(6313, "Y-axis translation", 0.0431d), + new EpsgOperationParameterRecord(6313, "Z-axis translation", 0.201d), + new EpsgOperationParameterRecord(6313, "X-axis rotation", 0.012464d), + new EpsgOperationParameterRecord(6313, "Y-axis rotation", 0.012013d), + new EpsgOperationParameterRecord(6313, "Z-axis rotation", 0.006434d), + new EpsgOperationParameterRecord(6313, "Scale difference", 0.024607d), + new EpsgOperationParameterRecord(6313, "Rate of change of X-axis translation", 0.0411d), + new EpsgOperationParameterRecord(6313, "Rate of change of Y-axis translation", 0.0218d), + new EpsgOperationParameterRecord(6313, "Rate of change of Z-axis translation", 0.0383d), + new EpsgOperationParameterRecord(6313, "Rate of change of X-axis rotation", 0.002542d), + new EpsgOperationParameterRecord(6313, "Rate of change of Y-axis rotation", 0.001431d), + new EpsgOperationParameterRecord(6313, "Rate of change of Z-axis rotation", -0.000234d), + new EpsgOperationParameterRecord(6313, "Rate of change of scale difference", 0.005897d), + new EpsgOperationParameterRecord(6313, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6315, "X-axis translation", -0.0761d), + new EpsgOperationParameterRecord(6315, "Y-axis translation", -0.0101d), + new EpsgOperationParameterRecord(6315, "Z-axis translation", 0.0444d), + new EpsgOperationParameterRecord(6315, "X-axis rotation", 0.008765d), + new EpsgOperationParameterRecord(6315, "Y-axis rotation", 0.009361d), + new EpsgOperationParameterRecord(6315, "Z-axis rotation", 0.009325d), + new EpsgOperationParameterRecord(6315, "Scale difference", 0.007935d), + new EpsgOperationParameterRecord(6315, "Rate of change of X-axis translation", 0.011d), + new EpsgOperationParameterRecord(6315, "Rate of change of Y-axis translation", -0.0045d), + new EpsgOperationParameterRecord(6315, "Rate of change of Z-axis translation", -0.0174d), + new EpsgOperationParameterRecord(6315, "Rate of change of X-axis rotation", 0.001034d), + new EpsgOperationParameterRecord(6315, "Rate of change of Y-axis rotation", 0.000671d), + new EpsgOperationParameterRecord(6315, "Rate of change of Z-axis rotation", 0.001039d), + new EpsgOperationParameterRecord(6315, "Rate of change of scale difference", -0.000538d), + new EpsgOperationParameterRecord(6315, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6373, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6373, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6373, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(6389, "X-axis translation", 2.0d), + new EpsgOperationParameterRecord(6389, "Y-axis translation", 0.9d), + new EpsgOperationParameterRecord(6389, "Z-axis translation", 4.7d), + new EpsgOperationParameterRecord(6389, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6389, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6389, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6389, "Scale difference", -0.94d), + new EpsgOperationParameterRecord(6389, "Rate of change of X-axis translation", -0.3d), + new EpsgOperationParameterRecord(6389, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6389, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(6389, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6389, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6389, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6389, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(6389, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6392, "X-axis translation", -0.2088d), + new EpsgOperationParameterRecord(6392, "Y-axis translation", 0.0119d), + new EpsgOperationParameterRecord(6392, "Z-axis translation", 0.1855d), + new EpsgOperationParameterRecord(6392, "X-axis rotation", 0.012059d), + new EpsgOperationParameterRecord(6392, "Y-axis rotation", 0.013639d), + new EpsgOperationParameterRecord(6392, "Z-axis rotation", 0.011825d), + new EpsgOperationParameterRecord(6392, "Scale difference", 0.004559d), + new EpsgOperationParameterRecord(6392, "Rate of change of X-axis translation", -0.022d), + new EpsgOperationParameterRecord(6392, "Rate of change of Y-axis translation", 0.0049d), + new EpsgOperationParameterRecord(6392, "Rate of change of Z-axis translation", 0.0169d), + new EpsgOperationParameterRecord(6392, "Rate of change of X-axis rotation", 0.00204d), + new EpsgOperationParameterRecord(6392, "Rate of change of Y-axis rotation", 0.001782d), + new EpsgOperationParameterRecord(6392, "Rate of change of Z-axis rotation", 0.001697d), + new EpsgOperationParameterRecord(6392, "Rate of change of scale difference", -0.00109d), + new EpsgOperationParameterRecord(6392, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(6698, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6698, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6698, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(6699, "Vertical Offset", 0.0d), + new EpsgOperationParameterRecord(6701, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6701, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6701, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(6711, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6711, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6711, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(6724, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(6724, "Longitude of natural origin", 105.0d), + new EpsgOperationParameterRecord(6724, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(6724, "False easting", 500000.0d), + new EpsgOperationParameterRecord(6724, "False northing", 10000000.0d), + new EpsgOperationParameterRecord(6724, "Easting offset", 550015.0d), + new EpsgOperationParameterRecord(6724, "Northing offset", 8780001.0d), + new EpsgOperationParameterRecord(6864, "X-axis translation", 0.991d), + new EpsgOperationParameterRecord(6864, "Y-axis translation", -1.9072d), + new EpsgOperationParameterRecord(6864, "Z-axis translation", -0.5129d), + new EpsgOperationParameterRecord(6864, "X-axis rotation", 25.79d), + new EpsgOperationParameterRecord(6864, "Y-axis rotation", 9.65d), + new EpsgOperationParameterRecord(6864, "Z-axis rotation", 11.66d), + new EpsgOperationParameterRecord(6864, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(6864, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(6864, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6864, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(6864, "Rate of change of X-axis rotation", 0.0532d), + new EpsgOperationParameterRecord(6864, "Rate of change of Y-axis rotation", -0.7423d), + new EpsgOperationParameterRecord(6864, "Rate of change of Z-axis rotation", -0.0316d), + new EpsgOperationParameterRecord(6864, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(6864, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(6865, "X-axis translation", 0.9889d), + new EpsgOperationParameterRecord(6865, "Y-axis translation", -1.9074d), + new EpsgOperationParameterRecord(6865, "Z-axis translation", -0.503d), + new EpsgOperationParameterRecord(6865, "X-axis rotation", 25.915d), + new EpsgOperationParameterRecord(6865, "Y-axis rotation", 9.426d), + new EpsgOperationParameterRecord(6865, "Z-axis rotation", 11.599d), + new EpsgOperationParameterRecord(6865, "Scale difference", -0.93d), + new EpsgOperationParameterRecord(6865, "Rate of change of X-axis translation", 0.0007d), + new EpsgOperationParameterRecord(6865, "Rate of change of Y-axis translation", -0.0001d), + new EpsgOperationParameterRecord(6865, "Rate of change of Z-axis translation", 0.0019d), + new EpsgOperationParameterRecord(6865, "Rate of change of X-axis rotation", 0.067d), + new EpsgOperationParameterRecord(6865, "Rate of change of Y-axis rotation", -0.757d), + new EpsgOperationParameterRecord(6865, "Rate of change of Z-axis rotation", -0.031d), + new EpsgOperationParameterRecord(6865, "Rate of change of scale difference", -0.19d), + new EpsgOperationParameterRecord(6865, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(6866, "X-axis translation", 0.9956d), + new EpsgOperationParameterRecord(6866, "Y-axis translation", -1.9013d), + new EpsgOperationParameterRecord(6866, "Z-axis translation", -0.5215d), + new EpsgOperationParameterRecord(6866, "X-axis rotation", 25.915d), + new EpsgOperationParameterRecord(6866, "Y-axis rotation", 9.426d), + new EpsgOperationParameterRecord(6866, "Z-axis rotation", 11.599d), + new EpsgOperationParameterRecord(6866, "Scale difference", 0.62d), + new EpsgOperationParameterRecord(6866, "Rate of change of X-axis translation", 0.0007d), + new EpsgOperationParameterRecord(6866, "Rate of change of Y-axis translation", -0.0007d), + new EpsgOperationParameterRecord(6866, "Rate of change of Z-axis translation", 0.0005d), + new EpsgOperationParameterRecord(6866, "Rate of change of X-axis rotation", 0.067d), + new EpsgOperationParameterRecord(6866, "Rate of change of Y-axis rotation", -0.757d), + new EpsgOperationParameterRecord(6866, "Rate of change of Z-axis rotation", -0.051d), + new EpsgOperationParameterRecord(6866, "Rate of change of scale difference", -0.18d), + new EpsgOperationParameterRecord(6866, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(6872, "X-axis translation", -123.1d), + new EpsgOperationParameterRecord(6872, "Y-axis translation", 53.2d), + new EpsgOperationParameterRecord(6872, "Z-axis translation", 465.4d), + new EpsgOperationParameterRecord(6873, "X-axis translation", -198.383d), + new EpsgOperationParameterRecord(6873, "Y-axis translation", -240.517d), + new EpsgOperationParameterRecord(6873, "Z-axis translation", -107.909d), + new EpsgOperationParameterRecord(6888, "X-axis translation", 205.435d), + new EpsgOperationParameterRecord(6888, "Y-axis translation", -29.099d), + new EpsgOperationParameterRecord(6888, "Z-axis translation", -292.202d), + new EpsgOperationParameterRecord(6889, "X-axis translation", 213.116d), + new EpsgOperationParameterRecord(6889, "Y-axis translation", 9.358d), + new EpsgOperationParameterRecord(6889, "Z-axis translation", -74.946d), + new EpsgOperationParameterRecord(6889, "X-axis rotation", 1.14e-05d), + new EpsgOperationParameterRecord(6889, "Y-axis rotation", -2.98e-07d), + new EpsgOperationParameterRecord(6889, "Z-axis rotation", 3.1e-05d), + new EpsgOperationParameterRecord(6889, "Scale difference", 5.22d), + new EpsgOperationParameterRecord(6889, "Ordinate 1 of evaluation point", 617749.7118d), + new EpsgOperationParameterRecord(6889, "Ordinate 2 of evaluation point", -6250547.7336d), + new EpsgOperationParameterRecord(6889, "Ordinate 3 of evaluation point", 1102063.6099d), + new EpsgOperationParameterRecord(6890, "X-axis translation", 213.11d), + new EpsgOperationParameterRecord(6890, "Y-axis translation", 9.37d), + new EpsgOperationParameterRecord(6890, "Z-axis translation", -74.95d), + new EpsgOperationParameterRecord(6891, "X-axis translation", 205.0d), + new EpsgOperationParameterRecord(6891, "Y-axis translation", 96.0d), + new EpsgOperationParameterRecord(6891, "Z-axis translation", -98.0d), + new EpsgOperationParameterRecord(6895, "X-axis translation", 98.0d), + new EpsgOperationParameterRecord(6895, "Y-axis translation", 390.0d), + new EpsgOperationParameterRecord(6895, "Z-axis translation", -22.0d), + new EpsgOperationParameterRecord(6896, "X-axis translation", -170.0d), + new EpsgOperationParameterRecord(6896, "Y-axis translation", 33.0d), + new EpsgOperationParameterRecord(6896, "Z-axis translation", 326.0d), + new EpsgOperationParameterRecord(6897, "X-axis translation", -153.0d), + new EpsgOperationParameterRecord(6897, "Y-axis translation", 153.0d), + new EpsgOperationParameterRecord(6897, "Z-axis translation", 307.0d), + new EpsgOperationParameterRecord(6898, "X-axis translation", -306.0d), + new EpsgOperationParameterRecord(6898, "Y-axis translation", -62.0d), + new EpsgOperationParameterRecord(6898, "Z-axis translation", 105.0d), + new EpsgOperationParameterRecord(6899, "X-axis translation", 22.0d), + new EpsgOperationParameterRecord(6899, "Y-axis translation", -126.0d), + new EpsgOperationParameterRecord(6899, "Z-axis translation", -85.0d), + new EpsgOperationParameterRecord(6900, "X-axis translation", -132.0d), + new EpsgOperationParameterRecord(6900, "Y-axis translation", -110.0d), + new EpsgOperationParameterRecord(6900, "Z-axis translation", -335.0d), + new EpsgOperationParameterRecord(6901, "X-axis translation", -80.0d), + new EpsgOperationParameterRecord(6901, "Y-axis translation", -100.0d), + new EpsgOperationParameterRecord(6901, "Z-axis translation", -228.0d), + new EpsgOperationParameterRecord(6902, "X-axis translation", -679.0d), + new EpsgOperationParameterRecord(6902, "Y-axis translation", 667.0d), + new EpsgOperationParameterRecord(6902, "Z-axis translation", -49.0d), + new EpsgOperationParameterRecord(6903, "X-axis translation", -30.0d), + new EpsgOperationParameterRecord(6903, "Y-axis translation", 190.0d), + new EpsgOperationParameterRecord(6903, "Z-axis translation", 89.0d), + new EpsgOperationParameterRecord(6904, "X-axis translation", -179.0d), + new EpsgOperationParameterRecord(6904, "Y-axis translation", -81.0d), + new EpsgOperationParameterRecord(6904, "Z-axis translation", -314.0d), + new EpsgOperationParameterRecord(6905, "X-axis translation", -128.0d), + new EpsgOperationParameterRecord(6905, "Y-axis translation", -52.0d), + new EpsgOperationParameterRecord(6905, "Z-axis translation", 153.0d), + new EpsgOperationParameterRecord(6906, "X-axis translation", -145.0d), + new EpsgOperationParameterRecord(6906, "Y-axis translation", -97.0d), + new EpsgOperationParameterRecord(6906, "Z-axis translation", -292.0d), + new EpsgOperationParameterRecord(6907, "X-axis translation", -77.0d), + new EpsgOperationParameterRecord(6907, "Y-axis translation", -128.0d), + new EpsgOperationParameterRecord(6907, "Z-axis translation", 142.0d), + new EpsgOperationParameterRecord(6908, "X-axis translation", -345.0d), + new EpsgOperationParameterRecord(6908, "Y-axis translation", 3.0d), + new EpsgOperationParameterRecord(6908, "Z-axis translation", 223.0d), + new EpsgOperationParameterRecord(6909, "X-axis translation", -73.0d), + new EpsgOperationParameterRecord(6909, "Y-axis translation", 47.0d), + new EpsgOperationParameterRecord(6909, "Z-axis translation", -83.0d), + new EpsgOperationParameterRecord(6910, "X-axis translation", -24.0d), + new EpsgOperationParameterRecord(6910, "Y-axis translation", -203.0d), + new EpsgOperationParameterRecord(6910, "Z-axis translation", 268.0d), + new EpsgOperationParameterRecord(6911, "X-axis translation", -183.0d), + new EpsgOperationParameterRecord(6911, "Y-axis translation", -15.0d), + new EpsgOperationParameterRecord(6911, "Z-axis translation", 273.0d), + new EpsgOperationParameterRecord(6912, "X-axis translation", -235.0d), + new EpsgOperationParameterRecord(6912, "Y-axis translation", -110.0d), + new EpsgOperationParameterRecord(6912, "Z-axis translation", 393.0d), + new EpsgOperationParameterRecord(6913, "X-axis translation", -63.0d), + new EpsgOperationParameterRecord(6913, "Y-axis translation", 176.0d), + new EpsgOperationParameterRecord(6913, "Z-axis translation", 185.0d), + new EpsgOperationParameterRecord(6914, "X-axis translation", -43.685d), + new EpsgOperationParameterRecord(6914, "Y-axis translation", -179.785d), + new EpsgOperationParameterRecord(6914, "Z-axis translation", -267.721d), + new EpsgOperationParameterRecord(6918, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(6918, "Longitude of natural origin", 3.0d), + new EpsgOperationParameterRecord(6918, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(6918, "False easting", 500000.0d), + new EpsgOperationParameterRecord(6918, "False northing", 0.0d), + new EpsgOperationParameterRecord(6918, "Bin grid origin I", 1.0d), + new EpsgOperationParameterRecord(6918, "Bin grid origin J", 1.0d), + new EpsgOperationParameterRecord(6918, "Bin grid origin Easting", 456781.0d), + new EpsgOperationParameterRecord(6918, "Bin grid origin Northing", 5836723.0d), + new EpsgOperationParameterRecord(6918, "Scale factor of bin grid", 0.99984d), + new EpsgOperationParameterRecord(6918, "Bin width on I-axis", 25.0d), + new EpsgOperationParameterRecord(6918, "Bin width on J-axis", 12.5d), + new EpsgOperationParameterRecord(6918, "Map grid bearing of bin grid J-axis", 20.0d), + new EpsgOperationParameterRecord(6918, "Bin node increment on I-axis", 1.0d), + new EpsgOperationParameterRecord(6918, "Bin node increment on J-axis", 1.0d), + new EpsgOperationParameterRecord(6919, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(6919, "Longitude of natural origin", -87.0d), + new EpsgOperationParameterRecord(6919, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(6919, "False easting", 1640416.67d), + new EpsgOperationParameterRecord(6919, "False northing", 0.0d), + new EpsgOperationParameterRecord(6919, "Bin grid origin I", 5000.0d), + new EpsgOperationParameterRecord(6919, "Bin grid origin J", 0.0d), + new EpsgOperationParameterRecord(6919, "Bin grid origin Easting", 871200.0d), + new EpsgOperationParameterRecord(6919, "Bin grid origin Northing", 10280160.0d), + new EpsgOperationParameterRecord(6919, "Scale factor of bin grid", 1.0d), + new EpsgOperationParameterRecord(6919, "Bin width on I-axis", 82.5d), + new EpsgOperationParameterRecord(6919, "Bin width on J-axis", 41.25d), + new EpsgOperationParameterRecord(6919, "Map grid bearing of bin grid J-axis", 340.0d), + new EpsgOperationParameterRecord(6919, "Bin node increment on I-axis", 1.0d), + new EpsgOperationParameterRecord(6919, "Bin node increment on J-axis", 1.0d), + new EpsgOperationParameterRecord(6926, "X-axis translation", -76.269d), + new EpsgOperationParameterRecord(6926, "Y-axis translation", -16.683d), + new EpsgOperationParameterRecord(6926, "Z-axis translation", 68.562d), + new EpsgOperationParameterRecord(6926, "X-axis rotation", -6.275d), + new EpsgOperationParameterRecord(6926, "Y-axis rotation", 10.536d), + new EpsgOperationParameterRecord(6926, "Z-axis rotation", -4.286d), + new EpsgOperationParameterRecord(6926, "Scale difference", -13.686d), + new EpsgOperationParameterRecord(6935, "X-axis translation", 0.208d), + new EpsgOperationParameterRecord(6935, "Y-axis translation", -0.012d), + new EpsgOperationParameterRecord(6935, "Z-axis translation", -0.229d), + new EpsgOperationParameterRecord(6935, "X-axis rotation", -0.01182d), + new EpsgOperationParameterRecord(6935, "Y-axis rotation", 0.00811d), + new EpsgOperationParameterRecord(6935, "Z-axis rotation", -0.01677d), + new EpsgOperationParameterRecord(6935, "Scale difference", -0.0059d), + new EpsgOperationParameterRecord(6935, "Ordinate 1 of evaluation point", 3777505.028d), + new EpsgOperationParameterRecord(6935, "Ordinate 2 of evaluation point", 3779254.396d), + new EpsgOperationParameterRecord(6935, "Ordinate 3 of evaluation point", 3471111.632d), + new EpsgOperationParameterRecord(6936, "X-axis translation", -0.214d), + new EpsgOperationParameterRecord(6936, "Y-axis translation", 0.119d), + new EpsgOperationParameterRecord(6936, "Z-axis translation", 0.156d), + new EpsgOperationParameterRecord(6936, "X-axis rotation", -0.01182d), + new EpsgOperationParameterRecord(6936, "Y-axis rotation", 0.00811d), + new EpsgOperationParameterRecord(6936, "Z-axis rotation", -0.01677d), + new EpsgOperationParameterRecord(6936, "Scale difference", -0.0059d), + new EpsgOperationParameterRecord(6937, "X-axis translation", -0.41d), + new EpsgOperationParameterRecord(6937, "Y-axis translation", -2.37d), + new EpsgOperationParameterRecord(6937, "Z-axis translation", 2.0d), + new EpsgOperationParameterRecord(6937, "X-axis rotation", 3.592d), + new EpsgOperationParameterRecord(6937, "Y-axis rotation", 3.698d), + new EpsgOperationParameterRecord(6937, "Z-axis rotation", 3.989d), + new EpsgOperationParameterRecord(6937, "Scale difference", 8.843d), + new EpsgOperationParameterRecord(6938, "X-axis translation", -129.0d), + new EpsgOperationParameterRecord(6938, "Y-axis translation", -58.0d), + new EpsgOperationParameterRecord(6938, "Z-axis translation", 152.0d), + new EpsgOperationParameterRecord(6939, "X-axis translation", -131.876d), + new EpsgOperationParameterRecord(6939, "Y-axis translation", -54.554d), + new EpsgOperationParameterRecord(6939, "Z-axis translation", 453.346d), + new EpsgOperationParameterRecord(6939, "X-axis rotation", -5.2155d), + new EpsgOperationParameterRecord(6939, "Y-axis rotation", -8.2042d), + new EpsgOperationParameterRecord(6939, "Z-axis rotation", 0.09d), + new EpsgOperationParameterRecord(6939, "Scale difference", 5.02d), + new EpsgOperationParameterRecord(6940, "X-axis translation", -131.3d), + new EpsgOperationParameterRecord(6940, "Y-axis translation", -55.3d), + new EpsgOperationParameterRecord(6940, "Z-axis translation", 151.8d), + new EpsgOperationParameterRecord(6941, "X-axis translation", 45.928d), + new EpsgOperationParameterRecord(6941, "Y-axis translation", -177.212d), + new EpsgOperationParameterRecord(6941, "Z-axis translation", 336.867d), + new EpsgOperationParameterRecord(6941, "X-axis rotation", -4.6039d), + new EpsgOperationParameterRecord(6941, "Y-axis rotation", -3.0921d), + new EpsgOperationParameterRecord(6941, "Z-axis rotation", 0.5729d), + new EpsgOperationParameterRecord(6941, "Scale difference", 36.796d), + new EpsgOperationParameterRecord(6942, "X-axis translation", -137.4d), + new EpsgOperationParameterRecord(6942, "Y-axis translation", -58.9d), + new EpsgOperationParameterRecord(6942, "Z-axis translation", 150.4d), + new EpsgOperationParameterRecord(6943, "X-axis translation", -129.0d), + new EpsgOperationParameterRecord(6943, "Y-axis translation", -58.0d), + new EpsgOperationParameterRecord(6943, "Z-axis translation", 152.0d), + new EpsgOperationParameterRecord(6944, "X-axis translation", -131.3d), + new EpsgOperationParameterRecord(6944, "Y-axis translation", -55.3d), + new EpsgOperationParameterRecord(6944, "Z-axis translation", 151.8d), + new EpsgOperationParameterRecord(6945, "X-axis translation", -137.4d), + new EpsgOperationParameterRecord(6945, "Y-axis translation", -58.9d), + new EpsgOperationParameterRecord(6945, "Z-axis translation", 150.4d), + new EpsgOperationParameterRecord(6949, "X-axis translation", -302.0d), + new EpsgOperationParameterRecord(6949, "Y-axis translation", 272.0d), + new EpsgOperationParameterRecord(6949, "Z-axis translation", -360.0d), + new EpsgOperationParameterRecord(6950, "X-axis translation", -328.0d), + new EpsgOperationParameterRecord(6950, "Y-axis translation", 340.0d), + new EpsgOperationParameterRecord(6950, "Z-axis translation", -329.0d), + new EpsgOperationParameterRecord(6951, "X-axis translation", -352.0d), + new EpsgOperationParameterRecord(6951, "Y-axis translation", 403.0d), + new EpsgOperationParameterRecord(6951, "Z-axis translation", -287.0d), + new EpsgOperationParameterRecord(6960, "X-axis translation", -191.90441429d), + new EpsgOperationParameterRecord(6960, "Y-axis translation", -39.30318279d), + new EpsgOperationParameterRecord(6960, "Z-axis translation", -111.45032835d), + new EpsgOperationParameterRecord(6960, "X-axis rotation", -0.00928836d), + new EpsgOperationParameterRecord(6960, "Y-axis rotation", 0.01975479d), + new EpsgOperationParameterRecord(6960, "Z-axis rotation", -0.00427372d), + new EpsgOperationParameterRecord(6960, "Scale difference", 0.252906278d), + new EpsgOperationParameterRecord(6968, "X-axis translation", -64.0d), + new EpsgOperationParameterRecord(6968, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6968, "Z-axis translation", -32.0d), + new EpsgOperationParameterRecord(6970, "X-axis translation", -79.0d), + new EpsgOperationParameterRecord(6970, "Y-axis translation", 13.0d), + new EpsgOperationParameterRecord(6970, "Z-axis translation", -14.0d), + new EpsgOperationParameterRecord(6971, "X-axis translation", -302.0d), + new EpsgOperationParameterRecord(6971, "Y-axis translation", 272.0d), + new EpsgOperationParameterRecord(6971, "Z-axis translation", -360.0d), + new EpsgOperationParameterRecord(6972, "X-axis translation", -328.0d), + new EpsgOperationParameterRecord(6972, "Y-axis translation", 340.0d), + new EpsgOperationParameterRecord(6972, "Z-axis translation", -329.0d), + new EpsgOperationParameterRecord(6973, "X-axis translation", -352.0d), + new EpsgOperationParameterRecord(6973, "Y-axis translation", 403.0d), + new EpsgOperationParameterRecord(6973, "Z-axis translation", -287.0d), + new EpsgOperationParameterRecord(6974, "X-axis translation", -59.0d), + new EpsgOperationParameterRecord(6974, "Y-axis translation", -11.0d), + new EpsgOperationParameterRecord(6974, "Z-axis translation", -52.0d), + new EpsgOperationParameterRecord(6975, "X-axis translation", -64.0d), + new EpsgOperationParameterRecord(6975, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(6975, "Z-axis translation", -32.0d), + new EpsgOperationParameterRecord(6976, "X-axis translation", -72.0d), + new EpsgOperationParameterRecord(6976, "Y-axis translation", 10.0d), + new EpsgOperationParameterRecord(6976, "Z-axis translation", -32.0d), + new EpsgOperationParameterRecord(6977, "X-axis translation", -79.0d), + new EpsgOperationParameterRecord(6977, "Y-axis translation", 13.0d), + new EpsgOperationParameterRecord(6977, "Z-axis translation", -14.0d), + new EpsgOperationParameterRecord(6992, "X-axis translation", 0.2255d), + new EpsgOperationParameterRecord(6992, "Y-axis translation", -0.3709d), + new EpsgOperationParameterRecord(6992, "Z-axis translation", -0.1171d), + new EpsgOperationParameterRecord(6992, "X-axis rotation", -0.00388d), + new EpsgOperationParameterRecord(6992, "Y-axis rotation", 0.00063d), + new EpsgOperationParameterRecord(6992, "Z-axis rotation", -0.0182d), + new EpsgOperationParameterRecord(6992, "Scale difference", 0.013443d), + new EpsgOperationParameterRecord(6993, "X-axis translation", -24.0024d), + new EpsgOperationParameterRecord(6993, "Y-axis translation", -17.1032d), + new EpsgOperationParameterRecord(6993, "Z-axis translation", -17.8444d), + new EpsgOperationParameterRecord(6993, "X-axis rotation", -0.33009d), + new EpsgOperationParameterRecord(6993, "Y-axis rotation", -1.85269d), + new EpsgOperationParameterRecord(6993, "Z-axis rotation", 1.66969d), + new EpsgOperationParameterRecord(6993, "Scale difference", 5.4248d), + new EpsgOperationParameterRecord(6998, "X-axis translation", -233.4d), + new EpsgOperationParameterRecord(6998, "Y-axis translation", -160.7d), + new EpsgOperationParameterRecord(6998, "Z-axis translation", 381.5d), + new EpsgOperationParameterRecord(6998, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6998, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(6998, "Z-axis rotation", -0.554d), + new EpsgOperationParameterRecord(6998, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(6999, "X-axis translation", -253.4392d), + new EpsgOperationParameterRecord(6999, "Y-axis translation", -148.452d), + new EpsgOperationParameterRecord(6999, "Z-axis translation", 386.5267d), + new EpsgOperationParameterRecord(6999, "X-axis rotation", -0.15605d), + new EpsgOperationParameterRecord(6999, "Y-axis rotation", -0.43d), + new EpsgOperationParameterRecord(6999, "Z-axis rotation", 0.1013d), + new EpsgOperationParameterRecord(6999, "Scale difference", -0.0424d), + new EpsgOperationParameterRecord(7002, "X-axis translation", -246.1633d), + new EpsgOperationParameterRecord(7002, "Y-axis translation", -152.9047d), + new EpsgOperationParameterRecord(7002, "Z-axis translation", 382.6047d), + new EpsgOperationParameterRecord(7002, "X-axis rotation", -0.0989d), + new EpsgOperationParameterRecord(7002, "Y-axis rotation", -0.1382d), + new EpsgOperationParameterRecord(7002, "Z-axis rotation", -0.0768d), + new EpsgOperationParameterRecord(7002, "Scale difference", 2.1e-06d), + new EpsgOperationParameterRecord(7003, "X-axis translation", -242.8907d), + new EpsgOperationParameterRecord(7003, "Y-axis translation", -149.0671d), + new EpsgOperationParameterRecord(7003, "Z-axis translation", 384.416d), + new EpsgOperationParameterRecord(7003, "X-axis rotation", -0.19044d), + new EpsgOperationParameterRecord(7003, "Y-axis rotation", -0.24987d), + new EpsgOperationParameterRecord(7003, "Z-axis rotation", -0.13925d), + new EpsgOperationParameterRecord(7003, "Scale difference", 0.0001746d), + new EpsgOperationParameterRecord(7004, "X-axis translation", -246.734d), + new EpsgOperationParameterRecord(7004, "Y-axis translation", -153.4345d), + new EpsgOperationParameterRecord(7004, "Z-axis translation", 382.1477d), + new EpsgOperationParameterRecord(7004, "X-axis rotation", 0.116617d), + new EpsgOperationParameterRecord(7004, "Y-axis rotation", 0.165167d), + new EpsgOperationParameterRecord(7004, "Z-axis rotation", 0.091327d), + new EpsgOperationParameterRecord(7004, "Scale difference", 1.94e-05d), + new EpsgOperationParameterRecord(7008, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7008, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7008, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7008, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7008, "False northing", 0.0d), + new EpsgOperationParameterRecord(7008, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7008, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7008, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7008, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7008, "False northing", 0.0d), + new EpsgOperationParameterRecord(7008, "Easting offset", 386.0d), + new EpsgOperationParameterRecord(7008, "Northing offset", 204.0d), + new EpsgOperationParameterRecord(7009, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7009, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7009, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7009, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7009, "False northing", 0.0d), + new EpsgOperationParameterRecord(7009, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7009, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7009, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7009, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7009, "False northing", 0.0d), + new EpsgOperationParameterRecord(7009, "Easting offset", 383.0d), + new EpsgOperationParameterRecord(7009, "Northing offset", 205.0d), + new EpsgOperationParameterRecord(7010, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7010, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7010, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7010, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7010, "False northing", 0.0d), + new EpsgOperationParameterRecord(7010, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7010, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7010, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7010, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7010, "False northing", 0.0d), + new EpsgOperationParameterRecord(7010, "Easting offset", 378.0d), + new EpsgOperationParameterRecord(7010, "Northing offset", 196.0d), + new EpsgOperationParameterRecord(7011, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7011, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7011, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7011, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7011, "False northing", 0.0d), + new EpsgOperationParameterRecord(7011, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7011, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7011, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7011, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7011, "False northing", 0.0d), + new EpsgOperationParameterRecord(7011, "Easting offset", 372.0d), + new EpsgOperationParameterRecord(7011, "Northing offset", 196.0d), + new EpsgOperationParameterRecord(7012, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7012, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7012, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7012, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7012, "False northing", 0.0d), + new EpsgOperationParameterRecord(7012, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7012, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7012, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7012, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7012, "False northing", 0.0d), + new EpsgOperationParameterRecord(7012, "Easting offset", 375.0d), + new EpsgOperationParameterRecord(7012, "Northing offset", 200.0d), + new EpsgOperationParameterRecord(7013, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7013, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7013, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7013, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7013, "False northing", 0.0d), + new EpsgOperationParameterRecord(7013, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7013, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7013, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7013, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7013, "False northing", 0.0d), + new EpsgOperationParameterRecord(7013, "Easting offset", 365.0d), + new EpsgOperationParameterRecord(7013, "Northing offset", 196.0d), + new EpsgOperationParameterRecord(7014, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7014, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7014, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7014, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7014, "False northing", 0.0d), + new EpsgOperationParameterRecord(7014, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7014, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7014, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7014, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7014, "False northing", 0.0d), + new EpsgOperationParameterRecord(7014, "Easting offset", 373.0d), + new EpsgOperationParameterRecord(7014, "Northing offset", 191.0d), + new EpsgOperationParameterRecord(7015, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7015, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7015, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7015, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7015, "False northing", 0.0d), + new EpsgOperationParameterRecord(7015, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7015, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7015, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7015, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7015, "False northing", 0.0d), + new EpsgOperationParameterRecord(7015, "Easting offset", 355.0d), + new EpsgOperationParameterRecord(7015, "Northing offset", 208.0d), + new EpsgOperationParameterRecord(7016, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7016, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7016, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7016, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7016, "False northing", 0.0d), + new EpsgOperationParameterRecord(7016, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7016, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7016, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7016, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7016, "False northing", 0.0d), + new EpsgOperationParameterRecord(7016, "Easting offset", 355.0d), + new EpsgOperationParameterRecord(7016, "Northing offset", 208.0d), + new EpsgOperationParameterRecord(7017, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7017, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7017, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7017, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7017, "False northing", 0.0d), + new EpsgOperationParameterRecord(7017, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7017, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7017, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7017, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7017, "False northing", 0.0d), + new EpsgOperationParameterRecord(7017, "Easting offset", 355.0d), + new EpsgOperationParameterRecord(7017, "Northing offset", 200.0d), + new EpsgOperationParameterRecord(7018, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7018, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7018, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7018, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7018, "False northing", 0.0d), + new EpsgOperationParameterRecord(7018, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7018, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7018, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7018, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7018, "False northing", 0.0d), + new EpsgOperationParameterRecord(7018, "Easting offset", 358.0d), + new EpsgOperationParameterRecord(7018, "Northing offset", 195.0d), + new EpsgOperationParameterRecord(7019, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7019, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7019, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7019, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7019, "False northing", 0.0d), + new EpsgOperationParameterRecord(7019, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7019, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7019, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7019, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7019, "False northing", 0.0d), + new EpsgOperationParameterRecord(7019, "Easting offset", 345.0d), + new EpsgOperationParameterRecord(7019, "Northing offset", 229.0d), + new EpsgOperationParameterRecord(7020, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7020, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7020, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7020, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7020, "False northing", 0.0d), + new EpsgOperationParameterRecord(7020, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7020, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7020, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7020, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7020, "False northing", 0.0d), + new EpsgOperationParameterRecord(7020, "Easting offset", 345.0d), + new EpsgOperationParameterRecord(7020, "Northing offset", 214.0d), + new EpsgOperationParameterRecord(7021, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7021, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7021, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7021, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7021, "False northing", 0.0d), + new EpsgOperationParameterRecord(7021, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7021, "Longitude of natural origin", 39.0d), + new EpsgOperationParameterRecord(7021, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7021, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7021, "False northing", 0.0d), + new EpsgOperationParameterRecord(7021, "Easting offset", 354.0d), + new EpsgOperationParameterRecord(7021, "Northing offset", 229.0d), + new EpsgOperationParameterRecord(7022, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7022, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7022, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7022, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7022, "False northing", 0.0d), + new EpsgOperationParameterRecord(7022, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7022, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7022, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7022, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7022, "False northing", 0.0d), + new EpsgOperationParameterRecord(7022, "Easting offset", 354.0d), + new EpsgOperationParameterRecord(7022, "Northing offset", 208.0d), + new EpsgOperationParameterRecord(7023, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7023, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7023, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7023, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7023, "False northing", 0.0d), + new EpsgOperationParameterRecord(7023, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7023, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7023, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7023, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7023, "False northing", 0.0d), + new EpsgOperationParameterRecord(7023, "Easting offset", 345.0d), + new EpsgOperationParameterRecord(7023, "Northing offset", 214.0d), + new EpsgOperationParameterRecord(7024, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7024, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7024, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7024, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7024, "False northing", 0.0d), + new EpsgOperationParameterRecord(7024, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7024, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7024, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7024, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7024, "False northing", 0.0d), + new EpsgOperationParameterRecord(7024, "Easting offset", 357.0d), + new EpsgOperationParameterRecord(7024, "Northing offset", 211.0d), + new EpsgOperationParameterRecord(7025, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7025, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7025, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7025, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7025, "False northing", 0.0d), + new EpsgOperationParameterRecord(7025, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7025, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7025, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7025, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7025, "False northing", 0.0d), + new EpsgOperationParameterRecord(7025, "Easting offset", 357.0d), + new EpsgOperationParameterRecord(7025, "Northing offset", 188.0d), + new EpsgOperationParameterRecord(7026, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7026, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7026, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7026, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7026, "False northing", 0.0d), + new EpsgOperationParameterRecord(7026, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7026, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7026, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7026, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7026, "False northing", 0.0d), + new EpsgOperationParameterRecord(7026, "Easting offset", 345.0d), + new EpsgOperationParameterRecord(7026, "Northing offset", 183.0d), + new EpsgOperationParameterRecord(7027, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7027, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7027, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7027, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7027, "False northing", 0.0d), + new EpsgOperationParameterRecord(7027, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7027, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7027, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7027, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7027, "False northing", 0.0d), + new EpsgOperationParameterRecord(7027, "Easting offset", 357.0d), + new EpsgOperationParameterRecord(7027, "Northing offset", 186.0d), + new EpsgOperationParameterRecord(7028, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7028, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7028, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7028, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7028, "False northing", 0.0d), + new EpsgOperationParameterRecord(7028, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7028, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7028, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7028, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7028, "False northing", 0.0d), + new EpsgOperationParameterRecord(7028, "Easting offset", 355.0d), + new EpsgOperationParameterRecord(7028, "Northing offset", 185.0d), + new EpsgOperationParameterRecord(7029, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7029, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7029, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7029, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7029, "False northing", 0.0d), + new EpsgOperationParameterRecord(7029, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7029, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7029, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7029, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7029, "False northing", 0.0d), + new EpsgOperationParameterRecord(7029, "Easting offset", 345.0d), + new EpsgOperationParameterRecord(7029, "Northing offset", 186.0d), + new EpsgOperationParameterRecord(7030, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7030, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7030, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7030, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7030, "False northing", 0.0d), + new EpsgOperationParameterRecord(7030, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7030, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7030, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7030, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7030, "False northing", 0.0d), + new EpsgOperationParameterRecord(7030, "Easting offset", 357.0d), + new EpsgOperationParameterRecord(7030, "Northing offset", 188.0d), + new EpsgOperationParameterRecord(7031, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7031, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7031, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7031, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7031, "False northing", 0.0d), + new EpsgOperationParameterRecord(7031, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7031, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7031, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7031, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7031, "False northing", 0.0d), + new EpsgOperationParameterRecord(7031, "Easting offset", 358.0d), + new EpsgOperationParameterRecord(7031, "Northing offset", 175.0d), + new EpsgOperationParameterRecord(7032, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7032, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7032, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7032, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7032, "False northing", 0.0d), + new EpsgOperationParameterRecord(7032, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(7032, "Longitude of natural origin", 45.0d), + new EpsgOperationParameterRecord(7032, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(7032, "False easting", 500000.0d), + new EpsgOperationParameterRecord(7032, "False northing", 0.0d), + new EpsgOperationParameterRecord(7032, "Easting offset", 343.0d), + new EpsgOperationParameterRecord(7032, "Northing offset", 175.0d), + new EpsgOperationParameterRecord(7033, "X-axis translation", -242.2d), + new EpsgOperationParameterRecord(7033, "Y-axis translation", -144.9d), + new EpsgOperationParameterRecord(7033, "Z-axis translation", 370.3d), + new EpsgOperationParameterRecord(7083, "X-axis translation", 324.912d), + new EpsgOperationParameterRecord(7083, "Y-axis translation", 153.282d), + new EpsgOperationParameterRecord(7083, "Z-axis translation", 172.026d), + new EpsgOperationParameterRecord(7140, "X-axis translation", -23.8085d), + new EpsgOperationParameterRecord(7140, "Y-axis translation", -17.5937d), + new EpsgOperationParameterRecord(7140, "Z-axis translation", -17.801d), + new EpsgOperationParameterRecord(7140, "X-axis rotation", -0.3306d), + new EpsgOperationParameterRecord(7140, "Y-axis rotation", -1.85706d), + new EpsgOperationParameterRecord(7140, "Z-axis rotation", 1.64828d), + new EpsgOperationParameterRecord(7140, "Scale difference", 5.4374d), + new EpsgOperationParameterRecord(7377, "X-axis translation", 0.819d), + new EpsgOperationParameterRecord(7377, "Y-axis translation", -0.5762d), + new EpsgOperationParameterRecord(7377, "Z-axis translation", -1.6446d), + new EpsgOperationParameterRecord(7377, "X-axis rotation", 0.00378d), + new EpsgOperationParameterRecord(7377, "Y-axis rotation", 0.03317d), + new EpsgOperationParameterRecord(7377, "Z-axis rotation", -0.00318d), + new EpsgOperationParameterRecord(7377, "Scale difference", 0.0693d), + new EpsgOperationParameterRecord(7442, "X-axis translation", -181.7d), + new EpsgOperationParameterRecord(7442, "Y-axis translation", 64.7d), + new EpsgOperationParameterRecord(7442, "Z-axis translation", 247.2d), + new EpsgOperationParameterRecord(7443, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7443, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7443, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7448, "X-axis translation", -59.0d), + new EpsgOperationParameterRecord(7448, "Y-axis translation", -11.0d), + new EpsgOperationParameterRecord(7448, "Z-axis translation", -52.0d), + new EpsgOperationParameterRecord(7449, "X-axis translation", -72.0d), + new EpsgOperationParameterRecord(7449, "Y-axis translation", 10.0d), + new EpsgOperationParameterRecord(7449, "Z-axis translation", -32.0d), + new EpsgOperationParameterRecord(7653, "Vertical Offset", -0.87d), + new EpsgOperationParameterRecord(7654, "Vertical Offset", -3.0d), + new EpsgOperationParameterRecord(7666, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7666, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7666, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7666, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7666, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7666, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7666, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7667, "X-axis translation", -4.0d), + new EpsgOperationParameterRecord(7667, "Y-axis translation", 3.0d), + new EpsgOperationParameterRecord(7667, "Z-axis translation", 4.0d), + new EpsgOperationParameterRecord(7667, "X-axis rotation", 0.27d), + new EpsgOperationParameterRecord(7667, "Y-axis rotation", -0.27d), + new EpsgOperationParameterRecord(7667, "Z-axis rotation", 0.38d), + new EpsgOperationParameterRecord(7667, "Scale difference", -6.9d), + new EpsgOperationParameterRecord(7668, "X-axis translation", -6.0d), + new EpsgOperationParameterRecord(7668, "Y-axis translation", 5.0d), + new EpsgOperationParameterRecord(7668, "Z-axis translation", 20.0d), + new EpsgOperationParameterRecord(7668, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7668, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7668, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7668, "Scale difference", -4.5d), + new EpsgOperationParameterRecord(7669, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7669, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7669, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7669, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7669, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7669, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7669, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7670, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7670, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7670, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7670, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7670, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7670, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7670, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7672, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7672, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7672, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7672, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7672, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7672, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7672, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7675, "X-axis translation", 577.88891d), + new EpsgOperationParameterRecord(7675, "Y-axis translation", 165.22205d), + new EpsgOperationParameterRecord(7675, "Z-axis translation", 391.18289d), + new EpsgOperationParameterRecord(7675, "X-axis rotation", -4.9145d), + new EpsgOperationParameterRecord(7675, "Y-axis rotation", 0.94729d), + new EpsgOperationParameterRecord(7675, "Z-axis rotation", 13.05098d), + new EpsgOperationParameterRecord(7675, "Scale difference", 7.78664d), + new EpsgOperationParameterRecord(7676, "X-axis translation", 577.88891d), + new EpsgOperationParameterRecord(7676, "Y-axis translation", 165.22205d), + new EpsgOperationParameterRecord(7676, "Z-axis translation", 391.18289d), + new EpsgOperationParameterRecord(7676, "X-axis rotation", -4.9145d), + new EpsgOperationParameterRecord(7676, "Y-axis rotation", 0.94729d), + new EpsgOperationParameterRecord(7676, "Z-axis rotation", 13.05098d), + new EpsgOperationParameterRecord(7676, "Scale difference", 7.78664d), + new EpsgOperationParameterRecord(7697, "X-axis translation", -127.535d), + new EpsgOperationParameterRecord(7697, "Y-axis translation", 113.495d), + new EpsgOperationParameterRecord(7697, "Z-axis translation", -12.7d), + new EpsgOperationParameterRecord(7697, "X-axis rotation", 1.603747d), + new EpsgOperationParameterRecord(7697, "Y-axis rotation", -0.153612d), + new EpsgOperationParameterRecord(7697, "Z-axis rotation", -5.364408d), + new EpsgOperationParameterRecord(7697, "Scale difference", 5.33745d), + new EpsgOperationParameterRecord(7697, "Ordinate 1 of evaluation point", 4854969.728d), + new EpsgOperationParameterRecord(7697, "Ordinate 2 of evaluation point", 2945552.013d), + new EpsgOperationParameterRecord(7697, "Ordinate 3 of evaluation point", 2868447.61d), + new EpsgOperationParameterRecord(7698, "X-axis translation", -32.3841359d), + new EpsgOperationParameterRecord(7698, "Y-axis translation", 180.4090461d), + new EpsgOperationParameterRecord(7698, "Z-axis translation", 120.8442577d), + new EpsgOperationParameterRecord(7698, "X-axis rotation", 2.1545854d), + new EpsgOperationParameterRecord(7698, "Y-axis rotation", 0.1498782d), + new EpsgOperationParameterRecord(7698, "Z-axis rotation", -0.5742915d), + new EpsgOperationParameterRecord(7698, "Scale difference", 8.1049164d), + new EpsgOperationParameterRecord(7701, "Ordinate 1 of evaluation point", 56.9666666666669d), + new EpsgOperationParameterRecord(7701, "Ordinate 2 of evaluation point", 24.8833333333336d), + new EpsgOperationParameterRecord(7701, "Vertical Offset", 0.003d), + new EpsgOperationParameterRecord(7701, "Inclination in latitude", 0.0d), + new EpsgOperationParameterRecord(7701, "Inclination in longitude", 0.0074d), + new EpsgOperationParameterRecord(7701, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(7702, "X-axis translation", -1.07d), + new EpsgOperationParameterRecord(7702, "Y-axis translation", -0.03d), + new EpsgOperationParameterRecord(7702, "Z-axis translation", 0.02d), + new EpsgOperationParameterRecord(7702, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7702, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7702, "Z-axis rotation", -130.0d), + new EpsgOperationParameterRecord(7702, "Scale difference", -0.22d), + new EpsgOperationParameterRecord(7702, "Transformation reference epoch", 2002.0d), + new EpsgOperationParameterRecord(7703, "X-axis translation", -0.373d), + new EpsgOperationParameterRecord(7703, "Y-axis translation", 0.186d), + new EpsgOperationParameterRecord(7703, "Z-axis translation", 0.202d), + new EpsgOperationParameterRecord(7703, "X-axis rotation", -2.3d), + new EpsgOperationParameterRecord(7703, "Y-axis rotation", 3.54d), + new EpsgOperationParameterRecord(7703, "Z-axis rotation", -4.21d), + new EpsgOperationParameterRecord(7703, "Scale difference", -0.008d), + new EpsgOperationParameterRecord(7703, "Transformation reference epoch", 2010.0d), + new EpsgOperationParameterRecord(7704, "X-axis translation", -1.443d), + new EpsgOperationParameterRecord(7704, "Y-axis translation", 0.156d), + new EpsgOperationParameterRecord(7704, "Z-axis translation", 0.222d), + new EpsgOperationParameterRecord(7704, "X-axis rotation", -2.3d), + new EpsgOperationParameterRecord(7704, "Y-axis rotation", 3.54d), + new EpsgOperationParameterRecord(7704, "Z-axis rotation", -134.21d), + new EpsgOperationParameterRecord(7704, "Scale difference", -0.228d), + new EpsgOperationParameterRecord(7705, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7705, "Y-axis translation", 0.014d), + new EpsgOperationParameterRecord(7705, "Z-axis translation", -0.008d), + new EpsgOperationParameterRecord(7705, "X-axis rotation", -0.562d), + new EpsgOperationParameterRecord(7705, "Y-axis rotation", -0.019d), + new EpsgOperationParameterRecord(7705, "Z-axis rotation", 0.053d), + new EpsgOperationParameterRecord(7705, "Scale difference", -0.0006d), + new EpsgOperationParameterRecord(7705, "Transformation reference epoch", 2011.0d), + new EpsgOperationParameterRecord(7720, "X-axis translation", 8.846d), + new EpsgOperationParameterRecord(7720, "Y-axis translation", -4.394d), + new EpsgOperationParameterRecord(7720, "Z-axis translation", -1.122d), + new EpsgOperationParameterRecord(7720, "X-axis rotation", 0.00237d), + new EpsgOperationParameterRecord(7720, "Y-axis rotation", 0.146528d), + new EpsgOperationParameterRecord(7720, "Z-axis rotation", -0.130428d), + new EpsgOperationParameterRecord(7720, "Scale difference", 0.783926d), + new EpsgOperationParameterRecord(7721, "X-axis translation", 8.846d), + new EpsgOperationParameterRecord(7721, "Y-axis translation", -4.394d), + new EpsgOperationParameterRecord(7721, "Z-axis translation", -1.122d), + new EpsgOperationParameterRecord(7721, "X-axis rotation", 0.00237d), + new EpsgOperationParameterRecord(7721, "Y-axis rotation", 0.146528d), + new EpsgOperationParameterRecord(7721, "Z-axis rotation", -0.130428d), + new EpsgOperationParameterRecord(7721, "Scale difference", 0.783926d), + new EpsgOperationParameterRecord(7790, "X-axis translation", -1.6d), + new EpsgOperationParameterRecord(7790, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(7790, "Z-axis translation", -2.4d), + new EpsgOperationParameterRecord(7790, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7790, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7790, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7790, "Scale difference", 0.02d), + new EpsgOperationParameterRecord(7790, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7790, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7790, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(7790, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7790, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7790, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7790, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(7790, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(7806, "X-axis translation", 5.0d), + new EpsgOperationParameterRecord(7806, "Y-axis translation", -133.0d), + new EpsgOperationParameterRecord(7806, "Z-axis translation", -104.0d), + new EpsgOperationParameterRecord(7806, "X-axis rotation", -1.4d), + new EpsgOperationParameterRecord(7806, "Y-axis rotation", -2.0d), + new EpsgOperationParameterRecord(7806, "Z-axis rotation", 3.4d), + new EpsgOperationParameterRecord(7806, "Scale difference", -3.9901d), + new EpsgOperationParameterRecord(7806, "Ordinate 1 of evaluation point", 4223032.0d), + new EpsgOperationParameterRecord(7806, "Ordinate 2 of evaluation point", 2032778.0d), + new EpsgOperationParameterRecord(7806, "Ordinate 3 of evaluation point", 4309209.0d), + new EpsgOperationParameterRecord(7807, "X-axis translation", 0.99343d), + new EpsgOperationParameterRecord(7807, "Y-axis translation", -1.90331d), + new EpsgOperationParameterRecord(7807, "Z-axis translation", -0.52655d), + new EpsgOperationParameterRecord(7807, "X-axis rotation", 25.91467d), + new EpsgOperationParameterRecord(7807, "Y-axis rotation", 9.42645d), + new EpsgOperationParameterRecord(7807, "Z-axis rotation", 11.59935d), + new EpsgOperationParameterRecord(7807, "Scale difference", 1.71504d), + new EpsgOperationParameterRecord(7807, "Rate of change of X-axis translation", 0.00079d), + new EpsgOperationParameterRecord(7807, "Rate of change of Y-axis translation", -0.0006d), + new EpsgOperationParameterRecord(7807, "Rate of change of Z-axis translation", -0.00134d), + new EpsgOperationParameterRecord(7807, "Rate of change of X-axis rotation", 0.06667d), + new EpsgOperationParameterRecord(7807, "Rate of change of Y-axis rotation", -0.75744d), + new EpsgOperationParameterRecord(7807, "Rate of change of Z-axis rotation", -0.05133d), + new EpsgOperationParameterRecord(7807, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(7807, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(7808, "X-axis translation", 0.908d), + new EpsgOperationParameterRecord(7808, "Y-axis translation", -2.0161d), + new EpsgOperationParameterRecord(7808, "Z-axis translation", -0.5653d), + new EpsgOperationParameterRecord(7808, "X-axis rotation", 27.741d), + new EpsgOperationParameterRecord(7808, "Y-axis rotation", 13.469d), + new EpsgOperationParameterRecord(7808, "Z-axis rotation", 2.712d), + new EpsgOperationParameterRecord(7808, "Scale difference", 1.1d), + new EpsgOperationParameterRecord(7808, "Rate of change of X-axis translation", 0.0001d), + new EpsgOperationParameterRecord(7808, "Rate of change of Y-axis translation", 0.0001d), + new EpsgOperationParameterRecord(7808, "Rate of change of Z-axis translation", -0.0018d), + new EpsgOperationParameterRecord(7808, "Rate of change of X-axis rotation", -0.384d), + new EpsgOperationParameterRecord(7808, "Rate of change of Y-axis rotation", 1.007d), + new EpsgOperationParameterRecord(7808, "Rate of change of Z-axis rotation", -2.186d), + new EpsgOperationParameterRecord(7808, "Rate of change of scale difference", 0.08d), + new EpsgOperationParameterRecord(7808, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(7809, "X-axis translation", 0.908d), + new EpsgOperationParameterRecord(7809, "Y-axis translation", -2.0161d), + new EpsgOperationParameterRecord(7809, "Z-axis translation", -0.5653d), + new EpsgOperationParameterRecord(7809, "X-axis rotation", 28.971d), + new EpsgOperationParameterRecord(7809, "Y-axis rotation", 10.42d), + new EpsgOperationParameterRecord(7809, "Z-axis rotation", 8.928d), + new EpsgOperationParameterRecord(7809, "Scale difference", 1.1d), + new EpsgOperationParameterRecord(7809, "Rate of change of X-axis translation", 0.0001d), + new EpsgOperationParameterRecord(7809, "Rate of change of Y-axis translation", 0.0001d), + new EpsgOperationParameterRecord(7809, "Rate of change of Z-axis translation", -0.0018d), + new EpsgOperationParameterRecord(7809, "Rate of change of X-axis rotation", -0.02d), + new EpsgOperationParameterRecord(7809, "Rate of change of Y-axis rotation", 0.105d), + new EpsgOperationParameterRecord(7809, "Rate of change of Z-axis rotation", -0.347d), + new EpsgOperationParameterRecord(7809, "Rate of change of scale difference", 0.08d), + new EpsgOperationParameterRecord(7809, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(7814, "X-axis translation", -2.97d), + new EpsgOperationParameterRecord(7814, "Y-axis translation", -4.75d), + new EpsgOperationParameterRecord(7814, "Z-axis translation", 7.39d), + new EpsgOperationParameterRecord(7814, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7814, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7814, "Z-axis rotation", 0.18d), + new EpsgOperationParameterRecord(7814, "Scale difference", -5.85d), + new EpsgOperationParameterRecord(7814, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7814, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(7814, "Rate of change of Z-axis translation", 0.14d), + new EpsgOperationParameterRecord(7814, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7814, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7814, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(7814, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7814, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(7817, "X-axis translation", 24.322d), + new EpsgOperationParameterRecord(7817, "Y-axis translation", -121.372d), + new EpsgOperationParameterRecord(7817, "Z-axis translation", -75.847d), + new EpsgOperationParameterRecord(7833, "X-axis translation", -44.183d), + new EpsgOperationParameterRecord(7833, "Y-axis translation", -0.58d), + new EpsgOperationParameterRecord(7833, "Z-axis translation", -38.489d), + new EpsgOperationParameterRecord(7833, "X-axis rotation", -2.3867d), + new EpsgOperationParameterRecord(7833, "Y-axis rotation", -2.7072d), + new EpsgOperationParameterRecord(7833, "Z-axis rotation", 3.5196d), + new EpsgOperationParameterRecord(7833, "Scale difference", -8.2703d), + new EpsgOperationParameterRecord(7834, "X-axis translation", -44.183d), + new EpsgOperationParameterRecord(7834, "Y-axis translation", -0.58d), + new EpsgOperationParameterRecord(7834, "Z-axis translation", -38.489d), + new EpsgOperationParameterRecord(7834, "X-axis rotation", -2.3867d), + new EpsgOperationParameterRecord(7834, "Y-axis rotation", -2.7072d), + new EpsgOperationParameterRecord(7834, "Z-axis rotation", 3.5196d), + new EpsgOperationParameterRecord(7834, "Scale difference", -8.2703d), + new EpsgOperationParameterRecord(7835, "X-axis translation", 74.5d), + new EpsgOperationParameterRecord(7835, "Y-axis translation", -112.5d), + new EpsgOperationParameterRecord(7835, "Z-axis translation", -44.3d), + new EpsgOperationParameterRecord(7836, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7836, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7836, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7838, "Ordinate 1 of evaluation point", 51.0500000000003d), + new EpsgOperationParameterRecord(7838, "Ordinate 2 of evaluation point", 10.2166666666669d), + new EpsgOperationParameterRecord(7838, "Vertical Offset", 0.014d), + new EpsgOperationParameterRecord(7838, "Inclination in latitude", -0.01d), + new EpsgOperationParameterRecord(7838, "Inclination in longitude", 0.0d), + new EpsgOperationParameterRecord(7838, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(7860, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7861, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7862, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7863, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7864, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7865, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7866, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7867, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7868, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7869, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7870, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7871, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7872, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(7873, "Vertical Offset", -1.58d), + new EpsgOperationParameterRecord(7874, "Vertical Offset", -0.93d), + new EpsgOperationParameterRecord(7892, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7892, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7892, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7893, "X-axis translation", -323.65d), + new EpsgOperationParameterRecord(7893, "Y-axis translation", 551.39d), + new EpsgOperationParameterRecord(7893, "Z-axis translation", -491.22d), + new EpsgOperationParameterRecord(7894, "X-axis translation", -323.65d), + new EpsgOperationParameterRecord(7894, "Y-axis translation", 551.39d), + new EpsgOperationParameterRecord(7894, "Z-axis translation", -491.22d), + new EpsgOperationParameterRecord(7895, "X-axis translation", -112.854d), + new EpsgOperationParameterRecord(7895, "Y-axis translation", 12.27d), + new EpsgOperationParameterRecord(7895, "Z-axis translation", -18.913d), + new EpsgOperationParameterRecord(7895, "X-axis rotation", 2.1692d), + new EpsgOperationParameterRecord(7895, "Y-axis rotation", 16.8896d), + new EpsgOperationParameterRecord(7895, "Z-axis rotation", 17.1961d), + new EpsgOperationParameterRecord(7895, "Scale difference", -19.54517d), + new EpsgOperationParameterRecord(7897, "X-axis translation", -0.077d), + new EpsgOperationParameterRecord(7897, "Y-axis translation", 0.079d), + new EpsgOperationParameterRecord(7897, "Z-axis translation", 0.086d), + new EpsgOperationParameterRecord(7898, "X-axis translation", -0.077d), + new EpsgOperationParameterRecord(7898, "Y-axis translation", 0.079d), + new EpsgOperationParameterRecord(7898, "Z-axis translation", 0.086d), + new EpsgOperationParameterRecord(7913, "Latitude of natural origin", 49.0d), + new EpsgOperationParameterRecord(7913, "Longitude of natural origin", -2.0d), + new EpsgOperationParameterRecord(7913, "Scale factor at natural origin", 0.9996012717d), + new EpsgOperationParameterRecord(7913, "False easting", 400000.0d), + new EpsgOperationParameterRecord(7913, "False northing", -100000.0d), + new EpsgOperationParameterRecord(7932, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7932, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7932, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7932, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7932, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7932, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7932, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7932, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7932, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7932, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7932, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(7932, "Rate of change of Y-axis rotation", 0.57d), + new EpsgOperationParameterRecord(7932, "Rate of change of Z-axis rotation", -0.71d), + new EpsgOperationParameterRecord(7932, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7932, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7933, "X-axis translation", 1.9d), + new EpsgOperationParameterRecord(7933, "Y-axis translation", 2.8d), + new EpsgOperationParameterRecord(7933, "Z-axis translation", -2.3d), + new EpsgOperationParameterRecord(7933, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7933, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7933, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7933, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7933, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7933, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7933, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7933, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(7933, "Rate of change of Y-axis rotation", 0.57d), + new EpsgOperationParameterRecord(7933, "Rate of change of Z-axis rotation", -0.71d), + new EpsgOperationParameterRecord(7933, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7933, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7934, "X-axis translation", 2.1d), + new EpsgOperationParameterRecord(7934, "Y-axis translation", 2.5d), + new EpsgOperationParameterRecord(7934, "Z-axis translation", -3.7d), + new EpsgOperationParameterRecord(7934, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7934, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7934, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7934, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7934, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7934, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7934, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7934, "Rate of change of X-axis rotation", 0.21d), + new EpsgOperationParameterRecord(7934, "Rate of change of Y-axis rotation", 0.52d), + new EpsgOperationParameterRecord(7934, "Rate of change of Z-axis rotation", -0.68d), + new EpsgOperationParameterRecord(7934, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7934, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7935, "X-axis translation", 3.8d), + new EpsgOperationParameterRecord(7935, "Y-axis translation", 4.0d), + new EpsgOperationParameterRecord(7935, "Z-axis translation", -3.7d), + new EpsgOperationParameterRecord(7935, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7935, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7935, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7935, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7935, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7935, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7935, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7935, "Rate of change of X-axis rotation", 0.21d), + new EpsgOperationParameterRecord(7935, "Rate of change of Y-axis rotation", 0.52d), + new EpsgOperationParameterRecord(7935, "Rate of change of Z-axis rotation", -0.68d), + new EpsgOperationParameterRecord(7935, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7935, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7936, "X-axis translation", 1.9d), + new EpsgOperationParameterRecord(7936, "Y-axis translation", 5.3d), + new EpsgOperationParameterRecord(7936, "Z-axis translation", -2.1d), + new EpsgOperationParameterRecord(7936, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7936, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7936, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7936, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7936, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7936, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7936, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7936, "Rate of change of X-axis rotation", 0.32d), + new EpsgOperationParameterRecord(7936, "Rate of change of Y-axis rotation", 0.78d), + new EpsgOperationParameterRecord(7936, "Rate of change of Z-axis rotation", -0.67d), + new EpsgOperationParameterRecord(7936, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7936, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7937, "X-axis translation", 4.1d), + new EpsgOperationParameterRecord(7937, "Y-axis translation", 4.1d), + new EpsgOperationParameterRecord(7937, "Z-axis translation", -4.9d), + new EpsgOperationParameterRecord(7937, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7937, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7937, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7937, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7937, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7937, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7937, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7937, "Rate of change of X-axis rotation", 0.2d), + new EpsgOperationParameterRecord(7937, "Rate of change of Y-axis rotation", 0.5d), + new EpsgOperationParameterRecord(7937, "Rate of change of Z-axis rotation", -0.65d), + new EpsgOperationParameterRecord(7937, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7937, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7938, "X-axis translation", 4.1d), + new EpsgOperationParameterRecord(7938, "Y-axis translation", 4.1d), + new EpsgOperationParameterRecord(7938, "Z-axis translation", -4.9d), + new EpsgOperationParameterRecord(7938, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7938, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7938, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7938, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7938, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7938, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7938, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7938, "Rate of change of X-axis rotation", 0.2d), + new EpsgOperationParameterRecord(7938, "Rate of change of Y-axis rotation", 0.5d), + new EpsgOperationParameterRecord(7938, "Rate of change of Z-axis rotation", -0.65d), + new EpsgOperationParameterRecord(7938, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7938, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7939, "X-axis translation", 4.1d), + new EpsgOperationParameterRecord(7939, "Y-axis translation", 4.1d), + new EpsgOperationParameterRecord(7939, "Z-axis translation", -4.9d), + new EpsgOperationParameterRecord(7939, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7939, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7939, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7939, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7939, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7939, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7939, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7939, "Rate of change of X-axis rotation", 0.2d), + new EpsgOperationParameterRecord(7939, "Rate of change of Y-axis rotation", 0.5d), + new EpsgOperationParameterRecord(7939, "Rate of change of Z-axis rotation", -0.65d), + new EpsgOperationParameterRecord(7939, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7939, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7940, "X-axis translation", 5.4d), + new EpsgOperationParameterRecord(7940, "Y-axis translation", 5.1d), + new EpsgOperationParameterRecord(7940, "Z-axis translation", -4.8d), + new EpsgOperationParameterRecord(7940, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7940, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7940, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7940, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7940, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7940, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7940, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7940, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7940, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7940, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(7940, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7940, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(7941, "X-axis translation", 54.0d), + new EpsgOperationParameterRecord(7941, "Y-axis translation", 51.0d), + new EpsgOperationParameterRecord(7941, "Z-axis translation", -48.0d), + new EpsgOperationParameterRecord(7941, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7941, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7941, "Z-axis rotation", -8.712d), + new EpsgOperationParameterRecord(7941, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7941, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7941, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(7941, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7941, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7941, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7941, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(7941, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(7941, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7942, "X-axis translation", 24.3d), + new EpsgOperationParameterRecord(7942, "Y-axis translation", 10.7d), + new EpsgOperationParameterRecord(7942, "Z-axis translation", 42.7d), + new EpsgOperationParameterRecord(7942, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7942, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7942, "Z-axis rotation", -8.772d), + new EpsgOperationParameterRecord(7942, "Scale difference", -5.97d), + new EpsgOperationParameterRecord(7942, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7942, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(7942, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(7942, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7942, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7942, "Rate of change of Z-axis rotation", -0.812d), + new EpsgOperationParameterRecord(7942, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7942, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7943, "X-axis translation", 29.3d), + new EpsgOperationParameterRecord(7943, "Y-axis translation", 34.7d), + new EpsgOperationParameterRecord(7943, "Z-axis translation", 4.7d), + new EpsgOperationParameterRecord(7943, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7943, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7943, "Z-axis rotation", -8.772d), + new EpsgOperationParameterRecord(7943, "Scale difference", -2.57d), + new EpsgOperationParameterRecord(7943, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7943, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(7943, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(7943, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7943, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7943, "Rate of change of Z-axis rotation", -0.812d), + new EpsgOperationParameterRecord(7943, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7943, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7944, "X-axis translation", 27.3d), + new EpsgOperationParameterRecord(7944, "Y-axis translation", 30.7d), + new EpsgOperationParameterRecord(7944, "Z-axis translation", -11.3d), + new EpsgOperationParameterRecord(7944, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7944, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7944, "Z-axis rotation", -8.772d), + new EpsgOperationParameterRecord(7944, "Scale difference", -2.27d), + new EpsgOperationParameterRecord(7944, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7944, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(7944, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(7944, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7944, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7944, "Rate of change of Z-axis rotation", -0.812d), + new EpsgOperationParameterRecord(7944, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7944, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7945, "X-axis translation", 39.3d), + new EpsgOperationParameterRecord(7945, "Y-axis translation", 44.7d), + new EpsgOperationParameterRecord(7945, "Z-axis translation", -17.3d), + new EpsgOperationParameterRecord(7945, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7945, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7945, "Z-axis rotation", -8.772d), + new EpsgOperationParameterRecord(7945, "Scale difference", -0.87d), + new EpsgOperationParameterRecord(7945, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7945, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(7945, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(7945, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7945, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7945, "Rate of change of Z-axis rotation", -0.812d), + new EpsgOperationParameterRecord(7945, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7945, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7946, "X-axis translation", 76.1d), + new EpsgOperationParameterRecord(7946, "Y-axis translation", 46.9d), + new EpsgOperationParameterRecord(7946, "Z-axis translation", -19.9d), + new EpsgOperationParameterRecord(7946, "X-axis rotation", 2.601d), + new EpsgOperationParameterRecord(7946, "Y-axis rotation", 6.87d), + new EpsgOperationParameterRecord(7946, "Z-axis rotation", -8.412d), + new EpsgOperationParameterRecord(7946, "Scale difference", -2.07d), + new EpsgOperationParameterRecord(7946, "Rate of change of X-axis translation", 2.9d), + new EpsgOperationParameterRecord(7946, "Rate of change of Y-axis translation", 0.2d), + new EpsgOperationParameterRecord(7946, "Rate of change of Z-axis translation", 0.6d), + new EpsgOperationParameterRecord(7946, "Rate of change of X-axis rotation", 0.191d), + new EpsgOperationParameterRecord(7946, "Rate of change of Y-axis rotation", 0.68d), + new EpsgOperationParameterRecord(7946, "Rate of change of Z-axis rotation", -0.862d), + new EpsgOperationParameterRecord(7946, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7946, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7947, "X-axis translation", 47.3d), + new EpsgOperationParameterRecord(7947, "Y-axis translation", 46.7d), + new EpsgOperationParameterRecord(7947, "Z-axis translation", -25.3d), + new EpsgOperationParameterRecord(7947, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7947, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7947, "Z-axis rotation", -8.772d), + new EpsgOperationParameterRecord(7947, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(7947, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7947, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(7947, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(7947, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7947, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7947, "Rate of change of Z-axis rotation", -0.812d), + new EpsgOperationParameterRecord(7947, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7947, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7948, "X-axis translation", 47.3d), + new EpsgOperationParameterRecord(7948, "Y-axis translation", 46.7d), + new EpsgOperationParameterRecord(7948, "Z-axis translation", -25.3d), + new EpsgOperationParameterRecord(7948, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7948, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7948, "Z-axis rotation", -8.772d), + new EpsgOperationParameterRecord(7948, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(7948, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7948, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(7948, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(7948, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7948, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7948, "Rate of change of Z-axis rotation", -0.812d), + new EpsgOperationParameterRecord(7948, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7948, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7949, "X-axis translation", 47.3d), + new EpsgOperationParameterRecord(7949, "Y-axis translation", 46.7d), + new EpsgOperationParameterRecord(7949, "Z-axis translation", -25.3d), + new EpsgOperationParameterRecord(7949, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7949, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7949, "Z-axis rotation", -8.772d), + new EpsgOperationParameterRecord(7949, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(7949, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(7949, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(7949, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(7949, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7949, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7949, "Rate of change of Z-axis rotation", -0.812d), + new EpsgOperationParameterRecord(7949, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(7949, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7950, "X-axis translation", 54.1d), + new EpsgOperationParameterRecord(7950, "Y-axis translation", 50.2d), + new EpsgOperationParameterRecord(7950, "Z-axis translation", -53.8d), + new EpsgOperationParameterRecord(7950, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7950, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7950, "Z-axis rotation", -8.712d), + new EpsgOperationParameterRecord(7950, "Scale difference", 0.4d), + new EpsgOperationParameterRecord(7950, "Rate of change of X-axis translation", -0.2d), + new EpsgOperationParameterRecord(7950, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(7950, "Rate of change of Z-axis translation", -1.8d), + new EpsgOperationParameterRecord(7950, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7950, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7950, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(7950, "Rate of change of scale difference", 0.08d), + new EpsgOperationParameterRecord(7950, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7951, "X-axis translation", 52.1d), + new EpsgOperationParameterRecord(7951, "Y-axis translation", 49.3d), + new EpsgOperationParameterRecord(7951, "Z-axis translation", -58.5d), + new EpsgOperationParameterRecord(7951, "X-axis rotation", 0.891d), + new EpsgOperationParameterRecord(7951, "Y-axis rotation", 5.39d), + new EpsgOperationParameterRecord(7951, "Z-axis rotation", -8.712d), + new EpsgOperationParameterRecord(7951, "Scale difference", 1.34d), + new EpsgOperationParameterRecord(7951, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(7951, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(7951, "Rate of change of Z-axis translation", -1.8d), + new EpsgOperationParameterRecord(7951, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(7951, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(7951, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(7951, "Rate of change of scale difference", 0.08d), + new EpsgOperationParameterRecord(7951, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(7952, "Latitude of natural origin", 49.0d), + new EpsgOperationParameterRecord(7952, "Longitude of natural origin", -2.0d), + new EpsgOperationParameterRecord(7952, "Scale factor at natural origin", 0.9996012717d), + new EpsgOperationParameterRecord(7952, "False easting", 400000.0d), + new EpsgOperationParameterRecord(7952, "False northing", -100000.0d), + new EpsgOperationParameterRecord(7953, "Latitude of natural origin", 49.0d), + new EpsgOperationParameterRecord(7953, "Longitude of natural origin", -2.0d), + new EpsgOperationParameterRecord(7953, "Scale factor at natural origin", 0.9996012717d), + new EpsgOperationParameterRecord(7953, "False easting", 400000.0d), + new EpsgOperationParameterRecord(7953, "False northing", -100000.0d), + new EpsgOperationParameterRecord(7960, "X-axis translation", -0.003d), + new EpsgOperationParameterRecord(7960, "Y-axis translation", -0.001d), + new EpsgOperationParameterRecord(7960, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(7960, "X-axis rotation", 0.019d), + new EpsgOperationParameterRecord(7960, "Y-axis rotation", -0.042d), + new EpsgOperationParameterRecord(7960, "Z-axis rotation", 0.002d), + new EpsgOperationParameterRecord(7960, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7960, "Transformation reference epoch", 2010.0d), + new EpsgOperationParameterRecord(7961, "X-axis translation", 0.36d), + new EpsgOperationParameterRecord(7961, "Y-axis translation", -0.08d), + new EpsgOperationParameterRecord(7961, "Z-axis translation", -0.18d), + new EpsgOperationParameterRecord(7961, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7961, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7961, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(7961, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(7961, "Transformation reference epoch", 2002.0d), + new EpsgOperationParameterRecord(7964, "Vertical Offset", -2.7d), + new EpsgOperationParameterRecord(7966, "Vertical Offset", -2.7d), + new EpsgOperationParameterRecord(7977, "Vertical Offset", -0.146d), + new EpsgOperationParameterRecord(7980, "Vertical Offset", -4.74d), + new EpsgOperationParameterRecord(7981, "Vertical Offset", -4.25d), + new EpsgOperationParameterRecord(8048, "X-axis translation", 61.55d), + new EpsgOperationParameterRecord(8048, "Y-axis translation", -10.87d), + new EpsgOperationParameterRecord(8048, "Z-axis translation", -40.19d), + new EpsgOperationParameterRecord(8048, "X-axis rotation", -39.4924d), + new EpsgOperationParameterRecord(8048, "Y-axis rotation", -32.7221d), + new EpsgOperationParameterRecord(8048, "Z-axis rotation", -32.8979d), + new EpsgOperationParameterRecord(8048, "Scale difference", -9.994d), + new EpsgOperationParameterRecord(8049, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8049, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8049, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8049, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8049, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8049, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8049, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8049, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8049, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8049, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8049, "Rate of change of X-axis rotation", 1.50379d), + new EpsgOperationParameterRecord(8049, "Rate of change of Y-axis rotation", 1.18346d), + new EpsgOperationParameterRecord(8049, "Rate of change of Z-axis rotation", 1.20716d), + new EpsgOperationParameterRecord(8049, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(8049, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(8069, "X-axis translation", -25.4d), + new EpsgOperationParameterRecord(8069, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8069, "Z-axis translation", 154.8d), + new EpsgOperationParameterRecord(8069, "X-axis rotation", -0.1d), + new EpsgOperationParameterRecord(8069, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8069, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8069, "Scale difference", -11.29d), + new EpsgOperationParameterRecord(8069, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8069, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8069, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8069, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8069, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8069, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8069, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8069, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8070, "X-axis translation", -30.4d), + new EpsgOperationParameterRecord(8070, "Y-axis translation", -35.5d), + new EpsgOperationParameterRecord(8070, "Z-axis translation", 130.8d), + new EpsgOperationParameterRecord(8070, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8070, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8070, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8070, "Scale difference", -8.19d), + new EpsgOperationParameterRecord(8070, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8070, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8070, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8070, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8070, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8070, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8070, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8070, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8071, "X-axis translation", -25.4d), + new EpsgOperationParameterRecord(8071, "Y-axis translation", -11.5d), + new EpsgOperationParameterRecord(8071, "Z-axis translation", 92.8d), + new EpsgOperationParameterRecord(8071, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8071, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8071, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8071, "Scale difference", -4.79d), + new EpsgOperationParameterRecord(8071, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8071, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8071, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8071, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8071, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8071, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8071, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8071, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8072, "X-axis translation", -27.4d), + new EpsgOperationParameterRecord(8072, "Y-axis translation", -15.5d), + new EpsgOperationParameterRecord(8072, "Z-axis translation", 76.8d), + new EpsgOperationParameterRecord(8072, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8072, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8072, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8072, "Scale difference", -4.49d), + new EpsgOperationParameterRecord(8072, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8072, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8072, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8072, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8072, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8072, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8072, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8072, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8073, "X-axis translation", -15.4d), + new EpsgOperationParameterRecord(8073, "Y-axis translation", -1.5d), + new EpsgOperationParameterRecord(8073, "Z-axis translation", 70.8d), + new EpsgOperationParameterRecord(8073, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8073, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8073, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8073, "Scale difference", -3.09d), + new EpsgOperationParameterRecord(8073, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8073, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8073, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8073, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8073, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8073, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8073, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8073, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8074, "X-axis translation", 50.4d), + new EpsgOperationParameterRecord(8074, "Y-axis translation", -3.3d), + new EpsgOperationParameterRecord(8074, "Z-axis translation", 60.2d), + new EpsgOperationParameterRecord(8074, "X-axis rotation", 2.81d), + new EpsgOperationParameterRecord(8074, "Y-axis rotation", 3.38d), + new EpsgOperationParameterRecord(8074, "Z-axis rotation", -0.4d), + new EpsgOperationParameterRecord(8074, "Scale difference", -4.29d), + new EpsgOperationParameterRecord(8074, "Rate of change of X-axis translation", 2.8d), + new EpsgOperationParameterRecord(8074, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(8074, "Rate of change of Z-axis translation", 2.5d), + new EpsgOperationParameterRecord(8074, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(8074, "Rate of change of Y-axis rotation", 0.19d), + new EpsgOperationParameterRecord(8074, "Rate of change of Z-axis rotation", -0.07d), + new EpsgOperationParameterRecord(8074, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8074, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8075, "X-axis translation", -7.4d), + new EpsgOperationParameterRecord(8075, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8075, "Z-axis translation", 62.8d), + new EpsgOperationParameterRecord(8075, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8075, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8075, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8075, "Scale difference", -3.8d), + new EpsgOperationParameterRecord(8075, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8075, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8075, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8075, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8075, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8075, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8075, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8075, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8076, "X-axis translation", -7.4d), + new EpsgOperationParameterRecord(8076, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8076, "Z-axis translation", 62.8d), + new EpsgOperationParameterRecord(8076, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8076, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8076, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8076, "Scale difference", -3.8d), + new EpsgOperationParameterRecord(8076, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8076, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8076, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8076, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8076, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8076, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8076, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8076, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8077, "X-axis translation", -7.4d), + new EpsgOperationParameterRecord(8077, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8077, "Z-axis translation", 62.8d), + new EpsgOperationParameterRecord(8077, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8077, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8077, "Z-axis rotation", -0.26d), + new EpsgOperationParameterRecord(8077, "Scale difference", -3.8d), + new EpsgOperationParameterRecord(8077, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8077, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8077, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8077, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8077, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8077, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(8077, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8077, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8078, "X-axis translation", -0.7d), + new EpsgOperationParameterRecord(8078, "Y-axis translation", -1.2d), + new EpsgOperationParameterRecord(8078, "Z-axis translation", 26.1d), + new EpsgOperationParameterRecord(8078, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8078, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8078, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8078, "Scale difference", -2.12d), + new EpsgOperationParameterRecord(8078, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8078, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(8078, "Rate of change of Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8078, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8078, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8078, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8078, "Rate of change of scale difference", -0.11d), + new EpsgOperationParameterRecord(8078, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8079, "X-axis translation", -2.6d), + new EpsgOperationParameterRecord(8079, "Y-axis translation", -1.0d), + new EpsgOperationParameterRecord(8079, "Z-axis translation", 2.3d), + new EpsgOperationParameterRecord(8079, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8079, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8079, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8079, "Scale difference", -0.92d), + new EpsgOperationParameterRecord(8079, "Rate of change of X-axis translation", -0.3d), + new EpsgOperationParameterRecord(8079, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8079, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(8079, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8079, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8079, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8079, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(8079, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8256, "X-axis translation", 0.936d), + new EpsgOperationParameterRecord(8256, "Y-axis translation", -1.984d), + new EpsgOperationParameterRecord(8256, "Z-axis translation", -0.543d), + new EpsgOperationParameterRecord(8256, "X-axis rotation", -27.5d), + new EpsgOperationParameterRecord(8256, "Y-axis rotation", -15.5d), + new EpsgOperationParameterRecord(8256, "Z-axis rotation", -10.7d), + new EpsgOperationParameterRecord(8256, "Scale difference", 5.0d), + new EpsgOperationParameterRecord(8256, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8256, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8256, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8256, "Rate of change of X-axis rotation", -0.052d), + new EpsgOperationParameterRecord(8256, "Rate of change of Y-axis rotation", 0.742d), + new EpsgOperationParameterRecord(8256, "Rate of change of Z-axis rotation", 0.032d), + new EpsgOperationParameterRecord(8256, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(8256, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(8257, "X-axis translation", 0.94d), + new EpsgOperationParameterRecord(8257, "Y-axis translation", -1.979d), + new EpsgOperationParameterRecord(8257, "Z-axis translation", -0.534d), + new EpsgOperationParameterRecord(8257, "X-axis rotation", -27.09d), + new EpsgOperationParameterRecord(8257, "Y-axis rotation", -16.22d), + new EpsgOperationParameterRecord(8257, "Z-axis rotation", -9.87d), + new EpsgOperationParameterRecord(8257, "Scale difference", 4.1d), + new EpsgOperationParameterRecord(8257, "Rate of change of X-axis translation", 0.0023d), + new EpsgOperationParameterRecord(8257, "Rate of change of Y-axis translation", 0.0004d), + new EpsgOperationParameterRecord(8257, "Rate of change of Z-axis translation", -0.0008d), + new EpsgOperationParameterRecord(8257, "Rate of change of X-axis rotation", 0.078d), + new EpsgOperationParameterRecord(8257, "Rate of change of Y-axis rotation", 0.962d), + new EpsgOperationParameterRecord(8257, "Rate of change of Z-axis rotation", -0.008d), + new EpsgOperationParameterRecord(8257, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(8257, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(8258, "X-axis translation", 0.942d), + new EpsgOperationParameterRecord(8258, "Y-axis translation", -1.979d), + new EpsgOperationParameterRecord(8258, "Z-axis translation", -0.534d), + new EpsgOperationParameterRecord(8258, "X-axis rotation", -27.3d), + new EpsgOperationParameterRecord(8258, "Y-axis rotation", -15.4d), + new EpsgOperationParameterRecord(8258, "Z-axis rotation", -10.7d), + new EpsgOperationParameterRecord(8258, "Scale difference", 4.9d), + new EpsgOperationParameterRecord(8258, "Rate of change of X-axis translation", -0.0004d), + new EpsgOperationParameterRecord(8258, "Rate of change of Y-axis translation", 0.0004d), + new EpsgOperationParameterRecord(8258, "Rate of change of Z-axis translation", -0.0008d), + new EpsgOperationParameterRecord(8258, "Rate of change of X-axis rotation", -0.052d), + new EpsgOperationParameterRecord(8258, "Rate of change of Y-axis rotation", 0.762d), + new EpsgOperationParameterRecord(8258, "Rate of change of Z-axis rotation", 0.032d), + new EpsgOperationParameterRecord(8258, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(8258, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(8259, "X-axis translation", 0.991d), + new EpsgOperationParameterRecord(8259, "Y-axis translation", -1.9072d), + new EpsgOperationParameterRecord(8259, "Z-axis translation", -0.5129d), + new EpsgOperationParameterRecord(8259, "X-axis rotation", -25.79d), + new EpsgOperationParameterRecord(8259, "Y-axis rotation", -9.65d), + new EpsgOperationParameterRecord(8259, "Z-axis rotation", -11.66d), + new EpsgOperationParameterRecord(8259, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8259, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8259, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8259, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8259, "Rate of change of X-axis rotation", -0.0532d), + new EpsgOperationParameterRecord(8259, "Rate of change of Y-axis rotation", 0.7423d), + new EpsgOperationParameterRecord(8259, "Rate of change of Z-axis rotation", 0.0316d), + new EpsgOperationParameterRecord(8259, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(8259, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(8260, "X-axis translation", 0.9889d), + new EpsgOperationParameterRecord(8260, "Y-axis translation", -1.9074d), + new EpsgOperationParameterRecord(8260, "Z-axis translation", -0.503d), + new EpsgOperationParameterRecord(8260, "X-axis rotation", -25.915d), + new EpsgOperationParameterRecord(8260, "Y-axis rotation", -9.426d), + new EpsgOperationParameterRecord(8260, "Z-axis rotation", -11.599d), + new EpsgOperationParameterRecord(8260, "Scale difference", -0.935d), + new EpsgOperationParameterRecord(8260, "Rate of change of X-axis translation", 0.0007d), + new EpsgOperationParameterRecord(8260, "Rate of change of Y-axis translation", -0.0001d), + new EpsgOperationParameterRecord(8260, "Rate of change of Z-axis translation", 0.0019d), + new EpsgOperationParameterRecord(8260, "Rate of change of X-axis rotation", -0.067d), + new EpsgOperationParameterRecord(8260, "Rate of change of Y-axis rotation", 0.757d), + new EpsgOperationParameterRecord(8260, "Rate of change of Z-axis rotation", 0.031d), + new EpsgOperationParameterRecord(8260, "Rate of change of scale difference", -0.192d), + new EpsgOperationParameterRecord(8260, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(8261, "X-axis translation", 0.9956d), + new EpsgOperationParameterRecord(8261, "Y-axis translation", -1.9013d), + new EpsgOperationParameterRecord(8261, "Z-axis translation", -0.5214d), + new EpsgOperationParameterRecord(8261, "X-axis rotation", -25.915d), + new EpsgOperationParameterRecord(8261, "Y-axis rotation", -9.426d), + new EpsgOperationParameterRecord(8261, "Z-axis rotation", -11.599d), + new EpsgOperationParameterRecord(8261, "Scale difference", 0.615d), + new EpsgOperationParameterRecord(8261, "Rate of change of X-axis translation", 0.0007d), + new EpsgOperationParameterRecord(8261, "Rate of change of Y-axis translation", -0.0007d), + new EpsgOperationParameterRecord(8261, "Rate of change of Z-axis translation", 0.0005d), + new EpsgOperationParameterRecord(8261, "Rate of change of X-axis rotation", -0.067d), + new EpsgOperationParameterRecord(8261, "Rate of change of Y-axis rotation", 0.757d), + new EpsgOperationParameterRecord(8261, "Rate of change of Z-axis rotation", 0.051d), + new EpsgOperationParameterRecord(8261, "Rate of change of scale difference", -0.182d), + new EpsgOperationParameterRecord(8261, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(8264, "X-axis translation", 0.99343d), + new EpsgOperationParameterRecord(8264, "Y-axis translation", -1.90331d), + new EpsgOperationParameterRecord(8264, "Z-axis translation", -0.52655d), + new EpsgOperationParameterRecord(8264, "X-axis rotation", -25.91467d), + new EpsgOperationParameterRecord(8264, "Y-axis rotation", -9.42645d), + new EpsgOperationParameterRecord(8264, "Z-axis rotation", -11.59935d), + new EpsgOperationParameterRecord(8264, "Scale difference", 1.71504d), + new EpsgOperationParameterRecord(8264, "Rate of change of X-axis translation", 0.00079d), + new EpsgOperationParameterRecord(8264, "Rate of change of Y-axis translation", -0.0006d), + new EpsgOperationParameterRecord(8264, "Rate of change of Z-axis translation", -0.00134d), + new EpsgOperationParameterRecord(8264, "Rate of change of X-axis rotation", -0.06667d), + new EpsgOperationParameterRecord(8264, "Rate of change of Y-axis rotation", 0.75744d), + new EpsgOperationParameterRecord(8264, "Rate of change of Z-axis rotation", 0.05133d), + new EpsgOperationParameterRecord(8264, "Rate of change of scale difference", -0.102d), + new EpsgOperationParameterRecord(8264, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(8265, "X-axis translation", 1.0053d), + new EpsgOperationParameterRecord(8265, "Y-axis translation", -1.90921d), + new EpsgOperationParameterRecord(8265, "Z-axis translation", -0.54157d), + new EpsgOperationParameterRecord(8265, "X-axis rotation", -26.78138d), + new EpsgOperationParameterRecord(8265, "Y-axis rotation", 0.42027d), + new EpsgOperationParameterRecord(8265, "Z-axis rotation", -10.93206d), + new EpsgOperationParameterRecord(8265, "Scale difference", 0.36891d), + new EpsgOperationParameterRecord(8265, "Rate of change of X-axis translation", 0.00079d), + new EpsgOperationParameterRecord(8265, "Rate of change of Y-axis translation", -0.0006d), + new EpsgOperationParameterRecord(8265, "Rate of change of Z-axis translation", -0.00144d), + new EpsgOperationParameterRecord(8265, "Rate of change of X-axis rotation", -0.06667d), + new EpsgOperationParameterRecord(8265, "Rate of change of Y-axis rotation", 0.75744d), + new EpsgOperationParameterRecord(8265, "Rate of change of Z-axis rotation", 0.05133d), + new EpsgOperationParameterRecord(8265, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(8265, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8270, "X-axis translation", 11.363d), + new EpsgOperationParameterRecord(8270, "Y-axis translation", 424.148d), + new EpsgOperationParameterRecord(8270, "Z-axis translation", 373.13d), + new EpsgOperationParameterRecord(8361, "EPSG code for Interpolation CRS", 11076.0d), + new EpsgOperationParameterRecord(8362, "EPSG code for Interpolation CRS", 11076.0d), + new EpsgOperationParameterRecord(8365, "X-axis translation", -485.014055d), + new EpsgOperationParameterRecord(8365, "Y-axis translation", -169.473618d), + new EpsgOperationParameterRecord(8365, "Z-axis translation", -483.842943d), + new EpsgOperationParameterRecord(8365, "X-axis rotation", 7.78625453d), + new EpsgOperationParameterRecord(8365, "Y-axis rotation", 4.39770887d), + new EpsgOperationParameterRecord(8365, "Z-axis rotation", 4.10248899d), + new EpsgOperationParameterRecord(8365, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8366, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8366, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8366, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8366, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8366, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8366, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8366, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8366, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8366, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8366, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8366, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8366, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8366, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(8366, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(8366, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(8367, "X-axis translation", 485.021d), + new EpsgOperationParameterRecord(8367, "Y-axis translation", 169.465d), + new EpsgOperationParameterRecord(8367, "Z-axis translation", 483.839d), + new EpsgOperationParameterRecord(8367, "X-axis rotation", -7.786342d), + new EpsgOperationParameterRecord(8367, "Y-axis rotation", -4.397554d), + new EpsgOperationParameterRecord(8367, "Z-axis rotation", -4.102655d), + new EpsgOperationParameterRecord(8367, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8368, "X-axis translation", 485.021d), + new EpsgOperationParameterRecord(8368, "Y-axis translation", 169.465d), + new EpsgOperationParameterRecord(8368, "Z-axis translation", 483.839d), + new EpsgOperationParameterRecord(8368, "X-axis rotation", -7.786342d), + new EpsgOperationParameterRecord(8368, "Y-axis rotation", -4.397554d), + new EpsgOperationParameterRecord(8368, "Z-axis rotation", -4.102655d), + new EpsgOperationParameterRecord(8368, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8405, "X-axis translation", 54.7d), + new EpsgOperationParameterRecord(8405, "Y-axis translation", 52.2d), + new EpsgOperationParameterRecord(8405, "Z-axis translation", -74.1d), + new EpsgOperationParameterRecord(8405, "X-axis rotation", 1.701d), + new EpsgOperationParameterRecord(8405, "Y-axis rotation", 10.29d), + new EpsgOperationParameterRecord(8405, "Z-axis rotation", -16.632d), + new EpsgOperationParameterRecord(8405, "Scale difference", 2.12d), + new EpsgOperationParameterRecord(8405, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(8405, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(8405, "Rate of change of Z-axis translation", -1.9d), + new EpsgOperationParameterRecord(8405, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(8405, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(8405, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(8405, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(8405, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8435, "X-axis translation", 202.865d), + new EpsgOperationParameterRecord(8435, "Y-axis translation", 303.99d), + new EpsgOperationParameterRecord(8435, "Z-axis translation", 155.873d), + new EpsgOperationParameterRecord(8435, "X-axis rotation", 34.067d), + new EpsgOperationParameterRecord(8435, "Y-axis rotation", -76.126d), + new EpsgOperationParameterRecord(8435, "Z-axis rotation", -32.647d), + new EpsgOperationParameterRecord(8435, "Scale difference", -6.096d), + new EpsgOperationParameterRecord(8435, "Ordinate 1 of evaluation point", -2361757.652d), + new EpsgOperationParameterRecord(8435, "Ordinate 2 of evaluation point", 5417232.187d), + new EpsgOperationParameterRecord(8435, "Ordinate 3 of evaluation point", 2391453.053d), + new EpsgOperationParameterRecord(8436, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8436, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8436, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8438, "X-axis translation", -202.865d), + new EpsgOperationParameterRecord(8438, "Y-axis translation", -303.99d), + new EpsgOperationParameterRecord(8438, "Z-axis translation", -155.873d), + new EpsgOperationParameterRecord(8438, "X-axis rotation", -34.079d), + new EpsgOperationParameterRecord(8438, "Y-axis rotation", 76.126d), + new EpsgOperationParameterRecord(8438, "Z-axis rotation", 32.66d), + new EpsgOperationParameterRecord(8438, "Scale difference", 6.096d), + new EpsgOperationParameterRecord(8438, "Ordinate 1 of evaluation point", -2361554.788d), + new EpsgOperationParameterRecord(8438, "Ordinate 2 of evaluation point", 5417536.177d), + new EpsgOperationParameterRecord(8438, "Ordinate 3 of evaluation point", 2391608.926d), + new EpsgOperationParameterRecord(8439, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8439, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8439, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8448, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8448, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8448, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8448, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8448, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8448, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8448, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8448, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8448, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8448, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8448, "Rate of change of X-axis rotation", -1.50379d), + new EpsgOperationParameterRecord(8448, "Rate of change of Y-axis rotation", -1.18346d), + new EpsgOperationParameterRecord(8448, "Rate of change of Z-axis rotation", -1.20716d), + new EpsgOperationParameterRecord(8448, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(8448, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(8450, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8450, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8450, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8452, "X-axis translation", -377.0d), + new EpsgOperationParameterRecord(8452, "Y-axis translation", 681.0d), + new EpsgOperationParameterRecord(8452, "Z-axis translation", -50.0d), + new EpsgOperationParameterRecord(8674, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8674, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8674, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8680, "X-axis translation", 489.88d), + new EpsgOperationParameterRecord(8680, "Y-axis translation", 183.912d), + new EpsgOperationParameterRecord(8680, "Z-axis translation", 533.711d), + new EpsgOperationParameterRecord(8680, "X-axis rotation", 5.76545d), + new EpsgOperationParameterRecord(8680, "Y-axis rotation", 4.69994d), + new EpsgOperationParameterRecord(8680, "Z-axis rotation", -12.58211d), + new EpsgOperationParameterRecord(8680, "Scale difference", 1.00646d), + new EpsgOperationParameterRecord(8688, "X-axis translation", 476.08d), + new EpsgOperationParameterRecord(8688, "Y-axis translation", 125.947d), + new EpsgOperationParameterRecord(8688, "Z-axis translation", 417.81d), + new EpsgOperationParameterRecord(8688, "X-axis rotation", -4.610862d), + new EpsgOperationParameterRecord(8688, "Y-axis rotation", -2.388137d), + new EpsgOperationParameterRecord(8688, "Z-axis rotation", 11.942335d), + new EpsgOperationParameterRecord(8688, "Scale difference", 9.896638d), + new EpsgOperationParameterRecord(8689, "X-axis translation", 476.08d), + new EpsgOperationParameterRecord(8689, "Y-axis translation", 125.947d), + new EpsgOperationParameterRecord(8689, "Z-axis translation", 417.81d), + new EpsgOperationParameterRecord(8689, "X-axis rotation", -4.610862d), + new EpsgOperationParameterRecord(8689, "Y-axis rotation", -2.388137d), + new EpsgOperationParameterRecord(8689, "Z-axis rotation", 11.942335d), + new EpsgOperationParameterRecord(8689, "Scale difference", 9.896638d), + new EpsgOperationParameterRecord(8695, "X-axis translation", 42.899d), + new EpsgOperationParameterRecord(8695, "Y-axis translation", -214.863d), + new EpsgOperationParameterRecord(8695, "Z-axis translation", -11.927d), + new EpsgOperationParameterRecord(8695, "X-axis rotation", -1.844d), + new EpsgOperationParameterRecord(8695, "Y-axis rotation", 0.648d), + new EpsgOperationParameterRecord(8695, "Z-axis rotation", -6.37d), + new EpsgOperationParameterRecord(8695, "Scale difference", 0.169d), + new EpsgOperationParameterRecord(8696, "X-axis translation", 45.799d), + new EpsgOperationParameterRecord(8696, "Y-axis translation", -212.263d), + new EpsgOperationParameterRecord(8696, "Z-axis translation", -11.927d), + new EpsgOperationParameterRecord(8696, "X-axis rotation", -1.844d), + new EpsgOperationParameterRecord(8696, "Y-axis rotation", 0.648d), + new EpsgOperationParameterRecord(8696, "Z-axis rotation", -6.37d), + new EpsgOperationParameterRecord(8696, "Scale difference", 0.169d), + new EpsgOperationParameterRecord(8819, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8819, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8819, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8822, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8822, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8822, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8823, "X-axis translation", 489.88d), + new EpsgOperationParameterRecord(8823, "Y-axis translation", 183.912d), + new EpsgOperationParameterRecord(8823, "Z-axis translation", 533.711d), + new EpsgOperationParameterRecord(8823, "X-axis rotation", 5.76545d), + new EpsgOperationParameterRecord(8823, "Y-axis rotation", 4.69994d), + new EpsgOperationParameterRecord(8823, "Z-axis rotation", -12.58211d), + new EpsgOperationParameterRecord(8823, "Scale difference", 1.00646d), + new EpsgOperationParameterRecord(8824, "X-axis translation", -61.15d), + new EpsgOperationParameterRecord(8824, "Y-axis translation", -315.86d), + new EpsgOperationParameterRecord(8824, "Z-axis translation", -3.51d), + new EpsgOperationParameterRecord(8824, "X-axis rotation", 0.41d), + new EpsgOperationParameterRecord(8824, "Y-axis rotation", 0.74d), + new EpsgOperationParameterRecord(8824, "Z-axis rotation", -3.52d), + new EpsgOperationParameterRecord(8824, "Scale difference", 1.36d), + new EpsgOperationParameterRecord(8827, "X-axis translation", -93.799d), + new EpsgOperationParameterRecord(8827, "Y-axis translation", -132.737d), + new EpsgOperationParameterRecord(8827, "Z-axis translation", -219.073d), + new EpsgOperationParameterRecord(8827, "X-axis rotation", 1.844d), + new EpsgOperationParameterRecord(8827, "Y-axis rotation", -0.648d), + new EpsgOperationParameterRecord(8827, "Z-axis rotation", 6.37d), + new EpsgOperationParameterRecord(8827, "Scale difference", -0.169d), + new EpsgOperationParameterRecord(8828, "X-axis translation", 0.072d), + new EpsgOperationParameterRecord(8828, "Y-axis translation", -0.507d), + new EpsgOperationParameterRecord(8828, "Z-axis translation", -0.245d), + new EpsgOperationParameterRecord(8828, "X-axis rotation", 0.0183d), + new EpsgOperationParameterRecord(8828, "Y-axis rotation", -0.0003d), + new EpsgOperationParameterRecord(8828, "Z-axis rotation", 0.007d), + new EpsgOperationParameterRecord(8828, "Scale difference", -0.0093d), + new EpsgOperationParameterRecord(8829, "X-axis translation", 221.525d), + new EpsgOperationParameterRecord(8829, "Y-axis translation", 152.948d), + new EpsgOperationParameterRecord(8829, "Z-axis translation", 176.768d), + new EpsgOperationParameterRecord(8829, "X-axis rotation", 2.3847d), + new EpsgOperationParameterRecord(8829, "Y-axis rotation", 1.3896d), + new EpsgOperationParameterRecord(8829, "Z-axis rotation", 0.877d), + new EpsgOperationParameterRecord(8829, "Scale difference", 11.4741d), + new EpsgOperationParameterRecord(8830, "X-axis translation", 221.597d), + new EpsgOperationParameterRecord(8830, "Y-axis translation", 152.441d), + new EpsgOperationParameterRecord(8830, "Z-axis translation", 176.523d), + new EpsgOperationParameterRecord(8830, "X-axis rotation", 2.403d), + new EpsgOperationParameterRecord(8830, "Y-axis rotation", 1.3893d), + new EpsgOperationParameterRecord(8830, "Z-axis rotation", 0.884d), + new EpsgOperationParameterRecord(8830, "Scale difference", 11.4648d), + new EpsgOperationParameterRecord(8831, "X-axis translation", 218.697d), + new EpsgOperationParameterRecord(8831, "Y-axis translation", 151.257d), + new EpsgOperationParameterRecord(8831, "Z-axis translation", 176.995d), + new EpsgOperationParameterRecord(8831, "X-axis rotation", 3.5048d), + new EpsgOperationParameterRecord(8831, "Y-axis rotation", 2.004d), + new EpsgOperationParameterRecord(8831, "Z-axis rotation", 1.281d), + new EpsgOperationParameterRecord(8831, "Scale difference", 10.991d), + new EpsgOperationParameterRecord(8832, "X-axis translation", 218.769d), + new EpsgOperationParameterRecord(8832, "Y-axis translation", 150.75d), + new EpsgOperationParameterRecord(8832, "Z-axis translation", 176.75d), + new EpsgOperationParameterRecord(8832, "X-axis rotation", 3.5231d), + new EpsgOperationParameterRecord(8832, "Y-axis rotation", 2.0037d), + new EpsgOperationParameterRecord(8832, "Z-axis rotation", 1.288d), + new EpsgOperationParameterRecord(8832, "Scale difference", 10.9817d), + new EpsgOperationParameterRecord(8833, "X-axis translation", 72.438d), + new EpsgOperationParameterRecord(8833, "Y-axis translation", 345.918d), + new EpsgOperationParameterRecord(8833, "Z-axis translation", 79.486d), + new EpsgOperationParameterRecord(8833, "X-axis rotation", -1.6045d), + new EpsgOperationParameterRecord(8833, "Y-axis rotation", -0.8823d), + new EpsgOperationParameterRecord(8833, "Z-axis rotation", -0.5565d), + new EpsgOperationParameterRecord(8833, "Scale difference", 1.3746d), + new EpsgOperationParameterRecord(8834, "X-axis translation", 72.51d), + new EpsgOperationParameterRecord(8834, "Y-axis translation", 345.411d), + new EpsgOperationParameterRecord(8834, "Z-axis translation", 79.241d), + new EpsgOperationParameterRecord(8834, "X-axis rotation", -1.5862d), + new EpsgOperationParameterRecord(8834, "Y-axis rotation", -0.8826d), + new EpsgOperationParameterRecord(8834, "Z-axis rotation", -0.5495d), + new EpsgOperationParameterRecord(8834, "Scale difference", 1.3653d), + new EpsgOperationParameterRecord(8835, "X-axis translation", 347.103d), + new EpsgOperationParameterRecord(8835, "Y-axis translation", 1078.125d), + new EpsgOperationParameterRecord(8835, "Z-axis translation", 2623.922d), + new EpsgOperationParameterRecord(8835, "X-axis rotation", 33.8875d), + new EpsgOperationParameterRecord(8835, "Y-axis rotation", -70.6773d), + new EpsgOperationParameterRecord(8835, "Z-axis rotation", 9.3943d), + new EpsgOperationParameterRecord(8835, "Scale difference", 186.074d), + new EpsgOperationParameterRecord(8842, "X-axis translation", 347.175d), + new EpsgOperationParameterRecord(8842, "Y-axis translation", 1077.618d), + new EpsgOperationParameterRecord(8842, "Z-axis translation", 2623.677d), + new EpsgOperationParameterRecord(8842, "X-axis rotation", 33.9058d), + new EpsgOperationParameterRecord(8842, "Y-axis rotation", -70.6776d), + new EpsgOperationParameterRecord(8842, "Z-axis rotation", 9.4013d), + new EpsgOperationParameterRecord(8842, "Scale difference", 186.0647d), + new EpsgOperationParameterRecord(8843, "X-axis translation", 410.721d), + new EpsgOperationParameterRecord(8843, "Y-axis translation", 55.049d), + new EpsgOperationParameterRecord(8843, "Z-axis translation", 80.746d), + new EpsgOperationParameterRecord(8843, "X-axis rotation", -2.5779d), + new EpsgOperationParameterRecord(8843, "Y-axis rotation", -2.3514d), + new EpsgOperationParameterRecord(8843, "Z-axis rotation", -0.6664d), + new EpsgOperationParameterRecord(8843, "Scale difference", 17.3311d), + new EpsgOperationParameterRecord(8844, "X-axis translation", 410.793d), + new EpsgOperationParameterRecord(8844, "Y-axis translation", 54.542d), + new EpsgOperationParameterRecord(8844, "Z-axis translation", 80.501d), + new EpsgOperationParameterRecord(8844, "X-axis rotation", -2.5596d), + new EpsgOperationParameterRecord(8844, "Y-axis rotation", -2.3517d), + new EpsgOperationParameterRecord(8844, "Z-axis rotation", -0.6594d), + new EpsgOperationParameterRecord(8844, "Scale difference", 17.3218d), + new EpsgOperationParameterRecord(8845, "X-axis translation", 374.715d), + new EpsgOperationParameterRecord(8845, "Y-axis translation", -58.407d), + new EpsgOperationParameterRecord(8845, "Z-axis translation", -0.957d), + new EpsgOperationParameterRecord(8845, "X-axis rotation", -16.2111d), + new EpsgOperationParameterRecord(8845, "Y-axis rotation", -11.4626d), + new EpsgOperationParameterRecord(8845, "Z-axis rotation", -5.5357d), + new EpsgOperationParameterRecord(8845, "Scale difference", -0.5409d), + new EpsgOperationParameterRecord(8846, "X-axis translation", 374.787d), + new EpsgOperationParameterRecord(8846, "Y-axis translation", -58.914d), + new EpsgOperationParameterRecord(8846, "Z-axis translation", -1.202d), + new EpsgOperationParameterRecord(8846, "X-axis rotation", -16.1928d), + new EpsgOperationParameterRecord(8846, "Y-axis rotation", -11.4629d), + new EpsgOperationParameterRecord(8846, "Z-axis rotation", -5.5287d), + new EpsgOperationParameterRecord(8846, "Scale difference", -0.5502d), + new EpsgOperationParameterRecord(8847, "X-axis translation", 165.732d), + new EpsgOperationParameterRecord(8847, "Y-axis translation", 216.72d), + new EpsgOperationParameterRecord(8847, "Z-axis translation", 180.505d), + new EpsgOperationParameterRecord(8847, "X-axis rotation", -0.6434d), + new EpsgOperationParameterRecord(8847, "Y-axis rotation", -0.4512d), + new EpsgOperationParameterRecord(8847, "Z-axis rotation", -0.0791d), + new EpsgOperationParameterRecord(8847, "Scale difference", 7.4204d), + new EpsgOperationParameterRecord(8848, "X-axis translation", 165.804d), + new EpsgOperationParameterRecord(8848, "Y-axis translation", 216.213d), + new EpsgOperationParameterRecord(8848, "Z-axis translation", 180.26d), + new EpsgOperationParameterRecord(8848, "X-axis rotation", -0.6251d), + new EpsgOperationParameterRecord(8848, "Y-axis rotation", -0.4515d), + new EpsgOperationParameterRecord(8848, "Z-axis rotation", -0.0721d), + new EpsgOperationParameterRecord(8848, "Scale difference", 7.4111d), + new EpsgOperationParameterRecord(8849, "X-axis translation", 1363.785d), + new EpsgOperationParameterRecord(8849, "Y-axis translation", 1362.687d), + new EpsgOperationParameterRecord(8849, "Z-axis translation", 398.811d), + new EpsgOperationParameterRecord(8849, "X-axis rotation", -4.5322d), + new EpsgOperationParameterRecord(8849, "Y-axis rotation", -6.7579d), + new EpsgOperationParameterRecord(8849, "Z-axis rotation", -1.0574d), + new EpsgOperationParameterRecord(8849, "Scale difference", 268.361d), + new EpsgOperationParameterRecord(8850, "X-axis translation", 1363.857d), + new EpsgOperationParameterRecord(8850, "Y-axis translation", 1362.18d), + new EpsgOperationParameterRecord(8850, "Z-axis translation", 398.566d), + new EpsgOperationParameterRecord(8850, "X-axis rotation", -4.5139d), + new EpsgOperationParameterRecord(8850, "Y-axis rotation", -6.7582d), + new EpsgOperationParameterRecord(8850, "Z-axis rotation", -1.0504d), + new EpsgOperationParameterRecord(8850, "Scale difference", 268.3517d), + new EpsgOperationParameterRecord(8851, "X-axis translation", 259.551d), + new EpsgOperationParameterRecord(8851, "Y-axis translation", 297.612d), + new EpsgOperationParameterRecord(8851, "Z-axis translation", 197.833d), + new EpsgOperationParameterRecord(8851, "X-axis rotation", 1.4866d), + new EpsgOperationParameterRecord(8851, "Y-axis rotation", 2.1224d), + new EpsgOperationParameterRecord(8851, "Z-axis rotation", 0.4612d), + new EpsgOperationParameterRecord(8851, "Scale difference", 27.0249d), + new EpsgOperationParameterRecord(8852, "X-axis translation", 259.623d), + new EpsgOperationParameterRecord(8852, "Y-axis translation", 297.105d), + new EpsgOperationParameterRecord(8852, "Z-axis translation", 197.588d), + new EpsgOperationParameterRecord(8852, "X-axis rotation", 1.5049d), + new EpsgOperationParameterRecord(8852, "Y-axis rotation", 2.1221d), + new EpsgOperationParameterRecord(8852, "Z-axis rotation", 0.4682d), + new EpsgOperationParameterRecord(8852, "Scale difference", 27.0156d), + new EpsgOperationParameterRecord(8853, "X-axis translation", 217.109d), + new EpsgOperationParameterRecord(8853, "Y-axis translation", 86.452d), + new EpsgOperationParameterRecord(8853, "Z-axis translation", 23.711d), + new EpsgOperationParameterRecord(8853, "X-axis rotation", 0.0183d), + new EpsgOperationParameterRecord(8853, "Y-axis rotation", -0.0003d), + new EpsgOperationParameterRecord(8853, "Z-axis rotation", 0.007d), + new EpsgOperationParameterRecord(8853, "Scale difference", -0.0093d), + new EpsgOperationParameterRecord(8869, "X-axis translation", -1.6d), + new EpsgOperationParameterRecord(8869, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(8869, "Z-axis translation", -2.4d), + new EpsgOperationParameterRecord(8869, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8869, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8869, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(8869, "Scale difference", 0.02d), + new EpsgOperationParameterRecord(8869, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8869, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8869, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(8869, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8869, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8869, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(8869, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(8869, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8870, "X-axis translation", -2.6d), + new EpsgOperationParameterRecord(8870, "Y-axis translation", -1.0d), + new EpsgOperationParameterRecord(8870, "Z-axis translation", 2.3d), + new EpsgOperationParameterRecord(8870, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8870, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8870, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(8870, "Scale difference", -0.92d), + new EpsgOperationParameterRecord(8870, "Rate of change of X-axis translation", -0.3d), + new EpsgOperationParameterRecord(8870, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8870, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(8870, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8870, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8870, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(8870, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(8870, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8871, "X-axis translation", -0.7d), + new EpsgOperationParameterRecord(8871, "Y-axis translation", -1.2d), + new EpsgOperationParameterRecord(8871, "Z-axis translation", 26.1d), + new EpsgOperationParameterRecord(8871, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8871, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8871, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(8871, "Scale difference", -2.12d), + new EpsgOperationParameterRecord(8871, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8871, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(8871, "Rate of change of Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8871, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8871, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8871, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(8871, "Rate of change of scale difference", -0.11d), + new EpsgOperationParameterRecord(8871, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8872, "X-axis translation", -7.4d), + new EpsgOperationParameterRecord(8872, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8872, "Z-axis translation", 62.8d), + new EpsgOperationParameterRecord(8872, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8872, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8872, "Z-axis rotation", -16.43d), + new EpsgOperationParameterRecord(8872, "Scale difference", -3.8d), + new EpsgOperationParameterRecord(8872, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8872, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8872, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8872, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8872, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8872, "Rate of change of Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(8872, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8872, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8873, "X-axis translation", -7.4d), + new EpsgOperationParameterRecord(8873, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8873, "Z-axis translation", 62.8d), + new EpsgOperationParameterRecord(8873, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8873, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8873, "Z-axis rotation", -16.43d), + new EpsgOperationParameterRecord(8873, "Scale difference", -3.8d), + new EpsgOperationParameterRecord(8873, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8873, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8873, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8873, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8873, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8873, "Rate of change of Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(8873, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8873, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8874, "X-axis translation", -7.4d), + new EpsgOperationParameterRecord(8874, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8874, "Z-axis translation", 62.8d), + new EpsgOperationParameterRecord(8874, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8874, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8874, "Z-axis rotation", -16.43d), + new EpsgOperationParameterRecord(8874, "Scale difference", -3.8d), + new EpsgOperationParameterRecord(8874, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8874, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8874, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8874, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8874, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8874, "Rate of change of Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(8874, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8874, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8875, "X-axis translation", 50.4d), + new EpsgOperationParameterRecord(8875, "Y-axis translation", -3.3d), + new EpsgOperationParameterRecord(8875, "Z-axis translation", 60.2d), + new EpsgOperationParameterRecord(8875, "X-axis rotation", 4.595d), + new EpsgOperationParameterRecord(8875, "Y-axis rotation", 14.531d), + new EpsgOperationParameterRecord(8875, "Z-axis rotation", -16.57d), + new EpsgOperationParameterRecord(8875, "Scale difference", -4.29d), + new EpsgOperationParameterRecord(8875, "Rate of change of X-axis translation", 2.8d), + new EpsgOperationParameterRecord(8875, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(8875, "Rate of change of Z-axis translation", 2.5d), + new EpsgOperationParameterRecord(8875, "Rate of change of X-axis rotation", 0.195d), + new EpsgOperationParameterRecord(8875, "Rate of change of Y-axis rotation", 0.721d), + new EpsgOperationParameterRecord(8875, "Rate of change of Z-axis rotation", -0.84d), + new EpsgOperationParameterRecord(8875, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8875, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8876, "X-axis translation", -15.4d), + new EpsgOperationParameterRecord(8876, "Y-axis translation", -1.5d), + new EpsgOperationParameterRecord(8876, "Z-axis translation", 70.8d), + new EpsgOperationParameterRecord(8876, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8876, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8876, "Z-axis rotation", -16.43d), + new EpsgOperationParameterRecord(8876, "Scale difference", -3.09d), + new EpsgOperationParameterRecord(8876, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8876, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8876, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8876, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8876, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8876, "Rate of change of Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(8876, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8876, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8877, "X-axis translation", -27.4d), + new EpsgOperationParameterRecord(8877, "Y-axis translation", -15.5d), + new EpsgOperationParameterRecord(8877, "Z-axis translation", 76.8d), + new EpsgOperationParameterRecord(8877, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8877, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8877, "Z-axis rotation", -16.43d), + new EpsgOperationParameterRecord(8877, "Scale difference", -4.49d), + new EpsgOperationParameterRecord(8877, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8877, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8877, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8877, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8877, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8877, "Rate of change of Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(8877, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8877, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8878, "X-axis translation", -25.4d), + new EpsgOperationParameterRecord(8878, "Y-axis translation", -11.5d), + new EpsgOperationParameterRecord(8878, "Z-axis translation", 92.8d), + new EpsgOperationParameterRecord(8878, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8878, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8878, "Z-axis rotation", -16.43d), + new EpsgOperationParameterRecord(8878, "Scale difference", -4.79d), + new EpsgOperationParameterRecord(8878, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8878, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8878, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8878, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8878, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8878, "Rate of change of Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(8878, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8878, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8879, "X-axis translation", -30.4d), + new EpsgOperationParameterRecord(8879, "Y-axis translation", -35.5d), + new EpsgOperationParameterRecord(8879, "Z-axis translation", 130.8d), + new EpsgOperationParameterRecord(8879, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8879, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8879, "Z-axis rotation", -16.43d), + new EpsgOperationParameterRecord(8879, "Scale difference", -8.19d), + new EpsgOperationParameterRecord(8879, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(8879, "Rate of change of Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(8879, "Rate of change of Z-axis translation", 3.3d), + new EpsgOperationParameterRecord(8879, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8879, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8879, "Rate of change of Z-axis rotation", -0.79d), + new EpsgOperationParameterRecord(8879, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(8879, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8880, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8880, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8880, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8880, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(8880, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(8880, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(8880, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8880, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8880, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8880, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8880, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(8880, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(8880, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(8880, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(8880, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8882, "X-axis translation", -93.799d), + new EpsgOperationParameterRecord(8882, "Y-axis translation", -132.737d), + new EpsgOperationParameterRecord(8882, "Z-axis translation", -219.073d), + new EpsgOperationParameterRecord(8882, "X-axis rotation", 1.844d), + new EpsgOperationParameterRecord(8882, "Y-axis rotation", -0.648d), + new EpsgOperationParameterRecord(8882, "Z-axis rotation", 6.37d), + new EpsgOperationParameterRecord(8882, "Scale difference", -0.169d), + new EpsgOperationParameterRecord(8883, "X-axis translation", -48.0d), + new EpsgOperationParameterRecord(8883, "Y-axis translation", -345.0d), + new EpsgOperationParameterRecord(8883, "Z-axis translation", -231.0d), + new EpsgOperationParameterRecord(8884, "X-axis translation", -50.9d), + new EpsgOperationParameterRecord(8884, "Y-axis translation", -347.6d), + new EpsgOperationParameterRecord(8884, "Z-axis translation", -231.0d), + new EpsgOperationParameterRecord(8886, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8886, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8886, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8887, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8887, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8887, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8890, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8890, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8890, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8894, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8894, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8894, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8952, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8952, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8952, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8952, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8952, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8952, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8952, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8952, "Transformation reference epoch", 2000.4d), + new EpsgOperationParameterRecord(8953, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8953, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8953, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8953, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8953, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8953, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8953, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8953, "Transformation reference epoch", 2000.0d), + new EpsgOperationParameterRecord(8954, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8954, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8954, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8954, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8954, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8954, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8954, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8954, "Transformation reference epoch", 1998.4d), + new EpsgOperationParameterRecord(8955, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8955, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8955, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8955, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8955, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8955, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8955, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8955, "Transformation reference epoch", 2000.0d), + new EpsgOperationParameterRecord(8956, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8956, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8956, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8956, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8956, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8956, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8956, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8956, "Transformation reference epoch", 2003.0d), + new EpsgOperationParameterRecord(8957, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8957, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8957, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8957, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8957, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8957, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8957, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8957, "Transformation reference epoch", 2004.0d), + new EpsgOperationParameterRecord(8958, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8958, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8958, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8958, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8958, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8958, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8958, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8958, "Transformation reference epoch", 2004.0d), + new EpsgOperationParameterRecord(8959, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8959, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8959, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8959, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8959, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8959, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8959, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8959, "Transformation reference epoch", 2004.5d), + new EpsgOperationParameterRecord(8960, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8960, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8960, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8960, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8960, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8960, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8960, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8960, "Transformation reference epoch", 2004.5d), + new EpsgOperationParameterRecord(8961, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8961, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8961, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8961, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8961, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8961, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8961, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8961, "Transformation reference epoch", 2005.0d), + new EpsgOperationParameterRecord(8962, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8962, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8962, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8962, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8962, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8962, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8962, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8962, "Transformation reference epoch", 2005.0d), + new EpsgOperationParameterRecord(8963, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8963, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8963, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8963, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8963, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8963, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8963, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8963, "Transformation reference epoch", 2005.0d), + new EpsgOperationParameterRecord(8964, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8964, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8964, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8964, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8964, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8964, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8964, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8964, "Transformation reference epoch", 2012.0d), + new EpsgOperationParameterRecord(8965, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8965, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8965, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8965, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8965, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8965, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8965, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8965, "Transformation reference epoch", 2013.0d), + new EpsgOperationParameterRecord(8966, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8966, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8966, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8966, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8966, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8966, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8966, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8966, "Transformation reference epoch", 2013.0d), + new EpsgOperationParameterRecord(8967, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8967, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8967, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8967, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8967, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8967, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8967, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8967, "Transformation reference epoch", 2015.0d), + new EpsgOperationParameterRecord(8970, "X-axis translation", 1.0053d), + new EpsgOperationParameterRecord(8970, "Y-axis translation", -1.90921d), + new EpsgOperationParameterRecord(8970, "Z-axis translation", -0.54157d), + new EpsgOperationParameterRecord(8970, "X-axis rotation", 26.78138d), + new EpsgOperationParameterRecord(8970, "Y-axis rotation", -0.42027d), + new EpsgOperationParameterRecord(8970, "Z-axis rotation", 10.93206d), + new EpsgOperationParameterRecord(8970, "Scale difference", 0.36891d), + new EpsgOperationParameterRecord(8970, "Rate of change of X-axis translation", 0.00079d), + new EpsgOperationParameterRecord(8970, "Rate of change of Y-axis translation", -0.0006d), + new EpsgOperationParameterRecord(8970, "Rate of change of Z-axis translation", -0.00144d), + new EpsgOperationParameterRecord(8970, "Rate of change of X-axis rotation", 0.06667d), + new EpsgOperationParameterRecord(8970, "Rate of change of Y-axis rotation", -0.75744d), + new EpsgOperationParameterRecord(8970, "Rate of change of Z-axis rotation", -0.05133d), + new EpsgOperationParameterRecord(8970, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(8970, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(8971, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8971, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8971, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9020, "X-axis translation", 0.5d), + new EpsgOperationParameterRecord(9020, "Y-axis translation", 3.6d), + new EpsgOperationParameterRecord(9020, "Z-axis translation", 2.4d), + new EpsgOperationParameterRecord(9020, "X-axis rotation", -0.1d), + new EpsgOperationParameterRecord(9020, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9020, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9020, "Scale difference", -3.0d), + new EpsgOperationParameterRecord(9021, "X-axis translation", -0.5d), + new EpsgOperationParameterRecord(9021, "Y-axis translation", -2.4d), + new EpsgOperationParameterRecord(9021, "Z-axis translation", 3.8d), + new EpsgOperationParameterRecord(9021, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9021, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9021, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9021, "Scale difference", -3.0d), + new EpsgOperationParameterRecord(9022, "X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9022, "Y-axis translation", 0.4d), + new EpsgOperationParameterRecord(9022, "Z-axis translation", 1.6d), + new EpsgOperationParameterRecord(9022, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9022, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9022, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9022, "Scale difference", -0.3d), + new EpsgOperationParameterRecord(9023, "X-axis translation", -1.1d), + new EpsgOperationParameterRecord(9023, "Y-axis translation", -1.4d), + new EpsgOperationParameterRecord(9023, "Z-axis translation", 0.6d), + new EpsgOperationParameterRecord(9023, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9023, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9023, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9023, "Scale difference", -1.4d), + new EpsgOperationParameterRecord(9024, "X-axis translation", -0.2d), + new EpsgOperationParameterRecord(9024, "Y-axis translation", -0.7d), + new EpsgOperationParameterRecord(9024, "Z-axis translation", -0.7d), + new EpsgOperationParameterRecord(9024, "X-axis rotation", -0.39d), + new EpsgOperationParameterRecord(9024, "Y-axis rotation", 0.8d), + new EpsgOperationParameterRecord(9024, "Z-axis rotation", -0.96d), + new EpsgOperationParameterRecord(9024, "Scale difference", 1.2d), + new EpsgOperationParameterRecord(9024, "Rate of change of X-axis translation", -0.29d), + new EpsgOperationParameterRecord(9024, "Rate of change of Y-axis translation", 0.04d), + new EpsgOperationParameterRecord(9024, "Rate of change of Z-axis translation", 0.08d), + new EpsgOperationParameterRecord(9024, "Rate of change of X-axis rotation", -0.11d), + new EpsgOperationParameterRecord(9024, "Rate of change of Y-axis rotation", -0.19d), + new EpsgOperationParameterRecord(9024, "Rate of change of Z-axis rotation", 0.05d), + new EpsgOperationParameterRecord(9024, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9024, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(9025, "X-axis translation", -0.6d), + new EpsgOperationParameterRecord(9025, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(9025, "Z-axis translation", 1.5d), + new EpsgOperationParameterRecord(9025, "X-axis rotation", 0.39d), + new EpsgOperationParameterRecord(9025, "Y-axis rotation", -0.8d), + new EpsgOperationParameterRecord(9025, "Z-axis rotation", 0.96d), + new EpsgOperationParameterRecord(9025, "Scale difference", -0.4d), + new EpsgOperationParameterRecord(9025, "Rate of change of X-axis translation", 0.29d), + new EpsgOperationParameterRecord(9025, "Rate of change of Y-axis translation", -0.04d), + new EpsgOperationParameterRecord(9025, "Rate of change of Z-axis translation", -0.08d), + new EpsgOperationParameterRecord(9025, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(9025, "Rate of change of Y-axis rotation", 0.19d), + new EpsgOperationParameterRecord(9025, "Rate of change of Z-axis rotation", -0.05d), + new EpsgOperationParameterRecord(9025, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9025, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(9026, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9026, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9026, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9026, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9026, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9026, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9026, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9026, "Transformation reference epoch", 1988.0d), + new EpsgOperationParameterRecord(9027, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9027, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9027, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9027, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9027, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9027, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9027, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9027, "Transformation reference epoch", 1988.0d), + new EpsgOperationParameterRecord(9028, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9028, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9028, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9028, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9028, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9028, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9028, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9028, "Transformation reference epoch", 1997.0d), + new EpsgOperationParameterRecord(9029, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9029, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9029, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9029, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9029, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9029, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9029, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9029, "Transformation reference epoch", 1998.0d), + new EpsgOperationParameterRecord(9030, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9030, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9030, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9030, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9030, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9030, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9030, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9030, "Transformation reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9031, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9031, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9031, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9031, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9031, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9031, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9031, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9031, "Transformation reference epoch", 2005.0d), + new EpsgOperationParameterRecord(9032, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9032, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9032, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9032, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9032, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9032, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9032, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9032, "Transformation reference epoch", 2010.0d), + new EpsgOperationParameterRecord(9033, "X-axis translation", -6.0d), + new EpsgOperationParameterRecord(9033, "Y-axis translation", -5.6d), + new EpsgOperationParameterRecord(9033, "Z-axis translation", 20.1d), + new EpsgOperationParameterRecord(9033, "X-axis rotation", -0.04d), + new EpsgOperationParameterRecord(9033, "Y-axis rotation", 0.001d), + new EpsgOperationParameterRecord(9033, "Z-axis rotation", 0.043d), + new EpsgOperationParameterRecord(9033, "Scale difference", -1.403d), + new EpsgOperationParameterRecord(9033, "Rate of change of X-axis translation", 0.4d), + new EpsgOperationParameterRecord(9033, "Rate of change of Y-axis translation", 0.8d), + new EpsgOperationParameterRecord(9033, "Rate of change of Z-axis translation", 1.5d), + new EpsgOperationParameterRecord(9033, "Rate of change of X-axis rotation", 0.004d), + new EpsgOperationParameterRecord(9033, "Rate of change of Y-axis rotation", -0.001d), + new EpsgOperationParameterRecord(9033, "Rate of change of Z-axis rotation", -0.003d), + new EpsgOperationParameterRecord(9033, "Rate of change of scale difference", -0.012d), + new EpsgOperationParameterRecord(9033, "Parameter reference epoch", 1998.0d), + new EpsgOperationParameterRecord(9034, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9034, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9034, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9034, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9034, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9034, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9034, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9034, "Transformation reference epoch", 1998.0d), + new EpsgOperationParameterRecord(9035, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9035, "Y-axis translation", 1.7d), + new EpsgOperationParameterRecord(9035, "Z-axis translation", 5.3d), + new EpsgOperationParameterRecord(9035, "X-axis rotation", 0.0224d), + new EpsgOperationParameterRecord(9035, "Y-axis rotation", -0.0341d), + new EpsgOperationParameterRecord(9035, "Z-axis rotation", 0.0099d), + new EpsgOperationParameterRecord(9035, "Scale difference", -0.8473d), + new EpsgOperationParameterRecord(9035, "Rate of change of X-axis translation", 0.4d), + new EpsgOperationParameterRecord(9035, "Rate of change of Y-axis translation", -0.7d), + new EpsgOperationParameterRecord(9035, "Rate of change of Z-axis translation", 1.8d), + new EpsgOperationParameterRecord(9035, "Rate of change of X-axis rotation", -0.0033d), + new EpsgOperationParameterRecord(9035, "Rate of change of Y-axis rotation", 0.0001d), + new EpsgOperationParameterRecord(9035, "Rate of change of Z-axis rotation", 0.0161d), + new EpsgOperationParameterRecord(9035, "Rate of change of scale difference", -0.1748d), + new EpsgOperationParameterRecord(9035, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9036, "X-axis translation", 1.5d), + new EpsgOperationParameterRecord(9036, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9036, "Z-axis translation", 5.8d), + new EpsgOperationParameterRecord(9036, "X-axis rotation", -0.012d), + new EpsgOperationParameterRecord(9036, "Y-axis rotation", 0.014d), + new EpsgOperationParameterRecord(9036, "Z-axis rotation", 0.014d), + new EpsgOperationParameterRecord(9036, "Scale difference", -1.04d), + new EpsgOperationParameterRecord(9036, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9036, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9036, "Rate of change of Z-axis translation", -0.1d), + new EpsgOperationParameterRecord(9036, "Rate of change of X-axis rotation", -0.002d), + new EpsgOperationParameterRecord(9036, "Rate of change of Y-axis rotation", -0.003d), + new EpsgOperationParameterRecord(9036, "Rate of change of Z-axis rotation", 0.001d), + new EpsgOperationParameterRecord(9036, "Rate of change of scale difference", 0.01d), + new EpsgOperationParameterRecord(9036, "Parameter reference epoch", 2005.0d), + new EpsgOperationParameterRecord(9037, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9037, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9037, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9037, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9037, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9037, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9037, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9037, "Transformation reference epoch", 2005.0d), + new EpsgOperationParameterRecord(9038, "X-axis translation", -1.6d), + new EpsgOperationParameterRecord(9038, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(9038, "Z-axis translation", -2.4d), + new EpsgOperationParameterRecord(9038, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9038, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9038, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9038, "Scale difference", 0.02d), + new EpsgOperationParameterRecord(9038, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9038, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9038, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(9038, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9038, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9038, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9038, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(9038, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(9041, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9041, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9041, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9041, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9041, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9041, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9041, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9041, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9041, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9041, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9042, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9042, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9042, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9042, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9042, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9042, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9042, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9042, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9042, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9042, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9042, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9042, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9042, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9042, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9043, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9043, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9043, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9043, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9043, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9043, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9043, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9043, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9043, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9043, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9044, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9044, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9044, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9044, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9044, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9044, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9044, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9044, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9044, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9044, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9044, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9044, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9044, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9044, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9045, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9045, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9045, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9045, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9045, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9045, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9045, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9045, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9045, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9045, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9046, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9046, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9046, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9046, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9046, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9046, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9046, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9046, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9046, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9046, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9046, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9046, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9046, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9046, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9047, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9047, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9047, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9047, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9047, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9047, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9047, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9047, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9047, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9047, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9048, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9048, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9048, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9048, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9048, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9048, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9048, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9048, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9048, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9048, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9048, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9048, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9048, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9048, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9049, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9049, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9049, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9049, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9049, "Latitude of natural origin", 52.0d), + new EpsgOperationParameterRecord(9049, "Longitude of natural origin", 10.0d), + new EpsgOperationParameterRecord(9049, "False easting", 4321000.0d), + new EpsgOperationParameterRecord(9049, "False northing", 3210000.0d), + new EpsgOperationParameterRecord(9049, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9049, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9050, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9050, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9050, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9050, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9050, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9050, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9050, "Latitude of false origin", 52.0d), + new EpsgOperationParameterRecord(9050, "Longitude of false origin", 10.0d), + new EpsgOperationParameterRecord(9050, "Latitude of 1st standard parallel", 35.0d), + new EpsgOperationParameterRecord(9050, "Latitude of 2nd standard parallel", 65.0d), + new EpsgOperationParameterRecord(9050, "Easting at false origin", 4000000.0d), + new EpsgOperationParameterRecord(9050, "Northing at false origin", 2800000.0d), + new EpsgOperationParameterRecord(9050, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(9050, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(9051, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9051, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9051, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9051, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9051, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9051, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9051, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9051, "Transformation reference epoch", 1995.4d), + new EpsgOperationParameterRecord(9052, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9052, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9052, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9052, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9052, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9052, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9052, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9052, "Transformation reference epoch", 2000.4d), + new EpsgOperationParameterRecord(9076, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9076, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9076, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9076, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9076, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9076, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9076, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9077, "X-axis translation", 0.9102d), + new EpsgOperationParameterRecord(9077, "Y-axis translation", -2.0141d), + new EpsgOperationParameterRecord(9077, "Z-axis translation", -0.5602d), + new EpsgOperationParameterRecord(9077, "X-axis rotation", 29.039d), + new EpsgOperationParameterRecord(9077, "Y-axis rotation", 10.065d), + new EpsgOperationParameterRecord(9077, "Z-axis rotation", 10.101d), + new EpsgOperationParameterRecord(9077, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9077, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9077, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9077, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9077, "Rate of change of X-axis rotation", -0.02d), + new EpsgOperationParameterRecord(9077, "Rate of change of Y-axis rotation", 0.105d), + new EpsgOperationParameterRecord(9077, "Rate of change of Z-axis rotation", -0.347d), + new EpsgOperationParameterRecord(9077, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9077, "Parameter reference epoch", 1993.62d), + new EpsgOperationParameterRecord(9078, "X-axis translation", 0.9102d), + new EpsgOperationParameterRecord(9078, "Y-axis translation", -2.0141d), + new EpsgOperationParameterRecord(9078, "Z-axis translation", -0.5602d), + new EpsgOperationParameterRecord(9078, "X-axis rotation", 29.039d), + new EpsgOperationParameterRecord(9078, "Y-axis rotation", 10.065d), + new EpsgOperationParameterRecord(9078, "Z-axis rotation", 10.101d), + new EpsgOperationParameterRecord(9078, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9078, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9078, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9078, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9078, "Rate of change of X-axis rotation", -0.384d), + new EpsgOperationParameterRecord(9078, "Rate of change of Y-axis rotation", 1.007d), + new EpsgOperationParameterRecord(9078, "Rate of change of Z-axis rotation", -2.186d), + new EpsgOperationParameterRecord(9078, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9078, "Parameter reference epoch", 1993.62d), + new EpsgOperationParameterRecord(9079, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9079, "Y-axis translation", -0.51d), + new EpsgOperationParameterRecord(9079, "Z-axis translation", 15.53d), + new EpsgOperationParameterRecord(9079, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9079, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9079, "Z-axis rotation", 0.05984d), + new EpsgOperationParameterRecord(9079, "Scale difference", -1.51099d), + new EpsgOperationParameterRecord(9079, "Rate of change of X-axis translation", 0.69d), + new EpsgOperationParameterRecord(9079, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(9079, "Rate of change of Z-axis translation", 1.86d), + new EpsgOperationParameterRecord(9079, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9079, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9079, "Rate of change of Z-axis rotation", -0.00027d), + new EpsgOperationParameterRecord(9079, "Rate of change of scale difference", -0.19201d), + new EpsgOperationParameterRecord(9079, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9080, "X-axis translation", 6.7d), + new EpsgOperationParameterRecord(9080, "Y-axis translation", 3.79d), + new EpsgOperationParameterRecord(9080, "Z-axis translation", -7.17d), + new EpsgOperationParameterRecord(9080, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9080, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9080, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9080, "Scale difference", 0.06901d), + new EpsgOperationParameterRecord(9080, "Rate of change of X-axis translation", 0.69d), + new EpsgOperationParameterRecord(9080, "Rate of change of Y-axis translation", -0.7d), + new EpsgOperationParameterRecord(9080, "Rate of change of Z-axis translation", 0.46d), + new EpsgOperationParameterRecord(9080, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9080, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9080, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9080, "Rate of change of scale difference", -0.18201d), + new EpsgOperationParameterRecord(9080, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9081, "X-axis translation", 6.8d), + new EpsgOperationParameterRecord(9081, "Y-axis translation", 2.99d), + new EpsgOperationParameterRecord(9081, "Z-axis translation", -12.97d), + new EpsgOperationParameterRecord(9081, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9081, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9081, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9081, "Scale difference", 0.46901d), + new EpsgOperationParameterRecord(9081, "Rate of change of X-axis translation", 0.49d), + new EpsgOperationParameterRecord(9081, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9081, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9081, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9081, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9081, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9081, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9081, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9082, "X-axis translation", 4.8d), + new EpsgOperationParameterRecord(9082, "Y-axis translation", 2.09d), + new EpsgOperationParameterRecord(9082, "Z-axis translation", -17.67d), + new EpsgOperationParameterRecord(9082, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9082, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9082, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9082, "Scale difference", 1.40901d), + new EpsgOperationParameterRecord(9082, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9082, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9082, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9082, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9082, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9082, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9082, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9082, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9083, "X-axis translation", 6.4d), + new EpsgOperationParameterRecord(9083, "Y-axis translation", 3.99d), + new EpsgOperationParameterRecord(9083, "Z-axis translation", -14.27d), + new EpsgOperationParameterRecord(9083, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9083, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9083, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9083, "Scale difference", 1.08901d), + new EpsgOperationParameterRecord(9083, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9083, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9083, "Rate of change of Z-axis translation", -1.44d), + new EpsgOperationParameterRecord(9083, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9083, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9083, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9083, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(9083, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9126, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9126, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9126, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9126, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9126, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9126, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9126, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9127, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9127, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9127, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9127, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9127, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9127, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9127, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9128, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9128, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9128, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9128, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9128, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9128, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9128, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9129, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9129, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9129, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9129, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9129, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9129, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9129, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9142, "X-axis translation", 628.54052d), + new EpsgOperationParameterRecord(9142, "Y-axis translation", 192.2538d), + new EpsgOperationParameterRecord(9142, "Z-axis translation", 498.43507d), + new EpsgOperationParameterRecord(9142, "X-axis rotation", -13.79189d), + new EpsgOperationParameterRecord(9142, "Y-axis rotation", -0.81467d), + new EpsgOperationParameterRecord(9142, "Z-axis rotation", 41.21533d), + new EpsgOperationParameterRecord(9142, "Scale difference", -17.40368d), + new EpsgOperationParameterRecord(9143, "X-axis translation", 628.54052d), + new EpsgOperationParameterRecord(9143, "Y-axis translation", 192.2538d), + new EpsgOperationParameterRecord(9143, "Z-axis translation", 498.43507d), + new EpsgOperationParameterRecord(9143, "X-axis rotation", -13.79189d), + new EpsgOperationParameterRecord(9143, "Y-axis rotation", -0.81467d), + new EpsgOperationParameterRecord(9143, "Z-axis rotation", 41.21533d), + new EpsgOperationParameterRecord(9143, "Scale difference", -17.40368d), + new EpsgOperationParameterRecord(9144, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9144, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9144, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9145, "X-axis translation", -0.06d), + new EpsgOperationParameterRecord(9145, "Y-axis translation", 0.517d), + new EpsgOperationParameterRecord(9145, "Z-axis translation", 0.223d), + new EpsgOperationParameterRecord(9145, "X-axis rotation", -0.0183d), + new EpsgOperationParameterRecord(9145, "Y-axis rotation", 0.0003d), + new EpsgOperationParameterRecord(9145, "Z-axis rotation", -0.007d), + new EpsgOperationParameterRecord(9145, "Scale difference", 0.011d), + new EpsgOperationParameterRecord(9177, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9177, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9177, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9177, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9177, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9177, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9177, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9177, "Transformation reference epoch", 2002.0d), + new EpsgOperationParameterRecord(9179, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9179, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9179, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9179, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9179, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9179, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9179, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9179, "Transformation reference epoch", 2013.0d), + new EpsgOperationParameterRecord(9180, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9180, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9180, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9180, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9180, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9180, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9180, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9180, "Transformation reference epoch", 2016.0d), + new EpsgOperationParameterRecord(9185, "X-axis translation", -136.9703d), + new EpsgOperationParameterRecord(9185, "Y-axis translation", -37.5638d), + new EpsgOperationParameterRecord(9185, "Z-axis translation", 124.4242d), + new EpsgOperationParameterRecord(9185, "X-axis rotation", -0.25676d), + new EpsgOperationParameterRecord(9185, "Y-axis rotation", -0.42966d), + new EpsgOperationParameterRecord(9185, "Z-axis rotation", -0.30077d), + new EpsgOperationParameterRecord(9185, "Scale difference", -4.61966d), + new EpsgOperationParameterRecord(9186, "X-axis translation", -23.772d), + new EpsgOperationParameterRecord(9186, "Y-axis translation", -17.49d), + new EpsgOperationParameterRecord(9186, "Z-axis translation", -17.859d), + new EpsgOperationParameterRecord(9186, "X-axis rotation", -0.3132d), + new EpsgOperationParameterRecord(9186, "Y-axis rotation", -1.85274d), + new EpsgOperationParameterRecord(9186, "Z-axis rotation", 1.67299d), + new EpsgOperationParameterRecord(9186, "Scale difference", 5.4262d), + new EpsgOperationParameterRecord(9189, "X-axis translation", -23.772d), + new EpsgOperationParameterRecord(9189, "Y-axis translation", -17.49d), + new EpsgOperationParameterRecord(9189, "Z-axis translation", -17.859d), + new EpsgOperationParameterRecord(9189, "X-axis rotation", -0.3132d), + new EpsgOperationParameterRecord(9189, "Y-axis rotation", -1.85274d), + new EpsgOperationParameterRecord(9189, "Z-axis rotation", 1.67299d), + new EpsgOperationParameterRecord(9189, "Scale difference", 5.4262d), + new EpsgOperationParameterRecord(9224, "X-axis translation", -157.89d), + new EpsgOperationParameterRecord(9224, "Y-axis translation", -17.16d), + new EpsgOperationParameterRecord(9224, "Z-axis translation", -78.41d), + new EpsgOperationParameterRecord(9224, "X-axis rotation", 2.118d), + new EpsgOperationParameterRecord(9224, "Y-axis rotation", 2.697d), + new EpsgOperationParameterRecord(9224, "Z-axis rotation", -1.434d), + new EpsgOperationParameterRecord(9224, "Scale difference", -5.38d), + new EpsgOperationParameterRecord(9225, "X-axis translation", 0.054d), + new EpsgOperationParameterRecord(9225, "Y-axis translation", 0.051d), + new EpsgOperationParameterRecord(9225, "Z-axis translation", -0.085d), + new EpsgOperationParameterRecord(9225, "X-axis rotation", 0.0021d), + new EpsgOperationParameterRecord(9225, "Y-axis rotation", 0.0126d), + new EpsgOperationParameterRecord(9225, "Z-axis rotation", -0.0204d), + new EpsgOperationParameterRecord(9225, "Scale difference", 0.0025d), + new EpsgOperationParameterRecord(9225, "Transformation reference epoch", 2014.81d), + new EpsgOperationParameterRecord(9226, "X-axis translation", 112.771d), + new EpsgOperationParameterRecord(9226, "Y-axis translation", -12.282d), + new EpsgOperationParameterRecord(9226, "Z-axis translation", 18.935d), + new EpsgOperationParameterRecord(9226, "X-axis rotation", -2.1692d), + new EpsgOperationParameterRecord(9226, "Y-axis rotation", -16.8896d), + new EpsgOperationParameterRecord(9226, "Z-axis rotation", -17.1961d), + new EpsgOperationParameterRecord(9226, "Scale difference", 19.54517d), + new EpsgOperationParameterRecord(9227, "X-axis translation", 0.9963d), + new EpsgOperationParameterRecord(9227, "Y-axis translation", -1.9024d), + new EpsgOperationParameterRecord(9227, "Z-axis translation", -0.5219d), + new EpsgOperationParameterRecord(9227, "X-axis rotation", -25.915d), + new EpsgOperationParameterRecord(9227, "Y-axis rotation", -9.426d), + new EpsgOperationParameterRecord(9227, "Z-axis rotation", -11.599d), + new EpsgOperationParameterRecord(9227, "Scale difference", 0.775d), + new EpsgOperationParameterRecord(9227, "Rate of change of X-axis translation", 0.0005d), + new EpsgOperationParameterRecord(9227, "Rate of change of Y-axis translation", -0.0006d), + new EpsgOperationParameterRecord(9227, "Rate of change of Z-axis translation", -0.0013d), + new EpsgOperationParameterRecord(9227, "Rate of change of X-axis rotation", -0.067d), + new EpsgOperationParameterRecord(9227, "Rate of change of Y-axis rotation", 0.757d), + new EpsgOperationParameterRecord(9227, "Rate of change of Z-axis rotation", 0.051d), + new EpsgOperationParameterRecord(9227, "Rate of change of scale difference", -0.102d), + new EpsgOperationParameterRecord(9227, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(9234, "X-axis translation", 230.25d), + new EpsgOperationParameterRecord(9234, "Y-axis translation", 632.76d), + new EpsgOperationParameterRecord(9234, "Z-axis translation", 161.03d), + new EpsgOperationParameterRecord(9234, "X-axis rotation", -1.114d), + new EpsgOperationParameterRecord(9234, "Y-axis rotation", 1.115d), + new EpsgOperationParameterRecord(9234, "Z-axis rotation", 1.212d), + new EpsgOperationParameterRecord(9234, "Scale difference", 12.584d), + new EpsgOperationParameterRecord(9257, "X-axis translation", 8.88d), + new EpsgOperationParameterRecord(9257, "Y-axis translation", 184.86d), + new EpsgOperationParameterRecord(9257, "Z-axis translation", 106.69d), + new EpsgOperationParameterRecord(9258, "X-axis translation", 15.75d), + new EpsgOperationParameterRecord(9258, "Y-axis translation", 164.93d), + new EpsgOperationParameterRecord(9258, "Z-axis translation", 126.18d), + new EpsgOperationParameterRecord(9259, "X-axis translation", -233.43d), + new EpsgOperationParameterRecord(9259, "Y-axis translation", 6.65d), + new EpsgOperationParameterRecord(9259, "Z-axis translation", 173.64d), + new EpsgOperationParameterRecord(9260, "X-axis translation", -192.26d), + new EpsgOperationParameterRecord(9260, "Y-axis translation", 65.72d), + new EpsgOperationParameterRecord(9260, "Z-axis translation", 132.08d), + new EpsgOperationParameterRecord(9261, "X-axis translation", -9.5d), + new EpsgOperationParameterRecord(9261, "Y-axis translation", 122.9d), + new EpsgOperationParameterRecord(9261, "Z-axis translation", 138.2d), + new EpsgOperationParameterRecord(9262, "X-axis translation", -78.1d), + new EpsgOperationParameterRecord(9262, "Y-axis translation", 101.6d), + new EpsgOperationParameterRecord(9262, "Z-axis translation", 133.3d), + new EpsgOperationParameterRecord(9263, "X-axis translation", 18.2d), + new EpsgOperationParameterRecord(9263, "Y-axis translation", 190.7d), + new EpsgOperationParameterRecord(9263, "Z-axis translation", 100.9d), + new EpsgOperationParameterRecord(9264, "X-axis translation", -0.41d), + new EpsgOperationParameterRecord(9264, "Y-axis translation", 0.46d), + new EpsgOperationParameterRecord(9264, "Z-axis translation", -0.35d), + new EpsgOperationParameterRecord(9275, "EPSG code for Interpolation CRS", 4312.0d), + new EpsgOperationParameterRecord(9276, "EPSG code for Interpolation CRS", 11057.0d), + new EpsgOperationParameterRecord(9277, "EPSG code for Interpolation CRS", 4312.0d), + new EpsgOperationParameterRecord(9281, "X-axis translation", 565.7381d), + new EpsgOperationParameterRecord(9281, "Y-axis translation", 50.4018d), + new EpsgOperationParameterRecord(9281, "Z-axis translation", 465.2904d), + new EpsgOperationParameterRecord(9281, "X-axis rotation", 1.91514d), + new EpsgOperationParameterRecord(9281, "Y-axis rotation", -1.60363d), + new EpsgOperationParameterRecord(9281, "Z-axis rotation", 9.09546d), + new EpsgOperationParameterRecord(9281, "Scale difference", 4.07244d), + new EpsgOperationParameterRecord(9291, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9291, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9291, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9298, "X-axis translation", 1.16835d), + new EpsgOperationParameterRecord(9298, "Y-axis translation", -1.42001d), + new EpsgOperationParameterRecord(9298, "Z-axis translation", -2.24431d), + new EpsgOperationParameterRecord(9298, "X-axis rotation", 0.00822d), + new EpsgOperationParameterRecord(9298, "Y-axis rotation", 0.05508d), + new EpsgOperationParameterRecord(9298, "Z-axis rotation", -0.01818d), + new EpsgOperationParameterRecord(9298, "Scale difference", 0.23388d), + new EpsgOperationParameterRecord(9312, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9313, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9314, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9315, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9316, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9317, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9318, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9319, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9320, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9321, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9322, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9323, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9324, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9327, "EPSG code for Interpolation CRS", 4171.0d), + new EpsgOperationParameterRecord(9327, "EPSG code for standard transformation T0", 1651.0d), + new EpsgOperationParameterRecord(9328, "EPSG code for Interpolation CRS", 4749.0d), + new EpsgOperationParameterRecord(9328, "EPSG code for standard transformation T0", 15886.0d), + new EpsgOperationParameterRecord(9329, "EPSG code for Interpolation CRS", 4749.0d), + new EpsgOperationParameterRecord(9329, "EPSG code for standard transformation T0", 15882.0d), + new EpsgOperationParameterRecord(9330, "EPSG code for Interpolation CRS", 4749.0d), + new EpsgOperationParameterRecord(9330, "EPSG code for standard transformation T0", 15882.0d), + new EpsgOperationParameterRecord(9334, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9334, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9334, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9334, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9334, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9334, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9334, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9334, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9334, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9334, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9334, "Rate of change of X-axis rotation", -1.199d), + new EpsgOperationParameterRecord(9334, "Rate of change of Y-axis rotation", 0.107d), + new EpsgOperationParameterRecord(9334, "Rate of change of Z-axis rotation", -1.468d), + new EpsgOperationParameterRecord(9334, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9334, "Parameter reference epoch", 2017.0d), + new EpsgOperationParameterRecord(9342, "X-axis translation", -302.0d), + new EpsgOperationParameterRecord(9342, "Y-axis translation", 272.0d), + new EpsgOperationParameterRecord(9342, "Z-axis translation", -360.0d), + new EpsgOperationParameterRecord(9343, "X-axis translation", -328.0d), + new EpsgOperationParameterRecord(9343, "Y-axis translation", 340.0d), + new EpsgOperationParameterRecord(9343, "Z-axis translation", -329.0d), + new EpsgOperationParameterRecord(9344, "X-axis translation", -352.0d), + new EpsgOperationParameterRecord(9344, "Y-axis translation", 403.0d), + new EpsgOperationParameterRecord(9344, "Z-axis translation", -287.0d), + new EpsgOperationParameterRecord(9345, "X-axis translation", -302.0d), + new EpsgOperationParameterRecord(9345, "Y-axis translation", 272.0d), + new EpsgOperationParameterRecord(9345, "Z-axis translation", -360.0d), + new EpsgOperationParameterRecord(9346, "X-axis translation", -328.0d), + new EpsgOperationParameterRecord(9346, "Y-axis translation", 340.0d), + new EpsgOperationParameterRecord(9346, "Z-axis translation", -329.0d), + new EpsgOperationParameterRecord(9347, "X-axis translation", -352.0d), + new EpsgOperationParameterRecord(9347, "Y-axis translation", 403.0d), + new EpsgOperationParameterRecord(9347, "Z-axis translation", -287.0d), + new EpsgOperationParameterRecord(9349, "X-axis translation", -79.0d), + new EpsgOperationParameterRecord(9349, "Y-axis translation", 13.0d), + new EpsgOperationParameterRecord(9349, "Z-axis translation", -14.0d), + new EpsgOperationParameterRecord(9350, "X-axis translation", -79.0d), + new EpsgOperationParameterRecord(9350, "Y-axis translation", 13.0d), + new EpsgOperationParameterRecord(9350, "Z-axis translation", -14.0d), + new EpsgOperationParameterRecord(9361, "X-axis translation", 0.0469d), + new EpsgOperationParameterRecord(9361, "Y-axis translation", -0.2827d), + new EpsgOperationParameterRecord(9361, "Z-axis translation", 0.0866d), + new EpsgOperationParameterRecord(9361, "X-axis rotation", 0.00559d), + new EpsgOperationParameterRecord(9361, "Y-axis rotation", -0.004981d), + new EpsgOperationParameterRecord(9361, "Z-axis rotation", 0.023108d), + new EpsgOperationParameterRecord(9361, "Scale difference", -0.008051d), + new EpsgOperationParameterRecord(9362, "X-axis translation", 13.8714d), + new EpsgOperationParameterRecord(9362, "Y-axis translation", -83.9721d), + new EpsgOperationParameterRecord(9362, "Z-axis translation", 101.674d), + new EpsgOperationParameterRecord(9363, "EPSG code for Interpolation CRS", 9333.0d), + new EpsgOperationParameterRecord(9363, "EPSG code for standard transformation T0", 9362.0d), + new EpsgOperationParameterRecord(9371, "Vertical Offset", 156.68d), + new EpsgOperationParameterRecord(9381, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9381, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9381, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9381, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9381, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9381, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9381, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9381, "Transformation reference epoch", 2010.0d), + new EpsgOperationParameterRecord(9382, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9382, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9382, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9382, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9382, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9382, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9382, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9382, "Transformation reference epoch", 2010.0d), + new EpsgOperationParameterRecord(9383, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9383, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9383, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9383, "X-axis rotation", -8.393d), + new EpsgOperationParameterRecord(9383, "Y-axis rotation", 0.749d), + new EpsgOperationParameterRecord(9383, "Z-axis rotation", -10.276d), + new EpsgOperationParameterRecord(9383, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9459, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9459, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9459, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9459, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9459, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9459, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9459, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9459, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9459, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9459, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9459, "Rate of change of X-axis rotation", 1.50379d), + new EpsgOperationParameterRecord(9459, "Rate of change of Y-axis rotation", 1.18346d), + new EpsgOperationParameterRecord(9459, "Rate of change of Z-axis rotation", 1.20716d), + new EpsgOperationParameterRecord(9459, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9459, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(9460, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9460, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9460, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9460, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9460, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9460, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9460, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9466, "EPSG code for Interpolation CRS", 7844.0d), + new EpsgOperationParameterRecord(9467, "EPSG code for Interpolation CRS", 4283.0d), + new EpsgOperationParameterRecord(9472, "X-axis translation", -0.2773d), + new EpsgOperationParameterRecord(9472, "Y-axis translation", 0.0534d), + new EpsgOperationParameterRecord(9472, "Z-axis translation", 0.4819d), + new EpsgOperationParameterRecord(9472, "X-axis rotation", 0.0935d), + new EpsgOperationParameterRecord(9472, "Y-axis rotation", -0.0286d), + new EpsgOperationParameterRecord(9472, "Z-axis rotation", 0.00969d), + new EpsgOperationParameterRecord(9472, "Scale difference", -0.028d), + new EpsgOperationParameterRecord(9486, "X-axis translation", 577.84843d), + new EpsgOperationParameterRecord(9486, "Y-axis translation", 165.45019d), + new EpsgOperationParameterRecord(9486, "Z-axis translation", 390.43652d), + new EpsgOperationParameterRecord(9486, "X-axis rotation", -4.93131d), + new EpsgOperationParameterRecord(9486, "Y-axis rotation", 0.96052d), + new EpsgOperationParameterRecord(9486, "Z-axis rotation", 13.05072d), + new EpsgOperationParameterRecord(9486, "Scale difference", 7.86546d), + new EpsgOperationParameterRecord(9495, "X-axis translation", 577.84843d), + new EpsgOperationParameterRecord(9495, "Y-axis translation", 165.45019d), + new EpsgOperationParameterRecord(9495, "Z-axis translation", 390.43652d), + new EpsgOperationParameterRecord(9495, "X-axis rotation", -4.93131d), + new EpsgOperationParameterRecord(9495, "Y-axis rotation", 0.96052d), + new EpsgOperationParameterRecord(9495, "Z-axis rotation", 13.05072d), + new EpsgOperationParameterRecord(9495, "Scale difference", 7.86546d), + new EpsgOperationParameterRecord(9551, "Vertical Offset", -0.394d), + new EpsgOperationParameterRecord(9552, "Vertical Offset", -0.448d), + new EpsgOperationParameterRecord(9553, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9554, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9555, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9556, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9557, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9558, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9561, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9562, "Vertical Offset", -0.17d), + new EpsgOperationParameterRecord(9563, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9564, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9565, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9566, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9567, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9568, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9569, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9570, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9571, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9572, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9573, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9574, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9575, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9576, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9577, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9578, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9579, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9580, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9581, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9582, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9583, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9584, "EPSG code for Interpolation CRS", 11009.0d), + new EpsgOperationParameterRecord(9585, "EPSG code for Interpolation CRS", 11009.0d), + new EpsgOperationParameterRecord(9586, "EPSG code for Interpolation CRS", 11009.0d), + new EpsgOperationParameterRecord(9587, "EPSG code for Interpolation CRS", 11009.0d), + new EpsgOperationParameterRecord(9588, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9589, "EPSG code for Interpolation CRS", 11009.0d), + new EpsgOperationParameterRecord(9590, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9591, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9592, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9593, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(9594, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(9595, "EPSG code for Interpolation CRS", 6318.0d), + new EpsgOperationParameterRecord(9596, "EPSG code for Interpolation CRS", 6318.0d), + new EpsgOperationParameterRecord(9597, "EPSG code for Interpolation CRS", 11037.0d), + new EpsgOperationParameterRecord(9598, "EPSG code for Interpolation CRS", 4747.0d), + new EpsgOperationParameterRecord(9599, "EPSG code for Interpolation CRS", 4747.0d), + new EpsgOperationParameterRecord(9600, "EPSG code for Interpolation CRS", 11057.0d), + new EpsgOperationParameterRecord(9601, "EPSG code for Interpolation CRS", 4312.0d), + new EpsgOperationParameterRecord(9602, "EPSG code for Interpolation CRS", 6135.0d), + new EpsgOperationParameterRecord(9603, "EPSG code for Interpolation CRS", 6135.0d), + new EpsgOperationParameterRecord(9604, "EPSG code for Interpolation CRS", 6135.0d), + new EpsgOperationParameterRecord(9605, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9606, "EPSG code for Interpolation CRS", 11134.0d), + new EpsgOperationParameterRecord(9607, "EPSG code for Interpolation CRS", 11134.0d), + new EpsgOperationParameterRecord(9608, "EPSG code for Interpolation CRS", 11134.0d), + new EpsgOperationParameterRecord(9609, "EPSG code for Interpolation CRS", 11134.0d), + new EpsgOperationParameterRecord(9610, "EPSG code for Interpolation CRS", 4081.0d), + new EpsgOperationParameterRecord(9611, "EPSG code for Interpolation CRS", 4081.0d), + new EpsgOperationParameterRecord(9612, "EPSG code for Interpolation CRS", 4081.0d), + new EpsgOperationParameterRecord(9613, "EPSG code for Interpolation CRS", 4081.0d), + new EpsgOperationParameterRecord(9614, "EPSG code for Interpolation CRS", 4081.0d), + new EpsgOperationParameterRecord(9615, "EPSG code for Interpolation CRS", 4081.0d), + new EpsgOperationParameterRecord(9616, "EPSG code for Interpolation CRS", 4081.0d), + new EpsgOperationParameterRecord(9618, "EPSG code for Interpolation CRS", 4326.0d), + new EpsgOperationParameterRecord(9619, "EPSG code for Interpolation CRS", 5593.0d), + new EpsgOperationParameterRecord(9620, "EPSG code for Interpolation CRS", 9333.0d), + new EpsgOperationParameterRecord(9621, "EPSG code for Interpolation CRS", 5340.0d), + new EpsgOperationParameterRecord(9622, "EPSG code for Interpolation CRS", 6318.0d), + new EpsgOperationParameterRecord(9623, "EPSG code for Interpolation CRS", 6318.0d), + new EpsgOperationParameterRecord(9624, "EPSG code for Interpolation CRS", 6325.0d), + new EpsgOperationParameterRecord(9625, "EPSG code for Interpolation CRS", 6325.0d), + new EpsgOperationParameterRecord(9626, "EPSG code for Interpolation CRS", 6322.0d), + new EpsgOperationParameterRecord(9627, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9628, "EPSG code for Interpolation CRS", 4167.0d), + new EpsgOperationParameterRecord(9629, "EPSG code for Interpolation CRS", 9470.0d), + new EpsgOperationParameterRecord(9630, "EPSG code for Interpolation CRS", 4624.0d), + new EpsgOperationParameterRecord(9631, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(9632, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(9633, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(9634, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(9635, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(9636, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(9637, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(9638, "EPSG code for Interpolation CRS", 9777.0d), + new EpsgOperationParameterRecord(9639, "EPSG code for Interpolation CRS", 9777.0d), + new EpsgOperationParameterRecord(9640, "EPSG code for Interpolation CRS", 4749.0d), + new EpsgOperationParameterRecord(9641, "EPSG code for Interpolation CRS", 4463.0d), + new EpsgOperationParameterRecord(9642, "EPSG code for Interpolation CRS", 4558.0d), + new EpsgOperationParameterRecord(9643, "EPSG code for Interpolation CRS", 8998.0d), + new EpsgOperationParameterRecord(9644, "EPSG code for Interpolation CRS", 8252.0d), + new EpsgOperationParameterRecord(9645, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9646, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9647, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9648, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9649, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9652, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9653, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9654, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9655, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9662, "EPSG code for Interpolation CRS", 9702.0d), + new EpsgOperationParameterRecord(9664, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9665, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9667, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9668, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9670, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9671, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9676, "X-axis translation", 23.772d), + new EpsgOperationParameterRecord(9676, "Y-axis translation", 17.49d), + new EpsgOperationParameterRecord(9676, "Z-axis translation", 17.859d), + new EpsgOperationParameterRecord(9676, "X-axis rotation", 0.3132d), + new EpsgOperationParameterRecord(9676, "Y-axis rotation", 1.85274d), + new EpsgOperationParameterRecord(9676, "Z-axis rotation", -1.67299d), + new EpsgOperationParameterRecord(9676, "Scale difference", -5.4262d), + new EpsgOperationParameterRecord(9679, "X-axis translation", 283.729d), + new EpsgOperationParameterRecord(9679, "Y-axis translation", 735.942d), + new EpsgOperationParameterRecord(9679, "Z-axis translation", 261.143d), + new EpsgOperationParameterRecord(9682, "X-axis translation", -61.55d), + new EpsgOperationParameterRecord(9682, "Y-axis translation", 10.87d), + new EpsgOperationParameterRecord(9682, "Z-axis translation", 40.19d), + new EpsgOperationParameterRecord(9682, "X-axis rotation", 39.4924d), + new EpsgOperationParameterRecord(9682, "Y-axis rotation", 32.7221d), + new EpsgOperationParameterRecord(9682, "Z-axis rotation", 32.8979d), + new EpsgOperationParameterRecord(9682, "Scale difference", 9.994d), + new EpsgOperationParameterRecord(9682, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9682, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9682, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9682, "Rate of change of X-axis rotation", 1.50379d), + new EpsgOperationParameterRecord(9682, "Rate of change of Y-axis rotation", 1.18346d), + new EpsgOperationParameterRecord(9682, "Rate of change of Z-axis rotation", 1.20716d), + new EpsgOperationParameterRecord(9682, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9682, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(9684, "X-axis translation", -61.55d), + new EpsgOperationParameterRecord(9684, "Y-axis translation", 10.87d), + new EpsgOperationParameterRecord(9684, "Z-axis translation", 40.19d), + new EpsgOperationParameterRecord(9684, "X-axis rotation", 39.4924d), + new EpsgOperationParameterRecord(9684, "Y-axis rotation", 32.7221d), + new EpsgOperationParameterRecord(9684, "Z-axis rotation", 32.8979d), + new EpsgOperationParameterRecord(9684, "Scale difference", 9.994d), + new EpsgOperationParameterRecord(9684, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9684, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9684, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9684, "Rate of change of X-axis rotation", 1.50379d), + new EpsgOperationParameterRecord(9684, "Rate of change of Y-axis rotation", 1.18346d), + new EpsgOperationParameterRecord(9684, "Rate of change of Z-axis rotation", 1.20716d), + new EpsgOperationParameterRecord(9684, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9684, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(9686, "X-axis translation", 61.55d), + new EpsgOperationParameterRecord(9686, "Y-axis translation", -10.87d), + new EpsgOperationParameterRecord(9686, "Z-axis translation", -40.19d), + new EpsgOperationParameterRecord(9686, "X-axis rotation", -39.4924d), + new EpsgOperationParameterRecord(9686, "Y-axis rotation", -32.7221d), + new EpsgOperationParameterRecord(9686, "Z-axis rotation", -32.8979d), + new EpsgOperationParameterRecord(9686, "Scale difference", -9.994d), + new EpsgOperationParameterRecord(9686, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9686, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9686, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9686, "Rate of change of X-axis rotation", -1.50379d), + new EpsgOperationParameterRecord(9686, "Rate of change of Y-axis rotation", -1.18346d), + new EpsgOperationParameterRecord(9686, "Rate of change of Z-axis rotation", -1.20716d), + new EpsgOperationParameterRecord(9686, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9686, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(9688, "X-axis translation", 61.55d), + new EpsgOperationParameterRecord(9688, "Y-axis translation", -10.87d), + new EpsgOperationParameterRecord(9688, "Z-axis translation", -40.19d), + new EpsgOperationParameterRecord(9688, "X-axis rotation", -39.4924d), + new EpsgOperationParameterRecord(9688, "Y-axis rotation", -32.7221d), + new EpsgOperationParameterRecord(9688, "Z-axis rotation", -32.8979d), + new EpsgOperationParameterRecord(9688, "Scale difference", -9.994d), + new EpsgOperationParameterRecord(9690, "X-axis translation", 61.55d), + new EpsgOperationParameterRecord(9690, "Y-axis translation", -10.87d), + new EpsgOperationParameterRecord(9690, "Z-axis translation", -40.19d), + new EpsgOperationParameterRecord(9690, "X-axis rotation", -39.4924d), + new EpsgOperationParameterRecord(9690, "Y-axis rotation", -32.7221d), + new EpsgOperationParameterRecord(9690, "Z-axis rotation", -32.8979d), + new EpsgOperationParameterRecord(9690, "Scale difference", -9.994d), + new EpsgOperationParameterRecord(9693, "EPSG code for Interpolation CRS", 7844.0d), + new EpsgOperationParameterRecord(9704, "EPSG code for Interpolation CRS", 4326.0d), + new EpsgOperationParameterRecord(9706, "EPSG code for Interpolation CRS", 4326.0d), + new EpsgOperationParameterRecord(9708, "EPSG code for Interpolation CRS", 4326.0d), + new EpsgOperationParameterRecord(9718, "EPSG code for Interpolation CRS", 9702.0d), + new EpsgOperationParameterRecord(9720, "EPSG code for Interpolation CRS", 9702.0d), + new EpsgOperationParameterRecord(9726, "Vertical Offset", 0.141d), + new EpsgOperationParameterRecord(9729, "EPSG code for Interpolation CRS", 6706.0d), + new EpsgOperationParameterRecord(9730, "EPSG code for Interpolation CRS", 6706.0d), + new EpsgOperationParameterRecord(9743, "X-axis translation", -307.0d), + new EpsgOperationParameterRecord(9743, "Y-axis translation", -92.0d), + new EpsgOperationParameterRecord(9743, "Z-axis translation", 127.0d), + new EpsgOperationParameterRecord(9744, "Ordinate 1 of evaluation point", 49.9166666666669d), + new EpsgOperationParameterRecord(9744, "Ordinate 2 of evaluation point", 15.2500000000003d), + new EpsgOperationParameterRecord(9744, "Vertical Offset", 0.13d), + new EpsgOperationParameterRecord(9744, "Inclination in latitude", 0.036d), + new EpsgOperationParameterRecord(9744, "Inclination in longitude", 0.006d), + new EpsgOperationParameterRecord(9744, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(9745, "Ordinate 1 of evaluation point", 49.9166666666669d), + new EpsgOperationParameterRecord(9745, "Ordinate 2 of evaluation point", 15.2500000000003d), + new EpsgOperationParameterRecord(9745, "Vertical Offset", 0.142d), + new EpsgOperationParameterRecord(9745, "Inclination in latitude", 0.026d), + new EpsgOperationParameterRecord(9745, "Inclination in longitude", 0.006d), + new EpsgOperationParameterRecord(9745, "EPSG code for Horizontal CRS", 4258.0d), + new EpsgOperationParameterRecord(9751, "X-axis translation", -0.16959d), + new EpsgOperationParameterRecord(9751, "Y-axis translation", 0.35312d), + new EpsgOperationParameterRecord(9751, "Z-axis translation", 0.51846d), + new EpsgOperationParameterRecord(9751, "X-axis rotation", -0.03385d), + new EpsgOperationParameterRecord(9751, "Y-axis rotation", 0.16325d), + new EpsgOperationParameterRecord(9751, "Z-axis rotation", -0.03446d), + new EpsgOperationParameterRecord(9751, "Scale difference", 0.03693d), + new EpsgOperationParameterRecord(9752, "X-axis translation", -0.16959d), + new EpsgOperationParameterRecord(9752, "Y-axis translation", 0.35312d), + new EpsgOperationParameterRecord(9752, "Z-axis translation", 0.51846d), + new EpsgOperationParameterRecord(9752, "X-axis rotation", -0.03385d), + new EpsgOperationParameterRecord(9752, "Y-axis rotation", 0.16325d), + new EpsgOperationParameterRecord(9752, "Z-axis rotation", -0.03446d), + new EpsgOperationParameterRecord(9752, "Scale difference", 0.03693d), + new EpsgOperationParameterRecord(9756, "X-axis translation", 0.0058d), + new EpsgOperationParameterRecord(9756, "Y-axis translation", -0.0064d), + new EpsgOperationParameterRecord(9756, "Z-axis translation", 0.007d), + new EpsgOperationParameterRecord(9756, "X-axis rotation", 0.08d), + new EpsgOperationParameterRecord(9756, "Y-axis rotation", 0.04d), + new EpsgOperationParameterRecord(9756, "Z-axis rotation", 0.12d), + new EpsgOperationParameterRecord(9756, "Scale difference", -4.4d), + new EpsgOperationParameterRecord(9757, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9757, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9757, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9757, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9757, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9757, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9757, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9768, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9768, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9768, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9769, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9769, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9769, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9770, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9770, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9770, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9771, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9771, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9771, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9772, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9772, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9772, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9773, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9773, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9773, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9774, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9774, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9774, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9787, "EPSG code for Interpolation CRS", 9782.0d), + new EpsgOperationParameterRecord(9788, "X-axis translation", -0.017d), + new EpsgOperationParameterRecord(9788, "Y-axis translation", 0.058d), + new EpsgOperationParameterRecord(9788, "Z-axis translation", 0.009d), + new EpsgOperationParameterRecord(9788, "X-axis rotation", 0.001305d), + new EpsgOperationParameterRecord(9788, "Y-axis rotation", 0.00068d), + new EpsgOperationParameterRecord(9788, "Z-axis rotation", -0.001467d), + new EpsgOperationParameterRecord(9788, "Scale difference", -0.00072d), + new EpsgOperationParameterRecord(9791, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9791, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9791, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9792, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9792, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9792, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9877, "EPSG code for Interpolation CRS", 9782.0d), + new EpsgOperationParameterRecord(9882, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9882, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9882, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9885, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(9888, "EPSG code for Interpolation CRS", 9777.0d), + new EpsgOperationParameterRecord(9888, "EPSG code for standard transformation T0", 1651.0d), + new EpsgOperationParameterRecord(9889, "EPSG code for Interpolation CRS", 9782.0d), + new EpsgOperationParameterRecord(9889, "EPSG code for standard transformation T0", 1651.0d), + new EpsgOperationParameterRecord(9898, "X-axis translation", -265.8979d), + new EpsgOperationParameterRecord(9898, "Y-axis translation", 76.9761d), + new EpsgOperationParameterRecord(9898, "Z-axis translation", 20.2504d), + new EpsgOperationParameterRecord(9898, "X-axis rotation", 0.43335d), + new EpsgOperationParameterRecord(9898, "Y-axis rotation", 3.11447d), + new EpsgOperationParameterRecord(9898, "Z-axis rotation", -2.63637d), + new EpsgOperationParameterRecord(9898, "Scale difference", 0.4752d), + new EpsgOperationParameterRecord(9898, "Ordinate 1 of evaluation point", 4103620.3891d), + new EpsgOperationParameterRecord(9898, "Ordinate 2 of evaluation point", 440486.4152d), + new EpsgOperationParameterRecord(9898, "Ordinate 3 of evaluation point", 4846923.4466d), + new EpsgOperationParameterRecord(9899, "X-axis translation", -189.033d), + new EpsgOperationParameterRecord(9899, "Y-axis translation", 14.1335d), + new EpsgOperationParameterRecord(9899, "Z-axis translation", -43.0901d), + new EpsgOperationParameterRecord(9899, "X-axis rotation", 0.43331d), + new EpsgOperationParameterRecord(9899, "Y-axis rotation", 3.11448d), + new EpsgOperationParameterRecord(9899, "Z-axis rotation", -2.63636d), + new EpsgOperationParameterRecord(9899, "Scale difference", 0.4752d), + new EpsgOperationParameterRecord(9900, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9902, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9903, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9904, "X-axis translation", -43.0d), + new EpsgOperationParameterRecord(9904, "Y-axis translation", -337.0d), + new EpsgOperationParameterRecord(9904, "Z-axis translation", -233.0d), + new EpsgOperationParameterRecord(9905, "X-axis translation", -41.057d), + new EpsgOperationParameterRecord(9905, "Y-axis translation", -374.564d), + new EpsgOperationParameterRecord(9905, "Z-axis translation", -226.287d), + new EpsgOperationParameterRecord(9905, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9905, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9905, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(9905, "Scale difference", 0.219d), + new EpsgOperationParameterRecord(9906, "X-axis translation", -254.1d), + new EpsgOperationParameterRecord(9906, "Y-axis translation", -5.36d), + new EpsgOperationParameterRecord(9906, "Z-axis translation", -100.29d), + new EpsgOperationParameterRecord(9909, "EPSG code for Interpolation CRS", 11215.0d), + new EpsgOperationParameterRecord(9913, "X-axis translation", -162.619d), + new EpsgOperationParameterRecord(9913, "Y-axis translation", -276.959d), + new EpsgOperationParameterRecord(9913, "Z-axis translation", -161.764d), + new EpsgOperationParameterRecord(9913, "X-axis rotation", -0.067753d), + new EpsgOperationParameterRecord(9913, "Y-axis rotation", 2.243648d), + new EpsgOperationParameterRecord(9913, "Z-axis rotation", 1.158828d), + new EpsgOperationParameterRecord(9913, "Scale difference", -1.094246d), + new EpsgOperationParameterRecord(9915, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9917, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9919, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9921, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9926, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(9936, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9936, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9936, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9937, "X-axis translation", -265.9196d), + new EpsgOperationParameterRecord(9937, "Y-axis translation", 76.9506d), + new EpsgOperationParameterRecord(9937, "Z-axis translation", 20.2222d), + new EpsgOperationParameterRecord(9937, "X-axis rotation", 0.48171d), + new EpsgOperationParameterRecord(9937, "Y-axis rotation", 3.09948d), + new EpsgOperationParameterRecord(9937, "Z-axis rotation", -2.68639d), + new EpsgOperationParameterRecord(9937, "Scale difference", 0.46346d), + new EpsgOperationParameterRecord(9937, "Ordinate 1 of evaluation point", 4101567.0943d), + new EpsgOperationParameterRecord(9937, "Ordinate 2 of evaluation point", 440245.0881d), + new EpsgOperationParameterRecord(9937, "Ordinate 3 of evaluation point", 4848681.4115d), + new EpsgOperationParameterRecord(9938, "X-axis translation", -189.228d), + new EpsgOperationParameterRecord(9938, "Y-axis translation", 12.0035d), + new EpsgOperationParameterRecord(9938, "Z-axis translation", -42.6303d), + new EpsgOperationParameterRecord(9938, "X-axis rotation", 0.48171d), + new EpsgOperationParameterRecord(9938, "Y-axis rotation", 3.09948d), + new EpsgOperationParameterRecord(9938, "Z-axis rotation", -2.68639d), + new EpsgOperationParameterRecord(9938, "Scale difference", 0.46346d), + new EpsgOperationParameterRecord(9955, "EPSG code for Interpolation CRS", 4659.0d), + new EpsgOperationParameterRecord(9957, "EPSG code for Interpolation CRS", 5324.0d), + new EpsgOperationParameterRecord(9959, "EPSG code for Interpolation CRS", 8086.0d), + new EpsgOperationParameterRecord(9960, "X-axis translation", -58.0d), + new EpsgOperationParameterRecord(9960, "Y-axis translation", 521.0d), + new EpsgOperationParameterRecord(9960, "Z-axis translation", 239.0d), + new EpsgOperationParameterRecord(9960, "X-axis rotation", 18.3d), + new EpsgOperationParameterRecord(9960, "Y-axis rotation", -0.3d), + new EpsgOperationParameterRecord(9960, "Z-axis rotation", 7.0d), + new EpsgOperationParameterRecord(9960, "Scale difference", 10.7d), + new EpsgOperationParameterRecord(9961, "X-axis translation", -20.0d), + new EpsgOperationParameterRecord(9961, "Y-axis translation", -16.0d), + new EpsgOperationParameterRecord(9961, "Z-axis translation", 14.0d), + new EpsgOperationParameterRecord(9961, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9961, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9961, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9961, "Scale difference", -0.69d), + new EpsgOperationParameterRecord(9962, "X-axis translation", 1.1d), + new EpsgOperationParameterRecord(9962, "Y-axis translation", -4.7d), + new EpsgOperationParameterRecord(9962, "Z-axis translation", 22.0d), + new EpsgOperationParameterRecord(9962, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9962, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9962, "Z-axis rotation", 0.16d), + new EpsgOperationParameterRecord(9962, "Scale difference", 1.45d), + new EpsgOperationParameterRecord(9962, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9962, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(9962, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(9962, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9962, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9962, "Rate of change of Z-axis rotation", 0.02d), + new EpsgOperationParameterRecord(9962, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(9962, "Parameter reference epoch", 2005.0d), + new EpsgOperationParameterRecord(9963, "X-axis translation", -2.4d), + new EpsgOperationParameterRecord(9963, "Y-axis translation", 1.6d), + new EpsgOperationParameterRecord(9963, "Z-axis translation", 23.2d), + new EpsgOperationParameterRecord(9963, "X-axis rotation", -0.27d), + new EpsgOperationParameterRecord(9963, "Y-axis rotation", 0.27d), + new EpsgOperationParameterRecord(9963, "Z-axis rotation", -0.38d), + new EpsgOperationParameterRecord(9963, "Scale difference", 2.08d), + new EpsgOperationParameterRecord(9963, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9963, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(9963, "Rate of change of Z-axis translation", 1.8d), + new EpsgOperationParameterRecord(9963, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9963, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9963, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9963, "Rate of change of scale difference", -0.08d), + new EpsgOperationParameterRecord(9963, "Parameter reference epoch", 2005.0d), + new EpsgOperationParameterRecord(9991, "X-axis translation", 1.4d), + new EpsgOperationParameterRecord(9991, "Y-axis translation", 0.9d), + new EpsgOperationParameterRecord(9991, "Z-axis translation", -1.4d), + new EpsgOperationParameterRecord(9991, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9991, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9991, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9991, "Scale difference", 0.42d), + new EpsgOperationParameterRecord(9991, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9991, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(9991, "Rate of change of Z-axis translation", -0.2d), + new EpsgOperationParameterRecord(9991, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9991, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9991, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9991, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9991, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9992, "X-axis translation", -0.2d), + new EpsgOperationParameterRecord(9992, "Y-axis translation", -1.0d), + new EpsgOperationParameterRecord(9992, "Z-axis translation", -3.3d), + new EpsgOperationParameterRecord(9992, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9992, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9992, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9992, "Scale difference", 0.29d), + new EpsgOperationParameterRecord(9992, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9992, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(9992, "Rate of change of Z-axis translation", -0.1d), + new EpsgOperationParameterRecord(9992, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9992, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9992, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9992, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(9992, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9993, "X-axis translation", -2.7d), + new EpsgOperationParameterRecord(9993, "Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(9993, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(9993, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9993, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9993, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9993, "Scale difference", -0.65d), + new EpsgOperationParameterRecord(9993, "Rate of change of X-axis translation", -0.3d), + new EpsgOperationParameterRecord(9993, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(9993, "Rate of change of Z-axis translation", -0.1d), + new EpsgOperationParameterRecord(9993, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9993, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9993, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9993, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(9993, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9994, "X-axis translation", 0.2d), + new EpsgOperationParameterRecord(9994, "Y-axis translation", -0.8d), + new EpsgOperationParameterRecord(9994, "Z-axis translation", 34.2d), + new EpsgOperationParameterRecord(9994, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9994, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9994, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9994, "Scale difference", -2.25d), + new EpsgOperationParameterRecord(9994, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9994, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9994, "Rate of change of Z-axis translation", 1.7d), + new EpsgOperationParameterRecord(9994, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9994, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9994, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9994, "Rate of change of scale difference", -0.11d), + new EpsgOperationParameterRecord(9994, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9995, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(9995, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(9995, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(9995, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9995, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9995, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(9995, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(9995, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9995, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(9995, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(9995, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9995, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9995, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(9995, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(9995, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9996, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(9996, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(9996, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(9996, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9996, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9996, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(9996, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(9996, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9996, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(9996, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(9996, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9996, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9996, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(9996, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(9996, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9997, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(9997, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(9997, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(9997, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9997, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9997, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(9997, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(9997, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9997, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(9997, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(9997, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9997, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9997, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(9997, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(9997, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9998, "X-axis translation", 65.8d), + new EpsgOperationParameterRecord(9998, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(9998, "Z-axis translation", 71.3d), + new EpsgOperationParameterRecord(9998, "X-axis rotation", 3.36d), + new EpsgOperationParameterRecord(9998, "Y-axis rotation", 4.33d), + new EpsgOperationParameterRecord(9998, "Z-axis rotation", -0.75d), + new EpsgOperationParameterRecord(9998, "Scale difference", -4.47d), + new EpsgOperationParameterRecord(9998, "Rate of change of X-axis translation", 2.8d), + new EpsgOperationParameterRecord(9998, "Rate of change of Y-axis translation", 0.2d), + new EpsgOperationParameterRecord(9998, "Rate of change of Z-axis translation", 2.3d), + new EpsgOperationParameterRecord(9998, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(9998, "Rate of change of Y-axis rotation", 0.19d), + new EpsgOperationParameterRecord(9998, "Rate of change of Z-axis rotation", -0.07d), + new EpsgOperationParameterRecord(9998, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(9998, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(9999, "X-axis translation", -14.5d), + new EpsgOperationParameterRecord(9999, "Y-axis translation", 1.9d), + new EpsgOperationParameterRecord(9999, "Z-axis translation", 85.9d), + new EpsgOperationParameterRecord(9999, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9999, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9999, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(9999, "Scale difference", -3.27d), + new EpsgOperationParameterRecord(9999, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(9999, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(9999, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(9999, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9999, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9999, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(9999, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(9999, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10085, "X-axis translation", -61.0d), + new EpsgOperationParameterRecord(10085, "Y-axis translation", 285.2d), + new EpsgOperationParameterRecord(10085, "Z-axis translation", 471.6d), + new EpsgOperationParameterRecord(10086, "X-axis translation", 48.0d), + new EpsgOperationParameterRecord(10086, "Y-axis translation", 208.0d), + new EpsgOperationParameterRecord(10086, "Z-axis translation", 382.0d), + new EpsgOperationParameterRecord(10087, "Latitude of natural origin", 18.0d), + new EpsgOperationParameterRecord(10087, "Longitude of natural origin", -77.0d), + new EpsgOperationParameterRecord(10087, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10087, "False easting", 550000.0d), + new EpsgOperationParameterRecord(10087, "False northing", 400000.0d), + new EpsgOperationParameterRecord(10087, "Latitude of natural origin", 18.0d), + new EpsgOperationParameterRecord(10087, "Longitude of natural origin", -77.0d), + new EpsgOperationParameterRecord(10087, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10087, "False easting", 250000.0d), + new EpsgOperationParameterRecord(10087, "False northing", 150000.0d), + new EpsgOperationParameterRecord(10087, "A0", 82357.457d), + new EpsgOperationParameterRecord(10087, "A1", 0.304794369d), + new EpsgOperationParameterRecord(10087, "A2", 1.5417425e-05d), + new EpsgOperationParameterRecord(10087, "B0", 28091.324d), + new EpsgOperationParameterRecord(10087, "B1", -1.5417425e-05d), + new EpsgOperationParameterRecord(10087, "B2", 0.304794369d), + new EpsgOperationParameterRecord(10089, "X-axis translation", -163.466d), + new EpsgOperationParameterRecord(10089, "Y-axis translation", 317.396d), + new EpsgOperationParameterRecord(10089, "Z-axis translation", -147.538d), + new EpsgOperationParameterRecord(10090, "X-axis translation", -170.0d), + new EpsgOperationParameterRecord(10090, "Y-axis translation", 305.0d), + new EpsgOperationParameterRecord(10090, "Z-axis translation", -145.0d), + new EpsgOperationParameterRecord(10091, "X-axis translation", -162.904d), + new EpsgOperationParameterRecord(10091, "Y-axis translation", 312.531d), + new EpsgOperationParameterRecord(10091, "Z-axis translation", -137.109d), + new EpsgOperationParameterRecord(10092, "X-axis translation", -158.0d), + new EpsgOperationParameterRecord(10092, "Y-axis translation", 309.0d), + new EpsgOperationParameterRecord(10092, "Z-axis translation", -151.0d), + new EpsgOperationParameterRecord(10093, "X-axis translation", -161.0d), + new EpsgOperationParameterRecord(10093, "Y-axis translation", 308.0d), + new EpsgOperationParameterRecord(10093, "Z-axis translation", -142.0d), + new EpsgOperationParameterRecord(10098, "X-axis translation", -96.062d), + new EpsgOperationParameterRecord(10098, "Y-axis translation", -82.428d), + new EpsgOperationParameterRecord(10098, "Z-axis translation", -121.753d), + new EpsgOperationParameterRecord(10098, "X-axis rotation", -4.801d), + new EpsgOperationParameterRecord(10098, "Y-axis rotation", -0.345d), + new EpsgOperationParameterRecord(10098, "Z-axis rotation", 1.376d), + new EpsgOperationParameterRecord(10098, "Scale difference", 1.496d), + new EpsgOperationParameterRecord(10099, "X-axis translation", -96.062d), + new EpsgOperationParameterRecord(10099, "Y-axis translation", -82.428d), + new EpsgOperationParameterRecord(10099, "Z-axis translation", -121.753d), + new EpsgOperationParameterRecord(10099, "X-axis rotation", -4.801d), + new EpsgOperationParameterRecord(10099, "Y-axis rotation", -0.345d), + new EpsgOperationParameterRecord(10099, "Z-axis rotation", 1.376d), + new EpsgOperationParameterRecord(10099, "Scale difference", 1.496d), + new EpsgOperationParameterRecord(10100, "X-axis translation", -26.5d), + new EpsgOperationParameterRecord(10100, "Y-axis translation", -12.1d), + new EpsgOperationParameterRecord(10100, "Z-axis translation", 91.9d), + new EpsgOperationParameterRecord(10100, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10100, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10100, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(10100, "Scale difference", -4.67d), + new EpsgOperationParameterRecord(10100, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10100, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10100, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10100, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10100, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10100, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(10100, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10100, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10103, "X-axis translation", -24.5d), + new EpsgOperationParameterRecord(10103, "Y-axis translation", -8.1d), + new EpsgOperationParameterRecord(10103, "Z-axis translation", 107.9d), + new EpsgOperationParameterRecord(10103, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10103, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10103, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(10103, "Scale difference", -4.97d), + new EpsgOperationParameterRecord(10103, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10103, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10103, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10103, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10103, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10103, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(10103, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10103, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10104, "X-axis translation", -29.5d), + new EpsgOperationParameterRecord(10104, "Y-axis translation", -32.1d), + new EpsgOperationParameterRecord(10104, "Z-axis translation", 145.9d), + new EpsgOperationParameterRecord(10104, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10104, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10104, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(10104, "Scale difference", -8.37d), + new EpsgOperationParameterRecord(10104, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10104, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10104, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10104, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10104, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10104, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(10104, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10104, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10105, "X-axis translation", -24.5d), + new EpsgOperationParameterRecord(10105, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(10105, "Z-axis translation", 169.9d), + new EpsgOperationParameterRecord(10105, "X-axis rotation", -0.1d), + new EpsgOperationParameterRecord(10105, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10105, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(10105, "Scale difference", -11.47d), + new EpsgOperationParameterRecord(10105, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10105, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10105, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10105, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10105, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10105, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(10105, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10105, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10107, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(10128, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10129, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10133, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(10134, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10134, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10134, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10134, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10134, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10134, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10134, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10134, "Transformation reference epoch", 2021.0d), + new EpsgOperationParameterRecord(10135, "X-axis translation", -302.0d), + new EpsgOperationParameterRecord(10135, "Y-axis translation", 272.0d), + new EpsgOperationParameterRecord(10135, "Z-axis translation", -360.0d), + new EpsgOperationParameterRecord(10136, "X-axis translation", -328.0d), + new EpsgOperationParameterRecord(10136, "Y-axis translation", 340.0d), + new EpsgOperationParameterRecord(10136, "Z-axis translation", -329.0d), + new EpsgOperationParameterRecord(10137, "X-axis translation", -352.0d), + new EpsgOperationParameterRecord(10137, "Y-axis translation", 403.0d), + new EpsgOperationParameterRecord(10137, "Z-axis translation", -287.0d), + new EpsgOperationParameterRecord(10138, "X-axis translation", -79.0d), + new EpsgOperationParameterRecord(10138, "Y-axis translation", 13.0d), + new EpsgOperationParameterRecord(10138, "Z-axis translation", -14.0d), + new EpsgOperationParameterRecord(10139, "X-axis translation", 0.5d), + new EpsgOperationParameterRecord(10139, "Y-axis translation", 3.6d), + new EpsgOperationParameterRecord(10139, "Z-axis translation", 2.4d), + new EpsgOperationParameterRecord(10139, "X-axis rotation", -0.1d), + new EpsgOperationParameterRecord(10139, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10139, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10139, "Scale difference", -3.1d), + new EpsgOperationParameterRecord(10140, "X-axis translation", -0.5d), + new EpsgOperationParameterRecord(10140, "Y-axis translation", -2.4d), + new EpsgOperationParameterRecord(10140, "Z-axis translation", 3.8d), + new EpsgOperationParameterRecord(10140, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10140, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10140, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10140, "Scale difference", -3.4d), + new EpsgOperationParameterRecord(10141, "X-axis translation", 0.2d), + new EpsgOperationParameterRecord(10141, "Y-axis translation", 0.4d), + new EpsgOperationParameterRecord(10141, "Z-axis translation", 1.6d), + new EpsgOperationParameterRecord(10141, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10141, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10141, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10141, "Scale difference", -0.3d), + new EpsgOperationParameterRecord(10142, "X-axis translation", -1.2d), + new EpsgOperationParameterRecord(10142, "Y-axis translation", -1.4d), + new EpsgOperationParameterRecord(10142, "Z-axis translation", 0.6d), + new EpsgOperationParameterRecord(10142, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10142, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10142, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10142, "Scale difference", -1.4d), + new EpsgOperationParameterRecord(10143, "X-axis translation", -0.6d), + new EpsgOperationParameterRecord(10143, "Y-axis translation", 0.5d), + new EpsgOperationParameterRecord(10143, "Z-axis translation", 1.5d), + new EpsgOperationParameterRecord(10143, "X-axis rotation", 0.39d), + new EpsgOperationParameterRecord(10143, "Y-axis rotation", -0.8d), + new EpsgOperationParameterRecord(10143, "Z-axis rotation", 0.96d), + new EpsgOperationParameterRecord(10143, "Scale difference", -0.49d), + new EpsgOperationParameterRecord(10143, "Rate of change of X-axis translation", 0.29d), + new EpsgOperationParameterRecord(10143, "Rate of change of Y-axis translation", -0.04d), + new EpsgOperationParameterRecord(10143, "Rate of change of Z-axis translation", -0.08d), + new EpsgOperationParameterRecord(10143, "Rate of change of X-axis rotation", 0.11d), + new EpsgOperationParameterRecord(10143, "Rate of change of Y-axis rotation", 0.19d), + new EpsgOperationParameterRecord(10143, "Rate of change of Z-axis rotation", -0.05d), + new EpsgOperationParameterRecord(10143, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10143, "Parameter reference epoch", 1988.0d), + new EpsgOperationParameterRecord(10145, "EPSG code for Interpolation CRS", 9470.0d), + new EpsgOperationParameterRecord(10149, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10149, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10149, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10152, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10153, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10179, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10179, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10179, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10179, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10179, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10179, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10179, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10179, "Transformation reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10180, "X-axis translation", 1.4d), + new EpsgOperationParameterRecord(10180, "Y-axis translation", 0.9d), + new EpsgOperationParameterRecord(10180, "Z-axis translation", -1.4d), + new EpsgOperationParameterRecord(10180, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10180, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10180, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10180, "Scale difference", 0.42d), + new EpsgOperationParameterRecord(10180, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10180, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(10180, "Rate of change of Z-axis translation", -0.2d), + new EpsgOperationParameterRecord(10180, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10180, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10180, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10180, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10180, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10216, "Latitude of natural origin", 4.59620041666694d), + new EpsgOperationParameterRecord(10216, "Longitude of natural origin", -80.0775079166669d), + new EpsgOperationParameterRecord(10216, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10216, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10216, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10216, "Latitude of natural origin", 4.5962032222225d), + new EpsgOperationParameterRecord(10216, "Longitude of natural origin", -80.0775077694447d), + new EpsgOperationParameterRecord(10216, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10216, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10216, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10216, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(10216, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(10221, "Latitude of natural origin", 4.59620041666694d), + new EpsgOperationParameterRecord(10221, "Longitude of natural origin", -77.0775079166669d), + new EpsgOperationParameterRecord(10221, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10221, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10221, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10221, "Latitude of natural origin", 4.5962032222225d), + new EpsgOperationParameterRecord(10221, "Longitude of natural origin", -77.0775077694447d), + new EpsgOperationParameterRecord(10221, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10221, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10221, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10221, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(10221, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(10242, "Latitude of natural origin", 4.59620041666694d), + new EpsgOperationParameterRecord(10242, "Longitude of natural origin", -74.0775079166669d), + new EpsgOperationParameterRecord(10242, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10242, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10242, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10242, "Latitude of natural origin", 4.5962032222225d), + new EpsgOperationParameterRecord(10242, "Longitude of natural origin", -74.0775077694447d), + new EpsgOperationParameterRecord(10242, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10242, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10242, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10242, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(10242, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(10243, "Latitude of natural origin", 4.59620041666694d), + new EpsgOperationParameterRecord(10243, "Longitude of natural origin", -71.0775079166669d), + new EpsgOperationParameterRecord(10243, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10243, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10243, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10243, "Latitude of natural origin", 4.5962032222225d), + new EpsgOperationParameterRecord(10243, "Longitude of natural origin", -71.0775077694447d), + new EpsgOperationParameterRecord(10243, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10243, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10243, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10243, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(10243, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(10244, "Latitude of natural origin", 4.59620041666694d), + new EpsgOperationParameterRecord(10244, "Longitude of natural origin", -68.0775079166669d), + new EpsgOperationParameterRecord(10244, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10244, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10244, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10244, "Latitude of natural origin", 4.5962032222225d), + new EpsgOperationParameterRecord(10244, "Longitude of natural origin", -68.0775077694447d), + new EpsgOperationParameterRecord(10244, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10244, "False easting", 1000000.0d), + new EpsgOperationParameterRecord(10244, "False northing", 1000000.0d), + new EpsgOperationParameterRecord(10244, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(10244, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(10248, "EPSG code for Interpolation CRS", 4765.0d), + new EpsgOperationParameterRecord(10264, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10264, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10264, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10292, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10292, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10292, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10292, "X-axis rotation", 0.658d), + new EpsgOperationParameterRecord(10292, "Y-axis rotation", -0.208d), + new EpsgOperationParameterRecord(10292, "Z-axis rotation", 0.755d), + new EpsgOperationParameterRecord(10292, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10295, "EPSG code for Interpolation CRS", 10284.0d), + new EpsgOperationParameterRecord(10296, "X-axis translation", -267.407d), + new EpsgOperationParameterRecord(10296, "Y-axis translation", -47.068d), + new EpsgOperationParameterRecord(10296, "Z-axis translation", 446.357d), + new EpsgOperationParameterRecord(10296, "X-axis rotation", -0.179423d), + new EpsgOperationParameterRecord(10296, "Y-axis rotation", 5.577661d), + new EpsgOperationParameterRecord(10296, "Z-axis rotation", -1.27762d), + new EpsgOperationParameterRecord(10296, "Scale difference", 1.204866d), + new EpsgOperationParameterRecord(10320, "EPSG code for Interpolation CRS", 10312.0d), + new EpsgOperationParameterRecord(10321, "X-axis translation", -0.584d), + new EpsgOperationParameterRecord(10321, "Y-axis translation", -1.117d), + new EpsgOperationParameterRecord(10321, "Z-axis translation", 1.125d), + new EpsgOperationParameterRecord(10322, "EPSG code for Interpolation CRS", 10310.0d), + new EpsgOperationParameterRecord(10322, "EPSG code for standard transformation T0", 10321.0d), + new EpsgOperationParameterRecord(10324, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10324, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10324, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10333, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10333, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10333, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10334, "X-axis translation", 1003.9d), + new EpsgOperationParameterRecord(10334, "Y-axis translation", -1909.61d), + new EpsgOperationParameterRecord(10334, "Z-axis translation", -541.17d), + new EpsgOperationParameterRecord(10334, "X-axis rotation", 26.78138d), + new EpsgOperationParameterRecord(10334, "Y-axis rotation", -0.42027d), + new EpsgOperationParameterRecord(10334, "Z-axis rotation", 10.93206d), + new EpsgOperationParameterRecord(10334, "Scale difference", -0.05109d), + new EpsgOperationParameterRecord(10334, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(10334, "Rate of change of Y-axis translation", -0.7d), + new EpsgOperationParameterRecord(10334, "Rate of change of Z-axis translation", -1.24d), + new EpsgOperationParameterRecord(10334, "Rate of change of X-axis rotation", 0.06667d), + new EpsgOperationParameterRecord(10334, "Rate of change of Y-axis rotation", -0.75744d), + new EpsgOperationParameterRecord(10334, "Rate of change of Z-axis rotation", -0.05133d), + new EpsgOperationParameterRecord(10334, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(10334, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10335, "X-axis translation", 0.9109d), + new EpsgOperationParameterRecord(10335, "Y-axis translation", -2.0129d), + new EpsgOperationParameterRecord(10335, "Z-axis translation", -0.5863d), + new EpsgOperationParameterRecord(10335, "X-axis rotation", 22.749d), + new EpsgOperationParameterRecord(10335, "Y-axis rotation", 26.56d), + new EpsgOperationParameterRecord(10335, "Z-axis rotation", -25.706d), + new EpsgOperationParameterRecord(10335, "Scale difference", 2.12d), + new EpsgOperationParameterRecord(10335, "Rate of change of X-axis translation", 0.0001d), + new EpsgOperationParameterRecord(10335, "Rate of change of Y-axis translation", 0.0001d), + new EpsgOperationParameterRecord(10335, "Rate of change of Z-axis translation", -0.0019d), + new EpsgOperationParameterRecord(10335, "Rate of change of X-axis rotation", -0.384d), + new EpsgOperationParameterRecord(10335, "Rate of change of Y-axis rotation", 1.007d), + new EpsgOperationParameterRecord(10335, "Rate of change of Z-axis rotation", -2.186d), + new EpsgOperationParameterRecord(10335, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(10335, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10336, "X-axis translation", 909.5d), + new EpsgOperationParameterRecord(10336, "Y-axis translation", -2013.3d), + new EpsgOperationParameterRecord(10336, "Z-axis translation", -585.9d), + new EpsgOperationParameterRecord(10336, "X-axis rotation", 22.749d), + new EpsgOperationParameterRecord(10336, "Y-axis rotation", 26.56d), + new EpsgOperationParameterRecord(10336, "Z-axis rotation", -25.706d), + new EpsgOperationParameterRecord(10336, "Scale difference", 1.7d), + new EpsgOperationParameterRecord(10336, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(10336, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10336, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(10336, "Rate of change of X-axis rotation", -0.384d), + new EpsgOperationParameterRecord(10336, "Rate of change of Y-axis rotation", 1.007d), + new EpsgOperationParameterRecord(10336, "Rate of change of Z-axis rotation", -2.186d), + new EpsgOperationParameterRecord(10336, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(10336, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10337, "X-axis translation", 0.9109d), + new EpsgOperationParameterRecord(10337, "Y-axis translation", -2.0129d), + new EpsgOperationParameterRecord(10337, "Z-axis translation", -0.5863d), + new EpsgOperationParameterRecord(10337, "X-axis rotation", 28.711d), + new EpsgOperationParameterRecord(10337, "Y-axis rotation", 11.785d), + new EpsgOperationParameterRecord(10337, "Z-axis rotation", 4.417d), + new EpsgOperationParameterRecord(10337, "Scale difference", 2.12d), + new EpsgOperationParameterRecord(10337, "Rate of change of X-axis translation", 0.0001d), + new EpsgOperationParameterRecord(10337, "Rate of change of Y-axis translation", 0.0001d), + new EpsgOperationParameterRecord(10337, "Rate of change of Z-axis translation", -0.0019d), + new EpsgOperationParameterRecord(10337, "Rate of change of X-axis rotation", -0.02d), + new EpsgOperationParameterRecord(10337, "Rate of change of Y-axis rotation", 0.105d), + new EpsgOperationParameterRecord(10337, "Rate of change of Z-axis rotation", -0.347d), + new EpsgOperationParameterRecord(10337, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(10337, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10338, "X-axis translation", 909.5d), + new EpsgOperationParameterRecord(10338, "Y-axis translation", -2013.3d), + new EpsgOperationParameterRecord(10338, "Z-axis translation", -585.9d), + new EpsgOperationParameterRecord(10338, "X-axis rotation", 28.711d), + new EpsgOperationParameterRecord(10338, "Y-axis rotation", 11.785d), + new EpsgOperationParameterRecord(10338, "Z-axis rotation", 4.417d), + new EpsgOperationParameterRecord(10338, "Scale difference", 1.7d), + new EpsgOperationParameterRecord(10338, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(10338, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10338, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(10338, "Rate of change of X-axis rotation", -0.02d), + new EpsgOperationParameterRecord(10338, "Rate of change of Y-axis rotation", 0.105d), + new EpsgOperationParameterRecord(10338, "Rate of change of Z-axis rotation", -0.347d), + new EpsgOperationParameterRecord(10338, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(10338, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10339, "X-axis translation", -152.9d), + new EpsgOperationParameterRecord(10339, "Y-axis translation", 43.8d), + new EpsgOperationParameterRecord(10339, "Z-axis translation", 358.3d), + new EpsgOperationParameterRecord(10339, "X-axis rotation", 2.714d), + new EpsgOperationParameterRecord(10339, "Y-axis rotation", 1.386d), + new EpsgOperationParameterRecord(10339, "Z-axis rotation", -2.788d), + new EpsgOperationParameterRecord(10339, "Scale difference", -6.743d), + new EpsgOperationParameterRecord(10340, "X-axis translation", -95.7d), + new EpsgOperationParameterRecord(10340, "Y-axis translation", 10.2d), + new EpsgOperationParameterRecord(10340, "Z-axis translation", 158.9d), + new EpsgOperationParameterRecord(10341, "X-axis translation", -165.914d), + new EpsgOperationParameterRecord(10341, "Y-axis translation", -70.607d), + new EpsgOperationParameterRecord(10341, "Z-axis translation", 305.009d), + new EpsgOperationParameterRecord(10342, "X-axis translation", -169.559d), + new EpsgOperationParameterRecord(10342, "Y-axis translation", -72.34d), + new EpsgOperationParameterRecord(10342, "Z-axis translation", 303.102d), + new EpsgOperationParameterRecord(10343, "X-axis translation", -168.52d), + new EpsgOperationParameterRecord(10343, "Y-axis translation", -72.05d), + new EpsgOperationParameterRecord(10343, "Z-axis translation", 304.3d), + new EpsgOperationParameterRecord(10344, "X-axis translation", -181.7d), + new EpsgOperationParameterRecord(10344, "Y-axis translation", 64.7d), + new EpsgOperationParameterRecord(10344, "Z-axis translation", 247.2d), + new EpsgOperationParameterRecord(10348, "EPSG code for Interpolation CRS", 9702.0d), + new EpsgOperationParameterRecord(10351, "EPSG code for Interpolation CRS", 11037.0d), + new EpsgOperationParameterRecord(10359, "EPSG code for Interpolation CRS", 11134.0d), + new EpsgOperationParameterRecord(10361, "EPSG code for Interpolation CRS", 11134.0d), + new EpsgOperationParameterRecord(10363, "EPSG code for Interpolation CRS", 11134.0d), + new EpsgOperationParameterRecord(10367, "EPSG code for Interpolation CRS", 4737.0d), + new EpsgOperationParameterRecord(10369, "EPSG code for Interpolation CRS", 4737.0d), + new EpsgOperationParameterRecord(10380, "Vertical Offset", -2.0d), + new EpsgOperationParameterRecord(10381, "Vertical Offset", -2.08d), + new EpsgOperationParameterRecord(10382, "Vertical Offset", -1.4d), + new EpsgOperationParameterRecord(10383, "Vertical Offset", -1.4d), + new EpsgOperationParameterRecord(10384, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10385, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10386, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10387, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10388, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10389, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10390, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10391, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10415, "X-axis translation", 1.0039d), + new EpsgOperationParameterRecord(10415, "Y-axis translation", -1.90961d), + new EpsgOperationParameterRecord(10415, "Z-axis translation", -0.54117d), + new EpsgOperationParameterRecord(10415, "X-axis rotation", -26.78138d), + new EpsgOperationParameterRecord(10415, "Y-axis rotation", 0.42027d), + new EpsgOperationParameterRecord(10415, "Z-axis rotation", -10.93206d), + new EpsgOperationParameterRecord(10415, "Scale difference", -0.05109d), + new EpsgOperationParameterRecord(10415, "Rate of change of X-axis translation", 0.00079d), + new EpsgOperationParameterRecord(10415, "Rate of change of Y-axis translation", -0.0007d), + new EpsgOperationParameterRecord(10415, "Rate of change of Z-axis translation", -0.00124d), + new EpsgOperationParameterRecord(10415, "Rate of change of X-axis rotation", -0.06667d), + new EpsgOperationParameterRecord(10415, "Rate of change of Y-axis rotation", 0.75744d), + new EpsgOperationParameterRecord(10415, "Rate of change of Z-axis rotation", 0.05133d), + new EpsgOperationParameterRecord(10415, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(10415, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10416, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10416, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10416, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10416, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10416, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10416, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10416, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10419, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10419, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10419, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10419, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10419, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10419, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10419, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10467, "EPSG code for Interpolation CRS", 11037.0d), + new EpsgOperationParameterRecord(10478, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10478, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10478, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10490, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10492, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10494, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10505, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(10508, "EPSG code for Interpolation CRS", 9782.0d), + new EpsgOperationParameterRecord(10510, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(10511, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10511, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10511, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10511, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10511, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10511, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10511, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10512, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10512, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10512, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10512, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10512, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10512, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10512, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10513, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10513, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10513, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10513, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10513, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10513, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10513, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10514, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10514, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10514, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10514, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10514, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10514, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10514, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10517, "Latitude of natural origin", 44.2533353222225d), + new EpsgOperationParameterRecord(10517, "Longitude of natural origin", -90.8442965138891d), + new EpsgOperationParameterRecord(10517, "Scale factor at natural origin", 1.0000353d), + new EpsgOperationParameterRecord(10517, "False easting", 88582.5d), + new EpsgOperationParameterRecord(10517, "False northing", 82020.833d), + new EpsgOperationParameterRecord(10517, "Latitude of natural origin", 44.2533351277781d), + new EpsgOperationParameterRecord(10517, "Longitude of natural origin", -90.8442965194447d), + new EpsgOperationParameterRecord(10517, "Scale factor at natural origin", 1.0000353d), + new EpsgOperationParameterRecord(10517, "False easting", 88582.5d), + new EpsgOperationParameterRecord(10517, "False northing", 82020.833d), + new EpsgOperationParameterRecord(10517, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(10517, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(10518, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10519, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10520, "EPSG code for Interpolation CRS", 8252.0d), + new EpsgOperationParameterRecord(10521, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10522, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10523, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10524, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10525, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10526, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10527, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10528, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10529, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10530, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10534, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10535, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10536, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10537, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10538, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10539, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10540, "EPSG code for Interpolation CRS", 8255.0d), + new EpsgOperationParameterRecord(10541, "EPSG code for Interpolation CRS", 8255.0d), + new EpsgOperationParameterRecord(10542, "EPSG code for Interpolation CRS", 8255.0d), + new EpsgOperationParameterRecord(10543, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10543, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10543, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10546, "EPSG code for Interpolation CRS", 11108.0d), + new EpsgOperationParameterRecord(10558, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10560, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10562, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10564, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10566, "EPSG code for Interpolation CRS", 4747.0d), + new EpsgOperationParameterRecord(10568, "EPSG code for Interpolation CRS", 11070.0d), + new EpsgOperationParameterRecord(10572, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10572, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10572, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10572, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10572, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10572, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10572, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10572, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10572, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10572, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10572, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10572, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10572, "Rate of change of Z-axis rotation", -0.753d), + new EpsgOperationParameterRecord(10572, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10572, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(10573, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10573, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10573, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10573, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10573, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10573, "Z-axis rotation", -19.578d), + new EpsgOperationParameterRecord(10573, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10573, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10573, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10573, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10573, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10573, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10573, "Rate of change of Z-axis rotation", -0.753d), + new EpsgOperationParameterRecord(10573, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10573, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10574, "X-axis translation", 1.4d), + new EpsgOperationParameterRecord(10574, "Y-axis translation", 0.9d), + new EpsgOperationParameterRecord(10574, "Z-axis translation", -1.4d), + new EpsgOperationParameterRecord(10574, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10574, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10574, "Z-axis rotation", -19.578d), + new EpsgOperationParameterRecord(10574, "Scale difference", 0.42d), + new EpsgOperationParameterRecord(10574, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10574, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(10574, "Rate of change of Z-axis translation", -0.2d), + new EpsgOperationParameterRecord(10574, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10574, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10574, "Rate of change of Z-axis rotation", -0.753d), + new EpsgOperationParameterRecord(10574, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10574, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10575, "X-axis translation", -0.2d), + new EpsgOperationParameterRecord(10575, "Y-axis translation", -1.0d), + new EpsgOperationParameterRecord(10575, "Z-axis translation", -3.3d), + new EpsgOperationParameterRecord(10575, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10575, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10575, "Z-axis rotation", -19.578d), + new EpsgOperationParameterRecord(10575, "Scale difference", 0.29d), + new EpsgOperationParameterRecord(10575, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10575, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(10575, "Rate of change of Z-axis translation", -0.1d), + new EpsgOperationParameterRecord(10575, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10575, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10575, "Rate of change of Z-axis rotation", -0.753d), + new EpsgOperationParameterRecord(10575, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(10575, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10576, "X-axis translation", -2.7d), + new EpsgOperationParameterRecord(10576, "Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10576, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10576, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10576, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10576, "Z-axis rotation", -19.578d), + new EpsgOperationParameterRecord(10576, "Scale difference", -0.65d), + new EpsgOperationParameterRecord(10576, "Rate of change of X-axis translation", -0.3d), + new EpsgOperationParameterRecord(10576, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(10576, "Rate of change of Z-axis translation", -0.1d), + new EpsgOperationParameterRecord(10576, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10576, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10576, "Rate of change of Z-axis rotation", -0.753d), + new EpsgOperationParameterRecord(10576, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(10576, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10577, "X-axis translation", 0.2d), + new EpsgOperationParameterRecord(10577, "Y-axis translation", -0.8d), + new EpsgOperationParameterRecord(10577, "Z-axis translation", 34.2d), + new EpsgOperationParameterRecord(10577, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10577, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10577, "Z-axis rotation", -19.578d), + new EpsgOperationParameterRecord(10577, "Scale difference", -2.25d), + new EpsgOperationParameterRecord(10577, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10577, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10577, "Rate of change of Z-axis translation", 1.7d), + new EpsgOperationParameterRecord(10577, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10577, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10577, "Rate of change of Z-axis rotation", -0.753d), + new EpsgOperationParameterRecord(10577, "Rate of change of scale difference", -0.11d), + new EpsgOperationParameterRecord(10577, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10578, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(10578, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(10578, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(10578, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10578, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10578, "Z-axis rotation", -19.938d), + new EpsgOperationParameterRecord(10578, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(10578, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10578, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10578, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10578, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10578, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10578, "Rate of change of Z-axis rotation", -0.773d), + new EpsgOperationParameterRecord(10578, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10578, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10579, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(10579, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(10579, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(10579, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10579, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10579, "Z-axis rotation", -19.938d), + new EpsgOperationParameterRecord(10579, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(10579, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10579, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10579, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10579, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10579, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10579, "Rate of change of Z-axis rotation", -0.773d), + new EpsgOperationParameterRecord(10579, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10579, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10580, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(10580, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(10580, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(10580, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10580, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10580, "Z-axis rotation", -19.938d), + new EpsgOperationParameterRecord(10580, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(10580, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10580, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10580, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10580, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10580, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10580, "Rate of change of Z-axis rotation", -0.773d), + new EpsgOperationParameterRecord(10580, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10580, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10581, "X-axis translation", 65.8d), + new EpsgOperationParameterRecord(10581, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(10581, "Z-axis translation", 71.3d), + new EpsgOperationParameterRecord(10581, "X-axis rotation", 5.596d), + new EpsgOperationParameterRecord(10581, "Y-axis rotation", 17.824d), + new EpsgOperationParameterRecord(10581, "Z-axis rotation", -20.328d), + new EpsgOperationParameterRecord(10581, "Scale difference", -4.47d), + new EpsgOperationParameterRecord(10581, "Rate of change of X-axis translation", 2.8d), + new EpsgOperationParameterRecord(10581, "Rate of change of Y-axis translation", 0.2d), + new EpsgOperationParameterRecord(10581, "Rate of change of Z-axis translation", 2.3d), + new EpsgOperationParameterRecord(10581, "Rate of change of X-axis rotation", 0.196d), + new EpsgOperationParameterRecord(10581, "Rate of change of Y-axis rotation", 0.709d), + new EpsgOperationParameterRecord(10581, "Rate of change of Z-axis rotation", -0.823d), + new EpsgOperationParameterRecord(10581, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10581, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10582, "X-axis translation", -14.5d), + new EpsgOperationParameterRecord(10582, "Y-axis translation", 1.9d), + new EpsgOperationParameterRecord(10582, "Z-axis translation", 85.9d), + new EpsgOperationParameterRecord(10582, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10582, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10582, "Z-axis rotation", -19.938d), + new EpsgOperationParameterRecord(10582, "Scale difference", -3.27d), + new EpsgOperationParameterRecord(10582, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10582, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10582, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10582, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10582, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10582, "Rate of change of Z-axis rotation", -0.773d), + new EpsgOperationParameterRecord(10582, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10582, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10583, "X-axis translation", -26.5d), + new EpsgOperationParameterRecord(10583, "Y-axis translation", -12.1d), + new EpsgOperationParameterRecord(10583, "Z-axis translation", 91.9d), + new EpsgOperationParameterRecord(10583, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10583, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10583, "Z-axis rotation", -19.938d), + new EpsgOperationParameterRecord(10583, "Scale difference", -4.67d), + new EpsgOperationParameterRecord(10583, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10583, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10583, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10583, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10583, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10583, "Rate of change of Z-axis rotation", -0.773d), + new EpsgOperationParameterRecord(10583, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10583, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10584, "X-axis translation", -24.5d), + new EpsgOperationParameterRecord(10584, "Y-axis translation", -8.1d), + new EpsgOperationParameterRecord(10584, "Z-axis translation", 107.9d), + new EpsgOperationParameterRecord(10584, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10584, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10584, "Z-axis rotation", -19.938d), + new EpsgOperationParameterRecord(10584, "Scale difference", -4.97d), + new EpsgOperationParameterRecord(10584, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10584, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10584, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10584, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10584, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10584, "Rate of change of Z-axis rotation", -0.773d), + new EpsgOperationParameterRecord(10584, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10584, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10585, "X-axis translation", -29.5d), + new EpsgOperationParameterRecord(10585, "Y-axis translation", -32.1d), + new EpsgOperationParameterRecord(10585, "Z-axis translation", 145.9d), + new EpsgOperationParameterRecord(10585, "X-axis rotation", 2.236d), + new EpsgOperationParameterRecord(10585, "Y-axis rotation", 13.494d), + new EpsgOperationParameterRecord(10585, "Z-axis rotation", -19.938d), + new EpsgOperationParameterRecord(10585, "Scale difference", -8.37d), + new EpsgOperationParameterRecord(10585, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(10585, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10585, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(10585, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(10585, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(10585, "Rate of change of Z-axis rotation", -0.773d), + new EpsgOperationParameterRecord(10585, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(10585, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10586, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(10586, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(10586, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(10586, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(10586, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(10586, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(10586, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(10586, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(10586, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10586, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(10586, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(10586, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(10586, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(10586, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(10586, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10587, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10587, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(10587, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10587, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(10587, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(10587, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(10587, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10587, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10587, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10587, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10587, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10587, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10587, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10587, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10587, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10589, "X-axis translation", 407.379d), + new EpsgOperationParameterRecord(10589, "Y-axis translation", -685.226d), + new EpsgOperationParameterRecord(10589, "Z-axis translation", -52.577d), + new EpsgOperationParameterRecord(10589, "X-axis rotation", -0.318d), + new EpsgOperationParameterRecord(10589, "Y-axis rotation", 0.107d), + new EpsgOperationParameterRecord(10589, "Z-axis rotation", -0.058d), + new EpsgOperationParameterRecord(10589, "Scale difference", 0.207d), + new EpsgOperationParameterRecord(10607, "X-axis translation", 2.6d), + new EpsgOperationParameterRecord(10607, "Y-axis translation", 5.4d), + new EpsgOperationParameterRecord(10607, "Z-axis translation", -0.9d), + new EpsgOperationParameterRecord(10607, "X-axis rotation", -0.01d), + new EpsgOperationParameterRecord(10607, "Y-axis rotation", -0.07d), + new EpsgOperationParameterRecord(10607, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10607, "Scale difference", 0.06d), + new EpsgOperationParameterRecord(10608, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10608, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10608, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10608, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10608, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10608, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10608, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10614, "EPSG code for Interpolation CRS", 7886.0d), + new EpsgOperationParameterRecord(10617, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10618, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10619, "EPSG code for Interpolation CRS", 8252.0d), + new EpsgOperationParameterRecord(10620, "Vertical Offset", 0.0d), + new EpsgOperationParameterRecord(10646, "X-axis translation", 1138.7432d), + new EpsgOperationParameterRecord(10646, "Y-axis translation", -2064.4761d), + new EpsgOperationParameterRecord(10646, "Z-axis translation", 110.7016d), + new EpsgOperationParameterRecord(10646, "X-axis rotation", -214.615206d), + new EpsgOperationParameterRecord(10646, "Y-axis rotation", 479.360036d), + new EpsgOperationParameterRecord(10646, "Z-axis rotation", -164.703951d), + new EpsgOperationParameterRecord(10646, "Scale difference", -402.32073d), + new EpsgOperationParameterRecord(10647, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10647, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10647, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10647, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10647, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10647, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10647, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10647, "Rate of change of X-axis translation", 0.00726d), + new EpsgOperationParameterRecord(10647, "Rate of change of Y-axis translation", 0.00848d), + new EpsgOperationParameterRecord(10647, "Rate of change of Z-axis translation", 0.01353d), + new EpsgOperationParameterRecord(10647, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10647, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10647, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10647, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10647, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(10648, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10648, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10648, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10654, "EPSG code for Interpolation CRS", 4747.0d), + new EpsgOperationParameterRecord(10656, "EPSG code for Interpolation CRS", 4747.0d), + new EpsgOperationParameterRecord(10657, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10658, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10662, "EPSG code for Interpolation CRS", 11163.0d), + new EpsgOperationParameterRecord(10667, "EPSG code for Interpolation CRS", 11163.0d), + new EpsgOperationParameterRecord(10676, "X-axis translation", 1138.7432d), + new EpsgOperationParameterRecord(10676, "Y-axis translation", -2064.4761d), + new EpsgOperationParameterRecord(10676, "Z-axis translation", 110.7016d), + new EpsgOperationParameterRecord(10676, "X-axis rotation", -214.615206d), + new EpsgOperationParameterRecord(10676, "Y-axis rotation", 479.360036d), + new EpsgOperationParameterRecord(10676, "Z-axis rotation", -164.703951d), + new EpsgOperationParameterRecord(10676, "Scale difference", -402.32073d), + new EpsgOperationParameterRecord(10681, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10682, "X-axis translation", -0.5377d), + new EpsgOperationParameterRecord(10682, "Y-axis translation", 0.3946d), + new EpsgOperationParameterRecord(10682, "Z-axis translation", 0.3608d), + new EpsgOperationParameterRecord(10683, "EPSG code for Interpolation CRS", 10671.0d), + new EpsgOperationParameterRecord(10683, "EPSG code for standard transformation T0", 10682.0d), + new EpsgOperationParameterRecord(10684, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10684, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10684, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10685, "EPSG code for Interpolation CRS", 4765.0d), + new EpsgOperationParameterRecord(10694, "EPSG code for Interpolation CRS", 10690.0d), + new EpsgOperationParameterRecord(10696, "EPSG code for Interpolation CRS", 10690.0d), + new EpsgOperationParameterRecord(10698, "EPSG code for Interpolation CRS", 10690.0d), + new EpsgOperationParameterRecord(10701, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10701, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10701, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10703, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(10703, "Longitude of natural origin", 27.0d), + new EpsgOperationParameterRecord(10703, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10703, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(10703, "False northing", 0.0d), + new EpsgOperationParameterRecord(10703, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(10703, "Longitude of natural origin", 27.0d), + new EpsgOperationParameterRecord(10703, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(10703, "False easting", 500000.0d), + new EpsgOperationParameterRecord(10703, "False northing", 0.0d), + new EpsgOperationParameterRecord(10704, "EPSG code for Interpolation CRS", 2393.0d), + new EpsgOperationParameterRecord(10705, "EPSG code for Interpolation CRS", 2393.0d), + new EpsgOperationParameterRecord(10708, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10709, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10710, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10711, "EPSG code for Interpolation CRS", 8237.0d), + new EpsgOperationParameterRecord(10712, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10713, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10714, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10715, "EPSG code for Interpolation CRS", 8240.0d), + new EpsgOperationParameterRecord(10716, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10717, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10718, "EPSG code for Interpolation CRS", 8246.0d), + new EpsgOperationParameterRecord(10748, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10748, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10748, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10748, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10748, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10748, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10748, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10748, "Rate of change of X-axis translation", 0.00743d), + new EpsgOperationParameterRecord(10748, "Rate of change of Y-axis translation", 0.00875d), + new EpsgOperationParameterRecord(10748, "Rate of change of Z-axis translation", 0.01402d), + new EpsgOperationParameterRecord(10748, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10748, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10748, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10748, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10748, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(10749, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10749, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10749, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10750, "X-axis translation", 1276.2485d), + new EpsgOperationParameterRecord(10750, "Y-axis translation", -2016.6406d), + new EpsgOperationParameterRecord(10750, "Z-axis translation", 667.4403d), + new EpsgOperationParameterRecord(10750, "X-axis rotation", -101.005288d), + new EpsgOperationParameterRecord(10750, "Y-axis rotation", 212.913401d), + new EpsgOperationParameterRecord(10750, "Z-axis rotation", -68.43277d), + new EpsgOperationParameterRecord(10750, "Scale difference", -431.59604d), + new EpsgOperationParameterRecord(10752, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10753, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10766, "X-axis translation", -366.1939d), + new EpsgOperationParameterRecord(10766, "Y-axis translation", -115.0688d), + new EpsgOperationParameterRecord(10766, "Z-axis translation", -776.7039d), + new EpsgOperationParameterRecord(10766, "X-axis rotation", 20.96308d), + new EpsgOperationParameterRecord(10766, "Y-axis rotation", 16.462749d), + new EpsgOperationParameterRecord(10766, "Z-axis rotation", -14.276379d), + new EpsgOperationParameterRecord(10766, "Scale difference", -12.809d), + new EpsgOperationParameterRecord(10768, "EPSG code for Interpolation CRS", 10762.0d), + new EpsgOperationParameterRecord(10769, "X-axis translation", -366.1939d), + new EpsgOperationParameterRecord(10769, "Y-axis translation", -115.0688d), + new EpsgOperationParameterRecord(10769, "Z-axis translation", -776.7039d), + new EpsgOperationParameterRecord(10769, "X-axis rotation", 20.96308d), + new EpsgOperationParameterRecord(10769, "Y-axis rotation", 16.462749d), + new EpsgOperationParameterRecord(10769, "Z-axis rotation", -14.276379d), + new EpsgOperationParameterRecord(10769, "Scale difference", -12.809d), + new EpsgOperationParameterRecord(10770, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10770, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10770, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10771, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10771, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10771, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10776, "EPSG code for Interpolation CRS", 9333.0d), + new EpsgOperationParameterRecord(10777, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10777, "Y-axis translation", -1.1d), + new EpsgOperationParameterRecord(10777, "Z-axis translation", 1.8d), + new EpsgOperationParameterRecord(10777, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10777, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10777, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10777, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10777, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10777, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10777, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10777, "Rate of change of X-axis rotation", -1.199d), + new EpsgOperationParameterRecord(10777, "Rate of change of Y-axis rotation", 0.107d), + new EpsgOperationParameterRecord(10777, "Rate of change of Z-axis rotation", -1.468d), + new EpsgOperationParameterRecord(10777, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10777, "Parameter reference epoch", 2017.0d), + new EpsgOperationParameterRecord(10782, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10782, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10782, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10782, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10782, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10782, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10782, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10782, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10782, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10782, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10782, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10782, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10782, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10782, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10782, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10786, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10786, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10786, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10786, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10786, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10786, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10786, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10786, "Transformation reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10787, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10787, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10787, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10787, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10787, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10787, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10787, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10787, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10787, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10787, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10787, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10787, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10787, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10787, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10787, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10796, "X-axis translation", -136.7231d), + new EpsgOperationParameterRecord(10796, "Y-axis translation", -87.8654d), + new EpsgOperationParameterRecord(10796, "Z-axis translation", 20.1215d), + new EpsgOperationParameterRecord(10796, "X-axis rotation", 4.966933d), + new EpsgOperationParameterRecord(10796, "Y-axis rotation", -9.01001d), + new EpsgOperationParameterRecord(10796, "Z-axis rotation", -2.72486d), + new EpsgOperationParameterRecord(10796, "Scale difference", 7.86009d), + new EpsgOperationParameterRecord(10797, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10797, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10797, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10803, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10803, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10803, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10804, "X-axis translation", 1276.2485d), + new EpsgOperationParameterRecord(10804, "Y-axis translation", -2016.6406d), + new EpsgOperationParameterRecord(10804, "Z-axis translation", 667.4403d), + new EpsgOperationParameterRecord(10804, "X-axis rotation", -101.005288d), + new EpsgOperationParameterRecord(10804, "Y-axis rotation", 212.913401d), + new EpsgOperationParameterRecord(10804, "Z-axis rotation", -68.43277d), + new EpsgOperationParameterRecord(10804, "Scale difference", -431.59604d), + new EpsgOperationParameterRecord(10809, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10809, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10809, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10810, "X-axis translation", 0.15651d), + new EpsgOperationParameterRecord(10810, "Y-axis translation", -0.10993d), + new EpsgOperationParameterRecord(10810, "Z-axis translation", -0.10935d), + new EpsgOperationParameterRecord(10810, "X-axis rotation", -3.12861d), + new EpsgOperationParameterRecord(10810, "Y-axis rotation", -3.78935d), + new EpsgOperationParameterRecord(10810, "Z-axis rotation", 4.03512d), + new EpsgOperationParameterRecord(10810, "Scale difference", 5.29d), + new EpsgOperationParameterRecord(10810, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10810, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10810, "Target epoch", 1997.0d), + new EpsgOperationParameterRecord(10811, "EPSG code for Interpolation CRS for geocentric translation grid file", 10807.0d), + new EpsgOperationParameterRecord(10811, "EPSG code for Interpolation CRS for point motion velocity grid file", 10807.0d), + new EpsgOperationParameterRecord(10811, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10811, "Target epoch", 1995.0d), + new EpsgOperationParameterRecord(10812, "X-axis translation", 0.36749d), + new EpsgOperationParameterRecord(10812, "Y-axis translation", 0.14351d), + new EpsgOperationParameterRecord(10812, "Z-axis translation", -0.18472d), + new EpsgOperationParameterRecord(10812, "X-axis rotation", 4.7914d), + new EpsgOperationParameterRecord(10812, "Y-axis rotation", -10.27566d), + new EpsgOperationParameterRecord(10812, "Z-axis rotation", 2.76102d), + new EpsgOperationParameterRecord(10812, "Scale difference", -3.684d), + new EpsgOperationParameterRecord(10812, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10812, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10812, "Target epoch", 2003.75d), + new EpsgOperationParameterRecord(10813, "X-axis translation", 0.03054d), + new EpsgOperationParameterRecord(10813, "Y-axis translation", 0.04606d), + new EpsgOperationParameterRecord(10813, "Z-axis translation", -0.07944d), + new EpsgOperationParameterRecord(10813, "X-axis rotation", 1.41958d), + new EpsgOperationParameterRecord(10813, "Y-axis rotation", 0.15132d), + new EpsgOperationParameterRecord(10813, "Z-axis rotation", 1.50337d), + new EpsgOperationParameterRecord(10813, "Scale difference", 3.002d), + new EpsgOperationParameterRecord(10813, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10813, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10813, "Target epoch", 1999.5d), + new EpsgOperationParameterRecord(10814, "X-axis translation", -0.05027d), + new EpsgOperationParameterRecord(10814, "Y-axis translation", -0.11595d), + new EpsgOperationParameterRecord(10814, "Z-axis translation", 0.03012d), + new EpsgOperationParameterRecord(10814, "X-axis rotation", -3.10814d), + new EpsgOperationParameterRecord(10814, "Y-axis rotation", 4.57237d), + new EpsgOperationParameterRecord(10814, "Z-axis rotation", 4.72406d), + new EpsgOperationParameterRecord(10814, "Scale difference", 3.191d), + new EpsgOperationParameterRecord(10814, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10814, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10814, "Target epoch", 1997.56d), + new EpsgOperationParameterRecord(10822, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10823, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10828, "EPSG code for Interpolation CRS", 4661.0d), + new EpsgOperationParameterRecord(10834, "X-axis translation", -2.0796d), + new EpsgOperationParameterRecord(10834, "Y-axis translation", -0.3484d), + new EpsgOperationParameterRecord(10834, "Z-axis translation", 1.7009d), + new EpsgOperationParameterRecord(10834, "X-axis rotation", 0.05465d), + new EpsgOperationParameterRecord(10834, "Y-axis rotation", -0.06718d), + new EpsgOperationParameterRecord(10834, "Z-axis rotation", 0.06143d), + new EpsgOperationParameterRecord(10834, "Scale difference", 0.0181d), + new EpsgOperationParameterRecord(10835, "X-axis translation", -40.7436d), + new EpsgOperationParameterRecord(10835, "Y-axis translation", -40.0018d), + new EpsgOperationParameterRecord(10835, "Z-axis translation", -56.707d), + new EpsgOperationParameterRecord(10835, "X-axis rotation", -1.2753d), + new EpsgOperationParameterRecord(10835, "Y-axis rotation", -1.42112d), + new EpsgOperationParameterRecord(10835, "Z-axis rotation", 2.69445d), + new EpsgOperationParameterRecord(10835, "Scale difference", -4.5284d), + new EpsgOperationParameterRecord(10840, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10840, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10840, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10848, "X-axis translation", -47.48d), + new EpsgOperationParameterRecord(10848, "Y-axis translation", 11.76d), + new EpsgOperationParameterRecord(10848, "Z-axis translation", -0.58d), + new EpsgOperationParameterRecord(10848, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10848, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10848, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10848, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10848, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10848, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10848, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10848, "Rate of change of X-axis rotation", -0.099d), + new EpsgOperationParameterRecord(10848, "Rate of change of Y-axis rotation", 0.614d), + new EpsgOperationParameterRecord(10848, "Rate of change of Z-axis rotation", -0.733d), + new EpsgOperationParameterRecord(10848, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10848, "Parameter reference epoch", 2021.112d), + new EpsgOperationParameterRecord(10853, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10853, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10853, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10853, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10853, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10853, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(10853, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10853, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10853, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10853, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10853, "Rate of change of X-axis rotation", 1.3242d), + new EpsgOperationParameterRecord(10853, "Rate of change of Y-axis rotation", 1.2788d), + new EpsgOperationParameterRecord(10853, "Rate of change of Z-axis rotation", 1.1675d), + new EpsgOperationParameterRecord(10853, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10853, "Parameter reference epoch", 1994.0d), + new EpsgOperationParameterRecord(10858, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10859, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10867, "EPSG code for Interpolation CRS", 6311.0d), + new EpsgOperationParameterRecord(10887, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(10888, "X-axis translation", 53.7d), + new EpsgOperationParameterRecord(10888, "Y-axis translation", 51.2d), + new EpsgOperationParameterRecord(10888, "Z-axis translation", -55.1d), + new EpsgOperationParameterRecord(10888, "X-axis rotation", -0.044d), + new EpsgOperationParameterRecord(10888, "Y-axis rotation", -0.451d), + new EpsgOperationParameterRecord(10888, "Z-axis rotation", -0.242d), + new EpsgOperationParameterRecord(10888, "Scale difference", 1.02d), + new EpsgOperationParameterRecord(10888, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(10888, "Rate of change of Y-axis translation", 0.1d), + new EpsgOperationParameterRecord(10888, "Rate of change of Z-axis translation", -1.9d), + new EpsgOperationParameterRecord(10888, "Rate of change of X-axis rotation", -0.004d), + new EpsgOperationParameterRecord(10888, "Rate of change of Y-axis rotation", -0.041d), + new EpsgOperationParameterRecord(10888, "Rate of change of Z-axis rotation", -0.022d), + new EpsgOperationParameterRecord(10888, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(10888, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(10893, "X-axis translation", 0.66818d), + new EpsgOperationParameterRecord(10893, "Y-axis translation", 0.04453d), + new EpsgOperationParameterRecord(10893, "Z-axis translation", -0.45049d), + new EpsgOperationParameterRecord(10893, "X-axis rotation", 3.12883d), + new EpsgOperationParameterRecord(10893, "Y-axis rotation", -23.73423d), + new EpsgOperationParameterRecord(10893, "Z-axis rotation", 4.42969d), + new EpsgOperationParameterRecord(10893, "Scale difference", -3.136d), + new EpsgOperationParameterRecord(10893, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10893, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10893, "Target epoch", 2015.829d), + new EpsgOperationParameterRecord(10897, "EPSG code for Interpolation CRS", 5489.0d), + new EpsgOperationParameterRecord(10905, "X-axis translation", -646.6552d), + new EpsgOperationParameterRecord(10905, "Y-axis translation", -165.0859d), + new EpsgOperationParameterRecord(10905, "Z-axis translation", -437.6858d), + new EpsgOperationParameterRecord(10905, "X-axis rotation", 4.77773d), + new EpsgOperationParameterRecord(10905, "Y-axis rotation", -0.39139d), + new EpsgOperationParameterRecord(10905, "Z-axis rotation", -1.07485d), + new EpsgOperationParameterRecord(10905, "Scale difference", 2.0025d), + new EpsgOperationParameterRecord(10907, "X-axis translation", 6.3d), + new EpsgOperationParameterRecord(10907, "Y-axis translation", 5.7d), + new EpsgOperationParameterRecord(10907, "Z-axis translation", 23.7d), + new EpsgOperationParameterRecord(10907, "X-axis rotation", -1.309d), + new EpsgOperationParameterRecord(10907, "Y-axis rotation", -0.11d), + new EpsgOperationParameterRecord(10907, "Z-axis rotation", -1.622d), + new EpsgOperationParameterRecord(10907, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(10907, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10907, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10907, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10907, "Rate of change of X-axis rotation", -0.119d), + new EpsgOperationParameterRecord(10907, "Rate of change of Y-axis rotation", -0.01d), + new EpsgOperationParameterRecord(10907, "Rate of change of Z-axis rotation", -0.162d), + new EpsgOperationParameterRecord(10907, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(10907, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(10928, "EPSG code for Interpolation CRS", 10910.0d), + new EpsgOperationParameterRecord(10930, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10930, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10930, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10935, "X-axis translation", 57.1d), + new EpsgOperationParameterRecord(10935, "Y-axis translation", -6.1d), + new EpsgOperationParameterRecord(10935, "Z-axis translation", 1.1d), + new EpsgOperationParameterRecord(10935, "X-axis rotation", -0.919d), + new EpsgOperationParameterRecord(10935, "Y-axis rotation", -1.71d), + new EpsgOperationParameterRecord(10935, "Z-axis rotation", -1.042d), + new EpsgOperationParameterRecord(10935, "Scale difference", -2.07d), + new EpsgOperationParameterRecord(10935, "Rate of change of X-axis translation", 2.9d), + new EpsgOperationParameterRecord(10935, "Rate of change of Y-axis translation", 0.2d), + new EpsgOperationParameterRecord(10935, "Rate of change of Z-axis translation", 0.6d), + new EpsgOperationParameterRecord(10935, "Rate of change of X-axis rotation", -0.129d), + new EpsgOperationParameterRecord(10935, "Rate of change of Y-axis rotation", -0.1d), + new EpsgOperationParameterRecord(10935, "Rate of change of Z-axis rotation", -0.192d), + new EpsgOperationParameterRecord(10935, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(10935, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(10953, "X-axis translation", 1015.75d), + new EpsgOperationParameterRecord(10953, "Y-axis translation", -1920.11d), + new EpsgOperationParameterRecord(10953, "Z-axis translation", -559.77d), + new EpsgOperationParameterRecord(10953, "X-axis rotation", 27.78143d), + new EpsgOperationParameterRecord(10953, "Y-axis rotation", -11.78187d), + new EpsgOperationParameterRecord(10953, "Z-axis rotation", 10.16211d), + new EpsgOperationParameterRecord(10953, "Scale difference", -1.13124d), + new EpsgOperationParameterRecord(10960, "X-axis translation", -0.30031d), + new EpsgOperationParameterRecord(10960, "Y-axis translation", -1.17512d), + new EpsgOperationParameterRecord(10960, "Z-axis translation", -0.30654d), + new EpsgOperationParameterRecord(10960, "X-axis rotation", 0.041614d), + new EpsgOperationParameterRecord(10960, "Y-axis rotation", -0.026303d), + new EpsgOperationParameterRecord(10960, "Z-axis rotation", -0.011214d), + new EpsgOperationParameterRecord(10960, "Scale difference", -0.01626d), + new EpsgOperationParameterRecord(10960, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10960, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10960, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10960, "Rate of change of X-axis rotation", 4.5e-05d), + new EpsgOperationParameterRecord(10960, "Rate of change of Y-axis rotation", -0.000666d), + new EpsgOperationParameterRecord(10960, "Rate of change of Z-axis rotation", -9.8e-05d), + new EpsgOperationParameterRecord(10960, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10960, "Parameter reference epoch", 2021.6164d), + new EpsgOperationParameterRecord(10961, "X-axis translation", 308.9415d), + new EpsgOperationParameterRecord(10961, "Y-axis translation", 136.202d), + new EpsgOperationParameterRecord(10961, "Z-axis translation", 986.3661d), + new EpsgOperationParameterRecord(10961, "X-axis rotation", -3.8742d), + new EpsgOperationParameterRecord(10961, "Y-axis rotation", 3.77827d), + new EpsgOperationParameterRecord(10961, "Z-axis rotation", -7.61345d), + new EpsgOperationParameterRecord(10961, "Scale difference", -171.67315d), + new EpsgOperationParameterRecord(10963, "X-axis translation", 218.233d), + new EpsgOperationParameterRecord(10963, "Y-axis translation", 270.6151d), + new EpsgOperationParameterRecord(10963, "Z-axis translation", 253.1391d), + new EpsgOperationParameterRecord(10963, "X-axis rotation", 0.26337d), + new EpsgOperationParameterRecord(10963, "Y-axis rotation", -0.15733d), + new EpsgOperationParameterRecord(10963, "Z-axis rotation", -1.19862d), + new EpsgOperationParameterRecord(10963, "Scale difference", -59.923872d), + new EpsgOperationParameterRecord(10965, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10965, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10965, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10969, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10969, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10969, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10988, "X-axis translation", 24.3d), + new EpsgOperationParameterRecord(10988, "Y-axis translation", 10.7d), + new EpsgOperationParameterRecord(10988, "Z-axis translation", 42.7d), + new EpsgOperationParameterRecord(10988, "X-axis rotation", -0.319d), + new EpsgOperationParameterRecord(10988, "Y-axis rotation", -0.88d), + new EpsgOperationParameterRecord(10988, "Z-axis rotation", -0.962d), + new EpsgOperationParameterRecord(10988, "Scale difference", -5.97d), + new EpsgOperationParameterRecord(10988, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10988, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(10988, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10988, "Rate of change of X-axis rotation", -0.029d), + new EpsgOperationParameterRecord(10988, "Rate of change of Y-axis rotation", -0.08d), + new EpsgOperationParameterRecord(10988, "Rate of change of Z-axis rotation", -0.102d), + new EpsgOperationParameterRecord(10988, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(10988, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(10996, "X-axis translation", 19.019d), + new EpsgOperationParameterRecord(10996, "Y-axis translation", 115.122d), + new EpsgOperationParameterRecord(10996, "Z-axis translation", -97.287d), + new EpsgOperationParameterRecord(10996, "X-axis rotation", -3.577824d), + new EpsgOperationParameterRecord(10996, "Y-axis rotation", 3.484437d), + new EpsgOperationParameterRecord(10996, "Z-axis rotation", 2.767646d), + new EpsgOperationParameterRecord(10996, "Scale difference", 18.6084754d), + new EpsgOperationParameterRecord(10998, "EPSG code for Interpolation CRS", 11009.0d), + new EpsgOperationParameterRecord(11004, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(11010, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11010, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11010, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11011, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11011, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11011, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11028, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11028, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11028, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11034, "X-axis translation", -1.6d), + new EpsgOperationParameterRecord(11034, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(11034, "Z-axis translation", -2.4d), + new EpsgOperationParameterRecord(11034, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11034, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11034, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11034, "Scale difference", 0.02d), + new EpsgOperationParameterRecord(11034, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11034, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11034, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(11034, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11034, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11034, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11034, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(11034, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(11034, "EPSG code for Interpolation CRS", 9470.0d), + new EpsgOperationParameterRecord(11034, "Source epoch", 2012.0d), + new EpsgOperationParameterRecord(11034, "Target epoch", 2021.0d), + new EpsgOperationParameterRecord(11038, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11038, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11038, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11039, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11039, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11039, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11039, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11039, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11039, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11039, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11039, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11039, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11039, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11039, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11039, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11039, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11039, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11039, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11040, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11040, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11040, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11044, "X-axis translation", -0.0533d), + new EpsgOperationParameterRecord(11044, "Y-axis translation", 0.0136d), + new EpsgOperationParameterRecord(11044, "Z-axis translation", -0.0707d), + new EpsgOperationParameterRecord(11044, "X-axis rotation", 0.0122d), + new EpsgOperationParameterRecord(11044, "Y-axis rotation", -0.0284d), + new EpsgOperationParameterRecord(11044, "Z-axis rotation", -0.0037d), + new EpsgOperationParameterRecord(11044, "Scale difference", 0.0209d), + new EpsgOperationParameterRecord(11044, "Ordinate 1 of evaluation point", 629134.9009d), + new EpsgOperationParameterRecord(11044, "Ordinate 2 of evaluation point", -6249130.4704d), + new EpsgOperationParameterRecord(11044, "Ordinate 3 of evaluation point", 1101655.6817d), + new EpsgOperationParameterRecord(11048, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11048, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11048, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11049, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11049, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11049, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11049, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11049, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11049, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11049, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11049, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11049, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11049, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11049, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11049, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11049, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11049, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11049, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11050, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11050, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11050, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11054, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11054, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11054, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11058, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11058, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11058, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11059, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11059, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11059, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11059, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11059, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11059, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11059, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11059, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11059, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11059, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11059, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11059, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11059, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11059, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11059, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11060, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11060, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11060, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11064, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11064, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11064, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11065, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11065, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11065, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11065, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11065, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11065, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11065, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11065, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11065, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11065, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11065, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11065, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11065, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11065, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11065, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11067, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11067, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11067, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11071, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11071, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11071, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11072, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11072, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11072, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11072, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11072, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11072, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11072, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11072, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11072, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11072, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11072, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11072, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11072, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11072, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11072, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11073, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11073, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11073, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11080, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11080, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11080, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11081, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11081, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11081, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11082, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11082, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11082, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11082, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11082, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11082, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11082, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11082, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11082, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11082, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11082, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11082, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11082, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11082, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11082, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11083, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11083, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11083, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11084, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11084, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11084, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11088, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11088, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11088, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11089, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11089, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11089, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11089, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11089, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11089, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11089, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11089, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11089, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11089, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11089, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11089, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11089, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11089, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11089, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11090, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11090, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11090, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11094, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11094, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11094, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11095, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11095, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11095, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11095, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11095, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11095, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11095, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11095, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11095, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11095, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11095, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11095, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11095, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11095, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11095, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11096, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11096, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11096, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11100, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11100, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11100, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11104, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11104, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11104, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11104, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11104, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11104, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11104, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11104, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11104, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11104, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11104, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11104, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11104, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11104, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11104, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11105, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11105, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11105, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11109, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11109, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11109, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11111, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11111, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11111, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11122, "EPSG code for Interpolation CRS", 11087.0d), + new EpsgOperationParameterRecord(11123, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11123, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11123, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11124, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11124, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11124, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11124, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11124, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11124, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11124, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11124, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11124, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11124, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11124, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11124, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11124, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11124, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11124, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11125, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11125, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11125, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11135, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11135, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11135, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11136, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11136, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11136, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11137, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11137, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11137, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11138, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11138, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11138, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11139, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11139, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11139, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11139, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11139, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11139, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11139, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11139, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11139, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11139, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11139, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11139, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11139, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11139, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11139, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11149, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11149, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11149, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11150, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11150, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11150, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11151, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11151, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11151, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11152, "X-axis translation", -236.635d), + new EpsgOperationParameterRecord(11152, "Y-axis translation", 98.535d), + new EpsgOperationParameterRecord(11152, "Z-axis translation", 201.265d), + new EpsgOperationParameterRecord(11152, "X-axis rotation", -17.79d), + new EpsgOperationParameterRecord(11152, "Y-axis rotation", 3.673d), + new EpsgOperationParameterRecord(11152, "Z-axis rotation", -24.3695d), + new EpsgOperationParameterRecord(11152, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(11153, "X-axis translation", 6.3d), + new EpsgOperationParameterRecord(11153, "Y-axis translation", 11.5536d), + new EpsgOperationParameterRecord(11153, "Z-axis translation", 37.3584d), + new EpsgOperationParameterRecord(11153, "X-axis rotation", -2.46996d), + new EpsgOperationParameterRecord(11153, "Y-axis rotation", -0.20756d), + new EpsgOperationParameterRecord(11153, "Z-axis rotation", -3.20247d), + new EpsgOperationParameterRecord(11153, "Scale difference", -1.67756d), + new EpsgOperationParameterRecord(11153, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11153, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11153, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11153, "Rate of change of X-axis rotation", -0.119d), + new EpsgOperationParameterRecord(11153, "Rate of change of Y-axis rotation", -0.01d), + new EpsgOperationParameterRecord(11153, "Rate of change of Z-axis rotation", -0.162d), + new EpsgOperationParameterRecord(11153, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11153, "Parameter reference epoch", 2009.756d), + new EpsgOperationParameterRecord(11154, "X-axis translation", 6.3d), + new EpsgOperationParameterRecord(11154, "Y-axis translation", 2.94d), + new EpsgOperationParameterRecord(11154, "Z-axis translation", 17.26d), + new EpsgOperationParameterRecord(11154, "X-axis rotation", -0.7616d), + new EpsgOperationParameterRecord(11154, "Y-axis rotation", -0.064d), + new EpsgOperationParameterRecord(11154, "Z-axis rotation", -0.8768d), + new EpsgOperationParameterRecord(11154, "Scale difference", -1.534d), + new EpsgOperationParameterRecord(11154, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11154, "Rate of change of Y-axis translation", 0.06d), + new EpsgOperationParameterRecord(11154, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11154, "Rate of change of X-axis rotation", -0.119d), + new EpsgOperationParameterRecord(11154, "Rate of change of Y-axis rotation", -0.01d), + new EpsgOperationParameterRecord(11154, "Rate of change of Z-axis rotation", -0.162d), + new EpsgOperationParameterRecord(11154, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11154, "Parameter reference epoch", 1995.4d), + new EpsgOperationParameterRecord(11155, "X-axis translation", 6.3d), + new EpsgOperationParameterRecord(11155, "Y-axis translation", 5.7d), + new EpsgOperationParameterRecord(11155, "Z-axis translation", 23.7d), + new EpsgOperationParameterRecord(11155, "X-axis rotation", -0.389d), + new EpsgOperationParameterRecord(11155, "Y-axis rotation", 2.19d), + new EpsgOperationParameterRecord(11155, "Z-axis rotation", -4.612d), + new EpsgOperationParameterRecord(11155, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(11155, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11155, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11155, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11155, "Rate of change of X-axis rotation", -0.119d), + new EpsgOperationParameterRecord(11155, "Rate of change of Y-axis rotation", -0.01d), + new EpsgOperationParameterRecord(11155, "Rate of change of Z-axis rotation", -0.162d), + new EpsgOperationParameterRecord(11155, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11155, "Parameter reference epoch", 1995.4d), + new EpsgOperationParameterRecord(11156, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(11160, "EPSG code for Interpolation CRS", 10671.0d), + new EpsgOperationParameterRecord(11164, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11164, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11164, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11165, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11165, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11165, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11165, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11165, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11165, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11165, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11165, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11165, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11165, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11165, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11165, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11165, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11165, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11165, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11166, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11166, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11166, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11167, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11167, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11167, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11168, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11168, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11168, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11182, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11182, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11182, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11183, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11183, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11183, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11183, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11183, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11183, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11183, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11183, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11183, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11183, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11183, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11183, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11183, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11183, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11183, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11184, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11184, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11184, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11184, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11184, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11184, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11184, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11184, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11184, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11184, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11184, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11184, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11184, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11184, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11184, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11185, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11185, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11185, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11186, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11186, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11186, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11186, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11186, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11186, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11186, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11186, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11186, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11186, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11186, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11186, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11186, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11186, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11186, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11190, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11190, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11190, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11191, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11191, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11191, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11192, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11192, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11192, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11193, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11193, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11193, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11193, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11193, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11193, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11193, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11193, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11193, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11193, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11193, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11193, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11193, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11193, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11193, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11195, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11195, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11195, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11200, "X-axis translation", -41.1d), + new EpsgOperationParameterRecord(11200, "Y-axis translation", -52.0d), + new EpsgOperationParameterRecord(11200, "Z-axis translation", 101.1d), + new EpsgOperationParameterRecord(11200, "X-axis rotation", 1.348d), + new EpsgOperationParameterRecord(11200, "Y-axis rotation", 0.719d), + new EpsgOperationParameterRecord(11200, "Z-axis rotation", 2.684d), + new EpsgOperationParameterRecord(11200, "Scale difference", -7.9d), + new EpsgOperationParameterRecord(11204, "X-axis translation", 41.1393d), + new EpsgOperationParameterRecord(11204, "Y-axis translation", 51.983d), + new EpsgOperationParameterRecord(11204, "Z-axis translation", -101.1455d), + new EpsgOperationParameterRecord(11204, "X-axis rotation", 0.8878d), + new EpsgOperationParameterRecord(11204, "Y-axis rotation", 12.7748d), + new EpsgOperationParameterRecord(11204, "Z-axis rotation", -22.2616d), + new EpsgOperationParameterRecord(11204, "Scale difference", 7.8918d), + new EpsgOperationParameterRecord(11204, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11204, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11204, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11204, "Rate of change of X-axis rotation", 0.086d), + new EpsgOperationParameterRecord(11204, "Rate of change of Y-axis rotation", 0.519d), + new EpsgOperationParameterRecord(11204, "Rate of change of Z-axis rotation", -0.753d), + new EpsgOperationParameterRecord(11204, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(11204, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11205, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11205, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11205, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11207, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11207, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11207, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11208, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11208, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11208, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11208, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11208, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11208, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11208, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11208, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11208, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11208, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11208, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11208, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11208, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11208, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11208, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11209, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11209, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11209, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11210, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11210, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11210, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11211, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11211, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11211, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11211, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11211, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11211, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11211, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11211, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11211, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11211, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11211, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11211, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11211, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11211, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11211, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11212, "X-axis translation", 290.435d), + new EpsgOperationParameterRecord(11212, "Y-axis translation", -46.735d), + new EpsgOperationParameterRecord(11212, "Z-axis translation", -283.465d), + new EpsgOperationParameterRecord(11212, "X-axis rotation", 19.896d), + new EpsgOperationParameterRecord(11212, "Y-axis rotation", 9.067d), + new EpsgOperationParameterRecord(11212, "Z-axis rotation", 3.7775d), + new EpsgOperationParameterRecord(11212, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11212, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11212, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11212, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11212, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11212, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11212, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11212, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11212, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11216, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11216, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11216, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11217, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11217, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11217, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11217, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11217, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11217, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11217, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11217, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11217, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11217, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11217, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11217, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11217, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11217, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11217, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11218, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11218, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11218, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11220, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11220, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11220, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11221, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11221, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11221, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11221, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11221, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11221, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11221, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11221, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11221, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11221, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11221, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11221, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11221, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11221, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11221, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11227, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11227, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11227, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11228, "X-axis translation", 6.3d), + new EpsgOperationParameterRecord(11228, "Y-axis translation", 5.7d), + new EpsgOperationParameterRecord(11228, "Z-axis translation", 23.7d), + new EpsgOperationParameterRecord(11228, "X-axis rotation", -1.309d), + new EpsgOperationParameterRecord(11228, "Y-axis rotation", -0.11d), + new EpsgOperationParameterRecord(11228, "Z-axis rotation", -1.622d), + new EpsgOperationParameterRecord(11228, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(11228, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11228, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11228, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11228, "Rate of change of X-axis rotation", -0.119d), + new EpsgOperationParameterRecord(11228, "Rate of change of Y-axis rotation", -0.01d), + new EpsgOperationParameterRecord(11228, "Rate of change of Z-axis rotation", -0.162d), + new EpsgOperationParameterRecord(11228, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11228, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(11229, "X-axis translation", 197.8579d), + new EpsgOperationParameterRecord(11229, "Y-axis translation", 146.5947d), + new EpsgOperationParameterRecord(11229, "Z-axis translation", -108.8501d), + new EpsgOperationParameterRecord(11229, "X-axis rotation", -0.85735d), + new EpsgOperationParameterRecord(11229, "Y-axis rotation", 0.36082d), + new EpsgOperationParameterRecord(11229, "Z-axis rotation", 0.38626d), + new EpsgOperationParameterRecord(11229, "Scale difference", -8.356137d), + new EpsgOperationParameterRecord(11275, "EPSG code for Interpolation CRS", 11030.0d), + new EpsgOperationParameterRecord(11286, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11286, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11286, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11286, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11286, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11286, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11286, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11286, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11286, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11286, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11286, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11286, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11286, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11286, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11286, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11308, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11308, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11308, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11309, "X-axis translation", 24.3d), + new EpsgOperationParameterRecord(11309, "Y-axis translation", 6.5d), + new EpsgOperationParameterRecord(11309, "Z-axis translation", 32.9d), + new EpsgOperationParameterRecord(11309, "X-axis rotation", -0.116d), + new EpsgOperationParameterRecord(11309, "Y-axis rotation", -0.32d), + new EpsgOperationParameterRecord(11309, "Z-axis rotation", -0.248d), + new EpsgOperationParameterRecord(11309, "Scale difference", -5.9d), + new EpsgOperationParameterRecord(11309, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11309, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11309, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11309, "Rate of change of X-axis rotation", -0.029d), + new EpsgOperationParameterRecord(11309, "Rate of change of Y-axis rotation", -0.08d), + new EpsgOperationParameterRecord(11309, "Rate of change of Z-axis rotation", -0.102d), + new EpsgOperationParameterRecord(11309, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11309, "Parameter reference epoch", 1993.0d), + new EpsgOperationParameterRecord(11316, "X-axis translation", -0.03958d), + new EpsgOperationParameterRecord(11316, "Y-axis translation", -0.05079d), + new EpsgOperationParameterRecord(11316, "Z-axis translation", 0.05751d), + new EpsgOperationParameterRecord(11316, "X-axis rotation", -1.70334d), + new EpsgOperationParameterRecord(11316, "Y-axis rotation", 1.7302d), + new EpsgOperationParameterRecord(11316, "Z-axis rotation", 1.3038d), + new EpsgOperationParameterRecord(11316, "Scale difference", -2.789d), + new EpsgOperationParameterRecord(11316, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(11316, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(11316, "Target epoch", 2020.28d), + new EpsgOperationParameterRecord(11384, "EPSG code for Interpolation CRS", 11226.0d), + new EpsgOperationParameterRecord(11387, "EPSG code for Interpolation CRS", 6365.0d), + new EpsgOperationParameterRecord(11388, "Latitude of natural origin", 49.0d), + new EpsgOperationParameterRecord(11388, "Longitude of natural origin", -2.0d), + new EpsgOperationParameterRecord(11388, "Scale factor at natural origin", 0.9996012717d), + new EpsgOperationParameterRecord(11388, "False easting", 400000.0d), + new EpsgOperationParameterRecord(11388, "False northing", -100000.0d), + new EpsgOperationParameterRecord(11388, "Ordinate 1 of evaluation point in target CRS", -504786.4675d), + new EpsgOperationParameterRecord(11388, "Ordinate 2 of evaluation point in target CRS", -156728.1037d), + new EpsgOperationParameterRecord(11388, "Scale factor for source CRS axes", 1.000259617024d), + new EpsgOperationParameterRecord(11388, "Rotation angle of source CRS axes", 0.02636190913444d), + new EpsgOperationParameterRecord(11396, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(11397, "X-axis translation", 565.7381d), + new EpsgOperationParameterRecord(11397, "Y-axis translation", 50.4018d), + new EpsgOperationParameterRecord(11397, "Z-axis translation", 465.2904d), + new EpsgOperationParameterRecord(11397, "X-axis rotation", 1.91514d), + new EpsgOperationParameterRecord(11397, "Y-axis rotation", -1.60363d), + new EpsgOperationParameterRecord(11397, "Z-axis rotation", 9.09546d), + new EpsgOperationParameterRecord(11397, "Scale difference", 4.07244d), + new EpsgOperationParameterRecord(11449, "EPSG code for Interpolation CRS", 8900.0d), + new EpsgOperationParameterRecord(15483, "X-axis translation", -146.414d), + new EpsgOperationParameterRecord(15483, "Y-axis translation", 507.337d), + new EpsgOperationParameterRecord(15483, "Z-axis translation", 680.507d), + new EpsgOperationParameterRecord(15484, "X-axis translation", -146.414d), + new EpsgOperationParameterRecord(15484, "Y-axis translation", 507.337d), + new EpsgOperationParameterRecord(15484, "Z-axis translation", 680.507d), + new EpsgOperationParameterRecord(15485, "X-axis translation", -67.35d), + new EpsgOperationParameterRecord(15485, "Y-axis translation", 3.88d), + new EpsgOperationParameterRecord(15485, "Z-axis translation", -38.22d), + new EpsgOperationParameterRecord(15487, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15487, "Longitude of natural origin", 121.0d), + new EpsgOperationParameterRecord(15487, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(15487, "False easting", 250000.0d), + new EpsgOperationParameterRecord(15487, "False northing", 0.0d), + new EpsgOperationParameterRecord(15487, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15487, "Longitude of natural origin", 121.0d), + new EpsgOperationParameterRecord(15487, "Scale factor at natural origin", 0.9999d), + new EpsgOperationParameterRecord(15487, "False easting", 250000.0d), + new EpsgOperationParameterRecord(15487, "False northing", 0.0d), + new EpsgOperationParameterRecord(15487, "Easting offset", 828.589d), + new EpsgOperationParameterRecord(15487, "Northing offset", -206.915d), + new EpsgOperationParameterRecord(15493, "X-axis translation", -94.031d), + new EpsgOperationParameterRecord(15493, "Y-axis translation", -83.317d), + new EpsgOperationParameterRecord(15493, "Z-axis translation", 116.708d), + new EpsgOperationParameterRecord(15494, "X-axis translation", 274.164d), + new EpsgOperationParameterRecord(15494, "Y-axis translation", 677.282d), + new EpsgOperationParameterRecord(15494, "Z-axis translation", 226.704d), + new EpsgOperationParameterRecord(15495, "X-axis translation", -171.16d), + new EpsgOperationParameterRecord(15495, "Y-axis translation", 17.29d), + new EpsgOperationParameterRecord(15495, "Z-axis translation", 325.21d), + new EpsgOperationParameterRecord(15495, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15495, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15495, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15495, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(15496, "X-axis translation", 44.107d), + new EpsgOperationParameterRecord(15496, "Y-axis translation", -116.147d), + new EpsgOperationParameterRecord(15496, "Z-axis translation", -54.648d), + new EpsgOperationParameterRecord(15497, "X-axis translation", 28.0d), + new EpsgOperationParameterRecord(15497, "Y-axis translation", -121.0d), + new EpsgOperationParameterRecord(15497, "Z-axis translation", -77.0d), + new EpsgOperationParameterRecord(15596, "Latitude offset", 7.94d), + new EpsgOperationParameterRecord(15596, "Longitude offset", -13.97d), + new EpsgOperationParameterRecord(15596, "Geoid height", 26.9d), + new EpsgOperationParameterRecord(15597, "Latitude offset", 8.1d), + new EpsgOperationParameterRecord(15597, "Longitude offset", -13.81d), + new EpsgOperationParameterRecord(15597, "Geoid height", 27.2d), + new EpsgOperationParameterRecord(15598, "Latitude offset", 8.15d), + new EpsgOperationParameterRecord(15598, "Longitude offset", -13.95d), + new EpsgOperationParameterRecord(15598, "Geoid height", 28.4d), + new EpsgOperationParameterRecord(15599, "Latitude offset", 8.37d), + new EpsgOperationParameterRecord(15599, "Longitude offset", -13.65d), + new EpsgOperationParameterRecord(15599, "Geoid height", 29.0d), + new EpsgOperationParameterRecord(15600, "Latitude offset", 8.44d), + new EpsgOperationParameterRecord(15600, "Longitude offset", -13.87d), + new EpsgOperationParameterRecord(15600, "Geoid height", 30.9d), + new EpsgOperationParameterRecord(15601, "Latitude offset", 8.61d), + new EpsgOperationParameterRecord(15601, "Longitude offset", -14.08d), + new EpsgOperationParameterRecord(15601, "Geoid height", 30.7d), + new EpsgOperationParameterRecord(15602, "Latitude offset", 8.73d), + new EpsgOperationParameterRecord(15602, "Longitude offset", -14.3d), + new EpsgOperationParameterRecord(15602, "Geoid height", 30.9d), + new EpsgOperationParameterRecord(15603, "Latitude offset", 8.63d), + new EpsgOperationParameterRecord(15603, "Longitude offset", -13.49d), + new EpsgOperationParameterRecord(15603, "Geoid height", 30.9d), + new EpsgOperationParameterRecord(15604, "Latitude offset", 8.71d), + new EpsgOperationParameterRecord(15604, "Longitude offset", -13.73d), + new EpsgOperationParameterRecord(15604, "Geoid height", 31.6d), + new EpsgOperationParameterRecord(15605, "Latitude offset", 8.84d), + new EpsgOperationParameterRecord(15605, "Longitude offset", -14.03d), + new EpsgOperationParameterRecord(15605, "Geoid height", 31.2d), + new EpsgOperationParameterRecord(15606, "Latitude offset", 8.98d), + new EpsgOperationParameterRecord(15606, "Longitude offset", -14.33d), + new EpsgOperationParameterRecord(15606, "Geoid height", 32.5d), + new EpsgOperationParameterRecord(15607, "Latitude offset", 9.1d), + new EpsgOperationParameterRecord(15607, "Longitude offset", -14.56d), + new EpsgOperationParameterRecord(15607, "Geoid height", 32.6d), + new EpsgOperationParameterRecord(15608, "Latitude offset", 8.79d), + new EpsgOperationParameterRecord(15608, "Longitude offset", -13.0d), + new EpsgOperationParameterRecord(15608, "Geoid height", 33.3d), + new EpsgOperationParameterRecord(15609, "Latitude offset", 8.84d), + new EpsgOperationParameterRecord(15609, "Longitude offset", -13.31d), + new EpsgOperationParameterRecord(15609, "Geoid height", 31.4d), + new EpsgOperationParameterRecord(15610, "Latitude offset", 8.98d), + new EpsgOperationParameterRecord(15610, "Longitude offset", -13.59d), + new EpsgOperationParameterRecord(15610, "Geoid height", 30.9d), + new EpsgOperationParameterRecord(15611, "Latitude offset", 9.1d), + new EpsgOperationParameterRecord(15611, "Longitude offset", -13.91d), + new EpsgOperationParameterRecord(15611, "Geoid height", 29.3d), + new EpsgOperationParameterRecord(15612, "Latitude offset", 9.17d), + new EpsgOperationParameterRecord(15612, "Longitude offset", -14.27d), + new EpsgOperationParameterRecord(15612, "Geoid height", 31.3d), + new EpsgOperationParameterRecord(15613, "Latitude offset", 9.23d), + new EpsgOperationParameterRecord(15613, "Longitude offset", -14.52d), + new EpsgOperationParameterRecord(15613, "Geoid height", 31.4d), + new EpsgOperationParameterRecord(15614, "Latitude offset", 8.9d), + new EpsgOperationParameterRecord(15614, "Longitude offset", -12.68d), + new EpsgOperationParameterRecord(15614, "Geoid height", 34.4d), + new EpsgOperationParameterRecord(15615, "Latitude offset", 8.99d), + new EpsgOperationParameterRecord(15615, "Longitude offset", -12.8d), + new EpsgOperationParameterRecord(15615, "Geoid height", 34.2d), + new EpsgOperationParameterRecord(15616, "Latitude offset", 9.0d), + new EpsgOperationParameterRecord(15616, "Longitude offset", -13.07d), + new EpsgOperationParameterRecord(15616, "Geoid height", 31.7d), + new EpsgOperationParameterRecord(15617, "Latitude offset", 9.21d), + new EpsgOperationParameterRecord(15617, "Longitude offset", -13.51d), + new EpsgOperationParameterRecord(15617, "Geoid height", 27.5d), + new EpsgOperationParameterRecord(15618, "Latitude offset", 9.33d), + new EpsgOperationParameterRecord(15618, "Longitude offset", -13.66d), + new EpsgOperationParameterRecord(15618, "Geoid height", 23.8d), + new EpsgOperationParameterRecord(15619, "Latitude offset", 9.25d), + new EpsgOperationParameterRecord(15619, "Longitude offset", -12.72d), + new EpsgOperationParameterRecord(15619, "Geoid height", 34.2d), + new EpsgOperationParameterRecord(15620, "Latitude offset", 9.39d), + new EpsgOperationParameterRecord(15620, "Longitude offset", -12.91d), + new EpsgOperationParameterRecord(15620, "Geoid height", 31.8d), + new EpsgOperationParameterRecord(15621, "Latitude offset", 9.55d), + new EpsgOperationParameterRecord(15621, "Longitude offset", -12.63d), + new EpsgOperationParameterRecord(15621, "Geoid height", 35.6d), + new EpsgOperationParameterRecord(15622, "Latitude offset", 9.62d), + new EpsgOperationParameterRecord(15622, "Longitude offset", -12.82d), + new EpsgOperationParameterRecord(15622, "Geoid height", 34.7d), + new EpsgOperationParameterRecord(15623, "Latitude offset", 9.81d), + new EpsgOperationParameterRecord(15623, "Longitude offset", -12.29d), + new EpsgOperationParameterRecord(15623, "Geoid height", 36.6d), + new EpsgOperationParameterRecord(15624, "Latitude offset", 9.81d), + new EpsgOperationParameterRecord(15624, "Longitude offset", -12.45d), + new EpsgOperationParameterRecord(15624, "Geoid height", 37.5d), + new EpsgOperationParameterRecord(15625, "Latitude offset", 9.92d), + new EpsgOperationParameterRecord(15625, "Longitude offset", -12.79d), + new EpsgOperationParameterRecord(15625, "Geoid height", 38.3d), + new EpsgOperationParameterRecord(15626, "Latitude offset", 9.91d), + new EpsgOperationParameterRecord(15626, "Longitude offset", -12.21d), + new EpsgOperationParameterRecord(15626, "Geoid height", 36.6d), + new EpsgOperationParameterRecord(15627, "Latitude offset", 10.08d), + new EpsgOperationParameterRecord(15627, "Longitude offset", -12.35d), + new EpsgOperationParameterRecord(15627, "Geoid height", 39.0d), + new EpsgOperationParameterRecord(15628, "Latitude offset", 10.19d), + new EpsgOperationParameterRecord(15628, "Longitude offset", -12.74d), + new EpsgOperationParameterRecord(15628, "Geoid height", 40.3d), + new EpsgOperationParameterRecord(15629, "Latitude offset", 10.29d), + new EpsgOperationParameterRecord(15629, "Longitude offset", -12.13d), + new EpsgOperationParameterRecord(15629, "Geoid height", 38.5d), + new EpsgOperationParameterRecord(15630, "Latitude offset", 10.33d), + new EpsgOperationParameterRecord(15630, "Longitude offset", -12.27d), + new EpsgOperationParameterRecord(15630, "Geoid height", 40.1d), + new EpsgOperationParameterRecord(15631, "Latitude offset", 10.45d), + new EpsgOperationParameterRecord(15631, "Longitude offset", -12.61d), + new EpsgOperationParameterRecord(15631, "Geoid height", 41.7d), + new EpsgOperationParameterRecord(15632, "Latitude offset", 10.54d), + new EpsgOperationParameterRecord(15632, "Longitude offset", -11.96d), + new EpsgOperationParameterRecord(15632, "Geoid height", 39.1d), + new EpsgOperationParameterRecord(15633, "Latitude offset", 10.65d), + new EpsgOperationParameterRecord(15633, "Longitude offset", -12.27d), + new EpsgOperationParameterRecord(15633, "Geoid height", 41.7d), + new EpsgOperationParameterRecord(15634, "Latitude offset", 10.67d), + new EpsgOperationParameterRecord(15634, "Longitude offset", -12.5d), + new EpsgOperationParameterRecord(15634, "Geoid height", 41.1d), + new EpsgOperationParameterRecord(15635, "Latitude offset", 10.67d), + new EpsgOperationParameterRecord(15635, "Longitude offset", -10.86d), + new EpsgOperationParameterRecord(15635, "Geoid height", 38.5d), + new EpsgOperationParameterRecord(15636, "Latitude offset", 10.68d), + new EpsgOperationParameterRecord(15636, "Longitude offset", -10.97d), + new EpsgOperationParameterRecord(15636, "Geoid height", 36.0d), + new EpsgOperationParameterRecord(15637, "Latitude offset", 10.8d), + new EpsgOperationParameterRecord(15637, "Longitude offset", -11.53d), + new EpsgOperationParameterRecord(15637, "Geoid height", 39.7d), + new EpsgOperationParameterRecord(15638, "Latitude offset", 10.8d), + new EpsgOperationParameterRecord(15638, "Longitude offset", -11.73d), + new EpsgOperationParameterRecord(15638, "Geoid height", 40.9d), + new EpsgOperationParameterRecord(15639, "Latitude offset", 10.92d), + new EpsgOperationParameterRecord(15639, "Longitude offset", -12.16d), + new EpsgOperationParameterRecord(15639, "Geoid height", 42.3d), + new EpsgOperationParameterRecord(15640, "Latitude offset", 11.0d), + new EpsgOperationParameterRecord(15640, "Longitude offset", -12.25d), + new EpsgOperationParameterRecord(15640, "Geoid height", 41.2d), + new EpsgOperationParameterRecord(15641, "Latitude offset", 10.83d), + new EpsgOperationParameterRecord(15641, "Longitude offset", -10.77d), + new EpsgOperationParameterRecord(15641, "Geoid height", 36.2d), + new EpsgOperationParameterRecord(15642, "Latitude offset", 10.95d), + new EpsgOperationParameterRecord(15642, "Longitude offset", -11.0d), + new EpsgOperationParameterRecord(15642, "Geoid height", 38.7d), + new EpsgOperationParameterRecord(15643, "Latitude offset", 10.97d), + new EpsgOperationParameterRecord(15643, "Longitude offset", -11.34d), + new EpsgOperationParameterRecord(15643, "Geoid height", 40.8d), + new EpsgOperationParameterRecord(15644, "Latitude offset", 11.04d), + new EpsgOperationParameterRecord(15644, "Longitude offset", -11.69d), + new EpsgOperationParameterRecord(15644, "Geoid height", 43.3d), + new EpsgOperationParameterRecord(15645, "Latitude offset", 11.17d), + new EpsgOperationParameterRecord(15645, "Longitude offset", -12.05d), + new EpsgOperationParameterRecord(15645, "Geoid height", 42.6d), + new EpsgOperationParameterRecord(15646, "Latitude offset", 11.11d), + new EpsgOperationParameterRecord(15646, "Longitude offset", -10.59d), + new EpsgOperationParameterRecord(15646, "Geoid height", 37.3d), + new EpsgOperationParameterRecord(15647, "Latitude offset", 11.16d), + new EpsgOperationParameterRecord(15647, "Longitude offset", -10.97d), + new EpsgOperationParameterRecord(15647, "Geoid height", 40.3d), + new EpsgOperationParameterRecord(15648, "Latitude offset", 11.29d), + new EpsgOperationParameterRecord(15648, "Longitude offset", -11.23d), + new EpsgOperationParameterRecord(15648, "Geoid height", 42.4d), + new EpsgOperationParameterRecord(15649, "Latitude offset", 11.36d), + new EpsgOperationParameterRecord(15649, "Longitude offset", -11.59d), + new EpsgOperationParameterRecord(15649, "Geoid height", 42.5d), + new EpsgOperationParameterRecord(15650, "Latitude offset", 11.44d), + new EpsgOperationParameterRecord(15650, "Longitude offset", -11.88d), + new EpsgOperationParameterRecord(15650, "Geoid height", 40.3d), + new EpsgOperationParameterRecord(15651, "Latitude offset", 11.27d), + new EpsgOperationParameterRecord(15651, "Longitude offset", -9.31d), + new EpsgOperationParameterRecord(15651, "Geoid height", 30.9d), + new EpsgOperationParameterRecord(15652, "Latitude offset", 11.33d), + new EpsgOperationParameterRecord(15652, "Longitude offset", -9.52d), + new EpsgOperationParameterRecord(15652, "Geoid height", 33.8d), + new EpsgOperationParameterRecord(15653, "Latitude offset", 11.38d), + new EpsgOperationParameterRecord(15653, "Longitude offset", -9.86d), + new EpsgOperationParameterRecord(15653, "Geoid height", 34.9d), + new EpsgOperationParameterRecord(15654, "Latitude offset", 11.41d), + new EpsgOperationParameterRecord(15654, "Longitude offset", -10.14d), + new EpsgOperationParameterRecord(15654, "Geoid height", 35.7d), + new EpsgOperationParameterRecord(15655, "Latitude offset", 11.39d), + new EpsgOperationParameterRecord(15655, "Longitude offset", -10.52d), + new EpsgOperationParameterRecord(15655, "Geoid height", 37.5d), + new EpsgOperationParameterRecord(15656, "Latitude offset", 11.49d), + new EpsgOperationParameterRecord(15656, "Longitude offset", -10.83d), + new EpsgOperationParameterRecord(15656, "Geoid height", 39.3d), + new EpsgOperationParameterRecord(15657, "Latitude offset", 11.58d), + new EpsgOperationParameterRecord(15657, "Longitude offset", -11.21d), + new EpsgOperationParameterRecord(15657, "Geoid height", 41.7d), + new EpsgOperationParameterRecord(15658, "Latitude offset", 11.65d), + new EpsgOperationParameterRecord(15658, "Longitude offset", -11.53d), + new EpsgOperationParameterRecord(15658, "Geoid height", 38.5d), + new EpsgOperationParameterRecord(15659, "Latitude offset", 11.72d), + new EpsgOperationParameterRecord(15659, "Longitude offset", -11.8d), + new EpsgOperationParameterRecord(15659, "Geoid height", 34.5d), + new EpsgOperationParameterRecord(15660, "Latitude offset", 11.44d), + new EpsgOperationParameterRecord(15660, "Longitude offset", -9.21d), + new EpsgOperationParameterRecord(15660, "Geoid height", 32.7d), + new EpsgOperationParameterRecord(15661, "Latitude offset", 11.47d), + new EpsgOperationParameterRecord(15661, "Longitude offset", -9.52d), + new EpsgOperationParameterRecord(15661, "Geoid height", 35.2d), + new EpsgOperationParameterRecord(15662, "Latitude offset", 11.55d), + new EpsgOperationParameterRecord(15662, "Longitude offset", -9.8d), + new EpsgOperationParameterRecord(15662, "Geoid height", 35.4d), + new EpsgOperationParameterRecord(15663, "Latitude offset", 11.61d), + new EpsgOperationParameterRecord(15663, "Longitude offset", -10.12d), + new EpsgOperationParameterRecord(15663, "Geoid height", 35.9d), + new EpsgOperationParameterRecord(15664, "Latitude offset", 11.66d), + new EpsgOperationParameterRecord(15664, "Longitude offset", -10.47d), + new EpsgOperationParameterRecord(15664, "Geoid height", 37.0d), + new EpsgOperationParameterRecord(15665, "Latitude offset", 11.78d), + new EpsgOperationParameterRecord(15665, "Longitude offset", -10.79d), + new EpsgOperationParameterRecord(15665, "Geoid height", 39.8d), + new EpsgOperationParameterRecord(15666, "Latitude offset", 11.85d), + new EpsgOperationParameterRecord(15666, "Longitude offset", -11.13d), + new EpsgOperationParameterRecord(15666, "Geoid height", 39.9d), + new EpsgOperationParameterRecord(15667, "Latitude offset", 11.9d), + new EpsgOperationParameterRecord(15667, "Longitude offset", -11.47d), + new EpsgOperationParameterRecord(15667, "Geoid height", 36.9d), + new EpsgOperationParameterRecord(15668, "Latitude offset", 11.91d), + new EpsgOperationParameterRecord(15668, "Longitude offset", -11.69d), + new EpsgOperationParameterRecord(15668, "Geoid height", 33.7d), + new EpsgOperationParameterRecord(15669, "Latitude offset", 11.65d), + new EpsgOperationParameterRecord(15669, "Longitude offset", -8.59d), + new EpsgOperationParameterRecord(15669, "Geoid height", 29.7d), + new EpsgOperationParameterRecord(15670, "Latitude offset", 11.68d), + new EpsgOperationParameterRecord(15670, "Longitude offset", -8.8d), + new EpsgOperationParameterRecord(15670, "Geoid height", 30.5d), + new EpsgOperationParameterRecord(15671, "Latitude offset", 11.73d), + new EpsgOperationParameterRecord(15671, "Longitude offset", -9.04d), + new EpsgOperationParameterRecord(15671, "Geoid height", 30.9d), + new EpsgOperationParameterRecord(15672, "Latitude offset", 11.72d), + new EpsgOperationParameterRecord(15672, "Longitude offset", -9.48d), + new EpsgOperationParameterRecord(15672, "Geoid height", 35.1d), + new EpsgOperationParameterRecord(15673, "Latitude offset", 11.81d), + new EpsgOperationParameterRecord(15673, "Longitude offset", 9.74d), + new EpsgOperationParameterRecord(15673, "Geoid height", 35.8d), + new EpsgOperationParameterRecord(15674, "Latitude offset", 11.88d), + new EpsgOperationParameterRecord(15674, "Longitude offset", -10.1d), + new EpsgOperationParameterRecord(15674, "Geoid height", 37.1d), + new EpsgOperationParameterRecord(15675, "Latitude offset", 11.91d), + new EpsgOperationParameterRecord(15675, "Longitude offset", -10.35d), + new EpsgOperationParameterRecord(15675, "Geoid height", 37.9d), + new EpsgOperationParameterRecord(15676, "Latitude offset", 11.9d), + new EpsgOperationParameterRecord(15676, "Longitude offset", -10.7d), + new EpsgOperationParameterRecord(15676, "Geoid height", 39.3d), + new EpsgOperationParameterRecord(15677, "Latitude offset", 12.02d), + new EpsgOperationParameterRecord(15677, "Longitude offset", -11.09d), + new EpsgOperationParameterRecord(15677, "Geoid height", 38.2d), + new EpsgOperationParameterRecord(15678, "Latitude offset", 11.87d), + new EpsgOperationParameterRecord(15678, "Longitude offset", -8.23d), + new EpsgOperationParameterRecord(15678, "Geoid height", 29.7d), + new EpsgOperationParameterRecord(15679, "Latitude offset", 11.84d), + new EpsgOperationParameterRecord(15679, "Longitude offset", -8.44d), + new EpsgOperationParameterRecord(15679, "Geoid height", 30.6d), + new EpsgOperationParameterRecord(15680, "Latitude offset", 11.94d), + new EpsgOperationParameterRecord(15680, "Longitude offset", -8.71d), + new EpsgOperationParameterRecord(15680, "Geoid height", 30.2d), + new EpsgOperationParameterRecord(15681, "Latitude offset", 11.99d), + new EpsgOperationParameterRecord(15681, "Longitude offset", -9.02d), + new EpsgOperationParameterRecord(15681, "Geoid height", 30.9d), + new EpsgOperationParameterRecord(15682, "Latitude offset", 12.05d), + new EpsgOperationParameterRecord(15682, "Longitude offset", -9.36d), + new EpsgOperationParameterRecord(15682, "Geoid height", 35.0d), + new EpsgOperationParameterRecord(15683, "Latitude offset", 12.1d), + new EpsgOperationParameterRecord(15683, "Longitude offset", -9.64d), + new EpsgOperationParameterRecord(15683, "Geoid height", 35.5d), + new EpsgOperationParameterRecord(15684, "Latitude offset", 12.1d), + new EpsgOperationParameterRecord(15684, "Longitude offset", -10.08d), + new EpsgOperationParameterRecord(15684, "Geoid height", 37.3d), + new EpsgOperationParameterRecord(15685, "Latitude offset", 12.07d), + new EpsgOperationParameterRecord(15685, "Longitude offset", -10.25d), + new EpsgOperationParameterRecord(15685, "Geoid height", 37.3d), + new EpsgOperationParameterRecord(15686, "Latitude offset", 12.0d), + new EpsgOperationParameterRecord(15686, "Longitude offset", -8.15d), + new EpsgOperationParameterRecord(15686, "Geoid height", 32.1d), + new EpsgOperationParameterRecord(15687, "Latitude offset", 12.06d), + new EpsgOperationParameterRecord(15687, "Longitude offset", -8.38d), + new EpsgOperationParameterRecord(15687, "Geoid height", 31.0d), + new EpsgOperationParameterRecord(15688, "Latitude offset", 12.17d), + new EpsgOperationParameterRecord(15688, "Longitude offset", -8.69d), + new EpsgOperationParameterRecord(15688, "Geoid height", 30.3d), + new EpsgOperationParameterRecord(15689, "Latitude offset", 12.23d), + new EpsgOperationParameterRecord(15689, "Longitude offset", -8.99d), + new EpsgOperationParameterRecord(15689, "Geoid height", 31.7d), + new EpsgOperationParameterRecord(15690, "Latitude offset", 12.21d), + new EpsgOperationParameterRecord(15690, "Longitude offset", -9.21d), + new EpsgOperationParameterRecord(15690, "Geoid height", 34.3d), + new EpsgOperationParameterRecord(15691, "Latitude offset", 12.28d), + new EpsgOperationParameterRecord(15691, "Longitude offset", -9.6d), + new EpsgOperationParameterRecord(15691, "Geoid height", 33.3d), + new EpsgOperationParameterRecord(15692, "Latitude offset", 12.28d), + new EpsgOperationParameterRecord(15692, "Longitude offset", -8.25d), + new EpsgOperationParameterRecord(15692, "Geoid height", 31.0d), + new EpsgOperationParameterRecord(15693, "Latitude offset", 12.37d), + new EpsgOperationParameterRecord(15693, "Longitude offset", -8.55d), + new EpsgOperationParameterRecord(15693, "Geoid height", 29.1d), + new EpsgOperationParameterRecord(15694, "Latitude offset", 12.53d), + new EpsgOperationParameterRecord(15694, "Longitude offset", -8.21d), + new EpsgOperationParameterRecord(15694, "Geoid height", 31.0d), + new EpsgOperationParameterRecord(15695, "Latitude offset", 12.57d), + new EpsgOperationParameterRecord(15695, "Longitude offset", -8.4d), + new EpsgOperationParameterRecord(15695, "Geoid height", 28.4d), + new EpsgOperationParameterRecord(15696, "Latitude offset", 12.71d), + new EpsgOperationParameterRecord(15696, "Longitude offset", -8.17d), + new EpsgOperationParameterRecord(15696, "Geoid height", 29.9d), + new EpsgOperationParameterRecord(15697, "Latitude offset", 7.92d), + new EpsgOperationParameterRecord(15697, "Longitude offset", -13.88d), + new EpsgOperationParameterRecord(15697, "Geoid height", 26.1d), + new EpsgOperationParameterRecord(15699, "X-axis translation", -2.0d), + new EpsgOperationParameterRecord(15699, "Y-axis translation", 124.7d), + new EpsgOperationParameterRecord(15699, "Z-axis translation", 196.0d), + new EpsgOperationParameterRecord(15701, "X-axis translation", 275.57d), + new EpsgOperationParameterRecord(15701, "Y-axis translation", 676.78d), + new EpsgOperationParameterRecord(15701, "Z-axis translation", 229.6d), + new EpsgOperationParameterRecord(15702, "X-axis translation", 278.9d), + new EpsgOperationParameterRecord(15702, "Y-axis translation", 684.39d), + new EpsgOperationParameterRecord(15702, "Z-axis translation", 226.05d), + new EpsgOperationParameterRecord(15703, "X-axis translation", 271.905d), + new EpsgOperationParameterRecord(15703, "Y-axis translation", 669.593d), + new EpsgOperationParameterRecord(15703, "Z-axis translation", 231.495d), + new EpsgOperationParameterRecord(15705, "X-axis translation", -83.13d), + new EpsgOperationParameterRecord(15705, "Y-axis translation", -104.95d), + new EpsgOperationParameterRecord(15705, "Z-axis translation", 114.63d), + new EpsgOperationParameterRecord(15705, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15705, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15705, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(15705, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(15706, "X-axis translation", -93.6d), + new EpsgOperationParameterRecord(15706, "Y-axis translation", -83.7d), + new EpsgOperationParameterRecord(15706, "Z-axis translation", 113.8d), + new EpsgOperationParameterRecord(15707, "X-axis translation", -118.996d), + new EpsgOperationParameterRecord(15707, "Y-axis translation", -111.177d), + new EpsgOperationParameterRecord(15707, "Z-axis translation", -198.687d), + new EpsgOperationParameterRecord(15708, "X-axis translation", -127.62d), + new EpsgOperationParameterRecord(15708, "Y-axis translation", -67.24d), + new EpsgOperationParameterRecord(15708, "Z-axis translation", -47.04d), + new EpsgOperationParameterRecord(15708, "X-axis rotation", 3.068d), + new EpsgOperationParameterRecord(15708, "Y-axis rotation", -4.903d), + new EpsgOperationParameterRecord(15708, "Z-axis rotation", -1.578d), + new EpsgOperationParameterRecord(15708, "Scale difference", -1.06d), + new EpsgOperationParameterRecord(15709, "X-axis translation", 124.5d), + new EpsgOperationParameterRecord(15709, "Y-axis translation", -63.5d), + new EpsgOperationParameterRecord(15709, "Z-axis translation", -281.0d), + new EpsgOperationParameterRecord(15710, "X-axis translation", -160.0d), + new EpsgOperationParameterRecord(15710, "Y-axis translation", 315.0d), + new EpsgOperationParameterRecord(15710, "Z-axis translation", -142.0d), + new EpsgOperationParameterRecord(15711, "X-axis translation", -158.0d), + new EpsgOperationParameterRecord(15711, "Y-axis translation", 309.0d), + new EpsgOperationParameterRecord(15711, "Z-axis translation", -147.0d), + new EpsgOperationParameterRecord(15712, "X-axis translation", -161.0d), + new EpsgOperationParameterRecord(15712, "Y-axis translation", 310.0d), + new EpsgOperationParameterRecord(15712, "Z-axis translation", -145.0d), + new EpsgOperationParameterRecord(15713, "X-axis translation", -133.0d), + new EpsgOperationParameterRecord(15713, "Y-axis translation", -321.0d), + new EpsgOperationParameterRecord(15713, "Z-axis translation", 50.0d), + new EpsgOperationParameterRecord(15714, "X-axis translation", -806.413d), + new EpsgOperationParameterRecord(15714, "Y-axis translation", -263.5d), + new EpsgOperationParameterRecord(15714, "Z-axis translation", -622.671d), + new EpsgOperationParameterRecord(15714, "X-axis rotation", 6.018583e-05d), + new EpsgOperationParameterRecord(15714, "Y-axis rotation", -1.450001e-05d), + new EpsgOperationParameterRecord(15714, "Z-axis rotation", -0.0001892455d), + new EpsgOperationParameterRecord(15714, "Scale difference", -20.81616d), + new EpsgOperationParameterRecord(15715, "X-axis translation", -806.413d), + new EpsgOperationParameterRecord(15715, "Y-axis translation", -263.5d), + new EpsgOperationParameterRecord(15715, "Z-axis translation", -622.671d), + new EpsgOperationParameterRecord(15715, "X-axis rotation", 6.018583e-05d), + new EpsgOperationParameterRecord(15715, "Y-axis rotation", -1.450001e-05d), + new EpsgOperationParameterRecord(15715, "Z-axis rotation", -0.0001892455d), + new EpsgOperationParameterRecord(15715, "Scale difference", -20.81616d), + new EpsgOperationParameterRecord(15716, "X-axis translation", 100.783d), + new EpsgOperationParameterRecord(15716, "Y-axis translation", 187.382d), + new EpsgOperationParameterRecord(15716, "Z-axis translation", -47.0d), + new EpsgOperationParameterRecord(15716, "X-axis rotation", -4.471839e-05d), + new EpsgOperationParameterRecord(15716, "Y-axis rotation", 1.175093e-05d), + new EpsgOperationParameterRecord(15716, "Z-axis rotation", -4.027967e-05d), + new EpsgOperationParameterRecord(15716, "Scale difference", -13.56561d), + new EpsgOperationParameterRecord(15717, "X-axis translation", 100.783d), + new EpsgOperationParameterRecord(15717, "Y-axis translation", 187.382d), + new EpsgOperationParameterRecord(15717, "Z-axis translation", -47.0d), + new EpsgOperationParameterRecord(15717, "X-axis rotation", -4.471839e-05d), + new EpsgOperationParameterRecord(15717, "Y-axis rotation", 1.175093e-05d), + new EpsgOperationParameterRecord(15717, "Z-axis rotation", -4.027967e-05d), + new EpsgOperationParameterRecord(15717, "Scale difference", -13.56561d), + new EpsgOperationParameterRecord(15718, "X-axis translation", 336.026d), + new EpsgOperationParameterRecord(15718, "Y-axis translation", 348.565d), + new EpsgOperationParameterRecord(15718, "Z-axis translation", 252.978d), + new EpsgOperationParameterRecord(15718, "X-axis rotation", -8.358813e-05d), + new EpsgOperationParameterRecord(15718, "Y-axis rotation", -3.057474e-05d), + new EpsgOperationParameterRecord(15718, "Z-axis rotation", 7.573031e-06d), + new EpsgOperationParameterRecord(15718, "Scale difference", -5.771909d), + new EpsgOperationParameterRecord(15719, "X-axis translation", 336.026d), + new EpsgOperationParameterRecord(15719, "Y-axis translation", 348.565d), + new EpsgOperationParameterRecord(15719, "Z-axis translation", 252.978d), + new EpsgOperationParameterRecord(15719, "X-axis rotation", -8.358813e-05d), + new EpsgOperationParameterRecord(15719, "Y-axis rotation", -3.057474e-05d), + new EpsgOperationParameterRecord(15719, "Z-axis rotation", 7.573031e-06d), + new EpsgOperationParameterRecord(15719, "Scale difference", -5.771909d), + new EpsgOperationParameterRecord(15720, "X-axis translation", 963.273d), + new EpsgOperationParameterRecord(15720, "Y-axis translation", 486.386d), + new EpsgOperationParameterRecord(15720, "Z-axis translation", 190.997d), + new EpsgOperationParameterRecord(15720, "X-axis rotation", -7.992171e-05d), + new EpsgOperationParameterRecord(15720, "Y-axis rotation", -8.090696e-06d), + new EpsgOperationParameterRecord(15720, "Z-axis rotation", 0.0001051699d), + new EpsgOperationParameterRecord(15720, "Scale difference", -13.89914d), + new EpsgOperationParameterRecord(15721, "X-axis translation", 963.273d), + new EpsgOperationParameterRecord(15721, "Y-axis translation", 486.386d), + new EpsgOperationParameterRecord(15721, "Z-axis translation", 190.997d), + new EpsgOperationParameterRecord(15721, "X-axis rotation", -7.992171e-05d), + new EpsgOperationParameterRecord(15721, "Y-axis rotation", -8.090696e-06d), + new EpsgOperationParameterRecord(15721, "Z-axis rotation", 0.0001051699d), + new EpsgOperationParameterRecord(15721, "Scale difference", -13.89914d), + new EpsgOperationParameterRecord(15722, "X-axis translation", -90.29d), + new EpsgOperationParameterRecord(15722, "Y-axis translation", 247.559d), + new EpsgOperationParameterRecord(15722, "Z-axis translation", -21.989d), + new EpsgOperationParameterRecord(15722, "X-axis rotation", -4.216369e-05d), + new EpsgOperationParameterRecord(15722, "Y-axis rotation", -2.030416e-05d), + new EpsgOperationParameterRecord(15722, "Z-axis rotation", -6.209623e-05d), + new EpsgOperationParameterRecord(15722, "Scale difference", 2.181658d), + new EpsgOperationParameterRecord(15723, "X-axis translation", -90.29d), + new EpsgOperationParameterRecord(15723, "Y-axis translation", 247.559d), + new EpsgOperationParameterRecord(15723, "Z-axis translation", -21.989d), + new EpsgOperationParameterRecord(15723, "X-axis rotation", -4.216369e-05d), + new EpsgOperationParameterRecord(15723, "Y-axis rotation", -2.030416e-05d), + new EpsgOperationParameterRecord(15723, "Z-axis rotation", -6.209623e-05d), + new EpsgOperationParameterRecord(15723, "Scale difference", 2.181658d), + new EpsgOperationParameterRecord(15724, "X-axis translation", -0.562d), + new EpsgOperationParameterRecord(15724, "Y-axis translation", 244.299d), + new EpsgOperationParameterRecord(15724, "Z-axis translation", -456.938d), + new EpsgOperationParameterRecord(15724, "X-axis rotation", 3.329153e-05d), + new EpsgOperationParameterRecord(15724, "Y-axis rotation", -4.001009e-05d), + new EpsgOperationParameterRecord(15724, "Z-axis rotation", -4.507206e-05d), + new EpsgOperationParameterRecord(15724, "Scale difference", 3.74656d), + new EpsgOperationParameterRecord(15725, "X-axis translation", -0.562d), + new EpsgOperationParameterRecord(15725, "Y-axis translation", 244.299d), + new EpsgOperationParameterRecord(15725, "Z-axis translation", -456.938d), + new EpsgOperationParameterRecord(15725, "X-axis rotation", 3.329153e-05d), + new EpsgOperationParameterRecord(15725, "Y-axis rotation", -4.001009e-05d), + new EpsgOperationParameterRecord(15725, "Z-axis rotation", -4.507206e-05d), + new EpsgOperationParameterRecord(15725, "Scale difference", 3.74656d), + new EpsgOperationParameterRecord(15726, "X-axis translation", -305.356d), + new EpsgOperationParameterRecord(15726, "Y-axis translation", 222.004d), + new EpsgOperationParameterRecord(15726, "Z-axis translation", -30.023d), + new EpsgOperationParameterRecord(15726, "X-axis rotation", -4.698084e-05d), + new EpsgOperationParameterRecord(15726, "Y-axis rotation", 5.003123e-06d), + new EpsgOperationParameterRecord(15726, "Z-axis rotation", -9.578655e-05d), + new EpsgOperationParameterRecord(15726, "Scale difference", 6.325747d), + new EpsgOperationParameterRecord(15727, "X-axis translation", -305.356d), + new EpsgOperationParameterRecord(15727, "Y-axis translation", 222.004d), + new EpsgOperationParameterRecord(15727, "Z-axis translation", -30.023d), + new EpsgOperationParameterRecord(15727, "X-axis rotation", -4.698084e-05d), + new EpsgOperationParameterRecord(15727, "Y-axis rotation", 5.003123e-06d), + new EpsgOperationParameterRecord(15727, "Z-axis rotation", -9.578655e-05d), + new EpsgOperationParameterRecord(15727, "Scale difference", 6.325747d), + new EpsgOperationParameterRecord(15728, "X-axis translation", 221.899d), + new EpsgOperationParameterRecord(15728, "Y-axis translation", 274.136d), + new EpsgOperationParameterRecord(15728, "Z-axis translation", -397.554d), + new EpsgOperationParameterRecord(15728, "X-axis rotation", 1.361573e-05d), + new EpsgOperationParameterRecord(15728, "Y-axis rotation", -2.174431e-06d), + new EpsgOperationParameterRecord(15728, "Z-axis rotation", -1.36241e-05d), + new EpsgOperationParameterRecord(15728, "Scale difference", -2.199943d), + new EpsgOperationParameterRecord(15729, "X-axis translation", 221.899d), + new EpsgOperationParameterRecord(15729, "Y-axis translation", 274.136d), + new EpsgOperationParameterRecord(15729, "Z-axis translation", -397.554d), + new EpsgOperationParameterRecord(15729, "X-axis rotation", 1.361573e-05d), + new EpsgOperationParameterRecord(15729, "Y-axis rotation", -2.174431e-06d), + new EpsgOperationParameterRecord(15729, "Z-axis rotation", -1.36241e-05d), + new EpsgOperationParameterRecord(15729, "Scale difference", -2.199943d), + new EpsgOperationParameterRecord(15730, "X-axis translation", 300.449d), + new EpsgOperationParameterRecord(15730, "Y-axis translation", 293.757d), + new EpsgOperationParameterRecord(15730, "Z-axis translation", -317.306d), + new EpsgOperationParameterRecord(15730, "X-axis rotation", 6.018581e-05d), + new EpsgOperationParameterRecord(15730, "Y-axis rotation", -1.450002e-05d), + new EpsgOperationParameterRecord(15730, "Z-axis rotation", -0.0001892455d), + new EpsgOperationParameterRecord(15730, "Scale difference", -20.81615d), + new EpsgOperationParameterRecord(15730, "Ordinate 1 of evaluation point", 1891881.173d), + new EpsgOperationParameterRecord(15730, "Ordinate 2 of evaluation point", -5961263.267d), + new EpsgOperationParameterRecord(15730, "Ordinate 3 of evaluation point", 1248403.057d), + new EpsgOperationParameterRecord(15731, "X-axis translation", 308.833d), + new EpsgOperationParameterRecord(15731, "Y-axis translation", 282.519d), + new EpsgOperationParameterRecord(15731, "Z-axis translation", -314.571d), + new EpsgOperationParameterRecord(15731, "X-axis rotation", -4.471845e-05d), + new EpsgOperationParameterRecord(15731, "Y-axis rotation", 1.175087e-05d), + new EpsgOperationParameterRecord(15731, "Z-axis rotation", -4.027981e-05d), + new EpsgOperationParameterRecord(15731, "Scale difference", -13.56561d), + new EpsgOperationParameterRecord(15731, "Ordinate 1 of evaluation point", 1625036.59d), + new EpsgOperationParameterRecord(15731, "Ordinate 2 of evaluation point", -6054644.061d), + new EpsgOperationParameterRecord(15731, "Ordinate 3 of evaluation point", 1172969.151d), + new EpsgOperationParameterRecord(15732, "X-axis translation", 311.118d), + new EpsgOperationParameterRecord(15732, "Y-axis translation", 289.167d), + new EpsgOperationParameterRecord(15732, "Z-axis translation", -310.641d), + new EpsgOperationParameterRecord(15732, "X-axis rotation", -8.358815e-05d), + new EpsgOperationParameterRecord(15732, "Y-axis rotation", -3.057474e-05d), + new EpsgOperationParameterRecord(15732, "Z-axis rotation", 7.573043e-06d), + new EpsgOperationParameterRecord(15732, "Scale difference", -5.771882d), + new EpsgOperationParameterRecord(15732, "Ordinate 1 of evaluation point", 1555622.801d), + new EpsgOperationParameterRecord(15732, "Ordinate 2 of evaluation point", -6105353.313d), + new EpsgOperationParameterRecord(15732, "Ordinate 3 of evaluation point", 991255.656d), + new EpsgOperationParameterRecord(15733, "X-axis translation", 306.666d), + new EpsgOperationParameterRecord(15733, "Y-axis translation", 315.063d), + new EpsgOperationParameterRecord(15733, "Z-axis translation", -318.837d), + new EpsgOperationParameterRecord(15733, "X-axis rotation", -7.992173e-05d), + new EpsgOperationParameterRecord(15733, "Y-axis rotation", -8.090698e-06d), + new EpsgOperationParameterRecord(15733, "Z-axis rotation", 0.0001051699d), + new EpsgOperationParameterRecord(15733, "Scale difference", -13.89912d), + new EpsgOperationParameterRecord(15733, "Ordinate 1 of evaluation point", 1845222.398d), + new EpsgOperationParameterRecord(15733, "Ordinate 2 of evaluation point", -6058604.495d), + new EpsgOperationParameterRecord(15733, "Ordinate 3 of evaluation point", 769132.398d), + new EpsgOperationParameterRecord(15734, "X-axis translation", 307.871d), + new EpsgOperationParameterRecord(15734, "Y-axis translation", 305.803d), + new EpsgOperationParameterRecord(15734, "Z-axis translation", -311.992d), + new EpsgOperationParameterRecord(15734, "X-axis rotation", -4.216368e-05d), + new EpsgOperationParameterRecord(15734, "Y-axis rotation", -2.030416e-05d), + new EpsgOperationParameterRecord(15734, "Z-axis rotation", -6.209624e-05d), + new EpsgOperationParameterRecord(15734, "Scale difference", 2.181655d), + new EpsgOperationParameterRecord(15734, "Ordinate 1 of evaluation point", 1594396.206d), + new EpsgOperationParameterRecord(15734, "Ordinate 2 of evaluation point", -6143812.398d), + new EpsgOperationParameterRecord(15734, "Ordinate 3 of evaluation point", 648855.829d), + new EpsgOperationParameterRecord(15735, "X-axis translation", 302.934d), + new EpsgOperationParameterRecord(15735, "Y-axis translation", 307.805d), + new EpsgOperationParameterRecord(15735, "Z-axis translation", -312.121d), + new EpsgOperationParameterRecord(15735, "X-axis rotation", 3.329153e-05d), + new EpsgOperationParameterRecord(15735, "Y-axis rotation", -4.001009e-05d), + new EpsgOperationParameterRecord(15735, "Z-axis rotation", -4.507205e-05d), + new EpsgOperationParameterRecord(15735, "Scale difference", 3.746562d), + new EpsgOperationParameterRecord(15735, "Ordinate 1 of evaluation point", 1558280.49d), + new EpsgOperationParameterRecord(15735, "Ordinate 2 of evaluation point", -6167355.092d), + new EpsgOperationParameterRecord(15735, "Ordinate 3 of evaluation point", 491954.219d), + new EpsgOperationParameterRecord(15736, "X-axis translation", 295.282d), + new EpsgOperationParameterRecord(15736, "Y-axis translation", 321.293d), + new EpsgOperationParameterRecord(15736, "Z-axis translation", -311.001d), + new EpsgOperationParameterRecord(15736, "X-axis rotation", -4.698084e-05d), + new EpsgOperationParameterRecord(15736, "Y-axis rotation", 5.003127e-06d), + new EpsgOperationParameterRecord(15736, "Z-axis rotation", -9.578653e-05d), + new EpsgOperationParameterRecord(15736, "Scale difference", 6.325744d), + new EpsgOperationParameterRecord(15736, "Ordinate 1 of evaluation point", 1564000.62d), + new EpsgOperationParameterRecord(15736, "Ordinate 2 of evaluation point", -6180004.879d), + new EpsgOperationParameterRecord(15736, "Ordinate 3 of evaluation point", 243257.955d), + new EpsgOperationParameterRecord(15737, "X-axis translation", 302.529d), + new EpsgOperationParameterRecord(15737, "Y-axis translation", 317.979d), + new EpsgOperationParameterRecord(15737, "Z-axis translation", -319.08d), + new EpsgOperationParameterRecord(15737, "X-axis rotation", 1.361566e-05d), + new EpsgOperationParameterRecord(15737, "Y-axis rotation", -2.174456e-06d), + new EpsgOperationParameterRecord(15737, "Z-axis rotation", -1.362418e-05d), + new EpsgOperationParameterRecord(15737, "Scale difference", -2.199976d), + new EpsgOperationParameterRecord(15737, "Ordinate 1 of evaluation point", 1738580.767d), + new EpsgOperationParameterRecord(15737, "Ordinate 2 of evaluation point", -6120500.388d), + new EpsgOperationParameterRecord(15737, "Ordinate 3 of evaluation point", 491473.306d), + new EpsgOperationParameterRecord(15738, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15738, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15738, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15739, "X-axis translation", 565.2369d), + new EpsgOperationParameterRecord(15739, "Y-axis translation", 50.0087d), + new EpsgOperationParameterRecord(15739, "Z-axis translation", 465.658d), + new EpsgOperationParameterRecord(15739, "X-axis rotation", 1.9725d), + new EpsgOperationParameterRecord(15739, "Y-axis rotation", -1.7004d), + new EpsgOperationParameterRecord(15739, "Z-axis rotation", 9.0677d), + new EpsgOperationParameterRecord(15739, "Scale difference", 4.0812d), + new EpsgOperationParameterRecord(15740, "X-axis translation", 593.0297d), + new EpsgOperationParameterRecord(15740, "Y-axis translation", 26.0038d), + new EpsgOperationParameterRecord(15740, "Z-axis translation", 478.7534d), + new EpsgOperationParameterRecord(15740, "X-axis rotation", 1.9725d), + new EpsgOperationParameterRecord(15740, "Y-axis rotation", -1.7004d), + new EpsgOperationParameterRecord(15740, "Z-axis rotation", 9.0677d), + new EpsgOperationParameterRecord(15740, "Scale difference", 4.0812d), + new EpsgOperationParameterRecord(15740, "Ordinate 1 of evaluation point", 3903453.1482d), + new EpsgOperationParameterRecord(15740, "Ordinate 2 of evaluation point", 368135.3134d), + new EpsgOperationParameterRecord(15740, "Ordinate 3 of evaluation point", 5012970.3051d), + new EpsgOperationParameterRecord(15741, "X-axis translation", -187.5d), + new EpsgOperationParameterRecord(15741, "Y-axis translation", 14.1d), + new EpsgOperationParameterRecord(15741, "Z-axis translation", 237.6d), + new EpsgOperationParameterRecord(15742, "X-axis translation", -190.421d), + new EpsgOperationParameterRecord(15742, "Y-axis translation", 8.532d), + new EpsgOperationParameterRecord(15742, "Z-axis translation", 238.69d), + new EpsgOperationParameterRecord(15743, "X-axis translation", -83.58d), + new EpsgOperationParameterRecord(15743, "Y-axis translation", -397.54d), + new EpsgOperationParameterRecord(15743, "Z-axis translation", 458.78d), + new EpsgOperationParameterRecord(15743, "X-axis rotation", -17.595d), + new EpsgOperationParameterRecord(15743, "Y-axis rotation", -2.847d), + new EpsgOperationParameterRecord(15743, "Z-axis rotation", 4.256d), + new EpsgOperationParameterRecord(15743, "Scale difference", 3.225d), + new EpsgOperationParameterRecord(15745, "X-axis translation", -123.02d), + new EpsgOperationParameterRecord(15745, "Y-axis translation", -158.95d), + new EpsgOperationParameterRecord(15745, "Z-axis translation", -168.47d), + new EpsgOperationParameterRecord(15746, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15746, "Y-axis translation", -0.15d), + new EpsgOperationParameterRecord(15746, "Z-axis translation", 0.68d), + new EpsgOperationParameterRecord(15747, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15747, "Longitude of natural origin", 51.0d), + new EpsgOperationParameterRecord(15747, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15747, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15747, "False northing", 0.0d), + new EpsgOperationParameterRecord(15747, "Ordinate 1 of evaluation point in target CRS", 611267.2865d), + new EpsgOperationParameterRecord(15747, "Ordinate 2 of evaluation point in target CRS", 3046565.8255d), + new EpsgOperationParameterRecord(15747, "Scale factor for source CRS axes", 0.9997728332d), + new EpsgOperationParameterRecord(15747, "Rotation angle of source CRS axes", 315.0d), + new EpsgOperationParameterRecord(15750, "X-axis translation", -7.0d), + new EpsgOperationParameterRecord(15750, "Y-axis translation", 215.0d), + new EpsgOperationParameterRecord(15750, "Z-axis translation", 225.0d), + new EpsgOperationParameterRecord(15751, "X-axis translation", 94.0d), + new EpsgOperationParameterRecord(15751, "Y-axis translation", -948.0d), + new EpsgOperationParameterRecord(15751, "Z-axis translation", -1262.0d), + new EpsgOperationParameterRecord(15752, "X-axis translation", -86.0d), + new EpsgOperationParameterRecord(15752, "Y-axis translation", -98.0d), + new EpsgOperationParameterRecord(15752, "Z-axis translation", -119.0d), + new EpsgOperationParameterRecord(15753, "Ordinate 1 of evaluation point", 55.0d), + new EpsgOperationParameterRecord(15753, "Ordinate 2 of evaluation point", 0.0d), + new EpsgOperationParameterRecord(15753, "Scaling factor for coord differences", 1.0d), + new EpsgOperationParameterRecord(15753, "A0", -5.56098e-06d), + new EpsgOperationParameterRecord(15753, "Au1v0", -1.55391e-06d), + new EpsgOperationParameterRecord(15753, "Au0v1", -4.0262e-07d), + new EpsgOperationParameterRecord(15753, "Au2v0", -5.09693e-07d), + new EpsgOperationParameterRecord(15753, "Au1v1", -8.19775e-07d), + new EpsgOperationParameterRecord(15753, "Au0v2", -2.47592e-07d), + new EpsgOperationParameterRecord(15753, "Au3v0", 1.36682e-07d), + new EpsgOperationParameterRecord(15753, "Au2v1", 1.86198e-07d), + new EpsgOperationParameterRecord(15753, "Au1v2", 1.2335e-07d), + new EpsgOperationParameterRecord(15753, "Au0v3", 5.68797e-08d), + new EpsgOperationParameterRecord(15753, "Au4v0", -2.32217e-09d), + new EpsgOperationParameterRecord(15753, "Au3v1", -7.69931e-09d), + new EpsgOperationParameterRecord(15753, "Au2v2", -7.86953e-09d), + new EpsgOperationParameterRecord(15753, "Au1v3", -6.12216e-09d), + new EpsgOperationParameterRecord(15753, "Au0v4", -4.01382e-09d), + new EpsgOperationParameterRecord(15753, "B0", 1.48944e-05d), + new EpsgOperationParameterRecord(15753, "Bu1v0", 2.68191e-06d), + new EpsgOperationParameterRecord(15753, "Bu0v1", 2.4529e-06d), + new EpsgOperationParameterRecord(15753, "Bu2v0", 2.944e-07d), + new EpsgOperationParameterRecord(15753, "Bu1v1", 1.5226e-06d), + new EpsgOperationParameterRecord(15753, "Bu0v2", 9.10592e-07d), + new EpsgOperationParameterRecord(15753, "Bu3v0", -3.68241e-07d), + new EpsgOperationParameterRecord(15753, "Bu2v1", -8.51732e-07d), + new EpsgOperationParameterRecord(15753, "Bu1v2", -5.66713e-07d), + new EpsgOperationParameterRecord(15753, "Bu0v3", -1.85188e-07d), + new EpsgOperationParameterRecord(15753, "Bu4v0", 2.84312e-08d), + new EpsgOperationParameterRecord(15753, "Bu3v1", 6.84853e-08d), + new EpsgOperationParameterRecord(15753, "Bu2v2", 5.00828e-08d), + new EpsgOperationParameterRecord(15753, "Bu1v3", 4.15937e-08d), + new EpsgOperationParameterRecord(15753, "Bu0v4", 7.62236e-09d), + new EpsgOperationParameterRecord(15754, "X-axis translation", -158.0d), + new EpsgOperationParameterRecord(15754, "Y-axis translation", 315.0d), + new EpsgOperationParameterRecord(15754, "Z-axis translation", -148.0d), + new EpsgOperationParameterRecord(15755, "X-axis translation", -90.2d), + new EpsgOperationParameterRecord(15755, "Y-axis translation", -87.32d), + new EpsgOperationParameterRecord(15755, "Z-axis translation", 114.17d), + new EpsgOperationParameterRecord(15759, "X-axis translation", 217.037d), + new EpsgOperationParameterRecord(15759, "Y-axis translation", 86.959d), + new EpsgOperationParameterRecord(15759, "Z-axis translation", 23.956d), + new EpsgOperationParameterRecord(15778, "X-axis translation", -114.7d), + new EpsgOperationParameterRecord(15778, "Y-axis translation", -98.5d), + new EpsgOperationParameterRecord(15778, "Z-axis translation", -150.7d), + new EpsgOperationParameterRecord(15779, "X-axis translation", 283.7d), + new EpsgOperationParameterRecord(15779, "Y-axis translation", 735.9d), + new EpsgOperationParameterRecord(15779, "Z-axis translation", 261.1d), + new EpsgOperationParameterRecord(15782, "X-axis translation", -148.0d), + new EpsgOperationParameterRecord(15782, "Y-axis translation", 136.0d), + new EpsgOperationParameterRecord(15782, "Z-axis translation", 90.0d), + new EpsgOperationParameterRecord(15783, "X-axis translation", 287.0d), + new EpsgOperationParameterRecord(15783, "Y-axis translation", 178.0d), + new EpsgOperationParameterRecord(15783, "Z-axis translation", -136.0d), + new EpsgOperationParameterRecord(15784, "X-axis translation", -770.1d), + new EpsgOperationParameterRecord(15784, "Y-axis translation", 158.4d), + new EpsgOperationParameterRecord(15784, "Z-axis translation", -498.2d), + new EpsgOperationParameterRecord(15787, "X-axis translation", -79.9d), + new EpsgOperationParameterRecord(15787, "Y-axis translation", -158.0d), + new EpsgOperationParameterRecord(15787, "Z-axis translation", -168.9d), + new EpsgOperationParameterRecord(15788, "X-axis translation", -127.8d), + new EpsgOperationParameterRecord(15788, "Y-axis translation", -52.3d), + new EpsgOperationParameterRecord(15788, "Z-axis translation", 152.9d), + new EpsgOperationParameterRecord(15789, "X-axis translation", -128.5d), + new EpsgOperationParameterRecord(15789, "Y-axis translation", -53.0d), + new EpsgOperationParameterRecord(15789, "Z-axis translation", 153.4d), + new EpsgOperationParameterRecord(15790, "X-axis translation", -255.0d), + new EpsgOperationParameterRecord(15790, "Y-axis translation", -29.0d), + new EpsgOperationParameterRecord(15790, "Z-axis translation", -105.0d), + new EpsgOperationParameterRecord(15791, "X-axis translation", -259.99d), + new EpsgOperationParameterRecord(15791, "Y-axis translation", -5.28d), + new EpsgOperationParameterRecord(15791, "Z-axis translation", -97.09d), + new EpsgOperationParameterRecord(15792, "X-axis translation", -123.0d), + new EpsgOperationParameterRecord(15792, "Y-axis translation", 98.0d), + new EpsgOperationParameterRecord(15792, "Z-axis translation", 2.0d), + new EpsgOperationParameterRecord(15793, "X-axis translation", 31.95d), + new EpsgOperationParameterRecord(15793, "Y-axis translation", 300.99d), + new EpsgOperationParameterRecord(15793, "Z-axis translation", 419.19d), + new EpsgOperationParameterRecord(15794, "X-axis translation", -491.0d), + new EpsgOperationParameterRecord(15794, "Y-axis translation", -22.0d), + new EpsgOperationParameterRecord(15794, "Z-axis translation", 435.0d), + new EpsgOperationParameterRecord(15795, "X-axis translation", 114.0d), + new EpsgOperationParameterRecord(15795, "Y-axis translation", -116.0d), + new EpsgOperationParameterRecord(15795, "Z-axis translation", -333.0d), + new EpsgOperationParameterRecord(15796, "X-axis translation", 145.0d), + new EpsgOperationParameterRecord(15796, "Y-axis translation", 75.0d), + new EpsgOperationParameterRecord(15796, "Z-axis translation", -272.0d), + new EpsgOperationParameterRecord(15797, "X-axis translation", -205.0d), + new EpsgOperationParameterRecord(15797, "Y-axis translation", 107.0d), + new EpsgOperationParameterRecord(15797, "Z-axis translation", 53.0d), + new EpsgOperationParameterRecord(15798, "X-axis translation", -320.0d), + new EpsgOperationParameterRecord(15798, "Y-axis translation", 550.0d), + new EpsgOperationParameterRecord(15798, "Z-axis translation", -494.0d), + new EpsgOperationParameterRecord(15799, "X-axis translation", 124.0d), + new EpsgOperationParameterRecord(15799, "Y-axis translation", -234.0d), + new EpsgOperationParameterRecord(15799, "Z-axis translation", -25.0d), + new EpsgOperationParameterRecord(15800, "X-axis translation", -79.0d), + new EpsgOperationParameterRecord(15800, "Y-axis translation", -129.0d), + new EpsgOperationParameterRecord(15800, "Z-axis translation", 145.0d), + new EpsgOperationParameterRecord(15801, "X-axis translation", -127.0d), + new EpsgOperationParameterRecord(15801, "Y-axis translation", -769.0d), + new EpsgOperationParameterRecord(15801, "Z-axis translation", 472.0d), + new EpsgOperationParameterRecord(15802, "X-axis translation", -104.0d), + new EpsgOperationParameterRecord(15802, "Y-axis translation", -129.0d), + new EpsgOperationParameterRecord(15802, "Z-axis translation", 239.0d), + new EpsgOperationParameterRecord(15803, "X-axis translation", 298.0d), + new EpsgOperationParameterRecord(15803, "Y-axis translation", -304.0d), + new EpsgOperationParameterRecord(15803, "Z-axis translation", -375.0d), + new EpsgOperationParameterRecord(15804, "X-axis translation", -2.0d), + new EpsgOperationParameterRecord(15804, "Y-axis translation", 151.0d), + new EpsgOperationParameterRecord(15804, "Z-axis translation", 181.0d), + new EpsgOperationParameterRecord(15805, "X-axis translation", 230.0d), + new EpsgOperationParameterRecord(15805, "Y-axis translation", -199.0d), + new EpsgOperationParameterRecord(15805, "Z-axis translation", -752.0d), + new EpsgOperationParameterRecord(15806, "X-axis translation", 211.0d), + new EpsgOperationParameterRecord(15806, "Y-axis translation", 147.0d), + new EpsgOperationParameterRecord(15806, "Z-axis translation", 111.0d), + new EpsgOperationParameterRecord(15807, "X-axis translation", 252.0d), + new EpsgOperationParameterRecord(15807, "Y-axis translation", -209.0d), + new EpsgOperationParameterRecord(15807, "Z-axis translation", -751.0d), + new EpsgOperationParameterRecord(15808, "X-axis translation", 208.0d), + new EpsgOperationParameterRecord(15808, "Y-axis translation", -435.0d), + new EpsgOperationParameterRecord(15808, "Z-axis translation", -229.0d), + new EpsgOperationParameterRecord(15809, "X-axis translation", 189.0d), + new EpsgOperationParameterRecord(15809, "Y-axis translation", -79.0d), + new EpsgOperationParameterRecord(15809, "Z-axis translation", -202.0d), + new EpsgOperationParameterRecord(15810, "X-axis translation", 647.0d), + new EpsgOperationParameterRecord(15810, "Y-axis translation", 1777.0d), + new EpsgOperationParameterRecord(15810, "Z-axis translation", -1124.0d), + new EpsgOperationParameterRecord(15811, "X-axis translation", -270.0d), + new EpsgOperationParameterRecord(15811, "Y-axis translation", 13.0d), + new EpsgOperationParameterRecord(15811, "Z-axis translation", 62.0d), + new EpsgOperationParameterRecord(15812, "X-axis translation", 260.0d), + new EpsgOperationParameterRecord(15812, "Y-axis translation", 12.0d), + new EpsgOperationParameterRecord(15812, "Z-axis translation", -147.0d), + new EpsgOperationParameterRecord(15813, "X-axis translation", -794.0d), + new EpsgOperationParameterRecord(15813, "Y-axis translation", 119.0d), + new EpsgOperationParameterRecord(15813, "Z-axis translation", -298.0d), + new EpsgOperationParameterRecord(15814, "X-axis translation", 42.0d), + new EpsgOperationParameterRecord(15814, "Y-axis translation", 124.0d), + new EpsgOperationParameterRecord(15814, "Z-axis translation", 147.0d), + new EpsgOperationParameterRecord(15815, "X-axis translation", -307.0d), + new EpsgOperationParameterRecord(15815, "Y-axis translation", -92.0d), + new EpsgOperationParameterRecord(15815, "Z-axis translation", 127.0d), + new EpsgOperationParameterRecord(15816, "X-axis translation", -632.0d), + new EpsgOperationParameterRecord(15816, "Y-axis translation", 438.0d), + new EpsgOperationParameterRecord(15816, "Z-axis translation", -609.0d), + new EpsgOperationParameterRecord(15817, "X-axis translation", 912.0d), + new EpsgOperationParameterRecord(15817, "Y-axis translation", -58.0d), + new EpsgOperationParameterRecord(15817, "Z-axis translation", 1227.0d), + new EpsgOperationParameterRecord(15818, "X-axis translation", 403.0d), + new EpsgOperationParameterRecord(15818, "Y-axis translation", -81.0d), + new EpsgOperationParameterRecord(15818, "Z-axis translation", 277.0d), + new EpsgOperationParameterRecord(15819, "X-axis translation", 185.0d), + new EpsgOperationParameterRecord(15819, "Y-axis translation", 165.0d), + new EpsgOperationParameterRecord(15819, "Z-axis translation", 42.0d), + new EpsgOperationParameterRecord(15820, "X-axis translation", 170.0d), + new EpsgOperationParameterRecord(15820, "Y-axis translation", 42.0d), + new EpsgOperationParameterRecord(15820, "Z-axis translation", 84.0d), + new EpsgOperationParameterRecord(15822, "X-axis translation", 102.0d), + new EpsgOperationParameterRecord(15822, "Y-axis translation", 52.0d), + new EpsgOperationParameterRecord(15822, "Z-axis translation", -38.0d), + new EpsgOperationParameterRecord(15823, "X-axis translation", 276.0d), + new EpsgOperationParameterRecord(15823, "Y-axis translation", -57.0d), + new EpsgOperationParameterRecord(15823, "Z-axis translation", 149.0d), + new EpsgOperationParameterRecord(15824, "X-axis translation", 61.0d), + new EpsgOperationParameterRecord(15824, "Y-axis translation", -285.0d), + new EpsgOperationParameterRecord(15824, "Z-axis translation", -181.0d), + new EpsgOperationParameterRecord(15825, "X-axis translation", 89.0d), + new EpsgOperationParameterRecord(15825, "Y-axis translation", -279.0d), + new EpsgOperationParameterRecord(15825, "Z-axis translation", -183.0d), + new EpsgOperationParameterRecord(15826, "X-axis translation", 45.0d), + new EpsgOperationParameterRecord(15826, "Y-axis translation", -290.0d), + new EpsgOperationParameterRecord(15826, "Z-axis translation", -172.0d), + new EpsgOperationParameterRecord(15827, "X-axis translation", 65.0d), + new EpsgOperationParameterRecord(15827, "Y-axis translation", -290.0d), + new EpsgOperationParameterRecord(15827, "Z-axis translation", -190.0d), + new EpsgOperationParameterRecord(15828, "X-axis translation", 58.0d), + new EpsgOperationParameterRecord(15828, "Y-axis translation", -283.0d), + new EpsgOperationParameterRecord(15828, "Z-axis translation", -182.0d), + new EpsgOperationParameterRecord(15829, "X-axis translation", 44.4d), + new EpsgOperationParameterRecord(15829, "Y-axis translation", 109.0d), + new EpsgOperationParameterRecord(15829, "Z-axis translation", 151.7d), + new EpsgOperationParameterRecord(15830, "X-axis translation", 67.8d), + new EpsgOperationParameterRecord(15830, "Y-axis translation", 106.1d), + new EpsgOperationParameterRecord(15830, "Z-axis translation", 138.8d), + new EpsgOperationParameterRecord(15831, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15831, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15831, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15833, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15833, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15833, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15842, "X-axis translation", -156.0d), + new EpsgOperationParameterRecord(15842, "Y-axis translation", -271.0d), + new EpsgOperationParameterRecord(15842, "Z-axis translation", -189.0d), + new EpsgOperationParameterRecord(15843, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15843, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15843, "Z-axis translation", 1.5d), + new EpsgOperationParameterRecord(15843, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15843, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15843, "Z-axis rotation", -0.076d), + new EpsgOperationParameterRecord(15843, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(15844, "X-axis translation", 25.0d), + new EpsgOperationParameterRecord(15844, "Y-axis translation", -141.0d), + new EpsgOperationParameterRecord(15844, "Z-axis translation", -80.0d), + new EpsgOperationParameterRecord(15844, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15844, "Y-axis rotation", -0.35d), + new EpsgOperationParameterRecord(15844, "Z-axis rotation", -0.66d), + new EpsgOperationParameterRecord(15844, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(15846, "X-axis translation", -146.21d), + new EpsgOperationParameterRecord(15846, "Y-axis translation", 112.63d), + new EpsgOperationParameterRecord(15846, "Z-axis translation", 4.05d), + new EpsgOperationParameterRecord(15847, "X-axis translation", 253.0d), + new EpsgOperationParameterRecord(15847, "Y-axis translation", -132.0d), + new EpsgOperationParameterRecord(15847, "Z-axis translation", -127.0d), + new EpsgOperationParameterRecord(15848, "X-axis translation", -13.0d), + new EpsgOperationParameterRecord(15848, "Y-axis translation", -348.0d), + new EpsgOperationParameterRecord(15848, "Z-axis translation", 292.0d), + new EpsgOperationParameterRecord(15849, "X-axis translation", -106.0d), + new EpsgOperationParameterRecord(15849, "Y-axis translation", -87.0d), + new EpsgOperationParameterRecord(15849, "Z-axis translation", 188.0d), + new EpsgOperationParameterRecord(15850, "X-axis translation", 145.0d), + new EpsgOperationParameterRecord(15850, "Y-axis translation", -187.0d), + new EpsgOperationParameterRecord(15850, "Z-axis translation", 103.0d), + new EpsgOperationParameterRecord(15852, "X-axis translation", -3.0d), + new EpsgOperationParameterRecord(15852, "Y-axis translation", 154.0d), + new EpsgOperationParameterRecord(15852, "Z-axis translation", 177.0d), + new EpsgOperationParameterRecord(15853, "X-axis translation", -7.0d), + new EpsgOperationParameterRecord(15853, "Y-axis translation", 151.0d), + new EpsgOperationParameterRecord(15853, "Z-axis translation", 175.0d), + new EpsgOperationParameterRecord(15854, "X-axis translation", -7.0d), + new EpsgOperationParameterRecord(15854, "Y-axis translation", 151.0d), + new EpsgOperationParameterRecord(15854, "Z-axis translation", 178.0d), + new EpsgOperationParameterRecord(15855, "X-axis translation", -8.0d), + new EpsgOperationParameterRecord(15855, "Y-axis translation", 125.0d), + new EpsgOperationParameterRecord(15855, "Z-axis translation", 190.0d), + new EpsgOperationParameterRecord(15856, "X-axis translation", -7.0d), + new EpsgOperationParameterRecord(15856, "Y-axis translation", 158.0d), + new EpsgOperationParameterRecord(15856, "Z-axis translation", 172.0d), + new EpsgOperationParameterRecord(15857, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15857, "Longitude of natural origin", -15.0d), + new EpsgOperationParameterRecord(15857, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15857, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15857, "False northing", 0.0d), + new EpsgOperationParameterRecord(15857, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15857, "Longitude of natural origin", -15.0d), + new EpsgOperationParameterRecord(15857, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15857, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15857, "False northing", 0.0d), + new EpsgOperationParameterRecord(15857, "A0", -532.876d), + new EpsgOperationParameterRecord(15857, "A1", 1.00017216658401d), + new EpsgOperationParameterRecord(15857, "A2", 9.029305555e-05d), + new EpsgOperationParameterRecord(15857, "B0", -34.015d), + new EpsgOperationParameterRecord(15857, "B1", -9.029305555e-05d), + new EpsgOperationParameterRecord(15857, "B2", 1.00017216658401d), + new EpsgOperationParameterRecord(15858, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15858, "Longitude of natural origin", -9.0d), + new EpsgOperationParameterRecord(15858, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15858, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15858, "False northing", 0.0d), + new EpsgOperationParameterRecord(15858, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15858, "Longitude of natural origin", -9.0d), + new EpsgOperationParameterRecord(15858, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15858, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15858, "False northing", 0.0d), + new EpsgOperationParameterRecord(15858, "A0", -409.264d), + new EpsgOperationParameterRecord(15858, "A1", 1.00017432259949d), + new EpsgOperationParameterRecord(15858, "A2", 9.14562824e-05d), + new EpsgOperationParameterRecord(15858, "B0", -88.803d), + new EpsgOperationParameterRecord(15858, "B1", -9.14562824e-05d), + new EpsgOperationParameterRecord(15858, "B2", 1.00017432259949d), + new EpsgOperationParameterRecord(15859, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15859, "Longitude of natural origin", -3.0d), + new EpsgOperationParameterRecord(15859, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15859, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15859, "False northing", 0.0d), + new EpsgOperationParameterRecord(15859, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15859, "Longitude of natural origin", -3.0d), + new EpsgOperationParameterRecord(15859, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15859, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15859, "False northing", 0.0d), + new EpsgOperationParameterRecord(15859, "A0", -286.351d), + new EpsgOperationParameterRecord(15859, "A1", 1.0001754456884d), + new EpsgOperationParameterRecord(15859, "A2", 9.270672363e-05d), + new EpsgOperationParameterRecord(15859, "B0", -146.722d), + new EpsgOperationParameterRecord(15859, "B1", -9.270672363e-05d), + new EpsgOperationParameterRecord(15859, "B2", 1.0001754456884d), + new EpsgOperationParameterRecord(15860, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15860, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15860, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15861, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15861, "Longitude of natural origin", -15.0d), + new EpsgOperationParameterRecord(15861, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15861, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15861, "False northing", 0.0d), + new EpsgOperationParameterRecord(15861, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15861, "Longitude of natural origin", -15.0d), + new EpsgOperationParameterRecord(15861, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15861, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15861, "False northing", 0.0d), + new EpsgOperationParameterRecord(15861, "A0", -532.876d), + new EpsgOperationParameterRecord(15861, "A1", 1.000172166584d), + new EpsgOperationParameterRecord(15861, "A2", 9.029305555e-05d), + new EpsgOperationParameterRecord(15861, "B0", -34.015d), + new EpsgOperationParameterRecord(15861, "B1", -9.029305555e-05d), + new EpsgOperationParameterRecord(15861, "B2", 1.000172166584d), + new EpsgOperationParameterRecord(15862, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15862, "Longitude of natural origin", -9.0d), + new EpsgOperationParameterRecord(15862, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15862, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15862, "False northing", 0.0d), + new EpsgOperationParameterRecord(15862, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15862, "Longitude of natural origin", -9.0d), + new EpsgOperationParameterRecord(15862, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15862, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15862, "False northing", 0.0d), + new EpsgOperationParameterRecord(15862, "A0", -409.264d), + new EpsgOperationParameterRecord(15862, "A1", 1.0001743225995d), + new EpsgOperationParameterRecord(15862, "A2", 9.14562824e-05d), + new EpsgOperationParameterRecord(15862, "B0", -88.803d), + new EpsgOperationParameterRecord(15862, "B1", -9.14562824e-05d), + new EpsgOperationParameterRecord(15862, "B2", 1.0001743225995d), + new EpsgOperationParameterRecord(15863, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15863, "Longitude of natural origin", -3.0d), + new EpsgOperationParameterRecord(15863, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15863, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15863, "False northing", 0.0d), + new EpsgOperationParameterRecord(15863, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(15863, "Longitude of natural origin", -3.0d), + new EpsgOperationParameterRecord(15863, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(15863, "False easting", 500000.0d), + new EpsgOperationParameterRecord(15863, "False northing", 0.0d), + new EpsgOperationParameterRecord(15863, "A0", -286.351d), + new EpsgOperationParameterRecord(15863, "A1", 1.0001754456884d), + new EpsgOperationParameterRecord(15863, "A2", 9.270672363e-05d), + new EpsgOperationParameterRecord(15863, "B0", -146.722d), + new EpsgOperationParameterRecord(15863, "B1", -9.270672363e-05d), + new EpsgOperationParameterRecord(15863, "B2", 1.0001754456884d), + new EpsgOperationParameterRecord(15865, "X-axis translation", 25.0d), + new EpsgOperationParameterRecord(15865, "Y-axis translation", -141.0d), + new EpsgOperationParameterRecord(15865, "Z-axis translation", -78.5d), + new EpsgOperationParameterRecord(15865, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15865, "Y-axis rotation", -0.35d), + new EpsgOperationParameterRecord(15865, "Z-axis rotation", -0.736d), + new EpsgOperationParameterRecord(15865, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(15866, "X-axis translation", -153.33d), + new EpsgOperationParameterRecord(15866, "Y-axis translation", -169.41d), + new EpsgOperationParameterRecord(15866, "Z-axis translation", 86.39d), + new EpsgOperationParameterRecord(15867, "X-axis translation", 599.4d), + new EpsgOperationParameterRecord(15867, "Y-axis translation", 72.4d), + new EpsgOperationParameterRecord(15867, "Z-axis translation", 419.2d), + new EpsgOperationParameterRecord(15867, "X-axis rotation", -0.062d), + new EpsgOperationParameterRecord(15867, "Y-axis rotation", -0.022d), + new EpsgOperationParameterRecord(15867, "Z-axis rotation", -2.723d), + new EpsgOperationParameterRecord(15867, "Scale difference", 6.46d), + new EpsgOperationParameterRecord(15868, "X-axis translation", 612.4d), + new EpsgOperationParameterRecord(15868, "Y-axis translation", 77.0d), + new EpsgOperationParameterRecord(15868, "Z-axis translation", 440.2d), + new EpsgOperationParameterRecord(15868, "X-axis rotation", -0.054d), + new EpsgOperationParameterRecord(15868, "Y-axis rotation", 0.057d), + new EpsgOperationParameterRecord(15868, "Z-axis rotation", -2.797d), + new EpsgOperationParameterRecord(15868, "Scale difference", 2.55d), + new EpsgOperationParameterRecord(15869, "X-axis translation", 612.4d), + new EpsgOperationParameterRecord(15869, "Y-axis translation", 77.0d), + new EpsgOperationParameterRecord(15869, "Z-axis translation", 440.2d), + new EpsgOperationParameterRecord(15869, "X-axis rotation", -0.054d), + new EpsgOperationParameterRecord(15869, "Y-axis rotation", 0.057d), + new EpsgOperationParameterRecord(15869, "Z-axis rotation", -2.797d), + new EpsgOperationParameterRecord(15869, "Scale difference", 2.55d), + new EpsgOperationParameterRecord(15870, "X-axis translation", -80.01d), + new EpsgOperationParameterRecord(15870, "Y-axis translation", 253.26d), + new EpsgOperationParameterRecord(15870, "Z-axis translation", 291.19d), + new EpsgOperationParameterRecord(15872, "X-axis translation", 84.1d), + new EpsgOperationParameterRecord(15872, "Y-axis translation", -320.1d), + new EpsgOperationParameterRecord(15872, "Z-axis translation", 218.7d), + new EpsgOperationParameterRecord(15873, "X-axis translation", -206.1d), + new EpsgOperationParameterRecord(15873, "Y-axis translation", -174.7d), + new EpsgOperationParameterRecord(15873, "Z-axis translation", -87.7d), + new EpsgOperationParameterRecord(15874, "X-axis translation", -169.559d), + new EpsgOperationParameterRecord(15874, "Y-axis translation", -72.34d), + new EpsgOperationParameterRecord(15874, "Z-axis translation", 303.102d), + new EpsgOperationParameterRecord(15875, "X-axis translation", 265.025d), + new EpsgOperationParameterRecord(15875, "Y-axis translation", 384.929d), + new EpsgOperationParameterRecord(15875, "Z-axis translation", -194.046d), + new EpsgOperationParameterRecord(15876, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15876, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15876, "Z-axis translation", 4.5d), + new EpsgOperationParameterRecord(15876, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15876, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15876, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(15876, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(15877, "X-axis translation", -35.173d), + new EpsgOperationParameterRecord(15877, "Y-axis translation", 136.571d), + new EpsgOperationParameterRecord(15877, "Z-axis translation", -36.964d), + new EpsgOperationParameterRecord(15877, "X-axis rotation", 1.37d), + new EpsgOperationParameterRecord(15877, "Y-axis rotation", -0.842d), + new EpsgOperationParameterRecord(15877, "Z-axis rotation", -4.718d), + new EpsgOperationParameterRecord(15877, "Scale difference", -1.537d), + new EpsgOperationParameterRecord(15878, "X-axis translation", 51.0d), + new EpsgOperationParameterRecord(15878, "Y-axis translation", 391.0d), + new EpsgOperationParameterRecord(15878, "Z-axis translation", -36.0d), + new EpsgOperationParameterRecord(15879, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15879, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15879, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15880, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15880, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15880, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15881, "X-axis translation", -56.263d), + new EpsgOperationParameterRecord(15881, "Y-axis translation", 16.136d), + new EpsgOperationParameterRecord(15881, "Z-axis translation", -22.856d), + new EpsgOperationParameterRecord(15882, "X-axis translation", -11.64d), + new EpsgOperationParameterRecord(15882, "Y-axis translation", -348.6d), + new EpsgOperationParameterRecord(15882, "Z-axis translation", 291.98d), + new EpsgOperationParameterRecord(15883, "X-axis translation", 335.47d), + new EpsgOperationParameterRecord(15883, "Y-axis translation", 222.58d), + new EpsgOperationParameterRecord(15883, "Z-axis translation", -230.94d), + new EpsgOperationParameterRecord(15884, "X-axis translation", 287.58d), + new EpsgOperationParameterRecord(15884, "Y-axis translation", 177.78d), + new EpsgOperationParameterRecord(15884, "Z-axis translation", -135.41d), + new EpsgOperationParameterRecord(15885, "X-axis translation", -56.263d), + new EpsgOperationParameterRecord(15885, "Y-axis translation", 16.136d), + new EpsgOperationParameterRecord(15885, "Z-axis translation", -22.856d), + new EpsgOperationParameterRecord(15886, "X-axis translation", -10.18d), + new EpsgOperationParameterRecord(15886, "Y-axis translation", -350.43d), + new EpsgOperationParameterRecord(15886, "Z-axis translation", 291.37d), + new EpsgOperationParameterRecord(15887, "X-axis translation", 97.297d), + new EpsgOperationParameterRecord(15887, "Y-axis translation", -263.243d), + new EpsgOperationParameterRecord(15887, "Z-axis translation", 310.879d), + new EpsgOperationParameterRecord(15887, "X-axis rotation", 1.5999d), + new EpsgOperationParameterRecord(15887, "Y-axis rotation", -0.8387d), + new EpsgOperationParameterRecord(15887, "Z-axis rotation", -3.1409d), + new EpsgOperationParameterRecord(15887, "Scale difference", 13.326d), + new EpsgOperationParameterRecord(15888, "X-axis translation", 48.812d), + new EpsgOperationParameterRecord(15888, "Y-axis translation", -205.932d), + new EpsgOperationParameterRecord(15888, "Z-axis translation", 343.993d), + new EpsgOperationParameterRecord(15888, "X-axis rotation", 3.4427d), + new EpsgOperationParameterRecord(15888, "Y-axis rotation", 0.4999d), + new EpsgOperationParameterRecord(15888, "Z-axis rotation", -4.0878d), + new EpsgOperationParameterRecord(15888, "Scale difference", 6.5215d), + new EpsgOperationParameterRecord(15889, "X-axis translation", -166.0684d), + new EpsgOperationParameterRecord(15889, "Y-axis translation", -154.7826d), + new EpsgOperationParameterRecord(15889, "Z-axis translation", 254.8282d), + new EpsgOperationParameterRecord(15889, "X-axis rotation", 37.546d), + new EpsgOperationParameterRecord(15889, "Y-axis rotation", -7.7018d), + new EpsgOperationParameterRecord(15889, "Z-axis rotation", 10.2029d), + new EpsgOperationParameterRecord(15889, "Scale difference", -30.84d), + new EpsgOperationParameterRecord(15890, "X-axis translation", 137.092d), + new EpsgOperationParameterRecord(15890, "Y-axis translation", 131.675d), + new EpsgOperationParameterRecord(15890, "Z-axis translation", 91.478d), + new EpsgOperationParameterRecord(15890, "X-axis rotation", 1.9435d), + new EpsgOperationParameterRecord(15890, "Y-axis rotation", 11.5995d), + new EpsgOperationParameterRecord(15890, "Z-axis rotation", 4.3316d), + new EpsgOperationParameterRecord(15890, "Scale difference", -7.4801d), + new EpsgOperationParameterRecord(15891, "X-axis translation", -408.809d), + new EpsgOperationParameterRecord(15891, "Y-axis translation", 366.857d), + new EpsgOperationParameterRecord(15891, "Z-axis translation", -412.987d), + new EpsgOperationParameterRecord(15891, "X-axis rotation", -1.8843d), + new EpsgOperationParameterRecord(15891, "Y-axis rotation", 0.5308d), + new EpsgOperationParameterRecord(15891, "Z-axis rotation", -2.1657d), + new EpsgOperationParameterRecord(15891, "Scale difference", -121.0994d), + new EpsgOperationParameterRecord(15892, "X-axis translation", -122.386d), + new EpsgOperationParameterRecord(15892, "Y-axis translation", -188.707d), + new EpsgOperationParameterRecord(15892, "Z-axis translation", 103.334d), + new EpsgOperationParameterRecord(15892, "X-axis rotation", -3.511d), + new EpsgOperationParameterRecord(15892, "Y-axis rotation", 4.9665d), + new EpsgOperationParameterRecord(15892, "Z-axis rotation", 5.7048d), + new EpsgOperationParameterRecord(15892, "Scale difference", 4.4799d), + new EpsgOperationParameterRecord(15893, "X-axis translation", 244.42d), + new EpsgOperationParameterRecord(15893, "Y-axis translation", 85.352d), + new EpsgOperationParameterRecord(15893, "Z-axis translation", 168.129d), + new EpsgOperationParameterRecord(15893, "X-axis rotation", 8.936d), + new EpsgOperationParameterRecord(15893, "Y-axis rotation", -7.752d), + new EpsgOperationParameterRecord(15893, "Z-axis rotation", -12.5952d), + new EpsgOperationParameterRecord(15893, "Scale difference", 14.2723d), + new EpsgOperationParameterRecord(15894, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15894, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15894, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15896, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15896, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15896, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15897, "X-axis translation", 51.0d), + new EpsgOperationParameterRecord(15897, "Y-axis translation", 391.0d), + new EpsgOperationParameterRecord(15897, "Z-axis translation", -36.0d), + new EpsgOperationParameterRecord(15899, "X-axis translation", 105.0d), + new EpsgOperationParameterRecord(15899, "Y-axis translation", 326.0d), + new EpsgOperationParameterRecord(15899, "Z-axis translation", -102.5d), + new EpsgOperationParameterRecord(15899, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15899, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15899, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15899, "Scale difference", -0.6d), + new EpsgOperationParameterRecord(15900, "X-axis translation", -45.0d), + new EpsgOperationParameterRecord(15900, "Y-axis translation", 417.0d), + new EpsgOperationParameterRecord(15900, "Z-axis translation", -3.5d), + new EpsgOperationParameterRecord(15900, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15900, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15900, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15900, "Scale difference", -0.6d), + new EpsgOperationParameterRecord(15901, "X-axis translation", 287.58d), + new EpsgOperationParameterRecord(15901, "Y-axis translation", 177.78d), + new EpsgOperationParameterRecord(15901, "Z-axis translation", -135.41d), + new EpsgOperationParameterRecord(15902, "X-axis translation", 335.47d), + new EpsgOperationParameterRecord(15902, "Y-axis translation", 222.58d), + new EpsgOperationParameterRecord(15902, "Z-axis translation", -230.94d), + new EpsgOperationParameterRecord(15903, "X-axis translation", -11.64d), + new EpsgOperationParameterRecord(15903, "Y-axis translation", -348.6d), + new EpsgOperationParameterRecord(15903, "Z-axis translation", 291.98d), + new EpsgOperationParameterRecord(15904, "X-axis translation", -10.18d), + new EpsgOperationParameterRecord(15904, "Y-axis translation", -350.43d), + new EpsgOperationParameterRecord(15904, "Z-axis translation", 291.37d), + new EpsgOperationParameterRecord(15908, "X-axis translation", -208.4058d), + new EpsgOperationParameterRecord(15908, "Y-axis translation", -109.8777d), + new EpsgOperationParameterRecord(15908, "Z-axis translation", -2.5764d), + new EpsgOperationParameterRecord(15909, "X-axis translation", -115.8543d), + new EpsgOperationParameterRecord(15909, "Y-axis translation", -99.0583d), + new EpsgOperationParameterRecord(15909, "Z-axis translation", -152.4616d), + new EpsgOperationParameterRecord(15911, "X-axis translation", -1.977d), + new EpsgOperationParameterRecord(15911, "Y-axis translation", -13.06d), + new EpsgOperationParameterRecord(15911, "Z-axis translation", -9.993d), + new EpsgOperationParameterRecord(15911, "X-axis rotation", -0.364d), + new EpsgOperationParameterRecord(15911, "Y-axis rotation", -0.254d), + new EpsgOperationParameterRecord(15911, "Z-axis rotation", -0.689d), + new EpsgOperationParameterRecord(15911, "Scale difference", -1.037d), + new EpsgOperationParameterRecord(15912, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15912, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15912, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15913, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15913, "Y-axis translation", 125.0d), + new EpsgOperationParameterRecord(15913, "Z-axis translation", 196.0d), + new EpsgOperationParameterRecord(15918, "X-axis translation", 12.646d), + new EpsgOperationParameterRecord(15918, "Y-axis translation", -155.176d), + new EpsgOperationParameterRecord(15918, "Z-axis translation", -80.863d), + new EpsgOperationParameterRecord(15919, "X-axis translation", 15.53d), + new EpsgOperationParameterRecord(15919, "Y-axis translation", -113.82d), + new EpsgOperationParameterRecord(15919, "Z-axis translation", -41.38d), + new EpsgOperationParameterRecord(15919, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15919, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15919, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15919, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(15920, "X-axis translation", 31.4d), + new EpsgOperationParameterRecord(15920, "Y-axis translation", -144.3d), + new EpsgOperationParameterRecord(15920, "Z-axis translation", -74.8d), + new EpsgOperationParameterRecord(15920, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15920, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15920, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15920, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(15921, "X-axis translation", 15.8d), + new EpsgOperationParameterRecord(15921, "Y-axis translation", -154.4d), + new EpsgOperationParameterRecord(15921, "Z-axis translation", -82.3d), + new EpsgOperationParameterRecord(15922, "Latitude of natural origin", 1.28764666666694d), + new EpsgOperationParameterRecord(15922, "Longitude of natural origin", 103.853002222223d), + new EpsgOperationParameterRecord(15922, "False easting", 30000.0d), + new EpsgOperationParameterRecord(15922, "False northing", 30000.0d), + new EpsgOperationParameterRecord(15922, "Latitude of natural origin", 1.36666666666694d), + new EpsgOperationParameterRecord(15922, "Longitude of natural origin", 103.833333333334d), + new EpsgOperationParameterRecord(15922, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(15922, "False easting", 28001.642d), + new EpsgOperationParameterRecord(15922, "False northing", 38744.572d), + new EpsgOperationParameterRecord(15922, "Easting offset", 0.0d), + new EpsgOperationParameterRecord(15922, "Northing offset", 0.0d), + new EpsgOperationParameterRecord(15923, "X-axis translation", -117.7d), + new EpsgOperationParameterRecord(15923, "Y-axis translation", -100.3d), + new EpsgOperationParameterRecord(15923, "Z-axis translation", -152.4d), + new EpsgOperationParameterRecord(15924, "X-axis translation", 92.5515d), + new EpsgOperationParameterRecord(15924, "Y-axis translation", 10.8194d), + new EpsgOperationParameterRecord(15924, "Z-axis translation", -149.8852d), + new EpsgOperationParameterRecord(15925, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15925, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15925, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15926, "X-axis translation", -33.722d), + new EpsgOperationParameterRecord(15926, "Y-axis translation", 153.789d), + new EpsgOperationParameterRecord(15926, "Z-axis translation", 94.959d), + new EpsgOperationParameterRecord(15926, "X-axis rotation", 8.581d), + new EpsgOperationParameterRecord(15926, "Y-axis rotation", 4.478d), + new EpsgOperationParameterRecord(15926, "Z-axis rotation", -4.54d), + new EpsgOperationParameterRecord(15926, "Scale difference", 8.95d), + new EpsgOperationParameterRecord(15927, "X-axis translation", -33.722d), + new EpsgOperationParameterRecord(15927, "Y-axis translation", 153.789d), + new EpsgOperationParameterRecord(15927, "Z-axis translation", 94.959d), + new EpsgOperationParameterRecord(15927, "X-axis rotation", 8.581d), + new EpsgOperationParameterRecord(15927, "Y-axis rotation", 4.478d), + new EpsgOperationParameterRecord(15927, "Z-axis rotation", -4.54d), + new EpsgOperationParameterRecord(15927, "Scale difference", 8.95d), + new EpsgOperationParameterRecord(15928, "X-axis translation", -106.8686d), + new EpsgOperationParameterRecord(15928, "Y-axis translation", 52.2978d), + new EpsgOperationParameterRecord(15928, "Z-axis translation", -103.7239d), + new EpsgOperationParameterRecord(15928, "X-axis rotation", -0.3366d), + new EpsgOperationParameterRecord(15928, "Y-axis rotation", 0.457d), + new EpsgOperationParameterRecord(15928, "Z-axis rotation", -1.8422d), + new EpsgOperationParameterRecord(15928, "Scale difference", -1.2747d), + new EpsgOperationParameterRecord(15929, "X-axis translation", -106.8686d), + new EpsgOperationParameterRecord(15929, "Y-axis translation", 52.2978d), + new EpsgOperationParameterRecord(15929, "Z-axis translation", -103.7239d), + new EpsgOperationParameterRecord(15929, "X-axis rotation", -0.3366d), + new EpsgOperationParameterRecord(15929, "Y-axis rotation", 0.457d), + new EpsgOperationParameterRecord(15929, "Z-axis rotation", -1.8422d), + new EpsgOperationParameterRecord(15929, "Scale difference", -1.2747d), + new EpsgOperationParameterRecord(15931, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15931, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15931, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15934, "X-axis translation", 565.2369d), + new EpsgOperationParameterRecord(15934, "Y-axis translation", 50.0087d), + new EpsgOperationParameterRecord(15934, "Z-axis translation", 465.658d), + new EpsgOperationParameterRecord(15934, "X-axis rotation", 1.9725d), + new EpsgOperationParameterRecord(15934, "Y-axis rotation", -1.7004d), + new EpsgOperationParameterRecord(15934, "Z-axis rotation", 9.0677d), + new EpsgOperationParameterRecord(15934, "Scale difference", 4.0812d), + new EpsgOperationParameterRecord(15935, "X-axis translation", 18.0d), + new EpsgOperationParameterRecord(15935, "Y-axis translation", -136.8d), + new EpsgOperationParameterRecord(15935, "Z-axis translation", -73.7d), + new EpsgOperationParameterRecord(15935, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15935, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15935, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15935, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(15936, "X-axis translation", 11.911d), + new EpsgOperationParameterRecord(15936, "Y-axis translation", -154.833d), + new EpsgOperationParameterRecord(15936, "Z-axis translation", -80.079d), + new EpsgOperationParameterRecord(15937, "X-axis translation", -245.8d), + new EpsgOperationParameterRecord(15937, "Y-axis translation", -152.2d), + new EpsgOperationParameterRecord(15937, "Z-axis translation", 382.9d), + new EpsgOperationParameterRecord(15938, "X-axis translation", -225.4d), + new EpsgOperationParameterRecord(15938, "Y-axis translation", -158.7d), + new EpsgOperationParameterRecord(15938, "Z-axis translation", 380.8d), + new EpsgOperationParameterRecord(15938, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15938, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15938, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15938, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(15950, "Latitude of natural origin", -18.0d), + new EpsgOperationParameterRecord(15950, "Longitude of natural origin", 178.0d), + new EpsgOperationParameterRecord(15950, "False easting", 544000.0d), + new EpsgOperationParameterRecord(15950, "False northing", 704000.0d), + new EpsgOperationParameterRecord(15950, "Latitude of natural origin", -17.0d), + new EpsgOperationParameterRecord(15950, "Longitude of natural origin", 178.75d), + new EpsgOperationParameterRecord(15950, "Scale factor at natural origin", 0.99985d), + new EpsgOperationParameterRecord(15950, "False easting", 2000000.0d), + new EpsgOperationParameterRecord(15950, "False northing", 4000000.0d), + new EpsgOperationParameterRecord(15950, "Ordinate 1 of evaluation point in source CRS", 0.0d), + new EpsgOperationParameterRecord(15950, "Ordinate 2 of evaluation point in source CRS", 0.0d), + new EpsgOperationParameterRecord(15950, "Ordinate 1 of evaluation point in target CRS", 0.0d), + new EpsgOperationParameterRecord(15950, "Ordinate 2 of evaluation point in target CRS", 0.0d), + new EpsgOperationParameterRecord(15950, "Scaling factor for source CRS coord differences", 1e-06d), + new EpsgOperationParameterRecord(15950, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(15950, "A0", 1811328.51d), + new EpsgOperationParameterRecord(15950, "Au1v0", -874.49d), + new EpsgOperationParameterRecord(15950, "Au0v1", -798796.49d), + new EpsgOperationParameterRecord(15950, "Au2v0", 39.11d), + new EpsgOperationParameterRecord(15950, "Au1v1", -1.53d), + new EpsgOperationParameterRecord(15950, "Au0v2", -42.86d), + new EpsgOperationParameterRecord(15950, "B0", 3747242.86d), + new EpsgOperationParameterRecord(15950, "Bu1v0", -798796.55d), + new EpsgOperationParameterRecord(15950, "Bu0v1", 886.6d), + new EpsgOperationParameterRecord(15950, "Bu2v0", -1.25d), + new EpsgOperationParameterRecord(15950, "Bu1v1", -90.72d), + new EpsgOperationParameterRecord(15950, "Bu0v2", -2.03d), + new EpsgOperationParameterRecord(15951, "Latitude of natural origin", -16.2500000000003d), + new EpsgOperationParameterRecord(15951, "Longitude of natural origin", 179.333333333334d), + new EpsgOperationParameterRecord(15951, "False easting", 1251331.8d), + new EpsgOperationParameterRecord(15951, "False northing", 1662888.5d), + new EpsgOperationParameterRecord(15951, "Latitude of natural origin", -17.0d), + new EpsgOperationParameterRecord(15951, "Longitude of natural origin", 178.75d), + new EpsgOperationParameterRecord(15951, "Scale factor at natural origin", 0.99985d), + new EpsgOperationParameterRecord(15951, "False easting", 2000000.0d), + new EpsgOperationParameterRecord(15951, "False northing", 4000000.0d), + new EpsgOperationParameterRecord(15951, "Ordinate 1 of evaluation point in source CRS", 0.0d), + new EpsgOperationParameterRecord(15951, "Ordinate 2 of evaluation point in source CRS", 0.0d), + new EpsgOperationParameterRecord(15951, "Ordinate 1 of evaluation point in target CRS", 0.0d), + new EpsgOperationParameterRecord(15951, "Ordinate 2 of evaluation point in target CRS", 0.0d), + new EpsgOperationParameterRecord(15951, "Scaling factor for source CRS coord differences", 1e-06d), + new EpsgOperationParameterRecord(15951, "Scaling factor for target CRS coord differences", 1.0d), + new EpsgOperationParameterRecord(15951, "A0", 1809256.92d), + new EpsgOperationParameterRecord(15951, "Au1v0", 684.56d), + new EpsgOperationParameterRecord(15951, "Au0v1", -798948.34d), + new EpsgOperationParameterRecord(15951, "Au2v0", -36.16d), + new EpsgOperationParameterRecord(15951, "Au1v1", 3.24d), + new EpsgOperationParameterRecord(15951, "Au0v2", 37.55d), + new EpsgOperationParameterRecord(15951, "B0", 3749072.47d), + new EpsgOperationParameterRecord(15951, "Bu1v0", -798876.75d), + new EpsgOperationParameterRecord(15951, "Bu0v1", -682.38d), + new EpsgOperationParameterRecord(15951, "Bu2v0", -21.62d), + new EpsgOperationParameterRecord(15951, "Bu1v1", 73.18d), + new EpsgOperationParameterRecord(15951, "Bu0v2", -2.97d), + new EpsgOperationParameterRecord(15952, "X-axis translation", -244.2d), + new EpsgOperationParameterRecord(15952, "Y-axis translation", -149.8d), + new EpsgOperationParameterRecord(15952, "Z-axis translation", 379.3d), + new EpsgOperationParameterRecord(15953, "X-axis translation", -250.7d), + new EpsgOperationParameterRecord(15953, "Y-axis translation", -157.9d), + new EpsgOperationParameterRecord(15953, "Z-axis translation", 380.4d), + new EpsgOperationParameterRecord(15957, "X-axis translation", 163.511d), + new EpsgOperationParameterRecord(15957, "Y-axis translation", 127.533d), + new EpsgOperationParameterRecord(15957, "Z-axis translation", -159.789d), + new EpsgOperationParameterRecord(15957, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15957, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(15957, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(15957, "Scale difference", -0.6d), + new EpsgOperationParameterRecord(15964, "X-axis translation", -86.277d), + new EpsgOperationParameterRecord(15964, "Y-axis translation", -108.879d), + new EpsgOperationParameterRecord(15964, "Z-axis translation", -120.181d), + new EpsgOperationParameterRecord(15965, "X-axis translation", 589.0d), + new EpsgOperationParameterRecord(15965, "Y-axis translation", 76.0d), + new EpsgOperationParameterRecord(15965, "Z-axis translation", 480.0d), + new EpsgOperationParameterRecord(15967, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15967, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15967, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15969, "X-axis translation", -292.295d), + new EpsgOperationParameterRecord(15969, "Y-axis translation", 248.758d), + new EpsgOperationParameterRecord(15969, "Z-axis translation", 429.447d), + new EpsgOperationParameterRecord(15969, "X-axis rotation", -4.9971d), + new EpsgOperationParameterRecord(15969, "Y-axis rotation", -2.99d), + new EpsgOperationParameterRecord(15969, "Z-axis rotation", -6.6906d), + new EpsgOperationParameterRecord(15969, "Scale difference", 1.0289d), + new EpsgOperationParameterRecord(15970, "X-axis translation", -292.295d), + new EpsgOperationParameterRecord(15970, "Y-axis translation", 248.758d), + new EpsgOperationParameterRecord(15970, "Z-axis translation", 429.447d), + new EpsgOperationParameterRecord(15970, "X-axis rotation", -4.9971d), + new EpsgOperationParameterRecord(15970, "Y-axis rotation", -2.99d), + new EpsgOperationParameterRecord(15970, "Z-axis rotation", -6.6906d), + new EpsgOperationParameterRecord(15970, "Scale difference", 1.0289d), + new EpsgOperationParameterRecord(15971, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15971, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15971, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15972, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15972, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15972, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15974, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15974, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15974, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15975, "X-axis translation", 54.4d), + new EpsgOperationParameterRecord(15975, "Y-axis translation", -20.1d), + new EpsgOperationParameterRecord(15975, "Z-axis translation", 183.1d), + new EpsgOperationParameterRecord(15976, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(15976, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(15976, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(15978, "X-axis translation", 2.478d), + new EpsgOperationParameterRecord(15978, "Y-axis translation", 149.752d), + new EpsgOperationParameterRecord(15978, "Z-axis translation", 197.726d), + new EpsgOperationParameterRecord(15978, "X-axis rotation", -0.526d), + new EpsgOperationParameterRecord(15978, "Y-axis rotation", -0.498d), + new EpsgOperationParameterRecord(15978, "Z-axis rotation", 0.501d), + new EpsgOperationParameterRecord(15978, "Scale difference", 0.685d), + new EpsgOperationParameterRecord(15979, "X-axis translation", -117.808d), + new EpsgOperationParameterRecord(15979, "Y-axis translation", -51.536d), + new EpsgOperationParameterRecord(15979, "Z-axis translation", 137.784d), + new EpsgOperationParameterRecord(15979, "X-axis rotation", -0.303d), + new EpsgOperationParameterRecord(15979, "Y-axis rotation", -0.446d), + new EpsgOperationParameterRecord(15979, "Z-axis rotation", -0.234d), + new EpsgOperationParameterRecord(15979, "Scale difference", -0.29d), + new EpsgOperationParameterRecord(15980, "X-axis translation", -117.808d), + new EpsgOperationParameterRecord(15980, "Y-axis translation", -51.536d), + new EpsgOperationParameterRecord(15980, "Z-axis translation", 137.784d), + new EpsgOperationParameterRecord(15980, "X-axis rotation", -0.303d), + new EpsgOperationParameterRecord(15980, "Y-axis rotation", -0.446d), + new EpsgOperationParameterRecord(15980, "Z-axis rotation", -0.234d), + new EpsgOperationParameterRecord(15980, "Scale difference", -0.29d), + new EpsgOperationParameterRecord(15993, "X-axis translation", 68.1564d), + new EpsgOperationParameterRecord(15993, "Y-axis translation", 32.7756d), + new EpsgOperationParameterRecord(15993, "Z-axis translation", 80.2249d), + new EpsgOperationParameterRecord(15993, "X-axis rotation", 2.20333014d), + new EpsgOperationParameterRecord(15993, "Y-axis rotation", 2.19256447d), + new EpsgOperationParameterRecord(15993, "Z-axis rotation", -2.54166911d), + new EpsgOperationParameterRecord(15993, "Scale difference", -0.14155333d), + new EpsgOperationParameterRecord(15994, "X-axis translation", 2.3287d), + new EpsgOperationParameterRecord(15994, "Y-axis translation", -147.0425d), + new EpsgOperationParameterRecord(15994, "Z-axis translation", -92.0802d), + new EpsgOperationParameterRecord(15994, "X-axis rotation", 0.3092483d), + new EpsgOperationParameterRecord(15994, "Y-axis rotation", -0.32482185d), + new EpsgOperationParameterRecord(15994, "Z-axis rotation", -0.49729934d), + new EpsgOperationParameterRecord(15994, "Scale difference", 5.68906266d), + new EpsgOperationParameterRecord(15995, "X-axis translation", 2.329d), + new EpsgOperationParameterRecord(15995, "Y-axis translation", -147.042d), + new EpsgOperationParameterRecord(15995, "Z-axis translation", -92.08d), + new EpsgOperationParameterRecord(15995, "X-axis rotation", 0.309d), + new EpsgOperationParameterRecord(15995, "Y-axis rotation", -0.325d), + new EpsgOperationParameterRecord(15995, "Z-axis rotation", -0.497d), + new EpsgOperationParameterRecord(15995, "Scale difference", 5.69d), + new EpsgOperationParameterRecord(15996, "X-axis translation", 28.0d), + new EpsgOperationParameterRecord(15996, "Y-axis translation", -121.0d), + new EpsgOperationParameterRecord(15996, "Z-axis translation", -77.0d), + new EpsgOperationParameterRecord(15997, "X-axis translation", 23.0d), + new EpsgOperationParameterRecord(15997, "Y-axis translation", -124.0d), + new EpsgOperationParameterRecord(15997, "Z-axis translation", -82.0d), + new EpsgOperationParameterRecord(15998, "X-axis translation", 26.0d), + new EpsgOperationParameterRecord(15998, "Y-axis translation", -121.0d), + new EpsgOperationParameterRecord(15998, "Z-axis translation", -78.0d), + new EpsgOperationParameterRecord(15999, "X-axis translation", 24.0d), + new EpsgOperationParameterRecord(15999, "Y-axis translation", -130.0d), + new EpsgOperationParameterRecord(15999, "Z-axis translation", -92.0d), + new EpsgOperationParameterRecord(3896, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(3896, "X-axis translation", 577.326d), + new EpsgOperationParameterRecord(3896, "Y-axis translation", 90.129d), + new EpsgOperationParameterRecord(3896, "Z-axis translation", 463.919d), + new EpsgOperationParameterRecord(3896, "X-axis rotation", 5.137d), + new EpsgOperationParameterRecord(3896, "Y-axis rotation", 1.474d), + new EpsgOperationParameterRecord(3896, "Z-axis rotation", 5.297d), + new EpsgOperationParameterRecord(3896, "Scale difference", 2.4232d), + new EpsgOperationParameterRecord(3966, "Longitude offset", -17.6627833333336d), + new EpsgOperationParameterRecord(3966, "X-axis translation", 682.0d), + new EpsgOperationParameterRecord(3966, "Y-axis translation", -203.0d), + new EpsgOperationParameterRecord(3966, "Z-axis translation", 480.0d), + new EpsgOperationParameterRecord(4837, "X-axis translation", 565.04d), + new EpsgOperationParameterRecord(4837, "Y-axis translation", 49.91d), + new EpsgOperationParameterRecord(4837, "Z-axis translation", 465.84d), + new EpsgOperationParameterRecord(4837, "X-axis rotation", 1.9848d), + new EpsgOperationParameterRecord(4837, "Y-axis rotation", -1.7439d), + new EpsgOperationParameterRecord(4837, "Z-axis rotation", 9.0587d), + new EpsgOperationParameterRecord(4837, "Scale difference", 4.0772d), + new EpsgOperationParameterRecord(4837, "X-axis translation", -89.5d), + new EpsgOperationParameterRecord(4837, "Y-axis translation", -93.8d), + new EpsgOperationParameterRecord(4837, "Z-axis translation", -123.1d), + new EpsgOperationParameterRecord(4837, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(4837, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(4837, "Z-axis rotation", -0.156d), + new EpsgOperationParameterRecord(4837, "Scale difference", 1.2d), + new EpsgOperationParameterRecord(5190, "Longitude offset", 10.405d), + new EpsgOperationParameterRecord(5190, "X-axis translation", -145.907d), + new EpsgOperationParameterRecord(5190, "Y-axis translation", 505.034d), + new EpsgOperationParameterRecord(5190, "Z-axis translation", 685.756d), + new EpsgOperationParameterRecord(5190, "X-axis rotation", -1.162d), + new EpsgOperationParameterRecord(5190, "Y-axis rotation", 2.347d), + new EpsgOperationParameterRecord(5190, "Z-axis rotation", 1.592d), + new EpsgOperationParameterRecord(5190, "Scale difference", 6.342d), + new EpsgOperationParameterRecord(5190, "Ordinate 1 of evaluation point", -3159521.31d), + new EpsgOperationParameterRecord(5190, "Ordinate 2 of evaluation point", 4068151.32d), + new EpsgOperationParameterRecord(5190, "Ordinate 3 of evaluation point", 3748113.85d), + new EpsgOperationParameterRecord(5192, "Longitude offset", 10.405d), + new EpsgOperationParameterRecord(5192, "X-axis translation", -145.907d), + new EpsgOperationParameterRecord(5192, "Y-axis translation", 505.034d), + new EpsgOperationParameterRecord(5192, "Z-axis translation", 685.756d), + new EpsgOperationParameterRecord(5192, "X-axis rotation", -1.162d), + new EpsgOperationParameterRecord(5192, "Y-axis rotation", 2.347d), + new EpsgOperationParameterRecord(5192, "Z-axis rotation", 1.592d), + new EpsgOperationParameterRecord(5192, "Scale difference", 6.342d), + new EpsgOperationParameterRecord(5192, "Ordinate 1 of evaluation point", -3159521.31d), + new EpsgOperationParameterRecord(5192, "Ordinate 2 of evaluation point", 4068151.32d), + new EpsgOperationParameterRecord(5192, "Ordinate 3 of evaluation point", 3748113.85d), + new EpsgOperationParameterRecord(5230, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(5230, "X-axis translation", 485.0d), + new EpsgOperationParameterRecord(5230, "Y-axis translation", 169.5d), + new EpsgOperationParameterRecord(5230, "Z-axis translation", 483.8d), + new EpsgOperationParameterRecord(5230, "X-axis rotation", 7.786d), + new EpsgOperationParameterRecord(5230, "Y-axis rotation", 4.398d), + new EpsgOperationParameterRecord(5230, "Z-axis rotation", 4.103d), + new EpsgOperationParameterRecord(5230, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(5240, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(5240, "X-axis translation", 572.213d), + new EpsgOperationParameterRecord(5240, "Y-axis translation", 85.334d), + new EpsgOperationParameterRecord(5240, "Z-axis translation", 461.94d), + new EpsgOperationParameterRecord(5240, "X-axis rotation", -4.9732d), + new EpsgOperationParameterRecord(5240, "Y-axis rotation", -1.529d), + new EpsgOperationParameterRecord(5240, "Z-axis rotation", -5.2484d), + new EpsgOperationParameterRecord(5240, "Scale difference", 3.5378d), + new EpsgOperationParameterRecord(5242, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(5242, "X-axis translation", 572.213d), + new EpsgOperationParameterRecord(5242, "Y-axis translation", 85.334d), + new EpsgOperationParameterRecord(5242, "Z-axis translation", 461.94d), + new EpsgOperationParameterRecord(5242, "X-axis rotation", -4.9732d), + new EpsgOperationParameterRecord(5242, "Y-axis rotation", -1.529d), + new EpsgOperationParameterRecord(5242, "Z-axis rotation", -5.2484d), + new EpsgOperationParameterRecord(5242, "Scale difference", 3.5378d), + new EpsgOperationParameterRecord(5838, "Longitude offset", -9.13190611111139d), + new EpsgOperationParameterRecord(5838, "X-axis translation", -288.885d), + new EpsgOperationParameterRecord(5838, "Y-axis translation", -91.744d), + new EpsgOperationParameterRecord(5838, "Z-axis translation", 126.244d), + new EpsgOperationParameterRecord(5838, "X-axis rotation", 1.691d), + new EpsgOperationParameterRecord(5838, "Y-axis rotation", -0.41d), + new EpsgOperationParameterRecord(5838, "Z-axis rotation", 0.211d), + new EpsgOperationParameterRecord(5838, "Scale difference", -4.598d), + new EpsgOperationParameterRecord(6874, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(6874, "X-axis translation", -198.383d), + new EpsgOperationParameterRecord(6874, "Y-axis translation", -240.517d), + new EpsgOperationParameterRecord(6874, "Z-axis translation", -107.909d), + new EpsgOperationParameterRecord(7811, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(7965, "Vertical Offset", -2.7d), + new EpsgOperationParameterRecord(7967, "Vertical Offset", -2.7d), + new EpsgOperationParameterRecord(7983, "Vertical Offset", -0.146d), + new EpsgOperationParameterRecord(7986, "Vertical Offset", -4.74d), + new EpsgOperationParameterRecord(8047, "X-axis translation", -1.51d), + new EpsgOperationParameterRecord(8047, "Y-axis translation", -0.84d), + new EpsgOperationParameterRecord(8047, "Z-axis translation", -3.5d), + new EpsgOperationParameterRecord(8047, "X-axis rotation", -1.893d), + new EpsgOperationParameterRecord(8047, "Y-axis rotation", -0.687d), + new EpsgOperationParameterRecord(8047, "Z-axis rotation", -2.764d), + new EpsgOperationParameterRecord(8047, "Scale difference", 0.609d), + new EpsgOperationParameterRecord(8047, "X-axis translation", -82.981d), + new EpsgOperationParameterRecord(8047, "Y-axis translation", -99.719d), + new EpsgOperationParameterRecord(8047, "Z-axis translation", -110.709d), + new EpsgOperationParameterRecord(8047, "X-axis rotation", -0.5076d), + new EpsgOperationParameterRecord(8047, "Y-axis rotation", 0.1503d), + new EpsgOperationParameterRecord(8047, "Z-axis rotation", 0.3898d), + new EpsgOperationParameterRecord(8047, "Scale difference", -0.3143d), + new EpsgOperationParameterRecord(8094, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(8094, "X-axis translation", -168.0d), + new EpsgOperationParameterRecord(8094, "Y-axis translation", -60.0d), + new EpsgOperationParameterRecord(8094, "Z-axis translation", 320.0d), + new EpsgOperationParameterRecord(8174, "Longitude offset", -74.0809166666669d), + new EpsgOperationParameterRecord(8174, "X-axis translation", 307.0d), + new EpsgOperationParameterRecord(8174, "Y-axis translation", 304.0d), + new EpsgOperationParameterRecord(8174, "Z-axis translation", -318.0d), + new EpsgOperationParameterRecord(8175, "Longitude offset", 12.4523333333336d), + new EpsgOperationParameterRecord(8175, "X-axis translation", -225.0d), + new EpsgOperationParameterRecord(8175, "Y-axis translation", -65.0d), + new EpsgOperationParameterRecord(8175, "Z-axis translation", 9.0d), + new EpsgOperationParameterRecord(8176, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(8176, "X-axis translation", -189.0d), + new EpsgOperationParameterRecord(8176, "Y-axis translation", -242.0d), + new EpsgOperationParameterRecord(8176, "Z-axis translation", -91.0d), + new EpsgOperationParameterRecord(8178, "Longitude offset", 106.807719444445d), + new EpsgOperationParameterRecord(8178, "X-axis translation", -377.0d), + new EpsgOperationParameterRecord(8178, "Y-axis translation", 681.0d), + new EpsgOperationParameterRecord(8178, "Z-axis translation", -50.0d), + new EpsgOperationParameterRecord(8186, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(8186, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(8186, "Y-axis translation", 37.0d), + new EpsgOperationParameterRecord(8186, "Z-axis translation", 437.0d), + new EpsgOperationParameterRecord(8188, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(8188, "X-axis translation", -168.0d), + new EpsgOperationParameterRecord(8188, "Y-axis translation", -72.0d), + new EpsgOperationParameterRecord(8188, "Z-axis translation", 314.0d), + new EpsgOperationParameterRecord(8211, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(8211, "X-axis translation", -73.0d), + new EpsgOperationParameterRecord(8211, "Y-axis translation", -247.0d), + new EpsgOperationParameterRecord(8211, "Z-axis translation", 227.0d), + new EpsgOperationParameterRecord(8241, "A0", 8.4386918d), + new EpsgOperationParameterRecord(8241, "A1", -0.0972d), + new EpsgOperationParameterRecord(8241, "A2", -0.03672d), + new EpsgOperationParameterRecord(8241, "A3", 4.06e-05d), + new EpsgOperationParameterRecord(8241, "B00", -13276.58d), + new EpsgOperationParameterRecord(8241, "B0", 2.6620443d), + new EpsgOperationParameterRecord(8241, "B1", 0.07992d), + new EpsgOperationParameterRecord(8241, "B2", -0.0036d), + new EpsgOperationParameterRecord(8241, "B3", -1.09e-05d), + new EpsgOperationParameterRecord(8241, "X-axis translation", -84.0d), + new EpsgOperationParameterRecord(8241, "Y-axis translation", -107.0d), + new EpsgOperationParameterRecord(8241, "Z-axis translation", -120.0d), + new EpsgOperationParameterRecord(8363, "EPSG code for Interpolation CRS", 11076.0d), + new EpsgOperationParameterRecord(8363, "EPSG code for Interpolation CRS", 11076.0d), + new EpsgOperationParameterRecord(8442, "X-axis translation", -485.014055d), + new EpsgOperationParameterRecord(8442, "Y-axis translation", -169.473618d), + new EpsgOperationParameterRecord(8442, "Z-axis translation", -483.842943d), + new EpsgOperationParameterRecord(8442, "X-axis rotation", 7.78625453d), + new EpsgOperationParameterRecord(8442, "Y-axis rotation", 4.39770887d), + new EpsgOperationParameterRecord(8442, "Z-axis rotation", 4.10248899d), + new EpsgOperationParameterRecord(8442, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8443, "X-axis translation", 485.021d), + new EpsgOperationParameterRecord(8443, "Y-axis translation", 169.465d), + new EpsgOperationParameterRecord(8443, "Z-axis translation", 483.839d), + new EpsgOperationParameterRecord(8443, "X-axis rotation", -7.786342d), + new EpsgOperationParameterRecord(8443, "Y-axis rotation", -4.397554d), + new EpsgOperationParameterRecord(8443, "Z-axis rotation", -4.102655d), + new EpsgOperationParameterRecord(8443, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8517, "X-axis translation", 160.0d), + new EpsgOperationParameterRecord(8517, "Y-axis translation", 26.0d), + new EpsgOperationParameterRecord(8517, "Z-axis translation", 41.0d), + new EpsgOperationParameterRecord(8517, "X-axis translation", -154.5d), + new EpsgOperationParameterRecord(8517, "Y-axis translation", 150.7d), + new EpsgOperationParameterRecord(8517, "Z-axis translation", 100.4d), + new EpsgOperationParameterRecord(8532, "X-axis translation", 199.0d), + new EpsgOperationParameterRecord(8532, "Y-axis translation", 931.0d), + new EpsgOperationParameterRecord(8532, "Z-axis translation", 317.0d), + new EpsgOperationParameterRecord(8532, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8532, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8532, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8532, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8532, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8532, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8532, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8537, "X-axis translation", -121.8d), + new EpsgOperationParameterRecord(8537, "Y-axis translation", 98.1d), + new EpsgOperationParameterRecord(8537, "Z-axis translation", -15.2d), + new EpsgOperationParameterRecord(8537, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8537, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8537, "Z-axis translation", 4.5d), + new EpsgOperationParameterRecord(8537, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8537, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8537, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(8537, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(8562, "X-axis translation", -156.5d), + new EpsgOperationParameterRecord(8562, "Y-axis translation", -87.2d), + new EpsgOperationParameterRecord(8562, "Z-axis translation", 285.9d), + new EpsgOperationParameterRecord(8562, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8562, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8562, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8562, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8562, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8562, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8562, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8568, "X-axis translation", -174.6d), + new EpsgOperationParameterRecord(8568, "Y-axis translation", -3.1d), + new EpsgOperationParameterRecord(8568, "Z-axis translation", 236.2d), + new EpsgOperationParameterRecord(8568, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8568, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8568, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8568, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8568, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8568, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8568, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8569, "X-axis translation", -116.641d), + new EpsgOperationParameterRecord(8569, "Y-axis translation", -56.931d), + new EpsgOperationParameterRecord(8569, "Z-axis translation", -110.559d), + new EpsgOperationParameterRecord(8569, "X-axis rotation", 4.327d), + new EpsgOperationParameterRecord(8569, "Y-axis rotation", 4.464d), + new EpsgOperationParameterRecord(8569, "Z-axis rotation", -4.444d), + new EpsgOperationParameterRecord(8569, "Scale difference", -3.52d), + new EpsgOperationParameterRecord(8569, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8569, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8569, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8571, "X-axis translation", -171.16d), + new EpsgOperationParameterRecord(8571, "Y-axis translation", 17.29d), + new EpsgOperationParameterRecord(8571, "Z-axis translation", 323.31d), + new EpsgOperationParameterRecord(8571, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8571, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8571, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8571, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8571, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8571, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8571, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8581, "X-axis translation", -182.046d), + new EpsgOperationParameterRecord(8581, "Y-axis translation", -225.604d), + new EpsgOperationParameterRecord(8581, "Z-axis translation", 168.884d), + new EpsgOperationParameterRecord(8581, "X-axis rotation", -0.616d), + new EpsgOperationParameterRecord(8581, "Y-axis rotation", -1.655d), + new EpsgOperationParameterRecord(8581, "Z-axis rotation", 7.824d), + new EpsgOperationParameterRecord(8581, "Scale difference", 16.641d), + new EpsgOperationParameterRecord(8581, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8581, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8581, "Z-axis translation", 4.5d), + new EpsgOperationParameterRecord(8581, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8581, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8581, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(8581, "Scale difference", 0.2263d), + new EpsgOperationParameterRecord(8631, "X-axis translation", -56.1d), + new EpsgOperationParameterRecord(8631, "Y-axis translation", -167.8d), + new EpsgOperationParameterRecord(8631, "Z-axis translation", 13.1d), + new EpsgOperationParameterRecord(8631, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8631, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8631, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8631, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8631, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8631, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8631, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8632, "X-axis translation", -104.4d), + new EpsgOperationParameterRecord(8632, "Y-axis translation", -136.6d), + new EpsgOperationParameterRecord(8632, "Z-axis translation", 201.2d), + new EpsgOperationParameterRecord(8632, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8632, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8632, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8632, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8632, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8632, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8632, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8633, "X-axis translation", -37.0d), + new EpsgOperationParameterRecord(8633, "Y-axis translation", 157.0d), + new EpsgOperationParameterRecord(8633, "Z-axis translation", 85.0d), + new EpsgOperationParameterRecord(8633, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8633, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8633, "Z-axis translation", 4.5d), + new EpsgOperationParameterRecord(8633, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8633, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8633, "Z-axis rotation", 0.554d), + new EpsgOperationParameterRecord(8633, "Scale difference", 0.219d), + new EpsgOperationParameterRecord(8634, "X-axis translation", -101.0d), + new EpsgOperationParameterRecord(8634, "Y-axis translation", -111.0d), + new EpsgOperationParameterRecord(8634, "Z-axis translation", 187.0d), + new EpsgOperationParameterRecord(8634, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8634, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8634, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8634, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8634, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8634, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8634, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8636, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(8636, "X-axis translation", -263.0d), + new EpsgOperationParameterRecord(8636, "Y-axis translation", 6.0d), + new EpsgOperationParameterRecord(8636, "Z-axis translation", 431.0d), + new EpsgOperationParameterRecord(8638, "Longitude offset", 106.807719444445d), + new EpsgOperationParameterRecord(8638, "X-axis translation", -587.8d), + new EpsgOperationParameterRecord(8638, "Y-axis translation", 519.75d), + new EpsgOperationParameterRecord(8638, "Z-axis translation", 145.76d), + new EpsgOperationParameterRecord(8639, "Longitude offset", 10.7229166666669d), + new EpsgOperationParameterRecord(8639, "X-axis translation", 278.3d), + new EpsgOperationParameterRecord(8639, "Y-axis translation", 93.0d), + new EpsgOperationParameterRecord(8639, "Z-axis translation", 474.5d), + new EpsgOperationParameterRecord(8639, "X-axis rotation", 7.889d), + new EpsgOperationParameterRecord(8639, "Y-axis rotation", 0.05d), + new EpsgOperationParameterRecord(8639, "Z-axis rotation", -6.61d), + new EpsgOperationParameterRecord(8639, "Scale difference", 6.21d), + new EpsgOperationParameterRecord(8641, "Longitude offset", 106.807719444445d), + new EpsgOperationParameterRecord(8641, "X-axis translation", -403.0d), + new EpsgOperationParameterRecord(8641, "Y-axis translation", 684.0d), + new EpsgOperationParameterRecord(8641, "Z-axis translation", 41.0d), + new EpsgOperationParameterRecord(8642, "Longitude offset", -17.6666666666669d), + new EpsgOperationParameterRecord(8642, "X-axis translation", 570.8d), + new EpsgOperationParameterRecord(8642, "Y-axis translation", 85.7d), + new EpsgOperationParameterRecord(8642, "Z-axis translation", 462.8d), + new EpsgOperationParameterRecord(8642, "X-axis rotation", 4.998d), + new EpsgOperationParameterRecord(8642, "Y-axis rotation", 1.587d), + new EpsgOperationParameterRecord(8642, "Z-axis rotation", 5.261d), + new EpsgOperationParameterRecord(8642, "Scale difference", 3.56d), + new EpsgOperationParameterRecord(8643, "Latitude offset", -5.86d), + new EpsgOperationParameterRecord(8643, "Longitude offset", 0.28d), + new EpsgOperationParameterRecord(8643, "X-axis translation", -199.87d), + new EpsgOperationParameterRecord(8643, "Y-axis translation", 74.79d), + new EpsgOperationParameterRecord(8643, "Z-axis translation", 246.62d), + new EpsgOperationParameterRecord(8644, "Longitude offset", 23.7163375000003d), + new EpsgOperationParameterRecord(8644, "Latitude offset", -5.86d), + new EpsgOperationParameterRecord(8644, "Longitude offset", 0.28d), + new EpsgOperationParameterRecord(8644, "X-axis translation", -199.87d), + new EpsgOperationParameterRecord(8644, "Y-axis translation", 74.79d), + new EpsgOperationParameterRecord(8644, "Z-axis translation", 246.62d), + new EpsgOperationParameterRecord(8647, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8647, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8647, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8647, "X-axis translation", -0.991d), + new EpsgOperationParameterRecord(8647, "Y-axis translation", 1.9072d), + new EpsgOperationParameterRecord(8647, "Z-axis translation", 0.5129d), + new EpsgOperationParameterRecord(8647, "X-axis rotation", -1.25033e-07d), + new EpsgOperationParameterRecord(8647, "Y-axis rotation", -4.6785e-08d), + new EpsgOperationParameterRecord(8647, "Z-axis rotation", -5.6529e-08d), + new EpsgOperationParameterRecord(8647, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(8648, "Longitude offset", -9.13190611111139d), + new EpsgOperationParameterRecord(8648, "X-axis translation", 508.088d), + new EpsgOperationParameterRecord(8648, "Y-axis translation", -191.042d), + new EpsgOperationParameterRecord(8648, "Z-axis translation", 565.223d), + new EpsgOperationParameterRecord(8649, "Longitude offset", -9.13190611111139d), + new EpsgOperationParameterRecord(8649, "X-axis translation", 631.392d), + new EpsgOperationParameterRecord(8649, "Y-axis translation", -66.551d), + new EpsgOperationParameterRecord(8649, "Z-axis translation", 481.442d), + new EpsgOperationParameterRecord(8649, "X-axis rotation", -1.09d), + new EpsgOperationParameterRecord(8649, "Y-axis rotation", 4.445d), + new EpsgOperationParameterRecord(8649, "Z-axis rotation", 4.487d), + new EpsgOperationParameterRecord(8649, "Scale difference", -4.43d), + new EpsgOperationParameterRecord(8650, "X-axis translation", -181.0d), + new EpsgOperationParameterRecord(8650, "Y-axis translation", -122.0d), + new EpsgOperationParameterRecord(8650, "Z-axis translation", 225.0d), + new EpsgOperationParameterRecord(8650, "X-axis translation", -48.0d), + new EpsgOperationParameterRecord(8650, "Y-axis translation", 55.0d), + new EpsgOperationParameterRecord(8650, "Z-axis translation", 52.0d), + new EpsgOperationParameterRecord(8651, "X-axis translation", -2.227d), + new EpsgOperationParameterRecord(8651, "Y-axis translation", 6.524d), + new EpsgOperationParameterRecord(8651, "Z-axis translation", 2.178d), + new EpsgOperationParameterRecord(8651, "X-axis translation", 44.585d), + new EpsgOperationParameterRecord(8651, "Y-axis translation", -131.212d), + new EpsgOperationParameterRecord(8651, "Z-axis translation", -39.544d), + new EpsgOperationParameterRecord(8652, "X-axis translation", -0.652d), + new EpsgOperationParameterRecord(8652, "Y-axis translation", 1.619d), + new EpsgOperationParameterRecord(8652, "Z-axis translation", 0.213d), + new EpsgOperationParameterRecord(8652, "X-axis translation", 44.585d), + new EpsgOperationParameterRecord(8652, "Y-axis translation", -131.212d), + new EpsgOperationParameterRecord(8652, "Z-axis translation", -39.544d), + new EpsgOperationParameterRecord(8653, "Ordinate 1 of evaluation point", 55.0d), + new EpsgOperationParameterRecord(8653, "Ordinate 2 of evaluation point", 0.0d), + new EpsgOperationParameterRecord(8653, "Scaling factor for coord differences", 1.0d), + new EpsgOperationParameterRecord(8653, "A0", -5.56098e-06d), + new EpsgOperationParameterRecord(8653, "Au1v0", -1.55391e-06d), + new EpsgOperationParameterRecord(8653, "Au0v1", -4.0262e-07d), + new EpsgOperationParameterRecord(8653, "Au2v0", -5.09693e-07d), + new EpsgOperationParameterRecord(8653, "Au1v1", -8.19775e-07d), + new EpsgOperationParameterRecord(8653, "Au0v2", -2.47592e-07d), + new EpsgOperationParameterRecord(8653, "Au3v0", 1.36682e-07d), + new EpsgOperationParameterRecord(8653, "Au2v1", 1.86198e-07d), + new EpsgOperationParameterRecord(8653, "Au1v2", 1.2335e-07d), + new EpsgOperationParameterRecord(8653, "Au0v3", 5.68797e-08d), + new EpsgOperationParameterRecord(8653, "Au4v0", -2.32217e-09d), + new EpsgOperationParameterRecord(8653, "Au3v1", -7.69931e-09d), + new EpsgOperationParameterRecord(8653, "Au2v2", -7.86953e-09d), + new EpsgOperationParameterRecord(8653, "Au1v3", -6.12216e-09d), + new EpsgOperationParameterRecord(8653, "Au0v4", -4.01382e-09d), + new EpsgOperationParameterRecord(8653, "B0", 1.48944e-05d), + new EpsgOperationParameterRecord(8653, "Bu1v0", 2.68191e-06d), + new EpsgOperationParameterRecord(8653, "Bu0v1", 2.4529e-06d), + new EpsgOperationParameterRecord(8653, "Bu2v0", 2.944e-07d), + new EpsgOperationParameterRecord(8653, "Bu1v1", 1.5226e-06d), + new EpsgOperationParameterRecord(8653, "Bu0v2", 9.10592e-07d), + new EpsgOperationParameterRecord(8653, "Bu3v0", -3.68241e-07d), + new EpsgOperationParameterRecord(8653, "Bu2v1", -8.51732e-07d), + new EpsgOperationParameterRecord(8653, "Bu1v2", -5.66713e-07d), + new EpsgOperationParameterRecord(8653, "Bu0v3", -1.85188e-07d), + new EpsgOperationParameterRecord(8653, "Bu4v0", 2.84312e-08d), + new EpsgOperationParameterRecord(8653, "Bu3v1", 6.84853e-08d), + new EpsgOperationParameterRecord(8653, "Bu2v2", 5.00828e-08d), + new EpsgOperationParameterRecord(8653, "Bu1v3", 4.15937e-08d), + new EpsgOperationParameterRecord(8653, "Bu0v4", 7.62236e-09d), + new EpsgOperationParameterRecord(8653, "X-axis translation", -82.981d), + new EpsgOperationParameterRecord(8653, "Y-axis translation", -99.719d), + new EpsgOperationParameterRecord(8653, "Z-axis translation", -110.709d), + new EpsgOperationParameterRecord(8653, "X-axis rotation", -0.5076d), + new EpsgOperationParameterRecord(8653, "Y-axis rotation", 0.1503d), + new EpsgOperationParameterRecord(8653, "Z-axis rotation", 0.3898d), + new EpsgOperationParameterRecord(8653, "Scale difference", -0.3143d), + new EpsgOperationParameterRecord(8654, "Ordinate 1 of evaluation point", 55.0d), + new EpsgOperationParameterRecord(8654, "Ordinate 2 of evaluation point", 0.0d), + new EpsgOperationParameterRecord(8654, "Scaling factor for coord differences", 1.0d), + new EpsgOperationParameterRecord(8654, "A0", -5.56098e-06d), + new EpsgOperationParameterRecord(8654, "Au1v0", -1.55391e-06d), + new EpsgOperationParameterRecord(8654, "Au0v1", -4.0262e-07d), + new EpsgOperationParameterRecord(8654, "Au2v0", -5.09693e-07d), + new EpsgOperationParameterRecord(8654, "Au1v1", -8.19775e-07d), + new EpsgOperationParameterRecord(8654, "Au0v2", -2.47592e-07d), + new EpsgOperationParameterRecord(8654, "Au3v0", 1.36682e-07d), + new EpsgOperationParameterRecord(8654, "Au2v1", 1.86198e-07d), + new EpsgOperationParameterRecord(8654, "Au1v2", 1.2335e-07d), + new EpsgOperationParameterRecord(8654, "Au0v3", 5.68797e-08d), + new EpsgOperationParameterRecord(8654, "Au4v0", -2.32217e-09d), + new EpsgOperationParameterRecord(8654, "Au3v1", -7.69931e-09d), + new EpsgOperationParameterRecord(8654, "Au2v2", -7.86953e-09d), + new EpsgOperationParameterRecord(8654, "Au1v3", -6.12216e-09d), + new EpsgOperationParameterRecord(8654, "Au0v4", -4.01382e-09d), + new EpsgOperationParameterRecord(8654, "B0", 1.48944e-05d), + new EpsgOperationParameterRecord(8654, "Bu1v0", 2.68191e-06d), + new EpsgOperationParameterRecord(8654, "Bu0v1", 2.4529e-06d), + new EpsgOperationParameterRecord(8654, "Bu2v0", 2.944e-07d), + new EpsgOperationParameterRecord(8654, "Bu1v1", 1.5226e-06d), + new EpsgOperationParameterRecord(8654, "Bu0v2", 9.10592e-07d), + new EpsgOperationParameterRecord(8654, "Bu3v0", -3.68241e-07d), + new EpsgOperationParameterRecord(8654, "Bu2v1", -8.51732e-07d), + new EpsgOperationParameterRecord(8654, "Bu1v2", -5.66713e-07d), + new EpsgOperationParameterRecord(8654, "Bu0v3", -1.85188e-07d), + new EpsgOperationParameterRecord(8654, "Bu4v0", 2.84312e-08d), + new EpsgOperationParameterRecord(8654, "Bu3v1", 6.84853e-08d), + new EpsgOperationParameterRecord(8654, "Bu2v2", 5.00828e-08d), + new EpsgOperationParameterRecord(8654, "Bu1v3", 4.15937e-08d), + new EpsgOperationParameterRecord(8654, "Bu0v4", 7.62236e-09d), + new EpsgOperationParameterRecord(8654, "X-axis translation", -82.981d), + new EpsgOperationParameterRecord(8654, "Y-axis translation", -99.719d), + new EpsgOperationParameterRecord(8654, "Z-axis translation", -110.709d), + new EpsgOperationParameterRecord(8654, "X-axis rotation", -0.5076d), + new EpsgOperationParameterRecord(8654, "Y-axis rotation", 0.1503d), + new EpsgOperationParameterRecord(8654, "Z-axis rotation", 0.3898d), + new EpsgOperationParameterRecord(8654, "Scale difference", -0.3143d), + new EpsgOperationParameterRecord(8654, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8654, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8654, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8655, "X-axis translation", -56.7d), + new EpsgOperationParameterRecord(8655, "Y-axis translation", -171.8d), + new EpsgOperationParameterRecord(8655, "Z-axis translation", -40.6d), + new EpsgOperationParameterRecord(8655, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8655, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8655, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8655, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8655, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8655, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8655, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8656, "X-axis translation", -255.0d), + new EpsgOperationParameterRecord(8656, "Y-axis translation", -29.0d), + new EpsgOperationParameterRecord(8656, "Z-axis translation", -105.0d), + new EpsgOperationParameterRecord(8656, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8656, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8656, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8656, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8656, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8656, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8656, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8657, "X-axis translation", -123.0d), + new EpsgOperationParameterRecord(8657, "Y-axis translation", 98.0d), + new EpsgOperationParameterRecord(8657, "Z-axis translation", 2.0d), + new EpsgOperationParameterRecord(8657, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8657, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8657, "Z-axis translation", 1.9d), + new EpsgOperationParameterRecord(8657, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8657, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(8657, "Z-axis rotation", 0.814d), + new EpsgOperationParameterRecord(8657, "Scale difference", -0.38d), + new EpsgOperationParameterRecord(8659, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(8659, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(8659, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(8659, "X-axis translation", -11.0d), + new EpsgOperationParameterRecord(8659, "Y-axis translation", 851.0d), + new EpsgOperationParameterRecord(8659, "Z-axis translation", 5.0d), + new EpsgOperationParameterRecord(9091, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9091, "Y-axis translation", -0.51d), + new EpsgOperationParameterRecord(9091, "Z-axis translation", 15.53d), + new EpsgOperationParameterRecord(9091, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9091, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9091, "Z-axis rotation", 0.05984d), + new EpsgOperationParameterRecord(9091, "Scale difference", -1.51099d), + new EpsgOperationParameterRecord(9091, "Rate of change of X-axis translation", 0.69d), + new EpsgOperationParameterRecord(9091, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(9091, "Rate of change of Z-axis translation", 1.86d), + new EpsgOperationParameterRecord(9091, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9091, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9091, "Rate of change of Z-axis rotation", -0.00027d), + new EpsgOperationParameterRecord(9091, "Rate of change of scale difference", -0.19201d), + new EpsgOperationParameterRecord(9091, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9092, "X-axis translation", 6.7d), + new EpsgOperationParameterRecord(9092, "Y-axis translation", 3.79d), + new EpsgOperationParameterRecord(9092, "Z-axis translation", -7.17d), + new EpsgOperationParameterRecord(9092, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9092, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9092, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9092, "Scale difference", 0.06901d), + new EpsgOperationParameterRecord(9092, "Rate of change of X-axis translation", 0.69d), + new EpsgOperationParameterRecord(9092, "Rate of change of Y-axis translation", -0.7d), + new EpsgOperationParameterRecord(9092, "Rate of change of Z-axis translation", 0.46d), + new EpsgOperationParameterRecord(9092, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9092, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9092, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9092, "Rate of change of scale difference", -0.18201d), + new EpsgOperationParameterRecord(9092, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9093, "X-axis translation", 6.8d), + new EpsgOperationParameterRecord(9093, "Y-axis translation", 2.99d), + new EpsgOperationParameterRecord(9093, "Z-axis translation", -12.97d), + new EpsgOperationParameterRecord(9093, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9093, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9093, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9093, "Scale difference", 0.46901d), + new EpsgOperationParameterRecord(9093, "Rate of change of X-axis translation", 0.49d), + new EpsgOperationParameterRecord(9093, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9093, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9093, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9093, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9093, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9093, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9093, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9094, "X-axis translation", 4.8d), + new EpsgOperationParameterRecord(9094, "Y-axis translation", 2.09d), + new EpsgOperationParameterRecord(9094, "Z-axis translation", -17.67d), + new EpsgOperationParameterRecord(9094, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9094, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9094, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9094, "Scale difference", 1.40901d), + new EpsgOperationParameterRecord(9094, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9094, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9094, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9094, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9094, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9094, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9094, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9094, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9095, "X-axis translation", 4.8d), + new EpsgOperationParameterRecord(9095, "Y-axis translation", 2.09d), + new EpsgOperationParameterRecord(9095, "Z-axis translation", -17.67d), + new EpsgOperationParameterRecord(9095, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9095, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9095, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9095, "Scale difference", 1.40901d), + new EpsgOperationParameterRecord(9095, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9095, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9095, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9095, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9095, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9095, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9095, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9095, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9096, "X-axis translation", 4.8d), + new EpsgOperationParameterRecord(9096, "Y-axis translation", 2.09d), + new EpsgOperationParameterRecord(9096, "Z-axis translation", -17.67d), + new EpsgOperationParameterRecord(9096, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9096, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9096, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9096, "Scale difference", 1.40901d), + new EpsgOperationParameterRecord(9096, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9096, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9096, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9096, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9096, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9096, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9096, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9096, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9097, "X-axis translation", 4.8d), + new EpsgOperationParameterRecord(9097, "Y-axis translation", 2.09d), + new EpsgOperationParameterRecord(9097, "Z-axis translation", -17.67d), + new EpsgOperationParameterRecord(9097, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9097, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9097, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9097, "Scale difference", 1.40901d), + new EpsgOperationParameterRecord(9097, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9097, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9097, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9097, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9097, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9097, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9097, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9097, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9098, "X-axis translation", 4.8d), + new EpsgOperationParameterRecord(9098, "Y-axis translation", 2.09d), + new EpsgOperationParameterRecord(9098, "Z-axis translation", -17.67d), + new EpsgOperationParameterRecord(9098, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9098, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9098, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9098, "Scale difference", 1.40901d), + new EpsgOperationParameterRecord(9098, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9098, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9098, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9098, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9098, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9098, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9098, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9098, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9099, "X-axis translation", 4.8d), + new EpsgOperationParameterRecord(9099, "Y-axis translation", 2.09d), + new EpsgOperationParameterRecord(9099, "Z-axis translation", -17.67d), + new EpsgOperationParameterRecord(9099, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9099, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9099, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9099, "Scale difference", 1.40901d), + new EpsgOperationParameterRecord(9099, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9099, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9099, "Rate of change of Z-axis translation", -1.34d), + new EpsgOperationParameterRecord(9099, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9099, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9099, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9099, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9099, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9100, "X-axis translation", 6.4d), + new EpsgOperationParameterRecord(9100, "Y-axis translation", 3.99d), + new EpsgOperationParameterRecord(9100, "Z-axis translation", -14.27d), + new EpsgOperationParameterRecord(9100, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9100, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9100, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9100, "Scale difference", 1.08901d), + new EpsgOperationParameterRecord(9100, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9100, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9100, "Rate of change of Z-axis translation", -1.44d), + new EpsgOperationParameterRecord(9100, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9100, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9100, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9100, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(9100, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9101, "X-axis translation", 6.4d), + new EpsgOperationParameterRecord(9101, "Y-axis translation", 3.99d), + new EpsgOperationParameterRecord(9101, "Z-axis translation", -14.27d), + new EpsgOperationParameterRecord(9101, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9101, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9101, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9101, "Scale difference", 1.08901d), + new EpsgOperationParameterRecord(9101, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9101, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9101, "Rate of change of Z-axis translation", -1.44d), + new EpsgOperationParameterRecord(9101, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9101, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9101, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9101, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(9101, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9102, "X-axis translation", 6.4d), + new EpsgOperationParameterRecord(9102, "Y-axis translation", 3.99d), + new EpsgOperationParameterRecord(9102, "Z-axis translation", -14.27d), + new EpsgOperationParameterRecord(9102, "X-axis rotation", -0.16508d), + new EpsgOperationParameterRecord(9102, "Y-axis rotation", 0.26897d), + new EpsgOperationParameterRecord(9102, "Z-axis rotation", 0.11984d), + new EpsgOperationParameterRecord(9102, "Scale difference", 1.08901d), + new EpsgOperationParameterRecord(9102, "Rate of change of X-axis translation", 0.79d), + new EpsgOperationParameterRecord(9102, "Rate of change of Y-axis translation", -0.6d), + new EpsgOperationParameterRecord(9102, "Rate of change of Z-axis translation", -1.44d), + new EpsgOperationParameterRecord(9102, "Rate of change of X-axis rotation", -0.01347d), + new EpsgOperationParameterRecord(9102, "Rate of change of Y-axis rotation", 0.01514d), + new EpsgOperationParameterRecord(9102, "Rate of change of Z-axis rotation", 0.01973d), + new EpsgOperationParameterRecord(9102, "Rate of change of scale difference", -0.07201d), + new EpsgOperationParameterRecord(9102, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(9103, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9103, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9103, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9103, "X-axis translation", 0.99343d), + new EpsgOperationParameterRecord(9103, "Y-axis translation", -1.90331d), + new EpsgOperationParameterRecord(9103, "Z-axis translation", -0.52655d), + new EpsgOperationParameterRecord(9103, "X-axis rotation", 25.91467d), + new EpsgOperationParameterRecord(9103, "Y-axis rotation", 9.42645d), + new EpsgOperationParameterRecord(9103, "Z-axis rotation", 11.59935d), + new EpsgOperationParameterRecord(9103, "Scale difference", 1.71504d), + new EpsgOperationParameterRecord(9103, "Rate of change of X-axis translation", 0.00079d), + new EpsgOperationParameterRecord(9103, "Rate of change of Y-axis translation", -0.0006d), + new EpsgOperationParameterRecord(9103, "Rate of change of Z-axis translation", -0.00134d), + new EpsgOperationParameterRecord(9103, "Rate of change of X-axis rotation", 0.06667d), + new EpsgOperationParameterRecord(9103, "Rate of change of Y-axis rotation", -0.75744d), + new EpsgOperationParameterRecord(9103, "Rate of change of Z-axis rotation", -0.05133d), + new EpsgOperationParameterRecord(9103, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9103, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(9103, "X-axis translation", -1.6d), + new EpsgOperationParameterRecord(9103, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(9103, "Z-axis translation", -2.4d), + new EpsgOperationParameterRecord(9103, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9103, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9103, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9103, "Scale difference", 0.02d), + new EpsgOperationParameterRecord(9103, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9103, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9103, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(9103, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9103, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9103, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9103, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(9103, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(9104, "X-axis translation", 0.99343d), + new EpsgOperationParameterRecord(9104, "Y-axis translation", -1.90331d), + new EpsgOperationParameterRecord(9104, "Z-axis translation", -0.52655d), + new EpsgOperationParameterRecord(9104, "X-axis rotation", 25.91467d), + new EpsgOperationParameterRecord(9104, "Y-axis rotation", 9.42645d), + new EpsgOperationParameterRecord(9104, "Z-axis rotation", 11.59935d), + new EpsgOperationParameterRecord(9104, "Scale difference", 1.71504d), + new EpsgOperationParameterRecord(9104, "Rate of change of X-axis translation", 0.00079d), + new EpsgOperationParameterRecord(9104, "Rate of change of Y-axis translation", -0.0006d), + new EpsgOperationParameterRecord(9104, "Rate of change of Z-axis translation", -0.00134d), + new EpsgOperationParameterRecord(9104, "Rate of change of X-axis rotation", 0.06667d), + new EpsgOperationParameterRecord(9104, "Rate of change of Y-axis rotation", -0.75744d), + new EpsgOperationParameterRecord(9104, "Rate of change of Z-axis rotation", -0.05133d), + new EpsgOperationParameterRecord(9104, "Rate of change of scale difference", -0.10201d), + new EpsgOperationParameterRecord(9104, "Parameter reference epoch", 1997.0d), + new EpsgOperationParameterRecord(9104, "X-axis translation", -1.6d), + new EpsgOperationParameterRecord(9104, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(9104, "Z-axis translation", -2.4d), + new EpsgOperationParameterRecord(9104, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9104, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9104, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9104, "Scale difference", 0.02d), + new EpsgOperationParameterRecord(9104, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9104, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9104, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(9104, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9104, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9104, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9104, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(9104, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(9337, "Longitude offset", 2.5969213d), + new EpsgOperationParameterRecord(9337, "EPSG code for Interpolation CRS", 4171.0d), + new EpsgOperationParameterRecord(9337, "EPSG code for standard transformation T0", 1651.0d), + new EpsgOperationParameterRecord(9499, "EPSG code for Interpolation CRS", 11057.0d), + new EpsgOperationParameterRecord(9499, "EPSG code for Interpolation CRS", 4312.0d), + new EpsgOperationParameterRecord(9683, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9683, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9683, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9683, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9683, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9683, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9683, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9683, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9683, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9683, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9683, "Rate of change of X-axis rotation", 1.50379d), + new EpsgOperationParameterRecord(9683, "Rate of change of Y-axis rotation", 1.18346d), + new EpsgOperationParameterRecord(9683, "Rate of change of Z-axis rotation", 1.20716d), + new EpsgOperationParameterRecord(9683, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9683, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(9685, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9685, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9685, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9685, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9685, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9685, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9685, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9685, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9685, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9685, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9685, "Rate of change of X-axis rotation", 1.50379d), + new EpsgOperationParameterRecord(9685, "Rate of change of Y-axis rotation", 1.18346d), + new EpsgOperationParameterRecord(9685, "Rate of change of Z-axis rotation", 1.20716d), + new EpsgOperationParameterRecord(9685, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9685, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(9687, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9687, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9687, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9687, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9687, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9687, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(9687, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(9687, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(9687, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(9687, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(9687, "Rate of change of X-axis rotation", -1.50379d), + new EpsgOperationParameterRecord(9687, "Rate of change of Y-axis rotation", -1.18346d), + new EpsgOperationParameterRecord(9687, "Rate of change of Z-axis rotation", -1.20716d), + new EpsgOperationParameterRecord(9687, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(9687, "Parameter reference epoch", 2020.0d), + new EpsgOperationParameterRecord(9750, "Vertical Offset", 0.141d), + new EpsgOperationParameterRecord(10392, "Vertical Offset", -2.0d), + new EpsgOperationParameterRecord(10393, "Vertical Offset", -2.08d), + new EpsgOperationParameterRecord(10394, "Vertical Offset", -1.4d), + new EpsgOperationParameterRecord(10395, "Vertical Offset", -1.4d), + new EpsgOperationParameterRecord(10396, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10397, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10398, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10399, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10400, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10409, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10410, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10411, "Vertical Offset", -1.0d), + new EpsgOperationParameterRecord(10495, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10495, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10496, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10496, "EPSG code for Interpolation CRS", 4258.0d), + new EpsgOperationParameterRecord(10616, "EPSG code for Interpolation CRS", 9470.0d), + new EpsgOperationParameterRecord(10616, "EPSG code for Interpolation CRS", 9470.0d), + new EpsgOperationParameterRecord(10675, "X-axis translation", 1138.7432d), + new EpsgOperationParameterRecord(10675, "Y-axis translation", -2064.4761d), + new EpsgOperationParameterRecord(10675, "Z-axis translation", 110.7016d), + new EpsgOperationParameterRecord(10675, "X-axis rotation", -214.615206d), + new EpsgOperationParameterRecord(10675, "Y-axis rotation", 479.360036d), + new EpsgOperationParameterRecord(10675, "Z-axis rotation", -164.703951d), + new EpsgOperationParameterRecord(10675, "Scale difference", -402.32073d), + new EpsgOperationParameterRecord(10675, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10754, "X-axis translation", 1276.2485d), + new EpsgOperationParameterRecord(10754, "Y-axis translation", -2016.6406d), + new EpsgOperationParameterRecord(10754, "Z-axis translation", 667.4403d), + new EpsgOperationParameterRecord(10754, "X-axis rotation", -101.005288d), + new EpsgOperationParameterRecord(10754, "Y-axis rotation", 212.913401d), + new EpsgOperationParameterRecord(10754, "Z-axis rotation", -68.43277d), + new EpsgOperationParameterRecord(10754, "Scale difference", -431.59604d), + new EpsgOperationParameterRecord(10754, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10755, "X-axis translation", 1138.7432d), + new EpsgOperationParameterRecord(10755, "Y-axis translation", -2064.4761d), + new EpsgOperationParameterRecord(10755, "Z-axis translation", 110.7016d), + new EpsgOperationParameterRecord(10755, "X-axis rotation", -214.615206d), + new EpsgOperationParameterRecord(10755, "Y-axis rotation", 479.360036d), + new EpsgOperationParameterRecord(10755, "Z-axis rotation", -164.703951d), + new EpsgOperationParameterRecord(10755, "Scale difference", -402.32073d), + new EpsgOperationParameterRecord(10755, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10756, "X-axis translation", 1276.2485d), + new EpsgOperationParameterRecord(10756, "Y-axis translation", -2016.6406d), + new EpsgOperationParameterRecord(10756, "Z-axis translation", 667.4403d), + new EpsgOperationParameterRecord(10756, "X-axis rotation", -101.005288d), + new EpsgOperationParameterRecord(10756, "Y-axis rotation", 212.913401d), + new EpsgOperationParameterRecord(10756, "Z-axis rotation", -68.43277d), + new EpsgOperationParameterRecord(10756, "Scale difference", -431.59604d), + new EpsgOperationParameterRecord(10756, "Geoid height", 0.0d), + new EpsgOperationParameterRecord(10778, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(10778, "Longitude of natural origin", 27.0d), + new EpsgOperationParameterRecord(10778, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10778, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(10778, "False northing", 0.0d), + new EpsgOperationParameterRecord(10778, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(10778, "Longitude of natural origin", 27.0d), + new EpsgOperationParameterRecord(10778, "Scale factor at natural origin", 1.0d), + new EpsgOperationParameterRecord(10778, "False easting", 3500000.0d), + new EpsgOperationParameterRecord(10778, "False northing", 0.0d), + new EpsgOperationParameterRecord(10778, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(10778, "Longitude of natural origin", 27.0d), + new EpsgOperationParameterRecord(10778, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(10778, "False easting", 500000.0d), + new EpsgOperationParameterRecord(10778, "False northing", 0.0d), + new EpsgOperationParameterRecord(10778, "Latitude of natural origin", 0.0d), + new EpsgOperationParameterRecord(10778, "Longitude of natural origin", 27.0d), + new EpsgOperationParameterRecord(10778, "Scale factor at natural origin", 0.9996d), + new EpsgOperationParameterRecord(10778, "False easting", 500000.0d), + new EpsgOperationParameterRecord(10778, "False northing", 0.0d), + new EpsgOperationParameterRecord(10815, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10815, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10815, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10815, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(10815, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(10815, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(10815, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10815, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10815, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10815, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10815, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10815, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10815, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10815, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10815, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10815, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10815, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10815, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10816, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10816, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10816, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10816, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(10816, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(10816, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(10816, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10816, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10816, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10816, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10816, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10816, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10816, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10816, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10816, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10816, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10816, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10816, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10816, "X-axis translation", 0.15651d), + new EpsgOperationParameterRecord(10816, "Y-axis translation", -0.10993d), + new EpsgOperationParameterRecord(10816, "Z-axis translation", -0.10935d), + new EpsgOperationParameterRecord(10816, "X-axis rotation", -3.12861d), + new EpsgOperationParameterRecord(10816, "Y-axis rotation", -3.78935d), + new EpsgOperationParameterRecord(10816, "Z-axis rotation", 4.03512d), + new EpsgOperationParameterRecord(10816, "Scale difference", 5.29d), + new EpsgOperationParameterRecord(10816, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10816, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10816, "Target epoch", 1997.0d), + new EpsgOperationParameterRecord(10817, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10817, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10817, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10817, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(10817, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(10817, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(10817, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10817, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10817, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10817, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10817, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10817, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10817, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10817, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10817, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10817, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10817, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10817, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10817, "EPSG code for Interpolation CRS for geocentric translation grid file", 10807.0d), + new EpsgOperationParameterRecord(10817, "EPSG code for Interpolation CRS for point motion velocity grid file", 10807.0d), + new EpsgOperationParameterRecord(10817, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10817, "Target epoch", 1995.0d), + new EpsgOperationParameterRecord(10818, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10818, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10818, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10818, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(10818, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(10818, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(10818, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10818, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10818, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10818, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10818, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10818, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10818, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10818, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10818, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10818, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10818, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10818, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10818, "X-axis translation", 0.36749d), + new EpsgOperationParameterRecord(10818, "Y-axis translation", 0.14351d), + new EpsgOperationParameterRecord(10818, "Z-axis translation", -0.18472d), + new EpsgOperationParameterRecord(10818, "X-axis rotation", 4.7914d), + new EpsgOperationParameterRecord(10818, "Y-axis rotation", -10.27566d), + new EpsgOperationParameterRecord(10818, "Z-axis rotation", 2.76102d), + new EpsgOperationParameterRecord(10818, "Scale difference", -3.684d), + new EpsgOperationParameterRecord(10818, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10818, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10818, "Target epoch", 2003.75d), + new EpsgOperationParameterRecord(10824, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10824, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10824, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10824, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(10824, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(10824, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(10824, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10824, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10824, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10824, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10824, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10824, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10824, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10824, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10824, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10824, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10824, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10824, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10824, "X-axis translation", 0.03054d), + new EpsgOperationParameterRecord(10824, "Y-axis translation", 0.04606d), + new EpsgOperationParameterRecord(10824, "Z-axis translation", -0.07944d), + new EpsgOperationParameterRecord(10824, "X-axis rotation", 1.41958d), + new EpsgOperationParameterRecord(10824, "Y-axis rotation", 0.15132d), + new EpsgOperationParameterRecord(10824, "Z-axis rotation", 1.50337d), + new EpsgOperationParameterRecord(10824, "Scale difference", 3.002d), + new EpsgOperationParameterRecord(10824, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10824, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10824, "Target epoch", 1999.5d), + new EpsgOperationParameterRecord(10825, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10825, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10825, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10825, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(10825, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(10825, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(10825, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10825, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10825, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10825, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10825, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10825, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10825, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10825, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10825, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10825, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10825, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10825, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10825, "X-axis translation", -0.05027d), + new EpsgOperationParameterRecord(10825, "Y-axis translation", -0.11595d), + new EpsgOperationParameterRecord(10825, "Z-axis translation", 0.03012d), + new EpsgOperationParameterRecord(10825, "X-axis rotation", -3.10814d), + new EpsgOperationParameterRecord(10825, "Y-axis rotation", 4.57237d), + new EpsgOperationParameterRecord(10825, "Z-axis rotation", 4.72406d), + new EpsgOperationParameterRecord(10825, "Scale difference", 3.191d), + new EpsgOperationParameterRecord(10825, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10825, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10825, "Target epoch", 1997.56d), + new EpsgOperationParameterRecord(10868, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10868, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(10868, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10868, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(10868, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(10868, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(10868, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10868, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10868, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10868, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10868, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10868, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10868, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10868, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10868, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10868, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10868, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10868, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10868, "X-axis translation", -0.05027d), + new EpsgOperationParameterRecord(10868, "Y-axis translation", -0.11595d), + new EpsgOperationParameterRecord(10868, "Z-axis translation", 0.03012d), + new EpsgOperationParameterRecord(10868, "X-axis rotation", -3.10814d), + new EpsgOperationParameterRecord(10868, "Y-axis rotation", 4.57237d), + new EpsgOperationParameterRecord(10868, "Z-axis rotation", 4.72406d), + new EpsgOperationParameterRecord(10868, "Scale difference", 3.191d), + new EpsgOperationParameterRecord(10868, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10868, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10868, "Target epoch", 1997.56d), + new EpsgOperationParameterRecord(10869, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10869, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(10869, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10869, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(10869, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(10869, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(10869, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10869, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10869, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10869, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10869, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10869, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10869, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10869, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10869, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10869, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10869, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10869, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10869, "X-axis translation", 0.15651d), + new EpsgOperationParameterRecord(10869, "Y-axis translation", -0.10993d), + new EpsgOperationParameterRecord(10869, "Z-axis translation", -0.10935d), + new EpsgOperationParameterRecord(10869, "X-axis rotation", -3.12861d), + new EpsgOperationParameterRecord(10869, "Y-axis rotation", -3.78935d), + new EpsgOperationParameterRecord(10869, "Z-axis rotation", 4.03512d), + new EpsgOperationParameterRecord(10869, "Scale difference", 5.29d), + new EpsgOperationParameterRecord(10869, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10869, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10869, "Target epoch", 1997.0d), + new EpsgOperationParameterRecord(10870, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10870, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(10870, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10870, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(10870, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(10870, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(10870, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10870, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10870, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10870, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10870, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10870, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10870, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10870, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10870, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10870, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10870, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10870, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10870, "EPSG code for Interpolation CRS for geocentric translation grid file", 10807.0d), + new EpsgOperationParameterRecord(10870, "EPSG code for Interpolation CRS for point motion velocity grid file", 10807.0d), + new EpsgOperationParameterRecord(10870, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10870, "Target epoch", 1995.0d), + new EpsgOperationParameterRecord(10871, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10871, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(10871, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10871, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(10871, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(10871, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(10871, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10871, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10871, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10871, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10871, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10871, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10871, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10871, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10871, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10871, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10871, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10871, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10871, "X-axis translation", 0.36749d), + new EpsgOperationParameterRecord(10871, "Y-axis translation", 0.14351d), + new EpsgOperationParameterRecord(10871, "Z-axis translation", -0.18472d), + new EpsgOperationParameterRecord(10871, "X-axis rotation", 4.7914d), + new EpsgOperationParameterRecord(10871, "Y-axis rotation", -10.27566d), + new EpsgOperationParameterRecord(10871, "Z-axis rotation", 2.76102d), + new EpsgOperationParameterRecord(10871, "Scale difference", -3.684d), + new EpsgOperationParameterRecord(10871, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10871, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10871, "Target epoch", 2003.75d), + new EpsgOperationParameterRecord(10872, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10872, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(10872, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10872, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(10872, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(10872, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(10872, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10872, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10872, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10872, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10872, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10872, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10872, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10872, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10872, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10872, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10872, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10872, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10872, "X-axis translation", 0.03054d), + new EpsgOperationParameterRecord(10872, "Y-axis translation", 0.04606d), + new EpsgOperationParameterRecord(10872, "Z-axis translation", -0.07944d), + new EpsgOperationParameterRecord(10872, "X-axis rotation", 1.41958d), + new EpsgOperationParameterRecord(10872, "Y-axis rotation", 0.15132d), + new EpsgOperationParameterRecord(10872, "Z-axis rotation", 1.50337d), + new EpsgOperationParameterRecord(10872, "Scale difference", 3.002d), + new EpsgOperationParameterRecord(10872, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10872, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10872, "Target epoch", 1999.5d), + new EpsgOperationParameterRecord(10894, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10894, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10894, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10894, "X-axis rotation", 1.785d), + new EpsgOperationParameterRecord(10894, "Y-axis rotation", 11.151d), + new EpsgOperationParameterRecord(10894, "Z-axis rotation", -16.17d), + new EpsgOperationParameterRecord(10894, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(10894, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10894, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(10894, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(10894, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10894, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10894, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10894, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10894, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(10894, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10894, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10894, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10894, "X-axis translation", 0.66818d), + new EpsgOperationParameterRecord(10894, "Y-axis translation", 0.04453d), + new EpsgOperationParameterRecord(10894, "Z-axis translation", -0.45049d), + new EpsgOperationParameterRecord(10894, "X-axis rotation", 3.12883d), + new EpsgOperationParameterRecord(10894, "Y-axis rotation", -23.73423d), + new EpsgOperationParameterRecord(10894, "Z-axis rotation", 4.42969d), + new EpsgOperationParameterRecord(10894, "Scale difference", -3.136d), + new EpsgOperationParameterRecord(10894, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10894, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10894, "Target epoch", 2015.829d), + new EpsgOperationParameterRecord(10895, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(10895, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(10895, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(10895, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(10895, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(10895, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(10895, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(10895, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(10895, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(10895, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(10895, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(10895, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(10895, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(10895, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(10895, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(10895, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(10895, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(10895, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(10895, "X-axis translation", 0.66818d), + new EpsgOperationParameterRecord(10895, "Y-axis translation", 0.04453d), + new EpsgOperationParameterRecord(10895, "Z-axis translation", -0.45049d), + new EpsgOperationParameterRecord(10895, "X-axis rotation", 3.12883d), + new EpsgOperationParameterRecord(10895, "Y-axis rotation", -23.73423d), + new EpsgOperationParameterRecord(10895, "Z-axis rotation", 4.42969d), + new EpsgOperationParameterRecord(10895, "Scale difference", -3.136d), + new EpsgOperationParameterRecord(10895, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(10895, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(10895, "Target epoch", 2015.829d), + new EpsgOperationParameterRecord(11005, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(11005, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(11066, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(11066, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(11066, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(11066, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11066, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11066, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(11066, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(11066, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(11066, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11066, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(11066, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11066, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11066, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(11066, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(11066, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11066, "X-axis translation", 4.1d), + new EpsgOperationParameterRecord(11066, "Y-axis translation", 4.1d), + new EpsgOperationParameterRecord(11066, "Z-axis translation", -4.9d), + new EpsgOperationParameterRecord(11066, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11066, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11066, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11066, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(11066, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11066, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11066, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11066, "Rate of change of X-axis rotation", 0.2d), + new EpsgOperationParameterRecord(11066, "Rate of change of Y-axis rotation", 0.5d), + new EpsgOperationParameterRecord(11066, "Rate of change of Z-axis rotation", -0.65d), + new EpsgOperationParameterRecord(11066, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(11066, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(11066, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11066, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11066, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11110, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(11110, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(11110, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(11110, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11110, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11110, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(11110, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(11110, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(11110, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11110, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(11110, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11110, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11110, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(11110, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(11110, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11110, "X-axis translation", 4.1d), + new EpsgOperationParameterRecord(11110, "Y-axis translation", 4.1d), + new EpsgOperationParameterRecord(11110, "Z-axis translation", -4.9d), + new EpsgOperationParameterRecord(11110, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11110, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11110, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11110, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(11110, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11110, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11110, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11110, "Rate of change of X-axis rotation", 0.2d), + new EpsgOperationParameterRecord(11110, "Rate of change of Y-axis rotation", 0.5d), + new EpsgOperationParameterRecord(11110, "Rate of change of Z-axis rotation", -0.65d), + new EpsgOperationParameterRecord(11110, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(11110, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(11110, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11110, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11110, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11140, "X-axis translation", -6.5d), + new EpsgOperationParameterRecord(11140, "Y-axis translation", 3.9d), + new EpsgOperationParameterRecord(11140, "Z-axis translation", 77.9d), + new EpsgOperationParameterRecord(11140, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11140, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11140, "Z-axis rotation", -0.36d), + new EpsgOperationParameterRecord(11140, "Scale difference", -3.98d), + new EpsgOperationParameterRecord(11140, "Rate of change of X-axis translation", -0.1d), + new EpsgOperationParameterRecord(11140, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11140, "Rate of change of Z-axis translation", 3.1d), + new EpsgOperationParameterRecord(11140, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11140, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11140, "Rate of change of Z-axis rotation", -0.02d), + new EpsgOperationParameterRecord(11140, "Rate of change of scale difference", -0.12d), + new EpsgOperationParameterRecord(11140, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11140, "X-axis translation", 4.1d), + new EpsgOperationParameterRecord(11140, "Y-axis translation", 4.1d), + new EpsgOperationParameterRecord(11140, "Z-axis translation", -4.9d), + new EpsgOperationParameterRecord(11140, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11140, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11140, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11140, "Scale difference", 0.0d), + new EpsgOperationParameterRecord(11140, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11140, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11140, "Rate of change of Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11140, "Rate of change of X-axis rotation", 0.2d), + new EpsgOperationParameterRecord(11140, "Rate of change of Y-axis rotation", 0.5d), + new EpsgOperationParameterRecord(11140, "Rate of change of Z-axis rotation", -0.65d), + new EpsgOperationParameterRecord(11140, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(11140, "Parameter reference epoch", 1989.0d), + new EpsgOperationParameterRecord(11140, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11140, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11140, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11194, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11194, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11194, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11194, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11194, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11194, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11194, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11194, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11194, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11194, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11194, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11194, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11194, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11194, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11194, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11194, "X-axis translation", 24.3d), + new EpsgOperationParameterRecord(11194, "Y-axis translation", 10.7d), + new EpsgOperationParameterRecord(11194, "Z-axis translation", 42.7d), + new EpsgOperationParameterRecord(11194, "X-axis rotation", -0.319d), + new EpsgOperationParameterRecord(11194, "Y-axis rotation", -0.88d), + new EpsgOperationParameterRecord(11194, "Z-axis rotation", -0.962d), + new EpsgOperationParameterRecord(11194, "Scale difference", -5.97d), + new EpsgOperationParameterRecord(11194, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11194, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11194, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11194, "Rate of change of X-axis rotation", -0.029d), + new EpsgOperationParameterRecord(11194, "Rate of change of Y-axis rotation", -0.08d), + new EpsgOperationParameterRecord(11194, "Rate of change of Z-axis rotation", -0.102d), + new EpsgOperationParameterRecord(11194, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11194, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(11194, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11194, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11194, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11196, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11196, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11196, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11196, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11196, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11196, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11196, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11196, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11196, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11196, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11196, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11196, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11196, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11196, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11196, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11196, "X-axis translation", 6.3d), + new EpsgOperationParameterRecord(11196, "Y-axis translation", 5.7d), + new EpsgOperationParameterRecord(11196, "Z-axis translation", 23.7d), + new EpsgOperationParameterRecord(11196, "X-axis rotation", -1.309d), + new EpsgOperationParameterRecord(11196, "Y-axis rotation", -0.11d), + new EpsgOperationParameterRecord(11196, "Z-axis rotation", -1.622d), + new EpsgOperationParameterRecord(11196, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(11196, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11196, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11196, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11196, "Rate of change of X-axis rotation", -0.119d), + new EpsgOperationParameterRecord(11196, "Rate of change of Y-axis rotation", -0.01d), + new EpsgOperationParameterRecord(11196, "Rate of change of Z-axis rotation", -0.162d), + new EpsgOperationParameterRecord(11196, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11196, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(11196, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11196, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11196, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11230, "X-axis translation", -0.2773d), + new EpsgOperationParameterRecord(11230, "Y-axis translation", 0.0534d), + new EpsgOperationParameterRecord(11230, "Z-axis translation", 0.4819d), + new EpsgOperationParameterRecord(11230, "X-axis rotation", 0.0935d), + new EpsgOperationParameterRecord(11230, "Y-axis rotation", -0.0286d), + new EpsgOperationParameterRecord(11230, "Z-axis rotation", 0.00969d), + new EpsgOperationParameterRecord(11230, "Scale difference", -0.028d), + new EpsgOperationParameterRecord(11230, "X-axis translation", -1.6d), + new EpsgOperationParameterRecord(11230, "Y-axis translation", -1.9d), + new EpsgOperationParameterRecord(11230, "Z-axis translation", -2.4d), + new EpsgOperationParameterRecord(11230, "X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11230, "Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11230, "Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11230, "Scale difference", 0.02d), + new EpsgOperationParameterRecord(11230, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11230, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11230, "Rate of change of Z-axis translation", 0.1d), + new EpsgOperationParameterRecord(11230, "Rate of change of X-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11230, "Rate of change of Y-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11230, "Rate of change of Z-axis rotation", 0.0d), + new EpsgOperationParameterRecord(11230, "Rate of change of scale difference", -0.03d), + new EpsgOperationParameterRecord(11230, "Parameter reference epoch", 2010.0d), + new EpsgOperationParameterRecord(11230, "EPSG code for Interpolation CRS", 9470.0d), + new EpsgOperationParameterRecord(11230, "Source epoch", 2012.0d), + new EpsgOperationParameterRecord(11230, "Target epoch", 2021.0d), + new EpsgOperationParameterRecord(11285, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11285, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11285, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11285, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11285, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11285, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11285, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11285, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11285, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11285, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11285, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11285, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11285, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11285, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11285, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11285, "X-axis translation", 6.3d), + new EpsgOperationParameterRecord(11285, "Y-axis translation", 5.7d), + new EpsgOperationParameterRecord(11285, "Z-axis translation", 23.7d), + new EpsgOperationParameterRecord(11285, "X-axis rotation", -1.309d), + new EpsgOperationParameterRecord(11285, "Y-axis rotation", -0.11d), + new EpsgOperationParameterRecord(11285, "Z-axis rotation", -1.622d), + new EpsgOperationParameterRecord(11285, "Scale difference", -1.58d), + new EpsgOperationParameterRecord(11285, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11285, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11285, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11285, "Rate of change of X-axis rotation", -0.119d), + new EpsgOperationParameterRecord(11285, "Rate of change of Y-axis rotation", -0.01d), + new EpsgOperationParameterRecord(11285, "Rate of change of Z-axis rotation", -0.162d), + new EpsgOperationParameterRecord(11285, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11285, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(11285, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11285, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11285, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11310, "X-axis translation", 53.8d), + new EpsgOperationParameterRecord(11310, "Y-axis translation", 51.8d), + new EpsgOperationParameterRecord(11310, "Z-axis translation", -82.2d), + new EpsgOperationParameterRecord(11310, "X-axis rotation", 2.106d), + new EpsgOperationParameterRecord(11310, "Y-axis rotation", 12.74d), + new EpsgOperationParameterRecord(11310, "Z-axis rotation", -20.592d), + new EpsgOperationParameterRecord(11310, "Scale difference", 2.25d), + new EpsgOperationParameterRecord(11310, "Rate of change of X-axis translation", 0.1d), + new EpsgOperationParameterRecord(11310, "Rate of change of Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11310, "Rate of change of Z-axis translation", -1.7d), + new EpsgOperationParameterRecord(11310, "Rate of change of X-axis rotation", 0.081d), + new EpsgOperationParameterRecord(11310, "Rate of change of Y-axis rotation", 0.49d), + new EpsgOperationParameterRecord(11310, "Rate of change of Z-axis rotation", -0.792d), + new EpsgOperationParameterRecord(11310, "Rate of change of scale difference", 0.11d), + new EpsgOperationParameterRecord(11310, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11310, "X-axis translation", 24.3d), + new EpsgOperationParameterRecord(11310, "Y-axis translation", 10.7d), + new EpsgOperationParameterRecord(11310, "Z-axis translation", 42.7d), + new EpsgOperationParameterRecord(11310, "X-axis rotation", -0.319d), + new EpsgOperationParameterRecord(11310, "Y-axis rotation", -0.88d), + new EpsgOperationParameterRecord(11310, "Z-axis rotation", -0.962d), + new EpsgOperationParameterRecord(11310, "Scale difference", -5.97d), + new EpsgOperationParameterRecord(11310, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11310, "Rate of change of Y-axis translation", 0.6d), + new EpsgOperationParameterRecord(11310, "Rate of change of Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11310, "Rate of change of X-axis rotation", -0.029d), + new EpsgOperationParameterRecord(11310, "Rate of change of Y-axis rotation", -0.08d), + new EpsgOperationParameterRecord(11310, "Rate of change of Z-axis rotation", -0.102d), + new EpsgOperationParameterRecord(11310, "Rate of change of scale difference", -0.01d), + new EpsgOperationParameterRecord(11310, "Parameter reference epoch", 2000.0d), + new EpsgOperationParameterRecord(11310, "X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11310, "Y-axis translation", 0.0d), + new EpsgOperationParameterRecord(11310, "Z-axis translation", 0.0d), + new EpsgOperationParameterRecord(11315, "X-axis translation", -1.4d), + new EpsgOperationParameterRecord(11315, "Y-axis translation", -0.9d), + new EpsgOperationParameterRecord(11315, "Z-axis translation", 1.4d), + new EpsgOperationParameterRecord(11315, "X-axis rotation", 2.21d), + new EpsgOperationParameterRecord(11315, "Y-axis rotation", 13.806d), + new EpsgOperationParameterRecord(11315, "Z-axis rotation", -20.02d), + new EpsgOperationParameterRecord(11315, "Scale difference", -0.42d), + new EpsgOperationParameterRecord(11315, "Rate of change of X-axis translation", 0.0d), + new EpsgOperationParameterRecord(11315, "Rate of change of Y-axis translation", -0.1d), + new EpsgOperationParameterRecord(11315, "Rate of change of Z-axis translation", 0.2d), + new EpsgOperationParameterRecord(11315, "Rate of change of X-axis rotation", 0.085d), + new EpsgOperationParameterRecord(11315, "Rate of change of Y-axis rotation", 0.531d), + new EpsgOperationParameterRecord(11315, "Rate of change of Z-axis rotation", -0.77d), + new EpsgOperationParameterRecord(11315, "Rate of change of scale difference", 0.0d), + new EpsgOperationParameterRecord(11315, "Parameter reference epoch", 2015.0d), + new EpsgOperationParameterRecord(11315, "EPSG code for Interpolation CRS", 9069.0d), + new EpsgOperationParameterRecord(11315, "Source epoch", -999.0d), + new EpsgOperationParameterRecord(11315, "Target epoch", 2000.0d), + new EpsgOperationParameterRecord(11315, "X-axis translation", -0.03958d), + new EpsgOperationParameterRecord(11315, "Y-axis translation", -0.05079d), + new EpsgOperationParameterRecord(11315, "Z-axis translation", 0.05751d), + new EpsgOperationParameterRecord(11315, "X-axis rotation", -1.70334d), + new EpsgOperationParameterRecord(11315, "Y-axis rotation", 1.7302d), + new EpsgOperationParameterRecord(11315, "Z-axis rotation", 1.3038d), + new EpsgOperationParameterRecord(11315, "Scale difference", -2.789d), + new EpsgOperationParameterRecord(11315, "EPSG code for Interpolation CRS", 10807.0d), + new EpsgOperationParameterRecord(11315, "Source epoch", 2000.0d), + new EpsgOperationParameterRecord(11315, "Target epoch", 2020.28d), + new EpsgOperationParameterRecord(11398, "EPSG code for Interpolation CRS", 10875.0d), + new EpsgOperationParameterRecord(11398, "EPSG code for Interpolation CRS", 10875.0d), + }; + + internal static bool TryGetConcatenatedOperationStepCount(int operationCode, out int stepCount) + { + switch (operationCode / 1000) + { + case 3: + return TryGetConcatenatedOperationStepCountBucket3(operationCode, out stepCount); + case 4: + return TryGetConcatenatedOperationStepCountBucket4(operationCode, out stepCount); + case 5: + return TryGetConcatenatedOperationStepCountBucket5(operationCode, out stepCount); + case 6: + return TryGetConcatenatedOperationStepCountBucket6(operationCode, out stepCount); + case 7: + return TryGetConcatenatedOperationStepCountBucket7(operationCode, out stepCount); + case 8: + return TryGetConcatenatedOperationStepCountBucket8(operationCode, out stepCount); + case 9: + return TryGetConcatenatedOperationStepCountBucket9(operationCode, out stepCount); + case 10: + return TryGetConcatenatedOperationStepCountBucket10(operationCode, out stepCount); + case 11: + return TryGetConcatenatedOperationStepCountBucket11(operationCode, out stepCount); + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket3(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 3896: + stepCount = 2; + return true; + case 3966: + stepCount = 2; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket4(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 4435: + stepCount = 2; + return true; + case 4837: + stepCount = 2; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket5(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 5190: + stepCount = 2; + return true; + case 5192: + stepCount = 2; + return true; + case 5230: + stepCount = 2; + return true; + case 5240: + stepCount = 2; + return true; + case 5242: + stepCount = 2; + return true; + case 5838: + stepCount = 2; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket6(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 6714: + stepCount = 2; + return true; + case 6739: + stepCount = 2; + return true; + case 6874: + stepCount = 2; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket7(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 7810: + stepCount = 2; + return true; + case 7811: + stepCount = 2; + return true; + case 7965: + stepCount = 2; + return true; + case 7967: + stepCount = 2; + return true; + case 7973: + stepCount = 2; + return true; + case 7974: + stepCount = 2; + return true; + case 7975: + stepCount = 2; + return true; + case 7983: + stepCount = 2; + return true; + case 7986: + stepCount = 2; + return true; + case 7987: + stepCount = 3; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket8(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 8046: + stepCount = 2; + return true; + case 8047: + stepCount = 2; + return true; + case 8094: + stepCount = 2; + return true; + case 8174: + stepCount = 2; + return true; + case 8175: + stepCount = 2; + return true; + case 8176: + stepCount = 2; + return true; + case 8178: + stepCount = 2; + return true; + case 8183: + stepCount = 2; + return true; + case 8186: + stepCount = 2; + return true; + case 8188: + stepCount = 2; + return true; + case 8190: + stepCount = 2; + return true; + case 8192: + stepCount = 2; + return true; + case 8194: + stepCount = 2; + return true; + case 8195: + stepCount = 2; + return true; + case 8199: + stepCount = 2; + return true; + case 8211: + stepCount = 2; + return true; + case 8215: + stepCount = 2; + return true; + case 8217: + stepCount = 2; + return true; + case 8219: + stepCount = 2; + return true; + case 8221: + stepCount = 2; + return true; + case 8223: + stepCount = 2; + return true; + case 8234: + stepCount = 2; + return true; + case 8236: + stepCount = 2; + return true; + case 8241: + stepCount = 2; + return true; + case 8243: + stepCount = 2; + return true; + case 8245: + stepCount = 2; + return true; + case 8263: + stepCount = 2; + return true; + case 8363: + stepCount = 2; + return true; + case 8386: + stepCount = 2; + return true; + case 8388: + stepCount = 2; + return true; + case 8390: + stepCount = 2; + return true; + case 8392: + stepCount = 2; + return true; + case 8394: + stepCount = 2; + return true; + case 8396: + stepCount = 2; + return true; + case 8398: + stepCount = 2; + return true; + case 8400: + stepCount = 2; + return true; + case 8402: + stepCount = 2; + return true; + case 8404: + stepCount = 2; + return true; + case 8406: + stepCount = 2; + return true; + case 8408: + stepCount = 2; + return true; + case 8418: + stepCount = 2; + return true; + case 8419: + stepCount = 2; + return true; + case 8420: + stepCount = 2; + return true; + case 8421: + stepCount = 2; + return true; + case 8422: + stepCount = 2; + return true; + case 8442: + stepCount = 2; + return true; + case 8443: + stepCount = 2; + return true; + case 8453: + stepCount = 2; + return true; + case 8454: + stepCount = 2; + return true; + case 8457: + stepCount = 2; + return true; + case 8460: + stepCount = 2; + return true; + case 8461: + stepCount = 2; + return true; + case 8462: + stepCount = 2; + return true; + case 8463: + stepCount = 2; + return true; + case 8464: + stepCount = 2; + return true; + case 8465: + stepCount = 2; + return true; + case 8466: + stepCount = 2; + return true; + case 8467: + stepCount = 2; + return true; + case 8468: + stepCount = 2; + return true; + case 8469: + stepCount = 2; + return true; + case 8470: + stepCount = 2; + return true; + case 8471: + stepCount = 2; + return true; + case 8472: + stepCount = 2; + return true; + case 8473: + stepCount = 2; + return true; + case 8474: + stepCount = 2; + return true; + case 8475: + stepCount = 2; + return true; + case 8476: + stepCount = 2; + return true; + case 8477: + stepCount = 2; + return true; + case 8478: + stepCount = 2; + return true; + case 8479: + stepCount = 2; + return true; + case 8480: + stepCount = 2; + return true; + case 8481: + stepCount = 2; + return true; + case 8482: + stepCount = 2; + return true; + case 8483: + stepCount = 2; + return true; + case 8484: + stepCount = 2; + return true; + case 8485: + stepCount = 2; + return true; + case 8486: + stepCount = 2; + return true; + case 8487: + stepCount = 2; + return true; + case 8488: + stepCount = 2; + return true; + case 8489: + stepCount = 2; + return true; + case 8496: + stepCount = 2; + return true; + case 8497: + stepCount = 2; + return true; + case 8508: + stepCount = 2; + return true; + case 8509: + stepCount = 2; + return true; + case 8510: + stepCount = 2; + return true; + case 8511: + stepCount = 2; + return true; + case 8512: + stepCount = 2; + return true; + case 8513: + stepCount = 2; + return true; + case 8514: + stepCount = 2; + return true; + case 8517: + stepCount = 2; + return true; + case 8530: + stepCount = 2; + return true; + case 8532: + stepCount = 2; + return true; + case 8537: + stepCount = 2; + return true; + case 8553: + stepCount = 2; + return true; + case 8554: + stepCount = 2; + return true; + case 8560: + stepCount = 2; + return true; + case 8562: + stepCount = 2; + return true; + case 8563: + stepCount = 2; + return true; + case 8564: + stepCount = 2; + return true; + case 8565: + stepCount = 2; + return true; + case 8566: + stepCount = 2; + return true; + case 8567: + stepCount = 2; + return true; + case 8568: + stepCount = 2; + return true; + case 8569: + stepCount = 2; + return true; + case 8570: + stepCount = 3; + return true; + case 8571: + stepCount = 2; + return true; + case 8572: + stepCount = 2; + return true; + case 8573: + stepCount = 2; + return true; + case 8574: + stepCount = 2; + return true; + case 8575: + stepCount = 2; + return true; + case 8576: + stepCount = 2; + return true; + case 8577: + stepCount = 2; + return true; + case 8578: + stepCount = 2; + return true; + case 8579: + stepCount = 2; + return true; + case 8580: + stepCount = 2; + return true; + case 8581: + stepCount = 2; + return true; + case 8582: + stepCount = 2; + return true; + case 8583: + stepCount = 2; + return true; + case 8584: + stepCount = 2; + return true; + case 8585: + stepCount = 2; + return true; + case 8586: + stepCount = 2; + return true; + case 8587: + stepCount = 2; + return true; + case 8588: + stepCount = 2; + return true; + case 8589: + stepCount = 2; + return true; + case 8590: + stepCount = 2; + return true; + case 8591: + stepCount = 2; + return true; + case 8592: + stepCount = 2; + return true; + case 8593: + stepCount = 2; + return true; + case 8594: + stepCount = 2; + return true; + case 8595: + stepCount = 2; + return true; + case 8596: + stepCount = 2; + return true; + case 8597: + stepCount = 2; + return true; + case 8598: + stepCount = 2; + return true; + case 8599: + stepCount = 2; + return true; + case 8600: + stepCount = 2; + return true; + case 8601: + stepCount = 2; + return true; + case 8602: + stepCount = 2; + return true; + case 8603: + stepCount = 2; + return true; + case 8604: + stepCount = 2; + return true; + case 8605: + stepCount = 2; + return true; + case 8606: + stepCount = 2; + return true; + case 8607: + stepCount = 2; + return true; + case 8608: + stepCount = 2; + return true; + case 8609: + stepCount = 2; + return true; + case 8610: + stepCount = 2; + return true; + case 8611: + stepCount = 2; + return true; + case 8612: + stepCount = 2; + return true; + case 8613: + stepCount = 2; + return true; + case 8614: + stepCount = 2; + return true; + case 8615: + stepCount = 2; + return true; + case 8616: + stepCount = 2; + return true; + case 8617: + stepCount = 2; + return true; + case 8618: + stepCount = 2; + return true; + case 8619: + stepCount = 2; + return true; + case 8620: + stepCount = 2; + return true; + case 8621: + stepCount = 2; + return true; + case 8622: + stepCount = 2; + return true; + case 8623: + stepCount = 2; + return true; + case 8624: + stepCount = 2; + return true; + case 8625: + stepCount = 2; + return true; + case 8626: + stepCount = 2; + return true; + case 8627: + stepCount = 2; + return true; + case 8628: + stepCount = 2; + return true; + case 8629: + stepCount = 2; + return true; + case 8630: + stepCount = 2; + return true; + case 8631: + stepCount = 2; + return true; + case 8632: + stepCount = 2; + return true; + case 8633: + stepCount = 2; + return true; + case 8634: + stepCount = 2; + return true; + case 8635: + stepCount = 2; + return true; + case 8636: + stepCount = 2; + return true; + case 8637: + stepCount = 2; + return true; + case 8638: + stepCount = 2; + return true; + case 8639: + stepCount = 2; + return true; + case 8640: + stepCount = 2; + return true; + case 8641: + stepCount = 2; + return true; + case 8642: + stepCount = 2; + return true; + case 8643: + stepCount = 2; + return true; + case 8644: + stepCount = 3; + return true; + case 8645: + stepCount = 2; + return true; + case 8646: + stepCount = 2; + return true; + case 8647: + stepCount = 3; + return true; + case 8648: + stepCount = 2; + return true; + case 8649: + stepCount = 2; + return true; + case 8650: + stepCount = 2; + return true; + case 8651: + stepCount = 2; + return true; + case 8652: + stepCount = 2; + return true; + case 8653: + stepCount = 2; + return true; + case 8654: + stepCount = 3; + return true; + case 8655: + stepCount = 2; + return true; + case 8656: + stepCount = 2; + return true; + case 8657: + stepCount = 2; + return true; + case 8659: + stepCount = 2; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket9(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 9091: + stepCount = 2; + return true; + case 9092: + stepCount = 2; + return true; + case 9093: + stepCount = 2; + return true; + case 9094: + stepCount = 2; + return true; + case 9095: + stepCount = 2; + return true; + case 9096: + stepCount = 2; + return true; + case 9097: + stepCount = 2; + return true; + case 9098: + stepCount = 2; + return true; + case 9099: + stepCount = 2; + return true; + case 9100: + stepCount = 2; + return true; + case 9101: + stepCount = 2; + return true; + case 9102: + stepCount = 2; + return true; + case 9103: + stepCount = 4; + return true; + case 9104: + stepCount = 7; + return true; + case 9336: + stepCount = 2; + return true; + case 9337: + stepCount = 2; + return true; + case 9499: + stepCount = 2; + return true; + case 9683: + stepCount = 2; + return true; + case 9685: + stepCount = 2; + return true; + case 9687: + stepCount = 2; + return true; + case 9731: + stepCount = 2; + return true; + case 9750: + stepCount = 2; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket10(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 10146: + stepCount = 2; + return true; + case 10392: + stepCount = 2; + return true; + case 10393: + stepCount = 2; + return true; + case 10394: + stepCount = 2; + return true; + case 10395: + stepCount = 2; + return true; + case 10396: + stepCount = 2; + return true; + case 10397: + stepCount = 2; + return true; + case 10398: + stepCount = 2; + return true; + case 10399: + stepCount = 2; + return true; + case 10400: + stepCount = 2; + return true; + case 10409: + stepCount = 2; + return true; + case 10410: + stepCount = 2; + return true; + case 10411: + stepCount = 2; + return true; + case 10495: + stepCount = 2; + return true; + case 10496: + stepCount = 2; + return true; + case 10616: + stepCount = 2; + return true; + case 10675: + stepCount = 2; + return true; + case 10754: + stepCount = 2; + return true; + case 10755: + stepCount = 2; + return true; + case 10756: + stepCount = 2; + return true; + case 10778: + stepCount = 3; + return true; + case 10815: + stepCount = 2; + return true; + case 10816: + stepCount = 3; + return true; + case 10817: + stepCount = 3; + return true; + case 10818: + stepCount = 3; + return true; + case 10824: + stepCount = 3; + return true; + case 10825: + stepCount = 3; + return true; + case 10868: + stepCount = 3; + return true; + case 10869: + stepCount = 3; + return true; + case 10870: + stepCount = 3; + return true; + case 10871: + stepCount = 3; + return true; + case 10872: + stepCount = 3; + return true; + case 10894: + stepCount = 3; + return true; + case 10895: + stepCount = 3; + return true; + default: + stepCount = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepCountBucket11(int operationCode, out int stepCount) + { + switch (operationCode) + { + case 11005: + stepCount = 2; + return true; + case 11066: + stepCount = 3; + return true; + case 11110: + stepCount = 3; + return true; + case 11140: + stepCount = 3; + return true; + case 11194: + stepCount = 3; + return true; + case 11196: + stepCount = 3; + return true; + case 11206: + stepCount = 3; + return true; + case 11230: + stepCount = 2; + return true; + case 11285: + stepCount = 3; + return true; + case 11310: + stepCount = 3; + return true; + case 11315: + stepCount = 3; + return true; + case 11398: + stepCount = 2; + return true; + default: + stepCount = 0; + return false; + } + } + + internal static bool TryGetConcatenatedOperationStep(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode / 1000) + { + case 3: + return TryGetConcatenatedOperationStepBucket3(operationCode, stepIndex, out stepOperationCode); + case 4: + return TryGetConcatenatedOperationStepBucket4(operationCode, stepIndex, out stepOperationCode); + case 5: + return TryGetConcatenatedOperationStepBucket5(operationCode, stepIndex, out stepOperationCode); + case 6: + return TryGetConcatenatedOperationStepBucket6(operationCode, stepIndex, out stepOperationCode); + case 7: + return TryGetConcatenatedOperationStepBucket7(operationCode, stepIndex, out stepOperationCode); + case 8: + return TryGetConcatenatedOperationStepBucket8(operationCode, stepIndex, out stepOperationCode); + case 9: + return TryGetConcatenatedOperationStepBucket9(operationCode, stepIndex, out stepOperationCode); + case 10: + return TryGetConcatenatedOperationStepBucket10(operationCode, stepIndex, out stepOperationCode); + case 11: + return TryGetConcatenatedOperationStepBucket11(operationCode, stepIndex, out stepOperationCode); + default: + stepOperationCode = 0; + return false; + } + } + + private static bool TryGetConcatenatedOperationStepBucket3(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 3896: + switch (stepIndex) + { + case 0: + stepOperationCode = 3895; + return true; + case 1: + stepOperationCode = 1618; + return true; + default: + break; + } + break; + case 3966: + switch (stepIndex) + { + case 0: + stepOperationCode = 3913; + return true; + case 1: + stepOperationCode = 3962; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket4(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 4435: + switch (stepIndex) + { + case 0: + stepOperationCode = 1461; + return true; + case 1: + stepOperationCode = 1495; + return true; + default: + break; + } + break; + case 4837: + switch (stepIndex) + { + case 0: + stepOperationCode = 1672; + return true; + case 1: + stepOperationCode = 1311; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket5(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 5190: + switch (stepIndex) + { + case 0: + stepOperationCode = 5134; + return true; + case 1: + stepOperationCode = 5189; + return true; + default: + break; + } + break; + case 5192: + switch (stepIndex) + { + case 0: + stepOperationCode = 5134; + return true; + case 1: + stepOperationCode = 5191; + return true; + default: + break; + } + break; + case 5230: + switch (stepIndex) + { + case 0: + stepOperationCode = 1884; + return true; + case 1: + stepOperationCode = 4836; + return true; + default: + break; + } + break; + case 5240: + switch (stepIndex) + { + case 0: + stepOperationCode = 5238; + return true; + case 1: + stepOperationCode = 5227; + return true; + default: + break; + } + break; + case 5242: + switch (stepIndex) + { + case 0: + stepOperationCode = 1884; + return true; + case 1: + stepOperationCode = 5239; + return true; + default: + break; + } + break; + case 5838: + switch (stepIndex) + { + case 0: + stepOperationCode = 1756; + return true; + case 1: + stepOperationCode = 1988; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket6(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 6714: + switch (stepIndex) + { + case 0: + stepOperationCode = 6712; + return true; + case 1: + stepOperationCode = 6713; + return true; + default: + break; + } + break; + case 6739: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1496; + return true; + default: + break; + } + break; + case 6874: + switch (stepIndex) + { + case 0: + stepOperationCode = 1265; + return true; + case 1: + stepOperationCode = 6873; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket7(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 7810: + switch (stepIndex) + { + case 0: + stepOperationCode = 1763; + return true; + case 1: + stepOperationCode = 1053; + return true; + default: + break; + } + break; + case 7811: + switch (stepIndex) + { + case 0: + stepOperationCode = 1763; + return true; + case 1: + stepOperationCode = 15958; + return true; + default: + break; + } + break; + case 7965: + switch (stepIndex) + { + case 0: + stepOperationCode = 7813; + return true; + case 1: + stepOperationCode = 7964; + return true; + default: + break; + } + break; + case 7967: + switch (stepIndex) + { + case 0: + stepOperationCode = 7813; + return true; + case 1: + stepOperationCode = 7966; + return true; + default: + break; + } + break; + case 7973: + switch (stepIndex) + { + case 0: + stepOperationCode = 7813; + return true; + case 1: + stepOperationCode = 7969; + return true; + default: + break; + } + break; + case 7974: + switch (stepIndex) + { + case 0: + stepOperationCode = 7813; + return true; + case 1: + stepOperationCode = 7970; + return true; + default: + break; + } + break; + case 7975: + switch (stepIndex) + { + case 0: + stepOperationCode = 7813; + return true; + case 1: + stepOperationCode = 7971; + return true; + default: + break; + } + break; + case 7983: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 7977; + return true; + default: + break; + } + break; + case 7986: + switch (stepIndex) + { + case 0: + stepOperationCode = 7980; + return true; + case 1: + stepOperationCode = 7812; + return true; + default: + break; + } + break; + case 7987: + switch (stepIndex) + { + case 0: + stepOperationCode = 7980; + return true; + case 1: + stepOperationCode = 7812; + return true; + case 2: + stepOperationCode = 7813; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket8(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 8046: + switch (stepIndex) + { + case 0: + stepOperationCode = 1043; + return true; + case 1: + stepOperationCode = 1146; + return true; + default: + break; + } + break; + case 8047: + switch (stepIndex) + { + case 0: + stepOperationCode = 1147; + return true; + case 1: + stepOperationCode = 1146; + return true; + default: + break; + } + break; + case 8094: + switch (stepIndex) + { + case 0: + stepOperationCode = 1763; + return true; + case 1: + stepOperationCode = 1193; + return true; + default: + break; + } + break; + case 8174: + switch (stepIndex) + { + case 0: + stepOperationCode = 1755; + return true; + case 1: + stepOperationCode = 1125; + return true; + default: + break; + } + break; + case 8175: + switch (stepIndex) + { + case 0: + stepOperationCode = 1262; + return true; + case 1: + stepOperationCode = 1169; + return true; + default: + break; + } + break; + case 8176: + switch (stepIndex) + { + case 0: + stepOperationCode = 1265; + return true; + case 1: + stepOperationCode = 1227; + return true; + default: + break; + } + break; + case 8178: + switch (stepIndex) + { + case 0: + stepOperationCode = 1759; + return true; + case 1: + stepOperationCode = 8452; + return true; + default: + break; + } + break; + case 8183: + switch (stepIndex) + { + case 0: + stepOperationCode = 1273; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8186: + switch (stepIndex) + { + case 0: + stepOperationCode = 1763; + return true; + case 1: + stepOperationCode = 1276; + return true; + default: + break; + } + break; + case 8188: + switch (stepIndex) + { + case 0: + stepOperationCode = 1763; + return true; + case 1: + stepOperationCode = 1277; + return true; + default: + break; + } + break; + case 8190: + switch (stepIndex) + { + case 0: + stepOperationCode = 1278; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8192: + switch (stepIndex) + { + case 0: + stepOperationCode = 1279; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8194: + switch (stepIndex) + { + case 0: + stepOperationCode = 1280; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8195: + switch (stepIndex) + { + case 0: + stepOperationCode = 1437; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8199: + switch (stepIndex) + { + case 0: + stepOperationCode = 1274; + return true; + case 1: + stepOperationCode = 1283; + return true; + default: + break; + } + break; + case 8211: + switch (stepIndex) + { + case 0: + stepOperationCode = 1266; + return true; + case 1: + stepOperationCode = 1294; + return true; + default: + break; + } + break; + case 8215: + switch (stepIndex) + { + case 0: + stepOperationCode = 1297; + return true; + case 1: + stepOperationCode = 1302; + return true; + default: + break; + } + break; + case 8217: + switch (stepIndex) + { + case 0: + stepOperationCode = 1298; + return true; + case 1: + stepOperationCode = 1302; + return true; + default: + break; + } + break; + case 8219: + switch (stepIndex) + { + case 0: + stepOperationCode = 1299; + return true; + case 1: + stepOperationCode = 1302; + return true; + default: + break; + } + break; + case 8221: + switch (stepIndex) + { + case 0: + stepOperationCode = 1300; + return true; + case 1: + stepOperationCode = 1302; + return true; + default: + break; + } + break; + case 8223: + switch (stepIndex) + { + case 0: + stepOperationCode = 1301; + return true; + case 1: + stepOperationCode = 1302; + return true; + default: + break; + } + break; + case 8234: + switch (stepIndex) + { + case 0: + stepOperationCode = 1309; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8236: + switch (stepIndex) + { + case 0: + stepOperationCode = 1310; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8241: + switch (stepIndex) + { + case 0: + stepOperationCode = 1026; + return true; + case 1: + stepOperationCode = 1145; + return true; + default: + break; + } + break; + case 8243: + switch (stepIndex) + { + case 0: + stepOperationCode = 1312; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8245: + switch (stepIndex) + { + case 0: + stepOperationCode = 1313; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8263: + switch (stepIndex) + { + case 0: + stepOperationCode = 1757; + return true; + case 1: + stepOperationCode = 1306; + return true; + default: + break; + } + break; + case 8363: + switch (stepIndex) + { + case 0: + stepOperationCode = 8361; + return true; + case 1: + stepOperationCode = 8362; + return true; + default: + break; + } + break; + case 8386: + switch (stepIndex) + { + case 0: + stepOperationCode = 1454; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8388: + switch (stepIndex) + { + case 0: + stepOperationCode = 1455; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8390: + switch (stepIndex) + { + case 0: + stepOperationCode = 1456; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8392: + switch (stepIndex) + { + case 0: + stepOperationCode = 1457; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8394: + switch (stepIndex) + { + case 0: + stepOperationCode = 1451; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8396: + switch (stepIndex) + { + case 0: + stepOperationCode = 1458; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8398: + switch (stepIndex) + { + case 0: + stepOperationCode = 1459; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8400: + switch (stepIndex) + { + case 0: + stepOperationCode = 1460; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8402: + switch (stepIndex) + { + case 0: + stepOperationCode = 1461; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8404: + switch (stepIndex) + { + case 0: + stepOperationCode = 1462; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8406: + switch (stepIndex) + { + case 0: + stepOperationCode = 1463; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8408: + switch (stepIndex) + { + case 0: + stepOperationCode = 1464; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8418: + switch (stepIndex) + { + case 0: + stepOperationCode = 1472; + return true; + case 1: + stepOperationCode = 1473; + return true; + default: + break; + } + break; + case 8419: + switch (stepIndex) + { + case 0: + stepOperationCode = 1599; + return true; + case 1: + stepOperationCode = 1473; + return true; + default: + break; + } + break; + case 8420: + switch (stepIndex) + { + case 0: + stepOperationCode = 1600; + return true; + case 1: + stepOperationCode = 1473; + return true; + default: + break; + } + break; + case 8421: + switch (stepIndex) + { + case 0: + stepOperationCode = 1601; + return true; + case 1: + stepOperationCode = 1473; + return true; + default: + break; + } + break; + case 8422: + switch (stepIndex) + { + case 0: + stepOperationCode = 1602; + return true; + case 1: + stepOperationCode = 1473; + return true; + default: + break; + } + break; + case 8442: + switch (stepIndex) + { + case 0: + stepOperationCode = 8365; + return true; + case 1: + stepOperationCode = 8364; + return true; + default: + break; + } + break; + case 8443: + switch (stepIndex) + { + case 0: + stepOperationCode = 8364; + return true; + case 1: + stepOperationCode = 8367; + return true; + default: + break; + } + break; + case 8453: + switch (stepIndex) + { + case 0: + stepOperationCode = 1506; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8454: + switch (stepIndex) + { + case 0: + stepOperationCode = 1507; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8457: + switch (stepIndex) + { + case 0: + stepOperationCode = 1509; + return true; + case 1: + stepOperationCode = 1511; + return true; + default: + break; + } + break; + case 8460: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1474; + return true; + default: + break; + } + break; + case 8461: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1475; + return true; + default: + break; + } + break; + case 8462: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1476; + return true; + default: + break; + } + break; + case 8463: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1477; + return true; + default: + break; + } + break; + case 8464: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1478; + return true; + default: + break; + } + break; + case 8465: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1479; + return true; + default: + break; + } + break; + case 8466: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1480; + return true; + default: + break; + } + break; + case 8467: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1481; + return true; + default: + break; + } + break; + case 8468: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1482; + return true; + default: + break; + } + break; + case 8469: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1483; + return true; + default: + break; + } + break; + case 8470: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1484; + return true; + default: + break; + } + break; + case 8471: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1485; + return true; + default: + break; + } + break; + case 8472: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1486; + return true; + default: + break; + } + break; + case 8473: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1487; + return true; + default: + break; + } + break; + case 8474: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1488; + return true; + default: + break; + } + break; + case 8475: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1489; + return true; + default: + break; + } + break; + case 8476: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1490; + return true; + default: + break; + } + break; + case 8477: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1491; + return true; + default: + break; + } + break; + case 8478: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1492; + return true; + default: + break; + } + break; + case 8479: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1493; + return true; + default: + break; + } + break; + case 8480: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1494; + return true; + default: + break; + } + break; + case 8481: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1495; + return true; + default: + break; + } + break; + case 8482: + switch (stepIndex) + { + case 0: + stepOperationCode = 1747; + return true; + case 1: + stepOperationCode = 1496; + return true; + default: + break; + } + break; + case 8483: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1497; + return true; + default: + break; + } + break; + case 8484: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1498; + return true; + default: + break; + } + break; + case 8485: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1499; + return true; + default: + break; + } + break; + case 8486: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1500; + return true; + default: + break; + } + break; + case 8487: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1501; + return true; + default: + break; + } + break; + case 8488: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1502; + return true; + default: + break; + } + break; + case 8489: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1503; + return true; + default: + break; + } + break; + case 8496: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1515; + return true; + default: + break; + } + break; + case 8497: + switch (stepIndex) + { + case 0: + stepOperationCode = 1243; + return true; + case 1: + stepOperationCode = 1515; + return true; + default: + break; + } + break; + case 8508: + switch (stepIndex) + { + case 0: + stepOperationCode = 1454; + return true; + case 1: + stepOperationCode = 1520; + return true; + default: + break; + } + break; + case 8509: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1521; + return true; + default: + break; + } + break; + case 8510: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1522; + return true; + default: + break; + } + break; + case 8511: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1523; + return true; + default: + break; + } + break; + case 8512: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1524; + return true; + default: + break; + } + break; + case 8513: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1525; + return true; + default: + break; + } + break; + case 8514: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1526; + return true; + default: + break; + } + break; + case 8517: + switch (stepIndex) + { + case 0: + stepOperationCode = 1528; + return true; + case 1: + stepOperationCode = 1527; + return true; + default: + break; + } + break; + case 8530: + switch (stepIndex) + { + case 0: + stepOperationCode = 1539; + return true; + case 1: + stepOperationCode = 1540; + return true; + default: + break; + } + break; + case 8532: + switch (stepIndex) + { + case 0: + stepOperationCode = 1541; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8537: + switch (stepIndex) + { + case 0: + stepOperationCode = 1545; + return true; + case 1: + stepOperationCode = 1237; + return true; + default: + break; + } + break; + case 8553: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1553; + return true; + default: + break; + } + break; + case 8554: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1554; + return true; + default: + break; + } + break; + case 8560: + switch (stepIndex) + { + case 0: + stepOperationCode = 1559; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8562: + switch (stepIndex) + { + case 0: + stepOperationCode = 1560; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8563: + switch (stepIndex) + { + case 0: + stepOperationCode = 1568; + return true; + case 1: + stepOperationCode = 1565; + return true; + default: + break; + } + break; + case 8564: + switch (stepIndex) + { + case 0: + stepOperationCode = 1576; + return true; + case 1: + stepOperationCode = 1473; + return true; + default: + break; + } + break; + case 8565: + switch (stepIndex) + { + case 0: + stepOperationCode = 1574; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8566: + switch (stepIndex) + { + case 0: + stepOperationCode = 1572; + return true; + case 1: + stepOperationCode = 1188; + return true; + default: + break; + } + break; + case 8567: + switch (stepIndex) + { + case 0: + stepOperationCode = 1036; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8568: + switch (stepIndex) + { + case 0: + stepOperationCode = 1584; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8569: + switch (stepIndex) + { + case 0: + stepOperationCode = 1588; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8570: + switch (stepIndex) + { + case 0: + stepOperationCode = 1043; + return true; + case 1: + stepOperationCode = 1146; + return true; + case 2: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8571: + switch (stepIndex) + { + case 0: + stepOperationCode = 1570; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8572: + switch (stepIndex) + { + case 0: + stepOperationCode = 1571; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8573: + switch (stepIndex) + { + case 0: + stepOperationCode = 1591; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8574: + switch (stepIndex) + { + case 0: + stepOperationCode = 1578; + return true; + case 1: + stepOperationCode = 1580; + return true; + default: + break; + } + break; + case 8575: + switch (stepIndex) + { + case 0: + stepOperationCode = 1579; + return true; + case 1: + stepOperationCode = 1580; + return true; + default: + break; + } + break; + case 8576: + switch (stepIndex) + { + case 0: + stepOperationCode = 1594; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8577: + switch (stepIndex) + { + case 0: + stepOperationCode = 1595; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8578: + switch (stepIndex) + { + case 0: + stepOperationCode = 1596; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8579: + switch (stepIndex) + { + case 0: + stepOperationCode = 1593; + return true; + case 1: + stepOperationCode = 1150; + return true; + default: + break; + } + break; + case 8580: + switch (stepIndex) + { + case 0: + stepOperationCode = 1611; + return true; + case 1: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8581: + switch (stepIndex) + { + case 0: + stepOperationCode = 1616; + return true; + case 1: + stepOperationCode = 1237; + return true; + default: + break; + } + break; + case 8582: + switch (stepIndex) + { + case 0: + stepOperationCode = 1454; + return true; + case 1: + stepOperationCode = 1741; + return true; + default: + break; + } + break; + case 8583: + switch (stepIndex) + { + case 0: + stepOperationCode = 1461; + return true; + case 1: + stepOperationCode = 1731; + return true; + default: + break; + } + break; + case 8584: + switch (stepIndex) + { + case 0: + stepOperationCode = 1313; + return true; + case 1: + stepOperationCode = 1752; + return true; + default: + break; + } + break; + case 8585: + switch (stepIndex) + { + case 0: + stepOperationCode = 1313; + return true; + case 1: + stepOperationCode = 1702; + return true; + default: + break; + } + break; + case 8586: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1704; + return true; + default: + break; + } + break; + case 8587: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1705; + return true; + default: + break; + } + break; + case 8588: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1706; + return true; + default: + break; + } + break; + case 8589: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1707; + return true; + default: + break; + } + break; + case 8590: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1717; + return true; + default: + break; + } + break; + case 8591: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1728; + return true; + default: + break; + } + break; + case 8592: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1708; + return true; + default: + break; + } + break; + case 8593: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1739; + return true; + default: + break; + } + break; + case 8594: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1750; + return true; + default: + break; + } + break; + case 8595: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1712; + return true; + default: + break; + } + break; + case 8596: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1714; + return true; + default: + break; + } + break; + case 8597: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1713; + return true; + default: + break; + } + break; + case 8598: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1748; + return true; + default: + break; + } + break; + case 8599: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1742; + return true; + default: + break; + } + break; + case 8600: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1709; + return true; + default: + break; + } + break; + case 8601: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1743; + return true; + default: + break; + } + break; + case 8602: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1718; + return true; + default: + break; + } + break; + case 8603: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1719; + return true; + default: + break; + } + break; + case 8604: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1721; + return true; + default: + break; + } + break; + case 8605: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1720; + return true; + default: + break; + } + break; + case 8606: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1725; + return true; + default: + break; + } + break; + case 8607: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1722; + return true; + default: + break; + } + break; + case 8608: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1710; + return true; + default: + break; + } + break; + case 8609: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1723; + return true; + default: + break; + } + break; + case 8610: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1711; + return true; + default: + break; + } + break; + case 8611: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1715; + return true; + default: + break; + } + break; + case 8612: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1716; + return true; + default: + break; + } + break; + case 8613: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1724; + return true; + default: + break; + } + break; + case 8614: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1744; + return true; + default: + break; + } + break; + case 8615: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1749; + return true; + default: + break; + } + break; + case 8616: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1726; + return true; + default: + break; + } + break; + case 8617: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1727; + return true; + default: + break; + } + break; + case 8618: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1729; + return true; + default: + break; + } + break; + case 8619: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1745; + return true; + default: + break; + } + break; + case 8620: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1730; + return true; + default: + break; + } + break; + case 8621: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1737; + return true; + default: + break; + } + break; + case 8622: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1732; + return true; + default: + break; + } + break; + case 8623: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1733; + return true; + default: + break; + } + break; + case 8624: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1734; + return true; + default: + break; + } + break; + case 8625: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1735; + return true; + default: + break; + } + break; + case 8626: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1746; + return true; + default: + break; + } + break; + case 8627: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1736; + return true; + default: + break; + } + break; + case 8628: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1747; + return true; + default: + break; + } + break; + case 8629: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1738; + return true; + default: + break; + } + break; + case 8630: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 1740; + return true; + default: + break; + } + break; + case 8631: + switch (stepIndex) + { + case 0: + stepOperationCode = 1805; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8632: + switch (stepIndex) + { + case 0: + stepOperationCode = 1806; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8633: + switch (stepIndex) + { + case 0: + stepOperationCode = 1828; + return true; + case 1: + stepOperationCode = 1238; + return true; + default: + break; + } + break; + case 8634: + switch (stepIndex) + { + case 0: + stepOperationCode = 1839; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8635: + switch (stepIndex) + { + case 0: + stepOperationCode = 1313; + return true; + case 1: + stepOperationCode = 1849; + return true; + default: + break; + } + break; + case 8636: + switch (stepIndex) + { + case 0: + stepOperationCode = 1881; + return true; + case 1: + stepOperationCode = 1130; + return true; + default: + break; + } + break; + case 8637: + switch (stepIndex) + { + case 0: + stepOperationCode = 1756; + return true; + case 1: + stepOperationCode = 1944; + return true; + default: + break; + } + break; + case 8638: + switch (stepIndex) + { + case 0: + stepOperationCode = 1260; + return true; + case 1: + stepOperationCode = 1837; + return true; + default: + break; + } + break; + case 8639: + switch (stepIndex) + { + case 0: + stepOperationCode = 1762; + return true; + case 1: + stepOperationCode = 1654; + return true; + default: + break; + } + break; + case 8640: + switch (stepIndex) + { + case 0: + stepOperationCode = 1882; + return true; + case 1: + stepOperationCode = 1253; + return true; + default: + break; + } + break; + case 8641: + switch (stepIndex) + { + case 0: + stepOperationCode = 1883; + return true; + case 1: + stepOperationCode = 1897; + return true; + default: + break; + } + break; + case 8642: + switch (stepIndex) + { + case 0: + stepOperationCode = 1884; + return true; + case 1: + stepOperationCode = 1623; + return true; + default: + break; + } + break; + case 8643: + switch (stepIndex) + { + case 0: + stepOperationCode = 1891; + return true; + case 1: + stepOperationCode = 1272; + return true; + default: + break; + } + break; + case 8644: + switch (stepIndex) + { + case 0: + stepOperationCode = 1761; + return true; + case 1: + stepOperationCode = 1891; + return true; + case 2: + stepOperationCode = 1272; + return true; + default: + break; + } + break; + case 8645: + switch (stepIndex) + { + case 0: + stepOperationCode = 1757; + return true; + case 1: + stepOperationCode = 1618; + return true; + default: + break; + } + break; + case 8646: + switch (stepIndex) + { + case 0: + stepOperationCode = 1902; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8647: + switch (stepIndex) + { + case 0: + stepOperationCode = 1313; + return true; + case 1: + stepOperationCode = 1950; + return true; + case 2: + stepOperationCode = 1946; + return true; + default: + break; + } + break; + case 8648: + switch (stepIndex) + { + case 0: + stepOperationCode = 1991; + return true; + case 1: + stepOperationCode = 1986; + return true; + default: + break; + } + break; + case 8649: + switch (stepIndex) + { + case 0: + stepOperationCode = 1991; + return true; + case 1: + stepOperationCode = 1990; + return true; + default: + break; + } + break; + case 8650: + switch (stepIndex) + { + case 0: + stepOperationCode = 1071; + return true; + case 1: + stepOperationCode = 1073; + return true; + default: + break; + } + break; + case 8651: + switch (stepIndex) + { + case 0: + stepOperationCode = 1063; + return true; + case 1: + stepOperationCode = 1065; + return true; + default: + break; + } + break; + case 8652: + switch (stepIndex) + { + case 0: + stepOperationCode = 1064; + return true; + case 1: + stepOperationCode = 1065; + return true; + default: + break; + } + break; + case 8653: + switch (stepIndex) + { + case 0: + stepOperationCode = 15753; + return true; + case 1: + stepOperationCode = 1146; + return true; + default: + break; + } + break; + case 8654: + switch (stepIndex) + { + case 0: + stepOperationCode = 15753; + return true; + case 1: + stepOperationCode = 1146; + return true; + case 2: + stepOperationCode = 1149; + return true; + default: + break; + } + break; + case 8655: + switch (stepIndex) + { + case 0: + stepOperationCode = 1902; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8656: + switch (stepIndex) + { + case 0: + stepOperationCode = 15790; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8657: + switch (stepIndex) + { + case 0: + stepOperationCode = 15792; + return true; + case 1: + stepOperationCode = 1240; + return true; + default: + break; + } + break; + case 8659: + switch (stepIndex) + { + case 0: + stepOperationCode = 15896; + return true; + case 1: + stepOperationCode = 1158; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket9(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 9091: + switch (stepIndex) + { + case 0: + stepOperationCode = 9079; + return true; + case 1: + stepOperationCode = 9084; + return true; + default: + break; + } + break; + case 9092: + switch (stepIndex) + { + case 0: + stepOperationCode = 9080; + return true; + case 1: + stepOperationCode = 9084; + return true; + default: + break; + } + break; + case 9093: + switch (stepIndex) + { + case 0: + stepOperationCode = 9081; + return true; + case 1: + stepOperationCode = 9084; + return true; + default: + break; + } + break; + case 9094: + switch (stepIndex) + { + case 0: + stepOperationCode = 9082; + return true; + case 1: + stepOperationCode = 9084; + return true; + default: + break; + } + break; + case 9095: + switch (stepIndex) + { + case 0: + stepOperationCode = 9082; + return true; + case 1: + stepOperationCode = 9085; + return true; + default: + break; + } + break; + case 9096: + switch (stepIndex) + { + case 0: + stepOperationCode = 9082; + return true; + case 1: + stepOperationCode = 9086; + return true; + default: + break; + } + break; + case 9097: + switch (stepIndex) + { + case 0: + stepOperationCode = 9082; + return true; + case 1: + stepOperationCode = 9087; + return true; + default: + break; + } + break; + case 9098: + switch (stepIndex) + { + case 0: + stepOperationCode = 9082; + return true; + case 1: + stepOperationCode = 9088; + return true; + default: + break; + } + break; + case 9099: + switch (stepIndex) + { + case 0: + stepOperationCode = 9082; + return true; + case 1: + stepOperationCode = 9089; + return true; + default: + break; + } + break; + case 9100: + switch (stepIndex) + { + case 0: + stepOperationCode = 9083; + return true; + case 1: + stepOperationCode = 9088; + return true; + default: + break; + } + break; + case 9101: + switch (stepIndex) + { + case 0: + stepOperationCode = 9083; + return true; + case 1: + stepOperationCode = 9089; + return true; + default: + break; + } + break; + case 9102: + switch (stepIndex) + { + case 0: + stepOperationCode = 9083; + return true; + case 1: + stepOperationCode = 9090; + return true; + default: + break; + } + break; + case 9103: + switch (stepIndex) + { + case 0: + stepOperationCode = 1241; + return true; + case 1: + stepOperationCode = 8971; + return true; + case 2: + stepOperationCode = 7807; + return true; + case 3: + stepOperationCode = 7790; + return true; + default: + break; + } + break; + case 9104: + switch (stepIndex) + { + case 0: + stepOperationCode = 8555; + return true; + case 1: + stepOperationCode = 8556; + return true; + case 2: + stepOperationCode = 8861; + return true; + case 3: + stepOperationCode = 8862; + return true; + case 4: + stepOperationCode = 8559; + return true; + case 5: + stepOperationCode = 7807; + return true; + case 6: + stepOperationCode = 7790; + return true; + default: + break; + } + break; + case 9336: + switch (stepIndex) + { + case 0: + stepOperationCode = 1313; + return true; + case 1: + stepOperationCode = 9244; + return true; + default: + break; + } + break; + case 9337: + switch (stepIndex) + { + case 0: + stepOperationCode = 1763; + return true; + case 1: + stepOperationCode = 9327; + return true; + default: + break; + } + break; + case 9499: + switch (stepIndex) + { + case 0: + stepOperationCode = 9276; + return true; + case 1: + stepOperationCode = 9275; + return true; + default: + break; + } + break; + case 9683: + switch (stepIndex) + { + case 0: + stepOperationCode = 8049; + return true; + case 1: + stepOperationCode = 8447; + return true; + default: + break; + } + break; + case 9685: + switch (stepIndex) + { + case 0: + stepOperationCode = 9459; + return true; + case 1: + stepOperationCode = 8447; + return true; + default: + break; + } + break; + case 9687: + switch (stepIndex) + { + case 0: + stepOperationCode = 8447; + return true; + case 1: + stepOperationCode = 8448; + return true; + default: + break; + } + break; + case 9731: + switch (stepIndex) + { + case 0: + stepOperationCode = 9729; + return true; + case 1: + stepOperationCode = 9726; + return true; + default: + break; + } + break; + case 9750: + switch (stepIndex) + { + case 0: + stepOperationCode = 9727; + return true; + case 1: + stepOperationCode = 9726; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket10(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 10146: + switch (stepIndex) + { + case 0: + stepOperationCode = 9629; + return true; + case 1: + stepOperationCode = 10145; + return true; + default: + break; + } + break; + case 10392: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10380; + return true; + default: + break; + } + break; + case 10393: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10381; + return true; + default: + break; + } + break; + case 10394: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10382; + return true; + default: + break; + } + break; + case 10395: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10383; + return true; + default: + break; + } + break; + case 10396: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10384; + return true; + default: + break; + } + break; + case 10397: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10385; + return true; + default: + break; + } + break; + case 10398: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10386; + return true; + default: + break; + } + break; + case 10399: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10387; + return true; + default: + break; + } + break; + case 10400: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10388; + return true; + default: + break; + } + break; + case 10409: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10389; + return true; + default: + break; + } + break; + case 10410: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10390; + return true; + default: + break; + } + break; + case 10411: + switch (stepIndex) + { + case 0: + stepOperationCode = 7812; + return true; + case 1: + stepOperationCode = 10391; + return true; + default: + break; + } + break; + case 10495: + switch (stepIndex) + { + case 0: + stepOperationCode = 10490; + return true; + case 1: + stepOperationCode = 10492; + return true; + default: + break; + } + break; + case 10496: + switch (stepIndex) + { + case 0: + stepOperationCode = 10492; + return true; + case 1: + stepOperationCode = 10494; + return true; + default: + break; + } + break; + case 10616: + switch (stepIndex) + { + case 0: + stepOperationCode = 9629; + return true; + case 1: + stepOperationCode = 10145; + return true; + default: + break; + } + break; + case 10675: + switch (stepIndex) + { + case 0: + stepOperationCode = 10646; + return true; + case 1: + stepOperationCode = 10657; + return true; + default: + break; + } + break; + case 10754: + switch (stepIndex) + { + case 0: + stepOperationCode = 10750; + return true; + case 1: + stepOperationCode = 10752; + return true; + default: + break; + } + break; + case 10755: + switch (stepIndex) + { + case 0: + stepOperationCode = 10646; + return true; + case 1: + stepOperationCode = 10658; + return true; + default: + break; + } + break; + case 10756: + switch (stepIndex) + { + case 0: + stepOperationCode = 10750; + return true; + case 1: + stepOperationCode = 10753; + return true; + default: + break; + } + break; + case 10778: + switch (stepIndex) + { + case 0: + stepOperationCode = 18193; + return true; + case 1: + stepOperationCode = 10703; + return true; + case 2: + stepOperationCode = 16065; + return true; + default: + break; + } + break; + case 10815: + switch (stepIndex) + { + case 0: + stepOperationCode = 8880; + return true; + case 1: + stepOperationCode = 10809; + return true; + default: + break; + } + break; + case 10816: + switch (stepIndex) + { + case 0: + stepOperationCode = 8880; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10810; + return true; + default: + break; + } + break; + case 10817: + switch (stepIndex) + { + case 0: + stepOperationCode = 8880; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10811; + return true; + default: + break; + } + break; + case 10818: + switch (stepIndex) + { + case 0: + stepOperationCode = 8880; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10812; + return true; + default: + break; + } + break; + case 10824: + switch (stepIndex) + { + case 0: + stepOperationCode = 8880; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10813; + return true; + default: + break; + } + break; + case 10825: + switch (stepIndex) + { + case 0: + stepOperationCode = 8880; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10814; + return true; + default: + break; + } + break; + case 10868: + switch (stepIndex) + { + case 0: + stepOperationCode = 10587; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10814; + return true; + default: + break; + } + break; + case 10869: + switch (stepIndex) + { + case 0: + stepOperationCode = 10587; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10810; + return true; + default: + break; + } + break; + case 10870: + switch (stepIndex) + { + case 0: + stepOperationCode = 10587; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10811; + return true; + default: + break; + } + break; + case 10871: + switch (stepIndex) + { + case 0: + stepOperationCode = 10587; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10812; + return true; + default: + break; + } + break; + case 10872: + switch (stepIndex) + { + case 0: + stepOperationCode = 10587; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10813; + return true; + default: + break; + } + break; + case 10894: + switch (stepIndex) + { + case 0: + stepOperationCode = 8880; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10893; + return true; + default: + break; + } + break; + case 10895: + switch (stepIndex) + { + case 0: + stepOperationCode = 10587; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 10893; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + private static bool TryGetConcatenatedOperationStepBucket11(int operationCode, int stepIndex, out int stepOperationCode) + { + switch (operationCode) + { + case 11005: + switch (stepIndex) + { + case 0: + stepOperationCode = 10107; + return true; + case 1: + stepOperationCode = 11004; + return true; + default: + break; + } + break; + case 11066: + switch (stepIndex) + { + case 0: + stepOperationCode = 9995; + return true; + case 1: + stepOperationCode = 7939; + return true; + case 2: + stepOperationCode = 11010; + return true; + default: + break; + } + break; + case 11110: + switch (stepIndex) + { + case 0: + stepOperationCode = 9995; + return true; + case 1: + stepOperationCode = 7939; + return true; + case 2: + stepOperationCode = 11109; + return true; + default: + break; + } + break; + case 11140: + switch (stepIndex) + { + case 0: + stepOperationCode = 9996; + return true; + case 1: + stepOperationCode = 7938; + return true; + case 2: + stepOperationCode = 11135; + return true; + default: + break; + } + break; + case 11194: + switch (stepIndex) + { + case 0: + stepOperationCode = 10586; + return true; + case 1: + stepOperationCode = 10988; + return true; + case 2: + stepOperationCode = 11192; + return true; + default: + break; + } + break; + case 11196: + switch (stepIndex) + { + case 0: + stepOperationCode = 10586; + return true; + case 1: + stepOperationCode = 11228; + return true; + case 2: + stepOperationCode = 11195; + return true; + default: + break; + } + break; + case 11206: + switch (stepIndex) + { + case 0: + stepOperationCode = 10586; + return true; + case 1: + stepOperationCode = 11228; + return true; + case 2: + stepOperationCode = 11205; + return true; + default: + break; + } + break; + case 11230: + switch (stepIndex) + { + case 0: + stepOperationCode = 9472; + return true; + case 1: + stepOperationCode = 11034; + return true; + default: + break; + } + break; + case 11285: + switch (stepIndex) + { + case 0: + stepOperationCode = 10586; + return true; + case 1: + stepOperationCode = 11228; + return true; + case 2: + stepOperationCode = 11205; + return true; + default: + break; + } + break; + case 11310: + switch (stepIndex) + { + case 0: + stepOperationCode = 10586; + return true; + case 1: + stepOperationCode = 10988; + return true; + case 2: + stepOperationCode = 11308; + return true; + default: + break; + } + break; + case 11315: + switch (stepIndex) + { + case 0: + stepOperationCode = 10587; + return true; + case 1: + stepOperationCode = 10809; + return true; + case 2: + stepOperationCode = 11316; + return true; + default: + break; + } + break; + case 11398: + switch (stepIndex) + { + case 0: + stepOperationCode = 9593; + return true; + case 1: + stepOperationCode = 11396; + return true; + default: + break; + } + break; + default: + break; + } + + stepOperationCode = 0; + return false; + } + + internal static bool TryGetExplicitOperationParameters(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode / 1000) + { + case 1: + return TryGetExplicitOperationParametersBucket1(operationCode, out parameters); + case 3: + return TryGetExplicitOperationParametersBucket3(operationCode, out parameters); + case 4: + return TryGetExplicitOperationParametersBucket4(operationCode, out parameters); + case 5: + return TryGetExplicitOperationParametersBucket5(operationCode, out parameters); + case 6: + return TryGetExplicitOperationParametersBucket6(operationCode, out parameters); + case 7: + return TryGetExplicitOperationParametersBucket7(operationCode, out parameters); + case 8: + return TryGetExplicitOperationParametersBucket8(operationCode, out parameters); + case 9: + return TryGetExplicitOperationParametersBucket9(operationCode, out parameters); + case 10: + return TryGetExplicitOperationParametersBucket10(operationCode, out parameters); + case 11: + return TryGetExplicitOperationParametersBucket11(operationCode, out parameters); + case 15: + return TryGetExplicitOperationParametersBucket15(operationCode, out parameters); + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket1(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 1024: + parameters = new EpsgExplicitOperationRecord(1024, 601.705d, 84.263d, 485.227d, 4.7354d, 1.3145d, 5.393d, -2.3887d); + return true; + case 1055: + parameters = new EpsgExplicitOperationRecord(1055, -145.7d, -249.1d, 1.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1056: + parameters = new EpsgExplicitOperationRecord(1056, -85.645d, -273.077d, -79.708d, 2.289d, -1.421d, 2.532d, 3.194d); + return true; + case 1057: + parameters = new EpsgExplicitOperationRecord(1057, -202.234d, -168.351d, -63.51d, 3.545d, 0.659d, -1.945d, 2.1d); + return true; + case 1058: + parameters = new EpsgExplicitOperationRecord(1058, -18.944d, -379.364d, -24.063d, 0.04d, -0.764d, 6.431d, 3.657d); + return true; + case 1059: + parameters = new EpsgExplicitOperationRecord(1059, -294.7d, -200.1d, 525.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1060: + parameters = new EpsgExplicitOperationRecord(1060, -3.2d, -5.7d, 2.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1061: + parameters = new EpsgExplicitOperationRecord(1061, -20.8d, 11.3d, 2.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1062: + parameters = new EpsgExplicitOperationRecord(1062, 226.702d, -193.337d, -35.371d, -2.229d, -4.391d, 9.238d, 0.9798d); + return true; + case 1063: + parameters = new EpsgExplicitOperationRecord(1063, -2.227d, 6.524d, 2.178d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1064: + parameters = new EpsgExplicitOperationRecord(1064, -0.652d, 1.619d, 0.213d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1065: + parameters = new EpsgExplicitOperationRecord(1065, 44.585d, -131.212d, -39.544d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1066: + parameters = new EpsgExplicitOperationRecord(1066, 593.032d, 26.0d, 478.741d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1067: + parameters = new EpsgExplicitOperationRecord(1067, -92.1d, -89.9d, 114.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1070: + parameters = new EpsgExplicitOperationRecord(1070, -100.0d, -248.0d, 259.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1071: + parameters = new EpsgExplicitOperationRecord(1071, -181.0d, -122.0d, 225.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1073: + parameters = new EpsgExplicitOperationRecord(1073, -48.0d, 55.0d, 52.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1074: + parameters = new EpsgExplicitOperationRecord(1074, -275.7224d, 94.7824d, 340.8944d, -8.001d, -4.42d, -11.821d, 1.0d); + return true; + case 1075: + parameters = new EpsgExplicitOperationRecord(1075, -89.05d, -87.03d, -124.56d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1078: + parameters = new EpsgExplicitOperationRecord(1078, -265.983d, 76.918d, 20.182d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1079: + parameters = new EpsgExplicitOperationRecord(1079, -265.983d, 76.918d, 20.182d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1080: + parameters = new EpsgExplicitOperationRecord(1080, 175.0d, -38.0d, 113.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1081: + parameters = new EpsgExplicitOperationRecord(1081, 174.05d, -25.49d, 112.57d, -0.0d, -0.0d, 0.554d, 0.2263d); + return true; + case 1082: + parameters = new EpsgExplicitOperationRecord(1082, 174.05d, -25.49d, 112.57d, -0.0d, -0.0d, 0.554d, 0.2263d); + return true; + case 1083: + parameters = new EpsgExplicitOperationRecord(1083, 50.0d, 212.0d, 381.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1084: + parameters = new EpsgExplicitOperationRecord(1084, 70.0d, 207.0d, 389.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1085: + parameters = new EpsgExplicitOperationRecord(1085, 65.334d, 212.46d, 387.63d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1087: + parameters = new EpsgExplicitOperationRecord(1087, -112.0d, -110.3d, -140.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1088: + parameters = new EpsgExplicitOperationRecord(1088, -223.7d, -67.38d, 1.34d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1089: + parameters = new EpsgExplicitOperationRecord(1089, -225.4d, -67.7d, 7.85d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1090: + parameters = new EpsgExplicitOperationRecord(1090, -227.1d, -68.1d, 14.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1091: + parameters = new EpsgExplicitOperationRecord(1091, -231.61d, -68.21d, 13.93d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1092: + parameters = new EpsgExplicitOperationRecord(1092, -225.06d, -67.37d, 14.61d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1093: + parameters = new EpsgExplicitOperationRecord(1093, -229.08d, -65.73d, 20.21d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1094: + parameters = new EpsgExplicitOperationRecord(1094, -230.47d, -56.08d, 22.43d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1095: + parameters = new EpsgExplicitOperationRecord(1095, -270.933d, 115.599d, -360.226d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1096: + parameters = new EpsgExplicitOperationRecord(1096, -270.933d, 115.599d, -360.226d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1099: + parameters = new EpsgExplicitOperationRecord(1099, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1100: + parameters = new EpsgExplicitOperationRecord(1100, -166.0d, -15.0d, 204.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1101: + parameters = new EpsgExplicitOperationRecord(1101, -118.0d, -14.0d, 218.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1102: + parameters = new EpsgExplicitOperationRecord(1102, -134.0d, -2.0d, 210.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1103: + parameters = new EpsgExplicitOperationRecord(1103, -165.0d, -11.0d, 206.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1104: + parameters = new EpsgExplicitOperationRecord(1104, -123.0d, -20.0d, 220.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1105: + parameters = new EpsgExplicitOperationRecord(1105, -128.0d, -18.0d, 224.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1106: + parameters = new EpsgExplicitOperationRecord(1106, -161.0d, -14.0d, 205.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1107: + parameters = new EpsgExplicitOperationRecord(1107, -43.0d, -163.0d, 45.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1108: + parameters = new EpsgExplicitOperationRecord(1108, -133.0d, -48.0d, 148.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1109: + parameters = new EpsgExplicitOperationRecord(1109, -134.0d, -48.0d, 149.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1110: + parameters = new EpsgExplicitOperationRecord(1110, -150.0d, -250.0d, -1.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1111: + parameters = new EpsgExplicitOperationRecord(1111, -143.0d, -236.0d, 7.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1112: + parameters = new EpsgExplicitOperationRecord(1112, 593.16d, 26.15d, 478.54d, -6.3239d, -0.5008d, -5.5487d, 4.0775d); + return true; + case 1113: + parameters = new EpsgExplicitOperationRecord(1113, -143.0d, -90.0d, -294.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1114: + parameters = new EpsgExplicitOperationRecord(1114, -138.0d, -105.0d, -289.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1116: + parameters = new EpsgExplicitOperationRecord(1116, -125.0d, -108.0d, -295.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1117: + parameters = new EpsgExplicitOperationRecord(1117, -161.0d, -73.0d, -317.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1118: + parameters = new EpsgExplicitOperationRecord(1118, -134.0d, -105.0d, -295.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1120: + parameters = new EpsgExplicitOperationRecord(1120, -147.0d, -74.0d, -283.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1121: + parameters = new EpsgExplicitOperationRecord(1121, -142.0d, -96.0d, -293.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1122: + parameters = new EpsgExplicitOperationRecord(1122, -160.0d, -6.0d, -302.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1124: + parameters = new EpsgExplicitOperationRecord(1124, -73.0d, 213.0d, 296.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1125: + parameters = new EpsgExplicitOperationRecord(1125, 307.0d, 304.0d, -318.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1126: + parameters = new EpsgExplicitOperationRecord(1126, -384.0d, 664.0d, -48.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1127: + parameters = new EpsgExplicitOperationRecord(1127, -148.0d, 136.0d, 90.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1128: + parameters = new EpsgExplicitOperationRecord(1128, -136.0d, -108.0d, -292.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1129: + parameters = new EpsgExplicitOperationRecord(1129, -134.73d, -110.92d, -292.66d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1130: + parameters = new EpsgExplicitOperationRecord(1130, -263.0d, 6.0d, 431.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1131: + parameters = new EpsgExplicitOperationRecord(1131, -134.0d, 229.0d, -29.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1132: + parameters = new EpsgExplicitOperationRecord(1132, -206.0d, 172.0d, -6.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1133: + parameters = new EpsgExplicitOperationRecord(1133, -87.0d, -98.0d, -121.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1134: + parameters = new EpsgExplicitOperationRecord(1134, -87.0d, -96.0d, -120.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1135: + parameters = new EpsgExplicitOperationRecord(1135, -103.0d, -106.0d, -141.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1136: + parameters = new EpsgExplicitOperationRecord(1136, -104.0d, -101.0d, -140.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1137: + parameters = new EpsgExplicitOperationRecord(1137, -130.0d, -117.0d, -151.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1138: + parameters = new EpsgExplicitOperationRecord(1138, -86.0d, -96.0d, -120.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1139: + parameters = new EpsgExplicitOperationRecord(1139, -87.0d, -95.0d, -120.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1140: + parameters = new EpsgExplicitOperationRecord(1140, -84.0d, -95.0d, -130.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1141: + parameters = new EpsgExplicitOperationRecord(1141, -117.0d, -132.0d, -164.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1142: + parameters = new EpsgExplicitOperationRecord(1142, -97.0d, -103.0d, -120.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1143: + parameters = new EpsgExplicitOperationRecord(1143, -97.0d, -88.0d, -135.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1144: + parameters = new EpsgExplicitOperationRecord(1144, -107.0d, -88.0d, -149.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1145: + parameters = new EpsgExplicitOperationRecord(1145, -84.0d, -107.0d, -120.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1146: + parameters = new EpsgExplicitOperationRecord(1146, -82.981d, -99.719d, -110.709d, -0.5076d, 0.1503d, 0.3898d, -0.3143d); + return true; + case 1147: + parameters = new EpsgExplicitOperationRecord(1147, -1.51d, -0.84d, -3.5d, -1.893d, -0.687d, -2.764d, 0.609d); + return true; + case 1148: + parameters = new EpsgExplicitOperationRecord(1148, -130.0d, 110.0d, -13.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1149: + parameters = new EpsgExplicitOperationRecord(1149, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1150: + parameters = new EpsgExplicitOperationRecord(1150, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1151: + parameters = new EpsgExplicitOperationRecord(1151, 84.0d, -22.0d, 209.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1152: + parameters = new EpsgExplicitOperationRecord(1152, -637.0d, -549.0d, -203.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1153: + parameters = new EpsgExplicitOperationRecord(1153, 217.0d, 823.0d, 299.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1154: + parameters = new EpsgExplicitOperationRecord(1154, 209.0d, 818.0d, 290.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1155: + parameters = new EpsgExplicitOperationRecord(1155, 282.0d, 726.0d, 254.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1156: + parameters = new EpsgExplicitOperationRecord(1156, 295.0d, 736.0d, 257.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1157: + parameters = new EpsgExplicitOperationRecord(1157, -97.0d, 787.0d, 86.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1158: + parameters = new EpsgExplicitOperationRecord(1158, -11.0d, 851.0d, 5.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1159: + parameters = new EpsgExplicitOperationRecord(1159, -130.0d, 29.0d, 364.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1160: + parameters = new EpsgExplicitOperationRecord(1160, -90.0d, 40.0d, 88.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1161: + parameters = new EpsgExplicitOperationRecord(1161, -133.0d, -77.0d, -51.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1162: + parameters = new EpsgExplicitOperationRecord(1162, -133.0d, -79.0d, -72.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1163: + parameters = new EpsgExplicitOperationRecord(1163, -74.0d, -130.0d, 42.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1164: + parameters = new EpsgExplicitOperationRecord(1164, 41.0d, -220.0d, -134.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1165: + parameters = new EpsgExplicitOperationRecord(1165, 639.0d, 405.0d, 60.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1166: + parameters = new EpsgExplicitOperationRecord(1166, 31.0d, 146.0d, 47.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1167: + parameters = new EpsgExplicitOperationRecord(1167, -81.0d, -84.0d, 115.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1168: + parameters = new EpsgExplicitOperationRecord(1168, -92.0d, -93.0d, 122.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1169: + parameters = new EpsgExplicitOperationRecord(1169, -225.0d, -65.0d, 9.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1170: + parameters = new EpsgExplicitOperationRecord(1170, -3.0d, 142.0d, 183.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1171: + parameters = new EpsgExplicitOperationRecord(1171, 0.0d, 125.0d, 194.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1172: + parameters = new EpsgExplicitOperationRecord(1172, -10.0d, 158.0d, 187.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1173: + parameters = new EpsgExplicitOperationRecord(1173, -8.0d, 160.0d, 176.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1174: + parameters = new EpsgExplicitOperationRecord(1174, -9.0d, 161.0d, 179.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1175: + parameters = new EpsgExplicitOperationRecord(1175, -8.0d, 159.0d, 175.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1176: + parameters = new EpsgExplicitOperationRecord(1176, -5.0d, 135.0d, 172.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1177: + parameters = new EpsgExplicitOperationRecord(1177, -4.0d, 154.0d, 178.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1178: + parameters = new EpsgExplicitOperationRecord(1178, 1.0d, 140.0d, 165.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1179: + parameters = new EpsgExplicitOperationRecord(1179, -7.0d, 162.0d, 188.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1180: + parameters = new EpsgExplicitOperationRecord(1180, -9.0d, 157.0d, 184.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1181: + parameters = new EpsgExplicitOperationRecord(1181, -22.0d, 160.0d, 190.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1182: + parameters = new EpsgExplicitOperationRecord(1182, 4.0d, 159.0d, 188.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1183: + parameters = new EpsgExplicitOperationRecord(1183, -7.0d, 139.0d, 181.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1184: + parameters = new EpsgExplicitOperationRecord(1184, 0.0d, 125.0d, 201.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1185: + parameters = new EpsgExplicitOperationRecord(1185, -9.0d, 152.0d, 178.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1186: + parameters = new EpsgExplicitOperationRecord(1186, 11.0d, 114.0d, 195.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1187: + parameters = new EpsgExplicitOperationRecord(1187, -12.0d, 130.0d, 190.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1188: + parameters = new EpsgExplicitOperationRecord(1188, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1189: + parameters = new EpsgExplicitOperationRecord(1189, -247.0d, -148.0d, 369.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1190: + parameters = new EpsgExplicitOperationRecord(1190, -243.0d, -192.0d, 477.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1191: + parameters = new EpsgExplicitOperationRecord(1191, -249.0d, -156.0d, 381.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1192: + parameters = new EpsgExplicitOperationRecord(1192, -10.0d, 375.0d, 165.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1193: + parameters = new EpsgExplicitOperationRecord(1193, -168.0d, -60.0d, 320.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1194: + parameters = new EpsgExplicitOperationRecord(1194, 601.705d, 84.263d, 485.227d, 4.7354d, 1.3145d, 5.393d, -2.3887d); + return true; + case 1195: + parameters = new EpsgExplicitOperationRecord(1195, 375.0d, -111.0d, 431.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1196: + parameters = new EpsgExplicitOperationRecord(1196, 371.0d, -112.0d, 434.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1197: + parameters = new EpsgExplicitOperationRecord(1197, 371.0d, -111.0d, 434.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1198: + parameters = new EpsgExplicitOperationRecord(1198, 384.0d, -111.0d, 425.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1199: + parameters = new EpsgExplicitOperationRecord(1199, 370.0d, -108.0d, 434.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1200: + parameters = new EpsgExplicitOperationRecord(1200, -148.0d, 51.0d, -291.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1201: + parameters = new EpsgExplicitOperationRecord(1201, -288.0d, 175.0d, -376.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1202: + parameters = new EpsgExplicitOperationRecord(1202, -270.0d, 188.0d, -388.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1203: + parameters = new EpsgExplicitOperationRecord(1203, -270.0d, 183.0d, -390.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1204: + parameters = new EpsgExplicitOperationRecord(1204, -305.0d, 243.0d, -442.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1205: + parameters = new EpsgExplicitOperationRecord(1205, -282.0d, 169.0d, -371.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1206: + parameters = new EpsgExplicitOperationRecord(1206, -278.0d, 171.0d, -367.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1207: + parameters = new EpsgExplicitOperationRecord(1207, -298.0d, 159.0d, -369.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1208: + parameters = new EpsgExplicitOperationRecord(1208, -279.0d, 175.0d, -379.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1209: + parameters = new EpsgExplicitOperationRecord(1209, -295.0d, 173.0d, -371.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1210: + parameters = new EpsgExplicitOperationRecord(1210, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1225: + parameters = new EpsgExplicitOperationRecord(1225, -355.0d, 21.0d, 72.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1226: + parameters = new EpsgExplicitOperationRecord(1226, 616.0d, 97.0d, -251.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1227: + parameters = new EpsgExplicitOperationRecord(1227, -189.0d, -242.0d, -91.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1228: + parameters = new EpsgExplicitOperationRecord(1228, -679.0d, 669.0d, -48.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1230: + parameters = new EpsgExplicitOperationRecord(1230, -148.0d, 507.0d, 685.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1231: + parameters = new EpsgExplicitOperationRecord(1231, -148.0d, 507.0d, 685.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1232: + parameters = new EpsgExplicitOperationRecord(1232, -146.0d, 507.0d, 687.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1233: + parameters = new EpsgExplicitOperationRecord(1233, -158.0d, 507.0d, 676.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1234: + parameters = new EpsgExplicitOperationRecord(1234, -155.0d, 171.0d, 37.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1235: + parameters = new EpsgExplicitOperationRecord(1235, -265.0d, 120.0d, -358.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1236: + parameters = new EpsgExplicitOperationRecord(1236, -116.0d, -50.47d, 141.69d, 0.23d, 0.39d, 0.344d, 0.0983d); + return true; + case 1237: + parameters = new EpsgExplicitOperationRecord(1237, 0.0d, 0.0d, 4.5d, 0.0d, 0.0d, 0.554d, 0.2263d); + return true; + case 1238: + parameters = new EpsgExplicitOperationRecord(1238, 0.0d, 0.0d, 4.5d, 0.0d, 0.0d, 0.554d, 0.219d); + return true; + case 1239: + parameters = new EpsgExplicitOperationRecord(1239, 0.0d, 0.0d, -2.6d, 0.0d, 0.0d, 0.26d, -0.6063d); + return true; + case 1240: + parameters = new EpsgExplicitOperationRecord(1240, 0.0d, 0.0d, 1.9d, 0.0d, 0.0d, 0.814d, -0.38d); + return true; + case 1242: + parameters = new EpsgExplicitOperationRecord(1242, 52.17d, -71.82d, -14.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1244: + parameters = new EpsgExplicitOperationRecord(1244, -1.08d, -0.27d, -0.9d, -0.0d, -0.0d, 0.16d, -0.12d); + return true; + case 1245: + parameters = new EpsgExplicitOperationRecord(1245, -112.0d, -77.0d, -145.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1246: + parameters = new EpsgExplicitOperationRecord(1246, -333.0d, -222.0d, 114.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1247: + parameters = new EpsgExplicitOperationRecord(1247, 283.0d, 682.0d, 231.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1248: + parameters = new EpsgExplicitOperationRecord(1248, -24.0d, -15.0d, 5.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1249: + parameters = new EpsgExplicitOperationRecord(1249, -2.0d, 152.0d, 149.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1250: + parameters = new EpsgExplicitOperationRecord(1250, 2.0d, 204.0d, 105.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1251: + parameters = new EpsgExplicitOperationRecord(1251, -2.0d, 0.0d, 4.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1252: + parameters = new EpsgExplicitOperationRecord(1252, 1.0d, 1.0d, -1.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1253: + parameters = new EpsgExplicitOperationRecord(1253, -186.0d, -93.0d, 310.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1254: + parameters = new EpsgExplicitOperationRecord(1254, 28.0d, -130.0d, -95.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1255: + parameters = new EpsgExplicitOperationRecord(1255, -123.0d, -206.0d, 219.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1256: + parameters = new EpsgExplicitOperationRecord(1256, -346.0d, -1.0d, 224.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1257: + parameters = new EpsgExplicitOperationRecord(1257, 25.9d, -130.94d, -81.76d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1267: + parameters = new EpsgExplicitOperationRecord(1267, 23.92d, -141.27d, -80.9d, -0.0d, 0.35d, 0.82d, -0.12d); + return true; + case 1271: + parameters = new EpsgExplicitOperationRecord(1271, 615.64d, 102.08d, -255.81d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1272: + parameters = new EpsgExplicitOperationRecord(1272, -199.87d, 74.79d, 246.62d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1274: + parameters = new EpsgExplicitOperationRecord(1274, -40.595d, -18.55d, -69.339d, 2.508d, 1.832d, -2.611d, -4.299d); + return true; + case 1275: + parameters = new EpsgExplicitOperationRecord(1275, -84.0d, -97.0d, -117.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1276: + parameters = new EpsgExplicitOperationRecord(1276, -84.0d, 37.0d, 437.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1277: + parameters = new EpsgExplicitOperationRecord(1277, -168.0d, -72.0d, 314.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1278: + parameters = new EpsgExplicitOperationRecord(1278, -127.8d, -52.3d, 152.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1279: + parameters = new EpsgExplicitOperationRecord(1279, -128.5d, -53.0d, 153.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1280: + parameters = new EpsgExplicitOperationRecord(1280, -117.763d, -51.51d, 139.061d, 0.292d, 0.443d, 0.277d, -0.191d); + return true; + case 1281: + parameters = new EpsgExplicitOperationRecord(1281, 24.82d, -131.21d, -82.66d, -0.0d, -0.0d, 0.16d, -0.12d); + return true; + case 1283: + parameters = new EpsgExplicitOperationRecord(1283, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1284: + parameters = new EpsgExplicitOperationRecord(1284, -157.0d, -2.0d, -299.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1285: + parameters = new EpsgExplicitOperationRecord(1285, -175.0d, -23.0d, -303.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1290: + parameters = new EpsgExplicitOperationRecord(1290, 24.0d, -124.0d, -82.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1291: + parameters = new EpsgExplicitOperationRecord(1291, 15.0d, -130.0d, -84.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1294: + parameters = new EpsgExplicitOperationRecord(1294, -73.0d, -247.0d, 227.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1296: + parameters = new EpsgExplicitOperationRecord(1296, -61.702d, 284.488d, 472.052d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1297: + parameters = new EpsgExplicitOperationRecord(1297, -115.064d, -87.39d, -101.716d, -0.058d, 4.001d, -2.062d, 9.366d); + return true; + case 1298: + parameters = new EpsgExplicitOperationRecord(1298, -82.875d, -57.097d, -156.768d, -2.158d, 1.524d, -0.982d, -0.359d); + return true; + case 1299: + parameters = new EpsgExplicitOperationRecord(1299, -138.527d, -91.999d, -114.591d, -0.14d, 3.363d, -2.217d, 11.748d); + return true; + case 1300: + parameters = new EpsgExplicitOperationRecord(1300, -73.472d, -51.66d, -112.482d, 0.953d, 4.6d, -2.368d, 0.586d); + return true; + case 1301: + parameters = new EpsgExplicitOperationRecord(1301, 219.315d, 168.975d, -166.145d, 0.198d, 5.926d, -2.356d, -57.104d); + return true; + case 1302: + parameters = new EpsgExplicitOperationRecord(1302, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 1303: + parameters = new EpsgExplicitOperationRecord(1303, 43.822d, -108.842d, -119.585d, 1.455d, -0.761d, 0.737d, 0.549d); + return true; + case 1304: + parameters = new EpsgExplicitOperationRecord(1304, 210.0d, 814.0d, 289.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1305: + parameters = new EpsgExplicitOperationRecord(1305, -147.0d, 506.0d, 687.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1307: + parameters = new EpsgExplicitOperationRecord(1307, -2.0d, 374.0d, 172.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1309: + parameters = new EpsgExplicitOperationRecord(1309, 582.0d, 105.0d, 414.0d, 1.04d, 0.35d, -3.08d, 8.3d); + return true; + case 1311: + parameters = new EpsgExplicitOperationRecord(1311, -89.5d, -93.8d, -123.1d, 0.0d, 0.0d, -0.156d, 1.2d); + return true; + case 1314: + parameters = new EpsgExplicitOperationRecord(1314, 446.448d, -125.157d, 542.06d, 0.15d, 0.247d, 0.842d, -20.489d); + return true; + case 1315: + parameters = new EpsgExplicitOperationRecord(1315, 535.948d, -31.357d, 665.16d, 0.15d, 0.247d, 0.998d, -21.689d); + return true; + case 1317: + parameters = new EpsgExplicitOperationRecord(1317, -37.2d, -370.6d, -228.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1318: + parameters = new EpsgExplicitOperationRecord(1318, -42.01d, -332.21d, -229.75d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1319: + parameters = new EpsgExplicitOperationRecord(1319, -40.0d, -354.0d, -224.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1320: + parameters = new EpsgExplicitOperationRecord(1320, -37.2d, -370.6d, -224.0d, 0.0d, 0.0d, 0.554d, 0.219d); + return true; + case 1321: + parameters = new EpsgExplicitOperationRecord(1321, -41.8d, -342.2d, -228.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1322: + parameters = new EpsgExplicitOperationRecord(1322, -55.5d, -348.0d, -229.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1323: + parameters = new EpsgExplicitOperationRecord(1323, -43.0d, -337.0d, -233.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1324: + parameters = new EpsgExplicitOperationRecord(1324, -48.0d, -345.0d, -231.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1325: + parameters = new EpsgExplicitOperationRecord(1325, -48.6d, -345.1d, -230.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1326: + parameters = new EpsgExplicitOperationRecord(1326, -41.057d, -374.564d, -226.287d, 0.0d, 0.0d, 0.554d, 0.219d); + return true; + case 1327: + parameters = new EpsgExplicitOperationRecord(1327, -50.9d, -347.6d, -231.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1330: + parameters = new EpsgExplicitOperationRecord(1330, -252.95d, -4.11d, -96.38d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1331: + parameters = new EpsgExplicitOperationRecord(1331, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 1332: + parameters = new EpsgExplicitOperationRecord(1332, 21.53219d, -97.00027d, -60.74046d, 0.99548d, 0.58147d, 0.2418d, -4.5981d); + return true; + case 1333: + parameters = new EpsgExplicitOperationRecord(1333, 0.055d, -0.541d, -0.185d, 0.0183d, -0.0003d, -0.007d, -0.014d); + return true; + case 1334: + parameters = new EpsgExplicitOperationRecord(1334, 21.58719d, -97.54127d, -60.92546d, 1.01378d, 0.58117d, 0.2348d, -4.6121d); + return true; + case 1437: + parameters = new EpsgExplicitOperationRecord(1437, 419.3836d, 99.3335d, 591.3451d, 0.850389d, 1.817277d, -7.862238d, -0.99496d); + return true; + case 1438: + parameters = new EpsgExplicitOperationRecord(1438, -333.102d, -11.02d, 230.69d, 0.0d, 0.0d, 0.554d, 0.219d); + return true; + case 1439: + parameters = new EpsgExplicitOperationRecord(1439, -180.624d, -225.516d, 173.919d, -0.81d, -1.898d, 8.336d, 16.71006d); + return true; + case 1440: + parameters = new EpsgExplicitOperationRecord(1440, -86.0d, -92.2d, -127.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1441: + parameters = new EpsgExplicitOperationRecord(1441, -255.0d, -15.0d, 71.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1442: + parameters = new EpsgExplicitOperationRecord(1442, 725.0d, 685.0d, 536.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1443: + parameters = new EpsgExplicitOperationRecord(1443, 72.0d, 213.7d, 93.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1444: + parameters = new EpsgExplicitOperationRecord(1444, 174.0d, 359.0d, 365.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1445: + parameters = new EpsgExplicitOperationRecord(1445, 9.0d, 183.0d, 236.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1446: + parameters = new EpsgExplicitOperationRecord(1446, -149.0d, 128.0d, 296.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1448: + parameters = new EpsgExplicitOperationRecord(1448, 52.684d, -71.194d, -13.975d, -0.312d, -0.1063d, -0.3729d, 1.0191d); + return true; + case 1449: + parameters = new EpsgExplicitOperationRecord(1449, 52.684d, -71.194d, -13.975d, -0.312d, -0.1063d, -0.3729d, 1.0191d); + return true; + case 1458: + parameters = new EpsgExplicitOperationRecord(1458, -129.193d, -41.212d, 130.73d, 0.246d, 0.374d, 0.329d, -2.955d); + return true; + case 1459: + parameters = new EpsgExplicitOperationRecord(1459, -120.695d, -62.73d, 165.46d, 0.109d, -0.141d, -0.116d, 2.733d); + return true; + case 1460: + parameters = new EpsgExplicitOperationRecord(1460, -119.353d, -48.301d, 139.484d, 0.415d, 0.26d, 0.437d, -0.613d); + return true; + case 1469: + parameters = new EpsgExplicitOperationRecord(1469, -125.0d, 53.0d, 467.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1470: + parameters = new EpsgExplicitOperationRecord(1470, -124.76d, 53.0d, 466.79d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1504: + parameters = new EpsgExplicitOperationRecord(1504, -134.73d, -110.92d, -292.66d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1505: + parameters = new EpsgExplicitOperationRecord(1505, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1509: + parameters = new EpsgExplicitOperationRecord(1509, 674.374d, 15.056d, 405.346d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1511: + parameters = new EpsgExplicitOperationRecord(1511, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1512: + parameters = new EpsgExplicitOperationRecord(1512, -133.63d, -157.5d, -158.62d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1513: + parameters = new EpsgExplicitOperationRecord(1513, -241.54d, -163.64d, 396.06d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1514: + parameters = new EpsgExplicitOperationRecord(1514, -110.33d, -97.73d, -119.85d, 0.3423d, 1.1634d, 0.2715d, 0.063d); + return true; + case 1516: + parameters = new EpsgExplicitOperationRecord(1516, -273.5d, 110.6d, -357.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1517: + parameters = new EpsgExplicitOperationRecord(1517, -23.0d, 259.0d, -9.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1518: + parameters = new EpsgExplicitOperationRecord(1518, -83.0d, 37.0d, 124.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1527: + parameters = new EpsgExplicitOperationRecord(1527, -154.5d, 150.7d, 100.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1528: + parameters = new EpsgExplicitOperationRecord(1528, 160.0d, 26.0d, 41.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1529: + parameters = new EpsgExplicitOperationRecord(1529, 18.38d, 192.45d, 96.82d, 0.056d, -0.142d, -0.2d, -0.0013d); + return true; + case 1530: + parameters = new EpsgExplicitOperationRecord(1530, -4.2d, 135.4d, 181.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1531: + parameters = new EpsgExplicitOperationRecord(1531, -245.0d, -153.9d, 382.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1532: + parameters = new EpsgExplicitOperationRecord(1532, -80.7d, -132.5d, 41.1d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1533: + parameters = new EpsgExplicitOperationRecord(1533, 214.0d, 804.0d, 268.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1536: + parameters = new EpsgExplicitOperationRecord(1536, -250.2d, -153.09d, 391.7d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1537: + parameters = new EpsgExplicitOperationRecord(1537, 204.64d, 834.74d, 293.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1538: + parameters = new EpsgExplicitOperationRecord(1538, -260.1d, 5.5d, 432.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1539: + parameters = new EpsgExplicitOperationRecord(1539, -76.0d, -138.0d, 67.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1540: + parameters = new EpsgExplicitOperationRecord(1540, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1541: + parameters = new EpsgExplicitOperationRecord(1541, 199.0d, 931.0d, 317.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1542: + parameters = new EpsgExplicitOperationRecord(1542, 198.0d, 881.0d, 317.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1543: + parameters = new EpsgExplicitOperationRecord(1543, 182.0d, 915.0d, 344.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1544: + parameters = new EpsgExplicitOperationRecord(1544, -17.51d, -108.32d, -62.39d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1545: + parameters = new EpsgExplicitOperationRecord(1545, -121.8d, 98.1d, -15.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1547: + parameters = new EpsgExplicitOperationRecord(1547, -173.0d, 253.0d, 27.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1550: + parameters = new EpsgExplicitOperationRecord(1550, -139.62d, 290.53d, -150.29d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1551: + parameters = new EpsgExplicitOperationRecord(1551, -141.15d, 293.44d, -150.56d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1552: + parameters = new EpsgExplicitOperationRecord(1552, -142.48d, 296.03d, -149.74d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1555: + parameters = new EpsgExplicitOperationRecord(1555, -0.465d, 372.095d, 171.736d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1556: + parameters = new EpsgExplicitOperationRecord(1556, -2.0d, 374.0d, 172.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1557: + parameters = new EpsgExplicitOperationRecord(1557, -254.1d, -5.36d, -100.29d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1558: + parameters = new EpsgExplicitOperationRecord(1558, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1560: + parameters = new EpsgExplicitOperationRecord(1560, -156.5d, -87.2d, 285.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1561: + parameters = new EpsgExplicitOperationRecord(1561, -128.0d, -283.0d, 22.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1562: + parameters = new EpsgExplicitOperationRecord(1562, -128.16d, -282.42d, 21.93d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1563: + parameters = new EpsgExplicitOperationRecord(1563, -128.033d, -283.697d, 21.052d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1564: + parameters = new EpsgExplicitOperationRecord(1564, 59.47d, -5.04d, 187.44d, 0.47d, -0.1d, 1.024d, -4.5993d); + return true; + case 1565: + parameters = new EpsgExplicitOperationRecord(1565, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1566: + parameters = new EpsgExplicitOperationRecord(1566, 54.4d, -20.1d, 183.1d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1569: + parameters = new EpsgExplicitOperationRecord(1569, -199.0d, 32.0d, 322.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1570: + parameters = new EpsgExplicitOperationRecord(1570, -171.16d, 17.29d, 323.31d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1577: + parameters = new EpsgExplicitOperationRecord(1577, -115.0d, 118.0d, 426.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1580: + parameters = new EpsgExplicitOperationRecord(1580, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1581: + parameters = new EpsgExplicitOperationRecord(1581, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1582: + parameters = new EpsgExplicitOperationRecord(1582, -259.73d, 173.12d, -398.27d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1583: + parameters = new EpsgExplicitOperationRecord(1583, -307.7d, 265.3d, -363.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1584: + parameters = new EpsgExplicitOperationRecord(1584, -174.6d, -3.1d, 236.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1586: + parameters = new EpsgExplicitOperationRecord(1586, -175.09d, 1.218d, 238.831d, -0.047d, 0.019d, 0.808d, 0.1698d); + return true; + case 1587: + parameters = new EpsgExplicitOperationRecord(1587, -191.77d, 15.01d, 235.07d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1588: + parameters = new EpsgExplicitOperationRecord(1588, -116.641d, -56.931d, -110.559d, 4.327d, 4.464d, -4.444d, -3.52d); + return true; + case 1592: + parameters = new EpsgExplicitOperationRecord(1592, -678.0d, 670.0d, -48.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1594: + parameters = new EpsgExplicitOperationRecord(1594, -120.271d, -64.543d, 161.632d, 0.217d, -0.067d, -0.129d, 2.499d); + return true; + case 1595: + parameters = new EpsgExplicitOperationRecord(1595, -124.133d, -42.003d, 137.4d, -0.008d, 0.557d, 0.178d, -1.854d); + return true; + case 1597: + parameters = new EpsgExplicitOperationRecord(1597, 304.5d, 306.5d, -318.1d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1609: + parameters = new EpsgExplicitOperationRecord(1609, -99.059d, 53.322d, -112.486d, 0.419d, -0.83d, 1.885d, -1.0d); + return true; + case 1610: + parameters = new EpsgExplicitOperationRecord(1610, -125.8d, 79.9d, -100.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1612: + parameters = new EpsgExplicitOperationRecord(1612, -116.641d, -56.931d, -110.559d, 0.893d, 0.921d, -0.917d, -3.52d); + return true; + case 1613: + parameters = new EpsgExplicitOperationRecord(1613, -90.365d, -101.13d, -123.384d, 0.333d, 0.077d, 0.894d, 1.994d); + return true; + case 1614: + parameters = new EpsgExplicitOperationRecord(1614, -88.0d, 4.0d, 101.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1615: + parameters = new EpsgExplicitOperationRecord(1615, -726.282d, 703.611d, -48.999d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1616: + parameters = new EpsgExplicitOperationRecord(1616, -182.046d, -225.604d, 168.884d, -0.616d, -1.655d, 7.824d, 16.641d); + return true; + case 1617: + parameters = new EpsgExplicitOperationRecord(1617, -191.808d, -250.512d, 167.861d, -0.792d, -1.653d, 8.558d, 20.703d); + return true; + case 1618: + parameters = new EpsgExplicitOperationRecord(1618, 577.326d, 90.129d, 463.919d, 5.137d, 1.474d, 5.297d, 2.4232d); + return true; + case 1619: + parameters = new EpsgExplicitOperationRecord(1619, 577.326d, 90.129d, 463.919d, 5.137d, 1.474d, 5.297d, 2.4232d); + return true; + case 1622: + parameters = new EpsgExplicitOperationRecord(1622, 570.8d, 85.7d, 462.8d, 4.998d, 1.587d, 5.261d, 3.56d); + return true; + case 1623: + parameters = new EpsgExplicitOperationRecord(1623, 570.8d, 85.7d, 462.8d, 4.998d, 1.587d, 5.261d, 3.56d); + return true; + case 1626: + parameters = new EpsgExplicitOperationRecord(1626, -81.1d, -89.4d, -115.8d, 0.485d, 0.024d, 0.413d, -0.54d); + return true; + case 1627: + parameters = new EpsgExplicitOperationRecord(1627, -81.1d, -89.4d, -115.8d, 0.485d, 0.024d, 0.413d, -0.54d); + return true; + case 1628: + parameters = new EpsgExplicitOperationRecord(1628, -116.8d, -106.4d, -154.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1629: + parameters = new EpsgExplicitOperationRecord(1629, -116.8d, -106.4d, -154.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1630: + parameters = new EpsgExplicitOperationRecord(1630, -181.5d, -90.3d, -187.2d, 0.144d, 0.492d, -0.394d, 17.57d); + return true; + case 1631: + parameters = new EpsgExplicitOperationRecord(1631, -181.5d, -90.3d, -187.2d, 0.144d, 0.492d, -0.394d, 17.57d); + return true; + case 1632: + parameters = new EpsgExplicitOperationRecord(1632, -131.0d, -100.3d, -163.4d, -1.244d, -0.02d, -1.144d, 9.39d); + return true; + case 1633: + parameters = new EpsgExplicitOperationRecord(1633, -131.0d, -100.3d, -163.4d, -1.244d, -0.02d, -1.144d, 9.39d); + return true; + case 1634: + parameters = new EpsgExplicitOperationRecord(1634, -178.4d, -83.2d, -221.3d, 0.54d, -0.532d, -0.126d, 21.2d); + return true; + case 1635: + parameters = new EpsgExplicitOperationRecord(1635, -178.4d, -83.2d, -221.3d, 0.54d, -0.532d, -0.126d, 21.2d); + return true; + case 1638: + parameters = new EpsgExplicitOperationRecord(1638, -90.7d, -106.1d, -119.2d, 4.09d, 0.218d, -1.05d, 1.37d); + return true; + case 1639: + parameters = new EpsgExplicitOperationRecord(1639, -90.7d, -106.1d, -119.2d, 4.09d, 0.218d, -1.05d, 1.37d); + return true; + case 1641: + parameters = new EpsgExplicitOperationRecord(1641, 482.5d, -130.6d, 564.6d, -1.042d, -0.214d, -0.631d, 8.15d); + return true; + case 1642: + parameters = new EpsgExplicitOperationRecord(1642, -193.0d, 13.7d, -39.3d, -0.41d, -2.933d, 2.688d, 0.43d); + return true; + case 1643: + parameters = new EpsgExplicitOperationRecord(1643, -193.0d, 13.7d, -39.3d, -0.41d, -2.933d, 2.688d, 0.43d); + return true; + case 1644: + parameters = new EpsgExplicitOperationRecord(1644, 33.4d, -146.6d, -76.3d, -0.359d, -0.053d, 0.844d, -0.84d); + return true; + case 1645: + parameters = new EpsgExplicitOperationRecord(1645, 33.4d, -146.6d, -76.3d, -0.359d, -0.053d, 0.844d, -0.84d); + return true; + case 1646: + parameters = new EpsgExplicitOperationRecord(1646, 674.374d, 15.056d, 405.346d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1647: + parameters = new EpsgExplicitOperationRecord(1647, 674.374d, 15.056d, 405.346d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1649: + parameters = new EpsgExplicitOperationRecord(1649, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1650: + parameters = new EpsgExplicitOperationRecord(1650, -84.0d, -97.0d, -117.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1651: + parameters = new EpsgExplicitOperationRecord(1651, -168.0d, -60.0d, 320.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1652: + parameters = new EpsgExplicitOperationRecord(1652, -99.1d, 53.3d, -112.5d, 0.419d, -0.83d, 1.885d, -1.0d); + return true; + case 1653: + parameters = new EpsgExplicitOperationRecord(1653, 278.3d, 93.0d, 474.5d, 7.889d, 0.05d, -6.61d, 6.21d); + return true; + case 1654: + parameters = new EpsgExplicitOperationRecord(1654, 278.3d, 93.0d, 474.5d, 7.889d, 0.05d, -6.61d, 6.21d); + return true; + case 1655: + parameters = new EpsgExplicitOperationRecord(1655, -280.9d, -89.8d, 130.2d, -1.721d, 0.355d, -0.371d, -5.92d); + return true; + case 1656: + parameters = new EpsgExplicitOperationRecord(1656, -280.9d, -89.8d, 130.2d, -1.721d, 0.355d, -0.371d, -5.92d); + return true; + case 1657: + parameters = new EpsgExplicitOperationRecord(1657, -238.2d, 85.2d, 29.9d, 0.166d, 0.046d, 1.248d, 2.03d); + return true; + case 1658: + parameters = new EpsgExplicitOperationRecord(1658, -238.2d, 85.2d, 29.9d, 0.166d, 0.046d, 1.248d, 2.03d); + return true; + case 1659: + parameters = new EpsgExplicitOperationRecord(1659, -104.1d, -49.1d, -9.9d, 0.971d, -2.917d, 0.714d, -11.68d); + return true; + case 1660: + parameters = new EpsgExplicitOperationRecord(1660, -104.1d, -49.1d, -9.9d, 0.971d, -2.917d, 0.714d, -11.68d); + return true; + case 1661: + parameters = new EpsgExplicitOperationRecord(1661, -168.6d, -34.0d, 38.6d, -0.374d, -0.679d, -1.379d, -9.48d); + return true; + case 1662: + parameters = new EpsgExplicitOperationRecord(1662, -168.6d, -34.0d, 38.6d, -0.374d, -0.679d, -1.379d, -9.48d); + return true; + case 1663: + parameters = new EpsgExplicitOperationRecord(1663, -50.2d, -50.4d, 84.8d, -0.69d, -2.012d, 0.459d, -28.08d); + return true; + case 1664: + parameters = new EpsgExplicitOperationRecord(1664, -50.2d, -50.4d, 84.8d, -0.69d, -2.012d, 0.459d, -28.08d); + return true; + case 1665: + parameters = new EpsgExplicitOperationRecord(1665, -129.193d, -41.212d, 130.73d, 0.246d, 0.374d, 0.329d, -2.955d); + return true; + case 1666: + parameters = new EpsgExplicitOperationRecord(1666, -119.353d, -48.301d, 139.484d, 0.415d, 0.26d, 0.437d, -0.613d); + return true; + case 1667: + parameters = new EpsgExplicitOperationRecord(1667, -120.271d, -64.543d, 161.632d, 0.217d, -0.067d, -0.129d, 2.499d); + return true; + case 1668: + parameters = new EpsgExplicitOperationRecord(1668, -124.133d, -42.003d, 137.4d, -0.008d, 0.557d, 0.178d, -1.854d); + return true; + case 1669: + parameters = new EpsgExplicitOperationRecord(1669, -117.763d, -51.51d, 139.061d, 0.292d, 0.443d, 0.277d, -0.191d); + return true; + case 1671: + parameters = new EpsgExplicitOperationRecord(1671, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1672: + parameters = new EpsgExplicitOperationRecord(1672, 565.04d, 49.91d, 465.84d, -1.9848d, 1.7439d, -9.0587d, 4.0772d); + return true; + case 1673: + parameters = new EpsgExplicitOperationRecord(1673, 582.0d, 105.0d, 414.0d, 1.04d, 0.35d, -3.08d, 8.3d); + return true; + case 1674: + parameters = new EpsgExplicitOperationRecord(1674, 24.0d, -123.0d, -94.0d, 0.02d, -0.25d, -0.13d, 1.1d); + return true; + case 1675: + parameters = new EpsgExplicitOperationRecord(1675, 24.0d, -123.0d, -94.0d, 0.02d, -0.25d, -0.13d, 1.1d); + return true; + case 1676: + parameters = new EpsgExplicitOperationRecord(1676, 674.374d, 15.056d, 405.346d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1678: + parameters = new EpsgExplicitOperationRecord(1678, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1679: + parameters = new EpsgExplicitOperationRecord(1679, -40.595d, -18.55d, -69.339d, 2.508d, 1.832d, -2.611d, -4.299d); + return true; + case 1680: + parameters = new EpsgExplicitOperationRecord(1680, 419.3836d, 99.3335d, 591.3451d, 0.850389d, 1.817277d, -7.862238d, -0.99496d); + return true; + case 1682: + parameters = new EpsgExplicitOperationRecord(1682, -76.0d, -138.0d, 67.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1683: + parameters = new EpsgExplicitOperationRecord(1683, -115.064d, -87.39d, -101.716d, -0.058d, 4.001d, -2.062d, 9.366d); + return true; + case 1684: + parameters = new EpsgExplicitOperationRecord(1684, -82.875d, -57.097d, -156.768d, -2.158d, 1.524d, -0.982d, -0.359d); + return true; + case 1685: + parameters = new EpsgExplicitOperationRecord(1685, -138.527d, -91.999d, -114.591d, -0.14d, 3.363d, -2.217d, 11.748d); + return true; + case 1686: + parameters = new EpsgExplicitOperationRecord(1686, -73.472d, -51.66d, -112.482d, 0.953d, 4.6d, -2.368d, 0.586d); + return true; + case 1687: + parameters = new EpsgExplicitOperationRecord(1687, 219.315d, 168.975d, -166.145d, 0.198d, 5.926d, -2.356d, -57.104d); + return true; + case 1701: + parameters = new EpsgExplicitOperationRecord(1701, 59.47d, -5.04d, 187.44d, 0.47d, -0.1d, 1.024d, -4.5993d); + return true; + case 1751: + parameters = new EpsgExplicitOperationRecord(1751, 565.04d, 49.91d, 465.84d, -1.9848d, 1.7439d, -9.0587d, 4.0772d); + return true; + case 1753: + parameters = new EpsgExplicitOperationRecord(1753, 660.077d, 13.551d, 369.344d, -2.484d, -1.783d, -2.939d, 5.66d); + return true; + case 1754: + parameters = new EpsgExplicitOperationRecord(1754, -111.92d, -87.85d, 114.5d, 1.875d, 0.202d, 0.219d, 0.032d); + return true; + case 1766: + parameters = new EpsgExplicitOperationRecord(1766, 674.374d, 15.056d, 405.346d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1767: + parameters = new EpsgExplicitOperationRecord(1767, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1768: + parameters = new EpsgExplicitOperationRecord(1768, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1769: + parameters = new EpsgExplicitOperationRecord(1769, -270.933d, 115.599d, -360.226d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1771: + parameters = new EpsgExplicitOperationRecord(1771, -270.933d, 115.599d, -360.226d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1773: + parameters = new EpsgExplicitOperationRecord(1773, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1774: + parameters = new EpsgExplicitOperationRecord(1774, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1775: + parameters = new EpsgExplicitOperationRecord(1775, 24.9d, -126.4d, -93.2d, -0.063d, -0.247d, -0.041d, 1.01d); + return true; + case 1776: + parameters = new EpsgExplicitOperationRecord(1776, 598.1d, 73.7d, 418.2d, 0.202d, 0.045d, -2.455d, 6.7d); + return true; + case 1777: + parameters = new EpsgExplicitOperationRecord(1777, 598.1d, 73.7d, 418.2d, 0.202d, 0.045d, -2.455d, 6.7d); + return true; + case 1778: + parameters = new EpsgExplicitOperationRecord(1778, 597.1d, 71.4d, 412.1d, 0.894d, 0.068d, -1.563d, 7.58d); + return true; + case 1779: + parameters = new EpsgExplicitOperationRecord(1779, 584.8d, 67.0d, 400.3d, 0.105d, 0.013d, -2.378d, 10.29d); + return true; + case 1780: + parameters = new EpsgExplicitOperationRecord(1780, 590.5d, 69.5d, 411.6d, -0.796d, -0.052d, -3.601d, 8.3d); + return true; + case 1783: + parameters = new EpsgExplicitOperationRecord(1783, -84.1d, -101.8d, -129.7d, 0.0d, 0.0d, 0.468d, 1.05d); + return true; + case 1784: + parameters = new EpsgExplicitOperationRecord(1784, -84.1d, -101.8d, -129.7d, 0.0d, 0.0d, 0.468d, 1.05d); + return true; + case 1796: + parameters = new EpsgExplicitOperationRecord(1796, -70.9d, -151.8d, -41.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1797: + parameters = new EpsgExplicitOperationRecord(1797, 164.0d, 138.0d, -189.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1798: + parameters = new EpsgExplicitOperationRecord(1798, 163.511d, 127.533d, -159.789d, 0.0d, 0.0d, 0.814d, -0.6d); + return true; + case 1799: + parameters = new EpsgExplicitOperationRecord(1799, 105.0d, 326.0d, -102.5d, 0.0d, 0.0d, 0.814d, -0.6d); + return true; + case 1800: + parameters = new EpsgExplicitOperationRecord(1800, -45.0d, 417.0d, -3.5d, 0.0d, 0.0d, 0.814d, -0.6d); + return true; + case 1801: + parameters = new EpsgExplicitOperationRecord(1801, -145.0d, 52.7d, -291.6d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1802: + parameters = new EpsgExplicitOperationRecord(1802, -178.3d, -316.7d, -131.5d, 5.278d, 6.077d, 10.979d, 19.166d); + return true; + case 1805: + parameters = new EpsgExplicitOperationRecord(1805, -56.1d, -167.8d, 13.1d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1806: + parameters = new EpsgExplicitOperationRecord(1806, -104.4d, -136.6d, 201.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1807: + parameters = new EpsgExplicitOperationRecord(1807, 27.0d, -135.0d, -84.5d, 0.0d, 0.0d, 0.554d, 0.2263d); + return true; + case 1808: + parameters = new EpsgExplicitOperationRecord(1808, 686.1d, -123.5d, -574.4d, 8.045d, -23.366d, 10.791d, -2.926d); + return true; + case 1809: + parameters = new EpsgExplicitOperationRecord(1809, 926.4d, -715.9d, -186.4d, -10.364d, -20.78d, 26.452d, -7.224d); + return true; + case 1810: + parameters = new EpsgExplicitOperationRecord(1810, -84.0d, -103.0d, -122.5d, 0.0d, 0.0d, 0.554d, 0.2263d); + return true; + case 1811: + parameters = new EpsgExplicitOperationRecord(1811, -291.87d, 106.37d, -364.52d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1812: + parameters = new EpsgExplicitOperationRecord(1812, 293.0d, 836.0d, 318.0d, 0.5d, 1.6d, -2.8d, 2.1d); + return true; + case 1813: + parameters = new EpsgExplicitOperationRecord(1813, -378.873d, 676.002d, -46.255d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1814: + parameters = new EpsgExplicitOperationRecord(1814, -377.7d, 675.1d, -52.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1815: + parameters = new EpsgExplicitOperationRecord(1815, -152.9d, 43.8d, 358.3d, 2.714d, 1.386d, -2.788d, -6.743d); + return true; + case 1816: + parameters = new EpsgExplicitOperationRecord(1816, -95.7d, 10.2d, 158.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1817: + parameters = new EpsgExplicitOperationRecord(1817, -165.914d, -70.607d, 305.009d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1818: + parameters = new EpsgExplicitOperationRecord(1818, -89.0d, -112.0d, 125.9d, 0.0d, 0.0d, 0.814d, -0.38d); + return true; + case 1820: + parameters = new EpsgExplicitOperationRecord(1820, -93.2d, -93.31d, 121.156d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1821: + parameters = new EpsgExplicitOperationRecord(1821, -88.98d, -83.23d, 113.55d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1822: + parameters = new EpsgExplicitOperationRecord(1822, -92.726d, -90.304d, 115.735d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1823: + parameters = new EpsgExplicitOperationRecord(1823, -93.134d, -86.647d, 114.196d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1824: + parameters = new EpsgExplicitOperationRecord(1824, -93.0d, -94.0d, 124.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1825: + parameters = new EpsgExplicitOperationRecord(1825, -162.619d, -276.959d, -161.764d, 0.067753d, -2.243648d, -1.158828d, -1.094246d); + return true; + case 1826: + parameters = new EpsgExplicitOperationRecord(1826, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1828: + parameters = new EpsgExplicitOperationRecord(1828, -37.0d, 157.0d, 85.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1829: + parameters = new EpsgExplicitOperationRecord(1829, 56.0d, -75.77d, -15.31d, -0.37d, -0.2d, -0.21d, 1.01d); + return true; + case 1830: + parameters = new EpsgExplicitOperationRecord(1830, 56.0d, -75.77d, -15.31d, -0.37d, -0.2d, -0.21d, 1.01d); + return true; + case 1831: + parameters = new EpsgExplicitOperationRecord(1831, 57.01d, -69.97d, -9.29d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1832: + parameters = new EpsgExplicitOperationRecord(1832, 2.691d, -14.757d, 4.724d, 0.0d, 0.0d, 0.774d, -0.6d); + return true; + case 1833: + parameters = new EpsgExplicitOperationRecord(1833, -1.977d, -13.06d, -9.993d, 0.364d, 0.254d, 0.689d, -1.037d); + return true; + case 1837: + parameters = new EpsgExplicitOperationRecord(1837, -587.8d, 519.75d, 145.76d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1838: + parameters = new EpsgExplicitOperationRecord(1838, -404.78d, 685.68d, 45.47d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1839: + parameters = new EpsgExplicitOperationRecord(1839, -101.0d, -111.0d, 187.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1840: + parameters = new EpsgExplicitOperationRecord(1840, -119.4248d, -303.65872d, -11.00061d, 1.164298d, 0.174458d, 1.096259d, 3.657065d); + return true; + case 1842: + parameters = new EpsgExplicitOperationRecord(1842, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1852: + parameters = new EpsgExplicitOperationRecord(1852, -533.4d, 669.2d, -52.5d, 0.0d, 0.0d, 4.28d, 9.4d); + return true; + case 1853: + parameters = new EpsgExplicitOperationRecord(1853, -82.31d, -95.23d, -114.96d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1854: + parameters = new EpsgExplicitOperationRecord(1854, -239.1d, -170.02d, 397.5d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1855: + parameters = new EpsgExplicitOperationRecord(1855, -244.72d, -162.773d, 400.75d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1856: + parameters = new EpsgExplicitOperationRecord(1856, -122.89d, -159.08d, -168.74d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1857: + parameters = new EpsgExplicitOperationRecord(1857, -84.78d, -107.55d, -137.25d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1858: + parameters = new EpsgExplicitOperationRecord(1858, -123.92d, -155.515d, -157.721d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1859: + parameters = new EpsgExplicitOperationRecord(1859, -69.06d, -90.71d, -142.56d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1860: + parameters = new EpsgExplicitOperationRecord(1860, -113.997d, -97.076d, -152.312d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1861: + parameters = new EpsgExplicitOperationRecord(1861, -114.5d, -96.1d, -151.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1862: + parameters = new EpsgExplicitOperationRecord(1862, -194.513d, -63.978d, -25.759d, -3.4027d, 3.756d, -3.352d, -0.9175d); + return true; + case 1863: + parameters = new EpsgExplicitOperationRecord(1863, -389.691d, 64.502d, 210.209d, 0.086d, 14.314d, -6.39d, 0.9264d); + return true; + case 1864: + parameters = new EpsgExplicitOperationRecord(1864, -57.0d, 1.0d, -41.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1865: + parameters = new EpsgExplicitOperationRecord(1865, -62.0d, -1.0d, -37.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1866: + parameters = new EpsgExplicitOperationRecord(1866, -61.0d, 2.0d, -48.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1867: + parameters = new EpsgExplicitOperationRecord(1867, -60.0d, -2.0d, -41.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1868: + parameters = new EpsgExplicitOperationRecord(1868, -75.0d, -1.0d, -44.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1869: + parameters = new EpsgExplicitOperationRecord(1869, -44.0d, 6.0d, -36.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1870: + parameters = new EpsgExplicitOperationRecord(1870, -48.0d, 3.0d, -44.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1871: + parameters = new EpsgExplicitOperationRecord(1871, -47.0d, 26.0d, -42.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1872: + parameters = new EpsgExplicitOperationRecord(1872, -53.0d, 3.0d, -47.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1873: + parameters = new EpsgExplicitOperationRecord(1873, -61.0d, 2.0d, -33.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1874: + parameters = new EpsgExplicitOperationRecord(1874, -58.0d, 0.0d, -44.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1875: + parameters = new EpsgExplicitOperationRecord(1875, -45.0d, 12.0d, -33.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1876: + parameters = new EpsgExplicitOperationRecord(1876, -45.0d, 8.0d, -33.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1877: + parameters = new EpsgExplicitOperationRecord(1877, -66.87d, 4.37d, -38.52d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1879: + parameters = new EpsgExplicitOperationRecord(1879, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1880: + parameters = new EpsgExplicitOperationRecord(1880, -106.0d, -129.0d, 165.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1885: + parameters = new EpsgExplicitOperationRecord(1885, -203.0d, 141.0d, 53.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1886: + parameters = new EpsgExplicitOperationRecord(1886, -104.0d, 167.0d, -38.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1887: + parameters = new EpsgExplicitOperationRecord(1887, -425.0d, -169.0d, 81.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1888: + parameters = new EpsgExplicitOperationRecord(1888, -499.0d, -249.0d, 314.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1890: + parameters = new EpsgExplicitOperationRecord(1890, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1892: + parameters = new EpsgExplicitOperationRecord(1892, 16.0d, 196.0d, 93.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1893: + parameters = new EpsgExplicitOperationRecord(1893, 11.0d, 72.0d, -101.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1895: + parameters = new EpsgExplicitOperationRecord(1895, 414.1d, 41.3d, 603.1d, -0.855d, 2.141d, -7.023d, 0.0d); + return true; + case 1896: + parameters = new EpsgExplicitOperationRecord(1896, 414.1d, 41.3d, 603.1d, -0.855d, 2.141d, -7.023d, 0.0d); + return true; + case 1897: + parameters = new EpsgExplicitOperationRecord(1897, -403.0d, 684.0d, 41.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1898: + parameters = new EpsgExplicitOperationRecord(1898, -387.06d, 636.53d, 46.29d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1899: + parameters = new EpsgExplicitOperationRecord(1899, -403.4d, 681.12d, 46.56d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1900: + parameters = new EpsgExplicitOperationRecord(1900, -0.9738d, 1.9453d, 0.5486d, 1.3357e-07d, 4.872e-08d, 5.507e-08d, 0.0d); + return true; + case 1901: + parameters = new EpsgExplicitOperationRecord(1901, -0.991d, 1.9072d, 0.5129d, 1.25033e-07d, 4.6785e-08d, 5.6529e-08d, 0.0d); + return true; + case 1902: + parameters = new EpsgExplicitOperationRecord(1902, -56.7d, -171.8d, -40.6d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1903: + parameters = new EpsgExplicitOperationRecord(1903, 137.0d, 248.0d, -430.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1904: + parameters = new EpsgExplicitOperationRecord(1904, -467.0d, -16.0d, -300.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1905: + parameters = new EpsgExplicitOperationRecord(1905, -472.29d, -5.63d, -304.12d, 0.4362d, -0.8374d, 0.2563d, 1.8984d); + return true; + case 1906: + parameters = new EpsgExplicitOperationRecord(1906, -186.0d, 230.0d, 110.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1908: + parameters = new EpsgExplicitOperationRecord(1908, -193.066d, 236.993d, 105.447d, 0.4814d, -0.8074d, 0.1276d, 1.5649d); + return true; + case 1909: + parameters = new EpsgExplicitOperationRecord(1909, 186.0d, 482.0d, 151.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1910: + parameters = new EpsgExplicitOperationRecord(1910, 126.93d, 547.94d, 130.41d, -2.7867d, 5.1612d, -0.8584d, 13.8227d); + return true; + case 1912: + parameters = new EpsgExplicitOperationRecord(1912, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1913: + parameters = new EpsgExplicitOperationRecord(1913, 65.0d, 342.0d, 77.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1914: + parameters = new EpsgExplicitOperationRecord(1914, 84.0d, 274.0d, 65.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1916: + parameters = new EpsgExplicitOperationRecord(1916, -382.0d, -59.0d, -262.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1917: + parameters = new EpsgExplicitOperationRecord(1917, 336.0d, 223.0d, -231.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1921: + parameters = new EpsgExplicitOperationRecord(1921, 365.0d, 194.0d, 166.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1922: + parameters = new EpsgExplicitOperationRecord(1922, 325.0d, 154.0d, 172.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1923: + parameters = new EpsgExplicitOperationRecord(1923, 30.0d, 430.0d, 368.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1924: + parameters = new EpsgExplicitOperationRecord(1924, 162.0d, 117.0d, 154.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1926: + parameters = new EpsgExplicitOperationRecord(1926, 789.524d, -626.486d, -89.904d, 0.6006d, 76.7946d, -10.5788d, -32.3241d); + return true; + case 1927: + parameters = new EpsgExplicitOperationRecord(1927, 137.092d, 131.66d, 91.475d, -1.9436d, -11.5993d, -4.3321d, -7.4824d); + return true; + case 1928: + parameters = new EpsgExplicitOperationRecord(1928, -408.809d, 366.856d, -412.987d, 1.8842d, -0.5308d, 2.1655d, -121.0993d); + return true; + case 1931: + parameters = new EpsgExplicitOperationRecord(1931, -480.26d, -438.32d, -643.429d, 16.3119d, 20.1721d, -4.0349d, -111.7002d); + return true; + case 1946: + parameters = new EpsgExplicitOperationRecord(1946, -0.991d, 1.9072d, 0.5129d, 1.25033e-07d, 4.6785e-08d, 5.6529e-08d, 0.0d); + return true; + case 1950: + parameters = new EpsgExplicitOperationRecord(1950, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1951: + parameters = new EpsgExplicitOperationRecord(1951, -73.0d, 46.0d, -86.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1952: + parameters = new EpsgExplicitOperationRecord(1952, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1953: + parameters = new EpsgExplicitOperationRecord(1953, 482.5d, -130.6d, 564.6d, -1.042d, -0.214d, -0.631d, 8.15d); + return true; + case 1954: + parameters = new EpsgExplicitOperationRecord(1954, 482.5d, -130.6d, 564.6d, -1.042d, -0.214d, -0.631d, 8.15d); + return true; + case 1955: + parameters = new EpsgExplicitOperationRecord(1955, 482.5d, -130.6d, 564.6d, -1.042d, -0.214d, -0.631d, 8.15d); + return true; + case 1956: + parameters = new EpsgExplicitOperationRecord(1956, 506.0d, -122.0d, 611.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1957: + parameters = new EpsgExplicitOperationRecord(1957, 982.6087d, 552.753d, -540.873d, 32.39344d, -153.25684d, -96.2266d, 16.805d); + return true; + case 1958: + parameters = new EpsgExplicitOperationRecord(1958, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1959: + parameters = new EpsgExplicitOperationRecord(1959, 195.671d, 332.517d, 274.607d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1962: + parameters = new EpsgExplicitOperationRecord(1962, -13.0d, -348.0d, 292.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1963: + parameters = new EpsgExplicitOperationRecord(1963, 97.295d, -263.247d, 310.882d, -1.5999d, 0.8386d, 3.1409d, 13.3259d); + return true; + case 1964: + parameters = new EpsgExplicitOperationRecord(1964, -789.99d, 627.333d, 89.685d, -0.6072d, -76.8019d, 10.568d, 32.2083d); + return true; + case 1965: + parameters = new EpsgExplicitOperationRecord(1965, -289.0d, -124.0d, 60.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1966: + parameters = new EpsgExplicitOperationRecord(1966, -502.862d, -247.438d, 312.724d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1967: + parameters = new EpsgExplicitOperationRecord(1967, -210.502d, -66.902d, -48.476d, 2.094d, -15.067d, -5.817d, 0.485d); + return true; + case 1968: + parameters = new EpsgExplicitOperationRecord(1968, -204.633d, 140.216d, 55.199d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1969: + parameters = new EpsgExplicitOperationRecord(1969, -211.939d, 137.626d, 58.3d, -0.089d, 0.251d, 0.079d, 0.384d); + return true; + case 1970: + parameters = new EpsgExplicitOperationRecord(1970, -204.619d, 140.176d, 55.226d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1971: + parameters = new EpsgExplicitOperationRecord(1971, -208.719d, 129.685d, 52.092d, -0.195d, -0.014d, 0.327d, 0.198d); + return true; + case 1972: + parameters = new EpsgExplicitOperationRecord(1972, -106.301d, 166.27d, -37.916d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1973: + parameters = new EpsgExplicitOperationRecord(1973, -105.854d, 165.589d, -38.312d, -0.003d, -0.026d, 0.024d, -0.048d); + return true; + case 1974: + parameters = new EpsgExplicitOperationRecord(1974, -106.248d, 166.244d, -37.845d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1975: + parameters = new EpsgExplicitOperationRecord(1975, -104.0d, 162.924d, -38.882d, -0.075d, -0.071d, 0.051d, -0.338d); + return true; + case 1976: + parameters = new EpsgExplicitOperationRecord(1976, -106.044d, 166.655d, -37.876d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1977: + parameters = new EpsgExplicitOperationRecord(1977, -95.323d, 166.098d, -69.942d, -0.215d, -1.031d, 0.047d, 1.922d); + return true; + case 1978: + parameters = new EpsgExplicitOperationRecord(1978, -106.253d, 166.239d, -37.854d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1979: + parameters = new EpsgExplicitOperationRecord(1979, -100.306d, 161.246d, -48.761d, -0.192d, -0.385d, 0.076d, 0.131d); + return true; + case 1980: + parameters = new EpsgExplicitOperationRecord(1980, -106.226d, 166.366d, -37.893d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1981: + parameters = new EpsgExplicitOperationRecord(1981, -103.088d, 162.481d, -28.276d, 0.167d, 0.082d, 0.168d, -1.504d); + return true; + case 1982: + parameters = new EpsgExplicitOperationRecord(1982, -422.651d, -172.995d, 84.02d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1983: + parameters = new EpsgExplicitOperationRecord(1983, -223.237d, 110.193d, 36.649d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1984: + parameters = new EpsgExplicitOperationRecord(1984, -304.046d, -60.576d, 103.64d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1985: + parameters = new EpsgExplicitOperationRecord(1985, -87.987d, -108.639d, -121.593d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1986: + parameters = new EpsgExplicitOperationRecord(1986, 508.088d, -191.042d, 565.223d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1987: + parameters = new EpsgExplicitOperationRecord(1987, -239.749d, 88.181d, 30.488d, 0.263d, 0.082d, 1.211d, 2.229d); + return true; + case 1988: + parameters = new EpsgExplicitOperationRecord(1988, -288.885d, -91.744d, 126.244d, -1.691d, 0.41d, -0.211d, -4.598d); + return true; + case 1989: + parameters = new EpsgExplicitOperationRecord(1989, -74.292d, -135.889d, -104.967d, -0.524d, -0.136d, 0.61d, -3.761d); + return true; + case 1990: + parameters = new EpsgExplicitOperationRecord(1990, 631.392d, -66.551d, 481.442d, 1.09d, -4.445d, -4.487d, -4.43d); + return true; + case 1992: + parameters = new EpsgExplicitOperationRecord(1992, -231.034d, 102.615d, 26.836d, 0.615d, -0.198d, 0.881d, 1.786d); + return true; + case 1993: + parameters = new EpsgExplicitOperationRecord(1993, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1994: + parameters = new EpsgExplicitOperationRecord(1994, -28.0d, 199.0d, 5.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1995: + parameters = new EpsgExplicitOperationRecord(1995, 103.25d, -100.4d, -307.19d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 1997: + parameters = new EpsgExplicitOperationRecord(1997, -282.1d, -72.2d, 120.0d, -1.529d, 0.145d, -0.89d, -4.46d); + return true; + case 1998: + parameters = new EpsgExplicitOperationRecord(1998, -157.89d, -17.16d, -78.41d, 2.118d, 2.697d, -1.434d, -5.38d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket3(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 3817: + parameters = new EpsgExplicitOperationRecord(3817, 595.48d, 121.69d, 515.35d, 4.115d, -2.9383d, 0.853d, -3.408d); + return true; + case 3830: + parameters = new EpsgExplicitOperationRecord(3830, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 3894: + parameters = new EpsgExplicitOperationRecord(3894, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 3904: + parameters = new EpsgExplicitOperationRecord(3904, -83.11d, -97.38d, -117.22d, 0.0276d, -0.2167d, 0.2147d, 0.1218d); + return true; + case 3905: + parameters = new EpsgExplicitOperationRecord(3905, -83.11d, -97.38d, -117.22d, 0.0276d, -0.2167d, 0.2147d, 0.1218d); + return true; + case 3914: + parameters = new EpsgExplicitOperationRecord(3914, 426.9d, 142.6d, 460.1d, 4.91d, 4.49d, -12.42d, 17.1d); + return true; + case 3915: + parameters = new EpsgExplicitOperationRecord(3915, 426.9d, 142.6d, 460.1d, 4.91d, 4.49d, -12.42d, 17.1d); + return true; + case 3916: + parameters = new EpsgExplicitOperationRecord(3916, 409.545d, 72.164d, 486.872d, 3.085957d, 5.46911d, -11.020289d, 17.919665d); + return true; + case 3917: + parameters = new EpsgExplicitOperationRecord(3917, 409.545d, 72.164d, 486.872d, 3.085957d, 5.46911d, -11.020289d, 17.919665d); + return true; + case 3918: + parameters = new EpsgExplicitOperationRecord(3918, 315.393d, 186.223d, 499.609d, 6.445954d, 8.131631d, -13.208641d, 23.449046d); + return true; + case 3919: + parameters = new EpsgExplicitOperationRecord(3919, 464.939d, -21.478d, 504.497d, -0.403d, 4.228747d, -9.954942d, 12.795378d); + return true; + case 3921: + parameters = new EpsgExplicitOperationRecord(3921, 459.968d, 82.193d, 458.756d, 3.565234d, 3.700593d, -10.860523d, 15.507563d); + return true; + case 3922: + parameters = new EpsgExplicitOperationRecord(3922, 427.914d, 105.528d, 510.908d, 4.992523d, 5.898813d, -10.306673d, 12.431493d); + return true; + case 3923: + parameters = new EpsgExplicitOperationRecord(3923, 468.63d, 81.389d, 445.221d, 3.839242d, 3.262525d, -10.566866d, 16.132726d); + return true; + case 3924: + parameters = new EpsgExplicitOperationRecord(3924, 439.5d, -11.77d, 494.976d, 0.026585d, 4.65641d, -10.155824d, 16.270002d); + return true; + case 3925: + parameters = new EpsgExplicitOperationRecord(3925, 524.442d, 3.275d, 519.002d, -0.013287d, 3.119714d, -10.232693d, 4.184981d); + return true; + case 3926: + parameters = new EpsgExplicitOperationRecord(3926, 281.529d, 45.963d, 537.515d, 2.570437d, 9.648271d, -10.759507d, 26.465548d); + return true; + case 3927: + parameters = new EpsgExplicitOperationRecord(3927, 355.845d, 274.282d, 462.979d, 9.086933d, 6.491055d, -14.502181d, 20.888647d); + return true; + case 3928: + parameters = new EpsgExplicitOperationRecord(3928, 400.629d, 90.651d, 472.249d, 3.261138d, 5.263404d, -11.83739d, 20.022676d); + return true; + case 3962: + parameters = new EpsgExplicitOperationRecord(3962, 682.0d, -203.0d, 480.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 3963: + parameters = new EpsgExplicitOperationRecord(3963, 551.7d, 162.9d, 467.9d, 6.04d, 1.96d, -11.38d, -4.82d); + return true; + case 3964: + parameters = new EpsgExplicitOperationRecord(3964, 551.7d, 162.9d, 467.9d, 6.04d, 1.96d, -11.38d, -4.82d); + return true; + case 3965: + parameters = new EpsgExplicitOperationRecord(3965, 695.5d, -216.6d, 491.1d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 3971: + parameters = new EpsgExplicitOperationRecord(3971, -60.31d, 245.935d, 31.008d, 12.324d, 3.755d, -7.37d, 0.447d); + return true; + case 3972: + parameters = new EpsgExplicitOperationRecord(3972, -143.87d, 243.37d, -33.52d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 3990: + parameters = new EpsgExplicitOperationRecord(3990, -60.31d, 245.935d, 31.008d, 12.324d, 3.755d, -7.37d, 0.447d); + return true; + case 3998: + parameters = new EpsgExplicitOperationRecord(3998, -153.0d, -5.0d, -292.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket4(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 4064: + parameters = new EpsgExplicitOperationRecord(4064, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4065: + parameters = new EpsgExplicitOperationRecord(4065, -103.746d, -9.614d, -255.95d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4066: + parameters = new EpsgExplicitOperationRecord(4066, -103.746d, -9.614d, -255.95d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4067: + parameters = new EpsgExplicitOperationRecord(4067, -102.283d, -10.277d, -257.396d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4068: + parameters = new EpsgExplicitOperationRecord(4068, -102.283d, -10.277d, -257.396d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4069: + parameters = new EpsgExplicitOperationRecord(4069, -144.35d, 242.88d, -33.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4077: + parameters = new EpsgExplicitOperationRecord(4077, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4078: + parameters = new EpsgExplicitOperationRecord(4078, -83.11d, -97.38d, -117.22d, 0.0276d, -0.2167d, 0.2147d, 0.1218d); + return true; + case 4084: + parameters = new EpsgExplicitOperationRecord(4084, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4290: + parameters = new EpsgExplicitOperationRecord(4290, -381.788d, -57.501d, -256.673d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4461: + parameters = new EpsgExplicitOperationRecord(4461, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4476: + parameters = new EpsgExplicitOperationRecord(4476, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4477: + parameters = new EpsgExplicitOperationRecord(4477, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4478: + parameters = new EpsgExplicitOperationRecord(4478, -381.788d, -57.501d, -256.673d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4560: + parameters = new EpsgExplicitOperationRecord(4560, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4827: + parameters = new EpsgExplicitOperationRecord(4827, 485.0d, 169.5d, 483.8d, 7.786d, 4.398d, 4.103d, 0.0d); + return true; + case 4829: + parameters = new EpsgExplicitOperationRecord(4829, 558.7d, 68.8d, 452.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4830: + parameters = new EpsgExplicitOperationRecord(4830, 565.4171d, 50.3319d, 465.5524d, -1.9342d, 1.6677d, -9.1019d, 4.0725d); + return true; + case 4831: + parameters = new EpsgExplicitOperationRecord(4831, 593.0248d, 25.9984d, 478.7459d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4832: + parameters = new EpsgExplicitOperationRecord(4832, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4833: + parameters = new EpsgExplicitOperationRecord(4833, 565.4171d, 50.3319d, 465.5524d, -1.9342d, 1.6677d, -9.1019d, 4.0725d); + return true; + case 4834: + parameters = new EpsgExplicitOperationRecord(4834, -144.35d, 242.88d, -33.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4836: + parameters = new EpsgExplicitOperationRecord(4836, 485.0d, 169.5d, 483.8d, 7.786d, 4.398d, 4.103d, 0.0d); + return true; + case 4840: + parameters = new EpsgExplicitOperationRecord(4840, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 4905: + parameters = new EpsgExplicitOperationRecord(4905, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket5(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 5021: + parameters = new EpsgExplicitOperationRecord(5021, -503.229d, -247.375d, 312.582d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5022: + parameters = new EpsgExplicitOperationRecord(5022, -303.956d, 224.556d, 214.306d, 9.405d, -6.626d, -12.583d, 1.327d); + return true; + case 5023: + parameters = new EpsgExplicitOperationRecord(5023, -503.3d, -247.574d, 313.025d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5024: + parameters = new EpsgExplicitOperationRecord(5024, -204.926d, 140.353d, 55.063d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5025: + parameters = new EpsgExplicitOperationRecord(5025, -204.519d, 140.159d, 55.404d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5026: + parameters = new EpsgExplicitOperationRecord(5026, -205.808d, 140.771d, 54.326d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5027: + parameters = new EpsgExplicitOperationRecord(5027, -105.679d, 166.1d, -37.322d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5028: + parameters = new EpsgExplicitOperationRecord(5028, -105.377d, 165.769d, -36.965d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5029: + parameters = new EpsgExplicitOperationRecord(5029, -105.359d, 165.804d, -37.05d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5030: + parameters = new EpsgExplicitOperationRecord(5030, -105.531d, 166.39d, -37.326d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5031: + parameters = new EpsgExplicitOperationRecord(5031, -105.756d, 165.972d, -37.313d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5032: + parameters = new EpsgExplicitOperationRecord(5032, -106.235d, 166.236d, -37.768d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5033: + parameters = new EpsgExplicitOperationRecord(5033, -423.058d, -172.868d, 83.772d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5034: + parameters = new EpsgExplicitOperationRecord(5034, -423.053d, -172.871d, 83.771d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5035: + parameters = new EpsgExplicitOperationRecord(5035, -423.024d, -172.923d, 83.83d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5036: + parameters = new EpsgExplicitOperationRecord(5036, -223.15d, 110.132d, 36.711d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5037: + parameters = new EpsgExplicitOperationRecord(5037, -230.994d, 102.591d, 25.199d, 0.633d, -0.239d, 0.9d, 1.95d); + return true; + case 5038: + parameters = new EpsgExplicitOperationRecord(5038, -303.861d, -60.693d, 103.607d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5039: + parameters = new EpsgExplicitOperationRecord(5039, 508.088d, -191.042d, 565.223d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5040: + parameters = new EpsgExplicitOperationRecord(5040, -87.987d, -108.639d, -121.593d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5043: + parameters = new EpsgExplicitOperationRecord(5043, 24.47d, -130.89d, -81.56d, -0.0d, -0.0d, 0.13d, -0.22d); + return true; + case 5044: + parameters = new EpsgExplicitOperationRecord(5044, 23.57d, -140.95d, -79.8d, -0.0d, 0.35d, 0.79d, -0.22d); + return true; + case 5050: + parameters = new EpsgExplicitOperationRecord(5050, -157.84d, 308.54d, -146.6d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5051: + parameters = new EpsgExplicitOperationRecord(5051, -157.84d, 308.54d, -146.6d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5052: + parameters = new EpsgExplicitOperationRecord(5052, -160.31d, 314.82d, -142.25d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5053: + parameters = new EpsgExplicitOperationRecord(5053, -160.31d, 314.82d, -142.25d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5054: + parameters = new EpsgExplicitOperationRecord(5054, -161.11d, 310.25d, -144.64d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5055: + parameters = new EpsgExplicitOperationRecord(5055, -161.11d, 310.25d, -144.64d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5056: + parameters = new EpsgExplicitOperationRecord(5056, -160.4d, 302.29d, -144.19d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5057: + parameters = new EpsgExplicitOperationRecord(5057, -160.4d, 302.29d, -144.19d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5058: + parameters = new EpsgExplicitOperationRecord(5058, -153.54d, 302.33d, -152.37d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5059: + parameters = new EpsgExplicitOperationRecord(5059, -153.54d, 302.33d, -152.37d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5060: + parameters = new EpsgExplicitOperationRecord(5060, -151.5d, 300.09d, -151.15d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5061: + parameters = new EpsgExplicitOperationRecord(5061, -151.5d, 300.09d, -151.15d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5062: + parameters = new EpsgExplicitOperationRecord(5062, -156.8d, 298.41d, -147.41d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5063: + parameters = new EpsgExplicitOperationRecord(5063, -156.8d, 298.41d, -147.41d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5064: + parameters = new EpsgExplicitOperationRecord(5064, -157.4d, 295.05d, -150.19d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5065: + parameters = new EpsgExplicitOperationRecord(5065, -157.4d, 295.05d, -150.19d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5066: + parameters = new EpsgExplicitOperationRecord(5066, -151.99d, 287.04d, -147.45d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5067: + parameters = new EpsgExplicitOperationRecord(5067, -151.99d, 287.04d, -147.45d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5077: + parameters = new EpsgExplicitOperationRecord(5077, 70.995d, -335.916d, 262.898d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5078: + parameters = new EpsgExplicitOperationRecord(5078, 70.995d, -335.916d, 262.898d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5189: + parameters = new EpsgExplicitOperationRecord(5189, -145.907d, 505.034d, 685.756d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5191: + parameters = new EpsgExplicitOperationRecord(5191, -145.907d, 505.034d, 685.756d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5194: + parameters = new EpsgExplicitOperationRecord(5194, -192.873d, -39.382d, -111.202d, -0.00205d, -0.0005d, 0.00335d, 0.0188d); + return true; + case 5226: + parameters = new EpsgExplicitOperationRecord(5226, 572.213d, 85.334d, 461.94d, 4.9732d, 1.529d, 5.2484d, 3.5378d); + return true; + case 5227: + parameters = new EpsgExplicitOperationRecord(5227, 572.213d, 85.334d, 461.94d, 4.9732d, 1.529d, 5.2484d, 3.5378d); + return true; + case 5236: + parameters = new EpsgExplicitOperationRecord(5236, -0.293d, 766.95d, 87.713d, 0.195704d, 1.695068d, 3.473016d, -0.039338d); + return true; + case 5239: + parameters = new EpsgExplicitOperationRecord(5239, 572.213d, 85.334d, 461.94d, 4.9732d, 1.529d, 5.2484d, 3.5378d); + return true; + case 5249: + parameters = new EpsgExplicitOperationRecord(5249, -689.5937d, 623.84046d, -65.93566d, -0.02331d, 1.17094d, -0.80054d, 5.88536d); + return true; + case 5260: + parameters = new EpsgExplicitOperationRecord(5260, 0.023d, 0.036d, -0.068d, 0.00176d, 0.00912d, -0.01136d, 0.00439d); + return true; + case 5261: + parameters = new EpsgExplicitOperationRecord(5261, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5267: + parameters = new EpsgExplicitOperationRecord(5267, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5327: + parameters = new EpsgExplicitOperationRecord(5327, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5350: + parameters = new EpsgExplicitOperationRecord(5350, -148.0d, 136.0d, 90.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5351: + parameters = new EpsgExplicitOperationRecord(5351, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5374: + parameters = new EpsgExplicitOperationRecord(5374, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5376: + parameters = new EpsgExplicitOperationRecord(5376, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5377: + parameters = new EpsgExplicitOperationRecord(5377, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5378: + parameters = new EpsgExplicitOperationRecord(5378, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5384: + parameters = new EpsgExplicitOperationRecord(5384, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5385: + parameters = new EpsgExplicitOperationRecord(5385, -124.45d, 183.74d, 44.64d, -0.4384d, 0.5446d, -0.9706d, -2.1365d); + return true; + case 5386: + parameters = new EpsgExplicitOperationRecord(5386, -124.45d, 183.74d, 44.64d, -0.4384d, 0.5446d, -0.9706d, -2.1365d); + return true; + case 5395: + parameters = new EpsgExplicitOperationRecord(5395, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5470: + parameters = new EpsgExplicitOperationRecord(5470, 213.11d, 9.37d, -74.95d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5483: + parameters = new EpsgExplicitOperationRecord(5483, -265.8867d, 76.9851d, 20.2667d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5484: + parameters = new EpsgExplicitOperationRecord(5484, -265.8867d, 76.9851d, 20.2667d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5485: + parameters = new EpsgExplicitOperationRecord(5485, -189.6806d, 18.3463d, -42.7695d, -0.33746d, -3.09264d, 2.53861d, 0.4598d); + return true; + case 5486: + parameters = new EpsgExplicitOperationRecord(5486, -189.6806d, 18.3463d, -42.7695d, -0.33746d, -3.09264d, 2.53861d, 0.4598d); + return true; + case 5491: + parameters = new EpsgExplicitOperationRecord(5491, 127.744d, 547.069d, 118.359d, -3.1116d, 4.9509d, -0.8837d, 14.1012d); + return true; + case 5492: + parameters = new EpsgExplicitOperationRecord(5492, -471.06d, -3.212d, -305.843d, 0.4752d, -0.9978d, 0.2068d, 2.1353d); + return true; + case 5493: + parameters = new EpsgExplicitOperationRecord(5493, 151.613d, 253.832d, -429.084d, -0.0506d, 0.0958d, -0.5974d, -0.3971d); + return true; + case 5494: + parameters = new EpsgExplicitOperationRecord(5494, 0.7696d, -0.8692d, -12.0631d, -0.32511d, -0.21041d, -0.0239d, 0.2829d); + return true; + case 5495: + parameters = new EpsgExplicitOperationRecord(5495, 1.2239d, 2.4156d, -1.7598d, 0.038d, -0.16101d, -0.04925d, 0.2387d); + return true; + case 5496: + parameters = new EpsgExplicitOperationRecord(5496, 14.6642d, 5.2493d, 0.1981d, -0.06838d, 0.09141d, -0.58131d, -0.4067d); + return true; + case 5497: + parameters = new EpsgExplicitOperationRecord(5497, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5501: + parameters = new EpsgExplicitOperationRecord(5501, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5521: + parameters = new EpsgExplicitOperationRecord(5521, -963.0d, 510.0d, -359.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5553: + parameters = new EpsgExplicitOperationRecord(5553, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5585: + parameters = new EpsgExplicitOperationRecord(5585, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5586: + parameters = new EpsgExplicitOperationRecord(5586, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5590: + parameters = new EpsgExplicitOperationRecord(5590, 25.0d, -141.0d, -78.5d, -0.0d, 0.35d, 0.736d, 0.0d); + return true; + case 5599: + parameters = new EpsgExplicitOperationRecord(5599, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5622: + parameters = new EpsgExplicitOperationRecord(5622, 370.936d, -108.938d, 435.682d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5630: + parameters = new EpsgExplicitOperationRecord(5630, -168.52d, -72.05d, 304.3d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5660: + parameters = new EpsgExplicitOperationRecord(5660, -209.3622d, -87.8162d, 404.6198d, 0.0046d, 3.4784d, 0.5805d, -1.4547d); + return true; + case 5662: + parameters = new EpsgExplicitOperationRecord(5662, -124.0d, -60.0d, 153.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5826: + parameters = new EpsgExplicitOperationRecord(5826, 584.9636d, 107.7175d, 413.8067d, 1.1155d, 0.2824d, -3.1384d, 7.9922d); + return true; + case 5827: + parameters = new EpsgExplicitOperationRecord(5827, -129.164d, -41.188d, 130.718d, 0.246d, 0.374d, 0.329d, -2.955d); + return true; + case 5840: + parameters = new EpsgExplicitOperationRecord(5840, 24.0d, -121.0d, -76.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5841: + parameters = new EpsgExplicitOperationRecord(5841, -124.0d, -60.0d, 154.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5878: + parameters = new EpsgExplicitOperationRecord(5878, -689.5937d, 623.84046d, -65.93566d, -0.02331d, 1.17094d, -0.80054d, 5.88536d); + return true; + case 5881: + parameters = new EpsgExplicitOperationRecord(5881, -67.35d, 3.88d, -38.22d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5882: + parameters = new EpsgExplicitOperationRecord(5882, -67.35d, 3.88d, -38.22d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 5888: + parameters = new EpsgExplicitOperationRecord(5888, -599.928d, -275.552d, -195.665d, -0.0835d, -0.4715d, 0.0602d, 49.2814d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket6(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 6136: + parameters = new EpsgExplicitOperationRecord(6136, -179.483d, -69.379d, -27.584d, -7.862d, 8.163d, 6.042d, -13.925d); + return true; + case 6137: + parameters = new EpsgExplicitOperationRecord(6137, 8.853d, -52.644d, 180.304d, -0.393d, -2.323d, 2.96d, -24.081d); + return true; + case 6142: + parameters = new EpsgExplicitOperationRecord(6142, -179.483d, -69.379d, -27.584d, -7.862d, 8.163d, 6.042d, -13.925d); + return true; + case 6143: + parameters = new EpsgExplicitOperationRecord(6143, 8.853d, -52.644d, 180.304d, -0.393d, -2.323d, 2.96d, -24.081d); + return true; + case 6177: + parameters = new EpsgExplicitOperationRecord(6177, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6191: + parameters = new EpsgExplicitOperationRecord(6191, -138.7d, 164.4d, 34.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6192: + parameters = new EpsgExplicitOperationRecord(6192, -205.57d, 168.77d, -4.12d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6193: + parameters = new EpsgExplicitOperationRecord(6193, -206.05d, 168.28d, -3.82d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6194: + parameters = new EpsgExplicitOperationRecord(6194, -206.05d, 168.28d, -3.82d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6195: + parameters = new EpsgExplicitOperationRecord(6195, -67.35d, 3.88d, -38.22d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6196: + parameters = new EpsgExplicitOperationRecord(6196, -93.179d, -87.124d, 114.338d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6205: + parameters = new EpsgExplicitOperationRecord(6205, 517.4399d, 228.7318d, 579.7954d, 4.045d, 4.304d, -15.612d, -8.312d); + return true; + case 6206: + parameters = new EpsgExplicitOperationRecord(6206, 521.748d, 229.489d, 590.921d, 4.029d, 4.488d, -15.521d, -9.78d); + return true; + case 6208: + parameters = new EpsgExplicitOperationRecord(6208, 293.17d, 726.18d, 245.36d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6276: + parameters = new EpsgExplicitOperationRecord(6276, -84.68d, -19.42d, 32.01d, 0.4254d, -2.2578d, -2.4015d, 9.71d); + return true; + case 6277: + parameters = new EpsgExplicitOperationRecord(6277, -79.73d, -6.86d, 38.03d, 0.0351d, -2.1211d, -2.1411d, 6.636d); + return true; + case 6278: + parameters = new EpsgExplicitOperationRecord(6278, -45.91d, -29.85d, -20.37d, 1.6705d, -0.4594d, -1.9356d, 7.07d); + return true; + case 6279: + parameters = new EpsgExplicitOperationRecord(6279, -14.63d, -27.62d, -25.32d, 1.7893d, 0.6047d, -0.9962d, 6.695d); + return true; + case 6280: + parameters = new EpsgExplicitOperationRecord(6280, 24.54d, -36.43d, -68.12d, 2.7359d, 2.0431d, -0.3731d, 6.901d); + return true; + case 6313: + parameters = new EpsgExplicitOperationRecord(6313, -0.014d, 0.0431d, 0.201d, -0.012464d, -0.012013d, -0.006434d, 0.024607d); + return true; + case 6315: + parameters = new EpsgExplicitOperationRecord(6315, -0.0761d, -0.0101d, 0.0444d, -0.008765d, -0.009361d, -0.009325d, 0.007935d); + return true; + case 6373: + parameters = new EpsgExplicitOperationRecord(6373, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6392: + parameters = new EpsgExplicitOperationRecord(6392, -0.2088d, 0.0119d, 0.1855d, -0.012059d, -0.013639d, -0.011825d, 0.004559d); + return true; + case 6698: + parameters = new EpsgExplicitOperationRecord(6698, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6701: + parameters = new EpsgExplicitOperationRecord(6701, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6711: + parameters = new EpsgExplicitOperationRecord(6711, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6864: + parameters = new EpsgExplicitOperationRecord(6864, 0.991d, -1.9072d, -0.5129d, -25.79d, -9.65d, -11.66d, 0.0d); + return true; + case 6865: + parameters = new EpsgExplicitOperationRecord(6865, 0.9889d, -1.9074d, -0.503d, -25.915d, -9.426d, -11.599d, -0.93d); + return true; + case 6866: + parameters = new EpsgExplicitOperationRecord(6866, 0.9956d, -1.9013d, -0.5215d, -25.915d, -9.426d, -11.599d, 0.62d); + return true; + case 6872: + parameters = new EpsgExplicitOperationRecord(6872, -123.1d, 53.2d, 465.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6873: + parameters = new EpsgExplicitOperationRecord(6873, -198.383d, -240.517d, -107.909d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6888: + parameters = new EpsgExplicitOperationRecord(6888, 205.435d, -29.099d, -292.202d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6889: + parameters = new EpsgExplicitOperationRecord(6889, 213.116d, 9.358d, -74.946d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6890: + parameters = new EpsgExplicitOperationRecord(6890, 213.11d, 9.37d, -74.95d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6891: + parameters = new EpsgExplicitOperationRecord(6891, 205.0d, 96.0d, -98.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6895: + parameters = new EpsgExplicitOperationRecord(6895, 98.0d, 390.0d, -22.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6896: + parameters = new EpsgExplicitOperationRecord(6896, -170.0d, 33.0d, 326.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6897: + parameters = new EpsgExplicitOperationRecord(6897, -153.0d, 153.0d, 307.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6898: + parameters = new EpsgExplicitOperationRecord(6898, -306.0d, -62.0d, 105.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6899: + parameters = new EpsgExplicitOperationRecord(6899, 22.0d, -126.0d, -85.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6900: + parameters = new EpsgExplicitOperationRecord(6900, -132.0d, -110.0d, -335.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6901: + parameters = new EpsgExplicitOperationRecord(6901, -80.0d, -100.0d, -228.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6902: + parameters = new EpsgExplicitOperationRecord(6902, -679.0d, 667.0d, -49.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6903: + parameters = new EpsgExplicitOperationRecord(6903, -30.0d, 190.0d, 89.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6904: + parameters = new EpsgExplicitOperationRecord(6904, -179.0d, -81.0d, -314.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6905: + parameters = new EpsgExplicitOperationRecord(6905, -128.0d, -52.0d, 153.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6906: + parameters = new EpsgExplicitOperationRecord(6906, -145.0d, -97.0d, -292.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6907: + parameters = new EpsgExplicitOperationRecord(6907, -77.0d, -128.0d, 142.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6908: + parameters = new EpsgExplicitOperationRecord(6908, -345.0d, 3.0d, 223.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6909: + parameters = new EpsgExplicitOperationRecord(6909, -73.0d, 47.0d, -83.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6910: + parameters = new EpsgExplicitOperationRecord(6910, -24.0d, -203.0d, 268.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6911: + parameters = new EpsgExplicitOperationRecord(6911, -183.0d, -15.0d, 273.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6912: + parameters = new EpsgExplicitOperationRecord(6912, -235.0d, -110.0d, 393.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6913: + parameters = new EpsgExplicitOperationRecord(6913, -63.0d, 176.0d, 185.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6914: + parameters = new EpsgExplicitOperationRecord(6914, -43.685d, -179.785d, -267.721d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6926: + parameters = new EpsgExplicitOperationRecord(6926, -76.269d, -16.683d, 68.562d, -6.275d, 10.536d, -4.286d, -13.686d); + return true; + case 6935: + parameters = new EpsgExplicitOperationRecord(6935, 0.208d, -0.012d, -0.229d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6936: + parameters = new EpsgExplicitOperationRecord(6936, -0.214d, 0.119d, 0.156d, -0.01182d, 0.00811d, -0.01677d, -0.0059d); + return true; + case 6937: + parameters = new EpsgExplicitOperationRecord(6937, -0.41d, -2.37d, 2.0d, 3.592d, 3.698d, 3.989d, 8.843d); + return true; + case 6938: + parameters = new EpsgExplicitOperationRecord(6938, -129.0d, -58.0d, 152.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6939: + parameters = new EpsgExplicitOperationRecord(6939, -131.876d, -54.554d, 453.346d, -5.2155d, -8.2042d, 0.09d, 5.02d); + return true; + case 6940: + parameters = new EpsgExplicitOperationRecord(6940, -131.3d, -55.3d, 151.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6941: + parameters = new EpsgExplicitOperationRecord(6941, 45.928d, -177.212d, 336.867d, -4.6039d, -3.0921d, 0.5729d, 36.796d); + return true; + case 6942: + parameters = new EpsgExplicitOperationRecord(6942, -137.4d, -58.9d, 150.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6943: + parameters = new EpsgExplicitOperationRecord(6943, -129.0d, -58.0d, 152.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6944: + parameters = new EpsgExplicitOperationRecord(6944, -131.3d, -55.3d, 151.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6945: + parameters = new EpsgExplicitOperationRecord(6945, -137.4d, -58.9d, 150.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6949: + parameters = new EpsgExplicitOperationRecord(6949, -302.0d, 272.0d, -360.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6950: + parameters = new EpsgExplicitOperationRecord(6950, -328.0d, 340.0d, -329.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6951: + parameters = new EpsgExplicitOperationRecord(6951, -352.0d, 403.0d, -287.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6960: + parameters = new EpsgExplicitOperationRecord(6960, -191.90441429d, -39.30318279d, -111.45032835d, 0.00928836d, -0.01975479d, 0.00427372d, 0.252906278d); + return true; + case 6968: + parameters = new EpsgExplicitOperationRecord(6968, -64.0d, 0.0d, -32.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6970: + parameters = new EpsgExplicitOperationRecord(6970, -79.0d, 13.0d, -14.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6971: + parameters = new EpsgExplicitOperationRecord(6971, -302.0d, 272.0d, -360.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6972: + parameters = new EpsgExplicitOperationRecord(6972, -328.0d, 340.0d, -329.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6973: + parameters = new EpsgExplicitOperationRecord(6973, -352.0d, 403.0d, -287.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6974: + parameters = new EpsgExplicitOperationRecord(6974, -59.0d, -11.0d, -52.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6975: + parameters = new EpsgExplicitOperationRecord(6975, -64.0d, 0.0d, -32.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6976: + parameters = new EpsgExplicitOperationRecord(6976, -72.0d, 10.0d, -32.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6977: + parameters = new EpsgExplicitOperationRecord(6977, -79.0d, 13.0d, -14.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 6992: + parameters = new EpsgExplicitOperationRecord(6992, 0.2255d, -0.3709d, -0.1171d, 0.00388d, -0.00063d, 0.0182d, 0.013443d); + return true; + case 6993: + parameters = new EpsgExplicitOperationRecord(6993, -24.0024d, -17.1032d, -17.8444d, 0.33009d, 1.85269d, -1.66969d, 5.4248d); + return true; + case 6998: + parameters = new EpsgExplicitOperationRecord(6998, -233.4d, -160.7d, 381.5d, -0.0d, -0.0d, 0.554d, 0.2263d); + return true; + case 6999: + parameters = new EpsgExplicitOperationRecord(6999, -253.4392d, -148.452d, 386.5267d, 0.15605d, 0.43d, -0.1013d, -0.0424d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket7(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 7002: + parameters = new EpsgExplicitOperationRecord(7002, -246.1633d, -152.9047d, 382.6047d, 0.0989d, 0.1382d, 0.0768d, 2.1e-06d); + return true; + case 7003: + parameters = new EpsgExplicitOperationRecord(7003, -242.8907d, -149.0671d, 384.416d, 0.19044d, 0.24987d, 0.13925d, 0.0001746d); + return true; + case 7004: + parameters = new EpsgExplicitOperationRecord(7004, -246.734d, -153.4345d, 382.1477d, -0.116617d, -0.165167d, -0.091327d, 1.94e-05d); + return true; + case 7033: + parameters = new EpsgExplicitOperationRecord(7033, -242.2d, -144.9d, 370.3d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7083: + parameters = new EpsgExplicitOperationRecord(7083, 324.912d, 153.282d, 172.026d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7140: + parameters = new EpsgExplicitOperationRecord(7140, -23.8085d, -17.5937d, -17.801d, 0.3306d, 1.85706d, -1.64828d, 5.4374d); + return true; + case 7377: + parameters = new EpsgExplicitOperationRecord(7377, 0.819d, -0.5762d, -1.6446d, -0.00378d, -0.03317d, 0.00318d, 0.0693d); + return true; + case 7442: + parameters = new EpsgExplicitOperationRecord(7442, -181.7d, 64.7d, 247.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7443: + parameters = new EpsgExplicitOperationRecord(7443, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7448: + parameters = new EpsgExplicitOperationRecord(7448, -59.0d, -11.0d, -52.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7449: + parameters = new EpsgExplicitOperationRecord(7449, -72.0d, 10.0d, -32.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7666: + parameters = new EpsgExplicitOperationRecord(7666, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 7667: + parameters = new EpsgExplicitOperationRecord(7667, -4.0d, 3.0d, 4.0d, -0.27d, 0.27d, -0.38d, -6.9d); + return true; + case 7668: + parameters = new EpsgExplicitOperationRecord(7668, -6.0d, 5.0d, 20.0d, -0.0d, -0.0d, -0.0d, -4.5d); + return true; + case 7669: + parameters = new EpsgExplicitOperationRecord(7669, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 7670: + parameters = new EpsgExplicitOperationRecord(7670, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 7672: + parameters = new EpsgExplicitOperationRecord(7672, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 7675: + parameters = new EpsgExplicitOperationRecord(7675, 577.88891d, 165.22205d, 391.18289d, 4.9145d, -0.94729d, -13.05098d, 7.78664d); + return true; + case 7676: + parameters = new EpsgExplicitOperationRecord(7676, 577.88891d, 165.22205d, 391.18289d, 4.9145d, -0.94729d, -13.05098d, 7.78664d); + return true; + case 7697: + parameters = new EpsgExplicitOperationRecord(7697, -127.535d, 113.495d, -12.7d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7698: + parameters = new EpsgExplicitOperationRecord(7698, -32.3841359d, 180.4090461d, 120.8442577d, -2.1545854d, -0.1498782d, 0.5742915d, 8.1049164d); + return true; + case 7702: + parameters = new EpsgExplicitOperationRecord(7702, -1.07d, -0.03d, 0.02d, -0.0d, -0.0d, 130.0d, -0.22d); + return true; + case 7703: + parameters = new EpsgExplicitOperationRecord(7703, -0.373d, 0.186d, 0.202d, 2.3d, -3.54d, 4.21d, -0.008d); + return true; + case 7704: + parameters = new EpsgExplicitOperationRecord(7704, -1.443d, 0.156d, 0.222d, 2.3d, -3.54d, 134.21d, -0.228d); + return true; + case 7705: + parameters = new EpsgExplicitOperationRecord(7705, 0.0d, 0.014d, -0.008d, 0.562d, 0.019d, -0.053d, -0.0006d); + return true; + case 7720: + parameters = new EpsgExplicitOperationRecord(7720, 8.846d, -4.394d, -1.122d, -0.00237d, -0.146528d, 0.130428d, 0.783926d); + return true; + case 7721: + parameters = new EpsgExplicitOperationRecord(7721, 8.846d, -4.394d, -1.122d, -0.00237d, -0.146528d, 0.130428d, 0.783926d); + return true; + case 7806: + parameters = new EpsgExplicitOperationRecord(7806, 5.0d, -133.0d, -104.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7807: + parameters = new EpsgExplicitOperationRecord(7807, 0.99343d, -1.90331d, -0.52655d, -25.91467d, -9.42645d, -11.59935d, 1.71504d); + return true; + case 7808: + parameters = new EpsgExplicitOperationRecord(7808, 0.908d, -2.0161d, -0.5653d, -27.741d, -13.469d, -2.712d, 1.1d); + return true; + case 7809: + parameters = new EpsgExplicitOperationRecord(7809, 0.908d, -2.0161d, -0.5653d, -28.971d, -10.42d, -8.928d, 1.1d); + return true; + case 7817: + parameters = new EpsgExplicitOperationRecord(7817, 24.322d, -121.372d, -75.847d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7833: + parameters = new EpsgExplicitOperationRecord(7833, -44.183d, -0.58d, -38.489d, 2.3867d, 2.7072d, -3.5196d, -8.2703d); + return true; + case 7834: + parameters = new EpsgExplicitOperationRecord(7834, -44.183d, -0.58d, -38.489d, 2.3867d, 2.7072d, -3.5196d, -8.2703d); + return true; + case 7835: + parameters = new EpsgExplicitOperationRecord(7835, 74.5d, -112.5d, -44.3d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7836: + parameters = new EpsgExplicitOperationRecord(7836, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7892: + parameters = new EpsgExplicitOperationRecord(7892, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7893: + parameters = new EpsgExplicitOperationRecord(7893, -323.65d, 551.39d, -491.22d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7894: + parameters = new EpsgExplicitOperationRecord(7894, -323.65d, 551.39d, -491.22d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7895: + parameters = new EpsgExplicitOperationRecord(7895, -112.854d, 12.27d, -18.913d, 2.1692d, 16.8896d, 17.1961d, -19.54517d); + return true; + case 7897: + parameters = new EpsgExplicitOperationRecord(7897, -0.077d, 0.079d, 0.086d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7898: + parameters = new EpsgExplicitOperationRecord(7898, -0.077d, 0.079d, 0.086d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 7960: + parameters = new EpsgExplicitOperationRecord(7960, -0.003d, -0.001d, 0.0d, -0.019d, 0.042d, -0.002d, 0.0d); + return true; + case 7961: + parameters = new EpsgExplicitOperationRecord(7961, 0.36d, -0.08d, -0.18d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket8(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 8048: + parameters = new EpsgExplicitOperationRecord(8048, 61.55d, -10.87d, -40.19d, 39.4924d, 32.7221d, 32.8979d, -9.994d); + return true; + case 8049: + parameters = new EpsgExplicitOperationRecord(8049, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 8270: + parameters = new EpsgExplicitOperationRecord(8270, 11.363d, 424.148d, 373.13d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8365: + parameters = new EpsgExplicitOperationRecord(8365, -485.014055d, -169.473618d, -483.842943d, -7.78625453d, -4.39770887d, -4.10248899d, 0.0d); + return true; + case 8367: + parameters = new EpsgExplicitOperationRecord(8367, 485.021d, 169.465d, 483.839d, 7.786342d, 4.397554d, 4.102655d, 0.0d); + return true; + case 8368: + parameters = new EpsgExplicitOperationRecord(8368, 485.021d, 169.465d, 483.839d, 7.786342d, 4.397554d, 4.102655d, 0.0d); + return true; + case 8435: + parameters = new EpsgExplicitOperationRecord(8435, 202.865d, 303.99d, 155.873d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8436: + parameters = new EpsgExplicitOperationRecord(8436, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8438: + parameters = new EpsgExplicitOperationRecord(8438, -202.865d, -303.99d, -155.873d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8439: + parameters = new EpsgExplicitOperationRecord(8439, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8448: + parameters = new EpsgExplicitOperationRecord(8448, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 8450: + parameters = new EpsgExplicitOperationRecord(8450, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8452: + parameters = new EpsgExplicitOperationRecord(8452, -377.0d, 681.0d, -50.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8674: + parameters = new EpsgExplicitOperationRecord(8674, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8680: + parameters = new EpsgExplicitOperationRecord(8680, 489.88d, 183.912d, 533.711d, 5.76545d, 4.69994d, -12.58211d, 1.00646d); + return true; + case 8688: + parameters = new EpsgExplicitOperationRecord(8688, 476.08d, 125.947d, 417.81d, 4.610862d, 2.388137d, -11.942335d, 9.896638d); + return true; + case 8689: + parameters = new EpsgExplicitOperationRecord(8689, 476.08d, 125.947d, 417.81d, 4.610862d, 2.388137d, -11.942335d, 9.896638d); + return true; + case 8695: + parameters = new EpsgExplicitOperationRecord(8695, 42.899d, -214.863d, -11.927d, 1.844d, -0.648d, 6.37d, 0.169d); + return true; + case 8696: + parameters = new EpsgExplicitOperationRecord(8696, 45.799d, -212.263d, -11.927d, 1.844d, -0.648d, 6.37d, 0.169d); + return true; + case 8819: + parameters = new EpsgExplicitOperationRecord(8819, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8822: + parameters = new EpsgExplicitOperationRecord(8822, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8823: + parameters = new EpsgExplicitOperationRecord(8823, 489.88d, 183.912d, 533.711d, 5.76545d, 4.69994d, -12.58211d, 1.00646d); + return true; + case 8824: + parameters = new EpsgExplicitOperationRecord(8824, -61.15d, -315.86d, -3.51d, -0.41d, -0.74d, 3.52d, 1.36d); + return true; + case 8827: + parameters = new EpsgExplicitOperationRecord(8827, -93.799d, -132.737d, -219.073d, -1.844d, 0.648d, -6.37d, -0.169d); + return true; + case 8828: + parameters = new EpsgExplicitOperationRecord(8828, 0.072d, -0.507d, -0.245d, 0.0183d, -0.0003d, 0.007d, -0.0093d); + return true; + case 8829: + parameters = new EpsgExplicitOperationRecord(8829, 221.525d, 152.948d, 176.768d, 2.3847d, 1.3896d, 0.877d, 11.4741d); + return true; + case 8830: + parameters = new EpsgExplicitOperationRecord(8830, 221.597d, 152.441d, 176.523d, 2.403d, 1.3893d, 0.884d, 11.4648d); + return true; + case 8831: + parameters = new EpsgExplicitOperationRecord(8831, 218.697d, 151.257d, 176.995d, 3.5048d, 2.004d, 1.281d, 10.991d); + return true; + case 8832: + parameters = new EpsgExplicitOperationRecord(8832, 218.769d, 150.75d, 176.75d, 3.5231d, 2.0037d, 1.288d, 10.9817d); + return true; + case 8833: + parameters = new EpsgExplicitOperationRecord(8833, 72.438d, 345.918d, 79.486d, -1.6045d, -0.8823d, -0.5565d, 1.3746d); + return true; + case 8834: + parameters = new EpsgExplicitOperationRecord(8834, 72.51d, 345.411d, 79.241d, -1.5862d, -0.8826d, -0.5495d, 1.3653d); + return true; + case 8835: + parameters = new EpsgExplicitOperationRecord(8835, 347.103d, 1078.125d, 2623.922d, 33.8875d, -70.6773d, 9.3943d, 186.074d); + return true; + case 8842: + parameters = new EpsgExplicitOperationRecord(8842, 347.175d, 1077.618d, 2623.677d, 33.9058d, -70.6776d, 9.4013d, 186.0647d); + return true; + case 8843: + parameters = new EpsgExplicitOperationRecord(8843, 410.721d, 55.049d, 80.746d, -2.5779d, -2.3514d, -0.6664d, 17.3311d); + return true; + case 8844: + parameters = new EpsgExplicitOperationRecord(8844, 410.793d, 54.542d, 80.501d, -2.5596d, -2.3517d, -0.6594d, 17.3218d); + return true; + case 8845: + parameters = new EpsgExplicitOperationRecord(8845, 374.715d, -58.407d, -0.957d, -16.2111d, -11.4626d, -5.5357d, -0.5409d); + return true; + case 8846: + parameters = new EpsgExplicitOperationRecord(8846, 374.787d, -58.914d, -1.202d, -16.1928d, -11.4629d, -5.5287d, -0.5502d); + return true; + case 8847: + parameters = new EpsgExplicitOperationRecord(8847, 165.732d, 216.72d, 180.505d, -0.6434d, -0.4512d, -0.0791d, 7.4204d); + return true; + case 8848: + parameters = new EpsgExplicitOperationRecord(8848, 165.804d, 216.213d, 180.26d, -0.6251d, -0.4515d, -0.0721d, 7.4111d); + return true; + case 8849: + parameters = new EpsgExplicitOperationRecord(8849, 1363.785d, 1362.687d, 398.811d, -4.5322d, -6.7579d, -1.0574d, 268.361d); + return true; + case 8850: + parameters = new EpsgExplicitOperationRecord(8850, 1363.857d, 1362.18d, 398.566d, -4.5139d, -6.7582d, -1.0504d, 268.3517d); + return true; + case 8851: + parameters = new EpsgExplicitOperationRecord(8851, 259.551d, 297.612d, 197.833d, 1.4866d, 2.1224d, 0.4612d, 27.0249d); + return true; + case 8852: + parameters = new EpsgExplicitOperationRecord(8852, 259.623d, 297.105d, 197.588d, 1.5049d, 2.1221d, 0.4682d, 27.0156d); + return true; + case 8853: + parameters = new EpsgExplicitOperationRecord(8853, 217.109d, 86.452d, 23.711d, 0.0183d, -0.0003d, 0.007d, -0.0093d); + return true; + case 8882: + parameters = new EpsgExplicitOperationRecord(8882, -93.799d, -132.737d, -219.073d, -1.844d, 0.648d, -6.37d, -0.169d); + return true; + case 8883: + parameters = new EpsgExplicitOperationRecord(8883, -48.0d, -345.0d, -231.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8884: + parameters = new EpsgExplicitOperationRecord(8884, -50.9d, -347.6d, -231.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8886: + parameters = new EpsgExplicitOperationRecord(8886, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8887: + parameters = new EpsgExplicitOperationRecord(8887, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8890: + parameters = new EpsgExplicitOperationRecord(8890, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8894: + parameters = new EpsgExplicitOperationRecord(8894, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 8970: + parameters = new EpsgExplicitOperationRecord(8970, 1.0053d, -1.90921d, -0.54157d, -26.78138d, 0.42027d, -10.93206d, 0.36891d); + return true; + case 8971: + parameters = new EpsgExplicitOperationRecord(8971, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket9(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 9020: + parameters = new EpsgExplicitOperationRecord(9020, 0.5d, 3.6d, 2.4d, -0.1d, 0.0d, 0.0d, -3.0d); + return true; + case 9021: + parameters = new EpsgExplicitOperationRecord(9021, -0.5d, -2.4d, 3.8d, 0.0d, 0.0d, 0.0d, -3.0d); + return true; + case 9022: + parameters = new EpsgExplicitOperationRecord(9022, -0.1d, 0.4d, 1.6d, 0.0d, 0.0d, 0.0d, -0.3d); + return true; + case 9023: + parameters = new EpsgExplicitOperationRecord(9023, -1.1d, -1.4d, 0.6d, 0.0d, 0.0d, 0.0d, -1.4d); + return true; + case 9076: + parameters = new EpsgExplicitOperationRecord(9076, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 9077: + parameters = new EpsgExplicitOperationRecord(9077, 0.9102d, -2.0141d, -0.5602d, -29.039d, -10.065d, -10.101d, 0.0d); + return true; + case 9078: + parameters = new EpsgExplicitOperationRecord(9078, 0.9102d, -2.0141d, -0.5602d, -29.039d, -10.065d, -10.101d, 0.0d); + return true; + case 9126: + parameters = new EpsgExplicitOperationRecord(9126, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9127: + parameters = new EpsgExplicitOperationRecord(9127, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9128: + parameters = new EpsgExplicitOperationRecord(9128, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9129: + parameters = new EpsgExplicitOperationRecord(9129, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9142: + parameters = new EpsgExplicitOperationRecord(9142, 628.54052d, 192.2538d, 498.43507d, -13.79189d, -0.81467d, 41.21533d, -17.40368d); + return true; + case 9143: + parameters = new EpsgExplicitOperationRecord(9143, 628.54052d, 192.2538d, 498.43507d, -13.79189d, -0.81467d, 41.21533d, -17.40368d); + return true; + case 9144: + parameters = new EpsgExplicitOperationRecord(9144, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9145: + parameters = new EpsgExplicitOperationRecord(9145, -0.06d, 0.517d, 0.223d, -0.0183d, 0.0003d, -0.007d, 0.011d); + return true; + case 9185: + parameters = new EpsgExplicitOperationRecord(9185, -136.9703d, -37.5638d, 124.4242d, 0.25676d, 0.42966d, 0.30077d, -4.61966d); + return true; + case 9186: + parameters = new EpsgExplicitOperationRecord(9186, -23.772d, -17.49d, -17.859d, 0.3132d, 1.85274d, -1.67299d, 5.4262d); + return true; + case 9189: + parameters = new EpsgExplicitOperationRecord(9189, -23.772d, -17.49d, -17.859d, 0.3132d, 1.85274d, -1.67299d, 5.4262d); + return true; + case 9224: + parameters = new EpsgExplicitOperationRecord(9224, -157.89d, -17.16d, -78.41d, 2.118d, 2.697d, -1.434d, -5.38d); + return true; + case 9226: + parameters = new EpsgExplicitOperationRecord(9226, 112.771d, -12.282d, 18.935d, -2.1692d, -16.8896d, -17.1961d, 19.54517d); + return true; + case 9234: + parameters = new EpsgExplicitOperationRecord(9234, 230.25d, 632.76d, 161.03d, 1.114d, -1.115d, -1.212d, 12.584d); + return true; + case 9257: + parameters = new EpsgExplicitOperationRecord(9257, 8.88d, 184.86d, 106.69d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9258: + parameters = new EpsgExplicitOperationRecord(9258, 15.75d, 164.93d, 126.18d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9259: + parameters = new EpsgExplicitOperationRecord(9259, -233.43d, 6.65d, 173.64d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9260: + parameters = new EpsgExplicitOperationRecord(9260, -192.26d, 65.72d, 132.08d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9261: + parameters = new EpsgExplicitOperationRecord(9261, -9.5d, 122.9d, 138.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9262: + parameters = new EpsgExplicitOperationRecord(9262, -78.1d, 101.6d, 133.3d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9263: + parameters = new EpsgExplicitOperationRecord(9263, 18.2d, 190.7d, 100.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9264: + parameters = new EpsgExplicitOperationRecord(9264, -0.41d, 0.46d, -0.35d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9281: + parameters = new EpsgExplicitOperationRecord(9281, 565.7381d, 50.4018d, 465.2904d, -1.91514d, 1.60363d, -9.09546d, 4.07244d); + return true; + case 9291: + parameters = new EpsgExplicitOperationRecord(9291, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9298: + parameters = new EpsgExplicitOperationRecord(9298, 1.16835d, -1.42001d, -2.24431d, -0.00822d, -0.05508d, 0.01818d, 0.23388d); + return true; + case 9342: + parameters = new EpsgExplicitOperationRecord(9342, -302.0d, 272.0d, -360.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9343: + parameters = new EpsgExplicitOperationRecord(9343, -328.0d, 340.0d, -329.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9344: + parameters = new EpsgExplicitOperationRecord(9344, -352.0d, 403.0d, -287.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9345: + parameters = new EpsgExplicitOperationRecord(9345, -302.0d, 272.0d, -360.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9346: + parameters = new EpsgExplicitOperationRecord(9346, -328.0d, 340.0d, -329.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9347: + parameters = new EpsgExplicitOperationRecord(9347, -352.0d, 403.0d, -287.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9349: + parameters = new EpsgExplicitOperationRecord(9349, -79.0d, 13.0d, -14.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9350: + parameters = new EpsgExplicitOperationRecord(9350, -79.0d, 13.0d, -14.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9361: + parameters = new EpsgExplicitOperationRecord(9361, 0.0469d, -0.2827d, 0.0866d, 0.00559d, -0.004981d, 0.023108d, -0.008051d); + return true; + case 9362: + parameters = new EpsgExplicitOperationRecord(9362, 13.8714d, -83.9721d, 101.674d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9383: + parameters = new EpsgExplicitOperationRecord(9383, 0.0d, 0.0d, 0.0d, -8.393d, 0.749d, -10.276d, 0.0d); + return true; + case 9459: + parameters = new EpsgExplicitOperationRecord(9459, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 9460: + parameters = new EpsgExplicitOperationRecord(9460, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 9472: + parameters = new EpsgExplicitOperationRecord(9472, -0.2773d, 0.0534d, 0.4819d, -0.0935d, 0.0286d, -0.00969d, -0.028d); + return true; + case 9486: + parameters = new EpsgExplicitOperationRecord(9486, 577.84843d, 165.45019d, 390.43652d, 4.93131d, -0.96052d, -13.05072d, 7.86546d); + return true; + case 9495: + parameters = new EpsgExplicitOperationRecord(9495, 577.84843d, 165.45019d, 390.43652d, 4.93131d, -0.96052d, -13.05072d, 7.86546d); + return true; + case 9676: + parameters = new EpsgExplicitOperationRecord(9676, 23.772d, 17.49d, 17.859d, -0.3132d, -1.85274d, 1.67299d, -5.4262d); + return true; + case 9679: + parameters = new EpsgExplicitOperationRecord(9679, 283.729d, 735.942d, 261.143d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9682: + parameters = new EpsgExplicitOperationRecord(9682, -61.55d, 10.87d, 40.19d, -39.4924d, -32.7221d, -32.8979d, 9.994d); + return true; + case 9684: + parameters = new EpsgExplicitOperationRecord(9684, -61.55d, 10.87d, 40.19d, -39.4924d, -32.7221d, -32.8979d, 9.994d); + return true; + case 9686: + parameters = new EpsgExplicitOperationRecord(9686, 61.55d, -10.87d, -40.19d, 39.4924d, 32.7221d, 32.8979d, -9.994d); + return true; + case 9688: + parameters = new EpsgExplicitOperationRecord(9688, 61.55d, -10.87d, -40.19d, 39.4924d, 32.7221d, 32.8979d, -9.994d); + return true; + case 9690: + parameters = new EpsgExplicitOperationRecord(9690, 61.55d, -10.87d, -40.19d, 39.4924d, 32.7221d, 32.8979d, -9.994d); + return true; + case 9743: + parameters = new EpsgExplicitOperationRecord(9743, -307.0d, -92.0d, 127.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9751: + parameters = new EpsgExplicitOperationRecord(9751, -0.16959d, 0.35312d, 0.51846d, 0.03385d, -0.16325d, 0.03446d, 0.03693d); + return true; + case 9752: + parameters = new EpsgExplicitOperationRecord(9752, -0.16959d, 0.35312d, 0.51846d, 0.03385d, -0.16325d, 0.03446d, 0.03693d); + return true; + case 9756: + parameters = new EpsgExplicitOperationRecord(9756, 0.0058d, -0.0064d, 0.007d, -0.08d, -0.04d, -0.12d, -4.4d); + return true; + case 9757: + parameters = new EpsgExplicitOperationRecord(9757, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 9768: + parameters = new EpsgExplicitOperationRecord(9768, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9769: + parameters = new EpsgExplicitOperationRecord(9769, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9770: + parameters = new EpsgExplicitOperationRecord(9770, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9771: + parameters = new EpsgExplicitOperationRecord(9771, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9772: + parameters = new EpsgExplicitOperationRecord(9772, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9773: + parameters = new EpsgExplicitOperationRecord(9773, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9774: + parameters = new EpsgExplicitOperationRecord(9774, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9788: + parameters = new EpsgExplicitOperationRecord(9788, -0.017d, 0.058d, 0.009d, 0.001305d, 0.00068d, -0.001467d, -0.00072d); + return true; + case 9791: + parameters = new EpsgExplicitOperationRecord(9791, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9792: + parameters = new EpsgExplicitOperationRecord(9792, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9882: + parameters = new EpsgExplicitOperationRecord(9882, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9898: + parameters = new EpsgExplicitOperationRecord(9898, -265.8979d, 76.9761d, 20.2504d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9899: + parameters = new EpsgExplicitOperationRecord(9899, -189.033d, 14.1335d, -43.0901d, -0.43331d, -3.11448d, 2.63636d, 0.4752d); + return true; + case 9904: + parameters = new EpsgExplicitOperationRecord(9904, -43.0d, -337.0d, -233.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9905: + parameters = new EpsgExplicitOperationRecord(9905, -41.057d, -374.564d, -226.287d, 0.0d, 0.0d, 0.554d, 0.219d); + return true; + case 9906: + parameters = new EpsgExplicitOperationRecord(9906, -254.1d, -5.36d, -100.29d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9913: + parameters = new EpsgExplicitOperationRecord(9913, -162.619d, -276.959d, -161.764d, 0.067753d, -2.243648d, -1.158828d, -1.094246d); + return true; + case 9936: + parameters = new EpsgExplicitOperationRecord(9936, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9937: + parameters = new EpsgExplicitOperationRecord(9937, -265.9196d, 76.9506d, 20.2222d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 9938: + parameters = new EpsgExplicitOperationRecord(9938, -189.228d, 12.0035d, -42.6303d, -0.48171d, -3.09948d, 2.68639d, 0.46346d); + return true; + case 9960: + parameters = new EpsgExplicitOperationRecord(9960, -58.0d, 521.0d, 239.0d, -18.3d, 0.3d, -7.0d, 10.7d); + return true; + case 9961: + parameters = new EpsgExplicitOperationRecord(9961, -20.0d, -16.0d, 14.0d, -0.0d, -0.0d, -0.0d, -0.69d); + return true; + case 9962: + parameters = new EpsgExplicitOperationRecord(9962, 1.1d, -4.7d, 22.0d, -0.0d, -0.0d, -0.16d, 1.45d); + return true; + case 9963: + parameters = new EpsgExplicitOperationRecord(9963, -2.4d, 1.6d, 23.2d, 0.27d, -0.27d, 0.38d, 2.08d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket10(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 10085: + parameters = new EpsgExplicitOperationRecord(10085, -61.0d, 285.2d, 471.6d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10086: + parameters = new EpsgExplicitOperationRecord(10086, 48.0d, 208.0d, 382.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10089: + parameters = new EpsgExplicitOperationRecord(10089, -163.466d, 317.396d, -147.538d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10090: + parameters = new EpsgExplicitOperationRecord(10090, -170.0d, 305.0d, -145.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10091: + parameters = new EpsgExplicitOperationRecord(10091, -162.904d, 312.531d, -137.109d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10092: + parameters = new EpsgExplicitOperationRecord(10092, -158.0d, 309.0d, -151.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10093: + parameters = new EpsgExplicitOperationRecord(10093, -161.0d, 308.0d, -142.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10098: + parameters = new EpsgExplicitOperationRecord(10098, -96.062d, -82.428d, -121.753d, 4.801d, 0.345d, -1.376d, 1.496d); + return true; + case 10099: + parameters = new EpsgExplicitOperationRecord(10099, -96.062d, -82.428d, -121.753d, 4.801d, 0.345d, -1.376d, 1.496d); + return true; + case 10135: + parameters = new EpsgExplicitOperationRecord(10135, -302.0d, 272.0d, -360.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10136: + parameters = new EpsgExplicitOperationRecord(10136, -328.0d, 340.0d, -329.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10137: + parameters = new EpsgExplicitOperationRecord(10137, -352.0d, 403.0d, -287.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10138: + parameters = new EpsgExplicitOperationRecord(10138, -79.0d, 13.0d, -14.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10139: + parameters = new EpsgExplicitOperationRecord(10139, 0.5d, 3.6d, 2.4d, -0.1d, 0.0d, 0.0d, -3.1d); + return true; + case 10140: + parameters = new EpsgExplicitOperationRecord(10140, -0.5d, -2.4d, 3.8d, 0.0d, 0.0d, 0.0d, -3.4d); + return true; + case 10141: + parameters = new EpsgExplicitOperationRecord(10141, 0.2d, 0.4d, 1.6d, 0.0d, 0.0d, 0.0d, -0.3d); + return true; + case 10142: + parameters = new EpsgExplicitOperationRecord(10142, -1.2d, -1.4d, 0.6d, 0.0d, 0.0d, 0.0d, -1.4d); + return true; + case 10149: + parameters = new EpsgExplicitOperationRecord(10149, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10264: + parameters = new EpsgExplicitOperationRecord(10264, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10292: + parameters = new EpsgExplicitOperationRecord(10292, 0.0d, 0.0d, 0.0d, 0.658d, -0.208d, 0.755d, 0.0d); + return true; + case 10296: + parameters = new EpsgExplicitOperationRecord(10296, -267.407d, -47.068d, 446.357d, -0.179423d, 5.577661d, -1.27762d, 1.204866d); + return true; + case 10321: + parameters = new EpsgExplicitOperationRecord(10321, -0.584d, -1.117d, 1.125d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10324: + parameters = new EpsgExplicitOperationRecord(10324, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10333: + parameters = new EpsgExplicitOperationRecord(10333, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10334: + parameters = new EpsgExplicitOperationRecord(10334, 1003.9d, -1909.61d, -541.17d, -26.78138d, 0.42027d, -10.93206d, -0.05109d); + return true; + case 10335: + parameters = new EpsgExplicitOperationRecord(10335, 0.9109d, -2.0129d, -0.5863d, -22.749d, -26.56d, 25.706d, 2.12d); + return true; + case 10336: + parameters = new EpsgExplicitOperationRecord(10336, 909.5d, -2013.3d, -585.9d, -22.749d, -26.56d, 25.706d, 1.7d); + return true; + case 10337: + parameters = new EpsgExplicitOperationRecord(10337, 0.9109d, -2.0129d, -0.5863d, -28.711d, -11.785d, -4.417d, 2.12d); + return true; + case 10338: + parameters = new EpsgExplicitOperationRecord(10338, 909.5d, -2013.3d, -585.9d, -28.711d, -11.785d, -4.417d, 1.7d); + return true; + case 10339: + parameters = new EpsgExplicitOperationRecord(10339, -152.9d, 43.8d, 358.3d, 2.714d, 1.386d, -2.788d, -6.743d); + return true; + case 10340: + parameters = new EpsgExplicitOperationRecord(10340, -95.7d, 10.2d, 158.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10341: + parameters = new EpsgExplicitOperationRecord(10341, -165.914d, -70.607d, 305.009d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10342: + parameters = new EpsgExplicitOperationRecord(10342, -169.559d, -72.34d, 303.102d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10343: + parameters = new EpsgExplicitOperationRecord(10343, -168.52d, -72.05d, 304.3d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10344: + parameters = new EpsgExplicitOperationRecord(10344, -181.7d, 64.7d, 247.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10416: + parameters = new EpsgExplicitOperationRecord(10416, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10419: + parameters = new EpsgExplicitOperationRecord(10419, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10478: + parameters = new EpsgExplicitOperationRecord(10478, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10511: + parameters = new EpsgExplicitOperationRecord(10511, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10512: + parameters = new EpsgExplicitOperationRecord(10512, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10513: + parameters = new EpsgExplicitOperationRecord(10513, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10514: + parameters = new EpsgExplicitOperationRecord(10514, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10543: + parameters = new EpsgExplicitOperationRecord(10543, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10589: + parameters = new EpsgExplicitOperationRecord(10589, 407.379d, -685.226d, -52.577d, 0.318d, -0.107d, 0.058d, 0.207d); + return true; + case 10607: + parameters = new EpsgExplicitOperationRecord(10607, 2.6d, 5.4d, -0.9d, 0.01d, 0.07d, -0.0d, 0.06d); + return true; + case 10608: + parameters = new EpsgExplicitOperationRecord(10608, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 10646: + parameters = new EpsgExplicitOperationRecord(10646, 1138.7432d, -2064.4761d, 110.7016d, 214.615206d, -479.360036d, 164.703951d, -402.32073d); + return true; + case 10647: + parameters = new EpsgExplicitOperationRecord(10647, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 10648: + parameters = new EpsgExplicitOperationRecord(10648, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10676: + parameters = new EpsgExplicitOperationRecord(10676, 1138.7432d, -2064.4761d, 110.7016d, 214.615206d, -479.360036d, 164.703951d, -402.32073d); + return true; + case 10682: + parameters = new EpsgExplicitOperationRecord(10682, -0.5377d, 0.3946d, 0.3608d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10684: + parameters = new EpsgExplicitOperationRecord(10684, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10701: + parameters = new EpsgExplicitOperationRecord(10701, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10748: + parameters = new EpsgExplicitOperationRecord(10748, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 10749: + parameters = new EpsgExplicitOperationRecord(10749, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10750: + parameters = new EpsgExplicitOperationRecord(10750, 1276.2485d, -2016.6406d, 667.4403d, 101.005288d, -212.913401d, 68.43277d, -431.59604d); + return true; + case 10766: + parameters = new EpsgExplicitOperationRecord(10766, -366.1939d, -115.0688d, -776.7039d, -20.96308d, -16.462749d, 14.276379d, -12.809d); + return true; + case 10769: + parameters = new EpsgExplicitOperationRecord(10769, -366.1939d, -115.0688d, -776.7039d, -20.96308d, -16.462749d, 14.276379d, -12.809d); + return true; + case 10770: + parameters = new EpsgExplicitOperationRecord(10770, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10771: + parameters = new EpsgExplicitOperationRecord(10771, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10796: + parameters = new EpsgExplicitOperationRecord(10796, -136.7231d, -87.8654d, 20.1215d, -4.966933d, 9.01001d, 2.72486d, 7.86009d); + return true; + case 10797: + parameters = new EpsgExplicitOperationRecord(10797, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10803: + parameters = new EpsgExplicitOperationRecord(10803, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10804: + parameters = new EpsgExplicitOperationRecord(10804, 1276.2485d, -2016.6406d, 667.4403d, 101.005288d, -212.913401d, 68.43277d, -431.59604d); + return true; + case 10834: + parameters = new EpsgExplicitOperationRecord(10834, -2.0796d, -0.3484d, 1.7009d, -0.05465d, 0.06718d, -0.06143d, 0.0181d); + return true; + case 10835: + parameters = new EpsgExplicitOperationRecord(10835, -40.7436d, -40.0018d, -56.707d, 1.2753d, 1.42112d, -2.69445d, -4.5284d); + return true; + case 10840: + parameters = new EpsgExplicitOperationRecord(10840, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10853: + parameters = new EpsgExplicitOperationRecord(10853, 0.0d, 0.0d, 0.0d, -0.0d, -0.0d, -0.0d, 0.0d); + return true; + case 10905: + parameters = new EpsgExplicitOperationRecord(10905, -646.6552d, -165.0859d, -437.6858d, -4.77773d, 0.39139d, 1.07485d, 2.0025d); + return true; + case 10930: + parameters = new EpsgExplicitOperationRecord(10930, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10953: + parameters = new EpsgExplicitOperationRecord(10953, 1015.75d, -1920.11d, -559.77d, -27.78143d, 11.78187d, -10.16211d, -1.13124d); + return true; + case 10960: + parameters = new EpsgExplicitOperationRecord(10960, -0.30031d, -1.17512d, -0.30654d, -0.041614d, 0.026303d, 0.011214d, -0.01626d); + return true; + case 10961: + parameters = new EpsgExplicitOperationRecord(10961, 308.9415d, 136.202d, 986.3661d, 3.8742d, -3.77827d, 7.61345d, -171.67315d); + return true; + case 10963: + parameters = new EpsgExplicitOperationRecord(10963, 218.233d, 270.6151d, 253.1391d, -0.26337d, 0.15733d, 1.19862d, -59.923872d); + return true; + case 10965: + parameters = new EpsgExplicitOperationRecord(10965, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10969: + parameters = new EpsgExplicitOperationRecord(10969, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 10996: + parameters = new EpsgExplicitOperationRecord(10996, 19.019d, 115.122d, -97.287d, 3.577824d, -3.484437d, -2.767646d, 18.6084754d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket11(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 11010: + parameters = new EpsgExplicitOperationRecord(11010, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11011: + parameters = new EpsgExplicitOperationRecord(11011, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11028: + parameters = new EpsgExplicitOperationRecord(11028, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11038: + parameters = new EpsgExplicitOperationRecord(11038, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11040: + parameters = new EpsgExplicitOperationRecord(11040, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11044: + parameters = new EpsgExplicitOperationRecord(11044, -0.0533d, 0.0136d, -0.0707d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11048: + parameters = new EpsgExplicitOperationRecord(11048, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11050: + parameters = new EpsgExplicitOperationRecord(11050, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11054: + parameters = new EpsgExplicitOperationRecord(11054, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11058: + parameters = new EpsgExplicitOperationRecord(11058, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11060: + parameters = new EpsgExplicitOperationRecord(11060, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11064: + parameters = new EpsgExplicitOperationRecord(11064, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11067: + parameters = new EpsgExplicitOperationRecord(11067, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11071: + parameters = new EpsgExplicitOperationRecord(11071, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11073: + parameters = new EpsgExplicitOperationRecord(11073, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11080: + parameters = new EpsgExplicitOperationRecord(11080, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11081: + parameters = new EpsgExplicitOperationRecord(11081, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11083: + parameters = new EpsgExplicitOperationRecord(11083, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11084: + parameters = new EpsgExplicitOperationRecord(11084, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11088: + parameters = new EpsgExplicitOperationRecord(11088, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11090: + parameters = new EpsgExplicitOperationRecord(11090, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11094: + parameters = new EpsgExplicitOperationRecord(11094, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11096: + parameters = new EpsgExplicitOperationRecord(11096, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11100: + parameters = new EpsgExplicitOperationRecord(11100, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11105: + parameters = new EpsgExplicitOperationRecord(11105, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11109: + parameters = new EpsgExplicitOperationRecord(11109, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11111: + parameters = new EpsgExplicitOperationRecord(11111, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11123: + parameters = new EpsgExplicitOperationRecord(11123, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11125: + parameters = new EpsgExplicitOperationRecord(11125, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11135: + parameters = new EpsgExplicitOperationRecord(11135, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11136: + parameters = new EpsgExplicitOperationRecord(11136, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11137: + parameters = new EpsgExplicitOperationRecord(11137, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11138: + parameters = new EpsgExplicitOperationRecord(11138, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11149: + parameters = new EpsgExplicitOperationRecord(11149, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11150: + parameters = new EpsgExplicitOperationRecord(11150, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11151: + parameters = new EpsgExplicitOperationRecord(11151, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11152: + parameters = new EpsgExplicitOperationRecord(11152, -236.635d, 98.535d, 201.265d, -17.79d, 3.673d, -24.3695d, 0.0d); + return true; + case 11164: + parameters = new EpsgExplicitOperationRecord(11164, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11166: + parameters = new EpsgExplicitOperationRecord(11166, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11167: + parameters = new EpsgExplicitOperationRecord(11167, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11168: + parameters = new EpsgExplicitOperationRecord(11168, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11182: + parameters = new EpsgExplicitOperationRecord(11182, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11185: + parameters = new EpsgExplicitOperationRecord(11185, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11190: + parameters = new EpsgExplicitOperationRecord(11190, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11191: + parameters = new EpsgExplicitOperationRecord(11191, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11192: + parameters = new EpsgExplicitOperationRecord(11192, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11195: + parameters = new EpsgExplicitOperationRecord(11195, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11200: + parameters = new EpsgExplicitOperationRecord(11200, -41.1d, -52.0d, 101.1d, 1.348d, 0.719d, 2.684d, -7.9d); + return true; + case 11205: + parameters = new EpsgExplicitOperationRecord(11205, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11207: + parameters = new EpsgExplicitOperationRecord(11207, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11209: + parameters = new EpsgExplicitOperationRecord(11209, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11210: + parameters = new EpsgExplicitOperationRecord(11210, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11216: + parameters = new EpsgExplicitOperationRecord(11216, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11218: + parameters = new EpsgExplicitOperationRecord(11218, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11220: + parameters = new EpsgExplicitOperationRecord(11220, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11227: + parameters = new EpsgExplicitOperationRecord(11227, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11229: + parameters = new EpsgExplicitOperationRecord(11229, 197.8579d, 146.5947d, -108.8501d, 0.85735d, -0.36082d, -0.38626d, -8.356137d); + return true; + case 11308: + parameters = new EpsgExplicitOperationRecord(11308, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 11397: + parameters = new EpsgExplicitOperationRecord(11397, 565.7381d, 50.4018d, 465.2904d, -1.91514d, 1.60363d, -9.09546d, 4.07244d); + return true; + default: + parameters = default; + return false; + } + } + + private static bool TryGetExplicitOperationParametersBucket15(int operationCode, out EpsgExplicitOperationRecord parameters) + { + switch (operationCode) + { + case 15483: + parameters = new EpsgExplicitOperationRecord(15483, -146.414d, 507.337d, 680.507d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15484: + parameters = new EpsgExplicitOperationRecord(15484, -146.414d, 507.337d, 680.507d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15485: + parameters = new EpsgExplicitOperationRecord(15485, -67.35d, 3.88d, -38.22d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15493: + parameters = new EpsgExplicitOperationRecord(15493, -94.031d, -83.317d, 116.708d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15494: + parameters = new EpsgExplicitOperationRecord(15494, 274.164d, 677.282d, 226.704d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15495: + parameters = new EpsgExplicitOperationRecord(15495, -171.16d, 17.29d, 325.21d, 0.0d, 0.0d, 0.814d, -0.38d); + return true; + case 15496: + parameters = new EpsgExplicitOperationRecord(15496, 44.107d, -116.147d, -54.648d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15497: + parameters = new EpsgExplicitOperationRecord(15497, 28.0d, -121.0d, -77.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15699: + parameters = new EpsgExplicitOperationRecord(15699, -2.0d, 124.7d, 196.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15701: + parameters = new EpsgExplicitOperationRecord(15701, 275.57d, 676.78d, 229.6d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15702: + parameters = new EpsgExplicitOperationRecord(15702, 278.9d, 684.39d, 226.05d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15703: + parameters = new EpsgExplicitOperationRecord(15703, 271.905d, 669.593d, 231.495d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15705: + parameters = new EpsgExplicitOperationRecord(15705, -83.13d, -104.95d, 114.63d, 0.0d, 0.0d, 0.554d, 0.0d); + return true; + case 15706: + parameters = new EpsgExplicitOperationRecord(15706, -93.6d, -83.7d, 113.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15707: + parameters = new EpsgExplicitOperationRecord(15707, -118.996d, -111.177d, -198.687d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15708: + parameters = new EpsgExplicitOperationRecord(15708, -127.62d, -67.24d, -47.04d, -3.068d, 4.903d, 1.578d, -1.06d); + return true; + case 15709: + parameters = new EpsgExplicitOperationRecord(15709, 124.5d, -63.5d, -281.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15710: + parameters = new EpsgExplicitOperationRecord(15710, -160.0d, 315.0d, -142.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15711: + parameters = new EpsgExplicitOperationRecord(15711, -158.0d, 309.0d, -147.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15712: + parameters = new EpsgExplicitOperationRecord(15712, -161.0d, 310.0d, -145.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15713: + parameters = new EpsgExplicitOperationRecord(15713, -133.0d, -321.0d, 50.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15714: + parameters = new EpsgExplicitOperationRecord(15714, -806.413d, -263.5d, -622.671d, -6.018583e-05d, 1.450001e-05d, 0.0001892455d, -20.81616d); + return true; + case 15715: + parameters = new EpsgExplicitOperationRecord(15715, -806.413d, -263.5d, -622.671d, -6.018583e-05d, 1.450001e-05d, 0.0001892455d, -20.81616d); + return true; + case 15716: + parameters = new EpsgExplicitOperationRecord(15716, 100.783d, 187.382d, -47.0d, 4.471839e-05d, -1.175093e-05d, 4.027967e-05d, -13.56561d); + return true; + case 15717: + parameters = new EpsgExplicitOperationRecord(15717, 100.783d, 187.382d, -47.0d, 4.471839e-05d, -1.175093e-05d, 4.027967e-05d, -13.56561d); + return true; + case 15718: + parameters = new EpsgExplicitOperationRecord(15718, 336.026d, 348.565d, 252.978d, 8.358813e-05d, 3.057474e-05d, -7.573031e-06d, -5.771909d); + return true; + case 15719: + parameters = new EpsgExplicitOperationRecord(15719, 336.026d, 348.565d, 252.978d, 8.358813e-05d, 3.057474e-05d, -7.573031e-06d, -5.771909d); + return true; + case 15720: + parameters = new EpsgExplicitOperationRecord(15720, 963.273d, 486.386d, 190.997d, 7.992171e-05d, 8.090696e-06d, -0.0001051699d, -13.89914d); + return true; + case 15721: + parameters = new EpsgExplicitOperationRecord(15721, 963.273d, 486.386d, 190.997d, 7.992171e-05d, 8.090696e-06d, -0.0001051699d, -13.89914d); + return true; + case 15722: + parameters = new EpsgExplicitOperationRecord(15722, -90.29d, 247.559d, -21.989d, 4.216369e-05d, 2.030416e-05d, 6.209623e-05d, 2.181658d); + return true; + case 15723: + parameters = new EpsgExplicitOperationRecord(15723, -90.29d, 247.559d, -21.989d, 4.216369e-05d, 2.030416e-05d, 6.209623e-05d, 2.181658d); + return true; + case 15724: + parameters = new EpsgExplicitOperationRecord(15724, -0.562d, 244.299d, -456.938d, -3.329153e-05d, 4.001009e-05d, 4.507206e-05d, 3.74656d); + return true; + case 15725: + parameters = new EpsgExplicitOperationRecord(15725, -0.562d, 244.299d, -456.938d, -3.329153e-05d, 4.001009e-05d, 4.507206e-05d, 3.74656d); + return true; + case 15726: + parameters = new EpsgExplicitOperationRecord(15726, -305.356d, 222.004d, -30.023d, 4.698084e-05d, -5.003123e-06d, 9.578655e-05d, 6.325747d); + return true; + case 15727: + parameters = new EpsgExplicitOperationRecord(15727, -305.356d, 222.004d, -30.023d, 4.698084e-05d, -5.003123e-06d, 9.578655e-05d, 6.325747d); + return true; + case 15728: + parameters = new EpsgExplicitOperationRecord(15728, 221.899d, 274.136d, -397.554d, -1.361573e-05d, 2.174431e-06d, 1.36241e-05d, -2.199943d); + return true; + case 15729: + parameters = new EpsgExplicitOperationRecord(15729, 221.899d, 274.136d, -397.554d, -1.361573e-05d, 2.174431e-06d, 1.36241e-05d, -2.199943d); + return true; + case 15730: + parameters = new EpsgExplicitOperationRecord(15730, 300.449d, 293.757d, -317.306d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15731: + parameters = new EpsgExplicitOperationRecord(15731, 308.833d, 282.519d, -314.571d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15732: + parameters = new EpsgExplicitOperationRecord(15732, 311.118d, 289.167d, -310.641d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15733: + parameters = new EpsgExplicitOperationRecord(15733, 306.666d, 315.063d, -318.837d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15734: + parameters = new EpsgExplicitOperationRecord(15734, 307.871d, 305.803d, -311.992d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15735: + parameters = new EpsgExplicitOperationRecord(15735, 302.934d, 307.805d, -312.121d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15736: + parameters = new EpsgExplicitOperationRecord(15736, 295.282d, 321.293d, -311.001d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15737: + parameters = new EpsgExplicitOperationRecord(15737, 302.529d, 317.979d, -319.08d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15738: + parameters = new EpsgExplicitOperationRecord(15738, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15739: + parameters = new EpsgExplicitOperationRecord(15739, 565.2369d, 50.0087d, 465.658d, -1.9725d, 1.7004d, -9.0677d, 4.0812d); + return true; + case 15740: + parameters = new EpsgExplicitOperationRecord(15740, 593.0297d, 26.0038d, 478.7534d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15741: + parameters = new EpsgExplicitOperationRecord(15741, -187.5d, 14.1d, 237.6d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15742: + parameters = new EpsgExplicitOperationRecord(15742, -190.421d, 8.532d, 238.69d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15743: + parameters = new EpsgExplicitOperationRecord(15743, -83.58d, -397.54d, 458.78d, -17.595d, -2.847d, 4.256d, 3.225d); + return true; + case 15745: + parameters = new EpsgExplicitOperationRecord(15745, -123.02d, -158.95d, -168.47d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15746: + parameters = new EpsgExplicitOperationRecord(15746, 0.0d, -0.15d, 0.68d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15750: + parameters = new EpsgExplicitOperationRecord(15750, -7.0d, 215.0d, 225.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15751: + parameters = new EpsgExplicitOperationRecord(15751, 94.0d, -948.0d, -1262.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15752: + parameters = new EpsgExplicitOperationRecord(15752, -86.0d, -98.0d, -119.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15754: + parameters = new EpsgExplicitOperationRecord(15754, -158.0d, 315.0d, -148.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15755: + parameters = new EpsgExplicitOperationRecord(15755, -90.2d, -87.32d, 114.17d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15759: + parameters = new EpsgExplicitOperationRecord(15759, 217.037d, 86.959d, 23.956d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15778: + parameters = new EpsgExplicitOperationRecord(15778, -114.7d, -98.5d, -150.7d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15779: + parameters = new EpsgExplicitOperationRecord(15779, 283.7d, 735.9d, 261.1d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15782: + parameters = new EpsgExplicitOperationRecord(15782, -148.0d, 136.0d, 90.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15783: + parameters = new EpsgExplicitOperationRecord(15783, 287.0d, 178.0d, -136.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15784: + parameters = new EpsgExplicitOperationRecord(15784, -770.1d, 158.4d, -498.2d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15787: + parameters = new EpsgExplicitOperationRecord(15787, -79.9d, -158.0d, -168.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15788: + parameters = new EpsgExplicitOperationRecord(15788, -127.8d, -52.3d, 152.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15789: + parameters = new EpsgExplicitOperationRecord(15789, -128.5d, -53.0d, 153.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15790: + parameters = new EpsgExplicitOperationRecord(15790, -255.0d, -29.0d, -105.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15791: + parameters = new EpsgExplicitOperationRecord(15791, -259.99d, -5.28d, -97.09d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15792: + parameters = new EpsgExplicitOperationRecord(15792, -123.0d, 98.0d, 2.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15793: + parameters = new EpsgExplicitOperationRecord(15793, 31.95d, 300.99d, 419.19d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15794: + parameters = new EpsgExplicitOperationRecord(15794, -491.0d, -22.0d, 435.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15795: + parameters = new EpsgExplicitOperationRecord(15795, 114.0d, -116.0d, -333.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15796: + parameters = new EpsgExplicitOperationRecord(15796, 145.0d, 75.0d, -272.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15797: + parameters = new EpsgExplicitOperationRecord(15797, -205.0d, 107.0d, 53.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15798: + parameters = new EpsgExplicitOperationRecord(15798, -320.0d, 550.0d, -494.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15799: + parameters = new EpsgExplicitOperationRecord(15799, 124.0d, -234.0d, -25.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15800: + parameters = new EpsgExplicitOperationRecord(15800, -79.0d, -129.0d, 145.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15801: + parameters = new EpsgExplicitOperationRecord(15801, -127.0d, -769.0d, 472.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15802: + parameters = new EpsgExplicitOperationRecord(15802, -104.0d, -129.0d, 239.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15803: + parameters = new EpsgExplicitOperationRecord(15803, 298.0d, -304.0d, -375.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15804: + parameters = new EpsgExplicitOperationRecord(15804, -2.0d, 151.0d, 181.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15805: + parameters = new EpsgExplicitOperationRecord(15805, 230.0d, -199.0d, -752.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15806: + parameters = new EpsgExplicitOperationRecord(15806, 211.0d, 147.0d, 111.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15807: + parameters = new EpsgExplicitOperationRecord(15807, 252.0d, -209.0d, -751.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15808: + parameters = new EpsgExplicitOperationRecord(15808, 208.0d, -435.0d, -229.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15809: + parameters = new EpsgExplicitOperationRecord(15809, 189.0d, -79.0d, -202.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15810: + parameters = new EpsgExplicitOperationRecord(15810, 647.0d, 1777.0d, -1124.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15811: + parameters = new EpsgExplicitOperationRecord(15811, -270.0d, 13.0d, 62.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15812: + parameters = new EpsgExplicitOperationRecord(15812, 260.0d, 12.0d, -147.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15813: + parameters = new EpsgExplicitOperationRecord(15813, -794.0d, 119.0d, -298.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15814: + parameters = new EpsgExplicitOperationRecord(15814, 42.0d, 124.0d, 147.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15815: + parameters = new EpsgExplicitOperationRecord(15815, -307.0d, -92.0d, 127.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15816: + parameters = new EpsgExplicitOperationRecord(15816, -632.0d, 438.0d, -609.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15817: + parameters = new EpsgExplicitOperationRecord(15817, 912.0d, -58.0d, 1227.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15818: + parameters = new EpsgExplicitOperationRecord(15818, 403.0d, -81.0d, 277.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15819: + parameters = new EpsgExplicitOperationRecord(15819, 185.0d, 165.0d, 42.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15820: + parameters = new EpsgExplicitOperationRecord(15820, 170.0d, 42.0d, 84.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15822: + parameters = new EpsgExplicitOperationRecord(15822, 102.0d, 52.0d, -38.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15823: + parameters = new EpsgExplicitOperationRecord(15823, 276.0d, -57.0d, 149.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15824: + parameters = new EpsgExplicitOperationRecord(15824, 61.0d, -285.0d, -181.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15825: + parameters = new EpsgExplicitOperationRecord(15825, 89.0d, -279.0d, -183.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15826: + parameters = new EpsgExplicitOperationRecord(15826, 45.0d, -290.0d, -172.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15827: + parameters = new EpsgExplicitOperationRecord(15827, 65.0d, -290.0d, -190.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15828: + parameters = new EpsgExplicitOperationRecord(15828, 58.0d, -283.0d, -182.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15829: + parameters = new EpsgExplicitOperationRecord(15829, 44.4d, 109.0d, 151.7d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15830: + parameters = new EpsgExplicitOperationRecord(15830, 67.8d, 106.1d, 138.8d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15831: + parameters = new EpsgExplicitOperationRecord(15831, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15833: + parameters = new EpsgExplicitOperationRecord(15833, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15842: + parameters = new EpsgExplicitOperationRecord(15842, -156.0d, -271.0d, -189.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15843: + parameters = new EpsgExplicitOperationRecord(15843, 0.0d, 0.0d, 1.5d, -0.0d, -0.0d, 0.076d, 0.0d); + return true; + case 15844: + parameters = new EpsgExplicitOperationRecord(15844, 25.0d, -141.0d, -80.0d, -0.0d, 0.35d, 0.66d, 0.0d); + return true; + case 15846: + parameters = new EpsgExplicitOperationRecord(15846, -146.21d, 112.63d, 4.05d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15847: + parameters = new EpsgExplicitOperationRecord(15847, 253.0d, -132.0d, -127.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15848: + parameters = new EpsgExplicitOperationRecord(15848, -13.0d, -348.0d, 292.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15849: + parameters = new EpsgExplicitOperationRecord(15849, -106.0d, -87.0d, 188.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15850: + parameters = new EpsgExplicitOperationRecord(15850, 145.0d, -187.0d, 103.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15852: + parameters = new EpsgExplicitOperationRecord(15852, -3.0d, 154.0d, 177.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15853: + parameters = new EpsgExplicitOperationRecord(15853, -7.0d, 151.0d, 175.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15854: + parameters = new EpsgExplicitOperationRecord(15854, -7.0d, 151.0d, 178.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15855: + parameters = new EpsgExplicitOperationRecord(15855, -8.0d, 125.0d, 190.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15856: + parameters = new EpsgExplicitOperationRecord(15856, -7.0d, 158.0d, 172.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15860: + parameters = new EpsgExplicitOperationRecord(15860, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15865: + parameters = new EpsgExplicitOperationRecord(15865, 25.0d, -141.0d, -78.5d, -0.0d, 0.35d, 0.736d, 0.0d); + return true; + case 15866: + parameters = new EpsgExplicitOperationRecord(15866, -153.33d, -169.41d, 86.39d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15867: + parameters = new EpsgExplicitOperationRecord(15867, 599.4d, 72.4d, 419.2d, -0.062d, -0.022d, -2.723d, 6.46d); + return true; + case 15868: + parameters = new EpsgExplicitOperationRecord(15868, 612.4d, 77.0d, 440.2d, -0.054d, 0.057d, -2.797d, 2.55d); + return true; + case 15869: + parameters = new EpsgExplicitOperationRecord(15869, 612.4d, 77.0d, 440.2d, -0.054d, 0.057d, -2.797d, 2.55d); + return true; + case 15870: + parameters = new EpsgExplicitOperationRecord(15870, -80.01d, 253.26d, 291.19d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15872: + parameters = new EpsgExplicitOperationRecord(15872, 84.1d, -320.1d, 218.7d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15873: + parameters = new EpsgExplicitOperationRecord(15873, -206.1d, -174.7d, -87.7d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15874: + parameters = new EpsgExplicitOperationRecord(15874, -169.559d, -72.34d, 303.102d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15875: + parameters = new EpsgExplicitOperationRecord(15875, 265.025d, 384.929d, -194.046d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15876: + parameters = new EpsgExplicitOperationRecord(15876, 0.0d, 0.0d, 4.5d, 0.0d, 0.0d, 0.554d, 0.2263d); + return true; + case 15877: + parameters = new EpsgExplicitOperationRecord(15877, -35.173d, 136.571d, -36.964d, -1.37d, 0.842d, 4.718d, -1.537d); + return true; + case 15878: + parameters = new EpsgExplicitOperationRecord(15878, 51.0d, 391.0d, -36.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15879: + parameters = new EpsgExplicitOperationRecord(15879, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15880: + parameters = new EpsgExplicitOperationRecord(15880, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15881: + parameters = new EpsgExplicitOperationRecord(15881, -56.263d, 16.136d, -22.856d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15882: + parameters = new EpsgExplicitOperationRecord(15882, -11.64d, -348.6d, 291.98d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15883: + parameters = new EpsgExplicitOperationRecord(15883, 335.47d, 222.58d, -230.94d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15884: + parameters = new EpsgExplicitOperationRecord(15884, 287.58d, 177.78d, -135.41d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15885: + parameters = new EpsgExplicitOperationRecord(15885, -56.263d, 16.136d, -22.856d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15886: + parameters = new EpsgExplicitOperationRecord(15886, -10.18d, -350.43d, 291.37d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15887: + parameters = new EpsgExplicitOperationRecord(15887, 97.297d, -263.243d, 310.879d, -1.5999d, 0.8387d, 3.1409d, 13.326d); + return true; + case 15888: + parameters = new EpsgExplicitOperationRecord(15888, 48.812d, -205.932d, 343.993d, -3.4427d, -0.4999d, 4.0878d, 6.5215d); + return true; + case 15889: + parameters = new EpsgExplicitOperationRecord(15889, -166.0684d, -154.7826d, 254.8282d, -37.546d, 7.7018d, -10.2029d, -30.84d); + return true; + case 15890: + parameters = new EpsgExplicitOperationRecord(15890, 137.092d, 131.675d, 91.478d, -1.9435d, -11.5995d, -4.3316d, -7.4801d); + return true; + case 15891: + parameters = new EpsgExplicitOperationRecord(15891, -408.809d, 366.857d, -412.987d, 1.8843d, -0.5308d, 2.1657d, -121.0994d); + return true; + case 15892: + parameters = new EpsgExplicitOperationRecord(15892, -122.386d, -188.707d, 103.334d, 3.511d, -4.9665d, -5.7048d, 4.4799d); + return true; + case 15893: + parameters = new EpsgExplicitOperationRecord(15893, 244.42d, 85.352d, 168.129d, -8.936d, 7.752d, 12.5952d, 14.2723d); + return true; + case 15894: + parameters = new EpsgExplicitOperationRecord(15894, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15896: + parameters = new EpsgExplicitOperationRecord(15896, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15897: + parameters = new EpsgExplicitOperationRecord(15897, 51.0d, 391.0d, -36.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15899: + parameters = new EpsgExplicitOperationRecord(15899, 105.0d, 326.0d, -102.5d, 0.0d, 0.0d, 0.814d, -0.6d); + return true; + case 15900: + parameters = new EpsgExplicitOperationRecord(15900, -45.0d, 417.0d, -3.5d, 0.0d, 0.0d, 0.814d, -0.6d); + return true; + case 15901: + parameters = new EpsgExplicitOperationRecord(15901, 287.58d, 177.78d, -135.41d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15902: + parameters = new EpsgExplicitOperationRecord(15902, 335.47d, 222.58d, -230.94d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15903: + parameters = new EpsgExplicitOperationRecord(15903, -11.64d, -348.6d, 291.98d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15904: + parameters = new EpsgExplicitOperationRecord(15904, -10.18d, -350.43d, 291.37d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15908: + parameters = new EpsgExplicitOperationRecord(15908, -208.4058d, -109.8777d, -2.5764d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15909: + parameters = new EpsgExplicitOperationRecord(15909, -115.8543d, -99.0583d, -152.4616d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15911: + parameters = new EpsgExplicitOperationRecord(15911, -1.977d, -13.06d, -9.993d, 0.364d, 0.254d, 0.689d, -1.037d); + return true; + case 15912: + parameters = new EpsgExplicitOperationRecord(15912, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15913: + parameters = new EpsgExplicitOperationRecord(15913, 0.0d, 125.0d, 196.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15918: + parameters = new EpsgExplicitOperationRecord(15918, 12.646d, -155.176d, -80.863d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15919: + parameters = new EpsgExplicitOperationRecord(15919, 15.53d, -113.82d, -41.38d, 0.0d, 0.0d, 0.814d, -0.38d); + return true; + case 15920: + parameters = new EpsgExplicitOperationRecord(15920, 31.4d, -144.3d, -74.8d, 0.0d, 0.0d, 0.814d, -0.38d); + return true; + case 15921: + parameters = new EpsgExplicitOperationRecord(15921, 15.8d, -154.4d, -82.3d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15923: + parameters = new EpsgExplicitOperationRecord(15923, -117.7d, -100.3d, -152.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15924: + parameters = new EpsgExplicitOperationRecord(15924, 92.5515d, 10.8194d, -149.8852d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15925: + parameters = new EpsgExplicitOperationRecord(15925, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15926: + parameters = new EpsgExplicitOperationRecord(15926, -33.722d, 153.789d, 94.959d, -8.581d, -4.478d, 4.54d, 8.95d); + return true; + case 15927: + parameters = new EpsgExplicitOperationRecord(15927, -33.722d, 153.789d, 94.959d, -8.581d, -4.478d, 4.54d, 8.95d); + return true; + case 15928: + parameters = new EpsgExplicitOperationRecord(15928, -106.8686d, 52.2978d, -103.7239d, 0.3366d, -0.457d, 1.8422d, -1.2747d); + return true; + case 15929: + parameters = new EpsgExplicitOperationRecord(15929, -106.8686d, 52.2978d, -103.7239d, 0.3366d, -0.457d, 1.8422d, -1.2747d); + return true; + case 15931: + parameters = new EpsgExplicitOperationRecord(15931, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15934: + parameters = new EpsgExplicitOperationRecord(15934, 565.2369d, 50.0087d, 465.658d, -1.9725d, 1.7004d, -9.0677d, 4.0812d); + return true; + case 15935: + parameters = new EpsgExplicitOperationRecord(15935, 18.0d, -136.8d, -73.7d, 0.0d, 0.0d, 0.814d, -0.38d); + return true; + case 15936: + parameters = new EpsgExplicitOperationRecord(15936, 11.911d, -154.833d, -80.079d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15937: + parameters = new EpsgExplicitOperationRecord(15937, -245.8d, -152.2d, 382.9d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15938: + parameters = new EpsgExplicitOperationRecord(15938, -225.4d, -158.7d, 380.8d, 0.0d, 0.0d, 0.814d, -0.38d); + return true; + case 15952: + parameters = new EpsgExplicitOperationRecord(15952, -244.2d, -149.8d, 379.3d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15953: + parameters = new EpsgExplicitOperationRecord(15953, -250.7d, -157.9d, 380.4d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15957: + parameters = new EpsgExplicitOperationRecord(15957, 163.511d, 127.533d, -159.789d, 0.0d, 0.0d, 0.814d, -0.6d); + return true; + case 15964: + parameters = new EpsgExplicitOperationRecord(15964, -86.277d, -108.879d, -120.181d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15965: + parameters = new EpsgExplicitOperationRecord(15965, 589.0d, 76.0d, 480.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15967: + parameters = new EpsgExplicitOperationRecord(15967, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15969: + parameters = new EpsgExplicitOperationRecord(15969, -292.295d, 248.758d, 429.447d, 4.9971d, 2.99d, 6.6906d, 1.0289d); + return true; + case 15970: + parameters = new EpsgExplicitOperationRecord(15970, -292.295d, 248.758d, 429.447d, 4.9971d, 2.99d, 6.6906d, 1.0289d); + return true; + case 15971: + parameters = new EpsgExplicitOperationRecord(15971, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15972: + parameters = new EpsgExplicitOperationRecord(15972, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15974: + parameters = new EpsgExplicitOperationRecord(15974, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15975: + parameters = new EpsgExplicitOperationRecord(15975, 54.4d, -20.1d, 183.1d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15976: + parameters = new EpsgExplicitOperationRecord(15976, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15978: + parameters = new EpsgExplicitOperationRecord(15978, 2.478d, 149.752d, 197.726d, 0.526d, 0.498d, -0.501d, 0.685d); + return true; + case 15979: + parameters = new EpsgExplicitOperationRecord(15979, -117.808d, -51.536d, 137.784d, 0.303d, 0.446d, 0.234d, -0.29d); + return true; + case 15980: + parameters = new EpsgExplicitOperationRecord(15980, -117.808d, -51.536d, 137.784d, 0.303d, 0.446d, 0.234d, -0.29d); + return true; + case 15993: + parameters = new EpsgExplicitOperationRecord(15993, 68.1564d, 32.7756d, 80.2249d, -2.20333014d, -2.19256447d, 2.54166911d, -0.14155333d); + return true; + case 15994: + parameters = new EpsgExplicitOperationRecord(15994, 2.3287d, -147.0425d, -92.0802d, -0.3092483d, 0.32482185d, 0.49729934d, 5.68906266d); + return true; + case 15995: + parameters = new EpsgExplicitOperationRecord(15995, 2.329d, -147.042d, -92.08d, -0.309d, 0.325d, 0.497d, 5.69d); + return true; + case 15996: + parameters = new EpsgExplicitOperationRecord(15996, 28.0d, -121.0d, -77.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15997: + parameters = new EpsgExplicitOperationRecord(15997, 23.0d, -124.0d, -82.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15998: + parameters = new EpsgExplicitOperationRecord(15998, 26.0d, -121.0d, -78.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + case 15999: + parameters = new EpsgExplicitOperationRecord(15999, 24.0d, -130.0d, -92.0d, 0.0d, 0.0d, 0.0d, 0.0d); + return true; + default: + parameters = default; + return false; + } + } + + } +} diff --git a/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Projected.g.cs b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Projected.g.cs new file mode 100644 index 00000000..abcfd491 --- /dev/null +++ b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Projected.g.cs @@ -0,0 +1,16289 @@ +// +// Generated by tools\Generate-EpsgManagedData.ps1 +// Source: EPSG-v12_054-WKT.Zip +// +#pragma warning disable SA0001, SA1512, SA1518, SA1600, SA1614, SA1616, SA1633, SA1636 +using System; + +namespace ProjNet.Data.Generated +{ + internal static partial class EpsgGeneratedCatalog + { + internal static bool TryGetProjectedCrs(int index, out EpsgProjectedCrsRecord record) + { + switch (index / 1000) + { + case 0: + return TryGetProjectedCrsBucket0(index, out record); + case 1: + return TryGetProjectedCrsBucket1(index, out record); + case 2: + return TryGetProjectedCrsBucket2(index, out record); + case 3: + return TryGetProjectedCrsBucket3(index, out record); + case 4: + return TryGetProjectedCrsBucket4(index, out record); + case 5: + return TryGetProjectedCrsBucket5(index, out record); + default: + record = default; + return false; + } + } + + private static bool TryGetProjectedCrsBucket0(int index, out EpsgProjectedCrsRecord record) + { + switch (index) + { + case 0: + record = new EpsgProjectedCrsRecord(2000, "Anguilla 1957 / British West Indies Grid", 4600, 4400, 19942); + return true; + case 1: + record = new EpsgProjectedCrsRecord(2001, "Antigua 1943 / British West Indies Grid", 4601, 4400, 19942); + return true; + case 2: + record = new EpsgProjectedCrsRecord(2002, "Dominica 1945 / British West Indies Grid", 4602, 4400, 19942); + return true; + case 3: + record = new EpsgProjectedCrsRecord(2003, "Grenada 1953 / British West Indies Grid", 4603, 4400, 19942); + return true; + case 4: + record = new EpsgProjectedCrsRecord(2004, "Montserrat 1958 / British West Indies Grid", 4604, 4400, 19942); + return true; + case 5: + record = new EpsgProjectedCrsRecord(2005, "St. Kitts 1955 / British West Indies Grid", 4605, 4400, 19942); + return true; + case 6: + record = new EpsgProjectedCrsRecord(2006, "St. Lucia 1955 / British West Indies Grid", 4606, 4400, 19942); + return true; + case 7: + record = new EpsgProjectedCrsRecord(2007, "St. Vincent 45 / British West Indies Grid", 4607, 4400, 19942); + return true; + case 8: + record = new EpsgProjectedCrsRecord(2009, "NAD27(CGQ77) / SCoPQ zone 3", 4609, 4499, 17703); + return true; + case 9: + record = new EpsgProjectedCrsRecord(2010, "NAD27(CGQ77) / SCoPQ zone 4", 4609, 4499, 17704); + return true; + case 10: + record = new EpsgProjectedCrsRecord(2011, "NAD27(CGQ77) / SCoPQ zone 5", 4609, 4499, 17705); + return true; + case 11: + record = new EpsgProjectedCrsRecord(2012, "NAD27(CGQ77) / SCoPQ zone 6", 4609, 4499, 17706); + return true; + case 12: + record = new EpsgProjectedCrsRecord(2013, "NAD27(CGQ77) / SCoPQ zone 7", 4609, 4499, 17707); + return true; + case 13: + record = new EpsgProjectedCrsRecord(2014, "NAD27(CGQ77) / SCoPQ zone 8", 4609, 4499, 17708); + return true; + case 14: + record = new EpsgProjectedCrsRecord(2015, "NAD27(CGQ77) / SCoPQ zone 9", 4609, 4499, 17709); + return true; + case 15: + record = new EpsgProjectedCrsRecord(2016, "NAD27(CGQ77) / SCoPQ zone 10", 4609, 4499, 17710); + return true; + case 16: + record = new EpsgProjectedCrsRecord(2017, "NAD27(76) / MTM zone 8", 4608, 4499, 17708); + return true; + case 17: + record = new EpsgProjectedCrsRecord(2018, "NAD27(76) / MTM zone 9", 4608, 4499, 17709); + return true; + case 18: + record = new EpsgProjectedCrsRecord(2019, "NAD27(76) / MTM zone 10", 4608, 4499, 17710); + return true; + case 19: + record = new EpsgProjectedCrsRecord(2020, "NAD27(76) / MTM zone 11", 4608, 4400, 17711); + return true; + case 20: + record = new EpsgProjectedCrsRecord(2021, "NAD27(76) / MTM zone 12", 4608, 4400, 17712); + return true; + case 21: + record = new EpsgProjectedCrsRecord(2022, "NAD27(76) / MTM zone 13", 4608, 4400, 17713); + return true; + case 22: + record = new EpsgProjectedCrsRecord(2023, "NAD27(76) / MTM zone 14", 4608, 4400, 17714); + return true; + case 23: + record = new EpsgProjectedCrsRecord(2024, "NAD27(76) / MTM zone 15", 4608, 4400, 17715); + return true; + case 24: + record = new EpsgProjectedCrsRecord(2025, "NAD27(76) / MTM zone 16", 4608, 4400, 17716); + return true; + case 25: + record = new EpsgProjectedCrsRecord(2026, "NAD27(76) / MTM zone 17", 4608, 4400, 17717); + return true; + case 26: + record = new EpsgProjectedCrsRecord(2027, "NAD27(76) / UTM zone 15N", 4608, 4400, 16015); + return true; + case 27: + record = new EpsgProjectedCrsRecord(2028, "NAD27(76) / UTM zone 16N", 4608, 4400, 16016); + return true; + case 28: + record = new EpsgProjectedCrsRecord(2029, "NAD27(76) / UTM zone 17N", 4608, 4400, 16017); + return true; + case 29: + record = new EpsgProjectedCrsRecord(2030, "NAD27(76) / UTM zone 18N", 4608, 4400, 16018); + return true; + case 30: + record = new EpsgProjectedCrsRecord(2031, "NAD27(CGQ77) / UTM zone 17N", 4609, 4400, 16017); + return true; + case 31: + record = new EpsgProjectedCrsRecord(2032, "NAD27(CGQ77) / UTM zone 18N", 4609, 4400, 16018); + return true; + case 32: + record = new EpsgProjectedCrsRecord(2033, "NAD27(CGQ77) / UTM zone 19N", 4609, 4400, 16019); + return true; + case 33: + record = new EpsgProjectedCrsRecord(2034, "NAD27(CGQ77) / UTM zone 20N", 4609, 4400, 16020); + return true; + case 34: + record = new EpsgProjectedCrsRecord(2035, "NAD27(CGQ77) / UTM zone 21N", 4609, 4400, 16021); + return true; + case 35: + record = new EpsgProjectedCrsRecord(2039, "Israel 1993 / Israeli TM Grid", 4141, 4400, 18204); + return true; + case 36: + record = new EpsgProjectedCrsRecord(2040, "Locodjo 1965 / UTM zone 30N", 4142, 4400, 16030); + return true; + case 37: + record = new EpsgProjectedCrsRecord(2041, "Abidjan 1987 / UTM zone 30N", 4143, 4400, 16030); + return true; + case 38: + record = new EpsgProjectedCrsRecord(2042, "Locodjo 1965 / UTM zone 29N", 4142, 4400, 16029); + return true; + case 39: + record = new EpsgProjectedCrsRecord(2043, "Abidjan 1987 / UTM zone 29N", 4143, 4400, 16029); + return true; + case 40: + record = new EpsgProjectedCrsRecord(2044, "Hanoi 1972 / Gauss-Kruger zone 18", 4147, 4530, 16218); + return true; + case 41: + record = new EpsgProjectedCrsRecord(2045, "Hanoi 1972 / Gauss-Kruger zone 19", 4147, 4530, 16219); + return true; + case 42: + record = new EpsgProjectedCrsRecord(2046, "Hartebeesthoek94 / Lo15", 4148, 6503, 17515); + return true; + case 43: + record = new EpsgProjectedCrsRecord(2047, "Hartebeesthoek94 / Lo17", 4148, 6503, 17517); + return true; + case 44: + record = new EpsgProjectedCrsRecord(2048, "Hartebeesthoek94 / Lo19", 4148, 6503, 17519); + return true; + case 45: + record = new EpsgProjectedCrsRecord(2049, "Hartebeesthoek94 / Lo21", 4148, 6503, 17521); + return true; + case 46: + record = new EpsgProjectedCrsRecord(2050, "Hartebeesthoek94 / Lo23", 4148, 6503, 17523); + return true; + case 47: + record = new EpsgProjectedCrsRecord(2051, "Hartebeesthoek94 / Lo25", 4148, 6503, 17525); + return true; + case 48: + record = new EpsgProjectedCrsRecord(2052, "Hartebeesthoek94 / Lo27", 4148, 6503, 17527); + return true; + case 49: + record = new EpsgProjectedCrsRecord(2053, "Hartebeesthoek94 / Lo29", 4148, 6503, 17529); + return true; + case 50: + record = new EpsgProjectedCrsRecord(2054, "Hartebeesthoek94 / Lo31", 4148, 6503, 17531); + return true; + case 51: + record = new EpsgProjectedCrsRecord(2055, "Hartebeesthoek94 / Lo33", 4148, 6503, 17533); + return true; + case 52: + record = new EpsgProjectedCrsRecord(2056, "CH1903+ / LV95", 4150, 4400, 19950); + return true; + case 53: + record = new EpsgProjectedCrsRecord(2057, "Rassadiran / Nakhl e Taqi", 4153, 4400, 19951); + return true; + case 54: + record = new EpsgProjectedCrsRecord(2058, "ED50(ED77) / UTM zone 38N", 4154, 4400, 16038); + return true; + case 55: + record = new EpsgProjectedCrsRecord(2059, "ED50(ED77) / UTM zone 39N", 4154, 4400, 16039); + return true; + case 56: + record = new EpsgProjectedCrsRecord(2060, "ED50(ED77) / UTM zone 40N", 4154, 4400, 16040); + return true; + case 57: + record = new EpsgProjectedCrsRecord(2061, "ED50(ED77) / UTM zone 41N", 4154, 4400, 16041); + return true; + case 58: + record = new EpsgProjectedCrsRecord(2062, "Madrid 1870 (Madrid) / Spain LCC", 4903, 4499, 19921); + return true; + case 59: + record = new EpsgProjectedCrsRecord(2065, "S-JTSK (Ferro) / Krovak", 4818, 6501, 19952); + return true; + case 60: + record = new EpsgProjectedCrsRecord(2066, "Mount Dillon / Tobago Grid", 4157, 4407, 19924); + return true; + case 61: + record = new EpsgProjectedCrsRecord(2067, "Naparima 1955 / UTM zone 20N", 4158, 4400, 16020); + return true; + case 62: + record = new EpsgProjectedCrsRecord(2068, "ELD79 / Libya zone 5", 4159, 4499, 18240); + return true; + case 63: + record = new EpsgProjectedCrsRecord(2069, "ELD79 / Libya zone 6", 4159, 4499, 18241); + return true; + case 64: + record = new EpsgProjectedCrsRecord(2070, "ELD79 / Libya zone 7", 4159, 4499, 18242); + return true; + case 65: + record = new EpsgProjectedCrsRecord(2071, "ELD79 / Libya zone 8", 4159, 4499, 18243); + return true; + case 66: + record = new EpsgProjectedCrsRecord(2072, "ELD79 / Libya zone 9", 4159, 4499, 18244); + return true; + case 67: + record = new EpsgProjectedCrsRecord(2073, "ELD79 / Libya zone 10", 4159, 4499, 18245); + return true; + case 68: + record = new EpsgProjectedCrsRecord(2074, "ELD79 / Libya zone 11", 4159, 4499, 18246); + return true; + case 69: + record = new EpsgProjectedCrsRecord(2075, "ELD79 / Libya zone 12", 4159, 4499, 18247); + return true; + case 70: + record = new EpsgProjectedCrsRecord(2076, "ELD79 / Libya zone 13", 4159, 4499, 18248); + return true; + case 71: + record = new EpsgProjectedCrsRecord(2077, "ELD79 / UTM zone 32N", 4159, 4400, 16032); + return true; + case 72: + record = new EpsgProjectedCrsRecord(2078, "ELD79 / UTM zone 33N", 4159, 4400, 16033); + return true; + case 73: + record = new EpsgProjectedCrsRecord(2079, "ELD79 / UTM zone 34N", 4159, 4400, 16034); + return true; + case 74: + record = new EpsgProjectedCrsRecord(2080, "ELD79 / UTM zone 35N", 4159, 4400, 16035); + return true; + case 75: + record = new EpsgProjectedCrsRecord(2081, "Chos Malal 1914 / Argentina 2", 4160, 4530, 18032); + return true; + case 76: + record = new EpsgProjectedCrsRecord(2082, "Pampa del Castillo / Argentina 2", 4161, 4530, 18032); + return true; + case 77: + record = new EpsgProjectedCrsRecord(2083, "Hito XVIII 1963 / Argentina 2", 4254, 4530, 18032); + return true; + case 78: + record = new EpsgProjectedCrsRecord(2084, "Hito XVIII 1963 / UTM zone 19S", 4254, 4400, 16119); + return true; + case 79: + record = new EpsgProjectedCrsRecord(2087, "ELD79 / TM 12 NE", 4159, 4400, 16412); + return true; + case 80: + record = new EpsgProjectedCrsRecord(2088, "Carthage / TM 11 NE", 4223, 4400, 16411); + return true; + case 81: + record = new EpsgProjectedCrsRecord(2089, "Yemen NGN96 / UTM zone 38N", 4163, 4400, 16038); + return true; + case 82: + record = new EpsgProjectedCrsRecord(2090, "Yemen NGN96 / UTM zone 39N", 4163, 4400, 16039); + return true; + case 83: + record = new EpsgProjectedCrsRecord(2093, "Hanoi 1972 / GK 106 NE", 4147, 4530, 16586); + return true; + case 84: + record = new EpsgProjectedCrsRecord(2094, "WGS 72BE / TM 106 NE", 4324, 4400, 16506); + return true; + case 85: + record = new EpsgProjectedCrsRecord(2095, "Bissau / UTM zone 28N", 4165, 4400, 16028); + return true; + case 86: + record = new EpsgProjectedCrsRecord(2096, "Korean 1985 / East Belt", 4162, 4530, 18251); + return true; + case 87: + record = new EpsgProjectedCrsRecord(2097, "Korean 1985 / Central Belt", 4162, 4530, 18252); + return true; + case 88: + record = new EpsgProjectedCrsRecord(2098, "Korean 1985 / West Belt", 4162, 4530, 18253); + return true; + case 89: + record = new EpsgProjectedCrsRecord(2099, "Qatar 1948 / Qatar Grid", 4286, 4400, 19953); + return true; + case 90: + record = new EpsgProjectedCrsRecord(2100, "GGRS87 / Greek Grid", 4121, 4400, 19930); + return true; + case 91: + record = new EpsgProjectedCrsRecord(2101, "Lake / Maracaibo Grid M1", 4249, 4499, 18260); + return true; + case 92: + record = new EpsgProjectedCrsRecord(2102, "Lake / Maracaibo Grid", 4249, 4499, 18261); + return true; + case 93: + record = new EpsgProjectedCrsRecord(2103, "Lake / Maracaibo Grid M3", 4249, 4499, 18262); + return true; + case 94: + record = new EpsgProjectedCrsRecord(2104, "Lake / Maracaibo La Rosa Grid", 4249, 4499, 18263); + return true; + case 95: + record = new EpsgProjectedCrsRecord(2105, "NZGD2000 / Mount Eden 2000", 4167, 4500, 17931); + return true; + case 96: + record = new EpsgProjectedCrsRecord(2106, "NZGD2000 / Bay of Plenty 2000", 4167, 4500, 17932); + return true; + case 97: + record = new EpsgProjectedCrsRecord(2107, "NZGD2000 / Poverty Bay 2000", 4167, 4500, 17933); + return true; + case 98: + record = new EpsgProjectedCrsRecord(2108, "NZGD2000 / Hawkes Bay 2000", 4167, 4500, 17934); + return true; + case 99: + record = new EpsgProjectedCrsRecord(2109, "NZGD2000 / Taranaki 2000", 4167, 4500, 17935); + return true; + case 100: + record = new EpsgProjectedCrsRecord(2110, "NZGD2000 / Tuhirangi 2000", 4167, 4500, 17936); + return true; + case 101: + record = new EpsgProjectedCrsRecord(2111, "NZGD2000 / Wanganui 2000", 4167, 4500, 17937); + return true; + case 102: + record = new EpsgProjectedCrsRecord(2112, "NZGD2000 / Wairarapa 2000", 4167, 4500, 17938); + return true; + case 103: + record = new EpsgProjectedCrsRecord(2113, "NZGD2000 / Wellington 2000", 4167, 4500, 17939); + return true; + case 104: + record = new EpsgProjectedCrsRecord(2114, "NZGD2000 / Collingwood 2000", 4167, 4500, 17940); + return true; + case 105: + record = new EpsgProjectedCrsRecord(2115, "NZGD2000 / Nelson 2000", 4167, 4500, 17941); + return true; + case 106: + record = new EpsgProjectedCrsRecord(2116, "NZGD2000 / Karamea 2000", 4167, 4500, 17942); + return true; + case 107: + record = new EpsgProjectedCrsRecord(2117, "NZGD2000 / Buller 2000", 4167, 4500, 17943); + return true; + case 108: + record = new EpsgProjectedCrsRecord(2118, "NZGD2000 / Grey 2000", 4167, 4500, 17944); + return true; + case 109: + record = new EpsgProjectedCrsRecord(2119, "NZGD2000 / Amuri 2000", 4167, 4500, 17945); + return true; + case 110: + record = new EpsgProjectedCrsRecord(2120, "NZGD2000 / Marlborough 2000", 4167, 4500, 17946); + return true; + case 111: + record = new EpsgProjectedCrsRecord(2121, "NZGD2000 / Hokitika 2000", 4167, 4500, 17947); + return true; + case 112: + record = new EpsgProjectedCrsRecord(2122, "NZGD2000 / Okarito 2000", 4167, 4500, 17948); + return true; + case 113: + record = new EpsgProjectedCrsRecord(2123, "NZGD2000 / Jacksons Bay 2000", 4167, 4500, 17949); + return true; + case 114: + record = new EpsgProjectedCrsRecord(2124, "NZGD2000 / Mount Pleasant 2000", 4167, 4500, 17950); + return true; + case 115: + record = new EpsgProjectedCrsRecord(2125, "NZGD2000 / Gawler 2000", 4167, 4500, 17951); + return true; + case 116: + record = new EpsgProjectedCrsRecord(2126, "NZGD2000 / Timaru 2000", 4167, 4500, 17952); + return true; + case 117: + record = new EpsgProjectedCrsRecord(2127, "NZGD2000 / Lindis Peak 2000", 4167, 4500, 17953); + return true; + case 118: + record = new EpsgProjectedCrsRecord(2128, "NZGD2000 / Mount Nicholas 2000", 4167, 4500, 17954); + return true; + case 119: + record = new EpsgProjectedCrsRecord(2129, "NZGD2000 / Mount York 2000", 4167, 4500, 17955); + return true; + case 120: + record = new EpsgProjectedCrsRecord(2130, "NZGD2000 / Observation Point 2000", 4167, 4500, 17956); + return true; + case 121: + record = new EpsgProjectedCrsRecord(2131, "NZGD2000 / North Taieri 2000", 4167, 4500, 17957); + return true; + case 122: + record = new EpsgProjectedCrsRecord(2132, "NZGD2000 / Bluff 2000", 4167, 4500, 17958); + return true; + case 123: + record = new EpsgProjectedCrsRecord(2133, "NZGD2000 / UTM zone 58S", 4167, 4400, 16158); + return true; + case 124: + record = new EpsgProjectedCrsRecord(2134, "NZGD2000 / UTM zone 59S", 4167, 4400, 16159); + return true; + case 125: + record = new EpsgProjectedCrsRecord(2135, "NZGD2000 / UTM zone 60S", 4167, 4400, 16160); + return true; + case 126: + record = new EpsgProjectedCrsRecord(2136, "Accra / Ghana National Grid", 4168, 4404, 19959); + return true; + case 127: + record = new EpsgProjectedCrsRecord(2137, "Accra / TM 1 NW", 4168, 4400, 17001); + return true; + case 128: + record = new EpsgProjectedCrsRecord(2138, "NAD27(CGQ77) / Quebec Lambert", 4609, 4499, 19944); + return true; + case 129: + record = new EpsgProjectedCrsRecord(2154, "ETRS89-FRA [RGF93 v1] / Lambert-93", 4171, 4499, 18085); + return true; + case 130: + record = new EpsgProjectedCrsRecord(2157, "ETRS89-IRE [ETRF2000] / Irish Transverse Mercator", 4173, 4400, 19962); + return true; + case 131: + record = new EpsgProjectedCrsRecord(2158, "ETRS89-IRE [ETRF2000] / UTM zone 29N", 4173, 4400, 16029); + return true; + case 132: + record = new EpsgProjectedCrsRecord(2159, "Sierra Leone 1924 / New Colony Grid", 4174, 4404, 19963); + return true; + case 133: + record = new EpsgProjectedCrsRecord(2160, "Sierra Leone 1924 / New War Office Grid", 4174, 4404, 19964); + return true; + case 134: + record = new EpsgProjectedCrsRecord(2161, "Sierra Leone 1968 / UTM zone 28N", 4175, 4400, 16028); + return true; + case 135: + record = new EpsgProjectedCrsRecord(2162, "Sierra Leone 1968 / UTM zone 29N", 4175, 4400, 16029); + return true; + case 136: + record = new EpsgProjectedCrsRecord(2164, "Locodjo 1965 / TM 5 NW", 4142, 4400, 17005); + return true; + case 137: + record = new EpsgProjectedCrsRecord(2165, "Abidjan 1987 / TM 5 NW", 4143, 4400, 17005); + return true; + case 138: + record = new EpsgProjectedCrsRecord(2169, "LUREF / Luxembourg TM", 4181, 4530, 19966); + return true; + case 139: + record = new EpsgProjectedCrsRecord(2172, "Pulkovo 1942(58) / Poland zone II", 4179, 4530, 18282); + return true; + case 140: + record = new EpsgProjectedCrsRecord(2173, "Pulkovo 1942(58) / Poland zone III", 4179, 4530, 18283); + return true; + case 141: + record = new EpsgProjectedCrsRecord(2174, "Pulkovo 1942(58) / Poland zone IV", 4179, 4530, 18284); + return true; + case 142: + record = new EpsgProjectedCrsRecord(2175, "Pulkovo 1942(58) / Poland zone V", 4179, 4530, 18285); + return true; + case 143: + record = new EpsgProjectedCrsRecord(2176, "ETRS89-POL [PL-ETRF2000] / CS2000/15", 9702, 4531, 18305); + return true; + case 144: + record = new EpsgProjectedCrsRecord(2177, "ETRS89-POL [PL-ETRF2000] / CS2000/18", 9702, 4531, 18306); + return true; + case 145: + record = new EpsgProjectedCrsRecord(2178, "ETRS89-POL [PL-ETRF2000] / CS2000/21", 9702, 4531, 18307); + return true; + case 146: + record = new EpsgProjectedCrsRecord(2179, "ETRS89-POL [PL-ETRF2000] / CS2000/24", 9702, 4531, 18308); + return true; + case 147: + record = new EpsgProjectedCrsRecord(2180, "ETRS89 / PL-1992", 4258, 4531, 18300); + return true; + case 148: + record = new EpsgProjectedCrsRecord(2188, "Azores Occidental 1939 / UTM zone 25N", 4182, 4400, 16025); + return true; + case 149: + record = new EpsgProjectedCrsRecord(2189, "Azores Central 1948 / UTM zone 26N", 4183, 4400, 16026); + return true; + case 150: + record = new EpsgProjectedCrsRecord(2190, "Azores Oriental 1940 / UTM zone 26N", 4184, 4400, 16026); + return true; + case 151: + record = new EpsgProjectedCrsRecord(2193, "NZGD2000 / New Zealand Transverse Mercator 2000", 4167, 4500, 19971); + return true; + case 152: + record = new EpsgProjectedCrsRecord(2195, "NAD83(HARN) / UTM zone 2S", 4152, 4400, 16102); + return true; + case 153: + record = new EpsgProjectedCrsRecord(2196, "ETRS89 / Kp2000 Jutland", 4258, 4400, 18401); + return true; + case 154: + record = new EpsgProjectedCrsRecord(2197, "ETRS89 / Kp2000 Zealand", 4258, 4400, 18402); + return true; + case 155: + record = new EpsgProjectedCrsRecord(2198, "ETRS89 / Kp2000 Bornholm", 4258, 4400, 18403); + return true; + case 156: + record = new EpsgProjectedCrsRecord(2200, "ATS77 / New Brunswick Stereographic (ATS77)", 4122, 4500, 19945); + return true; + case 157: + record = new EpsgProjectedCrsRecord(2201, "REGVEN / UTM zone 18N", 4189, 4400, 16018); + return true; + case 158: + record = new EpsgProjectedCrsRecord(2202, "REGVEN / UTM zone 19N", 4189, 4400, 16019); + return true; + case 159: + record = new EpsgProjectedCrsRecord(2203, "REGVEN / UTM zone 20N", 4189, 4400, 16020); + return true; + case 160: + record = new EpsgProjectedCrsRecord(2204, "NAD27 / Tennessee", 4267, 4497, 15302); + return true; + case 161: + record = new EpsgProjectedCrsRecord(2205, "NAD83 / Kentucky North", 4269, 4499, 15303); + return true; + case 162: + record = new EpsgProjectedCrsRecord(2206, "ED50 / 3-degree Gauss-Kruger zone 9", 4230, 4530, 16269); + return true; + case 163: + record = new EpsgProjectedCrsRecord(2207, "ED50 / 3-degree Gauss-Kruger zone 10", 4230, 4530, 16270); + return true; + case 164: + record = new EpsgProjectedCrsRecord(2208, "ED50 / 3-degree Gauss-Kruger zone 11", 4230, 4530, 16271); + return true; + case 165: + record = new EpsgProjectedCrsRecord(2209, "ED50 / 3-degree Gauss-Kruger zone 12", 4230, 4530, 16272); + return true; + case 166: + record = new EpsgProjectedCrsRecord(2210, "ED50 / 3-degree Gauss-Kruger zone 13", 4230, 4530, 16273); + return true; + case 167: + record = new EpsgProjectedCrsRecord(2211, "ED50 / 3-degree Gauss-Kruger zone 14", 4230, 4530, 16274); + return true; + case 168: + record = new EpsgProjectedCrsRecord(2212, "ED50 / 3-degree Gauss-Kruger zone 15", 4230, 4530, 16275); + return true; + case 169: + record = new EpsgProjectedCrsRecord(2213, "ETRS89 / TM 30 NE", 4258, 4400, 16430); + return true; + case 170: + record = new EpsgProjectedCrsRecord(2215, "Manoca 1962 / UTM zone 32N", 4193, 4400, 16032); + return true; + case 171: + record = new EpsgProjectedCrsRecord(2216, "Qoornoq 1927 / UTM zone 22N", 4194, 4400, 16022); + return true; + case 172: + record = new EpsgProjectedCrsRecord(2217, "Qoornoq 1927 / UTM zone 23N", 4194, 4400, 16023); + return true; + case 173: + record = new EpsgProjectedCrsRecord(2218, "Scoresbysund 1952 / Greenland zone 5 east", 4195, 1031, 18425); + return true; + case 174: + record = new EpsgProjectedCrsRecord(2219, "ATS77 / UTM zone 19N", 4122, 4400, 16019); + return true; + case 175: + record = new EpsgProjectedCrsRecord(2220, "ATS77 / UTM zone 20N", 4122, 4400, 16020); + return true; + case 176: + record = new EpsgProjectedCrsRecord(2221, "Scoresbysund 1952 / Greenland zone 6 east", 4195, 1031, 18426); + return true; + case 177: + record = new EpsgProjectedCrsRecord(2222, "NAD83 / Arizona East (ft)", 4269, 4495, 15304); + return true; + case 178: + record = new EpsgProjectedCrsRecord(2223, "NAD83 / Arizona Central (ft)", 4269, 4495, 15305); + return true; + case 179: + record = new EpsgProjectedCrsRecord(2224, "NAD83 / Arizona West (ft)", 4269, 4495, 15306); + return true; + case 180: + record = new EpsgProjectedCrsRecord(2225, "NAD83 / California zone 1 (ftUS)", 4269, 4497, 15307); + return true; + case 181: + record = new EpsgProjectedCrsRecord(2226, "NAD83 / California zone 2 (ftUS)", 4269, 4497, 15308); + return true; + case 182: + record = new EpsgProjectedCrsRecord(2227, "NAD83 / California zone 3 (ftUS)", 4269, 4497, 15309); + return true; + case 183: + record = new EpsgProjectedCrsRecord(2228, "NAD83 / California zone 4 (ftUS)", 4269, 4497, 15310); + return true; + case 184: + record = new EpsgProjectedCrsRecord(2229, "NAD83 / California zone 5 (ftUS)", 4269, 4497, 15311); + return true; + case 185: + record = new EpsgProjectedCrsRecord(2230, "NAD83 / California zone 6 (ftUS)", 4269, 4497, 15312); + return true; + case 186: + record = new EpsgProjectedCrsRecord(2231, "NAD83 / Colorado North (ftUS)", 4269, 4497, 15313); + return true; + case 187: + record = new EpsgProjectedCrsRecord(2232, "NAD83 / Colorado Central (ftUS)", 4269, 4497, 15314); + return true; + case 188: + record = new EpsgProjectedCrsRecord(2233, "NAD83 / Colorado South (ftUS)", 4269, 4497, 15315); + return true; + case 189: + record = new EpsgProjectedCrsRecord(2234, "NAD83 / Connecticut (ftUS)", 4269, 4497, 15316); + return true; + case 190: + record = new EpsgProjectedCrsRecord(2235, "NAD83 / Delaware (ftUS)", 4269, 4497, 15317); + return true; + case 191: + record = new EpsgProjectedCrsRecord(2236, "NAD83 / Florida East (ftUS)", 4269, 4497, 15318); + return true; + case 192: + record = new EpsgProjectedCrsRecord(2237, "NAD83 / Florida West (ftUS)", 4269, 4497, 15319); + return true; + case 193: + record = new EpsgProjectedCrsRecord(2238, "NAD83 / Florida North (ftUS)", 4269, 4497, 15320); + return true; + case 194: + record = new EpsgProjectedCrsRecord(2239, "NAD83 / Georgia East (ftUS)", 4269, 4497, 15321); + return true; + case 195: + record = new EpsgProjectedCrsRecord(2240, "NAD83 / Georgia West (ftUS)", 4269, 4497, 15322); + return true; + case 196: + record = new EpsgProjectedCrsRecord(2241, "NAD83 / Idaho East (ftUS)", 4269, 4497, 15323); + return true; + case 197: + record = new EpsgProjectedCrsRecord(2242, "NAD83 / Idaho Central (ftUS)", 4269, 4497, 15324); + return true; + case 198: + record = new EpsgProjectedCrsRecord(2243, "NAD83 / Idaho West (ftUS)", 4269, 4497, 15325); + return true; + case 199: + record = new EpsgProjectedCrsRecord(2246, "NAD83 / Kentucky North (ftUS)", 4269, 4497, 15328); + return true; + case 200: + record = new EpsgProjectedCrsRecord(2247, "NAD83 / Kentucky South (ftUS)", 4269, 4497, 15329); + return true; + case 201: + record = new EpsgProjectedCrsRecord(2248, "NAD83 / Maryland (ftUS)", 4269, 4497, 15330); + return true; + case 202: + record = new EpsgProjectedCrsRecord(2249, "NAD83 / Massachusetts Mainland (ftUS)", 4269, 4497, 15331); + return true; + case 203: + record = new EpsgProjectedCrsRecord(2250, "NAD83 / Massachusetts Island (ftUS)", 4269, 4497, 15332); + return true; + case 204: + record = new EpsgProjectedCrsRecord(2251, "NAD83 / Michigan North (ft)", 4269, 4495, 15333); + return true; + case 205: + record = new EpsgProjectedCrsRecord(2252, "NAD83 / Michigan Central (ft)", 4269, 4495, 15334); + return true; + case 206: + record = new EpsgProjectedCrsRecord(2253, "NAD83 / Michigan South (ft)", 4269, 4495, 15335); + return true; + case 207: + record = new EpsgProjectedCrsRecord(2254, "NAD83 / Mississippi East (ftUS)", 4269, 4497, 15336); + return true; + case 208: + record = new EpsgProjectedCrsRecord(2255, "NAD83 / Mississippi West (ftUS)", 4269, 4497, 15337); + return true; + case 209: + record = new EpsgProjectedCrsRecord(2256, "NAD83 / Montana (ft)", 4269, 4495, 15338); + return true; + case 210: + record = new EpsgProjectedCrsRecord(2257, "NAD83 / New Mexico East (ftUS)", 4269, 4497, 15339); + return true; + case 211: + record = new EpsgProjectedCrsRecord(2258, "NAD83 / New Mexico Central (ftUS)", 4269, 4497, 15340); + return true; + case 212: + record = new EpsgProjectedCrsRecord(2259, "NAD83 / New Mexico West (ftUS)", 4269, 4497, 15341); + return true; + case 213: + record = new EpsgProjectedCrsRecord(2260, "NAD83 / New York East (ftUS)", 4269, 4497, 15342); + return true; + case 214: + record = new EpsgProjectedCrsRecord(2261, "NAD83 / New York Central (ftUS)", 4269, 4497, 15343); + return true; + case 215: + record = new EpsgProjectedCrsRecord(2262, "NAD83 / New York West (ftUS)", 4269, 4497, 15344); + return true; + case 216: + record = new EpsgProjectedCrsRecord(2263, "NAD83 / New York Long Island (ftUS)", 4269, 4497, 15345); + return true; + case 217: + record = new EpsgProjectedCrsRecord(2264, "NAD83 / North Carolina (ftUS)", 4269, 4497, 15346); + return true; + case 218: + record = new EpsgProjectedCrsRecord(2265, "NAD83 / North Dakota North (ft)", 4269, 4495, 15347); + return true; + case 219: + record = new EpsgProjectedCrsRecord(2266, "NAD83 / North Dakota South (ft)", 4269, 4495, 15348); + return true; + case 220: + record = new EpsgProjectedCrsRecord(2267, "NAD83 / Oklahoma North (ftUS)", 4269, 4497, 15349); + return true; + case 221: + record = new EpsgProjectedCrsRecord(2268, "NAD83 / Oklahoma South (ftUS)", 4269, 4497, 15350); + return true; + case 222: + record = new EpsgProjectedCrsRecord(2269, "NAD83 / Oregon North (ft)", 4269, 4495, 15351); + return true; + case 223: + record = new EpsgProjectedCrsRecord(2270, "NAD83 / Oregon South (ft)", 4269, 4495, 15352); + return true; + case 224: + record = new EpsgProjectedCrsRecord(2271, "NAD83 / Pennsylvania North (ftUS)", 4269, 4497, 15353); + return true; + case 225: + record = new EpsgProjectedCrsRecord(2272, "NAD83 / Pennsylvania South (ftUS)", 4269, 4497, 15354); + return true; + case 226: + record = new EpsgProjectedCrsRecord(2273, "NAD83 / South Carolina (ft)", 4269, 4495, 15355); + return true; + case 227: + record = new EpsgProjectedCrsRecord(2274, "NAD83 / Tennessee (ftUS)", 4269, 4497, 15356); + return true; + case 228: + record = new EpsgProjectedCrsRecord(2275, "NAD83 / Texas North (ftUS)", 4269, 4497, 15357); + return true; + case 229: + record = new EpsgProjectedCrsRecord(2276, "NAD83 / Texas North Central (ftUS)", 4269, 4497, 15358); + return true; + case 230: + record = new EpsgProjectedCrsRecord(2277, "NAD83 / Texas Central (ftUS)", 4269, 4497, 15359); + return true; + case 231: + record = new EpsgProjectedCrsRecord(2278, "NAD83 / Texas South Central (ftUS)", 4269, 4497, 15360); + return true; + case 232: + record = new EpsgProjectedCrsRecord(2279, "NAD83 / Texas South (ftUS)", 4269, 4497, 15361); + return true; + case 233: + record = new EpsgProjectedCrsRecord(2280, "NAD83 / Utah North (ft)", 4269, 4495, 15362); + return true; + case 234: + record = new EpsgProjectedCrsRecord(2281, "NAD83 / Utah Central (ft)", 4269, 4495, 15363); + return true; + case 235: + record = new EpsgProjectedCrsRecord(2282, "NAD83 / Utah South (ft)", 4269, 4495, 15364); + return true; + case 236: + record = new EpsgProjectedCrsRecord(2283, "NAD83 / Virginia North (ftUS)", 4269, 4497, 15365); + return true; + case 237: + record = new EpsgProjectedCrsRecord(2284, "NAD83 / Virginia South (ftUS)", 4269, 4497, 15366); + return true; + case 238: + record = new EpsgProjectedCrsRecord(2285, "NAD83 / Washington North (ftUS)", 4269, 4497, 15367); + return true; + case 239: + record = new EpsgProjectedCrsRecord(2286, "NAD83 / Washington South (ftUS)", 4269, 4497, 15368); + return true; + case 240: + record = new EpsgProjectedCrsRecord(2287, "NAD83 / Wisconsin North (ftUS)", 4269, 4497, 15369); + return true; + case 241: + record = new EpsgProjectedCrsRecord(2288, "NAD83 / Wisconsin Central (ftUS)", 4269, 4497, 15370); + return true; + case 242: + record = new EpsgProjectedCrsRecord(2289, "NAD83 / Wisconsin South (ftUS)", 4269, 4497, 15371); + return true; + case 243: + record = new EpsgProjectedCrsRecord(2290, "ATS77 / Prince Edward Isl. Stereographic (ATS77)", 4122, 4496, 19933); + return true; + case 244: + record = new EpsgProjectedCrsRecord(2294, "ATS77 / MTM Nova Scotia zone 4", 4122, 4400, 17794); + return true; + case 245: + record = new EpsgProjectedCrsRecord(2295, "ATS77 / MTM Nova Scotia zone 5", 4122, 4400, 17795); + return true; + case 246: + record = new EpsgProjectedCrsRecord(2296, "Ammassalik 1958 / Greenland zone 7 east", 4196, 1031, 18427); + return true; + case 247: + record = new EpsgProjectedCrsRecord(2299, "Qoornoq 1927 / Greenland zone 2 west", 4194, 1031, 18432); + return true; + case 248: + record = new EpsgProjectedCrsRecord(2301, "Qoornoq 1927 / Greenland zone 3 west", 4194, 1031, 18433); + return true; + case 249: + record = new EpsgProjectedCrsRecord(2303, "Qoornoq 1927 / Greenland zone 4 west", 4194, 1031, 18434); + return true; + case 250: + record = new EpsgProjectedCrsRecord(2304, "Qoornoq 1927 / Greenland zone 5 west", 4194, 1031, 18435); + return true; + case 251: + record = new EpsgProjectedCrsRecord(2305, "Qoornoq 1927 / Greenland zone 6 west", 4194, 1031, 18436); + return true; + case 252: + record = new EpsgProjectedCrsRecord(2306, "Qoornoq 1927 / Greenland zone 7 west", 4194, 1031, 18437); + return true; + case 253: + record = new EpsgProjectedCrsRecord(2307, "Qoornoq 1927 / Greenland zone 8", 4194, 1031, 18428); + return true; + case 254: + record = new EpsgProjectedCrsRecord(2308, "Batavia / TM 109 SE", 4211, 4400, 16709); + return true; + case 255: + record = new EpsgProjectedCrsRecord(2309, "WGS 84 / TM 116 SE", 4326, 4400, 16716); + return true; + case 256: + record = new EpsgProjectedCrsRecord(2310, "WGS 84 / TM 132 SE", 4326, 4400, 16732); + return true; + case 257: + record = new EpsgProjectedCrsRecord(2311, "WGS 84 / TM 6 NE", 4326, 4400, 16406); + return true; + case 258: + record = new EpsgProjectedCrsRecord(2312, "Garoua / UTM zone 33N", 4197, 4400, 16033); + return true; + case 259: + record = new EpsgProjectedCrsRecord(2313, "Kousseri / UTM zone 33N", 4198, 4400, 16033); + return true; + case 260: + record = new EpsgProjectedCrsRecord(2314, "Trinidad 1903 / Trinidad Grid (ftCla)", 4302, 4403, 19975); + return true; + case 261: + record = new EpsgProjectedCrsRecord(2315, "Campo Inchauspe / UTM zone 19S", 4221, 4400, 16119); + return true; + case 262: + record = new EpsgProjectedCrsRecord(2316, "Campo Inchauspe / UTM zone 20S", 4221, 4400, 16120); + return true; + case 263: + record = new EpsgProjectedCrsRecord(2317, "PSAD56 / ICN Regional", 4248, 4499, 19976); + return true; + case 264: + record = new EpsgProjectedCrsRecord(2318, "Ain el Abd / Aramco Lambert", 4204, 4400, 19977); + return true; + case 265: + record = new EpsgProjectedCrsRecord(2319, "ED50 / TM27", 4230, 4530, 16305); + return true; + case 266: + record = new EpsgProjectedCrsRecord(2320, "ED50 / TM30", 4230, 4530, 16370); + return true; + case 267: + record = new EpsgProjectedCrsRecord(2321, "ED50 / TM33", 4230, 4530, 16306); + return true; + case 268: + record = new EpsgProjectedCrsRecord(2322, "ED50 / TM36", 4230, 4530, 16372); + return true; + case 269: + record = new EpsgProjectedCrsRecord(2323, "ED50 / TM39", 4230, 4530, 16307); + return true; + case 270: + record = new EpsgProjectedCrsRecord(2324, "ED50 / TM42", 4230, 4530, 16374); + return true; + case 271: + record = new EpsgProjectedCrsRecord(2325, "ED50 / TM45", 4230, 4530, 16308); + return true; + case 272: + record = new EpsgProjectedCrsRecord(2326, "Hong Kong 1980 Grid System", 4611, 4500, 19978); + return true; + case 273: + record = new EpsgProjectedCrsRecord(2327, "Xian 1980 / Gauss-Kruger zone 13", 4610, 4530, 16213); + return true; + case 274: + record = new EpsgProjectedCrsRecord(2328, "Xian 1980 / Gauss-Kruger zone 14", 4610, 4530, 16214); + return true; + case 275: + record = new EpsgProjectedCrsRecord(2329, "Xian 1980 / Gauss-Kruger zone 15", 4610, 4530, 16215); + return true; + case 276: + record = new EpsgProjectedCrsRecord(2330, "Xian 1980 / Gauss-Kruger zone 16", 4610, 4530, 16216); + return true; + case 277: + record = new EpsgProjectedCrsRecord(2331, "Xian 1980 / Gauss-Kruger zone 17", 4610, 4530, 16217); + return true; + case 278: + record = new EpsgProjectedCrsRecord(2332, "Xian 1980 / Gauss-Kruger zone 18", 4610, 4530, 16218); + return true; + case 279: + record = new EpsgProjectedCrsRecord(2333, "Xian 1980 / Gauss-Kruger zone 19", 4610, 4530, 16219); + return true; + case 280: + record = new EpsgProjectedCrsRecord(2334, "Xian 1980 / Gauss-Kruger zone 20", 4610, 4530, 16220); + return true; + case 281: + record = new EpsgProjectedCrsRecord(2335, "Xian 1980 / Gauss-Kruger zone 21", 4610, 4530, 16221); + return true; + case 282: + record = new EpsgProjectedCrsRecord(2336, "Xian 1980 / Gauss-Kruger zone 22", 4610, 4530, 16222); + return true; + case 283: + record = new EpsgProjectedCrsRecord(2337, "Xian 1980 / Gauss-Kruger zone 23", 4610, 4530, 16223); + return true; + case 284: + record = new EpsgProjectedCrsRecord(2338, "Xian 1980 / Gauss-Kruger CM 75E", 4610, 4530, 16313); + return true; + case 285: + record = new EpsgProjectedCrsRecord(2339, "Xian 1980 / Gauss-Kruger CM 81E", 4610, 4530, 16314); + return true; + case 286: + record = new EpsgProjectedCrsRecord(2340, "Xian 1980 / Gauss-Kruger CM 87E", 4610, 4530, 16315); + return true; + case 287: + record = new EpsgProjectedCrsRecord(2341, "Xian 1980 / Gauss-Kruger CM 93E", 4610, 4530, 16316); + return true; + case 288: + record = new EpsgProjectedCrsRecord(2342, "Xian 1980 / Gauss-Kruger CM 99E", 4610, 4530, 16317); + return true; + case 289: + record = new EpsgProjectedCrsRecord(2343, "Xian 1980 / Gauss-Kruger CM 105E", 4610, 4530, 16318); + return true; + case 290: + record = new EpsgProjectedCrsRecord(2344, "Xian 1980 / Gauss-Kruger CM 111E", 4610, 4530, 16319); + return true; + case 291: + record = new EpsgProjectedCrsRecord(2345, "Xian 1980 / Gauss-Kruger CM 117E", 4610, 4530, 16320); + return true; + case 292: + record = new EpsgProjectedCrsRecord(2346, "Xian 1980 / Gauss-Kruger CM 123E", 4610, 4530, 16321); + return true; + case 293: + record = new EpsgProjectedCrsRecord(2347, "Xian 1980 / Gauss-Kruger CM 129E", 4610, 4530, 16322); + return true; + case 294: + record = new EpsgProjectedCrsRecord(2348, "Xian 1980 / Gauss-Kruger CM 135E", 4610, 4530, 16323); + return true; + case 295: + record = new EpsgProjectedCrsRecord(2349, "Xian 1980 / 3-degree Gauss-Kruger zone 25", 4610, 4530, 16285); + return true; + case 296: + record = new EpsgProjectedCrsRecord(2350, "Xian 1980 / 3-degree Gauss-Kruger zone 26", 4610, 4530, 16286); + return true; + case 297: + record = new EpsgProjectedCrsRecord(2351, "Xian 1980 / 3-degree Gauss-Kruger zone 27", 4610, 4530, 16287); + return true; + case 298: + record = new EpsgProjectedCrsRecord(2352, "Xian 1980 / 3-degree Gauss-Kruger zone 28", 4610, 4530, 16288); + return true; + case 299: + record = new EpsgProjectedCrsRecord(2353, "Xian 1980 / 3-degree Gauss-Kruger zone 29", 4610, 4530, 16289); + return true; + case 300: + record = new EpsgProjectedCrsRecord(2354, "Xian 1980 / 3-degree Gauss-Kruger zone 30", 4610, 4530, 16290); + return true; + case 301: + record = new EpsgProjectedCrsRecord(2355, "Xian 1980 / 3-degree Gauss-Kruger zone 31", 4610, 4530, 16291); + return true; + case 302: + record = new EpsgProjectedCrsRecord(2356, "Xian 1980 / 3-degree Gauss-Kruger zone 32", 4610, 4530, 16292); + return true; + case 303: + record = new EpsgProjectedCrsRecord(2357, "Xian 1980 / 3-degree Gauss-Kruger zone 33", 4610, 4530, 16293); + return true; + case 304: + record = new EpsgProjectedCrsRecord(2358, "Xian 1980 / 3-degree Gauss-Kruger zone 34", 4610, 4530, 16294); + return true; + case 305: + record = new EpsgProjectedCrsRecord(2359, "Xian 1980 / 3-degree Gauss-Kruger zone 35", 4610, 4530, 16295); + return true; + case 306: + record = new EpsgProjectedCrsRecord(2360, "Xian 1980 / 3-degree Gauss-Kruger zone 36", 4610, 4530, 16296); + return true; + case 307: + record = new EpsgProjectedCrsRecord(2361, "Xian 1980 / 3-degree Gauss-Kruger zone 37", 4610, 4530, 16297); + return true; + case 308: + record = new EpsgProjectedCrsRecord(2362, "Xian 1980 / 3-degree Gauss-Kruger zone 38", 4610, 4530, 16298); + return true; + case 309: + record = new EpsgProjectedCrsRecord(2363, "Xian 1980 / 3-degree Gauss-Kruger zone 39", 4610, 4530, 16299); + return true; + case 310: + record = new EpsgProjectedCrsRecord(2364, "Xian 1980 / 3-degree Gauss-Kruger zone 40", 4610, 4530, 16070); + return true; + case 311: + record = new EpsgProjectedCrsRecord(2365, "Xian 1980 / 3-degree Gauss-Kruger zone 41", 4610, 4530, 16071); + return true; + case 312: + record = new EpsgProjectedCrsRecord(2366, "Xian 1980 / 3-degree Gauss-Kruger zone 42", 4610, 4530, 16072); + return true; + case 313: + record = new EpsgProjectedCrsRecord(2367, "Xian 1980 / 3-degree Gauss-Kruger zone 43", 4610, 4530, 16073); + return true; + case 314: + record = new EpsgProjectedCrsRecord(2368, "Xian 1980 / 3-degree Gauss-Kruger zone 44", 4610, 4530, 16074); + return true; + case 315: + record = new EpsgProjectedCrsRecord(2369, "Xian 1980 / 3-degree Gauss-Kruger zone 45", 4610, 4530, 16075); + return true; + case 316: + record = new EpsgProjectedCrsRecord(2370, "Xian 1980 / 3-degree Gauss-Kruger CM 75E", 4610, 4530, 16313); + return true; + case 317: + record = new EpsgProjectedCrsRecord(2371, "Xian 1980 / 3-degree Gauss-Kruger CM 78E", 4610, 4530, 16386); + return true; + case 318: + record = new EpsgProjectedCrsRecord(2372, "Xian 1980 / 3-degree Gauss-Kruger CM 81E", 4610, 4530, 16314); + return true; + case 319: + record = new EpsgProjectedCrsRecord(2373, "Xian 1980 / 3-degree Gauss-Kruger CM 84E", 4610, 4530, 16388); + return true; + case 320: + record = new EpsgProjectedCrsRecord(2374, "Xian 1980 / 3-degree Gauss-Kruger CM 87E", 4610, 4530, 16315); + return true; + case 321: + record = new EpsgProjectedCrsRecord(2375, "Xian 1980 / 3-degree Gauss-Kruger CM 90E", 4610, 4530, 16390); + return true; + case 322: + record = new EpsgProjectedCrsRecord(2376, "Xian 1980 / 3-degree Gauss-Kruger CM 93E", 4610, 4530, 16316); + return true; + case 323: + record = new EpsgProjectedCrsRecord(2377, "Xian 1980 / 3-degree Gauss-Kruger CM 96E", 4610, 4530, 16392); + return true; + case 324: + record = new EpsgProjectedCrsRecord(2378, "Xian 1980 / 3-degree Gauss-Kruger CM 99E", 4610, 4530, 16317); + return true; + case 325: + record = new EpsgProjectedCrsRecord(2379, "Xian 1980 / 3-degree Gauss-Kruger CM 102E", 4610, 4530, 16394); + return true; + case 326: + record = new EpsgProjectedCrsRecord(2380, "Xian 1980 / 3-degree Gauss-Kruger CM 105E", 4610, 4530, 16318); + return true; + case 327: + record = new EpsgProjectedCrsRecord(2381, "Xian 1980 / 3-degree Gauss-Kruger CM 108E", 4610, 4530, 16396); + return true; + case 328: + record = new EpsgProjectedCrsRecord(2382, "Xian 1980 / 3-degree Gauss-Kruger CM 111E", 4610, 4530, 16319); + return true; + case 329: + record = new EpsgProjectedCrsRecord(2383, "Xian 1980 / 3-degree Gauss-Kruger CM 114E", 4610, 4530, 16398); + return true; + case 330: + record = new EpsgProjectedCrsRecord(2384, "Xian 1980 / 3-degree Gauss-Kruger CM 117E", 4610, 4530, 16320); + return true; + case 331: + record = new EpsgProjectedCrsRecord(2385, "Xian 1980 / 3-degree Gauss-Kruger CM 120E", 4610, 4530, 16170); + return true; + case 332: + record = new EpsgProjectedCrsRecord(2386, "Xian 1980 / 3-degree Gauss-Kruger CM 123E", 4610, 4530, 16321); + return true; + case 333: + record = new EpsgProjectedCrsRecord(2387, "Xian 1980 / 3-degree Gauss-Kruger CM 126E", 4610, 4530, 16172); + return true; + case 334: + record = new EpsgProjectedCrsRecord(2388, "Xian 1980 / 3-degree Gauss-Kruger CM 129E", 4610, 4530, 16322); + return true; + case 335: + record = new EpsgProjectedCrsRecord(2389, "Xian 1980 / 3-degree Gauss-Kruger CM 132E", 4610, 4530, 16174); + return true; + case 336: + record = new EpsgProjectedCrsRecord(2390, "Xian 1980 / 3-degree Gauss-Kruger CM 135E", 4610, 4530, 16323); + return true; + case 337: + record = new EpsgProjectedCrsRecord(2391, "KKJ / Finland zone 1", 4123, 4530, 18191); + return true; + case 338: + record = new EpsgProjectedCrsRecord(2392, "KKJ / Finland zone 2", 4123, 4530, 18192); + return true; + case 339: + record = new EpsgProjectedCrsRecord(2393, "KKJ / Finland Uniform Coordinate System", 4123, 4530, 18193); + return true; + case 340: + record = new EpsgProjectedCrsRecord(2394, "KKJ / Finland zone 4", 4123, 4530, 18194); + return true; + case 341: + record = new EpsgProjectedCrsRecord(2395, "South Yemen / Gauss-Kruger zone 8", 4164, 4530, 16208); + return true; + case 342: + record = new EpsgProjectedCrsRecord(2396, "South Yemen / Gauss-Kruger zone 9", 4164, 4530, 16209); + return true; + case 343: + record = new EpsgProjectedCrsRecord(2397, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 3", 4178, 4530, 16263); + return true; + case 344: + record = new EpsgProjectedCrsRecord(2398, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 4", 4178, 4530, 16264); + return true; + case 345: + record = new EpsgProjectedCrsRecord(2399, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 5", 4178, 4530, 16265); + return true; + case 346: + record = new EpsgProjectedCrsRecord(2401, "Beijing 1954 / 3-degree Gauss-Kruger zone 25", 4214, 4530, 16285); + return true; + case 347: + record = new EpsgProjectedCrsRecord(2402, "Beijing 1954 / 3-degree Gauss-Kruger zone 26", 4214, 4530, 16286); + return true; + case 348: + record = new EpsgProjectedCrsRecord(2403, "Beijing 1954 / 3-degree Gauss-Kruger zone 27", 4214, 4530, 16287); + return true; + case 349: + record = new EpsgProjectedCrsRecord(2404, "Beijing 1954 / 3-degree Gauss-Kruger zone 28", 4214, 4530, 16288); + return true; + case 350: + record = new EpsgProjectedCrsRecord(2405, "Beijing 1954 / 3-degree Gauss-Kruger zone 29", 4214, 4530, 16289); + return true; + case 351: + record = new EpsgProjectedCrsRecord(2406, "Beijing 1954 / 3-degree Gauss-Kruger zone 30", 4214, 4530, 16290); + return true; + case 352: + record = new EpsgProjectedCrsRecord(2407, "Beijing 1954 / 3-degree Gauss-Kruger zone 31", 4214, 4530, 16291); + return true; + case 353: + record = new EpsgProjectedCrsRecord(2408, "Beijing 1954 / 3-degree Gauss-Kruger zone 32", 4214, 4530, 16292); + return true; + case 354: + record = new EpsgProjectedCrsRecord(2409, "Beijing 1954 / 3-degree Gauss-Kruger zone 33", 4214, 4530, 16293); + return true; + case 355: + record = new EpsgProjectedCrsRecord(2410, "Beijing 1954 / 3-degree Gauss-Kruger zone 34", 4214, 4530, 16294); + return true; + case 356: + record = new EpsgProjectedCrsRecord(2411, "Beijing 1954 / 3-degree Gauss-Kruger zone 35", 4214, 4530, 16295); + return true; + case 357: + record = new EpsgProjectedCrsRecord(2412, "Beijing 1954 / 3-degree Gauss-Kruger zone 36", 4214, 4530, 16296); + return true; + case 358: + record = new EpsgProjectedCrsRecord(2413, "Beijing 1954 / 3-degree Gauss-Kruger zone 37", 4214, 4530, 16297); + return true; + case 359: + record = new EpsgProjectedCrsRecord(2414, "Beijing 1954 / 3-degree Gauss-Kruger zone 38", 4214, 4530, 16298); + return true; + case 360: + record = new EpsgProjectedCrsRecord(2415, "Beijing 1954 / 3-degree Gauss-Kruger zone 39", 4214, 4530, 16299); + return true; + case 361: + record = new EpsgProjectedCrsRecord(2416, "Beijing 1954 / 3-degree Gauss-Kruger zone 40", 4214, 4530, 16070); + return true; + case 362: + record = new EpsgProjectedCrsRecord(2417, "Beijing 1954 / 3-degree Gauss-Kruger zone 41", 4214, 4530, 16071); + return true; + case 363: + record = new EpsgProjectedCrsRecord(2418, "Beijing 1954 / 3-degree Gauss-Kruger zone 42", 4214, 4530, 16072); + return true; + case 364: + record = new EpsgProjectedCrsRecord(2419, "Beijing 1954 / 3-degree Gauss-Kruger zone 43", 4214, 4530, 16073); + return true; + case 365: + record = new EpsgProjectedCrsRecord(2420, "Beijing 1954 / 3-degree Gauss-Kruger zone 44", 4214, 4530, 16074); + return true; + case 366: + record = new EpsgProjectedCrsRecord(2421, "Beijing 1954 / 3-degree Gauss-Kruger zone 45", 4214, 4530, 16075); + return true; + case 367: + record = new EpsgProjectedCrsRecord(2422, "Beijing 1954 / 3-degree Gauss-Kruger CM 75E", 4214, 4530, 16313); + return true; + case 368: + record = new EpsgProjectedCrsRecord(2423, "Beijing 1954 / 3-degree Gauss-Kruger CM 78E", 4214, 4530, 16386); + return true; + case 369: + record = new EpsgProjectedCrsRecord(2424, "Beijing 1954 / 3-degree Gauss-Kruger CM 81E", 4214, 4530, 16314); + return true; + case 370: + record = new EpsgProjectedCrsRecord(2425, "Beijing 1954 / 3-degree Gauss-Kruger CM 84E", 4214, 4530, 16388); + return true; + case 371: + record = new EpsgProjectedCrsRecord(2426, "Beijing 1954 / 3-degree Gauss-Kruger CM 87E", 4214, 4530, 16315); + return true; + case 372: + record = new EpsgProjectedCrsRecord(2427, "Beijing 1954 / 3-degree Gauss-Kruger CM 90E", 4214, 4530, 16390); + return true; + case 373: + record = new EpsgProjectedCrsRecord(2428, "Beijing 1954 / 3-degree Gauss-Kruger CM 93E", 4214, 4530, 16316); + return true; + case 374: + record = new EpsgProjectedCrsRecord(2429, "Beijing 1954 / 3-degree Gauss-Kruger CM 96E", 4214, 4530, 16392); + return true; + case 375: + record = new EpsgProjectedCrsRecord(2430, "Beijing 1954 / 3-degree Gauss-Kruger CM 99E", 4214, 4530, 16317); + return true; + case 376: + record = new EpsgProjectedCrsRecord(2431, "Beijing 1954 / 3-degree Gauss-Kruger CM 102E", 4214, 4530, 16394); + return true; + case 377: + record = new EpsgProjectedCrsRecord(2432, "Beijing 1954 / 3-degree Gauss-Kruger CM 105E", 4214, 4530, 16318); + return true; + case 378: + record = new EpsgProjectedCrsRecord(2433, "Beijing 1954 / 3-degree Gauss-Kruger CM 108E", 4214, 4530, 16396); + return true; + case 379: + record = new EpsgProjectedCrsRecord(2434, "Beijing 1954 / 3-degree Gauss-Kruger CM 111E", 4214, 4530, 16319); + return true; + case 380: + record = new EpsgProjectedCrsRecord(2435, "Beijing 1954 / 3-degree Gauss-Kruger CM 114E", 4214, 4530, 16398); + return true; + case 381: + record = new EpsgProjectedCrsRecord(2436, "Beijing 1954 / 3-degree Gauss-Kruger CM 117E", 4214, 4530, 16320); + return true; + case 382: + record = new EpsgProjectedCrsRecord(2437, "Beijing 1954 / 3-degree Gauss-Kruger CM 120E", 4214, 4530, 16170); + return true; + case 383: + record = new EpsgProjectedCrsRecord(2438, "Beijing 1954 / 3-degree Gauss-Kruger CM 123E", 4214, 4530, 16321); + return true; + case 384: + record = new EpsgProjectedCrsRecord(2439, "Beijing 1954 / 3-degree Gauss-Kruger CM 126E", 4214, 4530, 16172); + return true; + case 385: + record = new EpsgProjectedCrsRecord(2440, "Beijing 1954 / 3-degree Gauss-Kruger CM 129E", 4214, 4530, 16322); + return true; + case 386: + record = new EpsgProjectedCrsRecord(2441, "Beijing 1954 / 3-degree Gauss-Kruger CM 132E", 4214, 4530, 16174); + return true; + case 387: + record = new EpsgProjectedCrsRecord(2442, "Beijing 1954 / 3-degree Gauss-Kruger CM 135E", 4214, 4530, 16323); + return true; + case 388: + record = new EpsgProjectedCrsRecord(2443, "JGD2000 / Japan Plane Rectangular CS I", 4612, 4530, 17801); + return true; + case 389: + record = new EpsgProjectedCrsRecord(2444, "JGD2000 / Japan Plane Rectangular CS II", 4612, 4530, 17802); + return true; + case 390: + record = new EpsgProjectedCrsRecord(2445, "JGD2000 / Japan Plane Rectangular CS III", 4612, 4530, 17803); + return true; + case 391: + record = new EpsgProjectedCrsRecord(2446, "JGD2000 / Japan Plane Rectangular CS IV", 4612, 4530, 17804); + return true; + case 392: + record = new EpsgProjectedCrsRecord(2447, "JGD2000 / Japan Plane Rectangular CS V", 4612, 4530, 17805); + return true; + case 393: + record = new EpsgProjectedCrsRecord(2448, "JGD2000 / Japan Plane Rectangular CS VI", 4612, 4530, 17806); + return true; + case 394: + record = new EpsgProjectedCrsRecord(2449, "JGD2000 / Japan Plane Rectangular CS VII", 4612, 4530, 17807); + return true; + case 395: + record = new EpsgProjectedCrsRecord(2450, "JGD2000 / Japan Plane Rectangular CS VIII", 4612, 4530, 17808); + return true; + case 396: + record = new EpsgProjectedCrsRecord(2451, "JGD2000 / Japan Plane Rectangular CS IX", 4612, 4530, 17809); + return true; + case 397: + record = new EpsgProjectedCrsRecord(2452, "JGD2000 / Japan Plane Rectangular CS X", 4612, 4530, 17810); + return true; + case 398: + record = new EpsgProjectedCrsRecord(2453, "JGD2000 / Japan Plane Rectangular CS XI", 4612, 4530, 17811); + return true; + case 399: + record = new EpsgProjectedCrsRecord(2454, "JGD2000 / Japan Plane Rectangular CS XII", 4612, 4530, 17812); + return true; + case 400: + record = new EpsgProjectedCrsRecord(2455, "JGD2000 / Japan Plane Rectangular CS XIII", 4612, 4530, 17813); + return true; + case 401: + record = new EpsgProjectedCrsRecord(2456, "JGD2000 / Japan Plane Rectangular CS XIV", 4612, 4530, 17814); + return true; + case 402: + record = new EpsgProjectedCrsRecord(2457, "JGD2000 / Japan Plane Rectangular CS XV", 4612, 4530, 17815); + return true; + case 403: + record = new EpsgProjectedCrsRecord(2458, "JGD2000 / Japan Plane Rectangular CS XVI", 4612, 4530, 17816); + return true; + case 404: + record = new EpsgProjectedCrsRecord(2459, "JGD2000 / Japan Plane Rectangular CS XVII", 4612, 4530, 17817); + return true; + case 405: + record = new EpsgProjectedCrsRecord(2460, "JGD2000 / Japan Plane Rectangular CS XVIII", 4612, 4530, 17818); + return true; + case 406: + record = new EpsgProjectedCrsRecord(2461, "JGD2000 / Japan Plane Rectangular CS XIX", 4612, 4530, 17819); + return true; + case 407: + record = new EpsgProjectedCrsRecord(2462, "Albanian 1987 / Gauss-Kruger zone 4", 4191, 4530, 16204); + return true; + case 408: + record = new EpsgProjectedCrsRecord(2463, "Pulkovo 1995 / Gauss-Kruger CM 21E", 4200, 4530, 16304); + return true; + case 409: + record = new EpsgProjectedCrsRecord(2464, "Pulkovo 1995 / Gauss-Kruger CM 27E", 4200, 4530, 16305); + return true; + case 410: + record = new EpsgProjectedCrsRecord(2465, "Pulkovo 1995 / Gauss-Kruger CM 33E", 4200, 4530, 16306); + return true; + case 411: + record = new EpsgProjectedCrsRecord(2466, "Pulkovo 1995 / Gauss-Kruger CM 39E", 4200, 4530, 16307); + return true; + case 412: + record = new EpsgProjectedCrsRecord(2467, "Pulkovo 1995 / Gauss-Kruger CM 45E", 4200, 4530, 16308); + return true; + case 413: + record = new EpsgProjectedCrsRecord(2468, "Pulkovo 1995 / Gauss-Kruger CM 51E", 4200, 4530, 16309); + return true; + case 414: + record = new EpsgProjectedCrsRecord(2469, "Pulkovo 1995 / Gauss-Kruger CM 57E", 4200, 4530, 16310); + return true; + case 415: + record = new EpsgProjectedCrsRecord(2470, "Pulkovo 1995 / Gauss-Kruger CM 63E", 4200, 4530, 16311); + return true; + case 416: + record = new EpsgProjectedCrsRecord(2471, "Pulkovo 1995 / Gauss-Kruger CM 69E", 4200, 4530, 16312); + return true; + case 417: + record = new EpsgProjectedCrsRecord(2472, "Pulkovo 1995 / Gauss-Kruger CM 75E", 4200, 4530, 16313); + return true; + case 418: + record = new EpsgProjectedCrsRecord(2473, "Pulkovo 1995 / Gauss-Kruger CM 81E", 4200, 4530, 16314); + return true; + case 419: + record = new EpsgProjectedCrsRecord(2474, "Pulkovo 1995 / Gauss-Kruger CM 87E", 4200, 4530, 16315); + return true; + case 420: + record = new EpsgProjectedCrsRecord(2475, "Pulkovo 1995 / Gauss-Kruger CM 93E", 4200, 4530, 16316); + return true; + case 421: + record = new EpsgProjectedCrsRecord(2476, "Pulkovo 1995 / Gauss-Kruger CM 99E", 4200, 4530, 16317); + return true; + case 422: + record = new EpsgProjectedCrsRecord(2477, "Pulkovo 1995 / Gauss-Kruger CM 105E", 4200, 4530, 16318); + return true; + case 423: + record = new EpsgProjectedCrsRecord(2478, "Pulkovo 1995 / Gauss-Kruger CM 111E", 4200, 4530, 16319); + return true; + case 424: + record = new EpsgProjectedCrsRecord(2479, "Pulkovo 1995 / Gauss-Kruger CM 117E", 4200, 4530, 16320); + return true; + case 425: + record = new EpsgProjectedCrsRecord(2480, "Pulkovo 1995 / Gauss-Kruger CM 123E", 4200, 4530, 16321); + return true; + case 426: + record = new EpsgProjectedCrsRecord(2481, "Pulkovo 1995 / Gauss-Kruger CM 129E", 4200, 4530, 16322); + return true; + case 427: + record = new EpsgProjectedCrsRecord(2482, "Pulkovo 1995 / Gauss-Kruger CM 135E", 4200, 4530, 16323); + return true; + case 428: + record = new EpsgProjectedCrsRecord(2483, "Pulkovo 1995 / Gauss-Kruger CM 141E", 4200, 4530, 16324); + return true; + case 429: + record = new EpsgProjectedCrsRecord(2484, "Pulkovo 1995 / Gauss-Kruger CM 147E", 4200, 4530, 16325); + return true; + case 430: + record = new EpsgProjectedCrsRecord(2485, "Pulkovo 1995 / Gauss-Kruger CM 153E", 4200, 4530, 16326); + return true; + case 431: + record = new EpsgProjectedCrsRecord(2486, "Pulkovo 1995 / Gauss-Kruger CM 159E", 4200, 4530, 16327); + return true; + case 432: + record = new EpsgProjectedCrsRecord(2487, "Pulkovo 1995 / Gauss-Kruger CM 165E", 4200, 4530, 16328); + return true; + case 433: + record = new EpsgProjectedCrsRecord(2488, "Pulkovo 1995 / Gauss-Kruger CM 171E", 4200, 4530, 16329); + return true; + case 434: + record = new EpsgProjectedCrsRecord(2489, "Pulkovo 1995 / Gauss-Kruger CM 177E", 4200, 4530, 16330); + return true; + case 435: + record = new EpsgProjectedCrsRecord(2490, "Pulkovo 1995 / Gauss-Kruger CM 177W", 4200, 4530, 16331); + return true; + case 436: + record = new EpsgProjectedCrsRecord(2491, "Pulkovo 1995 / Gauss-Kruger CM 171W", 4200, 4530, 16332); + return true; + case 437: + record = new EpsgProjectedCrsRecord(2494, "Pulkovo 1942 / Gauss-Kruger CM 21E", 4284, 4530, 16304); + return true; + case 438: + record = new EpsgProjectedCrsRecord(2495, "Pulkovo 1942 / Gauss-Kruger CM 27E", 4284, 4530, 16305); + return true; + case 439: + record = new EpsgProjectedCrsRecord(2496, "Pulkovo 1942 / Gauss-Kruger CM 33E", 4284, 4530, 16306); + return true; + case 440: + record = new EpsgProjectedCrsRecord(2497, "Pulkovo 1942 / Gauss-Kruger CM 39E", 4284, 4530, 16307); + return true; + case 441: + record = new EpsgProjectedCrsRecord(2498, "Pulkovo 1942 / Gauss-Kruger CM 45E", 4284, 4530, 16308); + return true; + case 442: + record = new EpsgProjectedCrsRecord(2499, "Pulkovo 1942 / Gauss-Kruger CM 51E", 4284, 4530, 16309); + return true; + case 443: + record = new EpsgProjectedCrsRecord(2500, "Pulkovo 1942 / Gauss-Kruger CM 57E", 4284, 4530, 16310); + return true; + case 444: + record = new EpsgProjectedCrsRecord(2501, "Pulkovo 1942 / Gauss-Kruger CM 63E", 4284, 4530, 16311); + return true; + case 445: + record = new EpsgProjectedCrsRecord(2502, "Pulkovo 1942 / Gauss-Kruger CM 69E", 4284, 4530, 16312); + return true; + case 446: + record = new EpsgProjectedCrsRecord(2503, "Pulkovo 1942 / Gauss-Kruger CM 75E", 4284, 4530, 16313); + return true; + case 447: + record = new EpsgProjectedCrsRecord(2504, "Pulkovo 1942 / Gauss-Kruger CM 81E", 4284, 4530, 16314); + return true; + case 448: + record = new EpsgProjectedCrsRecord(2505, "Pulkovo 1942 / Gauss-Kruger CM 87E", 4284, 4530, 16315); + return true; + case 449: + record = new EpsgProjectedCrsRecord(2506, "Pulkovo 1942 / Gauss-Kruger CM 93E", 4284, 4530, 16316); + return true; + case 450: + record = new EpsgProjectedCrsRecord(2507, "Pulkovo 1942 / Gauss-Kruger CM 99E", 4284, 4530, 16317); + return true; + case 451: + record = new EpsgProjectedCrsRecord(2508, "Pulkovo 1942 / Gauss-Kruger CM 105E", 4284, 4530, 16318); + return true; + case 452: + record = new EpsgProjectedCrsRecord(2509, "Pulkovo 1942 / Gauss-Kruger CM 111E", 4284, 4530, 16319); + return true; + case 453: + record = new EpsgProjectedCrsRecord(2510, "Pulkovo 1942 / Gauss-Kruger CM 117E", 4284, 4530, 16320); + return true; + case 454: + record = new EpsgProjectedCrsRecord(2511, "Pulkovo 1942 / Gauss-Kruger CM 123E", 4284, 4530, 16321); + return true; + case 455: + record = new EpsgProjectedCrsRecord(2512, "Pulkovo 1942 / Gauss-Kruger CM 129E", 4284, 4530, 16322); + return true; + case 456: + record = new EpsgProjectedCrsRecord(2513, "Pulkovo 1942 / Gauss-Kruger CM 135E", 4284, 4530, 16323); + return true; + case 457: + record = new EpsgProjectedCrsRecord(2514, "Pulkovo 1942 / Gauss-Kruger CM 141E", 4284, 4530, 16324); + return true; + case 458: + record = new EpsgProjectedCrsRecord(2515, "Pulkovo 1942 / Gauss-Kruger CM 147E", 4284, 4530, 16325); + return true; + case 459: + record = new EpsgProjectedCrsRecord(2516, "Pulkovo 1942 / Gauss-Kruger CM 153E", 4284, 4530, 16326); + return true; + case 460: + record = new EpsgProjectedCrsRecord(2517, "Pulkovo 1942 / Gauss-Kruger CM 159E", 4284, 4530, 16327); + return true; + case 461: + record = new EpsgProjectedCrsRecord(2518, "Pulkovo 1942 / Gauss-Kruger CM 165E", 4284, 4530, 16328); + return true; + case 462: + record = new EpsgProjectedCrsRecord(2519, "Pulkovo 1942 / Gauss-Kruger CM 171E", 4284, 4530, 16329); + return true; + case 463: + record = new EpsgProjectedCrsRecord(2520, "Pulkovo 1942 / Gauss-Kruger CM 177E", 4284, 4530, 16330); + return true; + case 464: + record = new EpsgProjectedCrsRecord(2521, "Pulkovo 1942 / Gauss-Kruger CM 177W", 4284, 4530, 16331); + return true; + case 465: + record = new EpsgProjectedCrsRecord(2522, "Pulkovo 1942 / Gauss-Kruger CM 171W", 4284, 4530, 16332); + return true; + case 466: + record = new EpsgProjectedCrsRecord(2523, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 7", 4284, 4530, 16267); + return true; + case 467: + record = new EpsgProjectedCrsRecord(2524, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 8", 4284, 4530, 16268); + return true; + case 468: + record = new EpsgProjectedCrsRecord(2525, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 9", 4284, 4530, 16269); + return true; + case 469: + record = new EpsgProjectedCrsRecord(2526, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 10", 4284, 4530, 16270); + return true; + case 470: + record = new EpsgProjectedCrsRecord(2527, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 11", 4284, 4530, 16271); + return true; + case 471: + record = new EpsgProjectedCrsRecord(2528, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 12", 4284, 4530, 16272); + return true; + case 472: + record = new EpsgProjectedCrsRecord(2529, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 13", 4284, 4530, 16273); + return true; + case 473: + record = new EpsgProjectedCrsRecord(2530, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 14", 4284, 4530, 16274); + return true; + case 474: + record = new EpsgProjectedCrsRecord(2531, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 15", 4284, 4530, 16275); + return true; + case 475: + record = new EpsgProjectedCrsRecord(2532, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 16", 4284, 4530, 16276); + return true; + case 476: + record = new EpsgProjectedCrsRecord(2533, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 17", 4284, 4530, 16277); + return true; + case 477: + record = new EpsgProjectedCrsRecord(2534, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 18", 4284, 4530, 16278); + return true; + case 478: + record = new EpsgProjectedCrsRecord(2535, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 19", 4284, 4530, 16279); + return true; + case 479: + record = new EpsgProjectedCrsRecord(2536, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 20", 4284, 4530, 16280); + return true; + case 480: + record = new EpsgProjectedCrsRecord(2537, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 21", 4284, 4530, 16281); + return true; + case 481: + record = new EpsgProjectedCrsRecord(2538, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 22", 4284, 4530, 16282); + return true; + case 482: + record = new EpsgProjectedCrsRecord(2539, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 23", 4284, 4530, 16283); + return true; + case 483: + record = new EpsgProjectedCrsRecord(2540, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 24", 4284, 4530, 16284); + return true; + case 484: + record = new EpsgProjectedCrsRecord(2541, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 25", 4284, 4530, 16285); + return true; + case 485: + record = new EpsgProjectedCrsRecord(2542, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 26", 4284, 4530, 16286); + return true; + case 486: + record = new EpsgProjectedCrsRecord(2543, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 27", 4284, 4530, 16287); + return true; + case 487: + record = new EpsgProjectedCrsRecord(2544, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 28", 4284, 4530, 16288); + return true; + case 488: + record = new EpsgProjectedCrsRecord(2545, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 29", 4284, 4530, 16289); + return true; + case 489: + record = new EpsgProjectedCrsRecord(2546, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 30", 4284, 4530, 16290); + return true; + case 490: + record = new EpsgProjectedCrsRecord(2547, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 31", 4284, 4530, 16291); + return true; + case 491: + record = new EpsgProjectedCrsRecord(2548, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 32", 4284, 4530, 16292); + return true; + case 492: + record = new EpsgProjectedCrsRecord(2549, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 33", 4284, 4530, 16293); + return true; + case 493: + record = new EpsgProjectedCrsRecord(2551, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 34", 4284, 4530, 16294); + return true; + case 494: + record = new EpsgProjectedCrsRecord(2552, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 35", 4284, 4530, 16295); + return true; + case 495: + record = new EpsgProjectedCrsRecord(2553, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 36", 4284, 4530, 16296); + return true; + case 496: + record = new EpsgProjectedCrsRecord(2554, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 37", 4284, 4530, 16297); + return true; + case 497: + record = new EpsgProjectedCrsRecord(2555, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 38", 4284, 4530, 16298); + return true; + case 498: + record = new EpsgProjectedCrsRecord(2556, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 39", 4284, 4530, 16299); + return true; + case 499: + record = new EpsgProjectedCrsRecord(2557, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 40", 4284, 4530, 16070); + return true; + case 500: + record = new EpsgProjectedCrsRecord(2558, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 41", 4284, 4530, 16071); + return true; + case 501: + record = new EpsgProjectedCrsRecord(2559, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 42", 4284, 4530, 16072); + return true; + case 502: + record = new EpsgProjectedCrsRecord(2560, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 43", 4284, 4530, 16073); + return true; + case 503: + record = new EpsgProjectedCrsRecord(2561, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 44", 4284, 4530, 16074); + return true; + case 504: + record = new EpsgProjectedCrsRecord(2562, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 45", 4284, 4530, 16075); + return true; + case 505: + record = new EpsgProjectedCrsRecord(2563, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 46", 4284, 4530, 16076); + return true; + case 506: + record = new EpsgProjectedCrsRecord(2564, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 47", 4284, 4530, 16077); + return true; + case 507: + record = new EpsgProjectedCrsRecord(2565, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 48", 4284, 4530, 16078); + return true; + case 508: + record = new EpsgProjectedCrsRecord(2566, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 49", 4284, 4530, 16079); + return true; + case 509: + record = new EpsgProjectedCrsRecord(2567, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 50", 4284, 4530, 16080); + return true; + case 510: + record = new EpsgProjectedCrsRecord(2568, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 51", 4284, 4530, 16081); + return true; + case 511: + record = new EpsgProjectedCrsRecord(2569, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 52", 4284, 4530, 16082); + return true; + case 512: + record = new EpsgProjectedCrsRecord(2570, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 53", 4284, 4530, 16083); + return true; + case 513: + record = new EpsgProjectedCrsRecord(2571, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 54", 4284, 4530, 16084); + return true; + case 514: + record = new EpsgProjectedCrsRecord(2572, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 55", 4284, 4530, 16085); + return true; + case 515: + record = new EpsgProjectedCrsRecord(2573, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 56", 4284, 4530, 16086); + return true; + case 516: + record = new EpsgProjectedCrsRecord(2574, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 57", 4284, 4530, 16087); + return true; + case 517: + record = new EpsgProjectedCrsRecord(2575, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 58", 4284, 4530, 16088); + return true; + case 518: + record = new EpsgProjectedCrsRecord(2576, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 59", 4284, 4530, 16089); + return true; + case 519: + record = new EpsgProjectedCrsRecord(2578, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 61", 4284, 4530, 16091); + return true; + case 520: + record = new EpsgProjectedCrsRecord(2579, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 62", 4284, 4530, 16092); + return true; + case 521: + record = new EpsgProjectedCrsRecord(2580, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 63", 4284, 4530, 16093); + return true; + case 522: + record = new EpsgProjectedCrsRecord(2581, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 64", 4284, 4530, 16094); + return true; + case 523: + record = new EpsgProjectedCrsRecord(2582, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 21E", 4284, 4530, 16304); + return true; + case 524: + record = new EpsgProjectedCrsRecord(2583, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 24E", 4284, 4530, 16368); + return true; + case 525: + record = new EpsgProjectedCrsRecord(2584, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 27E", 4284, 4530, 16305); + return true; + case 526: + record = new EpsgProjectedCrsRecord(2585, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 30E", 4284, 4530, 16370); + return true; + case 527: + record = new EpsgProjectedCrsRecord(2586, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 33E", 4284, 4530, 16306); + return true; + case 528: + record = new EpsgProjectedCrsRecord(2587, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 36E", 4284, 4530, 16372); + return true; + case 529: + record = new EpsgProjectedCrsRecord(2588, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 39E", 4284, 4530, 16307); + return true; + case 530: + record = new EpsgProjectedCrsRecord(2589, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 42E", 4284, 4530, 16374); + return true; + case 531: + record = new EpsgProjectedCrsRecord(2590, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 45E", 4284, 4530, 16308); + return true; + case 532: + record = new EpsgProjectedCrsRecord(2591, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 48E", 4284, 4530, 16376); + return true; + case 533: + record = new EpsgProjectedCrsRecord(2592, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 51E", 4284, 4530, 16309); + return true; + case 534: + record = new EpsgProjectedCrsRecord(2593, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 54E", 4284, 4530, 16378); + return true; + case 535: + record = new EpsgProjectedCrsRecord(2594, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 57E", 4284, 4530, 16310); + return true; + case 536: + record = new EpsgProjectedCrsRecord(2595, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 60E", 4284, 4530, 16380); + return true; + case 537: + record = new EpsgProjectedCrsRecord(2596, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 63E", 4284, 4530, 16311); + return true; + case 538: + record = new EpsgProjectedCrsRecord(2597, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 66E", 4284, 4530, 16382); + return true; + case 539: + record = new EpsgProjectedCrsRecord(2598, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 69E", 4284, 4530, 16312); + return true; + case 540: + record = new EpsgProjectedCrsRecord(2599, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 72E", 4284, 4530, 16384); + return true; + case 541: + record = new EpsgProjectedCrsRecord(2601, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 75E", 4284, 4530, 16313); + return true; + case 542: + record = new EpsgProjectedCrsRecord(2602, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 78E", 4284, 4530, 16386); + return true; + case 543: + record = new EpsgProjectedCrsRecord(2603, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 81E", 4284, 4530, 16314); + return true; + case 544: + record = new EpsgProjectedCrsRecord(2604, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 84E", 4284, 4530, 16388); + return true; + case 545: + record = new EpsgProjectedCrsRecord(2605, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 87E", 4284, 4530, 16315); + return true; + case 546: + record = new EpsgProjectedCrsRecord(2606, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 90E", 4284, 4530, 16390); + return true; + case 547: + record = new EpsgProjectedCrsRecord(2607, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 93E", 4284, 4530, 16316); + return true; + case 548: + record = new EpsgProjectedCrsRecord(2608, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 96E", 4284, 4530, 16392); + return true; + case 549: + record = new EpsgProjectedCrsRecord(2609, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 99E", 4284, 4530, 16317); + return true; + case 550: + record = new EpsgProjectedCrsRecord(2610, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 102E", 4284, 4530, 16394); + return true; + case 551: + record = new EpsgProjectedCrsRecord(2611, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 105E", 4284, 4530, 16318); + return true; + case 552: + record = new EpsgProjectedCrsRecord(2612, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 108E", 4284, 4530, 16396); + return true; + case 553: + record = new EpsgProjectedCrsRecord(2613, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 111E", 4284, 4530, 16319); + return true; + case 554: + record = new EpsgProjectedCrsRecord(2614, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 114E", 4284, 4530, 16398); + return true; + case 555: + record = new EpsgProjectedCrsRecord(2615, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 117E", 4284, 4530, 16320); + return true; + case 556: + record = new EpsgProjectedCrsRecord(2616, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 120E", 4284, 4530, 16170); + return true; + case 557: + record = new EpsgProjectedCrsRecord(2617, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 123E", 4284, 4530, 16321); + return true; + case 558: + record = new EpsgProjectedCrsRecord(2618, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 126E", 4284, 4530, 16172); + return true; + case 559: + record = new EpsgProjectedCrsRecord(2619, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 129E", 4284, 4530, 16322); + return true; + case 560: + record = new EpsgProjectedCrsRecord(2620, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 132E", 4284, 4530, 16174); + return true; + case 561: + record = new EpsgProjectedCrsRecord(2621, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 135E", 4284, 4530, 16323); + return true; + case 562: + record = new EpsgProjectedCrsRecord(2622, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 138E", 4284, 4530, 16176); + return true; + case 563: + record = new EpsgProjectedCrsRecord(2623, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 141E", 4284, 4530, 16324); + return true; + case 564: + record = new EpsgProjectedCrsRecord(2624, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 144E", 4284, 4530, 16178); + return true; + case 565: + record = new EpsgProjectedCrsRecord(2625, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 147E", 4284, 4530, 16325); + return true; + case 566: + record = new EpsgProjectedCrsRecord(2626, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 150E", 4284, 4530, 16180); + return true; + case 567: + record = new EpsgProjectedCrsRecord(2627, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 153E", 4284, 4530, 16326); + return true; + case 568: + record = new EpsgProjectedCrsRecord(2628, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 156E", 4284, 4530, 16182); + return true; + case 569: + record = new EpsgProjectedCrsRecord(2629, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 159E", 4284, 4530, 16327); + return true; + case 570: + record = new EpsgProjectedCrsRecord(2630, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 162E", 4284, 4530, 16184); + return true; + case 571: + record = new EpsgProjectedCrsRecord(2631, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 165E", 4284, 4530, 16328); + return true; + case 572: + record = new EpsgProjectedCrsRecord(2632, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 168E", 4284, 4530, 16186); + return true; + case 573: + record = new EpsgProjectedCrsRecord(2633, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 171E", 4284, 4530, 16329); + return true; + case 574: + record = new EpsgProjectedCrsRecord(2634, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 174E", 4284, 4530, 16188); + return true; + case 575: + record = new EpsgProjectedCrsRecord(2635, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 177E", 4284, 4530, 16330); + return true; + case 576: + record = new EpsgProjectedCrsRecord(2636, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 180E", 4284, 4530, 16190); + return true; + case 577: + record = new EpsgProjectedCrsRecord(2637, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 177W", 4284, 4530, 16331); + return true; + case 578: + record = new EpsgProjectedCrsRecord(2638, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 174W", 4284, 4530, 16192); + return true; + case 579: + record = new EpsgProjectedCrsRecord(2639, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 171W", 4284, 4530, 16332); + return true; + case 580: + record = new EpsgProjectedCrsRecord(2640, "Pulkovo 1942 / 3-degree Gauss-Kruger CM 168W", 4284, 4530, 16194); + return true; + case 581: + record = new EpsgProjectedCrsRecord(2641, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 7", 4200, 4530, 16267); + return true; + case 582: + record = new EpsgProjectedCrsRecord(2642, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 8", 4200, 4530, 16268); + return true; + case 583: + record = new EpsgProjectedCrsRecord(2643, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 9", 4200, 4530, 16269); + return true; + case 584: + record = new EpsgProjectedCrsRecord(2644, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 10", 4200, 4530, 16270); + return true; + case 585: + record = new EpsgProjectedCrsRecord(2645, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 11", 4200, 4530, 16271); + return true; + case 586: + record = new EpsgProjectedCrsRecord(2646, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 12", 4200, 4530, 16272); + return true; + case 587: + record = new EpsgProjectedCrsRecord(2647, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 13", 4200, 4530, 16273); + return true; + case 588: + record = new EpsgProjectedCrsRecord(2648, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 14", 4200, 4530, 16274); + return true; + case 589: + record = new EpsgProjectedCrsRecord(2649, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 15", 4200, 4530, 16275); + return true; + case 590: + record = new EpsgProjectedCrsRecord(2650, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 16", 4200, 4530, 16276); + return true; + case 591: + record = new EpsgProjectedCrsRecord(2651, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 17", 4200, 4530, 16277); + return true; + case 592: + record = new EpsgProjectedCrsRecord(2652, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 18", 4200, 4530, 16278); + return true; + case 593: + record = new EpsgProjectedCrsRecord(2653, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 19", 4200, 4530, 16279); + return true; + case 594: + record = new EpsgProjectedCrsRecord(2654, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 20", 4200, 4530, 16280); + return true; + case 595: + record = new EpsgProjectedCrsRecord(2655, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 21", 4200, 4530, 16281); + return true; + case 596: + record = new EpsgProjectedCrsRecord(2656, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 22", 4200, 4530, 16282); + return true; + case 597: + record = new EpsgProjectedCrsRecord(2657, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 23", 4200, 4530, 16283); + return true; + case 598: + record = new EpsgProjectedCrsRecord(2658, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 24", 4200, 4530, 16284); + return true; + case 599: + record = new EpsgProjectedCrsRecord(2659, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 25", 4200, 4530, 16285); + return true; + case 600: + record = new EpsgProjectedCrsRecord(2660, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 26", 4200, 4530, 16286); + return true; + case 601: + record = new EpsgProjectedCrsRecord(2661, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 27", 4200, 4530, 16287); + return true; + case 602: + record = new EpsgProjectedCrsRecord(2662, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 28", 4200, 4530, 16288); + return true; + case 603: + record = new EpsgProjectedCrsRecord(2663, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 29", 4200, 4530, 16289); + return true; + case 604: + record = new EpsgProjectedCrsRecord(2664, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 30", 4200, 4530, 16290); + return true; + case 605: + record = new EpsgProjectedCrsRecord(2665, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 31", 4200, 4530, 16291); + return true; + case 606: + record = new EpsgProjectedCrsRecord(2666, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 32", 4200, 4530, 16292); + return true; + case 607: + record = new EpsgProjectedCrsRecord(2667, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 33", 4200, 4530, 16293); + return true; + case 608: + record = new EpsgProjectedCrsRecord(2668, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 34", 4200, 4530, 16294); + return true; + case 609: + record = new EpsgProjectedCrsRecord(2669, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 35", 4200, 4530, 16295); + return true; + case 610: + record = new EpsgProjectedCrsRecord(2670, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 36", 4200, 4530, 16296); + return true; + case 611: + record = new EpsgProjectedCrsRecord(2671, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 37", 4200, 4530, 16297); + return true; + case 612: + record = new EpsgProjectedCrsRecord(2672, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 38", 4200, 4530, 16298); + return true; + case 613: + record = new EpsgProjectedCrsRecord(2673, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 39", 4200, 4530, 16299); + return true; + case 614: + record = new EpsgProjectedCrsRecord(2674, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 40", 4200, 4530, 16070); + return true; + case 615: + record = new EpsgProjectedCrsRecord(2675, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 41", 4200, 4530, 16071); + return true; + case 616: + record = new EpsgProjectedCrsRecord(2676, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 42", 4200, 4530, 16072); + return true; + case 617: + record = new EpsgProjectedCrsRecord(2677, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 43", 4200, 4530, 16073); + return true; + case 618: + record = new EpsgProjectedCrsRecord(2678, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 44", 4200, 4530, 16074); + return true; + case 619: + record = new EpsgProjectedCrsRecord(2679, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 45", 4200, 4530, 16075); + return true; + case 620: + record = new EpsgProjectedCrsRecord(2680, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 46", 4200, 4530, 16076); + return true; + case 621: + record = new EpsgProjectedCrsRecord(2681, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 47", 4200, 4530, 16077); + return true; + case 622: + record = new EpsgProjectedCrsRecord(2682, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 48", 4200, 4530, 16078); + return true; + case 623: + record = new EpsgProjectedCrsRecord(2683, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 49", 4200, 4530, 16079); + return true; + case 624: + record = new EpsgProjectedCrsRecord(2684, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 50", 4200, 4530, 16080); + return true; + case 625: + record = new EpsgProjectedCrsRecord(2685, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 51", 4200, 4530, 16081); + return true; + case 626: + record = new EpsgProjectedCrsRecord(2686, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 52", 4200, 4530, 16082); + return true; + case 627: + record = new EpsgProjectedCrsRecord(2687, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 53", 4200, 4530, 16083); + return true; + case 628: + record = new EpsgProjectedCrsRecord(2688, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 54", 4200, 4530, 16084); + return true; + case 629: + record = new EpsgProjectedCrsRecord(2689, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 55", 4200, 4530, 16085); + return true; + case 630: + record = new EpsgProjectedCrsRecord(2690, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 56", 4200, 4530, 16086); + return true; + case 631: + record = new EpsgProjectedCrsRecord(2691, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 57", 4200, 4530, 16087); + return true; + case 632: + record = new EpsgProjectedCrsRecord(2692, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 58", 4200, 4530, 16088); + return true; + case 633: + record = new EpsgProjectedCrsRecord(2693, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 59", 4200, 4530, 16089); + return true; + case 634: + record = new EpsgProjectedCrsRecord(2695, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 61", 4200, 4530, 16091); + return true; + case 635: + record = new EpsgProjectedCrsRecord(2696, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 62", 4200, 4530, 16092); + return true; + case 636: + record = new EpsgProjectedCrsRecord(2697, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 63", 4200, 4530, 16093); + return true; + case 637: + record = new EpsgProjectedCrsRecord(2698, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 64", 4200, 4530, 16094); + return true; + case 638: + record = new EpsgProjectedCrsRecord(2699, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 21E", 4200, 4530, 16304); + return true; + case 639: + record = new EpsgProjectedCrsRecord(2700, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 24E", 4200, 4530, 16368); + return true; + case 640: + record = new EpsgProjectedCrsRecord(2701, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 27E", 4200, 4530, 16305); + return true; + case 641: + record = new EpsgProjectedCrsRecord(2702, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 30E", 4200, 4530, 16370); + return true; + case 642: + record = new EpsgProjectedCrsRecord(2703, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 33E", 4200, 4530, 16306); + return true; + case 643: + record = new EpsgProjectedCrsRecord(2704, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 36E", 4200, 4530, 16372); + return true; + case 644: + record = new EpsgProjectedCrsRecord(2705, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 39E", 4200, 4530, 16307); + return true; + case 645: + record = new EpsgProjectedCrsRecord(2706, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 42E", 4200, 4530, 16374); + return true; + case 646: + record = new EpsgProjectedCrsRecord(2707, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 45E", 4200, 4530, 16308); + return true; + case 647: + record = new EpsgProjectedCrsRecord(2708, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 48E", 4200, 4530, 16376); + return true; + case 648: + record = new EpsgProjectedCrsRecord(2709, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 51E", 4200, 4530, 16309); + return true; + case 649: + record = new EpsgProjectedCrsRecord(2710, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 54E", 4200, 4530, 16378); + return true; + case 650: + record = new EpsgProjectedCrsRecord(2711, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 57E", 4200, 4530, 16310); + return true; + case 651: + record = new EpsgProjectedCrsRecord(2712, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 60E", 4200, 4530, 16380); + return true; + case 652: + record = new EpsgProjectedCrsRecord(2713, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 63E", 4200, 4530, 16311); + return true; + case 653: + record = new EpsgProjectedCrsRecord(2714, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 66E", 4200, 4530, 16382); + return true; + case 654: + record = new EpsgProjectedCrsRecord(2715, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 69E", 4200, 4530, 16312); + return true; + case 655: + record = new EpsgProjectedCrsRecord(2716, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 72E", 4200, 4530, 16384); + return true; + case 656: + record = new EpsgProjectedCrsRecord(2717, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 75E", 4200, 4530, 16313); + return true; + case 657: + record = new EpsgProjectedCrsRecord(2718, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 78E", 4200, 4530, 16386); + return true; + case 658: + record = new EpsgProjectedCrsRecord(2719, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 81E", 4200, 4530, 16314); + return true; + case 659: + record = new EpsgProjectedCrsRecord(2720, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 84E", 4200, 4530, 16388); + return true; + case 660: + record = new EpsgProjectedCrsRecord(2721, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 87E", 4200, 4530, 16315); + return true; + case 661: + record = new EpsgProjectedCrsRecord(2722, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 90E", 4200, 4530, 16390); + return true; + case 662: + record = new EpsgProjectedCrsRecord(2723, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 93E", 4200, 4530, 16316); + return true; + case 663: + record = new EpsgProjectedCrsRecord(2724, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 96E", 4200, 4530, 16392); + return true; + case 664: + record = new EpsgProjectedCrsRecord(2725, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 99E", 4200, 4530, 16317); + return true; + case 665: + record = new EpsgProjectedCrsRecord(2726, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 102E", 4200, 4530, 16394); + return true; + case 666: + record = new EpsgProjectedCrsRecord(2727, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 105E", 4200, 4530, 16318); + return true; + case 667: + record = new EpsgProjectedCrsRecord(2728, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 108E", 4200, 4530, 16396); + return true; + case 668: + record = new EpsgProjectedCrsRecord(2729, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 111E", 4200, 4530, 16319); + return true; + case 669: + record = new EpsgProjectedCrsRecord(2730, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 114E", 4200, 4530, 16398); + return true; + case 670: + record = new EpsgProjectedCrsRecord(2731, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 117E", 4200, 4530, 16320); + return true; + case 671: + record = new EpsgProjectedCrsRecord(2732, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 120E", 4200, 4530, 16170); + return true; + case 672: + record = new EpsgProjectedCrsRecord(2733, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 123E", 4200, 4530, 16321); + return true; + case 673: + record = new EpsgProjectedCrsRecord(2734, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 126E", 4200, 4530, 16172); + return true; + case 674: + record = new EpsgProjectedCrsRecord(2735, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 129E", 4200, 4530, 16322); + return true; + case 675: + record = new EpsgProjectedCrsRecord(2736, "Tete / UTM zone 36S", 4127, 4400, 16136); + return true; + case 676: + record = new EpsgProjectedCrsRecord(2737, "Tete / UTM zone 37S", 4127, 4400, 16137); + return true; + case 677: + record = new EpsgProjectedCrsRecord(2738, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 132E", 4200, 4530, 16174); + return true; + case 678: + record = new EpsgProjectedCrsRecord(2739, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 135E", 4200, 4530, 16323); + return true; + case 679: + record = new EpsgProjectedCrsRecord(2740, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 138E", 4200, 4530, 16176); + return true; + case 680: + record = new EpsgProjectedCrsRecord(2741, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 141E", 4200, 4530, 16324); + return true; + case 681: + record = new EpsgProjectedCrsRecord(2742, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 144E", 4200, 4530, 16178); + return true; + case 682: + record = new EpsgProjectedCrsRecord(2743, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 147E", 4200, 4530, 16325); + return true; + case 683: + record = new EpsgProjectedCrsRecord(2744, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 150E", 4200, 4530, 16180); + return true; + case 684: + record = new EpsgProjectedCrsRecord(2745, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 153E", 4200, 4530, 16326); + return true; + case 685: + record = new EpsgProjectedCrsRecord(2746, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 156E", 4200, 4530, 16182); + return true; + case 686: + record = new EpsgProjectedCrsRecord(2747, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 159E", 4200, 4530, 16327); + return true; + case 687: + record = new EpsgProjectedCrsRecord(2748, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 162E", 4200, 4530, 16184); + return true; + case 688: + record = new EpsgProjectedCrsRecord(2749, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 165E", 4200, 4530, 16328); + return true; + case 689: + record = new EpsgProjectedCrsRecord(2750, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 168E", 4200, 4530, 16186); + return true; + case 690: + record = new EpsgProjectedCrsRecord(2751, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 171E", 4200, 4530, 16329); + return true; + case 691: + record = new EpsgProjectedCrsRecord(2752, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 174E", 4200, 4530, 16188); + return true; + case 692: + record = new EpsgProjectedCrsRecord(2753, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 177E", 4200, 4530, 16330); + return true; + case 693: + record = new EpsgProjectedCrsRecord(2754, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 180E", 4200, 4530, 16190); + return true; + case 694: + record = new EpsgProjectedCrsRecord(2755, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 177W", 4200, 4530, 16331); + return true; + case 695: + record = new EpsgProjectedCrsRecord(2756, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 174W", 4200, 4530, 16192); + return true; + case 696: + record = new EpsgProjectedCrsRecord(2757, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 171W", 4200, 4530, 16332); + return true; + case 697: + record = new EpsgProjectedCrsRecord(2758, "Pulkovo 1995 / 3-degree Gauss-Kruger CM 168W", 4200, 4530, 16194); + return true; + case 698: + record = new EpsgProjectedCrsRecord(2759, "NAD83(HARN) / Alabama East", 4152, 4499, 10131); + return true; + case 699: + record = new EpsgProjectedCrsRecord(2760, "NAD83(HARN) / Alabama West", 4152, 4499, 10132); + return true; + case 700: + record = new EpsgProjectedCrsRecord(2761, "NAD83(HARN) / Arizona East", 4152, 4499, 10231); + return true; + case 701: + record = new EpsgProjectedCrsRecord(2762, "NAD83(HARN) / Arizona Central", 4152, 4499, 10232); + return true; + case 702: + record = new EpsgProjectedCrsRecord(2763, "NAD83(HARN) / Arizona West", 4152, 4499, 10233); + return true; + case 703: + record = new EpsgProjectedCrsRecord(2764, "NAD83(HARN) / Arkansas North", 4152, 4499, 10331); + return true; + case 704: + record = new EpsgProjectedCrsRecord(2765, "NAD83(HARN) / Arkansas South", 4152, 4499, 10332); + return true; + case 705: + record = new EpsgProjectedCrsRecord(2766, "NAD83(HARN) / California zone 1", 4152, 4499, 10431); + return true; + case 706: + record = new EpsgProjectedCrsRecord(2767, "NAD83(HARN) / California zone 2", 4152, 4499, 10432); + return true; + case 707: + record = new EpsgProjectedCrsRecord(2768, "NAD83(HARN) / California zone 3", 4152, 4499, 10433); + return true; + case 708: + record = new EpsgProjectedCrsRecord(2769, "NAD83(HARN) / California zone 4", 4152, 4499, 10434); + return true; + case 709: + record = new EpsgProjectedCrsRecord(2770, "NAD83(HARN) / California zone 5", 4152, 4499, 10435); + return true; + case 710: + record = new EpsgProjectedCrsRecord(2771, "NAD83(HARN) / California zone 6", 4152, 4499, 10436); + return true; + case 711: + record = new EpsgProjectedCrsRecord(2772, "NAD83(HARN) / Colorado North", 4152, 4499, 10531); + return true; + case 712: + record = new EpsgProjectedCrsRecord(2773, "NAD83(HARN) / Colorado Central", 4152, 4499, 10532); + return true; + case 713: + record = new EpsgProjectedCrsRecord(2774, "NAD83(HARN) / Colorado South", 4152, 4499, 10533); + return true; + case 714: + record = new EpsgProjectedCrsRecord(2775, "NAD83(HARN) / Connecticut", 4152, 4499, 10630); + return true; + case 715: + record = new EpsgProjectedCrsRecord(2776, "NAD83(HARN) / Delaware", 4152, 4499, 10730); + return true; + case 716: + record = new EpsgProjectedCrsRecord(2777, "NAD83(HARN) / Florida East", 4152, 4499, 10931); + return true; + case 717: + record = new EpsgProjectedCrsRecord(2778, "NAD83(HARN) / Florida West", 4152, 4499, 10932); + return true; + case 718: + record = new EpsgProjectedCrsRecord(2779, "NAD83(HARN) / Florida North", 4152, 4499, 10933); + return true; + case 719: + record = new EpsgProjectedCrsRecord(2780, "NAD83(HARN) / Georgia East", 4152, 4499, 11031); + return true; + case 720: + record = new EpsgProjectedCrsRecord(2781, "NAD83(HARN) / Georgia West", 4152, 4499, 11032); + return true; + case 721: + record = new EpsgProjectedCrsRecord(2782, "NAD83(HARN) / Hawaii zone 1", 4152, 4499, 15131); + return true; + case 722: + record = new EpsgProjectedCrsRecord(2783, "NAD83(HARN) / Hawaii zone 2", 4152, 4499, 15132); + return true; + case 723: + record = new EpsgProjectedCrsRecord(2784, "NAD83(HARN) / Hawaii zone 3", 4152, 4499, 15133); + return true; + case 724: + record = new EpsgProjectedCrsRecord(2785, "NAD83(HARN) / Hawaii zone 4", 4152, 4499, 15134); + return true; + case 725: + record = new EpsgProjectedCrsRecord(2786, "NAD83(HARN) / Hawaii zone 5", 4152, 4499, 15135); + return true; + case 726: + record = new EpsgProjectedCrsRecord(2787, "NAD83(HARN) / Idaho East", 4152, 4499, 11131); + return true; + case 727: + record = new EpsgProjectedCrsRecord(2788, "NAD83(HARN) / Idaho Central", 4152, 4499, 11132); + return true; + case 728: + record = new EpsgProjectedCrsRecord(2789, "NAD83(HARN) / Idaho West", 4152, 4499, 11133); + return true; + case 729: + record = new EpsgProjectedCrsRecord(2790, "NAD83(HARN) / Illinois East", 4152, 4499, 11231); + return true; + case 730: + record = new EpsgProjectedCrsRecord(2791, "NAD83(HARN) / Illinois West", 4152, 4499, 11232); + return true; + case 731: + record = new EpsgProjectedCrsRecord(2792, "NAD83(HARN) / Indiana East", 4152, 4499, 11331); + return true; + case 732: + record = new EpsgProjectedCrsRecord(2793, "NAD83(HARN) / Indiana West", 4152, 4499, 11332); + return true; + case 733: + record = new EpsgProjectedCrsRecord(2794, "NAD83(HARN) / Iowa North", 4152, 4499, 11431); + return true; + case 734: + record = new EpsgProjectedCrsRecord(2795, "NAD83(HARN) / Iowa South", 4152, 4499, 11432); + return true; + case 735: + record = new EpsgProjectedCrsRecord(2796, "NAD83(HARN) / Kansas North", 4152, 4499, 11531); + return true; + case 736: + record = new EpsgProjectedCrsRecord(2797, "NAD83(HARN) / Kansas South", 4152, 4499, 11532); + return true; + case 737: + record = new EpsgProjectedCrsRecord(2798, "NAD83(HARN) / Kentucky North", 4152, 4499, 15303); + return true; + case 738: + record = new EpsgProjectedCrsRecord(2799, "NAD83(HARN) / Kentucky South", 4152, 4499, 11632); + return true; + case 739: + record = new EpsgProjectedCrsRecord(2800, "NAD83(HARN) / Louisiana North", 4152, 4499, 11731); + return true; + case 740: + record = new EpsgProjectedCrsRecord(2801, "NAD83(HARN) / Louisiana South", 4152, 4499, 11732); + return true; + case 741: + record = new EpsgProjectedCrsRecord(2802, "NAD83(HARN) / Maine East", 4152, 4499, 11831); + return true; + case 742: + record = new EpsgProjectedCrsRecord(2803, "NAD83(HARN) / Maine West", 4152, 4499, 11832); + return true; + case 743: + record = new EpsgProjectedCrsRecord(2804, "NAD83(HARN) / Maryland", 4152, 4499, 11930); + return true; + case 744: + record = new EpsgProjectedCrsRecord(2805, "NAD83(HARN) / Massachusetts Mainland", 4152, 4499, 12031); + return true; + case 745: + record = new EpsgProjectedCrsRecord(2806, "NAD83(HARN) / Massachusetts Island", 4152, 4499, 12032); + return true; + case 746: + record = new EpsgProjectedCrsRecord(2807, "NAD83(HARN) / Michigan North", 4152, 4499, 12141); + return true; + case 747: + record = new EpsgProjectedCrsRecord(2808, "NAD83(HARN) / Michigan Central", 4152, 4499, 12142); + return true; + case 748: + record = new EpsgProjectedCrsRecord(2809, "NAD83(HARN) / Michigan South", 4152, 4499, 12143); + return true; + case 749: + record = new EpsgProjectedCrsRecord(2810, "NAD83(HARN) / Minnesota North", 4152, 4499, 12231); + return true; + case 750: + record = new EpsgProjectedCrsRecord(2811, "NAD83(HARN) / Minnesota Central", 4152, 4499, 12232); + return true; + case 751: + record = new EpsgProjectedCrsRecord(2812, "NAD83(HARN) / Minnesota South", 4152, 4499, 12233); + return true; + case 752: + record = new EpsgProjectedCrsRecord(2813, "NAD83(HARN) / Mississippi East", 4152, 4499, 12331); + return true; + case 753: + record = new EpsgProjectedCrsRecord(2814, "NAD83(HARN) / Mississippi West", 4152, 4499, 12332); + return true; + case 754: + record = new EpsgProjectedCrsRecord(2815, "NAD83(HARN) / Missouri East", 4152, 4499, 12431); + return true; + case 755: + record = new EpsgProjectedCrsRecord(2816, "NAD83(HARN) / Missouri Central", 4152, 4499, 12432); + return true; + case 756: + record = new EpsgProjectedCrsRecord(2817, "NAD83(HARN) / Missouri West", 4152, 4499, 12433); + return true; + case 757: + record = new EpsgProjectedCrsRecord(2818, "NAD83(HARN) / Montana", 4152, 4499, 12530); + return true; + case 758: + record = new EpsgProjectedCrsRecord(2819, "NAD83(HARN) / Nebraska", 4152, 4499, 12630); + return true; + case 759: + record = new EpsgProjectedCrsRecord(2820, "NAD83(HARN) / Nevada East", 4152, 4499, 12731); + return true; + case 760: + record = new EpsgProjectedCrsRecord(2821, "NAD83(HARN) / Nevada Central", 4152, 4499, 12732); + return true; + case 761: + record = new EpsgProjectedCrsRecord(2822, "NAD83(HARN) / Nevada West", 4152, 4499, 12733); + return true; + case 762: + record = new EpsgProjectedCrsRecord(2823, "NAD83(HARN) / New Hampshire", 4152, 4499, 12830); + return true; + case 763: + record = new EpsgProjectedCrsRecord(2824, "NAD83(HARN) / New Jersey", 4152, 4499, 12930); + return true; + case 764: + record = new EpsgProjectedCrsRecord(2825, "NAD83(HARN) / New Mexico East", 4152, 4499, 13031); + return true; + case 765: + record = new EpsgProjectedCrsRecord(2826, "NAD83(HARN) / New Mexico Central", 4152, 4499, 13032); + return true; + case 766: + record = new EpsgProjectedCrsRecord(2827, "NAD83(HARN) / New Mexico West", 4152, 4499, 13033); + return true; + case 767: + record = new EpsgProjectedCrsRecord(2828, "NAD83(HARN) / New York East", 4152, 4499, 13131); + return true; + case 768: + record = new EpsgProjectedCrsRecord(2829, "NAD83(HARN) / New York Central", 4152, 4499, 13132); + return true; + case 769: + record = new EpsgProjectedCrsRecord(2830, "NAD83(HARN) / New York West", 4152, 4499, 13133); + return true; + case 770: + record = new EpsgProjectedCrsRecord(2831, "NAD83(HARN) / New York Long Island", 4152, 4499, 13134); + return true; + case 771: + record = new EpsgProjectedCrsRecord(2832, "NAD83(HARN) / North Dakota North", 4152, 4499, 13331); + return true; + case 772: + record = new EpsgProjectedCrsRecord(2833, "NAD83(HARN) / North Dakota South", 4152, 4499, 13332); + return true; + case 773: + record = new EpsgProjectedCrsRecord(2834, "NAD83(HARN) / Ohio North", 4152, 4499, 13431); + return true; + case 774: + record = new EpsgProjectedCrsRecord(2835, "NAD83(HARN) / Ohio South", 4152, 4499, 13432); + return true; + case 775: + record = new EpsgProjectedCrsRecord(2836, "NAD83(HARN) / Oklahoma North", 4152, 4499, 13531); + return true; + case 776: + record = new EpsgProjectedCrsRecord(2837, "NAD83(HARN) / Oklahoma South", 4152, 4499, 13532); + return true; + case 777: + record = new EpsgProjectedCrsRecord(2838, "NAD83(HARN) / Oregon North", 4152, 4499, 13631); + return true; + case 778: + record = new EpsgProjectedCrsRecord(2839, "NAD83(HARN) / Oregon South", 4152, 4499, 13632); + return true; + case 779: + record = new EpsgProjectedCrsRecord(2840, "NAD83(HARN) / Rhode Island", 4152, 4499, 13830); + return true; + case 780: + record = new EpsgProjectedCrsRecord(2841, "NAD83(HARN) / South Dakota North", 4152, 4499, 14031); + return true; + case 781: + record = new EpsgProjectedCrsRecord(2842, "NAD83(HARN) / South Dakota South", 4152, 4499, 14032); + return true; + case 782: + record = new EpsgProjectedCrsRecord(2843, "NAD83(HARN) / Tennessee", 4152, 4499, 14130); + return true; + case 783: + record = new EpsgProjectedCrsRecord(2844, "NAD83(HARN) / Texas North", 4152, 4499, 14231); + return true; + case 784: + record = new EpsgProjectedCrsRecord(2845, "NAD83(HARN) / Texas North Central", 4152, 4499, 14232); + return true; + case 785: + record = new EpsgProjectedCrsRecord(2846, "NAD83(HARN) / Texas Central", 4152, 4499, 14233); + return true; + case 786: + record = new EpsgProjectedCrsRecord(2847, "NAD83(HARN) / Texas South Central", 4152, 4499, 14234); + return true; + case 787: + record = new EpsgProjectedCrsRecord(2848, "NAD83(HARN) / Texas South", 4152, 4499, 14235); + return true; + case 788: + record = new EpsgProjectedCrsRecord(2849, "NAD83(HARN) / Utah North", 4152, 4499, 14331); + return true; + case 789: + record = new EpsgProjectedCrsRecord(2850, "NAD83(HARN) / Utah Central", 4152, 4499, 14332); + return true; + case 790: + record = new EpsgProjectedCrsRecord(2851, "NAD83(HARN) / Utah South", 4152, 4499, 14333); + return true; + case 791: + record = new EpsgProjectedCrsRecord(2852, "NAD83(HARN) / Vermont", 4152, 4499, 14430); + return true; + case 792: + record = new EpsgProjectedCrsRecord(2853, "NAD83(HARN) / Virginia North", 4152, 4499, 14531); + return true; + case 793: + record = new EpsgProjectedCrsRecord(2854, "NAD83(HARN) / Virginia South", 4152, 4499, 14532); + return true; + case 794: + record = new EpsgProjectedCrsRecord(2855, "NAD83(HARN) / Washington North", 4152, 4499, 14631); + return true; + case 795: + record = new EpsgProjectedCrsRecord(2856, "NAD83(HARN) / Washington South", 4152, 4499, 14632); + return true; + case 796: + record = new EpsgProjectedCrsRecord(2857, "NAD83(HARN) / West Virginia North", 4152, 4499, 14731); + return true; + case 797: + record = new EpsgProjectedCrsRecord(2858, "NAD83(HARN) / West Virginia South", 4152, 4499, 14732); + return true; + case 798: + record = new EpsgProjectedCrsRecord(2859, "NAD83(HARN) / Wisconsin North", 4152, 4499, 14831); + return true; + case 799: + record = new EpsgProjectedCrsRecord(2860, "NAD83(HARN) / Wisconsin Central", 4152, 4499, 14832); + return true; + case 800: + record = new EpsgProjectedCrsRecord(2861, "NAD83(HARN) / Wisconsin South", 4152, 4499, 14833); + return true; + case 801: + record = new EpsgProjectedCrsRecord(2862, "NAD83(HARN) / Wyoming East", 4152, 4499, 14931); + return true; + case 802: + record = new EpsgProjectedCrsRecord(2863, "NAD83(HARN) / Wyoming East Central", 4152, 4499, 14932); + return true; + case 803: + record = new EpsgProjectedCrsRecord(2864, "NAD83(HARN) / Wyoming West Central", 4152, 4499, 14933); + return true; + case 804: + record = new EpsgProjectedCrsRecord(2865, "NAD83(HARN) / Wyoming West", 4152, 4499, 14934); + return true; + case 805: + record = new EpsgProjectedCrsRecord(2866, "NAD83(HARN) / Puerto Rico and Virgin Is.", 4152, 4499, 15230); + return true; + case 806: + record = new EpsgProjectedCrsRecord(2867, "NAD83(HARN) / Arizona East (ft)", 4152, 4495, 15304); + return true; + case 807: + record = new EpsgProjectedCrsRecord(2868, "NAD83(HARN) / Arizona Central (ft)", 4152, 4495, 15305); + return true; + case 808: + record = new EpsgProjectedCrsRecord(2869, "NAD83(HARN) / Arizona West (ft)", 4152, 4495, 15306); + return true; + case 809: + record = new EpsgProjectedCrsRecord(2870, "NAD83(HARN) / California zone 1 (ftUS)", 4152, 4497, 15307); + return true; + case 810: + record = new EpsgProjectedCrsRecord(2871, "NAD83(HARN) / California zone 2 (ftUS)", 4152, 4497, 15308); + return true; + case 811: + record = new EpsgProjectedCrsRecord(2872, "NAD83(HARN) / California zone 3 (ftUS)", 4152, 4497, 15309); + return true; + case 812: + record = new EpsgProjectedCrsRecord(2873, "NAD83(HARN) / California zone 4 (ftUS)", 4152, 4497, 15310); + return true; + case 813: + record = new EpsgProjectedCrsRecord(2874, "NAD83(HARN) / California zone 5 (ftUS)", 4152, 4497, 15311); + return true; + case 814: + record = new EpsgProjectedCrsRecord(2875, "NAD83(HARN) / California zone 6 (ftUS)", 4152, 4497, 15312); + return true; + case 815: + record = new EpsgProjectedCrsRecord(2876, "NAD83(HARN) / Colorado North (ftUS)", 4152, 4497, 15313); + return true; + case 816: + record = new EpsgProjectedCrsRecord(2877, "NAD83(HARN) / Colorado Central (ftUS)", 4152, 4497, 15314); + return true; + case 817: + record = new EpsgProjectedCrsRecord(2878, "NAD83(HARN) / Colorado South (ftUS)", 4152, 4497, 15315); + return true; + case 818: + record = new EpsgProjectedCrsRecord(2879, "NAD83(HARN) / Connecticut (ftUS)", 4152, 4497, 15316); + return true; + case 819: + record = new EpsgProjectedCrsRecord(2880, "NAD83(HARN) / Delaware (ftUS)", 4152, 4497, 15317); + return true; + case 820: + record = new EpsgProjectedCrsRecord(2881, "NAD83(HARN) / Florida East (ftUS)", 4152, 4497, 15318); + return true; + case 821: + record = new EpsgProjectedCrsRecord(2882, "NAD83(HARN) / Florida West (ftUS)", 4152, 4497, 15319); + return true; + case 822: + record = new EpsgProjectedCrsRecord(2883, "NAD83(HARN) / Florida North (ftUS)", 4152, 4497, 15320); + return true; + case 823: + record = new EpsgProjectedCrsRecord(2884, "NAD83(HARN) / Georgia East (ftUS)", 4152, 4497, 15321); + return true; + case 824: + record = new EpsgProjectedCrsRecord(2885, "NAD83(HARN) / Georgia West (ftUS)", 4152, 4497, 15322); + return true; + case 825: + record = new EpsgProjectedCrsRecord(2886, "NAD83(HARN) / Idaho East (ftUS)", 4152, 4497, 15323); + return true; + case 826: + record = new EpsgProjectedCrsRecord(2887, "NAD83(HARN) / Idaho Central (ftUS)", 4152, 4497, 15324); + return true; + case 827: + record = new EpsgProjectedCrsRecord(2888, "NAD83(HARN) / Idaho West (ftUS)", 4152, 4497, 15325); + return true; + case 828: + record = new EpsgProjectedCrsRecord(2891, "NAD83(HARN) / Kentucky North (ftUS)", 4152, 4497, 15328); + return true; + case 829: + record = new EpsgProjectedCrsRecord(2892, "NAD83(HARN) / Kentucky South (ftUS)", 4152, 4497, 15329); + return true; + case 830: + record = new EpsgProjectedCrsRecord(2893, "NAD83(HARN) / Maryland (ftUS)", 4152, 4497, 15330); + return true; + case 831: + record = new EpsgProjectedCrsRecord(2894, "NAD83(HARN) / Massachusetts Mainland (ftUS)", 4152, 4497, 15331); + return true; + case 832: + record = new EpsgProjectedCrsRecord(2895, "NAD83(HARN) / Massachusetts Island (ftUS)", 4152, 4497, 15332); + return true; + case 833: + record = new EpsgProjectedCrsRecord(2896, "NAD83(HARN) / Michigan North (ft)", 4152, 4495, 15333); + return true; + case 834: + record = new EpsgProjectedCrsRecord(2897, "NAD83(HARN) / Michigan Central (ft)", 4152, 4495, 15334); + return true; + case 835: + record = new EpsgProjectedCrsRecord(2898, "NAD83(HARN) / Michigan South (ft)", 4152, 4495, 15335); + return true; + case 836: + record = new EpsgProjectedCrsRecord(2899, "NAD83(HARN) / Mississippi East (ftUS)", 4152, 4497, 15336); + return true; + case 837: + record = new EpsgProjectedCrsRecord(2900, "NAD83(HARN) / Mississippi West (ftUS)", 4152, 4497, 15337); + return true; + case 838: + record = new EpsgProjectedCrsRecord(2901, "NAD83(HARN) / Montana (ft)", 4152, 4495, 15338); + return true; + case 839: + record = new EpsgProjectedCrsRecord(2902, "NAD83(HARN) / New Mexico East (ftUS)", 4152, 4497, 15339); + return true; + case 840: + record = new EpsgProjectedCrsRecord(2903, "NAD83(HARN) / New Mexico Central (ftUS)", 4152, 4497, 15340); + return true; + case 841: + record = new EpsgProjectedCrsRecord(2904, "NAD83(HARN) / New Mexico West (ftUS)", 4152, 4497, 15341); + return true; + case 842: + record = new EpsgProjectedCrsRecord(2905, "NAD83(HARN) / New York East (ftUS)", 4152, 4497, 15342); + return true; + case 843: + record = new EpsgProjectedCrsRecord(2906, "NAD83(HARN) / New York Central (ftUS)", 4152, 4497, 15343); + return true; + case 844: + record = new EpsgProjectedCrsRecord(2907, "NAD83(HARN) / New York West (ftUS)", 4152, 4497, 15344); + return true; + case 845: + record = new EpsgProjectedCrsRecord(2908, "NAD83(HARN) / New York Long Island (ftUS)", 4152, 4497, 15345); + return true; + case 846: + record = new EpsgProjectedCrsRecord(2909, "NAD83(HARN) / North Dakota North (ft)", 4152, 4495, 15347); + return true; + case 847: + record = new EpsgProjectedCrsRecord(2910, "NAD83(HARN) / North Dakota South (ft)", 4152, 4495, 15348); + return true; + case 848: + record = new EpsgProjectedCrsRecord(2911, "NAD83(HARN) / Oklahoma North (ftUS)", 4152, 4497, 15349); + return true; + case 849: + record = new EpsgProjectedCrsRecord(2912, "NAD83(HARN) / Oklahoma South (ftUS)", 4152, 4497, 15350); + return true; + case 850: + record = new EpsgProjectedCrsRecord(2913, "NAD83(HARN) / Oregon North (ft)", 4152, 4495, 15351); + return true; + case 851: + record = new EpsgProjectedCrsRecord(2914, "NAD83(HARN) / Oregon South (ft)", 4152, 4495, 15352); + return true; + case 852: + record = new EpsgProjectedCrsRecord(2915, "NAD83(HARN) / Tennessee (ftUS)", 4152, 4497, 15356); + return true; + case 853: + record = new EpsgProjectedCrsRecord(2916, "NAD83(HARN) / Texas North (ftUS)", 4152, 4497, 15357); + return true; + case 854: + record = new EpsgProjectedCrsRecord(2917, "NAD83(HARN) / Texas North Central (ftUS)", 4152, 4497, 15358); + return true; + case 855: + record = new EpsgProjectedCrsRecord(2918, "NAD83(HARN) / Texas Central (ftUS)", 4152, 4497, 15359); + return true; + case 856: + record = new EpsgProjectedCrsRecord(2919, "NAD83(HARN) / Texas South Central (ftUS)", 4152, 4497, 15360); + return true; + case 857: + record = new EpsgProjectedCrsRecord(2920, "NAD83(HARN) / Texas South (ftUS)", 4152, 4497, 15361); + return true; + case 858: + record = new EpsgProjectedCrsRecord(2921, "NAD83(HARN) / Utah North (ft)", 4152, 4495, 15362); + return true; + case 859: + record = new EpsgProjectedCrsRecord(2922, "NAD83(HARN) / Utah Central (ft)", 4152, 4495, 15363); + return true; + case 860: + record = new EpsgProjectedCrsRecord(2923, "NAD83(HARN) / Utah South (ft)", 4152, 4495, 15364); + return true; + case 861: + record = new EpsgProjectedCrsRecord(2924, "NAD83(HARN) / Virginia North (ftUS)", 4152, 4497, 15365); + return true; + case 862: + record = new EpsgProjectedCrsRecord(2925, "NAD83(HARN) / Virginia South (ftUS)", 4152, 4497, 15366); + return true; + case 863: + record = new EpsgProjectedCrsRecord(2926, "NAD83(HARN) / Washington North (ftUS)", 4152, 4497, 15367); + return true; + case 864: + record = new EpsgProjectedCrsRecord(2927, "NAD83(HARN) / Washington South (ftUS)", 4152, 4497, 15368); + return true; + case 865: + record = new EpsgProjectedCrsRecord(2928, "NAD83(HARN) / Wisconsin North (ftUS)", 4152, 4497, 15369); + return true; + case 866: + record = new EpsgProjectedCrsRecord(2929, "NAD83(HARN) / Wisconsin Central (ftUS)", 4152, 4497, 15370); + return true; + case 867: + record = new EpsgProjectedCrsRecord(2930, "NAD83(HARN) / Wisconsin South (ftUS)", 4152, 4497, 15371); + return true; + case 868: + record = new EpsgProjectedCrsRecord(2931, "Beduaram / TM 13 NE", 4213, 4499, 16413); + return true; + case 869: + record = new EpsgProjectedCrsRecord(2932, "QND95 / Qatar National Grid", 4614, 4400, 19919); + return true; + case 870: + record = new EpsgProjectedCrsRecord(2933, "Segara / UTM zone 50S", 4613, 4400, 16150); + return true; + case 871: + record = new EpsgProjectedCrsRecord(2935, "Pulkovo 1942 / CS63 zone A1", 4284, 4530, 18441); + return true; + case 872: + record = new EpsgProjectedCrsRecord(2936, "Pulkovo 1942 / CS63 zone A2", 4284, 4530, 18442); + return true; + case 873: + record = new EpsgProjectedCrsRecord(2937, "Pulkovo 1942 / CS63 zone A3", 4284, 4530, 18443); + return true; + case 874: + record = new EpsgProjectedCrsRecord(2938, "Pulkovo 1942 / CS63 zone A4", 4284, 4530, 18444); + return true; + case 875: + record = new EpsgProjectedCrsRecord(2939, "Pulkovo 1942 / CS63 zone K2", 4284, 4530, 18446); + return true; + case 876: + record = new EpsgProjectedCrsRecord(2940, "Pulkovo 1942 / CS63 zone K3", 4284, 4530, 18447); + return true; + case 877: + record = new EpsgProjectedCrsRecord(2941, "Pulkovo 1942 / CS63 zone K4", 4284, 4530, 18448); + return true; + case 878: + record = new EpsgProjectedCrsRecord(2942, "Porto Santo / UTM zone 28N", 4615, 4400, 16028); + return true; + case 879: + record = new EpsgProjectedCrsRecord(2943, "Selvagem Grande / UTM zone 28N", 4616, 4400, 16028); + return true; + case 880: + record = new EpsgProjectedCrsRecord(2945, "NAD83(CSRS) / MTM zone 3", 4617, 4496, 17703); + return true; + case 881: + record = new EpsgProjectedCrsRecord(2946, "NAD83(CSRS) / MTM zone 4", 4617, 4496, 17704); + return true; + case 882: + record = new EpsgProjectedCrsRecord(2947, "NAD83(CSRS) / MTM zone 5", 4617, 4496, 17705); + return true; + case 883: + record = new EpsgProjectedCrsRecord(2948, "NAD83(CSRS) / MTM zone 6", 4617, 4496, 17706); + return true; + case 884: + record = new EpsgProjectedCrsRecord(2949, "NAD83(CSRS) / MTM zone 7", 4617, 4496, 17707); + return true; + case 885: + record = new EpsgProjectedCrsRecord(2950, "NAD83(CSRS) / MTM zone 8", 4617, 4496, 17708); + return true; + case 886: + record = new EpsgProjectedCrsRecord(2951, "NAD83(CSRS) / MTM zone 9", 4617, 4496, 17709); + return true; + case 887: + record = new EpsgProjectedCrsRecord(2952, "NAD83(CSRS) / MTM zone 10", 4617, 4496, 17710); + return true; + case 888: + record = new EpsgProjectedCrsRecord(2953, "NAD83(CSRS) / New Brunswick Stereographic", 4617, 4500, 19946); + return true; + case 889: + record = new EpsgProjectedCrsRecord(2954, "NAD83(CSRS) / Prince Edward Isl. Stereographic (NAD83)", 4617, 4496, 19960); + return true; + case 890: + record = new EpsgProjectedCrsRecord(2955, "NAD83(CSRS) / UTM zone 11N", 4617, 4400, 16011); + return true; + case 891: + record = new EpsgProjectedCrsRecord(2956, "NAD83(CSRS) / UTM zone 12N", 4617, 4400, 16012); + return true; + case 892: + record = new EpsgProjectedCrsRecord(2957, "NAD83(CSRS) / UTM zone 13N", 4617, 4400, 16013); + return true; + case 893: + record = new EpsgProjectedCrsRecord(2958, "NAD83(CSRS) / UTM zone 17N", 4617, 4400, 16017); + return true; + case 894: + record = new EpsgProjectedCrsRecord(2959, "NAD83(CSRS) / UTM zone 18N", 4617, 4400, 16018); + return true; + case 895: + record = new EpsgProjectedCrsRecord(2960, "NAD83(CSRS) / UTM zone 19N", 4617, 4400, 16019); + return true; + case 896: + record = new EpsgProjectedCrsRecord(2961, "NAD83(CSRS) / UTM zone 20N", 4617, 4400, 16020); + return true; + case 897: + record = new EpsgProjectedCrsRecord(2962, "NAD83(CSRS) / UTM zone 21N", 4617, 4400, 16021); + return true; + case 898: + record = new EpsgProjectedCrsRecord(2963, "Lisbon 1890 (Lisbon) / Portugal Bonne", 4904, 6509, 19979); + return true; + case 899: + record = new EpsgProjectedCrsRecord(2964, "NAD27 / Alaska Albers", 4267, 4497, 15020); + return true; + case 900: + record = new EpsgProjectedCrsRecord(2965, "NAD83 / Indiana East (ftUS)", 4269, 4497, 15372); + return true; + case 901: + record = new EpsgProjectedCrsRecord(2966, "NAD83 / Indiana West (ftUS)", 4269, 4497, 15373); + return true; + case 902: + record = new EpsgProjectedCrsRecord(2967, "NAD83(HARN) / Indiana East (ftUS)", 4152, 4497, 15372); + return true; + case 903: + record = new EpsgProjectedCrsRecord(2968, "NAD83(HARN) / Indiana West (ftUS)", 4152, 4497, 15373); + return true; + case 904: + record = new EpsgProjectedCrsRecord(2969, "Fort Marigot / UTM zone 20N", 4621, 4400, 16020); + return true; + case 905: + record = new EpsgProjectedCrsRecord(2970, "Guadeloupe 1948 / UTM zone 20N", 4622, 4400, 16020); + return true; + case 906: + record = new EpsgProjectedCrsRecord(2971, "CSG67 / UTM zone 22N", 4623, 4400, 16022); + return true; + case 907: + record = new EpsgProjectedCrsRecord(2972, "RGFG95 / UTM zone 22N", 4624, 4400, 16022); + return true; + case 908: + record = new EpsgProjectedCrsRecord(2973, "Martinique 1938 / UTM zone 20N", 4625, 4400, 16020); + return true; + case 909: + record = new EpsgProjectedCrsRecord(2975, "RGR92 / UTM zone 40S", 4627, 4400, 16140); + return true; + case 910: + record = new EpsgProjectedCrsRecord(2976, "Tahiti 52 / UTM zone 6S", 4628, 4400, 16106); + return true; + case 911: + record = new EpsgProjectedCrsRecord(2977, "Tahaa 54 / UTM zone 5S", 4629, 4400, 16105); + return true; + case 912: + record = new EpsgProjectedCrsRecord(2978, "IGN72 Nuku Hiva / UTM zone 7S", 4630, 4400, 16107); + return true; + case 913: + record = new EpsgProjectedCrsRecord(2980, "Combani 1950 / UTM zone 38S", 4632, 4400, 16138); + return true; + case 914: + record = new EpsgProjectedCrsRecord(2981, "IGN56 Lifou / UTM zone 58S", 4633, 4400, 16158); + return true; + case 915: + record = new EpsgProjectedCrsRecord(2985, "Petrels 1972 / Terre Adelie Polar Stereographic", 4636, 1025, 19983); + return true; + case 916: + record = new EpsgProjectedCrsRecord(2986, "Perroud 1950 / Terre Adelie Polar Stereographic", 4637, 1025, 19983); + return true; + case 917: + record = new EpsgProjectedCrsRecord(2987, "Saint Pierre et Miquelon 1950 / UTM zone 21N", 4638, 4400, 16021); + return true; + case 918: + record = new EpsgProjectedCrsRecord(2988, "MOP78 / UTM zone 1S", 4639, 4400, 16101); + return true; + case 919: + record = new EpsgProjectedCrsRecord(2991, "NAD83 / Oregon LCC (m)", 4269, 4499, 13633); + return true; + case 920: + record = new EpsgProjectedCrsRecord(2992, "NAD83 / Oregon GIC Lambert (ft)", 4269, 4495, 15374); + return true; + case 921: + record = new EpsgProjectedCrsRecord(2993, "NAD83(HARN) / Oregon LCC (m)", 4152, 4499, 13633); + return true; + case 922: + record = new EpsgProjectedCrsRecord(2994, "NAD83(HARN) / Oregon GIC Lambert (ft)", 4152, 4495, 15374); + return true; + case 923: + record = new EpsgProjectedCrsRecord(2995, "IGN53 Mare / UTM zone 58S", 4641, 4400, 16158); + return true; + case 924: + record = new EpsgProjectedCrsRecord(2996, "ST84 Ile des Pins / UTM zone 58S", 4642, 4400, 16158); + return true; + case 925: + record = new EpsgProjectedCrsRecord(2997, "ST71 Belep / UTM zone 58S", 4643, 4400, 16158); + return true; + case 926: + record = new EpsgProjectedCrsRecord(2998, "NEA74 Noumea / UTM zone 58S", 4644, 4400, 16158); + return true; + case 927: + record = new EpsgProjectedCrsRecord(2999, "Grand Comoros / UTM zone 38S", 4646, 4400, 16138); + return true; + case 928: + record = new EpsgProjectedCrsRecord(3000, "Segara / NEIEZ", 4613, 4499, 19905); + return true; + case 929: + record = new EpsgProjectedCrsRecord(3001, "Batavia / NEIEZ", 4211, 4499, 19905); + return true; + case 930: + record = new EpsgProjectedCrsRecord(3002, "Makassar / NEIEZ", 4257, 4499, 19905); + return true; + case 931: + record = new EpsgProjectedCrsRecord(3003, "Monte Mario / Italy zone 1", 4265, 4499, 18121); + return true; + case 932: + record = new EpsgProjectedCrsRecord(3004, "Monte Mario / Italy zone 2", 4265, 4499, 18122); + return true; + case 933: + record = new EpsgProjectedCrsRecord(3005, "NAD83 / BC Albers", 4269, 4400, 19984); + return true; + case 934: + record = new EpsgProjectedCrsRecord(3006, "ETRS89-SWE [SWEREF 99 TM]", 4619, 4500, 17333); + return true; + case 935: + record = new EpsgProjectedCrsRecord(3007, "ETRS89-SWE [SWEREF 99 12 00]", 4619, 4500, 17321); + return true; + case 936: + record = new EpsgProjectedCrsRecord(3008, "ETRS89-SWE [SWEREF 99 13 30]", 4619, 4500, 17322); + return true; + case 937: + record = new EpsgProjectedCrsRecord(3009, "ETRS89-SWE [SWEREF 99 15 00]", 4619, 4500, 17323); + return true; + case 938: + record = new EpsgProjectedCrsRecord(3010, "ETRS89-SWE [SWEREF 99 16 30]", 4619, 4500, 17324); + return true; + case 939: + record = new EpsgProjectedCrsRecord(3011, "ETRS89-SWE [SWEREF 99 18 00]", 4619, 4500, 17325); + return true; + case 940: + record = new EpsgProjectedCrsRecord(3012, "ETRS89-SWE [SWEREF 99 14 15]", 4619, 4500, 17326); + return true; + case 941: + record = new EpsgProjectedCrsRecord(3013, "ETRS89-SWE [SWEREF 99 15 45]", 4619, 4500, 17327); + return true; + case 942: + record = new EpsgProjectedCrsRecord(3014, "ETRS89-SWE [SWEREF 99 17 15]", 4619, 4500, 17328); + return true; + case 943: + record = new EpsgProjectedCrsRecord(3015, "ETRS89-SWE [SWEREF 99 18 45]", 4619, 4500, 17329); + return true; + case 944: + record = new EpsgProjectedCrsRecord(3016, "ETRS89-SWE [SWEREF 99 20 15]", 4619, 4500, 17330); + return true; + case 945: + record = new EpsgProjectedCrsRecord(3017, "ETRS89-SWE [SWEREF 99 21 45]", 4619, 4500, 17331); + return true; + case 946: + record = new EpsgProjectedCrsRecord(3018, "ETRS89-SWE [SWEREF 99 23 15]", 4619, 4500, 17332); + return true; + case 947: + record = new EpsgProjectedCrsRecord(3019, "RT90 7.5 gon V", 4124, 4530, 17334); + return true; + case 948: + record = new EpsgProjectedCrsRecord(3020, "RT90 5 gon V", 4124, 4530, 17335); + return true; + case 949: + record = new EpsgProjectedCrsRecord(3021, "RT90 2.5 gon V", 4124, 4530, 19929); + return true; + case 950: + record = new EpsgProjectedCrsRecord(3022, "RT90 0 gon", 4124, 4530, 17336); + return true; + case 951: + record = new EpsgProjectedCrsRecord(3023, "RT90 2.5 gon O", 4124, 4530, 17337); + return true; + case 952: + record = new EpsgProjectedCrsRecord(3024, "RT90 5 gon O", 4124, 4530, 17338); + return true; + case 953: + record = new EpsgProjectedCrsRecord(3025, "RT38 7.5 gon V", 4308, 4530, 17334); + return true; + case 954: + record = new EpsgProjectedCrsRecord(3026, "RT38 5 gon V", 4308, 4530, 17335); + return true; + case 955: + record = new EpsgProjectedCrsRecord(3027, "RT38 2.5 gon V", 4308, 4530, 19929); + return true; + case 956: + record = new EpsgProjectedCrsRecord(3028, "RT38 0 gon", 4308, 4530, 17336); + return true; + case 957: + record = new EpsgProjectedCrsRecord(3029, "RT38 2.5 gon O", 4308, 4530, 17337); + return true; + case 958: + record = new EpsgProjectedCrsRecord(3030, "RT38 5 gon O", 4308, 4530, 17338); + return true; + case 959: + record = new EpsgProjectedCrsRecord(3031, "WGS 84 / Antarctic Polar Stereographic", 4326, 4490, 19992); + return true; + case 960: + record = new EpsgProjectedCrsRecord(3032, "WGS 84 / Australian Antarctic Polar Stereographic", 4326, 4489, 19993); + return true; + case 961: + record = new EpsgProjectedCrsRecord(3033, "WGS 84 / Australian Antarctic Lambert", 4326, 4400, 19994); + return true; + case 962: + record = new EpsgProjectedCrsRecord(3034, "ETRS89-extended / LCC Europe", 4258, 4500, 19985); + return true; + case 963: + record = new EpsgProjectedCrsRecord(3035, "ETRS89-extended / LAEA Europe", 4258, 4532, 19986); + return true; + case 964: + record = new EpsgProjectedCrsRecord(3036, "Moznet / UTM zone 36S", 4130, 4400, 16136); + return true; + case 965: + record = new EpsgProjectedCrsRecord(3037, "Moznet / UTM zone 37S", 4130, 4400, 16137); + return true; + case 966: + record = new EpsgProjectedCrsRecord(3040, "ETRS89 / UTM zone 28N (N-E)", 4258, 4500, 16028); + return true; + case 967: + record = new EpsgProjectedCrsRecord(3041, "ETRS89 / UTM zone 29N (N-E)", 4258, 4500, 16029); + return true; + case 968: + record = new EpsgProjectedCrsRecord(3042, "ETRS89 / UTM zone 30N (N-E)", 4258, 4500, 16030); + return true; + case 969: + record = new EpsgProjectedCrsRecord(3043, "ETRS89 / UTM zone 31N (N-E)", 4258, 4500, 16031); + return true; + case 970: + record = new EpsgProjectedCrsRecord(3044, "ETRS89 / UTM zone 32N (N-E)", 4258, 4500, 16032); + return true; + case 971: + record = new EpsgProjectedCrsRecord(3045, "ETRS89 / UTM zone 33N (N-E)", 4258, 4500, 16033); + return true; + case 972: + record = new EpsgProjectedCrsRecord(3046, "ETRS89 / UTM zone 34N (N-E)", 4258, 4500, 16034); + return true; + case 973: + record = new EpsgProjectedCrsRecord(3047, "ETRS89 / UTM zone 35N (N-E)", 4258, 4500, 16035); + return true; + case 974: + record = new EpsgProjectedCrsRecord(3048, "ETRS89 / UTM zone 36N (N-E)", 4258, 4500, 16036); + return true; + case 975: + record = new EpsgProjectedCrsRecord(3049, "ETRS89 / UTM zone 37N (N-E)", 4258, 4500, 16037); + return true; + case 976: + record = new EpsgProjectedCrsRecord(3052, "Reykjavik 1900 / Lambert 1900", 4657, 4491, 19987); + return true; + case 977: + record = new EpsgProjectedCrsRecord(3053, "Hjorsey 1955 / Lambert 1955", 4658, 4491, 19988); + return true; + case 978: + record = new EpsgProjectedCrsRecord(3054, "Hjorsey 1955 / UTM zone 26N", 4658, 4400, 16026); + return true; + case 979: + record = new EpsgProjectedCrsRecord(3055, "Hjorsey 1955 / UTM zone 27N", 4658, 4400, 16027); + return true; + case 980: + record = new EpsgProjectedCrsRecord(3056, "Hjorsey 1955 / UTM zone 28N", 4658, 4400, 16028); + return true; + case 981: + record = new EpsgProjectedCrsRecord(3057, "ISN93 / Lambert 1993", 4659, 4499, 19989); + return true; + case 982: + record = new EpsgProjectedCrsRecord(3058, "Helle 1954 / Jan Mayen Grid", 4660, 4531, 19991); + return true; + case 983: + record = new EpsgProjectedCrsRecord(3059, "ETRS89-LVA [LKS-92] / Latvia TM", 4661, 4530, 19990); + return true; + case 984: + record = new EpsgProjectedCrsRecord(3060, "IGN72 Grande Terre / UTM zone 58S", 4662, 4400, 16158); + return true; + case 985: + record = new EpsgProjectedCrsRecord(3061, "Porto Santo 1995 / UTM zone 28N", 4663, 4400, 16028); + return true; + case 986: + record = new EpsgProjectedCrsRecord(3062, "Azores Oriental 1995 / UTM zone 26N", 4664, 4400, 16026); + return true; + case 987: + record = new EpsgProjectedCrsRecord(3063, "Azores Central 1995 / UTM zone 26N", 4665, 4400, 16026); + return true; + case 988: + record = new EpsgProjectedCrsRecord(3064, "ETRS89-ITA [IGM95] / UTM zone 32N", 4670, 4400, 16032); + return true; + case 989: + record = new EpsgProjectedCrsRecord(3065, "ETRS89-ITA [IGM95] / UTM zone 33N", 4670, 4400, 16033); + return true; + case 990: + record = new EpsgProjectedCrsRecord(3066, "ED50 / Jordan TM", 4230, 4400, 19995); + return true; + case 991: + record = new EpsgProjectedCrsRecord(3067, "ETRS89-FIN [EUREF-FIN] / TM35FIN(E,N)", 10690, 4400, 16065); + return true; + case 992: + record = new EpsgProjectedCrsRecord(3068, "DHDN / Soldner Berlin", 4314, 4531, 19996); + return true; + case 993: + record = new EpsgProjectedCrsRecord(3069, "NAD27 / Wisconsin Transverse Mercator", 4267, 4499, 14811); + return true; + case 994: + record = new EpsgProjectedCrsRecord(3070, "NAD83 / Wisconsin Transverse Mercator", 4269, 4499, 14841); + return true; + case 995: + record = new EpsgProjectedCrsRecord(3071, "NAD83(HARN) / Wisconsin Transverse Mercator", 4152, 4499, 14841); + return true; + case 996: + record = new EpsgProjectedCrsRecord(3072, "NAD83 / Maine CS2000 East", 4269, 4499, 11851); + return true; + case 997: + record = new EpsgProjectedCrsRecord(3074, "NAD83 / Maine CS2000 West", 4269, 4499, 11853); + return true; + case 998: + record = new EpsgProjectedCrsRecord(3075, "NAD83(HARN) / Maine CS2000 East", 4152, 4499, 11851); + return true; + case 999: + record = new EpsgProjectedCrsRecord(3077, "NAD83(HARN) / Maine CS2000 West", 4152, 4499, 11853); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetProjectedCrsBucket1(int index, out EpsgProjectedCrsRecord record) + { + switch (index) + { + case 1000: + record = new EpsgProjectedCrsRecord(3078, "NAD83 / Michigan Oblique Mercator", 4269, 4499, 12150); + return true; + case 1001: + record = new EpsgProjectedCrsRecord(3079, "NAD83(HARN) / Michigan Oblique Mercator", 4152, 4499, 12150); + return true; + case 1002: + record = new EpsgProjectedCrsRecord(3080, "NAD27 / Shackleford", 4267, 4495, 14252); + return true; + case 1003: + record = new EpsgProjectedCrsRecord(3081, "NAD83 / Texas State Mapping System", 4269, 4499, 14251); + return true; + case 1004: + record = new EpsgProjectedCrsRecord(3082, "NAD83 / Texas Centric Lambert Conformal", 4269, 4499, 14253); + return true; + case 1005: + record = new EpsgProjectedCrsRecord(3083, "NAD83 / Texas Centric Albers Equal Area", 4269, 4499, 14254); + return true; + case 1006: + record = new EpsgProjectedCrsRecord(3084, "NAD83(HARN) / Texas Centric Lambert Conformal", 4152, 4499, 14253); + return true; + case 1007: + record = new EpsgProjectedCrsRecord(3085, "NAD83(HARN) / Texas Centric Albers Equal Area", 4152, 4499, 14254); + return true; + case 1008: + record = new EpsgProjectedCrsRecord(3086, "NAD83 / Florida GDL Albers", 4269, 4499, 10934); + return true; + case 1009: + record = new EpsgProjectedCrsRecord(3087, "NAD83(HARN) / Florida GDL Albers", 4152, 4499, 10934); + return true; + case 1010: + record = new EpsgProjectedCrsRecord(3088, "NAD83 / Kentucky Single Zone", 4269, 4499, 11630); + return true; + case 1011: + record = new EpsgProjectedCrsRecord(3089, "NAD83 / Kentucky Single Zone (ftUS)", 4269, 4497, 15375); + return true; + case 1012: + record = new EpsgProjectedCrsRecord(3090, "NAD83(HARN) / Kentucky Single Zone", 4152, 4499, 11630); + return true; + case 1013: + record = new EpsgProjectedCrsRecord(3091, "NAD83(HARN) / Kentucky Single Zone (ftUS)", 4152, 4497, 15375); + return true; + case 1014: + record = new EpsgProjectedCrsRecord(3092, "Tokyo / UTM zone 51N", 4301, 4400, 16051); + return true; + case 1015: + record = new EpsgProjectedCrsRecord(3093, "Tokyo / UTM zone 52N", 4301, 4400, 16052); + return true; + case 1016: + record = new EpsgProjectedCrsRecord(3094, "Tokyo / UTM zone 53N", 4301, 4400, 16053); + return true; + case 1017: + record = new EpsgProjectedCrsRecord(3095, "Tokyo / UTM zone 54N", 4301, 4400, 16054); + return true; + case 1018: + record = new EpsgProjectedCrsRecord(3096, "Tokyo / UTM zone 55N", 4301, 4400, 16055); + return true; + case 1019: + record = new EpsgProjectedCrsRecord(3097, "JGD2000 / UTM zone 51N", 4612, 4400, 16051); + return true; + case 1020: + record = new EpsgProjectedCrsRecord(3098, "JGD2000 / UTM zone 52N", 4612, 4400, 16052); + return true; + case 1021: + record = new EpsgProjectedCrsRecord(3099, "JGD2000 / UTM zone 53N", 4612, 4400, 16053); + return true; + case 1022: + record = new EpsgProjectedCrsRecord(3100, "JGD2000 / UTM zone 54N", 4612, 4400, 16054); + return true; + case 1023: + record = new EpsgProjectedCrsRecord(3101, "JGD2000 / UTM zone 55N", 4612, 4400, 16055); + return true; + case 1024: + record = new EpsgProjectedCrsRecord(3102, "American Samoa 1962 / American Samoa Lambert", 4169, 4497, 15376); + return true; + case 1025: + record = new EpsgProjectedCrsRecord(3106, "Gulshan 303 / TM 90 NE", 4682, 4400, 16490); + return true; + case 1026: + record = new EpsgProjectedCrsRecord(3107, "GDA94 / SA Lambert", 4283, 4400, 17359); + return true; + case 1027: + record = new EpsgProjectedCrsRecord(3108, "ETRS89 / Guernsey Grid", 4258, 4400, 19998); + return true; + case 1028: + record = new EpsgProjectedCrsRecord(3109, "ETRS89 / Jersey Transverse Mercator", 4258, 4400, 19999); + return true; + case 1029: + record = new EpsgProjectedCrsRecord(3110, "AGD66 / Vicgrid66", 4202, 4400, 17360); + return true; + case 1030: + record = new EpsgProjectedCrsRecord(3111, "GDA94 / Vicgrid", 4283, 4400, 17361); + return true; + case 1031: + record = new EpsgProjectedCrsRecord(3112, "GDA94 / Geoscience Australia Lambert", 4283, 4400, 17362); + return true; + case 1032: + record = new EpsgProjectedCrsRecord(3113, "GDA94 / BCSG02", 4283, 4400, 17363); + return true; + case 1033: + record = new EpsgProjectedCrsRecord(3114, "MAGNA-SIRGAS / Colombia Far West zone", 4686, 4500, 18055); + return true; + case 1034: + record = new EpsgProjectedCrsRecord(3115, "MAGNA-SIRGAS / Colombia West zone", 4686, 4500, 18056); + return true; + case 1035: + record = new EpsgProjectedCrsRecord(3116, "MAGNA-SIRGAS / Colombia Bogota zone", 4686, 4500, 18057); + return true; + case 1036: + record = new EpsgProjectedCrsRecord(3117, "MAGNA-SIRGAS / Colombia East Central zone", 4686, 4500, 18058); + return true; + case 1037: + record = new EpsgProjectedCrsRecord(3118, "MAGNA-SIRGAS / Colombia East zone", 4686, 4500, 18059); + return true; + case 1038: + record = new EpsgProjectedCrsRecord(3119, "Douala 1948 / AEF west", 4192, 4400, 18415); + return true; + case 1039: + record = new EpsgProjectedCrsRecord(3120, "Pulkovo 1942(58) / Poland zone I", 4179, 4530, 18280); + return true; + case 1040: + record = new EpsgProjectedCrsRecord(3121, "PRS92 / Philippines zone 1", 4683, 4499, 18171); + return true; + case 1041: + record = new EpsgProjectedCrsRecord(3122, "PRS92 / Philippines zone 2", 4683, 4499, 18172); + return true; + case 1042: + record = new EpsgProjectedCrsRecord(3123, "PRS92 / Philippines zone 3", 4683, 4499, 18173); + return true; + case 1043: + record = new EpsgProjectedCrsRecord(3124, "PRS92 / Philippines zone 4", 4683, 4499, 18174); + return true; + case 1044: + record = new EpsgProjectedCrsRecord(3125, "PRS92 / Philippines zone 5", 4683, 4499, 18175); + return true; + case 1045: + record = new EpsgProjectedCrsRecord(3126, "ETRS89-FIN [EUREF-FIN] / ETRS-GK19FIN", 10690, 4500, 18183); + return true; + case 1046: + record = new EpsgProjectedCrsRecord(3127, "ETRS89-FIN [EUREF-FIN] / ETRS-GK20FIN", 10690, 4500, 18184); + return true; + case 1047: + record = new EpsgProjectedCrsRecord(3128, "ETRS89-FIN [EUREF-FIN] / ETRS-GK21FIN", 10690, 4500, 18185); + return true; + case 1048: + record = new EpsgProjectedCrsRecord(3129, "ETRS89-FIN [EUREF-FIN] / ETRS-GK22FIN", 10690, 4500, 18186); + return true; + case 1049: + record = new EpsgProjectedCrsRecord(3130, "ETRS89-FIN [EUREF-FIN] / ETRS-GK23FIN", 10690, 4500, 18187); + return true; + case 1050: + record = new EpsgProjectedCrsRecord(3131, "ETRS89-FIN [EUREF-FIN] / ETRS-GK24FIN", 10690, 4500, 18188); + return true; + case 1051: + record = new EpsgProjectedCrsRecord(3132, "ETRS89-FIN [EUREF-FIN] / ETRS-GK25FIN", 10690, 4500, 18189); + return true; + case 1052: + record = new EpsgProjectedCrsRecord(3133, "ETRS89-FIN [EUREF-FIN] / ETRS-GK26FIN", 10690, 4500, 18190); + return true; + case 1053: + record = new EpsgProjectedCrsRecord(3134, "ETRS89-FIN [EUREF-FIN] / ETRS-GK27FIN", 10690, 4500, 18195); + return true; + case 1054: + record = new EpsgProjectedCrsRecord(3135, "ETRS89-FIN [EUREF-FIN] / ETRS-GK28FIN", 10690, 4500, 18196); + return true; + case 1055: + record = new EpsgProjectedCrsRecord(3136, "ETRS89-FIN [EUREF-FIN] / ETRS-GK29FIN", 10690, 4500, 18197); + return true; + case 1056: + record = new EpsgProjectedCrsRecord(3137, "ETRS89-FIN [EUREF-FIN] / ETRS-GK30FIN", 10690, 4500, 18198); + return true; + case 1057: + record = new EpsgProjectedCrsRecord(3138, "ETRS89-FIN [EUREF-FIN] / ETRS-GK31FIN", 10690, 4500, 18199); + return true; + case 1058: + record = new EpsgProjectedCrsRecord(3139, "Vanua Levu 1915 / Vanua Levu Grid", 4748, 4533, 19878); + return true; + case 1059: + record = new EpsgProjectedCrsRecord(3140, "Viti Levu 1912 / Viti Levu Grid", 4752, 4533, 19879); + return true; + case 1060: + record = new EpsgProjectedCrsRecord(3141, "Fiji 1956 / UTM zone 60S", 4721, 4400, 16160); + return true; + case 1061: + record = new EpsgProjectedCrsRecord(3142, "Fiji 1956 / UTM zone 1S", 4721, 4400, 16101); + return true; + case 1062: + record = new EpsgProjectedCrsRecord(3144, "FD54 / Faroe Lambert", 4741, 1031, 19870); + return true; + case 1063: + record = new EpsgProjectedCrsRecord(3145, "ETRS89-FRO [2008] / Faroe Lambert", 11087, 1031, 19870); + return true; + case 1064: + record = new EpsgProjectedCrsRecord(3148, "Indian 1960 / UTM zone 48N", 4131, 4400, 16048); + return true; + case 1065: + record = new EpsgProjectedCrsRecord(3149, "Indian 1960 / UTM zone 49N", 4131, 4400, 16049); + return true; + case 1066: + record = new EpsgProjectedCrsRecord(3152, "ST74", 4619, 4531, 19876); + return true; + case 1067: + record = new EpsgProjectedCrsRecord(3153, "NAD83(CSRS) / BC Albers", 4617, 4400, 19984); + return true; + case 1068: + record = new EpsgProjectedCrsRecord(3154, "NAD83(CSRS) / UTM zone 7N", 4617, 4400, 16007); + return true; + case 1069: + record = new EpsgProjectedCrsRecord(3155, "NAD83(CSRS) / UTM zone 8N", 4617, 4400, 16008); + return true; + case 1070: + record = new EpsgProjectedCrsRecord(3156, "NAD83(CSRS) / UTM zone 9N", 4617, 4400, 16009); + return true; + case 1071: + record = new EpsgProjectedCrsRecord(3157, "NAD83(CSRS) / UTM zone 10N", 4617, 4400, 16010); + return true; + case 1072: + record = new EpsgProjectedCrsRecord(3158, "NAD83(CSRS) / UTM zone 14N", 4617, 4400, 16014); + return true; + case 1073: + record = new EpsgProjectedCrsRecord(3159, "NAD83(CSRS) / UTM zone 15N", 4617, 4400, 16015); + return true; + case 1074: + record = new EpsgProjectedCrsRecord(3160, "NAD83(CSRS) / UTM zone 16N", 4617, 4400, 16016); + return true; + case 1075: + record = new EpsgProjectedCrsRecord(3161, "NAD83 / Ontario MNR Lambert", 4269, 4400, 19875); + return true; + case 1076: + record = new EpsgProjectedCrsRecord(3162, "NAD83(CSRS) / Ontario MNR Lambert", 4617, 4400, 19875); + return true; + case 1077: + record = new EpsgProjectedCrsRecord(3163, "RGNC91-93 / Lambert New Caledonia", 4749, 4499, 19981); + return true; + case 1078: + record = new EpsgProjectedCrsRecord(3164, "ST87 Ouvea / UTM zone 58S", 4750, 4400, 16158); + return true; + case 1079: + record = new EpsgProjectedCrsRecord(3165, "NEA74 Noumea / Noumea Lambert", 4644, 4499, 19873); + return true; + case 1080: + record = new EpsgProjectedCrsRecord(3166, "NEA74 Noumea / Noumea Lambert 2", 4644, 4499, 19874); + return true; + case 1081: + record = new EpsgProjectedCrsRecord(3167, "Kertau (RSO) / RSO Malaya (ch)", 4751, 4410, 19871); + return true; + case 1082: + record = new EpsgProjectedCrsRecord(3168, "Kertau (RSO) / RSO Malaya (m)", 4751, 4400, 19872); + return true; + case 1083: + record = new EpsgProjectedCrsRecord(3169, "RGNC91-93 / UTM zone 57S", 4749, 4400, 16157); + return true; + case 1084: + record = new EpsgProjectedCrsRecord(3170, "RGNC91-93 / UTM zone 58S", 4749, 4400, 16158); + return true; + case 1085: + record = new EpsgProjectedCrsRecord(3171, "RGNC91-93 / UTM zone 59S", 4749, 4400, 16159); + return true; + case 1086: + record = new EpsgProjectedCrsRecord(3172, "IGN53 Mare / UTM zone 59S", 4641, 4400, 16159); + return true; + case 1087: + record = new EpsgProjectedCrsRecord(3173, "fk89 / Faroe Lambert FK89", 4753, 1031, 19877); + return true; + case 1088: + record = new EpsgProjectedCrsRecord(3174, "NAD83 / Great Lakes Albers", 4269, 4499, 15397); + return true; + case 1089: + record = new EpsgProjectedCrsRecord(3175, "NAD83 / Great Lakes and St Lawrence Albers", 4269, 4499, 15398); + return true; + case 1090: + record = new EpsgProjectedCrsRecord(3176, "Indian 1960 / TM 106 NE", 4131, 4400, 16506); + return true; + case 1091: + record = new EpsgProjectedCrsRecord(3177, "LGD2006 / Libya TM", 4754, 4499, 18319); + return true; + case 1092: + record = new EpsgProjectedCrsRecord(3178, "GR96 / UTM zone 18N", 4747, 4400, 16018); + return true; + case 1093: + record = new EpsgProjectedCrsRecord(3179, "GR96 / UTM zone 19N", 4747, 4400, 16019); + return true; + case 1094: + record = new EpsgProjectedCrsRecord(3180, "GR96 / UTM zone 20N", 4747, 4400, 16020); + return true; + case 1095: + record = new EpsgProjectedCrsRecord(3181, "GR96 / UTM zone 21N", 4747, 4400, 16021); + return true; + case 1096: + record = new EpsgProjectedCrsRecord(3182, "GR96 / UTM zone 22N", 4747, 4400, 16022); + return true; + case 1097: + record = new EpsgProjectedCrsRecord(3183, "GR96 / UTM zone 23N", 4747, 4400, 16023); + return true; + case 1098: + record = new EpsgProjectedCrsRecord(3184, "GR96 / UTM zone 24N", 4747, 4400, 16024); + return true; + case 1099: + record = new EpsgProjectedCrsRecord(3185, "GR96 / UTM zone 25N", 4747, 4400, 16025); + return true; + case 1100: + record = new EpsgProjectedCrsRecord(3186, "GR96 / UTM zone 26N", 4747, 4400, 16026); + return true; + case 1101: + record = new EpsgProjectedCrsRecord(3187, "GR96 / UTM zone 27N", 4747, 4400, 16027); + return true; + case 1102: + record = new EpsgProjectedCrsRecord(3188, "GR96 / UTM zone 28N", 4747, 4400, 16028); + return true; + case 1103: + record = new EpsgProjectedCrsRecord(3189, "GR96 / UTM zone 29N", 4747, 4400, 16029); + return true; + case 1104: + record = new EpsgProjectedCrsRecord(3190, "LGD2006 / Libya TM zone 5", 4754, 4499, 18310); + return true; + case 1105: + record = new EpsgProjectedCrsRecord(3191, "LGD2006 / Libya TM zone 6", 4754, 4499, 18311); + return true; + case 1106: + record = new EpsgProjectedCrsRecord(3192, "LGD2006 / Libya TM zone 7", 4754, 4499, 18312); + return true; + case 1107: + record = new EpsgProjectedCrsRecord(3193, "LGD2006 / Libya TM zone 8", 4754, 4499, 18313); + return true; + case 1108: + record = new EpsgProjectedCrsRecord(3194, "LGD2006 / Libya TM zone 9", 4754, 4499, 18314); + return true; + case 1109: + record = new EpsgProjectedCrsRecord(3195, "LGD2006 / Libya TM zone 10", 4754, 4499, 18315); + return true; + case 1110: + record = new EpsgProjectedCrsRecord(3196, "LGD2006 / Libya TM zone 11", 4754, 4499, 18316); + return true; + case 1111: + record = new EpsgProjectedCrsRecord(3197, "LGD2006 / Libya TM zone 12", 4754, 4499, 18317); + return true; + case 1112: + record = new EpsgProjectedCrsRecord(3198, "LGD2006 / Libya TM zone 13", 4754, 4499, 18318); + return true; + case 1113: + record = new EpsgProjectedCrsRecord(3199, "LGD2006 / UTM zone 32N", 4754, 4400, 16032); + return true; + case 1114: + record = new EpsgProjectedCrsRecord(3200, "FD58 / Iraq zone", 4132, 4400, 19906); + return true; + case 1115: + record = new EpsgProjectedCrsRecord(3201, "LGD2006 / UTM zone 33N", 4754, 4400, 16033); + return true; + case 1116: + record = new EpsgProjectedCrsRecord(3202, "LGD2006 / UTM zone 34N", 4754, 4400, 16034); + return true; + case 1117: + record = new EpsgProjectedCrsRecord(3203, "LGD2006 / UTM zone 35N", 4754, 4400, 16035); + return true; + case 1118: + record = new EpsgProjectedCrsRecord(3204, "WGS 84 / SCAR IMW SP19-20", 4326, 4400, 17204); + return true; + case 1119: + record = new EpsgProjectedCrsRecord(3205, "WGS 84 / SCAR IMW SP21-22", 4326, 4400, 17205); + return true; + case 1120: + record = new EpsgProjectedCrsRecord(3206, "WGS 84 / SCAR IMW SP23-24", 4326, 4400, 17206); + return true; + case 1121: + record = new EpsgProjectedCrsRecord(3207, "WGS 84 / SCAR IMW SQ01-02", 4326, 4400, 17207); + return true; + case 1122: + record = new EpsgProjectedCrsRecord(3208, "WGS 84 / SCAR IMW SQ19-20", 4326, 4400, 17208); + return true; + case 1123: + record = new EpsgProjectedCrsRecord(3209, "WGS 84 / SCAR IMW SQ21-22", 4326, 4400, 17209); + return true; + case 1124: + record = new EpsgProjectedCrsRecord(3210, "WGS 84 / SCAR IMW SQ37-38", 4326, 4400, 17210); + return true; + case 1125: + record = new EpsgProjectedCrsRecord(3211, "WGS 84 / SCAR IMW SQ39-40", 4326, 4400, 17211); + return true; + case 1126: + record = new EpsgProjectedCrsRecord(3212, "WGS 84 / SCAR IMW SQ41-42", 4326, 4400, 17212); + return true; + case 1127: + record = new EpsgProjectedCrsRecord(3213, "WGS 84 / SCAR IMW SQ43-44", 4326, 4400, 17213); + return true; + case 1128: + record = new EpsgProjectedCrsRecord(3214, "WGS 84 / SCAR IMW SQ45-46", 4326, 4400, 17214); + return true; + case 1129: + record = new EpsgProjectedCrsRecord(3215, "WGS 84 / SCAR IMW SQ47-48", 4326, 4400, 17215); + return true; + case 1130: + record = new EpsgProjectedCrsRecord(3216, "WGS 84 / SCAR IMW SQ49-50", 4326, 4400, 17216); + return true; + case 1131: + record = new EpsgProjectedCrsRecord(3217, "WGS 84 / SCAR IMW SQ51-52", 4326, 4400, 17217); + return true; + case 1132: + record = new EpsgProjectedCrsRecord(3218, "WGS 84 / SCAR IMW SQ53-54", 4326, 4400, 17218); + return true; + case 1133: + record = new EpsgProjectedCrsRecord(3219, "WGS 84 / SCAR IMW SQ55-56", 4326, 4400, 17219); + return true; + case 1134: + record = new EpsgProjectedCrsRecord(3220, "WGS 84 / SCAR IMW SQ57-58", 4326, 4400, 17220); + return true; + case 1135: + record = new EpsgProjectedCrsRecord(3221, "WGS 84 / SCAR IMW SR13-14", 4326, 4400, 17221); + return true; + case 1136: + record = new EpsgProjectedCrsRecord(3222, "WGS 84 / SCAR IMW SR15-16", 4326, 4400, 17222); + return true; + case 1137: + record = new EpsgProjectedCrsRecord(3223, "WGS 84 / SCAR IMW SR17-18", 4326, 4400, 17223); + return true; + case 1138: + record = new EpsgProjectedCrsRecord(3224, "WGS 84 / SCAR IMW SR19-20", 4326, 4400, 17224); + return true; + case 1139: + record = new EpsgProjectedCrsRecord(3225, "WGS 84 / SCAR IMW SR27-28", 4326, 4400, 17225); + return true; + case 1140: + record = new EpsgProjectedCrsRecord(3226, "WGS 84 / SCAR IMW SR29-30", 4326, 4400, 17226); + return true; + case 1141: + record = new EpsgProjectedCrsRecord(3227, "WGS 84 / SCAR IMW SR31-32", 4326, 4400, 17227); + return true; + case 1142: + record = new EpsgProjectedCrsRecord(3228, "WGS 84 / SCAR IMW SR33-34", 4326, 4400, 17228); + return true; + case 1143: + record = new EpsgProjectedCrsRecord(3229, "WGS 84 / SCAR IMW SR35-36", 4326, 4400, 17229); + return true; + case 1144: + record = new EpsgProjectedCrsRecord(3230, "WGS 84 / SCAR IMW SR37-38", 4326, 4400, 17230); + return true; + case 1145: + record = new EpsgProjectedCrsRecord(3231, "WGS 84 / SCAR IMW SR39-40", 4326, 4400, 17231); + return true; + case 1146: + record = new EpsgProjectedCrsRecord(3232, "WGS 84 / SCAR IMW SR41-42", 4326, 4400, 17232); + return true; + case 1147: + record = new EpsgProjectedCrsRecord(3233, "WGS 84 / SCAR IMW SR43-44", 4326, 4400, 17233); + return true; + case 1148: + record = new EpsgProjectedCrsRecord(3234, "WGS 84 / SCAR IMW SR45-46", 4326, 4400, 17234); + return true; + case 1149: + record = new EpsgProjectedCrsRecord(3235, "WGS 84 / SCAR IMW SR47-48", 4326, 4400, 17235); + return true; + case 1150: + record = new EpsgProjectedCrsRecord(3236, "WGS 84 / SCAR IMW SR49-50", 4326, 4400, 17236); + return true; + case 1151: + record = new EpsgProjectedCrsRecord(3237, "WGS 84 / SCAR IMW SR51-52", 4326, 4400, 17237); + return true; + case 1152: + record = new EpsgProjectedCrsRecord(3238, "WGS 84 / SCAR IMW SR53-54", 4326, 4400, 17238); + return true; + case 1153: + record = new EpsgProjectedCrsRecord(3239, "WGS 84 / SCAR IMW SR55-56", 4326, 4400, 17239); + return true; + case 1154: + record = new EpsgProjectedCrsRecord(3240, "WGS 84 / SCAR IMW SR57-58", 4326, 4400, 17240); + return true; + case 1155: + record = new EpsgProjectedCrsRecord(3241, "WGS 84 / SCAR IMW SR59-60", 4326, 4400, 17241); + return true; + case 1156: + record = new EpsgProjectedCrsRecord(3242, "WGS 84 / SCAR IMW SS04-06", 4326, 4400, 17242); + return true; + case 1157: + record = new EpsgProjectedCrsRecord(3243, "WGS 84 / SCAR IMW SS07-09", 4326, 4400, 17243); + return true; + case 1158: + record = new EpsgProjectedCrsRecord(3244, "WGS 84 / SCAR IMW SS10-12", 4326, 4400, 17244); + return true; + case 1159: + record = new EpsgProjectedCrsRecord(3245, "WGS 84 / SCAR IMW SS13-15", 4326, 4400, 17245); + return true; + case 1160: + record = new EpsgProjectedCrsRecord(3246, "WGS 84 / SCAR IMW SS16-18", 4326, 4400, 17246); + return true; + case 1161: + record = new EpsgProjectedCrsRecord(3247, "WGS 84 / SCAR IMW SS19-21", 4326, 4400, 17247); + return true; + case 1162: + record = new EpsgProjectedCrsRecord(3248, "WGS 84 / SCAR IMW SS25-27", 4326, 4400, 17248); + return true; + case 1163: + record = new EpsgProjectedCrsRecord(3249, "WGS 84 / SCAR IMW SS28-30", 4326, 4400, 17249); + return true; + case 1164: + record = new EpsgProjectedCrsRecord(3250, "WGS 84 / SCAR IMW SS31-33", 4326, 4400, 17250); + return true; + case 1165: + record = new EpsgProjectedCrsRecord(3251, "WGS 84 / SCAR IMW SS34-36", 4326, 4400, 17251); + return true; + case 1166: + record = new EpsgProjectedCrsRecord(3252, "WGS 84 / SCAR IMW SS37-39", 4326, 4400, 17252); + return true; + case 1167: + record = new EpsgProjectedCrsRecord(3253, "WGS 84 / SCAR IMW SS40-42", 4326, 4400, 17253); + return true; + case 1168: + record = new EpsgProjectedCrsRecord(3254, "WGS 84 / SCAR IMW SS43-45", 4326, 4400, 17254); + return true; + case 1169: + record = new EpsgProjectedCrsRecord(3255, "WGS 84 / SCAR IMW SS46-48", 4326, 4400, 17255); + return true; + case 1170: + record = new EpsgProjectedCrsRecord(3256, "WGS 84 / SCAR IMW SS49-51", 4326, 4400, 17256); + return true; + case 1171: + record = new EpsgProjectedCrsRecord(3257, "WGS 84 / SCAR IMW SS52-54", 4326, 4400, 17257); + return true; + case 1172: + record = new EpsgProjectedCrsRecord(3258, "WGS 84 / SCAR IMW SS55-57", 4326, 4400, 17258); + return true; + case 1173: + record = new EpsgProjectedCrsRecord(3259, "WGS 84 / SCAR IMW SS58-60", 4326, 4400, 17259); + return true; + case 1174: + record = new EpsgProjectedCrsRecord(3260, "WGS 84 / SCAR IMW ST01-04", 4326, 4400, 17260); + return true; + case 1175: + record = new EpsgProjectedCrsRecord(3261, "WGS 84 / SCAR IMW ST05-08", 4326, 4400, 17261); + return true; + case 1176: + record = new EpsgProjectedCrsRecord(3262, "WGS 84 / SCAR IMW ST09-12", 4326, 4400, 17262); + return true; + case 1177: + record = new EpsgProjectedCrsRecord(3263, "WGS 84 / SCAR IMW ST13-16", 4326, 4400, 17263); + return true; + case 1178: + record = new EpsgProjectedCrsRecord(3264, "WGS 84 / SCAR IMW ST17-20", 4326, 4400, 17264); + return true; + case 1179: + record = new EpsgProjectedCrsRecord(3265, "WGS 84 / SCAR IMW ST21-24", 4326, 4400, 17265); + return true; + case 1180: + record = new EpsgProjectedCrsRecord(3266, "WGS 84 / SCAR IMW ST25-28", 4326, 4400, 17266); + return true; + case 1181: + record = new EpsgProjectedCrsRecord(3267, "WGS 84 / SCAR IMW ST29-32", 4326, 4400, 17267); + return true; + case 1182: + record = new EpsgProjectedCrsRecord(3268, "WGS 84 / SCAR IMW ST33-36", 4326, 4400, 17268); + return true; + case 1183: + record = new EpsgProjectedCrsRecord(3269, "WGS 84 / SCAR IMW ST37-40", 4326, 4400, 17269); + return true; + case 1184: + record = new EpsgProjectedCrsRecord(3270, "WGS 84 / SCAR IMW ST41-44", 4326, 4400, 17270); + return true; + case 1185: + record = new EpsgProjectedCrsRecord(3271, "WGS 84 / SCAR IMW ST45-48", 4326, 4400, 17271); + return true; + case 1186: + record = new EpsgProjectedCrsRecord(3272, "WGS 84 / SCAR IMW ST49-52", 4326, 4400, 17272); + return true; + case 1187: + record = new EpsgProjectedCrsRecord(3273, "WGS 84 / SCAR IMW ST53-56", 4326, 4400, 17273); + return true; + case 1188: + record = new EpsgProjectedCrsRecord(3274, "WGS 84 / SCAR IMW ST57-60", 4326, 4400, 17274); + return true; + case 1189: + record = new EpsgProjectedCrsRecord(3275, "WGS 84 / SCAR IMW SU01-05", 4326, 4471, 17275); + return true; + case 1190: + record = new EpsgProjectedCrsRecord(3276, "WGS 84 / SCAR IMW SU06-10", 4326, 4473, 17276); + return true; + case 1191: + record = new EpsgProjectedCrsRecord(3277, "WGS 84 / SCAR IMW SU11-15", 4326, 4474, 17277); + return true; + case 1192: + record = new EpsgProjectedCrsRecord(3278, "WGS 84 / SCAR IMW SU16-20", 4326, 4476, 17278); + return true; + case 1193: + record = new EpsgProjectedCrsRecord(3279, "WGS 84 / SCAR IMW SU21-25", 4326, 4477, 17279); + return true; + case 1194: + record = new EpsgProjectedCrsRecord(3280, "WGS 84 / SCAR IMW SU26-30", 4326, 4479, 17280); + return true; + case 1195: + record = new EpsgProjectedCrsRecord(3281, "WGS 84 / SCAR IMW SU31-35", 4326, 4480, 17281); + return true; + case 1196: + record = new EpsgProjectedCrsRecord(3282, "WGS 84 / SCAR IMW SU36-40", 4326, 4482, 17282); + return true; + case 1197: + record = new EpsgProjectedCrsRecord(3283, "WGS 84 / SCAR IMW SU41-45", 4326, 4483, 17283); + return true; + case 1198: + record = new EpsgProjectedCrsRecord(3284, "WGS 84 / SCAR IMW SU46-50", 4326, 4485, 17284); + return true; + case 1199: + record = new EpsgProjectedCrsRecord(3285, "WGS 84 / SCAR IMW SU51-55", 4326, 4486, 17285); + return true; + case 1200: + record = new EpsgProjectedCrsRecord(3286, "WGS 84 / SCAR IMW SU56-60", 4326, 4488, 17286); + return true; + case 1201: + record = new EpsgProjectedCrsRecord(3287, "WGS 84 / SCAR IMW SV01-10", 4326, 4472, 17287); + return true; + case 1202: + record = new EpsgProjectedCrsRecord(3288, "WGS 84 / SCAR IMW SV11-20", 4326, 4475, 17288); + return true; + case 1203: + record = new EpsgProjectedCrsRecord(3289, "WGS 84 / SCAR IMW SV21-30", 4326, 4478, 17289); + return true; + case 1204: + record = new EpsgProjectedCrsRecord(3290, "WGS 84 / SCAR IMW SV31-40", 4326, 4481, 17290); + return true; + case 1205: + record = new EpsgProjectedCrsRecord(3291, "WGS 84 / SCAR IMW SV41-50", 4326, 4484, 17291); + return true; + case 1206: + record = new EpsgProjectedCrsRecord(3292, "WGS 84 / SCAR IMW SV51-60", 4326, 4487, 17292); + return true; + case 1207: + record = new EpsgProjectedCrsRecord(3293, "WGS 84 / SCAR IMW SW01-60", 4326, 4490, 17293); + return true; + case 1208: + record = new EpsgProjectedCrsRecord(3294, "WGS 84 / USGS Transantarctic Mountains", 4326, 4400, 17294); + return true; + case 1209: + record = new EpsgProjectedCrsRecord(3295, "Guam 1963 / Yap Islands", 4675, 4499, 15399); + return true; + case 1210: + record = new EpsgProjectedCrsRecord(3296, "RGPF / UTM zone 5S", 4687, 4400, 16105); + return true; + case 1211: + record = new EpsgProjectedCrsRecord(3297, "RGPF / UTM zone 6S", 4687, 4400, 16106); + return true; + case 1212: + record = new EpsgProjectedCrsRecord(3298, "RGPF / UTM zone 7S", 4687, 4400, 16107); + return true; + case 1213: + record = new EpsgProjectedCrsRecord(3299, "RGPF / UTM zone 8S", 4687, 4400, 16108); + return true; + case 1214: + record = new EpsgProjectedCrsRecord(3300, "Estonian Coordinate System of 1992", 4133, 4530, 19938); + return true; + case 1215: + record = new EpsgProjectedCrsRecord(3301, "Estonian Coordinate System of 1997", 4180, 4530, 19938); + return true; + case 1216: + record = new EpsgProjectedCrsRecord(3302, "IGN63 Hiva Oa / UTM zone 7S", 4689, 4400, 16107); + return true; + case 1217: + record = new EpsgProjectedCrsRecord(3303, "Fatu Iva 72 / UTM zone 7S", 4688, 4400, 16107); + return true; + case 1218: + record = new EpsgProjectedCrsRecord(3304, "Tahiti 79 / UTM zone 6S", 4690, 4400, 16106); + return true; + case 1219: + record = new EpsgProjectedCrsRecord(3305, "Moorea 87 / UTM zone 6S", 4691, 4400, 16106); + return true; + case 1220: + record = new EpsgProjectedCrsRecord(3306, "Maupiti 83 / UTM zone 5S", 4692, 4400, 16105); + return true; + case 1221: + record = new EpsgProjectedCrsRecord(3307, "Nakhl-e Ghanem / UTM zone 39N", 4693, 4400, 16039); + return true; + case 1222: + record = new EpsgProjectedCrsRecord(3308, "GDA94 / NSW Lambert", 4283, 4400, 17364); + return true; + case 1223: + record = new EpsgProjectedCrsRecord(3309, "NAD27 / California Albers", 4267, 4499, 10420); + return true; + case 1224: + record = new EpsgProjectedCrsRecord(3310, "NAD83 / California Albers", 4269, 4499, 10420); + return true; + case 1225: + record = new EpsgProjectedCrsRecord(3311, "NAD83(HARN) / California Albers", 4152, 4499, 10420); + return true; + case 1226: + record = new EpsgProjectedCrsRecord(3312, "CSG67 / UTM zone 21N", 4623, 4400, 16021); + return true; + case 1227: + record = new EpsgProjectedCrsRecord(3313, "RGFG95 / UTM zone 21N", 4624, 4400, 16021); + return true; + case 1228: + record = new EpsgProjectedCrsRecord(3316, "Kasai 1953 / Congo TM zone 22", 4696, 4400, 17422); + return true; + case 1229: + record = new EpsgProjectedCrsRecord(3317, "Kasai 1953 / Congo TM zone 24", 4696, 4400, 17424); + return true; + case 1230: + record = new EpsgProjectedCrsRecord(3318, "IGC 1962 / Congo TM zone 12", 4697, 4400, 17412); + return true; + case 1231: + record = new EpsgProjectedCrsRecord(3319, "IGC 1962 / Congo TM zone 14", 4697, 4400, 17414); + return true; + case 1232: + record = new EpsgProjectedCrsRecord(3320, "IGC 1962 / Congo TM zone 16", 4697, 4400, 17416); + return true; + case 1233: + record = new EpsgProjectedCrsRecord(3321, "IGC 1962 / Congo TM zone 18", 4697, 4400, 17418); + return true; + case 1234: + record = new EpsgProjectedCrsRecord(3322, "IGC 1962 / Congo TM zone 20", 4697, 4400, 17420); + return true; + case 1235: + record = new EpsgProjectedCrsRecord(3323, "IGC 1962 / Congo TM zone 22", 4697, 4400, 17422); + return true; + case 1236: + record = new EpsgProjectedCrsRecord(3324, "IGC 1962 / Congo TM zone 24", 4697, 4400, 17424); + return true; + case 1237: + record = new EpsgProjectedCrsRecord(3325, "IGC 1962 / Congo TM zone 26", 4697, 4400, 17426); + return true; + case 1238: + record = new EpsgProjectedCrsRecord(3326, "IGC 1962 / Congo TM zone 28", 4697, 4400, 17428); + return true; + case 1239: + record = new EpsgProjectedCrsRecord(3327, "IGC 1962 / Congo TM zone 30", 4697, 4400, 17430); + return true; + case 1240: + record = new EpsgProjectedCrsRecord(3328, "Pulkovo 1942(58) / GUGiK-80", 4179, 4530, 18286); + return true; + case 1241: + record = new EpsgProjectedCrsRecord(3329, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 5", 4179, 4530, 16265); + return true; + case 1242: + record = new EpsgProjectedCrsRecord(3330, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 6", 4179, 4530, 16266); + return true; + case 1243: + record = new EpsgProjectedCrsRecord(3331, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 7", 4179, 4530, 16267); + return true; + case 1244: + record = new EpsgProjectedCrsRecord(3332, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 8", 4179, 4530, 16268); + return true; + case 1245: + record = new EpsgProjectedCrsRecord(3333, "Pulkovo 1942(58) / Gauss-Kruger zone 3", 4179, 4530, 16203); + return true; + case 1246: + record = new EpsgProjectedCrsRecord(3334, "Pulkovo 1942(58) / Gauss-Kruger zone 4", 4179, 4530, 16204); + return true; + case 1247: + record = new EpsgProjectedCrsRecord(3335, "Pulkovo 1942(58) / Gauss-Kruger zone 5", 4179, 4530, 16205); + return true; + case 1248: + record = new EpsgProjectedCrsRecord(3336, "IGN 1962 Kerguelen / UTM zone 42S", 4698, 4400, 16142); + return true; + case 1249: + record = new EpsgProjectedCrsRecord(3337, "Le Pouce 1934 / Mauritius Grid", 4699, 4400, 19899); + return true; + case 1250: + record = new EpsgProjectedCrsRecord(3338, "NAD83 / Alaska Albers", 4269, 4499, 15021); + return true; + case 1251: + record = new EpsgProjectedCrsRecord(3339, "IGCB 1955 / Congo TM zone 12", 4701, 4400, 17412); + return true; + case 1252: + record = new EpsgProjectedCrsRecord(3340, "IGCB 1955 / Congo TM zone 14", 4701, 4400, 17414); + return true; + case 1253: + record = new EpsgProjectedCrsRecord(3341, "IGCB 1955 / Congo TM zone 16", 4701, 4400, 17416); + return true; + case 1254: + record = new EpsgProjectedCrsRecord(3342, "IGCB 1955 / UTM zone 33S", 4701, 4400, 16133); + return true; + case 1255: + record = new EpsgProjectedCrsRecord(3343, "Mauritania 1999 / UTM zone 28N", 4702, 4400, 16028); + return true; + case 1256: + record = new EpsgProjectedCrsRecord(3344, "Mauritania 1999 / UTM zone 29N", 4702, 4400, 16029); + return true; + case 1257: + record = new EpsgProjectedCrsRecord(3345, "Mauritania 1999 / UTM zone 30N", 4702, 4400, 16030); + return true; + case 1258: + record = new EpsgProjectedCrsRecord(3346, "ETRS89-LTU [LKS94] / Lithuania TM", 4669, 4530, 19934); + return true; + case 1259: + record = new EpsgProjectedCrsRecord(3347, "NAD83 / Statistics Canada Lambert", 4269, 4400, 19897); + return true; + case 1260: + record = new EpsgProjectedCrsRecord(3348, "NAD83(CSRS) / Statistics Canada Lambert", 4617, 4400, 19897); + return true; + case 1261: + record = new EpsgProjectedCrsRecord(3350, "Pulkovo 1942 / CS63 zone C0", 4284, 4530, 18450); + return true; + case 1262: + record = new EpsgProjectedCrsRecord(3351, "Pulkovo 1942 / CS63 zone C1", 4284, 4530, 18451); + return true; + case 1263: + record = new EpsgProjectedCrsRecord(3352, "Pulkovo 1942 / CS63 zone C2", 4284, 4530, 18452); + return true; + case 1264: + record = new EpsgProjectedCrsRecord(3353, "Mhast (onshore) / UTM zone 32S", 4704, 4400, 16132); + return true; + case 1265: + record = new EpsgProjectedCrsRecord(3354, "Mhast (offshore) / UTM zone 32S", 4705, 4400, 16132); + return true; + case 1266: + record = new EpsgProjectedCrsRecord(3355, "Egypt Gulf of Suez S-650 TL / Red Belt", 4706, 4400, 18072); + return true; + case 1267: + record = new EpsgProjectedCrsRecord(3358, "NAD83(HARN) / North Carolina", 4152, 4499, 13230); + return true; + case 1268: + record = new EpsgProjectedCrsRecord(3360, "NAD83(HARN) / South Carolina", 4152, 4499, 13930); + return true; + case 1269: + record = new EpsgProjectedCrsRecord(3361, "NAD83(HARN) / South Carolina (ft)", 4152, 4495, 15355); + return true; + case 1270: + record = new EpsgProjectedCrsRecord(3362, "NAD83(HARN) / Pennsylvania North", 4152, 4499, 13731); + return true; + case 1271: + record = new EpsgProjectedCrsRecord(3363, "NAD83(HARN) / Pennsylvania North (ftUS)", 4152, 4497, 15353); + return true; + case 1272: + record = new EpsgProjectedCrsRecord(3364, "NAD83(HARN) / Pennsylvania South", 4152, 4499, 13732); + return true; + case 1273: + record = new EpsgProjectedCrsRecord(3365, "NAD83(HARN) / Pennsylvania South (ftUS)", 4152, 4497, 15354); + return true; + case 1274: + record = new EpsgProjectedCrsRecord(3367, "IGN Astro 1960 / UTM zone 28N", 4700, 4400, 16028); + return true; + case 1275: + record = new EpsgProjectedCrsRecord(3368, "IGN Astro 1960 / UTM zone 29N", 4700, 4400, 16029); + return true; + case 1276: + record = new EpsgProjectedCrsRecord(3369, "IGN Astro 1960 / UTM zone 30N", 4700, 4400, 16030); + return true; + case 1277: + record = new EpsgProjectedCrsRecord(3370, "NAD27 / UTM zone 59N", 4267, 4400, 16059); + return true; + case 1278: + record = new EpsgProjectedCrsRecord(3371, "NAD27 / UTM zone 60N", 4267, 4400, 16060); + return true; + case 1279: + record = new EpsgProjectedCrsRecord(3372, "NAD83 / UTM zone 59N", 4269, 4400, 16059); + return true; + case 1280: + record = new EpsgProjectedCrsRecord(3373, "NAD83 / UTM zone 60N", 4269, 4400, 16060); + return true; + case 1281: + record = new EpsgProjectedCrsRecord(3374, "FD54 / UTM zone 29N", 4741, 4400, 16029); + return true; + case 1282: + record = new EpsgProjectedCrsRecord(3375, "GDM2000 / Peninsula RSO", 4742, 4400, 19895); + return true; + case 1283: + record = new EpsgProjectedCrsRecord(3376, "GDM2000 / East Malaysia BRSO", 4742, 4400, 19894); + return true; + case 1284: + record = new EpsgProjectedCrsRecord(3377, "GDM2000 / Johor Grid", 4742, 4400, 19893); + return true; + case 1285: + record = new EpsgProjectedCrsRecord(3378, "GDM2000 / Sembilan and Melaka Grid", 4742, 4400, 19892); + return true; + case 1286: + record = new EpsgProjectedCrsRecord(3379, "GDM2000 / Pahang Grid", 4742, 4400, 19891); + return true; + case 1287: + record = new EpsgProjectedCrsRecord(3380, "GDM2000 / Selangor Grid", 4742, 4400, 19890); + return true; + case 1288: + record = new EpsgProjectedCrsRecord(3381, "GDM2000 / Terengganu Grid", 4742, 4400, 19889); + return true; + case 1289: + record = new EpsgProjectedCrsRecord(3382, "GDM2000 / Pinang Grid", 4742, 4400, 19888); + return true; + case 1290: + record = new EpsgProjectedCrsRecord(3383, "GDM2000 / Kedah and Perlis Grid", 4742, 4400, 19887); + return true; + case 1291: + record = new EpsgProjectedCrsRecord(3384, "GDM2000 / Perak Grid", 4742, 4400, 19886); + return true; + case 1292: + record = new EpsgProjectedCrsRecord(3385, "GDM2000 / Kelantan Grid", 4742, 4400, 19885); + return true; + case 1293: + record = new EpsgProjectedCrsRecord(3386, "KKJ / Finland zone 0", 4123, 4530, 18180); + return true; + case 1294: + record = new EpsgProjectedCrsRecord(3387, "KKJ / Finland zone 5", 4123, 4530, 18205); + return true; + case 1295: + record = new EpsgProjectedCrsRecord(3388, "Pulkovo 1942 / Caspian Sea Mercator", 4284, 4534, 19884); + return true; + case 1296: + record = new EpsgProjectedCrsRecord(3389, "Pulkovo 1942 / 3-degree Gauss-Kruger zone 60", 4284, 4530, 16099); + return true; + case 1297: + record = new EpsgProjectedCrsRecord(3390, "Pulkovo 1995 / 3-degree Gauss-Kruger zone 60", 4200, 4530, 16099); + return true; + case 1298: + record = new EpsgProjectedCrsRecord(3391, "Karbala 1979 / UTM zone 37N", 4743, 4400, 16037); + return true; + case 1299: + record = new EpsgProjectedCrsRecord(3392, "Karbala 1979 / UTM zone 38N", 4743, 4400, 16038); + return true; + case 1300: + record = new EpsgProjectedCrsRecord(3393, "Karbala 1979 / UTM zone 39N", 4743, 4400, 16039); + return true; + case 1301: + record = new EpsgProjectedCrsRecord(3394, "Nahrwan 1934 / Iraq zone", 4744, 4400, 19906); + return true; + case 1302: + record = new EpsgProjectedCrsRecord(3395, "WGS 84 / World Mercator", 4326, 4400, 19883); + return true; + case 1303: + record = new EpsgProjectedCrsRecord(3396, "PD/83 / 3-degree Gauss-Kruger zone 3", 4746, 4530, 16263); + return true; + case 1304: + record = new EpsgProjectedCrsRecord(3397, "PD/83 / 3-degree Gauss-Kruger zone 4", 4746, 4530, 16264); + return true; + case 1305: + record = new EpsgProjectedCrsRecord(3398, "RD/83 / 3-degree Gauss-Kruger zone 4", 4745, 4530, 16264); + return true; + case 1306: + record = new EpsgProjectedCrsRecord(3399, "RD/83 / 3-degree Gauss-Kruger zone 5", 4745, 4530, 16265); + return true; + case 1307: + record = new EpsgProjectedCrsRecord(3400, "NAD83 / Alberta 10-TM (Forest)", 4269, 4400, 19881); + return true; + case 1308: + record = new EpsgProjectedCrsRecord(3401, "NAD83 / Alberta 10-TM (Resource)", 4269, 4400, 19882); + return true; + case 1309: + record = new EpsgProjectedCrsRecord(3402, "NAD83(CSRS) / Alberta 10-TM (Forest)", 4617, 4400, 19881); + return true; + case 1310: + record = new EpsgProjectedCrsRecord(3403, "NAD83(CSRS) / Alberta 10-TM (Resource)", 4617, 4400, 19882); + return true; + case 1311: + record = new EpsgProjectedCrsRecord(3404, "NAD83(HARN) / North Carolina (ftUS)", 4152, 4497, 15346); + return true; + case 1312: + record = new EpsgProjectedCrsRecord(3405, "VN-2000 / UTM zone 48N", 4756, 4400, 16048); + return true; + case 1313: + record = new EpsgProjectedCrsRecord(3406, "VN-2000 / UTM zone 49N", 4756, 4400, 16049); + return true; + case 1314: + record = new EpsgProjectedCrsRecord(3407, "Hong Kong 1963 Grid System", 4738, 4502, 19896); + return true; + case 1315: + record = new EpsgProjectedCrsRecord(3408, "NSIDC EASE-Grid North", 10346, 4469, 3897); + return true; + case 1316: + record = new EpsgProjectedCrsRecord(3409, "NSIDC EASE-Grid South", 10346, 4470, 3898); + return true; + case 1317: + record = new EpsgProjectedCrsRecord(3410, "NSIDC EASE-Grid Global", 10346, 4499, 19869); + return true; + case 1318: + record = new EpsgProjectedCrsRecord(3411, "NSIDC Sea Ice Polar Stereographic North", 10345, 4468, 19865); + return true; + case 1319: + record = new EpsgProjectedCrsRecord(3412, "NSIDC Sea Ice Polar Stereographic South", 10345, 4470, 19866); + return true; + case 1320: + record = new EpsgProjectedCrsRecord(3413, "WGS 84 / NSIDC Sea Ice Polar Stereographic North", 4326, 4468, 19865); + return true; + case 1321: + record = new EpsgProjectedCrsRecord(3414, "SVY21 / Singapore TM", 4757, 4500, 19864); + return true; + case 1322: + record = new EpsgProjectedCrsRecord(3415, "WGS 72BE / South China Sea Lambert", 4324, 4400, 19863); + return true; + case 1323: + record = new EpsgProjectedCrsRecord(3416, "ETRS89-AUT [2002] / Austria Lambert", 11057, 4530, 19947); + return true; + case 1324: + record = new EpsgProjectedCrsRecord(3417, "NAD83 / Iowa North (ftUS)", 4269, 4497, 15377); + return true; + case 1325: + record = new EpsgProjectedCrsRecord(3418, "NAD83 / Iowa South (ftUS)", 4269, 4497, 15378); + return true; + case 1326: + record = new EpsgProjectedCrsRecord(3419, "NAD83 / Kansas North (ftUS)", 4269, 4497, 15379); + return true; + case 1327: + record = new EpsgProjectedCrsRecord(3420, "NAD83 / Kansas South (ftUS)", 4269, 4497, 15380); + return true; + case 1328: + record = new EpsgProjectedCrsRecord(3421, "NAD83 / Nevada East (ftUS)", 4269, 4497, 15381); + return true; + case 1329: + record = new EpsgProjectedCrsRecord(3422, "NAD83 / Nevada Central (ftUS)", 4269, 4497, 15382); + return true; + case 1330: + record = new EpsgProjectedCrsRecord(3423, "NAD83 / Nevada West (ftUS)", 4269, 4497, 15383); + return true; + case 1331: + record = new EpsgProjectedCrsRecord(3424, "NAD83 / New Jersey (ftUS)", 4269, 4497, 15384); + return true; + case 1332: + record = new EpsgProjectedCrsRecord(3425, "NAD83(HARN) / Iowa North (ftUS)", 4152, 4497, 15377); + return true; + case 1333: + record = new EpsgProjectedCrsRecord(3426, "NAD83(HARN) / Iowa South (ftUS)", 4152, 4497, 15378); + return true; + case 1334: + record = new EpsgProjectedCrsRecord(3427, "NAD83(HARN) / Kansas North (ftUS)", 4152, 4497, 15379); + return true; + case 1335: + record = new EpsgProjectedCrsRecord(3428, "NAD83(HARN) / Kansas South (ftUS)", 4152, 4497, 15380); + return true; + case 1336: + record = new EpsgProjectedCrsRecord(3429, "NAD83(HARN) / Nevada East (ftUS)", 4152, 4497, 15381); + return true; + case 1337: + record = new EpsgProjectedCrsRecord(3430, "NAD83(HARN) / Nevada Central (ftUS)", 4152, 4497, 15382); + return true; + case 1338: + record = new EpsgProjectedCrsRecord(3431, "NAD83(HARN) / Nevada West (ftUS)", 4152, 4497, 15383); + return true; + case 1339: + record = new EpsgProjectedCrsRecord(3432, "NAD83(HARN) / New Jersey (ftUS)", 4152, 4497, 15384); + return true; + case 1340: + record = new EpsgProjectedCrsRecord(3433, "NAD83 / Arkansas North (ftUS)", 4269, 4497, 15385); + return true; + case 1341: + record = new EpsgProjectedCrsRecord(3434, "NAD83 / Arkansas South (ftUS)", 4269, 4497, 15386); + return true; + case 1342: + record = new EpsgProjectedCrsRecord(3435, "NAD83 / Illinois East (ftUS)", 4269, 4497, 15387); + return true; + case 1343: + record = new EpsgProjectedCrsRecord(3436, "NAD83 / Illinois West (ftUS)", 4269, 4497, 15388); + return true; + case 1344: + record = new EpsgProjectedCrsRecord(3437, "NAD83 / New Hampshire (ftUS)", 4269, 4497, 15389); + return true; + case 1345: + record = new EpsgProjectedCrsRecord(3438, "NAD83 / Rhode Island (ftUS)", 4269, 4497, 15390); + return true; + case 1346: + record = new EpsgProjectedCrsRecord(3439, "PSD93 / UTM zone 39N", 4134, 4400, 16039); + return true; + case 1347: + record = new EpsgProjectedCrsRecord(3440, "PSD93 / UTM zone 40N", 4134, 4400, 16040); + return true; + case 1348: + record = new EpsgProjectedCrsRecord(3441, "NAD83(HARN) / Arkansas North (ftUS)", 4152, 4497, 15385); + return true; + case 1349: + record = new EpsgProjectedCrsRecord(3442, "NAD83(HARN) / Arkansas South (ftUS)", 4152, 4497, 15386); + return true; + case 1350: + record = new EpsgProjectedCrsRecord(3443, "NAD83(HARN) / Illinois East (ftUS)", 4152, 4497, 15387); + return true; + case 1351: + record = new EpsgProjectedCrsRecord(3444, "NAD83(HARN) / Illinois West (ftUS)", 4152, 4497, 15388); + return true; + case 1352: + record = new EpsgProjectedCrsRecord(3445, "NAD83(HARN) / New Hampshire (ftUS)", 4152, 4497, 15389); + return true; + case 1353: + record = new EpsgProjectedCrsRecord(3446, "NAD83(HARN) / Rhode Island (ftUS)", 4152, 4497, 15390); + return true; + case 1354: + record = new EpsgProjectedCrsRecord(3447, "ETRS89-BEL [BEREF2002] / Belgian Lambert 2005", 11063, 4499, 19862); + return true; + case 1355: + record = new EpsgProjectedCrsRecord(3448, "JAD2001 / Jamaica Metric Grid", 4758, 4400, 19860); + return true; + case 1356: + record = new EpsgProjectedCrsRecord(3449, "JAD2001 / UTM zone 17N", 4758, 4400, 16017); + return true; + case 1357: + record = new EpsgProjectedCrsRecord(3450, "JAD2001 / UTM zone 18N", 4758, 4400, 16018); + return true; + case 1358: + record = new EpsgProjectedCrsRecord(3451, "NAD83 / Louisiana North (ftUS)", 4269, 4497, 15391); + return true; + case 1359: + record = new EpsgProjectedCrsRecord(3452, "NAD83 / Louisiana South (ftUS)", 4269, 4497, 15392); + return true; + case 1360: + record = new EpsgProjectedCrsRecord(3453, "NAD83 / Louisiana Offshore (ftUS)", 4269, 4497, 15393); + return true; + case 1361: + record = new EpsgProjectedCrsRecord(3455, "NAD83 / South Dakota South (ftUS)", 4269, 4497, 15395); + return true; + case 1362: + record = new EpsgProjectedCrsRecord(3456, "NAD83(HARN) / Louisiana North (ftUS)", 4152, 4497, 15391); + return true; + case 1363: + record = new EpsgProjectedCrsRecord(3457, "NAD83(HARN) / Louisiana South (ftUS)", 4152, 4497, 15392); + return true; + case 1364: + record = new EpsgProjectedCrsRecord(3458, "NAD83(HARN) / South Dakota North (ftUS)", 4152, 4497, 15394); + return true; + case 1365: + record = new EpsgProjectedCrsRecord(3459, "NAD83(HARN) / South Dakota South (ftUS)", 4152, 4497, 15395); + return true; + case 1366: + record = new EpsgProjectedCrsRecord(3460, "Fiji 1986 / Fiji Map Grid", 4720, 4400, 19859); + return true; + case 1367: + record = new EpsgProjectedCrsRecord(3461, "Dabola 1981 / UTM zone 28N", 4155, 4400, 16028); + return true; + case 1368: + record = new EpsgProjectedCrsRecord(3462, "Dabola 1981 / UTM zone 29N", 4155, 4400, 16029); + return true; + case 1369: + record = new EpsgProjectedCrsRecord(3463, "NAD83 / Maine CS2000 Central", 4269, 4499, 11854); + return true; + case 1370: + record = new EpsgProjectedCrsRecord(3464, "NAD83(HARN) / Maine CS2000 Central", 4152, 4499, 11854); + return true; + case 1371: + record = new EpsgProjectedCrsRecord(3465, "NAD83(NSRS2007) / Alabama East", 4759, 4499, 10131); + return true; + case 1372: + record = new EpsgProjectedCrsRecord(3466, "NAD83(NSRS2007) / Alabama West", 4759, 4499, 10132); + return true; + case 1373: + record = new EpsgProjectedCrsRecord(3467, "NAD83(NSRS2007) / Alaska Albers", 4759, 4499, 15021); + return true; + case 1374: + record = new EpsgProjectedCrsRecord(3468, "NAD83(NSRS2007) / Alaska zone 1", 4759, 4499, 15031); + return true; + case 1375: + record = new EpsgProjectedCrsRecord(3469, "NAD83(NSRS2007) / Alaska zone 2", 4759, 4499, 15032); + return true; + case 1376: + record = new EpsgProjectedCrsRecord(3470, "NAD83(NSRS2007) / Alaska zone 3", 4759, 4499, 15033); + return true; + case 1377: + record = new EpsgProjectedCrsRecord(3471, "NAD83(NSRS2007) / Alaska zone 4", 4759, 4499, 15034); + return true; + case 1378: + record = new EpsgProjectedCrsRecord(3472, "NAD83(NSRS2007) / Alaska zone 5", 4759, 4499, 15035); + return true; + case 1379: + record = new EpsgProjectedCrsRecord(3473, "NAD83(NSRS2007) / Alaska zone 6", 4759, 4499, 15036); + return true; + case 1380: + record = new EpsgProjectedCrsRecord(3474, "NAD83(NSRS2007) / Alaska zone 7", 4759, 4499, 15037); + return true; + case 1381: + record = new EpsgProjectedCrsRecord(3475, "NAD83(NSRS2007) / Alaska zone 8", 4759, 4499, 15038); + return true; + case 1382: + record = new EpsgProjectedCrsRecord(3476, "NAD83(NSRS2007) / Alaska zone 9", 4759, 4499, 15039); + return true; + case 1383: + record = new EpsgProjectedCrsRecord(3477, "NAD83(NSRS2007) / Alaska zone 10", 4759, 4499, 15040); + return true; + case 1384: + record = new EpsgProjectedCrsRecord(3478, "NAD83(NSRS2007) / Arizona Central", 4759, 4499, 10232); + return true; + case 1385: + record = new EpsgProjectedCrsRecord(3479, "NAD83(NSRS2007) / Arizona Central (ft)", 4759, 4495, 15305); + return true; + case 1386: + record = new EpsgProjectedCrsRecord(3480, "NAD83(NSRS2007) / Arizona East", 4759, 4499, 10231); + return true; + case 1387: + record = new EpsgProjectedCrsRecord(3481, "NAD83(NSRS2007) / Arizona East (ft)", 4759, 4495, 15304); + return true; + case 1388: + record = new EpsgProjectedCrsRecord(3482, "NAD83(NSRS2007) / Arizona West", 4759, 4499, 10233); + return true; + case 1389: + record = new EpsgProjectedCrsRecord(3483, "NAD83(NSRS2007) / Arizona West (ft)", 4759, 4495, 15306); + return true; + case 1390: + record = new EpsgProjectedCrsRecord(3484, "NAD83(NSRS2007) / Arkansas North", 4759, 4499, 10331); + return true; + case 1391: + record = new EpsgProjectedCrsRecord(3485, "NAD83(NSRS2007) / Arkansas North (ftUS)", 4759, 4497, 15385); + return true; + case 1392: + record = new EpsgProjectedCrsRecord(3486, "NAD83(NSRS2007) / Arkansas South", 4759, 4499, 10332); + return true; + case 1393: + record = new EpsgProjectedCrsRecord(3487, "NAD83(NSRS2007) / Arkansas South (ftUS)", 4759, 4497, 15386); + return true; + case 1394: + record = new EpsgProjectedCrsRecord(3488, "NAD83(NSRS2007) / California Albers", 4759, 4499, 10420); + return true; + case 1395: + record = new EpsgProjectedCrsRecord(3489, "NAD83(NSRS2007) / California zone 1", 4759, 4499, 10431); + return true; + case 1396: + record = new EpsgProjectedCrsRecord(3490, "NAD83(NSRS2007) / California zone 1 (ftUS)", 4759, 4497, 15307); + return true; + case 1397: + record = new EpsgProjectedCrsRecord(3491, "NAD83(NSRS2007) / California zone 2", 4759, 4499, 10432); + return true; + case 1398: + record = new EpsgProjectedCrsRecord(3492, "NAD83(NSRS2007) / California zone 2 (ftUS)", 4759, 4497, 15308); + return true; + case 1399: + record = new EpsgProjectedCrsRecord(3493, "NAD83(NSRS2007) / California zone 3", 4759, 4499, 10433); + return true; + case 1400: + record = new EpsgProjectedCrsRecord(3494, "NAD83(NSRS2007) / California zone 3 (ftUS)", 4759, 4497, 15309); + return true; + case 1401: + record = new EpsgProjectedCrsRecord(3495, "NAD83(NSRS2007) / California zone 4", 4759, 4499, 10434); + return true; + case 1402: + record = new EpsgProjectedCrsRecord(3496, "NAD83(NSRS2007) / California zone 4 (ftUS)", 4759, 4497, 15310); + return true; + case 1403: + record = new EpsgProjectedCrsRecord(3497, "NAD83(NSRS2007) / California zone 5", 4759, 4499, 10435); + return true; + case 1404: + record = new EpsgProjectedCrsRecord(3498, "NAD83(NSRS2007) / California zone 5 (ftUS)", 4759, 4497, 15311); + return true; + case 1405: + record = new EpsgProjectedCrsRecord(3499, "NAD83(NSRS2007) / California zone 6", 4759, 4499, 10436); + return true; + case 1406: + record = new EpsgProjectedCrsRecord(3500, "NAD83(NSRS2007) / California zone 6 (ftUS)", 4759, 4497, 15312); + return true; + case 1407: + record = new EpsgProjectedCrsRecord(3501, "NAD83(NSRS2007) / Colorado Central", 4759, 4499, 10532); + return true; + case 1408: + record = new EpsgProjectedCrsRecord(3502, "NAD83(NSRS2007) / Colorado Central (ftUS)", 4759, 4497, 15314); + return true; + case 1409: + record = new EpsgProjectedCrsRecord(3503, "NAD83(NSRS2007) / Colorado North", 4759, 4499, 10531); + return true; + case 1410: + record = new EpsgProjectedCrsRecord(3504, "NAD83(NSRS2007) / Colorado North (ftUS)", 4759, 4497, 15313); + return true; + case 1411: + record = new EpsgProjectedCrsRecord(3505, "NAD83(NSRS2007) / Colorado South", 4759, 4499, 10533); + return true; + case 1412: + record = new EpsgProjectedCrsRecord(3506, "NAD83(NSRS2007) / Colorado South (ftUS)", 4759, 4497, 15315); + return true; + case 1413: + record = new EpsgProjectedCrsRecord(3507, "NAD83(NSRS2007) / Connecticut", 4759, 4499, 10630); + return true; + case 1414: + record = new EpsgProjectedCrsRecord(3508, "NAD83(NSRS2007) / Connecticut (ftUS)", 4759, 4497, 15316); + return true; + case 1415: + record = new EpsgProjectedCrsRecord(3509, "NAD83(NSRS2007) / Delaware", 4759, 4499, 10730); + return true; + case 1416: + record = new EpsgProjectedCrsRecord(3510, "NAD83(NSRS2007) / Delaware (ftUS)", 4759, 4497, 15317); + return true; + case 1417: + record = new EpsgProjectedCrsRecord(3511, "NAD83(NSRS2007) / Florida East", 4759, 4499, 10931); + return true; + case 1418: + record = new EpsgProjectedCrsRecord(3512, "NAD83(NSRS2007) / Florida East (ftUS)", 4759, 4497, 15318); + return true; + case 1419: + record = new EpsgProjectedCrsRecord(3513, "NAD83(NSRS2007) / Florida GDL Albers", 4759, 4499, 10934); + return true; + case 1420: + record = new EpsgProjectedCrsRecord(3514, "NAD83(NSRS2007) / Florida North", 4759, 4499, 10933); + return true; + case 1421: + record = new EpsgProjectedCrsRecord(3515, "NAD83(NSRS2007) / Florida North (ftUS)", 4759, 4497, 15320); + return true; + case 1422: + record = new EpsgProjectedCrsRecord(3516, "NAD83(NSRS2007) / Florida West", 4759, 4499, 10932); + return true; + case 1423: + record = new EpsgProjectedCrsRecord(3517, "NAD83(NSRS2007) / Florida West (ftUS)", 4759, 4497, 15319); + return true; + case 1424: + record = new EpsgProjectedCrsRecord(3518, "NAD83(NSRS2007) / Georgia East", 4759, 4499, 11031); + return true; + case 1425: + record = new EpsgProjectedCrsRecord(3519, "NAD83(NSRS2007) / Georgia East (ftUS)", 4759, 4497, 15321); + return true; + case 1426: + record = new EpsgProjectedCrsRecord(3520, "NAD83(NSRS2007) / Georgia West", 4759, 4499, 11032); + return true; + case 1427: + record = new EpsgProjectedCrsRecord(3521, "NAD83(NSRS2007) / Georgia West (ftUS)", 4759, 4497, 15322); + return true; + case 1428: + record = new EpsgProjectedCrsRecord(3522, "NAD83(NSRS2007) / Idaho Central", 4759, 4499, 11132); + return true; + case 1429: + record = new EpsgProjectedCrsRecord(3523, "NAD83(NSRS2007) / Idaho Central (ftUS)", 4759, 4497, 15324); + return true; + case 1430: + record = new EpsgProjectedCrsRecord(3524, "NAD83(NSRS2007) / Idaho East", 4759, 4499, 11131); + return true; + case 1431: + record = new EpsgProjectedCrsRecord(3525, "NAD83(NSRS2007) / Idaho East (ftUS)", 4759, 4497, 15323); + return true; + case 1432: + record = new EpsgProjectedCrsRecord(3526, "NAD83(NSRS2007) / Idaho West", 4759, 4499, 11133); + return true; + case 1433: + record = new EpsgProjectedCrsRecord(3527, "NAD83(NSRS2007) / Idaho West (ftUS)", 4759, 4497, 15325); + return true; + case 1434: + record = new EpsgProjectedCrsRecord(3528, "NAD83(NSRS2007) / Illinois East", 4759, 4499, 11231); + return true; + case 1435: + record = new EpsgProjectedCrsRecord(3529, "NAD83(NSRS2007) / Illinois East (ftUS)", 4759, 4497, 15387); + return true; + case 1436: + record = new EpsgProjectedCrsRecord(3530, "NAD83(NSRS2007) / Illinois West", 4759, 4499, 11232); + return true; + case 1437: + record = new EpsgProjectedCrsRecord(3531, "NAD83(NSRS2007) / Illinois West (ftUS)", 4759, 4497, 15388); + return true; + case 1438: + record = new EpsgProjectedCrsRecord(3532, "NAD83(NSRS2007) / Indiana East", 4759, 4499, 11331); + return true; + case 1439: + record = new EpsgProjectedCrsRecord(3533, "NAD83(NSRS2007) / Indiana East (ftUS)", 4759, 4497, 15372); + return true; + case 1440: + record = new EpsgProjectedCrsRecord(3534, "NAD83(NSRS2007) / Indiana West", 4759, 4499, 11332); + return true; + case 1441: + record = new EpsgProjectedCrsRecord(3535, "NAD83(NSRS2007) / Indiana West (ftUS)", 4759, 4497, 15373); + return true; + case 1442: + record = new EpsgProjectedCrsRecord(3536, "NAD83(NSRS2007) / Iowa North", 4759, 4499, 11431); + return true; + case 1443: + record = new EpsgProjectedCrsRecord(3537, "NAD83(NSRS2007) / Iowa North (ftUS)", 4759, 4497, 15377); + return true; + case 1444: + record = new EpsgProjectedCrsRecord(3538, "NAD83(NSRS2007) / Iowa South", 4759, 4499, 11432); + return true; + case 1445: + record = new EpsgProjectedCrsRecord(3539, "NAD83(NSRS2007) / Iowa South (ftUS)", 4759, 4497, 15378); + return true; + case 1446: + record = new EpsgProjectedCrsRecord(3540, "NAD83(NSRS2007) / Kansas North", 4759, 4499, 11531); + return true; + case 1447: + record = new EpsgProjectedCrsRecord(3541, "NAD83(NSRS2007) / Kansas North (ftUS)", 4759, 4497, 15379); + return true; + case 1448: + record = new EpsgProjectedCrsRecord(3542, "NAD83(NSRS2007) / Kansas South", 4759, 4499, 11532); + return true; + case 1449: + record = new EpsgProjectedCrsRecord(3543, "NAD83(NSRS2007) / Kansas South (ftUS)", 4759, 4497, 15380); + return true; + case 1450: + record = new EpsgProjectedCrsRecord(3544, "NAD83(NSRS2007) / Kentucky North", 4759, 4499, 15303); + return true; + case 1451: + record = new EpsgProjectedCrsRecord(3545, "NAD83(NSRS2007) / Kentucky North (ftUS)", 4759, 4497, 15328); + return true; + case 1452: + record = new EpsgProjectedCrsRecord(3546, "NAD83(NSRS2007) / Kentucky Single Zone", 4759, 4499, 11630); + return true; + case 1453: + record = new EpsgProjectedCrsRecord(3547, "NAD83(NSRS2007) / Kentucky Single Zone (ftUS)", 4759, 4497, 15375); + return true; + case 1454: + record = new EpsgProjectedCrsRecord(3548, "NAD83(NSRS2007) / Kentucky South", 4759, 4499, 11632); + return true; + case 1455: + record = new EpsgProjectedCrsRecord(3549, "NAD83(NSRS2007) / Kentucky South (ftUS)", 4759, 4497, 15329); + return true; + case 1456: + record = new EpsgProjectedCrsRecord(3550, "NAD83(NSRS2007) / Louisiana North", 4759, 4499, 11731); + return true; + case 1457: + record = new EpsgProjectedCrsRecord(3551, "NAD83(NSRS2007) / Louisiana North (ftUS)", 4759, 4497, 15391); + return true; + case 1458: + record = new EpsgProjectedCrsRecord(3552, "NAD83(NSRS2007) / Louisiana South", 4759, 4499, 11732); + return true; + case 1459: + record = new EpsgProjectedCrsRecord(3553, "NAD83(NSRS2007) / Louisiana South (ftUS)", 4759, 4497, 15392); + return true; + case 1460: + record = new EpsgProjectedCrsRecord(3554, "NAD83(NSRS2007) / Maine CS2000 Central", 4759, 4499, 11854); + return true; + case 1461: + record = new EpsgProjectedCrsRecord(3555, "NAD83(NSRS2007) / Maine CS2000 East", 4759, 4499, 11851); + return true; + case 1462: + record = new EpsgProjectedCrsRecord(3556, "NAD83(NSRS2007) / Maine CS2000 West", 4759, 4499, 11853); + return true; + case 1463: + record = new EpsgProjectedCrsRecord(3557, "NAD83(NSRS2007) / Maine East", 4759, 4499, 11831); + return true; + case 1464: + record = new EpsgProjectedCrsRecord(3558, "NAD83(NSRS2007) / Maine West", 4759, 4499, 11832); + return true; + case 1465: + record = new EpsgProjectedCrsRecord(3559, "NAD83(NSRS2007) / Maryland", 4759, 4499, 11930); + return true; + case 1466: + record = new EpsgProjectedCrsRecord(3560, "NAD83 / Utah North (ftUS)", 4269, 4497, 15297); + return true; + case 1467: + record = new EpsgProjectedCrsRecord(3561, "Old Hawaiian / Hawaii zone 1", 4135, 4497, 15101); + return true; + case 1468: + record = new EpsgProjectedCrsRecord(3562, "Old Hawaiian / Hawaii zone 2", 4135, 4497, 15102); + return true; + case 1469: + record = new EpsgProjectedCrsRecord(3563, "Old Hawaiian / Hawaii zone 3", 4135, 4497, 15103); + return true; + case 1470: + record = new EpsgProjectedCrsRecord(3564, "Old Hawaiian / Hawaii zone 4", 4135, 4497, 15104); + return true; + case 1471: + record = new EpsgProjectedCrsRecord(3565, "Old Hawaiian / Hawaii zone 5", 4135, 4497, 15105); + return true; + case 1472: + record = new EpsgProjectedCrsRecord(3566, "NAD83 / Utah Central (ftUS)", 4269, 4497, 15298); + return true; + case 1473: + record = new EpsgProjectedCrsRecord(3567, "NAD83 / Utah South (ftUS)", 4269, 4497, 15299); + return true; + case 1474: + record = new EpsgProjectedCrsRecord(3568, "NAD83(HARN) / Utah North (ftUS)", 4152, 4497, 15297); + return true; + case 1475: + record = new EpsgProjectedCrsRecord(3569, "NAD83(HARN) / Utah Central (ftUS)", 4152, 4497, 15298); + return true; + case 1476: + record = new EpsgProjectedCrsRecord(3570, "NAD83(HARN) / Utah South (ftUS)", 4152, 4497, 15299); + return true; + case 1477: + record = new EpsgProjectedCrsRecord(3571, "WGS 84 / North Pole LAEA Bering Sea", 4326, 4464, 17295); + return true; + case 1478: + record = new EpsgProjectedCrsRecord(3572, "WGS 84 / North Pole LAEA Alaska", 4326, 4467, 17296); + return true; + case 1479: + record = new EpsgProjectedCrsRecord(3573, "WGS 84 / North Pole LAEA Canada", 4326, 4466, 17297); + return true; + case 1480: + record = new EpsgProjectedCrsRecord(3574, "WGS 84 / North Pole LAEA Atlantic", 4326, 4465, 17298); + return true; + case 1481: + record = new EpsgProjectedCrsRecord(3575, "WGS 84 / North Pole LAEA Europe", 4326, 4463, 17299); + return true; + case 1482: + record = new EpsgProjectedCrsRecord(3576, "WGS 84 / North Pole LAEA Russia", 4326, 1035, 17300); + return true; + case 1483: + record = new EpsgProjectedCrsRecord(3577, "GDA94 / Australian Albers", 4283, 4400, 17365); + return true; + case 1484: + record = new EpsgProjectedCrsRecord(3578, "NAD83 / Yukon Albers", 4269, 4400, 19858); + return true; + case 1485: + record = new EpsgProjectedCrsRecord(3579, "NAD83(CSRS) / Yukon Albers", 4617, 4400, 19858); + return true; + case 1486: + record = new EpsgProjectedCrsRecord(3580, "NAD83 / NWT Lambert", 4269, 4400, 19857); + return true; + case 1487: + record = new EpsgProjectedCrsRecord(3581, "NAD83(CSRS) / NWT Lambert", 4617, 4400, 19857); + return true; + case 1488: + record = new EpsgProjectedCrsRecord(3582, "NAD83(NSRS2007) / Maryland (ftUS)", 4759, 4497, 15330); + return true; + case 1489: + record = new EpsgProjectedCrsRecord(3583, "NAD83(NSRS2007) / Massachusetts Island", 4759, 4499, 12032); + return true; + case 1490: + record = new EpsgProjectedCrsRecord(3584, "NAD83(NSRS2007) / Massachusetts Island (ftUS)", 4759, 4497, 15332); + return true; + case 1491: + record = new EpsgProjectedCrsRecord(3585, "NAD83(NSRS2007) / Massachusetts Mainland", 4759, 4499, 12031); + return true; + case 1492: + record = new EpsgProjectedCrsRecord(3586, "NAD83(NSRS2007) / Massachusetts Mainland (ftUS)", 4759, 4497, 15331); + return true; + case 1493: + record = new EpsgProjectedCrsRecord(3587, "NAD83(NSRS2007) / Michigan Central", 4759, 4499, 12142); + return true; + case 1494: + record = new EpsgProjectedCrsRecord(3588, "NAD83(NSRS2007) / Michigan Central (ft)", 4759, 4495, 15334); + return true; + case 1495: + record = new EpsgProjectedCrsRecord(3589, "NAD83(NSRS2007) / Michigan North", 4759, 4499, 12141); + return true; + case 1496: + record = new EpsgProjectedCrsRecord(3590, "NAD83(NSRS2007) / Michigan North (ft)", 4759, 4495, 15333); + return true; + case 1497: + record = new EpsgProjectedCrsRecord(3591, "NAD83(NSRS2007) / Michigan Oblique Mercator", 4759, 4499, 12150); + return true; + case 1498: + record = new EpsgProjectedCrsRecord(3592, "NAD83(NSRS2007) / Michigan South", 4759, 4499, 12143); + return true; + case 1499: + record = new EpsgProjectedCrsRecord(3593, "NAD83(NSRS2007) / Michigan South (ft)", 4759, 4495, 15335); + return true; + case 1500: + record = new EpsgProjectedCrsRecord(3594, "NAD83(NSRS2007) / Minnesota Central", 4759, 4499, 12232); + return true; + case 1501: + record = new EpsgProjectedCrsRecord(3595, "NAD83(NSRS2007) / Minnesota North", 4759, 4499, 12231); + return true; + case 1502: + record = new EpsgProjectedCrsRecord(3596, "NAD83(NSRS2007) / Minnesota South", 4759, 4499, 12233); + return true; + case 1503: + record = new EpsgProjectedCrsRecord(3597, "NAD83(NSRS2007) / Mississippi East", 4759, 4499, 12331); + return true; + case 1504: + record = new EpsgProjectedCrsRecord(3598, "NAD83(NSRS2007) / Mississippi East (ftUS)", 4759, 4497, 15336); + return true; + case 1505: + record = new EpsgProjectedCrsRecord(3599, "NAD83(NSRS2007) / Mississippi West", 4759, 4499, 12332); + return true; + case 1506: + record = new EpsgProjectedCrsRecord(3600, "NAD83(NSRS2007) / Mississippi West (ftUS)", 4759, 4497, 15337); + return true; + case 1507: + record = new EpsgProjectedCrsRecord(3601, "NAD83(NSRS2007) / Missouri Central", 4759, 4499, 12432); + return true; + case 1508: + record = new EpsgProjectedCrsRecord(3602, "NAD83(NSRS2007) / Missouri East", 4759, 4499, 12431); + return true; + case 1509: + record = new EpsgProjectedCrsRecord(3603, "NAD83(NSRS2007) / Missouri West", 4759, 4499, 12433); + return true; + case 1510: + record = new EpsgProjectedCrsRecord(3604, "NAD83(NSRS2007) / Montana", 4759, 4499, 12530); + return true; + case 1511: + record = new EpsgProjectedCrsRecord(3605, "NAD83(NSRS2007) / Montana (ft)", 4759, 4495, 15338); + return true; + case 1512: + record = new EpsgProjectedCrsRecord(3606, "NAD83(NSRS2007) / Nebraska", 4759, 4499, 12630); + return true; + case 1513: + record = new EpsgProjectedCrsRecord(3607, "NAD83(NSRS2007) / Nevada Central", 4759, 4499, 12732); + return true; + case 1514: + record = new EpsgProjectedCrsRecord(3608, "NAD83(NSRS2007) / Nevada Central (ftUS)", 4759, 4497, 15382); + return true; + case 1515: + record = new EpsgProjectedCrsRecord(3609, "NAD83(NSRS2007) / Nevada East", 4759, 4499, 12731); + return true; + case 1516: + record = new EpsgProjectedCrsRecord(3610, "NAD83(NSRS2007) / Nevada East (ftUS)", 4759, 4497, 15381); + return true; + case 1517: + record = new EpsgProjectedCrsRecord(3611, "NAD83(NSRS2007) / Nevada West", 4759, 4499, 12733); + return true; + case 1518: + record = new EpsgProjectedCrsRecord(3612, "NAD83(NSRS2007) / Nevada West (ftUS)", 4759, 4497, 15383); + return true; + case 1519: + record = new EpsgProjectedCrsRecord(3613, "NAD83(NSRS2007) / New Hampshire", 4759, 4499, 12830); + return true; + case 1520: + record = new EpsgProjectedCrsRecord(3614, "NAD83(NSRS2007) / New Hampshire (ftUS)", 4759, 4497, 15389); + return true; + case 1521: + record = new EpsgProjectedCrsRecord(3615, "NAD83(NSRS2007) / New Jersey", 4759, 4499, 12930); + return true; + case 1522: + record = new EpsgProjectedCrsRecord(3616, "NAD83(NSRS2007) / New Jersey (ftUS)", 4759, 4497, 15384); + return true; + case 1523: + record = new EpsgProjectedCrsRecord(3617, "NAD83(NSRS2007) / New Mexico Central", 4759, 4499, 13032); + return true; + case 1524: + record = new EpsgProjectedCrsRecord(3618, "NAD83(NSRS2007) / New Mexico Central (ftUS)", 4759, 4497, 15340); + return true; + case 1525: + record = new EpsgProjectedCrsRecord(3619, "NAD83(NSRS2007) / New Mexico East", 4759, 4499, 13031); + return true; + case 1526: + record = new EpsgProjectedCrsRecord(3620, "NAD83(NSRS2007) / New Mexico East (ftUS)", 4759, 4497, 15339); + return true; + case 1527: + record = new EpsgProjectedCrsRecord(3621, "NAD83(NSRS2007) / New Mexico West", 4759, 4499, 13033); + return true; + case 1528: + record = new EpsgProjectedCrsRecord(3622, "NAD83(NSRS2007) / New Mexico West (ftUS)", 4759, 4497, 15341); + return true; + case 1529: + record = new EpsgProjectedCrsRecord(3623, "NAD83(NSRS2007) / New York Central", 4759, 4499, 13132); + return true; + case 1530: + record = new EpsgProjectedCrsRecord(3624, "NAD83(NSRS2007) / New York Central (ftUS)", 4759, 4497, 15343); + return true; + case 1531: + record = new EpsgProjectedCrsRecord(3625, "NAD83(NSRS2007) / New York East", 4759, 4499, 13131); + return true; + case 1532: + record = new EpsgProjectedCrsRecord(3626, "NAD83(NSRS2007) / New York East (ftUS)", 4759, 4497, 15342); + return true; + case 1533: + record = new EpsgProjectedCrsRecord(3627, "NAD83(NSRS2007) / New York Long Island", 4759, 4499, 13134); + return true; + case 1534: + record = new EpsgProjectedCrsRecord(3628, "NAD83(NSRS2007) / New York Long Island (ftUS)", 4759, 4497, 15345); + return true; + case 1535: + record = new EpsgProjectedCrsRecord(3629, "NAD83(NSRS2007) / New York West", 4759, 4499, 13133); + return true; + case 1536: + record = new EpsgProjectedCrsRecord(3630, "NAD83(NSRS2007) / New York West (ftUS)", 4759, 4497, 15344); + return true; + case 1537: + record = new EpsgProjectedCrsRecord(3631, "NAD83(NSRS2007) / North Carolina", 4759, 4499, 13230); + return true; + case 1538: + record = new EpsgProjectedCrsRecord(3632, "NAD83(NSRS2007) / North Carolina (ftUS)", 4759, 4497, 15346); + return true; + case 1539: + record = new EpsgProjectedCrsRecord(3633, "NAD83(NSRS2007) / North Dakota North", 4759, 4499, 13331); + return true; + case 1540: + record = new EpsgProjectedCrsRecord(3634, "NAD83(NSRS2007) / North Dakota North (ft)", 4759, 4495, 15347); + return true; + case 1541: + record = new EpsgProjectedCrsRecord(3635, "NAD83(NSRS2007) / North Dakota South", 4759, 4499, 13332); + return true; + case 1542: + record = new EpsgProjectedCrsRecord(3636, "NAD83(NSRS2007) / North Dakota South (ft)", 4759, 4495, 15348); + return true; + case 1543: + record = new EpsgProjectedCrsRecord(3637, "NAD83(NSRS2007) / Ohio North", 4759, 4499, 13431); + return true; + case 1544: + record = new EpsgProjectedCrsRecord(3638, "NAD83(NSRS2007) / Ohio South", 4759, 4499, 13432); + return true; + case 1545: + record = new EpsgProjectedCrsRecord(3639, "NAD83(NSRS2007) / Oklahoma North", 4759, 4499, 13531); + return true; + case 1546: + record = new EpsgProjectedCrsRecord(3640, "NAD83(NSRS2007) / Oklahoma North (ftUS)", 4759, 4497, 15349); + return true; + case 1547: + record = new EpsgProjectedCrsRecord(3641, "NAD83(NSRS2007) / Oklahoma South", 4759, 4499, 13532); + return true; + case 1548: + record = new EpsgProjectedCrsRecord(3642, "NAD83(NSRS2007) / Oklahoma South (ftUS)", 4759, 4497, 15350); + return true; + case 1549: + record = new EpsgProjectedCrsRecord(3643, "NAD83(NSRS2007) / Oregon LCC (m)", 4759, 4499, 13633); + return true; + case 1550: + record = new EpsgProjectedCrsRecord(3644, "NAD83(NSRS2007) / Oregon GIC Lambert (ft)", 4759, 4495, 15374); + return true; + case 1551: + record = new EpsgProjectedCrsRecord(3645, "NAD83(NSRS2007) / Oregon North", 4759, 4499, 13631); + return true; + case 1552: + record = new EpsgProjectedCrsRecord(3646, "NAD83(NSRS2007) / Oregon North (ft)", 4759, 4495, 15351); + return true; + case 1553: + record = new EpsgProjectedCrsRecord(3647, "NAD83(NSRS2007) / Oregon South", 4759, 4499, 13632); + return true; + case 1554: + record = new EpsgProjectedCrsRecord(3648, "NAD83(NSRS2007) / Oregon South (ft)", 4759, 4495, 15352); + return true; + case 1555: + record = new EpsgProjectedCrsRecord(3649, "NAD83(NSRS2007) / Pennsylvania North", 4759, 4499, 13731); + return true; + case 1556: + record = new EpsgProjectedCrsRecord(3650, "NAD83(NSRS2007) / Pennsylvania North (ftUS)", 4759, 4497, 15353); + return true; + case 1557: + record = new EpsgProjectedCrsRecord(3651, "NAD83(NSRS2007) / Pennsylvania South", 4759, 4499, 13732); + return true; + case 1558: + record = new EpsgProjectedCrsRecord(3652, "NAD83(NSRS2007) / Pennsylvania South (ftUS)", 4759, 4497, 15354); + return true; + case 1559: + record = new EpsgProjectedCrsRecord(3653, "NAD83(NSRS2007) / Rhode Island", 4759, 4499, 13830); + return true; + case 1560: + record = new EpsgProjectedCrsRecord(3654, "NAD83(NSRS2007) / Rhode Island (ftUS)", 4759, 4497, 15390); + return true; + case 1561: + record = new EpsgProjectedCrsRecord(3655, "NAD83(NSRS2007) / South Carolina", 4759, 4499, 13930); + return true; + case 1562: + record = new EpsgProjectedCrsRecord(3656, "NAD83(NSRS2007) / South Carolina (ft)", 4759, 4495, 15355); + return true; + case 1563: + record = new EpsgProjectedCrsRecord(3657, "NAD83(NSRS2007) / South Dakota North", 4759, 4499, 14031); + return true; + case 1564: + record = new EpsgProjectedCrsRecord(3658, "NAD83(NSRS2007) / South Dakota North (ftUS)", 4759, 4497, 15394); + return true; + case 1565: + record = new EpsgProjectedCrsRecord(3659, "NAD83(NSRS2007) / South Dakota South", 4759, 4499, 14032); + return true; + case 1566: + record = new EpsgProjectedCrsRecord(3660, "NAD83(NSRS2007) / South Dakota South (ftUS)", 4759, 4497, 15395); + return true; + case 1567: + record = new EpsgProjectedCrsRecord(3661, "NAD83(NSRS2007) / Tennessee", 4759, 4499, 14130); + return true; + case 1568: + record = new EpsgProjectedCrsRecord(3662, "NAD83(NSRS2007) / Tennessee (ftUS)", 4759, 4497, 15356); + return true; + case 1569: + record = new EpsgProjectedCrsRecord(3663, "NAD83(NSRS2007) / Texas Central", 4759, 4499, 14233); + return true; + case 1570: + record = new EpsgProjectedCrsRecord(3664, "NAD83(NSRS2007) / Texas Central (ftUS)", 4759, 4497, 15359); + return true; + case 1571: + record = new EpsgProjectedCrsRecord(3665, "NAD83(NSRS2007) / Texas Centric Albers Equal Area", 4759, 4499, 14254); + return true; + case 1572: + record = new EpsgProjectedCrsRecord(3666, "NAD83(NSRS2007) / Texas Centric Lambert Conformal", 4759, 4499, 14253); + return true; + case 1573: + record = new EpsgProjectedCrsRecord(3667, "NAD83(NSRS2007) / Texas North", 4759, 4499, 14231); + return true; + case 1574: + record = new EpsgProjectedCrsRecord(3668, "NAD83(NSRS2007) / Texas North (ftUS)", 4759, 4497, 15357); + return true; + case 1575: + record = new EpsgProjectedCrsRecord(3669, "NAD83(NSRS2007) / Texas North Central", 4759, 4499, 14232); + return true; + case 1576: + record = new EpsgProjectedCrsRecord(3670, "NAD83(NSRS2007) / Texas North Central (ftUS)", 4759, 4497, 15358); + return true; + case 1577: + record = new EpsgProjectedCrsRecord(3671, "NAD83(NSRS2007) / Texas South", 4759, 4499, 14235); + return true; + case 1578: + record = new EpsgProjectedCrsRecord(3672, "NAD83(NSRS2007) / Texas South (ftUS)", 4759, 4497, 15361); + return true; + case 1579: + record = new EpsgProjectedCrsRecord(3673, "NAD83(NSRS2007) / Texas South Central", 4759, 4499, 14234); + return true; + case 1580: + record = new EpsgProjectedCrsRecord(3674, "NAD83(NSRS2007) / Texas South Central (ftUS)", 4759, 4497, 15360); + return true; + case 1581: + record = new EpsgProjectedCrsRecord(3675, "NAD83(NSRS2007) / Utah Central", 4759, 4499, 14332); + return true; + case 1582: + record = new EpsgProjectedCrsRecord(3676, "NAD83(NSRS2007) / Utah Central (ft)", 4759, 4495, 15363); + return true; + case 1583: + record = new EpsgProjectedCrsRecord(3677, "NAD83(NSRS2007) / Utah Central (ftUS)", 4759, 4497, 15298); + return true; + case 1584: + record = new EpsgProjectedCrsRecord(3678, "NAD83(NSRS2007) / Utah North", 4759, 4499, 14331); + return true; + case 1585: + record = new EpsgProjectedCrsRecord(3679, "NAD83(NSRS2007) / Utah North (ft)", 4759, 4495, 15362); + return true; + case 1586: + record = new EpsgProjectedCrsRecord(3680, "NAD83(NSRS2007) / Utah North (ftUS)", 4759, 4497, 15297); + return true; + case 1587: + record = new EpsgProjectedCrsRecord(3681, "NAD83(NSRS2007) / Utah South", 4759, 4499, 14333); + return true; + case 1588: + record = new EpsgProjectedCrsRecord(3682, "NAD83(NSRS2007) / Utah South (ft)", 4759, 4495, 15364); + return true; + case 1589: + record = new EpsgProjectedCrsRecord(3683, "NAD83(NSRS2007) / Utah South (ftUS)", 4759, 4497, 15299); + return true; + case 1590: + record = new EpsgProjectedCrsRecord(3684, "NAD83(NSRS2007) / Vermont", 4759, 4499, 14430); + return true; + case 1591: + record = new EpsgProjectedCrsRecord(3685, "NAD83(NSRS2007) / Virginia North", 4759, 4499, 14531); + return true; + case 1592: + record = new EpsgProjectedCrsRecord(3686, "NAD83(NSRS2007) / Virginia North (ftUS)", 4759, 4497, 15365); + return true; + case 1593: + record = new EpsgProjectedCrsRecord(3687, "NAD83(NSRS2007) / Virginia South", 4759, 4499, 14532); + return true; + case 1594: + record = new EpsgProjectedCrsRecord(3688, "NAD83(NSRS2007) / Virginia South (ftUS)", 4759, 4497, 15366); + return true; + case 1595: + record = new EpsgProjectedCrsRecord(3689, "NAD83(NSRS2007) / Washington North", 4759, 4499, 14631); + return true; + case 1596: + record = new EpsgProjectedCrsRecord(3690, "NAD83(NSRS2007) / Washington North (ftUS)", 4759, 4497, 15367); + return true; + case 1597: + record = new EpsgProjectedCrsRecord(3691, "NAD83(NSRS2007) / Washington South", 4759, 4499, 14632); + return true; + case 1598: + record = new EpsgProjectedCrsRecord(3692, "NAD83(NSRS2007) / Washington South (ftUS)", 4759, 4497, 15368); + return true; + case 1599: + record = new EpsgProjectedCrsRecord(3693, "NAD83(NSRS2007) / West Virginia North", 4759, 4499, 14731); + return true; + case 1600: + record = new EpsgProjectedCrsRecord(3694, "NAD83(NSRS2007) / West Virginia South", 4759, 4499, 14732); + return true; + case 1601: + record = new EpsgProjectedCrsRecord(3695, "NAD83(NSRS2007) / Wisconsin Central", 4759, 4499, 14832); + return true; + case 1602: + record = new EpsgProjectedCrsRecord(3696, "NAD83(NSRS2007) / Wisconsin Central (ftUS)", 4759, 4497, 15370); + return true; + case 1603: + record = new EpsgProjectedCrsRecord(3697, "NAD83(NSRS2007) / Wisconsin North", 4759, 4499, 14831); + return true; + case 1604: + record = new EpsgProjectedCrsRecord(3698, "NAD83(NSRS2007) / Wisconsin North (ftUS)", 4759, 4497, 15369); + return true; + case 1605: + record = new EpsgProjectedCrsRecord(3699, "NAD83(NSRS2007) / Wisconsin South", 4759, 4499, 14833); + return true; + case 1606: + record = new EpsgProjectedCrsRecord(3700, "NAD83(NSRS2007) / Wisconsin South (ftUS)", 4759, 4497, 15371); + return true; + case 1607: + record = new EpsgProjectedCrsRecord(3701, "NAD83(NSRS2007) / Wisconsin Transverse Mercator", 4759, 4499, 14841); + return true; + case 1608: + record = new EpsgProjectedCrsRecord(3702, "NAD83(NSRS2007) / Wyoming East", 4759, 4499, 14931); + return true; + case 1609: + record = new EpsgProjectedCrsRecord(3703, "NAD83(NSRS2007) / Wyoming East Central", 4759, 4499, 14932); + return true; + case 1610: + record = new EpsgProjectedCrsRecord(3704, "NAD83(NSRS2007) / Wyoming West Central", 4759, 4499, 14933); + return true; + case 1611: + record = new EpsgProjectedCrsRecord(3705, "NAD83(NSRS2007) / Wyoming West", 4759, 4499, 14934); + return true; + case 1612: + record = new EpsgProjectedCrsRecord(3706, "NAD83(NSRS2007) / UTM zone 59N", 4759, 4400, 16059); + return true; + case 1613: + record = new EpsgProjectedCrsRecord(3707, "NAD83(NSRS2007) / UTM zone 60N", 4759, 4400, 16060); + return true; + case 1614: + record = new EpsgProjectedCrsRecord(3708, "NAD83(NSRS2007) / UTM zone 1N", 4759, 4400, 16001); + return true; + case 1615: + record = new EpsgProjectedCrsRecord(3709, "NAD83(NSRS2007) / UTM zone 2N", 4759, 4400, 16002); + return true; + case 1616: + record = new EpsgProjectedCrsRecord(3710, "NAD83(NSRS2007) / UTM zone 3N", 4759, 4400, 16003); + return true; + case 1617: + record = new EpsgProjectedCrsRecord(3711, "NAD83(NSRS2007) / UTM zone 4N", 4759, 4400, 16004); + return true; + case 1618: + record = new EpsgProjectedCrsRecord(3712, "NAD83(NSRS2007) / UTM zone 5N", 4759, 4400, 16005); + return true; + case 1619: + record = new EpsgProjectedCrsRecord(3713, "NAD83(NSRS2007) / UTM zone 6N", 4759, 4400, 16006); + return true; + case 1620: + record = new EpsgProjectedCrsRecord(3714, "NAD83(NSRS2007) / UTM zone 7N", 4759, 4400, 16007); + return true; + case 1621: + record = new EpsgProjectedCrsRecord(3715, "NAD83(NSRS2007) / UTM zone 8N", 4759, 4400, 16008); + return true; + case 1622: + record = new EpsgProjectedCrsRecord(3716, "NAD83(NSRS2007) / UTM zone 9N", 4759, 4400, 16009); + return true; + case 1623: + record = new EpsgProjectedCrsRecord(3717, "NAD83(NSRS2007) / UTM zone 10N", 4759, 4400, 16010); + return true; + case 1624: + record = new EpsgProjectedCrsRecord(3718, "NAD83(NSRS2007) / UTM zone 11N", 4759, 4400, 16011); + return true; + case 1625: + record = new EpsgProjectedCrsRecord(3719, "NAD83(NSRS2007) / UTM zone 12N", 4759, 4400, 16012); + return true; + case 1626: + record = new EpsgProjectedCrsRecord(3720, "NAD83(NSRS2007) / UTM zone 13N", 4759, 4400, 16013); + return true; + case 1627: + record = new EpsgProjectedCrsRecord(3721, "NAD83(NSRS2007) / UTM zone 14N", 4759, 4400, 16014); + return true; + case 1628: + record = new EpsgProjectedCrsRecord(3722, "NAD83(NSRS2007) / UTM zone 15N", 4759, 4400, 16015); + return true; + case 1629: + record = new EpsgProjectedCrsRecord(3723, "NAD83(NSRS2007) / UTM zone 16N", 4759, 4400, 16016); + return true; + case 1630: + record = new EpsgProjectedCrsRecord(3724, "NAD83(NSRS2007) / UTM zone 17N", 4759, 4400, 16017); + return true; + case 1631: + record = new EpsgProjectedCrsRecord(3725, "NAD83(NSRS2007) / UTM zone 18N", 4759, 4400, 16018); + return true; + case 1632: + record = new EpsgProjectedCrsRecord(3726, "NAD83(NSRS2007) / UTM zone 19N", 4759, 4400, 16019); + return true; + case 1633: + record = new EpsgProjectedCrsRecord(3727, "Reunion 1947 / TM Reunion", 4626, 4499, 19856); + return true; + case 1634: + record = new EpsgProjectedCrsRecord(3728, "NAD83(NSRS2007) / Ohio North (ftUS)", 4759, 4497, 13433); + return true; + case 1635: + record = new EpsgProjectedCrsRecord(3729, "NAD83(NSRS2007) / Ohio South (ftUS)", 4759, 4497, 13434); + return true; + case 1636: + record = new EpsgProjectedCrsRecord(3730, "NAD83(NSRS2007) / Wyoming East (ftUS)", 4759, 4497, 14935); + return true; + case 1637: + record = new EpsgProjectedCrsRecord(3731, "NAD83(NSRS2007) / Wyoming East Central (ftUS)", 4759, 4497, 14936); + return true; + case 1638: + record = new EpsgProjectedCrsRecord(3732, "NAD83(NSRS2007) / Wyoming West Central (ftUS)", 4759, 4497, 14937); + return true; + case 1639: + record = new EpsgProjectedCrsRecord(3733, "NAD83(NSRS2007) / Wyoming West (ftUS)", 4759, 4497, 14938); + return true; + case 1640: + record = new EpsgProjectedCrsRecord(3734, "NAD83 / Ohio North (ftUS)", 4269, 4497, 13433); + return true; + case 1641: + record = new EpsgProjectedCrsRecord(3735, "NAD83 / Ohio South (ftUS)", 4269, 4497, 13434); + return true; + case 1642: + record = new EpsgProjectedCrsRecord(3736, "NAD83 / Wyoming East (ftUS)", 4269, 4497, 14935); + return true; + case 1643: + record = new EpsgProjectedCrsRecord(3737, "NAD83 / Wyoming East Central (ftUS)", 4269, 4497, 14936); + return true; + case 1644: + record = new EpsgProjectedCrsRecord(3738, "NAD83 / Wyoming West Central (ftUS)", 4269, 4497, 14937); + return true; + case 1645: + record = new EpsgProjectedCrsRecord(3739, "NAD83 / Wyoming West (ftUS)", 4269, 4497, 14938); + return true; + case 1646: + record = new EpsgProjectedCrsRecord(3740, "NAD83(HARN) / UTM zone 10N", 4152, 4400, 16010); + return true; + case 1647: + record = new EpsgProjectedCrsRecord(3741, "NAD83(HARN) / UTM zone 11N", 4152, 4400, 16011); + return true; + case 1648: + record = new EpsgProjectedCrsRecord(3742, "NAD83(HARN) / UTM zone 12N", 4152, 4400, 16012); + return true; + case 1649: + record = new EpsgProjectedCrsRecord(3743, "NAD83(HARN) / UTM zone 13N", 4152, 4400, 16013); + return true; + case 1650: + record = new EpsgProjectedCrsRecord(3744, "NAD83(HARN) / UTM zone 14N", 4152, 4400, 16014); + return true; + case 1651: + record = new EpsgProjectedCrsRecord(3745, "NAD83(HARN) / UTM zone 15N", 4152, 4400, 16015); + return true; + case 1652: + record = new EpsgProjectedCrsRecord(3746, "NAD83(HARN) / UTM zone 16N", 4152, 4400, 16016); + return true; + case 1653: + record = new EpsgProjectedCrsRecord(3747, "NAD83(HARN) / UTM zone 17N", 4152, 4400, 16017); + return true; + case 1654: + record = new EpsgProjectedCrsRecord(3748, "NAD83(HARN) / UTM zone 18N", 4152, 4400, 16018); + return true; + case 1655: + record = new EpsgProjectedCrsRecord(3749, "NAD83(HARN) / UTM zone 19N", 4152, 4400, 16019); + return true; + case 1656: + record = new EpsgProjectedCrsRecord(3750, "NAD83(HARN) / UTM zone 4N", 4152, 4400, 16004); + return true; + case 1657: + record = new EpsgProjectedCrsRecord(3751, "NAD83(HARN) / UTM zone 5N", 4152, 4400, 16005); + return true; + case 1658: + record = new EpsgProjectedCrsRecord(3753, "NAD83(HARN) / Ohio North (ftUS)", 4152, 4497, 13433); + return true; + case 1659: + record = new EpsgProjectedCrsRecord(3754, "NAD83(HARN) / Ohio South (ftUS)", 4152, 4497, 13434); + return true; + case 1660: + record = new EpsgProjectedCrsRecord(3755, "NAD83(HARN) / Wyoming East (ftUS)", 4152, 4497, 14935); + return true; + case 1661: + record = new EpsgProjectedCrsRecord(3756, "NAD83(HARN) / Wyoming East Central (ftUS)", 4152, 4497, 14936); + return true; + case 1662: + record = new EpsgProjectedCrsRecord(3757, "NAD83(HARN) / Wyoming West Central (ftUS)", 4152, 4497, 14937); + return true; + case 1663: + record = new EpsgProjectedCrsRecord(3758, "NAD83(HARN) / Wyoming West (ftUS)", 4152, 4497, 14938); + return true; + case 1664: + record = new EpsgProjectedCrsRecord(3759, "NAD83 / Hawaii zone 3 (ftUS)", 4269, 4497, 15138); + return true; + case 1665: + record = new EpsgProjectedCrsRecord(3760, "NAD83(HARN) / Hawaii zone 3 (ftUS)", 4152, 4497, 15138); + return true; + case 1666: + record = new EpsgProjectedCrsRecord(3761, "NAD83(CSRS) / UTM zone 22N", 4617, 4400, 16022); + return true; + case 1667: + record = new EpsgProjectedCrsRecord(3762, "WGS 84 / South Georgia Lambert", 4326, 4400, 19854); + return true; + case 1668: + record = new EpsgProjectedCrsRecord(3763, "ETRS89-PRT [1995] / Portugal TM06", 11108, 4499, 19853); + return true; + case 1669: + record = new EpsgProjectedCrsRecord(3764, "NZGD2000 / Chatham Island Circuit 2000", 4167, 4500, 17959); + return true; + case 1670: + record = new EpsgProjectedCrsRecord(3765, "ETRS89-HRV [HTRS96] / Croatia TM", 4761, 4400, 19851); + return true; + case 1671: + record = new EpsgProjectedCrsRecord(3766, "ETRS89-HRV [HTRS96] / Croatia LCC", 4761, 4400, 19852); + return true; + case 1672: + record = new EpsgProjectedCrsRecord(3767, "ETRS89-HRV [HTRS96] / UTM zone 33N", 4761, 4400, 16033); + return true; + case 1673: + record = new EpsgProjectedCrsRecord(3768, "ETRS89-HRV [HTRS96] / UTM zone 34N", 4761, 4400, 16034); + return true; + case 1674: + record = new EpsgProjectedCrsRecord(3769, "Bermuda 1957 / UTM zone 20N", 4216, 4400, 16020); + return true; + case 1675: + record = new EpsgProjectedCrsRecord(3770, "BDA2000 / Bermuda 2000 National Grid", 4762, 4400, 19849); + return true; + case 1676: + record = new EpsgProjectedCrsRecord(3771, "NAD27 / Alberta 3TM ref merid 111 W", 4267, 4400, 17722); + return true; + case 1677: + record = new EpsgProjectedCrsRecord(3772, "NAD27 / Alberta 3TM ref merid 114 W", 4267, 4400, 17723); + return true; + case 1678: + record = new EpsgProjectedCrsRecord(3773, "NAD27 / Alberta 3TM ref merid 117 W", 4267, 4400, 17724); + return true; + case 1679: + record = new EpsgProjectedCrsRecord(3775, "NAD83 / Alberta 3TM ref merid 111 W", 4269, 4400, 17722); + return true; + case 1680: + record = new EpsgProjectedCrsRecord(3776, "NAD83 / Alberta 3TM ref merid 114 W", 4269, 4400, 17723); + return true; + case 1681: + record = new EpsgProjectedCrsRecord(3777, "NAD83 / Alberta 3TM ref merid 117 W", 4269, 4400, 17724); + return true; + case 1682: + record = new EpsgProjectedCrsRecord(3779, "NAD83(CSRS) / Alberta 3TM ref merid 111 W", 4617, 4400, 17722); + return true; + case 1683: + record = new EpsgProjectedCrsRecord(3780, "NAD83(CSRS) / Alberta 3TM ref merid 114 W", 4617, 4400, 17723); + return true; + case 1684: + record = new EpsgProjectedCrsRecord(3781, "NAD83(CSRS) / Alberta 3TM ref merid 117 W", 4617, 4400, 17724); + return true; + case 1685: + record = new EpsgProjectedCrsRecord(3783, "Pitcairn 2006 / Pitcairn TM 2006", 4763, 4400, 19848); + return true; + case 1686: + record = new EpsgProjectedCrsRecord(3784, "Pitcairn 1967 / UTM zone 9S", 4729, 4400, 16109); + return true; + case 1687: + record = new EpsgProjectedCrsRecord(3788, "NZGD2000 / Auckland Islands TM 2000", 4167, 4500, 17960); + return true; + case 1688: + record = new EpsgProjectedCrsRecord(3789, "NZGD2000 / Campbell Island TM 2000", 4167, 4500, 17961); + return true; + case 1689: + record = new EpsgProjectedCrsRecord(3790, "NZGD2000 / Antipodes Islands TM 2000", 4167, 4500, 17962); + return true; + case 1690: + record = new EpsgProjectedCrsRecord(3791, "NZGD2000 / Raoul Island TM 2000", 4167, 4500, 17963); + return true; + case 1691: + record = new EpsgProjectedCrsRecord(3793, "NZGD2000 / Chatham Islands TM 2000", 4167, 4500, 17965); + return true; + case 1692: + record = new EpsgProjectedCrsRecord(3794, "ETRS89-SVN [D96] / Slovene National Grid", 4765, 4400, 19845); + return true; + case 1693: + record = new EpsgProjectedCrsRecord(3795, "NAD27 / Cuba Norte", 4267, 4532, 18063); + return true; + case 1694: + record = new EpsgProjectedCrsRecord(3796, "NAD27 / Cuba Sur", 4267, 4532, 18064); + return true; + case 1695: + record = new EpsgProjectedCrsRecord(3797, "NAD27 / MTQ Lambert", 4267, 4499, 19844); + return true; + case 1696: + record = new EpsgProjectedCrsRecord(3798, "NAD83 / MTQ Lambert", 4269, 4499, 19844); + return true; + case 1697: + record = new EpsgProjectedCrsRecord(3799, "NAD83(CSRS) / MTQ Lambert", 4617, 4499, 19844); + return true; + case 1698: + record = new EpsgProjectedCrsRecord(3800, "NAD27 / Alberta 3TM ref merid 120 W", 4267, 4400, 17726); + return true; + case 1699: + record = new EpsgProjectedCrsRecord(3801, "NAD83 / Alberta 3TM ref merid 120 W", 4269, 4400, 17726); + return true; + case 1700: + record = new EpsgProjectedCrsRecord(3802, "NAD83(CSRS) / Alberta 3TM ref merid 120 W", 4617, 4400, 17726); + return true; + case 1701: + record = new EpsgProjectedCrsRecord(3812, "ETRS89-BEL [BEREF2002] / Belgian Lambert 2008", 11063, 4499, 3811); + return true; + case 1702: + record = new EpsgProjectedCrsRecord(3814, "NAD83 / Mississippi TM", 4269, 4499, 3813); + return true; + case 1703: + record = new EpsgProjectedCrsRecord(3815, "NAD83(HARN) / Mississippi TM", 4152, 4499, 3813); + return true; + case 1704: + record = new EpsgProjectedCrsRecord(3816, "NAD83(NSRS2007) / Mississippi TM", 4759, 4499, 3813); + return true; + case 1705: + record = new EpsgProjectedCrsRecord(3825, "TWD97 / TM2 zone 119", 3824, 4499, 3818); + return true; + case 1706: + record = new EpsgProjectedCrsRecord(3826, "TWD97 / TM2 zone 121", 3824, 4499, 3820); + return true; + case 1707: + record = new EpsgProjectedCrsRecord(3827, "TWD67 / TM2 zone 119", 3821, 4499, 3818); + return true; + case 1708: + record = new EpsgProjectedCrsRecord(3828, "TWD67 / TM2 zone 121", 3821, 4499, 3820); + return true; + case 1709: + record = new EpsgProjectedCrsRecord(3829, "Hu Tzu Shan 1950 / UTM zone 51N", 4236, 4400, 16051); + return true; + case 1710: + record = new EpsgProjectedCrsRecord(3832, "WGS 84 / PDC Mercator", 4326, 4400, 3831); + return true; + case 1711: + record = new EpsgProjectedCrsRecord(3833, "Pulkovo 1942(58) / Gauss-Kruger zone 2", 4179, 4530, 16202); + return true; + case 1712: + record = new EpsgProjectedCrsRecord(3834, "Pulkovo 1942(83) / Gauss-Kruger zone 2", 4178, 4530, 16202); + return true; + case 1713: + record = new EpsgProjectedCrsRecord(3835, "Pulkovo 1942(83) / Gauss-Kruger zone 3", 4178, 4530, 16203); + return true; + case 1714: + record = new EpsgProjectedCrsRecord(3836, "Pulkovo 1942(83) / Gauss-Kruger zone 4", 4178, 4530, 16204); + return true; + case 1715: + record = new EpsgProjectedCrsRecord(3837, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 3", 4179, 4530, 16263); + return true; + case 1716: + record = new EpsgProjectedCrsRecord(3838, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 4", 4179, 4530, 16264); + return true; + case 1717: + record = new EpsgProjectedCrsRecord(3839, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 9", 4179, 4530, 16269); + return true; + case 1718: + record = new EpsgProjectedCrsRecord(3840, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 10", 4179, 4530, 16270); + return true; + case 1719: + record = new EpsgProjectedCrsRecord(3841, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 6", 4178, 4530, 16266); + return true; + case 1720: + record = new EpsgProjectedCrsRecord(3844, "Pulkovo 1942(58) / Stereo70", 4179, 4530, 19926); + return true; + case 1721: + record = new EpsgProjectedCrsRecord(3845, "ETRS89-SWE [SWEREF 99] / RT90 7.5 gon V emulation", 4619, 4530, 17339); + return true; + case 1722: + record = new EpsgProjectedCrsRecord(3846, "ETRS89-SWE [SWEREF 99] / RT90 5 gon V emulation", 4619, 4530, 17340); + return true; + case 1723: + record = new EpsgProjectedCrsRecord(3847, "ETRS89-SWE [SWEREF 99] / RT90 2.5 gon V emulation", 4619, 4530, 17341); + return true; + case 1724: + record = new EpsgProjectedCrsRecord(3848, "ETRS89-SWE [SWEREF 99] / RT90 0 gon emulation", 4619, 4530, 17342); + return true; + case 1725: + record = new EpsgProjectedCrsRecord(3849, "ETRS89-SWE [SWEREF 99] / RT90 2.5 gon O emulation", 4619, 4530, 17343); + return true; + case 1726: + record = new EpsgProjectedCrsRecord(3850, "ETRS89-SWE [SWEREF 99] / RT90 5 gon O emulation", 4619, 4530, 17344); + return true; + case 1727: + record = new EpsgProjectedCrsRecord(3851, "NZGD2000 / NZCS2000", 4167, 4500, 17964); + return true; + case 1728: + record = new EpsgProjectedCrsRecord(3852, "RSRGD2000 / DGLC2000", 4764, 4500, 17966); + return true; + case 1729: + record = new EpsgProjectedCrsRecord(3854, "County ST74", 4619, 4531, 3853); + return true; + case 1730: + record = new EpsgProjectedCrsRecord(3857, "WGS 84 / Pseudo-Mercator", 4326, 4499, 3856); + return true; + case 1731: + record = new EpsgProjectedCrsRecord(3873, "ETRS89-FIN [EUREF-FIN] / GK19FIN", 10690, 4500, 3860); + return true; + case 1732: + record = new EpsgProjectedCrsRecord(3874, "ETRS89-FIN [EUREF-FIN] / GK20FIN", 10690, 4500, 3861); + return true; + case 1733: + record = new EpsgProjectedCrsRecord(3875, "ETRS89-FIN [EUREF-FIN] / GK21FIN", 10690, 4500, 3862); + return true; + case 1734: + record = new EpsgProjectedCrsRecord(3876, "ETRS89-FIN [EUREF-FIN] / GK22FIN", 10690, 4500, 3863); + return true; + case 1735: + record = new EpsgProjectedCrsRecord(3877, "ETRS89-FIN [EUREF-FIN] / GK23FIN", 10690, 4500, 3864); + return true; + case 1736: + record = new EpsgProjectedCrsRecord(3878, "ETRS89-FIN [EUREF-FIN] / GK24FIN", 10690, 4500, 3865); + return true; + case 1737: + record = new EpsgProjectedCrsRecord(3879, "ETRS89-FIN [EUREF-FIN] / GK25FIN", 10690, 4500, 3866); + return true; + case 1738: + record = new EpsgProjectedCrsRecord(3880, "ETRS89-FIN [EUREF-FIN] / GK26FIN", 10690, 4500, 3867); + return true; + case 1739: + record = new EpsgProjectedCrsRecord(3881, "ETRS89-FIN [EUREF-FIN] / GK27FIN", 10690, 4500, 3868); + return true; + case 1740: + record = new EpsgProjectedCrsRecord(3882, "ETRS89-FIN [EUREF-FIN] / GK28FIN", 10690, 4500, 3869); + return true; + case 1741: + record = new EpsgProjectedCrsRecord(3883, "ETRS89-FIN [EUREF-FIN] / GK29FIN", 10690, 4500, 3870); + return true; + case 1742: + record = new EpsgProjectedCrsRecord(3884, "ETRS89-FIN [EUREF-FIN] / GK30FIN", 10690, 4500, 3871); + return true; + case 1743: + record = new EpsgProjectedCrsRecord(3885, "ETRS89-FIN [EUREF-FIN] / GK31FIN", 10690, 4500, 3872); + return true; + case 1744: + record = new EpsgProjectedCrsRecord(3890, "IGRS / UTM zone 37N", 3889, 4400, 16037); + return true; + case 1745: + record = new EpsgProjectedCrsRecord(3891, "IGRS / UTM zone 38N", 3889, 4400, 16038); + return true; + case 1746: + record = new EpsgProjectedCrsRecord(3892, "IGRS / UTM zone 39N", 3889, 4400, 16039); + return true; + case 1747: + record = new EpsgProjectedCrsRecord(3893, "ED50 / Iraq National Grid", 4230, 4400, 19907); + return true; + case 1748: + record = new EpsgProjectedCrsRecord(3912, "MGI 1901 / Slovene National Grid", 3906, 4498, 19845); + return true; + case 1749: + record = new EpsgProjectedCrsRecord(3920, "Puerto Rico / UTM zone 20N", 4139, 4400, 16020); + return true; + case 1750: + record = new EpsgProjectedCrsRecord(3942, "ETRS89-FRA [RGF93 v1] / CC42", 4171, 4499, 18101); + return true; + case 1751: + record = new EpsgProjectedCrsRecord(3943, "ETRS89-FRA [RGF93 v1] / CC43", 4171, 4499, 18102); + return true; + case 1752: + record = new EpsgProjectedCrsRecord(3944, "ETRS89-FRA [RGF93 v1] / CC44", 4171, 4499, 18103); + return true; + case 1753: + record = new EpsgProjectedCrsRecord(3945, "ETRS89-FRA [RGF93 v1] / CC45", 4171, 4499, 18104); + return true; + case 1754: + record = new EpsgProjectedCrsRecord(3946, "ETRS89-FRA [RGF93 v1] / CC46", 4171, 4499, 18105); + return true; + case 1755: + record = new EpsgProjectedCrsRecord(3947, "ETRS89-FRA [RGF93 v1] / CC47", 4171, 4499, 18106); + return true; + case 1756: + record = new EpsgProjectedCrsRecord(3948, "ETRS89-FRA [RGF93 v1] / CC48", 4171, 4499, 18107); + return true; + case 1757: + record = new EpsgProjectedCrsRecord(3949, "ETRS89-FRA [RGF93 v1] / CC49", 4171, 4499, 18108); + return true; + case 1758: + record = new EpsgProjectedCrsRecord(3950, "ETRS89-FRA [RGF93 v1] / CC50", 4171, 4499, 18109); + return true; + case 1759: + record = new EpsgProjectedCrsRecord(3968, "NAD83 / Virginia Lambert", 4269, 4499, 3967); + return true; + case 1760: + record = new EpsgProjectedCrsRecord(3969, "NAD83(HARN) / Virginia Lambert", 4152, 4499, 3967); + return true; + case 1761: + record = new EpsgProjectedCrsRecord(3970, "NAD83(NSRS2007) / Virginia Lambert", 4759, 4499, 3967); + return true; + case 1762: + record = new EpsgProjectedCrsRecord(3976, "WGS 84 / NSIDC Sea Ice Polar Stereographic South", 4326, 4470, 19866); + return true; + case 1763: + record = new EpsgProjectedCrsRecord(3978, "NAD83 / Canada Atlas Lambert", 4269, 4400, 3977); + return true; + case 1764: + record = new EpsgProjectedCrsRecord(3979, "NAD83(CSRS) / Canada Atlas Lambert", 4617, 4400, 3977); + return true; + case 1765: + record = new EpsgProjectedCrsRecord(3986, "Katanga 1955 / Katanga Gauss zone A", 4695, 4499, 3981); + return true; + case 1766: + record = new EpsgProjectedCrsRecord(3987, "Katanga 1955 / Katanga Gauss zone B", 4695, 4499, 3982); + return true; + case 1767: + record = new EpsgProjectedCrsRecord(3988, "Katanga 1955 / Katanga Gauss zone C", 4695, 4499, 3983); + return true; + case 1768: + record = new EpsgProjectedCrsRecord(3989, "Katanga 1955 / Katanga Gauss zone D", 4695, 4499, 3984); + return true; + case 1769: + record = new EpsgProjectedCrsRecord(3991, "Puerto Rico State Plane CS of 1927", 4139, 4497, 15201); + return true; + case 1770: + record = new EpsgProjectedCrsRecord(3992, "Puerto Rico / St. Croix", 4139, 4497, 15202); + return true; + case 1771: + record = new EpsgProjectedCrsRecord(3993, "Guam 1963 / Guam SPCS", 4675, 4499, 15400); + return true; + case 1772: + record = new EpsgProjectedCrsRecord(3994, "WGS 84 / Mercator 41", 4326, 4499, 19843); + return true; + case 1773: + record = new EpsgProjectedCrsRecord(3995, "WGS 84 / Arctic Polar Stereographic", 4326, 4469, 19842); + return true; + case 1774: + record = new EpsgProjectedCrsRecord(3996, "WGS 84 / IBCAO Polar Stereographic", 4326, 4469, 19840); + return true; + case 1775: + record = new EpsgProjectedCrsRecord(3997, "WGS 84 / Dubai Local TM", 4326, 4400, 19839); + return true; + case 1776: + record = new EpsgProjectedCrsRecord(4026, "ETRS89-MDA [MOLDREF99] / Moldova TM", 4023, 4530, 3999); + return true; + case 1777: + record = new EpsgProjectedCrsRecord(4037, "WGS 84 / TMzn35N", 4326, 4500, 16035); + return true; + case 1778: + record = new EpsgProjectedCrsRecord(4038, "WGS 84 / TMzn36N", 4326, 4500, 16036); + return true; + case 1779: + record = new EpsgProjectedCrsRecord(4048, "RGRDC 2005 / Congo TM zone 12", 4046, 4499, 17412); + return true; + case 1780: + record = new EpsgProjectedCrsRecord(4049, "RGRDC 2005 / Congo TM zone 14", 4046, 4499, 17414); + return true; + case 1781: + record = new EpsgProjectedCrsRecord(4050, "RGRDC 2005 / Congo TM zone 16", 4046, 4499, 17416); + return true; + case 1782: + record = new EpsgProjectedCrsRecord(4051, "RGRDC 2005 / Congo TM zone 18", 4046, 4499, 17418); + return true; + case 1783: + record = new EpsgProjectedCrsRecord(4056, "RGRDC 2005 / Congo TM zone 20", 4046, 4499, 17420); + return true; + case 1784: + record = new EpsgProjectedCrsRecord(4057, "RGRDC 2005 / Congo TM zone 22", 4046, 4499, 17422); + return true; + case 1785: + record = new EpsgProjectedCrsRecord(4058, "RGRDC 2005 / Congo TM zone 24", 4046, 4499, 17424); + return true; + case 1786: + record = new EpsgProjectedCrsRecord(4059, "RGRDC 2005 / Congo TM zone 26", 4046, 4499, 17426); + return true; + case 1787: + record = new EpsgProjectedCrsRecord(4060, "RGRDC 2005 / Congo TM zone 28", 4046, 4499, 17428); + return true; + case 1788: + record = new EpsgProjectedCrsRecord(4061, "RGRDC 2005 / UTM zone 33S", 4046, 4499, 16133); + return true; + case 1789: + record = new EpsgProjectedCrsRecord(4062, "RGRDC 2005 / UTM zone 34S", 4046, 4499, 16134); + return true; + case 1790: + record = new EpsgProjectedCrsRecord(4063, "RGRDC 2005 / UTM zone 35S", 4046, 4499, 16135); + return true; + case 1791: + record = new EpsgProjectedCrsRecord(4071, "Chua / UTM zone 23S", 4224, 4400, 16123); + return true; + case 1792: + record = new EpsgProjectedCrsRecord(4082, "REGCAN95 / UTM zone 27N", 4081, 4400, 16027); + return true; + case 1793: + record = new EpsgProjectedCrsRecord(4083, "REGCAN95 / UTM zone 28N", 4081, 4400, 16028); + return true; + case 1794: + record = new EpsgProjectedCrsRecord(4087, "WGS 84 / World Equidistant Cylindrical", 4326, 4499, 4085); + return true; + case 1795: + record = new EpsgProjectedCrsRecord(4093, "ETRS89 / DKTM1", 4258, 4400, 4089); + return true; + case 1796: + record = new EpsgProjectedCrsRecord(4094, "ETRS89 / DKTM2", 4258, 4400, 4090); + return true; + case 1797: + record = new EpsgProjectedCrsRecord(4095, "ETRS89 / DKTM3", 4258, 4400, 4091); + return true; + case 1798: + record = new EpsgProjectedCrsRecord(4096, "ETRS89 / DKTM4", 4258, 4400, 4092); + return true; + case 1799: + record = new EpsgProjectedCrsRecord(4217, "NAD83 / BLM 59N (ftUS)", 4269, 4497, 4186); + return true; + case 1800: + record = new EpsgProjectedCrsRecord(4390, "Kertau 1968 / Johor Grid", 4245, 4400, 4114); + return true; + case 1801: + record = new EpsgProjectedCrsRecord(4391, "Kertau 1968 / Sembilan and Melaka Grid", 4245, 4400, 4115); + return true; + case 1802: + record = new EpsgProjectedCrsRecord(4392, "Kertau 1968 / Pahang Grid", 4245, 4400, 4116); + return true; + case 1803: + record = new EpsgProjectedCrsRecord(4393, "Kertau 1968 / Selangor Grid", 4245, 4400, 4117); + return true; + case 1804: + record = new EpsgProjectedCrsRecord(4394, "Kertau 1968 / Terengganu Grid", 4245, 4400, 4177); + return true; + case 1805: + record = new EpsgProjectedCrsRecord(4395, "Kertau 1968 / Pinang Grid", 4245, 4400, 4305); + return true; + case 1806: + record = new EpsgProjectedCrsRecord(4396, "Kertau 1968 / Kedah and Perlis Grid", 4245, 4400, 4320); + return true; + case 1807: + record = new EpsgProjectedCrsRecord(4397, "Kertau 1968 / Perak Revised Grid", 4245, 4400, 4321); + return true; + case 1808: + record = new EpsgProjectedCrsRecord(4398, "Kertau 1968 / Kelantan Grid", 4245, 4400, 4323); + return true; + case 1809: + record = new EpsgProjectedCrsRecord(4399, "NAD27 / BLM 59N (ftUS)", 4267, 4497, 4186); + return true; + case 1810: + record = new EpsgProjectedCrsRecord(4400, "NAD27 / BLM 60N (ftUS)", 4267, 4497, 4187); + return true; + case 1811: + record = new EpsgProjectedCrsRecord(4401, "NAD27 / BLM 1N (ftUS)", 4267, 4497, 4101); + return true; + case 1812: + record = new EpsgProjectedCrsRecord(4402, "NAD27 / BLM 2N (ftUS)", 4267, 4497, 4102); + return true; + case 1813: + record = new EpsgProjectedCrsRecord(4403, "NAD27 / BLM 3N (ftUS)", 4267, 4497, 4103); + return true; + case 1814: + record = new EpsgProjectedCrsRecord(4404, "NAD27 / BLM 4N (ftUS)", 4267, 4497, 4104); + return true; + case 1815: + record = new EpsgProjectedCrsRecord(4405, "NAD27 / BLM 5N (ftUS)", 4267, 4497, 4105); + return true; + case 1816: + record = new EpsgProjectedCrsRecord(4406, "NAD27 / BLM 6N (ftUS)", 4267, 4497, 4106); + return true; + case 1817: + record = new EpsgProjectedCrsRecord(4407, "NAD27 / BLM 7N (ftUS)", 4267, 4497, 4107); + return true; + case 1818: + record = new EpsgProjectedCrsRecord(4408, "NAD27 / BLM 8N (ftUS)", 4267, 4497, 4108); + return true; + case 1819: + record = new EpsgProjectedCrsRecord(4409, "NAD27 / BLM 9N (ftUS)", 4267, 4497, 4109); + return true; + case 1820: + record = new EpsgProjectedCrsRecord(4410, "NAD27 / BLM 10N (ftUS)", 4267, 4497, 4110); + return true; + case 1821: + record = new EpsgProjectedCrsRecord(4411, "NAD27 / BLM 11N (ftUS)", 4267, 4497, 4111); + return true; + case 1822: + record = new EpsgProjectedCrsRecord(4412, "NAD27 / BLM 12N (ftUS)", 4267, 4497, 4112); + return true; + case 1823: + record = new EpsgProjectedCrsRecord(4413, "NAD27 / BLM 13N (ftUS)", 4267, 4497, 4113); + return true; + case 1824: + record = new EpsgProjectedCrsRecord(4414, "NAD83(HARN) / Guam Map Grid", 4152, 4499, 4325); + return true; + case 1825: + record = new EpsgProjectedCrsRecord(4415, "Katanga 1955 / Katanga Lambert", 4695, 4499, 4416); + return true; + case 1826: + record = new EpsgProjectedCrsRecord(4417, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 7", 4178, 4530, 16267); + return true; + case 1827: + record = new EpsgProjectedCrsRecord(4418, "NAD27 / BLM 18N (ftUS)", 4267, 4497, 4118); + return true; + case 1828: + record = new EpsgProjectedCrsRecord(4419, "NAD27 / BLM 19N (ftUS)", 4267, 4497, 4119); + return true; + case 1829: + record = new EpsgProjectedCrsRecord(4420, "NAD83 / BLM 60N (ftUS)", 4269, 4497, 4187); + return true; + case 1830: + record = new EpsgProjectedCrsRecord(4421, "NAD83 / BLM 1N (ftUS)", 4269, 4497, 4101); + return true; + case 1831: + record = new EpsgProjectedCrsRecord(4422, "NAD83 / BLM 2N (ftUS)", 4269, 4497, 4102); + return true; + case 1832: + record = new EpsgProjectedCrsRecord(4423, "NAD83 / BLM 3N (ftUS)", 4269, 4497, 4103); + return true; + case 1833: + record = new EpsgProjectedCrsRecord(4424, "NAD83 / BLM 4N (ftUS)", 4269, 4497, 4104); + return true; + case 1834: + record = new EpsgProjectedCrsRecord(4425, "NAD83 / BLM 5N (ftUS)", 4269, 4497, 4105); + return true; + case 1835: + record = new EpsgProjectedCrsRecord(4426, "NAD83 / BLM 6N (ftUS)", 4269, 4497, 4106); + return true; + case 1836: + record = new EpsgProjectedCrsRecord(4427, "NAD83 / BLM 7N (ftUS)", 4269, 4497, 4107); + return true; + case 1837: + record = new EpsgProjectedCrsRecord(4428, "NAD83 / BLM 8N (ftUS)", 4269, 4497, 4108); + return true; + case 1838: + record = new EpsgProjectedCrsRecord(4429, "NAD83 / BLM 9N (ftUS)", 4269, 4497, 4109); + return true; + case 1839: + record = new EpsgProjectedCrsRecord(4430, "NAD83 / BLM 10N (ftUS)", 4269, 4497, 4110); + return true; + case 1840: + record = new EpsgProjectedCrsRecord(4431, "NAD83 / BLM 11N (ftUS)", 4269, 4497, 4111); + return true; + case 1841: + record = new EpsgProjectedCrsRecord(4432, "NAD83 / BLM 12N (ftUS)", 4269, 4497, 4112); + return true; + case 1842: + record = new EpsgProjectedCrsRecord(4433, "NAD83 / BLM 13N (ftUS)", 4269, 4497, 4113); + return true; + case 1843: + record = new EpsgProjectedCrsRecord(4434, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 8", 4178, 4530, 16268); + return true; + case 1844: + record = new EpsgProjectedCrsRecord(4437, "NAD83(NSRS2007) / Puerto Rico and Virgin Is.", 4759, 4499, 15230); + return true; + case 1845: + record = new EpsgProjectedCrsRecord(4438, "NAD83 / BLM 18N (ftUS)", 4269, 4497, 4118); + return true; + case 1846: + record = new EpsgProjectedCrsRecord(4439, "NAD83 / BLM 19N (ftUS)", 4269, 4497, 4119); + return true; + case 1847: + record = new EpsgProjectedCrsRecord(4455, "NAD27 / Pennsylvania South", 4267, 4497, 4436); + return true; + case 1848: + record = new EpsgProjectedCrsRecord(4456, "NAD27 / New York Long Island", 4267, 4497, 4454); + return true; + case 1849: + record = new EpsgProjectedCrsRecord(4457, "NAD83 / South Dakota North (ftUS)", 4269, 4497, 15394); + return true; + case 1850: + record = new EpsgProjectedCrsRecord(4462, "WGS 84 / Australian Centre for Remote Sensing Lambert", 4326, 4400, 4460); + return true; + case 1851: + record = new EpsgProjectedCrsRecord(4467, "RGSPM06 / UTM zone 21N", 4463, 4400, 16021); + return true; + case 1852: + record = new EpsgProjectedCrsRecord(4471, "RGM04 / UTM zone 38S", 4470, 4400, 16138); + return true; + case 1853: + record = new EpsgProjectedCrsRecord(4484, "Mexico ITRF92 / UTM zone 11N", 4483, 4400, 16011); + return true; + case 1854: + record = new EpsgProjectedCrsRecord(4485, "Mexico ITRF92 / UTM zone 12N", 4483, 4400, 16012); + return true; + case 1855: + record = new EpsgProjectedCrsRecord(4486, "Mexico ITRF92 / UTM zone 13N", 4483, 4400, 16013); + return true; + case 1856: + record = new EpsgProjectedCrsRecord(4487, "Mexico ITRF92 / UTM zone 14N", 4483, 4400, 16014); + return true; + case 1857: + record = new EpsgProjectedCrsRecord(4488, "Mexico ITRF92 / UTM zone 15N", 4483, 4400, 16015); + return true; + case 1858: + record = new EpsgProjectedCrsRecord(4489, "Mexico ITRF92 / UTM zone 16N", 4483, 4400, 16016); + return true; + case 1859: + record = new EpsgProjectedCrsRecord(4491, "CGCS2000 / Gauss-Kruger zone 13", 4490, 4530, 16213); + return true; + case 1860: + record = new EpsgProjectedCrsRecord(4492, "CGCS2000 / Gauss-Kruger zone 14", 4490, 4530, 16214); + return true; + case 1861: + record = new EpsgProjectedCrsRecord(4493, "CGCS2000 / Gauss-Kruger zone 15", 4490, 4530, 16215); + return true; + case 1862: + record = new EpsgProjectedCrsRecord(4494, "CGCS2000 / Gauss-Kruger zone 16", 4490, 4530, 16216); + return true; + case 1863: + record = new EpsgProjectedCrsRecord(4495, "CGCS2000 / Gauss-Kruger zone 17", 4490, 4530, 16217); + return true; + case 1864: + record = new EpsgProjectedCrsRecord(4496, "CGCS2000 / Gauss-Kruger zone 18", 4490, 4530, 16218); + return true; + case 1865: + record = new EpsgProjectedCrsRecord(4497, "CGCS2000 / Gauss-Kruger zone 19", 4490, 4530, 16219); + return true; + case 1866: + record = new EpsgProjectedCrsRecord(4498, "CGCS2000 / Gauss-Kruger zone 20", 4490, 4530, 16220); + return true; + case 1867: + record = new EpsgProjectedCrsRecord(4499, "CGCS2000 / Gauss-Kruger zone 21", 4490, 4530, 16221); + return true; + case 1868: + record = new EpsgProjectedCrsRecord(4500, "CGCS2000 / Gauss-Kruger zone 22", 4490, 4530, 16222); + return true; + case 1869: + record = new EpsgProjectedCrsRecord(4501, "CGCS2000 / Gauss-Kruger zone 23", 4490, 4530, 16223); + return true; + case 1870: + record = new EpsgProjectedCrsRecord(4502, "CGCS2000 / Gauss-Kruger CM 75E", 4490, 4530, 16313); + return true; + case 1871: + record = new EpsgProjectedCrsRecord(4503, "CGCS2000 / Gauss-Kruger CM 81E", 4490, 4530, 16314); + return true; + case 1872: + record = new EpsgProjectedCrsRecord(4504, "CGCS2000 / Gauss-Kruger CM 87E", 4490, 4530, 16315); + return true; + case 1873: + record = new EpsgProjectedCrsRecord(4505, "CGCS2000 / Gauss-Kruger CM 93E", 4490, 4530, 16316); + return true; + case 1874: + record = new EpsgProjectedCrsRecord(4506, "CGCS2000 / Gauss-Kruger CM 99E", 4490, 4530, 16317); + return true; + case 1875: + record = new EpsgProjectedCrsRecord(4507, "CGCS2000 / Gauss-Kruger CM 105E", 4490, 4530, 16318); + return true; + case 1876: + record = new EpsgProjectedCrsRecord(4508, "CGCS2000 / Gauss-Kruger CM 111E", 4490, 4530, 16319); + return true; + case 1877: + record = new EpsgProjectedCrsRecord(4509, "CGCS2000 / Gauss-Kruger CM 117E", 4490, 4530, 16320); + return true; + case 1878: + record = new EpsgProjectedCrsRecord(4510, "CGCS2000 / Gauss-Kruger CM 123E", 4490, 4530, 16321); + return true; + case 1879: + record = new EpsgProjectedCrsRecord(4511, "CGCS2000 / Gauss-Kruger CM 129E", 4490, 4530, 16322); + return true; + case 1880: + record = new EpsgProjectedCrsRecord(4512, "CGCS2000 / Gauss-Kruger CM 135E", 4490, 4530, 16323); + return true; + case 1881: + record = new EpsgProjectedCrsRecord(4513, "CGCS2000 / 3-degree Gauss-Kruger zone 25", 4490, 4530, 16285); + return true; + case 1882: + record = new EpsgProjectedCrsRecord(4514, "CGCS2000 / 3-degree Gauss-Kruger zone 26", 4490, 4530, 16286); + return true; + case 1883: + record = new EpsgProjectedCrsRecord(4515, "CGCS2000 / 3-degree Gauss-Kruger zone 27", 4490, 4530, 16287); + return true; + case 1884: + record = new EpsgProjectedCrsRecord(4516, "CGCS2000 / 3-degree Gauss-Kruger zone 28", 4490, 4530, 16288); + return true; + case 1885: + record = new EpsgProjectedCrsRecord(4517, "CGCS2000 / 3-degree Gauss-Kruger zone 29", 4490, 4530, 16289); + return true; + case 1886: + record = new EpsgProjectedCrsRecord(4518, "CGCS2000 / 3-degree Gauss-Kruger zone 30", 4490, 4530, 16290); + return true; + case 1887: + record = new EpsgProjectedCrsRecord(4519, "CGCS2000 / 3-degree Gauss-Kruger zone 31", 4490, 4530, 16291); + return true; + case 1888: + record = new EpsgProjectedCrsRecord(4520, "CGCS2000 / 3-degree Gauss-Kruger zone 32", 4490, 4530, 16292); + return true; + case 1889: + record = new EpsgProjectedCrsRecord(4521, "CGCS2000 / 3-degree Gauss-Kruger zone 33", 4490, 4530, 16293); + return true; + case 1890: + record = new EpsgProjectedCrsRecord(4522, "CGCS2000 / 3-degree Gauss-Kruger zone 34", 4490, 4530, 16294); + return true; + case 1891: + record = new EpsgProjectedCrsRecord(4523, "CGCS2000 / 3-degree Gauss-Kruger zone 35", 4490, 4530, 16295); + return true; + case 1892: + record = new EpsgProjectedCrsRecord(4524, "CGCS2000 / 3-degree Gauss-Kruger zone 36", 4490, 4530, 16296); + return true; + case 1893: + record = new EpsgProjectedCrsRecord(4525, "CGCS2000 / 3-degree Gauss-Kruger zone 37", 4490, 4530, 16297); + return true; + case 1894: + record = new EpsgProjectedCrsRecord(4526, "CGCS2000 / 3-degree Gauss-Kruger zone 38", 4490, 4530, 16298); + return true; + case 1895: + record = new EpsgProjectedCrsRecord(4527, "CGCS2000 / 3-degree Gauss-Kruger zone 39", 4490, 4530, 16299); + return true; + case 1896: + record = new EpsgProjectedCrsRecord(4528, "CGCS2000 / 3-degree Gauss-Kruger zone 40", 4490, 4530, 16070); + return true; + case 1897: + record = new EpsgProjectedCrsRecord(4529, "CGCS2000 / 3-degree Gauss-Kruger zone 41", 4490, 4530, 16071); + return true; + case 1898: + record = new EpsgProjectedCrsRecord(4530, "CGCS2000 / 3-degree Gauss-Kruger zone 42", 4490, 4530, 16072); + return true; + case 1899: + record = new EpsgProjectedCrsRecord(4531, "CGCS2000 / 3-degree Gauss-Kruger zone 43", 4490, 4530, 16073); + return true; + case 1900: + record = new EpsgProjectedCrsRecord(4532, "CGCS2000 / 3-degree Gauss-Kruger zone 44", 4490, 4530, 16074); + return true; + case 1901: + record = new EpsgProjectedCrsRecord(4533, "CGCS2000 / 3-degree Gauss-Kruger zone 45", 4490, 4530, 16075); + return true; + case 1902: + record = new EpsgProjectedCrsRecord(4534, "CGCS2000 / 3-degree Gauss-Kruger CM 75E", 4490, 4530, 16313); + return true; + case 1903: + record = new EpsgProjectedCrsRecord(4535, "CGCS2000 / 3-degree Gauss-Kruger CM 78E", 4490, 4530, 16386); + return true; + case 1904: + record = new EpsgProjectedCrsRecord(4536, "CGCS2000 / 3-degree Gauss-Kruger CM 81E", 4490, 4530, 16314); + return true; + case 1905: + record = new EpsgProjectedCrsRecord(4537, "CGCS2000 / 3-degree Gauss-Kruger CM 84E", 4490, 4530, 16388); + return true; + case 1906: + record = new EpsgProjectedCrsRecord(4538, "CGCS2000 / 3-degree Gauss-Kruger CM 87E", 4490, 4530, 16315); + return true; + case 1907: + record = new EpsgProjectedCrsRecord(4539, "CGCS2000 / 3-degree Gauss-Kruger CM 90E", 4490, 4530, 16390); + return true; + case 1908: + record = new EpsgProjectedCrsRecord(4540, "CGCS2000 / 3-degree Gauss-Kruger CM 93E", 4490, 4530, 16316); + return true; + case 1909: + record = new EpsgProjectedCrsRecord(4541, "CGCS2000 / 3-degree Gauss-Kruger CM 96E", 4490, 4530, 16392); + return true; + case 1910: + record = new EpsgProjectedCrsRecord(4542, "CGCS2000 / 3-degree Gauss-Kruger CM 99E", 4490, 4530, 16317); + return true; + case 1911: + record = new EpsgProjectedCrsRecord(4543, "CGCS2000 / 3-degree Gauss-Kruger CM 102E", 4490, 4530, 16394); + return true; + case 1912: + record = new EpsgProjectedCrsRecord(4544, "CGCS2000 / 3-degree Gauss-Kruger CM 105E", 4490, 4530, 16318); + return true; + case 1913: + record = new EpsgProjectedCrsRecord(4545, "CGCS2000 / 3-degree Gauss-Kruger CM 108E", 4490, 4530, 16396); + return true; + case 1914: + record = new EpsgProjectedCrsRecord(4546, "CGCS2000 / 3-degree Gauss-Kruger CM 111E", 4490, 4530, 16319); + return true; + case 1915: + record = new EpsgProjectedCrsRecord(4547, "CGCS2000 / 3-degree Gauss-Kruger CM 114E", 4490, 4530, 16398); + return true; + case 1916: + record = new EpsgProjectedCrsRecord(4548, "CGCS2000 / 3-degree Gauss-Kruger CM 117E", 4490, 4530, 16320); + return true; + case 1917: + record = new EpsgProjectedCrsRecord(4549, "CGCS2000 / 3-degree Gauss-Kruger CM 120E", 4490, 4530, 16170); + return true; + case 1918: + record = new EpsgProjectedCrsRecord(4550, "CGCS2000 / 3-degree Gauss-Kruger CM 123E", 4490, 4530, 16321); + return true; + case 1919: + record = new EpsgProjectedCrsRecord(4551, "CGCS2000 / 3-degree Gauss-Kruger CM 126E", 4490, 4530, 16172); + return true; + case 1920: + record = new EpsgProjectedCrsRecord(4552, "CGCS2000 / 3-degree Gauss-Kruger CM 129E", 4490, 4530, 16322); + return true; + case 1921: + record = new EpsgProjectedCrsRecord(4553, "CGCS2000 / 3-degree Gauss-Kruger CM 132E", 4490, 4530, 16174); + return true; + case 1922: + record = new EpsgProjectedCrsRecord(4554, "CGCS2000 / 3-degree Gauss-Kruger CM 135E", 4490, 4530, 16323); + return true; + case 1923: + record = new EpsgProjectedCrsRecord(4559, "RRAF 1991 / UTM zone 20N", 4558, 4400, 16020); + return true; + case 1924: + record = new EpsgProjectedCrsRecord(4568, "New Beijing / Gauss-Kruger zone 13", 4555, 4530, 16213); + return true; + case 1925: + record = new EpsgProjectedCrsRecord(4569, "New Beijing / Gauss-Kruger zone 14", 4555, 4530, 16214); + return true; + case 1926: + record = new EpsgProjectedCrsRecord(4570, "New Beijing / Gauss-Kruger zone 15", 4555, 4530, 16215); + return true; + case 1927: + record = new EpsgProjectedCrsRecord(4571, "New Beijing / Gauss-Kruger zone 16", 4555, 4530, 16216); + return true; + case 1928: + record = new EpsgProjectedCrsRecord(4572, "New Beijing / Gauss-Kruger zone 17", 4555, 4530, 16217); + return true; + case 1929: + record = new EpsgProjectedCrsRecord(4573, "New Beijing / Gauss-Kruger zone 18", 4555, 4530, 16218); + return true; + case 1930: + record = new EpsgProjectedCrsRecord(4574, "New Beijing / Gauss-Kruger zone 19", 4555, 4530, 16219); + return true; + case 1931: + record = new EpsgProjectedCrsRecord(4575, "New Beijing / Gauss-Kruger zone 20", 4555, 4530, 16220); + return true; + case 1932: + record = new EpsgProjectedCrsRecord(4576, "New Beijing / Gauss-Kruger zone 21", 4555, 4530, 16221); + return true; + case 1933: + record = new EpsgProjectedCrsRecord(4577, "New Beijing / Gauss-Kruger zone 22", 4555, 4530, 16222); + return true; + case 1934: + record = new EpsgProjectedCrsRecord(4578, "New Beijing / Gauss-Kruger zone 23", 4555, 4530, 16223); + return true; + case 1935: + record = new EpsgProjectedCrsRecord(4579, "New Beijing / Gauss-Kruger CM 75E", 4555, 4530, 16313); + return true; + case 1936: + record = new EpsgProjectedCrsRecord(4580, "New Beijing / Gauss-Kruger CM 81E", 4555, 4530, 16314); + return true; + case 1937: + record = new EpsgProjectedCrsRecord(4581, "New Beijing / Gauss-Kruger CM 87E", 4555, 4530, 16315); + return true; + case 1938: + record = new EpsgProjectedCrsRecord(4582, "New Beijing / Gauss-Kruger CM 93E", 4555, 4530, 16316); + return true; + case 1939: + record = new EpsgProjectedCrsRecord(4583, "New Beijing / Gauss-Kruger CM 99E", 4555, 4530, 16317); + return true; + case 1940: + record = new EpsgProjectedCrsRecord(4584, "New Beijing / Gauss-Kruger CM 105E", 4555, 4530, 16318); + return true; + case 1941: + record = new EpsgProjectedCrsRecord(4585, "New Beijing / Gauss-Kruger CM 111E", 4555, 4530, 16319); + return true; + case 1942: + record = new EpsgProjectedCrsRecord(4586, "New Beijing / Gauss-Kruger CM 117E", 4555, 4530, 16320); + return true; + case 1943: + record = new EpsgProjectedCrsRecord(4587, "New Beijing / Gauss-Kruger CM 123E", 4555, 4530, 16321); + return true; + case 1944: + record = new EpsgProjectedCrsRecord(4588, "New Beijing / Gauss-Kruger CM 129E", 4555, 4530, 16322); + return true; + case 1945: + record = new EpsgProjectedCrsRecord(4589, "New Beijing / Gauss-Kruger CM 135E", 4555, 4530, 16323); + return true; + case 1946: + record = new EpsgProjectedCrsRecord(4647, "ETRS89 / UTM zone 32N (zE-N)", 4258, 4400, 4648); + return true; + case 1947: + record = new EpsgProjectedCrsRecord(4652, "New Beijing / 3-degree Gauss-Kruger zone 25", 4555, 4530, 16285); + return true; + case 1948: + record = new EpsgProjectedCrsRecord(4653, "New Beijing / 3-degree Gauss-Kruger zone 26", 4555, 4530, 16286); + return true; + case 1949: + record = new EpsgProjectedCrsRecord(4654, "New Beijing / 3-degree Gauss-Kruger zone 27", 4555, 4530, 16287); + return true; + case 1950: + record = new EpsgProjectedCrsRecord(4655, "New Beijing / 3-degree Gauss-Kruger zone 28", 4555, 4530, 16288); + return true; + case 1951: + record = new EpsgProjectedCrsRecord(4656, "New Beijing / 3-degree Gauss-Kruger zone 29", 4555, 4530, 16289); + return true; + case 1952: + record = new EpsgProjectedCrsRecord(4766, "New Beijing / 3-degree Gauss-Kruger zone 30", 4555, 4530, 16290); + return true; + case 1953: + record = new EpsgProjectedCrsRecord(4767, "New Beijing / 3-degree Gauss-Kruger zone 31", 4555, 4530, 16291); + return true; + case 1954: + record = new EpsgProjectedCrsRecord(4768, "New Beijing / 3-degree Gauss-Kruger zone 32", 4555, 4530, 16292); + return true; + case 1955: + record = new EpsgProjectedCrsRecord(4769, "New Beijing / 3-degree Gauss-Kruger zone 33", 4555, 4530, 16293); + return true; + case 1956: + record = new EpsgProjectedCrsRecord(4770, "New Beijing / 3-degree Gauss-Kruger zone 34", 4555, 4530, 16294); + return true; + case 1957: + record = new EpsgProjectedCrsRecord(4771, "New Beijing / 3-degree Gauss-Kruger zone 35", 4555, 4530, 16295); + return true; + case 1958: + record = new EpsgProjectedCrsRecord(4772, "New Beijing / 3-degree Gauss-Kruger zone 36", 4555, 4530, 16296); + return true; + case 1959: + record = new EpsgProjectedCrsRecord(4773, "New Beijing / 3-degree Gauss-Kruger zone 37", 4555, 4530, 16297); + return true; + case 1960: + record = new EpsgProjectedCrsRecord(4774, "New Beijing / 3-degree Gauss-Kruger zone 38", 4555, 4530, 16298); + return true; + case 1961: + record = new EpsgProjectedCrsRecord(4775, "New Beijing / 3-degree Gauss-Kruger zone 39", 4555, 4530, 16299); + return true; + case 1962: + record = new EpsgProjectedCrsRecord(4776, "New Beijing / 3-degree Gauss-Kruger zone 40", 4555, 4530, 16070); + return true; + case 1963: + record = new EpsgProjectedCrsRecord(4777, "New Beijing / 3-degree Gauss-Kruger zone 41", 4555, 4530, 16071); + return true; + case 1964: + record = new EpsgProjectedCrsRecord(4778, "New Beijing / 3-degree Gauss-Kruger zone 42", 4555, 4530, 16072); + return true; + case 1965: + record = new EpsgProjectedCrsRecord(4779, "New Beijing / 3-degree Gauss-Kruger zone 43", 4555, 4530, 16073); + return true; + case 1966: + record = new EpsgProjectedCrsRecord(4780, "New Beijing / 3-degree Gauss-Kruger zone 44", 4555, 4530, 16074); + return true; + case 1967: + record = new EpsgProjectedCrsRecord(4781, "New Beijing / 3-degree Gauss-Kruger zone 45", 4555, 4530, 16075); + return true; + case 1968: + record = new EpsgProjectedCrsRecord(4782, "New Beijing / 3-degree Gauss-Kruger CM 75E", 4555, 4530, 16313); + return true; + case 1969: + record = new EpsgProjectedCrsRecord(4783, "New Beijing / 3-degree Gauss-Kruger CM 78E", 4555, 4530, 16386); + return true; + case 1970: + record = new EpsgProjectedCrsRecord(4784, "New Beijing / 3-degree Gauss-Kruger CM 81E", 4555, 4530, 16314); + return true; + case 1971: + record = new EpsgProjectedCrsRecord(4785, "New Beijing / 3-degree Gauss-Kruger CM 84E", 4555, 4530, 16388); + return true; + case 1972: + record = new EpsgProjectedCrsRecord(4786, "New Beijing / 3-degree Gauss-Kruger CM 87E", 4555, 4530, 16315); + return true; + case 1973: + record = new EpsgProjectedCrsRecord(4787, "New Beijing / 3-degree Gauss-Kruger CM 90E", 4555, 4530, 16390); + return true; + case 1974: + record = new EpsgProjectedCrsRecord(4788, "New Beijing / 3-degree Gauss-Kruger CM 93E", 4555, 4530, 16316); + return true; + case 1975: + record = new EpsgProjectedCrsRecord(4789, "New Beijing / 3-degree Gauss-Kruger CM 96E", 4555, 4530, 16392); + return true; + case 1976: + record = new EpsgProjectedCrsRecord(4790, "New Beijing / 3-degree Gauss-Kruger CM 99E", 4555, 4530, 16317); + return true; + case 1977: + record = new EpsgProjectedCrsRecord(4791, "New Beijing / 3-degree Gauss-Kruger CM 102E", 4555, 4530, 16394); + return true; + case 1978: + record = new EpsgProjectedCrsRecord(4792, "New Beijing / 3-degree Gauss-Kruger CM 105E", 4555, 4530, 16318); + return true; + case 1979: + record = new EpsgProjectedCrsRecord(4793, "New Beijing / 3-degree Gauss-Kruger CM 108E", 4555, 4530, 16396); + return true; + case 1980: + record = new EpsgProjectedCrsRecord(4794, "New Beijing / 3-degree Gauss-Kruger CM 111E", 4555, 4530, 16319); + return true; + case 1981: + record = new EpsgProjectedCrsRecord(4795, "New Beijing / 3-degree Gauss-Kruger CM 114E", 4555, 4530, 16398); + return true; + case 1982: + record = new EpsgProjectedCrsRecord(4796, "New Beijing / 3-degree Gauss-Kruger CM 117E", 4555, 4530, 16320); + return true; + case 1983: + record = new EpsgProjectedCrsRecord(4797, "New Beijing / 3-degree Gauss-Kruger CM 120E", 4555, 4530, 16170); + return true; + case 1984: + record = new EpsgProjectedCrsRecord(4798, "New Beijing / 3-degree Gauss-Kruger CM 123E", 4555, 4530, 16321); + return true; + case 1985: + record = new EpsgProjectedCrsRecord(4799, "New Beijing / 3-degree Gauss-Kruger CM 126E", 4555, 4530, 16172); + return true; + case 1986: + record = new EpsgProjectedCrsRecord(4800, "New Beijing / 3-degree Gauss-Kruger CM 129E", 4555, 4530, 16322); + return true; + case 1987: + record = new EpsgProjectedCrsRecord(4812, "New Beijing / 3-degree Gauss-Kruger CM 132E", 4555, 4530, 16174); + return true; + case 1988: + record = new EpsgProjectedCrsRecord(4822, "New Beijing / 3-degree Gauss-Kruger CM 135E", 4555, 4530, 16323); + return true; + case 1989: + record = new EpsgProjectedCrsRecord(4826, "WGS 84 / Cape Verde National", 4326, 1024, 4825); + return true; + case 1990: + record = new EpsgProjectedCrsRecord(4839, "ETRS89 / LCC Germany (N-E)", 4258, 4500, 4838); + return true; + case 1991: + record = new EpsgProjectedCrsRecord(5014, "PTRA08 / UTM zone 25N", 5013, 4400, 16025); + return true; + case 1992: + record = new EpsgProjectedCrsRecord(5015, "PTRA08 / UTM zone 26N", 5013, 4400, 16026); + return true; + case 1993: + record = new EpsgProjectedCrsRecord(5016, "PTRA08 / UTM zone 28N", 5013, 4400, 16028); + return true; + case 1994: + record = new EpsgProjectedCrsRecord(5017, "Lisbon 1890 / Portugal Bonne New", 4666, 6509, 5019); + return true; + case 1995: + record = new EpsgProjectedCrsRecord(5018, "Lisbon / Portuguese Grid New", 4207, 4499, 5020); + return true; + case 1996: + record = new EpsgProjectedCrsRecord(5041, "WGS 84 / UPS North (E,N)", 4326, 1026, 16061); + return true; + case 1997: + record = new EpsgProjectedCrsRecord(5042, "WGS 84 / UPS South (E,N)", 4326, 1027, 16161); + return true; + case 1998: + record = new EpsgProjectedCrsRecord(5048, "ETRS89-FIN [EUREF-FIN] / TM35FIN(N,E)", 10690, 4500, 16065); + return true; + case 1999: + record = new EpsgProjectedCrsRecord(5069, "NAD27 / Conus Albers", 4267, 4499, 5068); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetProjectedCrsBucket2(int index, out EpsgProjectedCrsRecord record) + { + switch (index) + { + case 2000: + record = new EpsgProjectedCrsRecord(5070, "NAD83 / Conus Albers", 4269, 4499, 5068); + return true; + case 2001: + record = new EpsgProjectedCrsRecord(5071, "NAD83(HARN) / Conus Albers", 4152, 4499, 5068); + return true; + case 2002: + record = new EpsgProjectedCrsRecord(5072, "NAD83(NSRS2007) / Conus Albers", 4759, 4499, 5068); + return true; + case 2003: + record = new EpsgProjectedCrsRecord(5105, "ETRS89-NOR [EUREF89] / NTM zone 5", 10875, 4500, 5135); + return true; + case 2004: + record = new EpsgProjectedCrsRecord(5106, "ETRS89-NOR [EUREF89] / NTM zone 6", 10875, 4500, 5136); + return true; + case 2005: + record = new EpsgProjectedCrsRecord(5107, "ETRS89-NOR [EUREF89] / NTM zone 7", 10875, 4500, 5137); + return true; + case 2006: + record = new EpsgProjectedCrsRecord(5108, "ETRS89-NOR [EUREF89] / NTM zone 8", 10875, 4500, 5138); + return true; + case 2007: + record = new EpsgProjectedCrsRecord(5109, "ETRS89-NOR [EUREF89] / NTM zone 9", 10875, 4500, 5139); + return true; + case 2008: + record = new EpsgProjectedCrsRecord(5110, "ETRS89-NOR [EUREF89] / NTM zone 10", 10875, 4500, 5140); + return true; + case 2009: + record = new EpsgProjectedCrsRecord(5111, "ETRS89-NOR [EUREF89] / NTM zone 11", 10875, 4500, 5141); + return true; + case 2010: + record = new EpsgProjectedCrsRecord(5112, "ETRS89-NOR [EUREF89] / NTM zone 12", 10875, 4500, 5142); + return true; + case 2011: + record = new EpsgProjectedCrsRecord(5113, "ETRS89-NOR [EUREF89] / NTM zone 13", 10875, 4500, 5143); + return true; + case 2012: + record = new EpsgProjectedCrsRecord(5114, "ETRS89-NOR [EUREF89] / NTM zone 14", 10875, 4500, 5144); + return true; + case 2013: + record = new EpsgProjectedCrsRecord(5115, "ETRS89-NOR [EUREF89] / NTM zone 15", 10875, 4500, 5145); + return true; + case 2014: + record = new EpsgProjectedCrsRecord(5116, "ETRS89-NOR [EUREF89] / NTM zone 16", 10875, 4500, 5146); + return true; + case 2015: + record = new EpsgProjectedCrsRecord(5117, "ETRS89-NOR [EUREF89] / NTM zone 17", 10875, 4500, 5147); + return true; + case 2016: + record = new EpsgProjectedCrsRecord(5118, "ETRS89-NOR [EUREF89] / NTM zone 18", 10875, 4500, 5148); + return true; + case 2017: + record = new EpsgProjectedCrsRecord(5119, "ETRS89-NOR [EUREF89] / NTM zone 19", 10875, 4500, 5149); + return true; + case 2018: + record = new EpsgProjectedCrsRecord(5120, "ETRS89-NOR [EUREF89] / NTM zone 20", 10875, 4500, 5150); + return true; + case 2019: + record = new EpsgProjectedCrsRecord(5121, "ETRS89-NOR [EUREF89] / NTM zone 21", 10875, 4500, 5151); + return true; + case 2020: + record = new EpsgProjectedCrsRecord(5122, "ETRS89-NOR [EUREF89] / NTM zone 22", 10875, 4500, 5152); + return true; + case 2021: + record = new EpsgProjectedCrsRecord(5123, "ETRS89-NOR [EUREF89] / NTM zone 23", 10875, 4500, 5153); + return true; + case 2022: + record = new EpsgProjectedCrsRecord(5124, "ETRS89-NOR [EUREF89] / NTM zone 24", 10875, 4500, 5154); + return true; + case 2023: + record = new EpsgProjectedCrsRecord(5125, "ETRS89-NOR [EUREF89] / NTM zone 25", 10875, 4500, 5155); + return true; + case 2024: + record = new EpsgProjectedCrsRecord(5126, "ETRS89-NOR [EUREF89] / NTM zone 26", 10875, 4500, 5156); + return true; + case 2025: + record = new EpsgProjectedCrsRecord(5127, "ETRS89-NOR [EUREF89] / NTM zone 27", 10875, 4500, 5157); + return true; + case 2026: + record = new EpsgProjectedCrsRecord(5128, "ETRS89-NOR [EUREF89] / NTM zone 28", 10875, 4500, 5158); + return true; + case 2027: + record = new EpsgProjectedCrsRecord(5129, "ETRS89-NOR [EUREF89] / NTM zone 29", 10875, 4500, 5159); + return true; + case 2028: + record = new EpsgProjectedCrsRecord(5130, "ETRS89-NOR [EUREF89] / NTM zone 30", 10875, 4500, 5160); + return true; + case 2029: + record = new EpsgProjectedCrsRecord(5167, "Korean 1985 / East Sea Belt", 4162, 4530, 5049); + return true; + case 2030: + record = new EpsgProjectedCrsRecord(5168, "Korean 1985 / Central Belt Jeju", 4162, 4530, 5131); + return true; + case 2031: + record = new EpsgProjectedCrsRecord(5169, "Tokyo 1892 / Korea West Belt", 5132, 4530, 18253); + return true; + case 2032: + record = new EpsgProjectedCrsRecord(5170, "Tokyo 1892 / Korea Central Belt", 5132, 4530, 18252); + return true; + case 2033: + record = new EpsgProjectedCrsRecord(5171, "Tokyo 1892 / Korea East Belt", 5132, 4530, 18251); + return true; + case 2034: + record = new EpsgProjectedCrsRecord(5172, "Tokyo 1892 / Korea East Sea Belt", 5132, 4530, 5049); + return true; + case 2035: + record = new EpsgProjectedCrsRecord(5173, "Korean 1985 / Modified West Belt", 4162, 4530, 5161); + return true; + case 2036: + record = new EpsgProjectedCrsRecord(5174, "Korean 1985 / Modified Central Belt", 4162, 4530, 5162); + return true; + case 2037: + record = new EpsgProjectedCrsRecord(5175, "Korean 1985 / Modified Central Belt Jeju", 4162, 4530, 5163); + return true; + case 2038: + record = new EpsgProjectedCrsRecord(5176, "Korean 1985 / Modified East Belt", 4162, 4530, 5164); + return true; + case 2039: + record = new EpsgProjectedCrsRecord(5177, "Korean 1985 / Modified East Sea Belt", 4162, 4530, 5165); + return true; + case 2040: + record = new EpsgProjectedCrsRecord(5178, "Korean 1985 / Unified CS", 4162, 4530, 5100); + return true; + case 2041: + record = new EpsgProjectedCrsRecord(5179, "KGD2002 / Unified CS", 4737, 4530, 5100); + return true; + case 2042: + record = new EpsgProjectedCrsRecord(5180, "KGD2002 / West Belt", 4737, 4530, 18253); + return true; + case 2043: + record = new EpsgProjectedCrsRecord(5181, "KGD2002 / Central Belt", 4737, 4530, 18252); + return true; + case 2044: + record = new EpsgProjectedCrsRecord(5182, "KGD2002 / Central Belt Jeju", 4737, 4530, 5131); + return true; + case 2045: + record = new EpsgProjectedCrsRecord(5183, "KGD2002 / East Belt", 4737, 4530, 18251); + return true; + case 2046: + record = new EpsgProjectedCrsRecord(5184, "KGD2002 / East Sea Belt", 4737, 4530, 5049); + return true; + case 2047: + record = new EpsgProjectedCrsRecord(5185, "KGD2002 / West Belt 2010", 4737, 4530, 5101); + return true; + case 2048: + record = new EpsgProjectedCrsRecord(5186, "KGD2002 / Central Belt 2010", 4737, 4530, 5102); + return true; + case 2049: + record = new EpsgProjectedCrsRecord(5187, "KGD2002 / East Belt 2010", 4737, 4530, 5103); + return true; + case 2050: + record = new EpsgProjectedCrsRecord(5188, "KGD2002 / East Sea Belt 2010", 4737, 4530, 5104); + return true; + case 2051: + record = new EpsgProjectedCrsRecord(5221, "S-JTSK (Ferro) / Krovak East North", 4818, 4499, 5218); + return true; + case 2052: + record = new EpsgProjectedCrsRecord(5223, "WGS 84 / Gabon TM", 4326, 4499, 5222); + return true; + case 2053: + record = new EpsgProjectedCrsRecord(5224, "S-JTSK/05 (Ferro) / Modified Krovak", 5229, 6501, 5219); + return true; + case 2054: + record = new EpsgProjectedCrsRecord(5225, "S-JTSK/05 (Ferro) / Modified Krovak East North", 5229, 4499, 5220); + return true; + case 2055: + record = new EpsgProjectedCrsRecord(5234, "Kandawala / Sri Lanka Grid", 4244, 4400, 5231); + return true; + case 2056: + record = new EpsgProjectedCrsRecord(5235, "SLD99 / Sri Lanka Grid 1999", 5233, 4400, 5232); + return true; + case 2057: + record = new EpsgProjectedCrsRecord(5243, "ETRS89 / LCC Germany (E-N)", 4258, 4400, 4838); + return true; + case 2058: + record = new EpsgProjectedCrsRecord(5247, "GDBD2009 / Brunei BRSO", 5246, 4400, 19894); + return true; + case 2059: + record = new EpsgProjectedCrsRecord(5253, "TUREF / TM27", 5252, 4530, 16305); + return true; + case 2060: + record = new EpsgProjectedCrsRecord(5254, "TUREF / TM30", 5252, 4530, 16370); + return true; + case 2061: + record = new EpsgProjectedCrsRecord(5255, "TUREF / TM33", 5252, 4530, 16306); + return true; + case 2062: + record = new EpsgProjectedCrsRecord(5256, "TUREF / TM36", 5252, 4530, 16372); + return true; + case 2063: + record = new EpsgProjectedCrsRecord(5257, "TUREF / TM39", 5252, 4530, 16307); + return true; + case 2064: + record = new EpsgProjectedCrsRecord(5258, "TUREF / TM42", 5252, 4530, 16374); + return true; + case 2065: + record = new EpsgProjectedCrsRecord(5259, "TUREF / TM45", 5252, 4530, 16308); + return true; + case 2066: + record = new EpsgProjectedCrsRecord(5266, "DRUKREF 03 / Bhutan National Grid", 5264, 4400, 5265); + return true; + case 2067: + record = new EpsgProjectedCrsRecord(5269, "TUREF / 3-degree Gauss-Kruger zone 9", 5252, 4530, 16269); + return true; + case 2068: + record = new EpsgProjectedCrsRecord(5270, "TUREF / 3-degree Gauss-Kruger zone 10", 5252, 4530, 16270); + return true; + case 2069: + record = new EpsgProjectedCrsRecord(5271, "TUREF / 3-degree Gauss-Kruger zone 11", 5252, 4530, 16271); + return true; + case 2070: + record = new EpsgProjectedCrsRecord(5272, "TUREF / 3-degree Gauss-Kruger zone 12", 5252, 4530, 16272); + return true; + case 2071: + record = new EpsgProjectedCrsRecord(5273, "TUREF / 3-degree Gauss-Kruger zone 13", 5252, 4530, 16273); + return true; + case 2072: + record = new EpsgProjectedCrsRecord(5274, "TUREF / 3-degree Gauss-Kruger zone 14", 5252, 4530, 16274); + return true; + case 2073: + record = new EpsgProjectedCrsRecord(5275, "TUREF / 3-degree Gauss-Kruger zone 15", 5252, 4530, 16275); + return true; + case 2074: + record = new EpsgProjectedCrsRecord(5292, "DRUKREF 03 / Bumthang TM", 5264, 4400, 5268); + return true; + case 2075: + record = new EpsgProjectedCrsRecord(5293, "DRUKREF 03 / Chhukha TM", 5264, 4400, 5276); + return true; + case 2076: + record = new EpsgProjectedCrsRecord(5294, "DRUKREF 03 / Dagana TM", 5264, 4400, 5277); + return true; + case 2077: + record = new EpsgProjectedCrsRecord(5295, "DRUKREF 03 / Gasa TM", 5264, 4400, 5278); + return true; + case 2078: + record = new EpsgProjectedCrsRecord(5296, "DRUKREF 03 / Ha TM", 5264, 4400, 5279); + return true; + case 2079: + record = new EpsgProjectedCrsRecord(5297, "DRUKREF 03 / Lhuentse TM", 5264, 4400, 5280); + return true; + case 2080: + record = new EpsgProjectedCrsRecord(5298, "DRUKREF 03 / Mongar TM", 5264, 4400, 5281); + return true; + case 2081: + record = new EpsgProjectedCrsRecord(5299, "DRUKREF 03 / Paro TM", 5264, 4400, 5282); + return true; + case 2082: + record = new EpsgProjectedCrsRecord(5300, "DRUKREF 03 / Pemagatshel TM", 5264, 4400, 5283); + return true; + case 2083: + record = new EpsgProjectedCrsRecord(5301, "DRUKREF 03 / Punakha TM", 5264, 4400, 5313); + return true; + case 2084: + record = new EpsgProjectedCrsRecord(5302, "DRUKREF 03 / Samdrup Jongkhar TM", 5264, 4400, 5285); + return true; + case 2085: + record = new EpsgProjectedCrsRecord(5303, "DRUKREF 03 / Samtse TM", 5264, 4400, 5286); + return true; + case 2086: + record = new EpsgProjectedCrsRecord(5304, "DRUKREF 03 / Sarpang TM", 5264, 4400, 5287); + return true; + case 2087: + record = new EpsgProjectedCrsRecord(5305, "DRUKREF 03 / Thimphu TM", 5264, 4400, 5312); + return true; + case 2088: + record = new EpsgProjectedCrsRecord(5306, "DRUKREF 03 / Trashigang TM", 5264, 4400, 5289); + return true; + case 2089: + record = new EpsgProjectedCrsRecord(5307, "DRUKREF 03 / Trongsa TM", 5264, 4400, 5290); + return true; + case 2090: + record = new EpsgProjectedCrsRecord(5308, "DRUKREF 03 / Tsirang TM", 5264, 4400, 5284); + return true; + case 2091: + record = new EpsgProjectedCrsRecord(5309, "DRUKREF 03 / Wangdue Phodrang TM", 5264, 4400, 5288); + return true; + case 2092: + record = new EpsgProjectedCrsRecord(5310, "DRUKREF 03 / Yangtse TM", 5264, 4400, 5314); + return true; + case 2093: + record = new EpsgProjectedCrsRecord(5311, "DRUKREF 03 / Zhemgang TM", 5264, 4400, 5291); + return true; + case 2094: + record = new EpsgProjectedCrsRecord(5316, "ETRS89-FRO [2008] / Faroe TM", 11087, 4400, 5315); + return true; + case 2095: + record = new EpsgProjectedCrsRecord(5320, "NAD83 / Teranet Ontario Lambert", 4269, 4499, 5319); + return true; + case 2096: + record = new EpsgProjectedCrsRecord(5321, "NAD83(CSRS) / Teranet Ontario Lambert", 4617, 4499, 5319); + return true; + case 2097: + record = new EpsgProjectedCrsRecord(5325, "ISN2004 / Lambert 2004", 5324, 4499, 5326); + return true; + case 2098: + record = new EpsgProjectedCrsRecord(5329, "Segara (Jakarta) / NEIEZ", 4820, 4499, 5328); + return true; + case 2099: + record = new EpsgProjectedCrsRecord(5330, "Batavia (Jakarta) / NEIEZ", 4813, 4499, 5328); + return true; + case 2100: + record = new EpsgProjectedCrsRecord(5331, "Makassar (Jakarta) / NEIEZ", 4804, 4499, 5328); + return true; + case 2101: + record = new EpsgProjectedCrsRecord(5337, "Aratu / UTM zone 25S", 4208, 4400, 16125); + return true; + case 2102: + record = new EpsgProjectedCrsRecord(5343, "POSGAR 2007 / Argentina 1", 5340, 4530, 18031); + return true; + case 2103: + record = new EpsgProjectedCrsRecord(5344, "POSGAR 2007 / Argentina 2", 5340, 4530, 18032); + return true; + case 2104: + record = new EpsgProjectedCrsRecord(5345, "POSGAR 2007 / Argentina 3", 5340, 4530, 18033); + return true; + case 2105: + record = new EpsgProjectedCrsRecord(5346, "POSGAR 2007 / Argentina 4", 5340, 4530, 18034); + return true; + case 2106: + record = new EpsgProjectedCrsRecord(5347, "POSGAR 2007 / Argentina 5", 5340, 4530, 18035); + return true; + case 2107: + record = new EpsgProjectedCrsRecord(5348, "POSGAR 2007 / Argentina 6", 5340, 4530, 18036); + return true; + case 2108: + record = new EpsgProjectedCrsRecord(5349, "POSGAR 2007 / Argentina 7", 5340, 4530, 18037); + return true; + case 2109: + record = new EpsgProjectedCrsRecord(5355, "MARGEN / UTM zone 20S", 5354, 4400, 16120); + return true; + case 2110: + record = new EpsgProjectedCrsRecord(5356, "MARGEN / UTM zone 19S", 5354, 4400, 16119); + return true; + case 2111: + record = new EpsgProjectedCrsRecord(5357, "MARGEN / UTM zone 21S", 5354, 4400, 16121); + return true; + case 2112: + record = new EpsgProjectedCrsRecord(5361, "SIRGAS-Chile 2002 / UTM zone 19S", 5360, 4400, 16119); + return true; + case 2113: + record = new EpsgProjectedCrsRecord(5362, "SIRGAS-Chile 2002 / UTM zone 18S", 5360, 4400, 16118); + return true; + case 2114: + record = new EpsgProjectedCrsRecord(5367, "CR05 / CRTM05", 5365, 4500, 5366); + return true; + case 2115: + record = new EpsgProjectedCrsRecord(5382, "SIRGAS-ROU98 / UTM zone 21S", 5381, 4400, 16121); + return true; + case 2116: + record = new EpsgProjectedCrsRecord(5383, "SIRGAS-ROU98 / UTM zone 22S", 5381, 4400, 16122); + return true; + case 2117: + record = new EpsgProjectedCrsRecord(5387, "Peru96 / UTM zone 18S", 5373, 4400, 16118); + return true; + case 2118: + record = new EpsgProjectedCrsRecord(5389, "Peru96 / UTM zone 19S", 5373, 4400, 16119); + return true; + case 2119: + record = new EpsgProjectedCrsRecord(5396, "SIRGAS 2000 / UTM zone 26S", 4674, 4400, 16126); + return true; + case 2120: + record = new EpsgProjectedCrsRecord(5456, "Ocotepeque 1935 / Costa Rica Norte", 5451, 4499, 5390); + return true; + case 2121: + record = new EpsgProjectedCrsRecord(5457, "Ocotepeque 1935 / Costa Rica Sur", 5451, 4499, 5394); + return true; + case 2122: + record = new EpsgProjectedCrsRecord(5459, "Ocotepeque 1935 / Guatemala Sur", 5451, 4499, 18212); + return true; + case 2123: + record = new EpsgProjectedCrsRecord(5460, "Ocotepeque 1935 / El Salvador Lambert", 5451, 4499, 5399); + return true; + case 2124: + record = new EpsgProjectedCrsRecord(5461, "Ocotepeque 1935 / Nicaragua Norte", 5451, 4499, 5439); + return true; + case 2125: + record = new EpsgProjectedCrsRecord(5462, "Ocotepeque 1935 / Nicaragua Sur", 5451, 4499, 5444); + return true; + case 2126: + record = new EpsgProjectedCrsRecord(5463, "SAD69 / UTM zone 17N", 4618, 4400, 16017); + return true; + case 2127: + record = new EpsgProjectedCrsRecord(5469, "Panama-Colon 1911 / Panama Lambert", 5467, 4499, 5468); + return true; + case 2128: + record = new EpsgProjectedCrsRecord(5472, "Panama-Colon 1911 / Panama Polyconic", 5467, 1028, 5471); + return true; + case 2129: + record = new EpsgProjectedCrsRecord(5479, "RSRGD2000 / MSLC2000", 4764, 4500, 5475); + return true; + case 2130: + record = new EpsgProjectedCrsRecord(5480, "RSRGD2000 / BCLC2000", 4764, 4500, 5476); + return true; + case 2131: + record = new EpsgProjectedCrsRecord(5481, "RSRGD2000 / PCLC2000", 4764, 4500, 5477); + return true; + case 2132: + record = new EpsgProjectedCrsRecord(5482, "RSRGD2000 / RSPS2000", 4764, 1044, 5478); + return true; + case 2133: + record = new EpsgProjectedCrsRecord(5490, "RGAF09 / UTM zone 20N", 5489, 4400, 16020); + return true; + case 2134: + record = new EpsgProjectedCrsRecord(5513, "S-JTSK / Krovak", 4156, 6501, 5509); + return true; + case 2135: + record = new EpsgProjectedCrsRecord(5514, "S-JTSK / Krovak East North", 4156, 4499, 5510); + return true; + case 2136: + record = new EpsgProjectedCrsRecord(5515, "S-JTSK/05 / Modified Krovak", 5228, 6501, 5511); + return true; + case 2137: + record = new EpsgProjectedCrsRecord(5516, "S-JTSK/05 / Modified Krovak East North", 5228, 4499, 5512); + return true; + case 2138: + record = new EpsgProjectedCrsRecord(5518, "CI1971 / Chatham Islands Map Grid", 4672, 4500, 5517); + return true; + case 2139: + record = new EpsgProjectedCrsRecord(5519, "CI1979 / Chatham Islands Map Grid", 4673, 4500, 5517); + return true; + case 2140: + record = new EpsgProjectedCrsRecord(5520, "DHDN / 3-degree Gauss-Kruger zone 1", 4314, 4530, 16261); + return true; + case 2141: + record = new EpsgProjectedCrsRecord(5523, "WGS 84 / Gabon TM 2011", 4326, 4499, 5522); + return true; + case 2142: + record = new EpsgProjectedCrsRecord(5530, "SAD69(96) / Brazil Polyconic", 5527, 4499, 19941); + return true; + case 2143: + record = new EpsgProjectedCrsRecord(5531, "SAD69(96) / UTM zone 21S", 5527, 4400, 16121); + return true; + case 2144: + record = new EpsgProjectedCrsRecord(5533, "SAD69(96) / UTM zone 23S", 5527, 4400, 16123); + return true; + case 2145: + record = new EpsgProjectedCrsRecord(5534, "SAD69(96) / UTM zone 24S", 5527, 4400, 16124); + return true; + case 2146: + record = new EpsgProjectedCrsRecord(5535, "SAD69(96) / UTM zone 25S", 5527, 4400, 16125); + return true; + case 2147: + record = new EpsgProjectedCrsRecord(5536, "Corrego Alegre 1961 / UTM zone 21S", 5524, 4400, 16121); + return true; + case 2148: + record = new EpsgProjectedCrsRecord(5537, "Corrego Alegre 1961 / UTM zone 22S", 5524, 4400, 16122); + return true; + case 2149: + record = new EpsgProjectedCrsRecord(5538, "Corrego Alegre 1961 / UTM zone 23S", 5524, 4400, 16123); + return true; + case 2150: + record = new EpsgProjectedCrsRecord(5539, "Corrego Alegre 1961 / UTM zone 24S", 5524, 4400, 16124); + return true; + case 2151: + record = new EpsgProjectedCrsRecord(5550, "PNG94 / PNGMG94 zone 54", 5546, 4400, 5547); + return true; + case 2152: + record = new EpsgProjectedCrsRecord(5551, "PNG94 / PNGMG94 zone 55", 5546, 4400, 5548); + return true; + case 2153: + record = new EpsgProjectedCrsRecord(5552, "PNG94 / PNGMG94 zone 56", 5546, 4400, 5549); + return true; + case 2154: + record = new EpsgProjectedCrsRecord(5559, "Ocotepeque 1935 / Guatemala Norte", 5451, 4499, 18211); + return true; + case 2155: + record = new EpsgProjectedCrsRecord(5562, "UCS-2000 / Gauss-Kruger zone 4", 5561, 4530, 16204); + return true; + case 2156: + record = new EpsgProjectedCrsRecord(5563, "UCS-2000 / Gauss-Kruger zone 5", 5561, 4530, 16205); + return true; + case 2157: + record = new EpsgProjectedCrsRecord(5564, "UCS-2000 / Gauss-Kruger zone 6", 5561, 4530, 16206); + return true; + case 2158: + record = new EpsgProjectedCrsRecord(5565, "UCS-2000 / Gauss-Kruger zone 7", 5561, 4530, 16207); + return true; + case 2159: + record = new EpsgProjectedCrsRecord(5566, "UCS-2000 / Gauss-Kruger CM 21E", 5561, 4530, 16304); + return true; + case 2160: + record = new EpsgProjectedCrsRecord(5567, "UCS-2000 / Gauss-Kruger CM 27E", 5561, 4530, 16305); + return true; + case 2161: + record = new EpsgProjectedCrsRecord(5568, "UCS-2000 / Gauss-Kruger CM 33E", 5561, 4530, 16306); + return true; + case 2162: + record = new EpsgProjectedCrsRecord(5569, "UCS-2000 / Gauss-Kruger CM 39E", 5561, 4530, 16307); + return true; + case 2163: + record = new EpsgProjectedCrsRecord(5588, "NAD27 / New Brunswick Stereographic (NAD27)", 4267, 1029, 5587); + return true; + case 2164: + record = new EpsgProjectedCrsRecord(5589, "Sibun Gorge 1922 / Colony Grid", 5464, 4403, 5465); + return true; + case 2165: + record = new EpsgProjectedCrsRecord(5596, "FEH2010 / Fehmarnbelt TM", 5593, 4400, 5595); + return true; + case 2166: + record = new EpsgProjectedCrsRecord(5623, "NAD27 / Michigan East", 4267, 4497, 12101); + return true; + case 2167: + record = new EpsgProjectedCrsRecord(5624, "NAD27 / Michigan Old Central", 4267, 4497, 12102); + return true; + case 2168: + record = new EpsgProjectedCrsRecord(5625, "NAD27 / Michigan West", 4267, 4497, 12103); + return true; + case 2169: + record = new EpsgProjectedCrsRecord(5627, "ED50 / TM 6 NE", 4230, 4400, 16406); + return true; + case 2170: + record = new EpsgProjectedCrsRecord(5629, "Moznet / UTM zone 38S", 4130, 4400, 16138); + return true; + case 2171: + record = new EpsgProjectedCrsRecord(5631, "Pulkovo 1942(58) / Gauss-Kruger zone 2 (E-N)", 4179, 4400, 16202); + return true; + case 2172: + record = new EpsgProjectedCrsRecord(5632, "PTRA08 / LCC Europe", 5013, 4500, 19985); + return true; + case 2173: + record = new EpsgProjectedCrsRecord(5633, "PTRA08 / LAEA Europe", 5013, 4532, 19986); + return true; + case 2174: + record = new EpsgProjectedCrsRecord(5634, "REGCAN95 / LCC Europe", 4081, 4500, 19985); + return true; + case 2175: + record = new EpsgProjectedCrsRecord(5635, "REGCAN95 / LAEA Europe", 4081, 4500, 19986); + return true; + case 2176: + record = new EpsgProjectedCrsRecord(5636, "TUREF / LAEA Europe", 5252, 4532, 19986); + return true; + case 2177: + record = new EpsgProjectedCrsRecord(5637, "TUREF / LCC Europe", 5252, 4500, 19985); + return true; + case 2178: + record = new EpsgProjectedCrsRecord(5638, "ISN2004 / LAEA Europe", 5324, 4532, 19986); + return true; + case 2179: + record = new EpsgProjectedCrsRecord(5639, "ISN2004 / LCC Europe", 5324, 4500, 19985); + return true; + case 2180: + record = new EpsgProjectedCrsRecord(5641, "SIRGAS 2000 / Brazil Mercator", 4674, 4499, 5640); + return true; + case 2181: + record = new EpsgProjectedCrsRecord(5643, "ED50 / SPBA LCC", 4230, 4400, 5642); + return true; + case 2182: + record = new EpsgProjectedCrsRecord(5644, "RGR92 / UTM zone 39S", 4627, 4400, 16139); + return true; + case 2183: + record = new EpsgProjectedCrsRecord(5646, "NAD83 / Vermont (ftUS)", 4269, 4497, 5645); + return true; + case 2184: + record = new EpsgProjectedCrsRecord(5649, "ETRS89 / UTM zone 31N (zE-N)", 4258, 4400, 5647); + return true; + case 2185: + record = new EpsgProjectedCrsRecord(5650, "ETRS89 / UTM zone 33N (zE-N)", 4258, 4400, 5648); + return true; + case 2186: + record = new EpsgProjectedCrsRecord(5651, "ETRS89 / UTM zone 31N (N-zE)", 4258, 4500, 5647); + return true; + case 2187: + record = new EpsgProjectedCrsRecord(5652, "ETRS89 / UTM zone 32N (N-zE)", 4258, 4500, 4648); + return true; + case 2188: + record = new EpsgProjectedCrsRecord(5653, "ETRS89 / UTM zone 33N (N-zE)", 4258, 4500, 5648); + return true; + case 2189: + record = new EpsgProjectedCrsRecord(5654, "NAD83(HARN) / Vermont (ftUS)", 4152, 4497, 5645); + return true; + case 2190: + record = new EpsgProjectedCrsRecord(5655, "NAD83(NSRS2007) / Vermont (ftUS)", 4759, 4497, 5645); + return true; + case 2191: + record = new EpsgProjectedCrsRecord(5659, "Monte Mario / TM Emilia-Romagna", 4265, 4499, 5658); + return true; + case 2192: + record = new EpsgProjectedCrsRecord(5663, "Pulkovo 1942(58) / Gauss-Kruger zone 3 (E-N)", 4179, 4400, 16203); + return true; + case 2193: + record = new EpsgProjectedCrsRecord(5664, "Pulkovo 1942(83) / Gauss-Kruger zone 2 (E-N)", 4178, 4400, 16202); + return true; + case 2194: + record = new EpsgProjectedCrsRecord(5665, "Pulkovo 1942(83) / Gauss-Kruger zone 3 (E-N)", 4178, 4400, 16203); + return true; + case 2195: + record = new EpsgProjectedCrsRecord(5666, "PD/83 / 3-degree Gauss-Kruger zone 3 (E-N)", 4746, 4400, 16263); + return true; + case 2196: + record = new EpsgProjectedCrsRecord(5667, "PD/83 / 3-degree Gauss-Kruger zone 4 (E-N)", 4746, 4400, 16264); + return true; + case 2197: + record = new EpsgProjectedCrsRecord(5668, "RD/83 / 3-degree Gauss-Kruger zone 4 (E-N)", 4745, 4400, 16264); + return true; + case 2198: + record = new EpsgProjectedCrsRecord(5669, "RD/83 / 3-degree Gauss-Kruger zone 5 (E-N)", 4745, 4400, 16265); + return true; + case 2199: + record = new EpsgProjectedCrsRecord(5670, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 3 (E-N)", 4179, 4400, 16263); + return true; + case 2200: + record = new EpsgProjectedCrsRecord(5671, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 4 (E-N)", 4179, 4400, 16264); + return true; + case 2201: + record = new EpsgProjectedCrsRecord(5672, "Pulkovo 1942(58) / 3-degree Gauss-Kruger zone 5 (E-N)", 4179, 4400, 16265); + return true; + case 2202: + record = new EpsgProjectedCrsRecord(5673, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 3 (E-N)", 4178, 4400, 16263); + return true; + case 2203: + record = new EpsgProjectedCrsRecord(5674, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 4 (E-N)", 4178, 4400, 16264); + return true; + case 2204: + record = new EpsgProjectedCrsRecord(5675, "Pulkovo 1942(83) / 3-degree Gauss-Kruger zone 5 (E-N)", 4178, 4400, 16265); + return true; + case 2205: + record = new EpsgProjectedCrsRecord(5676, "DHDN / 3-degree Gauss-Kruger zone 2 (E-N)", 4314, 4400, 16262); + return true; + case 2206: + record = new EpsgProjectedCrsRecord(5677, "DHDN / 3-degree Gauss-Kruger zone 3 (E-N)", 4314, 4400, 16263); + return true; + case 2207: + record = new EpsgProjectedCrsRecord(5678, "DHDN / 3-degree Gauss-Kruger zone 4 (E-N)", 4314, 4400, 16264); + return true; + case 2208: + record = new EpsgProjectedCrsRecord(5679, "DHDN / 3-degree Gauss-Kruger zone 5 (E-N)", 4314, 4400, 16265); + return true; + case 2209: + record = new EpsgProjectedCrsRecord(5680, "DHDN / 3-degree Gauss-Kruger zone 1 (E-N)", 4314, 4400, 16261); + return true; + case 2210: + record = new EpsgProjectedCrsRecord(5682, "DB_REF / 3-degree Gauss-Kruger zone 2 (E-N)", 5681, 4400, 16262); + return true; + case 2211: + record = new EpsgProjectedCrsRecord(5683, "DB_REF / 3-degree Gauss-Kruger zone 3 (E-N)", 5681, 4400, 16263); + return true; + case 2212: + record = new EpsgProjectedCrsRecord(5684, "DB_REF / 3-degree Gauss-Kruger zone 4 (E-N)", 5681, 4400, 16264); + return true; + case 2213: + record = new EpsgProjectedCrsRecord(5685, "DB_REF / 3-degree Gauss-Kruger zone 5 (E-N)", 5681, 4400, 16265); + return true; + case 2214: + record = new EpsgProjectedCrsRecord(5700, "NZGD2000 / UTM zone 1S", 4167, 4400, 16101); + return true; + case 2215: + record = new EpsgProjectedCrsRecord(5825, "AGD66 / ACT Standard Grid", 4202, 4400, 5824); + return true; + case 2216: + record = new EpsgProjectedCrsRecord(5836, "Yemen NGN96 / UTM zone 37N", 4163, 4400, 16037); + return true; + case 2217: + record = new EpsgProjectedCrsRecord(5837, "Yemen NGN96 / UTM zone 40N", 4163, 4400, 16040); + return true; + case 2218: + record = new EpsgProjectedCrsRecord(5839, "Peru96 / UTM zone 17S", 5373, 4400, 16117); + return true; + case 2219: + record = new EpsgProjectedCrsRecord(5842, "WGS 84 / TM 12 SE", 4326, 4400, 16612); + return true; + case 2220: + record = new EpsgProjectedCrsRecord(5844, "RGRDC 2005 / Congo TM zone 30", 4046, 4499, 17430); + return true; + case 2221: + record = new EpsgProjectedCrsRecord(5858, "SAD69(96) / UTM zone 22S", 5527, 4400, 16122); + return true; + case 2222: + record = new EpsgProjectedCrsRecord(5875, "SAD69(96) / UTM zone 18S", 5527, 4400, 16118); + return true; + case 2223: + record = new EpsgProjectedCrsRecord(5876, "SAD69(96) / UTM zone 19S", 5527, 4400, 16119); + return true; + case 2224: + record = new EpsgProjectedCrsRecord(5877, "SAD69(96) / UTM zone 20S", 5527, 4400, 16120); + return true; + case 2225: + record = new EpsgProjectedCrsRecord(5879, "Cadastre 1997 / UTM zone 38S", 4475, 4400, 16138); + return true; + case 2226: + record = new EpsgProjectedCrsRecord(5880, "SIRGAS 2000 / Brazil Polyconic", 4674, 4499, 19941); + return true; + case 2227: + record = new EpsgProjectedCrsRecord(5887, "TGD2005 / Tonga Map Grid", 5886, 4400, 5883); + return true; + case 2228: + record = new EpsgProjectedCrsRecord(5896, "VN-2000 / TM-3 zone 481", 4756, 4400, 5892); + return true; + case 2229: + record = new EpsgProjectedCrsRecord(5897, "VN-2000 / TM-3 zone 482", 4756, 4400, 5893); + return true; + case 2230: + record = new EpsgProjectedCrsRecord(5898, "VN-2000 / TM-3 zone 491", 4756, 4400, 5894); + return true; + case 2231: + record = new EpsgProjectedCrsRecord(5899, "VN-2000 / TM-3 107-45", 4756, 4400, 5895); + return true; + case 2232: + record = new EpsgProjectedCrsRecord(5921, "WGS 84 / EPSG Arctic Regional zone A1", 4326, 4400, 5906); + return true; + case 2233: + record = new EpsgProjectedCrsRecord(5922, "WGS 84 / EPSG Arctic Regional zone A2", 4326, 4400, 5907); + return true; + case 2234: + record = new EpsgProjectedCrsRecord(5923, "WGS 84 / EPSG Arctic Regional zone A3", 4326, 4400, 5908); + return true; + case 2235: + record = new EpsgProjectedCrsRecord(5924, "WGS 84 / EPSG Arctic Regional zone A4", 4326, 4400, 5909); + return true; + case 2236: + record = new EpsgProjectedCrsRecord(5925, "WGS 84 / EPSG Arctic Regional zone A5", 4326, 4400, 5910); + return true; + case 2237: + record = new EpsgProjectedCrsRecord(5926, "WGS 84 / EPSG Arctic Regional zone B1", 4326, 4400, 5911); + return true; + case 2238: + record = new EpsgProjectedCrsRecord(5927, "WGS 84 / EPSG Arctic Regional zone B2", 4326, 4400, 5912); + return true; + case 2239: + record = new EpsgProjectedCrsRecord(5928, "WGS 84 / EPSG Arctic Regional zone B3", 4326, 4400, 5913); + return true; + case 2240: + record = new EpsgProjectedCrsRecord(5929, "WGS 84 / EPSG Arctic Regional zone B4", 4326, 4400, 5914); + return true; + case 2241: + record = new EpsgProjectedCrsRecord(5930, "WGS 84 / EPSG Arctic Regional zone B5", 4326, 4400, 5915); + return true; + case 2242: + record = new EpsgProjectedCrsRecord(5931, "WGS 84 / EPSG Arctic Regional zone C1", 4326, 4400, 5916); + return true; + case 2243: + record = new EpsgProjectedCrsRecord(5932, "WGS 84 / EPSG Arctic Regional zone C2", 4326, 4400, 5917); + return true; + case 2244: + record = new EpsgProjectedCrsRecord(5933, "WGS 84 / EPSG Arctic Regional zone C3", 4326, 4400, 5918); + return true; + case 2245: + record = new EpsgProjectedCrsRecord(5934, "WGS 84 / EPSG Arctic Regional zone C4", 4326, 4400, 5919); + return true; + case 2246: + record = new EpsgProjectedCrsRecord(5935, "WGS 84 / EPSG Arctic Regional zone C5", 4326, 4400, 5920); + return true; + case 2247: + record = new EpsgProjectedCrsRecord(5936, "WGS 84 / EPSG Alaska Polar Stereographic", 4326, 4467, 5901); + return true; + case 2248: + record = new EpsgProjectedCrsRecord(5937, "WGS 84 / EPSG Canada Polar Stereographic", 4326, 4466, 5902); + return true; + case 2249: + record = new EpsgProjectedCrsRecord(5938, "WGS 84 / EPSG Greenland Polar Stereographic", 4326, 1036, 5903); + return true; + case 2250: + record = new EpsgProjectedCrsRecord(5939, "WGS 84 / EPSG Norway Polar Stereographic", 4326, 1037, 5904); + return true; + case 2251: + record = new EpsgProjectedCrsRecord(5940, "WGS 84 / EPSG Russia Polar Stereographic", 4326, 1038, 5905); + return true; + case 2252: + record = new EpsgProjectedCrsRecord(6050, "GR96 / EPSG Arctic zone 1-25", 4747, 4400, 5979); + return true; + case 2253: + record = new EpsgProjectedCrsRecord(6051, "GR96 / EPSG Arctic zone 2-18", 4747, 4400, 5987); + return true; + case 2254: + record = new EpsgProjectedCrsRecord(6052, "GR96 / EPSG Arctic zone 2-20", 4747, 4400, 5988); + return true; + case 2255: + record = new EpsgProjectedCrsRecord(6053, "GR96 / EPSG Arctic zone 3-29", 4747, 4400, 6002); + return true; + case 2256: + record = new EpsgProjectedCrsRecord(6054, "GR96 / EPSG Arctic zone 3-31", 4747, 4400, 6003); + return true; + case 2257: + record = new EpsgProjectedCrsRecord(6055, "GR96 / EPSG Arctic zone 3-33", 4747, 4400, 6004); + return true; + case 2258: + record = new EpsgProjectedCrsRecord(6056, "GR96 / EPSG Arctic zone 4-20", 4747, 4400, 6009); + return true; + case 2259: + record = new EpsgProjectedCrsRecord(6057, "GR96 / EPSG Arctic zone 4-22", 4747, 4400, 6010); + return true; + case 2260: + record = new EpsgProjectedCrsRecord(6058, "GR96 / EPSG Arctic zone 4-24", 4747, 4400, 6011); + return true; + case 2261: + record = new EpsgProjectedCrsRecord(6059, "GR96 / EPSG Arctic zone 5-41", 4747, 4400, 6035); + return true; + case 2262: + record = new EpsgProjectedCrsRecord(6060, "GR96 / EPSG Arctic zone 5-43", 4747, 4400, 6036); + return true; + case 2263: + record = new EpsgProjectedCrsRecord(6061, "GR96 / EPSG Arctic zone 5-45", 4747, 4400, 6037); + return true; + case 2264: + record = new EpsgProjectedCrsRecord(6062, "GR96 / EPSG Arctic zone 6-26", 4747, 4400, 6045); + return true; + case 2265: + record = new EpsgProjectedCrsRecord(6063, "GR96 / EPSG Arctic zone 6-28", 4747, 4400, 6046); + return true; + case 2266: + record = new EpsgProjectedCrsRecord(6064, "GR96 / EPSG Arctic zone 6-30", 4747, 4400, 6047); + return true; + case 2267: + record = new EpsgProjectedCrsRecord(6065, "GR96 / EPSG Arctic zone 7-11", 4747, 4400, 6048); + return true; + case 2268: + record = new EpsgProjectedCrsRecord(6066, "GR96 / EPSG Arctic zone 7-13", 4747, 4400, 6049); + return true; + case 2269: + record = new EpsgProjectedCrsRecord(6067, "GR96 / EPSG Arctic zone 8-20", 4747, 4400, 5943); + return true; + case 2270: + record = new EpsgProjectedCrsRecord(6068, "GR96 / EPSG Arctic zone 8-22", 4747, 4400, 5944); + return true; + case 2271: + record = new EpsgProjectedCrsRecord(6069, "ETRS89 / EPSG Arctic zone 2-22", 4258, 4400, 5989); + return true; + case 2272: + record = new EpsgProjectedCrsRecord(6070, "ETRS89 / EPSG Arctic zone 3-11", 4258, 4400, 5993); + return true; + case 2273: + record = new EpsgProjectedCrsRecord(6071, "ETRS89 / EPSG Arctic zone 4-26", 4258, 4400, 6012); + return true; + case 2274: + record = new EpsgProjectedCrsRecord(6072, "ETRS89 / EPSG Arctic zone 4-28", 4258, 4400, 6013); + return true; + case 2275: + record = new EpsgProjectedCrsRecord(6073, "ETRS89 / EPSG Arctic zone 5-11", 4258, 4400, 6020); + return true; + case 2276: + record = new EpsgProjectedCrsRecord(6074, "ETRS89 / EPSG Arctic zone 5-13", 4258, 4400, 6021); + return true; + case 2277: + record = new EpsgProjectedCrsRecord(6075, "WGS 84 / EPSG Arctic zone 2-24", 4326, 4400, 5990); + return true; + case 2278: + record = new EpsgProjectedCrsRecord(6076, "WGS 84 / EPSG Arctic zone 2-26", 4326, 4400, 5991); + return true; + case 2279: + record = new EpsgProjectedCrsRecord(6077, "WGS 84 / EPSG Arctic zone 3-13", 4326, 4400, 5994); + return true; + case 2280: + record = new EpsgProjectedCrsRecord(6078, "WGS 84 / EPSG Arctic zone 3-15", 4326, 4400, 5995); + return true; + case 2281: + record = new EpsgProjectedCrsRecord(6079, "WGS 84 / EPSG Arctic zone 3-17", 4326, 4400, 5996); + return true; + case 2282: + record = new EpsgProjectedCrsRecord(6080, "WGS 84 / EPSG Arctic zone 3-19", 4326, 4400, 5997); + return true; + case 2283: + record = new EpsgProjectedCrsRecord(6081, "WGS 84 / EPSG Arctic zone 4-30", 4326, 4400, 6014); + return true; + case 2284: + record = new EpsgProjectedCrsRecord(6082, "WGS 84 / EPSG Arctic zone 4-32", 4326, 4400, 6015); + return true; + case 2285: + record = new EpsgProjectedCrsRecord(6083, "WGS 84 / EPSG Arctic zone 4-34", 4326, 4400, 6016); + return true; + case 2286: + record = new EpsgProjectedCrsRecord(6084, "WGS 84 / EPSG Arctic zone 4-36", 4326, 4400, 6017); + return true; + case 2287: + record = new EpsgProjectedCrsRecord(6085, "WGS 84 / EPSG Arctic zone 4-38", 4326, 4400, 6018); + return true; + case 2288: + record = new EpsgProjectedCrsRecord(6086, "WGS 84 / EPSG Arctic zone 4-40", 4326, 4400, 6019); + return true; + case 2289: + record = new EpsgProjectedCrsRecord(6087, "WGS 84 / EPSG Arctic zone 5-15", 4326, 4400, 6022); + return true; + case 2290: + record = new EpsgProjectedCrsRecord(6088, "WGS 84 / EPSG Arctic zone 5-17", 4326, 4400, 6023); + return true; + case 2291: + record = new EpsgProjectedCrsRecord(6089, "WGS 84 / EPSG Arctic zone 5-19", 4326, 4400, 6024); + return true; + case 2292: + record = new EpsgProjectedCrsRecord(6090, "WGS 84 / EPSG Arctic zone 5-21", 4326, 4400, 6025); + return true; + case 2293: + record = new EpsgProjectedCrsRecord(6091, "WGS 84 / EPSG Arctic zone 5-23", 4326, 4400, 6026); + return true; + case 2294: + record = new EpsgProjectedCrsRecord(6092, "WGS 84 / EPSG Arctic zone 5-25", 4326, 4400, 6027); + return true; + case 2295: + record = new EpsgProjectedCrsRecord(6093, "WGS 84 / EPSG Arctic zone 5-27", 4326, 4400, 6028); + return true; + case 2296: + record = new EpsgProjectedCrsRecord(6094, "NAD83(NSRS2007) / EPSG Arctic zone 5-29", 4759, 4400, 6029); + return true; + case 2297: + record = new EpsgProjectedCrsRecord(6095, "NAD83(NSRS2007) / EPSG Arctic zone 5-31", 4759, 4400, 6030); + return true; + case 2298: + record = new EpsgProjectedCrsRecord(6096, "NAD83(NSRS2007) / EPSG Arctic zone 6-14", 4759, 4400, 6039); + return true; + case 2299: + record = new EpsgProjectedCrsRecord(6097, "NAD83(NSRS2007) / EPSG Arctic zone 6-16", 4759, 4400, 6040); + return true; + case 2300: + record = new EpsgProjectedCrsRecord(6098, "NAD83(CSRS) / EPSG Arctic zone 1-23", 4617, 4400, 5978); + return true; + case 2301: + record = new EpsgProjectedCrsRecord(6099, "NAD83(CSRS) / EPSG Arctic zone 2-14", 4617, 4400, 5985); + return true; + case 2302: + record = new EpsgProjectedCrsRecord(6100, "NAD83(CSRS) / EPSG Arctic zone 2-16", 4617, 4400, 5986); + return true; + case 2303: + record = new EpsgProjectedCrsRecord(6101, "NAD83(CSRS) / EPSG Arctic zone 3-25", 4617, 4400, 6000); + return true; + case 2304: + record = new EpsgProjectedCrsRecord(6102, "NAD83(CSRS) / EPSG Arctic zone 3-27", 4617, 4400, 6001); + return true; + case 2305: + record = new EpsgProjectedCrsRecord(6103, "NAD83(CSRS) / EPSG Arctic zone 3-29", 4617, 4400, 6002); + return true; + case 2306: + record = new EpsgProjectedCrsRecord(6104, "NAD83(CSRS) / EPSG Arctic zone 4-14", 4617, 4400, 6006); + return true; + case 2307: + record = new EpsgProjectedCrsRecord(6105, "NAD83(CSRS) / EPSG Arctic zone 4-16", 4617, 4400, 6007); + return true; + case 2308: + record = new EpsgProjectedCrsRecord(6106, "NAD83(CSRS) / EPSG Arctic zone 4-18", 4617, 4400, 6008); + return true; + case 2309: + record = new EpsgProjectedCrsRecord(6107, "NAD83(CSRS) / EPSG Arctic zone 5-33", 4617, 4400, 6031); + return true; + case 2310: + record = new EpsgProjectedCrsRecord(6108, "NAD83(CSRS) / EPSG Arctic zone 5-35", 4617, 4400, 6032); + return true; + case 2311: + record = new EpsgProjectedCrsRecord(6109, "NAD83(CSRS) / EPSG Arctic zone 5-37", 4617, 4400, 6033); + return true; + case 2312: + record = new EpsgProjectedCrsRecord(6110, "NAD83(CSRS) / EPSG Arctic zone 5-39", 4617, 4400, 6034); + return true; + case 2313: + record = new EpsgProjectedCrsRecord(6111, "NAD83(CSRS) / EPSG Arctic zone 6-18", 4617, 4400, 6041); + return true; + case 2314: + record = new EpsgProjectedCrsRecord(6112, "NAD83(CSRS) / EPSG Arctic zone 6-20", 4617, 4400, 6042); + return true; + case 2315: + record = new EpsgProjectedCrsRecord(6113, "NAD83(CSRS) / EPSG Arctic zone 6-22", 4617, 4400, 6043); + return true; + case 2316: + record = new EpsgProjectedCrsRecord(6114, "NAD83(CSRS) / EPSG Arctic zone 6-24", 4617, 4400, 6044); + return true; + case 2317: + record = new EpsgProjectedCrsRecord(6115, "WGS 84 / EPSG Arctic zone 1-27", 4326, 4400, 5980); + return true; + case 2318: + record = new EpsgProjectedCrsRecord(6116, "WGS 84 / EPSG Arctic zone 1-29", 4326, 4400, 5981); + return true; + case 2319: + record = new EpsgProjectedCrsRecord(6117, "WGS 84 / EPSG Arctic zone 1-31", 4326, 4400, 5982); + return true; + case 2320: + record = new EpsgProjectedCrsRecord(6118, "WGS 84 / EPSG Arctic zone 1-21", 4326, 4400, 5977); + return true; + case 2321: + record = new EpsgProjectedCrsRecord(6119, "WGS 84 / EPSG Arctic zone 2-28", 4326, 4400, 5992); + return true; + case 2322: + record = new EpsgProjectedCrsRecord(6120, "WGS 84 / EPSG Arctic zone 2-10", 4326, 4400, 5983); + return true; + case 2323: + record = new EpsgProjectedCrsRecord(6121, "WGS 84 / EPSG Arctic zone 2-12", 4326, 4400, 5984); + return true; + case 2324: + record = new EpsgProjectedCrsRecord(6122, "WGS 84 / EPSG Arctic zone 3-21", 4326, 4400, 5998); + return true; + case 2325: + record = new EpsgProjectedCrsRecord(6123, "WGS 84 / EPSG Arctic zone 3-23", 4326, 4400, 5999); + return true; + case 2326: + record = new EpsgProjectedCrsRecord(6124, "WGS 84 / EPSG Arctic zone 4-12", 4326, 4400, 6005); + return true; + case 2327: + record = new EpsgProjectedCrsRecord(6125, "ETRS89 / EPSG Arctic zone 5-47", 4258, 4400, 6038); + return true; + case 2328: + record = new EpsgProjectedCrsRecord(6128, "Grand Cayman National Grid 1959", 4723, 1039, 6127); + return true; + case 2329: + record = new EpsgProjectedCrsRecord(6129, "Sister Islands National Grid 1961", 4726, 1039, 6127); + return true; + case 2330: + record = new EpsgProjectedCrsRecord(6201, "NAD27 / Michigan Central", 4267, 4497, 6198); + return true; + case 2331: + record = new EpsgProjectedCrsRecord(6202, "NAD27 / Michigan South", 4267, 4497, 6199); + return true; + case 2332: + record = new EpsgProjectedCrsRecord(6204, "Macedonia State Coordinate System", 3906, 4498, 6203); + return true; + case 2333: + record = new EpsgProjectedCrsRecord(6210, "SIRGAS 2000 / UTM zone 23N", 4674, 4400, 16023); + return true; + case 2334: + record = new EpsgProjectedCrsRecord(6211, "SIRGAS 2000 / UTM zone 24N", 4674, 4400, 16024); + return true; + case 2335: + record = new EpsgProjectedCrsRecord(6244, "MAGNA-SIRGAS / Arauca urban grid", 4686, 4500, 6212); + return true; + case 2336: + record = new EpsgProjectedCrsRecord(6245, "MAGNA-SIRGAS / Armenia urban grid", 4686, 4500, 6213); + return true; + case 2337: + record = new EpsgProjectedCrsRecord(6246, "MAGNA-SIRGAS / Barranquilla urban grid", 4686, 4500, 6214); + return true; + case 2338: + record = new EpsgProjectedCrsRecord(6247, "MAGNA-SIRGAS / Bogota urban grid", 4686, 4500, 6215); + return true; + case 2339: + record = new EpsgProjectedCrsRecord(6248, "MAGNA-SIRGAS / Bucaramanga urban grid", 4686, 4500, 6216); + return true; + case 2340: + record = new EpsgProjectedCrsRecord(6249, "MAGNA-SIRGAS / Cali urban grid", 4686, 4500, 6217); + return true; + case 2341: + record = new EpsgProjectedCrsRecord(6250, "MAGNA-SIRGAS / Cartagena urban grid", 4686, 4500, 6218); + return true; + case 2342: + record = new EpsgProjectedCrsRecord(6251, "MAGNA-SIRGAS / Cucuta urban grid", 4686, 4500, 6219); + return true; + case 2343: + record = new EpsgProjectedCrsRecord(6252, "MAGNA-SIRGAS / Florencia urban grid", 4686, 4500, 6220); + return true; + case 2344: + record = new EpsgProjectedCrsRecord(6253, "MAGNA-SIRGAS / Ibague urban grid", 4686, 4500, 6221); + return true; + case 2345: + record = new EpsgProjectedCrsRecord(6254, "MAGNA-SIRGAS / Inirida urban grid", 4686, 4500, 6222); + return true; + case 2346: + record = new EpsgProjectedCrsRecord(6255, "MAGNA-SIRGAS / Leticia urban grid", 4686, 4500, 6223); + return true; + case 2347: + record = new EpsgProjectedCrsRecord(6256, "MAGNA-SIRGAS / Manizales urban grid", 4686, 4500, 6224); + return true; + case 2348: + record = new EpsgProjectedCrsRecord(6257, "MAGNA-SIRGAS / Medellin urban grid", 4686, 4500, 6225); + return true; + case 2349: + record = new EpsgProjectedCrsRecord(6258, "MAGNA-SIRGAS / Mitu urban grid", 4686, 4500, 6226); + return true; + case 2350: + record = new EpsgProjectedCrsRecord(6259, "MAGNA-SIRGAS / Mocoa urban grid", 4686, 4500, 6227); + return true; + case 2351: + record = new EpsgProjectedCrsRecord(6260, "MAGNA-SIRGAS / Monteria urban grid", 4686, 4500, 6228); + return true; + case 2352: + record = new EpsgProjectedCrsRecord(6261, "MAGNA-SIRGAS / Neiva urban grid", 4686, 4500, 6229); + return true; + case 2353: + record = new EpsgProjectedCrsRecord(6262, "MAGNA-SIRGAS / Pasto urban grid", 4686, 4500, 6230); + return true; + case 2354: + record = new EpsgProjectedCrsRecord(6263, "MAGNA-SIRGAS / Pereira urban grid", 4686, 4500, 6231); + return true; + case 2355: + record = new EpsgProjectedCrsRecord(6264, "MAGNA-SIRGAS / Popayan urban grid", 4686, 4500, 6232); + return true; + case 2356: + record = new EpsgProjectedCrsRecord(6265, "MAGNA-SIRGAS / Puerto Carreno urban grid", 4686, 4500, 6233); + return true; + case 2357: + record = new EpsgProjectedCrsRecord(6266, "MAGNA-SIRGAS / Quibdo urban grid", 4686, 4500, 6234); + return true; + case 2358: + record = new EpsgProjectedCrsRecord(6267, "MAGNA-SIRGAS / Riohacha urban grid", 4686, 4500, 6235); + return true; + case 2359: + record = new EpsgProjectedCrsRecord(6268, "MAGNA-SIRGAS / San Andres urban grid", 4686, 4500, 6236); + return true; + case 2360: + record = new EpsgProjectedCrsRecord(6269, "MAGNA-SIRGAS / San Jose del Guaviare urban grid", 4686, 4500, 6237); + return true; + case 2361: + record = new EpsgProjectedCrsRecord(6270, "MAGNA-SIRGAS / Santa Marta urban grid", 4686, 4500, 6238); + return true; + case 2362: + record = new EpsgProjectedCrsRecord(6271, "MAGNA-SIRGAS / Sucre urban grid", 4686, 4500, 6239); + return true; + case 2363: + record = new EpsgProjectedCrsRecord(6272, "MAGNA-SIRGAS / Tunja urban grid", 4686, 4500, 6240); + return true; + case 2364: + record = new EpsgProjectedCrsRecord(6273, "MAGNA-SIRGAS / Valledupar urban grid", 4686, 4500, 6241); + return true; + case 2365: + record = new EpsgProjectedCrsRecord(6274, "MAGNA-SIRGAS / Villavicencio urban grid", 4686, 4500, 6242); + return true; + case 2366: + record = new EpsgProjectedCrsRecord(6275, "MAGNA-SIRGAS / Yopal urban grid", 4686, 4500, 6243); + return true; + case 2367: + record = new EpsgProjectedCrsRecord(6307, "NAD83(CORS96) / Puerto Rico and Virgin Is.", 6783, 4499, 15230); + return true; + case 2368: + record = new EpsgProjectedCrsRecord(6312, "CGRS93 / Cyprus Local Transverse Mercator", 6311, 4400, 6308); + return true; + case 2369: + record = new EpsgProjectedCrsRecord(6316, "MGI 1901 / Balkans zone 7", 3906, 4498, 18277); + return true; + case 2370: + record = new EpsgProjectedCrsRecord(6328, "NAD83(2011) / UTM zone 59N", 6318, 4400, 16059); + return true; + case 2371: + record = new EpsgProjectedCrsRecord(6329, "NAD83(2011) / UTM zone 60N", 6318, 4400, 16060); + return true; + case 2372: + record = new EpsgProjectedCrsRecord(6330, "NAD83(2011) / UTM zone 1N", 6318, 4400, 16001); + return true; + case 2373: + record = new EpsgProjectedCrsRecord(6331, "NAD83(2011) / UTM zone 2N", 6318, 4400, 16002); + return true; + case 2374: + record = new EpsgProjectedCrsRecord(6332, "NAD83(2011) / UTM zone 3N", 6318, 4400, 16003); + return true; + case 2375: + record = new EpsgProjectedCrsRecord(6333, "NAD83(2011) / UTM zone 4N", 6318, 4400, 16004); + return true; + case 2376: + record = new EpsgProjectedCrsRecord(6334, "NAD83(2011) / UTM zone 5N", 6318, 4400, 16005); + return true; + case 2377: + record = new EpsgProjectedCrsRecord(6335, "NAD83(2011) / UTM zone 6N", 6318, 4400, 16006); + return true; + case 2378: + record = new EpsgProjectedCrsRecord(6336, "NAD83(2011) / UTM zone 7N", 6318, 4400, 16007); + return true; + case 2379: + record = new EpsgProjectedCrsRecord(6337, "NAD83(2011) / UTM zone 8N", 6318, 4400, 16008); + return true; + case 2380: + record = new EpsgProjectedCrsRecord(6338, "NAD83(2011) / UTM zone 9N", 6318, 4400, 16009); + return true; + case 2381: + record = new EpsgProjectedCrsRecord(6339, "NAD83(2011) / UTM zone 10N", 6318, 4400, 16010); + return true; + case 2382: + record = new EpsgProjectedCrsRecord(6340, "NAD83(2011) / UTM zone 11N", 6318, 4400, 16011); + return true; + case 2383: + record = new EpsgProjectedCrsRecord(6341, "NAD83(2011) / UTM zone 12N", 6318, 4400, 16012); + return true; + case 2384: + record = new EpsgProjectedCrsRecord(6342, "NAD83(2011) / UTM zone 13N", 6318, 4400, 16013); + return true; + case 2385: + record = new EpsgProjectedCrsRecord(6343, "NAD83(2011) / UTM zone 14N", 6318, 4400, 16014); + return true; + case 2386: + record = new EpsgProjectedCrsRecord(6344, "NAD83(2011) / UTM zone 15N", 6318, 4400, 16015); + return true; + case 2387: + record = new EpsgProjectedCrsRecord(6345, "NAD83(2011) / UTM zone 16N", 6318, 4400, 16016); + return true; + case 2388: + record = new EpsgProjectedCrsRecord(6346, "NAD83(2011) / UTM zone 17N", 6318, 4400, 16017); + return true; + case 2389: + record = new EpsgProjectedCrsRecord(6347, "NAD83(2011) / UTM zone 18N", 6318, 4400, 16018); + return true; + case 2390: + record = new EpsgProjectedCrsRecord(6348, "NAD83(2011) / UTM zone 19N", 6318, 4400, 16019); + return true; + case 2391: + record = new EpsgProjectedCrsRecord(6350, "NAD83(2011) / Conus Albers", 6318, 4499, 5068); + return true; + case 2392: + record = new EpsgProjectedCrsRecord(6351, "NAD83(2011) / EPSG Arctic zone 5-29", 6318, 4400, 6029); + return true; + case 2393: + record = new EpsgProjectedCrsRecord(6352, "NAD83(2011) / EPSG Arctic zone 5-31", 6318, 4400, 6030); + return true; + case 2394: + record = new EpsgProjectedCrsRecord(6353, "NAD83(2011) / EPSG Arctic zone 6-14", 6318, 4400, 6039); + return true; + case 2395: + record = new EpsgProjectedCrsRecord(6354, "NAD83(2011) / EPSG Arctic zone 6-16", 6318, 4400, 6040); + return true; + case 2396: + record = new EpsgProjectedCrsRecord(6355, "NAD83(2011) / Alabama East", 6318, 4499, 10131); + return true; + case 2397: + record = new EpsgProjectedCrsRecord(6356, "NAD83(2011) / Alabama West", 6318, 4499, 10132); + return true; + case 2398: + record = new EpsgProjectedCrsRecord(6362, "Mexico ITRF92 / LCC", 4483, 4500, 6361); + return true; + case 2399: + record = new EpsgProjectedCrsRecord(6366, "Mexico ITRF2008 / UTM zone 11N", 6365, 4400, 16011); + return true; + case 2400: + record = new EpsgProjectedCrsRecord(6367, "Mexico ITRF2008 / UTM zone 12N", 6365, 4400, 16012); + return true; + case 2401: + record = new EpsgProjectedCrsRecord(6368, "Mexico ITRF2008 / UTM zone 13N", 6365, 4400, 16013); + return true; + case 2402: + record = new EpsgProjectedCrsRecord(6369, "Mexico ITRF2008 / UTM zone 14N", 6365, 4400, 16014); + return true; + case 2403: + record = new EpsgProjectedCrsRecord(6370, "Mexico ITRF2008 / UTM zone 15N", 6365, 4400, 16015); + return true; + case 2404: + record = new EpsgProjectedCrsRecord(6371, "Mexico ITRF2008 / UTM zone 16N", 6365, 4400, 16016); + return true; + case 2405: + record = new EpsgProjectedCrsRecord(6372, "Mexico ITRF2008 / LCC", 6365, 4500, 6361); + return true; + case 2406: + record = new EpsgProjectedCrsRecord(6381, "UCS-2000 / Ukraine TM zone 7", 5561, 4530, 6374); + return true; + case 2407: + record = new EpsgProjectedCrsRecord(6382, "UCS-2000 / Ukraine TM zone 8", 5561, 4530, 6375); + return true; + case 2408: + record = new EpsgProjectedCrsRecord(6383, "UCS-2000 / Ukraine TM zone 9", 5561, 4530, 6376); + return true; + case 2409: + record = new EpsgProjectedCrsRecord(6384, "UCS-2000 / Ukraine TM zone 10", 5561, 4530, 6377); + return true; + case 2410: + record = new EpsgProjectedCrsRecord(6385, "UCS-2000 / Ukraine TM zone 11", 5561, 4530, 6378); + return true; + case 2411: + record = new EpsgProjectedCrsRecord(6386, "UCS-2000 / Ukraine TM zone 12", 5561, 4530, 6379); + return true; + case 2412: + record = new EpsgProjectedCrsRecord(6387, "UCS-2000 / Ukraine TM zone 13", 5561, 4530, 6380); + return true; + case 2413: + record = new EpsgProjectedCrsRecord(6391, "Cayman Islands National Grid 2011", 6135, 1039, 6390); + return true; + case 2414: + record = new EpsgProjectedCrsRecord(6393, "NAD83(2011) / Alaska Albers", 6318, 4499, 15021); + return true; + case 2415: + record = new EpsgProjectedCrsRecord(6394, "NAD83(2011) / Alaska zone 1", 6318, 4499, 15031); + return true; + case 2416: + record = new EpsgProjectedCrsRecord(6395, "NAD83(2011) / Alaska zone 2", 6318, 4499, 15032); + return true; + case 2417: + record = new EpsgProjectedCrsRecord(6396, "NAD83(2011) / Alaska zone 3", 6318, 4499, 15033); + return true; + case 2418: + record = new EpsgProjectedCrsRecord(6397, "NAD83(2011) / Alaska zone 4", 6318, 4499, 15034); + return true; + case 2419: + record = new EpsgProjectedCrsRecord(6398, "NAD83(2011) / Alaska zone 5", 6318, 4499, 15035); + return true; + case 2420: + record = new EpsgProjectedCrsRecord(6399, "NAD83(2011) / Alaska zone 6", 6318, 4499, 15036); + return true; + case 2421: + record = new EpsgProjectedCrsRecord(6400, "NAD83(2011) / Alaska zone 7", 6318, 4499, 15037); + return true; + case 2422: + record = new EpsgProjectedCrsRecord(6401, "NAD83(2011) / Alaska zone 8", 6318, 4499, 15038); + return true; + case 2423: + record = new EpsgProjectedCrsRecord(6402, "NAD83(2011) / Alaska zone 9", 6318, 4499, 15039); + return true; + case 2424: + record = new EpsgProjectedCrsRecord(6403, "NAD83(2011) / Alaska zone 10", 6318, 4499, 15040); + return true; + case 2425: + record = new EpsgProjectedCrsRecord(6404, "NAD83(2011) / Arizona Central", 6318, 4499, 10232); + return true; + case 2426: + record = new EpsgProjectedCrsRecord(6405, "NAD83(2011) / Arizona Central (ft)", 6318, 4495, 15305); + return true; + case 2427: + record = new EpsgProjectedCrsRecord(6406, "NAD83(2011) / Arizona East", 6318, 4499, 10231); + return true; + case 2428: + record = new EpsgProjectedCrsRecord(6407, "NAD83(2011) / Arizona East (ft)", 6318, 4495, 15304); + return true; + case 2429: + record = new EpsgProjectedCrsRecord(6408, "NAD83(2011) / Arizona West", 6318, 4499, 10233); + return true; + case 2430: + record = new EpsgProjectedCrsRecord(6409, "NAD83(2011) / Arizona West (ft)", 6318, 4495, 15306); + return true; + case 2431: + record = new EpsgProjectedCrsRecord(6410, "NAD83(2011) / Arkansas North", 6318, 4499, 10331); + return true; + case 2432: + record = new EpsgProjectedCrsRecord(6411, "NAD83(2011) / Arkansas North (ftUS)", 6318, 4497, 15385); + return true; + case 2433: + record = new EpsgProjectedCrsRecord(6412, "NAD83(2011) / Arkansas South", 6318, 4499, 10332); + return true; + case 2434: + record = new EpsgProjectedCrsRecord(6413, "NAD83(2011) / Arkansas South (ftUS)", 6318, 4497, 15386); + return true; + case 2435: + record = new EpsgProjectedCrsRecord(6414, "NAD83(2011) / California Albers", 6318, 4499, 10420); + return true; + case 2436: + record = new EpsgProjectedCrsRecord(6415, "NAD83(2011) / California zone 1", 6318, 4499, 10431); + return true; + case 2437: + record = new EpsgProjectedCrsRecord(6416, "NAD83(2011) / California zone 1 (ftUS)", 6318, 4497, 15307); + return true; + case 2438: + record = new EpsgProjectedCrsRecord(6417, "NAD83(2011) / California zone 2", 6318, 4499, 10432); + return true; + case 2439: + record = new EpsgProjectedCrsRecord(6418, "NAD83(2011) / California zone 2 (ftUS)", 6318, 4497, 15308); + return true; + case 2440: + record = new EpsgProjectedCrsRecord(6419, "NAD83(2011) / California zone 3", 6318, 4499, 10433); + return true; + case 2441: + record = new EpsgProjectedCrsRecord(6420, "NAD83(2011) / California zone 3 (ftUS)", 6318, 4497, 15309); + return true; + case 2442: + record = new EpsgProjectedCrsRecord(6421, "NAD83(2011) / California zone 4", 6318, 4499, 10434); + return true; + case 2443: + record = new EpsgProjectedCrsRecord(6422, "NAD83(2011) / California zone 4 (ftUS)", 6318, 4497, 15310); + return true; + case 2444: + record = new EpsgProjectedCrsRecord(6423, "NAD83(2011) / California zone 5", 6318, 4499, 10435); + return true; + case 2445: + record = new EpsgProjectedCrsRecord(6424, "NAD83(2011) / California zone 5 (ftUS)", 6318, 4497, 15311); + return true; + case 2446: + record = new EpsgProjectedCrsRecord(6425, "NAD83(2011) / California zone 6", 6318, 4499, 10436); + return true; + case 2447: + record = new EpsgProjectedCrsRecord(6426, "NAD83(2011) / California zone 6 (ftUS)", 6318, 4497, 15312); + return true; + case 2448: + record = new EpsgProjectedCrsRecord(6427, "NAD83(2011) / Colorado Central", 6318, 4499, 10532); + return true; + case 2449: + record = new EpsgProjectedCrsRecord(6428, "NAD83(2011) / Colorado Central (ftUS)", 6318, 4497, 15314); + return true; + case 2450: + record = new EpsgProjectedCrsRecord(6429, "NAD83(2011) / Colorado North", 6318, 4499, 10531); + return true; + case 2451: + record = new EpsgProjectedCrsRecord(6430, "NAD83(2011) / Colorado North (ftUS)", 6318, 4497, 15313); + return true; + case 2452: + record = new EpsgProjectedCrsRecord(6431, "NAD83(2011) / Colorado South", 6318, 4499, 10533); + return true; + case 2453: + record = new EpsgProjectedCrsRecord(6432, "NAD83(2011) / Colorado South (ftUS)", 6318, 4497, 15315); + return true; + case 2454: + record = new EpsgProjectedCrsRecord(6433, "NAD83(2011) / Connecticut", 6318, 4499, 10630); + return true; + case 2455: + record = new EpsgProjectedCrsRecord(6434, "NAD83(2011) / Connecticut (ftUS)", 6318, 4497, 15316); + return true; + case 2456: + record = new EpsgProjectedCrsRecord(6435, "NAD83(2011) / Delaware", 6318, 4499, 10730); + return true; + case 2457: + record = new EpsgProjectedCrsRecord(6436, "NAD83(2011) / Delaware (ftUS)", 6318, 4497, 15317); + return true; + case 2458: + record = new EpsgProjectedCrsRecord(6437, "NAD83(2011) / Florida East", 6318, 4499, 10931); + return true; + case 2459: + record = new EpsgProjectedCrsRecord(6438, "NAD83(2011) / Florida East (ftUS)", 6318, 4497, 15318); + return true; + case 2460: + record = new EpsgProjectedCrsRecord(6439, "NAD83(2011) / Florida GDL Albers", 6318, 4499, 10934); + return true; + case 2461: + record = new EpsgProjectedCrsRecord(6440, "NAD83(2011) / Florida North", 6318, 4499, 10933); + return true; + case 2462: + record = new EpsgProjectedCrsRecord(6441, "NAD83(2011) / Florida North (ftUS)", 6318, 4497, 15320); + return true; + case 2463: + record = new EpsgProjectedCrsRecord(6442, "NAD83(2011) / Florida West", 6318, 4499, 10932); + return true; + case 2464: + record = new EpsgProjectedCrsRecord(6443, "NAD83(2011) / Florida West (ftUS)", 6318, 4497, 15319); + return true; + case 2465: + record = new EpsgProjectedCrsRecord(6444, "NAD83(2011) / Georgia East", 6318, 4499, 11031); + return true; + case 2466: + record = new EpsgProjectedCrsRecord(6445, "NAD83(2011) / Georgia East (ftUS)", 6318, 4497, 15321); + return true; + case 2467: + record = new EpsgProjectedCrsRecord(6446, "NAD83(2011) / Georgia West", 6318, 4499, 11032); + return true; + case 2468: + record = new EpsgProjectedCrsRecord(6447, "NAD83(2011) / Georgia West (ftUS)", 6318, 4497, 15322); + return true; + case 2469: + record = new EpsgProjectedCrsRecord(6448, "NAD83(2011) / Idaho Central", 6318, 4499, 11132); + return true; + case 2470: + record = new EpsgProjectedCrsRecord(6449, "NAD83(2011) / Idaho Central (ftUS)", 6318, 4497, 15324); + return true; + case 2471: + record = new EpsgProjectedCrsRecord(6450, "NAD83(2011) / Idaho East", 6318, 4499, 11131); + return true; + case 2472: + record = new EpsgProjectedCrsRecord(6451, "NAD83(2011) / Idaho East (ftUS)", 6318, 4497, 15323); + return true; + case 2473: + record = new EpsgProjectedCrsRecord(6452, "NAD83(2011) / Idaho West", 6318, 4499, 11133); + return true; + case 2474: + record = new EpsgProjectedCrsRecord(6453, "NAD83(2011) / Idaho West (ftUS)", 6318, 4497, 15325); + return true; + case 2475: + record = new EpsgProjectedCrsRecord(6454, "NAD83(2011) / Illinois East", 6318, 4499, 11231); + return true; + case 2476: + record = new EpsgProjectedCrsRecord(6455, "NAD83(2011) / Illinois East (ftUS)", 6318, 4497, 15387); + return true; + case 2477: + record = new EpsgProjectedCrsRecord(6456, "NAD83(2011) / Illinois West", 6318, 4499, 11232); + return true; + case 2478: + record = new EpsgProjectedCrsRecord(6457, "NAD83(2011) / Illinois West (ftUS)", 6318, 4497, 15388); + return true; + case 2479: + record = new EpsgProjectedCrsRecord(6458, "NAD83(2011) / Indiana East", 6318, 4499, 11331); + return true; + case 2480: + record = new EpsgProjectedCrsRecord(6459, "NAD83(2011) / Indiana East (ftUS)", 6318, 4497, 15372); + return true; + case 2481: + record = new EpsgProjectedCrsRecord(6460, "NAD83(2011) / Indiana West", 6318, 4499, 11332); + return true; + case 2482: + record = new EpsgProjectedCrsRecord(6461, "NAD83(2011) / Indiana West (ftUS)", 6318, 4497, 15373); + return true; + case 2483: + record = new EpsgProjectedCrsRecord(6462, "NAD83(2011) / Iowa North", 6318, 4499, 11431); + return true; + case 2484: + record = new EpsgProjectedCrsRecord(6463, "NAD83(2011) / Iowa North (ftUS)", 6318, 4497, 15377); + return true; + case 2485: + record = new EpsgProjectedCrsRecord(6464, "NAD83(2011) / Iowa South", 6318, 4499, 11432); + return true; + case 2486: + record = new EpsgProjectedCrsRecord(6465, "NAD83(2011) / Iowa South (ftUS)", 6318, 4497, 15378); + return true; + case 2487: + record = new EpsgProjectedCrsRecord(6466, "NAD83(2011) / Kansas North", 6318, 4499, 11531); + return true; + case 2488: + record = new EpsgProjectedCrsRecord(6467, "NAD83(2011) / Kansas North (ftUS)", 6318, 4497, 15379); + return true; + case 2489: + record = new EpsgProjectedCrsRecord(6468, "NAD83(2011) / Kansas South", 6318, 4499, 11532); + return true; + case 2490: + record = new EpsgProjectedCrsRecord(6469, "NAD83(2011) / Kansas South (ftUS)", 6318, 4497, 15380); + return true; + case 2491: + record = new EpsgProjectedCrsRecord(6470, "NAD83(2011) / Kentucky North", 6318, 4499, 15303); + return true; + case 2492: + record = new EpsgProjectedCrsRecord(6471, "NAD83(2011) / Kentucky North (ftUS)", 6318, 4497, 15328); + return true; + case 2493: + record = new EpsgProjectedCrsRecord(6472, "NAD83(2011) / Kentucky Single Zone", 6318, 4499, 11630); + return true; + case 2494: + record = new EpsgProjectedCrsRecord(6473, "NAD83(2011) / Kentucky Single Zone (ftUS)", 6318, 4497, 15375); + return true; + case 2495: + record = new EpsgProjectedCrsRecord(6474, "NAD83(2011) / Kentucky South", 6318, 4499, 11632); + return true; + case 2496: + record = new EpsgProjectedCrsRecord(6475, "NAD83(2011) / Kentucky South (ftUS)", 6318, 4497, 15329); + return true; + case 2497: + record = new EpsgProjectedCrsRecord(6476, "NAD83(2011) / Louisiana North", 6318, 4499, 11731); + return true; + case 2498: + record = new EpsgProjectedCrsRecord(6477, "NAD83(2011) / Louisiana North (ftUS)", 6318, 4497, 15391); + return true; + case 2499: + record = new EpsgProjectedCrsRecord(6478, "NAD83(2011) / Louisiana South", 6318, 4499, 11732); + return true; + case 2500: + record = new EpsgProjectedCrsRecord(6479, "NAD83(2011) / Louisiana South (ftUS)", 6318, 4497, 15392); + return true; + case 2501: + record = new EpsgProjectedCrsRecord(6480, "NAD83(2011) / Maine CS2000 Central", 6318, 4499, 11854); + return true; + case 2502: + record = new EpsgProjectedCrsRecord(6481, "NAD83(2011) / Maine CS2000 East", 6318, 4499, 11851); + return true; + case 2503: + record = new EpsgProjectedCrsRecord(6482, "NAD83(2011) / Maine CS2000 West", 6318, 4499, 11853); + return true; + case 2504: + record = new EpsgProjectedCrsRecord(6483, "NAD83(2011) / Maine East", 6318, 4499, 11831); + return true; + case 2505: + record = new EpsgProjectedCrsRecord(6484, "NAD83(2011) / Maine East (ftUS)", 6318, 4497, 11833); + return true; + case 2506: + record = new EpsgProjectedCrsRecord(6485, "NAD83(2011) / Maine West", 6318, 4499, 11832); + return true; + case 2507: + record = new EpsgProjectedCrsRecord(6486, "NAD83(2011) / Maine West (ftUS)", 6318, 4497, 11834); + return true; + case 2508: + record = new EpsgProjectedCrsRecord(6487, "NAD83(2011) / Maryland", 6318, 4499, 11930); + return true; + case 2509: + record = new EpsgProjectedCrsRecord(6488, "NAD83(2011) / Maryland (ftUS)", 6318, 4497, 15330); + return true; + case 2510: + record = new EpsgProjectedCrsRecord(6489, "NAD83(2011) / Massachusetts Island", 6318, 4499, 12032); + return true; + case 2511: + record = new EpsgProjectedCrsRecord(6490, "NAD83(2011) / Massachusetts Island (ftUS)", 6318, 4497, 15332); + return true; + case 2512: + record = new EpsgProjectedCrsRecord(6491, "NAD83(2011) / Massachusetts Mainland", 6318, 4499, 12031); + return true; + case 2513: + record = new EpsgProjectedCrsRecord(6492, "NAD83(2011) / Massachusetts Mainland (ftUS)", 6318, 4497, 15331); + return true; + case 2514: + record = new EpsgProjectedCrsRecord(6493, "NAD83(2011) / Michigan Central", 6318, 4499, 12142); + return true; + case 2515: + record = new EpsgProjectedCrsRecord(6494, "NAD83(2011) / Michigan Central (ft)", 6318, 4495, 15334); + return true; + case 2516: + record = new EpsgProjectedCrsRecord(6495, "NAD83(2011) / Michigan North", 6318, 4499, 12141); + return true; + case 2517: + record = new EpsgProjectedCrsRecord(6496, "NAD83(2011) / Michigan North (ft)", 6318, 4495, 15333); + return true; + case 2518: + record = new EpsgProjectedCrsRecord(6497, "NAD83(2011) / Michigan Oblique Mercator", 6318, 4499, 12150); + return true; + case 2519: + record = new EpsgProjectedCrsRecord(6498, "NAD83(2011) / Michigan South", 6318, 4499, 12143); + return true; + case 2520: + record = new EpsgProjectedCrsRecord(6499, "NAD83(2011) / Michigan South (ft)", 6318, 4495, 15335); + return true; + case 2521: + record = new EpsgProjectedCrsRecord(6500, "NAD83(2011) / Minnesota Central", 6318, 4499, 12232); + return true; + case 2522: + record = new EpsgProjectedCrsRecord(6501, "NAD83(2011) / Minnesota Central (ftUS)", 6318, 4497, 12235); + return true; + case 2523: + record = new EpsgProjectedCrsRecord(6502, "NAD83(2011) / Minnesota North", 6318, 4499, 12231); + return true; + case 2524: + record = new EpsgProjectedCrsRecord(6503, "NAD83(2011) / Minnesota North (ftUS)", 6318, 4497, 12234); + return true; + case 2525: + record = new EpsgProjectedCrsRecord(6504, "NAD83(2011) / Minnesota South", 6318, 4499, 12233); + return true; + case 2526: + record = new EpsgProjectedCrsRecord(6505, "NAD83(2011) / Minnesota South (ftUS)", 6318, 4497, 12236); + return true; + case 2527: + record = new EpsgProjectedCrsRecord(6506, "NAD83(2011) / Mississippi East", 6318, 4499, 12331); + return true; + case 2528: + record = new EpsgProjectedCrsRecord(6507, "NAD83(2011) / Mississippi East (ftUS)", 6318, 4497, 15336); + return true; + case 2529: + record = new EpsgProjectedCrsRecord(6508, "NAD83(2011) / Mississippi TM", 6318, 4499, 3813); + return true; + case 2530: + record = new EpsgProjectedCrsRecord(6509, "NAD83(2011) / Mississippi West", 6318, 4499, 12332); + return true; + case 2531: + record = new EpsgProjectedCrsRecord(6510, "NAD83(2011) / Mississippi West (ftUS)", 6318, 4497, 15337); + return true; + case 2532: + record = new EpsgProjectedCrsRecord(6511, "NAD83(2011) / Missouri Central", 6318, 4499, 12432); + return true; + case 2533: + record = new EpsgProjectedCrsRecord(6512, "NAD83(2011) / Missouri East", 6318, 4499, 12431); + return true; + case 2534: + record = new EpsgProjectedCrsRecord(6513, "NAD83(2011) / Missouri West", 6318, 4499, 12433); + return true; + case 2535: + record = new EpsgProjectedCrsRecord(6514, "NAD83(2011) / Montana", 6318, 4499, 12530); + return true; + case 2536: + record = new EpsgProjectedCrsRecord(6515, "NAD83(2011) / Montana (ft)", 6318, 4495, 15338); + return true; + case 2537: + record = new EpsgProjectedCrsRecord(6516, "NAD83(2011) / Nebraska", 6318, 4499, 12630); + return true; + case 2538: + record = new EpsgProjectedCrsRecord(6518, "NAD83(2011) / Nevada Central", 6318, 4499, 12732); + return true; + case 2539: + record = new EpsgProjectedCrsRecord(6519, "NAD83(2011) / Nevada Central (ftUS)", 6318, 4497, 15382); + return true; + case 2540: + record = new EpsgProjectedCrsRecord(6520, "NAD83(2011) / Nevada East", 6318, 4499, 12731); + return true; + case 2541: + record = new EpsgProjectedCrsRecord(6521, "NAD83(2011) / Nevada East (ftUS)", 6318, 4497, 15381); + return true; + case 2542: + record = new EpsgProjectedCrsRecord(6522, "NAD83(2011) / Nevada West", 6318, 4499, 12733); + return true; + case 2543: + record = new EpsgProjectedCrsRecord(6523, "NAD83(2011) / Nevada West (ftUS)", 6318, 4497, 15383); + return true; + case 2544: + record = new EpsgProjectedCrsRecord(6524, "NAD83(2011) / New Hampshire", 6318, 4499, 12830); + return true; + case 2545: + record = new EpsgProjectedCrsRecord(6525, "NAD83(2011) / New Hampshire (ftUS)", 6318, 4497, 15389); + return true; + case 2546: + record = new EpsgProjectedCrsRecord(6526, "NAD83(2011) / New Jersey", 6318, 4499, 12930); + return true; + case 2547: + record = new EpsgProjectedCrsRecord(6527, "NAD83(2011) / New Jersey (ftUS)", 6318, 4497, 15384); + return true; + case 2548: + record = new EpsgProjectedCrsRecord(6528, "NAD83(2011) / New Mexico Central", 6318, 4499, 13032); + return true; + case 2549: + record = new EpsgProjectedCrsRecord(6529, "NAD83(2011) / New Mexico Central (ftUS)", 6318, 4497, 15340); + return true; + case 2550: + record = new EpsgProjectedCrsRecord(6530, "NAD83(2011) / New Mexico East", 6318, 4499, 13031); + return true; + case 2551: + record = new EpsgProjectedCrsRecord(6531, "NAD83(2011) / New Mexico East (ftUS)", 6318, 4497, 15339); + return true; + case 2552: + record = new EpsgProjectedCrsRecord(6532, "NAD83(2011) / New Mexico West", 6318, 4499, 13033); + return true; + case 2553: + record = new EpsgProjectedCrsRecord(6533, "NAD83(2011) / New Mexico West (ftUS)", 6318, 4497, 15341); + return true; + case 2554: + record = new EpsgProjectedCrsRecord(6534, "NAD83(2011) / New York Central", 6318, 4499, 13132); + return true; + case 2555: + record = new EpsgProjectedCrsRecord(6535, "NAD83(2011) / New York Central (ftUS)", 6318, 4497, 15343); + return true; + case 2556: + record = new EpsgProjectedCrsRecord(6536, "NAD83(2011) / New York East", 6318, 4499, 13131); + return true; + case 2557: + record = new EpsgProjectedCrsRecord(6537, "NAD83(2011) / New York East (ftUS)", 6318, 4497, 15342); + return true; + case 2558: + record = new EpsgProjectedCrsRecord(6538, "NAD83(2011) / New York Long Island", 6318, 4499, 13134); + return true; + case 2559: + record = new EpsgProjectedCrsRecord(6539, "NAD83(2011) / New York Long Island (ftUS)", 6318, 4497, 15345); + return true; + case 2560: + record = new EpsgProjectedCrsRecord(6540, "NAD83(2011) / New York West", 6318, 4499, 13133); + return true; + case 2561: + record = new EpsgProjectedCrsRecord(6541, "NAD83(2011) / New York West (ftUS)", 6318, 4497, 15344); + return true; + case 2562: + record = new EpsgProjectedCrsRecord(6542, "NAD83(2011) / North Carolina", 6318, 4499, 13230); + return true; + case 2563: + record = new EpsgProjectedCrsRecord(6543, "NAD83(2011) / North Carolina (ftUS)", 6318, 4497, 15346); + return true; + case 2564: + record = new EpsgProjectedCrsRecord(6544, "NAD83(2011) / North Dakota North", 6318, 4499, 13331); + return true; + case 2565: + record = new EpsgProjectedCrsRecord(6545, "NAD83(2011) / North Dakota North (ft)", 6318, 4495, 15347); + return true; + case 2566: + record = new EpsgProjectedCrsRecord(6546, "NAD83(2011) / North Dakota South", 6318, 4499, 13332); + return true; + case 2567: + record = new EpsgProjectedCrsRecord(6547, "NAD83(2011) / North Dakota South (ft)", 6318, 4495, 15348); + return true; + case 2568: + record = new EpsgProjectedCrsRecord(6548, "NAD83(2011) / Ohio North", 6318, 4499, 13431); + return true; + case 2569: + record = new EpsgProjectedCrsRecord(6549, "NAD83(2011) / Ohio North (ftUS)", 6318, 4497, 13433); + return true; + case 2570: + record = new EpsgProjectedCrsRecord(6550, "NAD83(2011) / Ohio South", 6318, 4499, 13432); + return true; + case 2571: + record = new EpsgProjectedCrsRecord(6551, "NAD83(2011) / Ohio South (ftUS)", 6318, 4497, 13434); + return true; + case 2572: + record = new EpsgProjectedCrsRecord(6552, "NAD83(2011) / Oklahoma North", 6318, 4499, 13531); + return true; + case 2573: + record = new EpsgProjectedCrsRecord(6553, "NAD83(2011) / Oklahoma North (ftUS)", 6318, 4497, 15349); + return true; + case 2574: + record = new EpsgProjectedCrsRecord(6554, "NAD83(2011) / Oklahoma South", 6318, 4499, 13532); + return true; + case 2575: + record = new EpsgProjectedCrsRecord(6555, "NAD83(2011) / Oklahoma South (ftUS)", 6318, 4497, 15350); + return true; + case 2576: + record = new EpsgProjectedCrsRecord(6556, "NAD83(2011) / Oregon LCC (m)", 6318, 4499, 13633); + return true; + case 2577: + record = new EpsgProjectedCrsRecord(6557, "NAD83(2011) / Oregon GIC Lambert (ft)", 6318, 4495, 15374); + return true; + case 2578: + record = new EpsgProjectedCrsRecord(6558, "NAD83(2011) / Oregon North", 6318, 4499, 13631); + return true; + case 2579: + record = new EpsgProjectedCrsRecord(6559, "NAD83(2011) / Oregon North (ft)", 6318, 4495, 15351); + return true; + case 2580: + record = new EpsgProjectedCrsRecord(6560, "NAD83(2011) / Oregon South", 6318, 4499, 13632); + return true; + case 2581: + record = new EpsgProjectedCrsRecord(6561, "NAD83(2011) / Oregon South (ft)", 6318, 4495, 15352); + return true; + case 2582: + record = new EpsgProjectedCrsRecord(6562, "NAD83(2011) / Pennsylvania North", 6318, 4499, 13731); + return true; + case 2583: + record = new EpsgProjectedCrsRecord(6563, "NAD83(2011) / Pennsylvania North (ftUS)", 6318, 4497, 15353); + return true; + case 2584: + record = new EpsgProjectedCrsRecord(6564, "NAD83(2011) / Pennsylvania South", 6318, 4499, 13732); + return true; + case 2585: + record = new EpsgProjectedCrsRecord(6565, "NAD83(2011) / Pennsylvania South (ftUS)", 6318, 4497, 15354); + return true; + case 2586: + record = new EpsgProjectedCrsRecord(6566, "NAD83(2011) / Puerto Rico and Virgin Is.", 6318, 4499, 15230); + return true; + case 2587: + record = new EpsgProjectedCrsRecord(6567, "NAD83(2011) / Rhode Island", 6318, 4499, 13830); + return true; + case 2588: + record = new EpsgProjectedCrsRecord(6568, "NAD83(2011) / Rhode Island (ftUS)", 6318, 4497, 15390); + return true; + case 2589: + record = new EpsgProjectedCrsRecord(6569, "NAD83(2011) / South Carolina", 6318, 4499, 13930); + return true; + case 2590: + record = new EpsgProjectedCrsRecord(6570, "NAD83(2011) / South Carolina (ft)", 6318, 4495, 15355); + return true; + case 2591: + record = new EpsgProjectedCrsRecord(6571, "NAD83(2011) / South Dakota North", 6318, 4499, 14031); + return true; + case 2592: + record = new EpsgProjectedCrsRecord(6572, "NAD83(2011) / South Dakota North (ftUS)", 6318, 4497, 15394); + return true; + case 2593: + record = new EpsgProjectedCrsRecord(6573, "NAD83(2011) / South Dakota South", 6318, 4499, 14032); + return true; + case 2594: + record = new EpsgProjectedCrsRecord(6574, "NAD83(2011) / South Dakota South (ftUS)", 6318, 4497, 15395); + return true; + case 2595: + record = new EpsgProjectedCrsRecord(6575, "NAD83(2011) / Tennessee", 6318, 4499, 14130); + return true; + case 2596: + record = new EpsgProjectedCrsRecord(6576, "NAD83(2011) / Tennessee (ftUS)", 6318, 4497, 15356); + return true; + case 2597: + record = new EpsgProjectedCrsRecord(6577, "NAD83(2011) / Texas Central", 6318, 4499, 14233); + return true; + case 2598: + record = new EpsgProjectedCrsRecord(6578, "NAD83(2011) / Texas Central (ftUS)", 6318, 4497, 15359); + return true; + case 2599: + record = new EpsgProjectedCrsRecord(6579, "NAD83(2011) / Texas Centric Albers Equal Area", 6318, 4499, 14254); + return true; + case 2600: + record = new EpsgProjectedCrsRecord(6580, "NAD83(2011) / Texas Centric Lambert Conformal", 6318, 4499, 14253); + return true; + case 2601: + record = new EpsgProjectedCrsRecord(6581, "NAD83(2011) / Texas North", 6318, 4499, 14231); + return true; + case 2602: + record = new EpsgProjectedCrsRecord(6582, "NAD83(2011) / Texas North (ftUS)", 6318, 4497, 15357); + return true; + case 2603: + record = new EpsgProjectedCrsRecord(6583, "NAD83(2011) / Texas North Central", 6318, 4499, 14232); + return true; + case 2604: + record = new EpsgProjectedCrsRecord(6584, "NAD83(2011) / Texas North Central (ftUS)", 6318, 4497, 15358); + return true; + case 2605: + record = new EpsgProjectedCrsRecord(6585, "NAD83(2011) / Texas South", 6318, 4499, 14235); + return true; + case 2606: + record = new EpsgProjectedCrsRecord(6586, "NAD83(2011) / Texas South (ftUS)", 6318, 4497, 15361); + return true; + case 2607: + record = new EpsgProjectedCrsRecord(6587, "NAD83(2011) / Texas South Central", 6318, 4499, 14234); + return true; + case 2608: + record = new EpsgProjectedCrsRecord(6588, "NAD83(2011) / Texas South Central (ftUS)", 6318, 4497, 15360); + return true; + case 2609: + record = new EpsgProjectedCrsRecord(6589, "NAD83(2011) / Vermont", 6318, 4499, 14430); + return true; + case 2610: + record = new EpsgProjectedCrsRecord(6590, "NAD83(2011) / Vermont (ftUS)", 6318, 4497, 5645); + return true; + case 2611: + record = new EpsgProjectedCrsRecord(6591, "NAD83(2011) / Virginia Lambert", 6318, 4499, 3967); + return true; + case 2612: + record = new EpsgProjectedCrsRecord(6592, "NAD83(2011) / Virginia North", 6318, 4499, 14531); + return true; + case 2613: + record = new EpsgProjectedCrsRecord(6593, "NAD83(2011) / Virginia North (ftUS)", 6318, 4497, 15365); + return true; + case 2614: + record = new EpsgProjectedCrsRecord(6594, "NAD83(2011) / Virginia South", 6318, 4499, 14532); + return true; + case 2615: + record = new EpsgProjectedCrsRecord(6595, "NAD83(2011) / Virginia South (ftUS)", 6318, 4497, 15366); + return true; + case 2616: + record = new EpsgProjectedCrsRecord(6596, "NAD83(2011) / Washington North", 6318, 4499, 14631); + return true; + case 2617: + record = new EpsgProjectedCrsRecord(6597, "NAD83(2011) / Washington North (ftUS)", 6318, 4497, 15367); + return true; + case 2618: + record = new EpsgProjectedCrsRecord(6598, "NAD83(2011) / Washington South", 6318, 4499, 14632); + return true; + case 2619: + record = new EpsgProjectedCrsRecord(6599, "NAD83(2011) / Washington South (ftUS)", 6318, 4497, 15368); + return true; + case 2620: + record = new EpsgProjectedCrsRecord(6600, "NAD83(2011) / West Virginia North", 6318, 4499, 14731); + return true; + case 2621: + record = new EpsgProjectedCrsRecord(6601, "NAD83(2011) / West Virginia North (ftUS)", 6318, 4497, 14735); + return true; + case 2622: + record = new EpsgProjectedCrsRecord(6602, "NAD83(2011) / West Virginia South", 6318, 4499, 14732); + return true; + case 2623: + record = new EpsgProjectedCrsRecord(6603, "NAD83(2011) / West Virginia South (ftUS)", 6318, 4497, 14736); + return true; + case 2624: + record = new EpsgProjectedCrsRecord(6605, "NAD83(2011) / Wisconsin Central (ftUS)", 6318, 4497, 15370); + return true; + case 2625: + record = new EpsgProjectedCrsRecord(6606, "NAD83(2011) / Wisconsin North", 6318, 4499, 14831); + return true; + case 2626: + record = new EpsgProjectedCrsRecord(6607, "NAD83(2011) / Wisconsin North (ftUS)", 6318, 4497, 15369); + return true; + case 2627: + record = new EpsgProjectedCrsRecord(6608, "NAD83(2011) / Wisconsin South", 6318, 4499, 14833); + return true; + case 2628: + record = new EpsgProjectedCrsRecord(6609, "NAD83(2011) / Wisconsin South (ftUS)", 6318, 4497, 15371); + return true; + case 2629: + record = new EpsgProjectedCrsRecord(6610, "NAD83(2011) / Wisconsin Transverse Mercator", 6318, 4499, 14841); + return true; + case 2630: + record = new EpsgProjectedCrsRecord(6611, "NAD83(2011) / Wyoming East", 6318, 4499, 14931); + return true; + case 2631: + record = new EpsgProjectedCrsRecord(6612, "NAD83(2011) / Wyoming East (ftUS)", 6318, 4497, 14935); + return true; + case 2632: + record = new EpsgProjectedCrsRecord(6613, "NAD83(2011) / Wyoming East Central", 6318, 4499, 14932); + return true; + case 2633: + record = new EpsgProjectedCrsRecord(6614, "NAD83(2011) / Wyoming East Central (ftUS)", 6318, 4497, 14936); + return true; + case 2634: + record = new EpsgProjectedCrsRecord(6615, "NAD83(2011) / Wyoming West", 6318, 4499, 14934); + return true; + case 2635: + record = new EpsgProjectedCrsRecord(6616, "NAD83(2011) / Wyoming West (ftUS)", 6318, 4497, 14938); + return true; + case 2636: + record = new EpsgProjectedCrsRecord(6617, "NAD83(2011) / Wyoming West Central", 6318, 4499, 14933); + return true; + case 2637: + record = new EpsgProjectedCrsRecord(6618, "NAD83(2011) / Wyoming West Central (ftUS)", 6318, 4497, 14937); + return true; + case 2638: + record = new EpsgProjectedCrsRecord(6619, "NAD83(2011) / Utah Central", 6318, 4499, 14332); + return true; + case 2639: + record = new EpsgProjectedCrsRecord(6620, "NAD83(2011) / Utah North", 6318, 4499, 14331); + return true; + case 2640: + record = new EpsgProjectedCrsRecord(6621, "NAD83(2011) / Utah South", 6318, 4499, 14333); + return true; + case 2641: + record = new EpsgProjectedCrsRecord(6622, "NAD83(CSRS)v2 / Quebec Lambert", 8237, 4499, 19944); + return true; + case 2642: + record = new EpsgProjectedCrsRecord(6623, "NAD83 / Quebec Albers", 4269, 4499, 6645); + return true; + case 2643: + record = new EpsgProjectedCrsRecord(6624, "NAD83(CSRS)v2 / Quebec Albers", 8237, 4499, 6645); + return true; + case 2644: + record = new EpsgProjectedCrsRecord(6625, "NAD83(2011) / Utah Central (ftUS)", 6318, 4497, 15298); + return true; + case 2645: + record = new EpsgProjectedCrsRecord(6626, "NAD83(2011) / Utah North (ftUS)", 6318, 4497, 15297); + return true; + case 2646: + record = new EpsgProjectedCrsRecord(6627, "NAD83(2011) / Utah South (ftUS)", 6318, 4497, 15299); + return true; + case 2647: + record = new EpsgProjectedCrsRecord(6628, "NAD83(PA11) / Hawaii zone 1", 6322, 4499, 15131); + return true; + case 2648: + record = new EpsgProjectedCrsRecord(6629, "NAD83(PA11) / Hawaii zone 2", 6322, 4499, 15132); + return true; + case 2649: + record = new EpsgProjectedCrsRecord(6630, "NAD83(PA11) / Hawaii zone 3", 6322, 4499, 15133); + return true; + case 2650: + record = new EpsgProjectedCrsRecord(6631, "NAD83(PA11) / Hawaii zone 4", 6322, 4499, 15134); + return true; + case 2651: + record = new EpsgProjectedCrsRecord(6632, "NAD83(PA11) / Hawaii zone 5", 6322, 4499, 15135); + return true; + case 2652: + record = new EpsgProjectedCrsRecord(6633, "NAD83(PA11) / Hawaii zone 3 (ftUS)", 6322, 4497, 15138); + return true; + case 2653: + record = new EpsgProjectedCrsRecord(6634, "NAD83(PA11) / UTM zone 4N", 6322, 4400, 16004); + return true; + case 2654: + record = new EpsgProjectedCrsRecord(6635, "NAD83(PA11) / UTM zone 5N", 6322, 4400, 16005); + return true; + case 2655: + record = new EpsgProjectedCrsRecord(6636, "NAD83(PA11) / UTM zone 2S", 6322, 4400, 16102); + return true; + case 2656: + record = new EpsgProjectedCrsRecord(6637, "NAD83(MA11) / Guam Map Grid", 6325, 4499, 4325); + return true; + case 2657: + record = new EpsgProjectedCrsRecord(6646, "Karbala 1979 / Iraq National Grid", 4743, 4400, 19907); + return true; + case 2658: + record = new EpsgProjectedCrsRecord(6669, "JGD2011 / Japan Plane Rectangular CS I", 6668, 4530, 17801); + return true; + case 2659: + record = new EpsgProjectedCrsRecord(6670, "JGD2011 / Japan Plane Rectangular CS II", 6668, 4530, 17802); + return true; + case 2660: + record = new EpsgProjectedCrsRecord(6671, "JGD2011 / Japan Plane Rectangular CS III", 6668, 4530, 17803); + return true; + case 2661: + record = new EpsgProjectedCrsRecord(6672, "JGD2011 / Japan Plane Rectangular CS IV", 6668, 4530, 17804); + return true; + case 2662: + record = new EpsgProjectedCrsRecord(6673, "JGD2011 / Japan Plane Rectangular CS V", 6668, 4530, 17805); + return true; + case 2663: + record = new EpsgProjectedCrsRecord(6674, "JGD2011 / Japan Plane Rectangular CS VI", 6668, 4530, 17806); + return true; + case 2664: + record = new EpsgProjectedCrsRecord(6675, "JGD2011 / Japan Plane Rectangular CS VII", 6668, 4530, 17807); + return true; + case 2665: + record = new EpsgProjectedCrsRecord(6676, "JGD2011 / Japan Plane Rectangular CS VIII", 6668, 4530, 17808); + return true; + case 2666: + record = new EpsgProjectedCrsRecord(6677, "JGD2011 / Japan Plane Rectangular CS IX", 6668, 4530, 17809); + return true; + case 2667: + record = new EpsgProjectedCrsRecord(6678, "JGD2011 / Japan Plane Rectangular CS X", 6668, 4530, 17810); + return true; + case 2668: + record = new EpsgProjectedCrsRecord(6679, "JGD2011 / Japan Plane Rectangular CS XI", 6668, 4530, 17811); + return true; + case 2669: + record = new EpsgProjectedCrsRecord(6680, "JGD2011 / Japan Plane Rectangular CS XII", 6668, 4530, 17812); + return true; + case 2670: + record = new EpsgProjectedCrsRecord(6681, "JGD2011 / Japan Plane Rectangular CS XIII", 6668, 4530, 17813); + return true; + case 2671: + record = new EpsgProjectedCrsRecord(6682, "JGD2011 / Japan Plane Rectangular CS XIV", 6668, 4530, 17814); + return true; + case 2672: + record = new EpsgProjectedCrsRecord(6683, "JGD2011 / Japan Plane Rectangular CS XV", 6668, 4530, 17815); + return true; + case 2673: + record = new EpsgProjectedCrsRecord(6684, "JGD2011 / Japan Plane Rectangular CS XVI", 6668, 4530, 17816); + return true; + case 2674: + record = new EpsgProjectedCrsRecord(6685, "JGD2011 / Japan Plane Rectangular CS XVII", 6668, 4530, 17817); + return true; + case 2675: + record = new EpsgProjectedCrsRecord(6686, "JGD2011 / Japan Plane Rectangular CS XVIII", 6668, 4530, 17818); + return true; + case 2676: + record = new EpsgProjectedCrsRecord(6687, "JGD2011 / Japan Plane Rectangular CS XIX", 6668, 4530, 17819); + return true; + case 2677: + record = new EpsgProjectedCrsRecord(6688, "JGD2011 / UTM zone 51N", 6668, 4400, 16051); + return true; + case 2678: + record = new EpsgProjectedCrsRecord(6689, "JGD2011 / UTM zone 52N", 6668, 4400, 16052); + return true; + case 2679: + record = new EpsgProjectedCrsRecord(6690, "JGD2011 / UTM zone 53N", 6668, 4400, 16053); + return true; + case 2680: + record = new EpsgProjectedCrsRecord(6691, "JGD2011 / UTM zone 54N", 6668, 4400, 16054); + return true; + case 2681: + record = new EpsgProjectedCrsRecord(6692, "JGD2011 / UTM zone 55N", 6668, 4400, 16055); + return true; + case 2682: + record = new EpsgProjectedCrsRecord(6703, "WGS 84 / TM 60 SW", 4326, 4400, 6702); + return true; + case 2683: + record = new EpsgProjectedCrsRecord(6707, "ETRS89-ITA [RDN2008] / UTM zone 32N (N-E)", 6706, 4500, 16032); + return true; + case 2684: + record = new EpsgProjectedCrsRecord(6708, "ETRS89-ITA [RDN2008] / UTM zone 33N (N-E)", 6706, 4500, 16033); + return true; + case 2685: + record = new EpsgProjectedCrsRecord(6709, "ETRS89-ITA [RDN2008] / UTM zone 34N (N-E)", 6706, 4500, 16034); + return true; + case 2686: + record = new EpsgProjectedCrsRecord(6720, "WGS 84 / CIG92", 4326, 4400, 6716); + return true; + case 2687: + record = new EpsgProjectedCrsRecord(6721, "GDA94 / CIG94", 4283, 4400, 6717); + return true; + case 2688: + record = new EpsgProjectedCrsRecord(6722, "WGS 84 / CKIG92", 4326, 4400, 6718); + return true; + case 2689: + record = new EpsgProjectedCrsRecord(6723, "GDA94 / CKIG94", 4283, 4400, 6719); + return true; + case 2690: + record = new EpsgProjectedCrsRecord(6736, "GDA94 / MGA zone 46", 4283, 4400, 6729); + return true; + case 2691: + record = new EpsgProjectedCrsRecord(6737, "GDA94 / MGA zone 47", 4283, 4400, 6730); + return true; + case 2692: + record = new EpsgProjectedCrsRecord(6738, "GDA94 / MGA zone 59", 4283, 4400, 6731); + return true; + case 2693: + record = new EpsgProjectedCrsRecord(6784, "NAD83(CORS96) / Oregon Baker zone (m)", 6783, 4499, 6741); + return true; + case 2694: + record = new EpsgProjectedCrsRecord(6785, "NAD83(CORS96) / Oregon Baker zone (ft)", 6783, 4495, 6742); + return true; + case 2695: + record = new EpsgProjectedCrsRecord(6786, "NAD83(2011) / Oregon Baker zone (m)", 6318, 4499, 6741); + return true; + case 2696: + record = new EpsgProjectedCrsRecord(6787, "NAD83(2011) / Oregon Baker zone (ft)", 6318, 4495, 6742); + return true; + case 2697: + record = new EpsgProjectedCrsRecord(6788, "NAD83(CORS96) / Oregon Bend-Klamath Falls zone (m)", 6783, 4499, 6743); + return true; + case 2698: + record = new EpsgProjectedCrsRecord(6789, "NAD83(CORS96) / Oregon Bend-Klamath Falls zone (ft)", 6783, 4495, 6744); + return true; + case 2699: + record = new EpsgProjectedCrsRecord(6790, "NAD83(2011) / Oregon Bend-Klamath Falls zone (m)", 6318, 4499, 6743); + return true; + case 2700: + record = new EpsgProjectedCrsRecord(6791, "NAD83(2011) / Oregon Bend-Klamath Falls zone (ft)", 6318, 4495, 6744); + return true; + case 2701: + record = new EpsgProjectedCrsRecord(6792, "NAD83(CORS96) / Oregon Bend-Redmond-Prineville zone (m)", 6783, 4499, 6745); + return true; + case 2702: + record = new EpsgProjectedCrsRecord(6793, "NAD83(CORS96) / Oregon Bend-Redmond-Prineville zone (ft)", 6783, 4495, 6746); + return true; + case 2703: + record = new EpsgProjectedCrsRecord(6794, "NAD83(2011) / Oregon Bend-Redmond-Prineville zone (m)", 6318, 4499, 6745); + return true; + case 2704: + record = new EpsgProjectedCrsRecord(6795, "NAD83(2011) / Oregon Bend-Redmond-Prineville zone (ft)", 6318, 4495, 6746); + return true; + case 2705: + record = new EpsgProjectedCrsRecord(6796, "NAD83(CORS96) / Oregon Bend-Burns zone (m)", 6783, 4499, 6747); + return true; + case 2706: + record = new EpsgProjectedCrsRecord(6797, "NAD83(CORS96) / Oregon Bend-Burns zone (ft)", 6783, 4495, 6748); + return true; + case 2707: + record = new EpsgProjectedCrsRecord(6798, "NAD83(2011) / Oregon Bend-Burns zone (m)", 6318, 4499, 6747); + return true; + case 2708: + record = new EpsgProjectedCrsRecord(6799, "NAD83(2011) / Oregon Bend-Burns zone (ft)", 6318, 4495, 6748); + return true; + case 2709: + record = new EpsgProjectedCrsRecord(6800, "NAD83(CORS96) / Oregon Canyonville-Grants Pass zone (m)", 6783, 4499, 6749); + return true; + case 2710: + record = new EpsgProjectedCrsRecord(6801, "NAD83(CORS96) / Oregon Canyonville-Grants Pass zone (ft)", 6783, 4495, 6750); + return true; + case 2711: + record = new EpsgProjectedCrsRecord(6802, "NAD83(2011) / Oregon Canyonville-Grants Pass zone (m)", 6318, 4499, 6749); + return true; + case 2712: + record = new EpsgProjectedCrsRecord(6803, "NAD83(2011) / Oregon Canyonville-Grants Pass zone (ft)", 6318, 4495, 6750); + return true; + case 2713: + record = new EpsgProjectedCrsRecord(6804, "NAD83(CORS96) / Oregon Columbia River East zone (m)", 6783, 4499, 6751); + return true; + case 2714: + record = new EpsgProjectedCrsRecord(6805, "NAD83(CORS96) / Oregon Columbia River East zone (ft)", 6783, 4495, 6752); + return true; + case 2715: + record = new EpsgProjectedCrsRecord(6806, "NAD83(2011) / Oregon Columbia River East zone (m)", 6318, 4499, 6751); + return true; + case 2716: + record = new EpsgProjectedCrsRecord(6807, "NAD83(2011) / Oregon Columbia River East zone (ft)", 6318, 4495, 6752); + return true; + case 2717: + record = new EpsgProjectedCrsRecord(6808, "NAD83(CORS96) / Oregon Columbia River West zone (m)", 6783, 4499, 6753); + return true; + case 2718: + record = new EpsgProjectedCrsRecord(6809, "NAD83(CORS96) / Oregon Columbia River West zone (ft)", 6783, 4495, 6754); + return true; + case 2719: + record = new EpsgProjectedCrsRecord(6810, "NAD83(2011) / Oregon Columbia River West zone (m)", 6318, 4499, 6753); + return true; + case 2720: + record = new EpsgProjectedCrsRecord(6811, "NAD83(2011) / Oregon Columbia River West zone (ft)", 6318, 4495, 6754); + return true; + case 2721: + record = new EpsgProjectedCrsRecord(6812, "NAD83(CORS96) / Oregon Cottage Grove-Canyonville zone (m)", 6783, 4499, 6755); + return true; + case 2722: + record = new EpsgProjectedCrsRecord(6813, "NAD83(CORS96) / Oregon Cottage Grove-Canyonville zone (ft)", 6783, 4495, 6756); + return true; + case 2723: + record = new EpsgProjectedCrsRecord(6814, "NAD83(2011) / Oregon Cottage Grove-Canyonville zone (m)", 6318, 4499, 6755); + return true; + case 2724: + record = new EpsgProjectedCrsRecord(6815, "NAD83(2011) / Oregon Cottage Grove-Canyonville zone (ft)", 6318, 4495, 6756); + return true; + case 2725: + record = new EpsgProjectedCrsRecord(6816, "NAD83(CORS96) / Oregon Dufur-Madras zone (m)", 6783, 4499, 6757); + return true; + case 2726: + record = new EpsgProjectedCrsRecord(6817, "NAD83(CORS96) / Oregon Dufur-Madras zone (ft)", 6783, 4495, 6758); + return true; + case 2727: + record = new EpsgProjectedCrsRecord(6818, "NAD83(2011) / Oregon Dufur-Madras zone (m)", 6318, 4499, 6757); + return true; + case 2728: + record = new EpsgProjectedCrsRecord(6819, "NAD83(2011) / Oregon Dufur-Madras zone (ft)", 6318, 4495, 6758); + return true; + case 2729: + record = new EpsgProjectedCrsRecord(6820, "NAD83(CORS96) / Oregon Eugene zone (m)", 6783, 4499, 6759); + return true; + case 2730: + record = new EpsgProjectedCrsRecord(6821, "NAD83(CORS96) / Oregon Eugene zone (ft)", 6783, 4495, 6760); + return true; + case 2731: + record = new EpsgProjectedCrsRecord(6822, "NAD83(2011) / Oregon Eugene zone (m)", 6318, 4499, 6759); + return true; + case 2732: + record = new EpsgProjectedCrsRecord(6823, "NAD83(2011) / Oregon Eugene zone (ft)", 6318, 4495, 6760); + return true; + case 2733: + record = new EpsgProjectedCrsRecord(6824, "NAD83(CORS96) / Oregon Grants Pass-Ashland zone (m)", 6783, 4499, 6761); + return true; + case 2734: + record = new EpsgProjectedCrsRecord(6825, "NAD83(CORS96) / Oregon Grants Pass-Ashland zone (ft)", 6783, 4495, 6762); + return true; + case 2735: + record = new EpsgProjectedCrsRecord(6826, "NAD83(2011) / Oregon Grants Pass-Ashland zone (m)", 6318, 4499, 6761); + return true; + case 2736: + record = new EpsgProjectedCrsRecord(6827, "NAD83(2011) / Oregon Grants Pass-Ashland zone (ft)", 6318, 4495, 6762); + return true; + case 2737: + record = new EpsgProjectedCrsRecord(6828, "NAD83(CORS96) / Oregon Gresham-Warm Springs zone (m)", 6783, 4499, 6763); + return true; + case 2738: + record = new EpsgProjectedCrsRecord(6829, "NAD83(CORS96) / Oregon Gresham-Warm Springs zone (ft)", 6783, 4495, 6764); + return true; + case 2739: + record = new EpsgProjectedCrsRecord(6830, "NAD83(2011) / Oregon Gresham-Warm Springs zone (m)", 6318, 4499, 6763); + return true; + case 2740: + record = new EpsgProjectedCrsRecord(6831, "NAD83(2011) / Oregon Gresham-Warm Springs zone (ft)", 6318, 4495, 6764); + return true; + case 2741: + record = new EpsgProjectedCrsRecord(6832, "NAD83(CORS96) / Oregon La Grande zone (m)", 6783, 4499, 6765); + return true; + case 2742: + record = new EpsgProjectedCrsRecord(6833, "NAD83(CORS96) / Oregon La Grande zone (ft)", 6783, 4495, 6766); + return true; + case 2743: + record = new EpsgProjectedCrsRecord(6834, "NAD83(2011) / Oregon La Grande zone (m)", 6318, 4499, 6765); + return true; + case 2744: + record = new EpsgProjectedCrsRecord(6835, "NAD83(2011) / Oregon La Grande zone (ft)", 6318, 4495, 6766); + return true; + case 2745: + record = new EpsgProjectedCrsRecord(6836, "NAD83(CORS96) / Oregon Ontario zone (m)", 6783, 4499, 6767); + return true; + case 2746: + record = new EpsgProjectedCrsRecord(6837, "NAD83(CORS96) / Oregon Ontario zone (ft)", 6783, 4495, 6768); + return true; + case 2747: + record = new EpsgProjectedCrsRecord(6838, "NAD83(2011) / Oregon Ontario zone (m)", 6318, 4499, 6767); + return true; + case 2748: + record = new EpsgProjectedCrsRecord(6839, "NAD83(2011) / Oregon Ontario zone (ft)", 6318, 4495, 6768); + return true; + case 2749: + record = new EpsgProjectedCrsRecord(6840, "NAD83(CORS96) / Oregon Coast zone (m)", 6783, 4499, 6769); + return true; + case 2750: + record = new EpsgProjectedCrsRecord(6841, "NAD83(CORS96) / Oregon Coast zone (ft)", 6783, 4495, 6770); + return true; + case 2751: + record = new EpsgProjectedCrsRecord(6842, "NAD83(2011) / Oregon Coast zone (m)", 6318, 4499, 6769); + return true; + case 2752: + record = new EpsgProjectedCrsRecord(6843, "NAD83(2011) / Oregon Coast zone (ft)", 6318, 4495, 6770); + return true; + case 2753: + record = new EpsgProjectedCrsRecord(6844, "NAD83(CORS96) / Oregon Pendleton zone (m)", 6783, 4499, 6771); + return true; + case 2754: + record = new EpsgProjectedCrsRecord(6845, "NAD83(CORS96) / Oregon Pendleton zone (ft)", 6783, 4495, 6772); + return true; + case 2755: + record = new EpsgProjectedCrsRecord(6846, "NAD83(2011) / Oregon Pendleton zone (m)", 6318, 4499, 6771); + return true; + case 2756: + record = new EpsgProjectedCrsRecord(6847, "NAD83(2011) / Oregon Pendleton zone (ft)", 6318, 4495, 6772); + return true; + case 2757: + record = new EpsgProjectedCrsRecord(6848, "NAD83(CORS96) / Oregon Pendleton-La Grande zone (m)", 6783, 4499, 6773); + return true; + case 2758: + record = new EpsgProjectedCrsRecord(6849, "NAD83(CORS96) / Oregon Pendleton-La Grande zone (ft)", 6783, 4495, 6774); + return true; + case 2759: + record = new EpsgProjectedCrsRecord(6850, "NAD83(2011) / Oregon Pendleton-La Grande zone (m)", 6318, 4499, 6773); + return true; + case 2760: + record = new EpsgProjectedCrsRecord(6851, "NAD83(2011) / Oregon Pendleton-La Grande zone (ft)", 6318, 4495, 6774); + return true; + case 2761: + record = new EpsgProjectedCrsRecord(6852, "NAD83(CORS96) / Oregon Portland zone (m)", 6783, 4499, 6775); + return true; + case 2762: + record = new EpsgProjectedCrsRecord(6853, "NAD83(CORS96) / Oregon Portland zone (ft)", 6783, 4495, 6776); + return true; + case 2763: + record = new EpsgProjectedCrsRecord(6854, "NAD83(2011) / Oregon Portland zone (m)", 6318, 4499, 6775); + return true; + case 2764: + record = new EpsgProjectedCrsRecord(6855, "NAD83(2011) / Oregon Portland zone (ft)", 6318, 4495, 6776); + return true; + case 2765: + record = new EpsgProjectedCrsRecord(6856, "NAD83(CORS96) / Oregon Salem zone (m)", 6783, 4499, 6777); + return true; + case 2766: + record = new EpsgProjectedCrsRecord(6857, "NAD83(CORS96) / Oregon Salem zone (ft)", 6783, 4495, 6778); + return true; + case 2767: + record = new EpsgProjectedCrsRecord(6858, "NAD83(2011) / Oregon Salem zone (m)", 6318, 4499, 6777); + return true; + case 2768: + record = new EpsgProjectedCrsRecord(6859, "NAD83(2011) / Oregon Salem zone (ft)", 6318, 4495, 6778); + return true; + case 2769: + record = new EpsgProjectedCrsRecord(6860, "NAD83(CORS96) / Oregon Santiam Pass zone (m)", 6783, 4499, 6779); + return true; + case 2770: + record = new EpsgProjectedCrsRecord(6861, "NAD83(CORS96) / Oregon Santiam Pass zone (ft)", 6783, 4495, 6780); + return true; + case 2771: + record = new EpsgProjectedCrsRecord(6862, "NAD83(2011) / Oregon Santiam Pass zone (m)", 6318, 4499, 6779); + return true; + case 2772: + record = new EpsgProjectedCrsRecord(6863, "NAD83(2011) / Oregon Santiam Pass zone (ft)", 6318, 4495, 6780); + return true; + case 2773: + record = new EpsgProjectedCrsRecord(6867, "NAD83(CORS96) / Oregon LCC (m)", 6783, 4499, 13633); + return true; + case 2774: + record = new EpsgProjectedCrsRecord(6868, "NAD83(CORS96) / Oregon GIC Lambert (ft)", 6783, 4495, 15374); + return true; + case 2775: + record = new EpsgProjectedCrsRecord(6870, "ETRS89-ALB [KRGJSH] / Albania TM 2010", 11047, 4530, 6869); + return true; + case 2776: + record = new EpsgProjectedCrsRecord(6875, "ETRS89-ITA [RDN2008] / Italy zone (N-E)", 6706, 4500, 6877); + return true; + case 2777: + record = new EpsgProjectedCrsRecord(6876, "ETRS89-ITA [RDN2008] / Zone 12 (N-E)", 6706, 4500, 6878); + return true; + case 2778: + record = new EpsgProjectedCrsRecord(6879, "NAD83(2011) / Wisconsin Central", 6318, 4499, 14832); + return true; + case 2779: + record = new EpsgProjectedCrsRecord(6880, "NAD83(2011) / Nebraska (ftUS)", 6318, 4497, 15396); + return true; + case 2780: + record = new EpsgProjectedCrsRecord(6884, "NAD83(CORS96) / Oregon North", 6783, 4499, 13631); + return true; + case 2781: + record = new EpsgProjectedCrsRecord(6885, "NAD83(CORS96) / Oregon North (ft)", 6783, 4495, 15351); + return true; + case 2782: + record = new EpsgProjectedCrsRecord(6886, "NAD83(CORS96) / Oregon South", 6783, 4499, 13632); + return true; + case 2783: + record = new EpsgProjectedCrsRecord(6887, "NAD83(CORS96) / Oregon South (ft)", 6783, 4495, 15352); + return true; + case 2784: + record = new EpsgProjectedCrsRecord(6915, "South East Island 1943 / UTM zone 40N", 6892, 4400, 16040); + return true; + case 2785: + record = new EpsgProjectedCrsRecord(6922, "NAD83 / Kansas LCC", 4269, 4499, 6920); + return true; + case 2786: + record = new EpsgProjectedCrsRecord(6923, "NAD83 / Kansas LCC (ftUS)", 4269, 4497, 6921); + return true; + case 2787: + record = new EpsgProjectedCrsRecord(6924, "NAD83(2011) / Kansas LCC", 6318, 4499, 6920); + return true; + case 2788: + record = new EpsgProjectedCrsRecord(6925, "NAD83(2011) / Kansas LCC (ftUS)", 6318, 4497, 6921); + return true; + case 2789: + record = new EpsgProjectedCrsRecord(6931, "WGS 84 / NSIDC EASE-Grid 2.0 North", 4326, 4469, 6929); + return true; + case 2790: + record = new EpsgProjectedCrsRecord(6932, "WGS 84 / NSIDC EASE-Grid 2.0 South", 4326, 4470, 6930); + return true; + case 2791: + record = new EpsgProjectedCrsRecord(6933, "WGS 84 / NSIDC EASE-Grid 2.0 Global", 4326, 4499, 6928); + return true; + case 2792: + record = new EpsgProjectedCrsRecord(6962, "ETRS89-ALB [KRGJSH] / Albania LCC 2010", 11047, 4530, 6961); + return true; + case 2793: + record = new EpsgProjectedCrsRecord(6966, "NAD27 / Michigan North", 4267, 4497, 6965); + return true; + case 2794: + record = new EpsgProjectedCrsRecord(6984, "Israeli Grid 05", 6983, 4400, 18204); + return true; + case 2795: + record = new EpsgProjectedCrsRecord(6991, "Israeli Grid 05/12", 6990, 4400, 18204); + return true; + case 2796: + record = new EpsgProjectedCrsRecord(7005, "Nahrwan 1934 / UTM zone 37N", 4744, 4400, 16037); + return true; + case 2797: + record = new EpsgProjectedCrsRecord(7006, "Nahrwan 1934 / UTM zone 38N", 4744, 4400, 16038); + return true; + case 2798: + record = new EpsgProjectedCrsRecord(7007, "Nahrwan 1934 / UTM zone 39N", 4744, 4400, 16039); + return true; + case 2799: + record = new EpsgProjectedCrsRecord(7057, "NAD83(2011) / IaRCS zone 1", 6318, 4497, 7043); + return true; + case 2800: + record = new EpsgProjectedCrsRecord(7058, "NAD83(2011) / IaRCS zone 2", 6318, 4497, 7044); + return true; + case 2801: + record = new EpsgProjectedCrsRecord(7059, "NAD83(2011) / IaRCS zone 3", 6318, 4497, 7045); + return true; + case 2802: + record = new EpsgProjectedCrsRecord(7060, "NAD83(2011) / IaRCS zone 4", 6318, 4497, 7046); + return true; + case 2803: + record = new EpsgProjectedCrsRecord(7061, "NAD83(2011) / IaRCS zone 5", 6318, 4497, 7047); + return true; + case 2804: + record = new EpsgProjectedCrsRecord(7062, "NAD83(2011) / IaRCS zone 6", 6318, 4497, 7048); + return true; + case 2805: + record = new EpsgProjectedCrsRecord(7063, "NAD83(2011) / IaRCS zone 7", 6318, 4497, 7049); + return true; + case 2806: + record = new EpsgProjectedCrsRecord(7064, "NAD83(2011) / IaRCS zone 8", 6318, 4497, 7050); + return true; + case 2807: + record = new EpsgProjectedCrsRecord(7065, "NAD83(2011) / IaRCS zone 9", 6318, 4497, 7051); + return true; + case 2808: + record = new EpsgProjectedCrsRecord(7066, "NAD83(2011) / IaRCS zone 10", 6318, 4497, 7052); + return true; + case 2809: + record = new EpsgProjectedCrsRecord(7067, "NAD83(2011) / IaRCS zone 11", 6318, 4497, 7053); + return true; + case 2810: + record = new EpsgProjectedCrsRecord(7068, "NAD83(2011) / IaRCS zone 12", 6318, 4497, 7054); + return true; + case 2811: + record = new EpsgProjectedCrsRecord(7069, "NAD83(2011) / IaRCS zone 13", 6318, 4497, 7055); + return true; + case 2812: + record = new EpsgProjectedCrsRecord(7070, "NAD83(2011) / IaRCS zone 14", 6318, 4497, 7056); + return true; + case 2813: + record = new EpsgProjectedCrsRecord(7074, "RGTAAF07 / UTM zone 37S", 7073, 4400, 16137); + return true; + case 2814: + record = new EpsgProjectedCrsRecord(7075, "RGTAAF07 / UTM zone 38S", 7073, 4400, 16138); + return true; + case 2815: + record = new EpsgProjectedCrsRecord(7076, "RGTAAF07 / UTM zone 39S", 7073, 4400, 16139); + return true; + case 2816: + record = new EpsgProjectedCrsRecord(7077, "RGTAAF07 / UTM zone 40S", 7073, 4400, 16140); + return true; + case 2817: + record = new EpsgProjectedCrsRecord(7078, "RGTAAF07 / UTM zone 41S", 7073, 4400, 16141); + return true; + case 2818: + record = new EpsgProjectedCrsRecord(7079, "RGTAAF07 / UTM zone 42S", 7073, 4400, 16142); + return true; + case 2819: + record = new EpsgProjectedCrsRecord(7080, "RGTAAF07 / UTM zone 43S", 7073, 4400, 16143); + return true; + case 2820: + record = new EpsgProjectedCrsRecord(7081, "RGTAAF07 / UTM zone 44S", 7073, 4400, 16144); + return true; + case 2821: + record = new EpsgProjectedCrsRecord(7109, "NAD83(2011) / RMTCRS St Mary (m)", 6318, 4499, 7089); + return true; + case 2822: + record = new EpsgProjectedCrsRecord(7110, "NAD83(2011) / RMTCRS Blackfeet (m)", 6318, 4499, 7091); + return true; + case 2823: + record = new EpsgProjectedCrsRecord(7111, "NAD83(2011) / RMTCRS Milk River (m)", 6318, 4499, 7093); + return true; + case 2824: + record = new EpsgProjectedCrsRecord(7112, "NAD83(2011) / RMTCRS Fort Belknap (m)", 6318, 4499, 7095); + return true; + case 2825: + record = new EpsgProjectedCrsRecord(7113, "NAD83(2011) / RMTCRS Fort Peck Assiniboine (m)", 6318, 4499, 7097); + return true; + case 2826: + record = new EpsgProjectedCrsRecord(7114, "NAD83(2011) / RMTCRS Fort Peck Sioux (m)", 6318, 4499, 7099); + return true; + case 2827: + record = new EpsgProjectedCrsRecord(7115, "NAD83(2011) / RMTCRS Crow (m)", 6318, 4499, 7101); + return true; + case 2828: + record = new EpsgProjectedCrsRecord(7116, "NAD83(2011) / RMTCRS Bobcat (m)", 6318, 4499, 7103); + return true; + case 2829: + record = new EpsgProjectedCrsRecord(7117, "NAD83(2011) / RMTCRS Billings (m)", 6318, 4499, 7105); + return true; + case 2830: + record = new EpsgProjectedCrsRecord(7118, "NAD83(2011) / RMTCRS Wind River (m)", 6318, 4499, 7107); + return true; + case 2831: + record = new EpsgProjectedCrsRecord(7119, "NAD83(2011) / RMTCRS St Mary (ft)", 6318, 4495, 7090); + return true; + case 2832: + record = new EpsgProjectedCrsRecord(7120, "NAD83(2011) / RMTCRS Blackfeet (ft)", 6318, 4495, 7092); + return true; + case 2833: + record = new EpsgProjectedCrsRecord(7121, "NAD83(2011) / RMTCRS Milk River (ft)", 6318, 4495, 7094); + return true; + case 2834: + record = new EpsgProjectedCrsRecord(7122, "NAD83(2011) / RMTCRS Fort Belknap (ft)", 6318, 4495, 7096); + return true; + case 2835: + record = new EpsgProjectedCrsRecord(7123, "NAD83(2011) / RMTCRS Fort Peck Assiniboine (ft)", 6318, 4495, 7098); + return true; + case 2836: + record = new EpsgProjectedCrsRecord(7124, "NAD83(2011) / RMTCRS Fort Peck Sioux (ft)", 6318, 4495, 7100); + return true; + case 2837: + record = new EpsgProjectedCrsRecord(7125, "NAD83(2011) / RMTCRS Crow (ft)", 6318, 4495, 7102); + return true; + case 2838: + record = new EpsgProjectedCrsRecord(7126, "NAD83(2011) / RMTCRS Bobcat (ft)", 6318, 4495, 7104); + return true; + case 2839: + record = new EpsgProjectedCrsRecord(7127, "NAD83(2011) / RMTCRS Billings (ft)", 6318, 4495, 7106); + return true; + case 2840: + record = new EpsgProjectedCrsRecord(7128, "NAD83(2011) / RMTCRS Wind River (ftUS)", 6318, 4497, 7108); + return true; + case 2841: + record = new EpsgProjectedCrsRecord(7131, "NAD83(2011) / San Francisco CS13", 6318, 4499, 7129); + return true; + case 2842: + record = new EpsgProjectedCrsRecord(7132, "NAD83(2011) / San Francisco CS13 (ftUS)", 6318, 4497, 7130); + return true; + case 2843: + record = new EpsgProjectedCrsRecord(7142, "Palestine 1923 / Palestine Grid modified", 4281, 4400, 7141); + return true; + case 2844: + record = new EpsgProjectedCrsRecord(7257, "NAD83(2011) / InGCS Adams (m)", 6318, 4499, 7143); + return true; + case 2845: + record = new EpsgProjectedCrsRecord(7258, "NAD83(2011) / InGCS Adams (ftUS)", 6318, 4497, 7144); + return true; + case 2846: + record = new EpsgProjectedCrsRecord(7259, "NAD83(2011) / InGCS Allen (m)", 6318, 4499, 7145); + return true; + case 2847: + record = new EpsgProjectedCrsRecord(7260, "NAD83(2011) / InGCS Allen (ftUS)", 6318, 4497, 7146); + return true; + case 2848: + record = new EpsgProjectedCrsRecord(7261, "NAD83(2011) / InGCS Bartholomew (m)", 6318, 4499, 7147); + return true; + case 2849: + record = new EpsgProjectedCrsRecord(7262, "NAD83(2011) / InGCS Bartholomew (ftUS)", 6318, 4497, 7148); + return true; + case 2850: + record = new EpsgProjectedCrsRecord(7263, "NAD83(2011) / InGCS Benton (m)", 6318, 4499, 7149); + return true; + case 2851: + record = new EpsgProjectedCrsRecord(7264, "NAD83(2011) / InGCS Benton (ftUS)", 6318, 4497, 7150); + return true; + case 2852: + record = new EpsgProjectedCrsRecord(7265, "NAD83(2011) / InGCS Blackford-Delaware (m)", 6318, 4499, 7151); + return true; + case 2853: + record = new EpsgProjectedCrsRecord(7266, "NAD83(2011) / InGCS Blackford-Delaware (ftUS)", 6318, 4497, 7152); + return true; + case 2854: + record = new EpsgProjectedCrsRecord(7267, "NAD83(2011) / InGCS Boone-Hendricks (m)", 6318, 4499, 7153); + return true; + case 2855: + record = new EpsgProjectedCrsRecord(7268, "NAD83(2011) / InGCS Boone-Hendricks (ftUS)", 6318, 4497, 7154); + return true; + case 2856: + record = new EpsgProjectedCrsRecord(7269, "NAD83(2011) / InGCS Brown (m)", 6318, 4499, 7155); + return true; + case 2857: + record = new EpsgProjectedCrsRecord(7270, "NAD83(2011) / InGCS Brown (ftUS)", 6318, 4497, 7156); + return true; + case 2858: + record = new EpsgProjectedCrsRecord(7271, "NAD83(2011) / InGCS Carroll (m)", 6318, 4499, 7157); + return true; + case 2859: + record = new EpsgProjectedCrsRecord(7272, "NAD83(2011) / InGCS Carroll (ftUS)", 6318, 4497, 7158); + return true; + case 2860: + record = new EpsgProjectedCrsRecord(7273, "NAD83(2011) / InGCS Cass (m)", 6318, 4499, 7159); + return true; + case 2861: + record = new EpsgProjectedCrsRecord(7274, "NAD83(2011) / InGCS Cass (ftUS)", 6318, 4497, 7160); + return true; + case 2862: + record = new EpsgProjectedCrsRecord(7275, "NAD83(2011) / InGCS Clark-Floyd-Scott (m)", 6318, 4499, 7161); + return true; + case 2863: + record = new EpsgProjectedCrsRecord(7276, "NAD83(2011) / InGCS Clark-Floyd-Scott (ftUS)", 6318, 4497, 7162); + return true; + case 2864: + record = new EpsgProjectedCrsRecord(7277, "NAD83(2011) / InGCS Clay (m)", 6318, 4499, 7163); + return true; + case 2865: + record = new EpsgProjectedCrsRecord(7278, "NAD83(2011) / InGCS Clay (ftUS)", 6318, 4497, 7164); + return true; + case 2866: + record = new EpsgProjectedCrsRecord(7279, "NAD83(2011) / InGCS Clinton (m)", 6318, 4499, 7165); + return true; + case 2867: + record = new EpsgProjectedCrsRecord(7280, "NAD83(2011) / InGCS Clinton (ftUS)", 6318, 4497, 7166); + return true; + case 2868: + record = new EpsgProjectedCrsRecord(7281, "NAD83(2011) / InGCS Crawford-Lawrence-Orange (m)", 6318, 4499, 7167); + return true; + case 2869: + record = new EpsgProjectedCrsRecord(7282, "NAD83(2011) / InGCS Crawford-Lawrence-Orange (ftUS)", 6318, 4497, 7168); + return true; + case 2870: + record = new EpsgProjectedCrsRecord(7283, "NAD83(2011) / InGCS Daviess-Greene (m)", 6318, 4499, 7169); + return true; + case 2871: + record = new EpsgProjectedCrsRecord(7284, "NAD83(2011) / InGCS Daviess-Greene (ftUS)", 6318, 4497, 7170); + return true; + case 2872: + record = new EpsgProjectedCrsRecord(7285, "NAD83(2011) / InGCS Dearborn-Ohio-Switzerland (m)", 6318, 4499, 7171); + return true; + case 2873: + record = new EpsgProjectedCrsRecord(7286, "NAD83(2011) / InGCS Dearborn-Ohio-Switzerland (ftUS)", 6318, 4497, 7172); + return true; + case 2874: + record = new EpsgProjectedCrsRecord(7287, "NAD83(2011) / InGCS Decatur-Rush (m)", 6318, 4499, 7173); + return true; + case 2875: + record = new EpsgProjectedCrsRecord(7288, "NAD83(2011) / InGCS Decatur-Rush (ftUS)", 6318, 4497, 7174); + return true; + case 2876: + record = new EpsgProjectedCrsRecord(7289, "NAD83(2011) / InGCS DeKalb (m)", 6318, 4499, 7175); + return true; + case 2877: + record = new EpsgProjectedCrsRecord(7290, "NAD83(2011) / InGCS DeKalb (ftUS)", 6318, 4497, 7176); + return true; + case 2878: + record = new EpsgProjectedCrsRecord(7291, "NAD83(2011) / InGCS Dubois-Martin (m)", 6318, 4499, 7177); + return true; + case 2879: + record = new EpsgProjectedCrsRecord(7292, "NAD83(2011) / InGCS Dubois-Martin (ftUS)", 6318, 4497, 7178); + return true; + case 2880: + record = new EpsgProjectedCrsRecord(7293, "NAD83(2011) / InGCS Elkhart-Kosciusko-Wabash (m)", 6318, 4499, 7179); + return true; + case 2881: + record = new EpsgProjectedCrsRecord(7294, "NAD83(2011) / InGCS Elkhart-Kosciusko-Wabash (ftUS)", 6318, 4497, 7180); + return true; + case 2882: + record = new EpsgProjectedCrsRecord(7295, "NAD83(2011) / InGCS Fayette-Franklin-Union (m)", 6318, 4499, 7181); + return true; + case 2883: + record = new EpsgProjectedCrsRecord(7296, "NAD83(2011) / InGCS Fayette-Franklin-Union (ftUS)", 6318, 4497, 7182); + return true; + case 2884: + record = new EpsgProjectedCrsRecord(7297, "NAD83(2011) / InGCS Fountain-Warren (m)", 6318, 4499, 7183); + return true; + case 2885: + record = new EpsgProjectedCrsRecord(7298, "NAD83(2011) / InGCS Fountain-Warren (ftUS)", 6318, 4497, 7184); + return true; + case 2886: + record = new EpsgProjectedCrsRecord(7299, "NAD83(2011) / InGCS Fulton-Marshall-St. Joseph (m)", 6318, 4499, 7185); + return true; + case 2887: + record = new EpsgProjectedCrsRecord(7300, "NAD83(2011) / InGCS Fulton-Marshall-St. Joseph (ftUS)", 6318, 4497, 7186); + return true; + case 2888: + record = new EpsgProjectedCrsRecord(7301, "NAD83(2011) / InGCS Gibson (m)", 6318, 4499, 7187); + return true; + case 2889: + record = new EpsgProjectedCrsRecord(7302, "NAD83(2011) / InGCS Gibson (ftUS)", 6318, 4497, 7188); + return true; + case 2890: + record = new EpsgProjectedCrsRecord(7303, "NAD83(2011) / InGCS Grant (m)", 6318, 4499, 7189); + return true; + case 2891: + record = new EpsgProjectedCrsRecord(7304, "NAD83(2011) / InGCS Grant (ftUS)", 6318, 4497, 7190); + return true; + case 2892: + record = new EpsgProjectedCrsRecord(7305, "NAD83(2011) / InGCS Hamilton-Tipton (m)", 6318, 4499, 7191); + return true; + case 2893: + record = new EpsgProjectedCrsRecord(7306, "NAD83(2011) / InGCS Hamilton-Tipton (ftUS)", 6318, 4497, 7192); + return true; + case 2894: + record = new EpsgProjectedCrsRecord(7307, "NAD83(2011) / InGCS Hancock-Madison (m)", 6318, 4499, 7193); + return true; + case 2895: + record = new EpsgProjectedCrsRecord(7308, "NAD83(2011) / InGCS Hancock-Madison (ftUS)", 6318, 4497, 7194); + return true; + case 2896: + record = new EpsgProjectedCrsRecord(7309, "NAD83(2011) / InGCS Harrison-Washington (m)", 6318, 4499, 7195); + return true; + case 2897: + record = new EpsgProjectedCrsRecord(7310, "NAD83(2011) / InGCS Harrison-Washington (ftUS)", 6318, 4497, 7196); + return true; + case 2898: + record = new EpsgProjectedCrsRecord(7311, "NAD83(2011) / InGCS Henry (m)", 6318, 4499, 7197); + return true; + case 2899: + record = new EpsgProjectedCrsRecord(7312, "NAD83(2011) / InGCS Henry (ftUS)", 6318, 4497, 7198); + return true; + case 2900: + record = new EpsgProjectedCrsRecord(7313, "NAD83(2011) / InGCS Howard-Miami (m)", 6318, 4499, 7199); + return true; + case 2901: + record = new EpsgProjectedCrsRecord(7314, "NAD83(2011) / InGCS Howard-Miami (ftUS)", 6318, 4497, 7200); + return true; + case 2902: + record = new EpsgProjectedCrsRecord(7315, "NAD83(2011) / InGCS Huntington-Whitley (m)", 6318, 4499, 7201); + return true; + case 2903: + record = new EpsgProjectedCrsRecord(7316, "NAD83(2011) / InGCS Huntington-Whitley (ftUS)", 6318, 4497, 7202); + return true; + case 2904: + record = new EpsgProjectedCrsRecord(7317, "NAD83(2011) / InGCS Jackson (m)", 6318, 4499, 7203); + return true; + case 2905: + record = new EpsgProjectedCrsRecord(7318, "NAD83(2011) / InGCS Jackson (ftUS)", 6318, 4497, 7204); + return true; + case 2906: + record = new EpsgProjectedCrsRecord(7319, "NAD83(2011) / InGCS Jasper-Porter (m)", 6318, 4499, 7205); + return true; + case 2907: + record = new EpsgProjectedCrsRecord(7320, "NAD83(2011) / InGCS Jasper-Porter (ftUS)", 6318, 4497, 7206); + return true; + case 2908: + record = new EpsgProjectedCrsRecord(7321, "NAD83(2011) / InGCS Jay (m)", 6318, 4499, 7207); + return true; + case 2909: + record = new EpsgProjectedCrsRecord(7322, "NAD83(2011) / InGCS Jay (ftUS)", 6318, 4497, 7208); + return true; + case 2910: + record = new EpsgProjectedCrsRecord(7323, "NAD83(2011) / InGCS Jefferson (m)", 6318, 4499, 7209); + return true; + case 2911: + record = new EpsgProjectedCrsRecord(7324, "NAD83(2011) / InGCS Jefferson (ftUS)", 6318, 4497, 7210); + return true; + case 2912: + record = new EpsgProjectedCrsRecord(7325, "NAD83(2011) / InGCS Jennings (m)", 6318, 4499, 7211); + return true; + case 2913: + record = new EpsgProjectedCrsRecord(7326, "NAD83(2011) / InGCS Jennings (ftUS)", 6318, 4497, 7212); + return true; + case 2914: + record = new EpsgProjectedCrsRecord(7327, "NAD83(2011) / InGCS Johnson-Marion (m)", 6318, 4499, 7213); + return true; + case 2915: + record = new EpsgProjectedCrsRecord(7328, "NAD83(2011) / InGCS Johnson-Marion (ftUS)", 6318, 4497, 7214); + return true; + case 2916: + record = new EpsgProjectedCrsRecord(7329, "NAD83(2011) / InGCS Knox (m)", 6318, 4499, 7215); + return true; + case 2917: + record = new EpsgProjectedCrsRecord(7330, "NAD83(2011) / InGCS Knox (ftUS)", 6318, 4497, 7216); + return true; + case 2918: + record = new EpsgProjectedCrsRecord(7331, "NAD83(2011) / InGCS LaGrange-Noble (m)", 6318, 4499, 7217); + return true; + case 2919: + record = new EpsgProjectedCrsRecord(7332, "NAD83(2011) / InGCS LaGrange-Noble (ftUS)", 6318, 4497, 7218); + return true; + case 2920: + record = new EpsgProjectedCrsRecord(7333, "NAD83(2011) / InGCS Lake-Newton (m)", 6318, 4499, 7219); + return true; + case 2921: + record = new EpsgProjectedCrsRecord(7334, "NAD83(2011) / InGCS Lake-Newton (ftUS)", 6318, 4497, 7220); + return true; + case 2922: + record = new EpsgProjectedCrsRecord(7335, "NAD83(2011) / InGCS LaPorte-Pulaski-Starke (m)", 6318, 4499, 7221); + return true; + case 2923: + record = new EpsgProjectedCrsRecord(7336, "NAD83(2011) / InGCS LaPorte-Pulaski-Starke (ftUS)", 6318, 4497, 7222); + return true; + case 2924: + record = new EpsgProjectedCrsRecord(7337, "NAD83(2011) / InGCS Monroe-Morgan (m)", 6318, 4499, 7223); + return true; + case 2925: + record = new EpsgProjectedCrsRecord(7338, "NAD83(2011) / InGCS Monroe-Morgan (ftUS)", 6318, 4497, 7224); + return true; + case 2926: + record = new EpsgProjectedCrsRecord(7339, "NAD83(2011) / InGCS Montgomery-Putnam (m)", 6318, 4499, 7225); + return true; + case 2927: + record = new EpsgProjectedCrsRecord(7340, "NAD83(2011) / InGCS Montgomery-Putnam (ftUS)", 6318, 4497, 7226); + return true; + case 2928: + record = new EpsgProjectedCrsRecord(7341, "NAD83(2011) / InGCS Owen (m)", 6318, 4499, 7227); + return true; + case 2929: + record = new EpsgProjectedCrsRecord(7342, "NAD83(2011) / InGCS Owen (ftUS)", 6318, 4497, 7228); + return true; + case 2930: + record = new EpsgProjectedCrsRecord(7343, "NAD83(2011) / InGCS Parke-Vermillion (m)", 6318, 4499, 7229); + return true; + case 2931: + record = new EpsgProjectedCrsRecord(7344, "NAD83(2011) / InGCS Parke-Vermillion (ftUS)", 6318, 4497, 7230); + return true; + case 2932: + record = new EpsgProjectedCrsRecord(7345, "NAD83(2011) / InGCS Perry (m)", 6318, 4499, 7231); + return true; + case 2933: + record = new EpsgProjectedCrsRecord(7346, "NAD83(2011) / InGCS Perry (ftUS)", 6318, 4497, 7232); + return true; + case 2934: + record = new EpsgProjectedCrsRecord(7347, "NAD83(2011) / InGCS Pike-Warrick (m)", 6318, 4499, 7233); + return true; + case 2935: + record = new EpsgProjectedCrsRecord(7348, "NAD83(2011) / InGCS Pike-Warrick (ftUS)", 6318, 4497, 7234); + return true; + case 2936: + record = new EpsgProjectedCrsRecord(7349, "NAD83(2011) / InGCS Posey (m)", 6318, 4499, 7235); + return true; + case 2937: + record = new EpsgProjectedCrsRecord(7350, "NAD83(2011) / InGCS Posey (ftUS)", 6318, 4497, 7236); + return true; + case 2938: + record = new EpsgProjectedCrsRecord(7351, "NAD83(2011) / InGCS Randolph-Wayne (m)", 6318, 4499, 7237); + return true; + case 2939: + record = new EpsgProjectedCrsRecord(7352, "NAD83(2011) / InGCS Randolph-Wayne (ftUS)", 6318, 4497, 7238); + return true; + case 2940: + record = new EpsgProjectedCrsRecord(7353, "NAD83(2011) / InGCS Ripley (m)", 6318, 4499, 7239); + return true; + case 2941: + record = new EpsgProjectedCrsRecord(7354, "NAD83(2011) / InGCS Ripley (ftUS)", 6318, 4497, 7240); + return true; + case 2942: + record = new EpsgProjectedCrsRecord(7355, "NAD83(2011) / InGCS Shelby (m)", 6318, 4499, 7241); + return true; + case 2943: + record = new EpsgProjectedCrsRecord(7356, "NAD83(2011) / InGCS Shelby (ftUS)", 6318, 4497, 7242); + return true; + case 2944: + record = new EpsgProjectedCrsRecord(7357, "NAD83(2011) / InGCS Spencer (m)", 6318, 4499, 7243); + return true; + case 2945: + record = new EpsgProjectedCrsRecord(7358, "NAD83(2011) / InGCS Spencer (ftUS)", 6318, 4497, 7244); + return true; + case 2946: + record = new EpsgProjectedCrsRecord(7359, "NAD83(2011) / InGCS Steuben (m)", 6318, 4499, 7245); + return true; + case 2947: + record = new EpsgProjectedCrsRecord(7360, "NAD83(2011) / InGCS Steuben (ftUS)", 6318, 4497, 7246); + return true; + case 2948: + record = new EpsgProjectedCrsRecord(7361, "NAD83(2011) / InGCS Sullivan (m)", 6318, 4499, 7247); + return true; + case 2949: + record = new EpsgProjectedCrsRecord(7362, "NAD83(2011) / InGCS Sullivan (ftUS)", 6318, 4497, 7248); + return true; + case 2950: + record = new EpsgProjectedCrsRecord(7363, "NAD83(2011) / InGCS Tippecanoe-White (m)", 6318, 4499, 7249); + return true; + case 2951: + record = new EpsgProjectedCrsRecord(7364, "NAD83(2011) / InGCS Tippecanoe-White (ftUS)", 6318, 4497, 7250); + return true; + case 2952: + record = new EpsgProjectedCrsRecord(7365, "NAD83(2011) / InGCS Vanderburgh (m)", 6318, 4499, 7251); + return true; + case 2953: + record = new EpsgProjectedCrsRecord(7366, "NAD83(2011) / InGCS Vanderburgh (ftUS)", 6318, 4497, 7252); + return true; + case 2954: + record = new EpsgProjectedCrsRecord(7367, "NAD83(2011) / InGCS Vigo (m)", 6318, 4499, 7253); + return true; + case 2955: + record = new EpsgProjectedCrsRecord(7368, "NAD83(2011) / InGCS Vigo (ftUS)", 6318, 4497, 7254); + return true; + case 2956: + record = new EpsgProjectedCrsRecord(7369, "NAD83(2011) / InGCS Wells (m)", 6318, 4499, 7255); + return true; + case 2957: + record = new EpsgProjectedCrsRecord(7370, "NAD83(2011) / InGCS Wells (ftUS)", 6318, 4497, 7256); + return true; + case 2958: + record = new EpsgProjectedCrsRecord(7374, "ONGD14 / UTM zone 39N", 7373, 4400, 16039); + return true; + case 2959: + record = new EpsgProjectedCrsRecord(7375, "ONGD14 / UTM zone 40N", 7373, 4400, 16040); + return true; + case 2960: + record = new EpsgProjectedCrsRecord(7376, "ONGD14 / UTM zone 41N", 7373, 4400, 16041); + return true; + case 2961: + record = new EpsgProjectedCrsRecord(7528, "NAD83(2011) / WISCRS Adams and Juneau (m)", 6318, 4499, 7484); + return true; + case 2962: + record = new EpsgProjectedCrsRecord(7529, "NAD83(2011) / WISCRS Ashland (m)", 6318, 4499, 7378); + return true; + case 2963: + record = new EpsgProjectedCrsRecord(7530, "NAD83(2011) / WISCRS Barron (m)", 6318, 4499, 7426); + return true; + case 2964: + record = new EpsgProjectedCrsRecord(7531, "NAD83(2011) / WISCRS Bayfield (m)", 6318, 4499, 7380); + return true; + case 2965: + record = new EpsgProjectedCrsRecord(7532, "NAD83(2011) / WISCRS Brown (m)", 6318, 4499, 7428); + return true; + case 2966: + record = new EpsgProjectedCrsRecord(7533, "NAD83(2011) / WISCRS Buffalo (m)", 6318, 4499, 7430); + return true; + case 2967: + record = new EpsgProjectedCrsRecord(7534, "NAD83(2011) / WISCRS Burnett (m)", 6318, 4499, 7382); + return true; + case 2968: + record = new EpsgProjectedCrsRecord(7535, "NAD83(2011) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (m)", 6318, 4499, 7486); + return true; + case 2969: + record = new EpsgProjectedCrsRecord(7536, "NAD83(2011) / WISCRS Chippewa (m)", 6318, 4499, 7432); + return true; + case 2970: + record = new EpsgProjectedCrsRecord(7537, "NAD83(2011) / WISCRS Clark (m)", 6318, 4499, 7434); + return true; + case 2971: + record = new EpsgProjectedCrsRecord(7538, "NAD83(2011) / WISCRS Columbia (m)", 6318, 4499, 7488); + return true; + case 2972: + record = new EpsgProjectedCrsRecord(7539, "NAD83(2011) / WISCRS Crawford (m)", 6318, 4499, 7490); + return true; + case 2973: + record = new EpsgProjectedCrsRecord(7540, "NAD83(2011) / WISCRS Dane (m)", 6318, 4499, 7492); + return true; + case 2974: + record = new EpsgProjectedCrsRecord(7541, "NAD83(2011) / WISCRS Dodge and Jefferson (m)", 6318, 4499, 7494); + return true; + case 2975: + record = new EpsgProjectedCrsRecord(7542, "NAD83(2011) / WISCRS Door (m)", 6318, 4499, 7436); + return true; + case 2976: + record = new EpsgProjectedCrsRecord(7543, "NAD83(2011) / WISCRS Douglas (m)", 6318, 4499, 7384); + return true; + case 2977: + record = new EpsgProjectedCrsRecord(7544, "NAD83(2011) / WISCRS Dunn (m)", 6318, 4499, 7438); + return true; + case 2978: + record = new EpsgProjectedCrsRecord(7545, "NAD83(2011) / WISCRS Eau Claire (m)", 6318, 4499, 7440); + return true; + case 2979: + record = new EpsgProjectedCrsRecord(7546, "NAD83(2011) / WISCRS Florence (m)", 6318, 4499, 7386); + return true; + case 2980: + record = new EpsgProjectedCrsRecord(7547, "NAD83(2011) / WISCRS Forest (m)", 6318, 4499, 7388); + return true; + case 2981: + record = new EpsgProjectedCrsRecord(7548, "NAD83(2011) / WISCRS Grant (m)", 6318, 4499, 7496); + return true; + case 2982: + record = new EpsgProjectedCrsRecord(7549, "NAD83(2011) / WISCRS Green and Lafayette (m)", 6318, 4499, 7498); + return true; + case 2983: + record = new EpsgProjectedCrsRecord(7550, "NAD83(2011) / WISCRS Green Lake and Marquette (m)", 6318, 4499, 7500); + return true; + case 2984: + record = new EpsgProjectedCrsRecord(7551, "NAD83(2011) / WISCRS Iowa (m)", 6318, 4499, 7502); + return true; + case 2985: + record = new EpsgProjectedCrsRecord(7552, "NAD83(2011) / WISCRS Iron (m)", 6318, 4499, 7390); + return true; + case 2986: + record = new EpsgProjectedCrsRecord(7553, "NAD83(2011) / WISCRS Jackson (m)", 6318, 4499, 7450); + return true; + case 2987: + record = new EpsgProjectedCrsRecord(7554, "NAD83(2011) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (m)", 6318, 4499, 7504); + return true; + case 2988: + record = new EpsgProjectedCrsRecord(7555, "NAD83(2011) / WISCRS Kewaunee, Manitowoc and Sheboygan (m)", 6318, 4499, 7506); + return true; + case 2989: + record = new EpsgProjectedCrsRecord(7556, "NAD83(2011) / WISCRS La Crosse (m)", 6318, 4499, 7508); + return true; + case 2990: + record = new EpsgProjectedCrsRecord(7557, "NAD83(2011) / WISCRS Langlade (m)", 6318, 4499, 7452); + return true; + case 2991: + record = new EpsgProjectedCrsRecord(7558, "NAD83(2011) / WISCRS Lincoln (m)", 6318, 4499, 7454); + return true; + case 2992: + record = new EpsgProjectedCrsRecord(7559, "NAD83(2011) / WISCRS Marathon (m)", 6318, 4499, 7456); + return true; + case 2993: + record = new EpsgProjectedCrsRecord(7560, "NAD83(2011) / WISCRS Marinette (m)", 6318, 4499, 7458); + return true; + case 2994: + record = new EpsgProjectedCrsRecord(7561, "NAD83(2011) / WISCRS Menominee (m)", 6318, 4499, 7460); + return true; + case 2995: + record = new EpsgProjectedCrsRecord(7562, "NAD83(2011) / WISCRS Monroe (m)", 6318, 4499, 7510); + return true; + case 2996: + record = new EpsgProjectedCrsRecord(7563, "NAD83(2011) / WISCRS Oconto (m)", 6318, 4499, 7462); + return true; + case 2997: + record = new EpsgProjectedCrsRecord(7564, "NAD83(2011) / WISCRS Oneida (m)", 6318, 4499, 7392); + return true; + case 2998: + record = new EpsgProjectedCrsRecord(7565, "NAD83(2011) / WISCRS Pepin and Pierce (m)", 6318, 4499, 7464); + return true; + case 2999: + record = new EpsgProjectedCrsRecord(7566, "NAD83(2011) / WISCRS Polk (m)", 6318, 4499, 7466); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetProjectedCrsBucket3(int index, out EpsgProjectedCrsRecord record) + { + switch (index) + { + case 3000: + record = new EpsgProjectedCrsRecord(7567, "NAD83(2011) / WISCRS Portage (m)", 6318, 4499, 7468); + return true; + case 3001: + record = new EpsgProjectedCrsRecord(7568, "NAD83(2011) / WISCRS Price (m)", 6318, 4499, 7394); + return true; + case 3002: + record = new EpsgProjectedCrsRecord(7569, "NAD83(2011) / WISCRS Richland (m)", 6318, 4499, 7512); + return true; + case 3003: + record = new EpsgProjectedCrsRecord(7570, "NAD83(2011) / WISCRS Rock (m)", 6318, 4499, 7514); + return true; + case 3004: + record = new EpsgProjectedCrsRecord(7571, "NAD83(2011) / WISCRS Rusk (m)", 6318, 4499, 7470); + return true; + case 3005: + record = new EpsgProjectedCrsRecord(7572, "NAD83(2011) / WISCRS Sauk (m)", 6318, 4499, 7516); + return true; + case 3006: + record = new EpsgProjectedCrsRecord(7573, "NAD83(2011) / WISCRS Sawyer (m)", 6318, 4499, 7396); + return true; + case 3007: + record = new EpsgProjectedCrsRecord(7574, "NAD83(2011) / WISCRS Shawano (m)", 6318, 4499, 7472); + return true; + case 3008: + record = new EpsgProjectedCrsRecord(7575, "NAD83(2011) / WISCRS St. Croix (m)", 6318, 4499, 7474); + return true; + case 3009: + record = new EpsgProjectedCrsRecord(7576, "NAD83(2011) / WISCRS Taylor (m)", 6318, 4499, 7476); + return true; + case 3010: + record = new EpsgProjectedCrsRecord(7577, "NAD83(2011) / WISCRS Trempealeau (m)", 6318, 4499, 7478); + return true; + case 3011: + record = new EpsgProjectedCrsRecord(7578, "NAD83(2011) / WISCRS Vernon (m)", 6318, 4499, 7518); + return true; + case 3012: + record = new EpsgProjectedCrsRecord(7579, "NAD83(2011) / WISCRS Vilas (m)", 6318, 4499, 7398); + return true; + case 3013: + record = new EpsgProjectedCrsRecord(7580, "NAD83(2011) / WISCRS Walworth (m)", 6318, 4499, 7520); + return true; + case 3014: + record = new EpsgProjectedCrsRecord(7581, "NAD83(2011) / WISCRS Washburn (m)", 6318, 4499, 7424); + return true; + case 3015: + record = new EpsgProjectedCrsRecord(7582, "NAD83(2011) / WISCRS Washington (m)", 6318, 4499, 7522); + return true; + case 3016: + record = new EpsgProjectedCrsRecord(7583, "NAD83(2011) / WISCRS Waukesha (m)", 6318, 4499, 7524); + return true; + case 3017: + record = new EpsgProjectedCrsRecord(7584, "NAD83(2011) / WISCRS Waupaca (m)", 6318, 4499, 7480); + return true; + case 3018: + record = new EpsgProjectedCrsRecord(7585, "NAD83(2011) / WISCRS Waushara (m)", 6318, 4499, 7526); + return true; + case 3019: + record = new EpsgProjectedCrsRecord(7586, "NAD83(2011) / WISCRS Wood (m)", 6318, 4499, 7482); + return true; + case 3020: + record = new EpsgProjectedCrsRecord(7587, "NAD83(2011) / WISCRS Adams and Juneau (ftUS)", 6318, 4497, 7485); + return true; + case 3021: + record = new EpsgProjectedCrsRecord(7588, "NAD83(2011) / WISCRS Ashland (ftUS)", 6318, 4497, 7379); + return true; + case 3022: + record = new EpsgProjectedCrsRecord(7589, "NAD83(2011) / WISCRS Barron (ftUS)", 6318, 4497, 7427); + return true; + case 3023: + record = new EpsgProjectedCrsRecord(7590, "NAD83(2011) / WISCRS Bayfield (ftUS)", 6318, 4497, 7381); + return true; + case 3024: + record = new EpsgProjectedCrsRecord(7591, "NAD83(2011) / WISCRS Brown (ftUS)", 6318, 4497, 7429); + return true; + case 3025: + record = new EpsgProjectedCrsRecord(7592, "NAD83(2011) / WISCRS Buffalo (ftUS)", 6318, 4497, 7431); + return true; + case 3026: + record = new EpsgProjectedCrsRecord(7593, "NAD83(2011) / WISCRS Burnett (ftUS)", 6318, 4497, 7383); + return true; + case 3027: + record = new EpsgProjectedCrsRecord(7594, "NAD83(2011) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (ftUS)", 6318, 4497, 7487); + return true; + case 3028: + record = new EpsgProjectedCrsRecord(7595, "NAD83(2011) / WISCRS Chippewa (ftUS)", 6318, 4497, 7433); + return true; + case 3029: + record = new EpsgProjectedCrsRecord(7596, "NAD83(2011) / WISCRS Clark (ftUS)", 6318, 4497, 7435); + return true; + case 3030: + record = new EpsgProjectedCrsRecord(7597, "NAD83(2011) / WISCRS Columbia (ftUS)", 6318, 4497, 7489); + return true; + case 3031: + record = new EpsgProjectedCrsRecord(7598, "NAD83(2011) / WISCRS Crawford (ftUS)", 6318, 4497, 7491); + return true; + case 3032: + record = new EpsgProjectedCrsRecord(7599, "NAD83(2011) / WISCRS Dane (ftUS)", 6318, 4497, 7493); + return true; + case 3033: + record = new EpsgProjectedCrsRecord(7600, "NAD83(2011) / WISCRS Dodge and Jefferson (ftUS)", 6318, 4497, 7495); + return true; + case 3034: + record = new EpsgProjectedCrsRecord(7601, "NAD83(2011) / WISCRS Door (ftUS)", 6318, 4497, 7437); + return true; + case 3035: + record = new EpsgProjectedCrsRecord(7602, "NAD83(2011) / WISCRS Douglas (ftUS)", 6318, 4497, 7385); + return true; + case 3036: + record = new EpsgProjectedCrsRecord(7603, "NAD83(2011) / WISCRS Dunn (ftUS)", 6318, 4497, 7439); + return true; + case 3037: + record = new EpsgProjectedCrsRecord(7604, "NAD83(2011) / WISCRS Eau Claire (ftUS)", 6318, 4497, 7441); + return true; + case 3038: + record = new EpsgProjectedCrsRecord(7605, "NAD83(2011) / WISCRS Florence (ftUS)", 6318, 4497, 7387); + return true; + case 3039: + record = new EpsgProjectedCrsRecord(7606, "NAD83(2011) / WISCRS Forest (ftUS)", 6318, 4497, 7389); + return true; + case 3040: + record = new EpsgProjectedCrsRecord(7607, "NAD83(2011) / WISCRS Grant (ftUS)", 6318, 4497, 7497); + return true; + case 3041: + record = new EpsgProjectedCrsRecord(7608, "NAD83(2011) / WISCRS Green and Lafayette (ftUS)", 6318, 4497, 7499); + return true; + case 3042: + record = new EpsgProjectedCrsRecord(7609, "NAD83(2011) / WISCRS Green Lake and Marquette (ftUS)", 6318, 4497, 7501); + return true; + case 3043: + record = new EpsgProjectedCrsRecord(7610, "NAD83(2011) / WISCRS Iowa (ftUS)", 6318, 4497, 7503); + return true; + case 3044: + record = new EpsgProjectedCrsRecord(7611, "NAD83(2011) / WISCRS Iron (ftUS)", 6318, 4497, 7391); + return true; + case 3045: + record = new EpsgProjectedCrsRecord(7612, "NAD83(2011) / WISCRS Jackson (ftUS)", 6318, 4497, 7451); + return true; + case 3046: + record = new EpsgProjectedCrsRecord(7613, "NAD83(2011) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (ftUS)", 6318, 4497, 7505); + return true; + case 3047: + record = new EpsgProjectedCrsRecord(7614, "NAD83(2011) / WISCRS Kewaunee, Manitowoc and Sheboygan (ftUS)", 6318, 4497, 7507); + return true; + case 3048: + record = new EpsgProjectedCrsRecord(7615, "NAD83(2011) / WISCRS La Crosse (ftUS)", 6318, 4497, 7509); + return true; + case 3049: + record = new EpsgProjectedCrsRecord(7616, "NAD83(2011) / WISCRS Langlade (ftUS)", 6318, 4497, 7453); + return true; + case 3050: + record = new EpsgProjectedCrsRecord(7617, "NAD83(2011) / WISCRS Lincoln (ftUS)", 6318, 4497, 7455); + return true; + case 3051: + record = new EpsgProjectedCrsRecord(7618, "NAD83(2011) / WISCRS Marathon (ftUS)", 6318, 4497, 7457); + return true; + case 3052: + record = new EpsgProjectedCrsRecord(7619, "NAD83(2011) / WISCRS Marinette (ftUS)", 6318, 4497, 7459); + return true; + case 3053: + record = new EpsgProjectedCrsRecord(7620, "NAD83(2011) / WISCRS Menominee (ftUS)", 6318, 4497, 7461); + return true; + case 3054: + record = new EpsgProjectedCrsRecord(7621, "NAD83(2011) / WISCRS Monroe (ftUS)", 6318, 4497, 7511); + return true; + case 3055: + record = new EpsgProjectedCrsRecord(7622, "NAD83(2011) / WISCRS Oconto (ftUS)", 6318, 4497, 7463); + return true; + case 3056: + record = new EpsgProjectedCrsRecord(7623, "NAD83(2011) / WISCRS Oneida (ftUS)", 6318, 4497, 7393); + return true; + case 3057: + record = new EpsgProjectedCrsRecord(7624, "NAD83(2011) / WISCRS Pepin and Pierce (ftUS)", 6318, 4497, 7465); + return true; + case 3058: + record = new EpsgProjectedCrsRecord(7625, "NAD83(2011) / WISCRS Polk (ftUS)", 6318, 4497, 7467); + return true; + case 3059: + record = new EpsgProjectedCrsRecord(7626, "NAD83(2011) / WISCRS Portage (ftUS)", 6318, 4497, 7469); + return true; + case 3060: + record = new EpsgProjectedCrsRecord(7627, "NAD83(2011) / WISCRS Price (ftUS)", 6318, 4497, 7395); + return true; + case 3061: + record = new EpsgProjectedCrsRecord(7628, "NAD83(2011) / WISCRS Richland (ftUS)", 6318, 4497, 7513); + return true; + case 3062: + record = new EpsgProjectedCrsRecord(7629, "NAD83(2011) / WISCRS Rock (ftUS)", 6318, 4497, 7515); + return true; + case 3063: + record = new EpsgProjectedCrsRecord(7630, "NAD83(2011) / WISCRS Rusk (ftUS)", 6318, 4497, 7471); + return true; + case 3064: + record = new EpsgProjectedCrsRecord(7631, "NAD83(2011) / WISCRS Sauk (ftUS)", 6318, 4497, 7517); + return true; + case 3065: + record = new EpsgProjectedCrsRecord(7632, "NAD83(2011) / WISCRS Sawyer (ftUS)", 6318, 4497, 7397); + return true; + case 3066: + record = new EpsgProjectedCrsRecord(7633, "NAD83(2011) / WISCRS Shawano (ftUS)", 6318, 4497, 7473); + return true; + case 3067: + record = new EpsgProjectedCrsRecord(7634, "NAD83(2011) / WISCRS St. Croix (ftUS)", 6318, 4497, 7475); + return true; + case 3068: + record = new EpsgProjectedCrsRecord(7635, "NAD83(2011) / WISCRS Taylor (ftUS)", 6318, 4497, 7477); + return true; + case 3069: + record = new EpsgProjectedCrsRecord(7636, "NAD83(2011) / WISCRS Trempealeau (ftUS)", 6318, 4497, 7479); + return true; + case 3070: + record = new EpsgProjectedCrsRecord(7637, "NAD83(2011) / WISCRS Vernon (ftUS)", 6318, 4497, 7519); + return true; + case 3071: + record = new EpsgProjectedCrsRecord(7638, "NAD83(2011) / WISCRS Vilas (ftUS)", 6318, 4497, 7399); + return true; + case 3072: + record = new EpsgProjectedCrsRecord(7639, "NAD83(2011) / WISCRS Walworth (ftUS)", 6318, 4497, 7521); + return true; + case 3073: + record = new EpsgProjectedCrsRecord(7640, "NAD83(2011) / WISCRS Washburn (ftUS)", 6318, 4497, 7425); + return true; + case 3074: + record = new EpsgProjectedCrsRecord(7641, "NAD83(2011) / WISCRS Washington (ftUS)", 6318, 4497, 7523); + return true; + case 3075: + record = new EpsgProjectedCrsRecord(7642, "NAD83(2011) / WISCRS Waukesha (ftUS)", 6318, 4497, 7525); + return true; + case 3076: + record = new EpsgProjectedCrsRecord(7643, "NAD83(2011) / WISCRS Waupaca (ftUS)", 6318, 4497, 7481); + return true; + case 3077: + record = new EpsgProjectedCrsRecord(7644, "NAD83(2011) / WISCRS Waushara (ftUS)", 6318, 4497, 7527); + return true; + case 3078: + record = new EpsgProjectedCrsRecord(7645, "NAD83(2011) / WISCRS Wood (ftUS)", 6318, 4497, 7483); + return true; + case 3079: + record = new EpsgProjectedCrsRecord(7692, "Kyrg-06 / zone 1", 7686, 4400, 7687); + return true; + case 3080: + record = new EpsgProjectedCrsRecord(7693, "Kyrg-06 / zone 2", 7686, 4400, 7688); + return true; + case 3081: + record = new EpsgProjectedCrsRecord(7694, "Kyrg-06 / zone 3", 7686, 4400, 7689); + return true; + case 3082: + record = new EpsgProjectedCrsRecord(7695, "Kyrg-06 / zone 4", 7686, 4400, 7690); + return true; + case 3083: + record = new EpsgProjectedCrsRecord(7696, "Kyrg-06 / zone 5", 7686, 4400, 7691); + return true; + case 3084: + record = new EpsgProjectedCrsRecord(7755, "WGS 84 / India NSF LCC", 4326, 4499, 7722); + return true; + case 3085: + record = new EpsgProjectedCrsRecord(7756, "WGS 84 / Andhra Pradesh", 4326, 4499, 7723); + return true; + case 3086: + record = new EpsgProjectedCrsRecord(7757, "WGS 84 / Arunachal Pradesh", 4326, 4499, 7724); + return true; + case 3087: + record = new EpsgProjectedCrsRecord(7758, "WGS 84 / Assam", 4326, 4499, 7725); + return true; + case 3088: + record = new EpsgProjectedCrsRecord(7759, "WGS 84 / Bihar", 4326, 4499, 7726); + return true; + case 3089: + record = new EpsgProjectedCrsRecord(7760, "WGS 84 / Delhi", 4326, 4499, 7727); + return true; + case 3090: + record = new EpsgProjectedCrsRecord(7761, "WGS 84 / Gujarat", 4326, 4499, 7728); + return true; + case 3091: + record = new EpsgProjectedCrsRecord(7762, "WGS 84 / Haryana", 4326, 4499, 7729); + return true; + case 3092: + record = new EpsgProjectedCrsRecord(7763, "WGS 84 / Himachal Pradesh", 4326, 4499, 7730); + return true; + case 3093: + record = new EpsgProjectedCrsRecord(7764, "WGS 84 / Jammu and Kashmir", 4326, 4499, 7731); + return true; + case 3094: + record = new EpsgProjectedCrsRecord(7765, "WGS 84 / Jharkhand", 4326, 4499, 7732); + return true; + case 3095: + record = new EpsgProjectedCrsRecord(7766, "WGS 84 / Madhya Pradesh", 4326, 4499, 7733); + return true; + case 3096: + record = new EpsgProjectedCrsRecord(7767, "WGS 84 / Maharashtra", 4326, 4499, 7734); + return true; + case 3097: + record = new EpsgProjectedCrsRecord(7768, "WGS 84 / Manipur", 4326, 4499, 7735); + return true; + case 3098: + record = new EpsgProjectedCrsRecord(7769, "WGS 84 / Meghalaya", 4326, 4499, 7736); + return true; + case 3099: + record = new EpsgProjectedCrsRecord(7770, "WGS 84 / Nagaland", 4326, 4499, 7737); + return true; + case 3100: + record = new EpsgProjectedCrsRecord(7771, "WGS 84 / India Northeast", 4326, 4499, 7738); + return true; + case 3101: + record = new EpsgProjectedCrsRecord(7772, "WGS 84 / Orissa", 4326, 4499, 7739); + return true; + case 3102: + record = new EpsgProjectedCrsRecord(7773, "WGS 84 / Punjab", 4326, 4499, 7740); + return true; + case 3103: + record = new EpsgProjectedCrsRecord(7774, "WGS 84 / Rajasthan", 4326, 4499, 7741); + return true; + case 3104: + record = new EpsgProjectedCrsRecord(7775, "WGS 84 / Uttar Pradesh", 4326, 4499, 7742); + return true; + case 3105: + record = new EpsgProjectedCrsRecord(7776, "WGS 84 / Uttaranchal", 4326, 4499, 7743); + return true; + case 3106: + record = new EpsgProjectedCrsRecord(7777, "WGS 84 / Andaman and Nicobar", 4326, 4499, 7744); + return true; + case 3107: + record = new EpsgProjectedCrsRecord(7778, "WGS 84 / Chhattisgarh", 4326, 4499, 7745); + return true; + case 3108: + record = new EpsgProjectedCrsRecord(7779, "WGS 84 / Goa", 4326, 4499, 7746); + return true; + case 3109: + record = new EpsgProjectedCrsRecord(7780, "WGS 84 / Karnataka", 4326, 4499, 7747); + return true; + case 3110: + record = new EpsgProjectedCrsRecord(7781, "WGS 84 / Kerala", 4326, 4499, 7748); + return true; + case 3111: + record = new EpsgProjectedCrsRecord(7782, "WGS 84 / Lakshadweep", 4326, 4499, 7749); + return true; + case 3112: + record = new EpsgProjectedCrsRecord(7783, "WGS 84 / Mizoram", 4326, 4499, 7750); + return true; + case 3113: + record = new EpsgProjectedCrsRecord(7784, "WGS 84 / Sikkim", 4326, 4499, 7751); + return true; + case 3114: + record = new EpsgProjectedCrsRecord(7785, "WGS 84 / Tamil Nadu", 4326, 4499, 7752); + return true; + case 3115: + record = new EpsgProjectedCrsRecord(7786, "WGS 84 / Tripura", 4326, 4499, 7753); + return true; + case 3116: + record = new EpsgProjectedCrsRecord(7787, "WGS 84 / West Bengal", 4326, 4499, 7754); + return true; + case 3117: + record = new EpsgProjectedCrsRecord(7791, "ETRS89-ITA [RDN2008] / UTM zone 32N", 6706, 4400, 16032); + return true; + case 3118: + record = new EpsgProjectedCrsRecord(7792, "ETRS89-ITA [RDN2008] / UTM zone 33N", 6706, 4400, 16033); + return true; + case 3119: + record = new EpsgProjectedCrsRecord(7793, "ETRS89-ITA [RDN2008] / UTM zone 34N", 6706, 4400, 16034); + return true; + case 3120: + record = new EpsgProjectedCrsRecord(7794, "ETRS89-ITA [RDN2008] / Italy zone (E-N)", 6706, 4400, 6877); + return true; + case 3121: + record = new EpsgProjectedCrsRecord(7795, "ETRS89-ITA [RDN2008] / Zone 12 (E-N)", 6706, 4400, 6878); + return true; + case 3122: + record = new EpsgProjectedCrsRecord(7799, "ETRS89-BGR [BGS2005] / UTM zone 34N (N-E)", 7798, 4531, 16034); + return true; + case 3123: + record = new EpsgProjectedCrsRecord(7800, "ETRS89-BGR [BGS2005] / UTM zone 35N (N-E)", 7798, 4531, 16035); + return true; + case 3124: + record = new EpsgProjectedCrsRecord(7801, "ETRS89-BGR [BGS2005] / CCS2005", 7798, 4531, 7802); + return true; + case 3125: + record = new EpsgProjectedCrsRecord(7803, "ETRS89-BGR [BGS2005] / UTM zone 34N", 7798, 4400, 16034); + return true; + case 3126: + record = new EpsgProjectedCrsRecord(7805, "ETRS89-BGR [BGS2005] / UTM zone 36N", 7798, 4400, 16036); + return true; + case 3127: + record = new EpsgProjectedCrsRecord(7825, "Pulkovo 1942 / CS63 zone X1", 4284, 4530, 7818); + return true; + case 3128: + record = new EpsgProjectedCrsRecord(7826, "Pulkovo 1942 / CS63 zone X2", 4284, 4530, 7819); + return true; + case 3129: + record = new EpsgProjectedCrsRecord(7827, "Pulkovo 1942 / CS63 zone X3", 4284, 4530, 7820); + return true; + case 3130: + record = new EpsgProjectedCrsRecord(7828, "Pulkovo 1942 / CS63 zone X4", 4284, 4530, 7821); + return true; + case 3131: + record = new EpsgProjectedCrsRecord(7829, "Pulkovo 1942 / CS63 zone X5", 4284, 4530, 7822); + return true; + case 3132: + record = new EpsgProjectedCrsRecord(7830, "Pulkovo 1942 / CS63 zone X6", 4284, 4530, 7823); + return true; + case 3133: + record = new EpsgProjectedCrsRecord(7831, "Pulkovo 1942 / CS63 zone X7", 4284, 4530, 7824); + return true; + case 3134: + record = new EpsgProjectedCrsRecord(7845, "GDA2020 / GA LCC", 7844, 4400, 17362); + return true; + case 3135: + record = new EpsgProjectedCrsRecord(7846, "GDA2020 / MGA zone 46", 7844, 4400, 6729); + return true; + case 3136: + record = new EpsgProjectedCrsRecord(7847, "GDA2020 / MGA zone 47", 7844, 4400, 6730); + return true; + case 3137: + record = new EpsgProjectedCrsRecord(7848, "GDA2020 / MGA zone 48", 7844, 4400, 17348); + return true; + case 3138: + record = new EpsgProjectedCrsRecord(7849, "GDA2020 / MGA zone 49", 7844, 4400, 17349); + return true; + case 3139: + record = new EpsgProjectedCrsRecord(7850, "GDA2020 / MGA zone 50", 7844, 4400, 17350); + return true; + case 3140: + record = new EpsgProjectedCrsRecord(7851, "GDA2020 / MGA zone 51", 7844, 4400, 17351); + return true; + case 3141: + record = new EpsgProjectedCrsRecord(7852, "GDA2020 / MGA zone 52", 7844, 4400, 17352); + return true; + case 3142: + record = new EpsgProjectedCrsRecord(7853, "GDA2020 / MGA zone 53", 7844, 4400, 17353); + return true; + case 3143: + record = new EpsgProjectedCrsRecord(7854, "GDA2020 / MGA zone 54", 7844, 4400, 17354); + return true; + case 3144: + record = new EpsgProjectedCrsRecord(7855, "GDA2020 / MGA zone 55", 7844, 4400, 17355); + return true; + case 3145: + record = new EpsgProjectedCrsRecord(7856, "GDA2020 / MGA zone 56", 7844, 4400, 17356); + return true; + case 3146: + record = new EpsgProjectedCrsRecord(7857, "GDA2020 / MGA zone 57", 7844, 4400, 17357); + return true; + case 3147: + record = new EpsgProjectedCrsRecord(7858, "GDA2020 / MGA zone 58", 7844, 4400, 17358); + return true; + case 3148: + record = new EpsgProjectedCrsRecord(7859, "GDA2020 / MGA zone 59", 7844, 4400, 6731); + return true; + case 3149: + record = new EpsgProjectedCrsRecord(7877, "Astro DOS 71 / SHLG71", 4710, 4400, 7875); + return true; + case 3150: + record = new EpsgProjectedCrsRecord(7878, "Astro DOS 71 / UTM zone 30S", 4710, 4400, 16130); + return true; + case 3151: + record = new EpsgProjectedCrsRecord(7882, "St. Helena Tritan / SHLG(Tritan)", 7881, 4400, 7876); + return true; + case 3152: + record = new EpsgProjectedCrsRecord(7883, "St. Helena Tritan / UTM zone 30S", 7881, 4400, 16130); + return true; + case 3153: + record = new EpsgProjectedCrsRecord(7887, "SHMG2015", 7886, 4400, 16130); + return true; + case 3154: + record = new EpsgProjectedCrsRecord(7899, "GDA2020 / Vicgrid", 7844, 4400, 17361); + return true; + case 3155: + record = new EpsgProjectedCrsRecord(7991, "NAD27 / MTM zone 10", 4267, 4499, 17710); + return true; + case 3156: + record = new EpsgProjectedCrsRecord(7992, "Malongo 1987 / UTM zone 33S", 4259, 4400, 16133); + return true; + case 3157: + record = new EpsgProjectedCrsRecord(8013, "GDA2020 / ALB2020", 7844, 4400, 7993); + return true; + case 3158: + record = new EpsgProjectedCrsRecord(8014, "GDA2020 / BIO2020", 7844, 4400, 7994); + return true; + case 3159: + record = new EpsgProjectedCrsRecord(8015, "GDA2020 / BRO2020", 7844, 4400, 7995); + return true; + case 3160: + record = new EpsgProjectedCrsRecord(8016, "GDA2020 / BCG2020", 7844, 4400, 7996); + return true; + case 3161: + record = new EpsgProjectedCrsRecord(8017, "GDA2020 / CARN2020", 7844, 4400, 7997); + return true; + case 3162: + record = new EpsgProjectedCrsRecord(8018, "GDA2020 / CIG2020", 7844, 4400, 7998); + return true; + case 3163: + record = new EpsgProjectedCrsRecord(8019, "GDA2020 / CKIG2020", 7844, 4400, 7999); + return true; + case 3164: + record = new EpsgProjectedCrsRecord(8020, "GDA2020 / COL2020", 7844, 4400, 8000); + return true; + case 3165: + record = new EpsgProjectedCrsRecord(8021, "GDA2020 / ESP2020", 7844, 4400, 8001); + return true; + case 3166: + record = new EpsgProjectedCrsRecord(8022, "GDA2020 / EXM2020", 7844, 4400, 8002); + return true; + case 3167: + record = new EpsgProjectedCrsRecord(8023, "GDA2020 / GCG2020", 7844, 4400, 8003); + return true; + case 3168: + record = new EpsgProjectedCrsRecord(8024, "GDA2020 / GOLD2020", 7844, 4400, 8004); + return true; + case 3169: + record = new EpsgProjectedCrsRecord(8025, "GDA2020 / JCG2020", 7844, 4400, 8005); + return true; + case 3170: + record = new EpsgProjectedCrsRecord(8026, "GDA2020 / KALB2020", 7844, 4400, 8006); + return true; + case 3171: + record = new EpsgProjectedCrsRecord(8027, "GDA2020 / KAR2020", 7844, 4400, 8007); + return true; + case 3172: + record = new EpsgProjectedCrsRecord(8028, "GDA2020 / KUN2020", 7844, 4400, 8008); + return true; + case 3173: + record = new EpsgProjectedCrsRecord(8029, "GDA2020 / LCG2020", 7844, 4400, 8009); + return true; + case 3174: + record = new EpsgProjectedCrsRecord(8030, "GDA2020 / MRCG2020", 7844, 4400, 8010); + return true; + case 3175: + record = new EpsgProjectedCrsRecord(8031, "GDA2020 / PCG2020", 7844, 4400, 8011); + return true; + case 3176: + record = new EpsgProjectedCrsRecord(8032, "GDA2020 / PHG2020", 7844, 4400, 8012); + return true; + case 3177: + record = new EpsgProjectedCrsRecord(8035, "WGS 84 / TM Zone 20N (ftUS)", 4326, 4497, 8033); + return true; + case 3178: + record = new EpsgProjectedCrsRecord(8036, "WGS 84 / TM Zone 21N (ftUS)", 4326, 4497, 8034); + return true; + case 3179: + record = new EpsgProjectedCrsRecord(8044, "Gusterberg Grid (Ferro)", 8042, 6501, 8040); + return true; + case 3180: + record = new EpsgProjectedCrsRecord(8045, "St. Stephen Grid (Ferro)", 8043, 6501, 8041); + return true; + case 3181: + record = new EpsgProjectedCrsRecord(8058, "GDA2020 / NSW Lambert", 7844, 4400, 17364); + return true; + case 3182: + record = new EpsgProjectedCrsRecord(8059, "GDA2020 / SA Lambert", 7844, 4400, 17359); + return true; + case 3183: + record = new EpsgProjectedCrsRecord(8065, "NAD83(2011) / PCCS zone 1 (ft)", 6318, 4495, 8061); + return true; + case 3184: + record = new EpsgProjectedCrsRecord(8066, "NAD83(2011) / PCCS zone 2 (ft)", 6318, 4495, 8062); + return true; + case 3185: + record = new EpsgProjectedCrsRecord(8067, "NAD83(2011) / PCCS zone 3 (ft)", 6318, 4495, 8063); + return true; + case 3186: + record = new EpsgProjectedCrsRecord(8068, "NAD83(2011) / PCCS zone 4 (ft)", 6318, 4495, 8064); + return true; + case 3187: + record = new EpsgProjectedCrsRecord(8082, "NAD83(CSRS)v6 / MTM NS 2010 zone 4", 8252, 4400, 8080); + return true; + case 3188: + record = new EpsgProjectedCrsRecord(8083, "NAD83(CSRS)v6 / MTM NS 2010 zone 5", 8252, 4400, 8081); + return true; + case 3189: + record = new EpsgProjectedCrsRecord(8088, "ISN2016 / Lambert 2016", 8086, 4499, 8087); + return true; + case 3190: + record = new EpsgProjectedCrsRecord(8090, "NAD83(HARN) / WISCRS Florence (m)", 4152, 4499, 7386); + return true; + case 3191: + record = new EpsgProjectedCrsRecord(8091, "NAD83(HARN) / WISCRS Florence (ftUS)", 4152, 4497, 7387); + return true; + case 3192: + record = new EpsgProjectedCrsRecord(8092, "NAD83(HARN) / WISCRS Eau Claire (m)", 4152, 4499, 7440); + return true; + case 3193: + record = new EpsgProjectedCrsRecord(8093, "NAD83(HARN) / WISCRS Eau Claire (ftUS)", 4152, 4497, 7441); + return true; + case 3194: + record = new EpsgProjectedCrsRecord(8095, "NAD83(HARN) / WISCRS Wood (m)", 4152, 4499, 7482); + return true; + case 3195: + record = new EpsgProjectedCrsRecord(8096, "NAD83(HARN) / WISCRS Wood (ftUS)", 4152, 4497, 7483); + return true; + case 3196: + record = new EpsgProjectedCrsRecord(8097, "NAD83(HARN) / WISCRS Waushara (m)", 4152, 4499, 7526); + return true; + case 3197: + record = new EpsgProjectedCrsRecord(8098, "NAD83(HARN) / WISCRS Waushara (ftUS)", 4152, 4497, 7527); + return true; + case 3198: + record = new EpsgProjectedCrsRecord(8099, "NAD83(HARN) / WISCRS Waupaca (m)", 4152, 4499, 7480); + return true; + case 3199: + record = new EpsgProjectedCrsRecord(8100, "NAD83(HARN) / WISCRS Waupaca (ftUS)", 4152, 4497, 7481); + return true; + case 3200: + record = new EpsgProjectedCrsRecord(8101, "NAD83(HARN) / WISCRS Waukesha (m)", 4152, 4499, 7524); + return true; + case 3201: + record = new EpsgProjectedCrsRecord(8102, "NAD83(HARN) / WISCRS Waukesha (ftUS)", 4152, 4497, 7525); + return true; + case 3202: + record = new EpsgProjectedCrsRecord(8103, "NAD83(HARN) / WISCRS Washington (m)", 4152, 4499, 7522); + return true; + case 3203: + record = new EpsgProjectedCrsRecord(8104, "NAD83(HARN) / WISCRS Washington (ftUS)", 4152, 4497, 7523); + return true; + case 3204: + record = new EpsgProjectedCrsRecord(8105, "NAD83(HARN) / WISCRS Washburn (m)", 4152, 4499, 7424); + return true; + case 3205: + record = new EpsgProjectedCrsRecord(8106, "NAD83(HARN) / WISCRS Washburn (ftUS)", 4152, 4497, 7425); + return true; + case 3206: + record = new EpsgProjectedCrsRecord(8107, "NAD83(HARN) / WISCRS Walworth (m)", 4152, 4499, 7520); + return true; + case 3207: + record = new EpsgProjectedCrsRecord(8108, "NAD83(HARN) / WISCRS Walworth (ftUS)", 4152, 4497, 7521); + return true; + case 3208: + record = new EpsgProjectedCrsRecord(8109, "NAD83(HARN) / WISCRS Vilas (m)", 4152, 4499, 7398); + return true; + case 3209: + record = new EpsgProjectedCrsRecord(8110, "NAD83(HARN) / WISCRS Vilas (ftUS)", 4152, 4497, 7399); + return true; + case 3210: + record = new EpsgProjectedCrsRecord(8111, "NAD83(HARN) / WISCRS Vernon (m)", 4152, 4499, 7518); + return true; + case 3211: + record = new EpsgProjectedCrsRecord(8112, "NAD83(HARN) / WISCRS Vernon (ftUS)", 4152, 4497, 7519); + return true; + case 3212: + record = new EpsgProjectedCrsRecord(8113, "NAD83(HARN) / WISCRS Trempealeau (m)", 4152, 4499, 7478); + return true; + case 3213: + record = new EpsgProjectedCrsRecord(8114, "NAD83(HARN) / WISCRS Trempealeau (ftUS)", 4152, 4497, 7479); + return true; + case 3214: + record = new EpsgProjectedCrsRecord(8115, "NAD83(HARN) / WISCRS Taylor (m)", 4152, 4499, 7476); + return true; + case 3215: + record = new EpsgProjectedCrsRecord(8116, "NAD83(HARN) / WISCRS Taylor (ftUS)", 4152, 4497, 7477); + return true; + case 3216: + record = new EpsgProjectedCrsRecord(8117, "NAD83(HARN) / WISCRS St. Croix (m)", 4152, 4499, 7474); + return true; + case 3217: + record = new EpsgProjectedCrsRecord(8118, "NAD83(HARN) / WISCRS St. Croix (ftUS)", 4152, 4497, 7475); + return true; + case 3218: + record = new EpsgProjectedCrsRecord(8119, "NAD83(HARN) / WISCRS Shawano (m)", 4152, 4499, 7472); + return true; + case 3219: + record = new EpsgProjectedCrsRecord(8120, "NAD83(HARN) / WISCRS Shawano (ftUS)", 4152, 4497, 7473); + return true; + case 3220: + record = new EpsgProjectedCrsRecord(8121, "NAD83(HARN) / WISCRS Sawyer (m)", 4152, 4499, 7396); + return true; + case 3221: + record = new EpsgProjectedCrsRecord(8122, "NAD83(HARN) / WISCRS Sawyer (ftUS)", 4152, 4497, 7397); + return true; + case 3222: + record = new EpsgProjectedCrsRecord(8123, "NAD83(HARN) / WISCRS Sauk (m)", 4152, 4499, 7516); + return true; + case 3223: + record = new EpsgProjectedCrsRecord(8124, "NAD83(HARN) / WISCRS Sauk (ftUS)", 4152, 4497, 7517); + return true; + case 3224: + record = new EpsgProjectedCrsRecord(8125, "NAD83(HARN) / WISCRS Rusk (m)", 4152, 4499, 7470); + return true; + case 3225: + record = new EpsgProjectedCrsRecord(8126, "NAD83(HARN) / WISCRS Rusk (ftUS)", 4152, 4497, 7471); + return true; + case 3226: + record = new EpsgProjectedCrsRecord(8127, "NAD83(HARN) / WISCRS Rock (m)", 4152, 4499, 7514); + return true; + case 3227: + record = new EpsgProjectedCrsRecord(8128, "NAD83(HARN) / WISCRS Rock (ftUS)", 4152, 4497, 7515); + return true; + case 3228: + record = new EpsgProjectedCrsRecord(8129, "NAD83(HARN) / WISCRS Richland (m)", 4152, 4499, 7512); + return true; + case 3229: + record = new EpsgProjectedCrsRecord(8130, "NAD83(HARN) / WISCRS Richland (ftUS)", 4152, 4497, 7513); + return true; + case 3230: + record = new EpsgProjectedCrsRecord(8131, "NAD83(HARN) / WISCRS Price (m)", 4152, 4499, 7394); + return true; + case 3231: + record = new EpsgProjectedCrsRecord(8132, "NAD83(HARN) / WISCRS Price (ftUS)", 4152, 4497, 7395); + return true; + case 3232: + record = new EpsgProjectedCrsRecord(8133, "NAD83(HARN) / WISCRS Portage (m)", 4152, 4499, 7468); + return true; + case 3233: + record = new EpsgProjectedCrsRecord(8134, "NAD83(HARN) / WISCRS Portage (ftUS)", 4152, 4497, 7469); + return true; + case 3234: + record = new EpsgProjectedCrsRecord(8135, "NAD83(HARN) / WISCRS Polk (m)", 4152, 4499, 7466); + return true; + case 3235: + record = new EpsgProjectedCrsRecord(8136, "NAD83(HARN) / WISCRS Polk (ftUS)", 4152, 4497, 7467); + return true; + case 3236: + record = new EpsgProjectedCrsRecord(8137, "NAD83(HARN) / WISCRS Pepin and Pierce (m)", 4152, 4499, 7464); + return true; + case 3237: + record = new EpsgProjectedCrsRecord(8138, "NAD83(HARN) / WISCRS Pepin and Pierce (ftUS)", 4152, 4497, 7465); + return true; + case 3238: + record = new EpsgProjectedCrsRecord(8139, "NAD83(HARN) / WISCRS Oneida (m)", 4152, 4499, 7392); + return true; + case 3239: + record = new EpsgProjectedCrsRecord(8140, "NAD83(HARN) / WISCRS Oneida (ftUS)", 4152, 4497, 7393); + return true; + case 3240: + record = new EpsgProjectedCrsRecord(8141, "NAD83(HARN) / WISCRS Oconto (m)", 4152, 4499, 7462); + return true; + case 3241: + record = new EpsgProjectedCrsRecord(8142, "NAD83(HARN) / WISCRS Oconto (ftUS)", 4152, 4497, 7463); + return true; + case 3242: + record = new EpsgProjectedCrsRecord(8143, "NAD83(HARN) / WISCRS Monroe (m)", 4152, 4499, 7510); + return true; + case 3243: + record = new EpsgProjectedCrsRecord(8144, "NAD83(HARN) / WISCRS Monroe (ftUS)", 4152, 4497, 7511); + return true; + case 3244: + record = new EpsgProjectedCrsRecord(8145, "NAD83(HARN) / WISCRS Menominee (m)", 4152, 4499, 7460); + return true; + case 3245: + record = new EpsgProjectedCrsRecord(8146, "NAD83(HARN) / WISCRS Menominee (ftUS)", 4152, 4497, 7461); + return true; + case 3246: + record = new EpsgProjectedCrsRecord(8147, "NAD83(HARN) / WISCRS Marinette (m)", 4152, 4499, 7458); + return true; + case 3247: + record = new EpsgProjectedCrsRecord(8148, "NAD83(HARN) / WISCRS Marinette (ftUS)", 4152, 4497, 7459); + return true; + case 3248: + record = new EpsgProjectedCrsRecord(8149, "NAD83(HARN) / WISCRS Marathon (m)", 4152, 4499, 7456); + return true; + case 3249: + record = new EpsgProjectedCrsRecord(8150, "NAD83(HARN) / WISCRS Marathon (ftUS)", 4152, 4497, 7457); + return true; + case 3250: + record = new EpsgProjectedCrsRecord(8151, "NAD83(HARN) / WISCRS Lincoln (m)", 4152, 4499, 7454); + return true; + case 3251: + record = new EpsgProjectedCrsRecord(8152, "NAD83(HARN) / WISCRS Lincoln (ftUS)", 4152, 4497, 7455); + return true; + case 3252: + record = new EpsgProjectedCrsRecord(8153, "NAD83(HARN) / WISCRS Langlade (m)", 4152, 4499, 7452); + return true; + case 3253: + record = new EpsgProjectedCrsRecord(8154, "NAD83(HARN) / WISCRS Langlade (ftUS)", 4152, 4497, 7453); + return true; + case 3254: + record = new EpsgProjectedCrsRecord(8155, "NAD83(HARN) / WISCRS La Crosse (m)", 4152, 4499, 7508); + return true; + case 3255: + record = new EpsgProjectedCrsRecord(8156, "NAD83(HARN) / WISCRS La Crosse (ftUS)", 4152, 4497, 7509); + return true; + case 3256: + record = new EpsgProjectedCrsRecord(8157, "NAD83(HARN) / WISCRS Kewaunee, Manitowoc and Sheboygan (m)", 4152, 4499, 7506); + return true; + case 3257: + record = new EpsgProjectedCrsRecord(8158, "NAD83(HARN) / WISCRS Kewaunee, Manitowoc and Sheboygan (ftUS)", 4152, 4497, 7507); + return true; + case 3258: + record = new EpsgProjectedCrsRecord(8159, "NAD83(HARN) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (m)", 4152, 4499, 7504); + return true; + case 3259: + record = new EpsgProjectedCrsRecord(8160, "NAD83(HARN) / WISCRS Kenosha, Milwaukee, Ozaukee and Racine (ftUS)", 4152, 4497, 7505); + return true; + case 3260: + record = new EpsgProjectedCrsRecord(8161, "NAD83(HARN) / WISCRS Jackson (m)", 4152, 4499, 7450); + return true; + case 3261: + record = new EpsgProjectedCrsRecord(8162, "NAD83(HARN) / WISCRS Jackson (ftUS)", 4152, 4497, 7451); + return true; + case 3262: + record = new EpsgProjectedCrsRecord(8163, "NAD83(HARN) / WISCRS Iron (m)", 4152, 4499, 7390); + return true; + case 3263: + record = new EpsgProjectedCrsRecord(8164, "NAD83(HARN) / WISCRS Iron (ftUS)", 4152, 4497, 7391); + return true; + case 3264: + record = new EpsgProjectedCrsRecord(8165, "NAD83(HARN) / WISCRS Iowa (m)", 4152, 4499, 7502); + return true; + case 3265: + record = new EpsgProjectedCrsRecord(8166, "NAD83(HARN) / WISCRS Iowa (ftUS)", 4152, 4497, 7503); + return true; + case 3266: + record = new EpsgProjectedCrsRecord(8167, "NAD83(HARN) / WISCRS Green Lake and Marquette (m)", 4152, 4499, 7500); + return true; + case 3267: + record = new EpsgProjectedCrsRecord(8168, "NAD83(HARN) / WISCRS Green Lake and Marquette (ftUS)", 4152, 4497, 7501); + return true; + case 3268: + record = new EpsgProjectedCrsRecord(8169, "NAD83(HARN) / WISCRS Green and Lafayette (m)", 4152, 4499, 7498); + return true; + case 3269: + record = new EpsgProjectedCrsRecord(8170, "NAD83(HARN) / WISCRS Green and Lafayette (ftUS)", 4152, 4497, 7499); + return true; + case 3270: + record = new EpsgProjectedCrsRecord(8171, "NAD83(HARN) / WISCRS Grant (m)", 4152, 4499, 7496); + return true; + case 3271: + record = new EpsgProjectedCrsRecord(8172, "NAD83(HARN) / WISCRS Grant (ftUS)", 4152, 4497, 7497); + return true; + case 3272: + record = new EpsgProjectedCrsRecord(8173, "NAD83(HARN) / WISCRS Forest (m)", 4152, 4499, 7388); + return true; + case 3273: + record = new EpsgProjectedCrsRecord(8177, "NAD83(HARN) / WISCRS Forest (ftUS)", 4152, 4497, 7389); + return true; + case 3274: + record = new EpsgProjectedCrsRecord(8179, "NAD83(HARN) / WISCRS Dunn (m)", 4152, 4499, 7438); + return true; + case 3275: + record = new EpsgProjectedCrsRecord(8180, "NAD83(HARN) / WISCRS Dunn (ftUS)", 4152, 4497, 7439); + return true; + case 3276: + record = new EpsgProjectedCrsRecord(8181, "NAD83(HARN) / WISCRS Douglas (m)", 4152, 4499, 7384); + return true; + case 3277: + record = new EpsgProjectedCrsRecord(8182, "NAD83(HARN) / WISCRS Douglas (ftUS)", 4152, 4497, 7385); + return true; + case 3278: + record = new EpsgProjectedCrsRecord(8184, "NAD83(HARN) / WISCRS Door (m)", 4152, 4499, 7436); + return true; + case 3279: + record = new EpsgProjectedCrsRecord(8185, "NAD83(HARN) / WISCRS Door (ftUS)", 4152, 4497, 7437); + return true; + case 3280: + record = new EpsgProjectedCrsRecord(8187, "NAD83(HARN) / WISCRS Dodge and Jefferson (m)", 4152, 4499, 7494); + return true; + case 3281: + record = new EpsgProjectedCrsRecord(8189, "NAD83(HARN) / WISCRS Dodge and Jefferson (ftUS)", 4152, 4497, 7495); + return true; + case 3282: + record = new EpsgProjectedCrsRecord(8191, "NAD83(HARN) / WISCRS Dane (m)", 4152, 4499, 7492); + return true; + case 3283: + record = new EpsgProjectedCrsRecord(8193, "NAD83(HARN) / WISCRS Dane (ftUS)", 4152, 4497, 7493); + return true; + case 3284: + record = new EpsgProjectedCrsRecord(8196, "NAD83(HARN) / WISCRS Crawford (m)", 4152, 4499, 7490); + return true; + case 3285: + record = new EpsgProjectedCrsRecord(8197, "NAD83(HARN) / WISCRS Crawford (ftUS)", 4152, 4497, 7491); + return true; + case 3286: + record = new EpsgProjectedCrsRecord(8198, "NAD83(HARN) / WISCRS Columbia (m)", 4152, 4499, 7488); + return true; + case 3287: + record = new EpsgProjectedCrsRecord(8200, "NAD83(HARN) / WISCRS Columbia (ftUS)", 4152, 4497, 7489); + return true; + case 3288: + record = new EpsgProjectedCrsRecord(8201, "NAD83(HARN) / WISCRS Clark (m)", 4152, 4499, 7434); + return true; + case 3289: + record = new EpsgProjectedCrsRecord(8202, "NAD83(HARN) / WISCRS Clark (ftUS)", 4152, 4497, 7435); + return true; + case 3290: + record = new EpsgProjectedCrsRecord(8203, "NAD83(HARN) / WISCRS Chippewa (m)", 4152, 4499, 7432); + return true; + case 3291: + record = new EpsgProjectedCrsRecord(8204, "NAD83(HARN) / WISCRS Chippewa (ftUS)", 4152, 4497, 7433); + return true; + case 3292: + record = new EpsgProjectedCrsRecord(8205, "NAD83(HARN) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (m)", 4152, 4499, 7486); + return true; + case 3293: + record = new EpsgProjectedCrsRecord(8206, "NAD83(HARN) / WISCRS Calumet, Fond du Lac, Outagamie and Winnebago (ftUS)", 4152, 4497, 7487); + return true; + case 3294: + record = new EpsgProjectedCrsRecord(8207, "NAD83(HARN) / WISCRS Burnett (m)", 4152, 4499, 7382); + return true; + case 3295: + record = new EpsgProjectedCrsRecord(8208, "NAD83(HARN) / WISCRS Burnett (ftUS)", 4152, 4497, 7383); + return true; + case 3296: + record = new EpsgProjectedCrsRecord(8209, "NAD83(HARN) / WISCRS Buffalo (m)", 4152, 4499, 7430); + return true; + case 3297: + record = new EpsgProjectedCrsRecord(8210, "NAD83(HARN) / WISCRS Buffalo (ftUS)", 4152, 4497, 7431); + return true; + case 3298: + record = new EpsgProjectedCrsRecord(8212, "NAD83(HARN) / WISCRS Brown (m)", 4152, 4499, 7428); + return true; + case 3299: + record = new EpsgProjectedCrsRecord(8213, "NAD83(HARN) / WISCRS Brown (ftUS)", 4152, 4497, 7429); + return true; + case 3300: + record = new EpsgProjectedCrsRecord(8214, "NAD83(HARN) / WISCRS Bayfield (m)", 4152, 4499, 7380); + return true; + case 3301: + record = new EpsgProjectedCrsRecord(8216, "NAD83(HARN) / WISCRS Bayfield (ftUS)", 4152, 4497, 7381); + return true; + case 3302: + record = new EpsgProjectedCrsRecord(8218, "NAD83(HARN) / WISCRS Barron (m)", 4152, 4499, 7426); + return true; + case 3303: + record = new EpsgProjectedCrsRecord(8220, "NAD83(HARN) / WISCRS Barron (ftUS)", 4152, 4497, 7427); + return true; + case 3304: + record = new EpsgProjectedCrsRecord(8222, "NAD83(HARN) / WISCRS Ashland (m)", 4152, 4499, 7378); + return true; + case 3305: + record = new EpsgProjectedCrsRecord(8224, "NAD83(HARN) / WISCRS Ashland (ftUS)", 4152, 4497, 7379); + return true; + case 3306: + record = new EpsgProjectedCrsRecord(8225, "NAD83(HARN) / WISCRS Adams and Juneau (m)", 4152, 4499, 7484); + return true; + case 3307: + record = new EpsgProjectedCrsRecord(8226, "NAD83(HARN) / WISCRS Adams and Juneau (ftUS)", 4152, 4497, 7485); + return true; + case 3308: + record = new EpsgProjectedCrsRecord(8311, "NAD83(2011) / Oregon Burns-Harper zone (m)", 6318, 4499, 8273); + return true; + case 3309: + record = new EpsgProjectedCrsRecord(8312, "NAD83(2011) / Oregon Burns-Harper zone (ft)", 6318, 4495, 8274); + return true; + case 3310: + record = new EpsgProjectedCrsRecord(8313, "NAD83(2011) / Oregon Canyon City-Burns zone (m)", 6318, 4499, 8275); + return true; + case 3311: + record = new EpsgProjectedCrsRecord(8314, "NAD83(2011) / Oregon Canyon City-Burns zone (ft)", 6318, 4495, 8276); + return true; + case 3312: + record = new EpsgProjectedCrsRecord(8315, "NAD83(2011) / Oregon Coast Range North zone (m)", 6318, 4499, 8277); + return true; + case 3313: + record = new EpsgProjectedCrsRecord(8316, "NAD83(2011) / Oregon Coast Range North zone (ft)", 6318, 4495, 8278); + return true; + case 3314: + record = new EpsgProjectedCrsRecord(8317, "NAD83(2011) / Oregon Dayville-Prairie City zone (m)", 6318, 4499, 8279); + return true; + case 3315: + record = new EpsgProjectedCrsRecord(8318, "NAD83(2011) / Oregon Dayville-Prairie City zone (ft)", 6318, 4495, 8280); + return true; + case 3316: + record = new EpsgProjectedCrsRecord(8319, "NAD83(2011) / Oregon Denio-Burns zone (m)", 6318, 4499, 8281); + return true; + case 3317: + record = new EpsgProjectedCrsRecord(8320, "NAD83(2011) / Oregon Denio-Burns zone (ft)", 6318, 4495, 8282); + return true; + case 3318: + record = new EpsgProjectedCrsRecord(8321, "NAD83(2011) / Oregon Halfway zone (m)", 6318, 4499, 8283); + return true; + case 3319: + record = new EpsgProjectedCrsRecord(8322, "NAD83(2011) / Oregon Halfway zone (ft)", 6318, 4495, 8284); + return true; + case 3320: + record = new EpsgProjectedCrsRecord(8323, "NAD83(2011) / Oregon Medford-Diamond Lake zone (m)", 6318, 4499, 8285); + return true; + case 3321: + record = new EpsgProjectedCrsRecord(8324, "NAD83(2011) / Oregon Medford-Diamond Lake zone (ft)", 6318, 4495, 8286); + return true; + case 3322: + record = new EpsgProjectedCrsRecord(8325, "NAD83(2011) / Oregon Mitchell zone (m)", 6318, 4499, 8287); + return true; + case 3323: + record = new EpsgProjectedCrsRecord(8326, "NAD83(2011) / Oregon Mitchell zone (ft)", 6318, 4495, 8288); + return true; + case 3324: + record = new EpsgProjectedCrsRecord(8327, "NAD83(2011) / Oregon North Central zone (m)", 6318, 4499, 8289); + return true; + case 3325: + record = new EpsgProjectedCrsRecord(8328, "NAD83(2011) / Oregon North Central zone (ft)", 6318, 4495, 8290); + return true; + case 3326: + record = new EpsgProjectedCrsRecord(8329, "NAD83(2011) / Oregon Ochoco Summit zone (m)", 6318, 4499, 8291); + return true; + case 3327: + record = new EpsgProjectedCrsRecord(8330, "NAD83(2011) / Oregon Ochoco Summit zone (ft)", 6318, 4495, 8292); + return true; + case 3328: + record = new EpsgProjectedCrsRecord(8331, "NAD83(2011) / Oregon Owyhee zone (m)", 6318, 4499, 8293); + return true; + case 3329: + record = new EpsgProjectedCrsRecord(8332, "NAD83(2011) / Oregon Owyhee zone (ft)", 6318, 4495, 8294); + return true; + case 3330: + record = new EpsgProjectedCrsRecord(8333, "NAD83(2011) / Oregon Pilot Rock-Ukiah zone (m)", 6318, 4499, 8295); + return true; + case 3331: + record = new EpsgProjectedCrsRecord(8334, "NAD83(2011) / Oregon Pilot Rock-Ukiah zone (ft)", 6318, 4495, 8296); + return true; + case 3332: + record = new EpsgProjectedCrsRecord(8335, "NAD83(2011) / Oregon Prairie City-Brogan zone (m)", 6318, 4499, 8297); + return true; + case 3333: + record = new EpsgProjectedCrsRecord(8336, "NAD83(2011) / Oregon Prairie City-Brogan zone (ft)", 6318, 4495, 8298); + return true; + case 3334: + record = new EpsgProjectedCrsRecord(8337, "NAD83(2011) / Oregon Riley-Lakeview zone (m)", 6318, 4499, 8299); + return true; + case 3335: + record = new EpsgProjectedCrsRecord(8338, "NAD83(2011) / Oregon Riley-Lakeview zone (ft)", 6318, 4495, 8300); + return true; + case 3336: + record = new EpsgProjectedCrsRecord(8339, "NAD83(2011) / Oregon Siskiyou Pass zone (m)", 6318, 4499, 8301); + return true; + case 3337: + record = new EpsgProjectedCrsRecord(8340, "NAD83(2011) / Oregon Siskiyou Pass zone (ft)", 6318, 4495, 8302); + return true; + case 3338: + record = new EpsgProjectedCrsRecord(8341, "NAD83(2011) / Oregon Ukiah-Fox zone (m)", 6318, 4499, 8303); + return true; + case 3339: + record = new EpsgProjectedCrsRecord(8342, "NAD83(2011) / Oregon Ukiah-Fox zone (ft)", 6318, 4495, 8304); + return true; + case 3340: + record = new EpsgProjectedCrsRecord(8343, "NAD83(2011) / Oregon Wallowa zone (m)", 6318, 4499, 8305); + return true; + case 3341: + record = new EpsgProjectedCrsRecord(8344, "NAD83(2011) / Oregon Wallowa zone (ft)", 6318, 4495, 8306); + return true; + case 3342: + record = new EpsgProjectedCrsRecord(8345, "NAD83(2011) / Oregon Warner Highway zone (m)", 6318, 4499, 8307); + return true; + case 3343: + record = new EpsgProjectedCrsRecord(8346, "NAD83(2011) / Oregon Warner Highway zone (ft)", 6318, 4495, 8308); + return true; + case 3344: + record = new EpsgProjectedCrsRecord(8347, "NAD83(2011) / Oregon Willamette Pass zone (m)", 6318, 4499, 8309); + return true; + case 3345: + record = new EpsgProjectedCrsRecord(8348, "NAD83(2011) / Oregon Willamette Pass zone (ft)", 6318, 4495, 8310); + return true; + case 3346: + record = new EpsgProjectedCrsRecord(8352, "S-JTSK [JTSK03] / Krovak", 8351, 6501, 5509); + return true; + case 3347: + record = new EpsgProjectedCrsRecord(8353, "S-JTSK [JTSK03] / Krovak East North", 8351, 4499, 5510); + return true; + case 3348: + record = new EpsgProjectedCrsRecord(8379, "NAD83 / NCRS Las Vegas (m)", 4269, 4499, 8373); + return true; + case 3349: + record = new EpsgProjectedCrsRecord(8380, "NAD83 / NCRS Las Vegas (ftUS)", 4269, 4497, 8374); + return true; + case 3350: + record = new EpsgProjectedCrsRecord(8381, "NAD83 / NCRS Las Vegas high (m)", 4269, 4499, 8375); + return true; + case 3351: + record = new EpsgProjectedCrsRecord(8382, "NAD83 / NCRS Las Vegas high (ftUS)", 4269, 4497, 8376); + return true; + case 3352: + record = new EpsgProjectedCrsRecord(8383, "NAD83(2011) / NCRS Las Vegas (m)", 6318, 4499, 8373); + return true; + case 3353: + record = new EpsgProjectedCrsRecord(8384, "NAD83(2011) / NCRS Las Vegas (ftUS)", 6318, 4497, 8374); + return true; + case 3354: + record = new EpsgProjectedCrsRecord(8385, "NAD83(2011) / NCRS Las Vegas high (m)", 6318, 4499, 8375); + return true; + case 3355: + record = new EpsgProjectedCrsRecord(8387, "NAD83(2011) / NCRS Las Vegas high (ftUS)", 6318, 4497, 8376); + return true; + case 3356: + record = new EpsgProjectedCrsRecord(8391, "GDA94 / WEIPA94", 4283, 4400, 8389); + return true; + case 3357: + record = new EpsgProjectedCrsRecord(8395, "ETRS89 / Gauss-Kruger CM 9E", 4258, 4400, 16302); + return true; + case 3358: + record = new EpsgProjectedCrsRecord(8433, "Macao 1920 / Macao Grid", 8428, 4500, 8432); + return true; + case 3359: + record = new EpsgProjectedCrsRecord(8441, "Tananarive / Laborde Grid", 4297, 4530, 8440); + return true; + case 3360: + record = new EpsgProjectedCrsRecord(8455, "RGTAAF07 / UTM zone 53S", 7073, 4400, 16153); + return true; + case 3361: + record = new EpsgProjectedCrsRecord(8456, "RGTAAF07 / UTM zone 54S", 7073, 4400, 16154); + return true; + case 3362: + record = new EpsgProjectedCrsRecord(8518, "NAD83(2011) / KS RCS zone 1", 6318, 4497, 8458); + return true; + case 3363: + record = new EpsgProjectedCrsRecord(8519, "NAD83(2011) / KS RCS zone 2", 6318, 4497, 8459); + return true; + case 3364: + record = new EpsgProjectedCrsRecord(8520, "NAD83(2011) / KS RCS zone 3", 6318, 4497, 8490); + return true; + case 3365: + record = new EpsgProjectedCrsRecord(8521, "NAD83(2011) / KS RCS zone 4", 6318, 4497, 8491); + return true; + case 3366: + record = new EpsgProjectedCrsRecord(8522, "NAD83(2011) / KS RCS zone 5", 6318, 4497, 8492); + return true; + case 3367: + record = new EpsgProjectedCrsRecord(8523, "NAD83(2011) / KS RCS zone 6", 6318, 4497, 8493); + return true; + case 3368: + record = new EpsgProjectedCrsRecord(8524, "NAD83(2011) / KS RCS zone 7", 6318, 4497, 8494); + return true; + case 3369: + record = new EpsgProjectedCrsRecord(8525, "NAD83(2011) / KS RCS zone 8", 6318, 4497, 8495); + return true; + case 3370: + record = new EpsgProjectedCrsRecord(8526, "NAD83(2011) / KS RCS zone 9", 6318, 4497, 8498); + return true; + case 3371: + record = new EpsgProjectedCrsRecord(8527, "NAD83(2011) / KS RCS zone 10", 6318, 4497, 8499); + return true; + case 3372: + record = new EpsgProjectedCrsRecord(8528, "NAD83(2011) / KS RCS zone 11", 6318, 4497, 8500); + return true; + case 3373: + record = new EpsgProjectedCrsRecord(8529, "NAD83(2011) / KS RCS zone 12", 6318, 4497, 8501); + return true; + case 3374: + record = new EpsgProjectedCrsRecord(8531, "NAD83(2011) / KS RCS zone 13", 6318, 4497, 8502); + return true; + case 3375: + record = new EpsgProjectedCrsRecord(8533, "NAD83(2011) / KS RCS zone 14", 6318, 4497, 8503); + return true; + case 3376: + record = new EpsgProjectedCrsRecord(8534, "NAD83(2011) / KS RCS zone 15", 6318, 4497, 8504); + return true; + case 3377: + record = new EpsgProjectedCrsRecord(8535, "NAD83(2011) / KS RCS zone 16", 6318, 4497, 8505); + return true; + case 3378: + record = new EpsgProjectedCrsRecord(8536, "NAD83(2011) / KS RCS zone 17", 6318, 4497, 8506); + return true; + case 3379: + record = new EpsgProjectedCrsRecord(8538, "NAD83(2011) / KS RCS zone 18", 6318, 4497, 8507); + return true; + case 3380: + record = new EpsgProjectedCrsRecord(8539, "NAD83(2011) / KS RCS zone 19", 6318, 4497, 8515); + return true; + case 3381: + record = new EpsgProjectedCrsRecord(8540, "NAD83(2011) / KS RCS zone 20", 6318, 4497, 8516); + return true; + case 3382: + record = new EpsgProjectedCrsRecord(8677, "MGI 1901 / Balkans zone 5", 3906, 4498, 18275); + return true; + case 3383: + record = new EpsgProjectedCrsRecord(8678, "MGI 1901 / Balkans zone 6", 3906, 4498, 18276); + return true; + case 3384: + record = new EpsgProjectedCrsRecord(8679, "MGI 1901 / Balkans zone 8", 3906, 4498, 18278); + return true; + case 3385: + record = new EpsgProjectedCrsRecord(8682, "ETRS89-SRB [STRS00] / UTM zone 34N", 8685, 4400, 16034); + return true; + case 3386: + record = new EpsgProjectedCrsRecord(8686, "MGI 1901 / Slovenia Grid", 3906, 4498, 19967); + return true; + case 3387: + record = new EpsgProjectedCrsRecord(8687, "ETRS89-SVN [D96] / UTM zone 33N", 4765, 4400, 16033); + return true; + case 3388: + record = new EpsgProjectedCrsRecord(8692, "NAD83(MA11) / UTM zone 54N", 6325, 4400, 16054); + return true; + case 3389: + record = new EpsgProjectedCrsRecord(8693, "NAD83(MA11) / UTM zone 55N", 6325, 4400, 16055); + return true; + case 3390: + record = new EpsgProjectedCrsRecord(8826, "NAD83 / Idaho Transverse Mercator", 4269, 4499, 8825); + return true; + case 3391: + record = new EpsgProjectedCrsRecord(8836, "MTRF-2000 / UTM zone 36N", 8818, 4400, 16036); + return true; + case 3392: + record = new EpsgProjectedCrsRecord(8837, "MTRF-2000 / UTM zone 37N", 8818, 4400, 16037); + return true; + case 3393: + record = new EpsgProjectedCrsRecord(8838, "MTRF-2000 / UTM zone 38N", 8818, 4400, 16038); + return true; + case 3394: + record = new EpsgProjectedCrsRecord(8839, "MTRF-2000 / UTM zone 39N", 8818, 4400, 16039); + return true; + case 3395: + record = new EpsgProjectedCrsRecord(8840, "MTRF-2000 / UTM zone 40N", 8818, 4400, 16040); + return true; + case 3396: + record = new EpsgProjectedCrsRecord(8857, "WGS 84 / Equal Earth Greenwich", 4326, 4400, 8854); + return true; + case 3397: + record = new EpsgProjectedCrsRecord(8858, "WGS 84 / Equal Earth Americas", 4326, 4400, 8855); + return true; + case 3398: + record = new EpsgProjectedCrsRecord(8859, "WGS 84 / Equal Earth Asia-Pacific", 4326, 4400, 8856); + return true; + case 3399: + record = new EpsgProjectedCrsRecord(8903, "RGWF96 / UTM zone 1S", 8900, 4400, 16101); + return true; + case 3400: + record = new EpsgProjectedCrsRecord(8908, "CR-SIRGAS epoch 2014.59 / CRTM05", 8907, 4400, 5366); + return true; + case 3401: + record = new EpsgProjectedCrsRecord(8909, "CR-SIRGAS epoch 2014.59 / UTM zone 16N", 8907, 4400, 16016); + return true; + case 3402: + record = new EpsgProjectedCrsRecord(8910, "CR-SIRGAS epoch 2014.59 / UTM zone 17N", 8907, 4400, 16017); + return true; + case 3403: + record = new EpsgProjectedCrsRecord(9039, "ISN2016 / LAEA Europe", 8086, 4532, 19986); + return true; + case 3404: + record = new EpsgProjectedCrsRecord(9040, "ISN2016 / LCC Europe", 8086, 4532, 19985); + return true; + case 3405: + record = new EpsgProjectedCrsRecord(9141, "ETRS89-XKX [KOSOVAREF01] / Balkans zone 7", 9140, 4400, 18277); + return true; + case 3406: + record = new EpsgProjectedCrsRecord(9149, "SIRGAS-Chile 2013 / UTM zone 18S", 9148, 4400, 16118); + return true; + case 3407: + record = new EpsgProjectedCrsRecord(9150, "SIRGAS-Chile 2013 / UTM zone 19S", 9148, 4400, 16119); + return true; + case 3408: + record = new EpsgProjectedCrsRecord(9154, "SIRGAS-Chile 2016 / UTM zone 18S", 9153, 4400, 16118); + return true; + case 3409: + record = new EpsgProjectedCrsRecord(9155, "SIRGAS-Chile 2016 / UTM zone 19S", 9153, 4400, 16119); + return true; + case 3410: + record = new EpsgProjectedCrsRecord(9156, "RSAO13 / UTM zone 32S", 8699, 4400, 16132); + return true; + case 3411: + record = new EpsgProjectedCrsRecord(9157, "RSAO13 / UTM zone 33S", 8699, 4400, 16133); + return true; + case 3412: + record = new EpsgProjectedCrsRecord(9158, "RSAO13 / UTM zone 34S", 8699, 4400, 16134); + return true; + case 3413: + record = new EpsgProjectedCrsRecord(9159, "RSAO13 / TM 12 SE", 8699, 4400, 16612); + return true; + case 3414: + record = new EpsgProjectedCrsRecord(9191, "WGS 84 / NIWA Albers", 4326, 4400, 9190); + return true; + case 3415: + record = new EpsgProjectedCrsRecord(9205, "VN-2000 / TM-3 103-00", 4756, 4400, 9058); + return true; + case 3416: + record = new EpsgProjectedCrsRecord(9206, "VN-2000 / TM-3 104-00", 4756, 4400, 9192); + return true; + case 3417: + record = new EpsgProjectedCrsRecord(9207, "VN-2000 / TM-3 104-30", 4756, 4400, 9193); + return true; + case 3418: + record = new EpsgProjectedCrsRecord(9208, "VN-2000 / TM-3 104-45", 4756, 4400, 9194); + return true; + case 3419: + record = new EpsgProjectedCrsRecord(9209, "VN-2000 / TM-3 105-30", 4756, 4400, 9195); + return true; + case 3420: + record = new EpsgProjectedCrsRecord(9210, "VN-2000 / TM-3 105-45", 4756, 4400, 9196); + return true; + case 3421: + record = new EpsgProjectedCrsRecord(9211, "VN-2000 / TM-3 106-00", 4756, 4400, 9197); + return true; + case 3422: + record = new EpsgProjectedCrsRecord(9212, "VN-2000 / TM-3 106-15", 4756, 4400, 9198); + return true; + case 3423: + record = new EpsgProjectedCrsRecord(9213, "VN-2000 / TM-3 106-30", 4756, 4400, 9199); + return true; + case 3424: + record = new EpsgProjectedCrsRecord(9214, "VN-2000 / TM-3 107-00", 4756, 4400, 9200); + return true; + case 3425: + record = new EpsgProjectedCrsRecord(9215, "VN-2000 / TM-3 107-15", 4756, 4400, 9201); + return true; + case 3426: + record = new EpsgProjectedCrsRecord(9216, "VN-2000 / TM-3 107-30", 4756, 4400, 9202); + return true; + case 3427: + record = new EpsgProjectedCrsRecord(9217, "VN-2000 / TM-3 108-15", 4756, 4400, 9203); + return true; + case 3428: + record = new EpsgProjectedCrsRecord(9218, "VN-2000 / TM-3 108-30", 4756, 4400, 9204); + return true; + case 3429: + record = new EpsgProjectedCrsRecord(9221, "Hartebeesthoek94 / ZAF BSU Albers 25E", 4148, 4500, 9219); + return true; + case 3430: + record = new EpsgProjectedCrsRecord(9222, "Hartebeesthoek94 / ZAF BSU Albers 44E", 4148, 4500, 9220); + return true; + case 3431: + record = new EpsgProjectedCrsRecord(9249, "Tapi Aike / Argentina 1", 9248, 4530, 18031); + return true; + case 3432: + record = new EpsgProjectedCrsRecord(9250, "Tapi Aike / Argentina 2", 9248, 4530, 18032); + return true; + case 3433: + record = new EpsgProjectedCrsRecord(9252, "MMN / Argentina 2", 9251, 4530, 18032); + return true; + case 3434: + record = new EpsgProjectedCrsRecord(9254, "MMS / Argentina 2", 9253, 4530, 18032); + return true; + case 3435: + record = new EpsgProjectedCrsRecord(9265, "POSGAR 2007 / UTM zone 19S", 5340, 4400, 16119); + return true; + case 3436: + record = new EpsgProjectedCrsRecord(9271, "MGI / Austria West", 4312, 4530, 9268); + return true; + case 3437: + record = new EpsgProjectedCrsRecord(9272, "MGI / Austria Central", 4312, 4530, 9269); + return true; + case 3438: + record = new EpsgProjectedCrsRecord(9273, "MGI / Austria East", 4312, 4530, 9270); + return true; + case 3439: + record = new EpsgProjectedCrsRecord(9284, "Pampa del Castillo / Argentina 1", 4161, 4530, 18031); + return true; + case 3440: + record = new EpsgProjectedCrsRecord(9285, "Pampa del Castillo / Argentina 3", 4161, 4530, 18033); + return true; + case 3441: + record = new EpsgProjectedCrsRecord(9295, "ONGD17 / UTM zone 39N", 9294, 4400, 16039); + return true; + case 3442: + record = new EpsgProjectedCrsRecord(9296, "ONGD17 / UTM zone 40N", 9294, 4400, 16040); + return true; + case 3443: + record = new EpsgProjectedCrsRecord(9297, "ONGD17 / UTM zone 41N", 9294, 4400, 16041); + return true; + case 3444: + record = new EpsgProjectedCrsRecord(9300, "HS2 Survey Grid", 9299, 4400, 9301); + return true; + case 3445: + record = new EpsgProjectedCrsRecord(9311, "NAD27 / US National Atlas Equal Area", 4267, 4499, 3899); + return true; + case 3446: + record = new EpsgProjectedCrsRecord(9354, "WGS 84 / IBCSO Polar Stereographic", 4326, 4470, 9353); + return true; + case 3447: + record = new EpsgProjectedCrsRecord(9356, "KSA-GRF17 / UTM zone 36N", 9333, 4400, 16036); + return true; + case 3448: + record = new EpsgProjectedCrsRecord(9357, "KSA-GRF17 / UTM zone 37N", 9333, 4400, 16037); + return true; + case 3449: + record = new EpsgProjectedCrsRecord(9358, "KSA-GRF17 / UTM zone 38N", 9333, 4400, 16038); + return true; + case 3450: + record = new EpsgProjectedCrsRecord(9359, "KSA-GRF17 / UTM zone 39N", 9333, 4400, 16039); + return true; + case 3451: + record = new EpsgProjectedCrsRecord(9360, "KSA-GRF17 / UTM zone 40N", 9333, 4400, 16040); + return true; + case 3452: + record = new EpsgProjectedCrsRecord(9367, "TPEN11 Grid", 9364, 4400, 9366); + return true; + case 3453: + record = new EpsgProjectedCrsRecord(9373, "MML07 Grid", 9372, 4400, 9370); + return true; + case 3454: + record = new EpsgProjectedCrsRecord(9377, "MAGNA-SIRGAS 2018 / Origen-Nacional", 20046, 4500, 9376); + return true; + case 3455: + record = new EpsgProjectedCrsRecord(9387, "AbInvA96_2020 Grid", 9384, 4400, 9385); + return true; + case 3456: + record = new EpsgProjectedCrsRecord(9391, "ETRS89-BGR [BGS2005] / UTM zone 35N", 7798, 4400, 16035); + return true; + case 3457: + record = new EpsgProjectedCrsRecord(9404, "PN68 / UTM zone 27N", 9403, 4400, 16027); + return true; + case 3458: + record = new EpsgProjectedCrsRecord(9405, "PN68 / UTM zone 28N", 9403, 4400, 16028); + return true; + case 3459: + record = new EpsgProjectedCrsRecord(9406, "PN84 / UTM zone 27N", 4728, 4400, 16027); + return true; + case 3460: + record = new EpsgProjectedCrsRecord(9407, "PN84 / UTM zone 28N", 4728, 4400, 16028); + return true; + case 3461: + record = new EpsgProjectedCrsRecord(9456, "GBK19 Grid", 9453, 4400, 9455); + return true; + case 3462: + record = new EpsgProjectedCrsRecord(9473, "GDA2020 / Australian Albers", 7844, 4400, 17365); + return true; + case 3463: + record = new EpsgProjectedCrsRecord(9476, "SRGI2013 / UTM zone 46N", 9470, 4400, 16046); + return true; + case 3464: + record = new EpsgProjectedCrsRecord(9477, "SRGI2013 / UTM zone 47N", 9470, 4400, 16047); + return true; + case 3465: + record = new EpsgProjectedCrsRecord(9478, "SRGI2013 / UTM zone 48N", 9470, 4400, 16048); + return true; + case 3466: + record = new EpsgProjectedCrsRecord(9479, "SRGI2013 / UTM zone 49N", 9470, 4400, 16049); + return true; + case 3467: + record = new EpsgProjectedCrsRecord(9480, "SRGI2013 / UTM zone 50N", 9470, 4400, 16050); + return true; + case 3468: + record = new EpsgProjectedCrsRecord(9481, "SRGI2013 / UTM zone 51N", 9470, 4400, 16051); + return true; + case 3469: + record = new EpsgProjectedCrsRecord(9482, "SRGI2013 / UTM zone 52N", 9470, 4400, 16052); + return true; + case 3470: + record = new EpsgProjectedCrsRecord(9487, "SRGI2013 / UTM zone 47S", 9470, 4400, 16147); + return true; + case 3471: + record = new EpsgProjectedCrsRecord(9488, "SRGI2013 / UTM zone 48S", 9470, 4400, 16148); + return true; + case 3472: + record = new EpsgProjectedCrsRecord(9489, "SRGI2013 / UTM zone 49S", 9470, 4400, 16149); + return true; + case 3473: + record = new EpsgProjectedCrsRecord(9490, "SRGI2013 / UTM zone 50S", 9470, 4400, 16150); + return true; + case 3474: + record = new EpsgProjectedCrsRecord(9491, "SRGI2013 / UTM zone 51S", 9470, 4400, 16151); + return true; + case 3475: + record = new EpsgProjectedCrsRecord(9492, "SRGI2013 / UTM zone 52S", 9470, 4400, 16152); + return true; + case 3476: + record = new EpsgProjectedCrsRecord(9493, "SRGI2013 / UTM zone 53S", 9470, 4400, 16153); + return true; + case 3477: + record = new EpsgProjectedCrsRecord(9494, "SRGI2013 / UTM zone 54S", 9470, 4400, 16154); + return true; + case 3478: + record = new EpsgProjectedCrsRecord(9498, "POSGAR 2007 / CABA 2019", 5340, 4530, 9497); + return true; + case 3479: + record = new EpsgProjectedCrsRecord(9549, "LTF2004(C)", 9547, 4400, 9548); + return true; + case 3480: + record = new EpsgProjectedCrsRecord(9674, "NAD83 / USFS R6 Albers", 4269, 4400, 9673); + return true; + case 3481: + record = new EpsgProjectedCrsRecord(9678, "Gulshan 303 / Bangladesh Transverse Mercator", 4682, 4400, 9677); + return true; + case 3482: + record = new EpsgProjectedCrsRecord(9680, "WGS 84 / TM 90 NE", 4326, 4400, 16490); + return true; + case 3483: + record = new EpsgProjectedCrsRecord(9697, "REDGEOMIN / UTM zone 12S", 9696, 4400, 16112); + return true; + case 3484: + record = new EpsgProjectedCrsRecord(9698, "REDGEOMIN / UTM zone 18S", 9696, 4400, 16118); + return true; + case 3485: + record = new EpsgProjectedCrsRecord(9699, "REDGEOMIN / UTM zone 19S", 9696, 4400, 16119); + return true; + case 3486: + record = new EpsgProjectedCrsRecord(9709, "NAD83(CSRS) / UTM zone 23N", 4617, 4400, 16023); + return true; + case 3487: + record = new EpsgProjectedCrsRecord(9712, "NAD83 / UTM zone 24N", 4269, 4400, 16024); + return true; + case 3488: + record = new EpsgProjectedCrsRecord(9713, "NAD83(CSRS) / UTM zone 24N", 4617, 4400, 16024); + return true; + case 3489: + record = new EpsgProjectedCrsRecord(9716, "ETRS89-ITA [IGM95] / UTM zone 34N", 4670, 4400, 16034); + return true; + case 3490: + record = new EpsgProjectedCrsRecord(9741, "EOS21 Grid", 9739, 4400, 9738); + return true; + case 3491: + record = new EpsgProjectedCrsRecord(9748, "NAD83(2011) / Alabama East (ftUS)", 6318, 4497, 9746); + return true; + case 3492: + record = new EpsgProjectedCrsRecord(9749, "NAD83(2011) / Alabama West (ftUS)", 6318, 4497, 9747); + return true; + case 3493: + record = new EpsgProjectedCrsRecord(9761, "ECML14_NB Grid", 9758, 4400, 9760); + return true; + case 3494: + record = new EpsgProjectedCrsRecord(9766, "EWR2 Grid", 9763, 4400, 9765); + return true; + case 3495: + record = new EpsgProjectedCrsRecord(9793, "ETRS89-FRA [RGF93 v2] / Lambert-93", 9777, 4499, 18085); + return true; + case 3496: + record = new EpsgProjectedCrsRecord(9794, "ETRS89-FRA [RGF93 v2b] / Lambert-93", 9782, 4499, 18085); + return true; + case 3497: + record = new EpsgProjectedCrsRecord(9821, "UCS-2000 / LCS-32 Kyiv region", 5561, 4531, 9796); + return true; + case 3498: + record = new EpsgProjectedCrsRecord(9822, "ETRS89-FRA [RGF93 v2] / CC42", 9777, 4499, 18101); + return true; + case 3499: + record = new EpsgProjectedCrsRecord(9823, "ETRS89-FRA [RGF93 v2] / CC43", 9777, 4499, 18102); + return true; + case 3500: + record = new EpsgProjectedCrsRecord(9824, "ETRS89-FRA [RGF93 v2] / CC44", 9777, 4499, 18103); + return true; + case 3501: + record = new EpsgProjectedCrsRecord(9825, "ETRS89-FRA [RGF93 v2] / CC45", 9777, 4499, 18104); + return true; + case 3502: + record = new EpsgProjectedCrsRecord(9826, "ETRS89-FRA [RGF93 v2] / CC46", 9777, 4499, 18105); + return true; + case 3503: + record = new EpsgProjectedCrsRecord(9827, "ETRS89-FRA [RGF93 v2] / CC47", 9777, 4499, 18106); + return true; + case 3504: + record = new EpsgProjectedCrsRecord(9828, "ETRS89-FRA [RGF93 v2] / CC48", 9777, 4499, 18107); + return true; + case 3505: + record = new EpsgProjectedCrsRecord(9829, "ETRS89-FRA [RGF93 v2] / CC49", 9777, 4499, 18108); + return true; + case 3506: + record = new EpsgProjectedCrsRecord(9830, "ETRS89-FRA [RGF93 v2] / CC50", 9777, 4499, 18109); + return true; + case 3507: + record = new EpsgProjectedCrsRecord(9831, "UCS-2000 / LCS-01 Crimea", 5561, 4531, 9797); + return true; + case 3508: + record = new EpsgProjectedCrsRecord(9832, "UCS-2000 / LCS-05 Vinnytsia", 5561, 4531, 9798); + return true; + case 3509: + record = new EpsgProjectedCrsRecord(9833, "UCS-2000 / LCS-07 Volyn", 5561, 4531, 9799); + return true; + case 3510: + record = new EpsgProjectedCrsRecord(9834, "UCS-2000 / LCS-12 Dnipropetrovsk", 5561, 4531, 9800); + return true; + case 3511: + record = new EpsgProjectedCrsRecord(9835, "UCS-2000 / LCS-14 Donetsk", 5561, 4531, 9801); + return true; + case 3512: + record = new EpsgProjectedCrsRecord(9836, "UCS-2000 / LCS-18 Zhytomyr", 5561, 4531, 9802); + return true; + case 3513: + record = new EpsgProjectedCrsRecord(9837, "UCS-2000 / LCS-21 Zakarpattia", 5561, 4531, 9803); + return true; + case 3514: + record = new EpsgProjectedCrsRecord(9838, "UCS-2000 / LCS-23 Zaporizhzhia", 5561, 4531, 9804); + return true; + case 3515: + record = new EpsgProjectedCrsRecord(9839, "UCS-2000 / LCS-26 Ivano-Frankivsk", 5561, 4531, 9805); + return true; + case 3516: + record = new EpsgProjectedCrsRecord(9840, "UCS-2000 / LCS-35 Kirovohrad", 5561, 4531, 9806); + return true; + case 3517: + record = new EpsgProjectedCrsRecord(9841, "UCS-2000 / LCS-44 Luhansk", 5561, 4531, 9807); + return true; + case 3518: + record = new EpsgProjectedCrsRecord(9842, "ETRS89-FRA [RGF93 v2b] / CC42", 9782, 4499, 18101); + return true; + case 3519: + record = new EpsgProjectedCrsRecord(9843, "ETRS89-FRA [RGF93 v2b] / CC43", 9782, 4499, 18102); + return true; + case 3520: + record = new EpsgProjectedCrsRecord(9844, "ETRS89-FRA [RGF93 v2b] / CC44", 9782, 4499, 18103); + return true; + case 3521: + record = new EpsgProjectedCrsRecord(9845, "ETRS89-FRA [RGF93 v2b] / CC45", 9782, 4499, 18104); + return true; + case 3522: + record = new EpsgProjectedCrsRecord(9846, "ETRS89-FRA [RGF93 v2b] / CC46", 9782, 4499, 18105); + return true; + case 3523: + record = new EpsgProjectedCrsRecord(9847, "ETRS89-FRA [RGF93 v2b] / CC47", 9782, 4499, 18106); + return true; + case 3524: + record = new EpsgProjectedCrsRecord(9848, "ETRS89-FRA [RGF93 v2b] / CC48", 9782, 4499, 18107); + return true; + case 3525: + record = new EpsgProjectedCrsRecord(9849, "ETRS89-FRA [RGF93 v2b] / CC49", 9782, 4499, 18108); + return true; + case 3526: + record = new EpsgProjectedCrsRecord(9850, "ETRS89-FRA [RGF93 v2b] / CC50", 9782, 4499, 18109); + return true; + case 3527: + record = new EpsgProjectedCrsRecord(9851, "UCS-2000 / LCS-46 Lviv", 5561, 4531, 9808); + return true; + case 3528: + record = new EpsgProjectedCrsRecord(9852, "UCS-2000 / LCS-48 Mykolaiv", 5561, 4531, 9809); + return true; + case 3529: + record = new EpsgProjectedCrsRecord(9853, "UCS-2000 / LCS-51 Odessa", 5561, 4531, 9810); + return true; + case 3530: + record = new EpsgProjectedCrsRecord(9854, "UCS-2000 / LCS-53 Poltava", 5561, 4531, 9811); + return true; + case 3531: + record = new EpsgProjectedCrsRecord(9855, "UCS-2000 / LCS-56 Rivne", 5561, 4531, 9812); + return true; + case 3532: + record = new EpsgProjectedCrsRecord(9856, "UCS-2000 / LCS-59 Sumy", 5561, 4531, 9813); + return true; + case 3533: + record = new EpsgProjectedCrsRecord(9857, "UCS-2000 / LCS-61 Ternopil", 5561, 4531, 9814); + return true; + case 3534: + record = new EpsgProjectedCrsRecord(9858, "UCS-2000 / LCS-63 Kharkiv", 5561, 4531, 9815); + return true; + case 3535: + record = new EpsgProjectedCrsRecord(9859, "UCS-2000 / LCS-65 Kherson", 5561, 4531, 9816); + return true; + case 3536: + record = new EpsgProjectedCrsRecord(9860, "UCS-2000 / LCS-68 Khmelnytsky", 5561, 4531, 9812); + return true; + case 3537: + record = new EpsgProjectedCrsRecord(9861, "UCS-2000 / LCS-71 Cherkasy", 5561, 4531, 9817); + return true; + case 3538: + record = new EpsgProjectedCrsRecord(9862, "UCS-2000 / LCS-73 Chernivtsi", 5561, 4531, 9818); + return true; + case 3539: + record = new EpsgProjectedCrsRecord(9863, "UCS-2000 / LCS-74 Chernihiv", 5561, 4531, 9819); + return true; + case 3540: + record = new EpsgProjectedCrsRecord(9864, "UCS-2000 / LCS-80 Kyiv city", 5561, 4531, 9796); + return true; + case 3541: + record = new EpsgProjectedCrsRecord(9865, "UCS-2000 / LCS-85 Sevastopol", 5561, 4531, 9820); + return true; + case 3542: + record = new EpsgProjectedCrsRecord(9869, "MRH21 Grid", 9866, 4400, 9868); + return true; + case 3543: + record = new EpsgProjectedCrsRecord(9874, "PNG94 / PNGMG94 zone 57", 5546, 4400, 9872); + return true; + case 3544: + record = new EpsgProjectedCrsRecord(9875, "PNG94 / PNGMG94 zone 58", 5546, 4400, 9873); + return true; + case 3545: + record = new EpsgProjectedCrsRecord(9880, "MOLDOR11 Grid", 9871, 4400, 9879); + return true; + case 3546: + record = new EpsgProjectedCrsRecord(9895, "LUREF / Luxembourg TM (3D)", 9893, 1046, 9894); + return true; + case 3547: + record = new EpsgProjectedCrsRecord(9943, "EBBWV14 Grid", 9939, 4400, 9942); + return true; + case 3548: + record = new EpsgProjectedCrsRecord(9945, "Macedonia State Coordinate System truncated", 3906, 4498, 9911); + return true; + case 3549: + record = new EpsgProjectedCrsRecord(9947, "ISN2004 / LAEA Iceland", 5324, 4400, 9946); + return true; + case 3550: + record = new EpsgProjectedCrsRecord(9967, "HULLEE13 Grid", 9964, 4400, 9966); + return true; + case 3551: + record = new EpsgProjectedCrsRecord(9972, "SCM22 Grid", 9969, 4400, 9971); + return true; + case 3552: + record = new EpsgProjectedCrsRecord(9977, "FNL22 Grid", 9974, 4400, 9976); + return true; + case 3553: + record = new EpsgProjectedCrsRecord(10160, "S34J reconstruction east-orientated", 10158, 4400, 10159); + return true; + case 3554: + record = new EpsgProjectedCrsRecord(10183, "DoPw22 Grid", 10175, 4400, 10182); + return true; + case 3555: + record = new EpsgProjectedCrsRecord(10188, "ShAb07 Grid", 10185, 4400, 10187); + return true; + case 3556: + record = new EpsgProjectedCrsRecord(10194, "CNH22 Grid", 10191, 4400, 10193); + return true; + case 3557: + record = new EpsgProjectedCrsRecord(10199, "CWS13 Grid", 10196, 4400, 10198); + return true; + case 3558: + record = new EpsgProjectedCrsRecord(10207, "DIBA15 Grid", 10204, 4400, 10206); + return true; + case 3559: + record = new EpsgProjectedCrsRecord(10212, "GWPBS22 Grid", 10209, 4400, 10211); + return true; + case 3560: + record = new EpsgProjectedCrsRecord(10217, "GWWAB22 Grid", 10214, 4400, 10211); + return true; + case 3561: + record = new EpsgProjectedCrsRecord(10222, "GWWWA22 Grid", 10219, 4400, 10211); + return true; + case 3562: + record = new EpsgProjectedCrsRecord(10227, "MALS09 Grid", 10224, 4400, 10226); + return true; + case 3563: + record = new EpsgProjectedCrsRecord(10235, "OxWo08 Grid", 10229, 4400, 10234); + return true; + case 3564: + record = new EpsgProjectedCrsRecord(10240, "SYC20 Grid", 10237, 4400, 10239); + return true; + case 3565: + record = new EpsgProjectedCrsRecord(10250, "S34S reconstruction east-orientated", 10249, 4400, 10159); + return true; + case 3566: + record = new EpsgProjectedCrsRecord(10254, "S45B reconstruction east-orientated", 10252, 4400, 10253); + return true; + case 3567: + record = new EpsgProjectedCrsRecord(10258, "GS reconstruction east-orientated", 10256, 4400, 10257); + return true; + case 3568: + record = new EpsgProjectedCrsRecord(10262, "GSB reconstruction east-orientated", 10260, 4400, 10261); + return true; + case 3569: + record = new EpsgProjectedCrsRecord(10266, "KK reconstruction east-orientated", 10265, 4400, 10257); + return true; + case 3570: + record = new EpsgProjectedCrsRecord(10270, "Ostenfeld reconstruction", 10268, 4400, 10269); + return true; + case 3571: + record = new EpsgProjectedCrsRecord(10275, "SMITB20 Grid", 10272, 4400, 10274); + return true; + case 3572: + record = new EpsgProjectedCrsRecord(10280, "RBEPP12 Grid", 10277, 4400, 10279); + return true; + case 3573: + record = new EpsgProjectedCrsRecord(10285, "ETRS89-DEU [ETRS89/DREF91/2016] / 3-degree Gauss-Kruger zone 3", 10284, 4400, 16263); + return true; + case 3574: + record = new EpsgProjectedCrsRecord(10286, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 31N (N-zE)", 10284, 4500, 5647); + return true; + case 3575: + record = new EpsgProjectedCrsRecord(10287, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 31N (zE-N)", 10284, 4400, 5647); + return true; + case 3576: + record = new EpsgProjectedCrsRecord(10288, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 32N (N-zE)", 10284, 4500, 4648); + return true; + case 3577: + record = new EpsgProjectedCrsRecord(10289, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 32N (zE-N)", 10284, 4400, 4648); + return true; + case 3578: + record = new EpsgProjectedCrsRecord(10290, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 33N (N-zE)", 10284, 4500, 5648); + return true; + case 3579: + record = new EpsgProjectedCrsRecord(10291, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 33N (zE-N)", 10284, 4400, 5648); + return true; + case 3580: + record = new EpsgProjectedCrsRecord(10306, "ETRS89-LVA [LKS-2020] / Latvia TM", 10305, 4530, 19990); + return true; + case 3581: + record = new EpsgProjectedCrsRecord(10314, "RGNC15 / Lambert New Caledonia 2015", 10310, 4400, 10313); + return true; + case 3582: + record = new EpsgProjectedCrsRecord(10315, "RGNC15 / UTM zone 57S", 10310, 4400, 16157); + return true; + case 3583: + record = new EpsgProjectedCrsRecord(10316, "RGNC15 / UTM zone 58S", 10310, 4400, 16158); + return true; + case 3584: + record = new EpsgProjectedCrsRecord(10317, "RGNC15 / UTM zone 59S", 10310, 4400, 16159); + return true; + case 3585: + record = new EpsgProjectedCrsRecord(10329, "ETRS89-BIH [BH_ETRS89] / TM", 10328, 4500, 10325); + return true; + case 3586: + record = new EpsgProjectedCrsRecord(10448, "GDA94 / ALB94", 4283, 4400, 10424); + return true; + case 3587: + record = new EpsgProjectedCrsRecord(10449, "GDA94 / BIO94", 4283, 4400, 10425); + return true; + case 3588: + record = new EpsgProjectedCrsRecord(10450, "GDA94 / BRO94", 4283, 4400, 10426); + return true; + case 3589: + record = new EpsgProjectedCrsRecord(10451, "GDA94 / BCG94", 4283, 4400, 10427); + return true; + case 3590: + record = new EpsgProjectedCrsRecord(10452, "GDA94 / CARN94", 4283, 4400, 10428); + return true; + case 3591: + record = new EpsgProjectedCrsRecord(10453, "GDA94 / COL94", 4283, 4400, 10429); + return true; + case 3592: + record = new EpsgProjectedCrsRecord(10454, "GDA94 / ESP94", 4283, 4400, 10430); + return true; + case 3593: + record = new EpsgProjectedCrsRecord(10455, "GDA94 / EXM94", 4283, 4400, 10437); + return true; + case 3594: + record = new EpsgProjectedCrsRecord(10456, "GDA94 / GCG94", 4283, 4400, 10438); + return true; + case 3595: + record = new EpsgProjectedCrsRecord(10457, "GDA94 / GOLD94", 4283, 4400, 10439); + return true; + case 3596: + record = new EpsgProjectedCrsRecord(10458, "GDA94 / JCG94", 4283, 4400, 10440); + return true; + case 3597: + record = new EpsgProjectedCrsRecord(10459, "GDA94 / KALB94", 4283, 4400, 10441); + return true; + case 3598: + record = new EpsgProjectedCrsRecord(10460, "GDA94 / KAR94", 4283, 4400, 10442); + return true; + case 3599: + record = new EpsgProjectedCrsRecord(10461, "GDA94 / KUN94", 4283, 4400, 10443); + return true; + case 3600: + record = new EpsgProjectedCrsRecord(10462, "GDA94 / LCG94", 4283, 4400, 10444); + return true; + case 3601: + record = new EpsgProjectedCrsRecord(10463, "GDA94 / MRCG94", 4283, 4400, 10445); + return true; + case 3602: + record = new EpsgProjectedCrsRecord(10464, "GDA94 / PCG94", 4283, 4400, 10446); + return true; + case 3603: + record = new EpsgProjectedCrsRecord(10465, "GDA94 / PHG94", 4283, 4400, 10447); + return true; + case 3604: + record = new EpsgProjectedCrsRecord(10471, "COV23 Grid", 10468, 4400, 10470); + return true; + case 3605: + record = new EpsgProjectedCrsRecord(10477, "BBT2000 / BBT-TM", 10475, 4400, 10476); + return true; + case 3606: + record = new EpsgProjectedCrsRecord(10481, "NAD83 / TWDB GM", 4269, 4497, 10479); + return true; + case 3607: + record = new EpsgProjectedCrsRecord(10516, "NAD83(2011) / Adjusted Jackson (ftUS)", 6318, 4497, 10515); + return true; + case 3608: + record = new EpsgProjectedCrsRecord(10592, "WGS 84 / GLANCE Africa", 4326, 4400, 10591); + return true; + case 3609: + record = new EpsgProjectedCrsRecord(10594, "WGS 84 / GLANCE Asia", 4326, 4400, 10593); + return true; + case 3610: + record = new EpsgProjectedCrsRecord(10596, "WGS 84 / GLANCE Europe", 4326, 4400, 10595); + return true; + case 3611: + record = new EpsgProjectedCrsRecord(10598, "WGS 84 / GLANCE North America", 4326, 4400, 10597); + return true; + case 3612: + record = new EpsgProjectedCrsRecord(10601, "WGS 84 / GLANCE Oceania", 4326, 4400, 10599); + return true; + case 3613: + record = new EpsgProjectedCrsRecord(10603, "WGS 84 / GLANCE South America", 4326, 4400, 10602); + return true; + case 3614: + record = new EpsgProjectedCrsRecord(10622, "NAD83(2011) / San Francisco SFO-B18 (ftUS)", 6318, 4497, 10621); + return true; + case 3615: + record = new EpsgProjectedCrsRecord(10626, "ECML14 Grid", 10623, 4400, 10625); + return true; + case 3616: + record = new EpsgProjectedCrsRecord(10632, "WC05 Grid", 10628, 4400, 10631); + return true; + case 3617: + record = new EpsgProjectedCrsRecord(10641, "Saba DPnet", 10636, 1054, 10640); + return true; + case 3618: + record = new EpsgProjectedCrsRecord(10665, "SIRGAS 2000 / Porto Alegre TM", 4674, 4400, 10664); + return true; + case 3619: + record = new EpsgProjectedCrsRecord(10674, "RGM23 / UTM zone 38S", 10671, 4400, 16138); + return true; + case 3620: + record = new EpsgProjectedCrsRecord(10699, "ETRS89-FIN [EUREF-FIN] / UTM zone 34N", 10690, 4400, 16034); + return true; + case 3621: + record = new EpsgProjectedCrsRecord(10702, "ETRS89-FIN [EUREF-FIN] / UTM zone 36N", 10690, 4400, 16036); + return true; + case 3622: + record = new EpsgProjectedCrsRecord(10726, "UZGD2024 / UzREF24 zone 40", 10725, 4400, 10719); + return true; + case 3623: + record = new EpsgProjectedCrsRecord(10727, "UZGD2024 / UzREF24 zone 41", 10725, 4400, 10720); + return true; + case 3624: + record = new EpsgProjectedCrsRecord(10728, "UZGD2024 / UzREF24 zone 42", 10725, 4400, 10721); + return true; + case 3625: + record = new EpsgProjectedCrsRecord(10729, "UZGD2024 / UzREF24 zone 43", 10725, 4400, 10722); + return true; + case 3626: + record = new EpsgProjectedCrsRecord(10731, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 31N", 10284, 4400, 16031); + return true; + case 3627: + record = new EpsgProjectedCrsRecord(10732, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 32N", 10284, 4400, 16032); + return true; + case 3628: + record = new EpsgProjectedCrsRecord(10733, "ETRS89-DEU [ETRS89/DREF91/2016] / UTM zone 33N", 10284, 4400, 16033); + return true; + case 3629: + record = new EpsgProjectedCrsRecord(10744, "Sint Eustatius DPnet short", 10736, 1054, 10743); + return true; + case 3630: + record = new EpsgProjectedCrsRecord(10745, "Sint Eustatius DPnet long", 10736, 1054, 16020); + return true; + case 3631: + record = new EpsgProjectedCrsRecord(10759, "Bonaire DPnet", 10758, 1054, 10757); + return true; + case 3632: + record = new EpsgProjectedCrsRecord(10773, "SIRGAS 2000 / Ribeirao Preto Local TM", 4674, 4400, 10772); + return true; + case 3633: + record = new EpsgProjectedCrsRecord(10792, "UGRF / UTM zone 35N", 10791, 4400, 16035); + return true; + case 3634: + record = new EpsgProjectedCrsRecord(10793, "UGRF / UTM zone 36N", 10791, 4400, 16036); + return true; + case 3635: + record = new EpsgProjectedCrsRecord(10794, "UGRF / UTM zone 36S", 10791, 4400, 16136); + return true; + case 3636: + record = new EpsgProjectedCrsRecord(10795, "UGRF / UTM zone 35S", 10791, 4400, 16135); + return true; + case 3637: + record = new EpsgProjectedCrsRecord(10801, "LibRef21 / UTM zone 28N", 10800, 4400, 16028); + return true; + case 3638: + record = new EpsgProjectedCrsRecord(10802, "LibRef21 / UTM zone 29N", 10800, 4400, 16029); + return true; + case 3639: + record = new EpsgProjectedCrsRecord(10820, "WGS 84 / Agriculture Canada Albers", 4326, 4400, 10819); + return true; + case 3640: + record = new EpsgProjectedCrsRecord(10833, "Georgia Geodetic Datum / Lambert", 10831, 4500, 10832); + return true; + case 3641: + record = new EpsgProjectedCrsRecord(10836, "Georgia Geodetic Datum / UTM zone 37N (N-E)", 10831, 4500, 16037); + return true; + case 3642: + record = new EpsgProjectedCrsRecord(10837, "Georgia Geodetic Datum / UTM zone 38N (N-E)", 10831, 4500, 16038); + return true; + case 3643: + record = new EpsgProjectedCrsRecord(10851, "EWR3 Grid", 10849, 4400, 9765); + return true; + case 3644: + record = new EpsgProjectedCrsRecord(10857, "SIRGAS 2000 / Brazil Albers", 4674, 4400, 10856); + return true; + case 3645: + record = new EpsgProjectedCrsRecord(10863, "WSPG Grid", 10860, 4400, 10862); + return true; + case 3646: + record = new EpsgProjectedCrsRecord(10899, "Asse 2025 / 3-degree Gauss-Kruger zone 4 (E-N)", 10898, 4400, 16264); + return true; + case 3647: + record = new EpsgProjectedCrsRecord(10911, "CSRN2025 (NAD83 2011) / California zone 1 (ftUS)", 10910, 4497, 15307); + return true; + case 3648: + record = new EpsgProjectedCrsRecord(10912, "CSRN2025 (NAD83 2011) / California zone 2 (ftUS)", 10910, 4497, 15308); + return true; + case 3649: + record = new EpsgProjectedCrsRecord(10913, "CSRN2025 (NAD83 2011) / California zone 3 (ftUS)", 10910, 4497, 15309); + return true; + case 3650: + record = new EpsgProjectedCrsRecord(10914, "CSRN2025 (NAD83 2011) / California zone 4 (ftUS)", 10910, 4497, 15310); + return true; + case 3651: + record = new EpsgProjectedCrsRecord(10915, "CSRN2025 (NAD83 2011) / California zone 5 (ftUS)", 10910, 4497, 15311); + return true; + case 3652: + record = new EpsgProjectedCrsRecord(10916, "CSRN2025 (NAD83 2011) / California zone 6 (ftUS)", 10910, 4497, 15312); + return true; + case 3653: + record = new EpsgProjectedCrsRecord(10917, "CSRN2025 (NAD83 2011) / California Albers", 10910, 4499, 10420); + return true; + case 3654: + record = new EpsgProjectedCrsRecord(10921, "CSRN2025 (NAD83 2011) / California zone 1", 10910, 4499, 10431); + return true; + case 3655: + record = new EpsgProjectedCrsRecord(10922, "CSRN2025 (NAD83 2011) / California zone 2", 10910, 4499, 10432); + return true; + case 3656: + record = new EpsgProjectedCrsRecord(10923, "CSRN2025 (NAD83 2011) / California zone 3", 10910, 4499, 10433); + return true; + case 3657: + record = new EpsgProjectedCrsRecord(10924, "CSRN2025 (NAD83 2011) / California zone 4", 10910, 4499, 10434); + return true; + case 3658: + record = new EpsgProjectedCrsRecord(10925, "CSRN2025 (NAD83 2011) / California zone 5", 10910, 4499, 10435); + return true; + case 3659: + record = new EpsgProjectedCrsRecord(10926, "CSRN2025 (NAD83 2011) / California zone 6", 10910, 4499, 10436); + return true; + case 3660: + record = new EpsgProjectedCrsRecord(10942, "QazTRF-23 / Gauss-Kruger zone 8", 10941, 4400, 16208); + return true; + case 3661: + record = new EpsgProjectedCrsRecord(10943, "QazTRF-23 / Gauss-Kruger zone 9", 10941, 4400, 16209); + return true; + case 3662: + record = new EpsgProjectedCrsRecord(10944, "QazTRF-23 / Gauss-Kruger zone 10", 10941, 4400, 16210); + return true; + case 3663: + record = new EpsgProjectedCrsRecord(10945, "QazTRF-23 / Gauss-Kruger zone 11", 10941, 4400, 16211); + return true; + case 3664: + record = new EpsgProjectedCrsRecord(10946, "QazTRF-23 / Gauss-Kruger zone 12", 10941, 4400, 16212); + return true; + case 3665: + record = new EpsgProjectedCrsRecord(10947, "QazTRF-23 / Gauss-Kruger zone 13", 10941, 4400, 16213); + return true; + case 3666: + record = new EpsgProjectedCrsRecord(10948, "QazTRF-23 / Gauss-Kruger zone 14", 10941, 4400, 16214); + return true; + case 3667: + record = new EpsgProjectedCrsRecord(10949, "QazTRF-23 / Gauss-Kruger zone 15", 10941, 4400, 16215); + return true; + case 3668: + record = new EpsgProjectedCrsRecord(10979, "NATRF2022 / Gulf", 10968, 4500, 10970); + return true; + case 3669: + record = new EpsgProjectedCrsRecord(10980, "NATRF2022 / Gulf (ft)", 10968, 1029, 10971); + return true; + case 3670: + record = new EpsgProjectedCrsRecord(10981, "NATRF2022 / Gulf Louisiana Shelf (ft)", 10968, 4495, 10972); + return true; + case 3671: + record = new EpsgProjectedCrsRecord(10982, "NATRF2022 / Gulf Texas Shelf North (ft)", 10968, 4495, 10973); + return true; + case 3672: + record = new EpsgProjectedCrsRecord(10983, "NATRF2022 / Gulf Texas Shelf South (ft)", 10968, 4495, 10974); + return true; + case 3673: + record = new EpsgProjectedCrsRecord(10984, "NATRF2022 / Gulf West (ft)", 10968, 4495, 10975); + return true; + case 3674: + record = new EpsgProjectedCrsRecord(10985, "NATRF2022 / Gulf West Central (ft)", 10968, 4495, 10976); + return true; + case 3675: + record = new EpsgProjectedCrsRecord(10986, "NATRF2022 / Gulf East Central (ft)", 10968, 4495, 10977); + return true; + case 3676: + record = new EpsgProjectedCrsRecord(10987, "NATRF2022 / Gulf East (ft)", 10968, 4495, 10978); + return true; + case 3677: + record = new EpsgProjectedCrsRecord(10995, "Xrail84 / London Survey Grid TM", 10993, 4400, 10994); + return true; + case 3678: + record = new EpsgProjectedCrsRecord(11012, "ETRS89-NOR [EUREF89] / UTM zone 30N (N-E)", 10875, 4500, 16030); + return true; + case 3679: + record = new EpsgProjectedCrsRecord(11013, "ETRS89-NOR [EUREF89] / UTM zone 31N (N-E)", 10875, 4500, 16031); + return true; + case 3680: + record = new EpsgProjectedCrsRecord(11014, "ETRS89-NOR [EUREF89] / UTM zone 32N (N-E)", 10875, 4500, 16032); + return true; + case 3681: + record = new EpsgProjectedCrsRecord(11015, "ETRS89-NOR [EUREF89] / UTM zone 33N (N-E)", 10875, 4500, 16033); + return true; + case 3682: + record = new EpsgProjectedCrsRecord(11016, "ETRS89-NOR [EUREF89] / UTM zone 34N (N-E)", 10875, 4500, 16034); + return true; + case 3683: + record = new EpsgProjectedCrsRecord(11017, "ETRS89-NOR [EUREF89] / UTM zone 35N (N-E)", 10875, 4500, 16035); + return true; + case 3684: + record = new EpsgProjectedCrsRecord(11018, "ETRS89-NOR [EUREF89] / UTM zone 36N (N-E)", 10875, 4500, 16036); + return true; + case 3685: + record = new EpsgProjectedCrsRecord(11019, "ETRS89-NOR [EUREF89] / UTM zone 37N (N-E)", 10875, 4500, 16037); + return true; + case 3686: + record = new EpsgProjectedCrsRecord(11020, "ETRS89-NOR [EUREF89] / UTM zone 30N", 10875, 4400, 16030); + return true; + case 3687: + record = new EpsgProjectedCrsRecord(11021, "ETRS89-NOR [EUREF89] / UTM zone 31N", 10875, 4400, 16031); + return true; + case 3688: + record = new EpsgProjectedCrsRecord(11022, "ETRS89-NOR [EUREF89] / UTM zone 32N", 10875, 4400, 16032); + return true; + case 3689: + record = new EpsgProjectedCrsRecord(11023, "ETRS89-NOR [EUREF89] / UTM zone 33N", 10875, 4400, 16033); + return true; + case 3690: + record = new EpsgProjectedCrsRecord(11024, "ETRS89-NOR [EUREF89] / UTM zone 34N", 10875, 4400, 16034); + return true; + case 3691: + record = new EpsgProjectedCrsRecord(11025, "ETRS89-NOR [EUREF89] / UTM zone 35N", 10875, 4400, 16035); + return true; + case 3692: + record = new EpsgProjectedCrsRecord(11026, "ETRS89-NOR [EUREF89] / UTM zone 36N", 10875, 4400, 16036); + return true; + case 3693: + record = new EpsgProjectedCrsRecord(11027, "ETRS89-NOR [EUREF89] / UTM zone 37N", 10875, 4400, 16037); + return true; + case 3694: + record = new EpsgProjectedCrsRecord(11114, "MAGNA-SIRGAS 2018 / Colombia Far West zone", 20046, 4500, 18065); + return true; + case 3695: + record = new EpsgProjectedCrsRecord(11115, "MAGNA-SIRGAS 2018 / Colombia West zone", 20046, 4500, 18066); + return true; + case 3696: + record = new EpsgProjectedCrsRecord(11116, "MAGNA-SIRGAS 2018 / Colombia Bogota zone", 20046, 4500, 18067); + return true; + case 3697: + record = new EpsgProjectedCrsRecord(11117, "MAGNA-SIRGAS 2018 / Colombia East Central zone", 20046, 4500, 18068); + return true; + case 3698: + record = new EpsgProjectedCrsRecord(11118, "MAGNA-SIRGAS 2018 / Colombia East zone", 20046, 4500, 18069); + return true; + case 3699: + record = new EpsgProjectedCrsRecord(11141, "ETRS89-ESP [REGENTE] / UTM zone 28N", 11134, 4400, 16028); + return true; + case 3700: + record = new EpsgProjectedCrsRecord(11142, "ETRS89-ESP [REGENTE] / UTM zone 28N (N-E)", 11134, 4500, 16028); + return true; + case 3701: + record = new EpsgProjectedCrsRecord(11143, "ETRS89-ESP [REGENTE] / UTM zone 29N", 11134, 4400, 16029); + return true; + case 3702: + record = new EpsgProjectedCrsRecord(11144, "ETRS89-ESP [REGENTE] / UTM zone 29N (N-E)", 11134, 4500, 16029); + return true; + case 3703: + record = new EpsgProjectedCrsRecord(11145, "ETRS89-ESP [REGENTE] / UTM zone 30N", 11134, 4400, 16030); + return true; + case 3704: + record = new EpsgProjectedCrsRecord(11146, "ETRS89-ESP [REGENTE] / UTM zone 30N (N-E)", 11134, 4500, 16030); + return true; + case 3705: + record = new EpsgProjectedCrsRecord(11147, "ETRS89-ESP [REGENTE] / UTM zone 31N", 11134, 4400, 16031); + return true; + case 3706: + record = new EpsgProjectedCrsRecord(11148, "ETRS89-ESP [REGENTE] / UTM zone 31N (N-E)", 11134, 4500, 16031); + return true; + case 3707: + record = new EpsgProjectedCrsRecord(11219, "ETRS89-BEL [BEREF2011] / Belgian Lambert 2008", 11215, 4499, 3811); + return true; + case 3708: + record = new EpsgProjectedCrsRecord(11266, "SRGI2013 epoch 2021.0 / UTM zone 46N", 11033, 4400, 16046); + return true; + case 3709: + record = new EpsgProjectedCrsRecord(11267, "SRGI2013 epoch 2021.0 / UTM zone 47N", 11033, 4400, 16047); + return true; + case 3710: + record = new EpsgProjectedCrsRecord(11268, "SRGI2013 epoch 2021.0 / UTM zone 48N", 11033, 4400, 16048); + return true; + case 3711: + record = new EpsgProjectedCrsRecord(11269, "SRGI2013 epoch 2021.0 / UTM zone 49N", 11033, 4400, 16049); + return true; + case 3712: + record = new EpsgProjectedCrsRecord(11270, "SRGI2013 epoch 2021.0 / UTM zone 50N", 11033, 4400, 16050); + return true; + case 3713: + record = new EpsgProjectedCrsRecord(11271, "SRGI2013 epoch 2021.0 / UTM zone 51N", 11033, 4400, 16051); + return true; + case 3714: + record = new EpsgProjectedCrsRecord(11272, "SRGI2013 epoch 2021.0 / UTM zone 52N", 11033, 4400, 16052); + return true; + case 3715: + record = new EpsgProjectedCrsRecord(11277, "SRGI2013 epoch 2021.0 / UTM zone 47S", 11033, 4400, 16147); + return true; + case 3716: + record = new EpsgProjectedCrsRecord(11278, "SRGI2013 epoch 2021.0 / UTM zone 48S", 11033, 4400, 16148); + return true; + case 3717: + record = new EpsgProjectedCrsRecord(11279, "SRGI2013 epoch 2021.0 / UTM zone 49S", 11033, 4400, 16149); + return true; + case 3718: + record = new EpsgProjectedCrsRecord(11280, "SRGI2013 epoch 2021.0 / UTM zone 50S", 11033, 4400, 16150); + return true; + case 3719: + record = new EpsgProjectedCrsRecord(11281, "SRGI2013 epoch 2021.0 / UTM zone 51S", 11033, 4400, 16151); + return true; + case 3720: + record = new EpsgProjectedCrsRecord(11282, "SRGI2013 epoch 2021.0 / UTM zone 52S", 11033, 4400, 16152); + return true; + case 3721: + record = new EpsgProjectedCrsRecord(11283, "SRGI2013 epoch 2021.0 / UTM zone 53S", 11033, 4400, 16153); + return true; + case 3722: + record = new EpsgProjectedCrsRecord(11284, "SRGI2013 epoch 2021.0 / UTM zone 54S", 11033, 4400, 16154); + return true; + case 3723: + record = new EpsgProjectedCrsRecord(11296, "Hartebeesthoek94 / Inverted Lo17", 4148, 4498, 11287); + return true; + case 3724: + record = new EpsgProjectedCrsRecord(11297, "Hartebeesthoek94 / Inverted Lo19", 4148, 4498, 11288); + return true; + case 3725: + record = new EpsgProjectedCrsRecord(11298, "Hartebeesthoek94 / Inverted Lo21", 4148, 4498, 11289); + return true; + case 3726: + record = new EpsgProjectedCrsRecord(11299, "Hartebeesthoek94 / Inverted Lo23", 4148, 4498, 11290); + return true; + case 3727: + record = new EpsgProjectedCrsRecord(11300, "Hartebeesthoek94 / Inverted Lo25", 4148, 4498, 11291); + return true; + case 3728: + record = new EpsgProjectedCrsRecord(11303, "Hartebeesthoek94 / Inverted Lo27", 4148, 4498, 11292); + return true; + case 3729: + record = new EpsgProjectedCrsRecord(11304, "Hartebeesthoek94 / Inverted Lo29", 4148, 4498, 11293); + return true; + case 3730: + record = new EpsgProjectedCrsRecord(11305, "Hartebeesthoek94 / Inverted Lo31", 4148, 4498, 11294); + return true; + case 3731: + record = new EpsgProjectedCrsRecord(11306, "Hartebeesthoek94 / Inverted Lo33", 4148, 4498, 11295); + return true; + case 3732: + record = new EpsgProjectedCrsRecord(11341, "DrukRef23 / BNG2023", 11226, 4400, 11340); + return true; + case 3733: + record = new EpsgProjectedCrsRecord(11360, "NAD83(2011) / RMTCRS Big Timber 83 (m)", 6318, 4499, 11342); + return true; + case 3734: + record = new EpsgProjectedCrsRecord(11361, "NAD83(2011) / RMTCRS Big Timber 83 (ft)", 6318, 4495, 11343); + return true; + case 3735: + record = new EpsgProjectedCrsRecord(11362, "NAD83(2011) / RMTCRS Butte 83 (m)", 6318, 4499, 11344); + return true; + case 3736: + record = new EpsgProjectedCrsRecord(11363, "NAD83(2011) / RMTCRS Butte 83 (ft)", 6318, 4495, 11345); + return true; + case 3737: + record = new EpsgProjectedCrsRecord(11364, "NAD83(2011) / RMTCRS Canyon Ferry 83 (m)", 6318, 4499, 11346); + return true; + case 3738: + record = new EpsgProjectedCrsRecord(11365, "NAD83(2011) / RMTCRS Canyon Ferry 83 (ft)", 6318, 4495, 11347); + return true; + case 3739: + record = new EpsgProjectedCrsRecord(11366, "NAD83(2011) / RMTCRS Flathead 83 (m)", 6318, 4499, 11348); + return true; + case 3740: + record = new EpsgProjectedCrsRecord(11367, "NAD83(2011) / RMTCRS Flathead 83 (ft)", 6318, 4495, 11349); + return true; + case 3741: + record = new EpsgProjectedCrsRecord(11368, "NAD83(2011) / RMTCRS Interstate 83 (m)", 6318, 4499, 11350); + return true; + case 3742: + record = new EpsgProjectedCrsRecord(11369, "NAD83(2011) / RMTCRS Interstate 83 (ft)", 6318, 4495, 11351); + return true; + case 3743: + record = new EpsgProjectedCrsRecord(11370, "NAD83(2011) / RMTCRS Mission 83 (m)", 6318, 4499, 11352); + return true; + case 3744: + record = new EpsgProjectedCrsRecord(11371, "NAD83(2011) / RMTCRS Mission 83 (ft)", 6318, 4495, 11353); + return true; + case 3745: + record = new EpsgProjectedCrsRecord(11372, "NAD83(2011) / RMTCRS Missoula 83 (m)", 6318, 4499, 11354); + return true; + case 3746: + record = new EpsgProjectedCrsRecord(11373, "NAD83(2011) / RMTCRS Missoula 83 (ft)", 6318, 4495, 11355); + return true; + case 3747: + record = new EpsgProjectedCrsRecord(11374, "NAD83(2011) / RMTCRS NECI 83 (m)", 6318, 4499, 11356); + return true; + case 3748: + record = new EpsgProjectedCrsRecord(11375, "NAD83(2011) / RMTCRS NECI 83 (ft)", 6318, 4495, 11357); + return true; + case 3749: + record = new EpsgProjectedCrsRecord(11376, "NAD83(2011) / RMTCRS Phillips 83 (m)", 6318, 4499, 11358); + return true; + case 3750: + record = new EpsgProjectedCrsRecord(11377, "NAD83(2011) / RMTCRS Phillips 83 (ft)", 6318, 4495, 11359); + return true; + case 3751: + record = new EpsgProjectedCrsRecord(11390, "OSGB36 / Heathrow Airport GIS Grid 2026", 4277, 4400, 11389); + return true; + case 3752: + record = new EpsgProjectedCrsRecord(20002, "MWC18 Grid", 20033, 4400, 10127); + return true; + case 3753: + record = new EpsgProjectedCrsRecord(20004, "Pulkovo 1995 / Gauss-Kruger zone 4", 4200, 4530, 16204); + return true; + case 3754: + record = new EpsgProjectedCrsRecord(20005, "Pulkovo 1995 / Gauss-Kruger zone 5", 4200, 4530, 16205); + return true; + case 3755: + record = new EpsgProjectedCrsRecord(20006, "Pulkovo 1995 / Gauss-Kruger zone 6", 4200, 4530, 16206); + return true; + case 3756: + record = new EpsgProjectedCrsRecord(20007, "Pulkovo 1995 / Gauss-Kruger zone 7", 4200, 4530, 16207); + return true; + case 3757: + record = new EpsgProjectedCrsRecord(20008, "Pulkovo 1995 / Gauss-Kruger zone 8", 4200, 4530, 16208); + return true; + case 3758: + record = new EpsgProjectedCrsRecord(20009, "Pulkovo 1995 / Gauss-Kruger zone 9", 4200, 4530, 16209); + return true; + case 3759: + record = new EpsgProjectedCrsRecord(20010, "Pulkovo 1995 / Gauss-Kruger zone 10", 4200, 4530, 16210); + return true; + case 3760: + record = new EpsgProjectedCrsRecord(20011, "Pulkovo 1995 / Gauss-Kruger zone 11", 4200, 4530, 16211); + return true; + case 3761: + record = new EpsgProjectedCrsRecord(20012, "Pulkovo 1995 / Gauss-Kruger zone 12", 4200, 4530, 16212); + return true; + case 3762: + record = new EpsgProjectedCrsRecord(20013, "Pulkovo 1995 / Gauss-Kruger zone 13", 4200, 4530, 16213); + return true; + case 3763: + record = new EpsgProjectedCrsRecord(20014, "Pulkovo 1995 / Gauss-Kruger zone 14", 4200, 4530, 16214); + return true; + case 3764: + record = new EpsgProjectedCrsRecord(20015, "Pulkovo 1995 / Gauss-Kruger zone 15", 4200, 4530, 16215); + return true; + case 3765: + record = new EpsgProjectedCrsRecord(20016, "Pulkovo 1995 / Gauss-Kruger zone 16", 4200, 4530, 16216); + return true; + case 3766: + record = new EpsgProjectedCrsRecord(20017, "Pulkovo 1995 / Gauss-Kruger zone 17", 4200, 4530, 16217); + return true; + case 3767: + record = new EpsgProjectedCrsRecord(20018, "Pulkovo 1995 / Gauss-Kruger zone 18", 4200, 4530, 16218); + return true; + case 3768: + record = new EpsgProjectedCrsRecord(20019, "Pulkovo 1995 / Gauss-Kruger zone 19", 4200, 4530, 16219); + return true; + case 3769: + record = new EpsgProjectedCrsRecord(20020, "Pulkovo 1995 / Gauss-Kruger zone 20", 4200, 4530, 16220); + return true; + case 3770: + record = new EpsgProjectedCrsRecord(20021, "Pulkovo 1995 / Gauss-Kruger zone 21", 4200, 4530, 16221); + return true; + case 3771: + record = new EpsgProjectedCrsRecord(20022, "Pulkovo 1995 / Gauss-Kruger zone 22", 4200, 4530, 16222); + return true; + case 3772: + record = new EpsgProjectedCrsRecord(20023, "Pulkovo 1995 / Gauss-Kruger zone 23", 4200, 4530, 16223); + return true; + case 3773: + record = new EpsgProjectedCrsRecord(20024, "Pulkovo 1995 / Gauss-Kruger zone 24", 4200, 4530, 16224); + return true; + case 3774: + record = new EpsgProjectedCrsRecord(20025, "Pulkovo 1995 / Gauss-Kruger zone 25", 4200, 4530, 16225); + return true; + case 3775: + record = new EpsgProjectedCrsRecord(20026, "Pulkovo 1995 / Gauss-Kruger zone 26", 4200, 4530, 16226); + return true; + case 3776: + record = new EpsgProjectedCrsRecord(20027, "Pulkovo 1995 / Gauss-Kruger zone 27", 4200, 4530, 16227); + return true; + case 3777: + record = new EpsgProjectedCrsRecord(20028, "Pulkovo 1995 / Gauss-Kruger zone 28", 4200, 4530, 16228); + return true; + case 3778: + record = new EpsgProjectedCrsRecord(20029, "Pulkovo 1995 / Gauss-Kruger zone 29", 4200, 4530, 16229); + return true; + case 3779: + record = new EpsgProjectedCrsRecord(20030, "Pulkovo 1995 / Gauss-Kruger zone 30", 4200, 4530, 16230); + return true; + case 3780: + record = new EpsgProjectedCrsRecord(20031, "Pulkovo 1995 / Gauss-Kruger zone 31", 4200, 4530, 16231); + return true; + case 3781: + record = new EpsgProjectedCrsRecord(20032, "Pulkovo 1995 / Gauss-Kruger zone 32", 4200, 4530, 16232); + return true; + case 3782: + record = new EpsgProjectedCrsRecord(20042, "SIRGAS-Chile 2021 / UTM zone 12S", 20041, 4400, 16112); + return true; + case 3783: + record = new EpsgProjectedCrsRecord(20047, "GDA2020 / BCSG2020", 7844, 4400, 10147); + return true; + case 3784: + record = new EpsgProjectedCrsRecord(20048, "SIRGAS-Chile 2021 / UTM zone 18S", 20041, 4400, 16118); + return true; + case 3785: + record = new EpsgProjectedCrsRecord(20049, "SIRGAS-Chile 2021 / UTM zone 19S", 20041, 4400, 16119); + return true; + case 3786: + record = new EpsgProjectedCrsRecord(20050, "NAD83(2011) / Amtrak NECCS21 (ft)", 6318, 4495, 10148); + return true; + case 3787: + record = new EpsgProjectedCrsRecord(20135, "Adindan / UTM zone 35N", 4201, 4400, 16035); + return true; + case 3788: + record = new EpsgProjectedCrsRecord(20136, "Adindan / UTM zone 36N", 4201, 4400, 16036); + return true; + case 3789: + record = new EpsgProjectedCrsRecord(20137, "Adindan / UTM zone 37N", 4201, 4400, 16037); + return true; + case 3790: + record = new EpsgProjectedCrsRecord(20138, "Adindan / UTM zone 38N", 4201, 4400, 16038); + return true; + case 3791: + record = new EpsgProjectedCrsRecord(20249, "AGD66 / AMG zone 49", 4202, 4400, 17449); + return true; + case 3792: + record = new EpsgProjectedCrsRecord(20250, "AGD66 / AMG zone 50", 4202, 4400, 17450); + return true; + case 3793: + record = new EpsgProjectedCrsRecord(20251, "AGD66 / AMG zone 51", 4202, 4400, 17451); + return true; + case 3794: + record = new EpsgProjectedCrsRecord(20252, "AGD66 / AMG zone 52", 4202, 4400, 17452); + return true; + case 3795: + record = new EpsgProjectedCrsRecord(20253, "AGD66 / AMG zone 53", 4202, 4400, 17453); + return true; + case 3796: + record = new EpsgProjectedCrsRecord(20254, "AGD66 / AMG zone 54", 4202, 4400, 17454); + return true; + case 3797: + record = new EpsgProjectedCrsRecord(20255, "AGD66 / AMG zone 55", 4202, 4400, 17455); + return true; + case 3798: + record = new EpsgProjectedCrsRecord(20256, "AGD66 / AMG zone 56", 4202, 4400, 17456); + return true; + case 3799: + record = new EpsgProjectedCrsRecord(20257, "AGD66 / AMG zone 57", 4202, 4400, 17457); + return true; + case 3800: + record = new EpsgProjectedCrsRecord(20258, "AGD66 / AMG zone 58", 4202, 4400, 17458); + return true; + case 3801: + record = new EpsgProjectedCrsRecord(20349, "AGD84 / AMG zone 49", 4203, 4400, 17449); + return true; + case 3802: + record = new EpsgProjectedCrsRecord(20350, "AGD84 / AMG zone 50", 4203, 4400, 17450); + return true; + case 3803: + record = new EpsgProjectedCrsRecord(20351, "AGD84 / AMG zone 51", 4203, 4400, 17451); + return true; + case 3804: + record = new EpsgProjectedCrsRecord(20352, "AGD84 / AMG zone 52", 4203, 4400, 17452); + return true; + case 3805: + record = new EpsgProjectedCrsRecord(20353, "AGD84 / AMG zone 53", 4203, 4400, 17453); + return true; + case 3806: + record = new EpsgProjectedCrsRecord(20354, "AGD84 / AMG zone 54", 4203, 4400, 17454); + return true; + case 3807: + record = new EpsgProjectedCrsRecord(20355, "AGD84 / AMG zone 55", 4203, 4400, 17455); + return true; + case 3808: + record = new EpsgProjectedCrsRecord(20356, "AGD84 / AMG zone 56", 4203, 4400, 17456); + return true; + case 3809: + record = new EpsgProjectedCrsRecord(20436, "Ain el Abd / UTM zone 36N", 4204, 4400, 16036); + return true; + case 3810: + record = new EpsgProjectedCrsRecord(20437, "Ain el Abd / UTM zone 37N", 4204, 4400, 16037); + return true; + case 3811: + record = new EpsgProjectedCrsRecord(20438, "Ain el Abd / UTM zone 38N", 4204, 4400, 16038); + return true; + case 3812: + record = new EpsgProjectedCrsRecord(20439, "Ain el Abd / UTM zone 39N", 4204, 4400, 16039); + return true; + case 3813: + record = new EpsgProjectedCrsRecord(20440, "Ain el Abd / UTM zone 40N", 4204, 4400, 16040); + return true; + case 3814: + record = new EpsgProjectedCrsRecord(20499, "Ain el Abd / Bahrain Grid", 4204, 4400, 19900); + return true; + case 3815: + record = new EpsgProjectedCrsRecord(20538, "Afgooye / UTM zone 38N", 4205, 4400, 16038); + return true; + case 3816: + record = new EpsgProjectedCrsRecord(20539, "Afgooye / UTM zone 39N", 4205, 4400, 16039); + return true; + case 3817: + record = new EpsgProjectedCrsRecord(20790, "Lisbon (Lisbon) / Portuguese National Grid", 4803, 4499, 19936); + return true; + case 3818: + record = new EpsgProjectedCrsRecord(20791, "Lisbon (Lisbon) / Portuguese Grid", 4803, 4499, 19969); + return true; + case 3819: + record = new EpsgProjectedCrsRecord(20822, "Aratu / UTM zone 22S", 4208, 4400, 16122); + return true; + case 3820: + record = new EpsgProjectedCrsRecord(20823, "Aratu / UTM zone 23S", 4208, 4400, 16123); + return true; + case 3821: + record = new EpsgProjectedCrsRecord(20824, "Aratu / UTM zone 24S", 4208, 4400, 16124); + return true; + case 3822: + record = new EpsgProjectedCrsRecord(20904, "GSK-2011 / Gauss-Kruger zone 4", 7683, 4530, 16204); + return true; + case 3823: + record = new EpsgProjectedCrsRecord(20905, "GSK-2011 / Gauss-Kruger zone 5", 7683, 4530, 16205); + return true; + case 3824: + record = new EpsgProjectedCrsRecord(20906, "GSK-2011 / Gauss-Kruger zone 6", 7683, 4530, 16206); + return true; + case 3825: + record = new EpsgProjectedCrsRecord(20907, "GSK-2011 / Gauss-Kruger zone 7", 7683, 4530, 16207); + return true; + case 3826: + record = new EpsgProjectedCrsRecord(20908, "GSK-2011 / Gauss-Kruger zone 8", 7683, 4530, 16208); + return true; + case 3827: + record = new EpsgProjectedCrsRecord(20909, "GSK-2011 / Gauss-Kruger zone 9", 7683, 4530, 16209); + return true; + case 3828: + record = new EpsgProjectedCrsRecord(20910, "GSK-2011 / Gauss-Kruger zone 10", 7683, 4530, 16210); + return true; + case 3829: + record = new EpsgProjectedCrsRecord(20911, "GSK-2011 / Gauss-Kruger zone 11", 7683, 4530, 16211); + return true; + case 3830: + record = new EpsgProjectedCrsRecord(20912, "GSK-2011 / Gauss-Kruger zone 12", 7683, 4530, 16212); + return true; + case 3831: + record = new EpsgProjectedCrsRecord(20913, "GSK-2011 / Gauss-Kruger zone 13", 7683, 4530, 16213); + return true; + case 3832: + record = new EpsgProjectedCrsRecord(20914, "GSK-2011 / Gauss-Kruger zone 14", 7683, 4530, 16214); + return true; + case 3833: + record = new EpsgProjectedCrsRecord(20915, "GSK-2011 / Gauss-Kruger zone 15", 7683, 4530, 16215); + return true; + case 3834: + record = new EpsgProjectedCrsRecord(20916, "GSK-2011 / Gauss-Kruger zone 16", 7683, 4530, 16216); + return true; + case 3835: + record = new EpsgProjectedCrsRecord(20917, "GSK-2011 / Gauss-Kruger zone 17", 7683, 4530, 16217); + return true; + case 3836: + record = new EpsgProjectedCrsRecord(20918, "GSK-2011 / Gauss-Kruger zone 18", 7683, 4530, 16218); + return true; + case 3837: + record = new EpsgProjectedCrsRecord(20919, "GSK-2011 / Gauss-Kruger zone 19", 7683, 4530, 16219); + return true; + case 3838: + record = new EpsgProjectedCrsRecord(20920, "GSK-2011 / Gauss-Kruger zone 20", 7683, 4530, 16220); + return true; + case 3839: + record = new EpsgProjectedCrsRecord(20921, "GSK-2011 / Gauss-Kruger zone 21", 7683, 4530, 16221); + return true; + case 3840: + record = new EpsgProjectedCrsRecord(20922, "GSK-2011 / Gauss-Kruger zone 22", 7683, 4530, 16222); + return true; + case 3841: + record = new EpsgProjectedCrsRecord(20923, "GSK-2011 / Gauss-Kruger zone 23", 7683, 4530, 16223); + return true; + case 3842: + record = new EpsgProjectedCrsRecord(20924, "GSK-2011 / Gauss-Kruger zone 24", 7683, 4530, 16224); + return true; + case 3843: + record = new EpsgProjectedCrsRecord(20925, "GSK-2011 / Gauss-Kruger zone 25", 7683, 4530, 16225); + return true; + case 3844: + record = new EpsgProjectedCrsRecord(20926, "GSK-2011 / Gauss-Kruger zone 26", 7683, 4530, 16226); + return true; + case 3845: + record = new EpsgProjectedCrsRecord(20927, "GSK-2011 / Gauss-Kruger zone 27", 7683, 4530, 16227); + return true; + case 3846: + record = new EpsgProjectedCrsRecord(20928, "GSK-2011 / Gauss-Kruger zone 28", 7683, 4530, 16228); + return true; + case 3847: + record = new EpsgProjectedCrsRecord(20929, "GSK-2011 / Gauss-Kruger zone 29", 7683, 4530, 16229); + return true; + case 3848: + record = new EpsgProjectedCrsRecord(20930, "GSK-2011 / Gauss-Kruger zone 30", 7683, 4530, 16230); + return true; + case 3849: + record = new EpsgProjectedCrsRecord(20931, "GSK-2011 / Gauss-Kruger zone 31", 7683, 4530, 16231); + return true; + case 3850: + record = new EpsgProjectedCrsRecord(20932, "GSK-2011 / Gauss-Kruger zone 32", 7683, 4530, 16232); + return true; + case 3851: + record = new EpsgProjectedCrsRecord(20934, "Arc 1950 / UTM zone 34S", 4209, 4400, 16134); + return true; + case 3852: + record = new EpsgProjectedCrsRecord(20935, "Arc 1950 / UTM zone 35S", 4209, 4400, 16135); + return true; + case 3853: + record = new EpsgProjectedCrsRecord(20936, "Arc 1950 / UTM zone 36S", 4209, 4400, 16136); + return true; + case 3854: + record = new EpsgProjectedCrsRecord(21004, "GSK-2011 / Gauss-Kruger CM 21E", 7683, 4530, 16304); + return true; + case 3855: + record = new EpsgProjectedCrsRecord(21005, "GSK-2011 / Gauss-Kruger CM 27E", 7683, 4530, 16305); + return true; + case 3856: + record = new EpsgProjectedCrsRecord(21006, "GSK-2011 / Gauss-Kruger CM 33E", 7683, 4530, 16306); + return true; + case 3857: + record = new EpsgProjectedCrsRecord(21007, "GSK-2011 / Gauss-Kruger CM 39E", 7683, 4530, 16307); + return true; + case 3858: + record = new EpsgProjectedCrsRecord(21008, "GSK-2011 / Gauss-Kruger CM 45E", 7683, 4530, 16308); + return true; + case 3859: + record = new EpsgProjectedCrsRecord(21009, "GSK-2011 / Gauss-Kruger CM 51E", 7683, 4530, 16309); + return true; + case 3860: + record = new EpsgProjectedCrsRecord(21010, "GSK-2011 / Gauss-Kruger CM 57E", 7683, 4530, 16310); + return true; + case 3861: + record = new EpsgProjectedCrsRecord(21011, "GSK-2011 / Gauss-Kruger CM 63E", 7683, 4530, 16311); + return true; + case 3862: + record = new EpsgProjectedCrsRecord(21012, "GSK-2011 / Gauss-Kruger CM 69E", 7683, 4530, 16312); + return true; + case 3863: + record = new EpsgProjectedCrsRecord(21013, "GSK-2011 / Gauss-Kruger CM 75E", 7683, 4530, 16313); + return true; + case 3864: + record = new EpsgProjectedCrsRecord(21014, "GSK-2011 / Gauss-Kruger CM 81E", 7683, 4530, 16314); + return true; + case 3865: + record = new EpsgProjectedCrsRecord(21015, "GSK-2011 / Gauss-Kruger CM 87E", 7683, 4530, 16315); + return true; + case 3866: + record = new EpsgProjectedCrsRecord(21016, "GSK-2011 / Gauss-Kruger CM 93E", 7683, 4530, 16316); + return true; + case 3867: + record = new EpsgProjectedCrsRecord(21017, "GSK-2011 / Gauss-Kruger CM 99E", 7683, 4530, 16317); + return true; + case 3868: + record = new EpsgProjectedCrsRecord(21018, "GSK-2011 / Gauss-Kruger CM 105E", 7683, 4530, 16318); + return true; + case 3869: + record = new EpsgProjectedCrsRecord(21019, "GSK-2011 / Gauss-Kruger CM 111E", 7683, 4530, 16319); + return true; + case 3870: + record = new EpsgProjectedCrsRecord(21020, "GSK-2011 / Gauss-Kruger CM 117E", 7683, 4530, 16320); + return true; + case 3871: + record = new EpsgProjectedCrsRecord(21021, "GSK-2011 / Gauss-Kruger CM 123E", 7683, 4530, 16321); + return true; + case 3872: + record = new EpsgProjectedCrsRecord(21022, "GSK-2011 / Gauss-Kruger CM 129E", 7683, 4530, 16322); + return true; + case 3873: + record = new EpsgProjectedCrsRecord(21023, "GSK-2011 / Gauss-Kruger CM 135E", 7683, 4530, 16323); + return true; + case 3874: + record = new EpsgProjectedCrsRecord(21024, "GSK-2011 / Gauss-Kruger CM 141E", 7683, 4530, 16324); + return true; + case 3875: + record = new EpsgProjectedCrsRecord(21025, "GSK-2011 / Gauss-Kruger CM 147E", 7683, 4530, 16325); + return true; + case 3876: + record = new EpsgProjectedCrsRecord(21026, "GSK-2011 / Gauss-Kruger CM 153E", 7683, 4530, 16326); + return true; + case 3877: + record = new EpsgProjectedCrsRecord(21027, "GSK-2011 / Gauss-Kruger CM 159E", 7683, 4530, 16327); + return true; + case 3878: + record = new EpsgProjectedCrsRecord(21028, "GSK-2011 / Gauss-Kruger CM 165E", 7683, 4530, 16328); + return true; + case 3879: + record = new EpsgProjectedCrsRecord(21029, "GSK-2011 / Gauss-Kruger CM 171E", 7683, 4530, 16329); + return true; + case 3880: + record = new EpsgProjectedCrsRecord(21030, "GSK-2011 / Gauss-Kruger CM 177E", 7683, 4530, 16330); + return true; + case 3881: + record = new EpsgProjectedCrsRecord(21031, "GSK-2011 / Gauss-Kruger CM 177W", 7683, 4530, 16331); + return true; + case 3882: + record = new EpsgProjectedCrsRecord(21032, "GSK-2011 / Gauss-Kruger CM 171W", 7683, 4530, 16332); + return true; + case 3883: + record = new EpsgProjectedCrsRecord(21035, "Arc 1960 / UTM zone 35S", 4210, 4400, 16135); + return true; + case 3884: + record = new EpsgProjectedCrsRecord(21036, "Arc 1960 / UTM zone 36S", 4210, 4400, 16136); + return true; + case 3885: + record = new EpsgProjectedCrsRecord(21037, "Arc 1960 / UTM zone 37S", 4210, 4400, 16137); + return true; + case 3886: + record = new EpsgProjectedCrsRecord(21095, "Arc 1960 / UTM zone 35N", 4210, 4400, 16035); + return true; + case 3887: + record = new EpsgProjectedCrsRecord(21096, "Arc 1960 / UTM zone 36N", 4210, 4400, 16036); + return true; + case 3888: + record = new EpsgProjectedCrsRecord(21097, "Arc 1960 / UTM zone 37N", 4210, 4400, 16037); + return true; + case 3889: + record = new EpsgProjectedCrsRecord(21148, "Batavia / UTM zone 48S", 4211, 4400, 16148); + return true; + case 3890: + record = new EpsgProjectedCrsRecord(21149, "Batavia / UTM zone 49S", 4211, 4400, 16149); + return true; + case 3891: + record = new EpsgProjectedCrsRecord(21150, "Batavia / UTM zone 50S", 4211, 4400, 16150); + return true; + case 3892: + record = new EpsgProjectedCrsRecord(21207, "GSK-2011 / GSK 3GK zone 7", 7683, 4530, 16907); + return true; + case 3893: + record = new EpsgProjectedCrsRecord(21208, "GSK-2011 / GSK 3GK zone 8", 7683, 4530, 16908); + return true; + case 3894: + record = new EpsgProjectedCrsRecord(21209, "GSK-2011 / GSK 3GK zone 9", 7683, 4530, 16909); + return true; + case 3895: + record = new EpsgProjectedCrsRecord(21210, "GSK-2011 / GSK 3GK zone 10", 7683, 4530, 16910); + return true; + case 3896: + record = new EpsgProjectedCrsRecord(21211, "GSK-2011 / GSK 3GK zone 11", 7683, 4530, 16911); + return true; + case 3897: + record = new EpsgProjectedCrsRecord(21212, "GSK-2011 / GSK 3GK zone 12", 7683, 4530, 16912); + return true; + case 3898: + record = new EpsgProjectedCrsRecord(21213, "GSK-2011 / GSK 3GK zone 13", 7683, 4530, 16913); + return true; + case 3899: + record = new EpsgProjectedCrsRecord(21214, "GSK-2011 / GSK 3GK zone 14", 7683, 4530, 16914); + return true; + case 3900: + record = new EpsgProjectedCrsRecord(21215, "GSK-2011 / GSK 3GK zone 15", 7683, 4530, 16915); + return true; + case 3901: + record = new EpsgProjectedCrsRecord(21216, "GSK-2011 / GSK 3GK zone 16", 7683, 4530, 16916); + return true; + case 3902: + record = new EpsgProjectedCrsRecord(21217, "GSK-2011 / GSK 3GK zone 17", 7683, 4530, 16917); + return true; + case 3903: + record = new EpsgProjectedCrsRecord(21218, "GSK-2011 / GSK 3GK zone 18", 7683, 4530, 16918); + return true; + case 3904: + record = new EpsgProjectedCrsRecord(21219, "GSK-2011 / GSK 3GK zone 19", 7683, 4530, 16919); + return true; + case 3905: + record = new EpsgProjectedCrsRecord(21220, "GSK-2011 / GSK 3GK zone 20", 7683, 4530, 16920); + return true; + case 3906: + record = new EpsgProjectedCrsRecord(21221, "GSK-2011 / GSK 3GK zone 21", 7683, 4530, 16921); + return true; + case 3907: + record = new EpsgProjectedCrsRecord(21222, "GSK-2011 / GSK 3GK zone 22", 7683, 4530, 16922); + return true; + case 3908: + record = new EpsgProjectedCrsRecord(21223, "GSK-2011 / GSK 3GK zone 23", 7683, 4530, 16923); + return true; + case 3909: + record = new EpsgProjectedCrsRecord(21224, "GSK-2011 / GSK 3GK zone 24", 7683, 4530, 16924); + return true; + case 3910: + record = new EpsgProjectedCrsRecord(21225, "GSK-2011 / GSK 3GK zone 25", 7683, 4530, 16925); + return true; + case 3911: + record = new EpsgProjectedCrsRecord(21226, "GSK-2011 / GSK 3GK zone 26", 7683, 4530, 16926); + return true; + case 3912: + record = new EpsgProjectedCrsRecord(21227, "GSK-2011 / GSK 3GK zone 27", 7683, 4530, 16927); + return true; + case 3913: + record = new EpsgProjectedCrsRecord(21228, "GSK-2011 / GSK 3GK zone 28", 7683, 4530, 16928); + return true; + case 3914: + record = new EpsgProjectedCrsRecord(21229, "GSK-2011 / GSK 3GK zone 29", 7683, 4530, 16929); + return true; + case 3915: + record = new EpsgProjectedCrsRecord(21230, "GSK-2011 / GSK 3GK zone 30", 7683, 4530, 16930); + return true; + case 3916: + record = new EpsgProjectedCrsRecord(21231, "GSK-2011 / GSK 3GK zone 31", 7683, 4530, 16931); + return true; + case 3917: + record = new EpsgProjectedCrsRecord(21232, "GSK-2011 / GSK 3GK zone 32", 7683, 4530, 16932); + return true; + case 3918: + record = new EpsgProjectedCrsRecord(21233, "GSK-2011 / GSK 3GK zone 33", 7683, 4530, 16933); + return true; + case 3919: + record = new EpsgProjectedCrsRecord(21234, "GSK-2011 / GSK 3GK zone 34", 7683, 4530, 16934); + return true; + case 3920: + record = new EpsgProjectedCrsRecord(21235, "GSK-2011 / GSK 3GK zone 35", 7683, 4530, 16935); + return true; + case 3921: + record = new EpsgProjectedCrsRecord(21236, "GSK-2011 / GSK 3GK zone 36", 7683, 4530, 16936); + return true; + case 3922: + record = new EpsgProjectedCrsRecord(21237, "GSK-2011 / GSK 3GK zone 37", 7683, 4530, 16937); + return true; + case 3923: + record = new EpsgProjectedCrsRecord(21238, "GSK-2011 / GSK 3GK zone 38", 7683, 4530, 16938); + return true; + case 3924: + record = new EpsgProjectedCrsRecord(21239, "GSK-2011 / GSK 3GK zone 39", 7683, 4530, 16939); + return true; + case 3925: + record = new EpsgProjectedCrsRecord(21240, "GSK-2011 / GSK 3GK zone 40", 7683, 4530, 16940); + return true; + case 3926: + record = new EpsgProjectedCrsRecord(21241, "GSK-2011 / GSK 3GK zone 41", 7683, 4530, 16941); + return true; + case 3927: + record = new EpsgProjectedCrsRecord(21242, "GSK-2011 / GSK 3GK zone 42", 7683, 4530, 16942); + return true; + case 3928: + record = new EpsgProjectedCrsRecord(21243, "GSK-2011 / GSK 3GK zone 43", 7683, 4530, 16943); + return true; + case 3929: + record = new EpsgProjectedCrsRecord(21244, "GSK-2011 / GSK 3GK zone 44", 7683, 4530, 16944); + return true; + case 3930: + record = new EpsgProjectedCrsRecord(21245, "GSK-2011 / GSK 3GK zone 45", 7683, 4530, 16945); + return true; + case 3931: + record = new EpsgProjectedCrsRecord(21246, "GSK-2011 / GSK 3GK zone 46", 7683, 4530, 16946); + return true; + case 3932: + record = new EpsgProjectedCrsRecord(21247, "GSK-2011 / GSK 3GK zone 47", 7683, 4530, 16947); + return true; + case 3933: + record = new EpsgProjectedCrsRecord(21248, "GSK-2011 / GSK 3GK zone 48", 7683, 4530, 16948); + return true; + case 3934: + record = new EpsgProjectedCrsRecord(21249, "GSK-2011 / GSK 3GK zone 49", 7683, 4530, 16949); + return true; + case 3935: + record = new EpsgProjectedCrsRecord(21250, "GSK-2011 / GSK 3GK zone 50", 7683, 4530, 16950); + return true; + case 3936: + record = new EpsgProjectedCrsRecord(21251, "GSK-2011 / GSK 3GK zone 51", 7683, 4530, 16951); + return true; + case 3937: + record = new EpsgProjectedCrsRecord(21252, "GSK-2011 / GSK 3GK zone 52", 7683, 4530, 16952); + return true; + case 3938: + record = new EpsgProjectedCrsRecord(21253, "GSK-2011 / GSK 3GK zone 53", 7683, 4530, 16953); + return true; + case 3939: + record = new EpsgProjectedCrsRecord(21254, "GSK-2011 / GSK 3GK zone 54", 7683, 4530, 16954); + return true; + case 3940: + record = new EpsgProjectedCrsRecord(21255, "GSK-2011 / GSK 3GK zone 55", 7683, 4530, 16955); + return true; + case 3941: + record = new EpsgProjectedCrsRecord(21256, "GSK-2011 / GSK 3GK zone 56", 7683, 4530, 16956); + return true; + case 3942: + record = new EpsgProjectedCrsRecord(21257, "GSK-2011 / GSK 3GK zone 57", 7683, 4530, 16957); + return true; + case 3943: + record = new EpsgProjectedCrsRecord(21258, "GSK-2011 / GSK 3GK zone 58", 7683, 4530, 16958); + return true; + case 3944: + record = new EpsgProjectedCrsRecord(21259, "GSK-2011 / GSK 3GK zone 59", 7683, 4530, 16959); + return true; + case 3945: + record = new EpsgProjectedCrsRecord(21260, "GSK-2011 / GSK 3GK zone 60", 7683, 4530, 16960); + return true; + case 3946: + record = new EpsgProjectedCrsRecord(21261, "GSK-2011 / GSK 3GK zone 61", 7683, 4530, 16961); + return true; + case 3947: + record = new EpsgProjectedCrsRecord(21262, "GSK-2011 / GSK 3GK zone 62", 7683, 4530, 16962); + return true; + case 3948: + record = new EpsgProjectedCrsRecord(21263, "GSK-2011 / GSK 3GK zone 63", 7683, 4530, 16963); + return true; + case 3949: + record = new EpsgProjectedCrsRecord(21264, "GSK-2011 / GSK 3GK zone 64", 7683, 4530, 16964); + return true; + case 3950: + record = new EpsgProjectedCrsRecord(21291, "Barbados 1938 / British West Indies Grid", 4212, 4400, 19942); + return true; + case 3951: + record = new EpsgProjectedCrsRecord(21292, "Barbados 1938 / Barbados National Grid", 4212, 4400, 19943); + return true; + case 3952: + record = new EpsgProjectedCrsRecord(21307, "GSK-2011 / GSK 3GK CM 21E", 7683, 4530, 17107); + return true; + case 3953: + record = new EpsgProjectedCrsRecord(21308, "GSK-2011 / GSK 3GK CM 24E", 7683, 4530, 17108); + return true; + case 3954: + record = new EpsgProjectedCrsRecord(21309, "GSK-2011 / GSK 3GK CM 27E", 7683, 4530, 17109); + return true; + case 3955: + record = new EpsgProjectedCrsRecord(21310, "GSK-2011 / GSK 3GK CM 30E", 7683, 4530, 17110); + return true; + case 3956: + record = new EpsgProjectedCrsRecord(21311, "GSK-2011 / GSK 3GK CM 33E", 7683, 4530, 17111); + return true; + case 3957: + record = new EpsgProjectedCrsRecord(21312, "GSK-2011 / GSK 3GK CM 36E", 7683, 4530, 17112); + return true; + case 3958: + record = new EpsgProjectedCrsRecord(21313, "GSK-2011 / GSK 3GK CM 39E", 7683, 4530, 17113); + return true; + case 3959: + record = new EpsgProjectedCrsRecord(21314, "GSK-2011 / GSK 3GK CM 42E", 7683, 4530, 17114); + return true; + case 3960: + record = new EpsgProjectedCrsRecord(21315, "GSK-2011 / GSK 3GK CM 45E", 7683, 4530, 17115); + return true; + case 3961: + record = new EpsgProjectedCrsRecord(21316, "GSK-2011 / GSK 3GK CM 48E", 7683, 4530, 17116); + return true; + case 3962: + record = new EpsgProjectedCrsRecord(21317, "GSK-2011 / GSK 3GK CM 51E", 7683, 4530, 17117); + return true; + case 3963: + record = new EpsgProjectedCrsRecord(21318, "GSK-2011 / GSK 3GK CM 54E", 7683, 4530, 17118); + return true; + case 3964: + record = new EpsgProjectedCrsRecord(21319, "GSK-2011 / GSK 3GK CM 57E", 7683, 4530, 17119); + return true; + case 3965: + record = new EpsgProjectedCrsRecord(21320, "GSK-2011 / GSK 3GK CM 60E", 7683, 4530, 17120); + return true; + case 3966: + record = new EpsgProjectedCrsRecord(21321, "GSK-2011 / GSK 3GK CM 63E", 7683, 4530, 17121); + return true; + case 3967: + record = new EpsgProjectedCrsRecord(21322, "GSK-2011 / GSK 3GK CM 66E", 7683, 4530, 17122); + return true; + case 3968: + record = new EpsgProjectedCrsRecord(21323, "GSK-2011 / GSK 3GK CM 69E", 7683, 4530, 17123); + return true; + case 3969: + record = new EpsgProjectedCrsRecord(21324, "GSK-2011 / GSK 3GK CM 72E", 7683, 4530, 17124); + return true; + case 3970: + record = new EpsgProjectedCrsRecord(21325, "GSK-2011 / GSK 3GK CM 75E", 7683, 4530, 17125); + return true; + case 3971: + record = new EpsgProjectedCrsRecord(21326, "GSK-2011 / GSK 3GK CM 78E", 7683, 4530, 17126); + return true; + case 3972: + record = new EpsgProjectedCrsRecord(21327, "GSK-2011 / GSK 3GK CM 81E", 7683, 4530, 17127); + return true; + case 3973: + record = new EpsgProjectedCrsRecord(21328, "GSK-2011 / GSK 3GK CM 84E", 7683, 4530, 17128); + return true; + case 3974: + record = new EpsgProjectedCrsRecord(21329, "GSK-2011 / GSK 3GK CM 87E", 7683, 4530, 17129); + return true; + case 3975: + record = new EpsgProjectedCrsRecord(21330, "GSK-2011 / GSK 3GK CM 90E", 7683, 4530, 17130); + return true; + case 3976: + record = new EpsgProjectedCrsRecord(21331, "GSK-2011 / GSK 3GK CM 93E", 7683, 4530, 17131); + return true; + case 3977: + record = new EpsgProjectedCrsRecord(21332, "GSK-2011 / GSK 3GK CM 96E", 7683, 4530, 17132); + return true; + case 3978: + record = new EpsgProjectedCrsRecord(21333, "GSK-2011 / GSK 3GK CM 99E", 7683, 4530, 17133); + return true; + case 3979: + record = new EpsgProjectedCrsRecord(21334, "GSK-2011 / GSK 3GK CM 102E", 7683, 4530, 17134); + return true; + case 3980: + record = new EpsgProjectedCrsRecord(21335, "GSK-2011 / GSK 3GK CM 105E", 7683, 4530, 17135); + return true; + case 3981: + record = new EpsgProjectedCrsRecord(21336, "GSK-2011 / GSK 3GK CM 108E", 7683, 4530, 17136); + return true; + case 3982: + record = new EpsgProjectedCrsRecord(21337, "GSK-2011 / GSK 3GK CM 111E", 7683, 4530, 17137); + return true; + case 3983: + record = new EpsgProjectedCrsRecord(21338, "GSK-2011 / GSK 3GK CM 114E", 7683, 4530, 17138); + return true; + case 3984: + record = new EpsgProjectedCrsRecord(21339, "GSK-2011 / GSK 3GK CM 117E", 7683, 4530, 17139); + return true; + case 3985: + record = new EpsgProjectedCrsRecord(21340, "GSK-2011 / GSK 3GK CM 120E", 7683, 4530, 17140); + return true; + case 3986: + record = new EpsgProjectedCrsRecord(21341, "GSK-2011 / GSK 3GK CM 123E", 7683, 4530, 17141); + return true; + case 3987: + record = new EpsgProjectedCrsRecord(21342, "GSK-2011 / GSK 3GK CM 126E", 7683, 4530, 17142); + return true; + case 3988: + record = new EpsgProjectedCrsRecord(21343, "GSK-2011 / GSK 3GK CM 129E", 7683, 4530, 17143); + return true; + case 3989: + record = new EpsgProjectedCrsRecord(21344, "GSK-2011 / GSK 3GK CM 132E", 7683, 4530, 17144); + return true; + case 3990: + record = new EpsgProjectedCrsRecord(21345, "GSK-2011 / GSK 3GK CM 135E", 7683, 4530, 17145); + return true; + case 3991: + record = new EpsgProjectedCrsRecord(21346, "GSK-2011 / GSK 3GK CM 138E", 7683, 4530, 17146); + return true; + case 3992: + record = new EpsgProjectedCrsRecord(21347, "GSK-2011 / GSK 3GK CM 141E", 7683, 4530, 17147); + return true; + case 3993: + record = new EpsgProjectedCrsRecord(21348, "GSK-2011 / GSK 3GK CM 144E", 7683, 4530, 17148); + return true; + case 3994: + record = new EpsgProjectedCrsRecord(21349, "GSK-2011 / GSK 3GK CM 147E", 7683, 4530, 17149); + return true; + case 3995: + record = new EpsgProjectedCrsRecord(21350, "GSK-2011 / GSK 3GK CM 150E", 7683, 4530, 17150); + return true; + case 3996: + record = new EpsgProjectedCrsRecord(21351, "GSK-2011 / GSK 3GK CM 153E", 7683, 4530, 17151); + return true; + case 3997: + record = new EpsgProjectedCrsRecord(21352, "GSK-2011 / GSK 3GK CM 156E", 7683, 4530, 17152); + return true; + case 3998: + record = new EpsgProjectedCrsRecord(21353, "GSK-2011 / GSK 3GK CM 159E", 7683, 4530, 17153); + return true; + case 3999: + record = new EpsgProjectedCrsRecord(21354, "GSK-2011 / GSK 3GK CM 162E", 7683, 4530, 17154); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetProjectedCrsBucket4(int index, out EpsgProjectedCrsRecord record) + { + switch (index) + { + case 4000: + record = new EpsgProjectedCrsRecord(21355, "GSK-2011 / GSK 3GK CM 165E", 7683, 4530, 17155); + return true; + case 4001: + record = new EpsgProjectedCrsRecord(21356, "GSK-2011 / GSK 3GK CM 168E", 7683, 4530, 17156); + return true; + case 4002: + record = new EpsgProjectedCrsRecord(21357, "GSK-2011 / GSK 3GK CM 171E", 7683, 4530, 17157); + return true; + case 4003: + record = new EpsgProjectedCrsRecord(21358, "GSK-2011 / GSK 3GK CM 174E", 7683, 4530, 17158); + return true; + case 4004: + record = new EpsgProjectedCrsRecord(21359, "GSK-2011 / GSK 3GK CM 177E", 7683, 4530, 17159); + return true; + case 4005: + record = new EpsgProjectedCrsRecord(21360, "GSK-2011 / GSK 3GK CM 180E", 7683, 4530, 17160); + return true; + case 4006: + record = new EpsgProjectedCrsRecord(21361, "GSK-2011 / GSK 3GK CM 177W", 7683, 4530, 17161); + return true; + case 4007: + record = new EpsgProjectedCrsRecord(21362, "GSK-2011 / GSK 3GK CM 174W", 7683, 4530, 17162); + return true; + case 4008: + record = new EpsgProjectedCrsRecord(21363, "GSK-2011 / GSK 3GK CM 171W", 7683, 4530, 17163); + return true; + case 4009: + record = new EpsgProjectedCrsRecord(21364, "GSK-2011 / GSK 3GK CM 168W", 7683, 4530, 17164); + return true; + case 4010: + record = new EpsgProjectedCrsRecord(21413, "Beijing 1954 / Gauss-Kruger zone 13", 4214, 4530, 16213); + return true; + case 4011: + record = new EpsgProjectedCrsRecord(21414, "Beijing 1954 / Gauss-Kruger zone 14", 4214, 4530, 16214); + return true; + case 4012: + record = new EpsgProjectedCrsRecord(21415, "Beijing 1954 / Gauss-Kruger zone 15", 4214, 4530, 16215); + return true; + case 4013: + record = new EpsgProjectedCrsRecord(21416, "Beijing 1954 / Gauss-Kruger zone 16", 4214, 4530, 16216); + return true; + case 4014: + record = new EpsgProjectedCrsRecord(21417, "Beijing 1954 / Gauss-Kruger zone 17", 4214, 4530, 16217); + return true; + case 4015: + record = new EpsgProjectedCrsRecord(21418, "Beijing 1954 / Gauss-Kruger zone 18", 4214, 4530, 16218); + return true; + case 4016: + record = new EpsgProjectedCrsRecord(21419, "Beijing 1954 / Gauss-Kruger zone 19", 4214, 4530, 16219); + return true; + case 4017: + record = new EpsgProjectedCrsRecord(21420, "Beijing 1954 / Gauss-Kruger zone 20", 4214, 4530, 16220); + return true; + case 4018: + record = new EpsgProjectedCrsRecord(21421, "Beijing 1954 / Gauss-Kruger zone 21", 4214, 4530, 16221); + return true; + case 4019: + record = new EpsgProjectedCrsRecord(21422, "Beijing 1954 / Gauss-Kruger zone 22", 4214, 4530, 16222); + return true; + case 4020: + record = new EpsgProjectedCrsRecord(21423, "Beijing 1954 / Gauss-Kruger zone 23", 4214, 4530, 16223); + return true; + case 4021: + record = new EpsgProjectedCrsRecord(21453, "Beijing 1954 / Gauss-Kruger CM 75E", 4214, 4530, 16313); + return true; + case 4022: + record = new EpsgProjectedCrsRecord(21454, "Beijing 1954 / Gauss-Kruger CM 81E", 4214, 4530, 16314); + return true; + case 4023: + record = new EpsgProjectedCrsRecord(21455, "Beijing 1954 / Gauss-Kruger CM 87E", 4214, 4530, 16315); + return true; + case 4024: + record = new EpsgProjectedCrsRecord(21456, "Beijing 1954 / Gauss-Kruger CM 93E", 4214, 4530, 16316); + return true; + case 4025: + record = new EpsgProjectedCrsRecord(21457, "Beijing 1954 / Gauss-Kruger CM 99E", 4214, 4530, 16317); + return true; + case 4026: + record = new EpsgProjectedCrsRecord(21458, "Beijing 1954 / Gauss-Kruger CM 105E", 4214, 4530, 16318); + return true; + case 4027: + record = new EpsgProjectedCrsRecord(21459, "Beijing 1954 / Gauss-Kruger CM 111E", 4214, 4530, 16319); + return true; + case 4028: + record = new EpsgProjectedCrsRecord(21460, "Beijing 1954 / Gauss-Kruger CM 117E", 4214, 4530, 16320); + return true; + case 4029: + record = new EpsgProjectedCrsRecord(21461, "Beijing 1954 / Gauss-Kruger CM 123E", 4214, 4530, 16321); + return true; + case 4030: + record = new EpsgProjectedCrsRecord(21462, "Beijing 1954 / Gauss-Kruger CM 129E", 4214, 4530, 16322); + return true; + case 4031: + record = new EpsgProjectedCrsRecord(21463, "Beijing 1954 / Gauss-Kruger CM 135E", 4214, 4530, 16323); + return true; + case 4032: + record = new EpsgProjectedCrsRecord(21500, "BD50 (Brussels) / Belge Lambert 50", 4809, 4499, 19901); + return true; + case 4033: + record = new EpsgProjectedCrsRecord(21780, "CH1903 (Bern) / LV03C", 4801, 4498, 19923); + return true; + case 4034: + record = new EpsgProjectedCrsRecord(21781, "CH1903 / LV03", 4149, 4498, 19922); + return true; + case 4035: + record = new EpsgProjectedCrsRecord(21782, "CH1903 / LV03C-G", 4149, 4498, 19841); + return true; + case 4036: + record = new EpsgProjectedCrsRecord(21818, "Bogota 1975 / UTM zone 18N", 4218, 4400, 16018); + return true; + case 4037: + record = new EpsgProjectedCrsRecord(21896, "Bogota 1975 / Colombia West zone", 4218, 4530, 18051); + return true; + case 4038: + record = new EpsgProjectedCrsRecord(21897, "Bogota 1975 / Colombia Bogota zone", 4218, 4530, 18052); + return true; + case 4039: + record = new EpsgProjectedCrsRecord(21898, "Bogota 1975 / Colombia East Central zone", 4218, 4530, 18053); + return true; + case 4040: + record = new EpsgProjectedCrsRecord(21899, "Bogota 1975 / Colombia East zone", 4218, 4530, 18054); + return true; + case 4041: + record = new EpsgProjectedCrsRecord(22032, "Camacupa 1948 / UTM zone 32S", 4220, 4400, 16132); + return true; + case 4042: + record = new EpsgProjectedCrsRecord(22033, "Camacupa 1948 / UTM zone 33S", 4220, 4400, 16133); + return true; + case 4043: + record = new EpsgProjectedCrsRecord(22091, "Camacupa 1948 / TM 11.30 SE", 4220, 4400, 16611); + return true; + case 4044: + record = new EpsgProjectedCrsRecord(22092, "Camacupa 1948 / TM 12 SE", 4220, 4400, 16612); + return true; + case 4045: + record = new EpsgProjectedCrsRecord(22171, "POSGAR 98 / Argentina 1", 4190, 4530, 18031); + return true; + case 4046: + record = new EpsgProjectedCrsRecord(22172, "POSGAR 98 / Argentina 2", 4190, 4530, 18032); + return true; + case 4047: + record = new EpsgProjectedCrsRecord(22173, "POSGAR 98 / Argentina 3", 4190, 4530, 18033); + return true; + case 4048: + record = new EpsgProjectedCrsRecord(22174, "POSGAR 98 / Argentina 4", 4190, 4530, 18034); + return true; + case 4049: + record = new EpsgProjectedCrsRecord(22175, "POSGAR 98 / Argentina 5", 4190, 4530, 18035); + return true; + case 4050: + record = new EpsgProjectedCrsRecord(22176, "POSGAR 98 / Argentina 6", 4190, 4530, 18036); + return true; + case 4051: + record = new EpsgProjectedCrsRecord(22177, "POSGAR 98 / Argentina 7", 4190, 4530, 18037); + return true; + case 4052: + record = new EpsgProjectedCrsRecord(22181, "POSGAR 94 / Argentina 1", 4694, 4530, 18031); + return true; + case 4053: + record = new EpsgProjectedCrsRecord(22182, "POSGAR 94 / Argentina 2", 4694, 4530, 18032); + return true; + case 4054: + record = new EpsgProjectedCrsRecord(22183, "POSGAR 94 / Argentina 3", 4694, 4530, 18033); + return true; + case 4055: + record = new EpsgProjectedCrsRecord(22184, "POSGAR 94 / Argentina 4", 4694, 4530, 18034); + return true; + case 4056: + record = new EpsgProjectedCrsRecord(22185, "POSGAR 94 / Argentina 5", 4694, 4530, 18035); + return true; + case 4057: + record = new EpsgProjectedCrsRecord(22186, "POSGAR 94 / Argentina 6", 4694, 4530, 18036); + return true; + case 4058: + record = new EpsgProjectedCrsRecord(22187, "POSGAR 94 / Argentina 7", 4694, 4530, 18037); + return true; + case 4059: + record = new EpsgProjectedCrsRecord(22191, "Campo Inchauspe / Argentina 1", 4221, 4530, 18031); + return true; + case 4060: + record = new EpsgProjectedCrsRecord(22192, "Campo Inchauspe / Argentina 2", 4221, 4530, 18032); + return true; + case 4061: + record = new EpsgProjectedCrsRecord(22193, "Campo Inchauspe / Argentina 3", 4221, 4530, 18033); + return true; + case 4062: + record = new EpsgProjectedCrsRecord(22194, "Campo Inchauspe / Argentina 4", 4221, 4530, 18034); + return true; + case 4063: + record = new EpsgProjectedCrsRecord(22195, "Campo Inchauspe / Argentina 5", 4221, 4530, 18035); + return true; + case 4064: + record = new EpsgProjectedCrsRecord(22196, "Campo Inchauspe / Argentina 6", 4221, 4530, 18036); + return true; + case 4065: + record = new EpsgProjectedCrsRecord(22197, "Campo Inchauspe / Argentina 7", 4221, 4530, 18037); + return true; + case 4066: + record = new EpsgProjectedCrsRecord(22207, "NAD83(CSRS)v2 / UTM zone 7N", 8237, 4400, 16007); + return true; + case 4067: + record = new EpsgProjectedCrsRecord(22208, "NAD83(CSRS)v2 / UTM zone 8N", 8237, 4400, 16008); + return true; + case 4068: + record = new EpsgProjectedCrsRecord(22209, "NAD83(CSRS)v2 / UTM zone 9N", 8237, 4400, 16009); + return true; + case 4069: + record = new EpsgProjectedCrsRecord(22210, "NAD83(CSRS)v2 / UTM zone 10N", 8237, 4400, 16010); + return true; + case 4070: + record = new EpsgProjectedCrsRecord(22211, "NAD83(CSRS)v2 / UTM zone 11N", 8237, 4400, 16011); + return true; + case 4071: + record = new EpsgProjectedCrsRecord(22212, "NAD83(CSRS)v2 / UTM zone 12N", 8237, 4400, 16012); + return true; + case 4072: + record = new EpsgProjectedCrsRecord(22213, "NAD83(CSRS)v2 / UTM zone 13N", 8237, 4400, 16013); + return true; + case 4073: + record = new EpsgProjectedCrsRecord(22214, "NAD83(CSRS)v2 / UTM zone 14N", 8237, 4400, 16014); + return true; + case 4074: + record = new EpsgProjectedCrsRecord(22215, "NAD83(CSRS)v2 / UTM zone 15N", 8237, 4400, 16015); + return true; + case 4075: + record = new EpsgProjectedCrsRecord(22216, "NAD83(CSRS)v2 / UTM zone 16N", 8237, 4400, 16016); + return true; + case 4076: + record = new EpsgProjectedCrsRecord(22217, "NAD83(CSRS)v2 / UTM zone 17N", 8237, 4400, 16017); + return true; + case 4077: + record = new EpsgProjectedCrsRecord(22218, "NAD83(CSRS)v2 / UTM zone 18N", 8237, 4400, 16018); + return true; + case 4078: + record = new EpsgProjectedCrsRecord(22219, "NAD83(CSRS)v2 / UTM zone 19N", 8237, 4400, 16019); + return true; + case 4079: + record = new EpsgProjectedCrsRecord(22220, "NAD83(CSRS)v2 / UTM zone 20N", 8237, 4400, 16020); + return true; + case 4080: + record = new EpsgProjectedCrsRecord(22221, "NAD83(CSRS)v2 / UTM zone 21N", 8237, 4400, 16021); + return true; + case 4081: + record = new EpsgProjectedCrsRecord(22222, "NAD83(CSRS)v2 / UTM zone 22N", 8237, 4400, 16022); + return true; + case 4082: + record = new EpsgProjectedCrsRecord(22229, "RGSH2020 / UTM zone 29N", 10299, 4400, 16029); + return true; + case 4083: + record = new EpsgProjectedCrsRecord(22230, "RGSH2020 / UTM zone 30N", 10299, 4400, 16030); + return true; + case 4084: + record = new EpsgProjectedCrsRecord(22231, "RGSH2020 / UTM zone 31N", 10299, 4400, 16031); + return true; + case 4085: + record = new EpsgProjectedCrsRecord(22232, "RGSH2020 / UTM zone 32N", 10299, 4400, 16032); + return true; + case 4086: + record = new EpsgProjectedCrsRecord(22234, "Cape / UTM zone 34S", 4222, 4400, 16134); + return true; + case 4087: + record = new EpsgProjectedCrsRecord(22235, "Cape / UTM zone 35S", 4222, 4400, 16135); + return true; + case 4088: + record = new EpsgProjectedCrsRecord(22239, "NAD83(CSRS)v2 / PEI Stereographic", 8237, 4496, 19960); + return true; + case 4089: + record = new EpsgProjectedCrsRecord(22240, "NAD83(CSRS)v2 / NB Stereographic", 8237, 4500, 19946); + return true; + case 4090: + record = new EpsgProjectedCrsRecord(22243, "NAD83(CSRS)v2 / SCoPQ zone 3", 8237, 4499, 17703); + return true; + case 4091: + record = new EpsgProjectedCrsRecord(22244, "NAD83(CSRS)v2 / SCoPQ zone 4", 8237, 4499, 17704); + return true; + case 4092: + record = new EpsgProjectedCrsRecord(22245, "NAD83(CSRS)v2 / SCoPQ zone 5", 8237, 4499, 17705); + return true; + case 4093: + record = new EpsgProjectedCrsRecord(22246, "NAD83(CSRS)v2 / SCoPQ zone 6", 8237, 4499, 17706); + return true; + case 4094: + record = new EpsgProjectedCrsRecord(22247, "NAD83(CSRS)v2 / SCoPQ zone 7", 8237, 4499, 17707); + return true; + case 4095: + record = new EpsgProjectedCrsRecord(22248, "NAD83(CSRS)v2 / SCoPQ zone 8", 8237, 4499, 17708); + return true; + case 4096: + record = new EpsgProjectedCrsRecord(22249, "NAD83(CSRS)v2 / SCoPQ zone 9", 8237, 4499, 17709); + return true; + case 4097: + record = new EpsgProjectedCrsRecord(22250, "NAD83(CSRS)v2 / SCoPQ zone 10", 8237, 4499, 17710); + return true; + case 4098: + record = new EpsgProjectedCrsRecord(22262, "NAD83(CSRS)v2 / Alberta 3TM ref merid 111 W", 8237, 4400, 17722); + return true; + case 4099: + record = new EpsgProjectedCrsRecord(22263, "NAD83(CSRS)v2 / Alberta 3TM ref merid 114 W", 8237, 4400, 17723); + return true; + case 4100: + record = new EpsgProjectedCrsRecord(22264, "NAD83(CSRS)v2 / Alberta 3TM ref merid 117 W", 8237, 4400, 17724); + return true; + case 4101: + record = new EpsgProjectedCrsRecord(22265, "NAD83(CSRS)v2 / Alberta 3TM ref merid 120 W", 8237, 4400, 17726); + return true; + case 4102: + record = new EpsgProjectedCrsRecord(22275, "Cape / Lo15", 4222, 6503, 17515); + return true; + case 4103: + record = new EpsgProjectedCrsRecord(22277, "Cape / Lo17", 4222, 6503, 17517); + return true; + case 4104: + record = new EpsgProjectedCrsRecord(22279, "Cape / Lo19", 4222, 6503, 17519); + return true; + case 4105: + record = new EpsgProjectedCrsRecord(22281, "Cape / Lo21", 4222, 6503, 17521); + return true; + case 4106: + record = new EpsgProjectedCrsRecord(22283, "Cape / Lo23", 4222, 6503, 17523); + return true; + case 4107: + record = new EpsgProjectedCrsRecord(22285, "Cape / Lo25", 4222, 6503, 17525); + return true; + case 4108: + record = new EpsgProjectedCrsRecord(22287, "Cape / Lo27", 4222, 6503, 17527); + return true; + case 4109: + record = new EpsgProjectedCrsRecord(22289, "Cape / Lo29", 4222, 6503, 17529); + return true; + case 4110: + record = new EpsgProjectedCrsRecord(22291, "Cape / Lo31", 4222, 6503, 17531); + return true; + case 4111: + record = new EpsgProjectedCrsRecord(22293, "Cape / Lo33", 4222, 6503, 17533); + return true; + case 4112: + record = new EpsgProjectedCrsRecord(22300, "Carthage (Paris) / Tunisia Mining Grid", 4816, 4406, 19937); + return true; + case 4113: + record = new EpsgProjectedCrsRecord(22307, "NAD83(CSRS)v3 / UTM zone 7N", 8240, 4400, 16007); + return true; + case 4114: + record = new EpsgProjectedCrsRecord(22308, "NAD83(CSRS)v3 / UTM zone 8N", 8240, 4400, 16008); + return true; + case 4115: + record = new EpsgProjectedCrsRecord(22309, "NAD83(CSRS)v3 / UTM zone 9N", 8240, 4400, 16009); + return true; + case 4116: + record = new EpsgProjectedCrsRecord(22310, "NAD83(CSRS)v3 / UTM zone 10N", 8240, 4400, 16010); + return true; + case 4117: + record = new EpsgProjectedCrsRecord(22311, "NAD83(CSRS)v3 / UTM zone 11N", 8240, 4400, 16011); + return true; + case 4118: + record = new EpsgProjectedCrsRecord(22312, "NAD83(CSRS)v3 / UTM zone 12N", 8240, 4400, 16012); + return true; + case 4119: + record = new EpsgProjectedCrsRecord(22313, "NAD83(CSRS)v3 / UTM zone 13N", 8240, 4400, 16013); + return true; + case 4120: + record = new EpsgProjectedCrsRecord(22314, "NAD83(CSRS)v3 / UTM zone 14N", 8240, 4400, 16014); + return true; + case 4121: + record = new EpsgProjectedCrsRecord(22315, "NAD83(CSRS)v3 / UTM zone 15N", 8240, 4400, 16015); + return true; + case 4122: + record = new EpsgProjectedCrsRecord(22316, "NAD83(CSRS)v3 / UTM zone 16N", 8240, 4400, 16016); + return true; + case 4123: + record = new EpsgProjectedCrsRecord(22317, "NAD83(CSRS)v3 / UTM zone 17N", 8240, 4400, 16017); + return true; + case 4124: + record = new EpsgProjectedCrsRecord(22318, "NAD83(CSRS)v3 / UTM zone 18N", 8240, 4400, 16018); + return true; + case 4125: + record = new EpsgProjectedCrsRecord(22319, "NAD83(CSRS)v3 / UTM zone 19N", 8240, 4400, 16019); + return true; + case 4126: + record = new EpsgProjectedCrsRecord(22320, "NAD83(CSRS)v3 / UTM zone 20N", 8240, 4400, 16020); + return true; + case 4127: + record = new EpsgProjectedCrsRecord(22321, "NAD83(CSRS)v3 / UTM zone 21N", 8240, 4400, 16021); + return true; + case 4128: + record = new EpsgProjectedCrsRecord(22322, "NAD83(CSRS)v3 / UTM zone 22N", 8240, 4400, 16022); + return true; + case 4129: + record = new EpsgProjectedCrsRecord(22332, "Carthage / UTM zone 32N", 4223, 4400, 16032); + return true; + case 4130: + record = new EpsgProjectedCrsRecord(22337, "NAD83(CSRS)v3 / MTM NS 1997 zone 5", 8240, 4400, 9982); + return true; + case 4131: + record = new EpsgProjectedCrsRecord(22338, "NAD83(CSRS)v3 / MTM NS 1997 zone 4", 8240, 4400, 9981); + return true; + case 4132: + record = new EpsgProjectedCrsRecord(22348, "NAD83(CSRS)v3 / MTM zone 8", 8240, 4496, 17708); + return true; + case 4133: + record = new EpsgProjectedCrsRecord(22349, "NAD83(CSRS)v3 / MTM zone 9", 8240, 4496, 17709); + return true; + case 4134: + record = new EpsgProjectedCrsRecord(22350, "NAD83(CSRS)v3 / MTM zone 10", 8240, 4496, 17710); + return true; + case 4135: + record = new EpsgProjectedCrsRecord(22351, "NAD83(CSRS)v3 / MTM zone 11", 8240, 4400, 17711); + return true; + case 4136: + record = new EpsgProjectedCrsRecord(22352, "NAD83(CSRS)v3 / MTM zone 12", 8240, 4400, 17712); + return true; + case 4137: + record = new EpsgProjectedCrsRecord(22353, "NAD83(CSRS)v3 / MTM zone 13", 8240, 4400, 17713); + return true; + case 4138: + record = new EpsgProjectedCrsRecord(22354, "NAD83(CSRS)v3 / MTM zone 14", 8240, 4400, 17714); + return true; + case 4139: + record = new EpsgProjectedCrsRecord(22355, "NAD83(CSRS)v3 / MTM zone 15", 8240, 4400, 17715); + return true; + case 4140: + record = new EpsgProjectedCrsRecord(22356, "NAD83(CSRS)v3 / MTM zone 16", 8240, 4400, 17716); + return true; + case 4141: + record = new EpsgProjectedCrsRecord(22357, "NAD83(CSRS)v3 / MTM zone 17", 8240, 4400, 17717); + return true; + case 4142: + record = new EpsgProjectedCrsRecord(22391, "Carthage / Nord Tunisie", 4223, 4499, 18181); + return true; + case 4143: + record = new EpsgProjectedCrsRecord(22392, "Carthage / Sud Tunisie", 4223, 4499, 18182); + return true; + case 4144: + record = new EpsgProjectedCrsRecord(22407, "NAD83(CSRS)v4 / UTM zone 7N", 8246, 4400, 16007); + return true; + case 4145: + record = new EpsgProjectedCrsRecord(22408, "NAD83(CSRS)v4 / UTM zone 8N", 8246, 4400, 16008); + return true; + case 4146: + record = new EpsgProjectedCrsRecord(22409, "NAD83(CSRS)v4 / UTM zone 9N", 8246, 4400, 16009); + return true; + case 4147: + record = new EpsgProjectedCrsRecord(22410, "NAD83(CSRS)v4 / UTM zone 10N", 8246, 4400, 16010); + return true; + case 4148: + record = new EpsgProjectedCrsRecord(22411, "NAD83(CSRS)v4 / UTM zone 11N", 8246, 4400, 16011); + return true; + case 4149: + record = new EpsgProjectedCrsRecord(22412, "NAD83(CSRS)v4 / UTM zone 12N", 8246, 4400, 16012); + return true; + case 4150: + record = new EpsgProjectedCrsRecord(22413, "NAD83(CSRS)v4 / UTM zone 13N", 8246, 4400, 16013); + return true; + case 4151: + record = new EpsgProjectedCrsRecord(22414, "NAD83(CSRS)v4 / UTM zone 14N", 8246, 4400, 16014); + return true; + case 4152: + record = new EpsgProjectedCrsRecord(22415, "NAD83(CSRS)v4 / UTM zone 15N", 8246, 4400, 16015); + return true; + case 4153: + record = new EpsgProjectedCrsRecord(22416, "NAD83(CSRS)v4 / UTM zone 16N", 8246, 4400, 16016); + return true; + case 4154: + record = new EpsgProjectedCrsRecord(22417, "NAD83(CSRS)v4 / UTM zone 17N", 8246, 4400, 16017); + return true; + case 4155: + record = new EpsgProjectedCrsRecord(22418, "NAD83(CSRS)v4 / UTM zone 18N", 8246, 4400, 16018); + return true; + case 4156: + record = new EpsgProjectedCrsRecord(22419, "NAD83(CSRS)v4 / UTM zone 19N", 8246, 4400, 16019); + return true; + case 4157: + record = new EpsgProjectedCrsRecord(22420, "NAD83(CSRS)v4 / UTM zone 20N", 8246, 4400, 16020); + return true; + case 4158: + record = new EpsgProjectedCrsRecord(22421, "NAD83(CSRS)v4 / UTM zone 21N", 8246, 4400, 16021); + return true; + case 4159: + record = new EpsgProjectedCrsRecord(22422, "NAD83(CSRS)v4 / UTM zone 22N", 8246, 4400, 16022); + return true; + case 4160: + record = new EpsgProjectedCrsRecord(22462, "NAD83(CSRS)v4 / Alberta 3TM ref merid 111 W", 8246, 4400, 17722); + return true; + case 4161: + record = new EpsgProjectedCrsRecord(22463, "NAD83(CSRS)v4 / Alberta 3TM ref merid 114 W", 8246, 4400, 17723); + return true; + case 4162: + record = new EpsgProjectedCrsRecord(22464, "NAD83(CSRS)v4 / Alberta 3TM ref merid 117 W", 8246, 4400, 17724); + return true; + case 4163: + record = new EpsgProjectedCrsRecord(22465, "NAD83(CSRS)v4 / Alberta 3TM ref merid 120 W", 8246, 4400, 17726); + return true; + case 4164: + record = new EpsgProjectedCrsRecord(22521, "Corrego Alegre 1970-72 / UTM zone 21S", 4225, 4400, 16121); + return true; + case 4165: + record = new EpsgProjectedCrsRecord(22522, "Corrego Alegre 1970-72 / UTM zone 22S", 4225, 4400, 16122); + return true; + case 4166: + record = new EpsgProjectedCrsRecord(22523, "Corrego Alegre 1970-72 / UTM zone 23S", 4225, 4400, 16123); + return true; + case 4167: + record = new EpsgProjectedCrsRecord(22524, "Corrego Alegre 1970-72 / UTM zone 24S", 4225, 4400, 16124); + return true; + case 4168: + record = new EpsgProjectedCrsRecord(22525, "Corrego Alegre 1970-72 / UTM zone 25S", 4225, 4400, 16125); + return true; + case 4169: + record = new EpsgProjectedCrsRecord(22607, "NAD83(CSRS)v6 / UTM zone 7N", 8252, 4400, 16007); + return true; + case 4170: + record = new EpsgProjectedCrsRecord(22608, "NAD83(CSRS)v6 / UTM zone 8N", 8252, 4400, 16008); + return true; + case 4171: + record = new EpsgProjectedCrsRecord(22609, "NAD83(CSRS)v6 / UTM zone 9N", 8252, 4400, 16009); + return true; + case 4172: + record = new EpsgProjectedCrsRecord(22610, "NAD83(CSRS)v6 / UTM zone 10N", 8252, 4400, 16010); + return true; + case 4173: + record = new EpsgProjectedCrsRecord(22611, "NAD83(CSRS)v6 / UTM zone 11N", 8252, 4400, 16011); + return true; + case 4174: + record = new EpsgProjectedCrsRecord(22612, "NAD83(CSRS)v6 / UTM zone 12N", 8252, 4400, 16012); + return true; + case 4175: + record = new EpsgProjectedCrsRecord(22613, "NAD83(CSRS)v6 / UTM zone 13N", 8252, 4400, 16013); + return true; + case 4176: + record = new EpsgProjectedCrsRecord(22614, "NAD83(CSRS)v6 / UTM zone 14N", 8252, 4400, 16014); + return true; + case 4177: + record = new EpsgProjectedCrsRecord(22615, "NAD83(CSRS)v6 / UTM zone 15N", 8252, 4400, 16015); + return true; + case 4178: + record = new EpsgProjectedCrsRecord(22616, "NAD83(CSRS)v6 / UTM zone 16N", 8252, 4400, 16016); + return true; + case 4179: + record = new EpsgProjectedCrsRecord(22617, "NAD83(CSRS)v6 / UTM zone 17N", 8252, 4400, 16017); + return true; + case 4180: + record = new EpsgProjectedCrsRecord(22618, "NAD83(CSRS)v6 / UTM zone 18N", 8252, 4400, 16018); + return true; + case 4181: + record = new EpsgProjectedCrsRecord(22619, "NAD83(CSRS)v6 / UTM zone 19N", 8252, 4400, 16019); + return true; + case 4182: + record = new EpsgProjectedCrsRecord(22620, "NAD83(CSRS)v6 / UTM zone 20N", 8252, 4400, 16020); + return true; + case 4183: + record = new EpsgProjectedCrsRecord(22621, "NAD83(CSRS)v6 / UTM zone 21N", 8252, 4400, 16021); + return true; + case 4184: + record = new EpsgProjectedCrsRecord(22622, "NAD83(CSRS)v6 / UTM zone 22N", 8252, 4400, 16022); + return true; + case 4185: + record = new EpsgProjectedCrsRecord(22639, "NAD83(CSRS)v6 / PEI Stereographic", 8252, 4496, 19960); + return true; + case 4186: + record = new EpsgProjectedCrsRecord(22641, "NAD83(CSRS)v6 / MTM zone 1", 8252, 4496, 17701); + return true; + case 4187: + record = new EpsgProjectedCrsRecord(22642, "NAD83(CSRS)v6 / MTM zone 2", 8252, 4496, 17702); + return true; + case 4188: + record = new EpsgProjectedCrsRecord(22643, "NAD83(CSRS)v6 / MTM zone 3", 8252, 4496, 17703); + return true; + case 4189: + record = new EpsgProjectedCrsRecord(22644, "NAD83(CSRS)v6 / MTM zone 4", 8252, 4496, 17704); + return true; + case 4190: + record = new EpsgProjectedCrsRecord(22645, "NAD83(CSRS)v6 / MTM zone 5", 8252, 4496, 17705); + return true; + case 4191: + record = new EpsgProjectedCrsRecord(22646, "NAD83(CSRS)v6 / MTM zone 6", 8252, 4496, 17706); + return true; + case 4192: + record = new EpsgProjectedCrsRecord(22648, "NAD83(CSRS)v6 / MTM zone 8", 8252, 4496, 17708); + return true; + case 4193: + record = new EpsgProjectedCrsRecord(22649, "NAD83(CSRS)v6 / MTM zone 9", 8252, 4496, 17709); + return true; + case 4194: + record = new EpsgProjectedCrsRecord(22650, "NAD83(CSRS)v6 / MTM zone 10", 8252, 4496, 17710); + return true; + case 4195: + record = new EpsgProjectedCrsRecord(22651, "NAD83(CSRS)v6 / MTM zone 11", 8252, 4400, 17711); + return true; + case 4196: + record = new EpsgProjectedCrsRecord(22652, "NAD83(CSRS)v6 / MTM zone 12", 8252, 4400, 17712); + return true; + case 4197: + record = new EpsgProjectedCrsRecord(22653, "NAD83(CSRS)v6 / MTM zone 13", 8252, 4400, 17713); + return true; + case 4198: + record = new EpsgProjectedCrsRecord(22654, "NAD83(CSRS)v6 / MTM zone 14", 8252, 4400, 17714); + return true; + case 4199: + record = new EpsgProjectedCrsRecord(22655, "NAD83(CSRS)v6 / MTM zone 15", 8252, 4400, 17715); + return true; + case 4200: + record = new EpsgProjectedCrsRecord(22656, "NAD83(CSRS)v6 / MTM zone 16", 8252, 4400, 17716); + return true; + case 4201: + record = new EpsgProjectedCrsRecord(22657, "NAD83(CSRS)v6 / MTM zone 17", 8252, 4400, 17717); + return true; + case 4202: + record = new EpsgProjectedCrsRecord(22700, "Deir ez Zor / Levant Zone", 4227, 4499, 19940); + return true; + case 4203: + record = new EpsgProjectedCrsRecord(22707, "NAD83(CSRS)v7 / UTM zone 7N", 8255, 4400, 16007); + return true; + case 4204: + record = new EpsgProjectedCrsRecord(22708, "NAD83(CSRS)v7 / UTM zone 8N", 8255, 4400, 16008); + return true; + case 4205: + record = new EpsgProjectedCrsRecord(22709, "NAD83(CSRS)v7 / UTM zone 9N", 8255, 4400, 16009); + return true; + case 4206: + record = new EpsgProjectedCrsRecord(22710, "NAD83(CSRS)v7 / UTM zone 10N", 8255, 4400, 16010); + return true; + case 4207: + record = new EpsgProjectedCrsRecord(22711, "NAD83(CSRS)v7 / UTM zone 11N", 8255, 4400, 16011); + return true; + case 4208: + record = new EpsgProjectedCrsRecord(22712, "NAD83(CSRS)v7 / UTM zone 12N", 8255, 4400, 16012); + return true; + case 4209: + record = new EpsgProjectedCrsRecord(22713, "NAD83(CSRS)v7 / UTM zone 13N", 8255, 4400, 16013); + return true; + case 4210: + record = new EpsgProjectedCrsRecord(22714, "NAD83(CSRS)v7 / UTM zone 14N", 8255, 4400, 16014); + return true; + case 4211: + record = new EpsgProjectedCrsRecord(22715, "NAD83(CSRS)v7 / UTM zone 15N", 8255, 4400, 16015); + return true; + case 4212: + record = new EpsgProjectedCrsRecord(22716, "NAD83(CSRS)v7 / UTM zone 16N", 8255, 4400, 16016); + return true; + case 4213: + record = new EpsgProjectedCrsRecord(22717, "NAD83(CSRS)v7 / UTM zone 17N", 8255, 4400, 16017); + return true; + case 4214: + record = new EpsgProjectedCrsRecord(22718, "NAD83(CSRS)v7 / UTM zone 18N", 8255, 4400, 16018); + return true; + case 4215: + record = new EpsgProjectedCrsRecord(22719, "NAD83(CSRS)v7 / UTM zone 19N", 8255, 4400, 16019); + return true; + case 4216: + record = new EpsgProjectedCrsRecord(22720, "NAD83(CSRS)v7 / UTM zone 20N", 8255, 4400, 16020); + return true; + case 4217: + record = new EpsgProjectedCrsRecord(22721, "NAD83(CSRS)v7 / UTM zone 21N", 8255, 4400, 16021); + return true; + case 4218: + record = new EpsgProjectedCrsRecord(22722, "NAD83(CSRS)v7 / UTM zone 22N", 8255, 4400, 16022); + return true; + case 4219: + record = new EpsgProjectedCrsRecord(22739, "NAD83(CSRS)v7 / PEI Stereographic", 8255, 4496, 19960); + return true; + case 4220: + record = new EpsgProjectedCrsRecord(22762, "NAD83(CSRS)v7 / Alberta 3TM ref merid 111 W", 8255, 4400, 17722); + return true; + case 4221: + record = new EpsgProjectedCrsRecord(22763, "NAD83(CSRS)v7 / Alberta 3TM ref merid 114 W", 8255, 4400, 17723); + return true; + case 4222: + record = new EpsgProjectedCrsRecord(22764, "NAD83(CSRS)v7 / Alberta 3TM ref merid 117 W", 8255, 4400, 17724); + return true; + case 4223: + record = new EpsgProjectedCrsRecord(22765, "NAD83(CSRS)v7 / Alberta 3TM ref merid 120 W", 8255, 4400, 17726); + return true; + case 4224: + record = new EpsgProjectedCrsRecord(22770, "Deir ez Zor / Syria Lambert", 4227, 4499, 19948); + return true; + case 4225: + record = new EpsgProjectedCrsRecord(22780, "Deir ez Zor / Levant Stereographic", 4227, 4499, 19949); + return true; + case 4226: + record = new EpsgProjectedCrsRecord(22807, "NAD83(CSRS)v8 / UTM zone 7N", 10414, 4400, 16007); + return true; + case 4227: + record = new EpsgProjectedCrsRecord(22808, "NAD83(CSRS)v8 / UTM zone 8N", 10414, 4400, 16008); + return true; + case 4228: + record = new EpsgProjectedCrsRecord(22809, "NAD83(CSRS)v8 / UTM zone 9N", 10414, 4400, 16009); + return true; + case 4229: + record = new EpsgProjectedCrsRecord(22810, "NAD83(CSRS)v8 / UTM zone 10N", 10414, 4400, 16010); + return true; + case 4230: + record = new EpsgProjectedCrsRecord(22811, "NAD83(CSRS)v8 / UTM zone 11N", 10414, 4400, 16011); + return true; + case 4231: + record = new EpsgProjectedCrsRecord(22812, "NAD83(CSRS)v8 / UTM zone 12N", 10414, 4400, 16012); + return true; + case 4232: + record = new EpsgProjectedCrsRecord(22813, "NAD83(CSRS)v8 / UTM zone 13N", 10414, 4400, 16013); + return true; + case 4233: + record = new EpsgProjectedCrsRecord(22814, "NAD83(CSRS)v8 / UTM zone 14N", 10414, 4400, 16014); + return true; + case 4234: + record = new EpsgProjectedCrsRecord(22815, "NAD83(CSRS)v8 / UTM zone 15N", 10414, 4400, 16015); + return true; + case 4235: + record = new EpsgProjectedCrsRecord(22816, "NAD83(CSRS)v8 / UTM zone 16N", 10414, 4400, 16016); + return true; + case 4236: + record = new EpsgProjectedCrsRecord(22817, "NAD83(CSRS)v8 / UTM zone 17N", 10414, 4400, 16017); + return true; + case 4237: + record = new EpsgProjectedCrsRecord(22818, "NAD83(CSRS)v8 / UTM zone 18N", 10414, 4400, 16018); + return true; + case 4238: + record = new EpsgProjectedCrsRecord(22819, "NAD83(CSRS)v8 / UTM zone 19N", 10414, 4400, 16019); + return true; + case 4239: + record = new EpsgProjectedCrsRecord(22820, "NAD83(CSRS)v8 / UTM zone 20N", 10414, 4400, 16020); + return true; + case 4240: + record = new EpsgProjectedCrsRecord(22821, "NAD83(CSRS)v8 / UTM zone 21N", 10414, 4400, 16021); + return true; + case 4241: + record = new EpsgProjectedCrsRecord(22822, "NAD83(CSRS)v8 / UTM zone 22N", 10414, 4400, 16022); + return true; + case 4242: + record = new EpsgProjectedCrsRecord(22991, "Egypt 1907 / Blue Belt", 4229, 4400, 18071); + return true; + case 4243: + record = new EpsgProjectedCrsRecord(22992, "Egypt 1907 / Red Belt", 4229, 4400, 18072); + return true; + case 4244: + record = new EpsgProjectedCrsRecord(22993, "Egypt 1907 / Purple Belt", 4229, 4400, 18073); + return true; + case 4245: + record = new EpsgProjectedCrsRecord(22994, "Egypt 1907 / Extended Purple Belt", 4229, 4400, 18074); + return true; + case 4246: + record = new EpsgProjectedCrsRecord(23028, "ED50 / UTM zone 28N", 4230, 4400, 16028); + return true; + case 4247: + record = new EpsgProjectedCrsRecord(23029, "ED50 / UTM zone 29N", 4230, 4400, 16029); + return true; + case 4248: + record = new EpsgProjectedCrsRecord(23030, "ED50 / UTM zone 30N", 4230, 4400, 16030); + return true; + case 4249: + record = new EpsgProjectedCrsRecord(23031, "ED50 / UTM zone 31N", 4230, 4400, 16031); + return true; + case 4250: + record = new EpsgProjectedCrsRecord(23032, "ED50 / UTM zone 32N", 4230, 4400, 16032); + return true; + case 4251: + record = new EpsgProjectedCrsRecord(23033, "ED50 / UTM zone 33N", 4230, 4400, 16033); + return true; + case 4252: + record = new EpsgProjectedCrsRecord(23034, "ED50 / UTM zone 34N", 4230, 4400, 16034); + return true; + case 4253: + record = new EpsgProjectedCrsRecord(23035, "ED50 / UTM zone 35N", 4230, 4400, 16035); + return true; + case 4254: + record = new EpsgProjectedCrsRecord(23036, "ED50 / UTM zone 36N", 4230, 4400, 16036); + return true; + case 4255: + record = new EpsgProjectedCrsRecord(23037, "ED50 / UTM zone 37N", 4230, 4400, 16037); + return true; + case 4256: + record = new EpsgProjectedCrsRecord(23038, "ED50 / UTM zone 38N", 4230, 4400, 16038); + return true; + case 4257: + record = new EpsgProjectedCrsRecord(23090, "ED50 / TM 0 N", 4230, 4400, 16400); + return true; + case 4258: + record = new EpsgProjectedCrsRecord(23095, "ED50 / TM 5 NE", 4230, 4400, 16405); + return true; + case 4259: + record = new EpsgProjectedCrsRecord(23239, "Fahud / UTM zone 39N", 4232, 4400, 16039); + return true; + case 4260: + record = new EpsgProjectedCrsRecord(23240, "Fahud / UTM zone 40N", 4232, 4400, 16040); + return true; + case 4261: + record = new EpsgProjectedCrsRecord(23301, "NAD83(2011) / ICS83-Freeport (ftUS)", 6318, 1053, 11264); + return true; + case 4262: + record = new EpsgProjectedCrsRecord(23302, "NAD83(2011) / ICS83-Rockford (ftUS)", 6318, 1053, 11265); + return true; + case 4263: + record = new EpsgProjectedCrsRecord(23303, "NAD83(2011) / ICS83-Aurora (ftUS)", 6318, 1053, 11233); + return true; + case 4264: + record = new EpsgProjectedCrsRecord(23304, "NAD83(2011) / ICS83-Chicago (ftUS)", 6318, 1053, 11234); + return true; + case 4265: + record = new EpsgProjectedCrsRecord(23305, "NAD83(2011) / ICS83-Moline (ftUS)", 6318, 1053, 11235); + return true; + case 4266: + record = new EpsgProjectedCrsRecord(23306, "NAD83(2011) / ICS83-Sterling (ftUS)", 6318, 1053, 11236); + return true; + case 4267: + record = new EpsgProjectedCrsRecord(23307, "NAD83(2011) / ICS83-Ottawa (ftUS)", 6318, 1053, 11237); + return true; + case 4268: + record = new EpsgProjectedCrsRecord(23308, "NAD83(2011) / ICS83-Joliet (ftUS)", 6318, 1053, 11238); + return true; + case 4269: + record = new EpsgProjectedCrsRecord(23309, "NAD83(2011) / ICS83-Monmouth (ftUS)", 6318, 1053, 11239); + return true; + case 4270: + record = new EpsgProjectedCrsRecord(23310, "NAD83(2011) / ICS83-Galesburg (ftUS)", 6318, 1053, 11240); + return true; + case 4271: + record = new EpsgProjectedCrsRecord(23311, "NAD83(2011) / ICS83-Peoria (ftUS)", 6318, 1053, 11241); + return true; + case 4272: + record = new EpsgProjectedCrsRecord(23312, "NAD83(2011) / ICS83-Eureka (ftUS)", 6318, 1053, 11242); + return true; + case 4273: + record = new EpsgProjectedCrsRecord(23313, "NAD83(2011) / ICS83-Bloomington (ftUS)", 6318, 1053, 11243); + return true; + case 4274: + record = new EpsgProjectedCrsRecord(23314, "NAD83(2011) / ICS83-Pontiac (ftUS)", 6318, 1053, 11244); + return true; + case 4275: + record = new EpsgProjectedCrsRecord(23315, "NAD83(2011) / ICS83-Watseka (ftUS)", 6318, 1053, 11245); + return true; + case 4276: + record = new EpsgProjectedCrsRecord(23316, "NAD83(2011) / ICS83-Quincy (ftUS)", 6318, 1053, 11246); + return true; + case 4277: + record = new EpsgProjectedCrsRecord(23317, "NAD83(2011) / ICS83-Macomb (ftUS)", 6318, 1053, 11247); + return true; + case 4278: + record = new EpsgProjectedCrsRecord(23318, "NAD83(2011) / ICS83-Lincoln (ftUS)", 6318, 1053, 11248); + return true; + case 4279: + record = new EpsgProjectedCrsRecord(23319, "NAD83(2011) / ICS83-Decatur (ftUS)", 6318, 1053, 11249); + return true; + case 4280: + record = new EpsgProjectedCrsRecord(23320, "NAD83(2011) / ICS83-Champaign (ftUS)", 6318, 1053, 11250); + return true; + case 4281: + record = new EpsgProjectedCrsRecord(23321, "NAD83(2011) / ICS83-Jacksonville (ftUS)", 6318, 1053, 11251); + return true; + case 4282: + record = new EpsgProjectedCrsRecord(23322, "NAD83(2011) / ICS83-Springfield (ftUS)", 6318, 1053, 11252); + return true; + case 4283: + record = new EpsgProjectedCrsRecord(23323, "NAD83(2011) / ICS83-Charleston (ftUS)", 6318, 1053, 11253); + return true; + case 4284: + record = new EpsgProjectedCrsRecord(23324, "NAD83(2011) / ICS83-Jerseyville (ftUS)", 6318, 1053, 11254); + return true; + case 4285: + record = new EpsgProjectedCrsRecord(23325, "NAD83(2011) / ICS83-Carlinville (ftUS)", 6318, 1053, 11255); + return true; + case 4286: + record = new EpsgProjectedCrsRecord(23326, "NAD83(2011) / ICS83-Taylorville (ftUS)", 6318, 1053, 11256); + return true; + case 4287: + record = new EpsgProjectedCrsRecord(23327, "NAD83(2011) / ICS83-Effingham (ftUS)", 6318, 1053, 11257); + return true; + case 4288: + record = new EpsgProjectedCrsRecord(23328, "NAD83(2011) / ICS83-Robinson (ftUS)", 6318, 1053, 11258); + return true; + case 4289: + record = new EpsgProjectedCrsRecord(23329, "NAD83(2011) / ICS83-Belleville (ftUS)", 6318, 1053, 11259); + return true; + case 4290: + record = new EpsgProjectedCrsRecord(23330, "NAD83(2011) / ICS83-Mount Vernon (ftUS)", 6318, 1053, 11260); + return true; + case 4291: + record = new EpsgProjectedCrsRecord(23331, "NAD83(2011) / ICS83-Olney (ftUS)", 6318, 1053, 11261); + return true; + case 4292: + record = new EpsgProjectedCrsRecord(23332, "NAD83(2011) / ICS83-Carbondale (ftUS)", 6318, 1053, 11262); + return true; + case 4293: + record = new EpsgProjectedCrsRecord(23333, "NAD83(2011) / ICS83-Metropolis (ftUS)", 6318, 1053, 11263); + return true; + case 4294: + record = new EpsgProjectedCrsRecord(23700, "HD72 / EOV", 4237, 4498, 19931); + return true; + case 4295: + record = new EpsgProjectedCrsRecord(23830, "DGN95 / Indonesia TM-3 zone 46.2", 4755, 4499, 17432); + return true; + case 4296: + record = new EpsgProjectedCrsRecord(23831, "DGN95 / Indonesia TM-3 zone 47.1", 4755, 4499, 17433); + return true; + case 4297: + record = new EpsgProjectedCrsRecord(23832, "DGN95 / Indonesia TM-3 zone 47.2", 4755, 4499, 17434); + return true; + case 4298: + record = new EpsgProjectedCrsRecord(23833, "DGN95 / Indonesia TM-3 zone 48.1", 4755, 4499, 17435); + return true; + case 4299: + record = new EpsgProjectedCrsRecord(23834, "DGN95 / Indonesia TM-3 zone 48.2", 4755, 4499, 17436); + return true; + case 4300: + record = new EpsgProjectedCrsRecord(23835, "DGN95 / Indonesia TM-3 zone 49.1", 4755, 4499, 17437); + return true; + case 4301: + record = new EpsgProjectedCrsRecord(23836, "DGN95 / Indonesia TM-3 zone 49.2", 4755, 4499, 17438); + return true; + case 4302: + record = new EpsgProjectedCrsRecord(23837, "DGN95 / Indonesia TM-3 zone 50.1", 4755, 4499, 17439); + return true; + case 4303: + record = new EpsgProjectedCrsRecord(23838, "DGN95 / Indonesia TM-3 zone 50.2", 4755, 4499, 17440); + return true; + case 4304: + record = new EpsgProjectedCrsRecord(23839, "DGN95 / Indonesia TM-3 zone 51.1", 4755, 4499, 17441); + return true; + case 4305: + record = new EpsgProjectedCrsRecord(23840, "DGN95 / Indonesia TM-3 zone 51.2", 4755, 4499, 17442); + return true; + case 4306: + record = new EpsgProjectedCrsRecord(23841, "DGN95 / Indonesia TM-3 zone 52.1", 4755, 4499, 17443); + return true; + case 4307: + record = new EpsgProjectedCrsRecord(23842, "DGN95 / Indonesia TM-3 zone 52.2", 4755, 4499, 17444); + return true; + case 4308: + record = new EpsgProjectedCrsRecord(23843, "DGN95 / Indonesia TM-3 zone 53.1", 4755, 4499, 17445); + return true; + case 4309: + record = new EpsgProjectedCrsRecord(23844, "DGN95 / Indonesia TM-3 zone 53.2", 4755, 4499, 17446); + return true; + case 4310: + record = new EpsgProjectedCrsRecord(23845, "DGN95 / Indonesia TM-3 zone 54.1", 4755, 4499, 17447); + return true; + case 4311: + record = new EpsgProjectedCrsRecord(23846, "ID74 / UTM zone 46N", 4238, 4400, 16046); + return true; + case 4312: + record = new EpsgProjectedCrsRecord(23847, "ID74 / UTM zone 47N", 4238, 4400, 16047); + return true; + case 4313: + record = new EpsgProjectedCrsRecord(23848, "ID74 / UTM zone 48N", 4238, 4400, 16048); + return true; + case 4314: + record = new EpsgProjectedCrsRecord(23849, "ID74 / UTM zone 49N", 4238, 4400, 16049); + return true; + case 4315: + record = new EpsgProjectedCrsRecord(23850, "ID74 / UTM zone 50N", 4238, 4400, 16050); + return true; + case 4316: + record = new EpsgProjectedCrsRecord(23851, "ID74 / UTM zone 51N", 4238, 4400, 16051); + return true; + case 4317: + record = new EpsgProjectedCrsRecord(23852, "ID74 / UTM zone 52N", 4238, 4400, 16052); + return true; + case 4318: + record = new EpsgProjectedCrsRecord(23866, "DGN95 / UTM zone 46N", 4755, 4400, 16046); + return true; + case 4319: + record = new EpsgProjectedCrsRecord(23867, "DGN95 / UTM zone 47N", 4755, 4400, 16047); + return true; + case 4320: + record = new EpsgProjectedCrsRecord(23868, "DGN95 / UTM zone 48N", 4755, 4400, 16048); + return true; + case 4321: + record = new EpsgProjectedCrsRecord(23869, "DGN95 / UTM zone 49N", 4755, 4400, 16049); + return true; + case 4322: + record = new EpsgProjectedCrsRecord(23870, "DGN95 / UTM zone 50N", 4755, 4400, 16050); + return true; + case 4323: + record = new EpsgProjectedCrsRecord(23871, "DGN95 / UTM zone 51N", 4755, 4400, 16051); + return true; + case 4324: + record = new EpsgProjectedCrsRecord(23872, "DGN95 / UTM zone 52N", 4755, 4400, 16052); + return true; + case 4325: + record = new EpsgProjectedCrsRecord(23877, "DGN95 / UTM zone 47S", 4755, 4400, 16147); + return true; + case 4326: + record = new EpsgProjectedCrsRecord(23878, "DGN95 / UTM zone 48S", 4755, 4400, 16148); + return true; + case 4327: + record = new EpsgProjectedCrsRecord(23879, "DGN95 / UTM zone 49S", 4755, 4400, 16149); + return true; + case 4328: + record = new EpsgProjectedCrsRecord(23880, "DGN95 / UTM zone 50S", 4755, 4400, 16150); + return true; + case 4329: + record = new EpsgProjectedCrsRecord(23881, "DGN95 / UTM zone 51S", 4755, 4400, 16151); + return true; + case 4330: + record = new EpsgProjectedCrsRecord(23882, "DGN95 / UTM zone 52S", 4755, 4400, 16152); + return true; + case 4331: + record = new EpsgProjectedCrsRecord(23883, "DGN95 / UTM zone 53S", 4755, 4400, 16153); + return true; + case 4332: + record = new EpsgProjectedCrsRecord(23884, "DGN95 / UTM zone 54S", 4755, 4400, 16154); + return true; + case 4333: + record = new EpsgProjectedCrsRecord(23887, "ID74 / UTM zone 47S", 4238, 4400, 16147); + return true; + case 4334: + record = new EpsgProjectedCrsRecord(23888, "ID74 / UTM zone 48S", 4238, 4400, 16148); + return true; + case 4335: + record = new EpsgProjectedCrsRecord(23889, "ID74 / UTM zone 49S", 4238, 4400, 16149); + return true; + case 4336: + record = new EpsgProjectedCrsRecord(23890, "ID74 / UTM zone 50S", 4238, 4400, 16150); + return true; + case 4337: + record = new EpsgProjectedCrsRecord(23891, "ID74 / UTM zone 51S", 4238, 4400, 16151); + return true; + case 4338: + record = new EpsgProjectedCrsRecord(23892, "ID74 / UTM zone 52S", 4238, 4400, 16152); + return true; + case 4339: + record = new EpsgProjectedCrsRecord(23893, "ID74 / UTM zone 53S", 4238, 4400, 16153); + return true; + case 4340: + record = new EpsgProjectedCrsRecord(23894, "ID74 / UTM zone 54S", 4238, 4400, 16154); + return true; + case 4341: + record = new EpsgProjectedCrsRecord(23946, "Indian 1954 / UTM zone 46N", 4239, 4400, 16046); + return true; + case 4342: + record = new EpsgProjectedCrsRecord(23947, "Indian 1954 / UTM zone 47N", 4239, 4400, 16047); + return true; + case 4343: + record = new EpsgProjectedCrsRecord(23948, "Indian 1954 / UTM zone 48N", 4239, 4400, 16048); + return true; + case 4344: + record = new EpsgProjectedCrsRecord(24047, "Indian 1975 / UTM zone 47N", 4240, 4400, 16047); + return true; + case 4345: + record = new EpsgProjectedCrsRecord(24048, "Indian 1975 / UTM zone 48N", 4240, 4400, 16048); + return true; + case 4346: + record = new EpsgProjectedCrsRecord(24100, "Jamaica 1875 / Jamaica (Old Grid)", 4241, 4403, 19909); + return true; + case 4347: + record = new EpsgProjectedCrsRecord(24200, "JAD69 / Jamaica National Grid", 4242, 4400, 19910); + return true; + case 4348: + record = new EpsgProjectedCrsRecord(24305, "Kalianpur 1937 / UTM zone 45N", 4144, 4400, 16045); + return true; + case 4349: + record = new EpsgProjectedCrsRecord(24306, "Kalianpur 1937 / UTM zone 46N", 4144, 4400, 16046); + return true; + case 4350: + record = new EpsgProjectedCrsRecord(24311, "Kalianpur 1962 / UTM zone 41N", 4145, 4400, 16041); + return true; + case 4351: + record = new EpsgProjectedCrsRecord(24312, "Kalianpur 1962 / UTM zone 42N", 4145, 4400, 16042); + return true; + case 4352: + record = new EpsgProjectedCrsRecord(24313, "Kalianpur 1962 / UTM zone 43N", 4145, 4400, 16043); + return true; + case 4353: + record = new EpsgProjectedCrsRecord(24342, "Kalianpur 1975 / UTM zone 42N", 4146, 4400, 16042); + return true; + case 4354: + record = new EpsgProjectedCrsRecord(24343, "Kalianpur 1975 / UTM zone 43N", 4146, 4400, 16043); + return true; + case 4355: + record = new EpsgProjectedCrsRecord(24344, "Kalianpur 1975 / UTM zone 44N", 4146, 4400, 16044); + return true; + case 4356: + record = new EpsgProjectedCrsRecord(24345, "Kalianpur 1975 / UTM zone 45N", 4146, 4400, 16045); + return true; + case 4357: + record = new EpsgProjectedCrsRecord(24346, "Kalianpur 1975 / UTM zone 46N", 4146, 4400, 16046); + return true; + case 4358: + record = new EpsgProjectedCrsRecord(24347, "Kalianpur 1975 / UTM zone 47N", 4146, 4400, 16047); + return true; + case 4359: + record = new EpsgProjectedCrsRecord(24370, "Kalianpur 1880 / India zone 0", 4243, 4408, 18110); + return true; + case 4360: + record = new EpsgProjectedCrsRecord(24371, "Kalianpur 1880 / India zone I", 4243, 4408, 18111); + return true; + case 4361: + record = new EpsgProjectedCrsRecord(24372, "Kalianpur 1880 / India zone IIa", 4243, 4408, 18112); + return true; + case 4362: + record = new EpsgProjectedCrsRecord(24373, "Kalianpur 1880 / India zone IIIa", 4243, 4408, 18114); + return true; + case 4363: + record = new EpsgProjectedCrsRecord(24374, "Kalianpur 1880 / India zone IVa", 4243, 4408, 18116); + return true; + case 4364: + record = new EpsgProjectedCrsRecord(24375, "Kalianpur 1937 / India zone IIb", 4144, 4400, 18238); + return true; + case 4365: + record = new EpsgProjectedCrsRecord(24376, "Kalianpur 1962 / India zone I", 4145, 4400, 18236); + return true; + case 4366: + record = new EpsgProjectedCrsRecord(24377, "Kalianpur 1962 / India zone IIa", 4145, 4400, 18237); + return true; + case 4367: + record = new EpsgProjectedCrsRecord(24378, "Kalianpur 1975 / India zone I", 4146, 4400, 18231); + return true; + case 4368: + record = new EpsgProjectedCrsRecord(24379, "Kalianpur 1975 / India zone IIa", 4146, 4400, 18232); + return true; + case 4369: + record = new EpsgProjectedCrsRecord(24380, "Kalianpur 1975 / India zone IIb", 4146, 4400, 18235); + return true; + case 4370: + record = new EpsgProjectedCrsRecord(24381, "Kalianpur 1975 / India zone IIIa", 4146, 4400, 18233); + return true; + case 4371: + record = new EpsgProjectedCrsRecord(24382, "Kalianpur 1880 / India zone IIb", 4243, 4408, 18113); + return true; + case 4372: + record = new EpsgProjectedCrsRecord(24383, "Kalianpur 1975 / India zone IVa", 4146, 4400, 18234); + return true; + case 4373: + record = new EpsgProjectedCrsRecord(24500, "Kertau 1968 / Singapore Grid", 4245, 4400, 19920); + return true; + case 4374: + record = new EpsgProjectedCrsRecord(24547, "Kertau 1968 / UTM zone 47N", 4245, 4400, 16047); + return true; + case 4375: + record = new EpsgProjectedCrsRecord(24548, "Kertau 1968 / UTM zone 48N", 4245, 4400, 16048); + return true; + case 4376: + record = new EpsgProjectedCrsRecord(24600, "KOC Lambert", 4246, 4400, 19906); + return true; + case 4377: + record = new EpsgProjectedCrsRecord(24718, "La Canoa / UTM zone 18N", 4247, 4400, 16018); + return true; + case 4378: + record = new EpsgProjectedCrsRecord(24719, "La Canoa / UTM zone 19N", 4247, 4400, 16019); + return true; + case 4379: + record = new EpsgProjectedCrsRecord(24720, "La Canoa / UTM zone 20N", 4247, 4400, 16020); + return true; + case 4380: + record = new EpsgProjectedCrsRecord(24817, "PSAD56 / UTM zone 17N", 4248, 4400, 16017); + return true; + case 4381: + record = new EpsgProjectedCrsRecord(24818, "PSAD56 / UTM zone 18N", 4248, 4400, 16018); + return true; + case 4382: + record = new EpsgProjectedCrsRecord(24819, "PSAD56 / UTM zone 19N", 4248, 4400, 16019); + return true; + case 4383: + record = new EpsgProjectedCrsRecord(24820, "PSAD56 / UTM zone 20N", 4248, 4400, 16020); + return true; + case 4384: + record = new EpsgProjectedCrsRecord(24821, "PSAD56 / UTM zone 21N", 4248, 4400, 16021); + return true; + case 4385: + record = new EpsgProjectedCrsRecord(24877, "PSAD56 / UTM zone 17S", 4248, 4400, 16117); + return true; + case 4386: + record = new EpsgProjectedCrsRecord(24878, "PSAD56 / UTM zone 18S", 4248, 4400, 16118); + return true; + case 4387: + record = new EpsgProjectedCrsRecord(24879, "PSAD56 / UTM zone 19S", 4248, 4400, 16119); + return true; + case 4388: + record = new EpsgProjectedCrsRecord(24880, "PSAD56 / UTM zone 20S", 4248, 4400, 16120); + return true; + case 4389: + record = new EpsgProjectedCrsRecord(24881, "PSAD56 / UTM zone 21S", 4248, 4400, 16121); + return true; + case 4390: + record = new EpsgProjectedCrsRecord(24882, "PSAD56 / UTM zone 22S", 4248, 4400, 16122); + return true; + case 4391: + record = new EpsgProjectedCrsRecord(24891, "PSAD56 / Peru west zone", 4248, 4499, 18161); + return true; + case 4392: + record = new EpsgProjectedCrsRecord(24892, "PSAD56 / Peru central zone", 4248, 4499, 18162); + return true; + case 4393: + record = new EpsgProjectedCrsRecord(24893, "PSAD56 / Peru east zone", 4248, 4499, 18163); + return true; + case 4394: + record = new EpsgProjectedCrsRecord(25000, "Leigon / Ghana Metre Grid", 4250, 4400, 19904); + return true; + case 4395: + record = new EpsgProjectedCrsRecord(25231, "Lome / UTM zone 31N", 4252, 4400, 16031); + return true; + case 4396: + record = new EpsgProjectedCrsRecord(25391, "Luzon 1911 / Philippines zone I", 4253, 4499, 18171); + return true; + case 4397: + record = new EpsgProjectedCrsRecord(25392, "Luzon 1911 / Philippines zone II", 4253, 4499, 18172); + return true; + case 4398: + record = new EpsgProjectedCrsRecord(25393, "Luzon 1911 / Philippines zone III", 4253, 4499, 18173); + return true; + case 4399: + record = new EpsgProjectedCrsRecord(25394, "Luzon 1911 / Philippines zone IV", 4253, 4499, 18174); + return true; + case 4400: + record = new EpsgProjectedCrsRecord(25395, "Luzon 1911 / Philippines zone V", 4253, 4499, 18175); + return true; + case 4401: + record = new EpsgProjectedCrsRecord(25828, "ETRS89 / UTM zone 28N", 4258, 4400, 16028); + return true; + case 4402: + record = new EpsgProjectedCrsRecord(25829, "ETRS89 / UTM zone 29N", 4258, 4400, 16029); + return true; + case 4403: + record = new EpsgProjectedCrsRecord(25830, "ETRS89 / UTM zone 30N", 4258, 4400, 16030); + return true; + case 4404: + record = new EpsgProjectedCrsRecord(25831, "ETRS89 / UTM zone 31N", 4258, 4400, 16031); + return true; + case 4405: + record = new EpsgProjectedCrsRecord(25832, "ETRS89 / UTM zone 32N", 4258, 4400, 16032); + return true; + case 4406: + record = new EpsgProjectedCrsRecord(25833, "ETRS89 / UTM zone 33N", 4258, 4400, 16033); + return true; + case 4407: + record = new EpsgProjectedCrsRecord(25834, "ETRS89 / UTM zone 34N", 4258, 4400, 16034); + return true; + case 4408: + record = new EpsgProjectedCrsRecord(25835, "ETRS89 / UTM zone 35N", 4258, 4400, 16035); + return true; + case 4409: + record = new EpsgProjectedCrsRecord(25836, "ETRS89 / UTM zone 36N", 4258, 4400, 16036); + return true; + case 4410: + record = new EpsgProjectedCrsRecord(25837, "ETRS89 / UTM zone 37N", 4258, 4400, 16037); + return true; + case 4411: + record = new EpsgProjectedCrsRecord(25884, "ETRS89 / TM Baltic93", 4258, 4530, 19939); + return true; + case 4412: + record = new EpsgProjectedCrsRecord(25932, "Malongo 1987 / UTM zone 32S", 4259, 4400, 16132); + return true; + case 4413: + record = new EpsgProjectedCrsRecord(26191, "Merchich / Nord Maroc", 4261, 4499, 18131); + return true; + case 4414: + record = new EpsgProjectedCrsRecord(26192, "Merchich / Sud Maroc", 4261, 4499, 18132); + return true; + case 4415: + record = new EpsgProjectedCrsRecord(26194, "Merchich / Sahara Nord", 4261, 4499, 18134); + return true; + case 4416: + record = new EpsgProjectedCrsRecord(26195, "Merchich / Sahara Sud", 4261, 4499, 18135); + return true; + case 4417: + record = new EpsgProjectedCrsRecord(26237, "Massawa / UTM zone 37N", 4262, 4400, 16037); + return true; + case 4418: + record = new EpsgProjectedCrsRecord(26331, "Minna / UTM zone 31N", 4263, 4400, 16031); + return true; + case 4419: + record = new EpsgProjectedCrsRecord(26332, "Minna / UTM zone 32N", 4263, 4400, 16032); + return true; + case 4420: + record = new EpsgProjectedCrsRecord(26391, "Minna / Nigeria West Belt", 4263, 4400, 18151); + return true; + case 4421: + record = new EpsgProjectedCrsRecord(26392, "Minna / Nigeria Mid Belt", 4263, 4400, 18152); + return true; + case 4422: + record = new EpsgProjectedCrsRecord(26393, "Minna / Nigeria East Belt", 4263, 4400, 18153); + return true; + case 4423: + record = new EpsgProjectedCrsRecord(26632, "M'poraloko / UTM zone 32N", 4266, 4400, 16032); + return true; + case 4424: + record = new EpsgProjectedCrsRecord(26692, "M'poraloko / UTM zone 32S", 4266, 4400, 16132); + return true; + case 4425: + record = new EpsgProjectedCrsRecord(26701, "NAD27 / UTM zone 1N", 4267, 4400, 16001); + return true; + case 4426: + record = new EpsgProjectedCrsRecord(26702, "NAD27 / UTM zone 2N", 4267, 4400, 16002); + return true; + case 4427: + record = new EpsgProjectedCrsRecord(26703, "NAD27 / UTM zone 3N", 4267, 4400, 16003); + return true; + case 4428: + record = new EpsgProjectedCrsRecord(26704, "NAD27 / UTM zone 4N", 4267, 4400, 16004); + return true; + case 4429: + record = new EpsgProjectedCrsRecord(26705, "NAD27 / UTM zone 5N", 4267, 4400, 16005); + return true; + case 4430: + record = new EpsgProjectedCrsRecord(26706, "NAD27 / UTM zone 6N", 4267, 4400, 16006); + return true; + case 4431: + record = new EpsgProjectedCrsRecord(26707, "NAD27 / UTM zone 7N", 4267, 4400, 16007); + return true; + case 4432: + record = new EpsgProjectedCrsRecord(26708, "NAD27 / UTM zone 8N", 4267, 4400, 16008); + return true; + case 4433: + record = new EpsgProjectedCrsRecord(26709, "NAD27 / UTM zone 9N", 4267, 4400, 16009); + return true; + case 4434: + record = new EpsgProjectedCrsRecord(26710, "NAD27 / UTM zone 10N", 4267, 4400, 16010); + return true; + case 4435: + record = new EpsgProjectedCrsRecord(26711, "NAD27 / UTM zone 11N", 4267, 4400, 16011); + return true; + case 4436: + record = new EpsgProjectedCrsRecord(26712, "NAD27 / UTM zone 12N", 4267, 4400, 16012); + return true; + case 4437: + record = new EpsgProjectedCrsRecord(26713, "NAD27 / UTM zone 13N", 4267, 4400, 16013); + return true; + case 4438: + record = new EpsgProjectedCrsRecord(26714, "NAD27 / UTM zone 14N", 4267, 4400, 16014); + return true; + case 4439: + record = new EpsgProjectedCrsRecord(26715, "NAD27 / UTM zone 15N", 4267, 4400, 16015); + return true; + case 4440: + record = new EpsgProjectedCrsRecord(26716, "NAD27 / UTM zone 16N", 4267, 4400, 16016); + return true; + case 4441: + record = new EpsgProjectedCrsRecord(26717, "NAD27 / UTM zone 17N", 4267, 4400, 16017); + return true; + case 4442: + record = new EpsgProjectedCrsRecord(26718, "NAD27 / UTM zone 18N", 4267, 4400, 16018); + return true; + case 4443: + record = new EpsgProjectedCrsRecord(26719, "NAD27 / UTM zone 19N", 4267, 4400, 16019); + return true; + case 4444: + record = new EpsgProjectedCrsRecord(26720, "NAD27 / UTM zone 20N", 4267, 4400, 16020); + return true; + case 4445: + record = new EpsgProjectedCrsRecord(26721, "NAD27 / UTM zone 21N", 4267, 4400, 16021); + return true; + case 4446: + record = new EpsgProjectedCrsRecord(26722, "NAD27 / UTM zone 22N", 4267, 4400, 16022); + return true; + case 4447: + record = new EpsgProjectedCrsRecord(26729, "NAD27 / Alabama East", 4267, 4497, 10101); + return true; + case 4448: + record = new EpsgProjectedCrsRecord(26730, "NAD27 / Alabama West", 4267, 4497, 10102); + return true; + case 4449: + record = new EpsgProjectedCrsRecord(26731, "NAD27 / Alaska zone 1", 4267, 4497, 15001); + return true; + case 4450: + record = new EpsgProjectedCrsRecord(26732, "NAD27 / Alaska zone 2", 4267, 4497, 15002); + return true; + case 4451: + record = new EpsgProjectedCrsRecord(26733, "NAD27 / Alaska zone 3", 4267, 4497, 15003); + return true; + case 4452: + record = new EpsgProjectedCrsRecord(26734, "NAD27 / Alaska zone 4", 4267, 4497, 15004); + return true; + case 4453: + record = new EpsgProjectedCrsRecord(26735, "NAD27 / Alaska zone 5", 4267, 4497, 15005); + return true; + case 4454: + record = new EpsgProjectedCrsRecord(26736, "NAD27 / Alaska zone 6", 4267, 4497, 15006); + return true; + case 4455: + record = new EpsgProjectedCrsRecord(26737, "NAD27 / Alaska zone 7", 4267, 4497, 15007); + return true; + case 4456: + record = new EpsgProjectedCrsRecord(26738, "NAD27 / Alaska zone 8", 4267, 4497, 15008); + return true; + case 4457: + record = new EpsgProjectedCrsRecord(26739, "NAD27 / Alaska zone 9", 4267, 4497, 15009); + return true; + case 4458: + record = new EpsgProjectedCrsRecord(26740, "NAD27 / Alaska zone 10", 4267, 4497, 15010); + return true; + case 4459: + record = new EpsgProjectedCrsRecord(26741, "NAD27 / California zone I", 4267, 4497, 10401); + return true; + case 4460: + record = new EpsgProjectedCrsRecord(26742, "NAD27 / California zone II", 4267, 4497, 10402); + return true; + case 4461: + record = new EpsgProjectedCrsRecord(26743, "NAD27 / California zone III", 4267, 4497, 10403); + return true; + case 4462: + record = new EpsgProjectedCrsRecord(26744, "NAD27 / California zone IV", 4267, 4497, 10404); + return true; + case 4463: + record = new EpsgProjectedCrsRecord(26745, "NAD27 / California zone V", 4267, 4497, 10405); + return true; + case 4464: + record = new EpsgProjectedCrsRecord(26746, "NAD27 / California zone VI", 4267, 4497, 10406); + return true; + case 4465: + record = new EpsgProjectedCrsRecord(26748, "NAD27 / Arizona East", 4267, 4497, 10201); + return true; + case 4466: + record = new EpsgProjectedCrsRecord(26749, "NAD27 / Arizona Central", 4267, 4497, 10202); + return true; + case 4467: + record = new EpsgProjectedCrsRecord(26750, "NAD27 / Arizona West", 4267, 4497, 10203); + return true; + case 4468: + record = new EpsgProjectedCrsRecord(26751, "NAD27 / Arkansas North", 4267, 4497, 10301); + return true; + case 4469: + record = new EpsgProjectedCrsRecord(26752, "NAD27 / Arkansas South", 4267, 4497, 10302); + return true; + case 4470: + record = new EpsgProjectedCrsRecord(26753, "NAD27 / Colorado North", 4267, 4497, 10501); + return true; + case 4471: + record = new EpsgProjectedCrsRecord(26754, "NAD27 / Colorado Central", 4267, 4497, 10502); + return true; + case 4472: + record = new EpsgProjectedCrsRecord(26755, "NAD27 / Colorado South", 4267, 4497, 10503); + return true; + case 4473: + record = new EpsgProjectedCrsRecord(26756, "NAD27 / Connecticut", 4267, 4497, 10600); + return true; + case 4474: + record = new EpsgProjectedCrsRecord(26757, "NAD27 / Delaware", 4267, 4497, 10700); + return true; + case 4475: + record = new EpsgProjectedCrsRecord(26758, "NAD27 / Florida East", 4267, 4497, 10901); + return true; + case 4476: + record = new EpsgProjectedCrsRecord(26759, "NAD27 / Florida West", 4267, 4497, 10902); + return true; + case 4477: + record = new EpsgProjectedCrsRecord(26760, "NAD27 / Florida North", 4267, 4497, 10903); + return true; + case 4478: + record = new EpsgProjectedCrsRecord(26766, "NAD27 / Georgia East", 4267, 4497, 11001); + return true; + case 4479: + record = new EpsgProjectedCrsRecord(26767, "NAD27 / Georgia West", 4267, 4497, 11002); + return true; + case 4480: + record = new EpsgProjectedCrsRecord(26768, "NAD27 / Idaho East", 4267, 4497, 11101); + return true; + case 4481: + record = new EpsgProjectedCrsRecord(26769, "NAD27 / Idaho Central", 4267, 4497, 11102); + return true; + case 4482: + record = new EpsgProjectedCrsRecord(26770, "NAD27 / Idaho West", 4267, 4497, 11103); + return true; + case 4483: + record = new EpsgProjectedCrsRecord(26771, "NAD27 / Illinois East", 4267, 4497, 11201); + return true; + case 4484: + record = new EpsgProjectedCrsRecord(26772, "NAD27 / Illinois West", 4267, 4497, 11202); + return true; + case 4485: + record = new EpsgProjectedCrsRecord(26773, "NAD27 / Indiana East", 4267, 4497, 11301); + return true; + case 4486: + record = new EpsgProjectedCrsRecord(26774, "NAD27 / Indiana West", 4267, 4497, 11302); + return true; + case 4487: + record = new EpsgProjectedCrsRecord(26775, "NAD27 / Iowa North", 4267, 4497, 11401); + return true; + case 4488: + record = new EpsgProjectedCrsRecord(26776, "NAD27 / Iowa South", 4267, 4497, 11402); + return true; + case 4489: + record = new EpsgProjectedCrsRecord(26777, "NAD27 / Kansas North", 4267, 4497, 11501); + return true; + case 4490: + record = new EpsgProjectedCrsRecord(26778, "NAD27 / Kansas South", 4267, 4497, 11502); + return true; + case 4491: + record = new EpsgProjectedCrsRecord(26779, "NAD27 / Kentucky North", 4267, 4497, 11601); + return true; + case 4492: + record = new EpsgProjectedCrsRecord(26780, "NAD27 / Kentucky South", 4267, 4497, 11602); + return true; + case 4493: + record = new EpsgProjectedCrsRecord(26781, "NAD27 / Louisiana North", 4267, 4497, 11701); + return true; + case 4494: + record = new EpsgProjectedCrsRecord(26782, "NAD27 / Louisiana South", 4267, 4497, 11702); + return true; + case 4495: + record = new EpsgProjectedCrsRecord(26783, "NAD27 / Maine East", 4267, 4497, 11801); + return true; + case 4496: + record = new EpsgProjectedCrsRecord(26784, "NAD27 / Maine West", 4267, 4497, 11802); + return true; + case 4497: + record = new EpsgProjectedCrsRecord(26785, "NAD27 / Maryland", 4267, 4497, 11900); + return true; + case 4498: + record = new EpsgProjectedCrsRecord(26786, "NAD27 / Massachusetts Mainland", 4267, 4497, 12001); + return true; + case 4499: + record = new EpsgProjectedCrsRecord(26787, "NAD27 / Massachusetts Island", 4267, 4497, 12002); + return true; + case 4500: + record = new EpsgProjectedCrsRecord(26791, "NAD27 / Minnesota North", 4267, 4497, 12201); + return true; + case 4501: + record = new EpsgProjectedCrsRecord(26792, "NAD27 / Minnesota Central", 4267, 4497, 12202); + return true; + case 4502: + record = new EpsgProjectedCrsRecord(26793, "NAD27 / Minnesota South", 4267, 4497, 12203); + return true; + case 4503: + record = new EpsgProjectedCrsRecord(26794, "NAD27 / Mississippi East", 4267, 4497, 12301); + return true; + case 4504: + record = new EpsgProjectedCrsRecord(26795, "NAD27 / Mississippi West", 4267, 4497, 12302); + return true; + case 4505: + record = new EpsgProjectedCrsRecord(26796, "NAD27 / Missouri East", 4267, 4497, 12401); + return true; + case 4506: + record = new EpsgProjectedCrsRecord(26797, "NAD27 / Missouri Central", 4267, 4497, 12402); + return true; + case 4507: + record = new EpsgProjectedCrsRecord(26798, "NAD27 / Missouri West", 4267, 4497, 12403); + return true; + case 4508: + record = new EpsgProjectedCrsRecord(26799, "NAD27 / California zone VII", 4267, 4497, 10408); + return true; + case 4509: + record = new EpsgProjectedCrsRecord(26847, "NAD83 / Maine East (ftUS)", 4269, 4497, 11833); + return true; + case 4510: + record = new EpsgProjectedCrsRecord(26848, "NAD83 / Maine West (ftUS)", 4269, 4497, 11834); + return true; + case 4511: + record = new EpsgProjectedCrsRecord(26849, "NAD83 / Minnesota North (ftUS)", 4269, 4497, 12234); + return true; + case 4512: + record = new EpsgProjectedCrsRecord(26850, "NAD83 / Minnesota Central (ftUS)", 4269, 4497, 12235); + return true; + case 4513: + record = new EpsgProjectedCrsRecord(26851, "NAD83 / Minnesota South (ftUS)", 4269, 4497, 12236); + return true; + case 4514: + record = new EpsgProjectedCrsRecord(26852, "NAD83 / Nebraska (ftUS)", 4269, 4497, 15396); + return true; + case 4515: + record = new EpsgProjectedCrsRecord(26853, "NAD83 / West Virginia North (ftUS)", 4269, 4497, 14735); + return true; + case 4516: + record = new EpsgProjectedCrsRecord(26854, "NAD83 / West Virginia South (ftUS)", 4269, 4497, 14736); + return true; + case 4517: + record = new EpsgProjectedCrsRecord(26855, "NAD83(HARN) / Maine East (ftUS)", 4152, 4497, 11833); + return true; + case 4518: + record = new EpsgProjectedCrsRecord(26856, "NAD83(HARN) / Maine West (ftUS)", 4152, 4497, 11834); + return true; + case 4519: + record = new EpsgProjectedCrsRecord(26857, "NAD83(HARN) / Minnesota North (ftUS)", 4152, 4497, 12234); + return true; + case 4520: + record = new EpsgProjectedCrsRecord(26858, "NAD83(HARN) / Minnesota Central (ftUS)", 4152, 4497, 12235); + return true; + case 4521: + record = new EpsgProjectedCrsRecord(26859, "NAD83(HARN) / Minnesota South (ftUS)", 4152, 4497, 12236); + return true; + case 4522: + record = new EpsgProjectedCrsRecord(26860, "NAD83(HARN) / Nebraska (ftUS)", 4152, 4497, 15396); + return true; + case 4523: + record = new EpsgProjectedCrsRecord(26861, "NAD83(HARN) / West Virginia North (ftUS)", 4152, 4497, 14735); + return true; + case 4524: + record = new EpsgProjectedCrsRecord(26862, "NAD83(HARN) / West Virginia South (ftUS)", 4152, 4497, 14736); + return true; + case 4525: + record = new EpsgProjectedCrsRecord(26863, "NAD83(NSRS2007) / Maine East (ftUS)", 4759, 4497, 11833); + return true; + case 4526: + record = new EpsgProjectedCrsRecord(26864, "NAD83(NSRS2007) / Maine West (ftUS)", 4759, 4497, 11834); + return true; + case 4527: + record = new EpsgProjectedCrsRecord(26865, "NAD83(NSRS2007) / Minnesota North (ftUS)", 4759, 4497, 12234); + return true; + case 4528: + record = new EpsgProjectedCrsRecord(26866, "NAD83(NSRS2007) / Minnesota Central (ftUS)", 4759, 4497, 12235); + return true; + case 4529: + record = new EpsgProjectedCrsRecord(26867, "NAD83(NSRS2007) / Minnesota South (ftUS)", 4759, 4497, 12236); + return true; + case 4530: + record = new EpsgProjectedCrsRecord(26868, "NAD83(NSRS2007) / Nebraska (ftUS)", 4759, 4497, 15396); + return true; + case 4531: + record = new EpsgProjectedCrsRecord(26869, "NAD83(NSRS2007) / West Virginia North (ftUS)", 4759, 4497, 14735); + return true; + case 4532: + record = new EpsgProjectedCrsRecord(26870, "NAD83(NSRS2007) / West Virginia South (ftUS)", 4759, 4497, 14736); + return true; + case 4533: + record = new EpsgProjectedCrsRecord(26891, "NAD83(CSRS) / MTM zone 11", 4617, 4400, 17711); + return true; + case 4534: + record = new EpsgProjectedCrsRecord(26892, "NAD83(CSRS) / MTM zone 12", 4617, 4400, 17712); + return true; + case 4535: + record = new EpsgProjectedCrsRecord(26893, "NAD83(CSRS) / MTM zone 13", 4617, 4400, 17713); + return true; + case 4536: + record = new EpsgProjectedCrsRecord(26894, "NAD83(CSRS) / MTM zone 14", 4617, 4400, 17714); + return true; + case 4537: + record = new EpsgProjectedCrsRecord(26895, "NAD83(CSRS) / MTM zone 15", 4617, 4400, 17715); + return true; + case 4538: + record = new EpsgProjectedCrsRecord(26896, "NAD83(CSRS) / MTM zone 16", 4617, 4400, 17716); + return true; + case 4539: + record = new EpsgProjectedCrsRecord(26897, "NAD83(CSRS) / MTM zone 17", 4617, 4400, 17717); + return true; + case 4540: + record = new EpsgProjectedCrsRecord(26898, "NAD83(CSRS) / MTM zone 1", 4617, 4496, 17701); + return true; + case 4541: + record = new EpsgProjectedCrsRecord(26899, "NAD83(CSRS) / MTM zone 2", 4617, 4496, 17702); + return true; + case 4542: + record = new EpsgProjectedCrsRecord(26901, "NAD83 / UTM zone 1N", 4269, 4400, 16001); + return true; + case 4543: + record = new EpsgProjectedCrsRecord(26902, "NAD83 / UTM zone 2N", 4269, 4400, 16002); + return true; + case 4544: + record = new EpsgProjectedCrsRecord(26903, "NAD83 / UTM zone 3N", 4269, 4400, 16003); + return true; + case 4545: + record = new EpsgProjectedCrsRecord(26904, "NAD83 / UTM zone 4N", 4269, 4400, 16004); + return true; + case 4546: + record = new EpsgProjectedCrsRecord(26905, "NAD83 / UTM zone 5N", 4269, 4400, 16005); + return true; + case 4547: + record = new EpsgProjectedCrsRecord(26906, "NAD83 / UTM zone 6N", 4269, 4400, 16006); + return true; + case 4548: + record = new EpsgProjectedCrsRecord(26907, "NAD83 / UTM zone 7N", 4269, 4400, 16007); + return true; + case 4549: + record = new EpsgProjectedCrsRecord(26908, "NAD83 / UTM zone 8N", 4269, 4400, 16008); + return true; + case 4550: + record = new EpsgProjectedCrsRecord(26909, "NAD83 / UTM zone 9N", 4269, 4400, 16009); + return true; + case 4551: + record = new EpsgProjectedCrsRecord(26910, "NAD83 / UTM zone 10N", 4269, 4400, 16010); + return true; + case 4552: + record = new EpsgProjectedCrsRecord(26911, "NAD83 / UTM zone 11N", 4269, 4400, 16011); + return true; + case 4553: + record = new EpsgProjectedCrsRecord(26912, "NAD83 / UTM zone 12N", 4269, 4400, 16012); + return true; + case 4554: + record = new EpsgProjectedCrsRecord(26913, "NAD83 / UTM zone 13N", 4269, 4400, 16013); + return true; + case 4555: + record = new EpsgProjectedCrsRecord(26914, "NAD83 / UTM zone 14N", 4269, 4400, 16014); + return true; + case 4556: + record = new EpsgProjectedCrsRecord(26915, "NAD83 / UTM zone 15N", 4269, 4400, 16015); + return true; + case 4557: + record = new EpsgProjectedCrsRecord(26916, "NAD83 / UTM zone 16N", 4269, 4400, 16016); + return true; + case 4558: + record = new EpsgProjectedCrsRecord(26917, "NAD83 / UTM zone 17N", 4269, 4400, 16017); + return true; + case 4559: + record = new EpsgProjectedCrsRecord(26918, "NAD83 / UTM zone 18N", 4269, 4400, 16018); + return true; + case 4560: + record = new EpsgProjectedCrsRecord(26919, "NAD83 / UTM zone 19N", 4269, 4400, 16019); + return true; + case 4561: + record = new EpsgProjectedCrsRecord(26920, "NAD83 / UTM zone 20N", 4269, 4400, 16020); + return true; + case 4562: + record = new EpsgProjectedCrsRecord(26921, "NAD83 / UTM zone 21N", 4269, 4400, 16021); + return true; + case 4563: + record = new EpsgProjectedCrsRecord(26922, "NAD83 / UTM zone 22N", 4269, 4400, 16022); + return true; + case 4564: + record = new EpsgProjectedCrsRecord(26923, "NAD83 / UTM zone 23N", 4269, 4400, 16023); + return true; + case 4565: + record = new EpsgProjectedCrsRecord(26929, "NAD83 / Alabama East", 4269, 4499, 10131); + return true; + case 4566: + record = new EpsgProjectedCrsRecord(26930, "NAD83 / Alabama West", 4269, 4499, 10132); + return true; + case 4567: + record = new EpsgProjectedCrsRecord(26931, "NAD83 / Alaska zone 1", 4269, 4499, 15031); + return true; + case 4568: + record = new EpsgProjectedCrsRecord(26932, "NAD83 / Alaska zone 2", 4269, 4499, 15032); + return true; + case 4569: + record = new EpsgProjectedCrsRecord(26933, "NAD83 / Alaska zone 3", 4269, 4499, 15033); + return true; + case 4570: + record = new EpsgProjectedCrsRecord(26934, "NAD83 / Alaska zone 4", 4269, 4499, 15034); + return true; + case 4571: + record = new EpsgProjectedCrsRecord(26935, "NAD83 / Alaska zone 5", 4269, 4499, 15035); + return true; + case 4572: + record = new EpsgProjectedCrsRecord(26936, "NAD83 / Alaska zone 6", 4269, 4499, 15036); + return true; + case 4573: + record = new EpsgProjectedCrsRecord(26937, "NAD83 / Alaska zone 7", 4269, 4499, 15037); + return true; + case 4574: + record = new EpsgProjectedCrsRecord(26938, "NAD83 / Alaska zone 8", 4269, 4499, 15038); + return true; + case 4575: + record = new EpsgProjectedCrsRecord(26939, "NAD83 / Alaska zone 9", 4269, 4499, 15039); + return true; + case 4576: + record = new EpsgProjectedCrsRecord(26940, "NAD83 / Alaska zone 10", 4269, 4499, 15040); + return true; + case 4577: + record = new EpsgProjectedCrsRecord(26941, "NAD83 / California zone 1", 4269, 4499, 10431); + return true; + case 4578: + record = new EpsgProjectedCrsRecord(26942, "NAD83 / California zone 2", 4269, 4499, 10432); + return true; + case 4579: + record = new EpsgProjectedCrsRecord(26943, "NAD83 / California zone 3", 4269, 4499, 10433); + return true; + case 4580: + record = new EpsgProjectedCrsRecord(26944, "NAD83 / California zone 4", 4269, 4499, 10434); + return true; + case 4581: + record = new EpsgProjectedCrsRecord(26945, "NAD83 / California zone 5", 4269, 4499, 10435); + return true; + case 4582: + record = new EpsgProjectedCrsRecord(26946, "NAD83 / California zone 6", 4269, 4499, 10436); + return true; + case 4583: + record = new EpsgProjectedCrsRecord(26948, "NAD83 / Arizona East", 4269, 4499, 10231); + return true; + case 4584: + record = new EpsgProjectedCrsRecord(26949, "NAD83 / Arizona Central", 4269, 4499, 10232); + return true; + case 4585: + record = new EpsgProjectedCrsRecord(26950, "NAD83 / Arizona West", 4269, 4499, 10233); + return true; + case 4586: + record = new EpsgProjectedCrsRecord(26951, "NAD83 / Arkansas North", 4269, 4499, 10331); + return true; + case 4587: + record = new EpsgProjectedCrsRecord(26952, "NAD83 / Arkansas South", 4269, 4499, 10332); + return true; + case 4588: + record = new EpsgProjectedCrsRecord(26953, "NAD83 / Colorado North", 4269, 4499, 10531); + return true; + case 4589: + record = new EpsgProjectedCrsRecord(26954, "NAD83 / Colorado Central", 4269, 4499, 10532); + return true; + case 4590: + record = new EpsgProjectedCrsRecord(26955, "NAD83 / Colorado South", 4269, 4499, 10533); + return true; + case 4591: + record = new EpsgProjectedCrsRecord(26956, "NAD83 / Connecticut", 4269, 4499, 10630); + return true; + case 4592: + record = new EpsgProjectedCrsRecord(26957, "NAD83 / Delaware", 4269, 4499, 10730); + return true; + case 4593: + record = new EpsgProjectedCrsRecord(26958, "NAD83 / Florida East", 4269, 4499, 10931); + return true; + case 4594: + record = new EpsgProjectedCrsRecord(26959, "NAD83 / Florida West", 4269, 4499, 10932); + return true; + case 4595: + record = new EpsgProjectedCrsRecord(26960, "NAD83 / Florida North", 4269, 4499, 10933); + return true; + case 4596: + record = new EpsgProjectedCrsRecord(26961, "NAD83 / Hawaii zone 1", 4269, 4499, 15131); + return true; + case 4597: + record = new EpsgProjectedCrsRecord(26962, "NAD83 / Hawaii zone 2", 4269, 4499, 15132); + return true; + case 4598: + record = new EpsgProjectedCrsRecord(26963, "NAD83 / Hawaii zone 3", 4269, 4499, 15133); + return true; + case 4599: + record = new EpsgProjectedCrsRecord(26964, "NAD83 / Hawaii zone 4", 4269, 4499, 15134); + return true; + case 4600: + record = new EpsgProjectedCrsRecord(26965, "NAD83 / Hawaii zone 5", 4269, 4499, 15135); + return true; + case 4601: + record = new EpsgProjectedCrsRecord(26966, "NAD83 / Georgia East", 4269, 4499, 11031); + return true; + case 4602: + record = new EpsgProjectedCrsRecord(26967, "NAD83 / Georgia West", 4269, 4499, 11032); + return true; + case 4603: + record = new EpsgProjectedCrsRecord(26968, "NAD83 / Idaho East", 4269, 4499, 11131); + return true; + case 4604: + record = new EpsgProjectedCrsRecord(26969, "NAD83 / Idaho Central", 4269, 4499, 11132); + return true; + case 4605: + record = new EpsgProjectedCrsRecord(26970, "NAD83 / Idaho West", 4269, 4499, 11133); + return true; + case 4606: + record = new EpsgProjectedCrsRecord(26971, "NAD83 / Illinois East", 4269, 4499, 11231); + return true; + case 4607: + record = new EpsgProjectedCrsRecord(26972, "NAD83 / Illinois West", 4269, 4499, 11232); + return true; + case 4608: + record = new EpsgProjectedCrsRecord(26973, "NAD83 / Indiana East", 4269, 4499, 11331); + return true; + case 4609: + record = new EpsgProjectedCrsRecord(26974, "NAD83 / Indiana West", 4269, 4499, 11332); + return true; + case 4610: + record = new EpsgProjectedCrsRecord(26975, "NAD83 / Iowa North", 4269, 4499, 11431); + return true; + case 4611: + record = new EpsgProjectedCrsRecord(26976, "NAD83 / Iowa South", 4269, 4499, 11432); + return true; + case 4612: + record = new EpsgProjectedCrsRecord(26977, "NAD83 / Kansas North", 4269, 4499, 11531); + return true; + case 4613: + record = new EpsgProjectedCrsRecord(26978, "NAD83 / Kansas South", 4269, 4499, 11532); + return true; + case 4614: + record = new EpsgProjectedCrsRecord(26980, "NAD83 / Kentucky South", 4269, 4499, 11632); + return true; + case 4615: + record = new EpsgProjectedCrsRecord(26981, "NAD83 / Louisiana North", 4269, 4499, 11731); + return true; + case 4616: + record = new EpsgProjectedCrsRecord(26982, "NAD83 / Louisiana South", 4269, 4499, 11732); + return true; + case 4617: + record = new EpsgProjectedCrsRecord(26983, "NAD83 / Maine East", 4269, 4499, 11831); + return true; + case 4618: + record = new EpsgProjectedCrsRecord(26984, "NAD83 / Maine West", 4269, 4499, 11832); + return true; + case 4619: + record = new EpsgProjectedCrsRecord(26985, "NAD83 / Maryland", 4269, 4499, 11930); + return true; + case 4620: + record = new EpsgProjectedCrsRecord(26986, "NAD83 / Massachusetts Mainland", 4269, 4499, 12031); + return true; + case 4621: + record = new EpsgProjectedCrsRecord(26987, "NAD83 / Massachusetts Island", 4269, 4499, 12032); + return true; + case 4622: + record = new EpsgProjectedCrsRecord(26988, "NAD83 / Michigan North", 4269, 4499, 12141); + return true; + case 4623: + record = new EpsgProjectedCrsRecord(26989, "NAD83 / Michigan Central", 4269, 4499, 12142); + return true; + case 4624: + record = new EpsgProjectedCrsRecord(26990, "NAD83 / Michigan South", 4269, 4499, 12143); + return true; + case 4625: + record = new EpsgProjectedCrsRecord(26991, "NAD83 / Minnesota North", 4269, 4499, 12231); + return true; + case 4626: + record = new EpsgProjectedCrsRecord(26992, "NAD83 / Minnesota Central", 4269, 4499, 12232); + return true; + case 4627: + record = new EpsgProjectedCrsRecord(26993, "NAD83 / Minnesota South", 4269, 4499, 12233); + return true; + case 4628: + record = new EpsgProjectedCrsRecord(26994, "NAD83 / Mississippi East", 4269, 4499, 12331); + return true; + case 4629: + record = new EpsgProjectedCrsRecord(26995, "NAD83 / Mississippi West", 4269, 4499, 12332); + return true; + case 4630: + record = new EpsgProjectedCrsRecord(26996, "NAD83 / Missouri East", 4269, 4499, 12431); + return true; + case 4631: + record = new EpsgProjectedCrsRecord(26997, "NAD83 / Missouri Central", 4269, 4499, 12432); + return true; + case 4632: + record = new EpsgProjectedCrsRecord(26998, "NAD83 / Missouri West", 4269, 4499, 12433); + return true; + case 4633: + record = new EpsgProjectedCrsRecord(27039, "Nahrwan 1967 / UTM zone 39N", 4270, 4400, 16039); + return true; + case 4634: + record = new EpsgProjectedCrsRecord(27040, "Nahrwan 1967 / UTM zone 40N", 4270, 4400, 16040); + return true; + case 4635: + record = new EpsgProjectedCrsRecord(27120, "Naparima 1972 / UTM zone 20N", 4271, 4400, 16020); + return true; + case 4636: + record = new EpsgProjectedCrsRecord(27200, "NZGD49 / New Zealand Map Grid", 4272, 4400, 19917); + return true; + case 4637: + record = new EpsgProjectedCrsRecord(27205, "NZGD49 / Mount Eden Circuit", 4272, 4500, 17901); + return true; + case 4638: + record = new EpsgProjectedCrsRecord(27206, "NZGD49 / Bay of Plenty Circuit", 4272, 4500, 17902); + return true; + case 4639: + record = new EpsgProjectedCrsRecord(27207, "NZGD49 / Poverty Bay Circuit", 4272, 4500, 17903); + return true; + case 4640: + record = new EpsgProjectedCrsRecord(27208, "NZGD49 / Hawkes Bay Circuit", 4272, 4500, 17904); + return true; + case 4641: + record = new EpsgProjectedCrsRecord(27209, "NZGD49 / Taranaki Circuit", 4272, 4500, 17905); + return true; + case 4642: + record = new EpsgProjectedCrsRecord(27210, "NZGD49 / Tuhirangi Circuit", 4272, 4500, 17906); + return true; + case 4643: + record = new EpsgProjectedCrsRecord(27211, "NZGD49 / Wanganui Circuit", 4272, 4500, 17907); + return true; + case 4644: + record = new EpsgProjectedCrsRecord(27212, "NZGD49 / Wairarapa Circuit", 4272, 4500, 17908); + return true; + case 4645: + record = new EpsgProjectedCrsRecord(27213, "NZGD49 / Wellington Circuit", 4272, 4500, 17909); + return true; + case 4646: + record = new EpsgProjectedCrsRecord(27214, "NZGD49 / Collingwood Circuit", 4272, 4500, 17910); + return true; + case 4647: + record = new EpsgProjectedCrsRecord(27215, "NZGD49 / Nelson Circuit", 4272, 4500, 17911); + return true; + case 4648: + record = new EpsgProjectedCrsRecord(27216, "NZGD49 / Karamea Circuit", 4272, 4500, 17912); + return true; + case 4649: + record = new EpsgProjectedCrsRecord(27217, "NZGD49 / Buller Circuit", 4272, 4500, 17913); + return true; + case 4650: + record = new EpsgProjectedCrsRecord(27218, "NZGD49 / Grey Circuit", 4272, 4500, 17914); + return true; + case 4651: + record = new EpsgProjectedCrsRecord(27219, "NZGD49 / Amuri Circuit", 4272, 4500, 17915); + return true; + case 4652: + record = new EpsgProjectedCrsRecord(27220, "NZGD49 / Marlborough Circuit", 4272, 4500, 17916); + return true; + case 4653: + record = new EpsgProjectedCrsRecord(27221, "NZGD49 / Hokitika Circuit", 4272, 4500, 17917); + return true; + case 4654: + record = new EpsgProjectedCrsRecord(27222, "NZGD49 / Okarito Circuit", 4272, 4500, 17918); + return true; + case 4655: + record = new EpsgProjectedCrsRecord(27223, "NZGD49 / Jacksons Bay Circuit", 4272, 4500, 17919); + return true; + case 4656: + record = new EpsgProjectedCrsRecord(27224, "NZGD49 / Mount Pleasant Circuit", 4272, 4500, 17920); + return true; + case 4657: + record = new EpsgProjectedCrsRecord(27225, "NZGD49 / Gawler Circuit", 4272, 4500, 17921); + return true; + case 4658: + record = new EpsgProjectedCrsRecord(27226, "NZGD49 / Timaru Circuit", 4272, 4500, 17922); + return true; + case 4659: + record = new EpsgProjectedCrsRecord(27227, "NZGD49 / Lindis Peak Circuit", 4272, 4500, 17923); + return true; + case 4660: + record = new EpsgProjectedCrsRecord(27228, "NZGD49 / Mount Nicholas Circuit", 4272, 4500, 17924); + return true; + case 4661: + record = new EpsgProjectedCrsRecord(27229, "NZGD49 / Mount York Circuit", 4272, 4500, 17925); + return true; + case 4662: + record = new EpsgProjectedCrsRecord(27230, "NZGD49 / Observation Point Circuit", 4272, 4500, 17926); + return true; + case 4663: + record = new EpsgProjectedCrsRecord(27231, "NZGD49 / North Taieri Circuit", 4272, 4500, 17927); + return true; + case 4664: + record = new EpsgProjectedCrsRecord(27232, "NZGD49 / Bluff Circuit", 4272, 4500, 17928); + return true; + case 4665: + record = new EpsgProjectedCrsRecord(27258, "NZGD49 / UTM zone 58S", 4272, 4400, 16158); + return true; + case 4666: + record = new EpsgProjectedCrsRecord(27259, "NZGD49 / UTM zone 59S", 4272, 4400, 16159); + return true; + case 4667: + record = new EpsgProjectedCrsRecord(27260, "NZGD49 / UTM zone 60S", 4272, 4400, 16160); + return true; + case 4668: + record = new EpsgProjectedCrsRecord(27291, "NZGD49 / North Island Grid", 4272, 4409, 18141); + return true; + case 4669: + record = new EpsgProjectedCrsRecord(27292, "NZGD49 / South Island Grid", 4272, 4409, 18142); + return true; + case 4670: + record = new EpsgProjectedCrsRecord(27391, "NGO 1948 (Oslo) / NGO zone I", 4817, 4531, 18221); + return true; + case 4671: + record = new EpsgProjectedCrsRecord(27392, "NGO 1948 (Oslo) / NGO zone II", 4817, 4531, 18222); + return true; + case 4672: + record = new EpsgProjectedCrsRecord(27393, "NGO 1948 (Oslo) / NGO zone III", 4817, 4531, 18223); + return true; + case 4673: + record = new EpsgProjectedCrsRecord(27394, "NGO 1948 (Oslo) / NGO zone IV", 4817, 4531, 18224); + return true; + case 4674: + record = new EpsgProjectedCrsRecord(27395, "NGO 1948 (Oslo) / NGO zone V", 4817, 4531, 18225); + return true; + case 4675: + record = new EpsgProjectedCrsRecord(27396, "NGO 1948 (Oslo) / NGO zone VI", 4817, 4531, 18226); + return true; + case 4676: + record = new EpsgProjectedCrsRecord(27397, "NGO 1948 (Oslo) / NGO zone VII", 4817, 4531, 18227); + return true; + case 4677: + record = new EpsgProjectedCrsRecord(27398, "NGO 1948 (Oslo) / NGO zone VIII", 4817, 4531, 18228); + return true; + case 4678: + record = new EpsgProjectedCrsRecord(27429, "Datum 73 / UTM zone 29N", 4274, 4400, 16029); + return true; + case 4679: + record = new EpsgProjectedCrsRecord(27493, "Datum 73 / Modified Portuguese Grid", 4274, 4499, 19974); + return true; + case 4680: + record = new EpsgProjectedCrsRecord(27500, "ATF (Paris) / Nord de Guerre", 4901, 4499, 19903); + return true; + case 4681: + record = new EpsgProjectedCrsRecord(27561, "NTF (Paris) / Lambert Nord France", 4807, 4499, 18091); + return true; + case 4682: + record = new EpsgProjectedCrsRecord(27562, "NTF (Paris) / Lambert Centre France", 4807, 4499, 18092); + return true; + case 4683: + record = new EpsgProjectedCrsRecord(27563, "NTF (Paris) / Lambert Sud France", 4807, 4499, 18093); + return true; + case 4684: + record = new EpsgProjectedCrsRecord(27564, "NTF (Paris) / Lambert Corse", 4807, 4499, 18094); + return true; + case 4685: + record = new EpsgProjectedCrsRecord(27571, "NTF (Paris) / Lambert zone I", 4807, 4499, 18081); + return true; + case 4686: + record = new EpsgProjectedCrsRecord(27572, "NTF (Paris) / Lambert zone II", 4807, 4499, 18082); + return true; + case 4687: + record = new EpsgProjectedCrsRecord(27573, "NTF (Paris) / Lambert zone III", 4807, 4499, 18083); + return true; + case 4688: + record = new EpsgProjectedCrsRecord(27574, "NTF (Paris) / Lambert zone IV", 4807, 4499, 18084); + return true; + case 4689: + record = new EpsgProjectedCrsRecord(27700, "OSGB36 / British National Grid", 4277, 4400, 19916); + return true; + case 4690: + record = new EpsgProjectedCrsRecord(27701, "WGS 84 / Equi7 Africa", 4326, 4400, 17771); + return true; + case 4691: + record = new EpsgProjectedCrsRecord(27702, "WGS 84 / Equi7 Antarctica", 4326, 1027, 17772); + return true; + case 4692: + record = new EpsgProjectedCrsRecord(27703, "WGS 84 / Equi7 Asia", 4326, 4400, 17773); + return true; + case 4693: + record = new EpsgProjectedCrsRecord(27704, "WGS 84 / Equi7 Europe", 4326, 4400, 17774); + return true; + case 4694: + record = new EpsgProjectedCrsRecord(27705, "WGS 84 / Equi7 North America", 4326, 4400, 17775); + return true; + case 4695: + record = new EpsgProjectedCrsRecord(27706, "WGS 84 / Equi7 Oceania", 4326, 4400, 17776); + return true; + case 4696: + record = new EpsgProjectedCrsRecord(27707, "WGS 84 / Equi7 South America", 4326, 4400, 17777); + return true; + case 4697: + record = new EpsgProjectedCrsRecord(28191, "Palestine 1923 / Palestine Grid", 4281, 4400, 18201); + return true; + case 4698: + record = new EpsgProjectedCrsRecord(28192, "Palestine 1923 / Palestine Belt", 4281, 4400, 18202); + return true; + case 4699: + record = new EpsgProjectedCrsRecord(28193, "Palestine 1923 / Israeli CS Grid", 4281, 4400, 18203); + return true; + case 4700: + record = new EpsgProjectedCrsRecord(28232, "Pointe Noire / UTM zone 32S", 4282, 4400, 16132); + return true; + case 4701: + record = new EpsgProjectedCrsRecord(28348, "GDA94 / MGA zone 48", 4283, 4400, 17348); + return true; + case 4702: + record = new EpsgProjectedCrsRecord(28349, "GDA94 / MGA zone 49", 4283, 4400, 17349); + return true; + case 4703: + record = new EpsgProjectedCrsRecord(28350, "GDA94 / MGA zone 50", 4283, 4400, 17350); + return true; + case 4704: + record = new EpsgProjectedCrsRecord(28351, "GDA94 / MGA zone 51", 4283, 4400, 17351); + return true; + case 4705: + record = new EpsgProjectedCrsRecord(28352, "GDA94 / MGA zone 52", 4283, 4400, 17352); + return true; + case 4706: + record = new EpsgProjectedCrsRecord(28353, "GDA94 / MGA zone 53", 4283, 4400, 17353); + return true; + case 4707: + record = new EpsgProjectedCrsRecord(28354, "GDA94 / MGA zone 54", 4283, 4400, 17354); + return true; + case 4708: + record = new EpsgProjectedCrsRecord(28355, "GDA94 / MGA zone 55", 4283, 4400, 17355); + return true; + case 4709: + record = new EpsgProjectedCrsRecord(28356, "GDA94 / MGA zone 56", 4283, 4400, 17356); + return true; + case 4710: + record = new EpsgProjectedCrsRecord(28357, "GDA94 / MGA zone 57", 4283, 4400, 17357); + return true; + case 4711: + record = new EpsgProjectedCrsRecord(28358, "GDA94 / MGA zone 58", 4283, 4400, 17358); + return true; + case 4712: + record = new EpsgProjectedCrsRecord(28404, "Pulkovo 1942 / Gauss-Kruger zone 4", 4284, 4530, 16204); + return true; + case 4713: + record = new EpsgProjectedCrsRecord(28405, "Pulkovo 1942 / Gauss-Kruger zone 5", 4284, 4530, 16205); + return true; + case 4714: + record = new EpsgProjectedCrsRecord(28406, "Pulkovo 1942 / Gauss-Kruger zone 6", 4284, 4530, 16206); + return true; + case 4715: + record = new EpsgProjectedCrsRecord(28407, "Pulkovo 1942 / Gauss-Kruger zone 7", 4284, 4530, 16207); + return true; + case 4716: + record = new EpsgProjectedCrsRecord(28408, "Pulkovo 1942 / Gauss-Kruger zone 8", 4284, 4530, 16208); + return true; + case 4717: + record = new EpsgProjectedCrsRecord(28409, "Pulkovo 1942 / Gauss-Kruger zone 9", 4284, 4530, 16209); + return true; + case 4718: + record = new EpsgProjectedCrsRecord(28410, "Pulkovo 1942 / Gauss-Kruger zone 10", 4284, 4530, 16210); + return true; + case 4719: + record = new EpsgProjectedCrsRecord(28411, "Pulkovo 1942 / Gauss-Kruger zone 11", 4284, 4530, 16211); + return true; + case 4720: + record = new EpsgProjectedCrsRecord(28412, "Pulkovo 1942 / Gauss-Kruger zone 12", 4284, 4530, 16212); + return true; + case 4721: + record = new EpsgProjectedCrsRecord(28413, "Pulkovo 1942 / Gauss-Kruger zone 13", 4284, 4530, 16213); + return true; + case 4722: + record = new EpsgProjectedCrsRecord(28414, "Pulkovo 1942 / Gauss-Kruger zone 14", 4284, 4530, 16214); + return true; + case 4723: + record = new EpsgProjectedCrsRecord(28415, "Pulkovo 1942 / Gauss-Kruger zone 15", 4284, 4530, 16215); + return true; + case 4724: + record = new EpsgProjectedCrsRecord(28416, "Pulkovo 1942 / Gauss-Kruger zone 16", 4284, 4530, 16216); + return true; + case 4725: + record = new EpsgProjectedCrsRecord(28417, "Pulkovo 1942 / Gauss-Kruger zone 17", 4284, 4530, 16217); + return true; + case 4726: + record = new EpsgProjectedCrsRecord(28418, "Pulkovo 1942 / Gauss-Kruger zone 18", 4284, 4530, 16218); + return true; + case 4727: + record = new EpsgProjectedCrsRecord(28419, "Pulkovo 1942 / Gauss-Kruger zone 19", 4284, 4530, 16219); + return true; + case 4728: + record = new EpsgProjectedCrsRecord(28420, "Pulkovo 1942 / Gauss-Kruger zone 20", 4284, 4530, 16220); + return true; + case 4729: + record = new EpsgProjectedCrsRecord(28421, "Pulkovo 1942 / Gauss-Kruger zone 21", 4284, 4530, 16221); + return true; + case 4730: + record = new EpsgProjectedCrsRecord(28422, "Pulkovo 1942 / Gauss-Kruger zone 22", 4284, 4530, 16222); + return true; + case 4731: + record = new EpsgProjectedCrsRecord(28423, "Pulkovo 1942 / Gauss-Kruger zone 23", 4284, 4530, 16223); + return true; + case 4732: + record = new EpsgProjectedCrsRecord(28424, "Pulkovo 1942 / Gauss-Kruger zone 24", 4284, 4530, 16224); + return true; + case 4733: + record = new EpsgProjectedCrsRecord(28425, "Pulkovo 1942 / Gauss-Kruger zone 25", 4284, 4530, 16225); + return true; + case 4734: + record = new EpsgProjectedCrsRecord(28426, "Pulkovo 1942 / Gauss-Kruger zone 26", 4284, 4530, 16226); + return true; + case 4735: + record = new EpsgProjectedCrsRecord(28427, "Pulkovo 1942 / Gauss-Kruger zone 27", 4284, 4530, 16227); + return true; + case 4736: + record = new EpsgProjectedCrsRecord(28428, "Pulkovo 1942 / Gauss-Kruger zone 28", 4284, 4530, 16228); + return true; + case 4737: + record = new EpsgProjectedCrsRecord(28429, "Pulkovo 1942 / Gauss-Kruger zone 29", 4284, 4530, 16229); + return true; + case 4738: + record = new EpsgProjectedCrsRecord(28430, "Pulkovo 1942 / Gauss-Kruger zone 30", 4284, 4530, 16230); + return true; + case 4739: + record = new EpsgProjectedCrsRecord(28431, "Pulkovo 1942 / Gauss-Kruger zone 31", 4284, 4530, 16231); + return true; + case 4740: + record = new EpsgProjectedCrsRecord(28432, "Pulkovo 1942 / Gauss-Kruger zone 32", 4284, 4530, 16232); + return true; + case 4741: + record = new EpsgProjectedCrsRecord(28600, "Qatar 1974 / Qatar National Grid", 4285, 4400, 19919); + return true; + case 4742: + record = new EpsgProjectedCrsRecord(28991, "RD Old", 4289, 1054, 19913); + return true; + case 4743: + record = new EpsgProjectedCrsRecord(28992, "RD", 4289, 1054, 19914); + return true; + case 4744: + record = new EpsgProjectedCrsRecord(29101, "SAD69 / Brazil Polyconic", 4618, 4499, 19941); + return true; + case 4745: + record = new EpsgProjectedCrsRecord(29168, "SAD69 / UTM zone 18N", 4618, 4400, 16018); + return true; + case 4746: + record = new EpsgProjectedCrsRecord(29169, "SAD69 / UTM zone 19N", 4618, 4400, 16019); + return true; + case 4747: + record = new EpsgProjectedCrsRecord(29170, "SAD69 / UTM zone 20N", 4618, 4400, 16020); + return true; + case 4748: + record = new EpsgProjectedCrsRecord(29171, "SAD69 / UTM zone 21N", 4618, 4400, 16021); + return true; + case 4749: + record = new EpsgProjectedCrsRecord(29172, "SAD69 / UTM zone 22N", 4618, 4400, 16022); + return true; + case 4750: + record = new EpsgProjectedCrsRecord(29187, "SAD69 / UTM zone 17S", 4618, 4400, 16117); + return true; + case 4751: + record = new EpsgProjectedCrsRecord(29188, "SAD69 / UTM zone 18S", 4618, 4400, 16118); + return true; + case 4752: + record = new EpsgProjectedCrsRecord(29189, "SAD69 / UTM zone 19S", 4618, 4400, 16119); + return true; + case 4753: + record = new EpsgProjectedCrsRecord(29190, "SAD69 / UTM zone 20S", 4618, 4400, 16120); + return true; + case 4754: + record = new EpsgProjectedCrsRecord(29191, "SAD69 / UTM zone 21S", 4618, 4400, 16121); + return true; + case 4755: + record = new EpsgProjectedCrsRecord(29192, "SAD69 / UTM zone 22S", 4618, 4400, 16122); + return true; + case 4756: + record = new EpsgProjectedCrsRecord(29193, "SAD69 / UTM zone 23S", 4618, 4400, 16123); + return true; + case 4757: + record = new EpsgProjectedCrsRecord(29194, "SAD69 / UTM zone 24S", 4618, 4400, 16124); + return true; + case 4758: + record = new EpsgProjectedCrsRecord(29195, "SAD69 / UTM zone 25S", 4618, 4400, 16125); + return true; + case 4759: + record = new EpsgProjectedCrsRecord(29220, "Sapper Hill 1943 / UTM zone 20S", 4292, 4400, 16120); + return true; + case 4760: + record = new EpsgProjectedCrsRecord(29221, "Sapper Hill 1943 / UTM zone 21S", 4292, 4400, 16121); + return true; + case 4761: + record = new EpsgProjectedCrsRecord(29333, "Schwarzeck / UTM zone 33S", 4293, 4400, 16133); + return true; + case 4762: + record = new EpsgProjectedCrsRecord(29371, "Schwarzeck / Lo22/11", 4293, 6502, 17611); + return true; + case 4763: + record = new EpsgProjectedCrsRecord(29373, "Schwarzeck / Lo22/13", 4293, 6502, 17613); + return true; + case 4764: + record = new EpsgProjectedCrsRecord(29375, "Schwarzeck / Lo22/15", 4293, 6502, 17615); + return true; + case 4765: + record = new EpsgProjectedCrsRecord(29377, "Schwarzeck / Lo22/17", 4293, 6502, 17617); + return true; + case 4766: + record = new EpsgProjectedCrsRecord(29379, "Schwarzeck / Lo22/19", 4293, 6502, 17619); + return true; + case 4767: + record = new EpsgProjectedCrsRecord(29381, "Schwarzeck / Lo22/21", 4293, 6502, 17621); + return true; + case 4768: + record = new EpsgProjectedCrsRecord(29383, "Schwarzeck / Lo22/23", 4293, 6502, 17623); + return true; + case 4769: + record = new EpsgProjectedCrsRecord(29385, "Schwarzeck / Lo22/25", 4293, 6502, 17625); + return true; + case 4770: + record = new EpsgProjectedCrsRecord(29701, "Tananarive (Paris) / Laborde Grid", 4810, 4530, 19861); + return true; + case 4771: + record = new EpsgProjectedCrsRecord(29702, "Tananarive (Paris) / Laborde Grid approximation", 4810, 4530, 19911); + return true; + case 4772: + record = new EpsgProjectedCrsRecord(29738, "Tananarive / UTM zone 38S", 4297, 4400, 16138); + return true; + case 4773: + record = new EpsgProjectedCrsRecord(29739, "Tananarive / UTM zone 39S", 4297, 4400, 16139); + return true; + case 4774: + record = new EpsgProjectedCrsRecord(29849, "Timbalai 1948 / UTM zone 49N", 4298, 4400, 16049); + return true; + case 4775: + record = new EpsgProjectedCrsRecord(29850, "Timbalai 1948 / UTM zone 50N", 4298, 4400, 16050); + return true; + case 4776: + record = new EpsgProjectedCrsRecord(29871, "Timbalai 1948 / RSO Borneo (ch)", 4298, 4402, 19956); + return true; + case 4777: + record = new EpsgProjectedCrsRecord(29872, "Timbalai 1948 / RSO Borneo (ftSe)", 4298, 4405, 19957); + return true; + case 4778: + record = new EpsgProjectedCrsRecord(29873, "Timbalai 1948 / RSO Borneo (m)", 4298, 4400, 19958); + return true; + case 4779: + record = new EpsgProjectedCrsRecord(29874, "Timbalai 1948 / RSO Sarawak LSD (m)", 4298, 4400, 19838); + return true; + case 4780: + record = new EpsgProjectedCrsRecord(29901, "OSNI 1952 / Irish National Grid", 4188, 4400, 19973); + return true; + case 4781: + record = new EpsgProjectedCrsRecord(29902, "TM65 / Irish Grid", 4299, 4400, 19972); + return true; + case 4782: + record = new EpsgProjectedCrsRecord(29903, "TM75 / Irish Grid", 4300, 4400, 19972); + return true; + case 4783: + record = new EpsgProjectedCrsRecord(30161, "Tokyo / Japan Plane Rectangular CS I", 4301, 4530, 17801); + return true; + case 4784: + record = new EpsgProjectedCrsRecord(30162, "Tokyo / Japan Plane Rectangular CS II", 4301, 4530, 17802); + return true; + case 4785: + record = new EpsgProjectedCrsRecord(30163, "Tokyo / Japan Plane Rectangular CS III", 4301, 4530, 17803); + return true; + case 4786: + record = new EpsgProjectedCrsRecord(30164, "Tokyo / Japan Plane Rectangular CS IV", 4301, 4530, 17804); + return true; + case 4787: + record = new EpsgProjectedCrsRecord(30165, "Tokyo / Japan Plane Rectangular CS V", 4301, 4530, 17805); + return true; + case 4788: + record = new EpsgProjectedCrsRecord(30166, "Tokyo / Japan Plane Rectangular CS VI", 4301, 4530, 17806); + return true; + case 4789: + record = new EpsgProjectedCrsRecord(30167, "Tokyo / Japan Plane Rectangular CS VII", 4301, 4530, 17807); + return true; + case 4790: + record = new EpsgProjectedCrsRecord(30168, "Tokyo / Japan Plane Rectangular CS VIII", 4301, 4530, 17808); + return true; + case 4791: + record = new EpsgProjectedCrsRecord(30169, "Tokyo / Japan Plane Rectangular CS IX", 4301, 4530, 17809); + return true; + case 4792: + record = new EpsgProjectedCrsRecord(30170, "Tokyo / Japan Plane Rectangular CS X", 4301, 4530, 17810); + return true; + case 4793: + record = new EpsgProjectedCrsRecord(30171, "Tokyo / Japan Plane Rectangular CS XI", 4301, 4530, 17811); + return true; + case 4794: + record = new EpsgProjectedCrsRecord(30172, "Tokyo / Japan Plane Rectangular CS XII", 4301, 4530, 17812); + return true; + case 4795: + record = new EpsgProjectedCrsRecord(30173, "Tokyo / Japan Plane Rectangular CS XIII", 4301, 4530, 17813); + return true; + case 4796: + record = new EpsgProjectedCrsRecord(30174, "Tokyo / Japan Plane Rectangular CS XIV", 4301, 4530, 17814); + return true; + case 4797: + record = new EpsgProjectedCrsRecord(30175, "Tokyo / Japan Plane Rectangular CS XV", 4301, 4530, 17815); + return true; + case 4798: + record = new EpsgProjectedCrsRecord(30176, "Tokyo / Japan Plane Rectangular CS XVI", 4301, 4530, 17816); + return true; + case 4799: + record = new EpsgProjectedCrsRecord(30177, "Tokyo / Japan Plane Rectangular CS XVII", 4301, 4530, 17817); + return true; + case 4800: + record = new EpsgProjectedCrsRecord(30178, "Tokyo / Japan Plane Rectangular CS XVIII", 4301, 4530, 17818); + return true; + case 4801: + record = new EpsgProjectedCrsRecord(30179, "Tokyo / Japan Plane Rectangular CS XIX", 4301, 4530, 17819); + return true; + case 4802: + record = new EpsgProjectedCrsRecord(30200, "Trinidad 1903 / Trinidad Grid", 4302, 4407, 19925); + return true; + case 4803: + record = new EpsgProjectedCrsRecord(30339, "TC(1948) / UTM zone 39N", 4303, 4400, 16039); + return true; + case 4804: + record = new EpsgProjectedCrsRecord(30340, "TC(1948) / UTM zone 40N", 4303, 4400, 16040); + return true; + case 4805: + record = new EpsgProjectedCrsRecord(30491, "Voirol 1875 / Nord Algerie (ancienne)", 4304, 4499, 18011); + return true; + case 4806: + record = new EpsgProjectedCrsRecord(30492, "Voirol 1875 / Sud Algerie (ancienne)", 4304, 4499, 18012); + return true; + case 4807: + record = new EpsgProjectedCrsRecord(30493, "Voirol 1879 / Nord Algerie (ancienne)", 4671, 4499, 18011); + return true; + case 4808: + record = new EpsgProjectedCrsRecord(30494, "Voirol 1879 / Sud Algerie (ancienne)", 4671, 4499, 18012); + return true; + case 4809: + record = new EpsgProjectedCrsRecord(30729, "Nord Sahara 1959 / UTM zone 29N", 4307, 4400, 16029); + return true; + case 4810: + record = new EpsgProjectedCrsRecord(30730, "Nord Sahara 1959 / UTM zone 30N", 4307, 4400, 16030); + return true; + case 4811: + record = new EpsgProjectedCrsRecord(30731, "Nord Sahara 1959 / UTM zone 31N", 4307, 4400, 16031); + return true; + case 4812: + record = new EpsgProjectedCrsRecord(30732, "Nord Sahara 1959 / UTM zone 32N", 4307, 4400, 16032); + return true; + case 4813: + record = new EpsgProjectedCrsRecord(30791, "Nord Sahara 1959 / Nord Algerie", 4307, 4499, 18021); + return true; + case 4814: + record = new EpsgProjectedCrsRecord(30792, "Nord Sahara 1959 / Sud Algerie", 4307, 4499, 18022); + return true; + case 4815: + record = new EpsgProjectedCrsRecord(31028, "Yoff / UTM zone 28N", 4310, 4400, 16028); + return true; + case 4816: + record = new EpsgProjectedCrsRecord(31121, "Zanderij / UTM zone 21N", 4311, 4400, 16021); + return true; + case 4817: + record = new EpsgProjectedCrsRecord(31154, "Zanderij / TM 54 NW", 4311, 4400, 17054); + return true; + case 4818: + record = new EpsgProjectedCrsRecord(31170, "Zanderij / Suriname Old TM", 4311, 4400, 19954); + return true; + case 4819: + record = new EpsgProjectedCrsRecord(31171, "Zanderij / Suriname TM", 4311, 4400, 19955); + return true; + case 4820: + record = new EpsgProjectedCrsRecord(31251, "MGI (Ferro) / Austria GK West Zone", 4805, 4530, 18001); + return true; + case 4821: + record = new EpsgProjectedCrsRecord(31252, "MGI (Ferro) / Austria GK Central Zone", 4805, 4530, 18002); + return true; + case 4822: + record = new EpsgProjectedCrsRecord(31253, "MGI (Ferro) / Austria GK East Zone", 4805, 4530, 18003); + return true; + case 4823: + record = new EpsgProjectedCrsRecord(31254, "MGI / Austria GK West", 4312, 4530, 18004); + return true; + case 4824: + record = new EpsgProjectedCrsRecord(31255, "MGI / Austria GK Central", 4312, 4530, 18005); + return true; + case 4825: + record = new EpsgProjectedCrsRecord(31256, "MGI / Austria GK East", 4312, 4530, 18006); + return true; + case 4826: + record = new EpsgProjectedCrsRecord(31257, "MGI / Austria GK M28", 4312, 4530, 18007); + return true; + case 4827: + record = new EpsgProjectedCrsRecord(31258, "MGI / Austria GK M31", 4312, 4530, 18008); + return true; + case 4828: + record = new EpsgProjectedCrsRecord(31259, "MGI / Austria GK M34", 4312, 4530, 18009); + return true; + case 4829: + record = new EpsgProjectedCrsRecord(31281, "MGI (Ferro) / Austria West Zone", 4805, 4530, 18041); + return true; + case 4830: + record = new EpsgProjectedCrsRecord(31282, "MGI (Ferro) / Austria Central Zone", 4805, 4530, 18042); + return true; + case 4831: + record = new EpsgProjectedCrsRecord(31283, "MGI (Ferro) / Austria East Zone", 4805, 4530, 18043); + return true; + case 4832: + record = new EpsgProjectedCrsRecord(31284, "MGI / Austria M28", 4312, 4530, 18044); + return true; + case 4833: + record = new EpsgProjectedCrsRecord(31285, "MGI / Austria M31", 4312, 4530, 18045); + return true; + case 4834: + record = new EpsgProjectedCrsRecord(31286, "MGI / Austria M34", 4312, 4530, 18046); + return true; + case 4835: + record = new EpsgProjectedCrsRecord(31287, "MGI / Austria Lambert", 4312, 4530, 19947); + return true; + case 4836: + record = new EpsgProjectedCrsRecord(31288, "MGI (Ferro) / Austria zone M28", 4805, 4530, 18047); + return true; + case 4837: + record = new EpsgProjectedCrsRecord(31289, "MGI (Ferro) / Austria zone M31", 4805, 4530, 18048); + return true; + case 4838: + record = new EpsgProjectedCrsRecord(31290, "MGI (Ferro) / Austria zone M34", 4805, 4530, 18049); + return true; + case 4839: + record = new EpsgProjectedCrsRecord(31300, "BD72 / Belge Lambert 72", 4313, 4499, 19902); + return true; + case 4840: + record = new EpsgProjectedCrsRecord(31370, "BD72 / Belgian Lambert 72", 4313, 4499, 19961); + return true; + case 4841: + record = new EpsgProjectedCrsRecord(31466, "DHDN / 3-degree Gauss-Kruger zone 2", 4314, 4530, 16262); + return true; + case 4842: + record = new EpsgProjectedCrsRecord(31467, "DHDN / 3-degree Gauss-Kruger zone 3", 4314, 4530, 16263); + return true; + case 4843: + record = new EpsgProjectedCrsRecord(31468, "DHDN / 3-degree Gauss-Kruger zone 4", 4314, 4530, 16264); + return true; + case 4844: + record = new EpsgProjectedCrsRecord(31469, "DHDN / 3-degree Gauss-Kruger zone 5", 4314, 4530, 16265); + return true; + case 4845: + record = new EpsgProjectedCrsRecord(31528, "Conakry 1905 / UTM zone 28N", 4315, 4400, 16028); + return true; + case 4846: + record = new EpsgProjectedCrsRecord(31529, "Conakry 1905 / UTM zone 29N", 4315, 4400, 16029); + return true; + case 4847: + record = new EpsgProjectedCrsRecord(31600, "Dealul Piscului 1930 / Stereo 33", 4316, 4499, 19927); + return true; + case 4848: + record = new EpsgProjectedCrsRecord(31838, "NGN / UTM zone 38N", 4318, 4400, 16038); + return true; + case 4849: + record = new EpsgProjectedCrsRecord(31839, "NGN / UTM zone 39N", 4318, 4400, 16039); + return true; + case 4850: + record = new EpsgProjectedCrsRecord(31901, "KUDAMS / KTM", 4319, 4400, 19997); + return true; + case 4851: + record = new EpsgProjectedCrsRecord(31965, "SIRGAS 2000 / UTM zone 11N", 4674, 4400, 16011); + return true; + case 4852: + record = new EpsgProjectedCrsRecord(31966, "SIRGAS 2000 / UTM zone 12N", 4674, 4400, 16012); + return true; + case 4853: + record = new EpsgProjectedCrsRecord(31967, "SIRGAS 2000 / UTM zone 13N", 4674, 4400, 16013); + return true; + case 4854: + record = new EpsgProjectedCrsRecord(31968, "SIRGAS 2000 / UTM zone 14N", 4674, 4400, 16014); + return true; + case 4855: + record = new EpsgProjectedCrsRecord(31969, "SIRGAS 2000 / UTM zone 15N", 4674, 4400, 16015); + return true; + case 4856: + record = new EpsgProjectedCrsRecord(31970, "SIRGAS 2000 / UTM zone 16N", 4674, 4400, 16016); + return true; + case 4857: + record = new EpsgProjectedCrsRecord(31971, "SIRGAS 2000 / UTM zone 17N", 4674, 4400, 16017); + return true; + case 4858: + record = new EpsgProjectedCrsRecord(31972, "SIRGAS 2000 / UTM zone 18N", 4674, 4400, 16018); + return true; + case 4859: + record = new EpsgProjectedCrsRecord(31973, "SIRGAS 2000 / UTM zone 19N", 4674, 4400, 16019); + return true; + case 4860: + record = new EpsgProjectedCrsRecord(31974, "SIRGAS 2000 / UTM zone 20N", 4674, 4400, 16020); + return true; + case 4861: + record = new EpsgProjectedCrsRecord(31975, "SIRGAS 2000 / UTM zone 21N", 4674, 4400, 16021); + return true; + case 4862: + record = new EpsgProjectedCrsRecord(31976, "SIRGAS 2000 / UTM zone 22N", 4674, 4400, 16022); + return true; + case 4863: + record = new EpsgProjectedCrsRecord(31977, "SIRGAS 2000 / UTM zone 17S", 4674, 4400, 16117); + return true; + case 4864: + record = new EpsgProjectedCrsRecord(31978, "SIRGAS 2000 / UTM zone 18S", 4674, 4400, 16118); + return true; + case 4865: + record = new EpsgProjectedCrsRecord(31979, "SIRGAS 2000 / UTM zone 19S", 4674, 4400, 16119); + return true; + case 4866: + record = new EpsgProjectedCrsRecord(31980, "SIRGAS 2000 / UTM zone 20S", 4674, 4400, 16120); + return true; + case 4867: + record = new EpsgProjectedCrsRecord(31981, "SIRGAS 2000 / UTM zone 21S", 4674, 4400, 16121); + return true; + case 4868: + record = new EpsgProjectedCrsRecord(31982, "SIRGAS 2000 / UTM zone 22S", 4674, 4400, 16122); + return true; + case 4869: + record = new EpsgProjectedCrsRecord(31983, "SIRGAS 2000 / UTM zone 23S", 4674, 4400, 16123); + return true; + case 4870: + record = new EpsgProjectedCrsRecord(31984, "SIRGAS 2000 / UTM zone 24S", 4674, 4400, 16124); + return true; + case 4871: + record = new EpsgProjectedCrsRecord(31985, "SIRGAS 2000 / UTM zone 25S", 4674, 4400, 16125); + return true; + case 4872: + record = new EpsgProjectedCrsRecord(31986, "SIRGAS 1995 / UTM zone 17N", 4170, 4400, 16017); + return true; + case 4873: + record = new EpsgProjectedCrsRecord(31987, "SIRGAS 1995 / UTM zone 18N", 4170, 4400, 16018); + return true; + case 4874: + record = new EpsgProjectedCrsRecord(31988, "SIRGAS 1995 / UTM zone 19N", 4170, 4400, 16019); + return true; + case 4875: + record = new EpsgProjectedCrsRecord(31989, "SIRGAS 1995 / UTM zone 20N", 4170, 4400, 16020); + return true; + case 4876: + record = new EpsgProjectedCrsRecord(31990, "SIRGAS 1995 / UTM zone 21N", 4170, 4400, 16021); + return true; + case 4877: + record = new EpsgProjectedCrsRecord(31991, "SIRGAS 1995 / UTM zone 22N", 4170, 4400, 16022); + return true; + case 4878: + record = new EpsgProjectedCrsRecord(31992, "SIRGAS 1995 / UTM zone 17S", 4170, 4400, 16117); + return true; + case 4879: + record = new EpsgProjectedCrsRecord(31993, "SIRGAS 1995 / UTM zone 18S", 4170, 4400, 16118); + return true; + case 4880: + record = new EpsgProjectedCrsRecord(31994, "SIRGAS 1995 / UTM zone 19S", 4170, 4400, 16119); + return true; + case 4881: + record = new EpsgProjectedCrsRecord(31995, "SIRGAS 1995 / UTM zone 20S", 4170, 4400, 16120); + return true; + case 4882: + record = new EpsgProjectedCrsRecord(31996, "SIRGAS 1995 / UTM zone 21S", 4170, 4400, 16121); + return true; + case 4883: + record = new EpsgProjectedCrsRecord(31997, "SIRGAS 1995 / UTM zone 22S", 4170, 4400, 16122); + return true; + case 4884: + record = new EpsgProjectedCrsRecord(31998, "SIRGAS 1995 / UTM zone 23S", 4170, 4400, 16123); + return true; + case 4885: + record = new EpsgProjectedCrsRecord(31999, "SIRGAS 1995 / UTM zone 24S", 4170, 4400, 16124); + return true; + case 4886: + record = new EpsgProjectedCrsRecord(32000, "SIRGAS 1995 / UTM zone 25S", 4170, 4400, 16125); + return true; + case 4887: + record = new EpsgProjectedCrsRecord(32001, "NAD27 / Montana North", 4267, 4497, 12501); + return true; + case 4888: + record = new EpsgProjectedCrsRecord(32002, "NAD27 / Montana Central", 4267, 4497, 12502); + return true; + case 4889: + record = new EpsgProjectedCrsRecord(32003, "NAD27 / Montana South", 4267, 4497, 12503); + return true; + case 4890: + record = new EpsgProjectedCrsRecord(32005, "NAD27 / Nebraska North", 4267, 4497, 12601); + return true; + case 4891: + record = new EpsgProjectedCrsRecord(32006, "NAD27 / Nebraska South", 4267, 4497, 12602); + return true; + case 4892: + record = new EpsgProjectedCrsRecord(32007, "NAD27 / Nevada East", 4267, 4497, 12701); + return true; + case 4893: + record = new EpsgProjectedCrsRecord(32008, "NAD27 / Nevada Central", 4267, 4497, 12702); + return true; + case 4894: + record = new EpsgProjectedCrsRecord(32009, "NAD27 / Nevada West", 4267, 4497, 12703); + return true; + case 4895: + record = new EpsgProjectedCrsRecord(32010, "NAD27 / New Hampshire", 4267, 4497, 12800); + return true; + case 4896: + record = new EpsgProjectedCrsRecord(32011, "NAD27 / New Jersey", 4267, 4497, 12900); + return true; + case 4897: + record = new EpsgProjectedCrsRecord(32012, "NAD27 / New Mexico East", 4267, 4497, 13001); + return true; + case 4898: + record = new EpsgProjectedCrsRecord(32013, "NAD27 / New Mexico Central", 4267, 4497, 13002); + return true; + case 4899: + record = new EpsgProjectedCrsRecord(32014, "NAD27 / New Mexico West", 4267, 4497, 13003); + return true; + case 4900: + record = new EpsgProjectedCrsRecord(32015, "NAD27 / New York East", 4267, 4497, 13101); + return true; + case 4901: + record = new EpsgProjectedCrsRecord(32016, "NAD27 / New York Central", 4267, 4497, 13102); + return true; + case 4902: + record = new EpsgProjectedCrsRecord(32017, "NAD27 / New York West", 4267, 4497, 13103); + return true; + case 4903: + record = new EpsgProjectedCrsRecord(32019, "NAD27 / North Carolina", 4267, 4497, 13200); + return true; + case 4904: + record = new EpsgProjectedCrsRecord(32020, "NAD27 / North Dakota North", 4267, 4497, 13301); + return true; + case 4905: + record = new EpsgProjectedCrsRecord(32021, "NAD27 / North Dakota South", 4267, 4497, 13302); + return true; + case 4906: + record = new EpsgProjectedCrsRecord(32022, "NAD27 / Ohio North", 4267, 4497, 13401); + return true; + case 4907: + record = new EpsgProjectedCrsRecord(32023, "NAD27 / Ohio South", 4267, 4497, 13402); + return true; + case 4908: + record = new EpsgProjectedCrsRecord(32024, "NAD27 / Oklahoma North", 4267, 4497, 13501); + return true; + case 4909: + record = new EpsgProjectedCrsRecord(32025, "NAD27 / Oklahoma South", 4267, 4497, 13502); + return true; + case 4910: + record = new EpsgProjectedCrsRecord(32026, "NAD27 / Oregon North", 4267, 4497, 13601); + return true; + case 4911: + record = new EpsgProjectedCrsRecord(32027, "NAD27 / Oregon South", 4267, 4497, 13602); + return true; + case 4912: + record = new EpsgProjectedCrsRecord(32028, "NAD27 / Pennsylvania North", 4267, 4497, 13701); + return true; + case 4913: + record = new EpsgProjectedCrsRecord(32030, "NAD27 / Rhode Island", 4267, 4497, 13800); + return true; + case 4914: + record = new EpsgProjectedCrsRecord(32031, "NAD27 / South Carolina North", 4267, 4497, 13901); + return true; + case 4915: + record = new EpsgProjectedCrsRecord(32033, "NAD27 / South Carolina South", 4267, 4497, 13902); + return true; + case 4916: + record = new EpsgProjectedCrsRecord(32034, "NAD27 / South Dakota North", 4267, 4497, 14001); + return true; + case 4917: + record = new EpsgProjectedCrsRecord(32035, "NAD27 / South Dakota South", 4267, 4497, 14002); + return true; + case 4918: + record = new EpsgProjectedCrsRecord(32037, "NAD27 / Texas North", 4267, 4497, 14201); + return true; + case 4919: + record = new EpsgProjectedCrsRecord(32038, "NAD27 / Texas North Central", 4267, 4497, 14202); + return true; + case 4920: + record = new EpsgProjectedCrsRecord(32039, "NAD27 / Texas Central", 4267, 4497, 14203); + return true; + case 4921: + record = new EpsgProjectedCrsRecord(32040, "NAD27 / Texas South Central", 4267, 4497, 14204); + return true; + case 4922: + record = new EpsgProjectedCrsRecord(32041, "NAD27 / Texas South", 4267, 4497, 14205); + return true; + case 4923: + record = new EpsgProjectedCrsRecord(32042, "NAD27 / Utah North", 4267, 4497, 14301); + return true; + case 4924: + record = new EpsgProjectedCrsRecord(32043, "NAD27 / Utah Central", 4267, 4497, 14302); + return true; + case 4925: + record = new EpsgProjectedCrsRecord(32044, "NAD27 / Utah South", 4267, 4497, 14303); + return true; + case 4926: + record = new EpsgProjectedCrsRecord(32045, "NAD27 / Vermont", 4267, 4497, 14400); + return true; + case 4927: + record = new EpsgProjectedCrsRecord(32046, "NAD27 / Virginia North", 4267, 4497, 14501); + return true; + case 4928: + record = new EpsgProjectedCrsRecord(32047, "NAD27 / Virginia South", 4267, 4497, 14502); + return true; + case 4929: + record = new EpsgProjectedCrsRecord(32048, "NAD27 / Washington North", 4267, 4497, 14601); + return true; + case 4930: + record = new EpsgProjectedCrsRecord(32049, "NAD27 / Washington South", 4267, 4497, 14602); + return true; + case 4931: + record = new EpsgProjectedCrsRecord(32050, "NAD27 / West Virginia North", 4267, 4497, 14701); + return true; + case 4932: + record = new EpsgProjectedCrsRecord(32051, "NAD27 / West Virginia South", 4267, 4497, 14702); + return true; + case 4933: + record = new EpsgProjectedCrsRecord(32052, "NAD27 / Wisconsin North", 4267, 4497, 14801); + return true; + case 4934: + record = new EpsgProjectedCrsRecord(32053, "NAD27 / Wisconsin Central", 4267, 4497, 14802); + return true; + case 4935: + record = new EpsgProjectedCrsRecord(32054, "NAD27 / Wisconsin South", 4267, 4497, 14803); + return true; + case 4936: + record = new EpsgProjectedCrsRecord(32055, "NAD27 / Wyoming East", 4267, 4497, 14901); + return true; + case 4937: + record = new EpsgProjectedCrsRecord(32056, "NAD27 / Wyoming East Central", 4267, 4497, 14902); + return true; + case 4938: + record = new EpsgProjectedCrsRecord(32057, "NAD27 / Wyoming West Central", 4267, 4497, 14903); + return true; + case 4939: + record = new EpsgProjectedCrsRecord(32058, "NAD27 / Wyoming West", 4267, 4497, 14904); + return true; + case 4940: + record = new EpsgProjectedCrsRecord(32064, "NAD27 / BLM 14N (ftUS)", 4267, 4497, 15914); + return true; + case 4941: + record = new EpsgProjectedCrsRecord(32065, "NAD27 / BLM 15N (ftUS)", 4267, 4497, 15915); + return true; + case 4942: + record = new EpsgProjectedCrsRecord(32066, "NAD27 / BLM 16N (ftUS)", 4267, 4497, 15916); + return true; + case 4943: + record = new EpsgProjectedCrsRecord(32067, "NAD27 / BLM 17N (ftUS)", 4267, 4497, 15917); + return true; + case 4944: + record = new EpsgProjectedCrsRecord(32081, "NAD27 / MTM zone 1", 4267, 4400, 17701); + return true; + case 4945: + record = new EpsgProjectedCrsRecord(32082, "NAD27 / MTM zone 2", 4267, 4400, 17702); + return true; + case 4946: + record = new EpsgProjectedCrsRecord(32083, "NAD27 / MTM zone 3", 4267, 4400, 17703); + return true; + case 4947: + record = new EpsgProjectedCrsRecord(32084, "NAD27 / MTM zone 4", 4267, 4400, 17704); + return true; + case 4948: + record = new EpsgProjectedCrsRecord(32085, "NAD27 / MTM zone 5", 4267, 4400, 17705); + return true; + case 4949: + record = new EpsgProjectedCrsRecord(32086, "NAD27 / MTM zone 6", 4267, 4400, 17706); + return true; + case 4950: + record = new EpsgProjectedCrsRecord(32098, "NAD27 / Quebec Lambert", 4267, 4499, 19944); + return true; + case 4951: + record = new EpsgProjectedCrsRecord(32099, "NAD27 / Louisiana Offshore", 4267, 4497, 11703); + return true; + case 4952: + record = new EpsgProjectedCrsRecord(32100, "NAD83 / Montana", 4269, 4499, 12530); + return true; + case 4953: + record = new EpsgProjectedCrsRecord(32104, "NAD83 / Nebraska", 4269, 4499, 12630); + return true; + case 4954: + record = new EpsgProjectedCrsRecord(32107, "NAD83 / Nevada East", 4269, 4499, 12731); + return true; + case 4955: + record = new EpsgProjectedCrsRecord(32108, "NAD83 / Nevada Central", 4269, 4499, 12732); + return true; + case 4956: + record = new EpsgProjectedCrsRecord(32109, "NAD83 / Nevada West", 4269, 4499, 12733); + return true; + case 4957: + record = new EpsgProjectedCrsRecord(32110, "NAD83 / New Hampshire", 4269, 4499, 12830); + return true; + case 4958: + record = new EpsgProjectedCrsRecord(32111, "NAD83 / New Jersey", 4269, 4499, 12930); + return true; + case 4959: + record = new EpsgProjectedCrsRecord(32112, "NAD83 / New Mexico East", 4269, 4499, 13031); + return true; + case 4960: + record = new EpsgProjectedCrsRecord(32113, "NAD83 / New Mexico Central", 4269, 4499, 13032); + return true; + case 4961: + record = new EpsgProjectedCrsRecord(32114, "NAD83 / New Mexico West", 4269, 4499, 13033); + return true; + case 4962: + record = new EpsgProjectedCrsRecord(32115, "NAD83 / New York East", 4269, 4499, 13131); + return true; + case 4963: + record = new EpsgProjectedCrsRecord(32116, "NAD83 / New York Central", 4269, 4499, 13132); + return true; + case 4964: + record = new EpsgProjectedCrsRecord(32117, "NAD83 / New York West", 4269, 4499, 13133); + return true; + case 4965: + record = new EpsgProjectedCrsRecord(32118, "NAD83 / New York Long Island", 4269, 4499, 13134); + return true; + case 4966: + record = new EpsgProjectedCrsRecord(32119, "NAD83 / North Carolina", 4269, 4499, 13230); + return true; + case 4967: + record = new EpsgProjectedCrsRecord(32120, "NAD83 / North Dakota North", 4269, 4499, 13331); + return true; + case 4968: + record = new EpsgProjectedCrsRecord(32121, "NAD83 / North Dakota South", 4269, 4499, 13332); + return true; + case 4969: + record = new EpsgProjectedCrsRecord(32122, "NAD83 / Ohio North", 4269, 4499, 13431); + return true; + case 4970: + record = new EpsgProjectedCrsRecord(32123, "NAD83 / Ohio South", 4269, 4499, 13432); + return true; + case 4971: + record = new EpsgProjectedCrsRecord(32124, "NAD83 / Oklahoma North", 4269, 4499, 13531); + return true; + case 4972: + record = new EpsgProjectedCrsRecord(32125, "NAD83 / Oklahoma South", 4269, 4499, 13532); + return true; + case 4973: + record = new EpsgProjectedCrsRecord(32126, "NAD83 / Oregon North", 4269, 4499, 13631); + return true; + case 4974: + record = new EpsgProjectedCrsRecord(32127, "NAD83 / Oregon South", 4269, 4499, 13632); + return true; + case 4975: + record = new EpsgProjectedCrsRecord(32128, "NAD83 / Pennsylvania North", 4269, 4499, 13731); + return true; + case 4976: + record = new EpsgProjectedCrsRecord(32129, "NAD83 / Pennsylvania South", 4269, 4499, 13732); + return true; + case 4977: + record = new EpsgProjectedCrsRecord(32130, "NAD83 / Rhode Island", 4269, 4499, 13830); + return true; + case 4978: + record = new EpsgProjectedCrsRecord(32133, "NAD83 / South Carolina", 4269, 4499, 13930); + return true; + case 4979: + record = new EpsgProjectedCrsRecord(32134, "NAD83 / South Dakota North", 4269, 4499, 14031); + return true; + case 4980: + record = new EpsgProjectedCrsRecord(32135, "NAD83 / South Dakota South", 4269, 4499, 14032); + return true; + case 4981: + record = new EpsgProjectedCrsRecord(32136, "NAD83 / Tennessee", 4269, 4499, 14130); + return true; + case 4982: + record = new EpsgProjectedCrsRecord(32137, "NAD83 / Texas North", 4269, 4499, 14231); + return true; + case 4983: + record = new EpsgProjectedCrsRecord(32138, "NAD83 / Texas North Central", 4269, 4499, 14232); + return true; + case 4984: + record = new EpsgProjectedCrsRecord(32139, "NAD83 / Texas Central", 4269, 4499, 14233); + return true; + case 4985: + record = new EpsgProjectedCrsRecord(32140, "NAD83 / Texas South Central", 4269, 4499, 14234); + return true; + case 4986: + record = new EpsgProjectedCrsRecord(32141, "NAD83 / Texas South", 4269, 4499, 14235); + return true; + case 4987: + record = new EpsgProjectedCrsRecord(32142, "NAD83 / Utah North", 4269, 4499, 14331); + return true; + case 4988: + record = new EpsgProjectedCrsRecord(32143, "NAD83 / Utah Central", 4269, 4499, 14332); + return true; + case 4989: + record = new EpsgProjectedCrsRecord(32144, "NAD83 / Utah South", 4269, 4499, 14333); + return true; + case 4990: + record = new EpsgProjectedCrsRecord(32145, "NAD83 / Vermont", 4269, 4499, 14430); + return true; + case 4991: + record = new EpsgProjectedCrsRecord(32146, "NAD83 / Virginia North", 4269, 4499, 14531); + return true; + case 4992: + record = new EpsgProjectedCrsRecord(32147, "NAD83 / Virginia South", 4269, 4499, 14532); + return true; + case 4993: + record = new EpsgProjectedCrsRecord(32148, "NAD83 / Washington North", 4269, 4499, 14631); + return true; + case 4994: + record = new EpsgProjectedCrsRecord(32149, "NAD83 / Washington South", 4269, 4499, 14632); + return true; + case 4995: + record = new EpsgProjectedCrsRecord(32150, "NAD83 / West Virginia North", 4269, 4499, 14731); + return true; + case 4996: + record = new EpsgProjectedCrsRecord(32151, "NAD83 / West Virginia South", 4269, 4499, 14732); + return true; + case 4997: + record = new EpsgProjectedCrsRecord(32152, "NAD83 / Wisconsin North", 4269, 4499, 14831); + return true; + case 4998: + record = new EpsgProjectedCrsRecord(32153, "NAD83 / Wisconsin Central", 4269, 4499, 14832); + return true; + case 4999: + record = new EpsgProjectedCrsRecord(32154, "NAD83 / Wisconsin South", 4269, 4499, 14833); + return true; + default: + record = default; + return false; + } + } + + private static bool TryGetProjectedCrsBucket5(int index, out EpsgProjectedCrsRecord record) + { + switch (index) + { + case 5000: + record = new EpsgProjectedCrsRecord(32155, "NAD83 / Wyoming East", 4269, 4499, 14931); + return true; + case 5001: + record = new EpsgProjectedCrsRecord(32156, "NAD83 / Wyoming East Central", 4269, 4499, 14932); + return true; + case 5002: + record = new EpsgProjectedCrsRecord(32157, "NAD83 / Wyoming West Central", 4269, 4499, 14933); + return true; + case 5003: + record = new EpsgProjectedCrsRecord(32158, "NAD83 / Wyoming West", 4269, 4499, 14934); + return true; + case 5004: + record = new EpsgProjectedCrsRecord(32159, "NAD83 / Wyoming Lambert", 4269, 4499, 14930); + return true; + case 5005: + record = new EpsgProjectedCrsRecord(32161, "NAD83 / Puerto Rico & Virgin Is.", 4269, 4499, 15230); + return true; + case 5006: + record = new EpsgProjectedCrsRecord(32164, "NAD83 / BLM 14N (ftUS)", 4269, 4497, 15914); + return true; + case 5007: + record = new EpsgProjectedCrsRecord(32165, "NAD83 / BLM 15N (ftUS)", 4269, 4497, 15915); + return true; + case 5008: + record = new EpsgProjectedCrsRecord(32166, "NAD83 / BLM 16N (ftUS)", 4269, 4497, 15916); + return true; + case 5009: + record = new EpsgProjectedCrsRecord(32167, "NAD83 / BLM 17N (ftUS)", 4269, 4497, 15917); + return true; + case 5010: + record = new EpsgProjectedCrsRecord(32181, "NAD83 / MTM zone 1", 4269, 4496, 17701); + return true; + case 5011: + record = new EpsgProjectedCrsRecord(32182, "NAD83 / MTM zone 2", 4269, 4496, 17702); + return true; + case 5012: + record = new EpsgProjectedCrsRecord(32183, "NAD83 / MTM zone 3", 4269, 4496, 17703); + return true; + case 5013: + record = new EpsgProjectedCrsRecord(32184, "NAD83 / MTM zone 4", 4269, 4496, 17704); + return true; + case 5014: + record = new EpsgProjectedCrsRecord(32185, "NAD83 / MTM zone 5", 4269, 4496, 17705); + return true; + case 5015: + record = new EpsgProjectedCrsRecord(32186, "NAD83 / MTM zone 6", 4269, 4496, 17706); + return true; + case 5016: + record = new EpsgProjectedCrsRecord(32187, "NAD83 / MTM zone 7", 4269, 4496, 17707); + return true; + case 5017: + record = new EpsgProjectedCrsRecord(32188, "NAD83 / MTM zone 8", 4269, 4496, 17708); + return true; + case 5018: + record = new EpsgProjectedCrsRecord(32189, "NAD83 / MTM zone 9", 4269, 4496, 17709); + return true; + case 5019: + record = new EpsgProjectedCrsRecord(32190, "NAD83 / MTM zone 10", 4269, 4496, 17710); + return true; + case 5020: + record = new EpsgProjectedCrsRecord(32191, "NAD83 / MTM zone 11", 4269, 4400, 17711); + return true; + case 5021: + record = new EpsgProjectedCrsRecord(32192, "NAD83 / MTM zone 12", 4269, 4400, 17712); + return true; + case 5022: + record = new EpsgProjectedCrsRecord(32193, "NAD83 / MTM zone 13", 4269, 4400, 17713); + return true; + case 5023: + record = new EpsgProjectedCrsRecord(32194, "NAD83 / MTM zone 14", 4269, 4400, 17714); + return true; + case 5024: + record = new EpsgProjectedCrsRecord(32195, "NAD83 / MTM zone 15", 4269, 4400, 17715); + return true; + case 5025: + record = new EpsgProjectedCrsRecord(32196, "NAD83 / MTM zone 16", 4269, 4400, 17716); + return true; + case 5026: + record = new EpsgProjectedCrsRecord(32197, "NAD83 / MTM zone 17", 4269, 4400, 17717); + return true; + case 5027: + record = new EpsgProjectedCrsRecord(32198, "NAD83 / Quebec Lambert", 4269, 4499, 19944); + return true; + case 5028: + record = new EpsgProjectedCrsRecord(32199, "NAD83 / Louisiana Offshore", 4269, 4499, 11733); + return true; + case 5029: + record = new EpsgProjectedCrsRecord(32201, "WGS 72 / UTM zone 1N", 4322, 4400, 16001); + return true; + case 5030: + record = new EpsgProjectedCrsRecord(32202, "WGS 72 / UTM zone 2N", 4322, 4400, 16002); + return true; + case 5031: + record = new EpsgProjectedCrsRecord(32203, "WGS 72 / UTM zone 3N", 4322, 4400, 16003); + return true; + case 5032: + record = new EpsgProjectedCrsRecord(32204, "WGS 72 / UTM zone 4N", 4322, 4400, 16004); + return true; + case 5033: + record = new EpsgProjectedCrsRecord(32205, "WGS 72 / UTM zone 5N", 4322, 4400, 16005); + return true; + case 5034: + record = new EpsgProjectedCrsRecord(32206, "WGS 72 / UTM zone 6N", 4322, 4400, 16006); + return true; + case 5035: + record = new EpsgProjectedCrsRecord(32207, "WGS 72 / UTM zone 7N", 4322, 4400, 16007); + return true; + case 5036: + record = new EpsgProjectedCrsRecord(32208, "WGS 72 / UTM zone 8N", 4322, 4400, 16008); + return true; + case 5037: + record = new EpsgProjectedCrsRecord(32209, "WGS 72 / UTM zone 9N", 4322, 4400, 16009); + return true; + case 5038: + record = new EpsgProjectedCrsRecord(32210, "WGS 72 / UTM zone 10N", 4322, 4400, 16010); + return true; + case 5039: + record = new EpsgProjectedCrsRecord(32211, "WGS 72 / UTM zone 11N", 4322, 4400, 16011); + return true; + case 5040: + record = new EpsgProjectedCrsRecord(32212, "WGS 72 / UTM zone 12N", 4322, 4400, 16012); + return true; + case 5041: + record = new EpsgProjectedCrsRecord(32213, "WGS 72 / UTM zone 13N", 4322, 4400, 16013); + return true; + case 5042: + record = new EpsgProjectedCrsRecord(32214, "WGS 72 / UTM zone 14N", 4322, 4400, 16014); + return true; + case 5043: + record = new EpsgProjectedCrsRecord(32215, "WGS 72 / UTM zone 15N", 4322, 4400, 16015); + return true; + case 5044: + record = new EpsgProjectedCrsRecord(32216, "WGS 72 / UTM zone 16N", 4322, 4400, 16016); + return true; + case 5045: + record = new EpsgProjectedCrsRecord(32217, "WGS 72 / UTM zone 17N", 4322, 4400, 16017); + return true; + case 5046: + record = new EpsgProjectedCrsRecord(32218, "WGS 72 / UTM zone 18N", 4322, 4400, 16018); + return true; + case 5047: + record = new EpsgProjectedCrsRecord(32219, "WGS 72 / UTM zone 19N", 4322, 4400, 16019); + return true; + case 5048: + record = new EpsgProjectedCrsRecord(32220, "WGS 72 / UTM zone 20N", 4322, 4400, 16020); + return true; + case 5049: + record = new EpsgProjectedCrsRecord(32221, "WGS 72 / UTM zone 21N", 4322, 4400, 16021); + return true; + case 5050: + record = new EpsgProjectedCrsRecord(32222, "WGS 72 / UTM zone 22N", 4322, 4400, 16022); + return true; + case 5051: + record = new EpsgProjectedCrsRecord(32223, "WGS 72 / UTM zone 23N", 4322, 4400, 16023); + return true; + case 5052: + record = new EpsgProjectedCrsRecord(32224, "WGS 72 / UTM zone 24N", 4322, 4400, 16024); + return true; + case 5053: + record = new EpsgProjectedCrsRecord(32225, "WGS 72 / UTM zone 25N", 4322, 4400, 16025); + return true; + case 5054: + record = new EpsgProjectedCrsRecord(32226, "WGS 72 / UTM zone 26N", 4322, 4400, 16026); + return true; + case 5055: + record = new EpsgProjectedCrsRecord(32227, "WGS 72 / UTM zone 27N", 4322, 4400, 16027); + return true; + case 5056: + record = new EpsgProjectedCrsRecord(32228, "WGS 72 / UTM zone 28N", 4322, 4400, 16028); + return true; + case 5057: + record = new EpsgProjectedCrsRecord(32229, "WGS 72 / UTM zone 29N", 4322, 4400, 16029); + return true; + case 5058: + record = new EpsgProjectedCrsRecord(32230, "WGS 72 / UTM zone 30N", 4322, 4400, 16030); + return true; + case 5059: + record = new EpsgProjectedCrsRecord(32231, "WGS 72 / UTM zone 31N", 4322, 4400, 16031); + return true; + case 5060: + record = new EpsgProjectedCrsRecord(32232, "WGS 72 / UTM zone 32N", 4322, 4400, 16032); + return true; + case 5061: + record = new EpsgProjectedCrsRecord(32233, "WGS 72 / UTM zone 33N", 4322, 4400, 16033); + return true; + case 5062: + record = new EpsgProjectedCrsRecord(32234, "WGS 72 / UTM zone 34N", 4322, 4400, 16034); + return true; + case 5063: + record = new EpsgProjectedCrsRecord(32235, "WGS 72 / UTM zone 35N", 4322, 4400, 16035); + return true; + case 5064: + record = new EpsgProjectedCrsRecord(32236, "WGS 72 / UTM zone 36N", 4322, 4400, 16036); + return true; + case 5065: + record = new EpsgProjectedCrsRecord(32237, "WGS 72 / UTM zone 37N", 4322, 4400, 16037); + return true; + case 5066: + record = new EpsgProjectedCrsRecord(32238, "WGS 72 / UTM zone 38N", 4322, 4400, 16038); + return true; + case 5067: + record = new EpsgProjectedCrsRecord(32239, "WGS 72 / UTM zone 39N", 4322, 4400, 16039); + return true; + case 5068: + record = new EpsgProjectedCrsRecord(32240, "WGS 72 / UTM zone 40N", 4322, 4400, 16040); + return true; + case 5069: + record = new EpsgProjectedCrsRecord(32241, "WGS 72 / UTM zone 41N", 4322, 4400, 16041); + return true; + case 5070: + record = new EpsgProjectedCrsRecord(32242, "WGS 72 / UTM zone 42N", 4322, 4400, 16042); + return true; + case 5071: + record = new EpsgProjectedCrsRecord(32243, "WGS 72 / UTM zone 43N", 4322, 4400, 16043); + return true; + case 5072: + record = new EpsgProjectedCrsRecord(32244, "WGS 72 / UTM zone 44N", 4322, 4400, 16044); + return true; + case 5073: + record = new EpsgProjectedCrsRecord(32245, "WGS 72 / UTM zone 45N", 4322, 4400, 16045); + return true; + case 5074: + record = new EpsgProjectedCrsRecord(32246, "WGS 72 / UTM zone 46N", 4322, 4400, 16046); + return true; + case 5075: + record = new EpsgProjectedCrsRecord(32247, "WGS 72 / UTM zone 47N", 4322, 4400, 16047); + return true; + case 5076: + record = new EpsgProjectedCrsRecord(32248, "WGS 72 / UTM zone 48N", 4322, 4400, 16048); + return true; + case 5077: + record = new EpsgProjectedCrsRecord(32249, "WGS 72 / UTM zone 49N", 4322, 4400, 16049); + return true; + case 5078: + record = new EpsgProjectedCrsRecord(32250, "WGS 72 / UTM zone 50N", 4322, 4400, 16050); + return true; + case 5079: + record = new EpsgProjectedCrsRecord(32251, "WGS 72 / UTM zone 51N", 4322, 4400, 16051); + return true; + case 5080: + record = new EpsgProjectedCrsRecord(32252, "WGS 72 / UTM zone 52N", 4322, 4400, 16052); + return true; + case 5081: + record = new EpsgProjectedCrsRecord(32253, "WGS 72 / UTM zone 53N", 4322, 4400, 16053); + return true; + case 5082: + record = new EpsgProjectedCrsRecord(32254, "WGS 72 / UTM zone 54N", 4322, 4400, 16054); + return true; + case 5083: + record = new EpsgProjectedCrsRecord(32255, "WGS 72 / UTM zone 55N", 4322, 4400, 16055); + return true; + case 5084: + record = new EpsgProjectedCrsRecord(32256, "WGS 72 / UTM zone 56N", 4322, 4400, 16056); + return true; + case 5085: + record = new EpsgProjectedCrsRecord(32257, "WGS 72 / UTM zone 57N", 4322, 4400, 16057); + return true; + case 5086: + record = new EpsgProjectedCrsRecord(32258, "WGS 72 / UTM zone 58N", 4322, 4400, 16058); + return true; + case 5087: + record = new EpsgProjectedCrsRecord(32259, "WGS 72 / UTM zone 59N", 4322, 4400, 16059); + return true; + case 5088: + record = new EpsgProjectedCrsRecord(32260, "WGS 72 / UTM zone 60N", 4322, 4400, 16060); + return true; + case 5089: + record = new EpsgProjectedCrsRecord(32301, "WGS 72 / UTM zone 1S", 4322, 4400, 16101); + return true; + case 5090: + record = new EpsgProjectedCrsRecord(32302, "WGS 72 / UTM zone 2S", 4322, 4400, 16102); + return true; + case 5091: + record = new EpsgProjectedCrsRecord(32303, "WGS 72 / UTM zone 3S", 4322, 4400, 16103); + return true; + case 5092: + record = new EpsgProjectedCrsRecord(32304, "WGS 72 / UTM zone 4S", 4322, 4400, 16104); + return true; + case 5093: + record = new EpsgProjectedCrsRecord(32305, "WGS 72 / UTM zone 5S", 4322, 4400, 16105); + return true; + case 5094: + record = new EpsgProjectedCrsRecord(32306, "WGS 72 / UTM zone 6S", 4322, 4400, 16106); + return true; + case 5095: + record = new EpsgProjectedCrsRecord(32307, "WGS 72 / UTM zone 7S", 4322, 4400, 16107); + return true; + case 5096: + record = new EpsgProjectedCrsRecord(32308, "WGS 72 / UTM zone 8S", 4322, 4400, 16108); + return true; + case 5097: + record = new EpsgProjectedCrsRecord(32309, "WGS 72 / UTM zone 9S", 4322, 4400, 16109); + return true; + case 5098: + record = new EpsgProjectedCrsRecord(32310, "WGS 72 / UTM zone 10S", 4322, 4400, 16110); + return true; + case 5099: + record = new EpsgProjectedCrsRecord(32311, "WGS 72 / UTM zone 11S", 4322, 4400, 16111); + return true; + case 5100: + record = new EpsgProjectedCrsRecord(32312, "WGS 72 / UTM zone 12S", 4322, 4400, 16112); + return true; + case 5101: + record = new EpsgProjectedCrsRecord(32313, "WGS 72 / UTM zone 13S", 4322, 4400, 16113); + return true; + case 5102: + record = new EpsgProjectedCrsRecord(32314, "WGS 72 / UTM zone 14S", 4322, 4400, 16114); + return true; + case 5103: + record = new EpsgProjectedCrsRecord(32315, "WGS 72 / UTM zone 15S", 4322, 4400, 16115); + return true; + case 5104: + record = new EpsgProjectedCrsRecord(32316, "WGS 72 / UTM zone 16S", 4322, 4400, 16116); + return true; + case 5105: + record = new EpsgProjectedCrsRecord(32317, "WGS 72 / UTM zone 17S", 4322, 4400, 16117); + return true; + case 5106: + record = new EpsgProjectedCrsRecord(32318, "WGS 72 / UTM zone 18S", 4322, 4400, 16118); + return true; + case 5107: + record = new EpsgProjectedCrsRecord(32319, "WGS 72 / UTM zone 19S", 4322, 4400, 16119); + return true; + case 5108: + record = new EpsgProjectedCrsRecord(32320, "WGS 72 / UTM zone 20S", 4322, 4400, 16120); + return true; + case 5109: + record = new EpsgProjectedCrsRecord(32321, "WGS 72 / UTM zone 21S", 4322, 4400, 16121); + return true; + case 5110: + record = new EpsgProjectedCrsRecord(32322, "WGS 72 / UTM zone 22S", 4322, 4400, 16122); + return true; + case 5111: + record = new EpsgProjectedCrsRecord(32323, "WGS 72 / UTM zone 23S", 4322, 4400, 16123); + return true; + case 5112: + record = new EpsgProjectedCrsRecord(32324, "WGS 72 / UTM zone 24S", 4322, 4400, 16124); + return true; + case 5113: + record = new EpsgProjectedCrsRecord(32325, "WGS 72 / UTM zone 25S", 4322, 4400, 16125); + return true; + case 5114: + record = new EpsgProjectedCrsRecord(32326, "WGS 72 / UTM zone 26S", 4322, 4400, 16126); + return true; + case 5115: + record = new EpsgProjectedCrsRecord(32327, "WGS 72 / UTM zone 27S", 4322, 4400, 16127); + return true; + case 5116: + record = new EpsgProjectedCrsRecord(32328, "WGS 72 / UTM zone 28S", 4322, 4400, 16128); + return true; + case 5117: + record = new EpsgProjectedCrsRecord(32329, "WGS 72 / UTM zone 29S", 4322, 4400, 16129); + return true; + case 5118: + record = new EpsgProjectedCrsRecord(32330, "WGS 72 / UTM zone 30S", 4322, 4400, 16130); + return true; + case 5119: + record = new EpsgProjectedCrsRecord(32331, "WGS 72 / UTM zone 31S", 4322, 4400, 16131); + return true; + case 5120: + record = new EpsgProjectedCrsRecord(32332, "WGS 72 / UTM zone 32S", 4322, 4400, 16132); + return true; + case 5121: + record = new EpsgProjectedCrsRecord(32333, "WGS 72 / UTM zone 33S", 4322, 4400, 16133); + return true; + case 5122: + record = new EpsgProjectedCrsRecord(32334, "WGS 72 / UTM zone 34S", 4322, 4400, 16134); + return true; + case 5123: + record = new EpsgProjectedCrsRecord(32335, "WGS 72 / UTM zone 35S", 4322, 4400, 16135); + return true; + case 5124: + record = new EpsgProjectedCrsRecord(32336, "WGS 72 / UTM zone 36S", 4322, 4400, 16136); + return true; + case 5125: + record = new EpsgProjectedCrsRecord(32337, "WGS 72 / UTM zone 37S", 4322, 4400, 16137); + return true; + case 5126: + record = new EpsgProjectedCrsRecord(32338, "WGS 72 / UTM zone 38S", 4322, 4400, 16138); + return true; + case 5127: + record = new EpsgProjectedCrsRecord(32339, "WGS 72 / UTM zone 39S", 4322, 4400, 16139); + return true; + case 5128: + record = new EpsgProjectedCrsRecord(32340, "WGS 72 / UTM zone 40S", 4322, 4400, 16140); + return true; + case 5129: + record = new EpsgProjectedCrsRecord(32341, "WGS 72 / UTM zone 41S", 4322, 4400, 16141); + return true; + case 5130: + record = new EpsgProjectedCrsRecord(32342, "WGS 72 / UTM zone 42S", 4322, 4400, 16142); + return true; + case 5131: + record = new EpsgProjectedCrsRecord(32343, "WGS 72 / UTM zone 43S", 4322, 4400, 16143); + return true; + case 5132: + record = new EpsgProjectedCrsRecord(32344, "WGS 72 / UTM zone 44S", 4322, 4400, 16144); + return true; + case 5133: + record = new EpsgProjectedCrsRecord(32345, "WGS 72 / UTM zone 45S", 4322, 4400, 16145); + return true; + case 5134: + record = new EpsgProjectedCrsRecord(32346, "WGS 72 / UTM zone 46S", 4322, 4400, 16146); + return true; + case 5135: + record = new EpsgProjectedCrsRecord(32347, "WGS 72 / UTM zone 47S", 4322, 4400, 16147); + return true; + case 5136: + record = new EpsgProjectedCrsRecord(32348, "WGS 72 / UTM zone 48S", 4322, 4400, 16148); + return true; + case 5137: + record = new EpsgProjectedCrsRecord(32349, "WGS 72 / UTM zone 49S", 4322, 4400, 16149); + return true; + case 5138: + record = new EpsgProjectedCrsRecord(32350, "WGS 72 / UTM zone 50S", 4322, 4400, 16150); + return true; + case 5139: + record = new EpsgProjectedCrsRecord(32351, "WGS 72 / UTM zone 51S", 4322, 4400, 16151); + return true; + case 5140: + record = new EpsgProjectedCrsRecord(32352, "WGS 72 / UTM zone 52S", 4322, 4400, 16152); + return true; + case 5141: + record = new EpsgProjectedCrsRecord(32353, "WGS 72 / UTM zone 53S", 4322, 4400, 16153); + return true; + case 5142: + record = new EpsgProjectedCrsRecord(32354, "WGS 72 / UTM zone 54S", 4322, 4400, 16154); + return true; + case 5143: + record = new EpsgProjectedCrsRecord(32355, "WGS 72 / UTM zone 55S", 4322, 4400, 16155); + return true; + case 5144: + record = new EpsgProjectedCrsRecord(32356, "WGS 72 / UTM zone 56S", 4322, 4400, 16156); + return true; + case 5145: + record = new EpsgProjectedCrsRecord(32357, "WGS 72 / UTM zone 57S", 4322, 4400, 16157); + return true; + case 5146: + record = new EpsgProjectedCrsRecord(32358, "WGS 72 / UTM zone 58S", 4322, 4400, 16158); + return true; + case 5147: + record = new EpsgProjectedCrsRecord(32359, "WGS 72 / UTM zone 59S", 4322, 4400, 16159); + return true; + case 5148: + record = new EpsgProjectedCrsRecord(32360, "WGS 72 / UTM zone 60S", 4322, 4400, 16160); + return true; + case 5149: + record = new EpsgProjectedCrsRecord(32401, "WGS 72BE / UTM zone 1N", 4324, 4400, 16001); + return true; + case 5150: + record = new EpsgProjectedCrsRecord(32402, "WGS 72BE / UTM zone 2N", 4324, 4400, 16002); + return true; + case 5151: + record = new EpsgProjectedCrsRecord(32403, "WGS 72BE / UTM zone 3N", 4324, 4400, 16003); + return true; + case 5152: + record = new EpsgProjectedCrsRecord(32404, "WGS 72BE / UTM zone 4N", 4324, 4400, 16004); + return true; + case 5153: + record = new EpsgProjectedCrsRecord(32405, "WGS 72BE / UTM zone 5N", 4324, 4400, 16005); + return true; + case 5154: + record = new EpsgProjectedCrsRecord(32406, "WGS 72BE / UTM zone 6N", 4324, 4400, 16006); + return true; + case 5155: + record = new EpsgProjectedCrsRecord(32407, "WGS 72BE / UTM zone 7N", 4324, 4400, 16007); + return true; + case 5156: + record = new EpsgProjectedCrsRecord(32408, "WGS 72BE / UTM zone 8N", 4324, 4400, 16008); + return true; + case 5157: + record = new EpsgProjectedCrsRecord(32409, "WGS 72BE / UTM zone 9N", 4324, 4400, 16009); + return true; + case 5158: + record = new EpsgProjectedCrsRecord(32410, "WGS 72BE / UTM zone 10N", 4324, 4400, 16010); + return true; + case 5159: + record = new EpsgProjectedCrsRecord(32411, "WGS 72BE / UTM zone 11N", 4324, 4400, 16011); + return true; + case 5160: + record = new EpsgProjectedCrsRecord(32412, "WGS 72BE / UTM zone 12N", 4324, 4400, 16012); + return true; + case 5161: + record = new EpsgProjectedCrsRecord(32413, "WGS 72BE / UTM zone 13N", 4324, 4400, 16013); + return true; + case 5162: + record = new EpsgProjectedCrsRecord(32414, "WGS 72BE / UTM zone 14N", 4324, 4400, 16014); + return true; + case 5163: + record = new EpsgProjectedCrsRecord(32415, "WGS 72BE / UTM zone 15N", 4324, 4400, 16015); + return true; + case 5164: + record = new EpsgProjectedCrsRecord(32416, "WGS 72BE / UTM zone 16N", 4324, 4400, 16016); + return true; + case 5165: + record = new EpsgProjectedCrsRecord(32417, "WGS 72BE / UTM zone 17N", 4324, 4400, 16017); + return true; + case 5166: + record = new EpsgProjectedCrsRecord(32418, "WGS 72BE / UTM zone 18N", 4324, 4400, 16018); + return true; + case 5167: + record = new EpsgProjectedCrsRecord(32419, "WGS 72BE / UTM zone 19N", 4324, 4400, 16019); + return true; + case 5168: + record = new EpsgProjectedCrsRecord(32420, "WGS 72BE / UTM zone 20N", 4324, 4400, 16020); + return true; + case 5169: + record = new EpsgProjectedCrsRecord(32421, "WGS 72BE / UTM zone 21N", 4324, 4400, 16021); + return true; + case 5170: + record = new EpsgProjectedCrsRecord(32422, "WGS 72BE / UTM zone 22N", 4324, 4400, 16022); + return true; + case 5171: + record = new EpsgProjectedCrsRecord(32423, "WGS 72BE / UTM zone 23N", 4324, 4400, 16023); + return true; + case 5172: + record = new EpsgProjectedCrsRecord(32424, "WGS 72BE / UTM zone 24N", 4324, 4400, 16024); + return true; + case 5173: + record = new EpsgProjectedCrsRecord(32425, "WGS 72BE / UTM zone 25N", 4324, 4400, 16025); + return true; + case 5174: + record = new EpsgProjectedCrsRecord(32426, "WGS 72BE / UTM zone 26N", 4324, 4400, 16026); + return true; + case 5175: + record = new EpsgProjectedCrsRecord(32427, "WGS 72BE / UTM zone 27N", 4324, 4400, 16027); + return true; + case 5176: + record = new EpsgProjectedCrsRecord(32428, "WGS 72BE / UTM zone 28N", 4324, 4400, 16028); + return true; + case 5177: + record = new EpsgProjectedCrsRecord(32429, "WGS 72BE / UTM zone 29N", 4324, 4400, 16029); + return true; + case 5178: + record = new EpsgProjectedCrsRecord(32430, "WGS 72BE / UTM zone 30N", 4324, 4400, 16030); + return true; + case 5179: + record = new EpsgProjectedCrsRecord(32431, "WGS 72BE / UTM zone 31N", 4324, 4400, 16031); + return true; + case 5180: + record = new EpsgProjectedCrsRecord(32432, "WGS 72BE / UTM zone 32N", 4324, 4400, 16032); + return true; + case 5181: + record = new EpsgProjectedCrsRecord(32433, "WGS 72BE / UTM zone 33N", 4324, 4400, 16033); + return true; + case 5182: + record = new EpsgProjectedCrsRecord(32434, "WGS 72BE / UTM zone 34N", 4324, 4400, 16034); + return true; + case 5183: + record = new EpsgProjectedCrsRecord(32435, "WGS 72BE / UTM zone 35N", 4324, 4400, 16035); + return true; + case 5184: + record = new EpsgProjectedCrsRecord(32436, "WGS 72BE / UTM zone 36N", 4324, 4400, 16036); + return true; + case 5185: + record = new EpsgProjectedCrsRecord(32437, "WGS 72BE / UTM zone 37N", 4324, 4400, 16037); + return true; + case 5186: + record = new EpsgProjectedCrsRecord(32438, "WGS 72BE / UTM zone 38N", 4324, 4400, 16038); + return true; + case 5187: + record = new EpsgProjectedCrsRecord(32439, "WGS 72BE / UTM zone 39N", 4324, 4400, 16039); + return true; + case 5188: + record = new EpsgProjectedCrsRecord(32440, "WGS 72BE / UTM zone 40N", 4324, 4400, 16040); + return true; + case 5189: + record = new EpsgProjectedCrsRecord(32441, "WGS 72BE / UTM zone 41N", 4324, 4400, 16041); + return true; + case 5190: + record = new EpsgProjectedCrsRecord(32442, "WGS 72BE / UTM zone 42N", 4324, 4400, 16042); + return true; + case 5191: + record = new EpsgProjectedCrsRecord(32443, "WGS 72BE / UTM zone 43N", 4324, 4400, 16043); + return true; + case 5192: + record = new EpsgProjectedCrsRecord(32444, "WGS 72BE / UTM zone 44N", 4324, 4400, 16044); + return true; + case 5193: + record = new EpsgProjectedCrsRecord(32445, "WGS 72BE / UTM zone 45N", 4324, 4400, 16045); + return true; + case 5194: + record = new EpsgProjectedCrsRecord(32446, "WGS 72BE / UTM zone 46N", 4324, 4400, 16046); + return true; + case 5195: + record = new EpsgProjectedCrsRecord(32447, "WGS 72BE / UTM zone 47N", 4324, 4400, 16047); + return true; + case 5196: + record = new EpsgProjectedCrsRecord(32448, "WGS 72BE / UTM zone 48N", 4324, 4400, 16048); + return true; + case 5197: + record = new EpsgProjectedCrsRecord(32449, "WGS 72BE / UTM zone 49N", 4324, 4400, 16049); + return true; + case 5198: + record = new EpsgProjectedCrsRecord(32450, "WGS 72BE / UTM zone 50N", 4324, 4400, 16050); + return true; + case 5199: + record = new EpsgProjectedCrsRecord(32451, "WGS 72BE / UTM zone 51N", 4324, 4400, 16051); + return true; + case 5200: + record = new EpsgProjectedCrsRecord(32452, "WGS 72BE / UTM zone 52N", 4324, 4400, 16052); + return true; + case 5201: + record = new EpsgProjectedCrsRecord(32453, "WGS 72BE / UTM zone 53N", 4324, 4400, 16053); + return true; + case 5202: + record = new EpsgProjectedCrsRecord(32454, "WGS 72BE / UTM zone 54N", 4324, 4400, 16054); + return true; + case 5203: + record = new EpsgProjectedCrsRecord(32455, "WGS 72BE / UTM zone 55N", 4324, 4400, 16055); + return true; + case 5204: + record = new EpsgProjectedCrsRecord(32456, "WGS 72BE / UTM zone 56N", 4324, 4400, 16056); + return true; + case 5205: + record = new EpsgProjectedCrsRecord(32457, "WGS 72BE / UTM zone 57N", 4324, 4400, 16057); + return true; + case 5206: + record = new EpsgProjectedCrsRecord(32458, "WGS 72BE / UTM zone 58N", 4324, 4400, 16058); + return true; + case 5207: + record = new EpsgProjectedCrsRecord(32459, "WGS 72BE / UTM zone 59N", 4324, 4400, 16059); + return true; + case 5208: + record = new EpsgProjectedCrsRecord(32460, "WGS 72BE / UTM zone 60N", 4324, 4400, 16060); + return true; + case 5209: + record = new EpsgProjectedCrsRecord(32501, "WGS 72BE / UTM zone 1S", 4324, 4400, 16101); + return true; + case 5210: + record = new EpsgProjectedCrsRecord(32502, "WGS 72BE / UTM zone 2S", 4324, 4400, 16102); + return true; + case 5211: + record = new EpsgProjectedCrsRecord(32503, "WGS 72BE / UTM zone 3S", 4324, 4400, 16103); + return true; + case 5212: + record = new EpsgProjectedCrsRecord(32504, "WGS 72BE / UTM zone 4S", 4324, 4400, 16104); + return true; + case 5213: + record = new EpsgProjectedCrsRecord(32505, "WGS 72BE / UTM zone 5S", 4324, 4400, 16105); + return true; + case 5214: + record = new EpsgProjectedCrsRecord(32506, "WGS 72BE / UTM zone 6S", 4324, 4400, 16106); + return true; + case 5215: + record = new EpsgProjectedCrsRecord(32507, "WGS 72BE / UTM zone 7S", 4324, 4400, 16107); + return true; + case 5216: + record = new EpsgProjectedCrsRecord(32508, "WGS 72BE / UTM zone 8S", 4324, 4400, 16108); + return true; + case 5217: + record = new EpsgProjectedCrsRecord(32509, "WGS 72BE / UTM zone 9S", 4324, 4400, 16109); + return true; + case 5218: + record = new EpsgProjectedCrsRecord(32510, "WGS 72BE / UTM zone 10S", 4324, 4400, 16110); + return true; + case 5219: + record = new EpsgProjectedCrsRecord(32511, "WGS 72BE / UTM zone 11S", 4324, 4400, 16111); + return true; + case 5220: + record = new EpsgProjectedCrsRecord(32512, "WGS 72BE / UTM zone 12S", 4324, 4400, 16112); + return true; + case 5221: + record = new EpsgProjectedCrsRecord(32513, "WGS 72BE / UTM zone 13S", 4324, 4400, 16113); + return true; + case 5222: + record = new EpsgProjectedCrsRecord(32514, "WGS 72BE / UTM zone 14S", 4324, 4400, 16114); + return true; + case 5223: + record = new EpsgProjectedCrsRecord(32515, "WGS 72BE / UTM zone 15S", 4324, 4400, 16115); + return true; + case 5224: + record = new EpsgProjectedCrsRecord(32516, "WGS 72BE / UTM zone 16S", 4324, 4400, 16116); + return true; + case 5225: + record = new EpsgProjectedCrsRecord(32517, "WGS 72BE / UTM zone 17S", 4324, 4400, 16117); + return true; + case 5226: + record = new EpsgProjectedCrsRecord(32518, "WGS 72BE / UTM zone 18S", 4324, 4400, 16118); + return true; + case 5227: + record = new EpsgProjectedCrsRecord(32519, "WGS 72BE / UTM zone 19S", 4324, 4400, 16119); + return true; + case 5228: + record = new EpsgProjectedCrsRecord(32520, "WGS 72BE / UTM zone 20S", 4324, 4400, 16120); + return true; + case 5229: + record = new EpsgProjectedCrsRecord(32521, "WGS 72BE / UTM zone 21S", 4324, 4400, 16121); + return true; + case 5230: + record = new EpsgProjectedCrsRecord(32522, "WGS 72BE / UTM zone 22S", 4324, 4400, 16122); + return true; + case 5231: + record = new EpsgProjectedCrsRecord(32523, "WGS 72BE / UTM zone 23S", 4324, 4400, 16123); + return true; + case 5232: + record = new EpsgProjectedCrsRecord(32524, "WGS 72BE / UTM zone 24S", 4324, 4400, 16124); + return true; + case 5233: + record = new EpsgProjectedCrsRecord(32525, "WGS 72BE / UTM zone 25S", 4324, 4400, 16125); + return true; + case 5234: + record = new EpsgProjectedCrsRecord(32526, "WGS 72BE / UTM zone 26S", 4324, 4400, 16126); + return true; + case 5235: + record = new EpsgProjectedCrsRecord(32527, "WGS 72BE / UTM zone 27S", 4324, 4400, 16127); + return true; + case 5236: + record = new EpsgProjectedCrsRecord(32528, "WGS 72BE / UTM zone 28S", 4324, 4400, 16128); + return true; + case 5237: + record = new EpsgProjectedCrsRecord(32529, "WGS 72BE / UTM zone 29S", 4324, 4400, 16129); + return true; + case 5238: + record = new EpsgProjectedCrsRecord(32530, "WGS 72BE / UTM zone 30S", 4324, 4400, 16130); + return true; + case 5239: + record = new EpsgProjectedCrsRecord(32531, "WGS 72BE / UTM zone 31S", 4324, 4400, 16131); + return true; + case 5240: + record = new EpsgProjectedCrsRecord(32532, "WGS 72BE / UTM zone 32S", 4324, 4400, 16132); + return true; + case 5241: + record = new EpsgProjectedCrsRecord(32533, "WGS 72BE / UTM zone 33S", 4324, 4400, 16133); + return true; + case 5242: + record = new EpsgProjectedCrsRecord(32534, "WGS 72BE / UTM zone 34S", 4324, 4400, 16134); + return true; + case 5243: + record = new EpsgProjectedCrsRecord(32535, "WGS 72BE / UTM zone 35S", 4324, 4400, 16135); + return true; + case 5244: + record = new EpsgProjectedCrsRecord(32536, "WGS 72BE / UTM zone 36S", 4324, 4400, 16136); + return true; + case 5245: + record = new EpsgProjectedCrsRecord(32537, "WGS 72BE / UTM zone 37S", 4324, 4400, 16137); + return true; + case 5246: + record = new EpsgProjectedCrsRecord(32538, "WGS 72BE / UTM zone 38S", 4324, 4400, 16138); + return true; + case 5247: + record = new EpsgProjectedCrsRecord(32539, "WGS 72BE / UTM zone 39S", 4324, 4400, 16139); + return true; + case 5248: + record = new EpsgProjectedCrsRecord(32540, "WGS 72BE / UTM zone 40S", 4324, 4400, 16140); + return true; + case 5249: + record = new EpsgProjectedCrsRecord(32541, "WGS 72BE / UTM zone 41S", 4324, 4400, 16141); + return true; + case 5250: + record = new EpsgProjectedCrsRecord(32542, "WGS 72BE / UTM zone 42S", 4324, 4400, 16142); + return true; + case 5251: + record = new EpsgProjectedCrsRecord(32543, "WGS 72BE / UTM zone 43S", 4324, 4400, 16143); + return true; + case 5252: + record = new EpsgProjectedCrsRecord(32544, "WGS 72BE / UTM zone 44S", 4324, 4400, 16144); + return true; + case 5253: + record = new EpsgProjectedCrsRecord(32545, "WGS 72BE / UTM zone 45S", 4324, 4400, 16145); + return true; + case 5254: + record = new EpsgProjectedCrsRecord(32546, "WGS 72BE / UTM zone 46S", 4324, 4400, 16146); + return true; + case 5255: + record = new EpsgProjectedCrsRecord(32547, "WGS 72BE / UTM zone 47S", 4324, 4400, 16147); + return true; + case 5256: + record = new EpsgProjectedCrsRecord(32548, "WGS 72BE / UTM zone 48S", 4324, 4400, 16148); + return true; + case 5257: + record = new EpsgProjectedCrsRecord(32549, "WGS 72BE / UTM zone 49S", 4324, 4400, 16149); + return true; + case 5258: + record = new EpsgProjectedCrsRecord(32550, "WGS 72BE / UTM zone 50S", 4324, 4400, 16150); + return true; + case 5259: + record = new EpsgProjectedCrsRecord(32551, "WGS 72BE / UTM zone 51S", 4324, 4400, 16151); + return true; + case 5260: + record = new EpsgProjectedCrsRecord(32552, "WGS 72BE / UTM zone 52S", 4324, 4400, 16152); + return true; + case 5261: + record = new EpsgProjectedCrsRecord(32553, "WGS 72BE / UTM zone 53S", 4324, 4400, 16153); + return true; + case 5262: + record = new EpsgProjectedCrsRecord(32554, "WGS 72BE / UTM zone 54S", 4324, 4400, 16154); + return true; + case 5263: + record = new EpsgProjectedCrsRecord(32555, "WGS 72BE / UTM zone 55S", 4324, 4400, 16155); + return true; + case 5264: + record = new EpsgProjectedCrsRecord(32556, "WGS 72BE / UTM zone 56S", 4324, 4400, 16156); + return true; + case 5265: + record = new EpsgProjectedCrsRecord(32557, "WGS 72BE / UTM zone 57S", 4324, 4400, 16157); + return true; + case 5266: + record = new EpsgProjectedCrsRecord(32558, "WGS 72BE / UTM zone 58S", 4324, 4400, 16158); + return true; + case 5267: + record = new EpsgProjectedCrsRecord(32559, "WGS 72BE / UTM zone 59S", 4324, 4400, 16159); + return true; + case 5268: + record = new EpsgProjectedCrsRecord(32560, "WGS 72BE / UTM zone 60S", 4324, 4400, 16160); + return true; + case 5269: + record = new EpsgProjectedCrsRecord(32600, "WGS 84 / UTM grid system (northern hemisphere)", 4326, 4400, 16000); + return true; + case 5270: + record = new EpsgProjectedCrsRecord(32601, "WGS 84 / UTM zone 1N", 4326, 4400, 16001); + return true; + case 5271: + record = new EpsgProjectedCrsRecord(32602, "WGS 84 / UTM zone 2N", 4326, 4400, 16002); + return true; + case 5272: + record = new EpsgProjectedCrsRecord(32603, "WGS 84 / UTM zone 3N", 4326, 4400, 16003); + return true; + case 5273: + record = new EpsgProjectedCrsRecord(32604, "WGS 84 / UTM zone 4N", 4326, 4400, 16004); + return true; + case 5274: + record = new EpsgProjectedCrsRecord(32605, "WGS 84 / UTM zone 5N", 4326, 4400, 16005); + return true; + case 5275: + record = new EpsgProjectedCrsRecord(32606, "WGS 84 / UTM zone 6N", 4326, 4400, 16006); + return true; + case 5276: + record = new EpsgProjectedCrsRecord(32607, "WGS 84 / UTM zone 7N", 4326, 4400, 16007); + return true; + case 5277: + record = new EpsgProjectedCrsRecord(32608, "WGS 84 / UTM zone 8N", 4326, 4400, 16008); + return true; + case 5278: + record = new EpsgProjectedCrsRecord(32609, "WGS 84 / UTM zone 9N", 4326, 4400, 16009); + return true; + case 5279: + record = new EpsgProjectedCrsRecord(32610, "WGS 84 / UTM zone 10N", 4326, 4400, 16010); + return true; + case 5280: + record = new EpsgProjectedCrsRecord(32611, "WGS 84 / UTM zone 11N", 4326, 4400, 16011); + return true; + case 5281: + record = new EpsgProjectedCrsRecord(32612, "WGS 84 / UTM zone 12N", 4326, 4400, 16012); + return true; + case 5282: + record = new EpsgProjectedCrsRecord(32613, "WGS 84 / UTM zone 13N", 4326, 4400, 16013); + return true; + case 5283: + record = new EpsgProjectedCrsRecord(32614, "WGS 84 / UTM zone 14N", 4326, 4400, 16014); + return true; + case 5284: + record = new EpsgProjectedCrsRecord(32615, "WGS 84 / UTM zone 15N", 4326, 4400, 16015); + return true; + case 5285: + record = new EpsgProjectedCrsRecord(32616, "WGS 84 / UTM zone 16N", 4326, 4400, 16016); + return true; + case 5286: + record = new EpsgProjectedCrsRecord(32617, "WGS 84 / UTM zone 17N", 4326, 4400, 16017); + return true; + case 5287: + record = new EpsgProjectedCrsRecord(32618, "WGS 84 / UTM zone 18N", 4326, 4400, 16018); + return true; + case 5288: + record = new EpsgProjectedCrsRecord(32619, "WGS 84 / UTM zone 19N", 4326, 4400, 16019); + return true; + case 5289: + record = new EpsgProjectedCrsRecord(32620, "WGS 84 / UTM zone 20N", 4326, 4400, 16020); + return true; + case 5290: + record = new EpsgProjectedCrsRecord(32621, "WGS 84 / UTM zone 21N", 4326, 4400, 16021); + return true; + case 5291: + record = new EpsgProjectedCrsRecord(32622, "WGS 84 / UTM zone 22N", 4326, 4400, 16022); + return true; + case 5292: + record = new EpsgProjectedCrsRecord(32623, "WGS 84 / UTM zone 23N", 4326, 4400, 16023); + return true; + case 5293: + record = new EpsgProjectedCrsRecord(32624, "WGS 84 / UTM zone 24N", 4326, 4400, 16024); + return true; + case 5294: + record = new EpsgProjectedCrsRecord(32625, "WGS 84 / UTM zone 25N", 4326, 4400, 16025); + return true; + case 5295: + record = new EpsgProjectedCrsRecord(32626, "WGS 84 / UTM zone 26N", 4326, 4400, 16026); + return true; + case 5296: + record = new EpsgProjectedCrsRecord(32627, "WGS 84 / UTM zone 27N", 4326, 4400, 16027); + return true; + case 5297: + record = new EpsgProjectedCrsRecord(32628, "WGS 84 / UTM zone 28N", 4326, 4400, 16028); + return true; + case 5298: + record = new EpsgProjectedCrsRecord(32629, "WGS 84 / UTM zone 29N", 4326, 4400, 16029); + return true; + case 5299: + record = new EpsgProjectedCrsRecord(32630, "WGS 84 / UTM zone 30N", 4326, 4400, 16030); + return true; + case 5300: + record = new EpsgProjectedCrsRecord(32631, "WGS 84 / UTM zone 31N", 4326, 4400, 16031); + return true; + case 5301: + record = new EpsgProjectedCrsRecord(32632, "WGS 84 / UTM zone 32N", 4326, 4400, 16032); + return true; + case 5302: + record = new EpsgProjectedCrsRecord(32633, "WGS 84 / UTM zone 33N", 4326, 4400, 16033); + return true; + case 5303: + record = new EpsgProjectedCrsRecord(32634, "WGS 84 / UTM zone 34N", 4326, 4400, 16034); + return true; + case 5304: + record = new EpsgProjectedCrsRecord(32635, "WGS 84 / UTM zone 35N", 4326, 4400, 16035); + return true; + case 5305: + record = new EpsgProjectedCrsRecord(32636, "WGS 84 / UTM zone 36N", 4326, 4400, 16036); + return true; + case 5306: + record = new EpsgProjectedCrsRecord(32637, "WGS 84 / UTM zone 37N", 4326, 4400, 16037); + return true; + case 5307: + record = new EpsgProjectedCrsRecord(32638, "WGS 84 / UTM zone 38N", 4326, 4400, 16038); + return true; + case 5308: + record = new EpsgProjectedCrsRecord(32639, "WGS 84 / UTM zone 39N", 4326, 4400, 16039); + return true; + case 5309: + record = new EpsgProjectedCrsRecord(32640, "WGS 84 / UTM zone 40N", 4326, 4400, 16040); + return true; + case 5310: + record = new EpsgProjectedCrsRecord(32641, "WGS 84 / UTM zone 41N", 4326, 4400, 16041); + return true; + case 5311: + record = new EpsgProjectedCrsRecord(32642, "WGS 84 / UTM zone 42N", 4326, 4400, 16042); + return true; + case 5312: + record = new EpsgProjectedCrsRecord(32643, "WGS 84 / UTM zone 43N", 4326, 4400, 16043); + return true; + case 5313: + record = new EpsgProjectedCrsRecord(32644, "WGS 84 / UTM zone 44N", 4326, 4400, 16044); + return true; + case 5314: + record = new EpsgProjectedCrsRecord(32645, "WGS 84 / UTM zone 45N", 4326, 4400, 16045); + return true; + case 5315: + record = new EpsgProjectedCrsRecord(32646, "WGS 84 / UTM zone 46N", 4326, 4400, 16046); + return true; + case 5316: + record = new EpsgProjectedCrsRecord(32647, "WGS 84 / UTM zone 47N", 4326, 4400, 16047); + return true; + case 5317: + record = new EpsgProjectedCrsRecord(32648, "WGS 84 / UTM zone 48N", 4326, 4400, 16048); + return true; + case 5318: + record = new EpsgProjectedCrsRecord(32649, "WGS 84 / UTM zone 49N", 4326, 4400, 16049); + return true; + case 5319: + record = new EpsgProjectedCrsRecord(32650, "WGS 84 / UTM zone 50N", 4326, 4400, 16050); + return true; + case 5320: + record = new EpsgProjectedCrsRecord(32651, "WGS 84 / UTM zone 51N", 4326, 4400, 16051); + return true; + case 5321: + record = new EpsgProjectedCrsRecord(32652, "WGS 84 / UTM zone 52N", 4326, 4400, 16052); + return true; + case 5322: + record = new EpsgProjectedCrsRecord(32653, "WGS 84 / UTM zone 53N", 4326, 4400, 16053); + return true; + case 5323: + record = new EpsgProjectedCrsRecord(32654, "WGS 84 / UTM zone 54N", 4326, 4400, 16054); + return true; + case 5324: + record = new EpsgProjectedCrsRecord(32655, "WGS 84 / UTM zone 55N", 4326, 4400, 16055); + return true; + case 5325: + record = new EpsgProjectedCrsRecord(32656, "WGS 84 / UTM zone 56N", 4326, 4400, 16056); + return true; + case 5326: + record = new EpsgProjectedCrsRecord(32657, "WGS 84 / UTM zone 57N", 4326, 4400, 16057); + return true; + case 5327: + record = new EpsgProjectedCrsRecord(32658, "WGS 84 / UTM zone 58N", 4326, 4400, 16058); + return true; + case 5328: + record = new EpsgProjectedCrsRecord(32659, "WGS 84 / UTM zone 59N", 4326, 4400, 16059); + return true; + case 5329: + record = new EpsgProjectedCrsRecord(32660, "WGS 84 / UTM zone 60N", 4326, 4400, 16060); + return true; + case 5330: + record = new EpsgProjectedCrsRecord(32661, "WGS 84 / UPS North (N,E)", 4326, 4493, 16061); + return true; + case 5331: + record = new EpsgProjectedCrsRecord(32664, "WGS 84 / BLM 14N (ftUS)", 4326, 4497, 15914); + return true; + case 5332: + record = new EpsgProjectedCrsRecord(32665, "WGS 84 / BLM 15N (ftUS)", 4326, 4497, 15915); + return true; + case 5333: + record = new EpsgProjectedCrsRecord(32666, "WGS 84 / BLM 16N (ftUS)", 4326, 4497, 15916); + return true; + case 5334: + record = new EpsgProjectedCrsRecord(32667, "WGS 84 / BLM 17N (ftUS)", 4326, 4497, 15917); + return true; + case 5335: + record = new EpsgProjectedCrsRecord(32700, "WGS 84 / UTM grid system (southern hemisphere)", 4326, 4400, 16100); + return true; + case 5336: + record = new EpsgProjectedCrsRecord(32701, "WGS 84 / UTM zone 1S", 4326, 4400, 16101); + return true; + case 5337: + record = new EpsgProjectedCrsRecord(32702, "WGS 84 / UTM zone 2S", 4326, 4400, 16102); + return true; + case 5338: + record = new EpsgProjectedCrsRecord(32703, "WGS 84 / UTM zone 3S", 4326, 4400, 16103); + return true; + case 5339: + record = new EpsgProjectedCrsRecord(32704, "WGS 84 / UTM zone 4S", 4326, 4400, 16104); + return true; + case 5340: + record = new EpsgProjectedCrsRecord(32705, "WGS 84 / UTM zone 5S", 4326, 4400, 16105); + return true; + case 5341: + record = new EpsgProjectedCrsRecord(32706, "WGS 84 / UTM zone 6S", 4326, 4400, 16106); + return true; + case 5342: + record = new EpsgProjectedCrsRecord(32707, "WGS 84 / UTM zone 7S", 4326, 4400, 16107); + return true; + case 5343: + record = new EpsgProjectedCrsRecord(32708, "WGS 84 / UTM zone 8S", 4326, 4400, 16108); + return true; + case 5344: + record = new EpsgProjectedCrsRecord(32709, "WGS 84 / UTM zone 9S", 4326, 4400, 16109); + return true; + case 5345: + record = new EpsgProjectedCrsRecord(32710, "WGS 84 / UTM zone 10S", 4326, 4400, 16110); + return true; + case 5346: + record = new EpsgProjectedCrsRecord(32711, "WGS 84 / UTM zone 11S", 4326, 4400, 16111); + return true; + case 5347: + record = new EpsgProjectedCrsRecord(32712, "WGS 84 / UTM zone 12S", 4326, 4400, 16112); + return true; + case 5348: + record = new EpsgProjectedCrsRecord(32713, "WGS 84 / UTM zone 13S", 4326, 4400, 16113); + return true; + case 5349: + record = new EpsgProjectedCrsRecord(32714, "WGS 84 / UTM zone 14S", 4326, 4400, 16114); + return true; + case 5350: + record = new EpsgProjectedCrsRecord(32715, "WGS 84 / UTM zone 15S", 4326, 4400, 16115); + return true; + case 5351: + record = new EpsgProjectedCrsRecord(32716, "WGS 84 / UTM zone 16S", 4326, 4400, 16116); + return true; + case 5352: + record = new EpsgProjectedCrsRecord(32717, "WGS 84 / UTM zone 17S", 4326, 4400, 16117); + return true; + case 5353: + record = new EpsgProjectedCrsRecord(32718, "WGS 84 / UTM zone 18S", 4326, 4400, 16118); + return true; + case 5354: + record = new EpsgProjectedCrsRecord(32719, "WGS 84 / UTM zone 19S", 4326, 4400, 16119); + return true; + case 5355: + record = new EpsgProjectedCrsRecord(32720, "WGS 84 / UTM zone 20S", 4326, 4400, 16120); + return true; + case 5356: + record = new EpsgProjectedCrsRecord(32721, "WGS 84 / UTM zone 21S", 4326, 4400, 16121); + return true; + case 5357: + record = new EpsgProjectedCrsRecord(32722, "WGS 84 / UTM zone 22S", 4326, 4400, 16122); + return true; + case 5358: + record = new EpsgProjectedCrsRecord(32723, "WGS 84 / UTM zone 23S", 4326, 4400, 16123); + return true; + case 5359: + record = new EpsgProjectedCrsRecord(32724, "WGS 84 / UTM zone 24S", 4326, 4400, 16124); + return true; + case 5360: + record = new EpsgProjectedCrsRecord(32725, "WGS 84 / UTM zone 25S", 4326, 4400, 16125); + return true; + case 5361: + record = new EpsgProjectedCrsRecord(32726, "WGS 84 / UTM zone 26S", 4326, 4400, 16126); + return true; + case 5362: + record = new EpsgProjectedCrsRecord(32727, "WGS 84 / UTM zone 27S", 4326, 4400, 16127); + return true; + case 5363: + record = new EpsgProjectedCrsRecord(32728, "WGS 84 / UTM zone 28S", 4326, 4400, 16128); + return true; + case 5364: + record = new EpsgProjectedCrsRecord(32729, "WGS 84 / UTM zone 29S", 4326, 4400, 16129); + return true; + case 5365: + record = new EpsgProjectedCrsRecord(32730, "WGS 84 / UTM zone 30S", 4326, 4400, 16130); + return true; + case 5366: + record = new EpsgProjectedCrsRecord(32731, "WGS 84 / UTM zone 31S", 4326, 4400, 16131); + return true; + case 5367: + record = new EpsgProjectedCrsRecord(32732, "WGS 84 / UTM zone 32S", 4326, 4400, 16132); + return true; + case 5368: + record = new EpsgProjectedCrsRecord(32733, "WGS 84 / UTM zone 33S", 4326, 4400, 16133); + return true; + case 5369: + record = new EpsgProjectedCrsRecord(32734, "WGS 84 / UTM zone 34S", 4326, 4400, 16134); + return true; + case 5370: + record = new EpsgProjectedCrsRecord(32735, "WGS 84 / UTM zone 35S", 4326, 4400, 16135); + return true; + case 5371: + record = new EpsgProjectedCrsRecord(32736, "WGS 84 / UTM zone 36S", 4326, 4400, 16136); + return true; + case 5372: + record = new EpsgProjectedCrsRecord(32737, "WGS 84 / UTM zone 37S", 4326, 4400, 16137); + return true; + case 5373: + record = new EpsgProjectedCrsRecord(32738, "WGS 84 / UTM zone 38S", 4326, 4400, 16138); + return true; + case 5374: + record = new EpsgProjectedCrsRecord(32739, "WGS 84 / UTM zone 39S", 4326, 4400, 16139); + return true; + case 5375: + record = new EpsgProjectedCrsRecord(32740, "WGS 84 / UTM zone 40S", 4326, 4400, 16140); + return true; + case 5376: + record = new EpsgProjectedCrsRecord(32741, "WGS 84 / UTM zone 41S", 4326, 4400, 16141); + return true; + case 5377: + record = new EpsgProjectedCrsRecord(32742, "WGS 84 / UTM zone 42S", 4326, 4400, 16142); + return true; + case 5378: + record = new EpsgProjectedCrsRecord(32743, "WGS 84 / UTM zone 43S", 4326, 4400, 16143); + return true; + case 5379: + record = new EpsgProjectedCrsRecord(32744, "WGS 84 / UTM zone 44S", 4326, 4400, 16144); + return true; + case 5380: + record = new EpsgProjectedCrsRecord(32745, "WGS 84 / UTM zone 45S", 4326, 4400, 16145); + return true; + case 5381: + record = new EpsgProjectedCrsRecord(32746, "WGS 84 / UTM zone 46S", 4326, 4400, 16146); + return true; + case 5382: + record = new EpsgProjectedCrsRecord(32747, "WGS 84 / UTM zone 47S", 4326, 4400, 16147); + return true; + case 5383: + record = new EpsgProjectedCrsRecord(32748, "WGS 84 / UTM zone 48S", 4326, 4400, 16148); + return true; + case 5384: + record = new EpsgProjectedCrsRecord(32749, "WGS 84 / UTM zone 49S", 4326, 4400, 16149); + return true; + case 5385: + record = new EpsgProjectedCrsRecord(32750, "WGS 84 / UTM zone 50S", 4326, 4400, 16150); + return true; + case 5386: + record = new EpsgProjectedCrsRecord(32751, "WGS 84 / UTM zone 51S", 4326, 4400, 16151); + return true; + case 5387: + record = new EpsgProjectedCrsRecord(32752, "WGS 84 / UTM zone 52S", 4326, 4400, 16152); + return true; + case 5388: + record = new EpsgProjectedCrsRecord(32753, "WGS 84 / UTM zone 53S", 4326, 4400, 16153); + return true; + case 5389: + record = new EpsgProjectedCrsRecord(32754, "WGS 84 / UTM zone 54S", 4326, 4400, 16154); + return true; + case 5390: + record = new EpsgProjectedCrsRecord(32755, "WGS 84 / UTM zone 55S", 4326, 4400, 16155); + return true; + case 5391: + record = new EpsgProjectedCrsRecord(32756, "WGS 84 / UTM zone 56S", 4326, 4400, 16156); + return true; + case 5392: + record = new EpsgProjectedCrsRecord(32757, "WGS 84 / UTM zone 57S", 4326, 4400, 16157); + return true; + case 5393: + record = new EpsgProjectedCrsRecord(32758, "WGS 84 / UTM zone 58S", 4326, 4400, 16158); + return true; + case 5394: + record = new EpsgProjectedCrsRecord(32759, "WGS 84 / UTM zone 59S", 4326, 4400, 16159); + return true; + case 5395: + record = new EpsgProjectedCrsRecord(32760, "WGS 84 / UTM zone 60S", 4326, 4400, 16160); + return true; + case 5396: + record = new EpsgProjectedCrsRecord(32761, "WGS 84 / UPS South (N,E)", 4326, 4494, 16161); + return true; + case 5397: + record = new EpsgProjectedCrsRecord(32766, "WGS 84 / TM 36 SE", 4326, 4400, 16636); + return true; + default: + record = default; + return false; + } + } + + } +} diff --git a/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Types.g.cs b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Types.g.cs new file mode 100644 index 00000000..5e53cfb3 --- /dev/null +++ b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.Types.g.cs @@ -0,0 +1,307 @@ +// +// Generated by tools\Generate-EpsgManagedData.ps1 +// Source: EPSG-v12_054-WKT.Zip +// +#pragma warning disable SA0001, SA1512, SA1518, SA1600, SA1614, SA1616, SA1633, SA1636 +using System; + +namespace ProjNet.Data.Generated +{ + internal enum EpsgOperationType : byte { Transformation = 0, ConcatenatedOperation = 1, PointMotionOperation = 2 } + internal enum EpsgCoordinateSystemKind : byte { Geographic2D = 0, Geocentric = 1, Projected = 2, Vertical = 3, Compound = 4 } + + internal readonly struct EpsgCoordinateReferenceRecord + { + internal EpsgCoordinateReferenceRecord(int srid, EpsgCoordinateSystemKind kind, int recordIndex) + { + Srid = srid; + Kind = kind; + RecordIndex = recordIndex; + } + + internal int Srid { get; } + internal EpsgCoordinateSystemKind Kind { get; } + internal int RecordIndex { get; } + } + + internal readonly struct EpsgGeographicCrsRecord + { + internal EpsgGeographicCrsRecord(int srid, string name, int datumCode, int coordinateSystemCode) + { + Srid = srid; + Name = name; + DatumCode = datumCode; + CoordinateSystemCode = coordinateSystemCode; + } + + internal int Srid { get; } + internal string Name { get; } + internal int DatumCode { get; } + internal int CoordinateSystemCode { get; } + } + + internal readonly struct EpsgGeocentricCrsRecord + { + internal EpsgGeocentricCrsRecord(int srid, string name, int datumCode, int coordinateSystemCode) + { + Srid = srid; + Name = name; + DatumCode = datumCode; + CoordinateSystemCode = coordinateSystemCode; + } + + internal int Srid { get; } + internal string Name { get; } + internal int DatumCode { get; } + internal int CoordinateSystemCode { get; } + } + + internal readonly struct EpsgProjectedCrsRecord + { + internal EpsgProjectedCrsRecord(int srid, string name, int baseSrid, int coordinateSystemCode, int conversionCode) + { + Srid = srid; + Name = name; + BaseSrid = baseSrid; + CoordinateSystemCode = coordinateSystemCode; + ConversionCode = conversionCode; + } + + internal int Srid { get; } + internal string Name { get; } + internal int BaseSrid { get; } + internal int CoordinateSystemCode { get; } + internal int ConversionCode { get; } + } + + internal readonly struct EpsgVerticalCrsRecord + { + internal EpsgVerticalCrsRecord(int srid, string name, int datumCode, int coordinateSystemCode) + { + Srid = srid; + Name = name; + DatumCode = datumCode; + CoordinateSystemCode = coordinateSystemCode; + } + + internal int Srid { get; } + internal string Name { get; } + internal int DatumCode { get; } + internal int CoordinateSystemCode { get; } + } + + internal readonly struct EpsgCompoundCrsRecord + { + internal EpsgCompoundCrsRecord(int srid, string name, int horizontalSrid, int verticalSrid) + { + Srid = srid; + Name = name; + HorizontalSrid = horizontalSrid; + VerticalSrid = verticalSrid; + } + + internal int Srid { get; } + internal string Name { get; } + internal int HorizontalSrid { get; } + internal int VerticalSrid { get; } + } + + internal readonly struct EpsgUnitRecord + { + internal EpsgUnitRecord(int code, byte unitType, double factor, string name) + { + Code = code; + UnitType = unitType; + Factor = factor; + Name = name; + } + + internal int Code { get; } + internal byte UnitType { get; } + internal double Factor { get; } + internal string Name { get; } + } + + internal readonly struct EpsgAxisRecord + { + internal EpsgAxisRecord(int coordinateSystemCode, byte axisOrder, string name, sbyte orientation, int unitCode) + { + CoordinateSystemCode = coordinateSystemCode; + AxisOrder = axisOrder; + Name = name; + Orientation = orientation; + UnitCode = unitCode; + } + + internal int CoordinateSystemCode { get; } + internal byte AxisOrder { get; } + internal string Name { get; } + internal sbyte Orientation { get; } + internal int UnitCode { get; } + } + + internal readonly struct EpsgEllipsoidRecord + { + internal EpsgEllipsoidRecord(int code, string name, double semiMajor, double semiMinor, double inverseFlattening, bool isInverseFlatteningDefinitive, int unitCode) + { + Code = code; + Name = name; + SemiMajor = semiMajor; + SemiMinor = semiMinor; + InverseFlattening = inverseFlattening; + IsInverseFlatteningDefinitive = isInverseFlatteningDefinitive; + UnitCode = unitCode; + } + + internal int Code { get; } + internal string Name { get; } + internal double SemiMajor { get; } + internal double SemiMinor { get; } + internal double InverseFlattening { get; } + internal bool IsInverseFlatteningDefinitive { get; } + internal int UnitCode { get; } + } + + internal readonly struct EpsgPrimeMeridianRecord + { + internal EpsgPrimeMeridianRecord(int code, string name, double longitude, int unitCode) + { + Code = code; + Name = name; + Longitude = longitude; + UnitCode = unitCode; + } + + internal int Code { get; } + internal string Name { get; } + internal double Longitude { get; } + internal int UnitCode { get; } + } + + internal readonly struct EpsgGeodeticDatumRecord + { + internal EpsgGeodeticDatumRecord(int code, string name, int ellipsoidCode, int primeMeridianCode) + { + Code = code; + Name = name; + EllipsoidCode = ellipsoidCode; + PrimeMeridianCode = primeMeridianCode; + } + + internal int Code { get; } + internal string Name { get; } + internal int EllipsoidCode { get; } + internal int PrimeMeridianCode { get; } + } + + internal readonly struct EpsgVerticalDatumRecord + { + internal EpsgVerticalDatumRecord(int code, string name) + { + Code = code; + Name = name; + } + + internal int Code { get; } + internal string Name { get; } + } + + internal readonly struct EpsgConversionRecord + { + internal EpsgConversionRecord(int code, string methodName, int parameterCount) + { + Code = code; + MethodName = methodName; + ParameterCount = parameterCount; + } + + internal int Code { get; } + internal string MethodName { get; } + internal int ParameterCount { get; } + } + + internal readonly struct EpsgConversionParameterRecord + { + internal EpsgConversionParameterRecord(string name, double value) + { + Name = name; + Value = value; + } + + internal string Name { get; } + internal double Value { get; } + } + + internal readonly struct EpsgOperationRecord + { + internal EpsgOperationRecord(EpsgOperationType operationType, int operationCode, int sourceSrid, int targetSrid, double accuracy, string methodName, string parameterFileName, double areaSouthLatitude, double areaNorthLatitude, double areaWestLongitude, double areaEastLongitude, int parameterStartIndex, int parameterCount) + { + OperationType = operationType; + OperationCode = operationCode; + SourceSrid = sourceSrid; + TargetSrid = targetSrid; + Accuracy = accuracy; + MethodName = methodName; + ParameterFileName = parameterFileName; + AreaSouthLatitude = areaSouthLatitude; + AreaNorthLatitude = areaNorthLatitude; + AreaWestLongitude = areaWestLongitude; + AreaEastLongitude = areaEastLongitude; + ParameterStartIndex = parameterStartIndex; + ParameterCount = parameterCount; + } + + internal EpsgOperationType OperationType { get; } + internal int OperationCode { get; } + internal int SourceSrid { get; } + internal int TargetSrid { get; } + internal double Accuracy { get; } + internal string MethodName { get; } + internal string ParameterFileName { get; } + internal double AreaSouthLatitude { get; } + internal double AreaNorthLatitude { get; } + internal double AreaWestLongitude { get; } + internal double AreaEastLongitude { get; } + internal int ParameterStartIndex { get; } + internal int ParameterCount { get; } + } + + internal readonly struct EpsgOperationParameterRecord + { + internal EpsgOperationParameterRecord(int operationCode, string name, double value) + { + OperationCode = operationCode; + Name = name; + Value = value; + } + + internal int OperationCode { get; } + internal string Name { get; } + internal double Value { get; } + } + + internal readonly struct EpsgExplicitOperationRecord + { + internal EpsgExplicitOperationRecord(int operationCode, double dx, double dy, double dz, double ex, double ey, double ez, double ppm) + { + OperationCode = operationCode; + Dx = dx; + Dy = dy; + Dz = dz; + Ex = ex; + Ey = ey; + Ez = ez; + Ppm = ppm; + } + + internal int OperationCode { get; } + internal double Dx { get; } + internal double Dy { get; } + internal double Dz { get; } + internal double Ex { get; } + internal double Ey { get; } + internal double Ez { get; } + internal double Ppm { get; } + } + +} diff --git a/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.g.cs b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.g.cs new file mode 100644 index 00000000..b070123b --- /dev/null +++ b/src/ProjNet/Data/Generated/EpsgGeneratedCatalog.g.cs @@ -0,0 +1,36233 @@ +// +// Generated by tools\Generate-EpsgManagedData.ps1 +// Source: EPSG-v12_054-WKT.Zip +// +#pragma warning disable SA0001, SA1512, SA1518, SA1600, SA1614, SA1616, SA1633, SA1636 +using System; + +namespace ProjNet.Data.Generated +{ + internal static partial class EpsgGeneratedCatalog + { + internal const string SourceArchive = "EPSG-v12_054-WKT.Zip"; + internal const int CoordinateReferenceCount = 7217; + private static readonly int[] CoordinateSridByCacheIndex = new int[] + { + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, + 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2039, 2040, 2041, 2042, 2043, + 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2065, + 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2087, + 2088, 2089, 2090, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, + 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, + 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137, 2138, 2154, 2157, 2158, 2159, 2160, 2161, 2162, 2164, 2165, 2169, 2172, + 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2188, 2189, 2190, 2193, 2195, 2196, 2197, 2198, 2200, 2201, 2202, 2203, + 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, + 2225, 2226, 2227, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2246, + 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, + 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, + 2287, 2288, 2289, 2290, 2294, 2295, 2296, 2299, 2301, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, + 2314, 2315, 2316, 2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2331, 2332, 2333, + 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, + 2354, 2355, 2356, 2357, 2358, 2359, 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, + 2374, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2393, + 2394, 2395, 2396, 2397, 2398, 2399, 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, + 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432, 2433, 2434, + 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2443, 2444, 2445, 2446, 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, + 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, 2472, 2473, 2474, + 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2494, 2495, 2496, + 2497, 2498, 2499, 2500, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513, 2514, 2515, 2516, + 2517, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2526, 2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, 2535, 2536, + 2537, 2538, 2539, 2540, 2541, 2542, 2543, 2544, 2545, 2546, 2547, 2548, 2549, 2551, 2552, 2553, 2554, 2555, 2556, 2557, + 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2578, + 2579, 2580, 2581, 2582, 2583, 2584, 2585, 2586, 2587, 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, 2596, 2597, 2598, + 2599, 2601, 2602, 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, 2617, 2618, 2619, + 2620, 2621, 2622, 2623, 2624, 2625, 2626, 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, 2636, 2637, 2638, 2639, + 2640, 2641, 2642, 2643, 2644, 2645, 2646, 2647, 2648, 2649, 2650, 2651, 2652, 2653, 2654, 2655, 2656, 2657, 2658, 2659, + 2660, 2661, 2662, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, 2677, 2678, 2679, + 2680, 2681, 2682, 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2690, 2691, 2692, 2693, 2695, 2696, 2697, 2698, 2699, 2700, + 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720, + 2721, 2722, 2723, 2724, 2725, 2726, 2727, 2728, 2729, 2730, 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, 2739, 2740, + 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2749, 2750, 2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, + 2761, 2762, 2763, 2764, 2765, 2766, 2767, 2768, 2769, 2770, 2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, 2780, + 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, 2789, 2790, 2791, 2792, 2793, 2794, 2795, 2796, 2797, 2798, 2799, 2800, + 2801, 2802, 2803, 2804, 2805, 2806, 2807, 2808, 2809, 2810, 2811, 2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, + 2821, 2822, 2823, 2824, 2825, 2826, 2827, 2828, 2829, 2830, 2831, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, + 2841, 2842, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2850, 2851, 2852, 2853, 2854, 2855, 2856, 2857, 2858, 2859, 2860, + 2861, 2862, 2863, 2864, 2865, 2866, 2867, 2868, 2869, 2870, 2871, 2872, 2873, 2874, 2875, 2876, 2877, 2878, 2879, 2880, + 2881, 2882, 2883, 2884, 2885, 2886, 2887, 2888, 2891, 2892, 2893, 2894, 2895, 2896, 2897, 2898, 2899, 2900, 2901, 2902, + 2903, 2904, 2905, 2906, 2907, 2908, 2909, 2910, 2911, 2912, 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, 2921, 2922, + 2923, 2924, 2925, 2926, 2927, 2928, 2929, 2930, 2931, 2932, 2933, 2935, 2936, 2937, 2938, 2939, 2940, 2941, 2942, 2943, + 2945, 2946, 2947, 2948, 2949, 2950, 2951, 2952, 2953, 2954, 2955, 2956, 2957, 2958, 2959, 2960, 2961, 2962, 2963, 2964, + 2965, 2966, 2967, 2968, 2969, 2970, 2971, 2972, 2973, 2975, 2976, 2977, 2978, 2980, 2981, 2985, 2986, 2987, 2988, 2991, + 2992, 2993, 2994, 2995, 2996, 2997, 2998, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011, + 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3024, 3025, 3026, 3027, 3028, 3029, 3030, 3031, + 3032, 3033, 3034, 3035, 3036, 3037, 3040, 3041, 3042, 3043, 3044, 3045, 3046, 3047, 3048, 3049, 3052, 3053, 3054, 3055, + 3056, 3057, 3058, 3059, 3060, 3061, 3062, 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3070, 3071, 3072, 3074, 3075, 3077, + 3078, 3079, 3080, 3081, 3082, 3083, 3084, 3085, 3086, 3087, 3088, 3089, 3090, 3091, 3092, 3093, 3094, 3095, 3096, 3097, + 3098, 3099, 3100, 3101, 3102, 3106, 3107, 3108, 3109, 3110, 3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, + 3121, 3122, 3123, 3124, 3125, 3126, 3127, 3128, 3129, 3130, 3131, 3132, 3133, 3134, 3135, 3136, 3137, 3138, 3139, 3140, + 3141, 3142, 3144, 3145, 3148, 3149, 3152, 3153, 3154, 3155, 3156, 3157, 3158, 3159, 3160, 3161, 3162, 3163, 3164, 3165, + 3166, 3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 3175, 3176, 3177, 3178, 3179, 3180, 3181, 3182, 3183, 3184, 3185, + 3186, 3187, 3188, 3189, 3190, 3191, 3192, 3193, 3194, 3195, 3196, 3197, 3198, 3199, 3200, 3201, 3202, 3203, 3204, 3205, + 3206, 3207, 3208, 3209, 3210, 3211, 3212, 3213, 3214, 3215, 3216, 3217, 3218, 3219, 3220, 3221, 3222, 3223, 3224, 3225, + 3226, 3227, 3228, 3229, 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3239, 3240, 3241, 3242, 3243, 3244, 3245, + 3246, 3247, 3248, 3249, 3250, 3251, 3252, 3253, 3254, 3255, 3256, 3257, 3258, 3259, 3260, 3261, 3262, 3263, 3264, 3265, + 3266, 3267, 3268, 3269, 3270, 3271, 3272, 3273, 3274, 3275, 3276, 3277, 3278, 3279, 3280, 3281, 3282, 3283, 3284, 3285, + 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, 3294, 3295, 3296, 3297, 3298, 3299, 3300, 3301, 3302, 3303, 3304, 3305, + 3306, 3307, 3308, 3309, 3310, 3311, 3312, 3313, 3316, 3317, 3318, 3319, 3320, 3321, 3322, 3323, 3324, 3325, 3326, 3327, + 3328, 3329, 3330, 3331, 3332, 3333, 3334, 3335, 3336, 3337, 3338, 3339, 3340, 3341, 3342, 3343, 3344, 3345, 3346, 3347, + 3348, 3350, 3351, 3352, 3353, 3354, 3355, 3358, 3360, 3361, 3362, 3363, 3364, 3365, 3367, 3368, 3369, 3370, 3371, 3372, + 3373, 3374, 3375, 3376, 3377, 3378, 3379, 3380, 3381, 3382, 3383, 3384, 3385, 3386, 3387, 3388, 3389, 3390, 3391, 3392, + 3393, 3394, 3395, 3396, 3397, 3398, 3399, 3400, 3401, 3402, 3403, 3404, 3405, 3406, 3407, 3408, 3409, 3410, 3411, 3412, + 3413, 3414, 3415, 3416, 3417, 3418, 3419, 3420, 3421, 3422, 3423, 3424, 3425, 3426, 3427, 3428, 3429, 3430, 3431, 3432, + 3433, 3434, 3435, 3436, 3437, 3438, 3439, 3440, 3441, 3442, 3443, 3444, 3445, 3446, 3447, 3448, 3449, 3450, 3451, 3452, + 3453, 3455, 3456, 3457, 3458, 3459, 3460, 3461, 3462, 3463, 3464, 3465, 3466, 3467, 3468, 3469, 3470, 3471, 3472, 3473, + 3474, 3475, 3476, 3477, 3478, 3479, 3480, 3481, 3482, 3483, 3484, 3485, 3486, 3487, 3488, 3489, 3490, 3491, 3492, 3493, + 3494, 3495, 3496, 3497, 3498, 3499, 3500, 3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508, 3509, 3510, 3511, 3512, 3513, + 3514, 3515, 3516, 3517, 3518, 3519, 3520, 3521, 3522, 3523, 3524, 3525, 3526, 3527, 3528, 3529, 3530, 3531, 3532, 3533, + 3534, 3535, 3536, 3537, 3538, 3539, 3540, 3541, 3542, 3543, 3544, 3545, 3546, 3547, 3548, 3549, 3550, 3551, 3552, 3553, + 3554, 3555, 3556, 3557, 3558, 3559, 3560, 3561, 3562, 3563, 3564, 3565, 3566, 3567, 3568, 3569, 3570, 3571, 3572, 3573, + 3574, 3575, 3576, 3577, 3578, 3579, 3580, 3581, 3582, 3583, 3584, 3585, 3586, 3587, 3588, 3589, 3590, 3591, 3592, 3593, + 3594, 3595, 3596, 3597, 3598, 3599, 3600, 3601, 3602, 3603, 3604, 3605, 3606, 3607, 3608, 3609, 3610, 3611, 3612, 3613, + 3614, 3615, 3616, 3617, 3618, 3619, 3620, 3621, 3622, 3623, 3624, 3625, 3626, 3627, 3628, 3629, 3630, 3631, 3632, 3633, + 3634, 3635, 3636, 3637, 3638, 3639, 3640, 3641, 3642, 3643, 3644, 3645, 3646, 3647, 3648, 3649, 3650, 3651, 3652, 3653, + 3654, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662, 3663, 3664, 3665, 3666, 3667, 3668, 3669, 3670, 3671, 3672, 3673, + 3674, 3675, 3676, 3677, 3678, 3679, 3680, 3681, 3682, 3683, 3684, 3685, 3686, 3687, 3688, 3689, 3690, 3691, 3692, 3693, + 3694, 3695, 3696, 3697, 3698, 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3706, 3707, 3708, 3709, 3710, 3711, 3712, 3713, + 3714, 3715, 3716, 3717, 3718, 3719, 3720, 3721, 3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, 3732, 3733, + 3734, 3735, 3736, 3737, 3738, 3739, 3740, 3741, 3742, 3743, 3744, 3745, 3746, 3747, 3748, 3749, 3750, 3751, 3753, 3754, + 3755, 3756, 3757, 3758, 3759, 3760, 3761, 3762, 3763, 3764, 3765, 3766, 3767, 3768, 3769, 3770, 3771, 3772, 3773, 3775, + 3776, 3777, 3779, 3780, 3781, 3783, 3784, 3788, 3789, 3790, 3791, 3793, 3794, 3795, 3796, 3797, 3798, 3799, 3800, 3801, + 3802, 3812, 3814, 3815, 3816, 3819, 3821, 3822, 3823, 3824, 3825, 3826, 3827, 3828, 3829, 3832, 3833, 3834, 3835, 3836, + 3837, 3838, 3839, 3840, 3841, 3844, 3845, 3846, 3847, 3848, 3849, 3850, 3851, 3852, 3854, 3855, 3857, 3873, 3874, 3875, + 3876, 3877, 3878, 3879, 3880, 3881, 3882, 3883, 3884, 3885, 3886, 3887, 3888, 3889, 3890, 3891, 3892, 3893, 3900, 3901, + 3902, 3903, 3906, 3912, 3920, 3942, 3943, 3944, 3945, 3946, 3947, 3948, 3949, 3950, 3968, 3969, 3970, 3976, 3978, 3979, + 3986, 3987, 3988, 3989, 3991, 3992, 3993, 3994, 3995, 3996, 3997, 4000, 4017, 4023, 4026, 4037, 4038, 4039, 4040, 4046, + 4048, 4049, 4050, 4051, 4056, 4057, 4058, 4059, 4060, 4061, 4062, 4063, 4071, 4073, 4074, 4075, 4079, 4080, 4081, 4082, + 4083, 4087, 4093, 4094, 4095, 4096, 4120, 4121, 4122, 4123, 4124, 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, + 4136, 4137, 4138, 4139, 4141, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, + 4157, 4158, 4159, 4160, 4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4173, 4174, 4175, 4176, 4178, + 4179, 4180, 4181, 4182, 4183, 4184, 4188, 4189, 4190, 4191, 4192, 4193, 4194, 4195, 4196, 4197, 4198, 4199, 4200, 4201, + 4202, 4203, 4204, 4205, 4206, 4207, 4208, 4209, 4210, 4211, 4212, 4213, 4214, 4215, 4216, 4217, 4218, 4219, 4220, 4221, + 4222, 4223, 4224, 4225, 4227, 4229, 4230, 4231, 4232, 4236, 4237, 4238, 4239, 4240, 4241, 4242, 4243, 4244, 4245, 4246, + 4247, 4248, 4249, 4250, 4251, 4252, 4253, 4254, 4255, 4256, 4257, 4258, 4259, 4261, 4262, 4263, 4265, 4266, 4267, 4269, + 4270, 4271, 4272, 4273, 4274, 4275, 4276, 4277, 4278, 4279, 4281, 4282, 4283, 4284, 4285, 4286, 4288, 4289, 4292, 4293, + 4295, 4297, 4298, 4299, 4300, 4301, 4302, 4303, 4304, 4306, 4307, 4308, 4309, 4310, 4311, 4312, 4313, 4314, 4315, 4316, + 4318, 4319, 4322, 4324, 4326, 4390, 4391, 4392, 4393, 4394, 4395, 4396, 4397, 4398, 4399, 4400, 4401, 4402, 4403, 4404, + 4405, 4406, 4407, 4408, 4409, 4410, 4411, 4412, 4413, 4414, 4415, 4417, 4418, 4419, 4420, 4421, 4422, 4423, 4424, 4425, + 4426, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4437, 4438, 4439, 4440, 4455, 4456, 4457, 4458, 4462, 4463, 4465, + 4466, 4467, 4468, 4469, 4470, 4471, 4472, 4473, 4475, 4479, 4480, 4481, 4482, 4483, 4484, 4485, 4486, 4487, 4488, 4489, + 4490, 4491, 4492, 4493, 4494, 4495, 4496, 4497, 4498, 4499, 4500, 4501, 4502, 4503, 4504, 4505, 4506, 4507, 4508, 4509, + 4510, 4511, 4512, 4513, 4514, 4515, 4516, 4517, 4518, 4519, 4520, 4521, 4522, 4523, 4524, 4525, 4526, 4527, 4528, 4529, + 4530, 4531, 4532, 4533, 4534, 4535, 4536, 4537, 4538, 4539, 4540, 4541, 4542, 4543, 4544, 4545, 4546, 4547, 4548, 4549, + 4550, 4551, 4552, 4553, 4554, 4555, 4556, 4557, 4558, 4559, 4568, 4569, 4570, 4571, 4572, 4573, 4574, 4575, 4576, 4577, + 4578, 4579, 4580, 4581, 4582, 4583, 4584, 4585, 4586, 4587, 4588, 4589, 4600, 4601, 4602, 4603, 4604, 4605, 4606, 4607, + 4608, 4609, 4610, 4611, 4612, 4613, 4614, 4615, 4616, 4617, 4618, 4619, 4620, 4621, 4622, 4623, 4624, 4625, 4626, 4627, + 4628, 4629, 4630, 4632, 4633, 4636, 4637, 4638, 4639, 4641, 4642, 4643, 4644, 4646, 4647, 4652, 4653, 4654, 4655, 4656, + 4657, 4658, 4659, 4660, 4661, 4662, 4663, 4664, 4665, 4666, 4667, 4668, 4669, 4670, 4671, 4672, 4673, 4674, 4675, 4676, + 4677, 4678, 4679, 4680, 4682, 4683, 4684, 4686, 4687, 4688, 4689, 4690, 4691, 4692, 4693, 4694, 4695, 4696, 4697, 4698, + 4699, 4700, 4701, 4702, 4703, 4704, 4705, 4706, 4707, 4708, 4709, 4710, 4711, 4712, 4713, 4714, 4715, 4716, 4717, 4718, + 4719, 4720, 4721, 4722, 4723, 4724, 4725, 4726, 4727, 4728, 4729, 4730, 4732, 4733, 4734, 4735, 4736, 4737, 4738, 4739, + 4740, 4741, 4742, 4743, 4744, 4745, 4746, 4747, 4748, 4749, 4750, 4751, 4752, 4753, 4754, 4755, 4756, 4757, 4758, 4759, + 4760, 4761, 4762, 4763, 4764, 4765, 4766, 4767, 4768, 4769, 4770, 4771, 4772, 4773, 4774, 4775, 4776, 4777, 4778, 4779, + 4780, 4781, 4782, 4783, 4784, 4785, 4786, 4787, 4788, 4789, 4790, 4791, 4792, 4793, 4794, 4795, 4796, 4797, 4798, 4799, + 4800, 4801, 4802, 4803, 4804, 4805, 4806, 4807, 4809, 4810, 4811, 4812, 4813, 4814, 4815, 4816, 4817, 4818, 4820, 4821, + 4822, 4823, 4824, 4826, 4839, 4882, 4883, 4884, 4885, 4886, 4887, 4888, 4889, 4890, 4891, 4892, 4893, 4894, 4895, 4896, + 4897, 4898, 4899, 4900, 4901, 4903, 4904, 4906, 4907, 4908, 4909, 4910, 4911, 4912, 4913, 4914, 4915, 4916, 4917, 4918, + 4919, 4920, 4921, 4922, 4923, 4924, 4925, 4926, 4927, 4928, 4929, 4930, 4931, 4932, 4933, 4934, 4935, 4936, 4937, 4938, + 4939, 4940, 4941, 4942, 4943, 4944, 4945, 4946, 4947, 4948, 4949, 4950, 4951, 4952, 4953, 4954, 4955, 4956, 4957, 4958, + 4959, 4960, 4961, 4962, 4963, 4964, 4965, 4966, 4967, 4970, 4971, 4974, 4975, 4976, 4977, 4978, 4979, 4980, 4981, 4982, + 4983, 4984, 4985, 4986, 4987, 4988, 4989, 4990, 4991, 4992, 4993, 4994, 4995, 4996, 4997, 4998, 4999, 5011, 5012, 5013, + 5014, 5015, 5016, 5017, 5018, 5041, 5042, 5048, 5069, 5070, 5071, 5072, 5105, 5106, 5107, 5108, 5109, 5110, 5111, 5112, + 5113, 5114, 5115, 5116, 5117, 5118, 5119, 5120, 5121, 5122, 5123, 5124, 5125, 5126, 5127, 5128, 5129, 5130, 5132, 5167, + 5168, 5169, 5170, 5171, 5172, 5173, 5174, 5175, 5176, 5177, 5178, 5179, 5180, 5181, 5182, 5183, 5184, 5185, 5186, 5187, + 5188, 5193, 5195, 5214, 5221, 5223, 5224, 5225, 5228, 5229, 5233, 5234, 5235, 5237, 5243, 5244, 5245, 5246, 5247, 5250, + 5251, 5252, 5253, 5254, 5255, 5256, 5257, 5258, 5259, 5262, 5263, 5264, 5266, 5269, 5270, 5271, 5272, 5273, 5274, 5275, + 5292, 5293, 5294, 5295, 5296, 5297, 5298, 5299, 5300, 5301, 5302, 5303, 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, + 5316, 5317, 5318, 5320, 5321, 5322, 5323, 5324, 5325, 5329, 5330, 5331, 5332, 5337, 5340, 5341, 5342, 5343, 5344, 5345, + 5346, 5347, 5348, 5349, 5352, 5353, 5354, 5355, 5356, 5357, 5358, 5359, 5360, 5361, 5362, 5363, 5364, 5365, 5367, 5368, + 5369, 5370, 5371, 5372, 5373, 5379, 5380, 5381, 5382, 5383, 5387, 5389, 5391, 5392, 5393, 5396, 5451, 5456, 5457, 5459, + 5460, 5461, 5462, 5463, 5464, 5467, 5469, 5472, 5479, 5480, 5481, 5482, 5487, 5488, 5489, 5490, 5498, 5499, 5500, 5513, + 5514, 5515, 5516, 5518, 5519, 5520, 5523, 5524, 5527, 5530, 5531, 5533, 5534, 5535, 5536, 5537, 5538, 5539, 5544, 5545, + 5546, 5550, 5551, 5552, 5554, 5555, 5556, 5558, 5559, 5560, 5561, 5562, 5563, 5564, 5565, 5566, 5567, 5568, 5569, 5588, + 5589, 5591, 5592, 5593, 5596, 5597, 5598, 5600, 5601, 5602, 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, 5611, 5613, + 5615, 5616, 5617, 5618, 5619, 5620, 5621, 5623, 5624, 5625, 5627, 5628, 5629, 5631, 5632, 5633, 5634, 5635, 5636, 5637, + 5638, 5639, 5641, 5643, 5644, 5646, 5649, 5650, 5651, 5652, 5653, 5654, 5655, 5659, 5663, 5664, 5665, 5666, 5667, 5668, + 5669, 5670, 5671, 5672, 5673, 5674, 5675, 5676, 5677, 5678, 5679, 5680, 5681, 5682, 5683, 5684, 5685, 5698, 5699, 5700, + 5701, 5702, 5703, 5705, 5707, 5708, 5709, 5710, 5711, 5712, 5713, 5714, 5716, 5717, 5718, 5719, 5720, 5721, 5722, 5723, + 5724, 5725, 5726, 5727, 5728, 5729, 5730, 5731, 5732, 5733, 5735, 5736, 5737, 5738, 5739, 5740, 5741, 5742, 5743, 5744, + 5745, 5746, 5747, 5748, 5749, 5750, 5751, 5752, 5753, 5754, 5755, 5756, 5757, 5758, 5759, 5760, 5761, 5762, 5763, 5764, + 5765, 5766, 5767, 5768, 5769, 5770, 5771, 5772, 5773, 5774, 5775, 5776, 5777, 5778, 5779, 5780, 5781, 5782, 5783, 5784, + 5785, 5786, 5787, 5788, 5790, 5791, 5792, 5793, 5794, 5795, 5796, 5797, 5798, 5825, 5828, 5829, 5830, 5836, 5837, 5839, + 5842, 5843, 5844, 5845, 5846, 5847, 5848, 5849, 5850, 5851, 5852, 5853, 5854, 5855, 5856, 5857, 5858, 5861, 5862, 5863, + 5864, 5865, 5866, 5867, 5868, 5869, 5870, 5871, 5872, 5873, 5874, 5875, 5876, 5877, 5879, 5880, 5884, 5885, 5886, 5887, + 5896, 5897, 5898, 5899, 5921, 5922, 5923, 5924, 5925, 5926, 5927, 5928, 5929, 5930, 5931, 5932, 5933, 5934, 5935, 5936, + 5937, 5938, 5939, 5940, 5941, 5942, 5945, 5946, 5947, 5948, 5949, 5950, 5951, 5952, 5953, 5954, 5955, 5956, 5957, 5958, + 5959, 5960, 5961, 5962, 5963, 5964, 5965, 5966, 5967, 5968, 5969, 5970, 5971, 5972, 5973, 5974, 5975, 5976, 6050, 6051, + 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, 6062, 6063, 6064, 6065, 6066, 6067, 6068, 6069, 6070, 6071, + 6072, 6073, 6074, 6075, 6076, 6077, 6078, 6079, 6080, 6081, 6082, 6083, 6084, 6085, 6086, 6087, 6088, 6089, 6090, 6091, + 6092, 6093, 6094, 6095, 6096, 6097, 6098, 6099, 6100, 6101, 6102, 6103, 6104, 6105, 6106, 6107, 6108, 6109, 6110, 6111, + 6112, 6113, 6114, 6115, 6116, 6117, 6118, 6119, 6120, 6121, 6122, 6123, 6124, 6125, 6128, 6129, 6130, 6131, 6132, 6133, + 6134, 6135, 6144, 6145, 6146, 6147, 6148, 6149, 6150, 6151, 6152, 6153, 6154, 6155, 6156, 6157, 6158, 6159, 6160, 6161, + 6162, 6163, 6164, 6165, 6166, 6167, 6168, 6169, 6170, 6171, 6172, 6173, 6174, 6175, 6176, 6178, 6179, 6180, 6181, 6182, + 6183, 6184, 6185, 6186, 6187, 6190, 6201, 6202, 6204, 6207, 6210, 6211, 6244, 6245, 6246, 6247, 6248, 6249, 6250, 6251, + 6252, 6253, 6254, 6255, 6256, 6257, 6258, 6259, 6260, 6261, 6262, 6263, 6264, 6265, 6266, 6267, 6268, 6269, 6270, 6271, + 6272, 6273, 6274, 6275, 6307, 6309, 6310, 6311, 6312, 6316, 6317, 6318, 6319, 6320, 6321, 6322, 6323, 6324, 6325, 6328, + 6329, 6330, 6331, 6332, 6333, 6334, 6335, 6336, 6337, 6338, 6339, 6340, 6341, 6342, 6343, 6344, 6345, 6346, 6347, 6348, + 6349, 6350, 6351, 6352, 6353, 6354, 6355, 6356, 6362, 6363, 6364, 6365, 6366, 6367, 6368, 6369, 6370, 6371, 6372, 6381, + 6382, 6383, 6384, 6385, 6386, 6387, 6391, 6393, 6394, 6395, 6396, 6397, 6398, 6399, 6400, 6401, 6402, 6403, 6404, 6405, + 6406, 6407, 6408, 6409, 6410, 6411, 6412, 6413, 6414, 6415, 6416, 6417, 6418, 6419, 6420, 6421, 6422, 6423, 6424, 6425, + 6426, 6427, 6428, 6429, 6430, 6431, 6432, 6433, 6434, 6435, 6436, 6437, 6438, 6439, 6440, 6441, 6442, 6443, 6444, 6445, + 6446, 6447, 6448, 6449, 6450, 6451, 6452, 6453, 6454, 6455, 6456, 6457, 6458, 6459, 6460, 6461, 6462, 6463, 6464, 6465, + 6466, 6467, 6468, 6469, 6470, 6471, 6472, 6473, 6474, 6475, 6476, 6477, 6478, 6479, 6480, 6481, 6482, 6483, 6484, 6485, + 6486, 6487, 6488, 6489, 6490, 6491, 6492, 6493, 6494, 6495, 6496, 6497, 6498, 6499, 6500, 6501, 6502, 6503, 6504, 6505, + 6506, 6507, 6508, 6509, 6510, 6511, 6512, 6513, 6514, 6515, 6516, 6518, 6519, 6520, 6521, 6522, 6523, 6524, 6525, 6526, + 6527, 6528, 6529, 6530, 6531, 6532, 6533, 6534, 6535, 6536, 6537, 6538, 6539, 6540, 6541, 6542, 6543, 6544, 6545, 6546, + 6547, 6548, 6549, 6550, 6551, 6552, 6553, 6554, 6555, 6556, 6557, 6558, 6559, 6560, 6561, 6562, 6563, 6564, 6565, 6566, + 6567, 6568, 6569, 6570, 6571, 6572, 6573, 6574, 6575, 6576, 6577, 6578, 6579, 6580, 6581, 6582, 6583, 6584, 6585, 6586, + 6587, 6588, 6589, 6590, 6591, 6592, 6593, 6594, 6595, 6596, 6597, 6598, 6599, 6600, 6601, 6602, 6603, 6605, 6606, 6607, + 6608, 6609, 6610, 6611, 6612, 6613, 6614, 6615, 6616, 6617, 6618, 6619, 6620, 6621, 6622, 6623, 6624, 6625, 6626, 6627, + 6628, 6629, 6630, 6631, 6632, 6633, 6634, 6635, 6636, 6637, 6638, 6639, 6640, 6641, 6642, 6643, 6644, 6646, 6647, 6649, + 6650, 6651, 6652, 6653, 6654, 6655, 6656, 6657, 6658, 6659, 6660, 6661, 6662, 6663, 6664, 6665, 6666, 6667, 6668, 6669, + 6670, 6671, 6672, 6673, 6674, 6675, 6676, 6677, 6678, 6679, 6680, 6681, 6682, 6683, 6684, 6685, 6686, 6687, 6688, 6689, + 6690, 6691, 6692, 6693, 6694, 6695, 6696, 6697, 6700, 6703, 6704, 6705, 6706, 6707, 6708, 6709, 6720, 6721, 6722, 6723, + 6736, 6737, 6738, 6781, 6782, 6783, 6784, 6785, 6786, 6787, 6788, 6789, 6790, 6791, 6792, 6793, 6794, 6795, 6796, 6797, + 6798, 6799, 6800, 6801, 6802, 6803, 6804, 6805, 6806, 6807, 6808, 6809, 6810, 6811, 6812, 6813, 6814, 6815, 6816, 6817, + 6818, 6819, 6820, 6821, 6822, 6823, 6824, 6825, 6826, 6827, 6828, 6829, 6830, 6831, 6832, 6833, 6834, 6835, 6836, 6837, + 6838, 6839, 6840, 6841, 6842, 6843, 6844, 6845, 6846, 6847, 6848, 6849, 6850, 6851, 6852, 6853, 6854, 6855, 6856, 6857, + 6858, 6859, 6860, 6861, 6862, 6863, 6867, 6868, 6870, 6875, 6876, 6879, 6880, 6881, 6882, 6883, 6884, 6885, 6886, 6887, + 6892, 6893, 6894, 6915, 6916, 6917, 6922, 6923, 6924, 6925, 6927, 6931, 6932, 6933, 6934, 6962, 6966, 6981, 6982, 6983, + 6984, 6988, 6989, 6990, 6991, 7005, 7006, 7007, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7057, 7058, 7059, + 7060, 7061, 7062, 7063, 7064, 7065, 7066, 7067, 7068, 7069, 7070, 7071, 7072, 7073, 7074, 7075, 7076, 7077, 7078, 7079, + 7080, 7081, 7084, 7085, 7086, 7087, 7109, 7110, 7111, 7112, 7113, 7114, 7115, 7116, 7117, 7118, 7119, 7120, 7121, 7122, + 7123, 7124, 7125, 7126, 7127, 7128, 7131, 7132, 7133, 7134, 7135, 7136, 7137, 7138, 7139, 7142, 7257, 7258, 7259, 7260, + 7261, 7262, 7263, 7264, 7265, 7266, 7267, 7268, 7269, 7270, 7271, 7272, 7273, 7274, 7275, 7276, 7277, 7278, 7279, 7280, + 7281, 7282, 7283, 7284, 7285, 7286, 7287, 7288, 7289, 7290, 7291, 7292, 7293, 7294, 7295, 7296, 7297, 7298, 7299, 7300, + 7301, 7302, 7303, 7304, 7305, 7306, 7307, 7308, 7309, 7310, 7311, 7312, 7313, 7314, 7315, 7316, 7317, 7318, 7319, 7320, + 7321, 7322, 7323, 7324, 7325, 7326, 7327, 7328, 7329, 7330, 7331, 7332, 7333, 7334, 7335, 7336, 7337, 7338, 7339, 7340, + 7341, 7342, 7343, 7344, 7345, 7346, 7347, 7348, 7349, 7350, 7351, 7352, 7353, 7354, 7355, 7356, 7357, 7358, 7359, 7360, + 7361, 7362, 7363, 7364, 7365, 7366, 7367, 7368, 7369, 7370, 7371, 7372, 7373, 7374, 7375, 7376, 7400, 7404, 7405, 7406, + 7407, 7409, 7410, 7411, 7414, 7415, 7421, 7422, 7423, 7446, 7447, 7528, 7529, 7530, 7531, 7532, 7533, 7534, 7535, 7536, + 7537, 7538, 7539, 7540, 7541, 7542, 7543, 7544, 7545, 7546, 7547, 7548, 7549, 7550, 7551, 7552, 7553, 7554, 7555, 7556, + 7557, 7558, 7559, 7560, 7561, 7562, 7563, 7564, 7565, 7566, 7567, 7568, 7569, 7570, 7571, 7572, 7573, 7574, 7575, 7576, + 7577, 7578, 7579, 7580, 7581, 7582, 7583, 7584, 7585, 7586, 7587, 7588, 7589, 7590, 7591, 7592, 7593, 7594, 7595, 7596, + 7597, 7598, 7599, 7600, 7601, 7602, 7603, 7604, 7605, 7606, 7607, 7608, 7609, 7610, 7611, 7612, 7613, 7614, 7615, 7616, + 7617, 7618, 7619, 7620, 7621, 7622, 7623, 7624, 7625, 7626, 7627, 7628, 7629, 7630, 7631, 7632, 7633, 7634, 7635, 7636, + 7637, 7638, 7639, 7640, 7641, 7642, 7643, 7644, 7645, 7651, 7652, 7656, 7657, 7658, 7659, 7660, 7661, 7662, 7663, 7664, + 7665, 7677, 7678, 7679, 7680, 7681, 7682, 7683, 7684, 7685, 7686, 7692, 7693, 7694, 7695, 7696, 7699, 7700, 7707, 7755, + 7756, 7757, 7758, 7759, 7760, 7761, 7762, 7763, 7764, 7765, 7766, 7767, 7768, 7769, 7770, 7771, 7772, 7773, 7774, 7775, + 7776, 7777, 7778, 7779, 7780, 7781, 7782, 7783, 7784, 7785, 7786, 7787, 7789, 7791, 7792, 7793, 7794, 7795, 7796, 7797, + 7798, 7799, 7800, 7801, 7803, 7805, 7815, 7816, 7825, 7826, 7827, 7828, 7829, 7830, 7831, 7832, 7837, 7839, 7841, 7842, + 7843, 7844, 7845, 7846, 7847, 7848, 7849, 7850, 7851, 7852, 7853, 7854, 7855, 7856, 7857, 7858, 7859, 7877, 7878, 7879, + 7880, 7881, 7882, 7883, 7884, 7885, 7886, 7887, 7888, 7889, 7890, 7899, 7900, 7901, 7902, 7903, 7904, 7905, 7906, 7907, + 7908, 7909, 7910, 7911, 7912, 7914, 7915, 7916, 7917, 7918, 7919, 7920, 7921, 7922, 7923, 7924, 7925, 7926, 7927, 7928, + 7929, 7930, 7931, 7954, 7955, 7956, 7979, 7991, 7992, 8013, 8014, 8015, 8016, 8017, 8018, 8019, 8020, 8021, 8022, 8023, + 8024, 8025, 8026, 8027, 8028, 8029, 8030, 8031, 8032, 8035, 8036, 8042, 8043, 8044, 8045, 8058, 8059, 8065, 8066, 8067, + 8068, 8082, 8083, 8084, 8085, 8086, 8088, 8089, 8090, 8091, 8092, 8093, 8095, 8096, 8097, 8098, 8099, 8100, 8101, 8102, + 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8110, 8111, 8112, 8113, 8114, 8115, 8116, 8117, 8118, 8119, 8120, 8121, 8122, + 8123, 8124, 8125, 8126, 8127, 8128, 8129, 8130, 8131, 8132, 8133, 8134, 8135, 8136, 8137, 8138, 8139, 8140, 8141, 8142, + 8143, 8144, 8145, 8146, 8147, 8148, 8149, 8150, 8151, 8152, 8153, 8154, 8155, 8156, 8157, 8158, 8159, 8160, 8161, 8162, + 8163, 8164, 8165, 8166, 8167, 8168, 8169, 8170, 8171, 8172, 8173, 8177, 8179, 8180, 8181, 8182, 8184, 8185, 8187, 8189, + 8191, 8193, 8196, 8197, 8198, 8200, 8201, 8202, 8203, 8204, 8205, 8206, 8207, 8208, 8209, 8210, 8212, 8213, 8214, 8216, + 8218, 8220, 8222, 8224, 8225, 8226, 8227, 8230, 8231, 8232, 8233, 8235, 8237, 8238, 8239, 8240, 8242, 8244, 8246, 8247, + 8248, 8249, 8250, 8251, 8252, 8253, 8254, 8255, 8266, 8267, 8311, 8312, 8313, 8314, 8315, 8316, 8317, 8318, 8319, 8320, + 8321, 8322, 8323, 8324, 8325, 8326, 8327, 8328, 8329, 8330, 8331, 8332, 8333, 8334, 8335, 8336, 8337, 8338, 8339, 8340, + 8341, 8342, 8343, 8344, 8345, 8346, 8347, 8348, 8349, 8350, 8351, 8352, 8353, 8357, 8370, 8378, 8379, 8380, 8381, 8382, + 8383, 8384, 8385, 8387, 8391, 8395, 8397, 8399, 8401, 8403, 8425, 8426, 8427, 8428, 8429, 8430, 8431, 8433, 8434, 8441, + 8455, 8456, 8518, 8519, 8520, 8521, 8522, 8523, 8524, 8525, 8526, 8527, 8528, 8529, 8531, 8533, 8534, 8535, 8536, 8538, + 8539, 8540, 8541, 8542, 8543, 8544, 8545, 8675, 8677, 8678, 8679, 8682, 8683, 8684, 8685, 8686, 8687, 8690, 8691, 8692, + 8693, 8694, 8697, 8698, 8699, 8801, 8802, 8803, 8804, 8805, 8806, 8807, 8808, 8809, 8810, 8811, 8812, 8813, 8814, 8815, + 8816, 8817, 8818, 8826, 8836, 8837, 8838, 8839, 8840, 8841, 8857, 8858, 8859, 8860, 8881, 8888, 8897, 8898, 8899, 8900, + 8901, 8902, 8903, 8904, 8905, 8906, 8907, 8908, 8909, 8910, 8911, 8912, 8915, 8916, 8917, 8918, 8919, 8920, 8921, 8922, + 8923, 8924, 8925, 8926, 8927, 8928, 8929, 8930, 8931, 8932, 8933, 8934, 8935, 8936, 8937, 8938, 8939, 8940, 8941, 8942, + 8943, 8944, 8945, 8946, 8972, 8973, 8974, 8975, 8976, 8977, 8978, 8979, 8980, 8981, 8982, 8983, 8984, 8985, 8986, 8987, + 8988, 8989, 8990, 8991, 8992, 8993, 8994, 8995, 8996, 8997, 8998, 8999, 9000, 9001, 9002, 9003, 9004, 9005, 9006, 9007, + 9008, 9009, 9010, 9011, 9012, 9013, 9014, 9015, 9016, 9017, 9018, 9019, 9039, 9040, 9053, 9054, 9055, 9056, 9057, 9059, + 9060, 9061, 9062, 9063, 9064, 9065, 9066, 9067, 9068, 9069, 9070, 9071, 9072, 9073, 9074, 9075, 9130, 9138, 9139, 9140, + 9141, 9146, 9147, 9148, 9149, 9150, 9151, 9152, 9153, 9154, 9155, 9156, 9157, 9158, 9159, 9191, 9205, 9206, 9207, 9208, + 9209, 9210, 9211, 9212, 9213, 9214, 9215, 9216, 9217, 9218, 9221, 9222, 9245, 9248, 9249, 9250, 9251, 9252, 9253, 9254, + 9255, 9265, 9266, 9267, 9271, 9272, 9273, 9274, 9279, 9284, 9285, 9286, 9287, 9288, 9289, 9290, 9292, 9293, 9294, 9295, + 9296, 9297, 9299, 9300, 9303, 9306, 9307, 9308, 9309, 9311, 9331, 9332, 9333, 9335, 9351, 9354, 9356, 9357, 9358, 9359, + 9360, 9364, 9367, 9368, 9372, 9373, 9374, 9377, 9378, 9379, 9380, 9384, 9387, 9388, 9389, 9390, 9391, 9392, 9393, 9394, + 9395, 9396, 9397, 9398, 9399, 9400, 9401, 9402, 9403, 9404, 9405, 9406, 9407, 9422, 9423, 9424, 9425, 9426, 9427, 9428, + 9429, 9430, 9449, 9450, 9453, 9456, 9457, 9458, 9462, 9463, 9464, 9468, 9469, 9470, 9471, 9473, 9474, 9475, 9476, 9477, + 9478, 9479, 9480, 9481, 9482, 9487, 9488, 9489, 9490, 9491, 9492, 9493, 9494, 9498, 9500, 9501, 9502, 9503, 9504, 9505, + 9506, 9507, 9508, 9509, 9510, 9511, 9512, 9513, 9514, 9515, 9516, 9517, 9518, 9519, 9520, 9521, 9522, 9523, 9524, 9525, + 9526, 9527, 9528, 9529, 9530, 9531, 9532, 9533, 9534, 9535, 9536, 9537, 9538, 9539, 9540, 9541, 9542, 9543, 9544, 9545, + 9546, 9547, 9549, 9650, 9651, 9656, 9657, 9663, 9666, 9669, 9672, 9674, 9675, 9678, 9680, 9681, 9694, 9695, 9696, 9697, + 9698, 9699, 9700, 9701, 9702, 9705, 9707, 9709, 9711, 9712, 9713, 9714, 9715, 9716, 9721, 9722, 9723, 9724, 9725, 9739, + 9741, 9742, 9748, 9749, 9753, 9754, 9755, 9758, 9761, 9762, 9763, 9766, 9767, 9775, 9776, 9777, 9778, 9779, 9780, 9781, + 9782, 9783, 9784, 9785, 9793, 9794, 9821, 9822, 9823, 9824, 9825, 9826, 9827, 9828, 9829, 9830, 9831, 9832, 9833, 9834, + 9835, 9836, 9837, 9838, 9839, 9840, 9841, 9842, 9843, 9844, 9845, 9846, 9847, 9848, 9849, 9850, 9851, 9852, 9853, 9854, + 9855, 9856, 9857, 9858, 9859, 9860, 9861, 9862, 9863, 9864, 9865, 9866, 9869, 9870, 9871, 9874, 9875, 9880, 9881, 9883, + 9892, 9893, 9895, 9897, 9907, 9923, 9924, 9927, 9928, 9929, 9930, 9931, 9932, 9933, 9934, 9935, 9939, 9943, 9944, 9945, + 9947, 9948, 9949, 9950, 9951, 9952, 9953, 9964, 9967, 9968, 9969, 9972, 9973, 9974, 9977, 9978, 9988, 9989, 9990, 10150, + 10151, 10156, 10157, 10158, 10160, 10162, 10163, 10164, 10165, 10166, 10167, 10168, 10169, 10170, 10171, 10172, 10173, 10174, 10175, 10176, + 10177, 10178, 10183, 10184, 10185, 10188, 10189, 10190, 10191, 10194, 10195, 10196, 10199, 10200, 10204, 10207, 10208, 10209, 10212, 10213, + 10214, 10217, 10218, 10219, 10222, 10223, 10224, 10227, 10228, 10229, 10235, 10236, 10237, 10240, 10241, 10245, 10246, 10249, 10250, 10252, + 10254, 10256, 10258, 10260, 10262, 10265, 10266, 10268, 10270, 10272, 10275, 10276, 10277, 10280, 10281, 10282, 10283, 10284, 10285, 10286, + 10287, 10288, 10289, 10290, 10291, 10293, 10297, 10298, 10299, 10300, 10303, 10304, 10305, 10306, 10307, 10308, 10309, 10310, 10311, 10312, + 10314, 10315, 10316, 10317, 10318, 10326, 10327, 10328, 10329, 10345, 10346, 10349, 10352, 10353, 10354, 10355, 10356, 10357, 10365, 10412, + 10413, 10414, 10448, 10449, 10450, 10451, 10452, 10453, 10454, 10455, 10456, 10457, 10458, 10459, 10460, 10461, 10462, 10463, 10464, 10465, + 10468, 10471, 10472, 10473, 10474, 10475, 10477, 10481, 10482, 10483, 10484, 10485, 10486, 10487, 10488, 10497, 10498, 10499, 10500, 10507, + 10516, 10545, 10547, 10548, 10549, 10550, 10553, 10554, 10555, 10556, 10565, 10569, 10570, 10571, 10588, 10592, 10594, 10596, 10598, 10601, + 10603, 10604, 10605, 10606, 10622, 10623, 10626, 10627, 10628, 10632, 10633, 10634, 10635, 10636, 10637, 10638, 10639, 10641, 10642, 10643, + 10644, 10645, 10649, 10650, 10651, 10652, 10659, 10660, 10665, 10669, 10670, 10671, 10672, 10673, 10674, 10678, 10679, 10686, 10687, 10688, + 10689, 10690, 10691, 10692, 10699, 10702, 10723, 10724, 10725, 10726, 10727, 10728, 10729, 10731, 10732, 10733, 10734, 10735, 10736, 10737, + 10738, 10739, 10740, 10741, 10742, 10744, 10745, 10746, 10747, 10758, 10759, 10760, 10761, 10762, 10763, 10764, 10765, 10773, 10774, 10779, + 10780, 10781, 10783, 10784, 10785, 10789, 10790, 10791, 10792, 10793, 10794, 10795, 10798, 10799, 10800, 10801, 10802, 10805, 10806, 10807, + 10820, 10826, 10829, 10830, 10831, 10833, 10836, 10837, 10839, 10849, 10851, 10852, 10857, 10860, 10863, 10864, 10865, 10873, 10874, 10875, + 10890, 10891, 10892, 10898, 10899, 10900, 10904, 10906, 10908, 10909, 10910, 10911, 10912, 10913, 10914, 10915, 10916, 10917, 10918, 10920, + 10921, 10922, 10923, 10924, 10925, 10926, 10939, 10940, 10941, 10942, 10943, 10944, 10945, 10946, 10947, 10948, 10949, 10950, 10951, 10952, + 10954, 10955, 10956, 10957, 10958, 10959, 10966, 10967, 10968, 10979, 10980, 10981, 10982, 10983, 10984, 10985, 10986, 10987, 10989, 10991, + 10992, 10993, 10995, 10997, 10999, 11000, 11006, 11007, 11008, 11009, 11012, 11013, 11014, 11015, 11016, 11017, 11018, 11019, 11020, 11021, + 11022, 11023, 11024, 11025, 11026, 11027, 11029, 11030, 11033, 11035, 11036, 11037, 11041, 11042, 11043, 11045, 11046, 11047, 11051, 11052, + 11053, 11055, 11056, 11057, 11061, 11062, 11063, 11068, 11069, 11070, 11074, 11075, 11076, 11077, 11078, 11079, 11085, 11086, 11087, 11091, + 11092, 11093, 11097, 11098, 11099, 11106, 11107, 11108, 11112, 11113, 11114, 11115, 11116, 11117, 11118, 11119, 11120, 11126, 11127, 11128, + 11129, 11130, 11134, 11141, 11142, 11143, 11144, 11145, 11146, 11147, 11148, 11157, 11158, 11161, 11162, 11163, 11169, 11170, 11171, 11172, + 11173, 11174, 11175, 11176, 11177, 11178, 11179, 11180, 11181, 11187, 11188, 11189, 11197, 11198, 11199, 11213, 11214, 11215, 11219, 11222, + 11223, 11224, 11225, 11226, 11266, 11267, 11268, 11269, 11270, 11271, 11272, 11274, 11277, 11278, 11279, 11280, 11281, 11282, 11283, 11284, + 11296, 11297, 11298, 11299, 11300, 11303, 11304, 11305, 11306, 11307, 11311, 11312, 11314, 11338, 11341, 11360, 11361, 11362, 11363, 11364, + 11365, 11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 11374, 11375, 11376, 11377, 11383, 11385, 11390, 11391, 11392, 11393, 11394, + 11399, 11400, 11403, 11404, 11405, 11406, 11407, 11408, 11409, 11410, 11411, 11412, 11413, 11414, 11415, 11416, 11417, 11418, 11419, 11420, + 11421, 11422, 11423, 11424, 11425, 11426, 11427, 11428, 11429, 11430, 11435, 11436, 11437, 11446, 11447, 20000, 20001, 20002, 20003, 20004, + 20005, 20006, 20007, 20008, 20009, 20010, 20011, 20012, 20013, 20014, 20015, 20016, 20017, 20018, 20019, 20020, 20021, 20022, 20023, 20024, + 20025, 20026, 20027, 20028, 20029, 20030, 20031, 20032, 20033, 20034, 20035, 20036, 20037, 20038, 20039, 20040, 20041, 20042, 20043, 20044, + 20045, 20046, 20047, 20048, 20049, 20050, 20135, 20136, 20137, 20138, 20249, 20250, 20251, 20252, 20253, 20254, 20255, 20256, 20257, 20258, + 20349, 20350, 20351, 20352, 20353, 20354, 20355, 20356, 20436, 20437, 20438, 20439, 20440, 20499, 20538, 20539, 20790, 20791, 20822, 20823, + 20824, 20904, 20905, 20906, 20907, 20908, 20909, 20910, 20911, 20912, 20913, 20914, 20915, 20916, 20917, 20918, 20919, 20920, 20921, 20922, + 20923, 20924, 20925, 20926, 20927, 20928, 20929, 20930, 20931, 20932, 20934, 20935, 20936, 21004, 21005, 21006, 21007, 21008, 21009, 21010, + 21011, 21012, 21013, 21014, 21015, 21016, 21017, 21018, 21019, 21020, 21021, 21022, 21023, 21024, 21025, 21026, 21027, 21028, 21029, 21030, + 21031, 21032, 21035, 21036, 21037, 21095, 21096, 21097, 21148, 21149, 21150, 21207, 21208, 21209, 21210, 21211, 21212, 21213, 21214, 21215, + 21216, 21217, 21218, 21219, 21220, 21221, 21222, 21223, 21224, 21225, 21226, 21227, 21228, 21229, 21230, 21231, 21232, 21233, 21234, 21235, + 21236, 21237, 21238, 21239, 21240, 21241, 21242, 21243, 21244, 21245, 21246, 21247, 21248, 21249, 21250, 21251, 21252, 21253, 21254, 21255, + 21256, 21257, 21258, 21259, 21260, 21261, 21262, 21263, 21264, 21291, 21292, 21307, 21308, 21309, 21310, 21311, 21312, 21313, 21314, 21315, + 21316, 21317, 21318, 21319, 21320, 21321, 21322, 21323, 21324, 21325, 21326, 21327, 21328, 21329, 21330, 21331, 21332, 21333, 21334, 21335, + 21336, 21337, 21338, 21339, 21340, 21341, 21342, 21343, 21344, 21345, 21346, 21347, 21348, 21349, 21350, 21351, 21352, 21353, 21354, 21355, + 21356, 21357, 21358, 21359, 21360, 21361, 21362, 21363, 21364, 21413, 21414, 21415, 21416, 21417, 21418, 21419, 21420, 21421, 21422, 21423, + 21453, 21454, 21455, 21456, 21457, 21458, 21459, 21460, 21461, 21462, 21463, 21500, 21780, 21781, 21782, 21818, 21896, 21897, 21898, 21899, + 22032, 22033, 22091, 22092, 22171, 22172, 22173, 22174, 22175, 22176, 22177, 22181, 22182, 22183, 22184, 22185, 22186, 22187, 22191, 22192, + 22193, 22194, 22195, 22196, 22197, 22207, 22208, 22209, 22210, 22211, 22212, 22213, 22214, 22215, 22216, 22217, 22218, 22219, 22220, 22221, + 22222, 22229, 22230, 22231, 22232, 22234, 22235, 22239, 22240, 22243, 22244, 22245, 22246, 22247, 22248, 22249, 22250, 22262, 22263, 22264, + 22265, 22275, 22277, 22279, 22281, 22283, 22285, 22287, 22289, 22291, 22293, 22300, 22307, 22308, 22309, 22310, 22311, 22312, 22313, 22314, + 22315, 22316, 22317, 22318, 22319, 22320, 22321, 22322, 22332, 22337, 22338, 22348, 22349, 22350, 22351, 22352, 22353, 22354, 22355, 22356, + 22357, 22391, 22392, 22407, 22408, 22409, 22410, 22411, 22412, 22413, 22414, 22415, 22416, 22417, 22418, 22419, 22420, 22421, 22422, 22462, + 22463, 22464, 22465, 22521, 22522, 22523, 22524, 22525, 22607, 22608, 22609, 22610, 22611, 22612, 22613, 22614, 22615, 22616, 22617, 22618, + 22619, 22620, 22621, 22622, 22639, 22641, 22642, 22643, 22644, 22645, 22646, 22648, 22649, 22650, 22651, 22652, 22653, 22654, 22655, 22656, + 22657, 22700, 22707, 22708, 22709, 22710, 22711, 22712, 22713, 22714, 22715, 22716, 22717, 22718, 22719, 22720, 22721, 22722, 22739, 22762, + 22763, 22764, 22765, 22770, 22780, 22807, 22808, 22809, 22810, 22811, 22812, 22813, 22814, 22815, 22816, 22817, 22818, 22819, 22820, 22821, + 22822, 22991, 22992, 22993, 22994, 23028, 23029, 23030, 23031, 23032, 23033, 23034, 23035, 23036, 23037, 23038, 23090, 23095, 23239, 23240, + 23301, 23302, 23303, 23304, 23305, 23306, 23307, 23308, 23309, 23310, 23311, 23312, 23313, 23314, 23315, 23316, 23317, 23318, 23319, 23320, + 23321, 23322, 23323, 23324, 23325, 23326, 23327, 23328, 23329, 23330, 23331, 23332, 23333, 23700, 23830, 23831, 23832, 23833, 23834, 23835, + 23836, 23837, 23838, 23839, 23840, 23841, 23842, 23843, 23844, 23845, 23846, 23847, 23848, 23849, 23850, 23851, 23852, 23866, 23867, 23868, + 23869, 23870, 23871, 23872, 23877, 23878, 23879, 23880, 23881, 23882, 23883, 23884, 23887, 23888, 23889, 23890, 23891, 23892, 23893, 23894, + 23946, 23947, 23948, 24047, 24048, 24100, 24200, 24305, 24306, 24311, 24312, 24313, 24342, 24343, 24344, 24345, 24346, 24347, 24370, 24371, + 24372, 24373, 24374, 24375, 24376, 24377, 24378, 24379, 24380, 24381, 24382, 24383, 24500, 24547, 24548, 24600, 24718, 24719, 24720, 24817, + 24818, 24819, 24820, 24821, 24877, 24878, 24879, 24880, 24881, 24882, 24891, 24892, 24893, 25000, 25231, 25391, 25392, 25393, 25394, 25395, + 25828, 25829, 25830, 25831, 25832, 25833, 25834, 25835, 25836, 25837, 25884, 25932, 26191, 26192, 26194, 26195, 26237, 26331, 26332, 26391, + 26392, 26393, 26632, 26692, 26701, 26702, 26703, 26704, 26705, 26706, 26707, 26708, 26709, 26710, 26711, 26712, 26713, 26714, 26715, 26716, + 26717, 26718, 26719, 26720, 26721, 26722, 26729, 26730, 26731, 26732, 26733, 26734, 26735, 26736, 26737, 26738, 26739, 26740, 26741, 26742, + 26743, 26744, 26745, 26746, 26748, 26749, 26750, 26751, 26752, 26753, 26754, 26755, 26756, 26757, 26758, 26759, 26760, 26766, 26767, 26768, + 26769, 26770, 26771, 26772, 26773, 26774, 26775, 26776, 26777, 26778, 26779, 26780, 26781, 26782, 26783, 26784, 26785, 26786, 26787, 26791, + 26792, 26793, 26794, 26795, 26796, 26797, 26798, 26799, 26847, 26848, 26849, 26850, 26851, 26852, 26853, 26854, 26855, 26856, 26857, 26858, + 26859, 26860, 26861, 26862, 26863, 26864, 26865, 26866, 26867, 26868, 26869, 26870, 26891, 26892, 26893, 26894, 26895, 26896, 26897, 26898, + 26899, 26901, 26902, 26903, 26904, 26905, 26906, 26907, 26908, 26909, 26910, 26911, 26912, 26913, 26914, 26915, 26916, 26917, 26918, 26919, + 26920, 26921, 26922, 26923, 26929, 26930, 26931, 26932, 26933, 26934, 26935, 26936, 26937, 26938, 26939, 26940, 26941, 26942, 26943, 26944, + 26945, 26946, 26948, 26949, 26950, 26951, 26952, 26953, 26954, 26955, 26956, 26957, 26958, 26959, 26960, 26961, 26962, 26963, 26964, 26965, + 26966, 26967, 26968, 26969, 26970, 26971, 26972, 26973, 26974, 26975, 26976, 26977, 26978, 26980, 26981, 26982, 26983, 26984, 26985, 26986, + 26987, 26988, 26989, 26990, 26991, 26992, 26993, 26994, 26995, 26996, 26997, 26998, 27039, 27040, 27120, 27200, 27205, 27206, 27207, 27208, + 27209, 27210, 27211, 27212, 27213, 27214, 27215, 27216, 27217, 27218, 27219, 27220, 27221, 27222, 27223, 27224, 27225, 27226, 27227, 27228, + 27229, 27230, 27231, 27232, 27258, 27259, 27260, 27291, 27292, 27391, 27392, 27393, 27394, 27395, 27396, 27397, 27398, 27429, 27493, 27500, + 27561, 27562, 27563, 27564, 27571, 27572, 27573, 27574, 27700, 27701, 27702, 27703, 27704, 27705, 27706, 27707, 28191, 28192, 28193, 28232, + 28348, 28349, 28350, 28351, 28352, 28353, 28354, 28355, 28356, 28357, 28358, 28404, 28405, 28406, 28407, 28408, 28409, 28410, 28411, 28412, + 28413, 28414, 28415, 28416, 28417, 28418, 28419, 28420, 28421, 28422, 28423, 28424, 28425, 28426, 28427, 28428, 28429, 28430, 28431, 28432, + 28600, 28991, 28992, 29101, 29168, 29169, 29170, 29171, 29172, 29187, 29188, 29189, 29190, 29191, 29192, 29193, 29194, 29195, 29220, 29221, + 29333, 29371, 29373, 29375, 29377, 29379, 29381, 29383, 29385, 29701, 29702, 29738, 29739, 29849, 29850, 29871, 29872, 29873, 29874, 29901, + 29902, 29903, 30161, 30162, 30163, 30164, 30165, 30166, 30167, 30168, 30169, 30170, 30171, 30172, 30173, 30174, 30175, 30176, 30177, 30178, + 30179, 30200, 30339, 30340, 30491, 30492, 30493, 30494, 30729, 30730, 30731, 30732, 30791, 30792, 31028, 31121, 31154, 31170, 31171, 31251, + 31252, 31253, 31254, 31255, 31256, 31257, 31258, 31259, 31281, 31282, 31283, 31284, 31285, 31286, 31287, 31288, 31289, 31290, 31300, 31370, + 31466, 31467, 31468, 31469, 31528, 31529, 31600, 31838, 31839, 31901, 31965, 31966, 31967, 31968, 31969, 31970, 31971, 31972, 31973, 31974, + 31975, 31976, 31977, 31978, 31979, 31980, 31981, 31982, 31983, 31984, 31985, 31986, 31987, 31988, 31989, 31990, 31991, 31992, 31993, 31994, + 31995, 31996, 31997, 31998, 31999, 32000, 32001, 32002, 32003, 32005, 32006, 32007, 32008, 32009, 32010, 32011, 32012, 32013, 32014, 32015, + 32016, 32017, 32019, 32020, 32021, 32022, 32023, 32024, 32025, 32026, 32027, 32028, 32030, 32031, 32033, 32034, 32035, 32037, 32038, 32039, + 32040, 32041, 32042, 32043, 32044, 32045, 32046, 32047, 32048, 32049, 32050, 32051, 32052, 32053, 32054, 32055, 32056, 32057, 32058, 32064, + 32065, 32066, 32067, 32081, 32082, 32083, 32084, 32085, 32086, 32098, 32099, 32100, 32104, 32107, 32108, 32109, 32110, 32111, 32112, 32113, + 32114, 32115, 32116, 32117, 32118, 32119, 32120, 32121, 32122, 32123, 32124, 32125, 32126, 32127, 32128, 32129, 32130, 32133, 32134, 32135, + 32136, 32137, 32138, 32139, 32140, 32141, 32142, 32143, 32144, 32145, 32146, 32147, 32148, 32149, 32150, 32151, 32152, 32153, 32154, 32155, + 32156, 32157, 32158, 32159, 32161, 32164, 32165, 32166, 32167, 32181, 32182, 32183, 32184, 32185, 32186, 32187, 32188, 32189, 32190, 32191, + 32192, 32193, 32194, 32195, 32196, 32197, 32198, 32199, 32201, 32202, 32203, 32204, 32205, 32206, 32207, 32208, 32209, 32210, 32211, 32212, + 32213, 32214, 32215, 32216, 32217, 32218, 32219, 32220, 32221, 32222, 32223, 32224, 32225, 32226, 32227, 32228, 32229, 32230, 32231, 32232, + 32233, 32234, 32235, 32236, 32237, 32238, 32239, 32240, 32241, 32242, 32243, 32244, 32245, 32246, 32247, 32248, 32249, 32250, 32251, 32252, + 32253, 32254, 32255, 32256, 32257, 32258, 32259, 32260, 32301, 32302, 32303, 32304, 32305, 32306, 32307, 32308, 32309, 32310, 32311, 32312, + 32313, 32314, 32315, 32316, 32317, 32318, 32319, 32320, 32321, 32322, 32323, 32324, 32325, 32326, 32327, 32328, 32329, 32330, 32331, 32332, + 32333, 32334, 32335, 32336, 32337, 32338, 32339, 32340, 32341, 32342, 32343, 32344, 32345, 32346, 32347, 32348, 32349, 32350, 32351, 32352, + 32353, 32354, 32355, 32356, 32357, 32358, 32359, 32360, 32401, 32402, 32403, 32404, 32405, 32406, 32407, 32408, 32409, 32410, 32411, 32412, + 32413, 32414, 32415, 32416, 32417, 32418, 32419, 32420, 32421, 32422, 32423, 32424, 32425, 32426, 32427, 32428, 32429, 32430, 32431, 32432, + 32433, 32434, 32435, 32436, 32437, 32438, 32439, 32440, 32441, 32442, 32443, 32444, 32445, 32446, 32447, 32448, 32449, 32450, 32451, 32452, + 32453, 32454, 32455, 32456, 32457, 32458, 32459, 32460, 32501, 32502, 32503, 32504, 32505, 32506, 32507, 32508, 32509, 32510, 32511, 32512, + 32513, 32514, 32515, 32516, 32517, 32518, 32519, 32520, 32521, 32522, 32523, 32524, 32525, 32526, 32527, 32528, 32529, 32530, 32531, 32532, + 32533, 32534, 32535, 32536, 32537, 32538, 32539, 32540, 32541, 32542, 32543, 32544, 32545, 32546, 32547, 32548, 32549, 32550, 32551, 32552, + 32553, 32554, 32555, 32556, 32557, 32558, 32559, 32560, 32600, 32601, 32602, 32603, 32604, 32605, 32606, 32607, 32608, 32609, 32610, 32611, + 32612, 32613, 32614, 32615, 32616, 32617, 32618, 32619, 32620, 32621, 32622, 32623, 32624, 32625, 32626, 32627, 32628, 32629, 32630, 32631, + 32632, 32633, 32634, 32635, 32636, 32637, 32638, 32639, 32640, 32641, 32642, 32643, 32644, 32645, 32646, 32647, 32648, 32649, 32650, 32651, + 32652, 32653, 32654, 32655, 32656, 32657, 32658, 32659, 32660, 32661, 32664, 32665, 32666, 32667, 32700, 32701, 32702, 32703, 32704, 32705, + 32706, 32707, 32708, 32709, 32710, 32711, 32712, 32713, 32714, 32715, 32716, 32717, 32718, 32719, 32720, 32721, 32722, 32723, 32724, 32725, + 32726, 32727, 32728, 32729, 32730, 32731, 32732, 32733, 32734, 32735, 32736, 32737, 32738, 32739, 32740, 32741, 32742, 32743, 32744, 32745, + 32746, 32747, 32748, 32749, 32750, 32751, 32752, 32753, 32754, 32755, 32756, 32757, 32758, 32759, 32760, 32761, 32766, + }; + + internal static bool TryGetGeographicCrs(int index, out EpsgGeographicCrsRecord record) + { + switch (index) + { + case 0: + record = new EpsgGeographicCrsRecord(3819, "HD1909", 1024, 6422); + return true; + case 1: + record = new EpsgGeographicCrsRecord(3821, "TWD67", 1025, 6422); + return true; + case 2: + record = new EpsgGeographicCrsRecord(3823, "TWD97", 1026, 6423); + return true; + case 3: + record = new EpsgGeographicCrsRecord(3824, "TWD97", 1026, 6422); + return true; + case 4: + record = new EpsgGeographicCrsRecord(3888, "IGRS", 1029, 6423); + return true; + case 5: + record = new EpsgGeographicCrsRecord(3889, "IGRS", 1029, 6422); + return true; + case 6: + record = new EpsgGeographicCrsRecord(3906, "MGI 1901", 1031, 6422); + return true; + case 7: + record = new EpsgGeographicCrsRecord(4017, "ETRS89-MDA [MOLDREF99]", 1032, 6423); + return true; + case 8: + record = new EpsgGeographicCrsRecord(4023, "ETRS89-MDA [MOLDREF99]", 1032, 6422); + return true; + case 9: + record = new EpsgGeographicCrsRecord(4040, "RGRDC 2005", 1033, 6423); + return true; + case 10: + record = new EpsgGeographicCrsRecord(4046, "RGRDC 2005", 1033, 6422); + return true; + case 11: + record = new EpsgGeographicCrsRecord(4074, "ETRS89-SRB [SREF98]", 1034, 6423); + return true; + case 12: + record = new EpsgGeographicCrsRecord(4075, "ETRS89-SRB [SREF98]", 1034, 6422); + return true; + case 13: + record = new EpsgGeographicCrsRecord(4080, "REGCAN95", 1035, 6423); + return true; + case 14: + record = new EpsgGeographicCrsRecord(4081, "REGCAN95", 1035, 6422); + return true; + case 15: + record = new EpsgGeographicCrsRecord(4120, "Greek", 6120, 6422); + return true; + case 16: + record = new EpsgGeographicCrsRecord(4121, "GGRS87", 6121, 6422); + return true; + case 17: + record = new EpsgGeographicCrsRecord(4122, "ATS77", 6122, 6422); + return true; + case 18: + record = new EpsgGeographicCrsRecord(4123, "KKJ", 6123, 6422); + return true; + case 19: + record = new EpsgGeographicCrsRecord(4124, "RT90", 6124, 6422); + return true; + case 20: + record = new EpsgGeographicCrsRecord(4127, "Tete", 6127, 6422); + return true; + case 21: + record = new EpsgGeographicCrsRecord(4128, "Madzansua", 6128, 6422); + return true; + case 22: + record = new EpsgGeographicCrsRecord(4129, "Observatario", 6129, 6422); + return true; + case 23: + record = new EpsgGeographicCrsRecord(4130, "Moznet", 6130, 6422); + return true; + case 24: + record = new EpsgGeographicCrsRecord(4131, "Indian 1960", 6131, 6422); + return true; + case 25: + record = new EpsgGeographicCrsRecord(4132, "FD58", 6132, 6422); + return true; + case 26: + record = new EpsgGeographicCrsRecord(4133, "EST92", 6133, 6422); + return true; + case 27: + record = new EpsgGeographicCrsRecord(4134, "PSD93", 6134, 6422); + return true; + case 28: + record = new EpsgGeographicCrsRecord(4135, "Old Hawaiian", 6135, 6422); + return true; + case 29: + record = new EpsgGeographicCrsRecord(4136, "St. Lawrence Island", 6136, 6422); + return true; + case 30: + record = new EpsgGeographicCrsRecord(4137, "St. Paul Island", 6137, 6422); + return true; + case 31: + record = new EpsgGeographicCrsRecord(4138, "St. George Island", 6138, 6422); + return true; + case 32: + record = new EpsgGeographicCrsRecord(4139, "Puerto Rico", 6139, 6422); + return true; + case 33: + record = new EpsgGeographicCrsRecord(4141, "Israel 1993", 6141, 6422); + return true; + case 34: + record = new EpsgGeographicCrsRecord(4142, "Locodjo 1965", 6142, 6422); + return true; + case 35: + record = new EpsgGeographicCrsRecord(4143, "Abidjan 1987", 6143, 6422); + return true; + case 36: + record = new EpsgGeographicCrsRecord(4144, "Kalianpur 1937", 6144, 6422); + return true; + case 37: + record = new EpsgGeographicCrsRecord(4145, "Kalianpur 1962", 6145, 6422); + return true; + case 38: + record = new EpsgGeographicCrsRecord(4146, "Kalianpur 1975", 6146, 6422); + return true; + case 39: + record = new EpsgGeographicCrsRecord(4147, "Hanoi 1972", 6147, 6422); + return true; + case 40: + record = new EpsgGeographicCrsRecord(4148, "Hartebeesthoek94", 6148, 6422); + return true; + case 41: + record = new EpsgGeographicCrsRecord(4149, "CH1903", 6149, 6422); + return true; + case 42: + record = new EpsgGeographicCrsRecord(4150, "CH1903+", 6150, 6422); + return true; + case 43: + record = new EpsgGeographicCrsRecord(4151, "CHTRS95", 6151, 6422); + return true; + case 44: + record = new EpsgGeographicCrsRecord(4152, "NAD83(HARN)", 6152, 6422); + return true; + case 45: + record = new EpsgGeographicCrsRecord(4153, "Rassadiran", 6153, 6422); + return true; + case 46: + record = new EpsgGeographicCrsRecord(4154, "ED50(ED77)", 6154, 6422); + return true; + case 47: + record = new EpsgGeographicCrsRecord(4155, "Dabola 1981", 6155, 6422); + return true; + case 48: + record = new EpsgGeographicCrsRecord(4156, "S-JTSK", 6156, 6422); + return true; + case 49: + record = new EpsgGeographicCrsRecord(4157, "Mount Dillon", 6157, 6422); + return true; + case 50: + record = new EpsgGeographicCrsRecord(4158, "Naparima 1955", 6158, 6422); + return true; + case 51: + record = new EpsgGeographicCrsRecord(4159, "ELD79", 6159, 6422); + return true; + case 52: + record = new EpsgGeographicCrsRecord(4160, "Chos Malal 1914", 6160, 6422); + return true; + case 53: + record = new EpsgGeographicCrsRecord(4161, "Pampa del Castillo", 6161, 6422); + return true; + case 54: + record = new EpsgGeographicCrsRecord(4162, "Korean 1985", 6162, 6422); + return true; + case 55: + record = new EpsgGeographicCrsRecord(4163, "Yemen NGN96", 6163, 6422); + return true; + case 56: + record = new EpsgGeographicCrsRecord(4164, "South Yemen", 6164, 6422); + return true; + case 57: + record = new EpsgGeographicCrsRecord(4165, "Bissau", 6165, 6422); + return true; + case 58: + record = new EpsgGeographicCrsRecord(4166, "Korean 1995", 6166, 6422); + return true; + case 59: + record = new EpsgGeographicCrsRecord(4167, "NZGD2000", 6167, 6422); + return true; + case 60: + record = new EpsgGeographicCrsRecord(4168, "Accra", 6168, 6422); + return true; + case 61: + record = new EpsgGeographicCrsRecord(4169, "American Samoa 1962", 6169, 6422); + return true; + case 62: + record = new EpsgGeographicCrsRecord(4170, "SIRGAS 1995", 6170, 6422); + return true; + case 63: + record = new EpsgGeographicCrsRecord(4171, "ETRS89-FRA [RGF93 v1]", 6171, 6422); + return true; + case 64: + record = new EpsgGeographicCrsRecord(4173, "ETRS89-IRE [ETRF2000]", 6173, 6422); + return true; + case 65: + record = new EpsgGeographicCrsRecord(4174, "Sierra Leone 1924", 6174, 6422); + return true; + case 66: + record = new EpsgGeographicCrsRecord(4175, "Sierra Leone 1968", 6175, 6422); + return true; + case 67: + record = new EpsgGeographicCrsRecord(4176, "Australian Antarctic", 6176, 6422); + return true; + case 68: + record = new EpsgGeographicCrsRecord(4178, "Pulkovo 1942(83)", 6178, 6422); + return true; + case 69: + record = new EpsgGeographicCrsRecord(4179, "Pulkovo 1942(58)", 6179, 6422); + return true; + case 70: + record = new EpsgGeographicCrsRecord(4180, "ETRS89-EST [EST97]", 6180, 6422); + return true; + case 71: + record = new EpsgGeographicCrsRecord(4181, "LUREF", 6181, 6422); + return true; + case 72: + record = new EpsgGeographicCrsRecord(4182, "Azores Occidental 1939", 6182, 6422); + return true; + case 73: + record = new EpsgGeographicCrsRecord(4183, "Azores Central 1948", 6183, 6422); + return true; + case 74: + record = new EpsgGeographicCrsRecord(4184, "Azores Oriental 1940", 6184, 6422); + return true; + case 75: + record = new EpsgGeographicCrsRecord(4188, "OSNI 1952", 6188, 6422); + return true; + case 76: + record = new EpsgGeographicCrsRecord(4189, "REGVEN", 6189, 6422); + return true; + case 77: + record = new EpsgGeographicCrsRecord(4190, "POSGAR 98", 6190, 6422); + return true; + case 78: + record = new EpsgGeographicCrsRecord(4191, "Albanian 1987", 6191, 6422); + return true; + case 79: + record = new EpsgGeographicCrsRecord(4192, "Douala 1948", 6192, 6422); + return true; + case 80: + record = new EpsgGeographicCrsRecord(4193, "Manoca 1962", 6193, 6422); + return true; + case 81: + record = new EpsgGeographicCrsRecord(4194, "Qoornoq 1927", 6194, 6422); + return true; + case 82: + record = new EpsgGeographicCrsRecord(4195, "Scoresbysund 1952", 6195, 6422); + return true; + case 83: + record = new EpsgGeographicCrsRecord(4196, "Ammassalik 1958", 6196, 6422); + return true; + case 84: + record = new EpsgGeographicCrsRecord(4197, "Garoua", 6197, 6422); + return true; + case 85: + record = new EpsgGeographicCrsRecord(4198, "Kousseri", 6198, 6422); + return true; + case 86: + record = new EpsgGeographicCrsRecord(4199, "Egypt 1930", 6199, 6422); + return true; + case 87: + record = new EpsgGeographicCrsRecord(4200, "Pulkovo 1995", 6200, 6422); + return true; + case 88: + record = new EpsgGeographicCrsRecord(4201, "Adindan", 6201, 6422); + return true; + case 89: + record = new EpsgGeographicCrsRecord(4202, "AGD66", 6202, 6422); + return true; + case 90: + record = new EpsgGeographicCrsRecord(4203, "AGD84", 6203, 6422); + return true; + case 91: + record = new EpsgGeographicCrsRecord(4204, "Ain el Abd", 6204, 6422); + return true; + case 92: + record = new EpsgGeographicCrsRecord(4205, "Afgooye", 6205, 6422); + return true; + case 93: + record = new EpsgGeographicCrsRecord(4206, "Agadez", 6206, 6422); + return true; + case 94: + record = new EpsgGeographicCrsRecord(4207, "Lisbon", 6207, 6422); + return true; + case 95: + record = new EpsgGeographicCrsRecord(4208, "Aratu", 6208, 6422); + return true; + case 96: + record = new EpsgGeographicCrsRecord(4209, "Arc 1950", 6209, 6422); + return true; + case 97: + record = new EpsgGeographicCrsRecord(4210, "Arc 1960", 6210, 6422); + return true; + case 98: + record = new EpsgGeographicCrsRecord(4211, "Batavia", 6211, 6422); + return true; + case 99: + record = new EpsgGeographicCrsRecord(4212, "Barbados 1938", 6212, 6422); + return true; + case 100: + record = new EpsgGeographicCrsRecord(4213, "Beduaram", 6213, 6422); + return true; + case 101: + record = new EpsgGeographicCrsRecord(4214, "Beijing 1954", 6214, 6422); + return true; + case 102: + record = new EpsgGeographicCrsRecord(4215, "BD50", 6215, 6422); + return true; + case 103: + record = new EpsgGeographicCrsRecord(4216, "Bermuda 1957", 6216, 6422); + return true; + case 104: + record = new EpsgGeographicCrsRecord(4218, "Bogota 1975", 6218, 6422); + return true; + case 105: + record = new EpsgGeographicCrsRecord(4219, "Bukit Rimpah", 6219, 6422); + return true; + case 106: + record = new EpsgGeographicCrsRecord(4220, "Camacupa 1948", 6220, 6422); + return true; + case 107: + record = new EpsgGeographicCrsRecord(4221, "Campo Inchauspe", 6221, 6422); + return true; + case 108: + record = new EpsgGeographicCrsRecord(4222, "Cape", 6222, 6422); + return true; + case 109: + record = new EpsgGeographicCrsRecord(4223, "Carthage", 6223, 6422); + return true; + case 110: + record = new EpsgGeographicCrsRecord(4224, "Chua", 6224, 6422); + return true; + case 111: + record = new EpsgGeographicCrsRecord(4225, "Corrego Alegre 1970-72", 6225, 6422); + return true; + case 112: + record = new EpsgGeographicCrsRecord(4227, "Deir ez Zor", 6227, 6422); + return true; + case 113: + record = new EpsgGeographicCrsRecord(4229, "Egypt 1907", 6229, 6422); + return true; + case 114: + record = new EpsgGeographicCrsRecord(4230, "ED50", 6230, 6422); + return true; + case 115: + record = new EpsgGeographicCrsRecord(4231, "ED87", 6231, 6422); + return true; + case 116: + record = new EpsgGeographicCrsRecord(4232, "Fahud", 6232, 6422); + return true; + case 117: + record = new EpsgGeographicCrsRecord(4236, "Hu Tzu Shan 1950", 6236, 6422); + return true; + case 118: + record = new EpsgGeographicCrsRecord(4237, "HD72", 6237, 6422); + return true; + case 119: + record = new EpsgGeographicCrsRecord(4238, "ID74", 6238, 6422); + return true; + case 120: + record = new EpsgGeographicCrsRecord(4239, "Indian 1954", 6239, 6422); + return true; + case 121: + record = new EpsgGeographicCrsRecord(4240, "Indian 1975", 6240, 6422); + return true; + case 122: + record = new EpsgGeographicCrsRecord(4241, "Jamaica 1875", 6241, 6422); + return true; + case 123: + record = new EpsgGeographicCrsRecord(4242, "JAD69", 6242, 6422); + return true; + case 124: + record = new EpsgGeographicCrsRecord(4243, "Kalianpur 1880", 6243, 6422); + return true; + case 125: + record = new EpsgGeographicCrsRecord(4244, "Kandawala", 6244, 6422); + return true; + case 126: + record = new EpsgGeographicCrsRecord(4245, "Kertau 1968", 6245, 6422); + return true; + case 127: + record = new EpsgGeographicCrsRecord(4246, "KOC", 6246, 6422); + return true; + case 128: + record = new EpsgGeographicCrsRecord(4247, "La Canoa", 6247, 6422); + return true; + case 129: + record = new EpsgGeographicCrsRecord(4248, "PSAD56", 6248, 6422); + return true; + case 130: + record = new EpsgGeographicCrsRecord(4249, "Lake", 6249, 6422); + return true; + case 131: + record = new EpsgGeographicCrsRecord(4250, "Leigon", 6250, 6422); + return true; + case 132: + record = new EpsgGeographicCrsRecord(4251, "Liberia 1964", 6251, 6422); + return true; + case 133: + record = new EpsgGeographicCrsRecord(4252, "Lome", 6252, 6422); + return true; + case 134: + record = new EpsgGeographicCrsRecord(4253, "Luzon 1911", 6253, 6422); + return true; + case 135: + record = new EpsgGeographicCrsRecord(4254, "Hito XVIII 1963", 6254, 6422); + return true; + case 136: + record = new EpsgGeographicCrsRecord(4255, "Herat North", 6255, 6422); + return true; + case 137: + record = new EpsgGeographicCrsRecord(4256, "Mahe 1971", 6256, 6422); + return true; + case 138: + record = new EpsgGeographicCrsRecord(4257, "Makassar", 6257, 6422); + return true; + case 139: + record = new EpsgGeographicCrsRecord(4258, "ETRS89", 6258, 6422); + return true; + case 140: + record = new EpsgGeographicCrsRecord(4259, "Malongo 1987", 6259, 6422); + return true; + case 141: + record = new EpsgGeographicCrsRecord(4261, "Merchich", 6261, 6422); + return true; + case 142: + record = new EpsgGeographicCrsRecord(4262, "Massawa", 6262, 6422); + return true; + case 143: + record = new EpsgGeographicCrsRecord(4263, "Minna", 6263, 6422); + return true; + case 144: + record = new EpsgGeographicCrsRecord(4265, "Monte Mario", 6265, 6422); + return true; + case 145: + record = new EpsgGeographicCrsRecord(4266, "M'poraloko", 6266, 6422); + return true; + case 146: + record = new EpsgGeographicCrsRecord(4267, "NAD27", 6267, 6422); + return true; + case 147: + record = new EpsgGeographicCrsRecord(4269, "NAD83", 6269, 6422); + return true; + case 148: + record = new EpsgGeographicCrsRecord(4270, "Nahrwan 1967", 6270, 6422); + return true; + case 149: + record = new EpsgGeographicCrsRecord(4271, "Naparima 1972", 6271, 6422); + return true; + case 150: + record = new EpsgGeographicCrsRecord(4272, "NZGD49", 6272, 6422); + return true; + case 151: + record = new EpsgGeographicCrsRecord(4273, "NGO 1948", 6273, 6422); + return true; + case 152: + record = new EpsgGeographicCrsRecord(4274, "Datum 73", 6274, 6422); + return true; + case 153: + record = new EpsgGeographicCrsRecord(4275, "NTF", 6275, 6422); + return true; + case 154: + record = new EpsgGeographicCrsRecord(4276, "NSWC 9Z-2", 6276, 6422); + return true; + case 155: + record = new EpsgGeographicCrsRecord(4277, "OSGB36", 6277, 6422); + return true; + case 156: + record = new EpsgGeographicCrsRecord(4278, "OSGB70", 6278, 6422); + return true; + case 157: + record = new EpsgGeographicCrsRecord(4279, "OS(SN)80", 6279, 6422); + return true; + case 158: + record = new EpsgGeographicCrsRecord(4281, "Palestine 1923", 6281, 6422); + return true; + case 159: + record = new EpsgGeographicCrsRecord(4282, "Pointe Noire", 6282, 6422); + return true; + case 160: + record = new EpsgGeographicCrsRecord(4283, "GDA94", 6283, 6422); + return true; + case 161: + record = new EpsgGeographicCrsRecord(4284, "Pulkovo 1942", 6284, 6422); + return true; + case 162: + record = new EpsgGeographicCrsRecord(4285, "Qatar 1974", 6285, 6422); + return true; + case 163: + record = new EpsgGeographicCrsRecord(4286, "Qatar 1948", 6286, 6422); + return true; + case 164: + record = new EpsgGeographicCrsRecord(4288, "Loma Quintana", 6288, 6422); + return true; + case 165: + record = new EpsgGeographicCrsRecord(4289, "Amersfoort", 6289, 6422); + return true; + case 166: + record = new EpsgGeographicCrsRecord(4292, "Sapper Hill 1943", 6292, 6422); + return true; + case 167: + record = new EpsgGeographicCrsRecord(4293, "Schwarzeck", 6293, 6422); + return true; + case 168: + record = new EpsgGeographicCrsRecord(4295, "Serindung", 6295, 6422); + return true; + case 169: + record = new EpsgGeographicCrsRecord(4297, "Tananarive", 6297, 6422); + return true; + case 170: + record = new EpsgGeographicCrsRecord(4298, "Timbalai 1948", 6298, 6422); + return true; + case 171: + record = new EpsgGeographicCrsRecord(4299, "TM65", 6299, 6422); + return true; + case 172: + record = new EpsgGeographicCrsRecord(4300, "TM75", 6300, 6422); + return true; + case 173: + record = new EpsgGeographicCrsRecord(4301, "Tokyo", 6301, 6422); + return true; + case 174: + record = new EpsgGeographicCrsRecord(4302, "Trinidad 1903", 6302, 6422); + return true; + case 175: + record = new EpsgGeographicCrsRecord(4303, "TC(1948)", 6303, 6422); + return true; + case 176: + record = new EpsgGeographicCrsRecord(4304, "Voirol 1875", 6304, 6422); + return true; + case 177: + record = new EpsgGeographicCrsRecord(4306, "Bern 1938", 6306, 6422); + return true; + case 178: + record = new EpsgGeographicCrsRecord(4307, "Nord Sahara 1959", 6307, 6422); + return true; + case 179: + record = new EpsgGeographicCrsRecord(4308, "RT38", 6308, 6422); + return true; + case 180: + record = new EpsgGeographicCrsRecord(4309, "Yacare", 6309, 6422); + return true; + case 181: + record = new EpsgGeographicCrsRecord(4310, "Yoff", 6310, 6422); + return true; + case 182: + record = new EpsgGeographicCrsRecord(4311, "Zanderij", 6311, 6422); + return true; + case 183: + record = new EpsgGeographicCrsRecord(4312, "MGI", 6312, 6422); + return true; + case 184: + record = new EpsgGeographicCrsRecord(4313, "BD72", 6313, 6422); + return true; + case 185: + record = new EpsgGeographicCrsRecord(4314, "DHDN", 6314, 6422); + return true; + case 186: + record = new EpsgGeographicCrsRecord(4315, "Conakry 1905", 6315, 6422); + return true; + case 187: + record = new EpsgGeographicCrsRecord(4316, "Dealul Piscului 1930", 6316, 6422); + return true; + case 188: + record = new EpsgGeographicCrsRecord(4318, "NGN", 6318, 6422); + return true; + case 189: + record = new EpsgGeographicCrsRecord(4319, "KUDAMS", 6319, 6422); + return true; + case 190: + record = new EpsgGeographicCrsRecord(4322, "WGS 72", 6322, 6422); + return true; + case 191: + record = new EpsgGeographicCrsRecord(4324, "WGS 72BE", 6324, 6422); + return true; + case 192: + record = new EpsgGeographicCrsRecord(4326, "WGS 84", 6326, 6422); + return true; + case 193: + record = new EpsgGeographicCrsRecord(4463, "RGSPM06", 1038, 6422); + return true; + case 194: + record = new EpsgGeographicCrsRecord(4466, "RGSPM06", 1038, 6423); + return true; + case 195: + record = new EpsgGeographicCrsRecord(4469, "RGM04", 1036, 6423); + return true; + case 196: + record = new EpsgGeographicCrsRecord(4470, "RGM04", 1036, 6422); + return true; + case 197: + record = new EpsgGeographicCrsRecord(4472, "Cadastre 1997", 1037, 6423); + return true; + case 198: + record = new EpsgGeographicCrsRecord(4475, "Cadastre 1997", 1037, 6422); + return true; + case 199: + record = new EpsgGeographicCrsRecord(4480, "China Geodetic Coordinate System 2000", 1043, 6423); + return true; + case 200: + record = new EpsgGeographicCrsRecord(4482, "Mexico ITRF92", 1042, 6423); + return true; + case 201: + record = new EpsgGeographicCrsRecord(4483, "Mexico ITRF92", 1042, 6422); + return true; + case 202: + record = new EpsgGeographicCrsRecord(4490, "China Geodetic Coordinate System 2000", 1043, 6422); + return true; + case 203: + record = new EpsgGeographicCrsRecord(4555, "New Beijing", 1045, 6422); + return true; + case 204: + record = new EpsgGeographicCrsRecord(4557, "RRAF 1991", 1047, 6423); + return true; + case 205: + record = new EpsgGeographicCrsRecord(4558, "RRAF 1991", 1047, 6422); + return true; + case 206: + record = new EpsgGeographicCrsRecord(4600, "Anguilla 1957", 6600, 6422); + return true; + case 207: + record = new EpsgGeographicCrsRecord(4601, "Antigua 1943", 6601, 6422); + return true; + case 208: + record = new EpsgGeographicCrsRecord(4602, "Dominica 1945", 6602, 6422); + return true; + case 209: + record = new EpsgGeographicCrsRecord(4603, "Grenada 1953", 6603, 6422); + return true; + case 210: + record = new EpsgGeographicCrsRecord(4604, "Montserrat 1958", 6604, 6422); + return true; + case 211: + record = new EpsgGeographicCrsRecord(4605, "St. Kitts 1955", 6605, 6422); + return true; + case 212: + record = new EpsgGeographicCrsRecord(4606, "St. Lucia 1955", 6606, 6422); + return true; + case 213: + record = new EpsgGeographicCrsRecord(4607, "St. Vincent 1945", 6607, 6422); + return true; + case 214: + record = new EpsgGeographicCrsRecord(4608, "NAD27(76)", 6608, 6422); + return true; + case 215: + record = new EpsgGeographicCrsRecord(4609, "NAD27(CGQ77)", 6609, 6422); + return true; + case 216: + record = new EpsgGeographicCrsRecord(4610, "Xian 1980", 6610, 6422); + return true; + case 217: + record = new EpsgGeographicCrsRecord(4611, "Hong Kong 1980", 6611, 6422); + return true; + case 218: + record = new EpsgGeographicCrsRecord(4612, "JGD2000", 6612, 6422); + return true; + case 219: + record = new EpsgGeographicCrsRecord(4613, "Segara", 6613, 6422); + return true; + case 220: + record = new EpsgGeographicCrsRecord(4614, "QND95", 6614, 6422); + return true; + case 221: + record = new EpsgGeographicCrsRecord(4615, "Porto Santo", 6615, 6422); + return true; + case 222: + record = new EpsgGeographicCrsRecord(4616, "Selvagem Grande", 6616, 6422); + return true; + case 223: + record = new EpsgGeographicCrsRecord(4617, "NAD83(CSRS)", 6140, 6422); + return true; + case 224: + record = new EpsgGeographicCrsRecord(4618, "SAD69", 6618, 6422); + return true; + case 225: + record = new EpsgGeographicCrsRecord(4619, "ETRS89-SWE [SWEREF 99]", 6619, 6422); + return true; + case 226: + record = new EpsgGeographicCrsRecord(4620, "Point 58", 6620, 6422); + return true; + case 227: + record = new EpsgGeographicCrsRecord(4621, "Fort Marigot", 6621, 6422); + return true; + case 228: + record = new EpsgGeographicCrsRecord(4622, "Guadeloupe 1948", 6622, 6422); + return true; + case 229: + record = new EpsgGeographicCrsRecord(4623, "CSG67", 6623, 6422); + return true; + case 230: + record = new EpsgGeographicCrsRecord(4624, "RGFG95", 6624, 6422); + return true; + case 231: + record = new EpsgGeographicCrsRecord(4625, "Martinique 1938", 6625, 6422); + return true; + case 232: + record = new EpsgGeographicCrsRecord(4626, "Reunion 1947", 6626, 6422); + return true; + case 233: + record = new EpsgGeographicCrsRecord(4627, "RGR92", 6627, 6422); + return true; + case 234: + record = new EpsgGeographicCrsRecord(4628, "Tahiti 52", 6628, 6422); + return true; + case 235: + record = new EpsgGeographicCrsRecord(4629, "Tahaa 54", 6629, 6422); + return true; + case 236: + record = new EpsgGeographicCrsRecord(4630, "IGN72 Nuku Hiva", 6630, 6422); + return true; + case 237: + record = new EpsgGeographicCrsRecord(4632, "Combani 1950", 6632, 6422); + return true; + case 238: + record = new EpsgGeographicCrsRecord(4633, "IGN56 Lifou", 6633, 6422); + return true; + case 239: + record = new EpsgGeographicCrsRecord(4636, "Petrels 1972", 6636, 6422); + return true; + case 240: + record = new EpsgGeographicCrsRecord(4637, "Perroud 1950", 6637, 6422); + return true; + case 241: + record = new EpsgGeographicCrsRecord(4638, "Saint Pierre et Miquelon 1950", 6638, 6422); + return true; + case 242: + record = new EpsgGeographicCrsRecord(4639, "MOP78", 6639, 6422); + return true; + case 243: + record = new EpsgGeographicCrsRecord(4641, "IGN53 Mare", 6641, 6422); + return true; + case 244: + record = new EpsgGeographicCrsRecord(4642, "ST84 Ile des Pins", 6642, 6422); + return true; + case 245: + record = new EpsgGeographicCrsRecord(4643, "ST71 Belep", 6643, 6422); + return true; + case 246: + record = new EpsgGeographicCrsRecord(4644, "NEA74 Noumea", 6644, 6422); + return true; + case 247: + record = new EpsgGeographicCrsRecord(4646, "Grand Comoros", 6646, 6422); + return true; + case 248: + record = new EpsgGeographicCrsRecord(4657, "Reykjavik 1900", 6657, 6422); + return true; + case 249: + record = new EpsgGeographicCrsRecord(4658, "Hjorsey 1955", 6658, 6422); + return true; + case 250: + record = new EpsgGeographicCrsRecord(4659, "ISN93", 6659, 6422); + return true; + case 251: + record = new EpsgGeographicCrsRecord(4660, "Helle 1954", 6660, 6422); + return true; + case 252: + record = new EpsgGeographicCrsRecord(4661, "ETRS89-LVA [LKS-92]", 6661, 6422); + return true; + case 253: + record = new EpsgGeographicCrsRecord(4662, "IGN72 Grande Terre", 6634, 6422); + return true; + case 254: + record = new EpsgGeographicCrsRecord(4663, "Porto Santo 1995", 6663, 6422); + return true; + case 255: + record = new EpsgGeographicCrsRecord(4664, "Azores Oriental 1995", 6664, 6422); + return true; + case 256: + record = new EpsgGeographicCrsRecord(4665, "Azores Central 1995", 6665, 6422); + return true; + case 257: + record = new EpsgGeographicCrsRecord(4666, "Lisbon 1890", 6666, 6422); + return true; + case 258: + record = new EpsgGeographicCrsRecord(4667, "IKBD-92", 6667, 6422); + return true; + case 259: + record = new EpsgGeographicCrsRecord(4668, "ED79", 6668, 6422); + return true; + case 260: + record = new EpsgGeographicCrsRecord(4669, "ETRS89-LTU [LKS94]", 6126, 6422); + return true; + case 261: + record = new EpsgGeographicCrsRecord(4670, "ETRS89-ITA [IGM95]", 6670, 6422); + return true; + case 262: + record = new EpsgGeographicCrsRecord(4671, "Voirol 1879", 6671, 6422); + return true; + case 263: + record = new EpsgGeographicCrsRecord(4672, "Chatham Islands 1971", 6672, 6422); + return true; + case 264: + record = new EpsgGeographicCrsRecord(4673, "Chatham Islands 1979", 6673, 6422); + return true; + case 265: + record = new EpsgGeographicCrsRecord(4674, "SIRGAS 2000", 6674, 6422); + return true; + case 266: + record = new EpsgGeographicCrsRecord(4675, "Guam 1963", 6675, 6422); + return true; + case 267: + record = new EpsgGeographicCrsRecord(4676, "Vientiane 1982", 6676, 6422); + return true; + case 268: + record = new EpsgGeographicCrsRecord(4677, "Lao 1993", 6677, 6422); + return true; + case 269: + record = new EpsgGeographicCrsRecord(4678, "Lao 1997", 6678, 6422); + return true; + case 270: + record = new EpsgGeographicCrsRecord(4679, "Jouik 1961", 6679, 6422); + return true; + case 271: + record = new EpsgGeographicCrsRecord(4680, "Nouakchott 1965", 6680, 6422); + return true; + case 272: + record = new EpsgGeographicCrsRecord(4682, "Gulshan 303", 6682, 6422); + return true; + case 273: + record = new EpsgGeographicCrsRecord(4683, "PRS92", 6683, 6422); + return true; + case 274: + record = new EpsgGeographicCrsRecord(4684, "Gan 1970", 6684, 6422); + return true; + case 275: + record = new EpsgGeographicCrsRecord(4686, "MAGNA-SIRGAS", 6686, 6422); + return true; + case 276: + record = new EpsgGeographicCrsRecord(4687, "RGPF", 6687, 6422); + return true; + case 277: + record = new EpsgGeographicCrsRecord(4688, "Fatu Iva 72", 6688, 6422); + return true; + case 278: + record = new EpsgGeographicCrsRecord(4689, "IGN63 Hiva Oa", 6689, 6422); + return true; + case 279: + record = new EpsgGeographicCrsRecord(4690, "Tahiti 79", 6690, 6422); + return true; + case 280: + record = new EpsgGeographicCrsRecord(4691, "Moorea 87", 6691, 6422); + return true; + case 281: + record = new EpsgGeographicCrsRecord(4692, "Maupiti 83", 6692, 6422); + return true; + case 282: + record = new EpsgGeographicCrsRecord(4693, "Nakhl-e Ghanem", 6693, 6422); + return true; + case 283: + record = new EpsgGeographicCrsRecord(4694, "POSGAR 94", 6694, 6422); + return true; + case 284: + record = new EpsgGeographicCrsRecord(4695, "Katanga 1955", 6695, 6422); + return true; + case 285: + record = new EpsgGeographicCrsRecord(4696, "Kasai 1953", 6696, 6422); + return true; + case 286: + record = new EpsgGeographicCrsRecord(4697, "IGC 1962 6th Parallel South", 6697, 6422); + return true; + case 287: + record = new EpsgGeographicCrsRecord(4698, "IGN 1962 Kerguelen", 6698, 6422); + return true; + case 288: + record = new EpsgGeographicCrsRecord(4699, "Le Pouce 1934", 6699, 6422); + return true; + case 289: + record = new EpsgGeographicCrsRecord(4700, "IGN Astro 1960", 6700, 6422); + return true; + case 290: + record = new EpsgGeographicCrsRecord(4701, "IGCB 1955", 6701, 6422); + return true; + case 291: + record = new EpsgGeographicCrsRecord(4702, "Mauritania 1999", 6702, 6422); + return true; + case 292: + record = new EpsgGeographicCrsRecord(4703, "Mhast 1951", 6703, 6422); + return true; + case 293: + record = new EpsgGeographicCrsRecord(4704, "Mhast (onshore)", 6704, 6422); + return true; + case 294: + record = new EpsgGeographicCrsRecord(4705, "Mhast (offshore)", 6705, 6422); + return true; + case 295: + record = new EpsgGeographicCrsRecord(4706, "Egypt Gulf of Suez S-650 TL", 6706, 6422); + return true; + case 296: + record = new EpsgGeographicCrsRecord(4707, "Tern Island 1961", 6707, 6422); + return true; + case 297: + record = new EpsgGeographicCrsRecord(4708, "Cocos Islands 1965", 6708, 6422); + return true; + case 298: + record = new EpsgGeographicCrsRecord(4709, "Iwo Jima 1945", 6709, 6422); + return true; + case 299: + record = new EpsgGeographicCrsRecord(4710, "Astro DOS 71", 6710, 6422); + return true; + case 300: + record = new EpsgGeographicCrsRecord(4711, "Marcus Island 1952", 6711, 6422); + return true; + case 301: + record = new EpsgGeographicCrsRecord(4712, "Ascension Island 1958", 6712, 6422); + return true; + case 302: + record = new EpsgGeographicCrsRecord(4713, "Ayabelle Lighthouse", 6713, 6422); + return true; + case 303: + record = new EpsgGeographicCrsRecord(4714, "Bellevue", 6714, 6422); + return true; + case 304: + record = new EpsgGeographicCrsRecord(4715, "Camp Area Astro", 6715, 6422); + return true; + case 305: + record = new EpsgGeographicCrsRecord(4716, "Phoenix Islands 1966", 6716, 6422); + return true; + case 306: + record = new EpsgGeographicCrsRecord(4717, "Cape Canaveral", 6717, 6422); + return true; + case 307: + record = new EpsgGeographicCrsRecord(4718, "Solomon 1968", 6718, 6422); + return true; + case 308: + record = new EpsgGeographicCrsRecord(4719, "Easter Island 1967", 6719, 6422); + return true; + case 309: + record = new EpsgGeographicCrsRecord(4720, "Fiji 1986", 6720, 6422); + return true; + case 310: + record = new EpsgGeographicCrsRecord(4721, "Fiji 1956", 6721, 6422); + return true; + case 311: + record = new EpsgGeographicCrsRecord(4722, "South Georgia 1968", 6722, 6422); + return true; + case 312: + record = new EpsgGeographicCrsRecord(4723, "GCGD59", 6723, 6422); + return true; + case 313: + record = new EpsgGeographicCrsRecord(4724, "Diego Garcia 1969", 6724, 6422); + return true; + case 314: + record = new EpsgGeographicCrsRecord(4725, "Johnston Island 1961", 6725, 6422); + return true; + case 315: + record = new EpsgGeographicCrsRecord(4726, "SIGD61", 6726, 6422); + return true; + case 316: + record = new EpsgGeographicCrsRecord(4727, "Midway 1961", 6727, 6422); + return true; + case 317: + record = new EpsgGeographicCrsRecord(4728, "PN84", 6728, 6422); + return true; + case 318: + record = new EpsgGeographicCrsRecord(4729, "Pitcairn 1967", 6729, 6422); + return true; + case 319: + record = new EpsgGeographicCrsRecord(4730, "Santo 1965", 6730, 6422); + return true; + case 320: + record = new EpsgGeographicCrsRecord(4732, "Marshall Islands 1960", 6732, 6422); + return true; + case 321: + record = new EpsgGeographicCrsRecord(4733, "Wake Island 1952", 6733, 6422); + return true; + case 322: + record = new EpsgGeographicCrsRecord(4734, "Tristan 1968", 6734, 6422); + return true; + case 323: + record = new EpsgGeographicCrsRecord(4735, "Kusaie 1951", 6735, 6422); + return true; + case 324: + record = new EpsgGeographicCrsRecord(4736, "Deception Island", 6736, 6422); + return true; + case 325: + record = new EpsgGeographicCrsRecord(4737, "KGD2002", 6737, 6422); + return true; + case 326: + record = new EpsgGeographicCrsRecord(4738, "Hong Kong 1963", 6738, 6422); + return true; + case 327: + record = new EpsgGeographicCrsRecord(4739, "Hong Kong 1963(67)", 6739, 6422); + return true; + case 328: + record = new EpsgGeographicCrsRecord(4740, "PZ-90", 6740, 6422); + return true; + case 329: + record = new EpsgGeographicCrsRecord(4741, "FD54", 6741, 6422); + return true; + case 330: + record = new EpsgGeographicCrsRecord(4742, "GDM2000", 6742, 6422); + return true; + case 331: + record = new EpsgGeographicCrsRecord(4743, "Karbala 1979", 6743, 6422); + return true; + case 332: + record = new EpsgGeographicCrsRecord(4744, "Nahrwan 1934", 6744, 6422); + return true; + case 333: + record = new EpsgGeographicCrsRecord(4745, "RD/83", 6745, 6422); + return true; + case 334: + record = new EpsgGeographicCrsRecord(4746, "PD/83", 6746, 6422); + return true; + case 335: + record = new EpsgGeographicCrsRecord(4747, "GR96", 1421, 6422); + return true; + case 336: + record = new EpsgGeographicCrsRecord(4748, "Vanua Levu 1915", 6748, 6422); + return true; + case 337: + record = new EpsgGeographicCrsRecord(4749, "RGNC91-93", 6749, 6422); + return true; + case 338: + record = new EpsgGeographicCrsRecord(4750, "ST87 Ouvea", 6750, 6422); + return true; + case 339: + record = new EpsgGeographicCrsRecord(4751, "Kertau (RSO)", 6751, 6422); + return true; + case 340: + record = new EpsgGeographicCrsRecord(4752, "Viti Levu 1912", 6752, 6422); + return true; + case 341: + record = new EpsgGeographicCrsRecord(4753, "fk89", 6753, 6422); + return true; + case 342: + record = new EpsgGeographicCrsRecord(4754, "LGD2006", 6754, 6422); + return true; + case 343: + record = new EpsgGeographicCrsRecord(4755, "DGN95", 6755, 6422); + return true; + case 344: + record = new EpsgGeographicCrsRecord(4756, "VN-2000", 6756, 6422); + return true; + case 345: + record = new EpsgGeographicCrsRecord(4757, "SVY21", 6757, 6422); + return true; + case 346: + record = new EpsgGeographicCrsRecord(4758, "JAD2001", 6758, 6422); + return true; + case 347: + record = new EpsgGeographicCrsRecord(4759, "NAD83(NSRS2007)", 6759, 6422); + return true; + case 348: + record = new EpsgGeographicCrsRecord(4760, "WGS 66", 6760, 6422); + return true; + case 349: + record = new EpsgGeographicCrsRecord(4761, "ETRS89-HRV [HTRS96]", 6761, 6422); + return true; + case 350: + record = new EpsgGeographicCrsRecord(4762, "BDA2000", 6762, 6422); + return true; + case 351: + record = new EpsgGeographicCrsRecord(4763, "Pitcairn 2006", 6763, 6422); + return true; + case 352: + record = new EpsgGeographicCrsRecord(4764, "RSRGD2000", 6764, 6422); + return true; + case 353: + record = new EpsgGeographicCrsRecord(4765, "ETRS89-SVN [D96]", 6765, 6422); + return true; + case 354: + record = new EpsgGeographicCrsRecord(4801, "CH1903 (Bern)", 6801, 6422); + return true; + case 355: + record = new EpsgGeographicCrsRecord(4802, "Bogota 1975 (Bogota)", 6802, 6422); + return true; + case 356: + record = new EpsgGeographicCrsRecord(4803, "Lisbon (Lisbon)", 6803, 6422); + return true; + case 357: + record = new EpsgGeographicCrsRecord(4804, "Makassar (Jakarta)", 6804, 6422); + return true; + case 358: + record = new EpsgGeographicCrsRecord(4805, "MGI (Ferro)", 6805, 6422); + return true; + case 359: + record = new EpsgGeographicCrsRecord(4806, "Monte Mario (Rome)", 6806, 6422); + return true; + case 360: + record = new EpsgGeographicCrsRecord(4807, "NTF (Paris)", 6807, 6403); + return true; + case 361: + record = new EpsgGeographicCrsRecord(4809, "BD50 (Brussels)", 6809, 6422); + return true; + case 362: + record = new EpsgGeographicCrsRecord(4810, "Tananarive (Paris)", 6810, 6403); + return true; + case 363: + record = new EpsgGeographicCrsRecord(4811, "Voirol 1875 (Paris)", 6811, 6403); + return true; + case 364: + record = new EpsgGeographicCrsRecord(4813, "Batavia (Jakarta)", 6813, 6422); + return true; + case 365: + record = new EpsgGeographicCrsRecord(4814, "RT38 (Stockholm)", 6814, 6422); + return true; + case 366: + record = new EpsgGeographicCrsRecord(4815, "Greek (Athens)", 6815, 6422); + return true; + case 367: + record = new EpsgGeographicCrsRecord(4816, "Carthage (Paris)", 6816, 6403); + return true; + case 368: + record = new EpsgGeographicCrsRecord(4817, "NGO 1948 (Oslo)", 6817, 6422); + return true; + case 369: + record = new EpsgGeographicCrsRecord(4818, "S-JTSK (Ferro)", 6818, 6422); + return true; + case 370: + record = new EpsgGeographicCrsRecord(4820, "Segara (Jakarta)", 6820, 6422); + return true; + case 371: + record = new EpsgGeographicCrsRecord(4821, "Voirol 1879 (Paris)", 6821, 6403); + return true; + case 372: + record = new EpsgGeographicCrsRecord(4823, "Sao Tome", 1044, 6422); + return true; + case 373: + record = new EpsgGeographicCrsRecord(4824, "Principe", 1046, 6422); + return true; + case 374: + record = new EpsgGeographicCrsRecord(4883, "ETRS89-SVN [D96]", 6765, 6423); + return true; + case 375: + record = new EpsgGeographicCrsRecord(4885, "RSRGD2000", 6764, 6423); + return true; + case 376: + record = new EpsgGeographicCrsRecord(4887, "BDA2000", 6762, 6423); + return true; + case 377: + record = new EpsgGeographicCrsRecord(4889, "ETRS89-HRV [HTRS96]", 6761, 6423); + return true; + case 378: + record = new EpsgGeographicCrsRecord(4891, "WGS 66", 6760, 6423); + return true; + case 379: + record = new EpsgGeographicCrsRecord(4893, "NAD83(NSRS2007)", 6759, 6423); + return true; + case 380: + record = new EpsgGeographicCrsRecord(4895, "JAD2001", 6758, 6423); + return true; + case 381: + record = new EpsgGeographicCrsRecord(4898, "DGN95", 6755, 6423); + return true; + case 382: + record = new EpsgGeographicCrsRecord(4900, "LGD2006", 6754, 6423); + return true; + case 383: + record = new EpsgGeographicCrsRecord(4901, "ATF (Paris)", 6901, 6403); + return true; + case 384: + record = new EpsgGeographicCrsRecord(4903, "Madrid 1870 (Madrid)", 6903, 6422); + return true; + case 385: + record = new EpsgGeographicCrsRecord(4904, "Lisbon 1890 (Lisbon)", 6904, 6422); + return true; + case 386: + record = new EpsgGeographicCrsRecord(4907, "RGNC91-93", 6749, 6423); + return true; + case 387: + record = new EpsgGeographicCrsRecord(4909, "GR96", 1421, 6423); + return true; + case 388: + record = new EpsgGeographicCrsRecord(4921, "GDM2000", 6742, 6423); + return true; + case 389: + record = new EpsgGeographicCrsRecord(4923, "PZ-90", 6740, 6423); + return true; + case 390: + record = new EpsgGeographicCrsRecord(4925, "Mauritania 1999", 6702, 6423); + return true; + case 391: + record = new EpsgGeographicCrsRecord(4927, "KGD2002", 6737, 6423); + return true; + case 392: + record = new EpsgGeographicCrsRecord(4929, "POSGAR 94", 6694, 6423); + return true; + case 393: + record = new EpsgGeographicCrsRecord(4931, "Australian Antarctic", 6176, 6423); + return true; + case 394: + record = new EpsgGeographicCrsRecord(4933, "CHTRS95", 6151, 6423); + return true; + case 395: + record = new EpsgGeographicCrsRecord(4935, "ETRS89-EST [EST97]", 6180, 6423); + return true; + case 396: + record = new EpsgGeographicCrsRecord(4937, "ETRS89", 6258, 6423); + return true; + case 397: + record = new EpsgGeographicCrsRecord(4939, "GDA94", 6283, 6423); + return true; + case 398: + record = new EpsgGeographicCrsRecord(4941, "Hartebeesthoek94", 6148, 6423); + return true; + case 399: + record = new EpsgGeographicCrsRecord(4943, "ETRS89-IRE [ETRF2000]", 6173, 6423); + return true; + case 400: + record = new EpsgGeographicCrsRecord(4945, "ISN93", 6659, 6423); + return true; + case 401: + record = new EpsgGeographicCrsRecord(4947, "JGD2000", 6612, 6423); + return true; + case 402: + record = new EpsgGeographicCrsRecord(4949, "ETRS89-LVA [LKS-92]", 6661, 6423); + return true; + case 403: + record = new EpsgGeographicCrsRecord(4951, "ETRS89-LTU [LKS94]", 6126, 6423); + return true; + case 404: + record = new EpsgGeographicCrsRecord(4953, "Moznet", 6130, 6423); + return true; + case 405: + record = new EpsgGeographicCrsRecord(4955, "NAD83(CSRS)", 6140, 6423); + return true; + case 406: + record = new EpsgGeographicCrsRecord(4957, "NAD83(HARN)", 6152, 6423); + return true; + case 407: + record = new EpsgGeographicCrsRecord(4959, "NZGD2000", 6167, 6423); + return true; + case 408: + record = new EpsgGeographicCrsRecord(4961, "POSGAR 98", 6190, 6423); + return true; + case 409: + record = new EpsgGeographicCrsRecord(4963, "REGVEN", 6189, 6423); + return true; + case 410: + record = new EpsgGeographicCrsRecord(4965, "ETRS89-FRA [RGF93 v1]", 6171, 6423); + return true; + case 411: + record = new EpsgGeographicCrsRecord(4967, "RGFG95", 6624, 6423); + return true; + case 412: + record = new EpsgGeographicCrsRecord(4971, "RGR92", 6627, 6423); + return true; + case 413: + record = new EpsgGeographicCrsRecord(4975, "SIRGAS 1995", 6170, 6423); + return true; + case 414: + record = new EpsgGeographicCrsRecord(4977, "ETRS89-SWE [SWEREF 99]", 6619, 6423); + return true; + case 415: + record = new EpsgGeographicCrsRecord(4979, "WGS 84", 6326, 6423); + return true; + case 416: + record = new EpsgGeographicCrsRecord(4981, "Yemen NGN96", 6163, 6423); + return true; + case 417: + record = new EpsgGeographicCrsRecord(4983, "ETRS89-ITA [IGM95]", 6670, 6423); + return true; + case 418: + record = new EpsgGeographicCrsRecord(4985, "WGS 72", 6322, 6423); + return true; + case 419: + record = new EpsgGeographicCrsRecord(4987, "WGS 72BE", 6324, 6423); + return true; + case 420: + record = new EpsgGeographicCrsRecord(4989, "SIRGAS 2000", 6674, 6423); + return true; + case 421: + record = new EpsgGeographicCrsRecord(4991, "Lao 1993", 6677, 6423); + return true; + case 422: + record = new EpsgGeographicCrsRecord(4993, "Lao 1997", 6678, 6423); + return true; + case 423: + record = new EpsgGeographicCrsRecord(4995, "PRS92", 6683, 6423); + return true; + case 424: + record = new EpsgGeographicCrsRecord(4997, "MAGNA-SIRGAS", 6686, 6423); + return true; + case 425: + record = new EpsgGeographicCrsRecord(4999, "RGPF", 6687, 6423); + return true; + case 426: + record = new EpsgGeographicCrsRecord(5012, "PTRA08", 1041, 6423); + return true; + case 427: + record = new EpsgGeographicCrsRecord(5013, "PTRA08", 1041, 6422); + return true; + case 428: + record = new EpsgGeographicCrsRecord(5132, "Tokyo 1892", 1048, 6422); + return true; + case 429: + record = new EpsgGeographicCrsRecord(5228, "S-JTSK/05", 1052, 6422); + return true; + case 430: + record = new EpsgGeographicCrsRecord(5229, "S-JTSK/05 (Ferro)", 1055, 6422); + return true; + case 431: + record = new EpsgGeographicCrsRecord(5233, "SLD99", 1053, 6422); + return true; + case 432: + record = new EpsgGeographicCrsRecord(5245, "GDBD2009", 1056, 6423); + return true; + case 433: + record = new EpsgGeographicCrsRecord(5246, "GDBD2009", 1056, 6422); + return true; + case 434: + record = new EpsgGeographicCrsRecord(5251, "TUREF", 1057, 6423); + return true; + case 435: + record = new EpsgGeographicCrsRecord(5252, "TUREF", 1057, 6422); + return true; + case 436: + record = new EpsgGeographicCrsRecord(5263, "DRUKREF 03", 1058, 6423); + return true; + case 437: + record = new EpsgGeographicCrsRecord(5264, "DRUKREF 03", 1058, 6422); + return true; + case 438: + record = new EpsgGeographicCrsRecord(5323, "ISN2004", 1060, 6423); + return true; + case 439: + record = new EpsgGeographicCrsRecord(5324, "ISN2004", 1060, 6422); + return true; + case 440: + record = new EpsgGeographicCrsRecord(5340, "POSGAR 2007", 1062, 6422); + return true; + case 441: + record = new EpsgGeographicCrsRecord(5342, "POSGAR 2007", 1062, 6423); + return true; + case 442: + record = new EpsgGeographicCrsRecord(5353, "MARGEN", 1063, 6423); + return true; + case 443: + record = new EpsgGeographicCrsRecord(5354, "MARGEN", 1063, 6422); + return true; + case 444: + record = new EpsgGeographicCrsRecord(5359, "SIRGAS-Chile 2002", 1064, 6423); + return true; + case 445: + record = new EpsgGeographicCrsRecord(5360, "SIRGAS-Chile 2002", 1064, 6422); + return true; + case 446: + record = new EpsgGeographicCrsRecord(5364, "CR05", 1065, 6423); + return true; + case 447: + record = new EpsgGeographicCrsRecord(5365, "CR05", 1065, 6422); + return true; + case 448: + record = new EpsgGeographicCrsRecord(5370, "MACARIO SOLIS", 1066, 6423); + return true; + case 449: + record = new EpsgGeographicCrsRecord(5371, "MACARIO SOLIS", 1066, 6422); + return true; + case 450: + record = new EpsgGeographicCrsRecord(5372, "Peru96", 1067, 6423); + return true; + case 451: + record = new EpsgGeographicCrsRecord(5373, "Peru96", 1067, 6422); + return true; + case 452: + record = new EpsgGeographicCrsRecord(5380, "SIRGAS-ROU98", 1068, 6423); + return true; + case 453: + record = new EpsgGeographicCrsRecord(5381, "SIRGAS-ROU98", 1068, 6422); + return true; + case 454: + record = new EpsgGeographicCrsRecord(5392, "SIRGAS_ES2007.8", 1069, 6423); + return true; + case 455: + record = new EpsgGeographicCrsRecord(5393, "SIRGAS_ES2007.8", 1069, 6422); + return true; + case 456: + record = new EpsgGeographicCrsRecord(5451, "Ocotepeque 1935", 1070, 6422); + return true; + case 457: + record = new EpsgGeographicCrsRecord(5464, "Sibun Gorge 1922", 1071, 6422); + return true; + case 458: + record = new EpsgGeographicCrsRecord(5467, "Panama-Colon 1911", 1072, 6422); + return true; + case 459: + record = new EpsgGeographicCrsRecord(5488, "RGAF09", 1073, 6423); + return true; + case 460: + record = new EpsgGeographicCrsRecord(5489, "RGAF09", 1073, 6422); + return true; + case 461: + record = new EpsgGeographicCrsRecord(5524, "Corrego Alegre 1961", 1074, 6422); + return true; + case 462: + record = new EpsgGeographicCrsRecord(5527, "SAD69(96)", 1075, 6422); + return true; + case 463: + record = new EpsgGeographicCrsRecord(5545, "PNG94", 1076, 6423); + return true; + case 464: + record = new EpsgGeographicCrsRecord(5546, "PNG94", 1076, 6422); + return true; + case 465: + record = new EpsgGeographicCrsRecord(5560, "UCS-2000", 1077, 6423); + return true; + case 466: + record = new EpsgGeographicCrsRecord(5561, "UCS-2000", 1077, 6422); + return true; + case 467: + record = new EpsgGeographicCrsRecord(5592, "FEH2010", 1078, 6423); + return true; + case 468: + record = new EpsgGeographicCrsRecord(5593, "FEH2010", 1078, 6422); + return true; + case 469: + record = new EpsgGeographicCrsRecord(5681, "DB_REF", 1081, 6422); + return true; + case 470: + record = new EpsgGeographicCrsRecord(5830, "DB_REF", 1081, 6423); + return true; + case 471: + record = new EpsgGeographicCrsRecord(5885, "TGD2005", 1095, 6423); + return true; + case 472: + record = new EpsgGeographicCrsRecord(5886, "TGD2005", 1095, 6422); + return true; + case 473: + record = new EpsgGeographicCrsRecord(6134, "CIGD11", 1100, 6423); + return true; + case 474: + record = new EpsgGeographicCrsRecord(6135, "CIGD11", 1100, 6422); + return true; + case 475: + record = new EpsgGeographicCrsRecord(6207, "Nepal 1981", 1111, 6422); + return true; + case 476: + record = new EpsgGeographicCrsRecord(6310, "CGRS93", 1112, 6423); + return true; + case 477: + record = new EpsgGeographicCrsRecord(6311, "CGRS93", 1112, 6422); + return true; + case 478: + record = new EpsgGeographicCrsRecord(6318, "NAD83(2011)", 1116, 6422); + return true; + case 479: + record = new EpsgGeographicCrsRecord(6319, "NAD83(2011)", 1116, 6423); + return true; + case 480: + record = new EpsgGeographicCrsRecord(6321, "NAD83(PA11)", 1117, 6423); + return true; + case 481: + record = new EpsgGeographicCrsRecord(6322, "NAD83(PA11)", 1117, 6422); + return true; + case 482: + record = new EpsgGeographicCrsRecord(6324, "NAD83(MA11)", 1118, 6423); + return true; + case 483: + record = new EpsgGeographicCrsRecord(6325, "NAD83(MA11)", 1118, 6422); + return true; + case 484: + record = new EpsgGeographicCrsRecord(6364, "Mexico ITRF2008", 1120, 6423); + return true; + case 485: + record = new EpsgGeographicCrsRecord(6365, "Mexico ITRF2008", 1120, 6422); + return true; + case 486: + record = new EpsgGeographicCrsRecord(6667, "JGD2011", 1128, 6423); + return true; + case 487: + record = new EpsgGeographicCrsRecord(6668, "JGD2011", 1128, 6422); + return true; + case 488: + record = new EpsgGeographicCrsRecord(6705, "ETRS89-ITA [RDN2008]", 1132, 6423); + return true; + case 489: + record = new EpsgGeographicCrsRecord(6706, "ETRS89-ITA [RDN2008]", 1132, 6422); + return true; + case 490: + record = new EpsgGeographicCrsRecord(6782, "NAD83(CORS96)", 1133, 6423); + return true; + case 491: + record = new EpsgGeographicCrsRecord(6783, "NAD83(CORS96)", 1133, 6422); + return true; + case 492: + record = new EpsgGeographicCrsRecord(6881, "Aden 1925", 1135, 6422); + return true; + case 493: + record = new EpsgGeographicCrsRecord(6882, "Bekaa Valley 1920", 1137, 6422); + return true; + case 494: + record = new EpsgGeographicCrsRecord(6883, "Bioko", 1136, 6422); + return true; + case 495: + record = new EpsgGeographicCrsRecord(6892, "South East Island 1943", 1138, 6422); + return true; + case 496: + record = new EpsgGeographicCrsRecord(6894, "Gambia", 1139, 6422); + return true; + case 497: + record = new EpsgGeographicCrsRecord(6982, "IG05 Intermediate CRS", 1142, 6423); + return true; + case 498: + record = new EpsgGeographicCrsRecord(6983, "IG05 Intermediate CRS", 1142, 6422); + return true; + case 499: + record = new EpsgGeographicCrsRecord(6989, "IG05/12 Intermediate CRS", 1144, 6423); + return true; + case 500: + record = new EpsgGeographicCrsRecord(6990, "IG05/12 Intermediate CRS", 1144, 6422); + return true; + case 501: + record = new EpsgGeographicCrsRecord(7034, "RGSPM06 (lon-lat)", 1038, 6426); + return true; + case 502: + record = new EpsgGeographicCrsRecord(7035, "RGSPM06 (lon-lat)", 1038, 6424); + return true; + case 503: + record = new EpsgGeographicCrsRecord(7036, "RGR92 (lon-lat)", 6627, 6426); + return true; + case 504: + record = new EpsgGeographicCrsRecord(7037, "RGR92 (lon-lat)", 6627, 6424); + return true; + case 505: + record = new EpsgGeographicCrsRecord(7038, "RGM04 (lon-lat)", 1036, 6426); + return true; + case 506: + record = new EpsgGeographicCrsRecord(7039, "RGM04 (lon-lat)", 1036, 6424); + return true; + case 507: + record = new EpsgGeographicCrsRecord(7040, "RGFG95 (lon-lat)", 6624, 6426); + return true; + case 508: + record = new EpsgGeographicCrsRecord(7041, "RGFG95 (lon-lat)", 6624, 6424); + return true; + case 509: + record = new EpsgGeographicCrsRecord(7042, "ETRS89-FRA [RGF93 v1] (lon-lat)", 6171, 6426); + return true; + case 510: + record = new EpsgGeographicCrsRecord(7072, "RGTAAF07", 1113, 6423); + return true; + case 511: + record = new EpsgGeographicCrsRecord(7073, "RGTAAF07", 1113, 6422); + return true; + case 512: + record = new EpsgGeographicCrsRecord(7084, "ETRS89-FRA [RGF93 v1] (lon-lat)", 6171, 6424); + return true; + case 513: + record = new EpsgGeographicCrsRecord(7085, "RGAF09 (lon-lat)", 1073, 6426); + return true; + case 514: + record = new EpsgGeographicCrsRecord(7086, "RGAF09 (lon-lat)", 1073, 6424); + return true; + case 515: + record = new EpsgGeographicCrsRecord(7087, "RGTAAF07 (lon-lat)", 1113, 6426); + return true; + case 516: + record = new EpsgGeographicCrsRecord(7133, "RGTAAF07 (lon-lat)", 1113, 6424); + return true; + case 517: + record = new EpsgGeographicCrsRecord(7135, "IGD05", 1114, 6423); + return true; + case 518: + record = new EpsgGeographicCrsRecord(7136, "IGD05", 1114, 6422); + return true; + case 519: + record = new EpsgGeographicCrsRecord(7138, "IGD05/12", 1115, 6423); + return true; + case 520: + record = new EpsgGeographicCrsRecord(7139, "IGD05/12", 1115, 6422); + return true; + case 521: + record = new EpsgGeographicCrsRecord(7372, "ONGD14", 1147, 6423); + return true; + case 522: + record = new EpsgGeographicCrsRecord(7373, "ONGD14", 1147, 6422); + return true; + case 523: + record = new EpsgGeographicCrsRecord(7657, "WGS 84 (G730)", 1152, 6423); + return true; + case 524: + record = new EpsgGeographicCrsRecord(7659, "WGS 84 (G873)", 1153, 6423); + return true; + case 525: + record = new EpsgGeographicCrsRecord(7661, "WGS 84 (G1150)", 1154, 6423); + return true; + case 526: + record = new EpsgGeographicCrsRecord(7663, "WGS 84 (G1674)", 1155, 6423); + return true; + case 527: + record = new EpsgGeographicCrsRecord(7665, "WGS 84 (G1762)", 1156, 6423); + return true; + case 528: + record = new EpsgGeographicCrsRecord(7678, "PZ-90.02", 1157, 6423); + return true; + case 529: + record = new EpsgGeographicCrsRecord(7680, "PZ-90.11", 1158, 6423); + return true; + case 530: + record = new EpsgGeographicCrsRecord(7682, "GSK-2011", 1159, 6423); + return true; + case 531: + record = new EpsgGeographicCrsRecord(7683, "GSK-2011", 1159, 6422); + return true; + case 532: + record = new EpsgGeographicCrsRecord(7685, "Kyrg-06", 1160, 6423); + return true; + case 533: + record = new EpsgGeographicCrsRecord(7686, "Kyrg-06", 1160, 6422); + return true; + case 534: + record = new EpsgGeographicCrsRecord(7797, "ETRS89-BGR [BGS2005]", 1167, 6423); + return true; + case 535: + record = new EpsgGeographicCrsRecord(7798, "ETRS89-BGR [BGS2005]", 1167, 6422); + return true; + case 536: + record = new EpsgGeographicCrsRecord(7816, "WGS 84 (Transit)", 1166, 6423); + return true; + case 537: + record = new EpsgGeographicCrsRecord(7843, "GDA2020", 1168, 6423); + return true; + case 538: + record = new EpsgGeographicCrsRecord(7844, "GDA2020", 1168, 6422); + return true; + case 539: + record = new EpsgGeographicCrsRecord(7880, "St. Helena Tritan", 1173, 6423); + return true; + case 540: + record = new EpsgGeographicCrsRecord(7881, "St. Helena Tritan", 1173, 6422); + return true; + case 541: + record = new EpsgGeographicCrsRecord(7885, "SHGD2015", 1174, 6423); + return true; + case 542: + record = new EpsgGeographicCrsRecord(7886, "SHGD2015", 1174, 6422); + return true; + case 543: + record = new EpsgGeographicCrsRecord(7900, "ITRF88", 6647, 6423); + return true; + case 544: + record = new EpsgGeographicCrsRecord(7901, "ITRF89", 6648, 6423); + return true; + case 545: + record = new EpsgGeographicCrsRecord(7902, "ITRF90", 6649, 6423); + return true; + case 546: + record = new EpsgGeographicCrsRecord(7903, "ITRF91", 6650, 6423); + return true; + case 547: + record = new EpsgGeographicCrsRecord(7904, "ITRF92", 6651, 6423); + return true; + case 548: + record = new EpsgGeographicCrsRecord(7905, "ITRF93", 6652, 6423); + return true; + case 549: + record = new EpsgGeographicCrsRecord(7906, "ITRF94", 6653, 6423); + return true; + case 550: + record = new EpsgGeographicCrsRecord(7907, "ITRF96", 6654, 6423); + return true; + case 551: + record = new EpsgGeographicCrsRecord(7908, "ITRF97", 6655, 6423); + return true; + case 552: + record = new EpsgGeographicCrsRecord(7909, "ITRF2000", 6656, 6423); + return true; + case 553: + record = new EpsgGeographicCrsRecord(7910, "ITRF2005", 6896, 6423); + return true; + case 554: + record = new EpsgGeographicCrsRecord(7911, "ITRF2008", 1061, 6423); + return true; + case 555: + record = new EpsgGeographicCrsRecord(7912, "ITRF2014", 1165, 6423); + return true; + case 556: + record = new EpsgGeographicCrsRecord(7915, "ETRF89", 1178, 6423); + return true; + case 557: + record = new EpsgGeographicCrsRecord(7917, "ETRF90", 1179, 6423); + return true; + case 558: + record = new EpsgGeographicCrsRecord(7919, "ETRF91", 1180, 6423); + return true; + case 559: + record = new EpsgGeographicCrsRecord(7921, "ETRF92", 1181, 6423); + return true; + case 560: + record = new EpsgGeographicCrsRecord(7923, "ETRF93", 1182, 6423); + return true; + case 561: + record = new EpsgGeographicCrsRecord(7925, "ETRF94", 1183, 6423); + return true; + case 562: + record = new EpsgGeographicCrsRecord(7927, "ETRF96", 1184, 6423); + return true; + case 563: + record = new EpsgGeographicCrsRecord(7929, "ETRF97", 1185, 6423); + return true; + case 564: + record = new EpsgGeographicCrsRecord(7931, "ETRF2000", 1186, 6423); + return true; + case 565: + record = new EpsgGeographicCrsRecord(8042, "Gusterberg (Ferro)", 1188, 6422); + return true; + case 566: + record = new EpsgGeographicCrsRecord(8043, "St. Stephen (Ferro)", 1189, 6422); + return true; + case 567: + record = new EpsgGeographicCrsRecord(8085, "ISN2016", 1187, 6423); + return true; + case 568: + record = new EpsgGeographicCrsRecord(8086, "ISN2016", 1187, 6422); + return true; + case 569: + record = new EpsgGeographicCrsRecord(8231, "NAD83(CSRS96)", 1192, 6423); + return true; + case 570: + record = new EpsgGeographicCrsRecord(8232, "NAD83(CSRS96)", 1192, 6422); + return true; + case 571: + record = new EpsgGeographicCrsRecord(8235, "NAD83(CSRS)v2", 1193, 6423); + return true; + case 572: + record = new EpsgGeographicCrsRecord(8237, "NAD83(CSRS)v2", 1193, 6422); + return true; + case 573: + record = new EpsgGeographicCrsRecord(8239, "NAD83(CSRS)v3", 1194, 6423); + return true; + case 574: + record = new EpsgGeographicCrsRecord(8240, "NAD83(CSRS)v3", 1194, 6422); + return true; + case 575: + record = new EpsgGeographicCrsRecord(8244, "NAD83(CSRS)v4", 1195, 6423); + return true; + case 576: + record = new EpsgGeographicCrsRecord(8246, "NAD83(CSRS)v4", 1195, 6422); + return true; + case 577: + record = new EpsgGeographicCrsRecord(8248, "NAD83(CSRS)v5", 1196, 6423); + return true; + case 578: + record = new EpsgGeographicCrsRecord(8249, "NAD83(CSRS)v5", 1196, 6422); + return true; + case 579: + record = new EpsgGeographicCrsRecord(8251, "NAD83(CSRS)v6", 1197, 6423); + return true; + case 580: + record = new EpsgGeographicCrsRecord(8252, "NAD83(CSRS)v6", 1197, 6422); + return true; + case 581: + record = new EpsgGeographicCrsRecord(8254, "NAD83(CSRS)v7", 1198, 6423); + return true; + case 582: + record = new EpsgGeographicCrsRecord(8255, "NAD83(CSRS)v7", 1198, 6422); + return true; + case 583: + record = new EpsgGeographicCrsRecord(8351, "S-JTSK [JTSK03]", 1201, 6422); + return true; + case 584: + record = new EpsgGeographicCrsRecord(8399, "ETRF2005", 1204, 6423); + return true; + case 585: + record = new EpsgGeographicCrsRecord(8403, "ETRF2014", 1206, 6423); + return true; + case 586: + record = new EpsgGeographicCrsRecord(8426, "Hong Kong Geodetic CS", 1209, 6423); + return true; + case 587: + record = new EpsgGeographicCrsRecord(8427, "Hong Kong Geodetic CS", 1209, 6422); + return true; + case 588: + record = new EpsgGeographicCrsRecord(8428, "Macao 1920", 1207, 6422); + return true; + case 589: + record = new EpsgGeographicCrsRecord(8430, "Macao 2008", 1208, 6423); + return true; + case 590: + record = new EpsgGeographicCrsRecord(8431, "Macao 2008", 1208, 6422); + return true; + case 591: + record = new EpsgGeographicCrsRecord(8542, "NAD83(FBN)", 1211, 6423); + return true; + case 592: + record = new EpsgGeographicCrsRecord(8544, "NAD83(HARN Corrected)", 1212, 6423); + return true; + case 593: + record = new EpsgGeographicCrsRecord(8545, "NAD83(HARN Corrected)", 1212, 6422); + return true; + case 594: + record = new EpsgGeographicCrsRecord(8684, "ETRS89-SRB [STRS00]", 1214, 6423); + return true; + case 595: + record = new EpsgGeographicCrsRecord(8685, "ETRS89-SRB [STRS00]", 1214, 6422); + return true; + case 596: + record = new EpsgGeographicCrsRecord(8694, "Camacupa 2015", 1217, 6422); + return true; + case 597: + record = new EpsgGeographicCrsRecord(8698, "RSAO13", 1220, 6423); + return true; + case 598: + record = new EpsgGeographicCrsRecord(8699, "RSAO13", 1220, 6422); + return true; + case 599: + record = new EpsgGeographicCrsRecord(8817, "MTRF-2000", 1218, 6423); + return true; + case 600: + record = new EpsgGeographicCrsRecord(8818, "MTRF-2000", 1218, 6422); + return true; + case 601: + record = new EpsgGeographicCrsRecord(8860, "NAD83(FBN)", 1211, 6422); + return true; + case 602: + record = new EpsgGeographicCrsRecord(8888, "WGS 84 (Transit)", 1166, 6422); + return true; + case 603: + record = new EpsgGeographicCrsRecord(8899, "RGWF96", 1223, 6423); + return true; + case 604: + record = new EpsgGeographicCrsRecord(8900, "RGWF96", 1223, 6422); + return true; + case 605: + record = new EpsgGeographicCrsRecord(8901, "RGWF96 (lon-lat)", 1223, 6426); + return true; + case 606: + record = new EpsgGeographicCrsRecord(8902, "RGWF96 (lon-lat)", 1223, 6424); + return true; + case 607: + record = new EpsgGeographicCrsRecord(8906, "CR-SIRGAS epoch 2014.59", 1225, 6423); + return true; + case 608: + record = new EpsgGeographicCrsRecord(8907, "CR-SIRGAS epoch 2014.59", 1225, 6422); + return true; + case 609: + record = new EpsgGeographicCrsRecord(8916, "SIRGAS-CON DGF00P01", 1227, 6423); + return true; + case 610: + record = new EpsgGeographicCrsRecord(8918, "SIRGAS-CON DGF01P01", 1228, 6423); + return true; + case 611: + record = new EpsgGeographicCrsRecord(8920, "SIRGAS-CON DGF01P02", 1229, 6423); + return true; + case 612: + record = new EpsgGeographicCrsRecord(8922, "SIRGAS-CON DGF02P01", 1230, 6423); + return true; + case 613: + record = new EpsgGeographicCrsRecord(8924, "SIRGAS-CON DGF04P01", 1231, 6423); + return true; + case 614: + record = new EpsgGeographicCrsRecord(8926, "SIRGAS-CON DGF05P01", 1232, 6423); + return true; + case 615: + record = new EpsgGeographicCrsRecord(8928, "SIRGAS-CON DGF06P01", 1233, 6423); + return true; + case 616: + record = new EpsgGeographicCrsRecord(8930, "SIRGAS-CON DGF07P01", 1234, 6423); + return true; + case 617: + record = new EpsgGeographicCrsRecord(8932, "SIRGAS-CON DGF08P01", 1235, 6423); + return true; + case 618: + record = new EpsgGeographicCrsRecord(8934, "SIRGAS-CON SIR09P01", 1236, 6423); + return true; + case 619: + record = new EpsgGeographicCrsRecord(8936, "SIRGAS-CON SIR10P01", 1237, 6423); + return true; + case 620: + record = new EpsgGeographicCrsRecord(8938, "SIRGAS-CON SIR11P01", 1238, 6423); + return true; + case 621: + record = new EpsgGeographicCrsRecord(8940, "SIRGAS-CON SIR13P01", 1239, 6423); + return true; + case 622: + record = new EpsgGeographicCrsRecord(8942, "SIRGAS-CON SIR14P01", 1240, 6423); + return true; + case 623: + record = new EpsgGeographicCrsRecord(8944, "SIRGAS-CON SIR15P01", 1241, 6423); + return true; + case 624: + record = new EpsgGeographicCrsRecord(8946, "SIRGAS-CON SIR17P01", 1242, 6423); + return true; + case 625: + record = new EpsgGeographicCrsRecord(8972, "SIRGAS-CON DGF00P01", 1227, 6422); + return true; + case 626: + record = new EpsgGeographicCrsRecord(8973, "SIRGAS-CON DGF01P01", 1228, 6422); + return true; + case 627: + record = new EpsgGeographicCrsRecord(8974, "SIRGAS-CON DGF01P02", 1229, 6422); + return true; + case 628: + record = new EpsgGeographicCrsRecord(8975, "SIRGAS-CON DGF02P01", 1230, 6422); + return true; + case 629: + record = new EpsgGeographicCrsRecord(8976, "SIRGAS-CON DGF04P01", 1231, 6422); + return true; + case 630: + record = new EpsgGeographicCrsRecord(8977, "SIRGAS-CON DGF05P01", 1232, 6422); + return true; + case 631: + record = new EpsgGeographicCrsRecord(8978, "SIRGAS-CON DGF06P01", 1233, 6422); + return true; + case 632: + record = new EpsgGeographicCrsRecord(8979, "SIRGAS-CON DGF07P01", 1234, 6422); + return true; + case 633: + record = new EpsgGeographicCrsRecord(8980, "SIRGAS-CON DGF08P01", 1235, 6422); + return true; + case 634: + record = new EpsgGeographicCrsRecord(8981, "SIRGAS-CON SIR09P01", 1236, 6422); + return true; + case 635: + record = new EpsgGeographicCrsRecord(8982, "SIRGAS-CON SIR10P01", 1237, 6422); + return true; + case 636: + record = new EpsgGeographicCrsRecord(8983, "SIRGAS-CON SIR11P01", 1238, 6422); + return true; + case 637: + record = new EpsgGeographicCrsRecord(8984, "SIRGAS-CON SIR13P01", 1239, 6422); + return true; + case 638: + record = new EpsgGeographicCrsRecord(8985, "SIRGAS-CON SIR14P01", 1240, 6422); + return true; + case 639: + record = new EpsgGeographicCrsRecord(8986, "SIRGAS-CON SIR15P01", 1241, 6422); + return true; + case 640: + record = new EpsgGeographicCrsRecord(8987, "SIRGAS-CON SIR17P01", 1242, 6422); + return true; + case 641: + record = new EpsgGeographicCrsRecord(8988, "ITRF88", 6647, 6422); + return true; + case 642: + record = new EpsgGeographicCrsRecord(8989, "ITRF89", 6648, 6422); + return true; + case 643: + record = new EpsgGeographicCrsRecord(8990, "ITRF90", 6649, 6422); + return true; + case 644: + record = new EpsgGeographicCrsRecord(8991, "ITRF91", 6650, 6422); + return true; + case 645: + record = new EpsgGeographicCrsRecord(8992, "ITRF92", 6651, 6422); + return true; + case 646: + record = new EpsgGeographicCrsRecord(8993, "ITRF93", 6652, 6422); + return true; + case 647: + record = new EpsgGeographicCrsRecord(8994, "ITRF94", 6653, 6422); + return true; + case 648: + record = new EpsgGeographicCrsRecord(8995, "ITRF96", 6654, 6422); + return true; + case 649: + record = new EpsgGeographicCrsRecord(8996, "ITRF97", 6655, 6422); + return true; + case 650: + record = new EpsgGeographicCrsRecord(8997, "ITRF2000", 6656, 6422); + return true; + case 651: + record = new EpsgGeographicCrsRecord(8998, "ITRF2005", 6896, 6422); + return true; + case 652: + record = new EpsgGeographicCrsRecord(8999, "ITRF2008", 1061, 6422); + return true; + case 653: + record = new EpsgGeographicCrsRecord(9000, "ITRF2014", 1165, 6422); + return true; + case 654: + record = new EpsgGeographicCrsRecord(9002, "IGS97", 1244, 6423); + return true; + case 655: + record = new EpsgGeographicCrsRecord(9003, "IGS97", 1244, 6422); + return true; + case 656: + record = new EpsgGeographicCrsRecord(9005, "IGS00", 1245, 6423); + return true; + case 657: + record = new EpsgGeographicCrsRecord(9006, "IGS00", 1245, 6422); + return true; + case 658: + record = new EpsgGeographicCrsRecord(9008, "IGb00", 1246, 6423); + return true; + case 659: + record = new EpsgGeographicCrsRecord(9009, "IGb00", 1246, 6422); + return true; + case 660: + record = new EpsgGeographicCrsRecord(9011, "IGS05", 1247, 6423); + return true; + case 661: + record = new EpsgGeographicCrsRecord(9012, "IGS05", 1247, 6422); + return true; + case 662: + record = new EpsgGeographicCrsRecord(9013, "IGS08", 1141, 6423); + return true; + case 663: + record = new EpsgGeographicCrsRecord(9014, "IGS08", 1141, 6422); + return true; + case 664: + record = new EpsgGeographicCrsRecord(9016, "IGb08", 1248, 6423); + return true; + case 665: + record = new EpsgGeographicCrsRecord(9017, "IGb08", 1248, 6422); + return true; + case 666: + record = new EpsgGeographicCrsRecord(9018, "IGS14", 1191, 6423); + return true; + case 667: + record = new EpsgGeographicCrsRecord(9019, "IGS14", 1191, 6422); + return true; + case 668: + record = new EpsgGeographicCrsRecord(9053, "WGS 84 (G730)", 1152, 6422); + return true; + case 669: + record = new EpsgGeographicCrsRecord(9054, "WGS 84 (G873)", 1153, 6422); + return true; + case 670: + record = new EpsgGeographicCrsRecord(9055, "WGS 84 (G1150)", 1154, 6422); + return true; + case 671: + record = new EpsgGeographicCrsRecord(9056, "WGS 84 (G1674)", 1155, 6422); + return true; + case 672: + record = new EpsgGeographicCrsRecord(9057, "WGS 84 (G1762)", 1156, 6422); + return true; + case 673: + record = new EpsgGeographicCrsRecord(9059, "ETRF89", 1178, 6422); + return true; + case 674: + record = new EpsgGeographicCrsRecord(9060, "ETRF90", 1179, 6422); + return true; + case 675: + record = new EpsgGeographicCrsRecord(9061, "ETRF91", 1180, 6422); + return true; + case 676: + record = new EpsgGeographicCrsRecord(9062, "ETRF92", 1181, 6422); + return true; + case 677: + record = new EpsgGeographicCrsRecord(9063, "ETRF93", 1182, 6422); + return true; + case 678: + record = new EpsgGeographicCrsRecord(9064, "ETRF94", 1183, 6422); + return true; + case 679: + record = new EpsgGeographicCrsRecord(9065, "ETRF96", 1184, 6422); + return true; + case 680: + record = new EpsgGeographicCrsRecord(9066, "ETRF97", 1185, 6422); + return true; + case 681: + record = new EpsgGeographicCrsRecord(9067, "ETRF2000", 1186, 6422); + return true; + case 682: + record = new EpsgGeographicCrsRecord(9068, "ETRF2005", 1204, 6422); + return true; + case 683: + record = new EpsgGeographicCrsRecord(9069, "ETRF2014", 1206, 6422); + return true; + case 684: + record = new EpsgGeographicCrsRecord(9071, "NAD83(MARP00)", 1221, 6423); + return true; + case 685: + record = new EpsgGeographicCrsRecord(9072, "NAD83(MARP00)", 1221, 6422); + return true; + case 686: + record = new EpsgGeographicCrsRecord(9074, "NAD83(PACP00)", 1249, 6423); + return true; + case 687: + record = new EpsgGeographicCrsRecord(9075, "NAD83(PACP00)", 1249, 6422); + return true; + case 688: + record = new EpsgGeographicCrsRecord(9139, "ETRS89-XKX [KOSOVAREF01]", 1251, 6423); + return true; + case 689: + record = new EpsgGeographicCrsRecord(9140, "ETRS89-XKX [KOSOVAREF01]", 1251, 6422); + return true; + case 690: + record = new EpsgGeographicCrsRecord(9147, "SIRGAS-Chile 2013", 1252, 6423); + return true; + case 691: + record = new EpsgGeographicCrsRecord(9148, "SIRGAS-Chile 2013", 1252, 6422); + return true; + case 692: + record = new EpsgGeographicCrsRecord(9152, "SIRGAS-Chile 2016", 1253, 6423); + return true; + case 693: + record = new EpsgGeographicCrsRecord(9153, "SIRGAS-Chile 2016", 1253, 6422); + return true; + case 694: + record = new EpsgGeographicCrsRecord(9248, "Tapi Aike", 1257, 6422); + return true; + case 695: + record = new EpsgGeographicCrsRecord(9251, "MMN", 1258, 6422); + return true; + case 696: + record = new EpsgGeographicCrsRecord(9253, "MMS", 1259, 6422); + return true; + case 697: + record = new EpsgGeographicCrsRecord(9267, "MGI", 6312, 6423); + return true; + case 698: + record = new EpsgGeographicCrsRecord(9293, "ONGD17", 1263, 6423); + return true; + case 699: + record = new EpsgGeographicCrsRecord(9294, "ONGD17", 1263, 6422); + return true; + case 700: + record = new EpsgGeographicCrsRecord(9299, "HS2-IRF", 1264, 6422); + return true; + case 701: + record = new EpsgGeographicCrsRecord(9308, "ATRF2014", 1291, 6423); + return true; + case 702: + record = new EpsgGeographicCrsRecord(9309, "ATRF2014", 1291, 6422); + return true; + case 703: + record = new EpsgGeographicCrsRecord(9332, "KSA-GRF17", 1268, 6423); + return true; + case 704: + record = new EpsgGeographicCrsRecord(9333, "KSA-GRF17", 1268, 6422); + return true; + case 705: + record = new EpsgGeographicCrsRecord(9364, "TPEN11-IRF", 1266, 6422); + return true; + case 706: + record = new EpsgGeographicCrsRecord(9372, "MML07-IRF", 1271, 6422); + return true; + case 707: + record = new EpsgGeographicCrsRecord(9379, "IGb14", 1272, 6423); + return true; + case 708: + record = new EpsgGeographicCrsRecord(9380, "IGb14", 1272, 6422); + return true; + case 709: + record = new EpsgGeographicCrsRecord(9384, "AbInvA96_2020-IRF", 1273, 6422); + return true; + case 710: + record = new EpsgGeographicCrsRecord(9403, "PN68", 1286, 6422); + return true; + case 711: + record = new EpsgGeographicCrsRecord(9453, "GBK19-IRF", 1289, 6422); + return true; + case 712: + record = new EpsgGeographicCrsRecord(9469, "SRGI2013", 1293, 6423); + return true; + case 713: + record = new EpsgGeographicCrsRecord(9470, "SRGI2013", 1293, 6422); + return true; + case 714: + record = new EpsgGeographicCrsRecord(9474, "PZ-90.02", 1157, 6422); + return true; + case 715: + record = new EpsgGeographicCrsRecord(9475, "PZ-90.11", 1158, 6422); + return true; + case 716: + record = new EpsgGeographicCrsRecord(9546, "LTF2004(G)", 1295, 6423); + return true; + case 717: + record = new EpsgGeographicCrsRecord(9547, "LTF2004(G)", 1295, 6422); + return true; + case 718: + record = new EpsgGeographicCrsRecord(9695, "REDGEOMIN", 1304, 6423); + return true; + case 719: + record = new EpsgGeographicCrsRecord(9696, "REDGEOMIN", 1304, 6422); + return true; + case 720: + record = new EpsgGeographicCrsRecord(9701, "ETRS89-POL [PL-ETRF2000]", 1305, 6423); + return true; + case 721: + record = new EpsgGeographicCrsRecord(9702, "ETRS89-POL [PL-ETRF2000]", 1305, 6422); + return true; + case 722: + record = new EpsgGeographicCrsRecord(9739, "EOS21-IRF", 1308, 6422); + return true; + case 723: + record = new EpsgGeographicCrsRecord(9754, "WGS 84 (G2139)", 1309, 6423); + return true; + case 724: + record = new EpsgGeographicCrsRecord(9755, "WGS 84 (G2139)", 1309, 6422); + return true; + case 725: + record = new EpsgGeographicCrsRecord(9758, "ECML14_NB-IRF", 1310, 6422); + return true; + case 726: + record = new EpsgGeographicCrsRecord(9763, "EWR2-IRF", 1311, 6422); + return true; + case 727: + record = new EpsgGeographicCrsRecord(9776, "ETRS89-FRA [RGF93 v2]", 1312, 6423); + return true; + case 728: + record = new EpsgGeographicCrsRecord(9777, "ETRS89-FRA [RGF93 v2]", 1312, 6422); + return true; + case 729: + record = new EpsgGeographicCrsRecord(9778, "ETRS89-FRA [RGF93 v2] (lon-lat)", 1312, 6426); + return true; + case 730: + record = new EpsgGeographicCrsRecord(9779, "ETRS89-FRA [RGF93 v2] (lon-lat)", 1312, 6424); + return true; + case 731: + record = new EpsgGeographicCrsRecord(9781, "ETRS89-FRA [RGF93 v2b]", 1313, 6423); + return true; + case 732: + record = new EpsgGeographicCrsRecord(9782, "ETRS89-FRA [RGF93 v2b]", 1313, 6422); + return true; + case 733: + record = new EpsgGeographicCrsRecord(9783, "ETRS89-FRA [RGF93 v2b] (lon-lat)", 1313, 6426); + return true; + case 734: + record = new EpsgGeographicCrsRecord(9784, "ETRS89-FRA [RGF93 v2b] (lon-lat)", 1313, 6424); + return true; + case 735: + record = new EpsgGeographicCrsRecord(9866, "MRH21-IRF", 1314, 6422); + return true; + case 736: + record = new EpsgGeographicCrsRecord(9871, "MOLDOR11-IRF", 1315, 6422); + return true; + case 737: + record = new EpsgGeographicCrsRecord(9893, "LUREF", 6181, 6423); + return true; + case 738: + record = new EpsgGeographicCrsRecord(9939, "EBBWV14-IRF", 1319, 6422); + return true; + case 739: + record = new EpsgGeographicCrsRecord(9964, "HULLEE13-IRF", 1317, 6422); + return true; + case 740: + record = new EpsgGeographicCrsRecord(9969, "SCM22-IRF", 1320, 6422); + return true; + case 741: + record = new EpsgGeographicCrsRecord(9974, "FNL22-IRF", 1321, 6422); + return true; + case 742: + record = new EpsgGeographicCrsRecord(9989, "ITRF2020", 1322, 6423); + return true; + case 743: + record = new EpsgGeographicCrsRecord(9990, "ITRF2020", 1322, 6422); + return true; + case 744: + record = new EpsgGeographicCrsRecord(10158, "S34J-IRF", 1332, 6422); + return true; + case 745: + record = new EpsgGeographicCrsRecord(10175, "DoPw22-IRF", 1334, 6422); + return true; + case 746: + record = new EpsgGeographicCrsRecord(10177, "IGS20", 1333, 6423); + return true; + case 747: + record = new EpsgGeographicCrsRecord(10178, "IGS20", 1333, 6422); + return true; + case 748: + record = new EpsgGeographicCrsRecord(10185, "ShAb07-IRF", 1335, 6422); + return true; + case 749: + record = new EpsgGeographicCrsRecord(10191, "CNH22-IRF", 1336, 6422); + return true; + case 750: + record = new EpsgGeographicCrsRecord(10196, "CWS13-IRF", 1338, 6422); + return true; + case 751: + record = new EpsgGeographicCrsRecord(10204, "DIBA15-IRF", 1339, 6422); + return true; + case 752: + record = new EpsgGeographicCrsRecord(10209, "GWPBS22-IRF", 1340, 6422); + return true; + case 753: + record = new EpsgGeographicCrsRecord(10214, "GWWAB22-IRF", 1341, 6422); + return true; + case 754: + record = new EpsgGeographicCrsRecord(10219, "GWWWA22-IRF", 1342, 6422); + return true; + case 755: + record = new EpsgGeographicCrsRecord(10224, "MALS09-IRF", 1343, 6422); + return true; + case 756: + record = new EpsgGeographicCrsRecord(10229, "OxWo08-IRF", 1344, 6422); + return true; + case 757: + record = new EpsgGeographicCrsRecord(10237, "SYC20-IRF", 1345, 6422); + return true; + case 758: + record = new EpsgGeographicCrsRecord(10249, "S34S-IRF", 1337, 6422); + return true; + case 759: + record = new EpsgGeographicCrsRecord(10252, "S45B-IRF", 1346, 6422); + return true; + case 760: + record = new EpsgGeographicCrsRecord(10256, "GS-IRF", 1347, 6422); + return true; + case 761: + record = new EpsgGeographicCrsRecord(10260, "GSB-IRF", 1348, 6422); + return true; + case 762: + record = new EpsgGeographicCrsRecord(10265, "KK-IRF", 1349, 6422); + return true; + case 763: + record = new EpsgGeographicCrsRecord(10268, "Ostenfeld-IRF", 1350, 6422); + return true; + case 764: + record = new EpsgGeographicCrsRecord(10272, "SMITB20-IRF", 1351, 6422); + return true; + case 765: + record = new EpsgGeographicCrsRecord(10277, "RBEPP12-IRF", 1352, 6422); + return true; + case 766: + record = new EpsgGeographicCrsRecord(10283, "ETRS89-DEU [ETRS89/DREF91/2016]", 1353, 6423); + return true; + case 767: + record = new EpsgGeographicCrsRecord(10284, "ETRS89-DEU [ETRS89/DREF91/2016]", 1353, 6422); + return true; + case 768: + record = new EpsgGeographicCrsRecord(10298, "RGSH2020", 1355, 6423); + return true; + case 769: + record = new EpsgGeographicCrsRecord(10299, "RGSH2020", 1355, 6422); + return true; + case 770: + record = new EpsgGeographicCrsRecord(10300, "RGNC91-93 (lon-lat)", 6749, 6426); + return true; + case 771: + record = new EpsgGeographicCrsRecord(10304, "ETRS89-LVA [LKS-2020]", 1356, 6423); + return true; + case 772: + record = new EpsgGeographicCrsRecord(10305, "ETRS89-LVA [LKS-2020]", 1356, 6422); + return true; + case 773: + record = new EpsgGeographicCrsRecord(10307, "RGNC91-93 (lon-lat)", 6749, 6424); + return true; + case 774: + record = new EpsgGeographicCrsRecord(10309, "RGNC15", 1357, 6423); + return true; + case 775: + record = new EpsgGeographicCrsRecord(10310, "RGNC15", 1357, 6422); + return true; + case 776: + record = new EpsgGeographicCrsRecord(10311, "RGNC15 (lon-lat)", 1357, 6426); + return true; + case 777: + record = new EpsgGeographicCrsRecord(10312, "RGNC15 (lon-lat)", 1357, 6424); + return true; + case 778: + record = new EpsgGeographicCrsRecord(10327, "ETRS89-BIH [BH_ETRS89]", 1358, 6423); + return true; + case 779: + record = new EpsgGeographicCrsRecord(10328, "ETRS89-BIH [BH_ETRS89]", 1358, 6422); + return true; + case 780: + record = new EpsgGeographicCrsRecord(10345, "Hughes 1980", 1359, 6422); + return true; + case 781: + record = new EpsgGeographicCrsRecord(10346, "NSIDC Authalic Sphere", 1360, 6422); + return true; + case 782: + record = new EpsgGeographicCrsRecord(10413, "NAD83(CSRS)v8", 1365, 6423); + return true; + case 783: + record = new EpsgGeographicCrsRecord(10414, "NAD83(CSRS)v8", 1365, 6422); + return true; + case 784: + record = new EpsgGeographicCrsRecord(10468, "COV23-IRF", 1366, 6422); + return true; + case 785: + record = new EpsgGeographicCrsRecord(10474, "BBT2000", 1367, 6423); + return true; + case 786: + record = new EpsgGeographicCrsRecord(10475, "BBT2000", 1367, 6422); + return true; + case 787: + record = new EpsgGeographicCrsRecord(10570, "ETRF2020", 1382, 6423); + return true; + case 788: + record = new EpsgGeographicCrsRecord(10571, "ETRF2020", 1382, 6422); + return true; + case 789: + record = new EpsgGeographicCrsRecord(10605, "WGS 84 (G2296)", 1383, 6423); + return true; + case 790: + record = new EpsgGeographicCrsRecord(10606, "WGS 84 (G2296)", 1383, 6422); + return true; + case 791: + record = new EpsgGeographicCrsRecord(10623, "ECML14-IRF", 1385, 6422); + return true; + case 792: + record = new EpsgGeographicCrsRecord(10628, "WC05-IRF", 1386, 6422); + return true; + case 793: + record = new EpsgGeographicCrsRecord(10635, "Saba", 1379, 6423); + return true; + case 794: + record = new EpsgGeographicCrsRecord(10636, "Saba", 1379, 6422); + return true; + case 795: + record = new EpsgGeographicCrsRecord(10638, "BES2020 Saba", 1380, 6423); + return true; + case 796: + record = new EpsgGeographicCrsRecord(10639, "BES2020 Saba", 1380, 6422); + return true; + case 797: + record = new EpsgGeographicCrsRecord(10670, "RGM23", 1389, 6423); + return true; + case 798: + record = new EpsgGeographicCrsRecord(10671, "RGM23", 1389, 6422); + return true; + case 799: + record = new EpsgGeographicCrsRecord(10672, "RGM23 (lon-lat)", 1389, 6426); + return true; + case 800: + record = new EpsgGeographicCrsRecord(10673, "RGM23 (lon-lat)", 1389, 6424); + return true; + case 801: + record = new EpsgGeographicCrsRecord(10689, "ETRS89-FIN [EUREF-FIN]", 1391, 6423); + return true; + case 802: + record = new EpsgGeographicCrsRecord(10690, "ETRS89-FIN [EUREF-FIN]", 1391, 6422); + return true; + case 803: + record = new EpsgGeographicCrsRecord(10724, "UZGD2024", 1392, 6423); + return true; + case 804: + record = new EpsgGeographicCrsRecord(10725, "UZGD2024", 1392, 6422); + return true; + case 805: + record = new EpsgGeographicCrsRecord(10735, "Sint Eustatius", 1393, 6423); + return true; + case 806: + record = new EpsgGeographicCrsRecord(10736, "Sint Eustatius", 1393, 6422); + return true; + case 807: + record = new EpsgGeographicCrsRecord(10738, "BES2020 Sint Eustatius", 1394, 6423); + return true; + case 808: + record = new EpsgGeographicCrsRecord(10739, "BES2020 Sint Eustatius", 1394, 6422); + return true; + case 809: + record = new EpsgGeographicCrsRecord(10758, "Bonaire", 1396, 6422); + return true; + case 810: + record = new EpsgGeographicCrsRecord(10761, "Bonaire 2004", 1397, 6423); + return true; + case 811: + record = new EpsgGeographicCrsRecord(10762, "Bonaire 2004", 1397, 6422); + return true; + case 812: + record = new EpsgGeographicCrsRecord(10780, "ITRF2020-u2023", 1399, 6423); + return true; + case 813: + record = new EpsgGeographicCrsRecord(10781, "ITRF2020-u2023", 1399, 6422); + return true; + case 814: + record = new EpsgGeographicCrsRecord(10784, "IGb20", 1400, 6423); + return true; + case 815: + record = new EpsgGeographicCrsRecord(10785, "IGb20", 1400, 6422); + return true; + case 816: + record = new EpsgGeographicCrsRecord(10790, "UGRF", 1401, 6423); + return true; + case 817: + record = new EpsgGeographicCrsRecord(10791, "UGRF", 1401, 6422); + return true; + case 818: + record = new EpsgGeographicCrsRecord(10799, "LibRef21", 1402, 6423); + return true; + case 819: + record = new EpsgGeographicCrsRecord(10800, "LibRef21", 1402, 6422); + return true; + case 820: + record = new EpsgGeographicCrsRecord(10806, "NKG_ETRF14", 1403, 6423); + return true; + case 821: + record = new EpsgGeographicCrsRecord(10807, "NKG_ETRF14", 1403, 6422); + return true; + case 822: + record = new EpsgGeographicCrsRecord(10830, "Georgia Geodetic Datum", 1404, 6423); + return true; + case 823: + record = new EpsgGeographicCrsRecord(10831, "Georgia Geodetic Datum", 1404, 6422); + return true; + case 824: + record = new EpsgGeographicCrsRecord(10849, "EWR3-IRF", 1405, 6422); + return true; + case 825: + record = new EpsgGeographicCrsRecord(10860, "WSPG-IRF", 1406, 6422); + return true; + case 826: + record = new EpsgGeographicCrsRecord(10874, "ETRS89-NOR [EUREF89]", 1407, 6423); + return true; + case 827: + record = new EpsgGeographicCrsRecord(10875, "ETRS89-NOR [EUREF89]", 1407, 6422); + return true; + case 828: + record = new EpsgGeographicCrsRecord(10891, "ETRS89-DNK", 1412, 6423); + return true; + case 829: + record = new EpsgGeographicCrsRecord(10892, "ETRS89-DNK", 1412, 6422); + return true; + case 830: + record = new EpsgGeographicCrsRecord(10898, "Asse 2025", 1413, 6422); + return true; + case 831: + record = new EpsgGeographicCrsRecord(10909, "CSRN epoch 2025.0 (NAD83 2011)", 1414, 6423); + return true; + case 832: + record = new EpsgGeographicCrsRecord(10910, "CSRN epoch 2025.0 (NAD83 2011)", 1414, 6422); + return true; + case 833: + record = new EpsgGeographicCrsRecord(10940, "QazTRF-23", 1417, 6423); + return true; + case 834: + record = new EpsgGeographicCrsRecord(10941, "QazTRF-23", 1417, 6422); + return true; + case 835: + record = new EpsgGeographicCrsRecord(10951, "CSRN epoch 2025.0 (ITRF2020)", 1418, 6423); + return true; + case 836: + record = new EpsgGeographicCrsRecord(10952, "CSRN epoch 2025.0 (ITRF2020)", 1418, 6422); + return true; + case 837: + record = new EpsgGeographicCrsRecord(10955, "GR96(1996)", 1420, 6423); + return true; + case 838: + record = new EpsgGeographicCrsRecord(10956, "GR96(1996)", 1420, 6422); + return true; + case 839: + record = new EpsgGeographicCrsRecord(10958, "GR96(2021)", 1419, 6423); + return true; + case 840: + record = new EpsgGeographicCrsRecord(10959, "GR96(2021)", 1419, 6422); + return true; + case 841: + record = new EpsgGeographicCrsRecord(10967, "NATRF2022", 1422, 6423); + return true; + case 842: + record = new EpsgGeographicCrsRecord(10968, "NATRF2022", 1422, 6422); + return true; + case 843: + record = new EpsgGeographicCrsRecord(10992, "Xrail84", 1408, 6423); + return true; + case 844: + record = new EpsgGeographicCrsRecord(10993, "Xrail84", 1408, 6422); + return true; + case 845: + record = new EpsgGeographicCrsRecord(11008, "ETRS89-GBR [OSNet v2009]", 1425, 6423); + return true; + case 846: + record = new EpsgGeographicCrsRecord(11009, "ETRS89-GBR [OSNet v2009]", 1425, 6422); + return true; + case 847: + record = new EpsgGeographicCrsRecord(11030, "SRGI2013 epoch 2021.0", 1426, 6423); + return true; + case 848: + record = new EpsgGeographicCrsRecord(11033, "SRGI2013 epoch 2021.0", 1426, 6422); + return true; + case 849: + record = new EpsgGeographicCrsRecord(11036, "ETRS89-NLD [AGRS2010]", 1427, 6423); + return true; + case 850: + record = new EpsgGeographicCrsRecord(11037, "ETRS89-NLD [AGRS2010]", 1427, 6422); + return true; + case 851: + record = new EpsgGeographicCrsRecord(11042, "CR-SIRGAS epoch 2019.24", 1428, 6423); + return true; + case 852: + record = new EpsgGeographicCrsRecord(11043, "CR-SIRGAS epoch 2019.24", 1428, 6422); + return true; + case 853: + record = new EpsgGeographicCrsRecord(11046, "ETRS89-ALB [KRGJSH 2010]", 1429, 6423); + return true; + case 854: + record = new EpsgGeographicCrsRecord(11047, "ETRS89-ALB [KRGJSH 2010] ", 1429, 6422); + return true; + case 855: + record = new EpsgGeographicCrsRecord(11052, "ETRS89-ALB [CORS]", 1430, 6423); + return true; + case 856: + record = new EpsgGeographicCrsRecord(11053, "ETRS89-ALB [CORS] ", 1430, 6422); + return true; + case 857: + record = new EpsgGeographicCrsRecord(11056, "ETRS89-AUT [2002]", 1431, 6423); + return true; + case 858: + record = new EpsgGeographicCrsRecord(11057, "ETRS89-AUT [2002]", 1431, 6422); + return true; + case 859: + record = new EpsgGeographicCrsRecord(11062, "ETRS89-BEL [BEREF2002]", 1432, 6423); + return true; + case 860: + record = new EpsgGeographicCrsRecord(11063, "ETRS89-BEL [BEREF2002]", 1432, 6422); + return true; + case 861: + record = new EpsgGeographicCrsRecord(11069, "ETRS89-CZE [2007]", 1433, 6423); + return true; + case 862: + record = new EpsgGeographicCrsRecord(11070, "ETRS89-CZE [2007]", 1433, 6422); + return true; + case 863: + record = new EpsgGeographicCrsRecord(11075, "ETRS89-SVK [SKTRF09]", 1434, 6423); + return true; + case 864: + record = new EpsgGeographicCrsRecord(11076, "ETRS89-SVK [SKTRF09]", 1434, 6422); + return true; + case 865: + record = new EpsgGeographicCrsRecord(11078, "ETRS89-SVK [SKTRF2022]", 1435, 6423); + return true; + case 866: + record = new EpsgGeographicCrsRecord(11079, "ETRS89-SVK [SKTRF2022]", 1435, 6422); + return true; + case 867: + record = new EpsgGeographicCrsRecord(11086, "ETRS89-FRO [2008]", 1436, 6423); + return true; + case 868: + record = new EpsgGeographicCrsRecord(11087, "ETRS89-FRO [2008]", 1436, 6422); + return true; + case 869: + record = new EpsgGeographicCrsRecord(11092, "ETRS89-GRC [HTRS07]", 1437, 6423); + return true; + case 870: + record = new EpsgGeographicCrsRecord(11093, "ETRS89-GRC [HTRS07]", 1437, 6422); + return true; + case 871: + record = new EpsgGeographicCrsRecord(11098, "ETRS89-MKD [EUREF-MAK2010]", 1438, 6423); + return true; + case 872: + record = new EpsgGeographicCrsRecord(11099, "ETRS89-MKD [EUREF-MAK2010]", 1438, 6422); + return true; + case 873: + record = new EpsgGeographicCrsRecord(11107, "ETRS89-PRT [1995]", 1439, 6423); + return true; + case 874: + record = new EpsgGeographicCrsRecord(11108, "ETRS89-PRT [1995]", 1439, 6422); + return true; + case 875: + record = new EpsgGeographicCrsRecord(11113, "ETRS89-ROU [ETRF2000]", 1440, 6423); + return true; + case 876: + record = new EpsgGeographicCrsRecord(11119, "ETRS89-ROU [ETRF2000]", 1440, 6422); + return true; + case 877: + record = new EpsgGeographicCrsRecord(11127, "ETRS89-ESP [ERGNSS]", 1442, 6423); + return true; + case 878: + record = new EpsgGeographicCrsRecord(11128, "ETRS89-ESP [ERGNSS]", 1442, 6422); + return true; + case 879: + record = new EpsgGeographicCrsRecord(11130, "ETRS89-ESP [REGENTE]", 1441, 6423); + return true; + case 880: + record = new EpsgGeographicCrsRecord(11134, "ETRS89-ESP [REGENTE]", 1441, 6422); + return true; + case 881: + record = new EpsgGeographicCrsRecord(11162, "ETRS89-HUN [ETRF2000]", 1444, 6423); + return true; + case 882: + record = new EpsgGeographicCrsRecord(11163, "ETRS89-HUN [ETRF2000]", 1444, 6422); + return true; + case 883: + record = new EpsgGeographicCrsRecord(11188, "ETRS89-HRV [CROPOS]", 1445, 6423); + return true; + case 884: + record = new EpsgGeographicCrsRecord(11189, "ETRS89-HRV [CROPOS]", 1445, 6422); + return true; + case 885: + record = new EpsgGeographicCrsRecord(11198, "ETRS89-DEU [ETRS89/DREF91/2025]", 1446, 6423); + return true; + case 886: + record = new EpsgGeographicCrsRecord(11199, "ETRS89-DEU [ETRS89/DREF91/2025]", 1446, 6422); + return true; + case 887: + record = new EpsgGeographicCrsRecord(11214, "ETRS89-BEL [BEREF2011]", 1447, 6423); + return true; + case 888: + record = new EpsgGeographicCrsRecord(11215, "ETRS89-BEL [BEREF2011]", 1447, 6422); + return true; + case 889: + record = new EpsgGeographicCrsRecord(11223, "ETRS89-CHE [CHTRF95]", 1449, 6423); + return true; + case 890: + record = new EpsgGeographicCrsRecord(11225, "DrukRef23", 1448, 6423); + return true; + case 891: + record = new EpsgGeographicCrsRecord(11226, "DrukRef23", 1448, 6422); + return true; + case 892: + record = new EpsgGeographicCrsRecord(11307, "ETRS89-CHE [CHTRF95]", 1449, 6422); + return true; + case 893: + record = new EpsgGeographicCrsRecord(11392, "ETRS89-LUX [ETRF2000]", 1453, 6423); + return true; + case 894: + record = new EpsgGeographicCrsRecord(11393, "ETRS89-LUX [ETRF2000]", 1453, 6422); + return true; + case 895: + record = new EpsgGeographicCrsRecord(20033, "MWC18-IRF", 1324, 6422); + return true; + case 896: + record = new EpsgGeographicCrsRecord(20040, "SIRGAS-Chile 2021", 1327, 6423); + return true; + case 897: + record = new EpsgGeographicCrsRecord(20041, "SIRGAS-Chile 2021", 1327, 6422); + return true; + case 898: + record = new EpsgGeographicCrsRecord(20045, "MAGNA-SIRGAS 2018", 1329, 6423); + return true; + case 899: + record = new EpsgGeographicCrsRecord(20046, "MAGNA-SIRGAS 2018", 1329, 6422); + return true; + default: + record = default; + return false; + } + } + + internal static bool TryGetGeocentricCrs(int index, out EpsgGeocentricCrsRecord record) + { + switch (index) + { + case 0: + record = new EpsgGeocentricCrsRecord(3822, "TWD97", 1026, 6500); + return true; + case 1: + record = new EpsgGeocentricCrsRecord(3887, "IGRS", 1029, 6500); + return true; + case 2: + record = new EpsgGeocentricCrsRecord(4000, "ETRS89-MDA [MOLDREF99]", 1032, 6500); + return true; + case 3: + record = new EpsgGeocentricCrsRecord(4039, "RGRDC 2005", 1033, 6500); + return true; + case 4: + record = new EpsgGeocentricCrsRecord(4073, "ETRS89-SRB [SREF98]", 1034, 6500); + return true; + case 5: + record = new EpsgGeocentricCrsRecord(4079, "REGCAN95", 1035, 6500); + return true; + case 6: + record = new EpsgGeocentricCrsRecord(4465, "RGSPM06", 1038, 6500); + return true; + case 7: + record = new EpsgGeocentricCrsRecord(4468, "RGM04", 1036, 6500); + return true; + case 8: + record = new EpsgGeocentricCrsRecord(4473, "Cadastre 1997", 1037, 6500); + return true; + case 9: + record = new EpsgGeocentricCrsRecord(4479, "China Geodetic Coordinate System 2000", 1043, 6500); + return true; + case 10: + record = new EpsgGeocentricCrsRecord(4481, "Mexico ITRF92", 1042, 6500); + return true; + case 11: + record = new EpsgGeocentricCrsRecord(4556, "RRAF 1991", 1047, 6500); + return true; + case 12: + record = new EpsgGeocentricCrsRecord(4882, "ETRS89-SVN [D96]", 6765, 6500); + return true; + case 13: + record = new EpsgGeocentricCrsRecord(4884, "RSRGD2000", 6764, 6500); + return true; + case 14: + record = new EpsgGeocentricCrsRecord(4886, "BDA2000", 6762, 6500); + return true; + case 15: + record = new EpsgGeocentricCrsRecord(4888, "ETRS89-HRV [HTRS96]", 6761, 6500); + return true; + case 16: + record = new EpsgGeocentricCrsRecord(4890, "WGS 66", 6760, 6500); + return true; + case 17: + record = new EpsgGeocentricCrsRecord(4892, "NAD83(NSRS2007)", 6759, 6500); + return true; + case 18: + record = new EpsgGeocentricCrsRecord(4894, "JAD2001", 6758, 6500); + return true; + case 19: + record = new EpsgGeocentricCrsRecord(4896, "ITRF2005", 6896, 6500); + return true; + case 20: + record = new EpsgGeocentricCrsRecord(4897, "DGN95", 6755, 6500); + return true; + case 21: + record = new EpsgGeocentricCrsRecord(4899, "LGD2006", 6754, 6500); + return true; + case 22: + record = new EpsgGeocentricCrsRecord(4906, "RGNC91-93", 6749, 6500); + return true; + case 23: + record = new EpsgGeocentricCrsRecord(4908, "GR96", 1421, 6500); + return true; + case 24: + record = new EpsgGeocentricCrsRecord(4910, "ITRF88", 6647, 6500); + return true; + case 25: + record = new EpsgGeocentricCrsRecord(4911, "ITRF89", 6648, 6500); + return true; + case 26: + record = new EpsgGeocentricCrsRecord(4912, "ITRF90", 6649, 6500); + return true; + case 27: + record = new EpsgGeocentricCrsRecord(4913, "ITRF91", 6650, 6500); + return true; + case 28: + record = new EpsgGeocentricCrsRecord(4914, "ITRF92", 6651, 6500); + return true; + case 29: + record = new EpsgGeocentricCrsRecord(4915, "ITRF93", 6652, 6500); + return true; + case 30: + record = new EpsgGeocentricCrsRecord(4916, "ITRF94", 6653, 6500); + return true; + case 31: + record = new EpsgGeocentricCrsRecord(4917, "ITRF96", 6654, 6500); + return true; + case 32: + record = new EpsgGeocentricCrsRecord(4918, "ITRF97", 6655, 6500); + return true; + case 33: + record = new EpsgGeocentricCrsRecord(4919, "ITRF2000", 6656, 6500); + return true; + case 34: + record = new EpsgGeocentricCrsRecord(4920, "GDM2000", 6742, 6500); + return true; + case 35: + record = new EpsgGeocentricCrsRecord(4922, "PZ-90", 6740, 6500); + return true; + case 36: + record = new EpsgGeocentricCrsRecord(4924, "Mauritania 1999", 6702, 6500); + return true; + case 37: + record = new EpsgGeocentricCrsRecord(4926, "KGD2002", 6737, 6500); + return true; + case 38: + record = new EpsgGeocentricCrsRecord(4928, "POSGAR 94", 6694, 6500); + return true; + case 39: + record = new EpsgGeocentricCrsRecord(4930, "Australian Antarctic", 6176, 6500); + return true; + case 40: + record = new EpsgGeocentricCrsRecord(4932, "CHTRS95", 6151, 6500); + return true; + case 41: + record = new EpsgGeocentricCrsRecord(4934, "ETRS89-EST [EST97]", 6180, 6500); + return true; + case 42: + record = new EpsgGeocentricCrsRecord(4936, "ETRS89", 6258, 6500); + return true; + case 43: + record = new EpsgGeocentricCrsRecord(4938, "GDA94", 6283, 6500); + return true; + case 44: + record = new EpsgGeocentricCrsRecord(4940, "Hartebeesthoek94", 6148, 6500); + return true; + case 45: + record = new EpsgGeocentricCrsRecord(4942, "ETRS89-IRE [ETRF2000]", 6173, 6500); + return true; + case 46: + record = new EpsgGeocentricCrsRecord(4944, "ISN93", 6659, 6500); + return true; + case 47: + record = new EpsgGeocentricCrsRecord(4946, "JGD2000", 6612, 6500); + return true; + case 48: + record = new EpsgGeocentricCrsRecord(4948, "ETRS89-LVA [LKS-92]", 6661, 6500); + return true; + case 49: + record = new EpsgGeocentricCrsRecord(4950, "ETRS89-LTU [LKS94]", 6126, 6500); + return true; + case 50: + record = new EpsgGeocentricCrsRecord(4952, "Moznet", 6130, 6500); + return true; + case 51: + record = new EpsgGeocentricCrsRecord(4954, "NAD83(CSRS)", 6140, 6500); + return true; + case 52: + record = new EpsgGeocentricCrsRecord(4956, "NAD83(HARN)", 6152, 6500); + return true; + case 53: + record = new EpsgGeocentricCrsRecord(4958, "NZGD2000", 6167, 6500); + return true; + case 54: + record = new EpsgGeocentricCrsRecord(4960, "POSGAR 98", 6190, 6500); + return true; + case 55: + record = new EpsgGeocentricCrsRecord(4962, "REGVEN", 6189, 6500); + return true; + case 56: + record = new EpsgGeocentricCrsRecord(4964, "ETRS89-FRA [RGF93 v1]", 6171, 6500); + return true; + case 57: + record = new EpsgGeocentricCrsRecord(4966, "RGFG95", 6624, 6500); + return true; + case 58: + record = new EpsgGeocentricCrsRecord(4970, "RGR92", 6627, 6500); + return true; + case 59: + record = new EpsgGeocentricCrsRecord(4974, "SIRGAS 1995", 6170, 6500); + return true; + case 60: + record = new EpsgGeocentricCrsRecord(4976, "ETRS89-SWE [SWEREF 99]", 6619, 6500); + return true; + case 61: + record = new EpsgGeocentricCrsRecord(4978, "WGS 84", 6326, 6500); + return true; + case 62: + record = new EpsgGeocentricCrsRecord(4980, "Yemen NGN96", 6163, 6500); + return true; + case 63: + record = new EpsgGeocentricCrsRecord(4982, "ETRS89-ITA [IGM95]", 6670, 6500); + return true; + case 64: + record = new EpsgGeocentricCrsRecord(4984, "WGS 72", 6322, 6500); + return true; + case 65: + record = new EpsgGeocentricCrsRecord(4986, "WGS 72BE", 6324, 6500); + return true; + case 66: + record = new EpsgGeocentricCrsRecord(4988, "SIRGAS 2000", 6674, 6500); + return true; + case 67: + record = new EpsgGeocentricCrsRecord(4990, "Lao 1993", 6677, 6500); + return true; + case 68: + record = new EpsgGeocentricCrsRecord(4992, "Lao 1997", 6678, 6500); + return true; + case 69: + record = new EpsgGeocentricCrsRecord(4994, "PRS92", 6683, 6500); + return true; + case 70: + record = new EpsgGeocentricCrsRecord(4996, "MAGNA-SIRGAS", 6686, 6500); + return true; + case 71: + record = new EpsgGeocentricCrsRecord(4998, "RGPF", 6687, 6500); + return true; + case 72: + record = new EpsgGeocentricCrsRecord(5011, "PTRA08", 1041, 6500); + return true; + case 73: + record = new EpsgGeocentricCrsRecord(5244, "GDBD2009", 1056, 6500); + return true; + case 74: + record = new EpsgGeocentricCrsRecord(5250, "TUREF", 1057, 6500); + return true; + case 75: + record = new EpsgGeocentricCrsRecord(5262, "DRUKREF 03", 1058, 6500); + return true; + case 76: + record = new EpsgGeocentricCrsRecord(5322, "ISN2004", 1060, 6500); + return true; + case 77: + record = new EpsgGeocentricCrsRecord(5332, "ITRF2008", 1061, 6500); + return true; + case 78: + record = new EpsgGeocentricCrsRecord(5341, "POSGAR 2007", 1062, 6500); + return true; + case 79: + record = new EpsgGeocentricCrsRecord(5352, "MARGEN", 1063, 6500); + return true; + case 80: + record = new EpsgGeocentricCrsRecord(5358, "SIRGAS-Chile 2002", 1064, 6500); + return true; + case 81: + record = new EpsgGeocentricCrsRecord(5363, "CR05", 1065, 6500); + return true; + case 82: + record = new EpsgGeocentricCrsRecord(5368, "MACARIO SOLIS", 1066, 6500); + return true; + case 83: + record = new EpsgGeocentricCrsRecord(5369, "Peru96", 1067, 6500); + return true; + case 84: + record = new EpsgGeocentricCrsRecord(5379, "SIRGAS-ROU98", 1068, 6500); + return true; + case 85: + record = new EpsgGeocentricCrsRecord(5391, "SIRGAS_ES2007.8", 1069, 6500); + return true; + case 86: + record = new EpsgGeocentricCrsRecord(5487, "RGAF09", 1073, 6500); + return true; + case 87: + record = new EpsgGeocentricCrsRecord(5544, "PNG94", 1076, 6500); + return true; + case 88: + record = new EpsgGeocentricCrsRecord(5558, "UCS-2000", 1077, 6500); + return true; + case 89: + record = new EpsgGeocentricCrsRecord(5591, "FEH2010", 1078, 6500); + return true; + case 90: + record = new EpsgGeocentricCrsRecord(5828, "DB_REF", 1081, 6500); + return true; + case 91: + record = new EpsgGeocentricCrsRecord(5884, "TGD2005", 1095, 6500); + return true; + case 92: + record = new EpsgGeocentricCrsRecord(6133, "CIGD11", 1100, 6500); + return true; + case 93: + record = new EpsgGeocentricCrsRecord(6309, "CGRS93", 1112, 6500); + return true; + case 94: + record = new EpsgGeocentricCrsRecord(6317, "NAD83(2011)", 1116, 6500); + return true; + case 95: + record = new EpsgGeocentricCrsRecord(6320, "NAD83(PA11)", 1117, 6500); + return true; + case 96: + record = new EpsgGeocentricCrsRecord(6323, "NAD83(MA11)", 1118, 6500); + return true; + case 97: + record = new EpsgGeocentricCrsRecord(6363, "Mexico ITRF2008", 1120, 6500); + return true; + case 98: + record = new EpsgGeocentricCrsRecord(6666, "JGD2011", 1128, 6500); + return true; + case 99: + record = new EpsgGeocentricCrsRecord(6704, "ETRS89-ITA [RDN2008]", 1132, 6500); + return true; + case 100: + record = new EpsgGeocentricCrsRecord(6781, "NAD83(CORS96)", 1133, 6500); + return true; + case 101: + record = new EpsgGeocentricCrsRecord(6934, "IGS08", 1141, 6500); + return true; + case 102: + record = new EpsgGeocentricCrsRecord(6981, "IG05 Intermediate CRS", 1142, 6500); + return true; + case 103: + record = new EpsgGeocentricCrsRecord(6988, "IG05/12 Intermediate CRS", 1144, 6500); + return true; + case 104: + record = new EpsgGeocentricCrsRecord(7071, "RGTAAF07", 1113, 6500); + return true; + case 105: + record = new EpsgGeocentricCrsRecord(7134, "IGD05", 1114, 6500); + return true; + case 106: + record = new EpsgGeocentricCrsRecord(7137, "IGD05/12", 1115, 6500); + return true; + case 107: + record = new EpsgGeocentricCrsRecord(7371, "ONGD14", 1147, 6500); + return true; + case 108: + record = new EpsgGeocentricCrsRecord(7656, "WGS 84 (G730)", 1152, 6500); + return true; + case 109: + record = new EpsgGeocentricCrsRecord(7658, "WGS 84 (G873)", 1153, 6500); + return true; + case 110: + record = new EpsgGeocentricCrsRecord(7660, "WGS 84 (G1150)", 1154, 6500); + return true; + case 111: + record = new EpsgGeocentricCrsRecord(7662, "WGS 84 (G1674)", 1155, 6500); + return true; + case 112: + record = new EpsgGeocentricCrsRecord(7664, "WGS 84 (G1762)", 1156, 6500); + return true; + case 113: + record = new EpsgGeocentricCrsRecord(7677, "PZ-90.02", 1157, 6500); + return true; + case 114: + record = new EpsgGeocentricCrsRecord(7679, "PZ-90.11", 1158, 6500); + return true; + case 115: + record = new EpsgGeocentricCrsRecord(7681, "GSK-2011", 1159, 6500); + return true; + case 116: + record = new EpsgGeocentricCrsRecord(7684, "Kyrg-06", 1160, 6500); + return true; + case 117: + record = new EpsgGeocentricCrsRecord(7789, "ITRF2014", 1165, 6500); + return true; + case 118: + record = new EpsgGeocentricCrsRecord(7796, "ETRS89-BGR [BGS2005]", 1167, 6500); + return true; + case 119: + record = new EpsgGeocentricCrsRecord(7815, "WGS 84 (Transit)", 1166, 6500); + return true; + case 120: + record = new EpsgGeocentricCrsRecord(7842, "GDA2020", 1168, 6500); + return true; + case 121: + record = new EpsgGeocentricCrsRecord(7879, "St. Helena Tritan", 1173, 6500); + return true; + case 122: + record = new EpsgGeocentricCrsRecord(7884, "SHGD2015", 1174, 6500); + return true; + case 123: + record = new EpsgGeocentricCrsRecord(7914, "ETRF89", 1178, 6500); + return true; + case 124: + record = new EpsgGeocentricCrsRecord(7916, "ETRF90", 1179, 6500); + return true; + case 125: + record = new EpsgGeocentricCrsRecord(7918, "ETRF91", 1180, 6500); + return true; + case 126: + record = new EpsgGeocentricCrsRecord(7920, "ETRF92", 1181, 6500); + return true; + case 127: + record = new EpsgGeocentricCrsRecord(7922, "ETRF93", 1182, 6500); + return true; + case 128: + record = new EpsgGeocentricCrsRecord(7924, "ETRF94", 1183, 6500); + return true; + case 129: + record = new EpsgGeocentricCrsRecord(7926, "ETRF96", 1184, 6500); + return true; + case 130: + record = new EpsgGeocentricCrsRecord(7928, "ETRF97", 1185, 6500); + return true; + case 131: + record = new EpsgGeocentricCrsRecord(7930, "ETRF2000", 1186, 6500); + return true; + case 132: + record = new EpsgGeocentricCrsRecord(8084, "ISN2016", 1187, 6500); + return true; + case 133: + record = new EpsgGeocentricCrsRecord(8227, "IGS14", 1191, 6500); + return true; + case 134: + record = new EpsgGeocentricCrsRecord(8230, "NAD83(CSRS96)", 1192, 6500); + return true; + case 135: + record = new EpsgGeocentricCrsRecord(8233, "NAD83(CSRS)v2", 1193, 6500); + return true; + case 136: + record = new EpsgGeocentricCrsRecord(8238, "NAD83(CSRS)v3", 1194, 6500); + return true; + case 137: + record = new EpsgGeocentricCrsRecord(8242, "NAD83(CSRS)v4", 1195, 6500); + return true; + case 138: + record = new EpsgGeocentricCrsRecord(8247, "NAD83(CSRS)v5", 1196, 6500); + return true; + case 139: + record = new EpsgGeocentricCrsRecord(8250, "NAD83(CSRS)v6", 1197, 6500); + return true; + case 140: + record = new EpsgGeocentricCrsRecord(8253, "NAD83(CSRS)v7", 1198, 6500); + return true; + case 141: + record = new EpsgGeocentricCrsRecord(8397, "ETRF2005", 1204, 6500); + return true; + case 142: + record = new EpsgGeocentricCrsRecord(8401, "ETRF2014", 1206, 6500); + return true; + case 143: + record = new EpsgGeocentricCrsRecord(8425, "Hong Kong Geodetic CS", 1209, 6500); + return true; + case 144: + record = new EpsgGeocentricCrsRecord(8429, "Macao 2008", 1208, 6500); + return true; + case 145: + record = new EpsgGeocentricCrsRecord(8541, "NAD83(FBN)", 1211, 6500); + return true; + case 146: + record = new EpsgGeocentricCrsRecord(8543, "NAD83(HARN Corrected)", 1212, 6500); + return true; + case 147: + record = new EpsgGeocentricCrsRecord(8683, "ETRS89-SRB [STRS00]", 1214, 6500); + return true; + case 148: + record = new EpsgGeocentricCrsRecord(8697, "RSAO13", 1220, 6500); + return true; + case 149: + record = new EpsgGeocentricCrsRecord(8816, "MTRF-2000", 1218, 6500); + return true; + case 150: + record = new EpsgGeocentricCrsRecord(8898, "RGWF96", 1223, 6500); + return true; + case 151: + record = new EpsgGeocentricCrsRecord(8905, "CR-SIRGAS epoch 2014.59", 1225, 6500); + return true; + case 152: + record = new EpsgGeocentricCrsRecord(8915, "SIRGAS-CON DGF00P01", 1227, 6500); + return true; + case 153: + record = new EpsgGeocentricCrsRecord(8917, "SIRGAS-CON DGF01P01", 1228, 6500); + return true; + case 154: + record = new EpsgGeocentricCrsRecord(8919, "SIRGAS-CON DGF01P02", 1229, 6500); + return true; + case 155: + record = new EpsgGeocentricCrsRecord(8921, "SIRGAS-CON DGF02P01", 1230, 6500); + return true; + case 156: + record = new EpsgGeocentricCrsRecord(8923, "SIRGAS-CON DGF04P01", 1231, 6500); + return true; + case 157: + record = new EpsgGeocentricCrsRecord(8925, "SIRGAS-CON DGF05P01", 1232, 6500); + return true; + case 158: + record = new EpsgGeocentricCrsRecord(8927, "SIRGAS-CON DGF06P01", 1233, 6500); + return true; + case 159: + record = new EpsgGeocentricCrsRecord(8929, "SIRGAS-CON DGF07P01", 1234, 6500); + return true; + case 160: + record = new EpsgGeocentricCrsRecord(8931, "SIRGAS-CON DGF08P01", 1235, 6500); + return true; + case 161: + record = new EpsgGeocentricCrsRecord(8933, "SIRGAS-CON SIR09P01", 1236, 6500); + return true; + case 162: + record = new EpsgGeocentricCrsRecord(8935, "SIRGAS-CON SIR10P01", 1237, 6500); + return true; + case 163: + record = new EpsgGeocentricCrsRecord(8937, "SIRGAS-CON SIR11P01", 1238, 6500); + return true; + case 164: + record = new EpsgGeocentricCrsRecord(8939, "SIRGAS-CON SIR13P01", 1239, 6500); + return true; + case 165: + record = new EpsgGeocentricCrsRecord(8941, "SIRGAS-CON SIR14P01", 1240, 6500); + return true; + case 166: + record = new EpsgGeocentricCrsRecord(8943, "SIRGAS-CON SIR15P01", 1241, 6500); + return true; + case 167: + record = new EpsgGeocentricCrsRecord(8945, "SIRGAS-CON SIR17P01", 1242, 6500); + return true; + case 168: + record = new EpsgGeocentricCrsRecord(9001, "IGS97", 1244, 6500); + return true; + case 169: + record = new EpsgGeocentricCrsRecord(9004, "IGS00", 1245, 6500); + return true; + case 170: + record = new EpsgGeocentricCrsRecord(9007, "IGb00", 1246, 6500); + return true; + case 171: + record = new EpsgGeocentricCrsRecord(9010, "IGS05", 1247, 6500); + return true; + case 172: + record = new EpsgGeocentricCrsRecord(9015, "IGb08", 1248, 6500); + return true; + case 173: + record = new EpsgGeocentricCrsRecord(9070, "NAD83(MARP00)", 1221, 6500); + return true; + case 174: + record = new EpsgGeocentricCrsRecord(9073, "NAD83(PACP00)", 1249, 6500); + return true; + case 175: + record = new EpsgGeocentricCrsRecord(9138, "ETRS89-XKX [KOSOVAREF01]", 1251, 6500); + return true; + case 176: + record = new EpsgGeocentricCrsRecord(9146, "SIRGAS-Chile 2013", 1252, 6500); + return true; + case 177: + record = new EpsgGeocentricCrsRecord(9151, "SIRGAS-Chile 2016", 1253, 6500); + return true; + case 178: + record = new EpsgGeocentricCrsRecord(9266, "MGI", 6312, 6500); + return true; + case 179: + record = new EpsgGeocentricCrsRecord(9292, "ONGD17", 1263, 6500); + return true; + case 180: + record = new EpsgGeocentricCrsRecord(9307, "ATRF2014", 1291, 6500); + return true; + case 181: + record = new EpsgGeocentricCrsRecord(9331, "KSA-GRF17", 1268, 6500); + return true; + case 182: + record = new EpsgGeocentricCrsRecord(9378, "IGb14", 1272, 6500); + return true; + case 183: + record = new EpsgGeocentricCrsRecord(9468, "SRGI2013", 1293, 6500); + return true; + case 184: + record = new EpsgGeocentricCrsRecord(9545, "LTF2004(G)", 1295, 6500); + return true; + case 185: + record = new EpsgGeocentricCrsRecord(9694, "REDGEOMIN", 1304, 6500); + return true; + case 186: + record = new EpsgGeocentricCrsRecord(9700, "ETRS89-POL [PL-ETRF2000]", 1305, 6500); + return true; + case 187: + record = new EpsgGeocentricCrsRecord(9753, "WGS 84 (G2139)", 1309, 6500); + return true; + case 188: + record = new EpsgGeocentricCrsRecord(9775, "ETRS89-FRA [RGF93 v2]", 1312, 6500); + return true; + case 189: + record = new EpsgGeocentricCrsRecord(9780, "ETRS89-FRA [RGF93 v2b]", 1313, 6500); + return true; + case 190: + record = new EpsgGeocentricCrsRecord(9892, "LUREF", 6181, 6500); + return true; + case 191: + record = new EpsgGeocentricCrsRecord(9988, "ITRF2020", 1322, 6500); + return true; + case 192: + record = new EpsgGeocentricCrsRecord(10176, "IGS20", 1333, 6500); + return true; + case 193: + record = new EpsgGeocentricCrsRecord(10282, "ETRS89-DEU [ETRS89/DREF91/2016]", 1353, 6500); + return true; + case 194: + record = new EpsgGeocentricCrsRecord(10297, "RGSH2020", 1355, 6500); + return true; + case 195: + record = new EpsgGeocentricCrsRecord(10303, "ETRS89-LVA [LKS-2020]", 1356, 6500); + return true; + case 196: + record = new EpsgGeocentricCrsRecord(10308, "RGNC15", 1357, 6500); + return true; + case 197: + record = new EpsgGeocentricCrsRecord(10326, "ETRS89-BIH [BH_ETRS89]", 1358, 6500); + return true; + case 198: + record = new EpsgGeocentricCrsRecord(10412, "NAD83(CSRS)v8", 1365, 6500); + return true; + case 199: + record = new EpsgGeocentricCrsRecord(10473, "BBT2000", 1367, 6500); + return true; + case 200: + record = new EpsgGeocentricCrsRecord(10569, "ETRF2020", 1382, 6500); + return true; + case 201: + record = new EpsgGeocentricCrsRecord(10604, "WGS 84 (G2296)", 1383, 6500); + return true; + case 202: + record = new EpsgGeocentricCrsRecord(10634, "Saba", 1379, 6500); + return true; + case 203: + record = new EpsgGeocentricCrsRecord(10637, "BES2020 Saba", 1380, 6500); + return true; + case 204: + record = new EpsgGeocentricCrsRecord(10669, "RGM23", 1389, 6500); + return true; + case 205: + record = new EpsgGeocentricCrsRecord(10688, "ETRS89-FIN [EUREF-FIN]", 1391, 6500); + return true; + case 206: + record = new EpsgGeocentricCrsRecord(10723, "UZGD2024", 1392, 6500); + return true; + case 207: + record = new EpsgGeocentricCrsRecord(10734, "Sint Eustatius", 1393, 6500); + return true; + case 208: + record = new EpsgGeocentricCrsRecord(10737, "BES2020 Sint Eustatius", 1394, 6500); + return true; + case 209: + record = new EpsgGeocentricCrsRecord(10760, "Bonaire 2004", 1397, 6500); + return true; + case 210: + record = new EpsgGeocentricCrsRecord(10779, "ITRF2020-u2023", 1399, 6500); + return true; + case 211: + record = new EpsgGeocentricCrsRecord(10783, "IGb20", 1400, 6500); + return true; + case 212: + record = new EpsgGeocentricCrsRecord(10789, "UGRF", 1401, 6500); + return true; + case 213: + record = new EpsgGeocentricCrsRecord(10798, "LibRef21", 1402, 6500); + return true; + case 214: + record = new EpsgGeocentricCrsRecord(10805, "NKG_ETRF14", 1403, 6500); + return true; + case 215: + record = new EpsgGeocentricCrsRecord(10829, "Georgia Geodetic Datum", 1404, 6500); + return true; + case 216: + record = new EpsgGeocentricCrsRecord(10873, "ETRS89-NOR [EUREF89]", 1407, 6500); + return true; + case 217: + record = new EpsgGeocentricCrsRecord(10890, "ETRS89-DNK", 1412, 6500); + return true; + case 218: + record = new EpsgGeocentricCrsRecord(10908, "CSRN epoch 2025.0 (NAD83 2011)", 1414, 6500); + return true; + case 219: + record = new EpsgGeocentricCrsRecord(10939, "QazTRF-23", 1417, 6500); + return true; + case 220: + record = new EpsgGeocentricCrsRecord(10950, "CSRN epoch 2025.0 (ITRF2020)", 1418, 6500); + return true; + case 221: + record = new EpsgGeocentricCrsRecord(10954, "GR96(1996)", 1420, 6500); + return true; + case 222: + record = new EpsgGeocentricCrsRecord(10957, "GR96(2021)", 1419, 6500); + return true; + case 223: + record = new EpsgGeocentricCrsRecord(10966, "NATRF2022", 1422, 6500); + return true; + case 224: + record = new EpsgGeocentricCrsRecord(10991, "Xrail84", 1408, 6500); + return true; + case 225: + record = new EpsgGeocentricCrsRecord(11007, "ETRS89-GBR [OSNet v2009]", 1425, 6500); + return true; + case 226: + record = new EpsgGeocentricCrsRecord(11029, "SRGI2013 epoch 2021.0", 1426, 6500); + return true; + case 227: + record = new EpsgGeocentricCrsRecord(11035, "ETRS89-NLD [AGRS2010]", 1427, 6500); + return true; + case 228: + record = new EpsgGeocentricCrsRecord(11041, "CR-SIRGAS epoch 2019.24", 1428, 6500); + return true; + case 229: + record = new EpsgGeocentricCrsRecord(11045, "ETRS89-ALB [KRGJSH 2010]", 1429, 6500); + return true; + case 230: + record = new EpsgGeocentricCrsRecord(11051, "ETRS89-ALB [CORS]", 1430, 6500); + return true; + case 231: + record = new EpsgGeocentricCrsRecord(11055, "ETRS89-AUT [2002]", 1431, 6500); + return true; + case 232: + record = new EpsgGeocentricCrsRecord(11061, "ETRS89-BEL [BEREF2002]", 1432, 6500); + return true; + case 233: + record = new EpsgGeocentricCrsRecord(11068, "ETRS89-CZE [2007]", 1433, 6500); + return true; + case 234: + record = new EpsgGeocentricCrsRecord(11074, "ETRS89-SVK [SKTRF09]", 1434, 6500); + return true; + case 235: + record = new EpsgGeocentricCrsRecord(11077, "ETRS89-SVK [SKTRF2022]", 1435, 6500); + return true; + case 236: + record = new EpsgGeocentricCrsRecord(11085, "ETRS89-FRO [2008]", 1436, 6500); + return true; + case 237: + record = new EpsgGeocentricCrsRecord(11091, "ETRS89-GRC [HTRS07]", 1437, 6500); + return true; + case 238: + record = new EpsgGeocentricCrsRecord(11097, "ETRS89-MKD [EUREF-MAK2010]", 1438, 6500); + return true; + case 239: + record = new EpsgGeocentricCrsRecord(11106, "ETRS89-PRT [1995]", 1439, 6500); + return true; + case 240: + record = new EpsgGeocentricCrsRecord(11112, "ETRS89-ROU [ETRF2000]", 1440, 6500); + return true; + case 241: + record = new EpsgGeocentricCrsRecord(11126, "ETRS89-ESP [ERGNSS]", 1442, 6500); + return true; + case 242: + record = new EpsgGeocentricCrsRecord(11129, "ETRS89-ESP [REGENTE]", 1441, 6500); + return true; + case 243: + record = new EpsgGeocentricCrsRecord(11161, "ETRS89-HUN [ETRF2000]", 1444, 6500); + return true; + case 244: + record = new EpsgGeocentricCrsRecord(11187, "ETRS89-HRV [CROPOS]", 1445, 6500); + return true; + case 245: + record = new EpsgGeocentricCrsRecord(11197, "ETRS89-DEU [ETRS89/DREF91/2025]", 1446, 6500); + return true; + case 246: + record = new EpsgGeocentricCrsRecord(11213, "ETRS89-BEL [BEREF2011]", 1447, 6500); + return true; + case 247: + record = new EpsgGeocentricCrsRecord(11222, "ETRS89-CHE [CHTRF95]", 1449, 6500); + return true; + case 248: + record = new EpsgGeocentricCrsRecord(11224, "DrukRef23", 1448, 6500); + return true; + case 249: + record = new EpsgGeocentricCrsRecord(11391, "ETRS89-LUX [ETRF2000]", 1453, 6500); + return true; + case 250: + record = new EpsgGeocentricCrsRecord(20039, "SIRGAS-Chile 2021", 1327, 6500); + return true; + case 251: + record = new EpsgGeocentricCrsRecord(20044, "MAGNA-SIRGAS 2018", 1329, 6500); + return true; + default: + record = default; + return false; + } + } + + internal static bool TryGetVerticalCrs(int index, out EpsgVerticalCrsRecord record) + { + switch (index) + { + case 0: + record = new EpsgVerticalCrsRecord(3855, "EGM2008 height", 1027, 6499); + return true; + case 1: + record = new EpsgVerticalCrsRecord(3886, "Fao 1979 height", 1028, 6499); + return true; + case 2: + record = new EpsgVerticalCrsRecord(3900, "N2000 height", 1030, 6499); + return true; + case 3: + record = new EpsgVerticalCrsRecord(4440, "NZVD2009 height", 1039, 6499); + return true; + case 4: + record = new EpsgVerticalCrsRecord(4458, "Dunedin-Bluff 1960 height", 1040, 6499); + return true; + case 5: + record = new EpsgVerticalCrsRecord(5193, "KVD1964 height", 1049, 6499); + return true; + case 6: + record = new EpsgVerticalCrsRecord(5195, "Trieste height", 1050, 6499); + return true; + case 7: + record = new EpsgVerticalCrsRecord(5214, "Genoa 1942 height", 1051, 6499); + return true; + case 8: + record = new EpsgVerticalCrsRecord(5237, "SLVD height", 1054, 6499); + return true; + case 9: + record = new EpsgVerticalCrsRecord(5317, "FVR09 height", 1059, 6499); + return true; + case 10: + record = new EpsgVerticalCrsRecord(5597, "FCSVR10 height", 1079, 6499); + return true; + case 11: + record = new EpsgVerticalCrsRecord(5600, "NGPF height", 5195, 6499); + return true; + case 12: + record = new EpsgVerticalCrsRecord(5601, "IGN 1966 height", 5196, 6499); + return true; + case 13: + record = new EpsgVerticalCrsRecord(5602, "Moorea SAU 1981 height", 5197, 6499); + return true; + case 14: + record = new EpsgVerticalCrsRecord(5603, "Raiatea SAU 2001 height", 5198, 6499); + return true; + case 15: + record = new EpsgVerticalCrsRecord(5604, "Maupiti SAU 2001 height", 5199, 6499); + return true; + case 16: + record = new EpsgVerticalCrsRecord(5605, "Huahine SAU 2001 height", 5200, 6499); + return true; + case 17: + record = new EpsgVerticalCrsRecord(5606, "Tahaa SAU 2001 height", 5201, 6499); + return true; + case 18: + record = new EpsgVerticalCrsRecord(5607, "Bora Bora SAU 2001 height", 5202, 6499); + return true; + case 19: + record = new EpsgVerticalCrsRecord(5608, "IGLD 1955 height", 5204, 6499); + return true; + case 20: + record = new EpsgVerticalCrsRecord(5609, "IGLD 1985 height", 5205, 6499); + return true; + case 21: + record = new EpsgVerticalCrsRecord(5610, "HVRS71 height", 5207, 6499); + return true; + case 22: + record = new EpsgVerticalCrsRecord(5611, "Caspian height", 5106, 6499); + return true; + case 23: + record = new EpsgVerticalCrsRecord(5613, "RH2000 height", 5208, 6499); + return true; + case 24: + record = new EpsgVerticalCrsRecord(5615, "RH00 height", 5209, 6499); + return true; + case 25: + record = new EpsgVerticalCrsRecord(5616, "IGN 1988 LS height", 5210, 6499); + return true; + case 26: + record = new EpsgVerticalCrsRecord(5617, "IGN 1988 MG height", 5211, 6499); + return true; + case 27: + record = new EpsgVerticalCrsRecord(5618, "IGN 1992 LD height", 5212, 6499); + return true; + case 28: + record = new EpsgVerticalCrsRecord(5619, "IGN 1988 SB height", 5213, 6499); + return true; + case 29: + record = new EpsgVerticalCrsRecord(5620, "IGN 1988 SM height", 5214, 6499); + return true; + case 30: + record = new EpsgVerticalCrsRecord(5621, "EVRF2007 height", 5215, 6499); + return true; + case 31: + record = new EpsgVerticalCrsRecord(5701, "ODN height", 5101, 6499); + return true; + case 32: + record = new EpsgVerticalCrsRecord(5702, "NGVD29 height (ftUS)", 5102, 6497); + return true; + case 33: + record = new EpsgVerticalCrsRecord(5703, "NAVD88 height", 5103, 6499); + return true; + case 34: + record = new EpsgVerticalCrsRecord(5705, "Baltic 1977 height", 5105, 6499); + return true; + case 35: + record = new EpsgVerticalCrsRecord(5709, "NAP height", 5109, 6499); + return true; + case 36: + record = new EpsgVerticalCrsRecord(5710, "Ostend height", 5110, 6499); + return true; + case 37: + record = new EpsgVerticalCrsRecord(5711, "AHD height", 5111, 6499); + return true; + case 38: + record = new EpsgVerticalCrsRecord(5712, "AHD (Tasmania) height", 5112, 6499); + return true; + case 39: + record = new EpsgVerticalCrsRecord(5713, "CGVD28 height", 5114, 6499); + return true; + case 40: + record = new EpsgVerticalCrsRecord(5714, "MSL height", 5100, 6499); + return true; + case 41: + record = new EpsgVerticalCrsRecord(5716, "Piraeus height", 5115, 6499); + return true; + case 42: + record = new EpsgVerticalCrsRecord(5717, "N60 height", 5116, 6499); + return true; + case 43: + record = new EpsgVerticalCrsRecord(5718, "RH70 height", 5117, 6499); + return true; + case 44: + record = new EpsgVerticalCrsRecord(5719, "NGF Lallemand height", 5118, 6499); + return true; + case 45: + record = new EpsgVerticalCrsRecord(5720, "NGF-IGN69 height", 5119, 6499); + return true; + case 46: + record = new EpsgVerticalCrsRecord(5721, "NGF-IGN78 height", 5120, 6499); + return true; + case 47: + record = new EpsgVerticalCrsRecord(5722, "Maputo height", 5121, 6499); + return true; + case 48: + record = new EpsgVerticalCrsRecord(5723, "JSLD69 height", 5122, 6499); + return true; + case 49: + record = new EpsgVerticalCrsRecord(5724, "PHD93 height", 5123, 6499); + return true; + case 50: + record = new EpsgVerticalCrsRecord(5725, "Fahud HD height", 5124, 6499); + return true; + case 51: + record = new EpsgVerticalCrsRecord(5726, "Ha Tien 1960 height", 5125, 6499); + return true; + case 52: + record = new EpsgVerticalCrsRecord(5727, "Hon Dau 1992 height", 5126, 6499); + return true; + case 53: + record = new EpsgVerticalCrsRecord(5728, "LN02 height", 5127, 6499); + return true; + case 54: + record = new EpsgVerticalCrsRecord(5729, "LHN95 height", 5128, 6499); + return true; + case 55: + record = new EpsgVerticalCrsRecord(5730, "EVRF2000 height", 5129, 6499); + return true; + case 56: + record = new EpsgVerticalCrsRecord(5731, "Malin Head height", 5130, 6499); + return true; + case 57: + record = new EpsgVerticalCrsRecord(5732, "Belfast height", 5131, 6499); + return true; + case 58: + record = new EpsgVerticalCrsRecord(5733, "DNN height", 5132, 6499); + return true; + case 59: + record = new EpsgVerticalCrsRecord(5735, "Black Sea height", 5134, 6499); + return true; + case 60: + record = new EpsgVerticalCrsRecord(5736, "Yellow Sea 1956 height", 5104, 6499); + return true; + case 61: + record = new EpsgVerticalCrsRecord(5737, "Yellow Sea 1985 height", 5137, 6499); + return true; + case 62: + record = new EpsgVerticalCrsRecord(5738, "HKPD height", 5135, 6499); + return true; + case 63: + record = new EpsgVerticalCrsRecord(5739, "HKCD depth", 5136, 6498); + return true; + case 64: + record = new EpsgVerticalCrsRecord(5740, "ODN Orkney height", 5138, 6499); + return true; + case 65: + record = new EpsgVerticalCrsRecord(5741, "Fair Isle height", 5139, 6499); + return true; + case 66: + record = new EpsgVerticalCrsRecord(5742, "Lerwick height", 5140, 6499); + return true; + case 67: + record = new EpsgVerticalCrsRecord(5743, "Foula height", 5141, 6499); + return true; + case 68: + record = new EpsgVerticalCrsRecord(5744, "Sule Skerry height", 5142, 6499); + return true; + case 69: + record = new EpsgVerticalCrsRecord(5745, "North Rona height", 5143, 6499); + return true; + case 70: + record = new EpsgVerticalCrsRecord(5746, "Stornoway height", 5144, 6499); + return true; + case 71: + record = new EpsgVerticalCrsRecord(5747, "St. Kilda height", 5145, 6499); + return true; + case 72: + record = new EpsgVerticalCrsRecord(5748, "Flannan Isles height", 5146, 6499); + return true; + case 73: + record = new EpsgVerticalCrsRecord(5749, "St. Marys height", 5147, 6499); + return true; + case 74: + record = new EpsgVerticalCrsRecord(5750, "Douglas height", 5148, 6499); + return true; + case 75: + record = new EpsgVerticalCrsRecord(5751, "Fao height", 5149, 6499); + return true; + case 76: + record = new EpsgVerticalCrsRecord(5752, "Bandar Abbas height", 5150, 6499); + return true; + case 77: + record = new EpsgVerticalCrsRecord(5753, "NGNC69 height", 5151, 6499); + return true; + case 78: + record = new EpsgVerticalCrsRecord(5754, "Poolbeg height (ft(Br36))", 5152, 6496); + return true; + case 79: + record = new EpsgVerticalCrsRecord(5755, "NGG1977 height", 5153, 6499); + return true; + case 80: + record = new EpsgVerticalCrsRecord(5756, "Martinique 1987 height", 5154, 6499); + return true; + case 81: + record = new EpsgVerticalCrsRecord(5757, "Guadeloupe 1988 height", 5155, 6499); + return true; + case 82: + record = new EpsgVerticalCrsRecord(5758, "Reunion 1989 height", 5156, 6499); + return true; + case 83: + record = new EpsgVerticalCrsRecord(5759, "Auckland 1946 height", 5157, 6499); + return true; + case 84: + record = new EpsgVerticalCrsRecord(5760, "Bluff 1955 height", 5158, 6499); + return true; + case 85: + record = new EpsgVerticalCrsRecord(5761, "Dunedin 1958 height", 5159, 6499); + return true; + case 86: + record = new EpsgVerticalCrsRecord(5762, "Gisborne 1926 height", 5160, 6499); + return true; + case 87: + record = new EpsgVerticalCrsRecord(5763, "Lyttelton 1937 height", 5161, 6499); + return true; + case 88: + record = new EpsgVerticalCrsRecord(5764, "Moturiki 1953 height", 5162, 6499); + return true; + case 89: + record = new EpsgVerticalCrsRecord(5765, "Napier 1962 height", 5163, 6499); + return true; + case 90: + record = new EpsgVerticalCrsRecord(5766, "Nelson 1955 height", 5164, 6499); + return true; + case 91: + record = new EpsgVerticalCrsRecord(5767, "One Tree Point 1964 height", 5165, 6499); + return true; + case 92: + record = new EpsgVerticalCrsRecord(5768, "Tararu 1952 height", 5166, 6499); + return true; + case 93: + record = new EpsgVerticalCrsRecord(5769, "Taranaki 1970 height", 5167, 6499); + return true; + case 94: + record = new EpsgVerticalCrsRecord(5770, "Wellington 1953 height", 5168, 6499); + return true; + case 95: + record = new EpsgVerticalCrsRecord(5771, "Chatham Island 1959 height", 5169, 6499); + return true; + case 96: + record = new EpsgVerticalCrsRecord(5772, "Stewart Island 1977 height", 5170, 6499); + return true; + case 97: + record = new EpsgVerticalCrsRecord(5773, "EGM96 height", 5171, 6499); + return true; + case 98: + record = new EpsgVerticalCrsRecord(5774, "NG95 height", 5172, 6499); + return true; + case 99: + record = new EpsgVerticalCrsRecord(5775, "Antalya height", 5173, 6499); + return true; + case 100: + record = new EpsgVerticalCrsRecord(5776, "NN54 height", 5174, 6499); + return true; + case 101: + record = new EpsgVerticalCrsRecord(5777, "Durres height", 5175, 6499); + return true; + case 102: + record = new EpsgVerticalCrsRecord(5778, "GHA height", 5176, 6499); + return true; + case 103: + record = new EpsgVerticalCrsRecord(5779, "SVS2000 height", 5177, 6499); + return true; + case 104: + record = new EpsgVerticalCrsRecord(5780, "Cascais height", 5178, 6499); + return true; + case 105: + record = new EpsgVerticalCrsRecord(5781, "Constanta height", 5179, 6499); + return true; + case 106: + record = new EpsgVerticalCrsRecord(5782, "Alicante height", 5180, 6499); + return true; + case 107: + record = new EpsgVerticalCrsRecord(5783, "DHHN92 height", 5181, 6499); + return true; + case 108: + record = new EpsgVerticalCrsRecord(5784, "DHHN85 height", 5182, 6499); + return true; + case 109: + record = new EpsgVerticalCrsRecord(5785, "SNN76 height", 5183, 6499); + return true; + case 110: + record = new EpsgVerticalCrsRecord(5786, "Baltic 1982 height", 5184, 6499); + return true; + case 111: + record = new EpsgVerticalCrsRecord(5787, "EOMA 1980 height", 5185, 6499); + return true; + case 112: + record = new EpsgVerticalCrsRecord(5788, "Kuwait PWD height", 5186, 6499); + return true; + case 113: + record = new EpsgVerticalCrsRecord(5790, "KOC CD height", 5188, 6499); + return true; + case 114: + record = new EpsgVerticalCrsRecord(5791, "NGC 1948 height", 5189, 6499); + return true; + case 115: + record = new EpsgVerticalCrsRecord(5792, "Danger 1950 height", 5190, 6499); + return true; + case 116: + record = new EpsgVerticalCrsRecord(5793, "Mayotte 1950 height", 5191, 6499); + return true; + case 117: + record = new EpsgVerticalCrsRecord(5794, "Martinique 1955 height", 5192, 6499); + return true; + case 118: + record = new EpsgVerticalCrsRecord(5795, "Guadeloupe 1951 height", 5193, 6499); + return true; + case 119: + record = new EpsgVerticalCrsRecord(5796, "Lagos 1955 height", 5194, 6499); + return true; + case 120: + record = new EpsgVerticalCrsRecord(5797, "AIOC95 height", 5133, 6499); + return true; + case 121: + record = new EpsgVerticalCrsRecord(5798, "EGM84 height", 5203, 6499); + return true; + case 122: + record = new EpsgVerticalCrsRecord(5829, "Instantaneous Water Level height", 5113, 6499); + return true; + case 123: + record = new EpsgVerticalCrsRecord(5843, "Ras Ghumays height", 1146, 6499); + return true; + case 124: + record = new EpsgVerticalCrsRecord(5861, "LAT depth", 1080, 6498); + return true; + case 125: + record = new EpsgVerticalCrsRecord(5862, "LLWLT depth", 1083, 6498); + return true; + case 126: + record = new EpsgVerticalCrsRecord(5863, "ISLW depth", 1085, 6498); + return true; + case 127: + record = new EpsgVerticalCrsRecord(5864, "MLLWS depth", 1086, 6498); + return true; + case 128: + record = new EpsgVerticalCrsRecord(5865, "MLWS depth", 1087, 6498); + return true; + case 129: + record = new EpsgVerticalCrsRecord(5866, "MLLW depth", 1089, 6498); + return true; + case 130: + record = new EpsgVerticalCrsRecord(5867, "MLW depth", 1091, 6498); + return true; + case 131: + record = new EpsgVerticalCrsRecord(5868, "MHW height", 1092, 6499); + return true; + case 132: + record = new EpsgVerticalCrsRecord(5869, "MHHW height", 1090, 6499); + return true; + case 133: + record = new EpsgVerticalCrsRecord(5870, "MHWS height", 1088, 6499); + return true; + case 134: + record = new EpsgVerticalCrsRecord(5871, "HHWLT height", 1084, 6499); + return true; + case 135: + record = new EpsgVerticalCrsRecord(5872, "HAT height", 1082, 6499); + return true; + case 136: + record = new EpsgVerticalCrsRecord(5873, "Low Water depth", 1093, 6498); + return true; + case 137: + record = new EpsgVerticalCrsRecord(5874, "High Water height", 1094, 6499); + return true; + case 138: + record = new EpsgVerticalCrsRecord(5941, "NN2000:2018 height", 1096, 6499); + return true; + case 139: + record = new EpsgVerticalCrsRecord(6130, "GCVD54 height (ft)", 1097, 1030); + return true; + case 140: + record = new EpsgVerticalCrsRecord(6131, "LCVD61 height (ft)", 1098, 1030); + return true; + case 141: + record = new EpsgVerticalCrsRecord(6132, "CBVD61 height (ft)", 1099, 1030); + return true; + case 142: + record = new EpsgVerticalCrsRecord(6178, "Cais da Pontinha height", 1101, 6499); + return true; + case 143: + record = new EpsgVerticalCrsRecord(6179, "Cais da Vila height", 1102, 6499); + return true; + case 144: + record = new EpsgVerticalCrsRecord(6180, "Cais das Velas height", 1103, 6499); + return true; + case 145: + record = new EpsgVerticalCrsRecord(6181, "Horta height", 1104, 6499); + return true; + case 146: + record = new EpsgVerticalCrsRecord(6182, "Cais da Madalena height", 1105, 6499); + return true; + case 147: + record = new EpsgVerticalCrsRecord(6183, "Santa Cruz da Graciosa height", 1106, 6499); + return true; + case 148: + record = new EpsgVerticalCrsRecord(6184, "Cais da Figueirinha height", 1107, 6499); + return true; + case 149: + record = new EpsgVerticalCrsRecord(6185, "Santa Cruz das Flores height", 1108, 6499); + return true; + case 150: + record = new EpsgVerticalCrsRecord(6186, "Cais da Vila do Porto height", 1109, 6499); + return true; + case 151: + record = new EpsgVerticalCrsRecord(6187, "Ponta Delgada height", 1110, 6499); + return true; + case 152: + record = new EpsgVerticalCrsRecord(6638, "Tutuila 1962 height", 1121, 6499); + return true; + case 153: + record = new EpsgVerticalCrsRecord(6639, "Guam 1963 height", 1122, 6499); + return true; + case 154: + record = new EpsgVerticalCrsRecord(6640, "NMVD03 height", 1119, 6499); + return true; + case 155: + record = new EpsgVerticalCrsRecord(6641, "PRVD02 height", 1123, 6499); + return true; + case 156: + record = new EpsgVerticalCrsRecord(6642, "VIVD09 height", 1124, 6499); + return true; + case 157: + record = new EpsgVerticalCrsRecord(6643, "ASVD02 height", 1125, 6499); + return true; + case 158: + record = new EpsgVerticalCrsRecord(6644, "GUVD04 height", 1126, 6499); + return true; + case 159: + record = new EpsgVerticalCrsRecord(6647, "CGVD2013(CGG2013) height", 1127, 6499); + return true; + case 160: + record = new EpsgVerticalCrsRecord(6693, "JSLD72 height", 1129, 6499); + return true; + case 161: + record = new EpsgVerticalCrsRecord(6694, "JGD2000 (vertical) height", 1130, 6499); + return true; + case 162: + record = new EpsgVerticalCrsRecord(6695, "JGD2011 (vertical) height", 1131, 6499); + return true; + case 163: + record = new EpsgVerticalCrsRecord(6916, "SHD height", 1140, 6499); + return true; + case 164: + record = new EpsgVerticalCrsRecord(7446, "Famagusta 1960 height", 1148, 6499); + return true; + case 165: + record = new EpsgVerticalCrsRecord(7447, "PNG08 height", 1149, 6499); + return true; + case 166: + record = new EpsgVerticalCrsRecord(7651, "Kumul 34 height", 1150, 6499); + return true; + case 167: + record = new EpsgVerticalCrsRecord(7652, "Kiunga height", 1151, 6499); + return true; + case 168: + record = new EpsgVerticalCrsRecord(7699, "DHHN12 height", 1161, 6499); + return true; + case 169: + record = new EpsgVerticalCrsRecord(7700, "Latvia 2000 height", 1162, 6499); + return true; + case 170: + record = new EpsgVerticalCrsRecord(7707, "ODN (Offshore) height", 1164, 6499); + return true; + case 171: + record = new EpsgVerticalCrsRecord(7832, "POM96 height", 1171, 6499); + return true; + case 172: + record = new EpsgVerticalCrsRecord(7837, "DHHN2016 height", 1170, 6499); + return true; + case 173: + record = new EpsgVerticalCrsRecord(7839, "NZVD2016 height", 1169, 6499); + return true; + case 174: + record = new EpsgVerticalCrsRecord(7841, "POM08 height", 1172, 6499); + return true; + case 175: + record = new EpsgVerticalCrsRecord(7888, "Jamestown 1971 height", 1175, 6499); + return true; + case 176: + record = new EpsgVerticalCrsRecord(7889, "St. Helena Tritan 2011 height", 1176, 6499); + return true; + case 177: + record = new EpsgVerticalCrsRecord(7890, "SHVD2015 height", 1177, 6499); + return true; + case 178: + record = new EpsgVerticalCrsRecord(7979, "KOC WD height", 5187, 6499); + return true; + case 179: + record = new EpsgVerticalCrsRecord(8089, "ISH2004 height", 1190, 6499); + return true; + case 180: + record = new EpsgVerticalCrsRecord(8266, "GVR2000 height", 1199, 6499); + return true; + case 181: + record = new EpsgVerticalCrsRecord(8267, "GVR2016 height", 1200, 6499); + return true; + case 182: + record = new EpsgVerticalCrsRecord(8357, "Baltic 1957 height", 1202, 6499); + return true; + case 183: + record = new EpsgVerticalCrsRecord(8378, "EPSG example wellbore local vertical CRS", 1205, 1049); + return true; + case 184: + record = new EpsgVerticalCrsRecord(8434, "Macao height", 1210, 6499); + return true; + case 185: + record = new EpsgVerticalCrsRecord(8675, "N43 height", 1213, 6499); + return true; + case 186: + record = new EpsgVerticalCrsRecord(8690, "SVS2010 height", 1215, 6499); + return true; + case 187: + record = new EpsgVerticalCrsRecord(8691, "SRB_VRS12 height", 1216, 6499); + return true; + case 188: + record = new EpsgVerticalCrsRecord(8841, "MVGC height", 1219, 6499); + return true; + case 189: + record = new EpsgVerticalCrsRecord(8881, "Vienna height", 1267, 6499); + return true; + case 190: + record = new EpsgVerticalCrsRecord(8897, "EPSG example wellbore local vertical CRS (ft)", 1205, 1050); + return true; + case 191: + record = new EpsgVerticalCrsRecord(8904, "TWVD 2001 height", 1224, 6499); + return true; + case 192: + record = new EpsgVerticalCrsRecord(8911, "DACR52 height", 1226, 6499); + return true; + case 193: + record = new EpsgVerticalCrsRecord(9130, "IGN 2008 LD height", 1250, 6499); + return true; + case 194: + record = new EpsgVerticalCrsRecord(9245, "CGVD2013a(2010) height", 1256, 6499); + return true; + case 195: + record = new EpsgVerticalCrsRecord(9255, "SRVN16 height", 1260, 6499); + return true; + case 196: + record = new EpsgVerticalCrsRecord(9274, "EVRF2000 Austria height", 1261, 6499); + return true; + case 197: + record = new EpsgVerticalCrsRecord(9279, "SA LLD height", 1262, 6499); + return true; + case 198: + record = new EpsgVerticalCrsRecord(9287, "LAT-NLD depth", 1290, 6498); + return true; + case 199: + record = new EpsgVerticalCrsRecord(9288, "MSL-NLD depth", 1270, 6498); + return true; + case 200: + record = new EpsgVerticalCrsRecord(9303, "HS2-VRF height", 1265, 6499); + return true; + case 201: + record = new EpsgVerticalCrsRecord(9335, "KSA-VRF14 height", 1269, 6499); + return true; + case 202: + record = new EpsgVerticalCrsRecord(9351, "NGNC08 height", 1255, 6499); + return true; + case 203: + record = new EpsgVerticalCrsRecord(9389, "EVRF2019 height", 1274, 6499); + return true; + case 204: + record = new EpsgVerticalCrsRecord(9390, "EVRF2019 mean-tide height", 1287, 6499); + return true; + case 205: + record = new EpsgVerticalCrsRecord(9392, "Mallorca height", 1275, 6499); + return true; + case 206: + record = new EpsgVerticalCrsRecord(9393, "Menorca height", 1276, 6499); + return true; + case 207: + record = new EpsgVerticalCrsRecord(9394, "Ibiza height", 1277, 6499); + return true; + case 208: + record = new EpsgVerticalCrsRecord(9395, "Lanzarote height", 1278, 6499); + return true; + case 209: + record = new EpsgVerticalCrsRecord(9396, "Fuerteventura height", 1279, 6499); + return true; + case 210: + record = new EpsgVerticalCrsRecord(9397, "Gran Canaria height", 1280, 6499); + return true; + case 211: + record = new EpsgVerticalCrsRecord(9398, "Tenerife height", 1281, 6499); + return true; + case 212: + record = new EpsgVerticalCrsRecord(9399, "La Gomera height", 1282, 6499); + return true; + case 213: + record = new EpsgVerticalCrsRecord(9400, "La Palma height", 1283, 6499); + return true; + case 214: + record = new EpsgVerticalCrsRecord(9401, "El Hierro height", 1284, 6499); + return true; + case 215: + record = new EpsgVerticalCrsRecord(9402, "Ceuta 2 height", 1285, 6499); + return true; + case 216: + record = new EpsgVerticalCrsRecord(9458, "AVWS height", 1292, 6499); + return true; + case 217: + record = new EpsgVerticalCrsRecord(9471, "INAGeoid2020 v1 height", 1294, 6499); + return true; + case 218: + record = new EpsgVerticalCrsRecord(9650, "Baltic 1986 height", 1296, 6499); + return true; + case 219: + record = new EpsgVerticalCrsRecord(9651, "PL-EVRF2007 height", 1297, 6499); + return true; + case 220: + record = new EpsgVerticalCrsRecord(9663, "EH2000 height", 1298, 6499); + return true; + case 221: + record = new EpsgVerticalCrsRecord(9666, "LAS07 height", 1299, 6499); + return true; + case 222: + record = new EpsgVerticalCrsRecord(9669, "BGS2005 height", 1300, 6499); + return true; + case 223: + record = new EpsgVerticalCrsRecord(9672, "CD Norway depth", 1301, 6498); + return true; + case 224: + record = new EpsgVerticalCrsRecord(9675, "Pago Pago 2020 height", 1302, 6499); + return true; + case 225: + record = new EpsgVerticalCrsRecord(9681, "NVD 1992 height", 1303, 6499); + return true; + case 226: + record = new EpsgVerticalCrsRecord(9721, "Catania 1965 height", 1306, 6499); + return true; + case 227: + record = new EpsgVerticalCrsRecord(9722, "Cagliari 1956 height", 1307, 6499); + return true; + case 228: + record = new EpsgVerticalCrsRecord(9923, "GNTRANS height", 1316, 6499); + return true; + case 229: + record = new EpsgVerticalCrsRecord(9927, "GNTRANS2016 height", 1318, 6499); + return true; + case 230: + record = new EpsgVerticalCrsRecord(10150, "MSL UK & Ireland VORF08 depth", 1330, 6498); + return true; + case 231: + record = new EpsgVerticalCrsRecord(10151, "CD UK & Ireland VORF08 depth", 1331, 6498); + return true; + case 232: + record = new EpsgVerticalCrsRecord(10190, "NGA 2022 height", 1354, 6499); + return true; + case 233: + record = new EpsgVerticalCrsRecord(10349, "ZH Portugal depth", 1361, 6498); + return true; + case 234: + record = new EpsgVerticalCrsRecord(10352, "Formentera height", 1362, 6499); + return true; + case 235: + record = new EpsgVerticalCrsRecord(10353, "Alboran height", 1363, 6499); + return true; + case 236: + record = new EpsgVerticalCrsRecord(10354, "Melilla height", 1364, 6499); + return true; + case 237: + record = new EpsgVerticalCrsRecord(10482, "DVR90(2000) height", 5206, 6499); + return true; + case 238: + record = new EpsgVerticalCrsRecord(10483, "DVR90(2002) height", 1368, 6499); + return true; + case 239: + record = new EpsgVerticalCrsRecord(10484, "DVR90(2013) height", 1369, 6499); + return true; + case 240: + record = new EpsgVerticalCrsRecord(10485, "DVR90(2023) height", 1370, 6499); + return true; + case 241: + record = new EpsgVerticalCrsRecord(10547, "DKMSL(2022) depth", 1372, 6498); + return true; + case 242: + record = new EpsgVerticalCrsRecord(10548, "DKLAT(2022) depth", 1373, 6498); + return true; + case 243: + record = new EpsgVerticalCrsRecord(10549, "DKMSL(2023) depth", 1374, 6498); + return true; + case 244: + record = new EpsgVerticalCrsRecord(10550, "DKLAT(2023) depth", 1375, 6498); + return true; + case 245: + record = new EpsgVerticalCrsRecord(10565, "GLLMSL(2022) height", 1378, 6499); + return true; + case 246: + record = new EpsgVerticalCrsRecord(10588, "CGVD28(HTv2.0) height", 1384, 6499); + return true; + case 247: + record = new EpsgVerticalCrsRecord(10642, "Saba height", 1381, 6499); + return true; + case 248: + record = new EpsgVerticalCrsRecord(10649, "GLMSL(2023) depth", 1387, 6498); + return true; + case 249: + record = new EpsgVerticalCrsRecord(10650, "GLLAT(2023) depth", 1388, 6498); + return true; + case 250: + record = new EpsgVerticalCrsRecord(10678, "BSCD2000 depth", 1390, 6498); + return true; + case 251: + record = new EpsgVerticalCrsRecord(10740, "Sint Eustatius height", 1395, 6499); + return true; + case 252: + record = new EpsgVerticalCrsRecord(10763, "Bonaire height", 1398, 6499); + return true; + case 253: + record = new EpsgVerticalCrsRecord(10900, "Asse 2025 height", 1415, 6499); + return true; + case 254: + record = new EpsgVerticalCrsRecord(10918, "COH88 2025 (NAVD88) height", 1416, 6499); + return true; + case 255: + record = new EpsgVerticalCrsRecord(10989, "London Survey Grid height", 1423, 6499); + return true; + case 256: + record = new EpsgVerticalCrsRecord(10999, "SVD2024 height", 1424, 6499); + return true; + case 257: + record = new EpsgVerticalCrsRecord(11157, "IGN 2023 Mayotte height", 1443, 6499); + return true; + case 258: + record = new EpsgVerticalCrsRecord(11338, "Bhutan Vertical Datum 2022 height", 1451, 6499); + return true; + case 259: + record = new EpsgVerticalCrsRecord(11394, "NN2000:2025 height", 1454, 6499); + return true; + case 260: + record = new EpsgVerticalCrsRecord(11446, "NGFA 2022 height", 1455, 6499); + return true; + case 261: + record = new EpsgVerticalCrsRecord(20000, "SVD2006 height", 1323, 6499); + return true; + case 262: + record = new EpsgVerticalCrsRecord(20034, "CGVD2013a(2002) height", 1325, 6499); + return true; + case 263: + record = new EpsgVerticalCrsRecord(20035, "CGVD2013a(1997) height", 1326, 6499); + return true; + case 264: + record = new EpsgVerticalCrsRecord(20036, "INAGeoid2020 v2 height", 1328, 6499); + return true; + default: + record = default; + return false; + } + } + + internal static bool TryGetCompoundCrs(int index, out EpsgCompoundCrsRecord record) + { + switch (index) + { + case 0: + record = new EpsgCompoundCrsRecord(3901, "KKJ / Finland Uniform Coordinate System + N60 height", 2393, 5717); + return true; + case 1: + record = new EpsgCompoundCrsRecord(3902, "ETRS89-FIN [EUREF-FIN] / TM35FIN(N,E) + N60 height", 5048, 5717); + return true; + case 2: + record = new EpsgCompoundCrsRecord(3903, "ETRS89-FIN [EUREF-FIN] / TM35FIN(N,E) + N2000 height", 5048, 3900); + return true; + case 3: + record = new EpsgCompoundCrsRecord(5318, "ETRS89-FRO [2008] / Faroe TM + FVR09 height", 5316, 5317); + return true; + case 4: + record = new EpsgCompoundCrsRecord(5498, "NAD83 + NAVD88 height", 4269, 5703); + return true; + case 5: + record = new EpsgCompoundCrsRecord(5499, "NAD83(HARN) + NAVD88 height", 4152, 5703); + return true; + case 6: + record = new EpsgCompoundCrsRecord(5500, "NAD83(NSRS2007) + NAVD88 height", 4759, 5703); + return true; + case 7: + record = new EpsgCompoundCrsRecord(5554, "ETRS89 / UTM zone 31N + DHHN92 height", 25831, 5783); + return true; + case 8: + record = new EpsgCompoundCrsRecord(5555, "ETRS89 / UTM zone 32N + DHHN92 height", 25832, 5783); + return true; + case 9: + record = new EpsgCompoundCrsRecord(5556, "ETRS89 / UTM zone 33N + DHHN92 height", 25833, 5783); + return true; + case 10: + record = new EpsgCompoundCrsRecord(5598, "FEH2010 / Fehmarnbelt TM + FCSVR10 height", 5596, 5597); + return true; + case 11: + record = new EpsgCompoundCrsRecord(5628, "ETRS89-SWE [SWEREF 99] + RH2000 height", 4619, 5613); + return true; + case 12: + record = new EpsgCompoundCrsRecord(5698, "ETRS89-FRA [RGF93 v1] / Lambert-93 + NGF-IGN69 height", 2154, 5720); + return true; + case 13: + record = new EpsgCompoundCrsRecord(5699, "ETRS89-FRA [RGF93 v1] / Lambert-93 + NGF-IGN78 height", 2154, 5721); + return true; + case 14: + record = new EpsgCompoundCrsRecord(5707, "NTF (Paris) / Lambert zone I + NGF-IGN69 height", 27571, 5720); + return true; + case 15: + record = new EpsgCompoundCrsRecord(5708, "NTF (Paris) / Lambert zone IV + NGF-IGN78 height", 27574, 5721); + return true; + case 16: + record = new EpsgCompoundCrsRecord(5845, "ETRS89-SWE [SWEREF 99] TM + RH2000 height", 3006, 5613); + return true; + case 17: + record = new EpsgCompoundCrsRecord(5846, "ETRS89-SWE [SWEREF 99 12 00] + RH2000 height", 3007, 5613); + return true; + case 18: + record = new EpsgCompoundCrsRecord(5847, "ETRS89-SWE [SWEREF 99 13 30] + RH2000 height", 3008, 5613); + return true; + case 19: + record = new EpsgCompoundCrsRecord(5848, "ETRS89-SWE [SWEREF 99 15 00] + RH2000 height", 3009, 5613); + return true; + case 20: + record = new EpsgCompoundCrsRecord(5849, "ETRS89-SWE [SWEREF 99 16 30] + RH2000 height", 3010, 5613); + return true; + case 21: + record = new EpsgCompoundCrsRecord(5850, "ETRS89-SWE [SWEREF 99 18 00] + RH2000 height", 3011, 5613); + return true; + case 22: + record = new EpsgCompoundCrsRecord(5851, "ETRS89-SWE [SWEREF 99 14 15] + RH2000 height", 3012, 5613); + return true; + case 23: + record = new EpsgCompoundCrsRecord(5852, "ETRS89-SWE [SWEREF 99 15 45] + RH2000 height", 3013, 5613); + return true; + case 24: + record = new EpsgCompoundCrsRecord(5853, "ETRS89-SWE [SWEREF 99 17 15] + RH2000 height", 3014, 5613); + return true; + case 25: + record = new EpsgCompoundCrsRecord(5854, "ETRS89-SWE [SWEREF 99 18 45] + RH2000 height", 3015, 5613); + return true; + case 26: + record = new EpsgCompoundCrsRecord(5855, "ETRS89-SWE [SWEREF 99 20 15] + RH2000 height", 3016, 5613); + return true; + case 27: + record = new EpsgCompoundCrsRecord(5856, "ETRS89-SWE [SWEREF 99 21 45] + RH2000 height", 3017, 5613); + return true; + case 28: + record = new EpsgCompoundCrsRecord(5857, "ETRS89-SWE [SWEREF 99 23 15] + RH2000 height", 3018, 5613); + return true; + case 29: + record = new EpsgCompoundCrsRecord(5942, "ETRS89-NOR [EUREF89] + NN2000:2018 height", 10875, 5941); + return true; + case 30: + record = new EpsgCompoundCrsRecord(5945, "ETRS89-NOR [EUREF89] / NTM zone 5 + NN2000:2018 height", 5105, 5941); + return true; + case 31: + record = new EpsgCompoundCrsRecord(5946, "ETRS89-NOR [EUREF89] / NTM zone 6 + NN2000:2018 height", 5106, 5941); + return true; + case 32: + record = new EpsgCompoundCrsRecord(5947, "ETRS89-NOR [EUREF89] / NTM zone 7 + NN2000:2018 height", 5107, 5941); + return true; + case 33: + record = new EpsgCompoundCrsRecord(5948, "ETRS89-NOR [EUREF89] / NTM zone 8 + NN2000:2018 height", 5108, 5941); + return true; + case 34: + record = new EpsgCompoundCrsRecord(5949, "ETRS89-NOR [EUREF89] / NTM zone 9 + NN2000:2018 height", 5109, 5941); + return true; + case 35: + record = new EpsgCompoundCrsRecord(5950, "ETRS89-NOR [EUREF89] / NTM zone 10 + NN2000:2018 height", 5110, 5941); + return true; + case 36: + record = new EpsgCompoundCrsRecord(5951, "ETRS89-NOR [EUREF89] / NTM zone 11 + NN2000:2018 height", 5111, 5941); + return true; + case 37: + record = new EpsgCompoundCrsRecord(5952, "ETRS89-NOR [EUREF89] / NTM zone 12 + NN2000:2018 height", 5112, 5941); + return true; + case 38: + record = new EpsgCompoundCrsRecord(5953, "ETRS89-NOR [EUREF89] / NTM zone 13 + NN2000:2018 height", 5113, 5941); + return true; + case 39: + record = new EpsgCompoundCrsRecord(5954, "ETRS89-NOR [EUREF89] / NTM zone 14 + NN2000:2018 height", 5114, 5941); + return true; + case 40: + record = new EpsgCompoundCrsRecord(5955, "ETRS89-NOR [EUREF89] / NTM zone 15 + NN2000:2018 height", 5115, 5941); + return true; + case 41: + record = new EpsgCompoundCrsRecord(5956, "ETRS89-NOR [EUREF89] / NTM zone 16 + NN2000:2018 height", 5116, 5941); + return true; + case 42: + record = new EpsgCompoundCrsRecord(5957, "ETRS89-NOR [EUREF89] / NTM zone 17 + NN2000:2018 height", 5117, 5941); + return true; + case 43: + record = new EpsgCompoundCrsRecord(5958, "ETRS89-NOR [EUREF89] / NTM zone 18 + NN2000:2018 height", 5118, 5941); + return true; + case 44: + record = new EpsgCompoundCrsRecord(5959, "ETRS89-NOR [EUREF89] / NTM zone 19 + NN2000:2018 height", 5119, 5941); + return true; + case 45: + record = new EpsgCompoundCrsRecord(5960, "ETRS89-NOR [EUREF89] / NTM zone 20 + NN2000:2018 height", 5120, 5941); + return true; + case 46: + record = new EpsgCompoundCrsRecord(5961, "ETRS89-NOR [EUREF89] / NTM zone 21 + NN2000:2018 height", 5121, 5941); + return true; + case 47: + record = new EpsgCompoundCrsRecord(5962, "ETRS89-NOR [EUREF89] / NTM zone 22 + NN2000:2018 height", 5122, 5941); + return true; + case 48: + record = new EpsgCompoundCrsRecord(5963, "ETRS89-NOR [EUREF89] / NTM zone 23 + NN2000:2018 height", 5123, 5941); + return true; + case 49: + record = new EpsgCompoundCrsRecord(5964, "ETRS89-NOR [EUREF89] / NTM zone 24 + NN2000:2018 height", 5124, 5941); + return true; + case 50: + record = new EpsgCompoundCrsRecord(5965, "ETRS89-NOR [EUREF89] / NTM zone 25 + NN2000:2018 height", 5125, 5941); + return true; + case 51: + record = new EpsgCompoundCrsRecord(5966, "ETRS89-NOR [EUREF89] / NTM zone 26 + NN2000:2018 height", 5126, 5941); + return true; + case 52: + record = new EpsgCompoundCrsRecord(5967, "ETRS89-NOR [EUREF89] / NTM zone 27 + NN2000:2018 height", 5127, 5941); + return true; + case 53: + record = new EpsgCompoundCrsRecord(5968, "ETRS89-NOR [EUREF89] / NTM zone 28 + NN2000:2018 height", 5128, 5941); + return true; + case 54: + record = new EpsgCompoundCrsRecord(5969, "ETRS89-NOR [EUREF89] / NTM zone 29 + NN2000:2018 height", 5129, 5941); + return true; + case 55: + record = new EpsgCompoundCrsRecord(5970, "ETRS89-NOR [EUREF89] / NTM zone 30 + NN2000:2018 height", 5130, 5941); + return true; + case 56: + record = new EpsgCompoundCrsRecord(5971, "ETRS89-NOR [EUREF89] / UTM zone 31N + NN2000:2018 height", 11021, 5941); + return true; + case 57: + record = new EpsgCompoundCrsRecord(5972, "ETRS89-NOR [EUREF89] / UTM zone 32N + NN2000:2018 height", 11022, 5941); + return true; + case 58: + record = new EpsgCompoundCrsRecord(5973, "ETRS89-NOR [EUREF89] / UTM zone 33N + NN2000:2018 height", 11023, 5941); + return true; + case 59: + record = new EpsgCompoundCrsRecord(5974, "ETRS89-NOR [EUREF89] / UTM zone 34N + NN2000:2018 height", 11024, 5941); + return true; + case 60: + record = new EpsgCompoundCrsRecord(5975, "ETRS89-NOR [EUREF89] / UTM zone 35N + NN2000:2018 height", 11025, 5941); + return true; + case 61: + record = new EpsgCompoundCrsRecord(5976, "ETRS89-NOR [EUREF89] / UTM zone 36N + NN2000:2018 height", 11026, 5941); + return true; + case 62: + record = new EpsgCompoundCrsRecord(6144, "ETRS89-NOR [EUREF89] + NN54 height", 10875, 5776); + return true; + case 63: + record = new EpsgCompoundCrsRecord(6145, "ETRS89-NOR [EUREF89] / NTM zone 5 + NN54 height", 5105, 5776); + return true; + case 64: + record = new EpsgCompoundCrsRecord(6146, "ETRS89-NOR [EUREF89] / NTM zone 6 + NN54 height", 5106, 5776); + return true; + case 65: + record = new EpsgCompoundCrsRecord(6147, "ETRS89-NOR [EUREF89] / NTM zone 7 + NN54 height", 5107, 5776); + return true; + case 66: + record = new EpsgCompoundCrsRecord(6148, "ETRS89-NOR [EUREF89] / NTM zone 8 + NN54 height", 5108, 5776); + return true; + case 67: + record = new EpsgCompoundCrsRecord(6149, "ETRS89-NOR [EUREF89] / NTM zone 9 + NN54 height", 5109, 5776); + return true; + case 68: + record = new EpsgCompoundCrsRecord(6150, "ETRS89-NOR [EUREF89] / NTM zone 10 + NN54 height", 5110, 5776); + return true; + case 69: + record = new EpsgCompoundCrsRecord(6151, "ETRS89-NOR [EUREF89] / NTM zone 11 + NN54 height", 5111, 5776); + return true; + case 70: + record = new EpsgCompoundCrsRecord(6152, "ETRS89-NOR [EUREF89] / NTM zone 12 + NN54 height", 5112, 5776); + return true; + case 71: + record = new EpsgCompoundCrsRecord(6153, "ETRS89-NOR [EUREF89] / NTM zone 13 + NN54 height", 5113, 5776); + return true; + case 72: + record = new EpsgCompoundCrsRecord(6154, "ETRS89-NOR [EUREF89] / NTM zone 14 + NN54 height", 5114, 5776); + return true; + case 73: + record = new EpsgCompoundCrsRecord(6155, "ETRS89-NOR [EUREF89] / NTM zone 15 + NN54 height", 5115, 5776); + return true; + case 74: + record = new EpsgCompoundCrsRecord(6156, "ETRS89-NOR [EUREF89] / NTM zone 16 + NN54 height", 5116, 5776); + return true; + case 75: + record = new EpsgCompoundCrsRecord(6157, "ETRS89-NOR [EUREF89] / NTM zone 17 + NN54 height", 5117, 5776); + return true; + case 76: + record = new EpsgCompoundCrsRecord(6158, "ETRS89-NOR [EUREF89] / NTM zone 18 + NN54 height", 5118, 5776); + return true; + case 77: + record = new EpsgCompoundCrsRecord(6159, "ETRS89-NOR [EUREF89] / NTM zone 19 + NN54 height", 5119, 5776); + return true; + case 78: + record = new EpsgCompoundCrsRecord(6160, "ETRS89-NOR [EUREF89] / NTM zone 20 + NN54 height", 5120, 5776); + return true; + case 79: + record = new EpsgCompoundCrsRecord(6161, "ETRS89-NOR [EUREF89] / NTM zone 21 + NN54 height", 5121, 5776); + return true; + case 80: + record = new EpsgCompoundCrsRecord(6162, "ETRS89-NOR [EUREF89] / NTM zone 22 + NN54 height", 5122, 5776); + return true; + case 81: + record = new EpsgCompoundCrsRecord(6163, "ETRS89-NOR [EUREF89] / NTM zone 23 + NN54 height", 5123, 5776); + return true; + case 82: + record = new EpsgCompoundCrsRecord(6164, "ETRS89-NOR [EUREF89] / NTM zone 24 + NN54 height", 5124, 5776); + return true; + case 83: + record = new EpsgCompoundCrsRecord(6165, "ETRS89-NOR [EUREF89] / NTM zone 25 + NN54 height", 5125, 5776); + return true; + case 84: + record = new EpsgCompoundCrsRecord(6166, "ETRS89-NOR [EUREF89] / NTM zone 26 + NN54 height", 5126, 5776); + return true; + case 85: + record = new EpsgCompoundCrsRecord(6167, "ETRS89-NOR [EUREF89] / NTM zone 27 + NN54 height", 5127, 5776); + return true; + case 86: + record = new EpsgCompoundCrsRecord(6168, "ETRS89-NOR [EUREF89] / NTM zone 28 + NN54 height", 5128, 5776); + return true; + case 87: + record = new EpsgCompoundCrsRecord(6169, "ETRS89-NOR [EUREF89] / NTM zone 29 + NN54 height", 5129, 5776); + return true; + case 88: + record = new EpsgCompoundCrsRecord(6170, "ETRS89-NOR [EUREF89] / NTM zone 30 + NN54 height", 5130, 5776); + return true; + case 89: + record = new EpsgCompoundCrsRecord(6171, "ETRS89-NOR [EUREF89] / UTM zone 31N + NN54 height", 11021, 5776); + return true; + case 90: + record = new EpsgCompoundCrsRecord(6172, "ETRS89-NOR [EUREF89] / UTM zone 32N + NN54 height", 11022, 5776); + return true; + case 91: + record = new EpsgCompoundCrsRecord(6173, "ETRS89-NOR [EUREF89] / UTM zone 33N + NN54 height", 11023, 5776); + return true; + case 92: + record = new EpsgCompoundCrsRecord(6174, "ETRS89-NOR [EUREF89] / UTM zone 34N + NN54 height", 11024, 5776); + return true; + case 93: + record = new EpsgCompoundCrsRecord(6175, "ETRS89-NOR [EUREF89] / UTM zone 35N + NN54 height", 11025, 5776); + return true; + case 94: + record = new EpsgCompoundCrsRecord(6176, "ETRS89-NOR [EUREF89] / UTM zone 36N + NN54 height", 11026, 5776); + return true; + case 95: + record = new EpsgCompoundCrsRecord(6190, "BD72 / Belgian Lambert 72 + Ostend height", 31370, 5710); + return true; + case 96: + record = new EpsgCompoundCrsRecord(6349, "NAD83(2011) + NAVD88 height", 6318, 5703); + return true; + case 97: + record = new EpsgCompoundCrsRecord(6649, "NAD83(CSRS) + CGVD2013(CGG2013) height", 4617, 6647); + return true; + case 98: + record = new EpsgCompoundCrsRecord(6650, "NAD83(CSRS) / UTM zone 7N + CGVD2013 height", 3154, 6647); + return true; + case 99: + record = new EpsgCompoundCrsRecord(6651, "NAD83(CSRS) / UTM zone 8N + CGVD2013 height", 3155, 6647); + return true; + case 100: + record = new EpsgCompoundCrsRecord(6652, "NAD83(CSRS) / UTM zone 9N + CGVD2013 height", 3156, 6647); + return true; + case 101: + record = new EpsgCompoundCrsRecord(6653, "NAD83(CSRS) / UTM zone 10N + CGVD2013 height", 3157, 6647); + return true; + case 102: + record = new EpsgCompoundCrsRecord(6654, "NAD83(CSRS) / UTM zone 11N + CGVD2013 height", 2955, 6647); + return true; + case 103: + record = new EpsgCompoundCrsRecord(6655, "NAD83(CSRS) / UTM zone 12N + CGVD2013 height", 2956, 6647); + return true; + case 104: + record = new EpsgCompoundCrsRecord(6656, "NAD83(CSRS) / UTM zone 13N + CGVD2013 height", 2957, 6647); + return true; + case 105: + record = new EpsgCompoundCrsRecord(6657, "NAD83(CSRS) / UTM zone 14N + CGVD2013 height", 3158, 6647); + return true; + case 106: + record = new EpsgCompoundCrsRecord(6658, "NAD83(CSRS) / UTM zone 15N + CGVD2013 height", 3159, 6647); + return true; + case 107: + record = new EpsgCompoundCrsRecord(6659, "NAD83(CSRS) / UTM zone 16N + CGVD2013 height", 3160, 6647); + return true; + case 108: + record = new EpsgCompoundCrsRecord(6660, "NAD83(CSRS) / UTM zone 17N + CGVD2013 height", 2958, 6647); + return true; + case 109: + record = new EpsgCompoundCrsRecord(6661, "NAD83(CSRS) / UTM zone 18N + CGVD2013 height", 2959, 6647); + return true; + case 110: + record = new EpsgCompoundCrsRecord(6662, "NAD83(CSRS) / UTM zone 19N + CGVD2013 height", 2960, 6647); + return true; + case 111: + record = new EpsgCompoundCrsRecord(6663, "NAD83(CSRS) / UTM zone 20N + CGVD2013 height", 2961, 6647); + return true; + case 112: + record = new EpsgCompoundCrsRecord(6664, "NAD83(CSRS) / UTM zone 21N + CGVD2013 height", 2962, 6647); + return true; + case 113: + record = new EpsgCompoundCrsRecord(6665, "NAD83(CSRS) / UTM zone 22N + CGVD2013 height", 3761, 6647); + return true; + case 114: + record = new EpsgCompoundCrsRecord(6696, "JGD2000 + JGD2000 (vertical) height", 4612, 6694); + return true; + case 115: + record = new EpsgCompoundCrsRecord(6697, "JGD2011 + JGD2011 (vertical) height", 6668, 6695); + return true; + case 116: + record = new EpsgCompoundCrsRecord(6700, "Tokyo + JSLD72 height", 4301, 6693); + return true; + case 117: + record = new EpsgCompoundCrsRecord(6893, "WGS 84 / World Mercator + EGM2008 height", 3395, 3855); + return true; + case 118: + record = new EpsgCompoundCrsRecord(6917, "SVY21 + SHD height", 4757, 6916); + return true; + case 119: + record = new EpsgCompoundCrsRecord(6927, "SVY21 / Singapore TM + SHD height", 3414, 6916); + return true; + case 120: + record = new EpsgCompoundCrsRecord(7400, "NTF (Paris) + NGF-IGN69 height", 4807, 5720); + return true; + case 121: + record = new EpsgCompoundCrsRecord(7404, "RT90 + RH70 height", 4124, 5718); + return true; + case 122: + record = new EpsgCompoundCrsRecord(7405, "OSGB36 / British National Grid + ODN height", 27700, 5701); + return true; + case 123: + record = new EpsgCompoundCrsRecord(7406, "NAD27 + NGVD29 height (ftUS)", 4267, 5702); + return true; + case 124: + record = new EpsgCompoundCrsRecord(7407, "NAD27 / Texas North + NGVD29 height (ftUS)", 32037, 5702); + return true; + case 125: + record = new EpsgCompoundCrsRecord(7409, "ETRS89 + EVRF2000 height", 4258, 5730); + return true; + case 126: + record = new EpsgCompoundCrsRecord(7410, "PSHD93", 4134, 5724); + return true; + case 127: + record = new EpsgCompoundCrsRecord(7411, "NTF (Paris) / Lambert zone II + NGF Lallemand height", 27572, 5719); + return true; + case 128: + record = new EpsgCompoundCrsRecord(7414, "Tokyo + JSLD69 height", 4301, 5723); + return true; + case 129: + record = new EpsgCompoundCrsRecord(7415, "RD + NAP height", 28992, 5709); + return true; + case 130: + record = new EpsgCompoundCrsRecord(7421, "NTF (Paris) / Lambert zone II + NGF-IGN69 height", 27572, 5720); + return true; + case 131: + record = new EpsgCompoundCrsRecord(7422, "NTF (Paris) / Lambert zone III + NGF-IGN69 height", 27573, 5720); + return true; + case 132: + record = new EpsgCompoundCrsRecord(7423, "ETRS89 + EVRF2007 height", 4258, 5621); + return true; + case 133: + record = new EpsgCompoundCrsRecord(7954, "Astro DOS 71 / UTM zone 30S + Jamestown 1971 height", 7878, 7888); + return true; + case 134: + record = new EpsgCompoundCrsRecord(7955, "St. Helena Tritan / UTM zone 30S + Tritan 2011 height", 7883, 7889); + return true; + case 135: + record = new EpsgCompoundCrsRecord(7956, "SHMG2015 + SHVD2015 height", 7887, 7890); + return true; + case 136: + record = new EpsgCompoundCrsRecord(8349, "GR96 + GVR2000 height", 4747, 8266); + return true; + case 137: + record = new EpsgCompoundCrsRecord(8350, "GR96 + GVR2016 height", 4747, 8267); + return true; + case 138: + record = new EpsgCompoundCrsRecord(8370, "ETRS89-BEL [BEREF2002] / Belgian Lambert 2008 + Ostend height", 3812, 5710); + return true; + case 139: + record = new EpsgCompoundCrsRecord(8801, "NAD83 / Alabama East + NAVD88 height", 26929, 5703); + return true; + case 140: + record = new EpsgCompoundCrsRecord(8802, "NAD83 / Alabama West + NAVD88 height", 26930, 5703); + return true; + case 141: + record = new EpsgCompoundCrsRecord(8803, "NAD83 / Alaska zone 1 + NAVD88 height", 26931, 5703); + return true; + case 142: + record = new EpsgCompoundCrsRecord(8804, "NAD83 / Alaska zone 2 + NAVD88 height", 26932, 5703); + return true; + case 143: + record = new EpsgCompoundCrsRecord(8805, "NAD83 / Alaska zone 3 + NAVD88 height", 26933, 5703); + return true; + case 144: + record = new EpsgCompoundCrsRecord(8806, "NAD83 / Alaska zone 4 + NAVD88 height", 26934, 5703); + return true; + case 145: + record = new EpsgCompoundCrsRecord(8807, "NAD83 / Alaska zone 5 + NAVD88 height", 26935, 5703); + return true; + case 146: + record = new EpsgCompoundCrsRecord(8808, "NAD83 / Alaska zone 6 + NAVD88 height", 26936, 5703); + return true; + case 147: + record = new EpsgCompoundCrsRecord(8809, "NAD83 / Alaska zone 7 + NAVD88 height", 26937, 5703); + return true; + case 148: + record = new EpsgCompoundCrsRecord(8810, "NAD83 / Alaska zone 8 + NAVD88 height", 26938, 5703); + return true; + case 149: + record = new EpsgCompoundCrsRecord(8811, "NAD83 / Alaska zone 9 + NAVD88 height", 26939, 5703); + return true; + case 150: + record = new EpsgCompoundCrsRecord(8812, "NAD83 / Alaska zone 10 + NAVD88 height", 26940, 5703); + return true; + case 151: + record = new EpsgCompoundCrsRecord(8813, "NAD83 / Missouri East + NAVD88 height", 26996, 5703); + return true; + case 152: + record = new EpsgCompoundCrsRecord(8814, "NAD83 / Missouri Central + NAVD88 height", 26997, 5703); + return true; + case 153: + record = new EpsgCompoundCrsRecord(8815, "NAD83 / Missouri West + NAVD88 height", 26998, 5703); + return true; + case 154: + record = new EpsgCompoundCrsRecord(8912, "CR-SIRGAS epoch 2014.59 / CRTM05 + DACR52 height", 8908, 8911); + return true; + case 155: + record = new EpsgCompoundCrsRecord(9286, "ETRS89-NLD [AGRS2010] + NAP height", 11037, 5709); + return true; + case 156: + record = new EpsgCompoundCrsRecord(9289, "ETRS89-NLD [AGRS2010] + LAT-NLD depth", 11037, 9287); + return true; + case 157: + record = new EpsgCompoundCrsRecord(9290, "ETRS89-NLD [AGRS2010] + MSL-NLD depth", 11037, 9288); + return true; + case 158: + record = new EpsgCompoundCrsRecord(9306, "HS2 Survey Grid + HS2-VRF height", 9300, 9303); + return true; + case 159: + record = new EpsgCompoundCrsRecord(9368, "TPEN11 Grid + ODN height", 9367, 5701); + return true; + case 160: + record = new EpsgCompoundCrsRecord(9374, "MML07 Grid + ODN height", 9373, 5701); + return true; + case 161: + record = new EpsgCompoundCrsRecord(9388, "AbInvA96_2020 Grid + ODN height", 9387, 5701); + return true; + case 162: + record = new EpsgCompoundCrsRecord(9422, "ETRS89 + EVRF2019 height", 4258, 9389); + return true; + case 163: + record = new EpsgCompoundCrsRecord(9423, "ETRS89 + EVRF2019 mean-tide height", 4258, 9390); + return true; + case 164: + record = new EpsgCompoundCrsRecord(9424, "ETRS89-GBR [OSNet v2009] + ODN height", 11009, 5701); + return true; + case 165: + record = new EpsgCompoundCrsRecord(9425, "ETRS89 + ODN (Offshore) height", 4258, 7707); + return true; + case 166: + record = new EpsgCompoundCrsRecord(9426, "ETRS89-GBR [OSNet v2009] + ODN Orkney height", 11009, 5740); + return true; + case 167: + record = new EpsgCompoundCrsRecord(9427, "ETRS89-GBR [OSNet v2009] + Lerwick height", 11009, 5742); + return true; + case 168: + record = new EpsgCompoundCrsRecord(9428, "ETRS89-GBR [OSNet v2009] + Stornoway height", 11009, 5746); + return true; + case 169: + record = new EpsgCompoundCrsRecord(9429, "ETRS89 + Douglas height", 4258, 5750); + return true; + case 170: + record = new EpsgCompoundCrsRecord(9430, "ETRS89-GBR [OSNet v2009] + St. Marys height", 11009, 5749); + return true; + case 171: + record = new EpsgCompoundCrsRecord(9449, "ETRS89-IRE [ETRF2000] + Malin Head height", 4173, 5731); + return true; + case 172: + record = new EpsgCompoundCrsRecord(9450, "ETRS89-IRE [ETRF2000] + Belfast height", 4173, 5732); + return true; + case 173: + record = new EpsgCompoundCrsRecord(9457, "GBK19 Grid + ODN height", 9456, 5701); + return true; + case 174: + record = new EpsgCompoundCrsRecord(9462, "GDA2020 + AVWS height", 7844, 9458); + return true; + case 175: + record = new EpsgCompoundCrsRecord(9463, "GDA2020 + AHD height", 7844, 5711); + return true; + case 176: + record = new EpsgCompoundCrsRecord(9464, "GDA94 + AHD height", 4283, 5711); + return true; + case 177: + record = new EpsgCompoundCrsRecord(9500, "ETRS89-AUT [2002] + EVRF2000 Austria height", 11057, 9274); + return true; + case 178: + record = new EpsgCompoundCrsRecord(9501, "MGI + EVRF2000 Austria height", 4312, 9274); + return true; + case 179: + record = new EpsgCompoundCrsRecord(9502, "CIGD11 + CBVD61 height (ft)", 6135, 6132); + return true; + case 180: + record = new EpsgCompoundCrsRecord(9503, "CIGD11 + GCVD54 height (ft)", 6135, 6130); + return true; + case 181: + record = new EpsgCompoundCrsRecord(9504, "CIGD11 + LCVD61 height (ft)", 6135, 6131); + return true; + case 182: + record = new EpsgCompoundCrsRecord(9505, "ETRS89-ESP [REGENTE] + Alicante height", 11134, 5782); + return true; + case 183: + record = new EpsgCompoundCrsRecord(9506, "ETRS89-ESP [REGENTE] + Ceuta 2 height", 11134, 9402); + return true; + case 184: + record = new EpsgCompoundCrsRecord(9507, "ETRS89-ESP [REGENTE] + Ibiza height", 11134, 9394); + return true; + case 185: + record = new EpsgCompoundCrsRecord(9508, "ETRS89-ESP [REGENTE] + Mallorca height", 11134, 9392); + return true; + case 186: + record = new EpsgCompoundCrsRecord(9509, "ETRS89-ESP [REGENTE] + Menorca height", 11134, 9393); + return true; + case 187: + record = new EpsgCompoundCrsRecord(9510, "REGCAN95 + El Hierro height", 4081, 9401); + return true; + case 188: + record = new EpsgCompoundCrsRecord(9511, "REGCAN95 + Fuerteventura height", 4081, 9396); + return true; + case 189: + record = new EpsgCompoundCrsRecord(9512, "REGCAN95 + Gran Canaria height", 4081, 9397); + return true; + case 190: + record = new EpsgCompoundCrsRecord(9513, "REGCAN95 + La Gomera height", 4081, 9399); + return true; + case 191: + record = new EpsgCompoundCrsRecord(9514, "REGCAN95 + La Palma height", 4081, 9400); + return true; + case 192: + record = new EpsgCompoundCrsRecord(9515, "REGCAN95 + Lanzarote height", 4081, 9395); + return true; + case 193: + record = new EpsgCompoundCrsRecord(9516, "REGCAN95 + Tenerife height", 4081, 9398); + return true; + case 194: + record = new EpsgCompoundCrsRecord(9517, "SHGD2015 + SHVD2015 height", 7886, 7890); + return true; + case 195: + record = new EpsgCompoundCrsRecord(9518, "WGS 84 + EGM2008 height", 4326, 3855); + return true; + case 196: + record = new EpsgCompoundCrsRecord(9519, "FEH2010 + FCSVR10 height", 5593, 5597); + return true; + case 197: + record = new EpsgCompoundCrsRecord(9520, "KSA-GRF17 + KSA-VRF14 height", 9333, 9335); + return true; + case 198: + record = new EpsgCompoundCrsRecord(9521, "POSGAR 2007 + SRVN16 height", 5340, 9255); + return true; + case 199: + record = new EpsgCompoundCrsRecord(9522, "NAD83(2011) + PRVD02 height", 6318, 6641); + return true; + case 200: + record = new EpsgCompoundCrsRecord(9523, "NAD83(2011) + VIVD09 height", 6318, 6642); + return true; + case 201: + record = new EpsgCompoundCrsRecord(9524, "NAD83(MA11) + GUVD04 height", 6325, 6644); + return true; + case 202: + record = new EpsgCompoundCrsRecord(9525, "NAD83(MA11) + NMVD03 height", 6325, 6640); + return true; + case 203: + record = new EpsgCompoundCrsRecord(9526, "NAD83(PA11) + ASVD02 height", 6322, 6643); + return true; + case 204: + record = new EpsgCompoundCrsRecord(9527, "NZGD2000 + NZVD2009 height", 4167, 4440); + return true; + case 205: + record = new EpsgCompoundCrsRecord(9528, "NZGD2000 + NZVD2016 height", 4167, 7839); + return true; + case 206: + record = new EpsgCompoundCrsRecord(9529, "SRGI2013 + INAGeoid2020 v1 height", 9470, 9471); + return true; + case 207: + record = new EpsgCompoundCrsRecord(9530, "RGFG95 + NGG1977 height", 4624, 5755); + return true; + case 208: + record = new EpsgCompoundCrsRecord(9531, "RGAF09 + Guadeloupe 1988 height", 5489, 5757); + return true; + case 209: + record = new EpsgCompoundCrsRecord(9532, "RGAF09 + IGN 1988 LS height", 5489, 5616); + return true; + case 210: + record = new EpsgCompoundCrsRecord(9533, "RGAF09 + IGN 1988 MG height", 5489, 5617); + return true; + case 211: + record = new EpsgCompoundCrsRecord(9534, "RGAF09 + IGN 1988 SB height", 5489, 5619); + return true; + case 212: + record = new EpsgCompoundCrsRecord(9535, "RGAF09 + IGN 1988 SM height", 5489, 5620); + return true; + case 213: + record = new EpsgCompoundCrsRecord(9536, "RGAF09 + IGN 2008 LD height", 5489, 9130); + return true; + case 214: + record = new EpsgCompoundCrsRecord(9537, "RGAF09 + Martinique 1987 height", 5489, 5756); + return true; + case 215: + record = new EpsgCompoundCrsRecord(9538, "ETRS89-FRA [RGF93 v2] + NGF-IGN69 height", 9777, 5720); + return true; + case 216: + record = new EpsgCompoundCrsRecord(9539, "ETRS89-FRA [RGF93 v2] + NGF-IGN78 height", 9777, 5721); + return true; + case 217: + record = new EpsgCompoundCrsRecord(9540, "RGNC91-93 + NGNC08 height", 4749, 9351); + return true; + case 218: + record = new EpsgCompoundCrsRecord(9541, "RGSPM06 + Danger 1950 height", 4463, 5792); + return true; + case 219: + record = new EpsgCompoundCrsRecord(9542, "RRAF 1991 + IGN 2008 LD height", 4558, 9130); + return true; + case 220: + record = new EpsgCompoundCrsRecord(9543, "ITRF2005 + SA LLD height", 8998, 9279); + return true; + case 221: + record = new EpsgCompoundCrsRecord(9544, "NAD83(CSRS)v6 + CGVD2013a(2010) height", 8252, 9245); + return true; + case 222: + record = new EpsgCompoundCrsRecord(9656, "ETRS89-POL [PL-ETRF2000] + Baltic 1986 height", 9702, 9650); + return true; + case 223: + record = new EpsgCompoundCrsRecord(9657, "ETRS89-POL [PL-ETRF2000] + PL-EVRF2007 height", 9702, 9651); + return true; + case 224: + record = new EpsgCompoundCrsRecord(9705, "WGS 84 + MSL height", 4326, 5714); + return true; + case 225: + record = new EpsgCompoundCrsRecord(9707, "WGS 84 + EGM96 height", 4326, 5773); + return true; + case 226: + record = new EpsgCompoundCrsRecord(9711, "NAD83(CSRS) / UTM zone 23N + CGVD2013 height", 9709, 6647); + return true; + case 227: + record = new EpsgCompoundCrsRecord(9714, "NAD83(CSRS) / UTM zone 24N + CGVD2013 height", 9713, 6647); + return true; + case 228: + record = new EpsgCompoundCrsRecord(9715, "NAD83(CSRS) / UTM zone 15N + CGVD2013a(2010) height", 3159, 9245); + return true; + case 229: + record = new EpsgCompoundCrsRecord(9723, "ETRS89-ITA [RDN2008] + Genoa 1942 height", 6706, 5214); + return true; + case 230: + record = new EpsgCompoundCrsRecord(9724, "ETRS89-ITA [RDN2008] + Catania 1965 height", 6706, 9721); + return true; + case 231: + record = new EpsgCompoundCrsRecord(9725, "ETRS89-ITA [RDN2008] + Cagliari 1956 height", 6706, 9722); + return true; + case 232: + record = new EpsgCompoundCrsRecord(9742, "EOS21 Grid + ODN height", 9741, 5701); + return true; + case 233: + record = new EpsgCompoundCrsRecord(9762, "ECML14_NB Grid + ODN height", 9761, 5701); + return true; + case 234: + record = new EpsgCompoundCrsRecord(9767, "EWR2 Grid + ODN height", 9766, 5701); + return true; + case 235: + record = new EpsgCompoundCrsRecord(9785, "ETRS89-FRA [RGF93 v2b] + NGF-IGN69 height", 9782, 5720); + return true; + case 236: + record = new EpsgCompoundCrsRecord(9870, "MRH21 Grid + ODN height", 9869, 5701); + return true; + case 237: + record = new EpsgCompoundCrsRecord(9881, "MOLDOR11 Grid + ODN height", 9880, 5701); + return true; + case 238: + record = new EpsgCompoundCrsRecord(9883, "ETRS89-NOR [EUREF89] + CD Norway depth", 10875, 9672); + return true; + case 239: + record = new EpsgCompoundCrsRecord(9897, "LUREF / Luxembourg TM + NG95 height", 2169, 5774); + return true; + case 240: + record = new EpsgCompoundCrsRecord(9907, "ETRS89-BEL [BEREF2011] + Ostend height", 11215, 5710); + return true; + case 241: + record = new EpsgCompoundCrsRecord(9924, "ETRS89 + DHHN2016 height", 4258, 7837); + return true; + case 242: + record = new EpsgCompoundCrsRecord(9928, "DB_REF2003 zone 2", 5682, 9923); + return true; + case 243: + record = new EpsgCompoundCrsRecord(9929, "DB_REF2003 zone 3", 5683, 9923); + return true; + case 244: + record = new EpsgCompoundCrsRecord(9930, "DB_REF2003 zone 4", 5684, 9923); + return true; + case 245: + record = new EpsgCompoundCrsRecord(9931, "DB_REF2003 zone 5", 5685, 9923); + return true; + case 246: + record = new EpsgCompoundCrsRecord(9932, "DB_REF2016 zone 2", 5682, 9927); + return true; + case 247: + record = new EpsgCompoundCrsRecord(9933, "DB_REF2016 zone 3", 5683, 9927); + return true; + case 248: + record = new EpsgCompoundCrsRecord(9934, "DB_REF2016 zone 4", 5684, 9927); + return true; + case 249: + record = new EpsgCompoundCrsRecord(9935, "DB_REF2016 zone 5", 5685, 9927); + return true; + case 250: + record = new EpsgCompoundCrsRecord(9944, "EBBWV14 Grid + ODN height", 9943, 5701); + return true; + case 251: + record = new EpsgCompoundCrsRecord(9948, "ISN93 + ISH2004 height", 4659, 8089); + return true; + case 252: + record = new EpsgCompoundCrsRecord(9949, "ISN2004 + ISH2004 height", 5324, 8089); + return true; + case 253: + record = new EpsgCompoundCrsRecord(9950, "ISN2016 + ISH2004 height", 8086, 8089); + return true; + case 254: + record = new EpsgCompoundCrsRecord(9951, "ISN93 / Lambert 1993 + ISH2004 height", 3057, 8089); + return true; + case 255: + record = new EpsgCompoundCrsRecord(9952, "ISN2004 / Lambert 2004 + ISH2004 height", 5325, 8089); + return true; + case 256: + record = new EpsgCompoundCrsRecord(9953, "ISN2016 / Lambert 2016 + ISH2004 height", 8088, 8089); + return true; + case 257: + record = new EpsgCompoundCrsRecord(9968, "HULLEE13 Grid + ODN height", 9967, 5701); + return true; + case 258: + record = new EpsgCompoundCrsRecord(9973, "SCM22 Grid + ODN height", 9972, 5701); + return true; + case 259: + record = new EpsgCompoundCrsRecord(9978, "FNL22 Grid + ODN height", 9977, 5701); + return true; + case 260: + record = new EpsgCompoundCrsRecord(10156, "ETRS89 + MSL UK & Ireland VORF08 depth", 4258, 10150); + return true; + case 261: + record = new EpsgCompoundCrsRecord(10157, "ETRS89 + CD UK & Ireland VORF08 depth", 4258, 10151); + return true; + case 262: + record = new EpsgCompoundCrsRecord(10162, "JGD2011 / Japan Plane Rectangular CS I + JGD2011 (vertical) height", 6669, 6695); + return true; + case 263: + record = new EpsgCompoundCrsRecord(10163, "JGD2011 / Japan Plane Rectangular CS II + JGD2011 (vertical) height", 6670, 6695); + return true; + case 264: + record = new EpsgCompoundCrsRecord(10164, "JGD2011 / Japan Plane Rectangular CS III + JGD2011 (vertical) height", 6671, 6695); + return true; + case 265: + record = new EpsgCompoundCrsRecord(10165, "JGD2011 / Japan Plane Rectangular CS IV + JGD2011 (vertical) height", 6672, 6695); + return true; + case 266: + record = new EpsgCompoundCrsRecord(10166, "JGD2011 / Japan Plane Rectangular CS V + JGD2011 (vertical) height", 6673, 6695); + return true; + case 267: + record = new EpsgCompoundCrsRecord(10167, "JGD2011 / Japan Plane Rectangular CS VI + JGD2011 (vertical) height", 6674, 6695); + return true; + case 268: + record = new EpsgCompoundCrsRecord(10168, "JGD2011 / Japan Plane Rectangular CS VII + JGD2011 (vertical) height", 6675, 6695); + return true; + case 269: + record = new EpsgCompoundCrsRecord(10169, "JGD2011 / Japan Plane Rectangular CS VIII + JGD2011 (vertical) height", 6676, 6695); + return true; + case 270: + record = new EpsgCompoundCrsRecord(10170, "JGD2011 / Japan Plane Rectangular CS IX + JGD2011 (vertical) height", 6677, 6695); + return true; + case 271: + record = new EpsgCompoundCrsRecord(10171, "JGD2011 / Japan Plane Rectangular CS X + JGD2011 (vertical) height", 6678, 6695); + return true; + case 272: + record = new EpsgCompoundCrsRecord(10172, "JGD2011 / Japan Plane Rectangular CS XI + JGD2011 (vertical) height", 6679, 6695); + return true; + case 273: + record = new EpsgCompoundCrsRecord(10173, "JGD2011 / Japan Plane Rectangular CS XII + JGD2011 (vertical) height", 6680, 6695); + return true; + case 274: + record = new EpsgCompoundCrsRecord(10174, "JGD2011 / Japan Plane Rectangular CS XIII + JGD2011 (vertical) height", 6681, 6695); + return true; + case 275: + record = new EpsgCompoundCrsRecord(10184, "DoPw22 Grid + ODN height", 10183, 5701); + return true; + case 276: + record = new EpsgCompoundCrsRecord(10189, "ShAb07 Grid + ODN height", 10188, 5701); + return true; + case 277: + record = new EpsgCompoundCrsRecord(10195, "CNH22 Grid + ODN height", 10194, 5701); + return true; + case 278: + record = new EpsgCompoundCrsRecord(10200, "CWS13 Grid + ODN height", 10199, 5701); + return true; + case 279: + record = new EpsgCompoundCrsRecord(10208, "DIBA15 Grid + ODN height", 10207, 5701); + return true; + case 280: + record = new EpsgCompoundCrsRecord(10213, "GWPBS22 Grid + ODN height", 10212, 5701); + return true; + case 281: + record = new EpsgCompoundCrsRecord(10218, "GWWAB22 Grid + ODN height", 10217, 5701); + return true; + case 282: + record = new EpsgCompoundCrsRecord(10223, "GWWWA22 Grid + ODN height", 10222, 5701); + return true; + case 283: + record = new EpsgCompoundCrsRecord(10228, "MALS09 Grid + ODN height", 10227, 5701); + return true; + case 284: + record = new EpsgCompoundCrsRecord(10236, "OxWo08 Grid + ODN height", 10235, 5701); + return true; + case 285: + record = new EpsgCompoundCrsRecord(10241, "SYC20 Grid + ODN height", 10240, 5701); + return true; + case 286: + record = new EpsgCompoundCrsRecord(10245, "ETRS89-SVN [D96] + SVS2010 height", 4765, 8690); + return true; + case 287: + record = new EpsgCompoundCrsRecord(10246, "ETRS89-SVN [D96] / Slovene National Grid + SVS2010 height", 3794, 8690); + return true; + case 288: + record = new EpsgCompoundCrsRecord(10276, "SMITB20 Grid + ODN height", 10275, 5701); + return true; + case 289: + record = new EpsgCompoundCrsRecord(10281, "RBEPP12 Grid + ODN height", 10280, 5701); + return true; + case 290: + record = new EpsgCompoundCrsRecord(10293, "ETRS89-DEU [ETRS89/DREF91/2016] + DHHN2016 height", 10284, 7837); + return true; + case 291: + record = new EpsgCompoundCrsRecord(10318, "RGNC15 (lon-lat) + NGNC08 height", 10312, 9351); + return true; + case 292: + record = new EpsgCompoundCrsRecord(10355, "ETRS89-ESP [REGENTE] + Formentera height", 11134, 10352); + return true; + case 293: + record = new EpsgCompoundCrsRecord(10356, "ETRS89-ESP [REGENTE] + Alboran height", 11134, 10353); + return true; + case 294: + record = new EpsgCompoundCrsRecord(10357, "ETRS89-ESP [REGENTE] + Melilla height", 11134, 10354); + return true; + case 295: + record = new EpsgCompoundCrsRecord(10365, "KGD2002 + KVD1964 height", 4737, 5193); + return true; + case 296: + record = new EpsgCompoundCrsRecord(10472, "COV23 Grid + ODN height", 10471, 5701); + return true; + case 297: + record = new EpsgCompoundCrsRecord(10486, "ETRS89 + DVR90(2002) height", 4258, 10483); + return true; + case 298: + record = new EpsgCompoundCrsRecord(10487, "ETRS89 + DVR90(2013) height", 4258, 10484); + return true; + case 299: + record = new EpsgCompoundCrsRecord(10488, "ETRS89 + DVR90(2023) height", 4258, 10485); + return true; + case 300: + record = new EpsgCompoundCrsRecord(10497, "ETRS89-FRA [RGF93 v2] / Lambert-93 + NGF-IGN69 height", 9793, 5720); + return true; + case 301: + record = new EpsgCompoundCrsRecord(10498, "ETRS89-FRA [RGF93 v2] / Lambert-93 + NGF-IGN78 height", 9793, 5721); + return true; + case 302: + record = new EpsgCompoundCrsRecord(10499, "ETRS89-FRA [RGF93 v2b] / Lambert-93 + NGF-IGN69 height", 9794, 5720); + return true; + case 303: + record = new EpsgCompoundCrsRecord(10500, "ETRS89-FRA [RGF93 v2b] / Lambert-93 + NGF-IGN78 height", 9794, 5721); + return true; + case 304: + record = new EpsgCompoundCrsRecord(10507, "ETRS89-FRA [RGF93 v2b] + NGF-IGN78 height", 9782, 5721); + return true; + case 305: + record = new EpsgCompoundCrsRecord(10545, "ETRS89-PRT [1995] + Cascais height", 11108, 5780); + return true; + case 306: + record = new EpsgCompoundCrsRecord(10553, "ETRS89 + DKMSL(2022) depth", 4258, 10547); + return true; + case 307: + record = new EpsgCompoundCrsRecord(10554, "ETRS89 + DKLAT(2022) depth", 4258, 10548); + return true; + case 308: + record = new EpsgCompoundCrsRecord(10555, "ETRS89 + DKMSL(2023) depth", 4258, 10549); + return true; + case 309: + record = new EpsgCompoundCrsRecord(10556, "ETRS89 + DKLAT(2023) depth", 4258, 10550); + return true; + case 310: + record = new EpsgCompoundCrsRecord(10627, "ECML14 Grid + ODN height", 10626, 5701); + return true; + case 311: + record = new EpsgCompoundCrsRecord(10633, "RGAF09 / UTM zone 20N + Martinique 1987 height", 5490, 5756); + return true; + case 312: + record = new EpsgCompoundCrsRecord(10643, "Saba + Saba height", 10636, 10642); + return true; + case 313: + record = new EpsgCompoundCrsRecord(10644, "BES2020 Saba + Saba height", 10639, 10642); + return true; + case 314: + record = new EpsgCompoundCrsRecord(10645, "Saba DPnet + Saba height", 10641, 10642); + return true; + case 315: + record = new EpsgCompoundCrsRecord(10651, "GR96 + GLMSL(2023) depth", 4747, 10649); + return true; + case 316: + record = new EpsgCompoundCrsRecord(10652, "GR96 + GLLAT(2023) depth", 4747, 10650); + return true; + case 317: + record = new EpsgCompoundCrsRecord(10659, "ETRS89-HUN [ETRF2000] + EOMA 1980 height", 11163, 5787); + return true; + case 318: + record = new EpsgCompoundCrsRecord(10660, "HD72 / EOV + EOMA 1980 height", 23700, 5787); + return true; + case 319: + record = new EpsgCompoundCrsRecord(10679, "ETRS89 + BSCD2000 depth", 4258, 10678); + return true; + case 320: + record = new EpsgCompoundCrsRecord(10686, "ETRS89-SVN [D96] + SVS2000 height", 4765, 5779); + return true; + case 321: + record = new EpsgCompoundCrsRecord(10687, "ETRS89-SVN [D96] / Slovene National Grid + SVS2000 height", 3794, 5779); + return true; + case 322: + record = new EpsgCompoundCrsRecord(10691, "ETRS89-FIN [EUREF-FIN] + N60 height", 10690, 5717); + return true; + case 323: + record = new EpsgCompoundCrsRecord(10692, "ETRS89-FIN [EUREF-FIN] + N2000 height", 10690, 3900); + return true; + case 324: + record = new EpsgCompoundCrsRecord(10741, "Sint Eustatius + Sint Eustatius height", 10736, 10740); + return true; + case 325: + record = new EpsgCompoundCrsRecord(10742, "BES2020 Sint Eustatius + Sint Eustatius height", 10739, 10740); + return true; + case 326: + record = new EpsgCompoundCrsRecord(10746, "Sint Eustatius DPnet short + Sint Eustatius height", 10744, 10740); + return true; + case 327: + record = new EpsgCompoundCrsRecord(10747, "Sint Eustatius DPnet long + Sint Eustatius height", 10745, 10740); + return true; + case 328: + record = new EpsgCompoundCrsRecord(10764, "Bonaire DPnet + Bonaire height", 10759, 10763); + return true; + case 329: + record = new EpsgCompoundCrsRecord(10765, "Bonaire 2004 + Bonaire height", 10762, 10763); + return true; + case 330: + record = new EpsgCompoundCrsRecord(10774, "ETRS89-FIN [EUREF-FIN] / TM35FIN(E,N) + N2000 height", 3067, 3900); + return true; + case 331: + record = new EpsgCompoundCrsRecord(10826, "ETRS89-LVA [LKS-92] + Latvia 2000 height", 4661, 7700); + return true; + case 332: + record = new EpsgCompoundCrsRecord(10839, "ETRS89-LVA [LKS-2020] + Latvia 2000 height", 10305, 7700); + return true; + case 333: + record = new EpsgCompoundCrsRecord(10852, "EWR3 Grid + ODN height", 10851, 5701); + return true; + case 334: + record = new EpsgCompoundCrsRecord(10864, "WSPG Grid + ODN height", 10863, 5701); + return true; + case 335: + record = new EpsgCompoundCrsRecord(10865, "CGRS93 + Famagusta 1960 height", 6311, 7446); + return true; + case 336: + record = new EpsgCompoundCrsRecord(10904, "Asse 2025 + Asse 2025 height", 10898, 10900); + return true; + case 337: + record = new EpsgCompoundCrsRecord(10906, "Asse 2025 / Gauss-Kruger zone 4 (E-N) + Asse 2025 height", 10899, 10900); + return true; + case 338: + record = new EpsgCompoundCrsRecord(10920, "CSRN2025 (NAD83 2011) + COH88 2025 (NAVD88) height", 10910, 10918); + return true; + case 339: + record = new EpsgCompoundCrsRecord(10997, "ETRS89-GBR [OSNet v2009] + London Survey Grid height", 11009, 10989); + return true; + case 340: + record = new EpsgCompoundCrsRecord(11000, "ETRS89-NOR [EUREF89] + SVD2024 height", 10875, 10999); + return true; + case 341: + record = new EpsgCompoundCrsRecord(11006, "London Survey Grid 2025", 10995, 10989); + return true; + case 342: + record = new EpsgCompoundCrsRecord(11120, "ETRS89-FRO [2008] + FVR09 height", 11087, 5317); + return true; + case 343: + record = new EpsgCompoundCrsRecord(11158, "RGM23 + IGN 2023 Mayotte height", 10671, 11157); + return true; + case 344: + record = new EpsgCompoundCrsRecord(11169, "ETRS89-FIN [EUREF-FIN] / GK19FIN + N2000 height", 3873, 3900); + return true; + case 345: + record = new EpsgCompoundCrsRecord(11170, "ETRS89-FIN [EUREF-FIN] / GK20FIN + N2000 height", 3874, 3900); + return true; + case 346: + record = new EpsgCompoundCrsRecord(11171, "ETRS89-FIN [EUREF-FIN] / GK21FIN + N2000 height", 3128, 3900); + return true; + case 347: + record = new EpsgCompoundCrsRecord(11172, "ETRS89-FIN [EUREF-FIN] / GK22FIN + N2000 height", 3876, 3900); + return true; + case 348: + record = new EpsgCompoundCrsRecord(11173, "ETRS89-FIN [EUREF-FIN] / GK23FIN + N2000 height", 3130, 3900); + return true; + case 349: + record = new EpsgCompoundCrsRecord(11174, "ETRS89-FIN [EUREF-FIN] / GK24FIN + N2000 height", 3878, 3900); + return true; + case 350: + record = new EpsgCompoundCrsRecord(11175, "ETRS89-FIN [EUREF-FIN] / GK25FIN + N2000 height", 3879, 3900); + return true; + case 351: + record = new EpsgCompoundCrsRecord(11176, "ETRS89-FIN [EUREF-FIN] / GK26FIN + N2000 height", 3880, 3900); + return true; + case 352: + record = new EpsgCompoundCrsRecord(11177, "ETRS89-FIN [EUREF-FIN] / GK27FIN + N2000 height", 3881, 3900); + return true; + case 353: + record = new EpsgCompoundCrsRecord(11178, "ETRS89-FIN [EUREF-FIN] / GK28FIN + N2000 height", 3882, 3900); + return true; + case 354: + record = new EpsgCompoundCrsRecord(11179, "ETRS89-FIN [EUREF-FIN] / GK29FIN + N2000 height", 3883, 3900); + return true; + case 355: + record = new EpsgCompoundCrsRecord(11180, "ETRS89-FIN [EUREF-FIN] / GK30FIN + N2000 height", 3884, 3900); + return true; + case 356: + record = new EpsgCompoundCrsRecord(11181, "ETRS89-FIN [EUREF-FIN] / GK31FIN + N2000 height", 3885, 3900); + return true; + case 357: + record = new EpsgCompoundCrsRecord(11274, "SRGI2013 epoch 2021.0 + INAGeoid2020 v2 height", 11033, 20036); + return true; + case 358: + record = new EpsgCompoundCrsRecord(11311, "ETRS89-CZE [2007] + Baltic 1957 height", 11070, 8357); + return true; + case 359: + record = new EpsgCompoundCrsRecord(11312, "ETRS89-SVK [SKTRF09] + Baltic 1957 height", 11076, 8357); + return true; + case 360: + record = new EpsgCompoundCrsRecord(11314, "ETRS89-SVK [SKTRF09] + EVRF2007 height", 11076, 5621); + return true; + case 361: + record = new EpsgCompoundCrsRecord(11383, "DrukRef23 + Bhutan Vertical Datum 2022 height", 11226, 11338); + return true; + case 362: + record = new EpsgCompoundCrsRecord(11385, "Mexico ITRF2008 + NAVD88 height", 6365, 5703); + return true; + case 363: + record = new EpsgCompoundCrsRecord(11399, "ETRS89-NOR [EUREF89] + NN2000:2025 height", 10875, 11394); + return true; + case 364: + record = new EpsgCompoundCrsRecord(11400, "ETRS89-NOR [EUREF89] / UTM zone 31N + NN2000:2025 height", 11021, 11394); + return true; + case 365: + record = new EpsgCompoundCrsRecord(11403, "ETRS89-NOR [EUREF89] / UTM zone 32N + NN2000:2025 height", 11022, 11394); + return true; + case 366: + record = new EpsgCompoundCrsRecord(11404, "ETRS89-NOR [EUREF89] / UTM zone 33N + NN2000:2025 height", 11023, 11394); + return true; + case 367: + record = new EpsgCompoundCrsRecord(11405, "ETRS89-NOR [EUREF89] / UTM zone 34N + NN2000:2025 height", 11024, 11394); + return true; + case 368: + record = new EpsgCompoundCrsRecord(11406, "ETRS89-NOR [EUREF89] / UTM zone 35N + NN2000:2025 height", 11025, 11394); + return true; + case 369: + record = new EpsgCompoundCrsRecord(11407, "ETRS89-NOR [EUREF89] / UTM zone 36N + NN2000:2025 height", 11026, 11394); + return true; + case 370: + record = new EpsgCompoundCrsRecord(11408, "ETRS89-NOR [EUREF89] / NTM zone 8 + NN2000:2025 height", 5108, 11394); + return true; + case 371: + record = new EpsgCompoundCrsRecord(11409, "ETRS89-NOR [EUREF89] / NTM zone 9 + NN2000:2025 height", 5109, 11394); + return true; + case 372: + record = new EpsgCompoundCrsRecord(11410, "ETRS89-NOR [EUREF89] / NTM zone 10 + NN2000:2025 height", 5110, 11394); + return true; + case 373: + record = new EpsgCompoundCrsRecord(11411, "ETRS89-NOR [EUREF89] / NTM zone 11 + NN2000:2025 height", 5111, 11394); + return true; + case 374: + record = new EpsgCompoundCrsRecord(11412, "ETRS89-NOR [EUREF89] / NTM zone 12 + NN2000:2025 height", 5112, 11394); + return true; + case 375: + record = new EpsgCompoundCrsRecord(11413, "ETRS89-NOR [EUREF89] / NTM zone 13 + NN2000:2025 height", 5113, 11394); + return true; + case 376: + record = new EpsgCompoundCrsRecord(11414, "ETRS89-NOR [EUREF89] / NTM zone 14 + NN2000:2025 height", 5114, 11394); + return true; + case 377: + record = new EpsgCompoundCrsRecord(11415, "ETRS89-NOR [EUREF89] / NTM zone 15 + NN2000:2025 height", 5115, 11394); + return true; + case 378: + record = new EpsgCompoundCrsRecord(11416, "ETRS89-NOR [EUREF89] / NTM zone 16 + NN2000:2025 height", 5116, 11394); + return true; + case 379: + record = new EpsgCompoundCrsRecord(11417, "ETRS89-NOR [EUREF89] / NTM zone 17 + NN2000:2025 height", 5117, 11394); + return true; + case 380: + record = new EpsgCompoundCrsRecord(11418, "ETRS89-NOR [EUREF89] / NTM zone 18 + NN2000:2025 height", 5118, 11394); + return true; + case 381: + record = new EpsgCompoundCrsRecord(11419, "ETRS89-NOR [EUREF89] / NTM zone 19 + NN2000:2025 height", 5119, 11394); + return true; + case 382: + record = new EpsgCompoundCrsRecord(11420, "ETRS89-NOR [EUREF89] / NTM zone 20 + NN2000:2025 height", 5120, 11394); + return true; + case 383: + record = new EpsgCompoundCrsRecord(11421, "ETRS89-NOR [EUREF89] / NTM zone 21 + NN2000:2025 height", 5121, 11394); + return true; + case 384: + record = new EpsgCompoundCrsRecord(11422, "ETRS89-NOR [EUREF89] / NTM zone 22 + NN2000:2025 height", 5122, 11394); + return true; + case 385: + record = new EpsgCompoundCrsRecord(11423, "ETRS89-NOR [EUREF89] / NTM zone 23 + NN2000:2025 height", 5123, 11394); + return true; + case 386: + record = new EpsgCompoundCrsRecord(11424, "ETRS89-NOR [EUREF89] / NTM zone 24 + NN2000:2025 height", 5124, 11394); + return true; + case 387: + record = new EpsgCompoundCrsRecord(11425, "ETRS89-NOR [EUREF89] / NTM zone 25 + NN2000:2025 height", 5125, 11394); + return true; + case 388: + record = new EpsgCompoundCrsRecord(11426, "ETRS89-NOR [EUREF89] / NTM zone 26 + NN2000:2025 height", 5126, 11394); + return true; + case 389: + record = new EpsgCompoundCrsRecord(11427, "ETRS89-NOR [EUREF89] / NTM zone 27 + NN2000:2025 height", 5127, 11394); + return true; + case 390: + record = new EpsgCompoundCrsRecord(11428, "ETRS89-NOR [EUREF89] / NTM zone 28 + NN2000:2025 height", 5128, 11394); + return true; + case 391: + record = new EpsgCompoundCrsRecord(11429, "ETRS89-NOR [EUREF89] / NTM zone 29 + NN2000:2025 height", 5129, 11394); + return true; + case 392: + record = new EpsgCompoundCrsRecord(11430, "ETRS89-NOR [EUREF89] / NTM zone 30 + NN2000:2025 height", 5130, 11394); + return true; + case 393: + record = new EpsgCompoundCrsRecord(11435, "ETRS89-NOR [EUREF89] / NTM zone 5 + NN2000:2025 height", 5105, 11394); + return true; + case 394: + record = new EpsgCompoundCrsRecord(11436, "ETRS89-NOR [EUREF89] / NTM zone 6 + NN2000:2025 height", 5106, 11394); + return true; + case 395: + record = new EpsgCompoundCrsRecord(11437, "ETRS89-NOR [EUREF89] / NTM zone 7 + NN2000:2025 height", 5107, 11394); + return true; + case 396: + record = new EpsgCompoundCrsRecord(11447, "RGWF96 + NGFA 2022 height", 8900, 11446); + return true; + case 397: + record = new EpsgCompoundCrsRecord(20001, "ETRS89-NOR [EUREF89] + SVD2006 height", 10875, 20000); + return true; + case 398: + record = new EpsgCompoundCrsRecord(20003, "MWC18 Grid + ODN height", 20002, 5701); + return true; + case 399: + record = new EpsgCompoundCrsRecord(20037, "NAD83(CSRS)v4 + CGVD2013a(2002) height", 8246, 20034); + return true; + case 400: + record = new EpsgCompoundCrsRecord(20038, "NAD83(CSRS)v3 + CGVD2013a(1997) height", 8240, 20035); + return true; + case 401: + record = new EpsgCompoundCrsRecord(20043, "SRGI2013 + INAGeoid2020 v2 height", 9470, 20036); + return true; + default: + record = default; + return false; + } + } + + internal static readonly EpsgUnitRecord[] Units = new EpsgUnitRecord[] + { + new EpsgUnitRecord(9001, 0, 1.0d, "metre"), + new EpsgUnitRecord(9002, 0, 0.3048d, "foot"), + new EpsgUnitRecord(9003, 0, 0.304800609601219d, "US survey foot"), + new EpsgUnitRecord(9005, 0, 0.3047972654d, "Clarke's foot"), + new EpsgUnitRecord(9031, 0, 1.0000135965d, "German legal metre"), + new EpsgUnitRecord(9036, 0, 1000.0d, "kilometre"), + new EpsgUnitRecord(9037, 0, 0.9143917962d, "Clarke's yard"), + new EpsgUnitRecord(9039, 0, 0.201166195164d, "Clarke's link"), + new EpsgUnitRecord(9040, 0, 0.914398414616029d, "British yard (Sears 1922)"), + new EpsgUnitRecord(9041, 0, 0.304799471538676d, "British foot (Sears 1922)"), + new EpsgUnitRecord(9042, 0, 20.1167651215526d, "British chain (Sears 1922)"), + new EpsgUnitRecord(9080, 0, 0.304799510248147d, "Indian foot"), + new EpsgUnitRecord(9084, 0, 0.914398530744441d, "Indian yard"), + new EpsgUnitRecord(9094, 0, 0.304799710181509d, "Gold Coast foot"), + new EpsgUnitRecord(9095, 0, 0.3048007491d, "British foot (1936)"), + new EpsgUnitRecord(9098, 0, 0.201168d, "link"), + new EpsgUnitRecord(9101, 1, 1.0d, "radian"), + new EpsgUnitRecord(9102, 1, 0.0174532925199433d, "degree"), + new EpsgUnitRecord(9105, 1, 0.015707963267949d, "grad"), + new EpsgUnitRecord(9301, 0, 20.116756d, "British chain (Sears 1922 truncated)"), + }; + + internal static readonly EpsgAxisRecord[] Axes = new EpsgAxisRecord[] + { + new EpsgAxisRecord(1024, (byte)1, "Easting (M)", (sbyte)3, 9001), + new EpsgAxisRecord(1024, (byte)2, "Northing (P)", (sbyte)1, 9001), + new EpsgAxisRecord(1025, (byte)1, "Easting (X)", (sbyte)1, 9001), + new EpsgAxisRecord(1025, (byte)2, "Northing (Y)", (sbyte)1, 9001), + new EpsgAxisRecord(1026, (byte)1, "Easting (E)", (sbyte)2, 9001), + new EpsgAxisRecord(1026, (byte)2, "Northing (N)", (sbyte)2, 9001), + new EpsgAxisRecord(1027, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(1027, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(1028, (byte)1, "Easting (E)", (sbyte)3, 9037), + new EpsgAxisRecord(1028, (byte)2, "Northing (N)", (sbyte)1, 9037), + new EpsgAxisRecord(1029, (byte)1, "Northing (N)", (sbyte)1, 9002), + new EpsgAxisRecord(1029, (byte)2, "Easting (E)", (sbyte)3, 9002), + new EpsgAxisRecord(1030, (byte)1, "Gravity-related height (H)", (sbyte)5, 9002), + new EpsgAxisRecord(1031, (byte)1, "Northing (Y)", (sbyte)1, 9001), + new EpsgAxisRecord(1031, (byte)2, "Westing (X)", (sbyte)4, 9001), + new EpsgAxisRecord(1035, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(1035, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(1036, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(1036, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(1037, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(1037, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(1038, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(1038, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(1039, (byte)1, "Easting (E)", (sbyte)3, 9002), + new EpsgAxisRecord(1039, (byte)2, "Northing (N)", (sbyte)1, 9002), + new EpsgAxisRecord(1042, (byte)3, "Platform Up (z)", (sbyte)5, 9001), + new EpsgAxisRecord(1043, (byte)1, "Depth (D)", (sbyte)6, 9003), + new EpsgAxisRecord(1044, (byte)1, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(1044, (byte)2, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(1046, (byte)1, "Northing (X)", (sbyte)1, 9001), + new EpsgAxisRecord(1046, (byte)2, "Easting (Y)", (sbyte)3, 9001), + new EpsgAxisRecord(1046, (byte)3, "Ellipsoidal height (h)", (sbyte)5, 9001), + new EpsgAxisRecord(1047, (byte)1, "Local northing (n)", (sbyte)1, 9001), + new EpsgAxisRecord(1047, (byte)2, "Local easting (e)", (sbyte)3, 9001), + new EpsgAxisRecord(1048, (byte)1, "Local northing (n)", (sbyte)1, 9002), + new EpsgAxisRecord(1048, (byte)2, "Local easting (e)", (sbyte)3, 9002), + new EpsgAxisRecord(1049, (byte)1, "Local depth (d)", (sbyte)6, 9001), + new EpsgAxisRecord(1050, (byte)1, "Local depth (d)", (sbyte)6, 9002), + new EpsgAxisRecord(1053, (byte)1, "Northing (N)", (sbyte)1, 9003), + new EpsgAxisRecord(1053, (byte)2, "Easting (E)", (sbyte)3, 9003), + new EpsgAxisRecord(1054, (byte)1, "Easting (x)", (sbyte)3, 9001), + new EpsgAxisRecord(1054, (byte)2, "Northing (y)", (sbyte)1, 9001), + new EpsgAxisRecord(4400, (byte)1, "Easting (E)", (sbyte)3, 9001), + new EpsgAxisRecord(4400, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4402, (byte)1, "Easting (E)", (sbyte)3, 9042), + new EpsgAxisRecord(4402, (byte)2, "Northing (N)", (sbyte)1, 9042), + new EpsgAxisRecord(4403, (byte)1, "Easting (E)", (sbyte)3, 9005), + new EpsgAxisRecord(4403, (byte)2, "Northing (N)", (sbyte)1, 9005), + new EpsgAxisRecord(4404, (byte)1, "Easting (E)", (sbyte)3, 9094), + new EpsgAxisRecord(4404, (byte)2, "Northing (N)", (sbyte)1, 9094), + new EpsgAxisRecord(4405, (byte)1, "Easting (E)", (sbyte)3, 9041), + new EpsgAxisRecord(4405, (byte)2, "Northing (N)", (sbyte)1, 9041), + new EpsgAxisRecord(4406, (byte)1, "Easting (X)", (sbyte)3, 9036), + new EpsgAxisRecord(4406, (byte)2, "Northing (Y)", (sbyte)1, 9036), + new EpsgAxisRecord(4407, (byte)1, "Easting (E)", (sbyte)3, 9039), + new EpsgAxisRecord(4407, (byte)2, "Northing (N)", (sbyte)1, 9039), + new EpsgAxisRecord(4408, (byte)1, "Easting (E)", (sbyte)3, 9084), + new EpsgAxisRecord(4408, (byte)2, "Northing (N)", (sbyte)1, 9084), + new EpsgAxisRecord(4409, (byte)1, "Easting (E)", (sbyte)3, 9040), + new EpsgAxisRecord(4409, (byte)2, "Northing (N)", (sbyte)1, 9040), + new EpsgAxisRecord(4410, (byte)1, "Easting (E)", (sbyte)3, 9301), + new EpsgAxisRecord(4410, (byte)2, "Northing (N)", (sbyte)1, 9301), + new EpsgAxisRecord(4463, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(4463, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(4464, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(4464, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(4465, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(4465, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(4466, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(4466, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(4467, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(4467, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(4468, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(4468, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(4469, (byte)1, "Easting (X)", (sbyte)2, 9001), + new EpsgAxisRecord(4469, (byte)2, "Northing (Y)", (sbyte)2, 9001), + new EpsgAxisRecord(4470, (byte)1, "Easting (X)", (sbyte)1, 9001), + new EpsgAxisRecord(4470, (byte)2, "Northing (Y)", (sbyte)1, 9001), + new EpsgAxisRecord(4471, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4471, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4472, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4472, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4473, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4473, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4474, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4474, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4475, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4475, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4476, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4476, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4477, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4477, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4478, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4478, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4479, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4479, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4480, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4480, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4481, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4481, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4482, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4482, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4483, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4483, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4484, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4484, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4485, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4485, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4486, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4486, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4487, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4487, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4488, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4488, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4489, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4489, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4490, (byte)1, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4490, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4491, (byte)1, "Westing (W)", (sbyte)4, 9001), + new EpsgAxisRecord(4491, (byte)2, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4493, (byte)1, "Northing (N)", (sbyte)2, 9001), + new EpsgAxisRecord(4493, (byte)2, "Easting (E)", (sbyte)2, 9001), + new EpsgAxisRecord(4494, (byte)1, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4494, (byte)2, "Easting (E)", (sbyte)1, 9001), + new EpsgAxisRecord(4495, (byte)1, "Easting (X)", (sbyte)3, 9002), + new EpsgAxisRecord(4495, (byte)2, "Northing (Y)", (sbyte)1, 9002), + new EpsgAxisRecord(4496, (byte)1, "Easting (E(X))", (sbyte)3, 9001), + new EpsgAxisRecord(4496, (byte)2, "Northing (N(Y))", (sbyte)1, 9001), + new EpsgAxisRecord(4497, (byte)1, "Easting (X)", (sbyte)3, 9003), + new EpsgAxisRecord(4497, (byte)2, "Northing (Y)", (sbyte)1, 9003), + new EpsgAxisRecord(4498, (byte)1, "Easting (Y)", (sbyte)3, 9001), + new EpsgAxisRecord(4498, (byte)2, "Northing (X)", (sbyte)1, 9001), + new EpsgAxisRecord(4499, (byte)1, "Easting (X)", (sbyte)3, 9001), + new EpsgAxisRecord(4499, (byte)2, "Northing (Y)", (sbyte)1, 9001), + new EpsgAxisRecord(4500, (byte)1, "Northing (N)", (sbyte)1, 9001), + new EpsgAxisRecord(4500, (byte)2, "Easting (E)", (sbyte)3, 9001), + new EpsgAxisRecord(4502, (byte)1, "Northing (N)", (sbyte)1, 9005), + new EpsgAxisRecord(4502, (byte)2, "Easting (E)", (sbyte)3, 9005), + new EpsgAxisRecord(4530, (byte)1, "Northing (X)", (sbyte)1, 9001), + new EpsgAxisRecord(4530, (byte)2, "Easting (Y)", (sbyte)3, 9001), + new EpsgAxisRecord(4531, (byte)1, "Northing (x)", (sbyte)1, 9001), + new EpsgAxisRecord(4531, (byte)2, "Easting (y)", (sbyte)3, 9001), + new EpsgAxisRecord(4532, (byte)1, "Northing (Y)", (sbyte)1, 9001), + new EpsgAxisRecord(4532, (byte)2, "Easting (X)", (sbyte)3, 9001), + new EpsgAxisRecord(4533, (byte)1, "Northing (X)", (sbyte)1, 9098), + new EpsgAxisRecord(4533, (byte)2, "Easting (Y)", (sbyte)3, 9098), + new EpsgAxisRecord(4534, (byte)1, "Northing (none)", (sbyte)1, 9001), + new EpsgAxisRecord(4534, (byte)2, "Easting (none)", (sbyte)3, 9001), + new EpsgAxisRecord(6403, (byte)1, "Geodetic latitude (Lat)", (sbyte)1, 9105), + new EpsgAxisRecord(6403, (byte)2, "Geodetic longitude (Lon)", (sbyte)3, 9105), + new EpsgAxisRecord(6422, (byte)1, "Geodetic latitude (Lat)", (sbyte)1, 9102), + new EpsgAxisRecord(6422, (byte)2, "Geodetic longitude (Lon)", (sbyte)3, 9102), + new EpsgAxisRecord(6423, (byte)1, "Geodetic latitude (Lat)", (sbyte)1, 9102), + new EpsgAxisRecord(6423, (byte)2, "Geodetic longitude (Lon)", (sbyte)3, 9102), + new EpsgAxisRecord(6423, (byte)3, "Ellipsoidal height (h)", (sbyte)5, 9001), + new EpsgAxisRecord(6424, (byte)1, "Geodetic longitude (Lon)", (sbyte)3, 9102), + new EpsgAxisRecord(6424, (byte)2, "Geodetic latitude (Lat)", (sbyte)1, 9102), + new EpsgAxisRecord(6426, (byte)1, "Geodetic longitude (Lon)", (sbyte)3, 9102), + new EpsgAxisRecord(6426, (byte)2, "Geodetic latitude (Lat)", (sbyte)1, 9102), + new EpsgAxisRecord(6426, (byte)3, "Ellipsoidal height (h)", (sbyte)5, 9001), + new EpsgAxisRecord(6496, (byte)1, "Gravity-related height (H)", (sbyte)5, 9095), + new EpsgAxisRecord(6497, (byte)1, "Gravity-related height (H)", (sbyte)5, 9003), + new EpsgAxisRecord(6498, (byte)1, "Depth (D)", (sbyte)6, 9001), + new EpsgAxisRecord(6499, (byte)1, "Gravity-related height (H)", (sbyte)5, 9001), + new EpsgAxisRecord(6500, (byte)1, "Geocentric X (X)", (sbyte)0, 9001), + new EpsgAxisRecord(6500, (byte)2, "Geocentric Y (Y)", (sbyte)3, 9001), + new EpsgAxisRecord(6500, (byte)3, "Geocentric Z (Z)", (sbyte)1, 9001), + new EpsgAxisRecord(6501, (byte)1, "Southing (X)", (sbyte)2, 9001), + new EpsgAxisRecord(6501, (byte)2, "Westing (Y)", (sbyte)4, 9001), + new EpsgAxisRecord(6502, (byte)1, "Westing (Y)", (sbyte)4, 9031), + new EpsgAxisRecord(6502, (byte)2, "Southing (X)", (sbyte)2, 9031), + new EpsgAxisRecord(6503, (byte)1, "Westing (Y)", (sbyte)4, 9001), + new EpsgAxisRecord(6503, (byte)2, "Southing (X)", (sbyte)2, 9001), + new EpsgAxisRecord(6507, (byte)1, "First local axis (X)", (sbyte)1, 9001), + new EpsgAxisRecord(6507, (byte)2, "Second local axis (Y)", (sbyte)4, 9001), + new EpsgAxisRecord(6509, (byte)1, "Southing (P)", (sbyte)2, 9001), + new EpsgAxisRecord(6509, (byte)2, "Westing (M)", (sbyte)4, 9001), + new EpsgAxisRecord(6510, (byte)1, "Plant East (x)", (sbyte)1, 9001), + new EpsgAxisRecord(6510, (byte)2, "Plant North (y)", (sbyte)1, 9001), + }; + + internal static readonly EpsgEllipsoidRecord[] Ellipsoids = new EpsgEllipsoidRecord[] + { + new EpsgEllipsoidRecord(1024, "CGCS2000", 6378137.0d, 6356752.314140356d, 298.257222101d, true, 9001), + new EpsgEllipsoidRecord(1025, "GSK-2011", 6378136.5d, 6356751.757955603d, 298.2564151d, true, 9001), + new EpsgEllipsoidRecord(1026, "Zach 1812", 6376045.0d, 6355477.112903226d, 310.0d, true, 9001), + new EpsgEllipsoidRecord(7001, "Airy 1830", 6377563.396d, 6356256.909237285d, 299.3249646d, true, 9001), + new EpsgEllipsoidRecord(7002, "Airy Modified 1849", 6377340.189d, 6356034.447938534d, 299.3249646d, true, 9001), + new EpsgEllipsoidRecord(7003, "Australian National Spheroid", 6378160.0d, 6356774.719195305d, 298.25d, true, 9001), + new EpsgEllipsoidRecord(7004, "Bessel 1841", 6377397.155d, 6356078.962818189d, 299.1528128d, true, 9001), + new EpsgEllipsoidRecord(7005, "Bessel Modified", 6377492.018d, 6356173.508712696d, 299.1528128d, true, 9001), + new EpsgEllipsoidRecord(7007, "Clarke 1858", 20926348.0d, 20855233.0d, 294.260676369257d, true, 9005), + new EpsgEllipsoidRecord(7008, "Clarke 1866", 6378206.4d, 6356583.8d, 294.978698213901d, true, 9001), + new EpsgEllipsoidRecord(7010, "Clarke 1880 (Benoit)", 6378300.789d, 6356566.435d, 293.46631553898d, true, 9001), + new EpsgEllipsoidRecord(7011, "Clarke 1880 (IGN)", 6378249.2d, 6356515.0d, 293.466021293627d, true, 9001), + new EpsgEllipsoidRecord(7012, "Clarke 1880 (RGS)", 6378249.145d, 6356514.8695497755d, 293.465d, true, 9001), + new EpsgEllipsoidRecord(7013, "Clarke 1880 (Arc)", 6378249.145d, 6356514.966398753d, 293.4663077d, true, 9001), + new EpsgEllipsoidRecord(7015, "Everest 1830 (1937 Adjustment)", 6377276.345d, 6356075.41314024d, 300.8017d, true, 9001), + new EpsgEllipsoidRecord(7016, "Everest 1830 (1967 Definition)", 6377298.556d, 6356097.550300896d, 300.8017d, true, 9001), + new EpsgEllipsoidRecord(7018, "Everest 1830 Modified", 6377304.063d, 6356103.038993155d, 300.8017d, true, 9001), + new EpsgEllipsoidRecord(7019, "GRS 1980", 6378137.0d, 6356752.314140356d, 298.257222101d, true, 9001), + new EpsgEllipsoidRecord(7020, "Helmert 1906", 6378200.0d, 6356818.169627891d, 298.3d, true, 9001), + new EpsgEllipsoidRecord(7021, "Indonesian National Spheroid", 6378160.0d, 6356774.50408554d, 298.247d, true, 9001), + new EpsgEllipsoidRecord(7022, "International 1924", 6378388.0d, 6356911.9461279465d, 297.0d, true, 9001), + new EpsgEllipsoidRecord(7024, "Krassowsky 1940", 6378245.0d, 6356863.018773047d, 298.3d, true, 9001), + new EpsgEllipsoidRecord(7025, "NWL 9D", 6378145.0d, 6356759.769488684d, 298.25d, true, 9001), + new EpsgEllipsoidRecord(7027, "Plessis 1817", 6376523.0d, 6355862.933255573d, 308.64d, true, 9001), + new EpsgEllipsoidRecord(7028, "Struve 1860", 6378298.3d, 6356657.142669561d, 294.73d, true, 9001), + new EpsgEllipsoidRecord(7029, "War Office", 6378300.0d, 6356751.689189189d, 296.0d, true, 9001), + new EpsgEllipsoidRecord(7030, "WGS 84", 6378137.0d, 6356752.314245179d, 298.257223563d, true, 9001), + new EpsgEllipsoidRecord(7034, "Clarke 1880", 20926202.0d, 20854895.0d, 293.466307655625d, true, 9005), + new EpsgEllipsoidRecord(7036, "GRS 1967", 6378160.0d, 6356774.516090714d, 298.247167427d, true, 9001), + new EpsgEllipsoidRecord(7041, "Average Terrestrial System 1977", 6378135.0d, 6356750.304921594d, 298.257d, true, 9001), + new EpsgEllipsoidRecord(7042, "Everest (1830 Definition)", 20922931.8d, 20853374.58d, 300.801725543365d, true, 9080), + new EpsgEllipsoidRecord(7043, "WGS 72", 6378135.0d, 6356750.520016094d, 298.26d, true, 9001), + new EpsgEllipsoidRecord(7044, "Everest 1830 (1962 Definition)", 6377301.243d, 6356100.230165384d, 300.8017255d, true, 9001), + new EpsgEllipsoidRecord(7045, "Everest 1830 (1975 Definition)", 6377299.151d, 6356098.145120132d, 300.8017255d, true, 9001), + new EpsgEllipsoidRecord(7046, "Bessel Namibia (GLM)", 6377397.155d, 6356078.962818189d, 299.1528128d, true, 9031), + new EpsgEllipsoidRecord(7049, "IAG 1975", 6378140.0d, 6356755.288157528d, 298.257d, true, 9001), + new EpsgEllipsoidRecord(7050, "GRS 1967 Modified", 6378160.0d, 6356774.719195305d, 298.25d, true, 9001), + new EpsgEllipsoidRecord(7051, "Danish 1876", 6377019.27d, 6355762.5391d, 300.0d, true, 9001), + new EpsgEllipsoidRecord(7053, "Hough 1960", 6378270.0d, 6356794.343434343d, 297.0d, true, 9001), + new EpsgEllipsoidRecord(7054, "PZ-90", 6378136.0d, 6356751.361745712d, 298.257839303d, true, 9001), + new EpsgEllipsoidRecord(7055, "Clarke 1880 (international foot)", 20926202.0d, 20854895.0d, 293.466307655625d, true, 9002), + new EpsgEllipsoidRecord(7056, "Everest 1830 (RSO 1969)", 6377295.664d, 6356094.667915204d, 300.8017d, true, 9001), + new EpsgEllipsoidRecord(7057, "International 1924 Authalic Sphere", 6371228.0d, 6371228.0d, 0.0d, false, 9001), + new EpsgEllipsoidRecord(7058, "Hughes 1980", 6378273.0d, 6356889.449d, 298.279411123061d, true, 9001), + }; + + internal static readonly EpsgPrimeMeridianRecord[] PrimeMeridians = new EpsgPrimeMeridianRecord[] + { + new EpsgPrimeMeridianRecord(8901, "Greenwich", 0.0d, 9102), + new EpsgPrimeMeridianRecord(8902, "Lisbon", -9.131906111d, 9102), + new EpsgPrimeMeridianRecord(8903, "Paris", 0.040792344d, 9101), + new EpsgPrimeMeridianRecord(8904, "Bogota", -74.080916667d, 9102), + new EpsgPrimeMeridianRecord(8905, "Madrid", -3.687375d, 9102), + new EpsgPrimeMeridianRecord(8906, "Rome", 12.452333333d, 9102), + new EpsgPrimeMeridianRecord(8907, "Bern", 7.439583333d, 9102), + new EpsgPrimeMeridianRecord(8908, "Jakarta", 106.807719444d, 9102), + new EpsgPrimeMeridianRecord(8909, "Ferro", -17.666666667d, 9102), + new EpsgPrimeMeridianRecord(8910, "Brussels", 4.367975d, 9102), + new EpsgPrimeMeridianRecord(8911, "Stockholm", 18.058277778d, 9102), + new EpsgPrimeMeridianRecord(8912, "Athens", 23.7163375d, 9102), + new EpsgPrimeMeridianRecord(8913, "Oslo", 10.722916667d, 9102), + new EpsgPrimeMeridianRecord(8914, "Paris RGS", 2.337208333d, 9102), + }; + + internal static readonly EpsgGeodeticDatumRecord[] GeodeticDatums = new EpsgGeodeticDatumRecord[] + { + new EpsgGeodeticDatumRecord(1024, "Hungarian Datum 1909", 7004, 8901), + new EpsgGeodeticDatumRecord(1025, "Taiwan Datum 1967", 7050, 8901), + new EpsgGeodeticDatumRecord(1026, "Taiwan Datum 1997", 7019, 8901), + new EpsgGeodeticDatumRecord(1029, "Iraqi Geospatial Reference System", 7019, 8901), + new EpsgGeodeticDatumRecord(1031, "MGI 1901", 7004, 8901), + new EpsgGeodeticDatumRecord(1032, "MOLDREF99", 7019, 8901), + new EpsgGeodeticDatumRecord(1033, "Reseau Geodesique de la RDC 2005", 7019, 8901), + new EpsgGeodeticDatumRecord(1034, "Serbian Reference Network 1998", 7019, 8901), + new EpsgGeodeticDatumRecord(1035, "Red Geodesica de Canarias 1995", 7019, 8901), + new EpsgGeodeticDatumRecord(1036, "Reseau Geodesique de Mayotte 2004", 7019, 8901), + new EpsgGeodeticDatumRecord(1037, "Cadastre 1997", 7022, 8901), + new EpsgGeodeticDatumRecord(1038, "Reseau Geodesique de Saint Pierre et Miquelon 2006", 7019, 8901), + new EpsgGeodeticDatumRecord(1041, "Autonomous Regions of Portugal 2008", 7019, 8901), + new EpsgGeodeticDatumRecord(1042, "Mexico ITRF92", 7019, 8901), + new EpsgGeodeticDatumRecord(1043, "China 2000", 1024, 8901), + new EpsgGeodeticDatumRecord(1044, "Sao Tome", 7022, 8901), + new EpsgGeodeticDatumRecord(1045, "New Beijing", 7024, 8901), + new EpsgGeodeticDatumRecord(1046, "Principe", 7022, 8901), + new EpsgGeodeticDatumRecord(1047, "Reseau de Reference des Antilles Francaises 1991", 7019, 8901), + new EpsgGeodeticDatumRecord(1048, "Tokyo 1892", 7004, 8901), + new EpsgGeodeticDatumRecord(1052, "System of the Unified Trigonometrical Cadastral Network/05", 7004, 8901), + new EpsgGeodeticDatumRecord(1053, "Sri Lanka Datum 1999", 7015, 8901), + new EpsgGeodeticDatumRecord(1055, "System of the Unified Trigonometrical Cadastral Network/05 (Ferro)", 7004, 8909), + new EpsgGeodeticDatumRecord(1056, "Geocentric Datum Brunei Darussalam 2009", 7019, 8901), + new EpsgGeodeticDatumRecord(1057, "Turkish National Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1058, "Bhutan National Geodetic Datum", 7019, 8901), + new EpsgGeodeticDatumRecord(1060, "Islands Net 2004", 7019, 8901), + new EpsgGeodeticDatumRecord(1061, "International Terrestrial Reference Frame 2008", 7019, 8901), + new EpsgGeodeticDatumRecord(1062, "Posiciones Geodesicas Argentinas 2007", 7030, 8901), + new EpsgGeodeticDatumRecord(1063, "Marco Geodesico Nacional de Bolivia", 7019, 8901), + new EpsgGeodeticDatumRecord(1064, "SIRGAS-Chile 2002", 7019, 8901), + new EpsgGeodeticDatumRecord(1065, "Costa Rica 2005", 7030, 8901), + new EpsgGeodeticDatumRecord(1066, "Sistema Geodesico Nacional de Panama MACARIO SOLIS", 7019, 8901), + new EpsgGeodeticDatumRecord(1067, "Peru96", 7019, 8901), + new EpsgGeodeticDatumRecord(1068, "SIRGAS-ROU98", 7030, 8901), + new EpsgGeodeticDatumRecord(1069, "SIRGAS_ES2007.8", 7019, 8901), + new EpsgGeodeticDatumRecord(1070, "Ocotepeque 1935", 7008, 8901), + new EpsgGeodeticDatumRecord(1071, "Sibun Gorge 1922", 7007, 8901), + new EpsgGeodeticDatumRecord(1072, "Panama-Colon 1911", 7008, 8901), + new EpsgGeodeticDatumRecord(1073, "Reseau Geodesique des Antilles Francaises 2009", 7019, 8901), + new EpsgGeodeticDatumRecord(1074, "Corrego Alegre 1961", 7022, 8901), + new EpsgGeodeticDatumRecord(1075, "South American Datum 1969(96)", 7050, 8901), + new EpsgGeodeticDatumRecord(1076, "Papua New Guinea Geodetic Datum 1994", 7019, 8901), + new EpsgGeodeticDatumRecord(1077, "Ukraine 2000", 7024, 8901), + new EpsgGeodeticDatumRecord(1078, "Fehmarnbelt Datum 2010", 7019, 8901), + new EpsgGeodeticDatumRecord(1081, "Deutsche Bahn Reference System", 7004, 8901), + new EpsgGeodeticDatumRecord(1095, "Tonga Geodetic Datum 2005", 7019, 8901), + new EpsgGeodeticDatumRecord(1100, "Cayman Islands Geodetic Datum 2011", 7019, 8901), + new EpsgGeodeticDatumRecord(1111, "Nepal 1981", 7015, 8901), + new EpsgGeodeticDatumRecord(1112, "Cyprus Geodetic Reference System 1993", 7030, 8901), + new EpsgGeodeticDatumRecord(1113, "Reseau Geodesique des Terres Australes et Antarctiques Francaises 2007", 7019, 8901), + new EpsgGeodeticDatumRecord(1114, "Israeli Geodetic Datum 2005", 7030, 8901), + new EpsgGeodeticDatumRecord(1115, "Israeli Geodetic Datum 2005(2012)", 7030, 8901), + new EpsgGeodeticDatumRecord(1116, "NAD83 (National Spatial Reference System 2011)", 7019, 8901), + new EpsgGeodeticDatumRecord(1117, "NAD83 (National Spatial Reference System PA11)", 7019, 8901), + new EpsgGeodeticDatumRecord(1118, "NAD83 (National Spatial Reference System MA11)", 7019, 8901), + new EpsgGeodeticDatumRecord(1120, "Mexico ITRF2008", 7019, 8901), + new EpsgGeodeticDatumRecord(1128, "Japanese Geodetic Datum 2011", 7019, 8901), + new EpsgGeodeticDatumRecord(1132, "Rete Dinamica Nazionale 2008", 7019, 8901), + new EpsgGeodeticDatumRecord(1133, "NAD83 (Continuously Operating Reference Station 1996)", 7019, 8901), + new EpsgGeodeticDatumRecord(1135, "Aden 1925", 7012, 8901), + new EpsgGeodeticDatumRecord(1136, "Bioko", 7022, 8901), + new EpsgGeodeticDatumRecord(1137, "Bekaa Valley 1920", 7012, 8901), + new EpsgGeodeticDatumRecord(1138, "South East Island 1943", 7012, 8901), + new EpsgGeodeticDatumRecord(1139, "Gambia", 7012, 8901), + new EpsgGeodeticDatumRecord(1141, "IGS08", 7019, 8901), + new EpsgGeodeticDatumRecord(1142, "IG05 Intermediate Datum", 7019, 8901), + new EpsgGeodeticDatumRecord(1144, "IG05/12 Intermediate Datum", 7019, 8901), + new EpsgGeodeticDatumRecord(1147, "Oman National Geodetic Datum 2014", 7019, 8901), + new EpsgGeodeticDatumRecord(1152, "World Geodetic System 1984 (G730)", 7030, 8901), + new EpsgGeodeticDatumRecord(1153, "World Geodetic System 1984 (G873)", 7030, 8901), + new EpsgGeodeticDatumRecord(1154, "World Geodetic System 1984 (G1150)", 7030, 8901), + new EpsgGeodeticDatumRecord(1155, "World Geodetic System 1984 (G1674)", 7030, 8901), + new EpsgGeodeticDatumRecord(1156, "World Geodetic System 1984 (G1762)", 7030, 8901), + new EpsgGeodeticDatumRecord(1157, "Parametry Zemli 1990.02", 7054, 8901), + new EpsgGeodeticDatumRecord(1158, "Parametry Zemli 1990.11", 7054, 8901), + new EpsgGeodeticDatumRecord(1159, "Geodezicheskaya Sistema Koordinat 2011", 1025, 8901), + new EpsgGeodeticDatumRecord(1160, "Kyrgyzstan Geodetic Datum 2006", 7019, 8901), + new EpsgGeodeticDatumRecord(1165, "International Terrestrial Reference Frame 2014", 7019, 8901), + new EpsgGeodeticDatumRecord(1166, "World Geodetic System 1984 (Transit)", 7030, 8901), + new EpsgGeodeticDatumRecord(1167, "Bulgaria Geodetic System 2005", 7019, 8901), + new EpsgGeodeticDatumRecord(1168, "Geocentric Datum of Australia 2020", 7019, 8901), + new EpsgGeodeticDatumRecord(1173, "St. Helena Tritan", 7030, 8901), + new EpsgGeodeticDatumRecord(1174, "St. Helena Geodetic Datum 2015", 7019, 8901), + new EpsgGeodeticDatumRecord(1178, "European Terrestrial Reference Frame 1989", 7019, 8901), + new EpsgGeodeticDatumRecord(1179, "European Terrestrial Reference Frame 1990", 7019, 8901), + new EpsgGeodeticDatumRecord(1180, "European Terrestrial Reference Frame 1991", 7019, 8901), + new EpsgGeodeticDatumRecord(1181, "European Terrestrial Reference Frame 1992", 7019, 8901), + new EpsgGeodeticDatumRecord(1182, "European Terrestrial Reference Frame 1993", 7019, 8901), + new EpsgGeodeticDatumRecord(1183, "European Terrestrial Reference Frame 1994", 7019, 8901), + new EpsgGeodeticDatumRecord(1184, "European Terrestrial Reference Frame 1996", 7019, 8901), + new EpsgGeodeticDatumRecord(1185, "European Terrestrial Reference Frame 1997", 7019, 8901), + new EpsgGeodeticDatumRecord(1186, "European Terrestrial Reference Frame 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(1187, "Islands Net 2016", 7019, 8901), + new EpsgGeodeticDatumRecord(1188, "Gusterberg (Ferro)", 1026, 8909), + new EpsgGeodeticDatumRecord(1189, "St. Stephen (Ferro)", 1026, 8909), + new EpsgGeodeticDatumRecord(1191, "IGS14", 7019, 8901), + new EpsgGeodeticDatumRecord(1192, "North American Datum of 1983 (CSRS96)", 7019, 8901), + new EpsgGeodeticDatumRecord(1193, "North American Datum of 1983 (CSRS) version 2", 7019, 8901), + new EpsgGeodeticDatumRecord(1194, "North American Datum of 1983 (CSRS) version 3", 7019, 8901), + new EpsgGeodeticDatumRecord(1195, "North American Datum of 1983 (CSRS) version 4", 7019, 8901), + new EpsgGeodeticDatumRecord(1196, "North American Datum of 1983 (CSRS) version 5", 7019, 8901), + new EpsgGeodeticDatumRecord(1197, "North American Datum of 1983 (CSRS) version 6", 7019, 8901), + new EpsgGeodeticDatumRecord(1198, "North American Datum of 1983 (CSRS) version 7", 7019, 8901), + new EpsgGeodeticDatumRecord(1201, "System of the Unified Trigonometrical Cadastral Network [JTSK03]", 7004, 8901), + new EpsgGeodeticDatumRecord(1204, "European Terrestrial Reference Frame 2005", 7019, 8901), + new EpsgGeodeticDatumRecord(1206, "European Terrestrial Reference Frame 2014", 7019, 8901), + new EpsgGeodeticDatumRecord(1207, "Macao 1920", 7022, 8901), + new EpsgGeodeticDatumRecord(1208, "Macao Geodetic Datum 2008", 7019, 8901), + new EpsgGeodeticDatumRecord(1209, "Hong Kong Geodetic", 7019, 8901), + new EpsgGeodeticDatumRecord(1211, "NAD83 (Federal Base Network)", 7019, 8901), + new EpsgGeodeticDatumRecord(1212, "NAD83 (High Accuracy Reference Network - Corrected)", 7019, 8901), + new EpsgGeodeticDatumRecord(1214, "Serbian Spatial Reference System 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(1217, "Camacupa 2015", 7012, 8901), + new EpsgGeodeticDatumRecord(1218, "MOMRA Terrestrial Reference Frame 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(1220, "Reference System de Angola 2013", 7019, 8901), + new EpsgGeodeticDatumRecord(1221, "North American Datum of 1983 (MARP00)", 7019, 8901), + new EpsgGeodeticDatumRecord(1223, "Reseau Geodesique de Wallis et Futuna 1996", 7019, 8901), + new EpsgGeodeticDatumRecord(1225, "CR-SIRGAS epoch 2014.59", 7019, 8901), + new EpsgGeodeticDatumRecord(1227, "SIRGAS Continuously Operating Network DGF00P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1228, "SIRGAS Continuously Operating Network DGF01P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1229, "SIRGAS Continuously Operating Network DGF01P02", 7019, 8901), + new EpsgGeodeticDatumRecord(1230, "SIRGAS Continuously Operating Network DGF02P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1231, "SIRGAS Continuously Operating Network DGF04P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1232, "SIRGAS Continuously Operating Network DGF05P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1233, "SIRGAS Continuously Operating Network DGF06P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1234, "SIRGAS Continuously Operating Network DGF07P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1235, "SIRGAS Continuously Operating Network DGF08P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1236, "SIRGAS Continuously Operating Network SIR09P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1237, "SIRGAS Continuously Operating Network SIR10P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1238, "SIRGAS Continuously Operating Network SIR11P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1239, "SIRGAS Continuously Operating Network SIR13P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1240, "SIRGAS Continuously Operating Network SIR14P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1241, "SIRGAS Continuously Operating Network SIR15P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1242, "SIRGAS Continuously Operating Network SIR17P01", 7019, 8901), + new EpsgGeodeticDatumRecord(1244, "IGS97", 7019, 8901), + new EpsgGeodeticDatumRecord(1245, "IGS00", 7019, 8901), + new EpsgGeodeticDatumRecord(1246, "IGb00", 7019, 8901), + new EpsgGeodeticDatumRecord(1247, "IGS05", 7019, 8901), + new EpsgGeodeticDatumRecord(1248, "IGb08", 7019, 8901), + new EpsgGeodeticDatumRecord(1249, "North American Datum of 1983 (PACP00)", 7019, 8901), + new EpsgGeodeticDatumRecord(1251, "Kosovo Reference System 2001", 7019, 8901), + new EpsgGeodeticDatumRecord(1252, "SIRGAS-Chile 2013", 7019, 8901), + new EpsgGeodeticDatumRecord(1253, "SIRGAS-Chile 2016", 7019, 8901), + new EpsgGeodeticDatumRecord(1257, "Tapi Aike", 7022, 8901), + new EpsgGeodeticDatumRecord(1258, "Ministerio de Marina Norte", 7022, 8901), + new EpsgGeodeticDatumRecord(1259, "Ministerio de Marina Sur", 7022, 8901), + new EpsgGeodeticDatumRecord(1263, "Oman National Geodetic Datum 2017", 7019, 8901), + new EpsgGeodeticDatumRecord(1264, "HS2 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1266, "TPEN11 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1268, "Kingdom of Saudi Arabia Geodetic Reference Frame 2017", 7019, 8901), + new EpsgGeodeticDatumRecord(1271, "MML07 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1272, "IGb14", 7019, 8901), + new EpsgGeodeticDatumRecord(1273, "AbInvA96_2020 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1286, "Pico de las Nieves 1968", 7022, 8901), + new EpsgGeodeticDatumRecord(1289, "GBK19 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1291, "Australian Terrestrial Reference Frame 2014", 7019, 8901), + new EpsgGeodeticDatumRecord(1293, "Sistem Referensi Geospasial Indonesia 2013", 7030, 8901), + new EpsgGeodeticDatumRecord(1295, "Lyon Turin Ferroviaire 2004", 7019, 8901), + new EpsgGeodeticDatumRecord(1304, "Red Geodesica Para Mineria en Chile", 7019, 8901), + new EpsgGeodeticDatumRecord(1305, "ETRF2000 Poland", 7019, 8901), + new EpsgGeodeticDatumRecord(1308, "EOS21 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1309, "World Geodetic System 1984 (G2139)", 7030, 8901), + new EpsgGeodeticDatumRecord(1310, "ECML14_NB Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1311, "EWR2 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1312, "Reseau Geodesique Francais 1993 v2", 7019, 8901), + new EpsgGeodeticDatumRecord(1313, "Reseau Geodesique Francais 1993 v2b", 7019, 8901), + new EpsgGeodeticDatumRecord(1314, "MRH21 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1315, "MOLDOR11 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1317, "HULLEE13 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1319, "EBBWV14 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1320, "SCM22 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1321, "FNL22 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1322, "International Terrestrial Reference Frame 2020", 7019, 8901), + new EpsgGeodeticDatumRecord(1324, "MWC18 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1327, "SIRGAS-Chile 2021", 7019, 8901), + new EpsgGeodeticDatumRecord(1329, "Marco Geocentrico Nacional de Referencia 2018", 7019, 8901), + new EpsgGeodeticDatumRecord(1332, "System 34 Jylland Intermediate Datum", 7022, 8901), + new EpsgGeodeticDatumRecord(1333, "IGS20", 7019, 8901), + new EpsgGeodeticDatumRecord(1334, "DoPw22 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1335, "ShAb07 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1336, "CNH22 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1337, "System 34 Sjaelland Intermediate Datum", 7022, 8901), + new EpsgGeodeticDatumRecord(1338, "CWS13 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1339, "DIBA15 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1340, "GWPBS22 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1341, "GWWAB22 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1342, "GWWWA22 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1343, "MALS09 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1344, "OxWo08 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1345, "SYC20 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1346, "System 45 Bornholm Intermediate Datum", 7022, 8901), + new EpsgGeodeticDatumRecord(1347, "Generalstabens System Intermediate Datum", 7051, 8901), + new EpsgGeodeticDatumRecord(1348, "Generalstabens System Bornholm Intermediate Datum", 7051, 8901), + new EpsgGeodeticDatumRecord(1349, "Copenhagen Commune Intermediate Datum", 7051, 8901), + new EpsgGeodeticDatumRecord(1350, "Ostenfeld Intermediate Datum", 7004, 8901), + new EpsgGeodeticDatumRecord(1351, "SMITB20 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1352, "RBEPP12 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1353, "ETRS89/DREF91 Realization 2016", 7019, 8901), + new EpsgGeodeticDatumRecord(1355, "Sonatrach Reference Frame 2020", 7019, 8901), + new EpsgGeodeticDatumRecord(1356, "Latvian coordinate system 2020", 7019, 8901), + new EpsgGeodeticDatumRecord(1357, "Reseau Geodesique de Nouvelle Caledonie 2015", 7019, 8901), + new EpsgGeodeticDatumRecord(1358, "BH_ETRS89", 7019, 8901), + new EpsgGeodeticDatumRecord(1359, "Hughes 1980", 7058, 8901), + new EpsgGeodeticDatumRecord(1360, "NSIDC International 1924 Authalic Sphere", 7057, 8901), + new EpsgGeodeticDatumRecord(1365, "North American Datum of 1983 (CSRS) version 8", 7019, 8901), + new EpsgGeodeticDatumRecord(1366, "COV23 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1367, "Brenner Base Tunnel 2000", 7030, 8901), + new EpsgGeodeticDatumRecord(1379, "Saba", 7022, 8901), + new EpsgGeodeticDatumRecord(1380, "BES2020 Saba", 7019, 8901), + new EpsgGeodeticDatumRecord(1382, "European Terrestrial Reference Frame 2020", 7019, 8901), + new EpsgGeodeticDatumRecord(1383, "World Geodetic System 1984 (G2296)", 7030, 8901), + new EpsgGeodeticDatumRecord(1385, "ECML14 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1386, "WC05 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1389, "Mayotte Geodetic Reference Frame 2023", 7019, 8901), + new EpsgGeodeticDatumRecord(1391, "EUREF-FIN", 7019, 8901), + new EpsgGeodeticDatumRecord(1392, "Uzbekistan Geodetic Datum 2024", 7019, 8901), + new EpsgGeodeticDatumRecord(1393, "Sint Eustatius", 7022, 8901), + new EpsgGeodeticDatumRecord(1394, "BES2020 Sint Eustatius", 7019, 8901), + new EpsgGeodeticDatumRecord(1396, "Bonaire", 7022, 8901), + new EpsgGeodeticDatumRecord(1397, "Bonaire 2004", 7019, 8901), + new EpsgGeodeticDatumRecord(1399, "International Terrestrial Reference Frame 2020-u2023", 7019, 8901), + new EpsgGeodeticDatumRecord(1400, "IGb20", 7019, 8901), + new EpsgGeodeticDatumRecord(1401, "Uganda Geodetic Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1402, "Liberia Reference Frame 2021", 7019, 8901), + new EpsgGeodeticDatumRecord(1403, "Nordic Geodetic Commission ETRF14", 7019, 8901), + new EpsgGeodeticDatumRecord(1404, "Georgia Geodetic Datum", 7019, 8901), + new EpsgGeodeticDatumRecord(1405, "EWR3 Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1406, "WSPG Intermediate Reference Frame", 7019, 8901), + new EpsgGeodeticDatumRecord(1407, "EUREF89", 7019, 8901), + new EpsgGeodeticDatumRecord(1408, "Xrail84", 7030, 8901), + new EpsgGeodeticDatumRecord(1412, "ETRS89-DNK", 7019, 8901), + new EpsgGeodeticDatumRecord(1413, "Asse geodetic datum 2025", 7004, 8901), + new EpsgGeodeticDatumRecord(1414, "California Spatial Reference Network epoch 2025.0 (NAD83 2011)", 7019, 8901), + new EpsgGeodeticDatumRecord(1417, "Kazakhstan Terrestrial Reference Frame 2023", 7019, 8901), + new EpsgGeodeticDatumRecord(1418, "California Spatial Reference Network epoch 2025.0 (ITRF2020) ", 7019, 8901), + new EpsgGeodeticDatumRecord(1419, "Greenland Reference 1996 (2021)", 7019, 8901), + new EpsgGeodeticDatumRecord(1420, "Greenland Reference 1996 (1996)", 7019, 8901), + new EpsgGeodeticDatumRecord(1421, "Greenland Reference 1996 ensemble", 7019, 8901), + new EpsgGeodeticDatumRecord(1422, "North American Terrestrial Reference Frame of 2022", 7019, 8901), + new EpsgGeodeticDatumRecord(1425, "OSNet v2009", 7019, 8901), + new EpsgGeodeticDatumRecord(1426, "Sistem Referensi Geospasial Indonesia 2013 epoch 2021.0", 7030, 8901), + new EpsgGeodeticDatumRecord(1427, "AGRS2010 (ETRF2000)", 7019, 8901), + new EpsgGeodeticDatumRecord(1428, "CR-SIRGAS epoch 2019.24", 7019, 8901), + new EpsgGeodeticDatumRecord(1429, "Albanian Geodetic Reference Frame 2010", 7019, 8901), + new EpsgGeodeticDatumRecord(1430, "ETRS89-ALB [CORS]", 7019, 8901), + new EpsgGeodeticDatumRecord(1431, "ETRS89-AUT [2002]", 7019, 8901), + new EpsgGeodeticDatumRecord(1432, "Belgian Reference Frame 2002", 7019, 8901), + new EpsgGeodeticDatumRecord(1433, "ETRS89-CZE [2007]", 7019, 8901), + new EpsgGeodeticDatumRecord(1434, "ETRS89-SVK [SKTRF09]", 7019, 8901), + new EpsgGeodeticDatumRecord(1435, "ETRS89-SVK [SKTRF2022]", 7019, 8901), + new EpsgGeodeticDatumRecord(1436, "ETRS89-FRO [2008]", 7019, 8901), + new EpsgGeodeticDatumRecord(1437, "ETRS89-GRC [HTRS07]", 7019, 8901), + new EpsgGeodeticDatumRecord(1438, "ETRS89-MKD [EUREF-MAK2010]", 7019, 8901), + new EpsgGeodeticDatumRecord(1439, "ETRS89-PRT [1995]", 7019, 8901), + new EpsgGeodeticDatumRecord(1440, "ETRS89-ROU [ETRF2000]", 7019, 8901), + new EpsgGeodeticDatumRecord(1441, "ETRS89-ESP [REGENTE]", 7019, 8901), + new EpsgGeodeticDatumRecord(1442, "ETRS89-ESP [ERGNSS]", 7019, 8901), + new EpsgGeodeticDatumRecord(1444, "ETRS89-HUN [ETRF2000]", 7019, 8901), + new EpsgGeodeticDatumRecord(1445, "CROPOS", 7019, 8901), + new EpsgGeodeticDatumRecord(1446, "ETRS89/DREF91 Realization 2025", 7019, 8901), + new EpsgGeodeticDatumRecord(1447, "Belgian Reference Frame 2011", 7019, 8901), + new EpsgGeodeticDatumRecord(1448, "National Reference Frame of Bhutan 2023", 7019, 8901), + new EpsgGeodeticDatumRecord(1449, "Swiss Terrestrial Reference Frame 1995", 7019, 8901), + new EpsgGeodeticDatumRecord(1453, "ETRS89-LUX [ETRF2000]", 7019, 8901), + new EpsgGeodeticDatumRecord(6120, "Greek", 7004, 8901), + new EpsgGeodeticDatumRecord(6121, "Greek Geodetic Reference System 1987", 7019, 8901), + new EpsgGeodeticDatumRecord(6122, "Average Terrestrial System 1977", 7041, 8901), + new EpsgGeodeticDatumRecord(6123, "Kartastokoordinaattijarjestelma (1966)", 7022, 8901), + new EpsgGeodeticDatumRecord(6124, "Rikets koordinatsystem 1990", 7004, 8901), + new EpsgGeodeticDatumRecord(6126, "Lithuania 1994 (ETRS89)", 7019, 8901), + new EpsgGeodeticDatumRecord(6127, "Tete", 7008, 8901), + new EpsgGeodeticDatumRecord(6128, "Madzansua", 7008, 8901), + new EpsgGeodeticDatumRecord(6129, "Observatario", 7008, 8901), + new EpsgGeodeticDatumRecord(6130, "Moznet (ITRF94)", 7030, 8901), + new EpsgGeodeticDatumRecord(6131, "Indian 1960", 7015, 8901), + new EpsgGeodeticDatumRecord(6132, "Final Datum 1958", 7012, 8901), + new EpsgGeodeticDatumRecord(6133, "Estonia 1992", 7019, 8901), + new EpsgGeodeticDatumRecord(6134, "PDO Survey Datum 1993", 7012, 8901), + new EpsgGeodeticDatumRecord(6135, "Old Hawaiian", 7008, 8901), + new EpsgGeodeticDatumRecord(6136, "St. Lawrence Island", 7008, 8901), + new EpsgGeodeticDatumRecord(6137, "St. Paul Island", 7008, 8901), + new EpsgGeodeticDatumRecord(6138, "St. George Island", 7008, 8901), + new EpsgGeodeticDatumRecord(6139, "Puerto Rico", 7008, 8901), + new EpsgGeodeticDatumRecord(6140, "NAD83 Canadian Spatial Reference System", 7019, 8901), + new EpsgGeodeticDatumRecord(6141, "Israel 1993", 7019, 8901), + new EpsgGeodeticDatumRecord(6142, "Locodjo 1965", 7012, 8901), + new EpsgGeodeticDatumRecord(6143, "Abidjan 1987", 7012, 8901), + new EpsgGeodeticDatumRecord(6144, "Kalianpur 1937", 7015, 8901), + new EpsgGeodeticDatumRecord(6145, "Kalianpur 1962", 7044, 8901), + new EpsgGeodeticDatumRecord(6146, "Kalianpur 1975", 7045, 8901), + new EpsgGeodeticDatumRecord(6147, "Hanoi 1972", 7024, 8901), + new EpsgGeodeticDatumRecord(6148, "Hartebeesthoek94", 7030, 8901), + new EpsgGeodeticDatumRecord(6149, "CH1903", 7004, 8901), + new EpsgGeodeticDatumRecord(6150, "CH1903+", 7004, 8901), + new EpsgGeodeticDatumRecord(6151, "Swiss Terrestrial Reference System 1995", 7019, 8901), + new EpsgGeodeticDatumRecord(6152, "NAD83 (High Accuracy Reference Network)", 7019, 8901), + new EpsgGeodeticDatumRecord(6153, "Rassadiran", 7022, 8901), + new EpsgGeodeticDatumRecord(6154, "European Datum 1950(1977)", 7022, 8901), + new EpsgGeodeticDatumRecord(6155, "Dabola 1981", 7011, 8901), + new EpsgGeodeticDatumRecord(6156, "System of the Unified Trigonometrical Cadastral Network", 7004, 8901), + new EpsgGeodeticDatumRecord(6157, "Mount Dillon", 7007, 8901), + new EpsgGeodeticDatumRecord(6158, "Naparima 1955", 7022, 8901), + new EpsgGeodeticDatumRecord(6159, "European Libyan Datum 1979", 7022, 8901), + new EpsgGeodeticDatumRecord(6160, "Chos Malal 1914", 7022, 8901), + new EpsgGeodeticDatumRecord(6161, "Pampa del Castillo", 7022, 8901), + new EpsgGeodeticDatumRecord(6162, "Korean Datum 1985", 7004, 8901), + new EpsgGeodeticDatumRecord(6163, "Yemen National Geodetic Network 1996", 7030, 8901), + new EpsgGeodeticDatumRecord(6164, "South Yemen", 7024, 8901), + new EpsgGeodeticDatumRecord(6165, "Bissau", 7022, 8901), + new EpsgGeodeticDatumRecord(6166, "Korean Datum 1995", 7030, 8901), + new EpsgGeodeticDatumRecord(6167, "New Zealand Geodetic Datum 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(6168, "Accra", 7029, 8901), + new EpsgGeodeticDatumRecord(6169, "American Samoa 1962", 7008, 8901), + new EpsgGeodeticDatumRecord(6170, "Sistema de Referencia Geocentrico para America del Sur 1995", 7019, 8901), + new EpsgGeodeticDatumRecord(6171, "Reseau Geodesique Francais 1993 v1", 7019, 8901), + new EpsgGeodeticDatumRecord(6173, "ETRS89-IRE [ETRF2000]", 7019, 8901), + new EpsgGeodeticDatumRecord(6174, "Sierra Leone Colony 1924", 7029, 8901), + new EpsgGeodeticDatumRecord(6175, "Sierra Leone 1968", 7012, 8901), + new EpsgGeodeticDatumRecord(6176, "Australian Antarctic Datum 1998", 7019, 8901), + new EpsgGeodeticDatumRecord(6178, "Pulkovo 1942(83)", 7024, 8901), + new EpsgGeodeticDatumRecord(6179, "Pulkovo 1942(58)", 7024, 8901), + new EpsgGeodeticDatumRecord(6180, "Estonia 1997", 7019, 8901), + new EpsgGeodeticDatumRecord(6181, "Luxembourg Reference Frame", 7022, 8901), + new EpsgGeodeticDatumRecord(6182, "Azores Occidental Islands 1939", 7022, 8901), + new EpsgGeodeticDatumRecord(6183, "Azores Central Islands 1948", 7022, 8901), + new EpsgGeodeticDatumRecord(6184, "Azores Oriental Islands 1940", 7022, 8901), + new EpsgGeodeticDatumRecord(6188, "OSNI 1952", 7001, 8901), + new EpsgGeodeticDatumRecord(6189, "Red Geodesica Venezolana", 7019, 8901), + new EpsgGeodeticDatumRecord(6190, "Posiciones Geodesicas Argentinas 1998", 7019, 8901), + new EpsgGeodeticDatumRecord(6191, "Albanian 1987", 7024, 8901), + new EpsgGeodeticDatumRecord(6192, "Douala 1948", 7022, 8901), + new EpsgGeodeticDatumRecord(6193, "Manoca 1962", 7011, 8901), + new EpsgGeodeticDatumRecord(6194, "Qoornoq 1927", 7022, 8901), + new EpsgGeodeticDatumRecord(6195, "Scoresbysund 1952", 7022, 8901), + new EpsgGeodeticDatumRecord(6196, "Ammassalik 1958", 7022, 8901), + new EpsgGeodeticDatumRecord(6197, "Garoua", 7012, 8901), + new EpsgGeodeticDatumRecord(6198, "Kousseri", 7012, 8901), + new EpsgGeodeticDatumRecord(6199, "Egypt 1930", 7022, 8901), + new EpsgGeodeticDatumRecord(6200, "Pulkovo 1995", 7024, 8901), + new EpsgGeodeticDatumRecord(6201, "Adindan", 7012, 8901), + new EpsgGeodeticDatumRecord(6202, "Australian Geodetic Datum 1966", 7003, 8901), + new EpsgGeodeticDatumRecord(6203, "Australian Geodetic Datum 1984", 7003, 8901), + new EpsgGeodeticDatumRecord(6204, "Ain el Abd 1970", 7022, 8901), + new EpsgGeodeticDatumRecord(6205, "Afgooye", 7024, 8901), + new EpsgGeodeticDatumRecord(6206, "Agadez", 7011, 8901), + new EpsgGeodeticDatumRecord(6207, "Lisbon 1937", 7022, 8901), + new EpsgGeodeticDatumRecord(6208, "Aratu", 7022, 8901), + new EpsgGeodeticDatumRecord(6209, "Arc 1950", 7013, 8901), + new EpsgGeodeticDatumRecord(6210, "Arc 1960", 7012, 8901), + new EpsgGeodeticDatumRecord(6211, "Batavia", 7004, 8901), + new EpsgGeodeticDatumRecord(6212, "Barbados 1938", 7012, 8901), + new EpsgGeodeticDatumRecord(6213, "Beduaram", 7011, 8901), + new EpsgGeodeticDatumRecord(6214, "Beijing 1954", 7024, 8901), + new EpsgGeodeticDatumRecord(6215, "Reseau National Belge 1950", 7022, 8901), + new EpsgGeodeticDatumRecord(6216, "Bermuda 1957", 7008, 8901), + new EpsgGeodeticDatumRecord(6218, "Bogota 1975", 7022, 8901), + new EpsgGeodeticDatumRecord(6219, "Bukit Rimpah", 7004, 8901), + new EpsgGeodeticDatumRecord(6220, "Camacupa 1948", 7012, 8901), + new EpsgGeodeticDatumRecord(6221, "Campo Inchauspe", 7022, 8901), + new EpsgGeodeticDatumRecord(6222, "Cape", 7013, 8901), + new EpsgGeodeticDatumRecord(6223, "Carthage", 7011, 8901), + new EpsgGeodeticDatumRecord(6224, "Chua", 7022, 8901), + new EpsgGeodeticDatumRecord(6225, "Corrego Alegre 1970-72", 7022, 8901), + new EpsgGeodeticDatumRecord(6227, "Deir ez Zor", 7011, 8901), + new EpsgGeodeticDatumRecord(6229, "Egypt 1907", 7020, 8901), + new EpsgGeodeticDatumRecord(6230, "European Datum 1950", 7022, 8901), + new EpsgGeodeticDatumRecord(6231, "European Datum 1987", 7022, 8901), + new EpsgGeodeticDatumRecord(6232, "Fahud", 7012, 8901), + new EpsgGeodeticDatumRecord(6236, "Hu Tzu Shan 1950", 7022, 8901), + new EpsgGeodeticDatumRecord(6237, "Hungarian Datum 1972", 7036, 8901), + new EpsgGeodeticDatumRecord(6238, "Indonesian Datum 1974", 7021, 8901), + new EpsgGeodeticDatumRecord(6239, "Indian 1954", 7015, 8901), + new EpsgGeodeticDatumRecord(6240, "Indian 1975", 7015, 8901), + new EpsgGeodeticDatumRecord(6241, "Jamaica 1875", 7034, 8901), + new EpsgGeodeticDatumRecord(6242, "Jamaica 1969", 7008, 8901), + new EpsgGeodeticDatumRecord(6243, "Kalianpur 1880", 7042, 8901), + new EpsgGeodeticDatumRecord(6244, "Kandawala", 7015, 8901), + new EpsgGeodeticDatumRecord(6245, "Kertau 1968", 7018, 8901), + new EpsgGeodeticDatumRecord(6246, "Kuwait Oil Company", 7012, 8901), + new EpsgGeodeticDatumRecord(6247, "La Canoa", 7022, 8901), + new EpsgGeodeticDatumRecord(6248, "Provisional South American Datum 1956", 7022, 8901), + new EpsgGeodeticDatumRecord(6249, "Lake", 7022, 8901), + new EpsgGeodeticDatumRecord(6250, "Leigon", 7012, 8901), + new EpsgGeodeticDatumRecord(6251, "Liberia 1964", 7012, 8901), + new EpsgGeodeticDatumRecord(6252, "Lome", 7011, 8901), + new EpsgGeodeticDatumRecord(6253, "Luzon 1911", 7008, 8901), + new EpsgGeodeticDatumRecord(6254, "Hito XVIII 1963", 7022, 8901), + new EpsgGeodeticDatumRecord(6255, "Herat North", 7022, 8901), + new EpsgGeodeticDatumRecord(6256, "Mahe 1971", 7012, 8901), + new EpsgGeodeticDatumRecord(6257, "Makassar", 7004, 8901), + new EpsgGeodeticDatumRecord(6258, "European Terrestrial Reference System 1989 ensemble", 7019, 8901), + new EpsgGeodeticDatumRecord(6259, "Malongo 1987", 7022, 8901), + new EpsgGeodeticDatumRecord(6261, "Merchich", 7011, 8901), + new EpsgGeodeticDatumRecord(6262, "Massawa", 7004, 8901), + new EpsgGeodeticDatumRecord(6263, "Minna", 7012, 8901), + new EpsgGeodeticDatumRecord(6265, "Monte Mario", 7022, 8901), + new EpsgGeodeticDatumRecord(6266, "M'poraloko", 7011, 8901), + new EpsgGeodeticDatumRecord(6267, "North American Datum 1927", 7008, 8901), + new EpsgGeodeticDatumRecord(6269, "North American Datum 1983", 7019, 8901), + new EpsgGeodeticDatumRecord(6270, "Nahrwan 1967", 7012, 8901), + new EpsgGeodeticDatumRecord(6271, "Naparima 1972", 7022, 8901), + new EpsgGeodeticDatumRecord(6272, "New Zealand Geodetic Datum 1949", 7022, 8901), + new EpsgGeodeticDatumRecord(6273, "NGO 1948", 7005, 8901), + new EpsgGeodeticDatumRecord(6274, "Datum 73", 7022, 8901), + new EpsgGeodeticDatumRecord(6275, "Nouvelle Triangulation Francaise", 7011, 8901), + new EpsgGeodeticDatumRecord(6276, "NSWC 9Z-2", 7025, 8901), + new EpsgGeodeticDatumRecord(6277, "Ordnance Survey of Great Britain 1936", 7001, 8901), + new EpsgGeodeticDatumRecord(6278, "OSGB 1970 (SN)", 7001, 8901), + new EpsgGeodeticDatumRecord(6279, "OS (SN) 1980", 7001, 8901), + new EpsgGeodeticDatumRecord(6281, "Palestine 1923", 7010, 8901), + new EpsgGeodeticDatumRecord(6282, "Congo 1960 Pointe Noire", 7011, 8901), + new EpsgGeodeticDatumRecord(6283, "Geocentric Datum of Australia 1994", 7019, 8901), + new EpsgGeodeticDatumRecord(6284, "Pulkovo 1942", 7024, 8901), + new EpsgGeodeticDatumRecord(6285, "Qatar 1974", 7022, 8901), + new EpsgGeodeticDatumRecord(6286, "Qatar 1948", 7020, 8901), + new EpsgGeodeticDatumRecord(6288, "Loma Quintana", 7022, 8901), + new EpsgGeodeticDatumRecord(6289, "Amersfoort", 7004, 8901), + new EpsgGeodeticDatumRecord(6292, "Sapper Hill 1943", 7022, 8901), + new EpsgGeodeticDatumRecord(6293, "Schwarzeck", 7046, 8901), + new EpsgGeodeticDatumRecord(6295, "Serindung", 7004, 8901), + new EpsgGeodeticDatumRecord(6297, "Tananarive 1925", 7022, 8901), + new EpsgGeodeticDatumRecord(6298, "Timbalai 1948", 7016, 8901), + new EpsgGeodeticDatumRecord(6299, "TM65", 7002, 8901), + new EpsgGeodeticDatumRecord(6300, "Geodetic Datum of 1965", 7002, 8901), + new EpsgGeodeticDatumRecord(6301, "Tokyo", 7004, 8901), + new EpsgGeodeticDatumRecord(6302, "Trinidad 1903", 7007, 8901), + new EpsgGeodeticDatumRecord(6303, "Trucial Coast 1948", 7020, 8901), + new EpsgGeodeticDatumRecord(6304, "Voirol 1875", 7011, 8901), + new EpsgGeodeticDatumRecord(6306, "Bern 1938", 7004, 8901), + new EpsgGeodeticDatumRecord(6307, "Nord Sahara 1959", 7012, 8901), + new EpsgGeodeticDatumRecord(6308, "Stockholm 1938", 7004, 8901), + new EpsgGeodeticDatumRecord(6309, "Yacare", 7022, 8901), + new EpsgGeodeticDatumRecord(6310, "Yoff", 7011, 8901), + new EpsgGeodeticDatumRecord(6311, "Zanderij", 7022, 8901), + new EpsgGeodeticDatumRecord(6312, "Militar-Geographische Institut", 7004, 8901), + new EpsgGeodeticDatumRecord(6313, "Reseau National Belge 1972", 7022, 8901), + new EpsgGeodeticDatumRecord(6314, "Deutsches Hauptdreiecksnetz", 7004, 8901), + new EpsgGeodeticDatumRecord(6315, "Conakry 1905", 7011, 8901), + new EpsgGeodeticDatumRecord(6316, "Dealul Piscului 1930", 7022, 8901), + new EpsgGeodeticDatumRecord(6318, "National Geodetic Network", 7030, 8901), + new EpsgGeodeticDatumRecord(6319, "Kuwait Utility", 7019, 8901), + new EpsgGeodeticDatumRecord(6322, "World Geodetic System 1972", 7043, 8901), + new EpsgGeodeticDatumRecord(6324, "WGS 72 Transit Broadcast Ephemeris", 7043, 8901), + new EpsgGeodeticDatumRecord(6326, "World Geodetic System 1984 ensemble", 7030, 8901), + new EpsgGeodeticDatumRecord(6600, "Anguilla 1957", 7012, 8901), + new EpsgGeodeticDatumRecord(6601, "Antigua 1943", 7012, 8901), + new EpsgGeodeticDatumRecord(6602, "Dominica 1945", 7012, 8901), + new EpsgGeodeticDatumRecord(6603, "Grenada 1953", 7012, 8901), + new EpsgGeodeticDatumRecord(6604, "Montserrat 1958", 7012, 8901), + new EpsgGeodeticDatumRecord(6605, "St. Kitts 1955", 7012, 8901), + new EpsgGeodeticDatumRecord(6606, "St. Lucia 1955", 7012, 8901), + new EpsgGeodeticDatumRecord(6607, "St. Vincent 1945", 7012, 8901), + new EpsgGeodeticDatumRecord(6608, "North American Datum 1927 (1976)", 7008, 8901), + new EpsgGeodeticDatumRecord(6609, "North American Datum 1927 (CGQ77)", 7008, 8901), + new EpsgGeodeticDatumRecord(6610, "Xian 1980", 7049, 8901), + new EpsgGeodeticDatumRecord(6611, "Hong Kong 1980", 7022, 8901), + new EpsgGeodeticDatumRecord(6612, "Japanese Geodetic Datum 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(6613, "Gunung Segara", 7004, 8901), + new EpsgGeodeticDatumRecord(6614, "Qatar National Datum 1995", 7022, 8901), + new EpsgGeodeticDatumRecord(6615, "Porto Santo 1936", 7022, 8901), + new EpsgGeodeticDatumRecord(6616, "Selvagem Grande", 7022, 8901), + new EpsgGeodeticDatumRecord(6618, "South American Datum 1969", 7050, 8901), + new EpsgGeodeticDatumRecord(6619, "SWEREF 99", 7019, 8901), + new EpsgGeodeticDatumRecord(6620, "Point 58", 7012, 8901), + new EpsgGeodeticDatumRecord(6621, "Fort Marigot", 7022, 8901), + new EpsgGeodeticDatumRecord(6622, "Guadeloupe 1948", 7022, 8901), + new EpsgGeodeticDatumRecord(6623, "Centre Spatial Guyanais 1967", 7022, 8901), + new EpsgGeodeticDatumRecord(6624, "Reseau Geodesique Francais Guyane 1995", 7019, 8901), + new EpsgGeodeticDatumRecord(6625, "Martinique 1938", 7022, 8901), + new EpsgGeodeticDatumRecord(6626, "Reunion 1947", 7022, 8901), + new EpsgGeodeticDatumRecord(6627, "Reseau Geodesique de la Reunion 1992", 7019, 8901), + new EpsgGeodeticDatumRecord(6628, "Tahiti 52", 7022, 8901), + new EpsgGeodeticDatumRecord(6629, "Tahaa 54", 7022, 8901), + new EpsgGeodeticDatumRecord(6630, "IGN72 Nuku Hiva", 7022, 8901), + new EpsgGeodeticDatumRecord(6632, "Combani 1950", 7022, 8901), + new EpsgGeodeticDatumRecord(6633, "IGN56 Lifou", 7022, 8901), + new EpsgGeodeticDatumRecord(6634, "IGN72 Grande Terre", 7022, 8901), + new EpsgGeodeticDatumRecord(6636, "Petrels 1972", 7022, 8901), + new EpsgGeodeticDatumRecord(6637, "Pointe Geologie Perroud 1950", 7022, 8901), + new EpsgGeodeticDatumRecord(6638, "Saint Pierre et Miquelon 1950", 7008, 8901), + new EpsgGeodeticDatumRecord(6639, "MOP78", 7022, 8901), + new EpsgGeodeticDatumRecord(6641, "IGN53 Mare", 7022, 8901), + new EpsgGeodeticDatumRecord(6642, "ST84 Ile des Pins", 7022, 8901), + new EpsgGeodeticDatumRecord(6643, "ST71 Belep", 7022, 8901), + new EpsgGeodeticDatumRecord(6644, "NEA74 Noumea", 7022, 8901), + new EpsgGeodeticDatumRecord(6646, "Grand Comoros", 7022, 8901), + new EpsgGeodeticDatumRecord(6647, "International Terrestrial Reference Frame 1988", 7019, 8901), + new EpsgGeodeticDatumRecord(6648, "International Terrestrial Reference Frame 1989", 7019, 8901), + new EpsgGeodeticDatumRecord(6649, "International Terrestrial Reference Frame 1990", 7019, 8901), + new EpsgGeodeticDatumRecord(6650, "International Terrestrial Reference Frame 1991", 7019, 8901), + new EpsgGeodeticDatumRecord(6651, "International Terrestrial Reference Frame 1992", 7019, 8901), + new EpsgGeodeticDatumRecord(6652, "International Terrestrial Reference Frame 1993", 7019, 8901), + new EpsgGeodeticDatumRecord(6653, "International Terrestrial Reference Frame 1994", 7019, 8901), + new EpsgGeodeticDatumRecord(6654, "International Terrestrial Reference Frame 1996", 7019, 8901), + new EpsgGeodeticDatumRecord(6655, "International Terrestrial Reference Frame 1997", 7019, 8901), + new EpsgGeodeticDatumRecord(6656, "International Terrestrial Reference Frame 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(6657, "Reykjavik 1900", 7051, 8901), + new EpsgGeodeticDatumRecord(6658, "Hjorsey 1955", 7022, 8901), + new EpsgGeodeticDatumRecord(6659, "Islands Net 1993", 7019, 8901), + new EpsgGeodeticDatumRecord(6660, "Helle 1954", 7022, 8901), + new EpsgGeodeticDatumRecord(6661, "Latvian geodetic coordinate system 1992", 7019, 8901), + new EpsgGeodeticDatumRecord(6663, "Porto Santo 1995", 7022, 8901), + new EpsgGeodeticDatumRecord(6664, "Azores Oriental Islands 1995", 7022, 8901), + new EpsgGeodeticDatumRecord(6665, "Azores Central Islands 1995", 7022, 8901), + new EpsgGeodeticDatumRecord(6666, "Lisbon 1890", 7004, 8901), + new EpsgGeodeticDatumRecord(6667, "Iraq-Kuwait Boundary Datum 1992", 7030, 8901), + new EpsgGeodeticDatumRecord(6668, "European Datum 1979", 7022, 8901), + new EpsgGeodeticDatumRecord(6670, "Istituto Geografico Militare 1995", 7019, 8901), + new EpsgGeodeticDatumRecord(6671, "Voirol 1879", 7011, 8901), + new EpsgGeodeticDatumRecord(6672, "Chatham Islands Datum 1971", 7022, 8901), + new EpsgGeodeticDatumRecord(6673, "Chatham Islands Datum 1979", 7022, 8901), + new EpsgGeodeticDatumRecord(6674, "Sistema de Referencia Geocentrico para las AmericaS 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(6675, "Guam 1963", 7008, 8901), + new EpsgGeodeticDatumRecord(6676, "Vientiane 1982", 7024, 8901), + new EpsgGeodeticDatumRecord(6677, "Lao 1993", 7024, 8901), + new EpsgGeodeticDatumRecord(6678, "Lao National Datum 1997", 7024, 8901), + new EpsgGeodeticDatumRecord(6679, "Jouik 1961", 7012, 8901), + new EpsgGeodeticDatumRecord(6680, "Nouakchott 1965", 7012, 8901), + new EpsgGeodeticDatumRecord(6682, "Gulshan 303", 7015, 8901), + new EpsgGeodeticDatumRecord(6683, "Philippine Reference System 1992", 7008, 8901), + new EpsgGeodeticDatumRecord(6684, "Gan 1970", 7022, 8901), + new EpsgGeodeticDatumRecord(6686, "Marco Geocentrico Nacional de Referencia", 7019, 8901), + new EpsgGeodeticDatumRecord(6687, "Reseau Geodesique de la Polynesie Francaise", 7019, 8901), + new EpsgGeodeticDatumRecord(6688, "Fatu Iva 72", 7022, 8901), + new EpsgGeodeticDatumRecord(6689, "IGN63 Hiva Oa", 7022, 8901), + new EpsgGeodeticDatumRecord(6690, "Tahiti 79", 7022, 8901), + new EpsgGeodeticDatumRecord(6691, "Moorea 87", 7022, 8901), + new EpsgGeodeticDatumRecord(6692, "Maupiti 83", 7022, 8901), + new EpsgGeodeticDatumRecord(6693, "Nakhl-e Ghanem", 7030, 8901), + new EpsgGeodeticDatumRecord(6694, "Posiciones Geodesicas Argentinas 1994", 7030, 8901), + new EpsgGeodeticDatumRecord(6695, "Katanga 1955", 7008, 8901), + new EpsgGeodeticDatumRecord(6696, "Kasai 1953", 7012, 8901), + new EpsgGeodeticDatumRecord(6697, "IGC 1962 Arc of the 6th Parallel South", 7012, 8901), + new EpsgGeodeticDatumRecord(6698, "IGN 1962 Kerguelen", 7022, 8901), + new EpsgGeodeticDatumRecord(6699, "Le Pouce 1934", 7012, 8901), + new EpsgGeodeticDatumRecord(6700, "IGN Astro 1960", 7012, 8901), + new EpsgGeodeticDatumRecord(6701, "Institut Geographique du Congo Belge 1955", 7012, 8901), + new EpsgGeodeticDatumRecord(6702, "Mauritania 1999", 7019, 8901), + new EpsgGeodeticDatumRecord(6703, "Missao Hidrografico Angola y Sao Tome 1951", 7012, 8901), + new EpsgGeodeticDatumRecord(6704, "Mhast (onshore)", 7022, 8901), + new EpsgGeodeticDatumRecord(6705, "Mhast (offshore)", 7022, 8901), + new EpsgGeodeticDatumRecord(6706, "Egypt Gulf of Suez S-650 TL", 7020, 8901), + new EpsgGeodeticDatumRecord(6707, "Tern Island 1961", 7022, 8901), + new EpsgGeodeticDatumRecord(6708, "Cocos Islands 1965", 7003, 8901), + new EpsgGeodeticDatumRecord(6709, "Iwo Jima 1945", 7022, 8901), + new EpsgGeodeticDatumRecord(6710, "Astro DOS 71", 7022, 8901), + new EpsgGeodeticDatumRecord(6711, "Marcus Island 1952", 7022, 8901), + new EpsgGeodeticDatumRecord(6712, "Ascension Island 1958", 7022, 8901), + new EpsgGeodeticDatumRecord(6713, "Ayabelle Lighthouse", 7012, 8901), + new EpsgGeodeticDatumRecord(6714, "Bellevue", 7022, 8901), + new EpsgGeodeticDatumRecord(6715, "Camp Area Astro", 7022, 8901), + new EpsgGeodeticDatumRecord(6716, "Phoenix Islands 1966", 7022, 8901), + new EpsgGeodeticDatumRecord(6717, "Cape Canaveral", 7008, 8901), + new EpsgGeodeticDatumRecord(6718, "Solomon 1968", 7022, 8901), + new EpsgGeodeticDatumRecord(6719, "Easter Island 1967", 7022, 8901), + new EpsgGeodeticDatumRecord(6720, "Fiji Geodetic Datum 1986", 7043, 8901), + new EpsgGeodeticDatumRecord(6721, "Fiji 1956", 7022, 8901), + new EpsgGeodeticDatumRecord(6722, "South Georgia 1968", 7022, 8901), + new EpsgGeodeticDatumRecord(6723, "Grand Cayman Geodetic Datum 1959", 7008, 8901), + new EpsgGeodeticDatumRecord(6724, "Diego Garcia 1969", 7022, 8901), + new EpsgGeodeticDatumRecord(6725, "Johnston Island 1961", 7022, 8901), + new EpsgGeodeticDatumRecord(6726, "Sister Islands Geodetic Datum 1961", 7008, 8901), + new EpsgGeodeticDatumRecord(6727, "Midway 1961", 7022, 8901), + new EpsgGeodeticDatumRecord(6728, "Pico de las Nieves 1984", 7022, 8901), + new EpsgGeodeticDatumRecord(6729, "Pitcairn 1967", 7022, 8901), + new EpsgGeodeticDatumRecord(6730, "Santo 1965", 7022, 8901), + new EpsgGeodeticDatumRecord(6732, "Marshall Islands 1960", 7053, 8901), + new EpsgGeodeticDatumRecord(6733, "Wake Island 1952", 7022, 8901), + new EpsgGeodeticDatumRecord(6734, "Tristan 1968", 7022, 8901), + new EpsgGeodeticDatumRecord(6735, "Kusaie 1951", 7022, 8901), + new EpsgGeodeticDatumRecord(6736, "Deception Island", 7012, 8901), + new EpsgGeodeticDatumRecord(6737, "Korean Geodetic Datum 2002", 7019, 8901), + new EpsgGeodeticDatumRecord(6738, "Hong Kong 1963", 7007, 8901), + new EpsgGeodeticDatumRecord(6739, "Hong Kong 1963(67)", 7022, 8901), + new EpsgGeodeticDatumRecord(6740, "Parametry Zemli 1990", 7054, 8901), + new EpsgGeodeticDatumRecord(6741, "Faroe Datum 1954", 7022, 8901), + new EpsgGeodeticDatumRecord(6742, "Geodetic Datum of Malaysia 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(6743, "Karbala 1979", 7012, 8901), + new EpsgGeodeticDatumRecord(6744, "Nahrwan 1934", 7012, 8901), + new EpsgGeodeticDatumRecord(6745, "Rauenberg Datum/83", 7004, 8901), + new EpsgGeodeticDatumRecord(6746, "Potsdam Datum/83", 7004, 8901), + new EpsgGeodeticDatumRecord(6748, "Vanua Levu 1915", 7055, 8901), + new EpsgGeodeticDatumRecord(6749, "Reseau Geodesique de Nouvelle Caledonie 91-93", 7019, 8901), + new EpsgGeodeticDatumRecord(6750, "ST87 Ouvea", 7030, 8901), + new EpsgGeodeticDatumRecord(6751, "Kertau (RSO)", 7056, 8901), + new EpsgGeodeticDatumRecord(6752, "Viti Levu 1912", 7055, 8901), + new EpsgGeodeticDatumRecord(6753, "fk89", 7022, 8901), + new EpsgGeodeticDatumRecord(6754, "Libyan Geodetic Datum 2006", 7022, 8901), + new EpsgGeodeticDatumRecord(6755, "Datum Geodesi Nasional 1995", 7030, 8901), + new EpsgGeodeticDatumRecord(6756, "Vietnam 2000", 7030, 8901), + new EpsgGeodeticDatumRecord(6757, "SVY21", 7030, 8901), + new EpsgGeodeticDatumRecord(6758, "Jamaica 2001", 7030, 8901), + new EpsgGeodeticDatumRecord(6759, "NAD83 (National Spatial Reference System 2007)", 7019, 8901), + new EpsgGeodeticDatumRecord(6760, "World Geodetic System 1966", 7025, 8901), + new EpsgGeodeticDatumRecord(6761, "Croatian Terrestrial Reference System 1996", 7019, 8901), + new EpsgGeodeticDatumRecord(6762, "Bermuda 2000", 7030, 8901), + new EpsgGeodeticDatumRecord(6763, "Pitcairn 2006", 7030, 8901), + new EpsgGeodeticDatumRecord(6764, "Ross Sea Region Geodetic Datum 2000", 7019, 8901), + new EpsgGeodeticDatumRecord(6765, "Slovenia Geodetic Datum 1996", 7019, 8901), + new EpsgGeodeticDatumRecord(6801, "CH1903 (Bern)", 7004, 8907), + new EpsgGeodeticDatumRecord(6802, "Bogota 1975 (Bogota)", 7022, 8904), + new EpsgGeodeticDatumRecord(6803, "Lisbon 1937 (Lisbon)", 7022, 8902), + new EpsgGeodeticDatumRecord(6804, "Makassar (Jakarta)", 7004, 8908), + new EpsgGeodeticDatumRecord(6805, "Militar-Geographische Institut (Ferro)", 7004, 8909), + new EpsgGeodeticDatumRecord(6806, "Monte Mario (Rome)", 7022, 8906), + new EpsgGeodeticDatumRecord(6807, "Nouvelle Triangulation Francaise (Paris)", 7011, 8903), + new EpsgGeodeticDatumRecord(6809, "Reseau National Belge 1950 (Brussels)", 7022, 8910), + new EpsgGeodeticDatumRecord(6810, "Tananarive 1925 (Paris)", 7022, 8903), + new EpsgGeodeticDatumRecord(6811, "Voirol 1875 (Paris)", 7011, 8903), + new EpsgGeodeticDatumRecord(6813, "Batavia (Jakarta)", 7004, 8908), + new EpsgGeodeticDatumRecord(6814, "Stockholm 1938 (Stockholm)", 7004, 8911), + new EpsgGeodeticDatumRecord(6815, "Greek (Athens)", 7004, 8912), + new EpsgGeodeticDatumRecord(6816, "Carthage (Paris)", 7011, 8903), + new EpsgGeodeticDatumRecord(6817, "NGO 1948 (Oslo)", 7005, 8913), + new EpsgGeodeticDatumRecord(6818, "System of the Unified Trigonometrical Cadastral Network (Ferro)", 7004, 8909), + new EpsgGeodeticDatumRecord(6820, "Gunung Segara (Jakarta)", 7004, 8908), + new EpsgGeodeticDatumRecord(6821, "Voirol 1879 (Paris)", 7011, 8903), + new EpsgGeodeticDatumRecord(6896, "International Terrestrial Reference Frame 2005", 7019, 8901), + new EpsgGeodeticDatumRecord(6901, "Ancienne Triangulation Francaise (Paris)", 7027, 8914), + new EpsgGeodeticDatumRecord(6903, "Madrid 1870 (Madrid)", 7028, 8905), + new EpsgGeodeticDatumRecord(6904, "Lisbon 1890 (Lisbon)", 7004, 8902), + }; + + internal static readonly EpsgVerticalDatumRecord[] VerticalDatums = new EpsgVerticalDatumRecord[] + { + new EpsgVerticalDatumRecord(1027, "EGM2008 geoid"), + new EpsgVerticalDatumRecord(1028, "Fao 1979"), + new EpsgVerticalDatumRecord(1030, "N2000"), + new EpsgVerticalDatumRecord(1039, "New Zealand Vertical Datum 2009"), + new EpsgVerticalDatumRecord(1040, "Dunedin-Bluff 1960"), + new EpsgVerticalDatumRecord(1049, "Korean Vertical Datum 1964"), + new EpsgVerticalDatumRecord(1050, "Trieste"), + new EpsgVerticalDatumRecord(1051, "Genoa 1942"), + new EpsgVerticalDatumRecord(1054, "Sri Lanka Vertical Datum"), + new EpsgVerticalDatumRecord(1059, "Faroe Islands Vertical Reference 2009"), + new EpsgVerticalDatumRecord(1079, "Fehmarnbelt Vertical Reference 2010"), + new EpsgVerticalDatumRecord(1080, "Lowest Astronomical Tide"), + new EpsgVerticalDatumRecord(1082, "Highest Astronomical Tide"), + new EpsgVerticalDatumRecord(1083, "Lower Low Water Large Tide"), + new EpsgVerticalDatumRecord(1084, "Higher High Water Large Tide"), + new EpsgVerticalDatumRecord(1085, "Indian Spring Low Water"), + new EpsgVerticalDatumRecord(1086, "Mean Lower Low Water Spring Tides"), + new EpsgVerticalDatumRecord(1087, "Mean Low Water Spring Tides"), + new EpsgVerticalDatumRecord(1088, "Mean High Water Spring Tides"), + new EpsgVerticalDatumRecord(1089, "Mean Lower Low Water"), + new EpsgVerticalDatumRecord(1090, "Mean Higher High Water"), + new EpsgVerticalDatumRecord(1091, "Mean Low Water"), + new EpsgVerticalDatumRecord(1092, "Mean High Water"), + new EpsgVerticalDatumRecord(1093, "Low Water"), + new EpsgVerticalDatumRecord(1094, "High Water"), + new EpsgVerticalDatumRecord(1096, "Norway Normal Null 2000:2018"), + new EpsgVerticalDatumRecord(1097, "Grand Cayman Vertical Datum 1954"), + new EpsgVerticalDatumRecord(1098, "Little Cayman Vertical Datum 1961"), + new EpsgVerticalDatumRecord(1099, "Cayman Brac Vertical Datum 1961"), + new EpsgVerticalDatumRecord(1101, "Cais da Pontinha"), + new EpsgVerticalDatumRecord(1102, "Cais da Vila"), + new EpsgVerticalDatumRecord(1103, "Cais das Velas"), + new EpsgVerticalDatumRecord(1104, "Horta"), + new EpsgVerticalDatumRecord(1105, "Cais da Madalena"), + new EpsgVerticalDatumRecord(1106, "Santa Cruz da Graciosa"), + new EpsgVerticalDatumRecord(1107, "Cais da Figueirinha"), + new EpsgVerticalDatumRecord(1108, "Santa Cruz das Flores"), + new EpsgVerticalDatumRecord(1109, "Cais da Vila do Porto"), + new EpsgVerticalDatumRecord(1110, "Ponta Delgada"), + new EpsgVerticalDatumRecord(1119, "Northern Marianas Vertical Datum of 2003"), + new EpsgVerticalDatumRecord(1121, "Tutuila Vertical Datum of 1962"), + new EpsgVerticalDatumRecord(1122, "Guam Vertical Datum of 1963"), + new EpsgVerticalDatumRecord(1123, "Puerto Rico Vertical Datum of 2002"), + new EpsgVerticalDatumRecord(1124, "Virgin Islands Vertical Datum of 2009"), + new EpsgVerticalDatumRecord(1125, "American Samoa Vertical Datum of 2002"), + new EpsgVerticalDatumRecord(1126, "Guam Vertical Datum of 2004"), + new EpsgVerticalDatumRecord(1127, "Canadian Geodetic Vertical Datum of 2013 (CGG2013)"), + new EpsgVerticalDatumRecord(1129, "Japanese Standard Levelling Datum 1972"), + new EpsgVerticalDatumRecord(1130, "Japanese Geodetic Datum 2000 (vertical)"), + new EpsgVerticalDatumRecord(1131, "Japanese Geodetic Datum 2011 (vertical)"), + new EpsgVerticalDatumRecord(1140, "Singapore Height Datum"), + new EpsgVerticalDatumRecord(1146, "Ras Ghumays"), + new EpsgVerticalDatumRecord(1148, "Famagusta 1960"), + new EpsgVerticalDatumRecord(1149, "PNG08"), + new EpsgVerticalDatumRecord(1150, "Kumul 34"), + new EpsgVerticalDatumRecord(1151, "Kiunga"), + new EpsgVerticalDatumRecord(1161, "Deutsches Haupthoehennetz 1912"), + new EpsgVerticalDatumRecord(1162, "Latvian Height System 2000"), + new EpsgVerticalDatumRecord(1164, "Ordnance Datum Newlyn (Offshore)"), + new EpsgVerticalDatumRecord(1169, "New Zealand Vertical Datum 2016"), + new EpsgVerticalDatumRecord(1170, "Deutsches Haupthoehennetz 2016"), + new EpsgVerticalDatumRecord(1171, "Port Moresby 1996"), + new EpsgVerticalDatumRecord(1172, "Port Moresby 2008"), + new EpsgVerticalDatumRecord(1175, "Jamestown 1971"), + new EpsgVerticalDatumRecord(1176, "St. Helena Tritan Vertical Datum 2011"), + new EpsgVerticalDatumRecord(1177, "St. Helena Vertical Datum 2015"), + new EpsgVerticalDatumRecord(1190, "Landshaedarkerfi Islands 2004"), + new EpsgVerticalDatumRecord(1199, "Greenland Vertical Reference 2000"), + new EpsgVerticalDatumRecord(1200, "Greenland Vertical Reference 2016"), + new EpsgVerticalDatumRecord(1202, "Baltic 1957"), + new EpsgVerticalDatumRecord(1205, "EPSG example wellbore vertical datum"), + new EpsgVerticalDatumRecord(1210, "Macao Height Datum"), + new EpsgVerticalDatumRecord(1213, "Helsinki 1943"), + new EpsgVerticalDatumRecord(1215, "Slovenian Vertical System 2010"), + new EpsgVerticalDatumRecord(1216, "Serbian Vertical Reference System 2012"), + new EpsgVerticalDatumRecord(1219, "MOMRA Vertical Geodetic Control"), + new EpsgVerticalDatumRecord(1224, "Taiwan Vertical Datum 2001"), + new EpsgVerticalDatumRecord(1226, "Datum Altimetrico de Costa Rica 1952"), + new EpsgVerticalDatumRecord(1250, "IGN 2008 LD"), + new EpsgVerticalDatumRecord(1255, "Nivellement General de Nouvelle Caledonie 2008"), + new EpsgVerticalDatumRecord(1256, "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"), + new EpsgVerticalDatumRecord(1260, "Sistema de Referencia Vertical Nacional 2016"), + new EpsgVerticalDatumRecord(1261, "European Vertical Reference Frame 2000 Austria"), + new EpsgVerticalDatumRecord(1262, "South Africa Land Levelling Datum"), + new EpsgVerticalDatumRecord(1265, "HS2 Vertical Reference Frame"), + new EpsgVerticalDatumRecord(1267, "Wiener Null"), + new EpsgVerticalDatumRecord(1269, "Kingdom of Saudi Arabia Vertical Reference Frame Jeddah 2014"), + new EpsgVerticalDatumRecord(1270, "Mean Sea Level Netherlands"), + new EpsgVerticalDatumRecord(1274, "European Vertical Reference Frame 2019"), + new EpsgVerticalDatumRecord(1275, "Mallorca"), + new EpsgVerticalDatumRecord(1276, "Menorca"), + new EpsgVerticalDatumRecord(1277, "Ibiza"), + new EpsgVerticalDatumRecord(1278, "Lanzarote"), + new EpsgVerticalDatumRecord(1279, "Fuerteventura"), + new EpsgVerticalDatumRecord(1280, "Gran Canaria"), + new EpsgVerticalDatumRecord(1281, "Tenerife"), + new EpsgVerticalDatumRecord(1282, "La Gomera"), + new EpsgVerticalDatumRecord(1283, "La Palma"), + new EpsgVerticalDatumRecord(1284, "El Hierro"), + new EpsgVerticalDatumRecord(1285, "Ceuta 2"), + new EpsgVerticalDatumRecord(1287, "European Vertical Reference Frame 2019 mean tide"), + new EpsgVerticalDatumRecord(1290, "Lowest Astronomical Tide Netherlands"), + new EpsgVerticalDatumRecord(1292, "Australian Vertical Working Surface"), + new EpsgVerticalDatumRecord(1294, "Indonesian Geoid 2020 version 1"), + new EpsgVerticalDatumRecord(1296, "Baltic 1986"), + new EpsgVerticalDatumRecord(1297, "European Vertical Reference Frame 2007 Poland"), + new EpsgVerticalDatumRecord(1298, "Estonian Height System 2000"), + new EpsgVerticalDatumRecord(1299, "Lithuanian Height System 2007"), + new EpsgVerticalDatumRecord(1300, "Bulgarian Height System 2005"), + new EpsgVerticalDatumRecord(1301, "Norwegian Chart Datum"), + new EpsgVerticalDatumRecord(1302, "Local Tidal Datum at Pago Pago 2020"), + new EpsgVerticalDatumRecord(1303, "National Vertical Datum 1992"), + new EpsgVerticalDatumRecord(1306, "Catania 1965"), + new EpsgVerticalDatumRecord(1307, "Cagliari 1956"), + new EpsgVerticalDatumRecord(1316, "GNTRANS"), + new EpsgVerticalDatumRecord(1318, "GNTRANS2016"), + new EpsgVerticalDatumRecord(1323, "Svalbard vertical datum 2006"), + new EpsgVerticalDatumRecord(1325, "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"), + new EpsgVerticalDatumRecord(1326, "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"), + new EpsgVerticalDatumRecord(1328, "Indonesian Geoid 2020 version 2"), + new EpsgVerticalDatumRecord(1330, "Mean Sea Level UK & Ireland VORF08"), + new EpsgVerticalDatumRecord(1331, "Chart Datum UK & Ireland VORF08"), + new EpsgVerticalDatumRecord(1354, "Nivellement General de l'Algerie 2022"), + new EpsgVerticalDatumRecord(1361, "Chart Datum Portugal "), + new EpsgVerticalDatumRecord(1362, "Formentera"), + new EpsgVerticalDatumRecord(1363, "Alboran"), + new EpsgVerticalDatumRecord(1364, "Melilla"), + new EpsgVerticalDatumRecord(1368, "Dansk Vertikal Reference 1990 (2002)"), + new EpsgVerticalDatumRecord(1369, "Dansk Vertikal Reference 1990 (2013)"), + new EpsgVerticalDatumRecord(1370, "Dansk Vertikal Reference 1990 (2023)"), + new EpsgVerticalDatumRecord(1372, "Denmark Mean Sea Level (2022)"), + new EpsgVerticalDatumRecord(1373, "Denmark Lowest Astronomical Tide (2022)"), + new EpsgVerticalDatumRecord(1374, "Denmark Mean Sea Level (2023)"), + new EpsgVerticalDatumRecord(1375, "Denmark Lowest Astronomic Tide (2023)"), + new EpsgVerticalDatumRecord(1378, "Greenland Local Mean Sea Level (2022)"), + new EpsgVerticalDatumRecord(1381, "Saba Vertical Datum"), + new EpsgVerticalDatumRecord(1384, "Canadian Geodetic Vertical Datum of 1928 (Height Transformation version 2.0)"), + new EpsgVerticalDatumRecord(1387, "Greenland Mean Sea Level (2023)"), + new EpsgVerticalDatumRecord(1388, "Greenland Lowest Astronomic Tide (2023)"), + new EpsgVerticalDatumRecord(1390, "Baltic Sea Chart Datum 2000"), + new EpsgVerticalDatumRecord(1395, "Sint Eustatius Vertical Datum"), + new EpsgVerticalDatumRecord(1398, "Bonaire Vertical Datum"), + new EpsgVerticalDatumRecord(1415, "Asse vertical datum 2025"), + new EpsgVerticalDatumRecord(1416, "Derived California Orthometric Heights of 1988 epoch 2025"), + new EpsgVerticalDatumRecord(1423, "London Survey Grid height datum"), + new EpsgVerticalDatumRecord(1424, "Svalbard vertical datum 2024"), + new EpsgVerticalDatumRecord(1443, "IGN 2023 Mayotte"), + new EpsgVerticalDatumRecord(1451, "Bhutan Vertical Datum 2022"), + new EpsgVerticalDatumRecord(1454, "Norway Normal Null 2000:2025"), + new EpsgVerticalDatumRecord(1455, "Nivellement General de Futuna et d’Alofi 2022"), + new EpsgVerticalDatumRecord(5100, "Mean Sea Level"), + new EpsgVerticalDatumRecord(5101, "Ordnance Datum Newlyn"), + new EpsgVerticalDatumRecord(5102, "National Geodetic Vertical Datum 1929"), + new EpsgVerticalDatumRecord(5103, "North American Vertical Datum 1988"), + new EpsgVerticalDatumRecord(5104, "Yellow Sea 1956"), + new EpsgVerticalDatumRecord(5105, "Baltic 1977"), + new EpsgVerticalDatumRecord(5106, "Caspian Sea"), + new EpsgVerticalDatumRecord(5109, "Normaal Amsterdams Peil"), + new EpsgVerticalDatumRecord(5110, "Ostend"), + new EpsgVerticalDatumRecord(5111, "Australian Height Datum"), + new EpsgVerticalDatumRecord(5112, "Australian Height Datum (Tasmania)"), + new EpsgVerticalDatumRecord(5113, "Instantaneous Water Level"), + new EpsgVerticalDatumRecord(5114, "Canadian Geodetic Vertical Datum of 1928"), + new EpsgVerticalDatumRecord(5115, "Piraeus Harbour 1986"), + new EpsgVerticalDatumRecord(5116, "Helsinki 1960"), + new EpsgVerticalDatumRecord(5117, "Rikets hojdsystem 1970"), + new EpsgVerticalDatumRecord(5118, "Nivellement General de la France - Lallemand"), + new EpsgVerticalDatumRecord(5119, "Nivellement General de la France - IGN69"), + new EpsgVerticalDatumRecord(5120, "Nivellement General de la France - IGN78"), + new EpsgVerticalDatumRecord(5121, "Maputo"), + new EpsgVerticalDatumRecord(5122, "Japanese Standard Levelling Datum 1969"), + new EpsgVerticalDatumRecord(5123, "PDO Height Datum 1993"), + new EpsgVerticalDatumRecord(5124, "Fahud Height Datum"), + new EpsgVerticalDatumRecord(5125, "Ha Tien 1960"), + new EpsgVerticalDatumRecord(5126, "Hon Dau 1992"), + new EpsgVerticalDatumRecord(5127, "Landesnivellement 1902"), + new EpsgVerticalDatumRecord(5128, "Landeshohennetz 1995"), + new EpsgVerticalDatumRecord(5129, "European Vertical Reference Frame 2000"), + new EpsgVerticalDatumRecord(5130, "Malin Head"), + new EpsgVerticalDatumRecord(5131, "Belfast Lough"), + new EpsgVerticalDatumRecord(5132, "Dansk Normal Nul"), + new EpsgVerticalDatumRecord(5133, "AIOC 1995"), + new EpsgVerticalDatumRecord(5134, "Black Sea"), + new EpsgVerticalDatumRecord(5135, "Hong Kong Principal Datum"), + new EpsgVerticalDatumRecord(5136, "Hong Kong Chart Datum"), + new EpsgVerticalDatumRecord(5137, "Yellow Sea 1985"), + new EpsgVerticalDatumRecord(5138, "Ordnance Datum Newlyn (Orkney Isles)"), + new EpsgVerticalDatumRecord(5139, "Fair Isle"), + new EpsgVerticalDatumRecord(5140, "Lerwick"), + new EpsgVerticalDatumRecord(5141, "Foula"), + new EpsgVerticalDatumRecord(5142, "Sule Skerry"), + new EpsgVerticalDatumRecord(5143, "North Rona"), + new EpsgVerticalDatumRecord(5144, "Stornoway"), + new EpsgVerticalDatumRecord(5145, "St. Kilda"), + new EpsgVerticalDatumRecord(5146, "Flannan Isles"), + new EpsgVerticalDatumRecord(5147, "St. Marys"), + new EpsgVerticalDatumRecord(5148, "Douglas"), + new EpsgVerticalDatumRecord(5149, "Fao"), + new EpsgVerticalDatumRecord(5150, "Bandar Abbas"), + new EpsgVerticalDatumRecord(5151, "Nivellement General de Nouvelle Caledonie"), + new EpsgVerticalDatumRecord(5152, "Poolbeg"), + new EpsgVerticalDatumRecord(5153, "Nivellement General Guyanais 1977"), + new EpsgVerticalDatumRecord(5154, "Martinique 1987"), + new EpsgVerticalDatumRecord(5155, "Guadeloupe 1988"), + new EpsgVerticalDatumRecord(5156, "Reunion 1989"), + new EpsgVerticalDatumRecord(5157, "Auckland 1946"), + new EpsgVerticalDatumRecord(5158, "Bluff 1955"), + new EpsgVerticalDatumRecord(5159, "Dunedin 1958"), + new EpsgVerticalDatumRecord(5160, "Gisborne 1926"), + new EpsgVerticalDatumRecord(5161, "Lyttelton 1937"), + new EpsgVerticalDatumRecord(5162, "Moturiki 1953"), + new EpsgVerticalDatumRecord(5163, "Napier 1962"), + new EpsgVerticalDatumRecord(5164, "Nelson 1955"), + new EpsgVerticalDatumRecord(5165, "One Tree Point 1964"), + new EpsgVerticalDatumRecord(5166, "Tararu 1952"), + new EpsgVerticalDatumRecord(5167, "Taranaki 1970"), + new EpsgVerticalDatumRecord(5168, "Wellington 1953"), + new EpsgVerticalDatumRecord(5169, "Waitangi (Chatham Island) 1959"), + new EpsgVerticalDatumRecord(5170, "Stewart Island 1977"), + new EpsgVerticalDatumRecord(5171, "EGM96 geoid"), + new EpsgVerticalDatumRecord(5172, "Nivellement General du Luxembourg 1995"), + new EpsgVerticalDatumRecord(5173, "Antalya"), + new EpsgVerticalDatumRecord(5174, "Norway Normal Null 1954"), + new EpsgVerticalDatumRecord(5175, "Durres"), + new EpsgVerticalDatumRecord(5176, "Gebrauchshohen ADRIA"), + new EpsgVerticalDatumRecord(5177, "Slovenian Vertical System 2000"), + new EpsgVerticalDatumRecord(5178, "Cascais"), + new EpsgVerticalDatumRecord(5179, "Constanta"), + new EpsgVerticalDatumRecord(5180, "Alicante"), + new EpsgVerticalDatumRecord(5181, "Deutsches Haupthoehennetz 1992"), + new EpsgVerticalDatumRecord(5182, "Deutsches Haupthoehennetz 1985"), + new EpsgVerticalDatumRecord(5183, "Staatlichen Nivellementnetzes 1976"), + new EpsgVerticalDatumRecord(5184, "Baltic 1982"), + new EpsgVerticalDatumRecord(5185, "Baltic 1980"), + new EpsgVerticalDatumRecord(5186, "Kuwait PWD"), + new EpsgVerticalDatumRecord(5187, "KOC Well Datum"), + new EpsgVerticalDatumRecord(5188, "KOC Construction Datum"), + new EpsgVerticalDatumRecord(5189, "Nivellement General de la Corse 1948"), + new EpsgVerticalDatumRecord(5190, "Danger 1950"), + new EpsgVerticalDatumRecord(5191, "Mayotte 1950"), + new EpsgVerticalDatumRecord(5192, "Martinique 1955"), + new EpsgVerticalDatumRecord(5193, "Guadeloupe 1951"), + new EpsgVerticalDatumRecord(5194, "Lagos 1955"), + new EpsgVerticalDatumRecord(5195, "Nivellement General de Polynesie Francaise"), + new EpsgVerticalDatumRecord(5196, "IGN 1966"), + new EpsgVerticalDatumRecord(5197, "Moorea SAU 1981"), + new EpsgVerticalDatumRecord(5198, "Raiatea SAU 2001"), + new EpsgVerticalDatumRecord(5199, "Maupiti SAU 2001"), + new EpsgVerticalDatumRecord(5200, "Huahine SAU 2001"), + new EpsgVerticalDatumRecord(5201, "Tahaa SAU 2001"), + new EpsgVerticalDatumRecord(5202, "Bora Bora SAU 2001"), + new EpsgVerticalDatumRecord(5203, "EGM84 geoid"), + new EpsgVerticalDatumRecord(5204, "International Great Lakes Datum 1955"), + new EpsgVerticalDatumRecord(5205, "International Great Lakes Datum 1985"), + new EpsgVerticalDatumRecord(5206, "Dansk Vertikal Reference 1990 (2000)"), + new EpsgVerticalDatumRecord(5207, "Croatian Vertical Reference Datum 1971"), + new EpsgVerticalDatumRecord(5208, "Rikets hojdsystem 2000"), + new EpsgVerticalDatumRecord(5209, "Rikets hojdsystem 1900"), + new EpsgVerticalDatumRecord(5210, "IGN 1988 LS"), + new EpsgVerticalDatumRecord(5211, "IGN 1988 MG"), + new EpsgVerticalDatumRecord(5212, "IGN 1992 LD"), + new EpsgVerticalDatumRecord(5213, "IGN 1988 SB"), + new EpsgVerticalDatumRecord(5214, "IGN 1988 SM"), + new EpsgVerticalDatumRecord(5215, "European Vertical Reference Frame 2007"), + }; + + internal static bool TryGetCoordinateReference(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid / 1000) + { + case 2: + return TryGetCoordinateReferenceBucket2(srid, out reference, out cacheIndex); + case 3: + return TryGetCoordinateReferenceBucket3(srid, out reference, out cacheIndex); + case 4: + return TryGetCoordinateReferenceBucket4(srid, out reference, out cacheIndex); + case 5: + return TryGetCoordinateReferenceBucket5(srid, out reference, out cacheIndex); + case 6: + return TryGetCoordinateReferenceBucket6(srid, out reference, out cacheIndex); + case 7: + return TryGetCoordinateReferenceBucket7(srid, out reference, out cacheIndex); + case 8: + return TryGetCoordinateReferenceBucket8(srid, out reference, out cacheIndex); + case 9: + return TryGetCoordinateReferenceBucket9(srid, out reference, out cacheIndex); + case 10: + return TryGetCoordinateReferenceBucket10(srid, out reference, out cacheIndex); + case 11: + return TryGetCoordinateReferenceBucket11(srid, out reference, out cacheIndex); + case 20: + return TryGetCoordinateReferenceBucket20(srid, out reference, out cacheIndex); + case 21: + return TryGetCoordinateReferenceBucket21(srid, out reference, out cacheIndex); + case 22: + return TryGetCoordinateReferenceBucket22(srid, out reference, out cacheIndex); + case 23: + return TryGetCoordinateReferenceBucket23(srid, out reference, out cacheIndex); + case 24: + return TryGetCoordinateReferenceBucket24(srid, out reference, out cacheIndex); + case 25: + return TryGetCoordinateReferenceBucket25(srid, out reference, out cacheIndex); + case 26: + return TryGetCoordinateReferenceBucket26(srid, out reference, out cacheIndex); + case 27: + return TryGetCoordinateReferenceBucket27(srid, out reference, out cacheIndex); + case 28: + return TryGetCoordinateReferenceBucket28(srid, out reference, out cacheIndex); + case 29: + return TryGetCoordinateReferenceBucket29(srid, out reference, out cacheIndex); + case 30: + return TryGetCoordinateReferenceBucket30(srid, out reference, out cacheIndex); + case 31: + return TryGetCoordinateReferenceBucket31(srid, out reference, out cacheIndex); + case 32: + return TryGetCoordinateReferenceBucket32(srid, out reference, out cacheIndex); + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket2(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 2000: + cacheIndex = 0; + reference = new EpsgCoordinateReferenceRecord(2000, (EpsgCoordinateSystemKind)2, 0); + return true; + case 2001: + cacheIndex = 1; + reference = new EpsgCoordinateReferenceRecord(2001, (EpsgCoordinateSystemKind)2, 1); + return true; + case 2002: + cacheIndex = 2; + reference = new EpsgCoordinateReferenceRecord(2002, (EpsgCoordinateSystemKind)2, 2); + return true; + case 2003: + cacheIndex = 3; + reference = new EpsgCoordinateReferenceRecord(2003, (EpsgCoordinateSystemKind)2, 3); + return true; + case 2004: + cacheIndex = 4; + reference = new EpsgCoordinateReferenceRecord(2004, (EpsgCoordinateSystemKind)2, 4); + return true; + case 2005: + cacheIndex = 5; + reference = new EpsgCoordinateReferenceRecord(2005, (EpsgCoordinateSystemKind)2, 5); + return true; + case 2006: + cacheIndex = 6; + reference = new EpsgCoordinateReferenceRecord(2006, (EpsgCoordinateSystemKind)2, 6); + return true; + case 2007: + cacheIndex = 7; + reference = new EpsgCoordinateReferenceRecord(2007, (EpsgCoordinateSystemKind)2, 7); + return true; + case 2009: + cacheIndex = 8; + reference = new EpsgCoordinateReferenceRecord(2009, (EpsgCoordinateSystemKind)2, 8); + return true; + case 2010: + cacheIndex = 9; + reference = new EpsgCoordinateReferenceRecord(2010, (EpsgCoordinateSystemKind)2, 9); + return true; + case 2011: + cacheIndex = 10; + reference = new EpsgCoordinateReferenceRecord(2011, (EpsgCoordinateSystemKind)2, 10); + return true; + case 2012: + cacheIndex = 11; + reference = new EpsgCoordinateReferenceRecord(2012, (EpsgCoordinateSystemKind)2, 11); + return true; + case 2013: + cacheIndex = 12; + reference = new EpsgCoordinateReferenceRecord(2013, (EpsgCoordinateSystemKind)2, 12); + return true; + case 2014: + cacheIndex = 13; + reference = new EpsgCoordinateReferenceRecord(2014, (EpsgCoordinateSystemKind)2, 13); + return true; + case 2015: + cacheIndex = 14; + reference = new EpsgCoordinateReferenceRecord(2015, (EpsgCoordinateSystemKind)2, 14); + return true; + case 2016: + cacheIndex = 15; + reference = new EpsgCoordinateReferenceRecord(2016, (EpsgCoordinateSystemKind)2, 15); + return true; + case 2017: + cacheIndex = 16; + reference = new EpsgCoordinateReferenceRecord(2017, (EpsgCoordinateSystemKind)2, 16); + return true; + case 2018: + cacheIndex = 17; + reference = new EpsgCoordinateReferenceRecord(2018, (EpsgCoordinateSystemKind)2, 17); + return true; + case 2019: + cacheIndex = 18; + reference = new EpsgCoordinateReferenceRecord(2019, (EpsgCoordinateSystemKind)2, 18); + return true; + case 2020: + cacheIndex = 19; + reference = new EpsgCoordinateReferenceRecord(2020, (EpsgCoordinateSystemKind)2, 19); + return true; + case 2021: + cacheIndex = 20; + reference = new EpsgCoordinateReferenceRecord(2021, (EpsgCoordinateSystemKind)2, 20); + return true; + case 2022: + cacheIndex = 21; + reference = new EpsgCoordinateReferenceRecord(2022, (EpsgCoordinateSystemKind)2, 21); + return true; + case 2023: + cacheIndex = 22; + reference = new EpsgCoordinateReferenceRecord(2023, (EpsgCoordinateSystemKind)2, 22); + return true; + case 2024: + cacheIndex = 23; + reference = new EpsgCoordinateReferenceRecord(2024, (EpsgCoordinateSystemKind)2, 23); + return true; + case 2025: + cacheIndex = 24; + reference = new EpsgCoordinateReferenceRecord(2025, (EpsgCoordinateSystemKind)2, 24); + return true; + case 2026: + cacheIndex = 25; + reference = new EpsgCoordinateReferenceRecord(2026, (EpsgCoordinateSystemKind)2, 25); + return true; + case 2027: + cacheIndex = 26; + reference = new EpsgCoordinateReferenceRecord(2027, (EpsgCoordinateSystemKind)2, 26); + return true; + case 2028: + cacheIndex = 27; + reference = new EpsgCoordinateReferenceRecord(2028, (EpsgCoordinateSystemKind)2, 27); + return true; + case 2029: + cacheIndex = 28; + reference = new EpsgCoordinateReferenceRecord(2029, (EpsgCoordinateSystemKind)2, 28); + return true; + case 2030: + cacheIndex = 29; + reference = new EpsgCoordinateReferenceRecord(2030, (EpsgCoordinateSystemKind)2, 29); + return true; + case 2031: + cacheIndex = 30; + reference = new EpsgCoordinateReferenceRecord(2031, (EpsgCoordinateSystemKind)2, 30); + return true; + case 2032: + cacheIndex = 31; + reference = new EpsgCoordinateReferenceRecord(2032, (EpsgCoordinateSystemKind)2, 31); + return true; + case 2033: + cacheIndex = 32; + reference = new EpsgCoordinateReferenceRecord(2033, (EpsgCoordinateSystemKind)2, 32); + return true; + case 2034: + cacheIndex = 33; + reference = new EpsgCoordinateReferenceRecord(2034, (EpsgCoordinateSystemKind)2, 33); + return true; + case 2035: + cacheIndex = 34; + reference = new EpsgCoordinateReferenceRecord(2035, (EpsgCoordinateSystemKind)2, 34); + return true; + case 2039: + cacheIndex = 35; + reference = new EpsgCoordinateReferenceRecord(2039, (EpsgCoordinateSystemKind)2, 35); + return true; + case 2040: + cacheIndex = 36; + reference = new EpsgCoordinateReferenceRecord(2040, (EpsgCoordinateSystemKind)2, 36); + return true; + case 2041: + cacheIndex = 37; + reference = new EpsgCoordinateReferenceRecord(2041, (EpsgCoordinateSystemKind)2, 37); + return true; + case 2042: + cacheIndex = 38; + reference = new EpsgCoordinateReferenceRecord(2042, (EpsgCoordinateSystemKind)2, 38); + return true; + case 2043: + cacheIndex = 39; + reference = new EpsgCoordinateReferenceRecord(2043, (EpsgCoordinateSystemKind)2, 39); + return true; + case 2044: + cacheIndex = 40; + reference = new EpsgCoordinateReferenceRecord(2044, (EpsgCoordinateSystemKind)2, 40); + return true; + case 2045: + cacheIndex = 41; + reference = new EpsgCoordinateReferenceRecord(2045, (EpsgCoordinateSystemKind)2, 41); + return true; + case 2046: + cacheIndex = 42; + reference = new EpsgCoordinateReferenceRecord(2046, (EpsgCoordinateSystemKind)2, 42); + return true; + case 2047: + cacheIndex = 43; + reference = new EpsgCoordinateReferenceRecord(2047, (EpsgCoordinateSystemKind)2, 43); + return true; + case 2048: + cacheIndex = 44; + reference = new EpsgCoordinateReferenceRecord(2048, (EpsgCoordinateSystemKind)2, 44); + return true; + case 2049: + cacheIndex = 45; + reference = new EpsgCoordinateReferenceRecord(2049, (EpsgCoordinateSystemKind)2, 45); + return true; + case 2050: + cacheIndex = 46; + reference = new EpsgCoordinateReferenceRecord(2050, (EpsgCoordinateSystemKind)2, 46); + return true; + case 2051: + cacheIndex = 47; + reference = new EpsgCoordinateReferenceRecord(2051, (EpsgCoordinateSystemKind)2, 47); + return true; + case 2052: + cacheIndex = 48; + reference = new EpsgCoordinateReferenceRecord(2052, (EpsgCoordinateSystemKind)2, 48); + return true; + case 2053: + cacheIndex = 49; + reference = new EpsgCoordinateReferenceRecord(2053, (EpsgCoordinateSystemKind)2, 49); + return true; + case 2054: + cacheIndex = 50; + reference = new EpsgCoordinateReferenceRecord(2054, (EpsgCoordinateSystemKind)2, 50); + return true; + case 2055: + cacheIndex = 51; + reference = new EpsgCoordinateReferenceRecord(2055, (EpsgCoordinateSystemKind)2, 51); + return true; + case 2056: + cacheIndex = 52; + reference = new EpsgCoordinateReferenceRecord(2056, (EpsgCoordinateSystemKind)2, 52); + return true; + case 2057: + cacheIndex = 53; + reference = new EpsgCoordinateReferenceRecord(2057, (EpsgCoordinateSystemKind)2, 53); + return true; + case 2058: + cacheIndex = 54; + reference = new EpsgCoordinateReferenceRecord(2058, (EpsgCoordinateSystemKind)2, 54); + return true; + case 2059: + cacheIndex = 55; + reference = new EpsgCoordinateReferenceRecord(2059, (EpsgCoordinateSystemKind)2, 55); + return true; + case 2060: + cacheIndex = 56; + reference = new EpsgCoordinateReferenceRecord(2060, (EpsgCoordinateSystemKind)2, 56); + return true; + case 2061: + cacheIndex = 57; + reference = new EpsgCoordinateReferenceRecord(2061, (EpsgCoordinateSystemKind)2, 57); + return true; + case 2062: + cacheIndex = 58; + reference = new EpsgCoordinateReferenceRecord(2062, (EpsgCoordinateSystemKind)2, 58); + return true; + case 2065: + cacheIndex = 59; + reference = new EpsgCoordinateReferenceRecord(2065, (EpsgCoordinateSystemKind)2, 59); + return true; + case 2066: + cacheIndex = 60; + reference = new EpsgCoordinateReferenceRecord(2066, (EpsgCoordinateSystemKind)2, 60); + return true; + case 2067: + cacheIndex = 61; + reference = new EpsgCoordinateReferenceRecord(2067, (EpsgCoordinateSystemKind)2, 61); + return true; + case 2068: + cacheIndex = 62; + reference = new EpsgCoordinateReferenceRecord(2068, (EpsgCoordinateSystemKind)2, 62); + return true; + case 2069: + cacheIndex = 63; + reference = new EpsgCoordinateReferenceRecord(2069, (EpsgCoordinateSystemKind)2, 63); + return true; + case 2070: + cacheIndex = 64; + reference = new EpsgCoordinateReferenceRecord(2070, (EpsgCoordinateSystemKind)2, 64); + return true; + case 2071: + cacheIndex = 65; + reference = new EpsgCoordinateReferenceRecord(2071, (EpsgCoordinateSystemKind)2, 65); + return true; + case 2072: + cacheIndex = 66; + reference = new EpsgCoordinateReferenceRecord(2072, (EpsgCoordinateSystemKind)2, 66); + return true; + case 2073: + cacheIndex = 67; + reference = new EpsgCoordinateReferenceRecord(2073, (EpsgCoordinateSystemKind)2, 67); + return true; + case 2074: + cacheIndex = 68; + reference = new EpsgCoordinateReferenceRecord(2074, (EpsgCoordinateSystemKind)2, 68); + return true; + case 2075: + cacheIndex = 69; + reference = new EpsgCoordinateReferenceRecord(2075, (EpsgCoordinateSystemKind)2, 69); + return true; + case 2076: + cacheIndex = 70; + reference = new EpsgCoordinateReferenceRecord(2076, (EpsgCoordinateSystemKind)2, 70); + return true; + case 2077: + cacheIndex = 71; + reference = new EpsgCoordinateReferenceRecord(2077, (EpsgCoordinateSystemKind)2, 71); + return true; + case 2078: + cacheIndex = 72; + reference = new EpsgCoordinateReferenceRecord(2078, (EpsgCoordinateSystemKind)2, 72); + return true; + case 2079: + cacheIndex = 73; + reference = new EpsgCoordinateReferenceRecord(2079, (EpsgCoordinateSystemKind)2, 73); + return true; + case 2080: + cacheIndex = 74; + reference = new EpsgCoordinateReferenceRecord(2080, (EpsgCoordinateSystemKind)2, 74); + return true; + case 2081: + cacheIndex = 75; + reference = new EpsgCoordinateReferenceRecord(2081, (EpsgCoordinateSystemKind)2, 75); + return true; + case 2082: + cacheIndex = 76; + reference = new EpsgCoordinateReferenceRecord(2082, (EpsgCoordinateSystemKind)2, 76); + return true; + case 2083: + cacheIndex = 77; + reference = new EpsgCoordinateReferenceRecord(2083, (EpsgCoordinateSystemKind)2, 77); + return true; + case 2084: + cacheIndex = 78; + reference = new EpsgCoordinateReferenceRecord(2084, (EpsgCoordinateSystemKind)2, 78); + return true; + case 2087: + cacheIndex = 79; + reference = new EpsgCoordinateReferenceRecord(2087, (EpsgCoordinateSystemKind)2, 79); + return true; + case 2088: + cacheIndex = 80; + reference = new EpsgCoordinateReferenceRecord(2088, (EpsgCoordinateSystemKind)2, 80); + return true; + case 2089: + cacheIndex = 81; + reference = new EpsgCoordinateReferenceRecord(2089, (EpsgCoordinateSystemKind)2, 81); + return true; + case 2090: + cacheIndex = 82; + reference = new EpsgCoordinateReferenceRecord(2090, (EpsgCoordinateSystemKind)2, 82); + return true; + case 2093: + cacheIndex = 83; + reference = new EpsgCoordinateReferenceRecord(2093, (EpsgCoordinateSystemKind)2, 83); + return true; + case 2094: + cacheIndex = 84; + reference = new EpsgCoordinateReferenceRecord(2094, (EpsgCoordinateSystemKind)2, 84); + return true; + case 2095: + cacheIndex = 85; + reference = new EpsgCoordinateReferenceRecord(2095, (EpsgCoordinateSystemKind)2, 85); + return true; + case 2096: + cacheIndex = 86; + reference = new EpsgCoordinateReferenceRecord(2096, (EpsgCoordinateSystemKind)2, 86); + return true; + case 2097: + cacheIndex = 87; + reference = new EpsgCoordinateReferenceRecord(2097, (EpsgCoordinateSystemKind)2, 87); + return true; + case 2098: + cacheIndex = 88; + reference = new EpsgCoordinateReferenceRecord(2098, (EpsgCoordinateSystemKind)2, 88); + return true; + case 2099: + cacheIndex = 89; + reference = new EpsgCoordinateReferenceRecord(2099, (EpsgCoordinateSystemKind)2, 89); + return true; + case 2100: + cacheIndex = 90; + reference = new EpsgCoordinateReferenceRecord(2100, (EpsgCoordinateSystemKind)2, 90); + return true; + case 2101: + cacheIndex = 91; + reference = new EpsgCoordinateReferenceRecord(2101, (EpsgCoordinateSystemKind)2, 91); + return true; + case 2102: + cacheIndex = 92; + reference = new EpsgCoordinateReferenceRecord(2102, (EpsgCoordinateSystemKind)2, 92); + return true; + case 2103: + cacheIndex = 93; + reference = new EpsgCoordinateReferenceRecord(2103, (EpsgCoordinateSystemKind)2, 93); + return true; + case 2104: + cacheIndex = 94; + reference = new EpsgCoordinateReferenceRecord(2104, (EpsgCoordinateSystemKind)2, 94); + return true; + case 2105: + cacheIndex = 95; + reference = new EpsgCoordinateReferenceRecord(2105, (EpsgCoordinateSystemKind)2, 95); + return true; + case 2106: + cacheIndex = 96; + reference = new EpsgCoordinateReferenceRecord(2106, (EpsgCoordinateSystemKind)2, 96); + return true; + case 2107: + cacheIndex = 97; + reference = new EpsgCoordinateReferenceRecord(2107, (EpsgCoordinateSystemKind)2, 97); + return true; + case 2108: + cacheIndex = 98; + reference = new EpsgCoordinateReferenceRecord(2108, (EpsgCoordinateSystemKind)2, 98); + return true; + case 2109: + cacheIndex = 99; + reference = new EpsgCoordinateReferenceRecord(2109, (EpsgCoordinateSystemKind)2, 99); + return true; + case 2110: + cacheIndex = 100; + reference = new EpsgCoordinateReferenceRecord(2110, (EpsgCoordinateSystemKind)2, 100); + return true; + case 2111: + cacheIndex = 101; + reference = new EpsgCoordinateReferenceRecord(2111, (EpsgCoordinateSystemKind)2, 101); + return true; + case 2112: + cacheIndex = 102; + reference = new EpsgCoordinateReferenceRecord(2112, (EpsgCoordinateSystemKind)2, 102); + return true; + case 2113: + cacheIndex = 103; + reference = new EpsgCoordinateReferenceRecord(2113, (EpsgCoordinateSystemKind)2, 103); + return true; + case 2114: + cacheIndex = 104; + reference = new EpsgCoordinateReferenceRecord(2114, (EpsgCoordinateSystemKind)2, 104); + return true; + case 2115: + cacheIndex = 105; + reference = new EpsgCoordinateReferenceRecord(2115, (EpsgCoordinateSystemKind)2, 105); + return true; + case 2116: + cacheIndex = 106; + reference = new EpsgCoordinateReferenceRecord(2116, (EpsgCoordinateSystemKind)2, 106); + return true; + case 2117: + cacheIndex = 107; + reference = new EpsgCoordinateReferenceRecord(2117, (EpsgCoordinateSystemKind)2, 107); + return true; + case 2118: + cacheIndex = 108; + reference = new EpsgCoordinateReferenceRecord(2118, (EpsgCoordinateSystemKind)2, 108); + return true; + case 2119: + cacheIndex = 109; + reference = new EpsgCoordinateReferenceRecord(2119, (EpsgCoordinateSystemKind)2, 109); + return true; + case 2120: + cacheIndex = 110; + reference = new EpsgCoordinateReferenceRecord(2120, (EpsgCoordinateSystemKind)2, 110); + return true; + case 2121: + cacheIndex = 111; + reference = new EpsgCoordinateReferenceRecord(2121, (EpsgCoordinateSystemKind)2, 111); + return true; + case 2122: + cacheIndex = 112; + reference = new EpsgCoordinateReferenceRecord(2122, (EpsgCoordinateSystemKind)2, 112); + return true; + case 2123: + cacheIndex = 113; + reference = new EpsgCoordinateReferenceRecord(2123, (EpsgCoordinateSystemKind)2, 113); + return true; + case 2124: + cacheIndex = 114; + reference = new EpsgCoordinateReferenceRecord(2124, (EpsgCoordinateSystemKind)2, 114); + return true; + case 2125: + cacheIndex = 115; + reference = new EpsgCoordinateReferenceRecord(2125, (EpsgCoordinateSystemKind)2, 115); + return true; + case 2126: + cacheIndex = 116; + reference = new EpsgCoordinateReferenceRecord(2126, (EpsgCoordinateSystemKind)2, 116); + return true; + case 2127: + cacheIndex = 117; + reference = new EpsgCoordinateReferenceRecord(2127, (EpsgCoordinateSystemKind)2, 117); + return true; + case 2128: + cacheIndex = 118; + reference = new EpsgCoordinateReferenceRecord(2128, (EpsgCoordinateSystemKind)2, 118); + return true; + case 2129: + cacheIndex = 119; + reference = new EpsgCoordinateReferenceRecord(2129, (EpsgCoordinateSystemKind)2, 119); + return true; + case 2130: + cacheIndex = 120; + reference = new EpsgCoordinateReferenceRecord(2130, (EpsgCoordinateSystemKind)2, 120); + return true; + case 2131: + cacheIndex = 121; + reference = new EpsgCoordinateReferenceRecord(2131, (EpsgCoordinateSystemKind)2, 121); + return true; + case 2132: + cacheIndex = 122; + reference = new EpsgCoordinateReferenceRecord(2132, (EpsgCoordinateSystemKind)2, 122); + return true; + case 2133: + cacheIndex = 123; + reference = new EpsgCoordinateReferenceRecord(2133, (EpsgCoordinateSystemKind)2, 123); + return true; + case 2134: + cacheIndex = 124; + reference = new EpsgCoordinateReferenceRecord(2134, (EpsgCoordinateSystemKind)2, 124); + return true; + case 2135: + cacheIndex = 125; + reference = new EpsgCoordinateReferenceRecord(2135, (EpsgCoordinateSystemKind)2, 125); + return true; + case 2136: + cacheIndex = 126; + reference = new EpsgCoordinateReferenceRecord(2136, (EpsgCoordinateSystemKind)2, 126); + return true; + case 2137: + cacheIndex = 127; + reference = new EpsgCoordinateReferenceRecord(2137, (EpsgCoordinateSystemKind)2, 127); + return true; + case 2138: + cacheIndex = 128; + reference = new EpsgCoordinateReferenceRecord(2138, (EpsgCoordinateSystemKind)2, 128); + return true; + case 2154: + cacheIndex = 129; + reference = new EpsgCoordinateReferenceRecord(2154, (EpsgCoordinateSystemKind)2, 129); + return true; + case 2157: + cacheIndex = 130; + reference = new EpsgCoordinateReferenceRecord(2157, (EpsgCoordinateSystemKind)2, 130); + return true; + case 2158: + cacheIndex = 131; + reference = new EpsgCoordinateReferenceRecord(2158, (EpsgCoordinateSystemKind)2, 131); + return true; + case 2159: + cacheIndex = 132; + reference = new EpsgCoordinateReferenceRecord(2159, (EpsgCoordinateSystemKind)2, 132); + return true; + case 2160: + cacheIndex = 133; + reference = new EpsgCoordinateReferenceRecord(2160, (EpsgCoordinateSystemKind)2, 133); + return true; + case 2161: + cacheIndex = 134; + reference = new EpsgCoordinateReferenceRecord(2161, (EpsgCoordinateSystemKind)2, 134); + return true; + case 2162: + cacheIndex = 135; + reference = new EpsgCoordinateReferenceRecord(2162, (EpsgCoordinateSystemKind)2, 135); + return true; + case 2164: + cacheIndex = 136; + reference = new EpsgCoordinateReferenceRecord(2164, (EpsgCoordinateSystemKind)2, 136); + return true; + case 2165: + cacheIndex = 137; + reference = new EpsgCoordinateReferenceRecord(2165, (EpsgCoordinateSystemKind)2, 137); + return true; + case 2169: + cacheIndex = 138; + reference = new EpsgCoordinateReferenceRecord(2169, (EpsgCoordinateSystemKind)2, 138); + return true; + case 2172: + cacheIndex = 139; + reference = new EpsgCoordinateReferenceRecord(2172, (EpsgCoordinateSystemKind)2, 139); + return true; + case 2173: + cacheIndex = 140; + reference = new EpsgCoordinateReferenceRecord(2173, (EpsgCoordinateSystemKind)2, 140); + return true; + case 2174: + cacheIndex = 141; + reference = new EpsgCoordinateReferenceRecord(2174, (EpsgCoordinateSystemKind)2, 141); + return true; + case 2175: + cacheIndex = 142; + reference = new EpsgCoordinateReferenceRecord(2175, (EpsgCoordinateSystemKind)2, 142); + return true; + case 2176: + cacheIndex = 143; + reference = new EpsgCoordinateReferenceRecord(2176, (EpsgCoordinateSystemKind)2, 143); + return true; + case 2177: + cacheIndex = 144; + reference = new EpsgCoordinateReferenceRecord(2177, (EpsgCoordinateSystemKind)2, 144); + return true; + case 2178: + cacheIndex = 145; + reference = new EpsgCoordinateReferenceRecord(2178, (EpsgCoordinateSystemKind)2, 145); + return true; + case 2179: + cacheIndex = 146; + reference = new EpsgCoordinateReferenceRecord(2179, (EpsgCoordinateSystemKind)2, 146); + return true; + case 2180: + cacheIndex = 147; + reference = new EpsgCoordinateReferenceRecord(2180, (EpsgCoordinateSystemKind)2, 147); + return true; + case 2188: + cacheIndex = 148; + reference = new EpsgCoordinateReferenceRecord(2188, (EpsgCoordinateSystemKind)2, 148); + return true; + case 2189: + cacheIndex = 149; + reference = new EpsgCoordinateReferenceRecord(2189, (EpsgCoordinateSystemKind)2, 149); + return true; + case 2190: + cacheIndex = 150; + reference = new EpsgCoordinateReferenceRecord(2190, (EpsgCoordinateSystemKind)2, 150); + return true; + case 2193: + cacheIndex = 151; + reference = new EpsgCoordinateReferenceRecord(2193, (EpsgCoordinateSystemKind)2, 151); + return true; + case 2195: + cacheIndex = 152; + reference = new EpsgCoordinateReferenceRecord(2195, (EpsgCoordinateSystemKind)2, 152); + return true; + case 2196: + cacheIndex = 153; + reference = new EpsgCoordinateReferenceRecord(2196, (EpsgCoordinateSystemKind)2, 153); + return true; + case 2197: + cacheIndex = 154; + reference = new EpsgCoordinateReferenceRecord(2197, (EpsgCoordinateSystemKind)2, 154); + return true; + case 2198: + cacheIndex = 155; + reference = new EpsgCoordinateReferenceRecord(2198, (EpsgCoordinateSystemKind)2, 155); + return true; + case 2200: + cacheIndex = 156; + reference = new EpsgCoordinateReferenceRecord(2200, (EpsgCoordinateSystemKind)2, 156); + return true; + case 2201: + cacheIndex = 157; + reference = new EpsgCoordinateReferenceRecord(2201, (EpsgCoordinateSystemKind)2, 157); + return true; + case 2202: + cacheIndex = 158; + reference = new EpsgCoordinateReferenceRecord(2202, (EpsgCoordinateSystemKind)2, 158); + return true; + case 2203: + cacheIndex = 159; + reference = new EpsgCoordinateReferenceRecord(2203, (EpsgCoordinateSystemKind)2, 159); + return true; + case 2204: + cacheIndex = 160; + reference = new EpsgCoordinateReferenceRecord(2204, (EpsgCoordinateSystemKind)2, 160); + return true; + case 2205: + cacheIndex = 161; + reference = new EpsgCoordinateReferenceRecord(2205, (EpsgCoordinateSystemKind)2, 161); + return true; + case 2206: + cacheIndex = 162; + reference = new EpsgCoordinateReferenceRecord(2206, (EpsgCoordinateSystemKind)2, 162); + return true; + case 2207: + cacheIndex = 163; + reference = new EpsgCoordinateReferenceRecord(2207, (EpsgCoordinateSystemKind)2, 163); + return true; + case 2208: + cacheIndex = 164; + reference = new EpsgCoordinateReferenceRecord(2208, (EpsgCoordinateSystemKind)2, 164); + return true; + case 2209: + cacheIndex = 165; + reference = new EpsgCoordinateReferenceRecord(2209, (EpsgCoordinateSystemKind)2, 165); + return true; + case 2210: + cacheIndex = 166; + reference = new EpsgCoordinateReferenceRecord(2210, (EpsgCoordinateSystemKind)2, 166); + return true; + case 2211: + cacheIndex = 167; + reference = new EpsgCoordinateReferenceRecord(2211, (EpsgCoordinateSystemKind)2, 167); + return true; + case 2212: + cacheIndex = 168; + reference = new EpsgCoordinateReferenceRecord(2212, (EpsgCoordinateSystemKind)2, 168); + return true; + case 2213: + cacheIndex = 169; + reference = new EpsgCoordinateReferenceRecord(2213, (EpsgCoordinateSystemKind)2, 169); + return true; + case 2215: + cacheIndex = 170; + reference = new EpsgCoordinateReferenceRecord(2215, (EpsgCoordinateSystemKind)2, 170); + return true; + case 2216: + cacheIndex = 171; + reference = new EpsgCoordinateReferenceRecord(2216, (EpsgCoordinateSystemKind)2, 171); + return true; + case 2217: + cacheIndex = 172; + reference = new EpsgCoordinateReferenceRecord(2217, (EpsgCoordinateSystemKind)2, 172); + return true; + case 2218: + cacheIndex = 173; + reference = new EpsgCoordinateReferenceRecord(2218, (EpsgCoordinateSystemKind)2, 173); + return true; + case 2219: + cacheIndex = 174; + reference = new EpsgCoordinateReferenceRecord(2219, (EpsgCoordinateSystemKind)2, 174); + return true; + case 2220: + cacheIndex = 175; + reference = new EpsgCoordinateReferenceRecord(2220, (EpsgCoordinateSystemKind)2, 175); + return true; + case 2221: + cacheIndex = 176; + reference = new EpsgCoordinateReferenceRecord(2221, (EpsgCoordinateSystemKind)2, 176); + return true; + case 2222: + cacheIndex = 177; + reference = new EpsgCoordinateReferenceRecord(2222, (EpsgCoordinateSystemKind)2, 177); + return true; + case 2223: + cacheIndex = 178; + reference = new EpsgCoordinateReferenceRecord(2223, (EpsgCoordinateSystemKind)2, 178); + return true; + case 2224: + cacheIndex = 179; + reference = new EpsgCoordinateReferenceRecord(2224, (EpsgCoordinateSystemKind)2, 179); + return true; + case 2225: + cacheIndex = 180; + reference = new EpsgCoordinateReferenceRecord(2225, (EpsgCoordinateSystemKind)2, 180); + return true; + case 2226: + cacheIndex = 181; + reference = new EpsgCoordinateReferenceRecord(2226, (EpsgCoordinateSystemKind)2, 181); + return true; + case 2227: + cacheIndex = 182; + reference = new EpsgCoordinateReferenceRecord(2227, (EpsgCoordinateSystemKind)2, 182); + return true; + case 2228: + cacheIndex = 183; + reference = new EpsgCoordinateReferenceRecord(2228, (EpsgCoordinateSystemKind)2, 183); + return true; + case 2229: + cacheIndex = 184; + reference = new EpsgCoordinateReferenceRecord(2229, (EpsgCoordinateSystemKind)2, 184); + return true; + case 2230: + cacheIndex = 185; + reference = new EpsgCoordinateReferenceRecord(2230, (EpsgCoordinateSystemKind)2, 185); + return true; + case 2231: + cacheIndex = 186; + reference = new EpsgCoordinateReferenceRecord(2231, (EpsgCoordinateSystemKind)2, 186); + return true; + case 2232: + cacheIndex = 187; + reference = new EpsgCoordinateReferenceRecord(2232, (EpsgCoordinateSystemKind)2, 187); + return true; + case 2233: + cacheIndex = 188; + reference = new EpsgCoordinateReferenceRecord(2233, (EpsgCoordinateSystemKind)2, 188); + return true; + case 2234: + cacheIndex = 189; + reference = new EpsgCoordinateReferenceRecord(2234, (EpsgCoordinateSystemKind)2, 189); + return true; + case 2235: + cacheIndex = 190; + reference = new EpsgCoordinateReferenceRecord(2235, (EpsgCoordinateSystemKind)2, 190); + return true; + case 2236: + cacheIndex = 191; + reference = new EpsgCoordinateReferenceRecord(2236, (EpsgCoordinateSystemKind)2, 191); + return true; + case 2237: + cacheIndex = 192; + reference = new EpsgCoordinateReferenceRecord(2237, (EpsgCoordinateSystemKind)2, 192); + return true; + case 2238: + cacheIndex = 193; + reference = new EpsgCoordinateReferenceRecord(2238, (EpsgCoordinateSystemKind)2, 193); + return true; + case 2239: + cacheIndex = 194; + reference = new EpsgCoordinateReferenceRecord(2239, (EpsgCoordinateSystemKind)2, 194); + return true; + case 2240: + cacheIndex = 195; + reference = new EpsgCoordinateReferenceRecord(2240, (EpsgCoordinateSystemKind)2, 195); + return true; + case 2241: + cacheIndex = 196; + reference = new EpsgCoordinateReferenceRecord(2241, (EpsgCoordinateSystemKind)2, 196); + return true; + case 2242: + cacheIndex = 197; + reference = new EpsgCoordinateReferenceRecord(2242, (EpsgCoordinateSystemKind)2, 197); + return true; + case 2243: + cacheIndex = 198; + reference = new EpsgCoordinateReferenceRecord(2243, (EpsgCoordinateSystemKind)2, 198); + return true; + case 2246: + cacheIndex = 199; + reference = new EpsgCoordinateReferenceRecord(2246, (EpsgCoordinateSystemKind)2, 199); + return true; + case 2247: + cacheIndex = 200; + reference = new EpsgCoordinateReferenceRecord(2247, (EpsgCoordinateSystemKind)2, 200); + return true; + case 2248: + cacheIndex = 201; + reference = new EpsgCoordinateReferenceRecord(2248, (EpsgCoordinateSystemKind)2, 201); + return true; + case 2249: + cacheIndex = 202; + reference = new EpsgCoordinateReferenceRecord(2249, (EpsgCoordinateSystemKind)2, 202); + return true; + case 2250: + cacheIndex = 203; + reference = new EpsgCoordinateReferenceRecord(2250, (EpsgCoordinateSystemKind)2, 203); + return true; + case 2251: + cacheIndex = 204; + reference = new EpsgCoordinateReferenceRecord(2251, (EpsgCoordinateSystemKind)2, 204); + return true; + case 2252: + cacheIndex = 205; + reference = new EpsgCoordinateReferenceRecord(2252, (EpsgCoordinateSystemKind)2, 205); + return true; + case 2253: + cacheIndex = 206; + reference = new EpsgCoordinateReferenceRecord(2253, (EpsgCoordinateSystemKind)2, 206); + return true; + case 2254: + cacheIndex = 207; + reference = new EpsgCoordinateReferenceRecord(2254, (EpsgCoordinateSystemKind)2, 207); + return true; + case 2255: + cacheIndex = 208; + reference = new EpsgCoordinateReferenceRecord(2255, (EpsgCoordinateSystemKind)2, 208); + return true; + case 2256: + cacheIndex = 209; + reference = new EpsgCoordinateReferenceRecord(2256, (EpsgCoordinateSystemKind)2, 209); + return true; + case 2257: + cacheIndex = 210; + reference = new EpsgCoordinateReferenceRecord(2257, (EpsgCoordinateSystemKind)2, 210); + return true; + case 2258: + cacheIndex = 211; + reference = new EpsgCoordinateReferenceRecord(2258, (EpsgCoordinateSystemKind)2, 211); + return true; + case 2259: + cacheIndex = 212; + reference = new EpsgCoordinateReferenceRecord(2259, (EpsgCoordinateSystemKind)2, 212); + return true; + case 2260: + cacheIndex = 213; + reference = new EpsgCoordinateReferenceRecord(2260, (EpsgCoordinateSystemKind)2, 213); + return true; + case 2261: + cacheIndex = 214; + reference = new EpsgCoordinateReferenceRecord(2261, (EpsgCoordinateSystemKind)2, 214); + return true; + case 2262: + cacheIndex = 215; + reference = new EpsgCoordinateReferenceRecord(2262, (EpsgCoordinateSystemKind)2, 215); + return true; + case 2263: + cacheIndex = 216; + reference = new EpsgCoordinateReferenceRecord(2263, (EpsgCoordinateSystemKind)2, 216); + return true; + case 2264: + cacheIndex = 217; + reference = new EpsgCoordinateReferenceRecord(2264, (EpsgCoordinateSystemKind)2, 217); + return true; + case 2265: + cacheIndex = 218; + reference = new EpsgCoordinateReferenceRecord(2265, (EpsgCoordinateSystemKind)2, 218); + return true; + case 2266: + cacheIndex = 219; + reference = new EpsgCoordinateReferenceRecord(2266, (EpsgCoordinateSystemKind)2, 219); + return true; + case 2267: + cacheIndex = 220; + reference = new EpsgCoordinateReferenceRecord(2267, (EpsgCoordinateSystemKind)2, 220); + return true; + case 2268: + cacheIndex = 221; + reference = new EpsgCoordinateReferenceRecord(2268, (EpsgCoordinateSystemKind)2, 221); + return true; + case 2269: + cacheIndex = 222; + reference = new EpsgCoordinateReferenceRecord(2269, (EpsgCoordinateSystemKind)2, 222); + return true; + case 2270: + cacheIndex = 223; + reference = new EpsgCoordinateReferenceRecord(2270, (EpsgCoordinateSystemKind)2, 223); + return true; + case 2271: + cacheIndex = 224; + reference = new EpsgCoordinateReferenceRecord(2271, (EpsgCoordinateSystemKind)2, 224); + return true; + case 2272: + cacheIndex = 225; + reference = new EpsgCoordinateReferenceRecord(2272, (EpsgCoordinateSystemKind)2, 225); + return true; + case 2273: + cacheIndex = 226; + reference = new EpsgCoordinateReferenceRecord(2273, (EpsgCoordinateSystemKind)2, 226); + return true; + case 2274: + cacheIndex = 227; + reference = new EpsgCoordinateReferenceRecord(2274, (EpsgCoordinateSystemKind)2, 227); + return true; + case 2275: + cacheIndex = 228; + reference = new EpsgCoordinateReferenceRecord(2275, (EpsgCoordinateSystemKind)2, 228); + return true; + case 2276: + cacheIndex = 229; + reference = new EpsgCoordinateReferenceRecord(2276, (EpsgCoordinateSystemKind)2, 229); + return true; + case 2277: + cacheIndex = 230; + reference = new EpsgCoordinateReferenceRecord(2277, (EpsgCoordinateSystemKind)2, 230); + return true; + case 2278: + cacheIndex = 231; + reference = new EpsgCoordinateReferenceRecord(2278, (EpsgCoordinateSystemKind)2, 231); + return true; + case 2279: + cacheIndex = 232; + reference = new EpsgCoordinateReferenceRecord(2279, (EpsgCoordinateSystemKind)2, 232); + return true; + case 2280: + cacheIndex = 233; + reference = new EpsgCoordinateReferenceRecord(2280, (EpsgCoordinateSystemKind)2, 233); + return true; + case 2281: + cacheIndex = 234; + reference = new EpsgCoordinateReferenceRecord(2281, (EpsgCoordinateSystemKind)2, 234); + return true; + case 2282: + cacheIndex = 235; + reference = new EpsgCoordinateReferenceRecord(2282, (EpsgCoordinateSystemKind)2, 235); + return true; + case 2283: + cacheIndex = 236; + reference = new EpsgCoordinateReferenceRecord(2283, (EpsgCoordinateSystemKind)2, 236); + return true; + case 2284: + cacheIndex = 237; + reference = new EpsgCoordinateReferenceRecord(2284, (EpsgCoordinateSystemKind)2, 237); + return true; + case 2285: + cacheIndex = 238; + reference = new EpsgCoordinateReferenceRecord(2285, (EpsgCoordinateSystemKind)2, 238); + return true; + case 2286: + cacheIndex = 239; + reference = new EpsgCoordinateReferenceRecord(2286, (EpsgCoordinateSystemKind)2, 239); + return true; + case 2287: + cacheIndex = 240; + reference = new EpsgCoordinateReferenceRecord(2287, (EpsgCoordinateSystemKind)2, 240); + return true; + case 2288: + cacheIndex = 241; + reference = new EpsgCoordinateReferenceRecord(2288, (EpsgCoordinateSystemKind)2, 241); + return true; + case 2289: + cacheIndex = 242; + reference = new EpsgCoordinateReferenceRecord(2289, (EpsgCoordinateSystemKind)2, 242); + return true; + case 2290: + cacheIndex = 243; + reference = new EpsgCoordinateReferenceRecord(2290, (EpsgCoordinateSystemKind)2, 243); + return true; + case 2294: + cacheIndex = 244; + reference = new EpsgCoordinateReferenceRecord(2294, (EpsgCoordinateSystemKind)2, 244); + return true; + case 2295: + cacheIndex = 245; + reference = new EpsgCoordinateReferenceRecord(2295, (EpsgCoordinateSystemKind)2, 245); + return true; + case 2296: + cacheIndex = 246; + reference = new EpsgCoordinateReferenceRecord(2296, (EpsgCoordinateSystemKind)2, 246); + return true; + case 2299: + cacheIndex = 247; + reference = new EpsgCoordinateReferenceRecord(2299, (EpsgCoordinateSystemKind)2, 247); + return true; + case 2301: + cacheIndex = 248; + reference = new EpsgCoordinateReferenceRecord(2301, (EpsgCoordinateSystemKind)2, 248); + return true; + case 2303: + cacheIndex = 249; + reference = new EpsgCoordinateReferenceRecord(2303, (EpsgCoordinateSystemKind)2, 249); + return true; + case 2304: + cacheIndex = 250; + reference = new EpsgCoordinateReferenceRecord(2304, (EpsgCoordinateSystemKind)2, 250); + return true; + case 2305: + cacheIndex = 251; + reference = new EpsgCoordinateReferenceRecord(2305, (EpsgCoordinateSystemKind)2, 251); + return true; + case 2306: + cacheIndex = 252; + reference = new EpsgCoordinateReferenceRecord(2306, (EpsgCoordinateSystemKind)2, 252); + return true; + case 2307: + cacheIndex = 253; + reference = new EpsgCoordinateReferenceRecord(2307, (EpsgCoordinateSystemKind)2, 253); + return true; + case 2308: + cacheIndex = 254; + reference = new EpsgCoordinateReferenceRecord(2308, (EpsgCoordinateSystemKind)2, 254); + return true; + case 2309: + cacheIndex = 255; + reference = new EpsgCoordinateReferenceRecord(2309, (EpsgCoordinateSystemKind)2, 255); + return true; + case 2310: + cacheIndex = 256; + reference = new EpsgCoordinateReferenceRecord(2310, (EpsgCoordinateSystemKind)2, 256); + return true; + case 2311: + cacheIndex = 257; + reference = new EpsgCoordinateReferenceRecord(2311, (EpsgCoordinateSystemKind)2, 257); + return true; + case 2312: + cacheIndex = 258; + reference = new EpsgCoordinateReferenceRecord(2312, (EpsgCoordinateSystemKind)2, 258); + return true; + case 2313: + cacheIndex = 259; + reference = new EpsgCoordinateReferenceRecord(2313, (EpsgCoordinateSystemKind)2, 259); + return true; + case 2314: + cacheIndex = 260; + reference = new EpsgCoordinateReferenceRecord(2314, (EpsgCoordinateSystemKind)2, 260); + return true; + case 2315: + cacheIndex = 261; + reference = new EpsgCoordinateReferenceRecord(2315, (EpsgCoordinateSystemKind)2, 261); + return true; + case 2316: + cacheIndex = 262; + reference = new EpsgCoordinateReferenceRecord(2316, (EpsgCoordinateSystemKind)2, 262); + return true; + case 2317: + cacheIndex = 263; + reference = new EpsgCoordinateReferenceRecord(2317, (EpsgCoordinateSystemKind)2, 263); + return true; + case 2318: + cacheIndex = 264; + reference = new EpsgCoordinateReferenceRecord(2318, (EpsgCoordinateSystemKind)2, 264); + return true; + case 2319: + cacheIndex = 265; + reference = new EpsgCoordinateReferenceRecord(2319, (EpsgCoordinateSystemKind)2, 265); + return true; + case 2320: + cacheIndex = 266; + reference = new EpsgCoordinateReferenceRecord(2320, (EpsgCoordinateSystemKind)2, 266); + return true; + case 2321: + cacheIndex = 267; + reference = new EpsgCoordinateReferenceRecord(2321, (EpsgCoordinateSystemKind)2, 267); + return true; + case 2322: + cacheIndex = 268; + reference = new EpsgCoordinateReferenceRecord(2322, (EpsgCoordinateSystemKind)2, 268); + return true; + case 2323: + cacheIndex = 269; + reference = new EpsgCoordinateReferenceRecord(2323, (EpsgCoordinateSystemKind)2, 269); + return true; + case 2324: + cacheIndex = 270; + reference = new EpsgCoordinateReferenceRecord(2324, (EpsgCoordinateSystemKind)2, 270); + return true; + case 2325: + cacheIndex = 271; + reference = new EpsgCoordinateReferenceRecord(2325, (EpsgCoordinateSystemKind)2, 271); + return true; + case 2326: + cacheIndex = 272; + reference = new EpsgCoordinateReferenceRecord(2326, (EpsgCoordinateSystemKind)2, 272); + return true; + case 2327: + cacheIndex = 273; + reference = new EpsgCoordinateReferenceRecord(2327, (EpsgCoordinateSystemKind)2, 273); + return true; + case 2328: + cacheIndex = 274; + reference = new EpsgCoordinateReferenceRecord(2328, (EpsgCoordinateSystemKind)2, 274); + return true; + case 2329: + cacheIndex = 275; + reference = new EpsgCoordinateReferenceRecord(2329, (EpsgCoordinateSystemKind)2, 275); + return true; + case 2330: + cacheIndex = 276; + reference = new EpsgCoordinateReferenceRecord(2330, (EpsgCoordinateSystemKind)2, 276); + return true; + case 2331: + cacheIndex = 277; + reference = new EpsgCoordinateReferenceRecord(2331, (EpsgCoordinateSystemKind)2, 277); + return true; + case 2332: + cacheIndex = 278; + reference = new EpsgCoordinateReferenceRecord(2332, (EpsgCoordinateSystemKind)2, 278); + return true; + case 2333: + cacheIndex = 279; + reference = new EpsgCoordinateReferenceRecord(2333, (EpsgCoordinateSystemKind)2, 279); + return true; + case 2334: + cacheIndex = 280; + reference = new EpsgCoordinateReferenceRecord(2334, (EpsgCoordinateSystemKind)2, 280); + return true; + case 2335: + cacheIndex = 281; + reference = new EpsgCoordinateReferenceRecord(2335, (EpsgCoordinateSystemKind)2, 281); + return true; + case 2336: + cacheIndex = 282; + reference = new EpsgCoordinateReferenceRecord(2336, (EpsgCoordinateSystemKind)2, 282); + return true; + case 2337: + cacheIndex = 283; + reference = new EpsgCoordinateReferenceRecord(2337, (EpsgCoordinateSystemKind)2, 283); + return true; + case 2338: + cacheIndex = 284; + reference = new EpsgCoordinateReferenceRecord(2338, (EpsgCoordinateSystemKind)2, 284); + return true; + case 2339: + cacheIndex = 285; + reference = new EpsgCoordinateReferenceRecord(2339, (EpsgCoordinateSystemKind)2, 285); + return true; + case 2340: + cacheIndex = 286; + reference = new EpsgCoordinateReferenceRecord(2340, (EpsgCoordinateSystemKind)2, 286); + return true; + case 2341: + cacheIndex = 287; + reference = new EpsgCoordinateReferenceRecord(2341, (EpsgCoordinateSystemKind)2, 287); + return true; + case 2342: + cacheIndex = 288; + reference = new EpsgCoordinateReferenceRecord(2342, (EpsgCoordinateSystemKind)2, 288); + return true; + case 2343: + cacheIndex = 289; + reference = new EpsgCoordinateReferenceRecord(2343, (EpsgCoordinateSystemKind)2, 289); + return true; + case 2344: + cacheIndex = 290; + reference = new EpsgCoordinateReferenceRecord(2344, (EpsgCoordinateSystemKind)2, 290); + return true; + case 2345: + cacheIndex = 291; + reference = new EpsgCoordinateReferenceRecord(2345, (EpsgCoordinateSystemKind)2, 291); + return true; + case 2346: + cacheIndex = 292; + reference = new EpsgCoordinateReferenceRecord(2346, (EpsgCoordinateSystemKind)2, 292); + return true; + case 2347: + cacheIndex = 293; + reference = new EpsgCoordinateReferenceRecord(2347, (EpsgCoordinateSystemKind)2, 293); + return true; + case 2348: + cacheIndex = 294; + reference = new EpsgCoordinateReferenceRecord(2348, (EpsgCoordinateSystemKind)2, 294); + return true; + case 2349: + cacheIndex = 295; + reference = new EpsgCoordinateReferenceRecord(2349, (EpsgCoordinateSystemKind)2, 295); + return true; + case 2350: + cacheIndex = 296; + reference = new EpsgCoordinateReferenceRecord(2350, (EpsgCoordinateSystemKind)2, 296); + return true; + case 2351: + cacheIndex = 297; + reference = new EpsgCoordinateReferenceRecord(2351, (EpsgCoordinateSystemKind)2, 297); + return true; + case 2352: + cacheIndex = 298; + reference = new EpsgCoordinateReferenceRecord(2352, (EpsgCoordinateSystemKind)2, 298); + return true; + case 2353: + cacheIndex = 299; + reference = new EpsgCoordinateReferenceRecord(2353, (EpsgCoordinateSystemKind)2, 299); + return true; + case 2354: + cacheIndex = 300; + reference = new EpsgCoordinateReferenceRecord(2354, (EpsgCoordinateSystemKind)2, 300); + return true; + case 2355: + cacheIndex = 301; + reference = new EpsgCoordinateReferenceRecord(2355, (EpsgCoordinateSystemKind)2, 301); + return true; + case 2356: + cacheIndex = 302; + reference = new EpsgCoordinateReferenceRecord(2356, (EpsgCoordinateSystemKind)2, 302); + return true; + case 2357: + cacheIndex = 303; + reference = new EpsgCoordinateReferenceRecord(2357, (EpsgCoordinateSystemKind)2, 303); + return true; + case 2358: + cacheIndex = 304; + reference = new EpsgCoordinateReferenceRecord(2358, (EpsgCoordinateSystemKind)2, 304); + return true; + case 2359: + cacheIndex = 305; + reference = new EpsgCoordinateReferenceRecord(2359, (EpsgCoordinateSystemKind)2, 305); + return true; + case 2360: + cacheIndex = 306; + reference = new EpsgCoordinateReferenceRecord(2360, (EpsgCoordinateSystemKind)2, 306); + return true; + case 2361: + cacheIndex = 307; + reference = new EpsgCoordinateReferenceRecord(2361, (EpsgCoordinateSystemKind)2, 307); + return true; + case 2362: + cacheIndex = 308; + reference = new EpsgCoordinateReferenceRecord(2362, (EpsgCoordinateSystemKind)2, 308); + return true; + case 2363: + cacheIndex = 309; + reference = new EpsgCoordinateReferenceRecord(2363, (EpsgCoordinateSystemKind)2, 309); + return true; + case 2364: + cacheIndex = 310; + reference = new EpsgCoordinateReferenceRecord(2364, (EpsgCoordinateSystemKind)2, 310); + return true; + case 2365: + cacheIndex = 311; + reference = new EpsgCoordinateReferenceRecord(2365, (EpsgCoordinateSystemKind)2, 311); + return true; + case 2366: + cacheIndex = 312; + reference = new EpsgCoordinateReferenceRecord(2366, (EpsgCoordinateSystemKind)2, 312); + return true; + case 2367: + cacheIndex = 313; + reference = new EpsgCoordinateReferenceRecord(2367, (EpsgCoordinateSystemKind)2, 313); + return true; + case 2368: + cacheIndex = 314; + reference = new EpsgCoordinateReferenceRecord(2368, (EpsgCoordinateSystemKind)2, 314); + return true; + case 2369: + cacheIndex = 315; + reference = new EpsgCoordinateReferenceRecord(2369, (EpsgCoordinateSystemKind)2, 315); + return true; + case 2370: + cacheIndex = 316; + reference = new EpsgCoordinateReferenceRecord(2370, (EpsgCoordinateSystemKind)2, 316); + return true; + case 2371: + cacheIndex = 317; + reference = new EpsgCoordinateReferenceRecord(2371, (EpsgCoordinateSystemKind)2, 317); + return true; + case 2372: + cacheIndex = 318; + reference = new EpsgCoordinateReferenceRecord(2372, (EpsgCoordinateSystemKind)2, 318); + return true; + case 2373: + cacheIndex = 319; + reference = new EpsgCoordinateReferenceRecord(2373, (EpsgCoordinateSystemKind)2, 319); + return true; + case 2374: + cacheIndex = 320; + reference = new EpsgCoordinateReferenceRecord(2374, (EpsgCoordinateSystemKind)2, 320); + return true; + case 2375: + cacheIndex = 321; + reference = new EpsgCoordinateReferenceRecord(2375, (EpsgCoordinateSystemKind)2, 321); + return true; + case 2376: + cacheIndex = 322; + reference = new EpsgCoordinateReferenceRecord(2376, (EpsgCoordinateSystemKind)2, 322); + return true; + case 2377: + cacheIndex = 323; + reference = new EpsgCoordinateReferenceRecord(2377, (EpsgCoordinateSystemKind)2, 323); + return true; + case 2378: + cacheIndex = 324; + reference = new EpsgCoordinateReferenceRecord(2378, (EpsgCoordinateSystemKind)2, 324); + return true; + case 2379: + cacheIndex = 325; + reference = new EpsgCoordinateReferenceRecord(2379, (EpsgCoordinateSystemKind)2, 325); + return true; + case 2380: + cacheIndex = 326; + reference = new EpsgCoordinateReferenceRecord(2380, (EpsgCoordinateSystemKind)2, 326); + return true; + case 2381: + cacheIndex = 327; + reference = new EpsgCoordinateReferenceRecord(2381, (EpsgCoordinateSystemKind)2, 327); + return true; + case 2382: + cacheIndex = 328; + reference = new EpsgCoordinateReferenceRecord(2382, (EpsgCoordinateSystemKind)2, 328); + return true; + case 2383: + cacheIndex = 329; + reference = new EpsgCoordinateReferenceRecord(2383, (EpsgCoordinateSystemKind)2, 329); + return true; + case 2384: + cacheIndex = 330; + reference = new EpsgCoordinateReferenceRecord(2384, (EpsgCoordinateSystemKind)2, 330); + return true; + case 2385: + cacheIndex = 331; + reference = new EpsgCoordinateReferenceRecord(2385, (EpsgCoordinateSystemKind)2, 331); + return true; + case 2386: + cacheIndex = 332; + reference = new EpsgCoordinateReferenceRecord(2386, (EpsgCoordinateSystemKind)2, 332); + return true; + case 2387: + cacheIndex = 333; + reference = new EpsgCoordinateReferenceRecord(2387, (EpsgCoordinateSystemKind)2, 333); + return true; + case 2388: + cacheIndex = 334; + reference = new EpsgCoordinateReferenceRecord(2388, (EpsgCoordinateSystemKind)2, 334); + return true; + case 2389: + cacheIndex = 335; + reference = new EpsgCoordinateReferenceRecord(2389, (EpsgCoordinateSystemKind)2, 335); + return true; + case 2390: + cacheIndex = 336; + reference = new EpsgCoordinateReferenceRecord(2390, (EpsgCoordinateSystemKind)2, 336); + return true; + case 2391: + cacheIndex = 337; + reference = new EpsgCoordinateReferenceRecord(2391, (EpsgCoordinateSystemKind)2, 337); + return true; + case 2392: + cacheIndex = 338; + reference = new EpsgCoordinateReferenceRecord(2392, (EpsgCoordinateSystemKind)2, 338); + return true; + case 2393: + cacheIndex = 339; + reference = new EpsgCoordinateReferenceRecord(2393, (EpsgCoordinateSystemKind)2, 339); + return true; + case 2394: + cacheIndex = 340; + reference = new EpsgCoordinateReferenceRecord(2394, (EpsgCoordinateSystemKind)2, 340); + return true; + case 2395: + cacheIndex = 341; + reference = new EpsgCoordinateReferenceRecord(2395, (EpsgCoordinateSystemKind)2, 341); + return true; + case 2396: + cacheIndex = 342; + reference = new EpsgCoordinateReferenceRecord(2396, (EpsgCoordinateSystemKind)2, 342); + return true; + case 2397: + cacheIndex = 343; + reference = new EpsgCoordinateReferenceRecord(2397, (EpsgCoordinateSystemKind)2, 343); + return true; + case 2398: + cacheIndex = 344; + reference = new EpsgCoordinateReferenceRecord(2398, (EpsgCoordinateSystemKind)2, 344); + return true; + case 2399: + cacheIndex = 345; + reference = new EpsgCoordinateReferenceRecord(2399, (EpsgCoordinateSystemKind)2, 345); + return true; + case 2401: + cacheIndex = 346; + reference = new EpsgCoordinateReferenceRecord(2401, (EpsgCoordinateSystemKind)2, 346); + return true; + case 2402: + cacheIndex = 347; + reference = new EpsgCoordinateReferenceRecord(2402, (EpsgCoordinateSystemKind)2, 347); + return true; + case 2403: + cacheIndex = 348; + reference = new EpsgCoordinateReferenceRecord(2403, (EpsgCoordinateSystemKind)2, 348); + return true; + case 2404: + cacheIndex = 349; + reference = new EpsgCoordinateReferenceRecord(2404, (EpsgCoordinateSystemKind)2, 349); + return true; + case 2405: + cacheIndex = 350; + reference = new EpsgCoordinateReferenceRecord(2405, (EpsgCoordinateSystemKind)2, 350); + return true; + case 2406: + cacheIndex = 351; + reference = new EpsgCoordinateReferenceRecord(2406, (EpsgCoordinateSystemKind)2, 351); + return true; + case 2407: + cacheIndex = 352; + reference = new EpsgCoordinateReferenceRecord(2407, (EpsgCoordinateSystemKind)2, 352); + return true; + case 2408: + cacheIndex = 353; + reference = new EpsgCoordinateReferenceRecord(2408, (EpsgCoordinateSystemKind)2, 353); + return true; + case 2409: + cacheIndex = 354; + reference = new EpsgCoordinateReferenceRecord(2409, (EpsgCoordinateSystemKind)2, 354); + return true; + case 2410: + cacheIndex = 355; + reference = new EpsgCoordinateReferenceRecord(2410, (EpsgCoordinateSystemKind)2, 355); + return true; + case 2411: + cacheIndex = 356; + reference = new EpsgCoordinateReferenceRecord(2411, (EpsgCoordinateSystemKind)2, 356); + return true; + case 2412: + cacheIndex = 357; + reference = new EpsgCoordinateReferenceRecord(2412, (EpsgCoordinateSystemKind)2, 357); + return true; + case 2413: + cacheIndex = 358; + reference = new EpsgCoordinateReferenceRecord(2413, (EpsgCoordinateSystemKind)2, 358); + return true; + case 2414: + cacheIndex = 359; + reference = new EpsgCoordinateReferenceRecord(2414, (EpsgCoordinateSystemKind)2, 359); + return true; + case 2415: + cacheIndex = 360; + reference = new EpsgCoordinateReferenceRecord(2415, (EpsgCoordinateSystemKind)2, 360); + return true; + case 2416: + cacheIndex = 361; + reference = new EpsgCoordinateReferenceRecord(2416, (EpsgCoordinateSystemKind)2, 361); + return true; + case 2417: + cacheIndex = 362; + reference = new EpsgCoordinateReferenceRecord(2417, (EpsgCoordinateSystemKind)2, 362); + return true; + case 2418: + cacheIndex = 363; + reference = new EpsgCoordinateReferenceRecord(2418, (EpsgCoordinateSystemKind)2, 363); + return true; + case 2419: + cacheIndex = 364; + reference = new EpsgCoordinateReferenceRecord(2419, (EpsgCoordinateSystemKind)2, 364); + return true; + case 2420: + cacheIndex = 365; + reference = new EpsgCoordinateReferenceRecord(2420, (EpsgCoordinateSystemKind)2, 365); + return true; + case 2421: + cacheIndex = 366; + reference = new EpsgCoordinateReferenceRecord(2421, (EpsgCoordinateSystemKind)2, 366); + return true; + case 2422: + cacheIndex = 367; + reference = new EpsgCoordinateReferenceRecord(2422, (EpsgCoordinateSystemKind)2, 367); + return true; + case 2423: + cacheIndex = 368; + reference = new EpsgCoordinateReferenceRecord(2423, (EpsgCoordinateSystemKind)2, 368); + return true; + case 2424: + cacheIndex = 369; + reference = new EpsgCoordinateReferenceRecord(2424, (EpsgCoordinateSystemKind)2, 369); + return true; + case 2425: + cacheIndex = 370; + reference = new EpsgCoordinateReferenceRecord(2425, (EpsgCoordinateSystemKind)2, 370); + return true; + case 2426: + cacheIndex = 371; + reference = new EpsgCoordinateReferenceRecord(2426, (EpsgCoordinateSystemKind)2, 371); + return true; + case 2427: + cacheIndex = 372; + reference = new EpsgCoordinateReferenceRecord(2427, (EpsgCoordinateSystemKind)2, 372); + return true; + case 2428: + cacheIndex = 373; + reference = new EpsgCoordinateReferenceRecord(2428, (EpsgCoordinateSystemKind)2, 373); + return true; + case 2429: + cacheIndex = 374; + reference = new EpsgCoordinateReferenceRecord(2429, (EpsgCoordinateSystemKind)2, 374); + return true; + case 2430: + cacheIndex = 375; + reference = new EpsgCoordinateReferenceRecord(2430, (EpsgCoordinateSystemKind)2, 375); + return true; + case 2431: + cacheIndex = 376; + reference = new EpsgCoordinateReferenceRecord(2431, (EpsgCoordinateSystemKind)2, 376); + return true; + case 2432: + cacheIndex = 377; + reference = new EpsgCoordinateReferenceRecord(2432, (EpsgCoordinateSystemKind)2, 377); + return true; + case 2433: + cacheIndex = 378; + reference = new EpsgCoordinateReferenceRecord(2433, (EpsgCoordinateSystemKind)2, 378); + return true; + case 2434: + cacheIndex = 379; + reference = new EpsgCoordinateReferenceRecord(2434, (EpsgCoordinateSystemKind)2, 379); + return true; + case 2435: + cacheIndex = 380; + reference = new EpsgCoordinateReferenceRecord(2435, (EpsgCoordinateSystemKind)2, 380); + return true; + case 2436: + cacheIndex = 381; + reference = new EpsgCoordinateReferenceRecord(2436, (EpsgCoordinateSystemKind)2, 381); + return true; + case 2437: + cacheIndex = 382; + reference = new EpsgCoordinateReferenceRecord(2437, (EpsgCoordinateSystemKind)2, 382); + return true; + case 2438: + cacheIndex = 383; + reference = new EpsgCoordinateReferenceRecord(2438, (EpsgCoordinateSystemKind)2, 383); + return true; + case 2439: + cacheIndex = 384; + reference = new EpsgCoordinateReferenceRecord(2439, (EpsgCoordinateSystemKind)2, 384); + return true; + case 2440: + cacheIndex = 385; + reference = new EpsgCoordinateReferenceRecord(2440, (EpsgCoordinateSystemKind)2, 385); + return true; + case 2441: + cacheIndex = 386; + reference = new EpsgCoordinateReferenceRecord(2441, (EpsgCoordinateSystemKind)2, 386); + return true; + case 2442: + cacheIndex = 387; + reference = new EpsgCoordinateReferenceRecord(2442, (EpsgCoordinateSystemKind)2, 387); + return true; + case 2443: + cacheIndex = 388; + reference = new EpsgCoordinateReferenceRecord(2443, (EpsgCoordinateSystemKind)2, 388); + return true; + case 2444: + cacheIndex = 389; + reference = new EpsgCoordinateReferenceRecord(2444, (EpsgCoordinateSystemKind)2, 389); + return true; + case 2445: + cacheIndex = 390; + reference = new EpsgCoordinateReferenceRecord(2445, (EpsgCoordinateSystemKind)2, 390); + return true; + case 2446: + cacheIndex = 391; + reference = new EpsgCoordinateReferenceRecord(2446, (EpsgCoordinateSystemKind)2, 391); + return true; + case 2447: + cacheIndex = 392; + reference = new EpsgCoordinateReferenceRecord(2447, (EpsgCoordinateSystemKind)2, 392); + return true; + case 2448: + cacheIndex = 393; + reference = new EpsgCoordinateReferenceRecord(2448, (EpsgCoordinateSystemKind)2, 393); + return true; + case 2449: + cacheIndex = 394; + reference = new EpsgCoordinateReferenceRecord(2449, (EpsgCoordinateSystemKind)2, 394); + return true; + case 2450: + cacheIndex = 395; + reference = new EpsgCoordinateReferenceRecord(2450, (EpsgCoordinateSystemKind)2, 395); + return true; + case 2451: + cacheIndex = 396; + reference = new EpsgCoordinateReferenceRecord(2451, (EpsgCoordinateSystemKind)2, 396); + return true; + case 2452: + cacheIndex = 397; + reference = new EpsgCoordinateReferenceRecord(2452, (EpsgCoordinateSystemKind)2, 397); + return true; + case 2453: + cacheIndex = 398; + reference = new EpsgCoordinateReferenceRecord(2453, (EpsgCoordinateSystemKind)2, 398); + return true; + case 2454: + cacheIndex = 399; + reference = new EpsgCoordinateReferenceRecord(2454, (EpsgCoordinateSystemKind)2, 399); + return true; + case 2455: + cacheIndex = 400; + reference = new EpsgCoordinateReferenceRecord(2455, (EpsgCoordinateSystemKind)2, 400); + return true; + case 2456: + cacheIndex = 401; + reference = new EpsgCoordinateReferenceRecord(2456, (EpsgCoordinateSystemKind)2, 401); + return true; + case 2457: + cacheIndex = 402; + reference = new EpsgCoordinateReferenceRecord(2457, (EpsgCoordinateSystemKind)2, 402); + return true; + case 2458: + cacheIndex = 403; + reference = new EpsgCoordinateReferenceRecord(2458, (EpsgCoordinateSystemKind)2, 403); + return true; + case 2459: + cacheIndex = 404; + reference = new EpsgCoordinateReferenceRecord(2459, (EpsgCoordinateSystemKind)2, 404); + return true; + case 2460: + cacheIndex = 405; + reference = new EpsgCoordinateReferenceRecord(2460, (EpsgCoordinateSystemKind)2, 405); + return true; + case 2461: + cacheIndex = 406; + reference = new EpsgCoordinateReferenceRecord(2461, (EpsgCoordinateSystemKind)2, 406); + return true; + case 2462: + cacheIndex = 407; + reference = new EpsgCoordinateReferenceRecord(2462, (EpsgCoordinateSystemKind)2, 407); + return true; + case 2463: + cacheIndex = 408; + reference = new EpsgCoordinateReferenceRecord(2463, (EpsgCoordinateSystemKind)2, 408); + return true; + case 2464: + cacheIndex = 409; + reference = new EpsgCoordinateReferenceRecord(2464, (EpsgCoordinateSystemKind)2, 409); + return true; + case 2465: + cacheIndex = 410; + reference = new EpsgCoordinateReferenceRecord(2465, (EpsgCoordinateSystemKind)2, 410); + return true; + case 2466: + cacheIndex = 411; + reference = new EpsgCoordinateReferenceRecord(2466, (EpsgCoordinateSystemKind)2, 411); + return true; + case 2467: + cacheIndex = 412; + reference = new EpsgCoordinateReferenceRecord(2467, (EpsgCoordinateSystemKind)2, 412); + return true; + case 2468: + cacheIndex = 413; + reference = new EpsgCoordinateReferenceRecord(2468, (EpsgCoordinateSystemKind)2, 413); + return true; + case 2469: + cacheIndex = 414; + reference = new EpsgCoordinateReferenceRecord(2469, (EpsgCoordinateSystemKind)2, 414); + return true; + case 2470: + cacheIndex = 415; + reference = new EpsgCoordinateReferenceRecord(2470, (EpsgCoordinateSystemKind)2, 415); + return true; + case 2471: + cacheIndex = 416; + reference = new EpsgCoordinateReferenceRecord(2471, (EpsgCoordinateSystemKind)2, 416); + return true; + case 2472: + cacheIndex = 417; + reference = new EpsgCoordinateReferenceRecord(2472, (EpsgCoordinateSystemKind)2, 417); + return true; + case 2473: + cacheIndex = 418; + reference = new EpsgCoordinateReferenceRecord(2473, (EpsgCoordinateSystemKind)2, 418); + return true; + case 2474: + cacheIndex = 419; + reference = new EpsgCoordinateReferenceRecord(2474, (EpsgCoordinateSystemKind)2, 419); + return true; + case 2475: + cacheIndex = 420; + reference = new EpsgCoordinateReferenceRecord(2475, (EpsgCoordinateSystemKind)2, 420); + return true; + case 2476: + cacheIndex = 421; + reference = new EpsgCoordinateReferenceRecord(2476, (EpsgCoordinateSystemKind)2, 421); + return true; + case 2477: + cacheIndex = 422; + reference = new EpsgCoordinateReferenceRecord(2477, (EpsgCoordinateSystemKind)2, 422); + return true; + case 2478: + cacheIndex = 423; + reference = new EpsgCoordinateReferenceRecord(2478, (EpsgCoordinateSystemKind)2, 423); + return true; + case 2479: + cacheIndex = 424; + reference = new EpsgCoordinateReferenceRecord(2479, (EpsgCoordinateSystemKind)2, 424); + return true; + case 2480: + cacheIndex = 425; + reference = new EpsgCoordinateReferenceRecord(2480, (EpsgCoordinateSystemKind)2, 425); + return true; + case 2481: + cacheIndex = 426; + reference = new EpsgCoordinateReferenceRecord(2481, (EpsgCoordinateSystemKind)2, 426); + return true; + case 2482: + cacheIndex = 427; + reference = new EpsgCoordinateReferenceRecord(2482, (EpsgCoordinateSystemKind)2, 427); + return true; + case 2483: + cacheIndex = 428; + reference = new EpsgCoordinateReferenceRecord(2483, (EpsgCoordinateSystemKind)2, 428); + return true; + case 2484: + cacheIndex = 429; + reference = new EpsgCoordinateReferenceRecord(2484, (EpsgCoordinateSystemKind)2, 429); + return true; + case 2485: + cacheIndex = 430; + reference = new EpsgCoordinateReferenceRecord(2485, (EpsgCoordinateSystemKind)2, 430); + return true; + case 2486: + cacheIndex = 431; + reference = new EpsgCoordinateReferenceRecord(2486, (EpsgCoordinateSystemKind)2, 431); + return true; + case 2487: + cacheIndex = 432; + reference = new EpsgCoordinateReferenceRecord(2487, (EpsgCoordinateSystemKind)2, 432); + return true; + case 2488: + cacheIndex = 433; + reference = new EpsgCoordinateReferenceRecord(2488, (EpsgCoordinateSystemKind)2, 433); + return true; + case 2489: + cacheIndex = 434; + reference = new EpsgCoordinateReferenceRecord(2489, (EpsgCoordinateSystemKind)2, 434); + return true; + case 2490: + cacheIndex = 435; + reference = new EpsgCoordinateReferenceRecord(2490, (EpsgCoordinateSystemKind)2, 435); + return true; + case 2491: + cacheIndex = 436; + reference = new EpsgCoordinateReferenceRecord(2491, (EpsgCoordinateSystemKind)2, 436); + return true; + case 2494: + cacheIndex = 437; + reference = new EpsgCoordinateReferenceRecord(2494, (EpsgCoordinateSystemKind)2, 437); + return true; + case 2495: + cacheIndex = 438; + reference = new EpsgCoordinateReferenceRecord(2495, (EpsgCoordinateSystemKind)2, 438); + return true; + case 2496: + cacheIndex = 439; + reference = new EpsgCoordinateReferenceRecord(2496, (EpsgCoordinateSystemKind)2, 439); + return true; + case 2497: + cacheIndex = 440; + reference = new EpsgCoordinateReferenceRecord(2497, (EpsgCoordinateSystemKind)2, 440); + return true; + case 2498: + cacheIndex = 441; + reference = new EpsgCoordinateReferenceRecord(2498, (EpsgCoordinateSystemKind)2, 441); + return true; + case 2499: + cacheIndex = 442; + reference = new EpsgCoordinateReferenceRecord(2499, (EpsgCoordinateSystemKind)2, 442); + return true; + case 2500: + cacheIndex = 443; + reference = new EpsgCoordinateReferenceRecord(2500, (EpsgCoordinateSystemKind)2, 443); + return true; + case 2501: + cacheIndex = 444; + reference = new EpsgCoordinateReferenceRecord(2501, (EpsgCoordinateSystemKind)2, 444); + return true; + case 2502: + cacheIndex = 445; + reference = new EpsgCoordinateReferenceRecord(2502, (EpsgCoordinateSystemKind)2, 445); + return true; + case 2503: + cacheIndex = 446; + reference = new EpsgCoordinateReferenceRecord(2503, (EpsgCoordinateSystemKind)2, 446); + return true; + case 2504: + cacheIndex = 447; + reference = new EpsgCoordinateReferenceRecord(2504, (EpsgCoordinateSystemKind)2, 447); + return true; + case 2505: + cacheIndex = 448; + reference = new EpsgCoordinateReferenceRecord(2505, (EpsgCoordinateSystemKind)2, 448); + return true; + case 2506: + cacheIndex = 449; + reference = new EpsgCoordinateReferenceRecord(2506, (EpsgCoordinateSystemKind)2, 449); + return true; + case 2507: + cacheIndex = 450; + reference = new EpsgCoordinateReferenceRecord(2507, (EpsgCoordinateSystemKind)2, 450); + return true; + case 2508: + cacheIndex = 451; + reference = new EpsgCoordinateReferenceRecord(2508, (EpsgCoordinateSystemKind)2, 451); + return true; + case 2509: + cacheIndex = 452; + reference = new EpsgCoordinateReferenceRecord(2509, (EpsgCoordinateSystemKind)2, 452); + return true; + case 2510: + cacheIndex = 453; + reference = new EpsgCoordinateReferenceRecord(2510, (EpsgCoordinateSystemKind)2, 453); + return true; + case 2511: + cacheIndex = 454; + reference = new EpsgCoordinateReferenceRecord(2511, (EpsgCoordinateSystemKind)2, 454); + return true; + case 2512: + cacheIndex = 455; + reference = new EpsgCoordinateReferenceRecord(2512, (EpsgCoordinateSystemKind)2, 455); + return true; + case 2513: + cacheIndex = 456; + reference = new EpsgCoordinateReferenceRecord(2513, (EpsgCoordinateSystemKind)2, 456); + return true; + case 2514: + cacheIndex = 457; + reference = new EpsgCoordinateReferenceRecord(2514, (EpsgCoordinateSystemKind)2, 457); + return true; + case 2515: + cacheIndex = 458; + reference = new EpsgCoordinateReferenceRecord(2515, (EpsgCoordinateSystemKind)2, 458); + return true; + case 2516: + cacheIndex = 459; + reference = new EpsgCoordinateReferenceRecord(2516, (EpsgCoordinateSystemKind)2, 459); + return true; + case 2517: + cacheIndex = 460; + reference = new EpsgCoordinateReferenceRecord(2517, (EpsgCoordinateSystemKind)2, 460); + return true; + case 2518: + cacheIndex = 461; + reference = new EpsgCoordinateReferenceRecord(2518, (EpsgCoordinateSystemKind)2, 461); + return true; + case 2519: + cacheIndex = 462; + reference = new EpsgCoordinateReferenceRecord(2519, (EpsgCoordinateSystemKind)2, 462); + return true; + case 2520: + cacheIndex = 463; + reference = new EpsgCoordinateReferenceRecord(2520, (EpsgCoordinateSystemKind)2, 463); + return true; + case 2521: + cacheIndex = 464; + reference = new EpsgCoordinateReferenceRecord(2521, (EpsgCoordinateSystemKind)2, 464); + return true; + case 2522: + cacheIndex = 465; + reference = new EpsgCoordinateReferenceRecord(2522, (EpsgCoordinateSystemKind)2, 465); + return true; + case 2523: + cacheIndex = 466; + reference = new EpsgCoordinateReferenceRecord(2523, (EpsgCoordinateSystemKind)2, 466); + return true; + case 2524: + cacheIndex = 467; + reference = new EpsgCoordinateReferenceRecord(2524, (EpsgCoordinateSystemKind)2, 467); + return true; + case 2525: + cacheIndex = 468; + reference = new EpsgCoordinateReferenceRecord(2525, (EpsgCoordinateSystemKind)2, 468); + return true; + case 2526: + cacheIndex = 469; + reference = new EpsgCoordinateReferenceRecord(2526, (EpsgCoordinateSystemKind)2, 469); + return true; + case 2527: + cacheIndex = 470; + reference = new EpsgCoordinateReferenceRecord(2527, (EpsgCoordinateSystemKind)2, 470); + return true; + case 2528: + cacheIndex = 471; + reference = new EpsgCoordinateReferenceRecord(2528, (EpsgCoordinateSystemKind)2, 471); + return true; + case 2529: + cacheIndex = 472; + reference = new EpsgCoordinateReferenceRecord(2529, (EpsgCoordinateSystemKind)2, 472); + return true; + case 2530: + cacheIndex = 473; + reference = new EpsgCoordinateReferenceRecord(2530, (EpsgCoordinateSystemKind)2, 473); + return true; + case 2531: + cacheIndex = 474; + reference = new EpsgCoordinateReferenceRecord(2531, (EpsgCoordinateSystemKind)2, 474); + return true; + case 2532: + cacheIndex = 475; + reference = new EpsgCoordinateReferenceRecord(2532, (EpsgCoordinateSystemKind)2, 475); + return true; + case 2533: + cacheIndex = 476; + reference = new EpsgCoordinateReferenceRecord(2533, (EpsgCoordinateSystemKind)2, 476); + return true; + case 2534: + cacheIndex = 477; + reference = new EpsgCoordinateReferenceRecord(2534, (EpsgCoordinateSystemKind)2, 477); + return true; + case 2535: + cacheIndex = 478; + reference = new EpsgCoordinateReferenceRecord(2535, (EpsgCoordinateSystemKind)2, 478); + return true; + case 2536: + cacheIndex = 479; + reference = new EpsgCoordinateReferenceRecord(2536, (EpsgCoordinateSystemKind)2, 479); + return true; + case 2537: + cacheIndex = 480; + reference = new EpsgCoordinateReferenceRecord(2537, (EpsgCoordinateSystemKind)2, 480); + return true; + case 2538: + cacheIndex = 481; + reference = new EpsgCoordinateReferenceRecord(2538, (EpsgCoordinateSystemKind)2, 481); + return true; + case 2539: + cacheIndex = 482; + reference = new EpsgCoordinateReferenceRecord(2539, (EpsgCoordinateSystemKind)2, 482); + return true; + case 2540: + cacheIndex = 483; + reference = new EpsgCoordinateReferenceRecord(2540, (EpsgCoordinateSystemKind)2, 483); + return true; + case 2541: + cacheIndex = 484; + reference = new EpsgCoordinateReferenceRecord(2541, (EpsgCoordinateSystemKind)2, 484); + return true; + case 2542: + cacheIndex = 485; + reference = new EpsgCoordinateReferenceRecord(2542, (EpsgCoordinateSystemKind)2, 485); + return true; + case 2543: + cacheIndex = 486; + reference = new EpsgCoordinateReferenceRecord(2543, (EpsgCoordinateSystemKind)2, 486); + return true; + case 2544: + cacheIndex = 487; + reference = new EpsgCoordinateReferenceRecord(2544, (EpsgCoordinateSystemKind)2, 487); + return true; + case 2545: + cacheIndex = 488; + reference = new EpsgCoordinateReferenceRecord(2545, (EpsgCoordinateSystemKind)2, 488); + return true; + case 2546: + cacheIndex = 489; + reference = new EpsgCoordinateReferenceRecord(2546, (EpsgCoordinateSystemKind)2, 489); + return true; + case 2547: + cacheIndex = 490; + reference = new EpsgCoordinateReferenceRecord(2547, (EpsgCoordinateSystemKind)2, 490); + return true; + case 2548: + cacheIndex = 491; + reference = new EpsgCoordinateReferenceRecord(2548, (EpsgCoordinateSystemKind)2, 491); + return true; + case 2549: + cacheIndex = 492; + reference = new EpsgCoordinateReferenceRecord(2549, (EpsgCoordinateSystemKind)2, 492); + return true; + case 2551: + cacheIndex = 493; + reference = new EpsgCoordinateReferenceRecord(2551, (EpsgCoordinateSystemKind)2, 493); + return true; + case 2552: + cacheIndex = 494; + reference = new EpsgCoordinateReferenceRecord(2552, (EpsgCoordinateSystemKind)2, 494); + return true; + case 2553: + cacheIndex = 495; + reference = new EpsgCoordinateReferenceRecord(2553, (EpsgCoordinateSystemKind)2, 495); + return true; + case 2554: + cacheIndex = 496; + reference = new EpsgCoordinateReferenceRecord(2554, (EpsgCoordinateSystemKind)2, 496); + return true; + case 2555: + cacheIndex = 497; + reference = new EpsgCoordinateReferenceRecord(2555, (EpsgCoordinateSystemKind)2, 497); + return true; + case 2556: + cacheIndex = 498; + reference = new EpsgCoordinateReferenceRecord(2556, (EpsgCoordinateSystemKind)2, 498); + return true; + case 2557: + cacheIndex = 499; + reference = new EpsgCoordinateReferenceRecord(2557, (EpsgCoordinateSystemKind)2, 499); + return true; + case 2558: + cacheIndex = 500; + reference = new EpsgCoordinateReferenceRecord(2558, (EpsgCoordinateSystemKind)2, 500); + return true; + case 2559: + cacheIndex = 501; + reference = new EpsgCoordinateReferenceRecord(2559, (EpsgCoordinateSystemKind)2, 501); + return true; + case 2560: + cacheIndex = 502; + reference = new EpsgCoordinateReferenceRecord(2560, (EpsgCoordinateSystemKind)2, 502); + return true; + case 2561: + cacheIndex = 503; + reference = new EpsgCoordinateReferenceRecord(2561, (EpsgCoordinateSystemKind)2, 503); + return true; + case 2562: + cacheIndex = 504; + reference = new EpsgCoordinateReferenceRecord(2562, (EpsgCoordinateSystemKind)2, 504); + return true; + case 2563: + cacheIndex = 505; + reference = new EpsgCoordinateReferenceRecord(2563, (EpsgCoordinateSystemKind)2, 505); + return true; + case 2564: + cacheIndex = 506; + reference = new EpsgCoordinateReferenceRecord(2564, (EpsgCoordinateSystemKind)2, 506); + return true; + case 2565: + cacheIndex = 507; + reference = new EpsgCoordinateReferenceRecord(2565, (EpsgCoordinateSystemKind)2, 507); + return true; + case 2566: + cacheIndex = 508; + reference = new EpsgCoordinateReferenceRecord(2566, (EpsgCoordinateSystemKind)2, 508); + return true; + case 2567: + cacheIndex = 509; + reference = new EpsgCoordinateReferenceRecord(2567, (EpsgCoordinateSystemKind)2, 509); + return true; + case 2568: + cacheIndex = 510; + reference = new EpsgCoordinateReferenceRecord(2568, (EpsgCoordinateSystemKind)2, 510); + return true; + case 2569: + cacheIndex = 511; + reference = new EpsgCoordinateReferenceRecord(2569, (EpsgCoordinateSystemKind)2, 511); + return true; + case 2570: + cacheIndex = 512; + reference = new EpsgCoordinateReferenceRecord(2570, (EpsgCoordinateSystemKind)2, 512); + return true; + case 2571: + cacheIndex = 513; + reference = new EpsgCoordinateReferenceRecord(2571, (EpsgCoordinateSystemKind)2, 513); + return true; + case 2572: + cacheIndex = 514; + reference = new EpsgCoordinateReferenceRecord(2572, (EpsgCoordinateSystemKind)2, 514); + return true; + case 2573: + cacheIndex = 515; + reference = new EpsgCoordinateReferenceRecord(2573, (EpsgCoordinateSystemKind)2, 515); + return true; + case 2574: + cacheIndex = 516; + reference = new EpsgCoordinateReferenceRecord(2574, (EpsgCoordinateSystemKind)2, 516); + return true; + case 2575: + cacheIndex = 517; + reference = new EpsgCoordinateReferenceRecord(2575, (EpsgCoordinateSystemKind)2, 517); + return true; + case 2576: + cacheIndex = 518; + reference = new EpsgCoordinateReferenceRecord(2576, (EpsgCoordinateSystemKind)2, 518); + return true; + case 2578: + cacheIndex = 519; + reference = new EpsgCoordinateReferenceRecord(2578, (EpsgCoordinateSystemKind)2, 519); + return true; + case 2579: + cacheIndex = 520; + reference = new EpsgCoordinateReferenceRecord(2579, (EpsgCoordinateSystemKind)2, 520); + return true; + case 2580: + cacheIndex = 521; + reference = new EpsgCoordinateReferenceRecord(2580, (EpsgCoordinateSystemKind)2, 521); + return true; + case 2581: + cacheIndex = 522; + reference = new EpsgCoordinateReferenceRecord(2581, (EpsgCoordinateSystemKind)2, 522); + return true; + case 2582: + cacheIndex = 523; + reference = new EpsgCoordinateReferenceRecord(2582, (EpsgCoordinateSystemKind)2, 523); + return true; + case 2583: + cacheIndex = 524; + reference = new EpsgCoordinateReferenceRecord(2583, (EpsgCoordinateSystemKind)2, 524); + return true; + case 2584: + cacheIndex = 525; + reference = new EpsgCoordinateReferenceRecord(2584, (EpsgCoordinateSystemKind)2, 525); + return true; + case 2585: + cacheIndex = 526; + reference = new EpsgCoordinateReferenceRecord(2585, (EpsgCoordinateSystemKind)2, 526); + return true; + case 2586: + cacheIndex = 527; + reference = new EpsgCoordinateReferenceRecord(2586, (EpsgCoordinateSystemKind)2, 527); + return true; + case 2587: + cacheIndex = 528; + reference = new EpsgCoordinateReferenceRecord(2587, (EpsgCoordinateSystemKind)2, 528); + return true; + case 2588: + cacheIndex = 529; + reference = new EpsgCoordinateReferenceRecord(2588, (EpsgCoordinateSystemKind)2, 529); + return true; + case 2589: + cacheIndex = 530; + reference = new EpsgCoordinateReferenceRecord(2589, (EpsgCoordinateSystemKind)2, 530); + return true; + case 2590: + cacheIndex = 531; + reference = new EpsgCoordinateReferenceRecord(2590, (EpsgCoordinateSystemKind)2, 531); + return true; + case 2591: + cacheIndex = 532; + reference = new EpsgCoordinateReferenceRecord(2591, (EpsgCoordinateSystemKind)2, 532); + return true; + case 2592: + cacheIndex = 533; + reference = new EpsgCoordinateReferenceRecord(2592, (EpsgCoordinateSystemKind)2, 533); + return true; + case 2593: + cacheIndex = 534; + reference = new EpsgCoordinateReferenceRecord(2593, (EpsgCoordinateSystemKind)2, 534); + return true; + case 2594: + cacheIndex = 535; + reference = new EpsgCoordinateReferenceRecord(2594, (EpsgCoordinateSystemKind)2, 535); + return true; + case 2595: + cacheIndex = 536; + reference = new EpsgCoordinateReferenceRecord(2595, (EpsgCoordinateSystemKind)2, 536); + return true; + case 2596: + cacheIndex = 537; + reference = new EpsgCoordinateReferenceRecord(2596, (EpsgCoordinateSystemKind)2, 537); + return true; + case 2597: + cacheIndex = 538; + reference = new EpsgCoordinateReferenceRecord(2597, (EpsgCoordinateSystemKind)2, 538); + return true; + case 2598: + cacheIndex = 539; + reference = new EpsgCoordinateReferenceRecord(2598, (EpsgCoordinateSystemKind)2, 539); + return true; + case 2599: + cacheIndex = 540; + reference = new EpsgCoordinateReferenceRecord(2599, (EpsgCoordinateSystemKind)2, 540); + return true; + case 2601: + cacheIndex = 541; + reference = new EpsgCoordinateReferenceRecord(2601, (EpsgCoordinateSystemKind)2, 541); + return true; + case 2602: + cacheIndex = 542; + reference = new EpsgCoordinateReferenceRecord(2602, (EpsgCoordinateSystemKind)2, 542); + return true; + case 2603: + cacheIndex = 543; + reference = new EpsgCoordinateReferenceRecord(2603, (EpsgCoordinateSystemKind)2, 543); + return true; + case 2604: + cacheIndex = 544; + reference = new EpsgCoordinateReferenceRecord(2604, (EpsgCoordinateSystemKind)2, 544); + return true; + case 2605: + cacheIndex = 545; + reference = new EpsgCoordinateReferenceRecord(2605, (EpsgCoordinateSystemKind)2, 545); + return true; + case 2606: + cacheIndex = 546; + reference = new EpsgCoordinateReferenceRecord(2606, (EpsgCoordinateSystemKind)2, 546); + return true; + case 2607: + cacheIndex = 547; + reference = new EpsgCoordinateReferenceRecord(2607, (EpsgCoordinateSystemKind)2, 547); + return true; + case 2608: + cacheIndex = 548; + reference = new EpsgCoordinateReferenceRecord(2608, (EpsgCoordinateSystemKind)2, 548); + return true; + case 2609: + cacheIndex = 549; + reference = new EpsgCoordinateReferenceRecord(2609, (EpsgCoordinateSystemKind)2, 549); + return true; + case 2610: + cacheIndex = 550; + reference = new EpsgCoordinateReferenceRecord(2610, (EpsgCoordinateSystemKind)2, 550); + return true; + case 2611: + cacheIndex = 551; + reference = new EpsgCoordinateReferenceRecord(2611, (EpsgCoordinateSystemKind)2, 551); + return true; + case 2612: + cacheIndex = 552; + reference = new EpsgCoordinateReferenceRecord(2612, (EpsgCoordinateSystemKind)2, 552); + return true; + case 2613: + cacheIndex = 553; + reference = new EpsgCoordinateReferenceRecord(2613, (EpsgCoordinateSystemKind)2, 553); + return true; + case 2614: + cacheIndex = 554; + reference = new EpsgCoordinateReferenceRecord(2614, (EpsgCoordinateSystemKind)2, 554); + return true; + case 2615: + cacheIndex = 555; + reference = new EpsgCoordinateReferenceRecord(2615, (EpsgCoordinateSystemKind)2, 555); + return true; + case 2616: + cacheIndex = 556; + reference = new EpsgCoordinateReferenceRecord(2616, (EpsgCoordinateSystemKind)2, 556); + return true; + case 2617: + cacheIndex = 557; + reference = new EpsgCoordinateReferenceRecord(2617, (EpsgCoordinateSystemKind)2, 557); + return true; + case 2618: + cacheIndex = 558; + reference = new EpsgCoordinateReferenceRecord(2618, (EpsgCoordinateSystemKind)2, 558); + return true; + case 2619: + cacheIndex = 559; + reference = new EpsgCoordinateReferenceRecord(2619, (EpsgCoordinateSystemKind)2, 559); + return true; + case 2620: + cacheIndex = 560; + reference = new EpsgCoordinateReferenceRecord(2620, (EpsgCoordinateSystemKind)2, 560); + return true; + case 2621: + cacheIndex = 561; + reference = new EpsgCoordinateReferenceRecord(2621, (EpsgCoordinateSystemKind)2, 561); + return true; + case 2622: + cacheIndex = 562; + reference = new EpsgCoordinateReferenceRecord(2622, (EpsgCoordinateSystemKind)2, 562); + return true; + case 2623: + cacheIndex = 563; + reference = new EpsgCoordinateReferenceRecord(2623, (EpsgCoordinateSystemKind)2, 563); + return true; + case 2624: + cacheIndex = 564; + reference = new EpsgCoordinateReferenceRecord(2624, (EpsgCoordinateSystemKind)2, 564); + return true; + case 2625: + cacheIndex = 565; + reference = new EpsgCoordinateReferenceRecord(2625, (EpsgCoordinateSystemKind)2, 565); + return true; + case 2626: + cacheIndex = 566; + reference = new EpsgCoordinateReferenceRecord(2626, (EpsgCoordinateSystemKind)2, 566); + return true; + case 2627: + cacheIndex = 567; + reference = new EpsgCoordinateReferenceRecord(2627, (EpsgCoordinateSystemKind)2, 567); + return true; + case 2628: + cacheIndex = 568; + reference = new EpsgCoordinateReferenceRecord(2628, (EpsgCoordinateSystemKind)2, 568); + return true; + case 2629: + cacheIndex = 569; + reference = new EpsgCoordinateReferenceRecord(2629, (EpsgCoordinateSystemKind)2, 569); + return true; + case 2630: + cacheIndex = 570; + reference = new EpsgCoordinateReferenceRecord(2630, (EpsgCoordinateSystemKind)2, 570); + return true; + case 2631: + cacheIndex = 571; + reference = new EpsgCoordinateReferenceRecord(2631, (EpsgCoordinateSystemKind)2, 571); + return true; + case 2632: + cacheIndex = 572; + reference = new EpsgCoordinateReferenceRecord(2632, (EpsgCoordinateSystemKind)2, 572); + return true; + case 2633: + cacheIndex = 573; + reference = new EpsgCoordinateReferenceRecord(2633, (EpsgCoordinateSystemKind)2, 573); + return true; + case 2634: + cacheIndex = 574; + reference = new EpsgCoordinateReferenceRecord(2634, (EpsgCoordinateSystemKind)2, 574); + return true; + case 2635: + cacheIndex = 575; + reference = new EpsgCoordinateReferenceRecord(2635, (EpsgCoordinateSystemKind)2, 575); + return true; + case 2636: + cacheIndex = 576; + reference = new EpsgCoordinateReferenceRecord(2636, (EpsgCoordinateSystemKind)2, 576); + return true; + case 2637: + cacheIndex = 577; + reference = new EpsgCoordinateReferenceRecord(2637, (EpsgCoordinateSystemKind)2, 577); + return true; + case 2638: + cacheIndex = 578; + reference = new EpsgCoordinateReferenceRecord(2638, (EpsgCoordinateSystemKind)2, 578); + return true; + case 2639: + cacheIndex = 579; + reference = new EpsgCoordinateReferenceRecord(2639, (EpsgCoordinateSystemKind)2, 579); + return true; + case 2640: + cacheIndex = 580; + reference = new EpsgCoordinateReferenceRecord(2640, (EpsgCoordinateSystemKind)2, 580); + return true; + case 2641: + cacheIndex = 581; + reference = new EpsgCoordinateReferenceRecord(2641, (EpsgCoordinateSystemKind)2, 581); + return true; + case 2642: + cacheIndex = 582; + reference = new EpsgCoordinateReferenceRecord(2642, (EpsgCoordinateSystemKind)2, 582); + return true; + case 2643: + cacheIndex = 583; + reference = new EpsgCoordinateReferenceRecord(2643, (EpsgCoordinateSystemKind)2, 583); + return true; + case 2644: + cacheIndex = 584; + reference = new EpsgCoordinateReferenceRecord(2644, (EpsgCoordinateSystemKind)2, 584); + return true; + case 2645: + cacheIndex = 585; + reference = new EpsgCoordinateReferenceRecord(2645, (EpsgCoordinateSystemKind)2, 585); + return true; + case 2646: + cacheIndex = 586; + reference = new EpsgCoordinateReferenceRecord(2646, (EpsgCoordinateSystemKind)2, 586); + return true; + case 2647: + cacheIndex = 587; + reference = new EpsgCoordinateReferenceRecord(2647, (EpsgCoordinateSystemKind)2, 587); + return true; + case 2648: + cacheIndex = 588; + reference = new EpsgCoordinateReferenceRecord(2648, (EpsgCoordinateSystemKind)2, 588); + return true; + case 2649: + cacheIndex = 589; + reference = new EpsgCoordinateReferenceRecord(2649, (EpsgCoordinateSystemKind)2, 589); + return true; + case 2650: + cacheIndex = 590; + reference = new EpsgCoordinateReferenceRecord(2650, (EpsgCoordinateSystemKind)2, 590); + return true; + case 2651: + cacheIndex = 591; + reference = new EpsgCoordinateReferenceRecord(2651, (EpsgCoordinateSystemKind)2, 591); + return true; + case 2652: + cacheIndex = 592; + reference = new EpsgCoordinateReferenceRecord(2652, (EpsgCoordinateSystemKind)2, 592); + return true; + case 2653: + cacheIndex = 593; + reference = new EpsgCoordinateReferenceRecord(2653, (EpsgCoordinateSystemKind)2, 593); + return true; + case 2654: + cacheIndex = 594; + reference = new EpsgCoordinateReferenceRecord(2654, (EpsgCoordinateSystemKind)2, 594); + return true; + case 2655: + cacheIndex = 595; + reference = new EpsgCoordinateReferenceRecord(2655, (EpsgCoordinateSystemKind)2, 595); + return true; + case 2656: + cacheIndex = 596; + reference = new EpsgCoordinateReferenceRecord(2656, (EpsgCoordinateSystemKind)2, 596); + return true; + case 2657: + cacheIndex = 597; + reference = new EpsgCoordinateReferenceRecord(2657, (EpsgCoordinateSystemKind)2, 597); + return true; + case 2658: + cacheIndex = 598; + reference = new EpsgCoordinateReferenceRecord(2658, (EpsgCoordinateSystemKind)2, 598); + return true; + case 2659: + cacheIndex = 599; + reference = new EpsgCoordinateReferenceRecord(2659, (EpsgCoordinateSystemKind)2, 599); + return true; + case 2660: + cacheIndex = 600; + reference = new EpsgCoordinateReferenceRecord(2660, (EpsgCoordinateSystemKind)2, 600); + return true; + case 2661: + cacheIndex = 601; + reference = new EpsgCoordinateReferenceRecord(2661, (EpsgCoordinateSystemKind)2, 601); + return true; + case 2662: + cacheIndex = 602; + reference = new EpsgCoordinateReferenceRecord(2662, (EpsgCoordinateSystemKind)2, 602); + return true; + case 2663: + cacheIndex = 603; + reference = new EpsgCoordinateReferenceRecord(2663, (EpsgCoordinateSystemKind)2, 603); + return true; + case 2664: + cacheIndex = 604; + reference = new EpsgCoordinateReferenceRecord(2664, (EpsgCoordinateSystemKind)2, 604); + return true; + case 2665: + cacheIndex = 605; + reference = new EpsgCoordinateReferenceRecord(2665, (EpsgCoordinateSystemKind)2, 605); + return true; + case 2666: + cacheIndex = 606; + reference = new EpsgCoordinateReferenceRecord(2666, (EpsgCoordinateSystemKind)2, 606); + return true; + case 2667: + cacheIndex = 607; + reference = new EpsgCoordinateReferenceRecord(2667, (EpsgCoordinateSystemKind)2, 607); + return true; + case 2668: + cacheIndex = 608; + reference = new EpsgCoordinateReferenceRecord(2668, (EpsgCoordinateSystemKind)2, 608); + return true; + case 2669: + cacheIndex = 609; + reference = new EpsgCoordinateReferenceRecord(2669, (EpsgCoordinateSystemKind)2, 609); + return true; + case 2670: + cacheIndex = 610; + reference = new EpsgCoordinateReferenceRecord(2670, (EpsgCoordinateSystemKind)2, 610); + return true; + case 2671: + cacheIndex = 611; + reference = new EpsgCoordinateReferenceRecord(2671, (EpsgCoordinateSystemKind)2, 611); + return true; + case 2672: + cacheIndex = 612; + reference = new EpsgCoordinateReferenceRecord(2672, (EpsgCoordinateSystemKind)2, 612); + return true; + case 2673: + cacheIndex = 613; + reference = new EpsgCoordinateReferenceRecord(2673, (EpsgCoordinateSystemKind)2, 613); + return true; + case 2674: + cacheIndex = 614; + reference = new EpsgCoordinateReferenceRecord(2674, (EpsgCoordinateSystemKind)2, 614); + return true; + case 2675: + cacheIndex = 615; + reference = new EpsgCoordinateReferenceRecord(2675, (EpsgCoordinateSystemKind)2, 615); + return true; + case 2676: + cacheIndex = 616; + reference = new EpsgCoordinateReferenceRecord(2676, (EpsgCoordinateSystemKind)2, 616); + return true; + case 2677: + cacheIndex = 617; + reference = new EpsgCoordinateReferenceRecord(2677, (EpsgCoordinateSystemKind)2, 617); + return true; + case 2678: + cacheIndex = 618; + reference = new EpsgCoordinateReferenceRecord(2678, (EpsgCoordinateSystemKind)2, 618); + return true; + case 2679: + cacheIndex = 619; + reference = new EpsgCoordinateReferenceRecord(2679, (EpsgCoordinateSystemKind)2, 619); + return true; + case 2680: + cacheIndex = 620; + reference = new EpsgCoordinateReferenceRecord(2680, (EpsgCoordinateSystemKind)2, 620); + return true; + case 2681: + cacheIndex = 621; + reference = new EpsgCoordinateReferenceRecord(2681, (EpsgCoordinateSystemKind)2, 621); + return true; + case 2682: + cacheIndex = 622; + reference = new EpsgCoordinateReferenceRecord(2682, (EpsgCoordinateSystemKind)2, 622); + return true; + case 2683: + cacheIndex = 623; + reference = new EpsgCoordinateReferenceRecord(2683, (EpsgCoordinateSystemKind)2, 623); + return true; + case 2684: + cacheIndex = 624; + reference = new EpsgCoordinateReferenceRecord(2684, (EpsgCoordinateSystemKind)2, 624); + return true; + case 2685: + cacheIndex = 625; + reference = new EpsgCoordinateReferenceRecord(2685, (EpsgCoordinateSystemKind)2, 625); + return true; + case 2686: + cacheIndex = 626; + reference = new EpsgCoordinateReferenceRecord(2686, (EpsgCoordinateSystemKind)2, 626); + return true; + case 2687: + cacheIndex = 627; + reference = new EpsgCoordinateReferenceRecord(2687, (EpsgCoordinateSystemKind)2, 627); + return true; + case 2688: + cacheIndex = 628; + reference = new EpsgCoordinateReferenceRecord(2688, (EpsgCoordinateSystemKind)2, 628); + return true; + case 2689: + cacheIndex = 629; + reference = new EpsgCoordinateReferenceRecord(2689, (EpsgCoordinateSystemKind)2, 629); + return true; + case 2690: + cacheIndex = 630; + reference = new EpsgCoordinateReferenceRecord(2690, (EpsgCoordinateSystemKind)2, 630); + return true; + case 2691: + cacheIndex = 631; + reference = new EpsgCoordinateReferenceRecord(2691, (EpsgCoordinateSystemKind)2, 631); + return true; + case 2692: + cacheIndex = 632; + reference = new EpsgCoordinateReferenceRecord(2692, (EpsgCoordinateSystemKind)2, 632); + return true; + case 2693: + cacheIndex = 633; + reference = new EpsgCoordinateReferenceRecord(2693, (EpsgCoordinateSystemKind)2, 633); + return true; + case 2695: + cacheIndex = 634; + reference = new EpsgCoordinateReferenceRecord(2695, (EpsgCoordinateSystemKind)2, 634); + return true; + case 2696: + cacheIndex = 635; + reference = new EpsgCoordinateReferenceRecord(2696, (EpsgCoordinateSystemKind)2, 635); + return true; + case 2697: + cacheIndex = 636; + reference = new EpsgCoordinateReferenceRecord(2697, (EpsgCoordinateSystemKind)2, 636); + return true; + case 2698: + cacheIndex = 637; + reference = new EpsgCoordinateReferenceRecord(2698, (EpsgCoordinateSystemKind)2, 637); + return true; + case 2699: + cacheIndex = 638; + reference = new EpsgCoordinateReferenceRecord(2699, (EpsgCoordinateSystemKind)2, 638); + return true; + case 2700: + cacheIndex = 639; + reference = new EpsgCoordinateReferenceRecord(2700, (EpsgCoordinateSystemKind)2, 639); + return true; + case 2701: + cacheIndex = 640; + reference = new EpsgCoordinateReferenceRecord(2701, (EpsgCoordinateSystemKind)2, 640); + return true; + case 2702: + cacheIndex = 641; + reference = new EpsgCoordinateReferenceRecord(2702, (EpsgCoordinateSystemKind)2, 641); + return true; + case 2703: + cacheIndex = 642; + reference = new EpsgCoordinateReferenceRecord(2703, (EpsgCoordinateSystemKind)2, 642); + return true; + case 2704: + cacheIndex = 643; + reference = new EpsgCoordinateReferenceRecord(2704, (EpsgCoordinateSystemKind)2, 643); + return true; + case 2705: + cacheIndex = 644; + reference = new EpsgCoordinateReferenceRecord(2705, (EpsgCoordinateSystemKind)2, 644); + return true; + case 2706: + cacheIndex = 645; + reference = new EpsgCoordinateReferenceRecord(2706, (EpsgCoordinateSystemKind)2, 645); + return true; + case 2707: + cacheIndex = 646; + reference = new EpsgCoordinateReferenceRecord(2707, (EpsgCoordinateSystemKind)2, 646); + return true; + case 2708: + cacheIndex = 647; + reference = new EpsgCoordinateReferenceRecord(2708, (EpsgCoordinateSystemKind)2, 647); + return true; + case 2709: + cacheIndex = 648; + reference = new EpsgCoordinateReferenceRecord(2709, (EpsgCoordinateSystemKind)2, 648); + return true; + case 2710: + cacheIndex = 649; + reference = new EpsgCoordinateReferenceRecord(2710, (EpsgCoordinateSystemKind)2, 649); + return true; + case 2711: + cacheIndex = 650; + reference = new EpsgCoordinateReferenceRecord(2711, (EpsgCoordinateSystemKind)2, 650); + return true; + case 2712: + cacheIndex = 651; + reference = new EpsgCoordinateReferenceRecord(2712, (EpsgCoordinateSystemKind)2, 651); + return true; + case 2713: + cacheIndex = 652; + reference = new EpsgCoordinateReferenceRecord(2713, (EpsgCoordinateSystemKind)2, 652); + return true; + case 2714: + cacheIndex = 653; + reference = new EpsgCoordinateReferenceRecord(2714, (EpsgCoordinateSystemKind)2, 653); + return true; + case 2715: + cacheIndex = 654; + reference = new EpsgCoordinateReferenceRecord(2715, (EpsgCoordinateSystemKind)2, 654); + return true; + case 2716: + cacheIndex = 655; + reference = new EpsgCoordinateReferenceRecord(2716, (EpsgCoordinateSystemKind)2, 655); + return true; + case 2717: + cacheIndex = 656; + reference = new EpsgCoordinateReferenceRecord(2717, (EpsgCoordinateSystemKind)2, 656); + return true; + case 2718: + cacheIndex = 657; + reference = new EpsgCoordinateReferenceRecord(2718, (EpsgCoordinateSystemKind)2, 657); + return true; + case 2719: + cacheIndex = 658; + reference = new EpsgCoordinateReferenceRecord(2719, (EpsgCoordinateSystemKind)2, 658); + return true; + case 2720: + cacheIndex = 659; + reference = new EpsgCoordinateReferenceRecord(2720, (EpsgCoordinateSystemKind)2, 659); + return true; + case 2721: + cacheIndex = 660; + reference = new EpsgCoordinateReferenceRecord(2721, (EpsgCoordinateSystemKind)2, 660); + return true; + case 2722: + cacheIndex = 661; + reference = new EpsgCoordinateReferenceRecord(2722, (EpsgCoordinateSystemKind)2, 661); + return true; + case 2723: + cacheIndex = 662; + reference = new EpsgCoordinateReferenceRecord(2723, (EpsgCoordinateSystemKind)2, 662); + return true; + case 2724: + cacheIndex = 663; + reference = new EpsgCoordinateReferenceRecord(2724, (EpsgCoordinateSystemKind)2, 663); + return true; + case 2725: + cacheIndex = 664; + reference = new EpsgCoordinateReferenceRecord(2725, (EpsgCoordinateSystemKind)2, 664); + return true; + case 2726: + cacheIndex = 665; + reference = new EpsgCoordinateReferenceRecord(2726, (EpsgCoordinateSystemKind)2, 665); + return true; + case 2727: + cacheIndex = 666; + reference = new EpsgCoordinateReferenceRecord(2727, (EpsgCoordinateSystemKind)2, 666); + return true; + case 2728: + cacheIndex = 667; + reference = new EpsgCoordinateReferenceRecord(2728, (EpsgCoordinateSystemKind)2, 667); + return true; + case 2729: + cacheIndex = 668; + reference = new EpsgCoordinateReferenceRecord(2729, (EpsgCoordinateSystemKind)2, 668); + return true; + case 2730: + cacheIndex = 669; + reference = new EpsgCoordinateReferenceRecord(2730, (EpsgCoordinateSystemKind)2, 669); + return true; + case 2731: + cacheIndex = 670; + reference = new EpsgCoordinateReferenceRecord(2731, (EpsgCoordinateSystemKind)2, 670); + return true; + case 2732: + cacheIndex = 671; + reference = new EpsgCoordinateReferenceRecord(2732, (EpsgCoordinateSystemKind)2, 671); + return true; + case 2733: + cacheIndex = 672; + reference = new EpsgCoordinateReferenceRecord(2733, (EpsgCoordinateSystemKind)2, 672); + return true; + case 2734: + cacheIndex = 673; + reference = new EpsgCoordinateReferenceRecord(2734, (EpsgCoordinateSystemKind)2, 673); + return true; + case 2735: + cacheIndex = 674; + reference = new EpsgCoordinateReferenceRecord(2735, (EpsgCoordinateSystemKind)2, 674); + return true; + case 2736: + cacheIndex = 675; + reference = new EpsgCoordinateReferenceRecord(2736, (EpsgCoordinateSystemKind)2, 675); + return true; + case 2737: + cacheIndex = 676; + reference = new EpsgCoordinateReferenceRecord(2737, (EpsgCoordinateSystemKind)2, 676); + return true; + case 2738: + cacheIndex = 677; + reference = new EpsgCoordinateReferenceRecord(2738, (EpsgCoordinateSystemKind)2, 677); + return true; + case 2739: + cacheIndex = 678; + reference = new EpsgCoordinateReferenceRecord(2739, (EpsgCoordinateSystemKind)2, 678); + return true; + case 2740: + cacheIndex = 679; + reference = new EpsgCoordinateReferenceRecord(2740, (EpsgCoordinateSystemKind)2, 679); + return true; + case 2741: + cacheIndex = 680; + reference = new EpsgCoordinateReferenceRecord(2741, (EpsgCoordinateSystemKind)2, 680); + return true; + case 2742: + cacheIndex = 681; + reference = new EpsgCoordinateReferenceRecord(2742, (EpsgCoordinateSystemKind)2, 681); + return true; + case 2743: + cacheIndex = 682; + reference = new EpsgCoordinateReferenceRecord(2743, (EpsgCoordinateSystemKind)2, 682); + return true; + case 2744: + cacheIndex = 683; + reference = new EpsgCoordinateReferenceRecord(2744, (EpsgCoordinateSystemKind)2, 683); + return true; + case 2745: + cacheIndex = 684; + reference = new EpsgCoordinateReferenceRecord(2745, (EpsgCoordinateSystemKind)2, 684); + return true; + case 2746: + cacheIndex = 685; + reference = new EpsgCoordinateReferenceRecord(2746, (EpsgCoordinateSystemKind)2, 685); + return true; + case 2747: + cacheIndex = 686; + reference = new EpsgCoordinateReferenceRecord(2747, (EpsgCoordinateSystemKind)2, 686); + return true; + case 2748: + cacheIndex = 687; + reference = new EpsgCoordinateReferenceRecord(2748, (EpsgCoordinateSystemKind)2, 687); + return true; + case 2749: + cacheIndex = 688; + reference = new EpsgCoordinateReferenceRecord(2749, (EpsgCoordinateSystemKind)2, 688); + return true; + case 2750: + cacheIndex = 689; + reference = new EpsgCoordinateReferenceRecord(2750, (EpsgCoordinateSystemKind)2, 689); + return true; + case 2751: + cacheIndex = 690; + reference = new EpsgCoordinateReferenceRecord(2751, (EpsgCoordinateSystemKind)2, 690); + return true; + case 2752: + cacheIndex = 691; + reference = new EpsgCoordinateReferenceRecord(2752, (EpsgCoordinateSystemKind)2, 691); + return true; + case 2753: + cacheIndex = 692; + reference = new EpsgCoordinateReferenceRecord(2753, (EpsgCoordinateSystemKind)2, 692); + return true; + case 2754: + cacheIndex = 693; + reference = new EpsgCoordinateReferenceRecord(2754, (EpsgCoordinateSystemKind)2, 693); + return true; + case 2755: + cacheIndex = 694; + reference = new EpsgCoordinateReferenceRecord(2755, (EpsgCoordinateSystemKind)2, 694); + return true; + case 2756: + cacheIndex = 695; + reference = new EpsgCoordinateReferenceRecord(2756, (EpsgCoordinateSystemKind)2, 695); + return true; + case 2757: + cacheIndex = 696; + reference = new EpsgCoordinateReferenceRecord(2757, (EpsgCoordinateSystemKind)2, 696); + return true; + case 2758: + cacheIndex = 697; + reference = new EpsgCoordinateReferenceRecord(2758, (EpsgCoordinateSystemKind)2, 697); + return true; + case 2759: + cacheIndex = 698; + reference = new EpsgCoordinateReferenceRecord(2759, (EpsgCoordinateSystemKind)2, 698); + return true; + case 2760: + cacheIndex = 699; + reference = new EpsgCoordinateReferenceRecord(2760, (EpsgCoordinateSystemKind)2, 699); + return true; + case 2761: + cacheIndex = 700; + reference = new EpsgCoordinateReferenceRecord(2761, (EpsgCoordinateSystemKind)2, 700); + return true; + case 2762: + cacheIndex = 701; + reference = new EpsgCoordinateReferenceRecord(2762, (EpsgCoordinateSystemKind)2, 701); + return true; + case 2763: + cacheIndex = 702; + reference = new EpsgCoordinateReferenceRecord(2763, (EpsgCoordinateSystemKind)2, 702); + return true; + case 2764: + cacheIndex = 703; + reference = new EpsgCoordinateReferenceRecord(2764, (EpsgCoordinateSystemKind)2, 703); + return true; + case 2765: + cacheIndex = 704; + reference = new EpsgCoordinateReferenceRecord(2765, (EpsgCoordinateSystemKind)2, 704); + return true; + case 2766: + cacheIndex = 705; + reference = new EpsgCoordinateReferenceRecord(2766, (EpsgCoordinateSystemKind)2, 705); + return true; + case 2767: + cacheIndex = 706; + reference = new EpsgCoordinateReferenceRecord(2767, (EpsgCoordinateSystemKind)2, 706); + return true; + case 2768: + cacheIndex = 707; + reference = new EpsgCoordinateReferenceRecord(2768, (EpsgCoordinateSystemKind)2, 707); + return true; + case 2769: + cacheIndex = 708; + reference = new EpsgCoordinateReferenceRecord(2769, (EpsgCoordinateSystemKind)2, 708); + return true; + case 2770: + cacheIndex = 709; + reference = new EpsgCoordinateReferenceRecord(2770, (EpsgCoordinateSystemKind)2, 709); + return true; + case 2771: + cacheIndex = 710; + reference = new EpsgCoordinateReferenceRecord(2771, (EpsgCoordinateSystemKind)2, 710); + return true; + case 2772: + cacheIndex = 711; + reference = new EpsgCoordinateReferenceRecord(2772, (EpsgCoordinateSystemKind)2, 711); + return true; + case 2773: + cacheIndex = 712; + reference = new EpsgCoordinateReferenceRecord(2773, (EpsgCoordinateSystemKind)2, 712); + return true; + case 2774: + cacheIndex = 713; + reference = new EpsgCoordinateReferenceRecord(2774, (EpsgCoordinateSystemKind)2, 713); + return true; + case 2775: + cacheIndex = 714; + reference = new EpsgCoordinateReferenceRecord(2775, (EpsgCoordinateSystemKind)2, 714); + return true; + case 2776: + cacheIndex = 715; + reference = new EpsgCoordinateReferenceRecord(2776, (EpsgCoordinateSystemKind)2, 715); + return true; + case 2777: + cacheIndex = 716; + reference = new EpsgCoordinateReferenceRecord(2777, (EpsgCoordinateSystemKind)2, 716); + return true; + case 2778: + cacheIndex = 717; + reference = new EpsgCoordinateReferenceRecord(2778, (EpsgCoordinateSystemKind)2, 717); + return true; + case 2779: + cacheIndex = 718; + reference = new EpsgCoordinateReferenceRecord(2779, (EpsgCoordinateSystemKind)2, 718); + return true; + case 2780: + cacheIndex = 719; + reference = new EpsgCoordinateReferenceRecord(2780, (EpsgCoordinateSystemKind)2, 719); + return true; + case 2781: + cacheIndex = 720; + reference = new EpsgCoordinateReferenceRecord(2781, (EpsgCoordinateSystemKind)2, 720); + return true; + case 2782: + cacheIndex = 721; + reference = new EpsgCoordinateReferenceRecord(2782, (EpsgCoordinateSystemKind)2, 721); + return true; + case 2783: + cacheIndex = 722; + reference = new EpsgCoordinateReferenceRecord(2783, (EpsgCoordinateSystemKind)2, 722); + return true; + case 2784: + cacheIndex = 723; + reference = new EpsgCoordinateReferenceRecord(2784, (EpsgCoordinateSystemKind)2, 723); + return true; + case 2785: + cacheIndex = 724; + reference = new EpsgCoordinateReferenceRecord(2785, (EpsgCoordinateSystemKind)2, 724); + return true; + case 2786: + cacheIndex = 725; + reference = new EpsgCoordinateReferenceRecord(2786, (EpsgCoordinateSystemKind)2, 725); + return true; + case 2787: + cacheIndex = 726; + reference = new EpsgCoordinateReferenceRecord(2787, (EpsgCoordinateSystemKind)2, 726); + return true; + case 2788: + cacheIndex = 727; + reference = new EpsgCoordinateReferenceRecord(2788, (EpsgCoordinateSystemKind)2, 727); + return true; + case 2789: + cacheIndex = 728; + reference = new EpsgCoordinateReferenceRecord(2789, (EpsgCoordinateSystemKind)2, 728); + return true; + case 2790: + cacheIndex = 729; + reference = new EpsgCoordinateReferenceRecord(2790, (EpsgCoordinateSystemKind)2, 729); + return true; + case 2791: + cacheIndex = 730; + reference = new EpsgCoordinateReferenceRecord(2791, (EpsgCoordinateSystemKind)2, 730); + return true; + case 2792: + cacheIndex = 731; + reference = new EpsgCoordinateReferenceRecord(2792, (EpsgCoordinateSystemKind)2, 731); + return true; + case 2793: + cacheIndex = 732; + reference = new EpsgCoordinateReferenceRecord(2793, (EpsgCoordinateSystemKind)2, 732); + return true; + case 2794: + cacheIndex = 733; + reference = new EpsgCoordinateReferenceRecord(2794, (EpsgCoordinateSystemKind)2, 733); + return true; + case 2795: + cacheIndex = 734; + reference = new EpsgCoordinateReferenceRecord(2795, (EpsgCoordinateSystemKind)2, 734); + return true; + case 2796: + cacheIndex = 735; + reference = new EpsgCoordinateReferenceRecord(2796, (EpsgCoordinateSystemKind)2, 735); + return true; + case 2797: + cacheIndex = 736; + reference = new EpsgCoordinateReferenceRecord(2797, (EpsgCoordinateSystemKind)2, 736); + return true; + case 2798: + cacheIndex = 737; + reference = new EpsgCoordinateReferenceRecord(2798, (EpsgCoordinateSystemKind)2, 737); + return true; + case 2799: + cacheIndex = 738; + reference = new EpsgCoordinateReferenceRecord(2799, (EpsgCoordinateSystemKind)2, 738); + return true; + case 2800: + cacheIndex = 739; + reference = new EpsgCoordinateReferenceRecord(2800, (EpsgCoordinateSystemKind)2, 739); + return true; + case 2801: + cacheIndex = 740; + reference = new EpsgCoordinateReferenceRecord(2801, (EpsgCoordinateSystemKind)2, 740); + return true; + case 2802: + cacheIndex = 741; + reference = new EpsgCoordinateReferenceRecord(2802, (EpsgCoordinateSystemKind)2, 741); + return true; + case 2803: + cacheIndex = 742; + reference = new EpsgCoordinateReferenceRecord(2803, (EpsgCoordinateSystemKind)2, 742); + return true; + case 2804: + cacheIndex = 743; + reference = new EpsgCoordinateReferenceRecord(2804, (EpsgCoordinateSystemKind)2, 743); + return true; + case 2805: + cacheIndex = 744; + reference = new EpsgCoordinateReferenceRecord(2805, (EpsgCoordinateSystemKind)2, 744); + return true; + case 2806: + cacheIndex = 745; + reference = new EpsgCoordinateReferenceRecord(2806, (EpsgCoordinateSystemKind)2, 745); + return true; + case 2807: + cacheIndex = 746; + reference = new EpsgCoordinateReferenceRecord(2807, (EpsgCoordinateSystemKind)2, 746); + return true; + case 2808: + cacheIndex = 747; + reference = new EpsgCoordinateReferenceRecord(2808, (EpsgCoordinateSystemKind)2, 747); + return true; + case 2809: + cacheIndex = 748; + reference = new EpsgCoordinateReferenceRecord(2809, (EpsgCoordinateSystemKind)2, 748); + return true; + case 2810: + cacheIndex = 749; + reference = new EpsgCoordinateReferenceRecord(2810, (EpsgCoordinateSystemKind)2, 749); + return true; + case 2811: + cacheIndex = 750; + reference = new EpsgCoordinateReferenceRecord(2811, (EpsgCoordinateSystemKind)2, 750); + return true; + case 2812: + cacheIndex = 751; + reference = new EpsgCoordinateReferenceRecord(2812, (EpsgCoordinateSystemKind)2, 751); + return true; + case 2813: + cacheIndex = 752; + reference = new EpsgCoordinateReferenceRecord(2813, (EpsgCoordinateSystemKind)2, 752); + return true; + case 2814: + cacheIndex = 753; + reference = new EpsgCoordinateReferenceRecord(2814, (EpsgCoordinateSystemKind)2, 753); + return true; + case 2815: + cacheIndex = 754; + reference = new EpsgCoordinateReferenceRecord(2815, (EpsgCoordinateSystemKind)2, 754); + return true; + case 2816: + cacheIndex = 755; + reference = new EpsgCoordinateReferenceRecord(2816, (EpsgCoordinateSystemKind)2, 755); + return true; + case 2817: + cacheIndex = 756; + reference = new EpsgCoordinateReferenceRecord(2817, (EpsgCoordinateSystemKind)2, 756); + return true; + case 2818: + cacheIndex = 757; + reference = new EpsgCoordinateReferenceRecord(2818, (EpsgCoordinateSystemKind)2, 757); + return true; + case 2819: + cacheIndex = 758; + reference = new EpsgCoordinateReferenceRecord(2819, (EpsgCoordinateSystemKind)2, 758); + return true; + case 2820: + cacheIndex = 759; + reference = new EpsgCoordinateReferenceRecord(2820, (EpsgCoordinateSystemKind)2, 759); + return true; + case 2821: + cacheIndex = 760; + reference = new EpsgCoordinateReferenceRecord(2821, (EpsgCoordinateSystemKind)2, 760); + return true; + case 2822: + cacheIndex = 761; + reference = new EpsgCoordinateReferenceRecord(2822, (EpsgCoordinateSystemKind)2, 761); + return true; + case 2823: + cacheIndex = 762; + reference = new EpsgCoordinateReferenceRecord(2823, (EpsgCoordinateSystemKind)2, 762); + return true; + case 2824: + cacheIndex = 763; + reference = new EpsgCoordinateReferenceRecord(2824, (EpsgCoordinateSystemKind)2, 763); + return true; + case 2825: + cacheIndex = 764; + reference = new EpsgCoordinateReferenceRecord(2825, (EpsgCoordinateSystemKind)2, 764); + return true; + case 2826: + cacheIndex = 765; + reference = new EpsgCoordinateReferenceRecord(2826, (EpsgCoordinateSystemKind)2, 765); + return true; + case 2827: + cacheIndex = 766; + reference = new EpsgCoordinateReferenceRecord(2827, (EpsgCoordinateSystemKind)2, 766); + return true; + case 2828: + cacheIndex = 767; + reference = new EpsgCoordinateReferenceRecord(2828, (EpsgCoordinateSystemKind)2, 767); + return true; + case 2829: + cacheIndex = 768; + reference = new EpsgCoordinateReferenceRecord(2829, (EpsgCoordinateSystemKind)2, 768); + return true; + case 2830: + cacheIndex = 769; + reference = new EpsgCoordinateReferenceRecord(2830, (EpsgCoordinateSystemKind)2, 769); + return true; + case 2831: + cacheIndex = 770; + reference = new EpsgCoordinateReferenceRecord(2831, (EpsgCoordinateSystemKind)2, 770); + return true; + case 2832: + cacheIndex = 771; + reference = new EpsgCoordinateReferenceRecord(2832, (EpsgCoordinateSystemKind)2, 771); + return true; + case 2833: + cacheIndex = 772; + reference = new EpsgCoordinateReferenceRecord(2833, (EpsgCoordinateSystemKind)2, 772); + return true; + case 2834: + cacheIndex = 773; + reference = new EpsgCoordinateReferenceRecord(2834, (EpsgCoordinateSystemKind)2, 773); + return true; + case 2835: + cacheIndex = 774; + reference = new EpsgCoordinateReferenceRecord(2835, (EpsgCoordinateSystemKind)2, 774); + return true; + case 2836: + cacheIndex = 775; + reference = new EpsgCoordinateReferenceRecord(2836, (EpsgCoordinateSystemKind)2, 775); + return true; + case 2837: + cacheIndex = 776; + reference = new EpsgCoordinateReferenceRecord(2837, (EpsgCoordinateSystemKind)2, 776); + return true; + case 2838: + cacheIndex = 777; + reference = new EpsgCoordinateReferenceRecord(2838, (EpsgCoordinateSystemKind)2, 777); + return true; + case 2839: + cacheIndex = 778; + reference = new EpsgCoordinateReferenceRecord(2839, (EpsgCoordinateSystemKind)2, 778); + return true; + case 2840: + cacheIndex = 779; + reference = new EpsgCoordinateReferenceRecord(2840, (EpsgCoordinateSystemKind)2, 779); + return true; + case 2841: + cacheIndex = 780; + reference = new EpsgCoordinateReferenceRecord(2841, (EpsgCoordinateSystemKind)2, 780); + return true; + case 2842: + cacheIndex = 781; + reference = new EpsgCoordinateReferenceRecord(2842, (EpsgCoordinateSystemKind)2, 781); + return true; + case 2843: + cacheIndex = 782; + reference = new EpsgCoordinateReferenceRecord(2843, (EpsgCoordinateSystemKind)2, 782); + return true; + case 2844: + cacheIndex = 783; + reference = new EpsgCoordinateReferenceRecord(2844, (EpsgCoordinateSystemKind)2, 783); + return true; + case 2845: + cacheIndex = 784; + reference = new EpsgCoordinateReferenceRecord(2845, (EpsgCoordinateSystemKind)2, 784); + return true; + case 2846: + cacheIndex = 785; + reference = new EpsgCoordinateReferenceRecord(2846, (EpsgCoordinateSystemKind)2, 785); + return true; + case 2847: + cacheIndex = 786; + reference = new EpsgCoordinateReferenceRecord(2847, (EpsgCoordinateSystemKind)2, 786); + return true; + case 2848: + cacheIndex = 787; + reference = new EpsgCoordinateReferenceRecord(2848, (EpsgCoordinateSystemKind)2, 787); + return true; + case 2849: + cacheIndex = 788; + reference = new EpsgCoordinateReferenceRecord(2849, (EpsgCoordinateSystemKind)2, 788); + return true; + case 2850: + cacheIndex = 789; + reference = new EpsgCoordinateReferenceRecord(2850, (EpsgCoordinateSystemKind)2, 789); + return true; + case 2851: + cacheIndex = 790; + reference = new EpsgCoordinateReferenceRecord(2851, (EpsgCoordinateSystemKind)2, 790); + return true; + case 2852: + cacheIndex = 791; + reference = new EpsgCoordinateReferenceRecord(2852, (EpsgCoordinateSystemKind)2, 791); + return true; + case 2853: + cacheIndex = 792; + reference = new EpsgCoordinateReferenceRecord(2853, (EpsgCoordinateSystemKind)2, 792); + return true; + case 2854: + cacheIndex = 793; + reference = new EpsgCoordinateReferenceRecord(2854, (EpsgCoordinateSystemKind)2, 793); + return true; + case 2855: + cacheIndex = 794; + reference = new EpsgCoordinateReferenceRecord(2855, (EpsgCoordinateSystemKind)2, 794); + return true; + case 2856: + cacheIndex = 795; + reference = new EpsgCoordinateReferenceRecord(2856, (EpsgCoordinateSystemKind)2, 795); + return true; + case 2857: + cacheIndex = 796; + reference = new EpsgCoordinateReferenceRecord(2857, (EpsgCoordinateSystemKind)2, 796); + return true; + case 2858: + cacheIndex = 797; + reference = new EpsgCoordinateReferenceRecord(2858, (EpsgCoordinateSystemKind)2, 797); + return true; + case 2859: + cacheIndex = 798; + reference = new EpsgCoordinateReferenceRecord(2859, (EpsgCoordinateSystemKind)2, 798); + return true; + case 2860: + cacheIndex = 799; + reference = new EpsgCoordinateReferenceRecord(2860, (EpsgCoordinateSystemKind)2, 799); + return true; + case 2861: + cacheIndex = 800; + reference = new EpsgCoordinateReferenceRecord(2861, (EpsgCoordinateSystemKind)2, 800); + return true; + case 2862: + cacheIndex = 801; + reference = new EpsgCoordinateReferenceRecord(2862, (EpsgCoordinateSystemKind)2, 801); + return true; + case 2863: + cacheIndex = 802; + reference = new EpsgCoordinateReferenceRecord(2863, (EpsgCoordinateSystemKind)2, 802); + return true; + case 2864: + cacheIndex = 803; + reference = new EpsgCoordinateReferenceRecord(2864, (EpsgCoordinateSystemKind)2, 803); + return true; + case 2865: + cacheIndex = 804; + reference = new EpsgCoordinateReferenceRecord(2865, (EpsgCoordinateSystemKind)2, 804); + return true; + case 2866: + cacheIndex = 805; + reference = new EpsgCoordinateReferenceRecord(2866, (EpsgCoordinateSystemKind)2, 805); + return true; + case 2867: + cacheIndex = 806; + reference = new EpsgCoordinateReferenceRecord(2867, (EpsgCoordinateSystemKind)2, 806); + return true; + case 2868: + cacheIndex = 807; + reference = new EpsgCoordinateReferenceRecord(2868, (EpsgCoordinateSystemKind)2, 807); + return true; + case 2869: + cacheIndex = 808; + reference = new EpsgCoordinateReferenceRecord(2869, (EpsgCoordinateSystemKind)2, 808); + return true; + case 2870: + cacheIndex = 809; + reference = new EpsgCoordinateReferenceRecord(2870, (EpsgCoordinateSystemKind)2, 809); + return true; + case 2871: + cacheIndex = 810; + reference = new EpsgCoordinateReferenceRecord(2871, (EpsgCoordinateSystemKind)2, 810); + return true; + case 2872: + cacheIndex = 811; + reference = new EpsgCoordinateReferenceRecord(2872, (EpsgCoordinateSystemKind)2, 811); + return true; + case 2873: + cacheIndex = 812; + reference = new EpsgCoordinateReferenceRecord(2873, (EpsgCoordinateSystemKind)2, 812); + return true; + case 2874: + cacheIndex = 813; + reference = new EpsgCoordinateReferenceRecord(2874, (EpsgCoordinateSystemKind)2, 813); + return true; + case 2875: + cacheIndex = 814; + reference = new EpsgCoordinateReferenceRecord(2875, (EpsgCoordinateSystemKind)2, 814); + return true; + case 2876: + cacheIndex = 815; + reference = new EpsgCoordinateReferenceRecord(2876, (EpsgCoordinateSystemKind)2, 815); + return true; + case 2877: + cacheIndex = 816; + reference = new EpsgCoordinateReferenceRecord(2877, (EpsgCoordinateSystemKind)2, 816); + return true; + case 2878: + cacheIndex = 817; + reference = new EpsgCoordinateReferenceRecord(2878, (EpsgCoordinateSystemKind)2, 817); + return true; + case 2879: + cacheIndex = 818; + reference = new EpsgCoordinateReferenceRecord(2879, (EpsgCoordinateSystemKind)2, 818); + return true; + case 2880: + cacheIndex = 819; + reference = new EpsgCoordinateReferenceRecord(2880, (EpsgCoordinateSystemKind)2, 819); + return true; + case 2881: + cacheIndex = 820; + reference = new EpsgCoordinateReferenceRecord(2881, (EpsgCoordinateSystemKind)2, 820); + return true; + case 2882: + cacheIndex = 821; + reference = new EpsgCoordinateReferenceRecord(2882, (EpsgCoordinateSystemKind)2, 821); + return true; + case 2883: + cacheIndex = 822; + reference = new EpsgCoordinateReferenceRecord(2883, (EpsgCoordinateSystemKind)2, 822); + return true; + case 2884: + cacheIndex = 823; + reference = new EpsgCoordinateReferenceRecord(2884, (EpsgCoordinateSystemKind)2, 823); + return true; + case 2885: + cacheIndex = 824; + reference = new EpsgCoordinateReferenceRecord(2885, (EpsgCoordinateSystemKind)2, 824); + return true; + case 2886: + cacheIndex = 825; + reference = new EpsgCoordinateReferenceRecord(2886, (EpsgCoordinateSystemKind)2, 825); + return true; + case 2887: + cacheIndex = 826; + reference = new EpsgCoordinateReferenceRecord(2887, (EpsgCoordinateSystemKind)2, 826); + return true; + case 2888: + cacheIndex = 827; + reference = new EpsgCoordinateReferenceRecord(2888, (EpsgCoordinateSystemKind)2, 827); + return true; + case 2891: + cacheIndex = 828; + reference = new EpsgCoordinateReferenceRecord(2891, (EpsgCoordinateSystemKind)2, 828); + return true; + case 2892: + cacheIndex = 829; + reference = new EpsgCoordinateReferenceRecord(2892, (EpsgCoordinateSystemKind)2, 829); + return true; + case 2893: + cacheIndex = 830; + reference = new EpsgCoordinateReferenceRecord(2893, (EpsgCoordinateSystemKind)2, 830); + return true; + case 2894: + cacheIndex = 831; + reference = new EpsgCoordinateReferenceRecord(2894, (EpsgCoordinateSystemKind)2, 831); + return true; + case 2895: + cacheIndex = 832; + reference = new EpsgCoordinateReferenceRecord(2895, (EpsgCoordinateSystemKind)2, 832); + return true; + case 2896: + cacheIndex = 833; + reference = new EpsgCoordinateReferenceRecord(2896, (EpsgCoordinateSystemKind)2, 833); + return true; + case 2897: + cacheIndex = 834; + reference = new EpsgCoordinateReferenceRecord(2897, (EpsgCoordinateSystemKind)2, 834); + return true; + case 2898: + cacheIndex = 835; + reference = new EpsgCoordinateReferenceRecord(2898, (EpsgCoordinateSystemKind)2, 835); + return true; + case 2899: + cacheIndex = 836; + reference = new EpsgCoordinateReferenceRecord(2899, (EpsgCoordinateSystemKind)2, 836); + return true; + case 2900: + cacheIndex = 837; + reference = new EpsgCoordinateReferenceRecord(2900, (EpsgCoordinateSystemKind)2, 837); + return true; + case 2901: + cacheIndex = 838; + reference = new EpsgCoordinateReferenceRecord(2901, (EpsgCoordinateSystemKind)2, 838); + return true; + case 2902: + cacheIndex = 839; + reference = new EpsgCoordinateReferenceRecord(2902, (EpsgCoordinateSystemKind)2, 839); + return true; + case 2903: + cacheIndex = 840; + reference = new EpsgCoordinateReferenceRecord(2903, (EpsgCoordinateSystemKind)2, 840); + return true; + case 2904: + cacheIndex = 841; + reference = new EpsgCoordinateReferenceRecord(2904, (EpsgCoordinateSystemKind)2, 841); + return true; + case 2905: + cacheIndex = 842; + reference = new EpsgCoordinateReferenceRecord(2905, (EpsgCoordinateSystemKind)2, 842); + return true; + case 2906: + cacheIndex = 843; + reference = new EpsgCoordinateReferenceRecord(2906, (EpsgCoordinateSystemKind)2, 843); + return true; + case 2907: + cacheIndex = 844; + reference = new EpsgCoordinateReferenceRecord(2907, (EpsgCoordinateSystemKind)2, 844); + return true; + case 2908: + cacheIndex = 845; + reference = new EpsgCoordinateReferenceRecord(2908, (EpsgCoordinateSystemKind)2, 845); + return true; + case 2909: + cacheIndex = 846; + reference = new EpsgCoordinateReferenceRecord(2909, (EpsgCoordinateSystemKind)2, 846); + return true; + case 2910: + cacheIndex = 847; + reference = new EpsgCoordinateReferenceRecord(2910, (EpsgCoordinateSystemKind)2, 847); + return true; + case 2911: + cacheIndex = 848; + reference = new EpsgCoordinateReferenceRecord(2911, (EpsgCoordinateSystemKind)2, 848); + return true; + case 2912: + cacheIndex = 849; + reference = new EpsgCoordinateReferenceRecord(2912, (EpsgCoordinateSystemKind)2, 849); + return true; + case 2913: + cacheIndex = 850; + reference = new EpsgCoordinateReferenceRecord(2913, (EpsgCoordinateSystemKind)2, 850); + return true; + case 2914: + cacheIndex = 851; + reference = new EpsgCoordinateReferenceRecord(2914, (EpsgCoordinateSystemKind)2, 851); + return true; + case 2915: + cacheIndex = 852; + reference = new EpsgCoordinateReferenceRecord(2915, (EpsgCoordinateSystemKind)2, 852); + return true; + case 2916: + cacheIndex = 853; + reference = new EpsgCoordinateReferenceRecord(2916, (EpsgCoordinateSystemKind)2, 853); + return true; + case 2917: + cacheIndex = 854; + reference = new EpsgCoordinateReferenceRecord(2917, (EpsgCoordinateSystemKind)2, 854); + return true; + case 2918: + cacheIndex = 855; + reference = new EpsgCoordinateReferenceRecord(2918, (EpsgCoordinateSystemKind)2, 855); + return true; + case 2919: + cacheIndex = 856; + reference = new EpsgCoordinateReferenceRecord(2919, (EpsgCoordinateSystemKind)2, 856); + return true; + case 2920: + cacheIndex = 857; + reference = new EpsgCoordinateReferenceRecord(2920, (EpsgCoordinateSystemKind)2, 857); + return true; + case 2921: + cacheIndex = 858; + reference = new EpsgCoordinateReferenceRecord(2921, (EpsgCoordinateSystemKind)2, 858); + return true; + case 2922: + cacheIndex = 859; + reference = new EpsgCoordinateReferenceRecord(2922, (EpsgCoordinateSystemKind)2, 859); + return true; + case 2923: + cacheIndex = 860; + reference = new EpsgCoordinateReferenceRecord(2923, (EpsgCoordinateSystemKind)2, 860); + return true; + case 2924: + cacheIndex = 861; + reference = new EpsgCoordinateReferenceRecord(2924, (EpsgCoordinateSystemKind)2, 861); + return true; + case 2925: + cacheIndex = 862; + reference = new EpsgCoordinateReferenceRecord(2925, (EpsgCoordinateSystemKind)2, 862); + return true; + case 2926: + cacheIndex = 863; + reference = new EpsgCoordinateReferenceRecord(2926, (EpsgCoordinateSystemKind)2, 863); + return true; + case 2927: + cacheIndex = 864; + reference = new EpsgCoordinateReferenceRecord(2927, (EpsgCoordinateSystemKind)2, 864); + return true; + case 2928: + cacheIndex = 865; + reference = new EpsgCoordinateReferenceRecord(2928, (EpsgCoordinateSystemKind)2, 865); + return true; + case 2929: + cacheIndex = 866; + reference = new EpsgCoordinateReferenceRecord(2929, (EpsgCoordinateSystemKind)2, 866); + return true; + case 2930: + cacheIndex = 867; + reference = new EpsgCoordinateReferenceRecord(2930, (EpsgCoordinateSystemKind)2, 867); + return true; + case 2931: + cacheIndex = 868; + reference = new EpsgCoordinateReferenceRecord(2931, (EpsgCoordinateSystemKind)2, 868); + return true; + case 2932: + cacheIndex = 869; + reference = new EpsgCoordinateReferenceRecord(2932, (EpsgCoordinateSystemKind)2, 869); + return true; + case 2933: + cacheIndex = 870; + reference = new EpsgCoordinateReferenceRecord(2933, (EpsgCoordinateSystemKind)2, 870); + return true; + case 2935: + cacheIndex = 871; + reference = new EpsgCoordinateReferenceRecord(2935, (EpsgCoordinateSystemKind)2, 871); + return true; + case 2936: + cacheIndex = 872; + reference = new EpsgCoordinateReferenceRecord(2936, (EpsgCoordinateSystemKind)2, 872); + return true; + case 2937: + cacheIndex = 873; + reference = new EpsgCoordinateReferenceRecord(2937, (EpsgCoordinateSystemKind)2, 873); + return true; + case 2938: + cacheIndex = 874; + reference = new EpsgCoordinateReferenceRecord(2938, (EpsgCoordinateSystemKind)2, 874); + return true; + case 2939: + cacheIndex = 875; + reference = new EpsgCoordinateReferenceRecord(2939, (EpsgCoordinateSystemKind)2, 875); + return true; + case 2940: + cacheIndex = 876; + reference = new EpsgCoordinateReferenceRecord(2940, (EpsgCoordinateSystemKind)2, 876); + return true; + case 2941: + cacheIndex = 877; + reference = new EpsgCoordinateReferenceRecord(2941, (EpsgCoordinateSystemKind)2, 877); + return true; + case 2942: + cacheIndex = 878; + reference = new EpsgCoordinateReferenceRecord(2942, (EpsgCoordinateSystemKind)2, 878); + return true; + case 2943: + cacheIndex = 879; + reference = new EpsgCoordinateReferenceRecord(2943, (EpsgCoordinateSystemKind)2, 879); + return true; + case 2945: + cacheIndex = 880; + reference = new EpsgCoordinateReferenceRecord(2945, (EpsgCoordinateSystemKind)2, 880); + return true; + case 2946: + cacheIndex = 881; + reference = new EpsgCoordinateReferenceRecord(2946, (EpsgCoordinateSystemKind)2, 881); + return true; + case 2947: + cacheIndex = 882; + reference = new EpsgCoordinateReferenceRecord(2947, (EpsgCoordinateSystemKind)2, 882); + return true; + case 2948: + cacheIndex = 883; + reference = new EpsgCoordinateReferenceRecord(2948, (EpsgCoordinateSystemKind)2, 883); + return true; + case 2949: + cacheIndex = 884; + reference = new EpsgCoordinateReferenceRecord(2949, (EpsgCoordinateSystemKind)2, 884); + return true; + case 2950: + cacheIndex = 885; + reference = new EpsgCoordinateReferenceRecord(2950, (EpsgCoordinateSystemKind)2, 885); + return true; + case 2951: + cacheIndex = 886; + reference = new EpsgCoordinateReferenceRecord(2951, (EpsgCoordinateSystemKind)2, 886); + return true; + case 2952: + cacheIndex = 887; + reference = new EpsgCoordinateReferenceRecord(2952, (EpsgCoordinateSystemKind)2, 887); + return true; + case 2953: + cacheIndex = 888; + reference = new EpsgCoordinateReferenceRecord(2953, (EpsgCoordinateSystemKind)2, 888); + return true; + case 2954: + cacheIndex = 889; + reference = new EpsgCoordinateReferenceRecord(2954, (EpsgCoordinateSystemKind)2, 889); + return true; + case 2955: + cacheIndex = 890; + reference = new EpsgCoordinateReferenceRecord(2955, (EpsgCoordinateSystemKind)2, 890); + return true; + case 2956: + cacheIndex = 891; + reference = new EpsgCoordinateReferenceRecord(2956, (EpsgCoordinateSystemKind)2, 891); + return true; + case 2957: + cacheIndex = 892; + reference = new EpsgCoordinateReferenceRecord(2957, (EpsgCoordinateSystemKind)2, 892); + return true; + case 2958: + cacheIndex = 893; + reference = new EpsgCoordinateReferenceRecord(2958, (EpsgCoordinateSystemKind)2, 893); + return true; + case 2959: + cacheIndex = 894; + reference = new EpsgCoordinateReferenceRecord(2959, (EpsgCoordinateSystemKind)2, 894); + return true; + case 2960: + cacheIndex = 895; + reference = new EpsgCoordinateReferenceRecord(2960, (EpsgCoordinateSystemKind)2, 895); + return true; + case 2961: + cacheIndex = 896; + reference = new EpsgCoordinateReferenceRecord(2961, (EpsgCoordinateSystemKind)2, 896); + return true; + case 2962: + cacheIndex = 897; + reference = new EpsgCoordinateReferenceRecord(2962, (EpsgCoordinateSystemKind)2, 897); + return true; + case 2963: + cacheIndex = 898; + reference = new EpsgCoordinateReferenceRecord(2963, (EpsgCoordinateSystemKind)2, 898); + return true; + case 2964: + cacheIndex = 899; + reference = new EpsgCoordinateReferenceRecord(2964, (EpsgCoordinateSystemKind)2, 899); + return true; + case 2965: + cacheIndex = 900; + reference = new EpsgCoordinateReferenceRecord(2965, (EpsgCoordinateSystemKind)2, 900); + return true; + case 2966: + cacheIndex = 901; + reference = new EpsgCoordinateReferenceRecord(2966, (EpsgCoordinateSystemKind)2, 901); + return true; + case 2967: + cacheIndex = 902; + reference = new EpsgCoordinateReferenceRecord(2967, (EpsgCoordinateSystemKind)2, 902); + return true; + case 2968: + cacheIndex = 903; + reference = new EpsgCoordinateReferenceRecord(2968, (EpsgCoordinateSystemKind)2, 903); + return true; + case 2969: + cacheIndex = 904; + reference = new EpsgCoordinateReferenceRecord(2969, (EpsgCoordinateSystemKind)2, 904); + return true; + case 2970: + cacheIndex = 905; + reference = new EpsgCoordinateReferenceRecord(2970, (EpsgCoordinateSystemKind)2, 905); + return true; + case 2971: + cacheIndex = 906; + reference = new EpsgCoordinateReferenceRecord(2971, (EpsgCoordinateSystemKind)2, 906); + return true; + case 2972: + cacheIndex = 907; + reference = new EpsgCoordinateReferenceRecord(2972, (EpsgCoordinateSystemKind)2, 907); + return true; + case 2973: + cacheIndex = 908; + reference = new EpsgCoordinateReferenceRecord(2973, (EpsgCoordinateSystemKind)2, 908); + return true; + case 2975: + cacheIndex = 909; + reference = new EpsgCoordinateReferenceRecord(2975, (EpsgCoordinateSystemKind)2, 909); + return true; + case 2976: + cacheIndex = 910; + reference = new EpsgCoordinateReferenceRecord(2976, (EpsgCoordinateSystemKind)2, 910); + return true; + case 2977: + cacheIndex = 911; + reference = new EpsgCoordinateReferenceRecord(2977, (EpsgCoordinateSystemKind)2, 911); + return true; + case 2978: + cacheIndex = 912; + reference = new EpsgCoordinateReferenceRecord(2978, (EpsgCoordinateSystemKind)2, 912); + return true; + case 2980: + cacheIndex = 913; + reference = new EpsgCoordinateReferenceRecord(2980, (EpsgCoordinateSystemKind)2, 913); + return true; + case 2981: + cacheIndex = 914; + reference = new EpsgCoordinateReferenceRecord(2981, (EpsgCoordinateSystemKind)2, 914); + return true; + case 2985: + cacheIndex = 915; + reference = new EpsgCoordinateReferenceRecord(2985, (EpsgCoordinateSystemKind)2, 915); + return true; + case 2986: + cacheIndex = 916; + reference = new EpsgCoordinateReferenceRecord(2986, (EpsgCoordinateSystemKind)2, 916); + return true; + case 2987: + cacheIndex = 917; + reference = new EpsgCoordinateReferenceRecord(2987, (EpsgCoordinateSystemKind)2, 917); + return true; + case 2988: + cacheIndex = 918; + reference = new EpsgCoordinateReferenceRecord(2988, (EpsgCoordinateSystemKind)2, 918); + return true; + case 2991: + cacheIndex = 919; + reference = new EpsgCoordinateReferenceRecord(2991, (EpsgCoordinateSystemKind)2, 919); + return true; + case 2992: + cacheIndex = 920; + reference = new EpsgCoordinateReferenceRecord(2992, (EpsgCoordinateSystemKind)2, 920); + return true; + case 2993: + cacheIndex = 921; + reference = new EpsgCoordinateReferenceRecord(2993, (EpsgCoordinateSystemKind)2, 921); + return true; + case 2994: + cacheIndex = 922; + reference = new EpsgCoordinateReferenceRecord(2994, (EpsgCoordinateSystemKind)2, 922); + return true; + case 2995: + cacheIndex = 923; + reference = new EpsgCoordinateReferenceRecord(2995, (EpsgCoordinateSystemKind)2, 923); + return true; + case 2996: + cacheIndex = 924; + reference = new EpsgCoordinateReferenceRecord(2996, (EpsgCoordinateSystemKind)2, 924); + return true; + case 2997: + cacheIndex = 925; + reference = new EpsgCoordinateReferenceRecord(2997, (EpsgCoordinateSystemKind)2, 925); + return true; + case 2998: + cacheIndex = 926; + reference = new EpsgCoordinateReferenceRecord(2998, (EpsgCoordinateSystemKind)2, 926); + return true; + case 2999: + cacheIndex = 927; + reference = new EpsgCoordinateReferenceRecord(2999, (EpsgCoordinateSystemKind)2, 927); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket3(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 3000: + cacheIndex = 928; + reference = new EpsgCoordinateReferenceRecord(3000, (EpsgCoordinateSystemKind)2, 928); + return true; + case 3001: + cacheIndex = 929; + reference = new EpsgCoordinateReferenceRecord(3001, (EpsgCoordinateSystemKind)2, 929); + return true; + case 3002: + cacheIndex = 930; + reference = new EpsgCoordinateReferenceRecord(3002, (EpsgCoordinateSystemKind)2, 930); + return true; + case 3003: + cacheIndex = 931; + reference = new EpsgCoordinateReferenceRecord(3003, (EpsgCoordinateSystemKind)2, 931); + return true; + case 3004: + cacheIndex = 932; + reference = new EpsgCoordinateReferenceRecord(3004, (EpsgCoordinateSystemKind)2, 932); + return true; + case 3005: + cacheIndex = 933; + reference = new EpsgCoordinateReferenceRecord(3005, (EpsgCoordinateSystemKind)2, 933); + return true; + case 3006: + cacheIndex = 934; + reference = new EpsgCoordinateReferenceRecord(3006, (EpsgCoordinateSystemKind)2, 934); + return true; + case 3007: + cacheIndex = 935; + reference = new EpsgCoordinateReferenceRecord(3007, (EpsgCoordinateSystemKind)2, 935); + return true; + case 3008: + cacheIndex = 936; + reference = new EpsgCoordinateReferenceRecord(3008, (EpsgCoordinateSystemKind)2, 936); + return true; + case 3009: + cacheIndex = 937; + reference = new EpsgCoordinateReferenceRecord(3009, (EpsgCoordinateSystemKind)2, 937); + return true; + case 3010: + cacheIndex = 938; + reference = new EpsgCoordinateReferenceRecord(3010, (EpsgCoordinateSystemKind)2, 938); + return true; + case 3011: + cacheIndex = 939; + reference = new EpsgCoordinateReferenceRecord(3011, (EpsgCoordinateSystemKind)2, 939); + return true; + case 3012: + cacheIndex = 940; + reference = new EpsgCoordinateReferenceRecord(3012, (EpsgCoordinateSystemKind)2, 940); + return true; + case 3013: + cacheIndex = 941; + reference = new EpsgCoordinateReferenceRecord(3013, (EpsgCoordinateSystemKind)2, 941); + return true; + case 3014: + cacheIndex = 942; + reference = new EpsgCoordinateReferenceRecord(3014, (EpsgCoordinateSystemKind)2, 942); + return true; + case 3015: + cacheIndex = 943; + reference = new EpsgCoordinateReferenceRecord(3015, (EpsgCoordinateSystemKind)2, 943); + return true; + case 3016: + cacheIndex = 944; + reference = new EpsgCoordinateReferenceRecord(3016, (EpsgCoordinateSystemKind)2, 944); + return true; + case 3017: + cacheIndex = 945; + reference = new EpsgCoordinateReferenceRecord(3017, (EpsgCoordinateSystemKind)2, 945); + return true; + case 3018: + cacheIndex = 946; + reference = new EpsgCoordinateReferenceRecord(3018, (EpsgCoordinateSystemKind)2, 946); + return true; + case 3019: + cacheIndex = 947; + reference = new EpsgCoordinateReferenceRecord(3019, (EpsgCoordinateSystemKind)2, 947); + return true; + case 3020: + cacheIndex = 948; + reference = new EpsgCoordinateReferenceRecord(3020, (EpsgCoordinateSystemKind)2, 948); + return true; + case 3021: + cacheIndex = 949; + reference = new EpsgCoordinateReferenceRecord(3021, (EpsgCoordinateSystemKind)2, 949); + return true; + case 3022: + cacheIndex = 950; + reference = new EpsgCoordinateReferenceRecord(3022, (EpsgCoordinateSystemKind)2, 950); + return true; + case 3023: + cacheIndex = 951; + reference = new EpsgCoordinateReferenceRecord(3023, (EpsgCoordinateSystemKind)2, 951); + return true; + case 3024: + cacheIndex = 952; + reference = new EpsgCoordinateReferenceRecord(3024, (EpsgCoordinateSystemKind)2, 952); + return true; + case 3025: + cacheIndex = 953; + reference = new EpsgCoordinateReferenceRecord(3025, (EpsgCoordinateSystemKind)2, 953); + return true; + case 3026: + cacheIndex = 954; + reference = new EpsgCoordinateReferenceRecord(3026, (EpsgCoordinateSystemKind)2, 954); + return true; + case 3027: + cacheIndex = 955; + reference = new EpsgCoordinateReferenceRecord(3027, (EpsgCoordinateSystemKind)2, 955); + return true; + case 3028: + cacheIndex = 956; + reference = new EpsgCoordinateReferenceRecord(3028, (EpsgCoordinateSystemKind)2, 956); + return true; + case 3029: + cacheIndex = 957; + reference = new EpsgCoordinateReferenceRecord(3029, (EpsgCoordinateSystemKind)2, 957); + return true; + case 3030: + cacheIndex = 958; + reference = new EpsgCoordinateReferenceRecord(3030, (EpsgCoordinateSystemKind)2, 958); + return true; + case 3031: + cacheIndex = 959; + reference = new EpsgCoordinateReferenceRecord(3031, (EpsgCoordinateSystemKind)2, 959); + return true; + case 3032: + cacheIndex = 960; + reference = new EpsgCoordinateReferenceRecord(3032, (EpsgCoordinateSystemKind)2, 960); + return true; + case 3033: + cacheIndex = 961; + reference = new EpsgCoordinateReferenceRecord(3033, (EpsgCoordinateSystemKind)2, 961); + return true; + case 3034: + cacheIndex = 962; + reference = new EpsgCoordinateReferenceRecord(3034, (EpsgCoordinateSystemKind)2, 962); + return true; + case 3035: + cacheIndex = 963; + reference = new EpsgCoordinateReferenceRecord(3035, (EpsgCoordinateSystemKind)2, 963); + return true; + case 3036: + cacheIndex = 964; + reference = new EpsgCoordinateReferenceRecord(3036, (EpsgCoordinateSystemKind)2, 964); + return true; + case 3037: + cacheIndex = 965; + reference = new EpsgCoordinateReferenceRecord(3037, (EpsgCoordinateSystemKind)2, 965); + return true; + case 3040: + cacheIndex = 966; + reference = new EpsgCoordinateReferenceRecord(3040, (EpsgCoordinateSystemKind)2, 966); + return true; + case 3041: + cacheIndex = 967; + reference = new EpsgCoordinateReferenceRecord(3041, (EpsgCoordinateSystemKind)2, 967); + return true; + case 3042: + cacheIndex = 968; + reference = new EpsgCoordinateReferenceRecord(3042, (EpsgCoordinateSystemKind)2, 968); + return true; + case 3043: + cacheIndex = 969; + reference = new EpsgCoordinateReferenceRecord(3043, (EpsgCoordinateSystemKind)2, 969); + return true; + case 3044: + cacheIndex = 970; + reference = new EpsgCoordinateReferenceRecord(3044, (EpsgCoordinateSystemKind)2, 970); + return true; + case 3045: + cacheIndex = 971; + reference = new EpsgCoordinateReferenceRecord(3045, (EpsgCoordinateSystemKind)2, 971); + return true; + case 3046: + cacheIndex = 972; + reference = new EpsgCoordinateReferenceRecord(3046, (EpsgCoordinateSystemKind)2, 972); + return true; + case 3047: + cacheIndex = 973; + reference = new EpsgCoordinateReferenceRecord(3047, (EpsgCoordinateSystemKind)2, 973); + return true; + case 3048: + cacheIndex = 974; + reference = new EpsgCoordinateReferenceRecord(3048, (EpsgCoordinateSystemKind)2, 974); + return true; + case 3049: + cacheIndex = 975; + reference = new EpsgCoordinateReferenceRecord(3049, (EpsgCoordinateSystemKind)2, 975); + return true; + case 3052: + cacheIndex = 976; + reference = new EpsgCoordinateReferenceRecord(3052, (EpsgCoordinateSystemKind)2, 976); + return true; + case 3053: + cacheIndex = 977; + reference = new EpsgCoordinateReferenceRecord(3053, (EpsgCoordinateSystemKind)2, 977); + return true; + case 3054: + cacheIndex = 978; + reference = new EpsgCoordinateReferenceRecord(3054, (EpsgCoordinateSystemKind)2, 978); + return true; + case 3055: + cacheIndex = 979; + reference = new EpsgCoordinateReferenceRecord(3055, (EpsgCoordinateSystemKind)2, 979); + return true; + case 3056: + cacheIndex = 980; + reference = new EpsgCoordinateReferenceRecord(3056, (EpsgCoordinateSystemKind)2, 980); + return true; + case 3057: + cacheIndex = 981; + reference = new EpsgCoordinateReferenceRecord(3057, (EpsgCoordinateSystemKind)2, 981); + return true; + case 3058: + cacheIndex = 982; + reference = new EpsgCoordinateReferenceRecord(3058, (EpsgCoordinateSystemKind)2, 982); + return true; + case 3059: + cacheIndex = 983; + reference = new EpsgCoordinateReferenceRecord(3059, (EpsgCoordinateSystemKind)2, 983); + return true; + case 3060: + cacheIndex = 984; + reference = new EpsgCoordinateReferenceRecord(3060, (EpsgCoordinateSystemKind)2, 984); + return true; + case 3061: + cacheIndex = 985; + reference = new EpsgCoordinateReferenceRecord(3061, (EpsgCoordinateSystemKind)2, 985); + return true; + case 3062: + cacheIndex = 986; + reference = new EpsgCoordinateReferenceRecord(3062, (EpsgCoordinateSystemKind)2, 986); + return true; + case 3063: + cacheIndex = 987; + reference = new EpsgCoordinateReferenceRecord(3063, (EpsgCoordinateSystemKind)2, 987); + return true; + case 3064: + cacheIndex = 988; + reference = new EpsgCoordinateReferenceRecord(3064, (EpsgCoordinateSystemKind)2, 988); + return true; + case 3065: + cacheIndex = 989; + reference = new EpsgCoordinateReferenceRecord(3065, (EpsgCoordinateSystemKind)2, 989); + return true; + case 3066: + cacheIndex = 990; + reference = new EpsgCoordinateReferenceRecord(3066, (EpsgCoordinateSystemKind)2, 990); + return true; + case 3067: + cacheIndex = 991; + reference = new EpsgCoordinateReferenceRecord(3067, (EpsgCoordinateSystemKind)2, 991); + return true; + case 3068: + cacheIndex = 992; + reference = new EpsgCoordinateReferenceRecord(3068, (EpsgCoordinateSystemKind)2, 992); + return true; + case 3069: + cacheIndex = 993; + reference = new EpsgCoordinateReferenceRecord(3069, (EpsgCoordinateSystemKind)2, 993); + return true; + case 3070: + cacheIndex = 994; + reference = new EpsgCoordinateReferenceRecord(3070, (EpsgCoordinateSystemKind)2, 994); + return true; + case 3071: + cacheIndex = 995; + reference = new EpsgCoordinateReferenceRecord(3071, (EpsgCoordinateSystemKind)2, 995); + return true; + case 3072: + cacheIndex = 996; + reference = new EpsgCoordinateReferenceRecord(3072, (EpsgCoordinateSystemKind)2, 996); + return true; + case 3074: + cacheIndex = 997; + reference = new EpsgCoordinateReferenceRecord(3074, (EpsgCoordinateSystemKind)2, 997); + return true; + case 3075: + cacheIndex = 998; + reference = new EpsgCoordinateReferenceRecord(3075, (EpsgCoordinateSystemKind)2, 998); + return true; + case 3077: + cacheIndex = 999; + reference = new EpsgCoordinateReferenceRecord(3077, (EpsgCoordinateSystemKind)2, 999); + return true; + case 3078: + cacheIndex = 1000; + reference = new EpsgCoordinateReferenceRecord(3078, (EpsgCoordinateSystemKind)2, 1000); + return true; + case 3079: + cacheIndex = 1001; + reference = new EpsgCoordinateReferenceRecord(3079, (EpsgCoordinateSystemKind)2, 1001); + return true; + case 3080: + cacheIndex = 1002; + reference = new EpsgCoordinateReferenceRecord(3080, (EpsgCoordinateSystemKind)2, 1002); + return true; + case 3081: + cacheIndex = 1003; + reference = new EpsgCoordinateReferenceRecord(3081, (EpsgCoordinateSystemKind)2, 1003); + return true; + case 3082: + cacheIndex = 1004; + reference = new EpsgCoordinateReferenceRecord(3082, (EpsgCoordinateSystemKind)2, 1004); + return true; + case 3083: + cacheIndex = 1005; + reference = new EpsgCoordinateReferenceRecord(3083, (EpsgCoordinateSystemKind)2, 1005); + return true; + case 3084: + cacheIndex = 1006; + reference = new EpsgCoordinateReferenceRecord(3084, (EpsgCoordinateSystemKind)2, 1006); + return true; + case 3085: + cacheIndex = 1007; + reference = new EpsgCoordinateReferenceRecord(3085, (EpsgCoordinateSystemKind)2, 1007); + return true; + case 3086: + cacheIndex = 1008; + reference = new EpsgCoordinateReferenceRecord(3086, (EpsgCoordinateSystemKind)2, 1008); + return true; + case 3087: + cacheIndex = 1009; + reference = new EpsgCoordinateReferenceRecord(3087, (EpsgCoordinateSystemKind)2, 1009); + return true; + case 3088: + cacheIndex = 1010; + reference = new EpsgCoordinateReferenceRecord(3088, (EpsgCoordinateSystemKind)2, 1010); + return true; + case 3089: + cacheIndex = 1011; + reference = new EpsgCoordinateReferenceRecord(3089, (EpsgCoordinateSystemKind)2, 1011); + return true; + case 3090: + cacheIndex = 1012; + reference = new EpsgCoordinateReferenceRecord(3090, (EpsgCoordinateSystemKind)2, 1012); + return true; + case 3091: + cacheIndex = 1013; + reference = new EpsgCoordinateReferenceRecord(3091, (EpsgCoordinateSystemKind)2, 1013); + return true; + case 3092: + cacheIndex = 1014; + reference = new EpsgCoordinateReferenceRecord(3092, (EpsgCoordinateSystemKind)2, 1014); + return true; + case 3093: + cacheIndex = 1015; + reference = new EpsgCoordinateReferenceRecord(3093, (EpsgCoordinateSystemKind)2, 1015); + return true; + case 3094: + cacheIndex = 1016; + reference = new EpsgCoordinateReferenceRecord(3094, (EpsgCoordinateSystemKind)2, 1016); + return true; + case 3095: + cacheIndex = 1017; + reference = new EpsgCoordinateReferenceRecord(3095, (EpsgCoordinateSystemKind)2, 1017); + return true; + case 3096: + cacheIndex = 1018; + reference = new EpsgCoordinateReferenceRecord(3096, (EpsgCoordinateSystemKind)2, 1018); + return true; + case 3097: + cacheIndex = 1019; + reference = new EpsgCoordinateReferenceRecord(3097, (EpsgCoordinateSystemKind)2, 1019); + return true; + case 3098: + cacheIndex = 1020; + reference = new EpsgCoordinateReferenceRecord(3098, (EpsgCoordinateSystemKind)2, 1020); + return true; + case 3099: + cacheIndex = 1021; + reference = new EpsgCoordinateReferenceRecord(3099, (EpsgCoordinateSystemKind)2, 1021); + return true; + case 3100: + cacheIndex = 1022; + reference = new EpsgCoordinateReferenceRecord(3100, (EpsgCoordinateSystemKind)2, 1022); + return true; + case 3101: + cacheIndex = 1023; + reference = new EpsgCoordinateReferenceRecord(3101, (EpsgCoordinateSystemKind)2, 1023); + return true; + case 3102: + cacheIndex = 1024; + reference = new EpsgCoordinateReferenceRecord(3102, (EpsgCoordinateSystemKind)2, 1024); + return true; + case 3106: + cacheIndex = 1025; + reference = new EpsgCoordinateReferenceRecord(3106, (EpsgCoordinateSystemKind)2, 1025); + return true; + case 3107: + cacheIndex = 1026; + reference = new EpsgCoordinateReferenceRecord(3107, (EpsgCoordinateSystemKind)2, 1026); + return true; + case 3108: + cacheIndex = 1027; + reference = new EpsgCoordinateReferenceRecord(3108, (EpsgCoordinateSystemKind)2, 1027); + return true; + case 3109: + cacheIndex = 1028; + reference = new EpsgCoordinateReferenceRecord(3109, (EpsgCoordinateSystemKind)2, 1028); + return true; + case 3110: + cacheIndex = 1029; + reference = new EpsgCoordinateReferenceRecord(3110, (EpsgCoordinateSystemKind)2, 1029); + return true; + case 3111: + cacheIndex = 1030; + reference = new EpsgCoordinateReferenceRecord(3111, (EpsgCoordinateSystemKind)2, 1030); + return true; + case 3112: + cacheIndex = 1031; + reference = new EpsgCoordinateReferenceRecord(3112, (EpsgCoordinateSystemKind)2, 1031); + return true; + case 3113: + cacheIndex = 1032; + reference = new EpsgCoordinateReferenceRecord(3113, (EpsgCoordinateSystemKind)2, 1032); + return true; + case 3114: + cacheIndex = 1033; + reference = new EpsgCoordinateReferenceRecord(3114, (EpsgCoordinateSystemKind)2, 1033); + return true; + case 3115: + cacheIndex = 1034; + reference = new EpsgCoordinateReferenceRecord(3115, (EpsgCoordinateSystemKind)2, 1034); + return true; + case 3116: + cacheIndex = 1035; + reference = new EpsgCoordinateReferenceRecord(3116, (EpsgCoordinateSystemKind)2, 1035); + return true; + case 3117: + cacheIndex = 1036; + reference = new EpsgCoordinateReferenceRecord(3117, (EpsgCoordinateSystemKind)2, 1036); + return true; + case 3118: + cacheIndex = 1037; + reference = new EpsgCoordinateReferenceRecord(3118, (EpsgCoordinateSystemKind)2, 1037); + return true; + case 3119: + cacheIndex = 1038; + reference = new EpsgCoordinateReferenceRecord(3119, (EpsgCoordinateSystemKind)2, 1038); + return true; + case 3120: + cacheIndex = 1039; + reference = new EpsgCoordinateReferenceRecord(3120, (EpsgCoordinateSystemKind)2, 1039); + return true; + case 3121: + cacheIndex = 1040; + reference = new EpsgCoordinateReferenceRecord(3121, (EpsgCoordinateSystemKind)2, 1040); + return true; + case 3122: + cacheIndex = 1041; + reference = new EpsgCoordinateReferenceRecord(3122, (EpsgCoordinateSystemKind)2, 1041); + return true; + case 3123: + cacheIndex = 1042; + reference = new EpsgCoordinateReferenceRecord(3123, (EpsgCoordinateSystemKind)2, 1042); + return true; + case 3124: + cacheIndex = 1043; + reference = new EpsgCoordinateReferenceRecord(3124, (EpsgCoordinateSystemKind)2, 1043); + return true; + case 3125: + cacheIndex = 1044; + reference = new EpsgCoordinateReferenceRecord(3125, (EpsgCoordinateSystemKind)2, 1044); + return true; + case 3126: + cacheIndex = 1045; + reference = new EpsgCoordinateReferenceRecord(3126, (EpsgCoordinateSystemKind)2, 1045); + return true; + case 3127: + cacheIndex = 1046; + reference = new EpsgCoordinateReferenceRecord(3127, (EpsgCoordinateSystemKind)2, 1046); + return true; + case 3128: + cacheIndex = 1047; + reference = new EpsgCoordinateReferenceRecord(3128, (EpsgCoordinateSystemKind)2, 1047); + return true; + case 3129: + cacheIndex = 1048; + reference = new EpsgCoordinateReferenceRecord(3129, (EpsgCoordinateSystemKind)2, 1048); + return true; + case 3130: + cacheIndex = 1049; + reference = new EpsgCoordinateReferenceRecord(3130, (EpsgCoordinateSystemKind)2, 1049); + return true; + case 3131: + cacheIndex = 1050; + reference = new EpsgCoordinateReferenceRecord(3131, (EpsgCoordinateSystemKind)2, 1050); + return true; + case 3132: + cacheIndex = 1051; + reference = new EpsgCoordinateReferenceRecord(3132, (EpsgCoordinateSystemKind)2, 1051); + return true; + case 3133: + cacheIndex = 1052; + reference = new EpsgCoordinateReferenceRecord(3133, (EpsgCoordinateSystemKind)2, 1052); + return true; + case 3134: + cacheIndex = 1053; + reference = new EpsgCoordinateReferenceRecord(3134, (EpsgCoordinateSystemKind)2, 1053); + return true; + case 3135: + cacheIndex = 1054; + reference = new EpsgCoordinateReferenceRecord(3135, (EpsgCoordinateSystemKind)2, 1054); + return true; + case 3136: + cacheIndex = 1055; + reference = new EpsgCoordinateReferenceRecord(3136, (EpsgCoordinateSystemKind)2, 1055); + return true; + case 3137: + cacheIndex = 1056; + reference = new EpsgCoordinateReferenceRecord(3137, (EpsgCoordinateSystemKind)2, 1056); + return true; + case 3138: + cacheIndex = 1057; + reference = new EpsgCoordinateReferenceRecord(3138, (EpsgCoordinateSystemKind)2, 1057); + return true; + case 3139: + cacheIndex = 1058; + reference = new EpsgCoordinateReferenceRecord(3139, (EpsgCoordinateSystemKind)2, 1058); + return true; + case 3140: + cacheIndex = 1059; + reference = new EpsgCoordinateReferenceRecord(3140, (EpsgCoordinateSystemKind)2, 1059); + return true; + case 3141: + cacheIndex = 1060; + reference = new EpsgCoordinateReferenceRecord(3141, (EpsgCoordinateSystemKind)2, 1060); + return true; + case 3142: + cacheIndex = 1061; + reference = new EpsgCoordinateReferenceRecord(3142, (EpsgCoordinateSystemKind)2, 1061); + return true; + case 3144: + cacheIndex = 1062; + reference = new EpsgCoordinateReferenceRecord(3144, (EpsgCoordinateSystemKind)2, 1062); + return true; + case 3145: + cacheIndex = 1063; + reference = new EpsgCoordinateReferenceRecord(3145, (EpsgCoordinateSystemKind)2, 1063); + return true; + case 3148: + cacheIndex = 1064; + reference = new EpsgCoordinateReferenceRecord(3148, (EpsgCoordinateSystemKind)2, 1064); + return true; + case 3149: + cacheIndex = 1065; + reference = new EpsgCoordinateReferenceRecord(3149, (EpsgCoordinateSystemKind)2, 1065); + return true; + case 3152: + cacheIndex = 1066; + reference = new EpsgCoordinateReferenceRecord(3152, (EpsgCoordinateSystemKind)2, 1066); + return true; + case 3153: + cacheIndex = 1067; + reference = new EpsgCoordinateReferenceRecord(3153, (EpsgCoordinateSystemKind)2, 1067); + return true; + case 3154: + cacheIndex = 1068; + reference = new EpsgCoordinateReferenceRecord(3154, (EpsgCoordinateSystemKind)2, 1068); + return true; + case 3155: + cacheIndex = 1069; + reference = new EpsgCoordinateReferenceRecord(3155, (EpsgCoordinateSystemKind)2, 1069); + return true; + case 3156: + cacheIndex = 1070; + reference = new EpsgCoordinateReferenceRecord(3156, (EpsgCoordinateSystemKind)2, 1070); + return true; + case 3157: + cacheIndex = 1071; + reference = new EpsgCoordinateReferenceRecord(3157, (EpsgCoordinateSystemKind)2, 1071); + return true; + case 3158: + cacheIndex = 1072; + reference = new EpsgCoordinateReferenceRecord(3158, (EpsgCoordinateSystemKind)2, 1072); + return true; + case 3159: + cacheIndex = 1073; + reference = new EpsgCoordinateReferenceRecord(3159, (EpsgCoordinateSystemKind)2, 1073); + return true; + case 3160: + cacheIndex = 1074; + reference = new EpsgCoordinateReferenceRecord(3160, (EpsgCoordinateSystemKind)2, 1074); + return true; + case 3161: + cacheIndex = 1075; + reference = new EpsgCoordinateReferenceRecord(3161, (EpsgCoordinateSystemKind)2, 1075); + return true; + case 3162: + cacheIndex = 1076; + reference = new EpsgCoordinateReferenceRecord(3162, (EpsgCoordinateSystemKind)2, 1076); + return true; + case 3163: + cacheIndex = 1077; + reference = new EpsgCoordinateReferenceRecord(3163, (EpsgCoordinateSystemKind)2, 1077); + return true; + case 3164: + cacheIndex = 1078; + reference = new EpsgCoordinateReferenceRecord(3164, (EpsgCoordinateSystemKind)2, 1078); + return true; + case 3165: + cacheIndex = 1079; + reference = new EpsgCoordinateReferenceRecord(3165, (EpsgCoordinateSystemKind)2, 1079); + return true; + case 3166: + cacheIndex = 1080; + reference = new EpsgCoordinateReferenceRecord(3166, (EpsgCoordinateSystemKind)2, 1080); + return true; + case 3167: + cacheIndex = 1081; + reference = new EpsgCoordinateReferenceRecord(3167, (EpsgCoordinateSystemKind)2, 1081); + return true; + case 3168: + cacheIndex = 1082; + reference = new EpsgCoordinateReferenceRecord(3168, (EpsgCoordinateSystemKind)2, 1082); + return true; + case 3169: + cacheIndex = 1083; + reference = new EpsgCoordinateReferenceRecord(3169, (EpsgCoordinateSystemKind)2, 1083); + return true; + case 3170: + cacheIndex = 1084; + reference = new EpsgCoordinateReferenceRecord(3170, (EpsgCoordinateSystemKind)2, 1084); + return true; + case 3171: + cacheIndex = 1085; + reference = new EpsgCoordinateReferenceRecord(3171, (EpsgCoordinateSystemKind)2, 1085); + return true; + case 3172: + cacheIndex = 1086; + reference = new EpsgCoordinateReferenceRecord(3172, (EpsgCoordinateSystemKind)2, 1086); + return true; + case 3173: + cacheIndex = 1087; + reference = new EpsgCoordinateReferenceRecord(3173, (EpsgCoordinateSystemKind)2, 1087); + return true; + case 3174: + cacheIndex = 1088; + reference = new EpsgCoordinateReferenceRecord(3174, (EpsgCoordinateSystemKind)2, 1088); + return true; + case 3175: + cacheIndex = 1089; + reference = new EpsgCoordinateReferenceRecord(3175, (EpsgCoordinateSystemKind)2, 1089); + return true; + case 3176: + cacheIndex = 1090; + reference = new EpsgCoordinateReferenceRecord(3176, (EpsgCoordinateSystemKind)2, 1090); + return true; + case 3177: + cacheIndex = 1091; + reference = new EpsgCoordinateReferenceRecord(3177, (EpsgCoordinateSystemKind)2, 1091); + return true; + case 3178: + cacheIndex = 1092; + reference = new EpsgCoordinateReferenceRecord(3178, (EpsgCoordinateSystemKind)2, 1092); + return true; + case 3179: + cacheIndex = 1093; + reference = new EpsgCoordinateReferenceRecord(3179, (EpsgCoordinateSystemKind)2, 1093); + return true; + case 3180: + cacheIndex = 1094; + reference = new EpsgCoordinateReferenceRecord(3180, (EpsgCoordinateSystemKind)2, 1094); + return true; + case 3181: + cacheIndex = 1095; + reference = new EpsgCoordinateReferenceRecord(3181, (EpsgCoordinateSystemKind)2, 1095); + return true; + case 3182: + cacheIndex = 1096; + reference = new EpsgCoordinateReferenceRecord(3182, (EpsgCoordinateSystemKind)2, 1096); + return true; + case 3183: + cacheIndex = 1097; + reference = new EpsgCoordinateReferenceRecord(3183, (EpsgCoordinateSystemKind)2, 1097); + return true; + case 3184: + cacheIndex = 1098; + reference = new EpsgCoordinateReferenceRecord(3184, (EpsgCoordinateSystemKind)2, 1098); + return true; + case 3185: + cacheIndex = 1099; + reference = new EpsgCoordinateReferenceRecord(3185, (EpsgCoordinateSystemKind)2, 1099); + return true; + case 3186: + cacheIndex = 1100; + reference = new EpsgCoordinateReferenceRecord(3186, (EpsgCoordinateSystemKind)2, 1100); + return true; + case 3187: + cacheIndex = 1101; + reference = new EpsgCoordinateReferenceRecord(3187, (EpsgCoordinateSystemKind)2, 1101); + return true; + case 3188: + cacheIndex = 1102; + reference = new EpsgCoordinateReferenceRecord(3188, (EpsgCoordinateSystemKind)2, 1102); + return true; + case 3189: + cacheIndex = 1103; + reference = new EpsgCoordinateReferenceRecord(3189, (EpsgCoordinateSystemKind)2, 1103); + return true; + case 3190: + cacheIndex = 1104; + reference = new EpsgCoordinateReferenceRecord(3190, (EpsgCoordinateSystemKind)2, 1104); + return true; + case 3191: + cacheIndex = 1105; + reference = new EpsgCoordinateReferenceRecord(3191, (EpsgCoordinateSystemKind)2, 1105); + return true; + case 3192: + cacheIndex = 1106; + reference = new EpsgCoordinateReferenceRecord(3192, (EpsgCoordinateSystemKind)2, 1106); + return true; + case 3193: + cacheIndex = 1107; + reference = new EpsgCoordinateReferenceRecord(3193, (EpsgCoordinateSystemKind)2, 1107); + return true; + case 3194: + cacheIndex = 1108; + reference = new EpsgCoordinateReferenceRecord(3194, (EpsgCoordinateSystemKind)2, 1108); + return true; + case 3195: + cacheIndex = 1109; + reference = new EpsgCoordinateReferenceRecord(3195, (EpsgCoordinateSystemKind)2, 1109); + return true; + case 3196: + cacheIndex = 1110; + reference = new EpsgCoordinateReferenceRecord(3196, (EpsgCoordinateSystemKind)2, 1110); + return true; + case 3197: + cacheIndex = 1111; + reference = new EpsgCoordinateReferenceRecord(3197, (EpsgCoordinateSystemKind)2, 1111); + return true; + case 3198: + cacheIndex = 1112; + reference = new EpsgCoordinateReferenceRecord(3198, (EpsgCoordinateSystemKind)2, 1112); + return true; + case 3199: + cacheIndex = 1113; + reference = new EpsgCoordinateReferenceRecord(3199, (EpsgCoordinateSystemKind)2, 1113); + return true; + case 3200: + cacheIndex = 1114; + reference = new EpsgCoordinateReferenceRecord(3200, (EpsgCoordinateSystemKind)2, 1114); + return true; + case 3201: + cacheIndex = 1115; + reference = new EpsgCoordinateReferenceRecord(3201, (EpsgCoordinateSystemKind)2, 1115); + return true; + case 3202: + cacheIndex = 1116; + reference = new EpsgCoordinateReferenceRecord(3202, (EpsgCoordinateSystemKind)2, 1116); + return true; + case 3203: + cacheIndex = 1117; + reference = new EpsgCoordinateReferenceRecord(3203, (EpsgCoordinateSystemKind)2, 1117); + return true; + case 3204: + cacheIndex = 1118; + reference = new EpsgCoordinateReferenceRecord(3204, (EpsgCoordinateSystemKind)2, 1118); + return true; + case 3205: + cacheIndex = 1119; + reference = new EpsgCoordinateReferenceRecord(3205, (EpsgCoordinateSystemKind)2, 1119); + return true; + case 3206: + cacheIndex = 1120; + reference = new EpsgCoordinateReferenceRecord(3206, (EpsgCoordinateSystemKind)2, 1120); + return true; + case 3207: + cacheIndex = 1121; + reference = new EpsgCoordinateReferenceRecord(3207, (EpsgCoordinateSystemKind)2, 1121); + return true; + case 3208: + cacheIndex = 1122; + reference = new EpsgCoordinateReferenceRecord(3208, (EpsgCoordinateSystemKind)2, 1122); + return true; + case 3209: + cacheIndex = 1123; + reference = new EpsgCoordinateReferenceRecord(3209, (EpsgCoordinateSystemKind)2, 1123); + return true; + case 3210: + cacheIndex = 1124; + reference = new EpsgCoordinateReferenceRecord(3210, (EpsgCoordinateSystemKind)2, 1124); + return true; + case 3211: + cacheIndex = 1125; + reference = new EpsgCoordinateReferenceRecord(3211, (EpsgCoordinateSystemKind)2, 1125); + return true; + case 3212: + cacheIndex = 1126; + reference = new EpsgCoordinateReferenceRecord(3212, (EpsgCoordinateSystemKind)2, 1126); + return true; + case 3213: + cacheIndex = 1127; + reference = new EpsgCoordinateReferenceRecord(3213, (EpsgCoordinateSystemKind)2, 1127); + return true; + case 3214: + cacheIndex = 1128; + reference = new EpsgCoordinateReferenceRecord(3214, (EpsgCoordinateSystemKind)2, 1128); + return true; + case 3215: + cacheIndex = 1129; + reference = new EpsgCoordinateReferenceRecord(3215, (EpsgCoordinateSystemKind)2, 1129); + return true; + case 3216: + cacheIndex = 1130; + reference = new EpsgCoordinateReferenceRecord(3216, (EpsgCoordinateSystemKind)2, 1130); + return true; + case 3217: + cacheIndex = 1131; + reference = new EpsgCoordinateReferenceRecord(3217, (EpsgCoordinateSystemKind)2, 1131); + return true; + case 3218: + cacheIndex = 1132; + reference = new EpsgCoordinateReferenceRecord(3218, (EpsgCoordinateSystemKind)2, 1132); + return true; + case 3219: + cacheIndex = 1133; + reference = new EpsgCoordinateReferenceRecord(3219, (EpsgCoordinateSystemKind)2, 1133); + return true; + case 3220: + cacheIndex = 1134; + reference = new EpsgCoordinateReferenceRecord(3220, (EpsgCoordinateSystemKind)2, 1134); + return true; + case 3221: + cacheIndex = 1135; + reference = new EpsgCoordinateReferenceRecord(3221, (EpsgCoordinateSystemKind)2, 1135); + return true; + case 3222: + cacheIndex = 1136; + reference = new EpsgCoordinateReferenceRecord(3222, (EpsgCoordinateSystemKind)2, 1136); + return true; + case 3223: + cacheIndex = 1137; + reference = new EpsgCoordinateReferenceRecord(3223, (EpsgCoordinateSystemKind)2, 1137); + return true; + case 3224: + cacheIndex = 1138; + reference = new EpsgCoordinateReferenceRecord(3224, (EpsgCoordinateSystemKind)2, 1138); + return true; + case 3225: + cacheIndex = 1139; + reference = new EpsgCoordinateReferenceRecord(3225, (EpsgCoordinateSystemKind)2, 1139); + return true; + case 3226: + cacheIndex = 1140; + reference = new EpsgCoordinateReferenceRecord(3226, (EpsgCoordinateSystemKind)2, 1140); + return true; + case 3227: + cacheIndex = 1141; + reference = new EpsgCoordinateReferenceRecord(3227, (EpsgCoordinateSystemKind)2, 1141); + return true; + case 3228: + cacheIndex = 1142; + reference = new EpsgCoordinateReferenceRecord(3228, (EpsgCoordinateSystemKind)2, 1142); + return true; + case 3229: + cacheIndex = 1143; + reference = new EpsgCoordinateReferenceRecord(3229, (EpsgCoordinateSystemKind)2, 1143); + return true; + case 3230: + cacheIndex = 1144; + reference = new EpsgCoordinateReferenceRecord(3230, (EpsgCoordinateSystemKind)2, 1144); + return true; + case 3231: + cacheIndex = 1145; + reference = new EpsgCoordinateReferenceRecord(3231, (EpsgCoordinateSystemKind)2, 1145); + return true; + case 3232: + cacheIndex = 1146; + reference = new EpsgCoordinateReferenceRecord(3232, (EpsgCoordinateSystemKind)2, 1146); + return true; + case 3233: + cacheIndex = 1147; + reference = new EpsgCoordinateReferenceRecord(3233, (EpsgCoordinateSystemKind)2, 1147); + return true; + case 3234: + cacheIndex = 1148; + reference = new EpsgCoordinateReferenceRecord(3234, (EpsgCoordinateSystemKind)2, 1148); + return true; + case 3235: + cacheIndex = 1149; + reference = new EpsgCoordinateReferenceRecord(3235, (EpsgCoordinateSystemKind)2, 1149); + return true; + case 3236: + cacheIndex = 1150; + reference = new EpsgCoordinateReferenceRecord(3236, (EpsgCoordinateSystemKind)2, 1150); + return true; + case 3237: + cacheIndex = 1151; + reference = new EpsgCoordinateReferenceRecord(3237, (EpsgCoordinateSystemKind)2, 1151); + return true; + case 3238: + cacheIndex = 1152; + reference = new EpsgCoordinateReferenceRecord(3238, (EpsgCoordinateSystemKind)2, 1152); + return true; + case 3239: + cacheIndex = 1153; + reference = new EpsgCoordinateReferenceRecord(3239, (EpsgCoordinateSystemKind)2, 1153); + return true; + case 3240: + cacheIndex = 1154; + reference = new EpsgCoordinateReferenceRecord(3240, (EpsgCoordinateSystemKind)2, 1154); + return true; + case 3241: + cacheIndex = 1155; + reference = new EpsgCoordinateReferenceRecord(3241, (EpsgCoordinateSystemKind)2, 1155); + return true; + case 3242: + cacheIndex = 1156; + reference = new EpsgCoordinateReferenceRecord(3242, (EpsgCoordinateSystemKind)2, 1156); + return true; + case 3243: + cacheIndex = 1157; + reference = new EpsgCoordinateReferenceRecord(3243, (EpsgCoordinateSystemKind)2, 1157); + return true; + case 3244: + cacheIndex = 1158; + reference = new EpsgCoordinateReferenceRecord(3244, (EpsgCoordinateSystemKind)2, 1158); + return true; + case 3245: + cacheIndex = 1159; + reference = new EpsgCoordinateReferenceRecord(3245, (EpsgCoordinateSystemKind)2, 1159); + return true; + case 3246: + cacheIndex = 1160; + reference = new EpsgCoordinateReferenceRecord(3246, (EpsgCoordinateSystemKind)2, 1160); + return true; + case 3247: + cacheIndex = 1161; + reference = new EpsgCoordinateReferenceRecord(3247, (EpsgCoordinateSystemKind)2, 1161); + return true; + case 3248: + cacheIndex = 1162; + reference = new EpsgCoordinateReferenceRecord(3248, (EpsgCoordinateSystemKind)2, 1162); + return true; + case 3249: + cacheIndex = 1163; + reference = new EpsgCoordinateReferenceRecord(3249, (EpsgCoordinateSystemKind)2, 1163); + return true; + case 3250: + cacheIndex = 1164; + reference = new EpsgCoordinateReferenceRecord(3250, (EpsgCoordinateSystemKind)2, 1164); + return true; + case 3251: + cacheIndex = 1165; + reference = new EpsgCoordinateReferenceRecord(3251, (EpsgCoordinateSystemKind)2, 1165); + return true; + case 3252: + cacheIndex = 1166; + reference = new EpsgCoordinateReferenceRecord(3252, (EpsgCoordinateSystemKind)2, 1166); + return true; + case 3253: + cacheIndex = 1167; + reference = new EpsgCoordinateReferenceRecord(3253, (EpsgCoordinateSystemKind)2, 1167); + return true; + case 3254: + cacheIndex = 1168; + reference = new EpsgCoordinateReferenceRecord(3254, (EpsgCoordinateSystemKind)2, 1168); + return true; + case 3255: + cacheIndex = 1169; + reference = new EpsgCoordinateReferenceRecord(3255, (EpsgCoordinateSystemKind)2, 1169); + return true; + case 3256: + cacheIndex = 1170; + reference = new EpsgCoordinateReferenceRecord(3256, (EpsgCoordinateSystemKind)2, 1170); + return true; + case 3257: + cacheIndex = 1171; + reference = new EpsgCoordinateReferenceRecord(3257, (EpsgCoordinateSystemKind)2, 1171); + return true; + case 3258: + cacheIndex = 1172; + reference = new EpsgCoordinateReferenceRecord(3258, (EpsgCoordinateSystemKind)2, 1172); + return true; + case 3259: + cacheIndex = 1173; + reference = new EpsgCoordinateReferenceRecord(3259, (EpsgCoordinateSystemKind)2, 1173); + return true; + case 3260: + cacheIndex = 1174; + reference = new EpsgCoordinateReferenceRecord(3260, (EpsgCoordinateSystemKind)2, 1174); + return true; + case 3261: + cacheIndex = 1175; + reference = new EpsgCoordinateReferenceRecord(3261, (EpsgCoordinateSystemKind)2, 1175); + return true; + case 3262: + cacheIndex = 1176; + reference = new EpsgCoordinateReferenceRecord(3262, (EpsgCoordinateSystemKind)2, 1176); + return true; + case 3263: + cacheIndex = 1177; + reference = new EpsgCoordinateReferenceRecord(3263, (EpsgCoordinateSystemKind)2, 1177); + return true; + case 3264: + cacheIndex = 1178; + reference = new EpsgCoordinateReferenceRecord(3264, (EpsgCoordinateSystemKind)2, 1178); + return true; + case 3265: + cacheIndex = 1179; + reference = new EpsgCoordinateReferenceRecord(3265, (EpsgCoordinateSystemKind)2, 1179); + return true; + case 3266: + cacheIndex = 1180; + reference = new EpsgCoordinateReferenceRecord(3266, (EpsgCoordinateSystemKind)2, 1180); + return true; + case 3267: + cacheIndex = 1181; + reference = new EpsgCoordinateReferenceRecord(3267, (EpsgCoordinateSystemKind)2, 1181); + return true; + case 3268: + cacheIndex = 1182; + reference = new EpsgCoordinateReferenceRecord(3268, (EpsgCoordinateSystemKind)2, 1182); + return true; + case 3269: + cacheIndex = 1183; + reference = new EpsgCoordinateReferenceRecord(3269, (EpsgCoordinateSystemKind)2, 1183); + return true; + case 3270: + cacheIndex = 1184; + reference = new EpsgCoordinateReferenceRecord(3270, (EpsgCoordinateSystemKind)2, 1184); + return true; + case 3271: + cacheIndex = 1185; + reference = new EpsgCoordinateReferenceRecord(3271, (EpsgCoordinateSystemKind)2, 1185); + return true; + case 3272: + cacheIndex = 1186; + reference = new EpsgCoordinateReferenceRecord(3272, (EpsgCoordinateSystemKind)2, 1186); + return true; + case 3273: + cacheIndex = 1187; + reference = new EpsgCoordinateReferenceRecord(3273, (EpsgCoordinateSystemKind)2, 1187); + return true; + case 3274: + cacheIndex = 1188; + reference = new EpsgCoordinateReferenceRecord(3274, (EpsgCoordinateSystemKind)2, 1188); + return true; + case 3275: + cacheIndex = 1189; + reference = new EpsgCoordinateReferenceRecord(3275, (EpsgCoordinateSystemKind)2, 1189); + return true; + case 3276: + cacheIndex = 1190; + reference = new EpsgCoordinateReferenceRecord(3276, (EpsgCoordinateSystemKind)2, 1190); + return true; + case 3277: + cacheIndex = 1191; + reference = new EpsgCoordinateReferenceRecord(3277, (EpsgCoordinateSystemKind)2, 1191); + return true; + case 3278: + cacheIndex = 1192; + reference = new EpsgCoordinateReferenceRecord(3278, (EpsgCoordinateSystemKind)2, 1192); + return true; + case 3279: + cacheIndex = 1193; + reference = new EpsgCoordinateReferenceRecord(3279, (EpsgCoordinateSystemKind)2, 1193); + return true; + case 3280: + cacheIndex = 1194; + reference = new EpsgCoordinateReferenceRecord(3280, (EpsgCoordinateSystemKind)2, 1194); + return true; + case 3281: + cacheIndex = 1195; + reference = new EpsgCoordinateReferenceRecord(3281, (EpsgCoordinateSystemKind)2, 1195); + return true; + case 3282: + cacheIndex = 1196; + reference = new EpsgCoordinateReferenceRecord(3282, (EpsgCoordinateSystemKind)2, 1196); + return true; + case 3283: + cacheIndex = 1197; + reference = new EpsgCoordinateReferenceRecord(3283, (EpsgCoordinateSystemKind)2, 1197); + return true; + case 3284: + cacheIndex = 1198; + reference = new EpsgCoordinateReferenceRecord(3284, (EpsgCoordinateSystemKind)2, 1198); + return true; + case 3285: + cacheIndex = 1199; + reference = new EpsgCoordinateReferenceRecord(3285, (EpsgCoordinateSystemKind)2, 1199); + return true; + case 3286: + cacheIndex = 1200; + reference = new EpsgCoordinateReferenceRecord(3286, (EpsgCoordinateSystemKind)2, 1200); + return true; + case 3287: + cacheIndex = 1201; + reference = new EpsgCoordinateReferenceRecord(3287, (EpsgCoordinateSystemKind)2, 1201); + return true; + case 3288: + cacheIndex = 1202; + reference = new EpsgCoordinateReferenceRecord(3288, (EpsgCoordinateSystemKind)2, 1202); + return true; + case 3289: + cacheIndex = 1203; + reference = new EpsgCoordinateReferenceRecord(3289, (EpsgCoordinateSystemKind)2, 1203); + return true; + case 3290: + cacheIndex = 1204; + reference = new EpsgCoordinateReferenceRecord(3290, (EpsgCoordinateSystemKind)2, 1204); + return true; + case 3291: + cacheIndex = 1205; + reference = new EpsgCoordinateReferenceRecord(3291, (EpsgCoordinateSystemKind)2, 1205); + return true; + case 3292: + cacheIndex = 1206; + reference = new EpsgCoordinateReferenceRecord(3292, (EpsgCoordinateSystemKind)2, 1206); + return true; + case 3293: + cacheIndex = 1207; + reference = new EpsgCoordinateReferenceRecord(3293, (EpsgCoordinateSystemKind)2, 1207); + return true; + case 3294: + cacheIndex = 1208; + reference = new EpsgCoordinateReferenceRecord(3294, (EpsgCoordinateSystemKind)2, 1208); + return true; + case 3295: + cacheIndex = 1209; + reference = new EpsgCoordinateReferenceRecord(3295, (EpsgCoordinateSystemKind)2, 1209); + return true; + case 3296: + cacheIndex = 1210; + reference = new EpsgCoordinateReferenceRecord(3296, (EpsgCoordinateSystemKind)2, 1210); + return true; + case 3297: + cacheIndex = 1211; + reference = new EpsgCoordinateReferenceRecord(3297, (EpsgCoordinateSystemKind)2, 1211); + return true; + case 3298: + cacheIndex = 1212; + reference = new EpsgCoordinateReferenceRecord(3298, (EpsgCoordinateSystemKind)2, 1212); + return true; + case 3299: + cacheIndex = 1213; + reference = new EpsgCoordinateReferenceRecord(3299, (EpsgCoordinateSystemKind)2, 1213); + return true; + case 3300: + cacheIndex = 1214; + reference = new EpsgCoordinateReferenceRecord(3300, (EpsgCoordinateSystemKind)2, 1214); + return true; + case 3301: + cacheIndex = 1215; + reference = new EpsgCoordinateReferenceRecord(3301, (EpsgCoordinateSystemKind)2, 1215); + return true; + case 3302: + cacheIndex = 1216; + reference = new EpsgCoordinateReferenceRecord(3302, (EpsgCoordinateSystemKind)2, 1216); + return true; + case 3303: + cacheIndex = 1217; + reference = new EpsgCoordinateReferenceRecord(3303, (EpsgCoordinateSystemKind)2, 1217); + return true; + case 3304: + cacheIndex = 1218; + reference = new EpsgCoordinateReferenceRecord(3304, (EpsgCoordinateSystemKind)2, 1218); + return true; + case 3305: + cacheIndex = 1219; + reference = new EpsgCoordinateReferenceRecord(3305, (EpsgCoordinateSystemKind)2, 1219); + return true; + case 3306: + cacheIndex = 1220; + reference = new EpsgCoordinateReferenceRecord(3306, (EpsgCoordinateSystemKind)2, 1220); + return true; + case 3307: + cacheIndex = 1221; + reference = new EpsgCoordinateReferenceRecord(3307, (EpsgCoordinateSystemKind)2, 1221); + return true; + case 3308: + cacheIndex = 1222; + reference = new EpsgCoordinateReferenceRecord(3308, (EpsgCoordinateSystemKind)2, 1222); + return true; + case 3309: + cacheIndex = 1223; + reference = new EpsgCoordinateReferenceRecord(3309, (EpsgCoordinateSystemKind)2, 1223); + return true; + case 3310: + cacheIndex = 1224; + reference = new EpsgCoordinateReferenceRecord(3310, (EpsgCoordinateSystemKind)2, 1224); + return true; + case 3311: + cacheIndex = 1225; + reference = new EpsgCoordinateReferenceRecord(3311, (EpsgCoordinateSystemKind)2, 1225); + return true; + case 3312: + cacheIndex = 1226; + reference = new EpsgCoordinateReferenceRecord(3312, (EpsgCoordinateSystemKind)2, 1226); + return true; + case 3313: + cacheIndex = 1227; + reference = new EpsgCoordinateReferenceRecord(3313, (EpsgCoordinateSystemKind)2, 1227); + return true; + case 3316: + cacheIndex = 1228; + reference = new EpsgCoordinateReferenceRecord(3316, (EpsgCoordinateSystemKind)2, 1228); + return true; + case 3317: + cacheIndex = 1229; + reference = new EpsgCoordinateReferenceRecord(3317, (EpsgCoordinateSystemKind)2, 1229); + return true; + case 3318: + cacheIndex = 1230; + reference = new EpsgCoordinateReferenceRecord(3318, (EpsgCoordinateSystemKind)2, 1230); + return true; + case 3319: + cacheIndex = 1231; + reference = new EpsgCoordinateReferenceRecord(3319, (EpsgCoordinateSystemKind)2, 1231); + return true; + case 3320: + cacheIndex = 1232; + reference = new EpsgCoordinateReferenceRecord(3320, (EpsgCoordinateSystemKind)2, 1232); + return true; + case 3321: + cacheIndex = 1233; + reference = new EpsgCoordinateReferenceRecord(3321, (EpsgCoordinateSystemKind)2, 1233); + return true; + case 3322: + cacheIndex = 1234; + reference = new EpsgCoordinateReferenceRecord(3322, (EpsgCoordinateSystemKind)2, 1234); + return true; + case 3323: + cacheIndex = 1235; + reference = new EpsgCoordinateReferenceRecord(3323, (EpsgCoordinateSystemKind)2, 1235); + return true; + case 3324: + cacheIndex = 1236; + reference = new EpsgCoordinateReferenceRecord(3324, (EpsgCoordinateSystemKind)2, 1236); + return true; + case 3325: + cacheIndex = 1237; + reference = new EpsgCoordinateReferenceRecord(3325, (EpsgCoordinateSystemKind)2, 1237); + return true; + case 3326: + cacheIndex = 1238; + reference = new EpsgCoordinateReferenceRecord(3326, (EpsgCoordinateSystemKind)2, 1238); + return true; + case 3327: + cacheIndex = 1239; + reference = new EpsgCoordinateReferenceRecord(3327, (EpsgCoordinateSystemKind)2, 1239); + return true; + case 3328: + cacheIndex = 1240; + reference = new EpsgCoordinateReferenceRecord(3328, (EpsgCoordinateSystemKind)2, 1240); + return true; + case 3329: + cacheIndex = 1241; + reference = new EpsgCoordinateReferenceRecord(3329, (EpsgCoordinateSystemKind)2, 1241); + return true; + case 3330: + cacheIndex = 1242; + reference = new EpsgCoordinateReferenceRecord(3330, (EpsgCoordinateSystemKind)2, 1242); + return true; + case 3331: + cacheIndex = 1243; + reference = new EpsgCoordinateReferenceRecord(3331, (EpsgCoordinateSystemKind)2, 1243); + return true; + case 3332: + cacheIndex = 1244; + reference = new EpsgCoordinateReferenceRecord(3332, (EpsgCoordinateSystemKind)2, 1244); + return true; + case 3333: + cacheIndex = 1245; + reference = new EpsgCoordinateReferenceRecord(3333, (EpsgCoordinateSystemKind)2, 1245); + return true; + case 3334: + cacheIndex = 1246; + reference = new EpsgCoordinateReferenceRecord(3334, (EpsgCoordinateSystemKind)2, 1246); + return true; + case 3335: + cacheIndex = 1247; + reference = new EpsgCoordinateReferenceRecord(3335, (EpsgCoordinateSystemKind)2, 1247); + return true; + case 3336: + cacheIndex = 1248; + reference = new EpsgCoordinateReferenceRecord(3336, (EpsgCoordinateSystemKind)2, 1248); + return true; + case 3337: + cacheIndex = 1249; + reference = new EpsgCoordinateReferenceRecord(3337, (EpsgCoordinateSystemKind)2, 1249); + return true; + case 3338: + cacheIndex = 1250; + reference = new EpsgCoordinateReferenceRecord(3338, (EpsgCoordinateSystemKind)2, 1250); + return true; + case 3339: + cacheIndex = 1251; + reference = new EpsgCoordinateReferenceRecord(3339, (EpsgCoordinateSystemKind)2, 1251); + return true; + case 3340: + cacheIndex = 1252; + reference = new EpsgCoordinateReferenceRecord(3340, (EpsgCoordinateSystemKind)2, 1252); + return true; + case 3341: + cacheIndex = 1253; + reference = new EpsgCoordinateReferenceRecord(3341, (EpsgCoordinateSystemKind)2, 1253); + return true; + case 3342: + cacheIndex = 1254; + reference = new EpsgCoordinateReferenceRecord(3342, (EpsgCoordinateSystemKind)2, 1254); + return true; + case 3343: + cacheIndex = 1255; + reference = new EpsgCoordinateReferenceRecord(3343, (EpsgCoordinateSystemKind)2, 1255); + return true; + case 3344: + cacheIndex = 1256; + reference = new EpsgCoordinateReferenceRecord(3344, (EpsgCoordinateSystemKind)2, 1256); + return true; + case 3345: + cacheIndex = 1257; + reference = new EpsgCoordinateReferenceRecord(3345, (EpsgCoordinateSystemKind)2, 1257); + return true; + case 3346: + cacheIndex = 1258; + reference = new EpsgCoordinateReferenceRecord(3346, (EpsgCoordinateSystemKind)2, 1258); + return true; + case 3347: + cacheIndex = 1259; + reference = new EpsgCoordinateReferenceRecord(3347, (EpsgCoordinateSystemKind)2, 1259); + return true; + case 3348: + cacheIndex = 1260; + reference = new EpsgCoordinateReferenceRecord(3348, (EpsgCoordinateSystemKind)2, 1260); + return true; + case 3350: + cacheIndex = 1261; + reference = new EpsgCoordinateReferenceRecord(3350, (EpsgCoordinateSystemKind)2, 1261); + return true; + case 3351: + cacheIndex = 1262; + reference = new EpsgCoordinateReferenceRecord(3351, (EpsgCoordinateSystemKind)2, 1262); + return true; + case 3352: + cacheIndex = 1263; + reference = new EpsgCoordinateReferenceRecord(3352, (EpsgCoordinateSystemKind)2, 1263); + return true; + case 3353: + cacheIndex = 1264; + reference = new EpsgCoordinateReferenceRecord(3353, (EpsgCoordinateSystemKind)2, 1264); + return true; + case 3354: + cacheIndex = 1265; + reference = new EpsgCoordinateReferenceRecord(3354, (EpsgCoordinateSystemKind)2, 1265); + return true; + case 3355: + cacheIndex = 1266; + reference = new EpsgCoordinateReferenceRecord(3355, (EpsgCoordinateSystemKind)2, 1266); + return true; + case 3358: + cacheIndex = 1267; + reference = new EpsgCoordinateReferenceRecord(3358, (EpsgCoordinateSystemKind)2, 1267); + return true; + case 3360: + cacheIndex = 1268; + reference = new EpsgCoordinateReferenceRecord(3360, (EpsgCoordinateSystemKind)2, 1268); + return true; + case 3361: + cacheIndex = 1269; + reference = new EpsgCoordinateReferenceRecord(3361, (EpsgCoordinateSystemKind)2, 1269); + return true; + case 3362: + cacheIndex = 1270; + reference = new EpsgCoordinateReferenceRecord(3362, (EpsgCoordinateSystemKind)2, 1270); + return true; + case 3363: + cacheIndex = 1271; + reference = new EpsgCoordinateReferenceRecord(3363, (EpsgCoordinateSystemKind)2, 1271); + return true; + case 3364: + cacheIndex = 1272; + reference = new EpsgCoordinateReferenceRecord(3364, (EpsgCoordinateSystemKind)2, 1272); + return true; + case 3365: + cacheIndex = 1273; + reference = new EpsgCoordinateReferenceRecord(3365, (EpsgCoordinateSystemKind)2, 1273); + return true; + case 3367: + cacheIndex = 1274; + reference = new EpsgCoordinateReferenceRecord(3367, (EpsgCoordinateSystemKind)2, 1274); + return true; + case 3368: + cacheIndex = 1275; + reference = new EpsgCoordinateReferenceRecord(3368, (EpsgCoordinateSystemKind)2, 1275); + return true; + case 3369: + cacheIndex = 1276; + reference = new EpsgCoordinateReferenceRecord(3369, (EpsgCoordinateSystemKind)2, 1276); + return true; + case 3370: + cacheIndex = 1277; + reference = new EpsgCoordinateReferenceRecord(3370, (EpsgCoordinateSystemKind)2, 1277); + return true; + case 3371: + cacheIndex = 1278; + reference = new EpsgCoordinateReferenceRecord(3371, (EpsgCoordinateSystemKind)2, 1278); + return true; + case 3372: + cacheIndex = 1279; + reference = new EpsgCoordinateReferenceRecord(3372, (EpsgCoordinateSystemKind)2, 1279); + return true; + case 3373: + cacheIndex = 1280; + reference = new EpsgCoordinateReferenceRecord(3373, (EpsgCoordinateSystemKind)2, 1280); + return true; + case 3374: + cacheIndex = 1281; + reference = new EpsgCoordinateReferenceRecord(3374, (EpsgCoordinateSystemKind)2, 1281); + return true; + case 3375: + cacheIndex = 1282; + reference = new EpsgCoordinateReferenceRecord(3375, (EpsgCoordinateSystemKind)2, 1282); + return true; + case 3376: + cacheIndex = 1283; + reference = new EpsgCoordinateReferenceRecord(3376, (EpsgCoordinateSystemKind)2, 1283); + return true; + case 3377: + cacheIndex = 1284; + reference = new EpsgCoordinateReferenceRecord(3377, (EpsgCoordinateSystemKind)2, 1284); + return true; + case 3378: + cacheIndex = 1285; + reference = new EpsgCoordinateReferenceRecord(3378, (EpsgCoordinateSystemKind)2, 1285); + return true; + case 3379: + cacheIndex = 1286; + reference = new EpsgCoordinateReferenceRecord(3379, (EpsgCoordinateSystemKind)2, 1286); + return true; + case 3380: + cacheIndex = 1287; + reference = new EpsgCoordinateReferenceRecord(3380, (EpsgCoordinateSystemKind)2, 1287); + return true; + case 3381: + cacheIndex = 1288; + reference = new EpsgCoordinateReferenceRecord(3381, (EpsgCoordinateSystemKind)2, 1288); + return true; + case 3382: + cacheIndex = 1289; + reference = new EpsgCoordinateReferenceRecord(3382, (EpsgCoordinateSystemKind)2, 1289); + return true; + case 3383: + cacheIndex = 1290; + reference = new EpsgCoordinateReferenceRecord(3383, (EpsgCoordinateSystemKind)2, 1290); + return true; + case 3384: + cacheIndex = 1291; + reference = new EpsgCoordinateReferenceRecord(3384, (EpsgCoordinateSystemKind)2, 1291); + return true; + case 3385: + cacheIndex = 1292; + reference = new EpsgCoordinateReferenceRecord(3385, (EpsgCoordinateSystemKind)2, 1292); + return true; + case 3386: + cacheIndex = 1293; + reference = new EpsgCoordinateReferenceRecord(3386, (EpsgCoordinateSystemKind)2, 1293); + return true; + case 3387: + cacheIndex = 1294; + reference = new EpsgCoordinateReferenceRecord(3387, (EpsgCoordinateSystemKind)2, 1294); + return true; + case 3388: + cacheIndex = 1295; + reference = new EpsgCoordinateReferenceRecord(3388, (EpsgCoordinateSystemKind)2, 1295); + return true; + case 3389: + cacheIndex = 1296; + reference = new EpsgCoordinateReferenceRecord(3389, (EpsgCoordinateSystemKind)2, 1296); + return true; + case 3390: + cacheIndex = 1297; + reference = new EpsgCoordinateReferenceRecord(3390, (EpsgCoordinateSystemKind)2, 1297); + return true; + case 3391: + cacheIndex = 1298; + reference = new EpsgCoordinateReferenceRecord(3391, (EpsgCoordinateSystemKind)2, 1298); + return true; + case 3392: + cacheIndex = 1299; + reference = new EpsgCoordinateReferenceRecord(3392, (EpsgCoordinateSystemKind)2, 1299); + return true; + case 3393: + cacheIndex = 1300; + reference = new EpsgCoordinateReferenceRecord(3393, (EpsgCoordinateSystemKind)2, 1300); + return true; + case 3394: + cacheIndex = 1301; + reference = new EpsgCoordinateReferenceRecord(3394, (EpsgCoordinateSystemKind)2, 1301); + return true; + case 3395: + cacheIndex = 1302; + reference = new EpsgCoordinateReferenceRecord(3395, (EpsgCoordinateSystemKind)2, 1302); + return true; + case 3396: + cacheIndex = 1303; + reference = new EpsgCoordinateReferenceRecord(3396, (EpsgCoordinateSystemKind)2, 1303); + return true; + case 3397: + cacheIndex = 1304; + reference = new EpsgCoordinateReferenceRecord(3397, (EpsgCoordinateSystemKind)2, 1304); + return true; + case 3398: + cacheIndex = 1305; + reference = new EpsgCoordinateReferenceRecord(3398, (EpsgCoordinateSystemKind)2, 1305); + return true; + case 3399: + cacheIndex = 1306; + reference = new EpsgCoordinateReferenceRecord(3399, (EpsgCoordinateSystemKind)2, 1306); + return true; + case 3400: + cacheIndex = 1307; + reference = new EpsgCoordinateReferenceRecord(3400, (EpsgCoordinateSystemKind)2, 1307); + return true; + case 3401: + cacheIndex = 1308; + reference = new EpsgCoordinateReferenceRecord(3401, (EpsgCoordinateSystemKind)2, 1308); + return true; + case 3402: + cacheIndex = 1309; + reference = new EpsgCoordinateReferenceRecord(3402, (EpsgCoordinateSystemKind)2, 1309); + return true; + case 3403: + cacheIndex = 1310; + reference = new EpsgCoordinateReferenceRecord(3403, (EpsgCoordinateSystemKind)2, 1310); + return true; + case 3404: + cacheIndex = 1311; + reference = new EpsgCoordinateReferenceRecord(3404, (EpsgCoordinateSystemKind)2, 1311); + return true; + case 3405: + cacheIndex = 1312; + reference = new EpsgCoordinateReferenceRecord(3405, (EpsgCoordinateSystemKind)2, 1312); + return true; + case 3406: + cacheIndex = 1313; + reference = new EpsgCoordinateReferenceRecord(3406, (EpsgCoordinateSystemKind)2, 1313); + return true; + case 3407: + cacheIndex = 1314; + reference = new EpsgCoordinateReferenceRecord(3407, (EpsgCoordinateSystemKind)2, 1314); + return true; + case 3408: + cacheIndex = 1315; + reference = new EpsgCoordinateReferenceRecord(3408, (EpsgCoordinateSystemKind)2, 1315); + return true; + case 3409: + cacheIndex = 1316; + reference = new EpsgCoordinateReferenceRecord(3409, (EpsgCoordinateSystemKind)2, 1316); + return true; + case 3410: + cacheIndex = 1317; + reference = new EpsgCoordinateReferenceRecord(3410, (EpsgCoordinateSystemKind)2, 1317); + return true; + case 3411: + cacheIndex = 1318; + reference = new EpsgCoordinateReferenceRecord(3411, (EpsgCoordinateSystemKind)2, 1318); + return true; + case 3412: + cacheIndex = 1319; + reference = new EpsgCoordinateReferenceRecord(3412, (EpsgCoordinateSystemKind)2, 1319); + return true; + case 3413: + cacheIndex = 1320; + reference = new EpsgCoordinateReferenceRecord(3413, (EpsgCoordinateSystemKind)2, 1320); + return true; + case 3414: + cacheIndex = 1321; + reference = new EpsgCoordinateReferenceRecord(3414, (EpsgCoordinateSystemKind)2, 1321); + return true; + case 3415: + cacheIndex = 1322; + reference = new EpsgCoordinateReferenceRecord(3415, (EpsgCoordinateSystemKind)2, 1322); + return true; + case 3416: + cacheIndex = 1323; + reference = new EpsgCoordinateReferenceRecord(3416, (EpsgCoordinateSystemKind)2, 1323); + return true; + case 3417: + cacheIndex = 1324; + reference = new EpsgCoordinateReferenceRecord(3417, (EpsgCoordinateSystemKind)2, 1324); + return true; + case 3418: + cacheIndex = 1325; + reference = new EpsgCoordinateReferenceRecord(3418, (EpsgCoordinateSystemKind)2, 1325); + return true; + case 3419: + cacheIndex = 1326; + reference = new EpsgCoordinateReferenceRecord(3419, (EpsgCoordinateSystemKind)2, 1326); + return true; + case 3420: + cacheIndex = 1327; + reference = new EpsgCoordinateReferenceRecord(3420, (EpsgCoordinateSystemKind)2, 1327); + return true; + case 3421: + cacheIndex = 1328; + reference = new EpsgCoordinateReferenceRecord(3421, (EpsgCoordinateSystemKind)2, 1328); + return true; + case 3422: + cacheIndex = 1329; + reference = new EpsgCoordinateReferenceRecord(3422, (EpsgCoordinateSystemKind)2, 1329); + return true; + case 3423: + cacheIndex = 1330; + reference = new EpsgCoordinateReferenceRecord(3423, (EpsgCoordinateSystemKind)2, 1330); + return true; + case 3424: + cacheIndex = 1331; + reference = new EpsgCoordinateReferenceRecord(3424, (EpsgCoordinateSystemKind)2, 1331); + return true; + case 3425: + cacheIndex = 1332; + reference = new EpsgCoordinateReferenceRecord(3425, (EpsgCoordinateSystemKind)2, 1332); + return true; + case 3426: + cacheIndex = 1333; + reference = new EpsgCoordinateReferenceRecord(3426, (EpsgCoordinateSystemKind)2, 1333); + return true; + case 3427: + cacheIndex = 1334; + reference = new EpsgCoordinateReferenceRecord(3427, (EpsgCoordinateSystemKind)2, 1334); + return true; + case 3428: + cacheIndex = 1335; + reference = new EpsgCoordinateReferenceRecord(3428, (EpsgCoordinateSystemKind)2, 1335); + return true; + case 3429: + cacheIndex = 1336; + reference = new EpsgCoordinateReferenceRecord(3429, (EpsgCoordinateSystemKind)2, 1336); + return true; + case 3430: + cacheIndex = 1337; + reference = new EpsgCoordinateReferenceRecord(3430, (EpsgCoordinateSystemKind)2, 1337); + return true; + case 3431: + cacheIndex = 1338; + reference = new EpsgCoordinateReferenceRecord(3431, (EpsgCoordinateSystemKind)2, 1338); + return true; + case 3432: + cacheIndex = 1339; + reference = new EpsgCoordinateReferenceRecord(3432, (EpsgCoordinateSystemKind)2, 1339); + return true; + case 3433: + cacheIndex = 1340; + reference = new EpsgCoordinateReferenceRecord(3433, (EpsgCoordinateSystemKind)2, 1340); + return true; + case 3434: + cacheIndex = 1341; + reference = new EpsgCoordinateReferenceRecord(3434, (EpsgCoordinateSystemKind)2, 1341); + return true; + case 3435: + cacheIndex = 1342; + reference = new EpsgCoordinateReferenceRecord(3435, (EpsgCoordinateSystemKind)2, 1342); + return true; + case 3436: + cacheIndex = 1343; + reference = new EpsgCoordinateReferenceRecord(3436, (EpsgCoordinateSystemKind)2, 1343); + return true; + case 3437: + cacheIndex = 1344; + reference = new EpsgCoordinateReferenceRecord(3437, (EpsgCoordinateSystemKind)2, 1344); + return true; + case 3438: + cacheIndex = 1345; + reference = new EpsgCoordinateReferenceRecord(3438, (EpsgCoordinateSystemKind)2, 1345); + return true; + case 3439: + cacheIndex = 1346; + reference = new EpsgCoordinateReferenceRecord(3439, (EpsgCoordinateSystemKind)2, 1346); + return true; + case 3440: + cacheIndex = 1347; + reference = new EpsgCoordinateReferenceRecord(3440, (EpsgCoordinateSystemKind)2, 1347); + return true; + case 3441: + cacheIndex = 1348; + reference = new EpsgCoordinateReferenceRecord(3441, (EpsgCoordinateSystemKind)2, 1348); + return true; + case 3442: + cacheIndex = 1349; + reference = new EpsgCoordinateReferenceRecord(3442, (EpsgCoordinateSystemKind)2, 1349); + return true; + case 3443: + cacheIndex = 1350; + reference = new EpsgCoordinateReferenceRecord(3443, (EpsgCoordinateSystemKind)2, 1350); + return true; + case 3444: + cacheIndex = 1351; + reference = new EpsgCoordinateReferenceRecord(3444, (EpsgCoordinateSystemKind)2, 1351); + return true; + case 3445: + cacheIndex = 1352; + reference = new EpsgCoordinateReferenceRecord(3445, (EpsgCoordinateSystemKind)2, 1352); + return true; + case 3446: + cacheIndex = 1353; + reference = new EpsgCoordinateReferenceRecord(3446, (EpsgCoordinateSystemKind)2, 1353); + return true; + case 3447: + cacheIndex = 1354; + reference = new EpsgCoordinateReferenceRecord(3447, (EpsgCoordinateSystemKind)2, 1354); + return true; + case 3448: + cacheIndex = 1355; + reference = new EpsgCoordinateReferenceRecord(3448, (EpsgCoordinateSystemKind)2, 1355); + return true; + case 3449: + cacheIndex = 1356; + reference = new EpsgCoordinateReferenceRecord(3449, (EpsgCoordinateSystemKind)2, 1356); + return true; + case 3450: + cacheIndex = 1357; + reference = new EpsgCoordinateReferenceRecord(3450, (EpsgCoordinateSystemKind)2, 1357); + return true; + case 3451: + cacheIndex = 1358; + reference = new EpsgCoordinateReferenceRecord(3451, (EpsgCoordinateSystemKind)2, 1358); + return true; + case 3452: + cacheIndex = 1359; + reference = new EpsgCoordinateReferenceRecord(3452, (EpsgCoordinateSystemKind)2, 1359); + return true; + case 3453: + cacheIndex = 1360; + reference = new EpsgCoordinateReferenceRecord(3453, (EpsgCoordinateSystemKind)2, 1360); + return true; + case 3455: + cacheIndex = 1361; + reference = new EpsgCoordinateReferenceRecord(3455, (EpsgCoordinateSystemKind)2, 1361); + return true; + case 3456: + cacheIndex = 1362; + reference = new EpsgCoordinateReferenceRecord(3456, (EpsgCoordinateSystemKind)2, 1362); + return true; + case 3457: + cacheIndex = 1363; + reference = new EpsgCoordinateReferenceRecord(3457, (EpsgCoordinateSystemKind)2, 1363); + return true; + case 3458: + cacheIndex = 1364; + reference = new EpsgCoordinateReferenceRecord(3458, (EpsgCoordinateSystemKind)2, 1364); + return true; + case 3459: + cacheIndex = 1365; + reference = new EpsgCoordinateReferenceRecord(3459, (EpsgCoordinateSystemKind)2, 1365); + return true; + case 3460: + cacheIndex = 1366; + reference = new EpsgCoordinateReferenceRecord(3460, (EpsgCoordinateSystemKind)2, 1366); + return true; + case 3461: + cacheIndex = 1367; + reference = new EpsgCoordinateReferenceRecord(3461, (EpsgCoordinateSystemKind)2, 1367); + return true; + case 3462: + cacheIndex = 1368; + reference = new EpsgCoordinateReferenceRecord(3462, (EpsgCoordinateSystemKind)2, 1368); + return true; + case 3463: + cacheIndex = 1369; + reference = new EpsgCoordinateReferenceRecord(3463, (EpsgCoordinateSystemKind)2, 1369); + return true; + case 3464: + cacheIndex = 1370; + reference = new EpsgCoordinateReferenceRecord(3464, (EpsgCoordinateSystemKind)2, 1370); + return true; + case 3465: + cacheIndex = 1371; + reference = new EpsgCoordinateReferenceRecord(3465, (EpsgCoordinateSystemKind)2, 1371); + return true; + case 3466: + cacheIndex = 1372; + reference = new EpsgCoordinateReferenceRecord(3466, (EpsgCoordinateSystemKind)2, 1372); + return true; + case 3467: + cacheIndex = 1373; + reference = new EpsgCoordinateReferenceRecord(3467, (EpsgCoordinateSystemKind)2, 1373); + return true; + case 3468: + cacheIndex = 1374; + reference = new EpsgCoordinateReferenceRecord(3468, (EpsgCoordinateSystemKind)2, 1374); + return true; + case 3469: + cacheIndex = 1375; + reference = new EpsgCoordinateReferenceRecord(3469, (EpsgCoordinateSystemKind)2, 1375); + return true; + case 3470: + cacheIndex = 1376; + reference = new EpsgCoordinateReferenceRecord(3470, (EpsgCoordinateSystemKind)2, 1376); + return true; + case 3471: + cacheIndex = 1377; + reference = new EpsgCoordinateReferenceRecord(3471, (EpsgCoordinateSystemKind)2, 1377); + return true; + case 3472: + cacheIndex = 1378; + reference = new EpsgCoordinateReferenceRecord(3472, (EpsgCoordinateSystemKind)2, 1378); + return true; + case 3473: + cacheIndex = 1379; + reference = new EpsgCoordinateReferenceRecord(3473, (EpsgCoordinateSystemKind)2, 1379); + return true; + case 3474: + cacheIndex = 1380; + reference = new EpsgCoordinateReferenceRecord(3474, (EpsgCoordinateSystemKind)2, 1380); + return true; + case 3475: + cacheIndex = 1381; + reference = new EpsgCoordinateReferenceRecord(3475, (EpsgCoordinateSystemKind)2, 1381); + return true; + case 3476: + cacheIndex = 1382; + reference = new EpsgCoordinateReferenceRecord(3476, (EpsgCoordinateSystemKind)2, 1382); + return true; + case 3477: + cacheIndex = 1383; + reference = new EpsgCoordinateReferenceRecord(3477, (EpsgCoordinateSystemKind)2, 1383); + return true; + case 3478: + cacheIndex = 1384; + reference = new EpsgCoordinateReferenceRecord(3478, (EpsgCoordinateSystemKind)2, 1384); + return true; + case 3479: + cacheIndex = 1385; + reference = new EpsgCoordinateReferenceRecord(3479, (EpsgCoordinateSystemKind)2, 1385); + return true; + case 3480: + cacheIndex = 1386; + reference = new EpsgCoordinateReferenceRecord(3480, (EpsgCoordinateSystemKind)2, 1386); + return true; + case 3481: + cacheIndex = 1387; + reference = new EpsgCoordinateReferenceRecord(3481, (EpsgCoordinateSystemKind)2, 1387); + return true; + case 3482: + cacheIndex = 1388; + reference = new EpsgCoordinateReferenceRecord(3482, (EpsgCoordinateSystemKind)2, 1388); + return true; + case 3483: + cacheIndex = 1389; + reference = new EpsgCoordinateReferenceRecord(3483, (EpsgCoordinateSystemKind)2, 1389); + return true; + case 3484: + cacheIndex = 1390; + reference = new EpsgCoordinateReferenceRecord(3484, (EpsgCoordinateSystemKind)2, 1390); + return true; + case 3485: + cacheIndex = 1391; + reference = new EpsgCoordinateReferenceRecord(3485, (EpsgCoordinateSystemKind)2, 1391); + return true; + case 3486: + cacheIndex = 1392; + reference = new EpsgCoordinateReferenceRecord(3486, (EpsgCoordinateSystemKind)2, 1392); + return true; + case 3487: + cacheIndex = 1393; + reference = new EpsgCoordinateReferenceRecord(3487, (EpsgCoordinateSystemKind)2, 1393); + return true; + case 3488: + cacheIndex = 1394; + reference = new EpsgCoordinateReferenceRecord(3488, (EpsgCoordinateSystemKind)2, 1394); + return true; + case 3489: + cacheIndex = 1395; + reference = new EpsgCoordinateReferenceRecord(3489, (EpsgCoordinateSystemKind)2, 1395); + return true; + case 3490: + cacheIndex = 1396; + reference = new EpsgCoordinateReferenceRecord(3490, (EpsgCoordinateSystemKind)2, 1396); + return true; + case 3491: + cacheIndex = 1397; + reference = new EpsgCoordinateReferenceRecord(3491, (EpsgCoordinateSystemKind)2, 1397); + return true; + case 3492: + cacheIndex = 1398; + reference = new EpsgCoordinateReferenceRecord(3492, (EpsgCoordinateSystemKind)2, 1398); + return true; + case 3493: + cacheIndex = 1399; + reference = new EpsgCoordinateReferenceRecord(3493, (EpsgCoordinateSystemKind)2, 1399); + return true; + case 3494: + cacheIndex = 1400; + reference = new EpsgCoordinateReferenceRecord(3494, (EpsgCoordinateSystemKind)2, 1400); + return true; + case 3495: + cacheIndex = 1401; + reference = new EpsgCoordinateReferenceRecord(3495, (EpsgCoordinateSystemKind)2, 1401); + return true; + case 3496: + cacheIndex = 1402; + reference = new EpsgCoordinateReferenceRecord(3496, (EpsgCoordinateSystemKind)2, 1402); + return true; + case 3497: + cacheIndex = 1403; + reference = new EpsgCoordinateReferenceRecord(3497, (EpsgCoordinateSystemKind)2, 1403); + return true; + case 3498: + cacheIndex = 1404; + reference = new EpsgCoordinateReferenceRecord(3498, (EpsgCoordinateSystemKind)2, 1404); + return true; + case 3499: + cacheIndex = 1405; + reference = new EpsgCoordinateReferenceRecord(3499, (EpsgCoordinateSystemKind)2, 1405); + return true; + case 3500: + cacheIndex = 1406; + reference = new EpsgCoordinateReferenceRecord(3500, (EpsgCoordinateSystemKind)2, 1406); + return true; + case 3501: + cacheIndex = 1407; + reference = new EpsgCoordinateReferenceRecord(3501, (EpsgCoordinateSystemKind)2, 1407); + return true; + case 3502: + cacheIndex = 1408; + reference = new EpsgCoordinateReferenceRecord(3502, (EpsgCoordinateSystemKind)2, 1408); + return true; + case 3503: + cacheIndex = 1409; + reference = new EpsgCoordinateReferenceRecord(3503, (EpsgCoordinateSystemKind)2, 1409); + return true; + case 3504: + cacheIndex = 1410; + reference = new EpsgCoordinateReferenceRecord(3504, (EpsgCoordinateSystemKind)2, 1410); + return true; + case 3505: + cacheIndex = 1411; + reference = new EpsgCoordinateReferenceRecord(3505, (EpsgCoordinateSystemKind)2, 1411); + return true; + case 3506: + cacheIndex = 1412; + reference = new EpsgCoordinateReferenceRecord(3506, (EpsgCoordinateSystemKind)2, 1412); + return true; + case 3507: + cacheIndex = 1413; + reference = new EpsgCoordinateReferenceRecord(3507, (EpsgCoordinateSystemKind)2, 1413); + return true; + case 3508: + cacheIndex = 1414; + reference = new EpsgCoordinateReferenceRecord(3508, (EpsgCoordinateSystemKind)2, 1414); + return true; + case 3509: + cacheIndex = 1415; + reference = new EpsgCoordinateReferenceRecord(3509, (EpsgCoordinateSystemKind)2, 1415); + return true; + case 3510: + cacheIndex = 1416; + reference = new EpsgCoordinateReferenceRecord(3510, (EpsgCoordinateSystemKind)2, 1416); + return true; + case 3511: + cacheIndex = 1417; + reference = new EpsgCoordinateReferenceRecord(3511, (EpsgCoordinateSystemKind)2, 1417); + return true; + case 3512: + cacheIndex = 1418; + reference = new EpsgCoordinateReferenceRecord(3512, (EpsgCoordinateSystemKind)2, 1418); + return true; + case 3513: + cacheIndex = 1419; + reference = new EpsgCoordinateReferenceRecord(3513, (EpsgCoordinateSystemKind)2, 1419); + return true; + case 3514: + cacheIndex = 1420; + reference = new EpsgCoordinateReferenceRecord(3514, (EpsgCoordinateSystemKind)2, 1420); + return true; + case 3515: + cacheIndex = 1421; + reference = new EpsgCoordinateReferenceRecord(3515, (EpsgCoordinateSystemKind)2, 1421); + return true; + case 3516: + cacheIndex = 1422; + reference = new EpsgCoordinateReferenceRecord(3516, (EpsgCoordinateSystemKind)2, 1422); + return true; + case 3517: + cacheIndex = 1423; + reference = new EpsgCoordinateReferenceRecord(3517, (EpsgCoordinateSystemKind)2, 1423); + return true; + case 3518: + cacheIndex = 1424; + reference = new EpsgCoordinateReferenceRecord(3518, (EpsgCoordinateSystemKind)2, 1424); + return true; + case 3519: + cacheIndex = 1425; + reference = new EpsgCoordinateReferenceRecord(3519, (EpsgCoordinateSystemKind)2, 1425); + return true; + case 3520: + cacheIndex = 1426; + reference = new EpsgCoordinateReferenceRecord(3520, (EpsgCoordinateSystemKind)2, 1426); + return true; + case 3521: + cacheIndex = 1427; + reference = new EpsgCoordinateReferenceRecord(3521, (EpsgCoordinateSystemKind)2, 1427); + return true; + case 3522: + cacheIndex = 1428; + reference = new EpsgCoordinateReferenceRecord(3522, (EpsgCoordinateSystemKind)2, 1428); + return true; + case 3523: + cacheIndex = 1429; + reference = new EpsgCoordinateReferenceRecord(3523, (EpsgCoordinateSystemKind)2, 1429); + return true; + case 3524: + cacheIndex = 1430; + reference = new EpsgCoordinateReferenceRecord(3524, (EpsgCoordinateSystemKind)2, 1430); + return true; + case 3525: + cacheIndex = 1431; + reference = new EpsgCoordinateReferenceRecord(3525, (EpsgCoordinateSystemKind)2, 1431); + return true; + case 3526: + cacheIndex = 1432; + reference = new EpsgCoordinateReferenceRecord(3526, (EpsgCoordinateSystemKind)2, 1432); + return true; + case 3527: + cacheIndex = 1433; + reference = new EpsgCoordinateReferenceRecord(3527, (EpsgCoordinateSystemKind)2, 1433); + return true; + case 3528: + cacheIndex = 1434; + reference = new EpsgCoordinateReferenceRecord(3528, (EpsgCoordinateSystemKind)2, 1434); + return true; + case 3529: + cacheIndex = 1435; + reference = new EpsgCoordinateReferenceRecord(3529, (EpsgCoordinateSystemKind)2, 1435); + return true; + case 3530: + cacheIndex = 1436; + reference = new EpsgCoordinateReferenceRecord(3530, (EpsgCoordinateSystemKind)2, 1436); + return true; + case 3531: + cacheIndex = 1437; + reference = new EpsgCoordinateReferenceRecord(3531, (EpsgCoordinateSystemKind)2, 1437); + return true; + case 3532: + cacheIndex = 1438; + reference = new EpsgCoordinateReferenceRecord(3532, (EpsgCoordinateSystemKind)2, 1438); + return true; + case 3533: + cacheIndex = 1439; + reference = new EpsgCoordinateReferenceRecord(3533, (EpsgCoordinateSystemKind)2, 1439); + return true; + case 3534: + cacheIndex = 1440; + reference = new EpsgCoordinateReferenceRecord(3534, (EpsgCoordinateSystemKind)2, 1440); + return true; + case 3535: + cacheIndex = 1441; + reference = new EpsgCoordinateReferenceRecord(3535, (EpsgCoordinateSystemKind)2, 1441); + return true; + case 3536: + cacheIndex = 1442; + reference = new EpsgCoordinateReferenceRecord(3536, (EpsgCoordinateSystemKind)2, 1442); + return true; + case 3537: + cacheIndex = 1443; + reference = new EpsgCoordinateReferenceRecord(3537, (EpsgCoordinateSystemKind)2, 1443); + return true; + case 3538: + cacheIndex = 1444; + reference = new EpsgCoordinateReferenceRecord(3538, (EpsgCoordinateSystemKind)2, 1444); + return true; + case 3539: + cacheIndex = 1445; + reference = new EpsgCoordinateReferenceRecord(3539, (EpsgCoordinateSystemKind)2, 1445); + return true; + case 3540: + cacheIndex = 1446; + reference = new EpsgCoordinateReferenceRecord(3540, (EpsgCoordinateSystemKind)2, 1446); + return true; + case 3541: + cacheIndex = 1447; + reference = new EpsgCoordinateReferenceRecord(3541, (EpsgCoordinateSystemKind)2, 1447); + return true; + case 3542: + cacheIndex = 1448; + reference = new EpsgCoordinateReferenceRecord(3542, (EpsgCoordinateSystemKind)2, 1448); + return true; + case 3543: + cacheIndex = 1449; + reference = new EpsgCoordinateReferenceRecord(3543, (EpsgCoordinateSystemKind)2, 1449); + return true; + case 3544: + cacheIndex = 1450; + reference = new EpsgCoordinateReferenceRecord(3544, (EpsgCoordinateSystemKind)2, 1450); + return true; + case 3545: + cacheIndex = 1451; + reference = new EpsgCoordinateReferenceRecord(3545, (EpsgCoordinateSystemKind)2, 1451); + return true; + case 3546: + cacheIndex = 1452; + reference = new EpsgCoordinateReferenceRecord(3546, (EpsgCoordinateSystemKind)2, 1452); + return true; + case 3547: + cacheIndex = 1453; + reference = new EpsgCoordinateReferenceRecord(3547, (EpsgCoordinateSystemKind)2, 1453); + return true; + case 3548: + cacheIndex = 1454; + reference = new EpsgCoordinateReferenceRecord(3548, (EpsgCoordinateSystemKind)2, 1454); + return true; + case 3549: + cacheIndex = 1455; + reference = new EpsgCoordinateReferenceRecord(3549, (EpsgCoordinateSystemKind)2, 1455); + return true; + case 3550: + cacheIndex = 1456; + reference = new EpsgCoordinateReferenceRecord(3550, (EpsgCoordinateSystemKind)2, 1456); + return true; + case 3551: + cacheIndex = 1457; + reference = new EpsgCoordinateReferenceRecord(3551, (EpsgCoordinateSystemKind)2, 1457); + return true; + case 3552: + cacheIndex = 1458; + reference = new EpsgCoordinateReferenceRecord(3552, (EpsgCoordinateSystemKind)2, 1458); + return true; + case 3553: + cacheIndex = 1459; + reference = new EpsgCoordinateReferenceRecord(3553, (EpsgCoordinateSystemKind)2, 1459); + return true; + case 3554: + cacheIndex = 1460; + reference = new EpsgCoordinateReferenceRecord(3554, (EpsgCoordinateSystemKind)2, 1460); + return true; + case 3555: + cacheIndex = 1461; + reference = new EpsgCoordinateReferenceRecord(3555, (EpsgCoordinateSystemKind)2, 1461); + return true; + case 3556: + cacheIndex = 1462; + reference = new EpsgCoordinateReferenceRecord(3556, (EpsgCoordinateSystemKind)2, 1462); + return true; + case 3557: + cacheIndex = 1463; + reference = new EpsgCoordinateReferenceRecord(3557, (EpsgCoordinateSystemKind)2, 1463); + return true; + case 3558: + cacheIndex = 1464; + reference = new EpsgCoordinateReferenceRecord(3558, (EpsgCoordinateSystemKind)2, 1464); + return true; + case 3559: + cacheIndex = 1465; + reference = new EpsgCoordinateReferenceRecord(3559, (EpsgCoordinateSystemKind)2, 1465); + return true; + case 3560: + cacheIndex = 1466; + reference = new EpsgCoordinateReferenceRecord(3560, (EpsgCoordinateSystemKind)2, 1466); + return true; + case 3561: + cacheIndex = 1467; + reference = new EpsgCoordinateReferenceRecord(3561, (EpsgCoordinateSystemKind)2, 1467); + return true; + case 3562: + cacheIndex = 1468; + reference = new EpsgCoordinateReferenceRecord(3562, (EpsgCoordinateSystemKind)2, 1468); + return true; + case 3563: + cacheIndex = 1469; + reference = new EpsgCoordinateReferenceRecord(3563, (EpsgCoordinateSystemKind)2, 1469); + return true; + case 3564: + cacheIndex = 1470; + reference = new EpsgCoordinateReferenceRecord(3564, (EpsgCoordinateSystemKind)2, 1470); + return true; + case 3565: + cacheIndex = 1471; + reference = new EpsgCoordinateReferenceRecord(3565, (EpsgCoordinateSystemKind)2, 1471); + return true; + case 3566: + cacheIndex = 1472; + reference = new EpsgCoordinateReferenceRecord(3566, (EpsgCoordinateSystemKind)2, 1472); + return true; + case 3567: + cacheIndex = 1473; + reference = new EpsgCoordinateReferenceRecord(3567, (EpsgCoordinateSystemKind)2, 1473); + return true; + case 3568: + cacheIndex = 1474; + reference = new EpsgCoordinateReferenceRecord(3568, (EpsgCoordinateSystemKind)2, 1474); + return true; + case 3569: + cacheIndex = 1475; + reference = new EpsgCoordinateReferenceRecord(3569, (EpsgCoordinateSystemKind)2, 1475); + return true; + case 3570: + cacheIndex = 1476; + reference = new EpsgCoordinateReferenceRecord(3570, (EpsgCoordinateSystemKind)2, 1476); + return true; + case 3571: + cacheIndex = 1477; + reference = new EpsgCoordinateReferenceRecord(3571, (EpsgCoordinateSystemKind)2, 1477); + return true; + case 3572: + cacheIndex = 1478; + reference = new EpsgCoordinateReferenceRecord(3572, (EpsgCoordinateSystemKind)2, 1478); + return true; + case 3573: + cacheIndex = 1479; + reference = new EpsgCoordinateReferenceRecord(3573, (EpsgCoordinateSystemKind)2, 1479); + return true; + case 3574: + cacheIndex = 1480; + reference = new EpsgCoordinateReferenceRecord(3574, (EpsgCoordinateSystemKind)2, 1480); + return true; + case 3575: + cacheIndex = 1481; + reference = new EpsgCoordinateReferenceRecord(3575, (EpsgCoordinateSystemKind)2, 1481); + return true; + case 3576: + cacheIndex = 1482; + reference = new EpsgCoordinateReferenceRecord(3576, (EpsgCoordinateSystemKind)2, 1482); + return true; + case 3577: + cacheIndex = 1483; + reference = new EpsgCoordinateReferenceRecord(3577, (EpsgCoordinateSystemKind)2, 1483); + return true; + case 3578: + cacheIndex = 1484; + reference = new EpsgCoordinateReferenceRecord(3578, (EpsgCoordinateSystemKind)2, 1484); + return true; + case 3579: + cacheIndex = 1485; + reference = new EpsgCoordinateReferenceRecord(3579, (EpsgCoordinateSystemKind)2, 1485); + return true; + case 3580: + cacheIndex = 1486; + reference = new EpsgCoordinateReferenceRecord(3580, (EpsgCoordinateSystemKind)2, 1486); + return true; + case 3581: + cacheIndex = 1487; + reference = new EpsgCoordinateReferenceRecord(3581, (EpsgCoordinateSystemKind)2, 1487); + return true; + case 3582: + cacheIndex = 1488; + reference = new EpsgCoordinateReferenceRecord(3582, (EpsgCoordinateSystemKind)2, 1488); + return true; + case 3583: + cacheIndex = 1489; + reference = new EpsgCoordinateReferenceRecord(3583, (EpsgCoordinateSystemKind)2, 1489); + return true; + case 3584: + cacheIndex = 1490; + reference = new EpsgCoordinateReferenceRecord(3584, (EpsgCoordinateSystemKind)2, 1490); + return true; + case 3585: + cacheIndex = 1491; + reference = new EpsgCoordinateReferenceRecord(3585, (EpsgCoordinateSystemKind)2, 1491); + return true; + case 3586: + cacheIndex = 1492; + reference = new EpsgCoordinateReferenceRecord(3586, (EpsgCoordinateSystemKind)2, 1492); + return true; + case 3587: + cacheIndex = 1493; + reference = new EpsgCoordinateReferenceRecord(3587, (EpsgCoordinateSystemKind)2, 1493); + return true; + case 3588: + cacheIndex = 1494; + reference = new EpsgCoordinateReferenceRecord(3588, (EpsgCoordinateSystemKind)2, 1494); + return true; + case 3589: + cacheIndex = 1495; + reference = new EpsgCoordinateReferenceRecord(3589, (EpsgCoordinateSystemKind)2, 1495); + return true; + case 3590: + cacheIndex = 1496; + reference = new EpsgCoordinateReferenceRecord(3590, (EpsgCoordinateSystemKind)2, 1496); + return true; + case 3591: + cacheIndex = 1497; + reference = new EpsgCoordinateReferenceRecord(3591, (EpsgCoordinateSystemKind)2, 1497); + return true; + case 3592: + cacheIndex = 1498; + reference = new EpsgCoordinateReferenceRecord(3592, (EpsgCoordinateSystemKind)2, 1498); + return true; + case 3593: + cacheIndex = 1499; + reference = new EpsgCoordinateReferenceRecord(3593, (EpsgCoordinateSystemKind)2, 1499); + return true; + case 3594: + cacheIndex = 1500; + reference = new EpsgCoordinateReferenceRecord(3594, (EpsgCoordinateSystemKind)2, 1500); + return true; + case 3595: + cacheIndex = 1501; + reference = new EpsgCoordinateReferenceRecord(3595, (EpsgCoordinateSystemKind)2, 1501); + return true; + case 3596: + cacheIndex = 1502; + reference = new EpsgCoordinateReferenceRecord(3596, (EpsgCoordinateSystemKind)2, 1502); + return true; + case 3597: + cacheIndex = 1503; + reference = new EpsgCoordinateReferenceRecord(3597, (EpsgCoordinateSystemKind)2, 1503); + return true; + case 3598: + cacheIndex = 1504; + reference = new EpsgCoordinateReferenceRecord(3598, (EpsgCoordinateSystemKind)2, 1504); + return true; + case 3599: + cacheIndex = 1505; + reference = new EpsgCoordinateReferenceRecord(3599, (EpsgCoordinateSystemKind)2, 1505); + return true; + case 3600: + cacheIndex = 1506; + reference = new EpsgCoordinateReferenceRecord(3600, (EpsgCoordinateSystemKind)2, 1506); + return true; + case 3601: + cacheIndex = 1507; + reference = new EpsgCoordinateReferenceRecord(3601, (EpsgCoordinateSystemKind)2, 1507); + return true; + case 3602: + cacheIndex = 1508; + reference = new EpsgCoordinateReferenceRecord(3602, (EpsgCoordinateSystemKind)2, 1508); + return true; + case 3603: + cacheIndex = 1509; + reference = new EpsgCoordinateReferenceRecord(3603, (EpsgCoordinateSystemKind)2, 1509); + return true; + case 3604: + cacheIndex = 1510; + reference = new EpsgCoordinateReferenceRecord(3604, (EpsgCoordinateSystemKind)2, 1510); + return true; + case 3605: + cacheIndex = 1511; + reference = new EpsgCoordinateReferenceRecord(3605, (EpsgCoordinateSystemKind)2, 1511); + return true; + case 3606: + cacheIndex = 1512; + reference = new EpsgCoordinateReferenceRecord(3606, (EpsgCoordinateSystemKind)2, 1512); + return true; + case 3607: + cacheIndex = 1513; + reference = new EpsgCoordinateReferenceRecord(3607, (EpsgCoordinateSystemKind)2, 1513); + return true; + case 3608: + cacheIndex = 1514; + reference = new EpsgCoordinateReferenceRecord(3608, (EpsgCoordinateSystemKind)2, 1514); + return true; + case 3609: + cacheIndex = 1515; + reference = new EpsgCoordinateReferenceRecord(3609, (EpsgCoordinateSystemKind)2, 1515); + return true; + case 3610: + cacheIndex = 1516; + reference = new EpsgCoordinateReferenceRecord(3610, (EpsgCoordinateSystemKind)2, 1516); + return true; + case 3611: + cacheIndex = 1517; + reference = new EpsgCoordinateReferenceRecord(3611, (EpsgCoordinateSystemKind)2, 1517); + return true; + case 3612: + cacheIndex = 1518; + reference = new EpsgCoordinateReferenceRecord(3612, (EpsgCoordinateSystemKind)2, 1518); + return true; + case 3613: + cacheIndex = 1519; + reference = new EpsgCoordinateReferenceRecord(3613, (EpsgCoordinateSystemKind)2, 1519); + return true; + case 3614: + cacheIndex = 1520; + reference = new EpsgCoordinateReferenceRecord(3614, (EpsgCoordinateSystemKind)2, 1520); + return true; + case 3615: + cacheIndex = 1521; + reference = new EpsgCoordinateReferenceRecord(3615, (EpsgCoordinateSystemKind)2, 1521); + return true; + case 3616: + cacheIndex = 1522; + reference = new EpsgCoordinateReferenceRecord(3616, (EpsgCoordinateSystemKind)2, 1522); + return true; + case 3617: + cacheIndex = 1523; + reference = new EpsgCoordinateReferenceRecord(3617, (EpsgCoordinateSystemKind)2, 1523); + return true; + case 3618: + cacheIndex = 1524; + reference = new EpsgCoordinateReferenceRecord(3618, (EpsgCoordinateSystemKind)2, 1524); + return true; + case 3619: + cacheIndex = 1525; + reference = new EpsgCoordinateReferenceRecord(3619, (EpsgCoordinateSystemKind)2, 1525); + return true; + case 3620: + cacheIndex = 1526; + reference = new EpsgCoordinateReferenceRecord(3620, (EpsgCoordinateSystemKind)2, 1526); + return true; + case 3621: + cacheIndex = 1527; + reference = new EpsgCoordinateReferenceRecord(3621, (EpsgCoordinateSystemKind)2, 1527); + return true; + case 3622: + cacheIndex = 1528; + reference = new EpsgCoordinateReferenceRecord(3622, (EpsgCoordinateSystemKind)2, 1528); + return true; + case 3623: + cacheIndex = 1529; + reference = new EpsgCoordinateReferenceRecord(3623, (EpsgCoordinateSystemKind)2, 1529); + return true; + case 3624: + cacheIndex = 1530; + reference = new EpsgCoordinateReferenceRecord(3624, (EpsgCoordinateSystemKind)2, 1530); + return true; + case 3625: + cacheIndex = 1531; + reference = new EpsgCoordinateReferenceRecord(3625, (EpsgCoordinateSystemKind)2, 1531); + return true; + case 3626: + cacheIndex = 1532; + reference = new EpsgCoordinateReferenceRecord(3626, (EpsgCoordinateSystemKind)2, 1532); + return true; + case 3627: + cacheIndex = 1533; + reference = new EpsgCoordinateReferenceRecord(3627, (EpsgCoordinateSystemKind)2, 1533); + return true; + case 3628: + cacheIndex = 1534; + reference = new EpsgCoordinateReferenceRecord(3628, (EpsgCoordinateSystemKind)2, 1534); + return true; + case 3629: + cacheIndex = 1535; + reference = new EpsgCoordinateReferenceRecord(3629, (EpsgCoordinateSystemKind)2, 1535); + return true; + case 3630: + cacheIndex = 1536; + reference = new EpsgCoordinateReferenceRecord(3630, (EpsgCoordinateSystemKind)2, 1536); + return true; + case 3631: + cacheIndex = 1537; + reference = new EpsgCoordinateReferenceRecord(3631, (EpsgCoordinateSystemKind)2, 1537); + return true; + case 3632: + cacheIndex = 1538; + reference = new EpsgCoordinateReferenceRecord(3632, (EpsgCoordinateSystemKind)2, 1538); + return true; + case 3633: + cacheIndex = 1539; + reference = new EpsgCoordinateReferenceRecord(3633, (EpsgCoordinateSystemKind)2, 1539); + return true; + case 3634: + cacheIndex = 1540; + reference = new EpsgCoordinateReferenceRecord(3634, (EpsgCoordinateSystemKind)2, 1540); + return true; + case 3635: + cacheIndex = 1541; + reference = new EpsgCoordinateReferenceRecord(3635, (EpsgCoordinateSystemKind)2, 1541); + return true; + case 3636: + cacheIndex = 1542; + reference = new EpsgCoordinateReferenceRecord(3636, (EpsgCoordinateSystemKind)2, 1542); + return true; + case 3637: + cacheIndex = 1543; + reference = new EpsgCoordinateReferenceRecord(3637, (EpsgCoordinateSystemKind)2, 1543); + return true; + case 3638: + cacheIndex = 1544; + reference = new EpsgCoordinateReferenceRecord(3638, (EpsgCoordinateSystemKind)2, 1544); + return true; + case 3639: + cacheIndex = 1545; + reference = new EpsgCoordinateReferenceRecord(3639, (EpsgCoordinateSystemKind)2, 1545); + return true; + case 3640: + cacheIndex = 1546; + reference = new EpsgCoordinateReferenceRecord(3640, (EpsgCoordinateSystemKind)2, 1546); + return true; + case 3641: + cacheIndex = 1547; + reference = new EpsgCoordinateReferenceRecord(3641, (EpsgCoordinateSystemKind)2, 1547); + return true; + case 3642: + cacheIndex = 1548; + reference = new EpsgCoordinateReferenceRecord(3642, (EpsgCoordinateSystemKind)2, 1548); + return true; + case 3643: + cacheIndex = 1549; + reference = new EpsgCoordinateReferenceRecord(3643, (EpsgCoordinateSystemKind)2, 1549); + return true; + case 3644: + cacheIndex = 1550; + reference = new EpsgCoordinateReferenceRecord(3644, (EpsgCoordinateSystemKind)2, 1550); + return true; + case 3645: + cacheIndex = 1551; + reference = new EpsgCoordinateReferenceRecord(3645, (EpsgCoordinateSystemKind)2, 1551); + return true; + case 3646: + cacheIndex = 1552; + reference = new EpsgCoordinateReferenceRecord(3646, (EpsgCoordinateSystemKind)2, 1552); + return true; + case 3647: + cacheIndex = 1553; + reference = new EpsgCoordinateReferenceRecord(3647, (EpsgCoordinateSystemKind)2, 1553); + return true; + case 3648: + cacheIndex = 1554; + reference = new EpsgCoordinateReferenceRecord(3648, (EpsgCoordinateSystemKind)2, 1554); + return true; + case 3649: + cacheIndex = 1555; + reference = new EpsgCoordinateReferenceRecord(3649, (EpsgCoordinateSystemKind)2, 1555); + return true; + case 3650: + cacheIndex = 1556; + reference = new EpsgCoordinateReferenceRecord(3650, (EpsgCoordinateSystemKind)2, 1556); + return true; + case 3651: + cacheIndex = 1557; + reference = new EpsgCoordinateReferenceRecord(3651, (EpsgCoordinateSystemKind)2, 1557); + return true; + case 3652: + cacheIndex = 1558; + reference = new EpsgCoordinateReferenceRecord(3652, (EpsgCoordinateSystemKind)2, 1558); + return true; + case 3653: + cacheIndex = 1559; + reference = new EpsgCoordinateReferenceRecord(3653, (EpsgCoordinateSystemKind)2, 1559); + return true; + case 3654: + cacheIndex = 1560; + reference = new EpsgCoordinateReferenceRecord(3654, (EpsgCoordinateSystemKind)2, 1560); + return true; + case 3655: + cacheIndex = 1561; + reference = new EpsgCoordinateReferenceRecord(3655, (EpsgCoordinateSystemKind)2, 1561); + return true; + case 3656: + cacheIndex = 1562; + reference = new EpsgCoordinateReferenceRecord(3656, (EpsgCoordinateSystemKind)2, 1562); + return true; + case 3657: + cacheIndex = 1563; + reference = new EpsgCoordinateReferenceRecord(3657, (EpsgCoordinateSystemKind)2, 1563); + return true; + case 3658: + cacheIndex = 1564; + reference = new EpsgCoordinateReferenceRecord(3658, (EpsgCoordinateSystemKind)2, 1564); + return true; + case 3659: + cacheIndex = 1565; + reference = new EpsgCoordinateReferenceRecord(3659, (EpsgCoordinateSystemKind)2, 1565); + return true; + case 3660: + cacheIndex = 1566; + reference = new EpsgCoordinateReferenceRecord(3660, (EpsgCoordinateSystemKind)2, 1566); + return true; + case 3661: + cacheIndex = 1567; + reference = new EpsgCoordinateReferenceRecord(3661, (EpsgCoordinateSystemKind)2, 1567); + return true; + case 3662: + cacheIndex = 1568; + reference = new EpsgCoordinateReferenceRecord(3662, (EpsgCoordinateSystemKind)2, 1568); + return true; + case 3663: + cacheIndex = 1569; + reference = new EpsgCoordinateReferenceRecord(3663, (EpsgCoordinateSystemKind)2, 1569); + return true; + case 3664: + cacheIndex = 1570; + reference = new EpsgCoordinateReferenceRecord(3664, (EpsgCoordinateSystemKind)2, 1570); + return true; + case 3665: + cacheIndex = 1571; + reference = new EpsgCoordinateReferenceRecord(3665, (EpsgCoordinateSystemKind)2, 1571); + return true; + case 3666: + cacheIndex = 1572; + reference = new EpsgCoordinateReferenceRecord(3666, (EpsgCoordinateSystemKind)2, 1572); + return true; + case 3667: + cacheIndex = 1573; + reference = new EpsgCoordinateReferenceRecord(3667, (EpsgCoordinateSystemKind)2, 1573); + return true; + case 3668: + cacheIndex = 1574; + reference = new EpsgCoordinateReferenceRecord(3668, (EpsgCoordinateSystemKind)2, 1574); + return true; + case 3669: + cacheIndex = 1575; + reference = new EpsgCoordinateReferenceRecord(3669, (EpsgCoordinateSystemKind)2, 1575); + return true; + case 3670: + cacheIndex = 1576; + reference = new EpsgCoordinateReferenceRecord(3670, (EpsgCoordinateSystemKind)2, 1576); + return true; + case 3671: + cacheIndex = 1577; + reference = new EpsgCoordinateReferenceRecord(3671, (EpsgCoordinateSystemKind)2, 1577); + return true; + case 3672: + cacheIndex = 1578; + reference = new EpsgCoordinateReferenceRecord(3672, (EpsgCoordinateSystemKind)2, 1578); + return true; + case 3673: + cacheIndex = 1579; + reference = new EpsgCoordinateReferenceRecord(3673, (EpsgCoordinateSystemKind)2, 1579); + return true; + case 3674: + cacheIndex = 1580; + reference = new EpsgCoordinateReferenceRecord(3674, (EpsgCoordinateSystemKind)2, 1580); + return true; + case 3675: + cacheIndex = 1581; + reference = new EpsgCoordinateReferenceRecord(3675, (EpsgCoordinateSystemKind)2, 1581); + return true; + case 3676: + cacheIndex = 1582; + reference = new EpsgCoordinateReferenceRecord(3676, (EpsgCoordinateSystemKind)2, 1582); + return true; + case 3677: + cacheIndex = 1583; + reference = new EpsgCoordinateReferenceRecord(3677, (EpsgCoordinateSystemKind)2, 1583); + return true; + case 3678: + cacheIndex = 1584; + reference = new EpsgCoordinateReferenceRecord(3678, (EpsgCoordinateSystemKind)2, 1584); + return true; + case 3679: + cacheIndex = 1585; + reference = new EpsgCoordinateReferenceRecord(3679, (EpsgCoordinateSystemKind)2, 1585); + return true; + case 3680: + cacheIndex = 1586; + reference = new EpsgCoordinateReferenceRecord(3680, (EpsgCoordinateSystemKind)2, 1586); + return true; + case 3681: + cacheIndex = 1587; + reference = new EpsgCoordinateReferenceRecord(3681, (EpsgCoordinateSystemKind)2, 1587); + return true; + case 3682: + cacheIndex = 1588; + reference = new EpsgCoordinateReferenceRecord(3682, (EpsgCoordinateSystemKind)2, 1588); + return true; + case 3683: + cacheIndex = 1589; + reference = new EpsgCoordinateReferenceRecord(3683, (EpsgCoordinateSystemKind)2, 1589); + return true; + case 3684: + cacheIndex = 1590; + reference = new EpsgCoordinateReferenceRecord(3684, (EpsgCoordinateSystemKind)2, 1590); + return true; + case 3685: + cacheIndex = 1591; + reference = new EpsgCoordinateReferenceRecord(3685, (EpsgCoordinateSystemKind)2, 1591); + return true; + case 3686: + cacheIndex = 1592; + reference = new EpsgCoordinateReferenceRecord(3686, (EpsgCoordinateSystemKind)2, 1592); + return true; + case 3687: + cacheIndex = 1593; + reference = new EpsgCoordinateReferenceRecord(3687, (EpsgCoordinateSystemKind)2, 1593); + return true; + case 3688: + cacheIndex = 1594; + reference = new EpsgCoordinateReferenceRecord(3688, (EpsgCoordinateSystemKind)2, 1594); + return true; + case 3689: + cacheIndex = 1595; + reference = new EpsgCoordinateReferenceRecord(3689, (EpsgCoordinateSystemKind)2, 1595); + return true; + case 3690: + cacheIndex = 1596; + reference = new EpsgCoordinateReferenceRecord(3690, (EpsgCoordinateSystemKind)2, 1596); + return true; + case 3691: + cacheIndex = 1597; + reference = new EpsgCoordinateReferenceRecord(3691, (EpsgCoordinateSystemKind)2, 1597); + return true; + case 3692: + cacheIndex = 1598; + reference = new EpsgCoordinateReferenceRecord(3692, (EpsgCoordinateSystemKind)2, 1598); + return true; + case 3693: + cacheIndex = 1599; + reference = new EpsgCoordinateReferenceRecord(3693, (EpsgCoordinateSystemKind)2, 1599); + return true; + case 3694: + cacheIndex = 1600; + reference = new EpsgCoordinateReferenceRecord(3694, (EpsgCoordinateSystemKind)2, 1600); + return true; + case 3695: + cacheIndex = 1601; + reference = new EpsgCoordinateReferenceRecord(3695, (EpsgCoordinateSystemKind)2, 1601); + return true; + case 3696: + cacheIndex = 1602; + reference = new EpsgCoordinateReferenceRecord(3696, (EpsgCoordinateSystemKind)2, 1602); + return true; + case 3697: + cacheIndex = 1603; + reference = new EpsgCoordinateReferenceRecord(3697, (EpsgCoordinateSystemKind)2, 1603); + return true; + case 3698: + cacheIndex = 1604; + reference = new EpsgCoordinateReferenceRecord(3698, (EpsgCoordinateSystemKind)2, 1604); + return true; + case 3699: + cacheIndex = 1605; + reference = new EpsgCoordinateReferenceRecord(3699, (EpsgCoordinateSystemKind)2, 1605); + return true; + case 3700: + cacheIndex = 1606; + reference = new EpsgCoordinateReferenceRecord(3700, (EpsgCoordinateSystemKind)2, 1606); + return true; + case 3701: + cacheIndex = 1607; + reference = new EpsgCoordinateReferenceRecord(3701, (EpsgCoordinateSystemKind)2, 1607); + return true; + case 3702: + cacheIndex = 1608; + reference = new EpsgCoordinateReferenceRecord(3702, (EpsgCoordinateSystemKind)2, 1608); + return true; + case 3703: + cacheIndex = 1609; + reference = new EpsgCoordinateReferenceRecord(3703, (EpsgCoordinateSystemKind)2, 1609); + return true; + case 3704: + cacheIndex = 1610; + reference = new EpsgCoordinateReferenceRecord(3704, (EpsgCoordinateSystemKind)2, 1610); + return true; + case 3705: + cacheIndex = 1611; + reference = new EpsgCoordinateReferenceRecord(3705, (EpsgCoordinateSystemKind)2, 1611); + return true; + case 3706: + cacheIndex = 1612; + reference = new EpsgCoordinateReferenceRecord(3706, (EpsgCoordinateSystemKind)2, 1612); + return true; + case 3707: + cacheIndex = 1613; + reference = new EpsgCoordinateReferenceRecord(3707, (EpsgCoordinateSystemKind)2, 1613); + return true; + case 3708: + cacheIndex = 1614; + reference = new EpsgCoordinateReferenceRecord(3708, (EpsgCoordinateSystemKind)2, 1614); + return true; + case 3709: + cacheIndex = 1615; + reference = new EpsgCoordinateReferenceRecord(3709, (EpsgCoordinateSystemKind)2, 1615); + return true; + case 3710: + cacheIndex = 1616; + reference = new EpsgCoordinateReferenceRecord(3710, (EpsgCoordinateSystemKind)2, 1616); + return true; + case 3711: + cacheIndex = 1617; + reference = new EpsgCoordinateReferenceRecord(3711, (EpsgCoordinateSystemKind)2, 1617); + return true; + case 3712: + cacheIndex = 1618; + reference = new EpsgCoordinateReferenceRecord(3712, (EpsgCoordinateSystemKind)2, 1618); + return true; + case 3713: + cacheIndex = 1619; + reference = new EpsgCoordinateReferenceRecord(3713, (EpsgCoordinateSystemKind)2, 1619); + return true; + case 3714: + cacheIndex = 1620; + reference = new EpsgCoordinateReferenceRecord(3714, (EpsgCoordinateSystemKind)2, 1620); + return true; + case 3715: + cacheIndex = 1621; + reference = new EpsgCoordinateReferenceRecord(3715, (EpsgCoordinateSystemKind)2, 1621); + return true; + case 3716: + cacheIndex = 1622; + reference = new EpsgCoordinateReferenceRecord(3716, (EpsgCoordinateSystemKind)2, 1622); + return true; + case 3717: + cacheIndex = 1623; + reference = new EpsgCoordinateReferenceRecord(3717, (EpsgCoordinateSystemKind)2, 1623); + return true; + case 3718: + cacheIndex = 1624; + reference = new EpsgCoordinateReferenceRecord(3718, (EpsgCoordinateSystemKind)2, 1624); + return true; + case 3719: + cacheIndex = 1625; + reference = new EpsgCoordinateReferenceRecord(3719, (EpsgCoordinateSystemKind)2, 1625); + return true; + case 3720: + cacheIndex = 1626; + reference = new EpsgCoordinateReferenceRecord(3720, (EpsgCoordinateSystemKind)2, 1626); + return true; + case 3721: + cacheIndex = 1627; + reference = new EpsgCoordinateReferenceRecord(3721, (EpsgCoordinateSystemKind)2, 1627); + return true; + case 3722: + cacheIndex = 1628; + reference = new EpsgCoordinateReferenceRecord(3722, (EpsgCoordinateSystemKind)2, 1628); + return true; + case 3723: + cacheIndex = 1629; + reference = new EpsgCoordinateReferenceRecord(3723, (EpsgCoordinateSystemKind)2, 1629); + return true; + case 3724: + cacheIndex = 1630; + reference = new EpsgCoordinateReferenceRecord(3724, (EpsgCoordinateSystemKind)2, 1630); + return true; + case 3725: + cacheIndex = 1631; + reference = new EpsgCoordinateReferenceRecord(3725, (EpsgCoordinateSystemKind)2, 1631); + return true; + case 3726: + cacheIndex = 1632; + reference = new EpsgCoordinateReferenceRecord(3726, (EpsgCoordinateSystemKind)2, 1632); + return true; + case 3727: + cacheIndex = 1633; + reference = new EpsgCoordinateReferenceRecord(3727, (EpsgCoordinateSystemKind)2, 1633); + return true; + case 3728: + cacheIndex = 1634; + reference = new EpsgCoordinateReferenceRecord(3728, (EpsgCoordinateSystemKind)2, 1634); + return true; + case 3729: + cacheIndex = 1635; + reference = new EpsgCoordinateReferenceRecord(3729, (EpsgCoordinateSystemKind)2, 1635); + return true; + case 3730: + cacheIndex = 1636; + reference = new EpsgCoordinateReferenceRecord(3730, (EpsgCoordinateSystemKind)2, 1636); + return true; + case 3731: + cacheIndex = 1637; + reference = new EpsgCoordinateReferenceRecord(3731, (EpsgCoordinateSystemKind)2, 1637); + return true; + case 3732: + cacheIndex = 1638; + reference = new EpsgCoordinateReferenceRecord(3732, (EpsgCoordinateSystemKind)2, 1638); + return true; + case 3733: + cacheIndex = 1639; + reference = new EpsgCoordinateReferenceRecord(3733, (EpsgCoordinateSystemKind)2, 1639); + return true; + case 3734: + cacheIndex = 1640; + reference = new EpsgCoordinateReferenceRecord(3734, (EpsgCoordinateSystemKind)2, 1640); + return true; + case 3735: + cacheIndex = 1641; + reference = new EpsgCoordinateReferenceRecord(3735, (EpsgCoordinateSystemKind)2, 1641); + return true; + case 3736: + cacheIndex = 1642; + reference = new EpsgCoordinateReferenceRecord(3736, (EpsgCoordinateSystemKind)2, 1642); + return true; + case 3737: + cacheIndex = 1643; + reference = new EpsgCoordinateReferenceRecord(3737, (EpsgCoordinateSystemKind)2, 1643); + return true; + case 3738: + cacheIndex = 1644; + reference = new EpsgCoordinateReferenceRecord(3738, (EpsgCoordinateSystemKind)2, 1644); + return true; + case 3739: + cacheIndex = 1645; + reference = new EpsgCoordinateReferenceRecord(3739, (EpsgCoordinateSystemKind)2, 1645); + return true; + case 3740: + cacheIndex = 1646; + reference = new EpsgCoordinateReferenceRecord(3740, (EpsgCoordinateSystemKind)2, 1646); + return true; + case 3741: + cacheIndex = 1647; + reference = new EpsgCoordinateReferenceRecord(3741, (EpsgCoordinateSystemKind)2, 1647); + return true; + case 3742: + cacheIndex = 1648; + reference = new EpsgCoordinateReferenceRecord(3742, (EpsgCoordinateSystemKind)2, 1648); + return true; + case 3743: + cacheIndex = 1649; + reference = new EpsgCoordinateReferenceRecord(3743, (EpsgCoordinateSystemKind)2, 1649); + return true; + case 3744: + cacheIndex = 1650; + reference = new EpsgCoordinateReferenceRecord(3744, (EpsgCoordinateSystemKind)2, 1650); + return true; + case 3745: + cacheIndex = 1651; + reference = new EpsgCoordinateReferenceRecord(3745, (EpsgCoordinateSystemKind)2, 1651); + return true; + case 3746: + cacheIndex = 1652; + reference = new EpsgCoordinateReferenceRecord(3746, (EpsgCoordinateSystemKind)2, 1652); + return true; + case 3747: + cacheIndex = 1653; + reference = new EpsgCoordinateReferenceRecord(3747, (EpsgCoordinateSystemKind)2, 1653); + return true; + case 3748: + cacheIndex = 1654; + reference = new EpsgCoordinateReferenceRecord(3748, (EpsgCoordinateSystemKind)2, 1654); + return true; + case 3749: + cacheIndex = 1655; + reference = new EpsgCoordinateReferenceRecord(3749, (EpsgCoordinateSystemKind)2, 1655); + return true; + case 3750: + cacheIndex = 1656; + reference = new EpsgCoordinateReferenceRecord(3750, (EpsgCoordinateSystemKind)2, 1656); + return true; + case 3751: + cacheIndex = 1657; + reference = new EpsgCoordinateReferenceRecord(3751, (EpsgCoordinateSystemKind)2, 1657); + return true; + case 3753: + cacheIndex = 1658; + reference = new EpsgCoordinateReferenceRecord(3753, (EpsgCoordinateSystemKind)2, 1658); + return true; + case 3754: + cacheIndex = 1659; + reference = new EpsgCoordinateReferenceRecord(3754, (EpsgCoordinateSystemKind)2, 1659); + return true; + case 3755: + cacheIndex = 1660; + reference = new EpsgCoordinateReferenceRecord(3755, (EpsgCoordinateSystemKind)2, 1660); + return true; + case 3756: + cacheIndex = 1661; + reference = new EpsgCoordinateReferenceRecord(3756, (EpsgCoordinateSystemKind)2, 1661); + return true; + case 3757: + cacheIndex = 1662; + reference = new EpsgCoordinateReferenceRecord(3757, (EpsgCoordinateSystemKind)2, 1662); + return true; + case 3758: + cacheIndex = 1663; + reference = new EpsgCoordinateReferenceRecord(3758, (EpsgCoordinateSystemKind)2, 1663); + return true; + case 3759: + cacheIndex = 1664; + reference = new EpsgCoordinateReferenceRecord(3759, (EpsgCoordinateSystemKind)2, 1664); + return true; + case 3760: + cacheIndex = 1665; + reference = new EpsgCoordinateReferenceRecord(3760, (EpsgCoordinateSystemKind)2, 1665); + return true; + case 3761: + cacheIndex = 1666; + reference = new EpsgCoordinateReferenceRecord(3761, (EpsgCoordinateSystemKind)2, 1666); + return true; + case 3762: + cacheIndex = 1667; + reference = new EpsgCoordinateReferenceRecord(3762, (EpsgCoordinateSystemKind)2, 1667); + return true; + case 3763: + cacheIndex = 1668; + reference = new EpsgCoordinateReferenceRecord(3763, (EpsgCoordinateSystemKind)2, 1668); + return true; + case 3764: + cacheIndex = 1669; + reference = new EpsgCoordinateReferenceRecord(3764, (EpsgCoordinateSystemKind)2, 1669); + return true; + case 3765: + cacheIndex = 1670; + reference = new EpsgCoordinateReferenceRecord(3765, (EpsgCoordinateSystemKind)2, 1670); + return true; + case 3766: + cacheIndex = 1671; + reference = new EpsgCoordinateReferenceRecord(3766, (EpsgCoordinateSystemKind)2, 1671); + return true; + case 3767: + cacheIndex = 1672; + reference = new EpsgCoordinateReferenceRecord(3767, (EpsgCoordinateSystemKind)2, 1672); + return true; + case 3768: + cacheIndex = 1673; + reference = new EpsgCoordinateReferenceRecord(3768, (EpsgCoordinateSystemKind)2, 1673); + return true; + case 3769: + cacheIndex = 1674; + reference = new EpsgCoordinateReferenceRecord(3769, (EpsgCoordinateSystemKind)2, 1674); + return true; + case 3770: + cacheIndex = 1675; + reference = new EpsgCoordinateReferenceRecord(3770, (EpsgCoordinateSystemKind)2, 1675); + return true; + case 3771: + cacheIndex = 1676; + reference = new EpsgCoordinateReferenceRecord(3771, (EpsgCoordinateSystemKind)2, 1676); + return true; + case 3772: + cacheIndex = 1677; + reference = new EpsgCoordinateReferenceRecord(3772, (EpsgCoordinateSystemKind)2, 1677); + return true; + case 3773: + cacheIndex = 1678; + reference = new EpsgCoordinateReferenceRecord(3773, (EpsgCoordinateSystemKind)2, 1678); + return true; + case 3775: + cacheIndex = 1679; + reference = new EpsgCoordinateReferenceRecord(3775, (EpsgCoordinateSystemKind)2, 1679); + return true; + case 3776: + cacheIndex = 1680; + reference = new EpsgCoordinateReferenceRecord(3776, (EpsgCoordinateSystemKind)2, 1680); + return true; + case 3777: + cacheIndex = 1681; + reference = new EpsgCoordinateReferenceRecord(3777, (EpsgCoordinateSystemKind)2, 1681); + return true; + case 3779: + cacheIndex = 1682; + reference = new EpsgCoordinateReferenceRecord(3779, (EpsgCoordinateSystemKind)2, 1682); + return true; + case 3780: + cacheIndex = 1683; + reference = new EpsgCoordinateReferenceRecord(3780, (EpsgCoordinateSystemKind)2, 1683); + return true; + case 3781: + cacheIndex = 1684; + reference = new EpsgCoordinateReferenceRecord(3781, (EpsgCoordinateSystemKind)2, 1684); + return true; + case 3783: + cacheIndex = 1685; + reference = new EpsgCoordinateReferenceRecord(3783, (EpsgCoordinateSystemKind)2, 1685); + return true; + case 3784: + cacheIndex = 1686; + reference = new EpsgCoordinateReferenceRecord(3784, (EpsgCoordinateSystemKind)2, 1686); + return true; + case 3788: + cacheIndex = 1687; + reference = new EpsgCoordinateReferenceRecord(3788, (EpsgCoordinateSystemKind)2, 1687); + return true; + case 3789: + cacheIndex = 1688; + reference = new EpsgCoordinateReferenceRecord(3789, (EpsgCoordinateSystemKind)2, 1688); + return true; + case 3790: + cacheIndex = 1689; + reference = new EpsgCoordinateReferenceRecord(3790, (EpsgCoordinateSystemKind)2, 1689); + return true; + case 3791: + cacheIndex = 1690; + reference = new EpsgCoordinateReferenceRecord(3791, (EpsgCoordinateSystemKind)2, 1690); + return true; + case 3793: + cacheIndex = 1691; + reference = new EpsgCoordinateReferenceRecord(3793, (EpsgCoordinateSystemKind)2, 1691); + return true; + case 3794: + cacheIndex = 1692; + reference = new EpsgCoordinateReferenceRecord(3794, (EpsgCoordinateSystemKind)2, 1692); + return true; + case 3795: + cacheIndex = 1693; + reference = new EpsgCoordinateReferenceRecord(3795, (EpsgCoordinateSystemKind)2, 1693); + return true; + case 3796: + cacheIndex = 1694; + reference = new EpsgCoordinateReferenceRecord(3796, (EpsgCoordinateSystemKind)2, 1694); + return true; + case 3797: + cacheIndex = 1695; + reference = new EpsgCoordinateReferenceRecord(3797, (EpsgCoordinateSystemKind)2, 1695); + return true; + case 3798: + cacheIndex = 1696; + reference = new EpsgCoordinateReferenceRecord(3798, (EpsgCoordinateSystemKind)2, 1696); + return true; + case 3799: + cacheIndex = 1697; + reference = new EpsgCoordinateReferenceRecord(3799, (EpsgCoordinateSystemKind)2, 1697); + return true; + case 3800: + cacheIndex = 1698; + reference = new EpsgCoordinateReferenceRecord(3800, (EpsgCoordinateSystemKind)2, 1698); + return true; + case 3801: + cacheIndex = 1699; + reference = new EpsgCoordinateReferenceRecord(3801, (EpsgCoordinateSystemKind)2, 1699); + return true; + case 3802: + cacheIndex = 1700; + reference = new EpsgCoordinateReferenceRecord(3802, (EpsgCoordinateSystemKind)2, 1700); + return true; + case 3812: + cacheIndex = 1701; + reference = new EpsgCoordinateReferenceRecord(3812, (EpsgCoordinateSystemKind)2, 1701); + return true; + case 3814: + cacheIndex = 1702; + reference = new EpsgCoordinateReferenceRecord(3814, (EpsgCoordinateSystemKind)2, 1702); + return true; + case 3815: + cacheIndex = 1703; + reference = new EpsgCoordinateReferenceRecord(3815, (EpsgCoordinateSystemKind)2, 1703); + return true; + case 3816: + cacheIndex = 1704; + reference = new EpsgCoordinateReferenceRecord(3816, (EpsgCoordinateSystemKind)2, 1704); + return true; + case 3819: + cacheIndex = 1705; + reference = new EpsgCoordinateReferenceRecord(3819, (EpsgCoordinateSystemKind)0, 0); + return true; + case 3821: + cacheIndex = 1706; + reference = new EpsgCoordinateReferenceRecord(3821, (EpsgCoordinateSystemKind)0, 1); + return true; + case 3822: + cacheIndex = 1707; + reference = new EpsgCoordinateReferenceRecord(3822, (EpsgCoordinateSystemKind)1, 0); + return true; + case 3823: + cacheIndex = 1708; + reference = new EpsgCoordinateReferenceRecord(3823, (EpsgCoordinateSystemKind)0, 2); + return true; + case 3824: + cacheIndex = 1709; + reference = new EpsgCoordinateReferenceRecord(3824, (EpsgCoordinateSystemKind)0, 3); + return true; + case 3825: + cacheIndex = 1710; + reference = new EpsgCoordinateReferenceRecord(3825, (EpsgCoordinateSystemKind)2, 1705); + return true; + case 3826: + cacheIndex = 1711; + reference = new EpsgCoordinateReferenceRecord(3826, (EpsgCoordinateSystemKind)2, 1706); + return true; + case 3827: + cacheIndex = 1712; + reference = new EpsgCoordinateReferenceRecord(3827, (EpsgCoordinateSystemKind)2, 1707); + return true; + case 3828: + cacheIndex = 1713; + reference = new EpsgCoordinateReferenceRecord(3828, (EpsgCoordinateSystemKind)2, 1708); + return true; + case 3829: + cacheIndex = 1714; + reference = new EpsgCoordinateReferenceRecord(3829, (EpsgCoordinateSystemKind)2, 1709); + return true; + case 3832: + cacheIndex = 1715; + reference = new EpsgCoordinateReferenceRecord(3832, (EpsgCoordinateSystemKind)2, 1710); + return true; + case 3833: + cacheIndex = 1716; + reference = new EpsgCoordinateReferenceRecord(3833, (EpsgCoordinateSystemKind)2, 1711); + return true; + case 3834: + cacheIndex = 1717; + reference = new EpsgCoordinateReferenceRecord(3834, (EpsgCoordinateSystemKind)2, 1712); + return true; + case 3835: + cacheIndex = 1718; + reference = new EpsgCoordinateReferenceRecord(3835, (EpsgCoordinateSystemKind)2, 1713); + return true; + case 3836: + cacheIndex = 1719; + reference = new EpsgCoordinateReferenceRecord(3836, (EpsgCoordinateSystemKind)2, 1714); + return true; + case 3837: + cacheIndex = 1720; + reference = new EpsgCoordinateReferenceRecord(3837, (EpsgCoordinateSystemKind)2, 1715); + return true; + case 3838: + cacheIndex = 1721; + reference = new EpsgCoordinateReferenceRecord(3838, (EpsgCoordinateSystemKind)2, 1716); + return true; + case 3839: + cacheIndex = 1722; + reference = new EpsgCoordinateReferenceRecord(3839, (EpsgCoordinateSystemKind)2, 1717); + return true; + case 3840: + cacheIndex = 1723; + reference = new EpsgCoordinateReferenceRecord(3840, (EpsgCoordinateSystemKind)2, 1718); + return true; + case 3841: + cacheIndex = 1724; + reference = new EpsgCoordinateReferenceRecord(3841, (EpsgCoordinateSystemKind)2, 1719); + return true; + case 3844: + cacheIndex = 1725; + reference = new EpsgCoordinateReferenceRecord(3844, (EpsgCoordinateSystemKind)2, 1720); + return true; + case 3845: + cacheIndex = 1726; + reference = new EpsgCoordinateReferenceRecord(3845, (EpsgCoordinateSystemKind)2, 1721); + return true; + case 3846: + cacheIndex = 1727; + reference = new EpsgCoordinateReferenceRecord(3846, (EpsgCoordinateSystemKind)2, 1722); + return true; + case 3847: + cacheIndex = 1728; + reference = new EpsgCoordinateReferenceRecord(3847, (EpsgCoordinateSystemKind)2, 1723); + return true; + case 3848: + cacheIndex = 1729; + reference = new EpsgCoordinateReferenceRecord(3848, (EpsgCoordinateSystemKind)2, 1724); + return true; + case 3849: + cacheIndex = 1730; + reference = new EpsgCoordinateReferenceRecord(3849, (EpsgCoordinateSystemKind)2, 1725); + return true; + case 3850: + cacheIndex = 1731; + reference = new EpsgCoordinateReferenceRecord(3850, (EpsgCoordinateSystemKind)2, 1726); + return true; + case 3851: + cacheIndex = 1732; + reference = new EpsgCoordinateReferenceRecord(3851, (EpsgCoordinateSystemKind)2, 1727); + return true; + case 3852: + cacheIndex = 1733; + reference = new EpsgCoordinateReferenceRecord(3852, (EpsgCoordinateSystemKind)2, 1728); + return true; + case 3854: + cacheIndex = 1734; + reference = new EpsgCoordinateReferenceRecord(3854, (EpsgCoordinateSystemKind)2, 1729); + return true; + case 3855: + cacheIndex = 1735; + reference = new EpsgCoordinateReferenceRecord(3855, (EpsgCoordinateSystemKind)3, 0); + return true; + case 3857: + cacheIndex = 1736; + reference = new EpsgCoordinateReferenceRecord(3857, (EpsgCoordinateSystemKind)2, 1730); + return true; + case 3873: + cacheIndex = 1737; + reference = new EpsgCoordinateReferenceRecord(3873, (EpsgCoordinateSystemKind)2, 1731); + return true; + case 3874: + cacheIndex = 1738; + reference = new EpsgCoordinateReferenceRecord(3874, (EpsgCoordinateSystemKind)2, 1732); + return true; + case 3875: + cacheIndex = 1739; + reference = new EpsgCoordinateReferenceRecord(3875, (EpsgCoordinateSystemKind)2, 1733); + return true; + case 3876: + cacheIndex = 1740; + reference = new EpsgCoordinateReferenceRecord(3876, (EpsgCoordinateSystemKind)2, 1734); + return true; + case 3877: + cacheIndex = 1741; + reference = new EpsgCoordinateReferenceRecord(3877, (EpsgCoordinateSystemKind)2, 1735); + return true; + case 3878: + cacheIndex = 1742; + reference = new EpsgCoordinateReferenceRecord(3878, (EpsgCoordinateSystemKind)2, 1736); + return true; + case 3879: + cacheIndex = 1743; + reference = new EpsgCoordinateReferenceRecord(3879, (EpsgCoordinateSystemKind)2, 1737); + return true; + case 3880: + cacheIndex = 1744; + reference = new EpsgCoordinateReferenceRecord(3880, (EpsgCoordinateSystemKind)2, 1738); + return true; + case 3881: + cacheIndex = 1745; + reference = new EpsgCoordinateReferenceRecord(3881, (EpsgCoordinateSystemKind)2, 1739); + return true; + case 3882: + cacheIndex = 1746; + reference = new EpsgCoordinateReferenceRecord(3882, (EpsgCoordinateSystemKind)2, 1740); + return true; + case 3883: + cacheIndex = 1747; + reference = new EpsgCoordinateReferenceRecord(3883, (EpsgCoordinateSystemKind)2, 1741); + return true; + case 3884: + cacheIndex = 1748; + reference = new EpsgCoordinateReferenceRecord(3884, (EpsgCoordinateSystemKind)2, 1742); + return true; + case 3885: + cacheIndex = 1749; + reference = new EpsgCoordinateReferenceRecord(3885, (EpsgCoordinateSystemKind)2, 1743); + return true; + case 3886: + cacheIndex = 1750; + reference = new EpsgCoordinateReferenceRecord(3886, (EpsgCoordinateSystemKind)3, 1); + return true; + case 3887: + cacheIndex = 1751; + reference = new EpsgCoordinateReferenceRecord(3887, (EpsgCoordinateSystemKind)1, 1); + return true; + case 3888: + cacheIndex = 1752; + reference = new EpsgCoordinateReferenceRecord(3888, (EpsgCoordinateSystemKind)0, 4); + return true; + case 3889: + cacheIndex = 1753; + reference = new EpsgCoordinateReferenceRecord(3889, (EpsgCoordinateSystemKind)0, 5); + return true; + case 3890: + cacheIndex = 1754; + reference = new EpsgCoordinateReferenceRecord(3890, (EpsgCoordinateSystemKind)2, 1744); + return true; + case 3891: + cacheIndex = 1755; + reference = new EpsgCoordinateReferenceRecord(3891, (EpsgCoordinateSystemKind)2, 1745); + return true; + case 3892: + cacheIndex = 1756; + reference = new EpsgCoordinateReferenceRecord(3892, (EpsgCoordinateSystemKind)2, 1746); + return true; + case 3893: + cacheIndex = 1757; + reference = new EpsgCoordinateReferenceRecord(3893, (EpsgCoordinateSystemKind)2, 1747); + return true; + case 3900: + cacheIndex = 1758; + reference = new EpsgCoordinateReferenceRecord(3900, (EpsgCoordinateSystemKind)3, 2); + return true; + case 3901: + cacheIndex = 1759; + reference = new EpsgCoordinateReferenceRecord(3901, (EpsgCoordinateSystemKind)4, 0); + return true; + case 3902: + cacheIndex = 1760; + reference = new EpsgCoordinateReferenceRecord(3902, (EpsgCoordinateSystemKind)4, 1); + return true; + case 3903: + cacheIndex = 1761; + reference = new EpsgCoordinateReferenceRecord(3903, (EpsgCoordinateSystemKind)4, 2); + return true; + case 3906: + cacheIndex = 1762; + reference = new EpsgCoordinateReferenceRecord(3906, (EpsgCoordinateSystemKind)0, 6); + return true; + case 3912: + cacheIndex = 1763; + reference = new EpsgCoordinateReferenceRecord(3912, (EpsgCoordinateSystemKind)2, 1748); + return true; + case 3920: + cacheIndex = 1764; + reference = new EpsgCoordinateReferenceRecord(3920, (EpsgCoordinateSystemKind)2, 1749); + return true; + case 3942: + cacheIndex = 1765; + reference = new EpsgCoordinateReferenceRecord(3942, (EpsgCoordinateSystemKind)2, 1750); + return true; + case 3943: + cacheIndex = 1766; + reference = new EpsgCoordinateReferenceRecord(3943, (EpsgCoordinateSystemKind)2, 1751); + return true; + case 3944: + cacheIndex = 1767; + reference = new EpsgCoordinateReferenceRecord(3944, (EpsgCoordinateSystemKind)2, 1752); + return true; + case 3945: + cacheIndex = 1768; + reference = new EpsgCoordinateReferenceRecord(3945, (EpsgCoordinateSystemKind)2, 1753); + return true; + case 3946: + cacheIndex = 1769; + reference = new EpsgCoordinateReferenceRecord(3946, (EpsgCoordinateSystemKind)2, 1754); + return true; + case 3947: + cacheIndex = 1770; + reference = new EpsgCoordinateReferenceRecord(3947, (EpsgCoordinateSystemKind)2, 1755); + return true; + case 3948: + cacheIndex = 1771; + reference = new EpsgCoordinateReferenceRecord(3948, (EpsgCoordinateSystemKind)2, 1756); + return true; + case 3949: + cacheIndex = 1772; + reference = new EpsgCoordinateReferenceRecord(3949, (EpsgCoordinateSystemKind)2, 1757); + return true; + case 3950: + cacheIndex = 1773; + reference = new EpsgCoordinateReferenceRecord(3950, (EpsgCoordinateSystemKind)2, 1758); + return true; + case 3968: + cacheIndex = 1774; + reference = new EpsgCoordinateReferenceRecord(3968, (EpsgCoordinateSystemKind)2, 1759); + return true; + case 3969: + cacheIndex = 1775; + reference = new EpsgCoordinateReferenceRecord(3969, (EpsgCoordinateSystemKind)2, 1760); + return true; + case 3970: + cacheIndex = 1776; + reference = new EpsgCoordinateReferenceRecord(3970, (EpsgCoordinateSystemKind)2, 1761); + return true; + case 3976: + cacheIndex = 1777; + reference = new EpsgCoordinateReferenceRecord(3976, (EpsgCoordinateSystemKind)2, 1762); + return true; + case 3978: + cacheIndex = 1778; + reference = new EpsgCoordinateReferenceRecord(3978, (EpsgCoordinateSystemKind)2, 1763); + return true; + case 3979: + cacheIndex = 1779; + reference = new EpsgCoordinateReferenceRecord(3979, (EpsgCoordinateSystemKind)2, 1764); + return true; + case 3986: + cacheIndex = 1780; + reference = new EpsgCoordinateReferenceRecord(3986, (EpsgCoordinateSystemKind)2, 1765); + return true; + case 3987: + cacheIndex = 1781; + reference = new EpsgCoordinateReferenceRecord(3987, (EpsgCoordinateSystemKind)2, 1766); + return true; + case 3988: + cacheIndex = 1782; + reference = new EpsgCoordinateReferenceRecord(3988, (EpsgCoordinateSystemKind)2, 1767); + return true; + case 3989: + cacheIndex = 1783; + reference = new EpsgCoordinateReferenceRecord(3989, (EpsgCoordinateSystemKind)2, 1768); + return true; + case 3991: + cacheIndex = 1784; + reference = new EpsgCoordinateReferenceRecord(3991, (EpsgCoordinateSystemKind)2, 1769); + return true; + case 3992: + cacheIndex = 1785; + reference = new EpsgCoordinateReferenceRecord(3992, (EpsgCoordinateSystemKind)2, 1770); + return true; + case 3993: + cacheIndex = 1786; + reference = new EpsgCoordinateReferenceRecord(3993, (EpsgCoordinateSystemKind)2, 1771); + return true; + case 3994: + cacheIndex = 1787; + reference = new EpsgCoordinateReferenceRecord(3994, (EpsgCoordinateSystemKind)2, 1772); + return true; + case 3995: + cacheIndex = 1788; + reference = new EpsgCoordinateReferenceRecord(3995, (EpsgCoordinateSystemKind)2, 1773); + return true; + case 3996: + cacheIndex = 1789; + reference = new EpsgCoordinateReferenceRecord(3996, (EpsgCoordinateSystemKind)2, 1774); + return true; + case 3997: + cacheIndex = 1790; + reference = new EpsgCoordinateReferenceRecord(3997, (EpsgCoordinateSystemKind)2, 1775); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket4(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 4000: + cacheIndex = 1791; + reference = new EpsgCoordinateReferenceRecord(4000, (EpsgCoordinateSystemKind)1, 2); + return true; + case 4017: + cacheIndex = 1792; + reference = new EpsgCoordinateReferenceRecord(4017, (EpsgCoordinateSystemKind)0, 7); + return true; + case 4023: + cacheIndex = 1793; + reference = new EpsgCoordinateReferenceRecord(4023, (EpsgCoordinateSystemKind)0, 8); + return true; + case 4026: + cacheIndex = 1794; + reference = new EpsgCoordinateReferenceRecord(4026, (EpsgCoordinateSystemKind)2, 1776); + return true; + case 4037: + cacheIndex = 1795; + reference = new EpsgCoordinateReferenceRecord(4037, (EpsgCoordinateSystemKind)2, 1777); + return true; + case 4038: + cacheIndex = 1796; + reference = new EpsgCoordinateReferenceRecord(4038, (EpsgCoordinateSystemKind)2, 1778); + return true; + case 4039: + cacheIndex = 1797; + reference = new EpsgCoordinateReferenceRecord(4039, (EpsgCoordinateSystemKind)1, 3); + return true; + case 4040: + cacheIndex = 1798; + reference = new EpsgCoordinateReferenceRecord(4040, (EpsgCoordinateSystemKind)0, 9); + return true; + case 4046: + cacheIndex = 1799; + reference = new EpsgCoordinateReferenceRecord(4046, (EpsgCoordinateSystemKind)0, 10); + return true; + case 4048: + cacheIndex = 1800; + reference = new EpsgCoordinateReferenceRecord(4048, (EpsgCoordinateSystemKind)2, 1779); + return true; + case 4049: + cacheIndex = 1801; + reference = new EpsgCoordinateReferenceRecord(4049, (EpsgCoordinateSystemKind)2, 1780); + return true; + case 4050: + cacheIndex = 1802; + reference = new EpsgCoordinateReferenceRecord(4050, (EpsgCoordinateSystemKind)2, 1781); + return true; + case 4051: + cacheIndex = 1803; + reference = new EpsgCoordinateReferenceRecord(4051, (EpsgCoordinateSystemKind)2, 1782); + return true; + case 4056: + cacheIndex = 1804; + reference = new EpsgCoordinateReferenceRecord(4056, (EpsgCoordinateSystemKind)2, 1783); + return true; + case 4057: + cacheIndex = 1805; + reference = new EpsgCoordinateReferenceRecord(4057, (EpsgCoordinateSystemKind)2, 1784); + return true; + case 4058: + cacheIndex = 1806; + reference = new EpsgCoordinateReferenceRecord(4058, (EpsgCoordinateSystemKind)2, 1785); + return true; + case 4059: + cacheIndex = 1807; + reference = new EpsgCoordinateReferenceRecord(4059, (EpsgCoordinateSystemKind)2, 1786); + return true; + case 4060: + cacheIndex = 1808; + reference = new EpsgCoordinateReferenceRecord(4060, (EpsgCoordinateSystemKind)2, 1787); + return true; + case 4061: + cacheIndex = 1809; + reference = new EpsgCoordinateReferenceRecord(4061, (EpsgCoordinateSystemKind)2, 1788); + return true; + case 4062: + cacheIndex = 1810; + reference = new EpsgCoordinateReferenceRecord(4062, (EpsgCoordinateSystemKind)2, 1789); + return true; + case 4063: + cacheIndex = 1811; + reference = new EpsgCoordinateReferenceRecord(4063, (EpsgCoordinateSystemKind)2, 1790); + return true; + case 4071: + cacheIndex = 1812; + reference = new EpsgCoordinateReferenceRecord(4071, (EpsgCoordinateSystemKind)2, 1791); + return true; + case 4073: + cacheIndex = 1813; + reference = new EpsgCoordinateReferenceRecord(4073, (EpsgCoordinateSystemKind)1, 4); + return true; + case 4074: + cacheIndex = 1814; + reference = new EpsgCoordinateReferenceRecord(4074, (EpsgCoordinateSystemKind)0, 11); + return true; + case 4075: + cacheIndex = 1815; + reference = new EpsgCoordinateReferenceRecord(4075, (EpsgCoordinateSystemKind)0, 12); + return true; + case 4079: + cacheIndex = 1816; + reference = new EpsgCoordinateReferenceRecord(4079, (EpsgCoordinateSystemKind)1, 5); + return true; + case 4080: + cacheIndex = 1817; + reference = new EpsgCoordinateReferenceRecord(4080, (EpsgCoordinateSystemKind)0, 13); + return true; + case 4081: + cacheIndex = 1818; + reference = new EpsgCoordinateReferenceRecord(4081, (EpsgCoordinateSystemKind)0, 14); + return true; + case 4082: + cacheIndex = 1819; + reference = new EpsgCoordinateReferenceRecord(4082, (EpsgCoordinateSystemKind)2, 1792); + return true; + case 4083: + cacheIndex = 1820; + reference = new EpsgCoordinateReferenceRecord(4083, (EpsgCoordinateSystemKind)2, 1793); + return true; + case 4087: + cacheIndex = 1821; + reference = new EpsgCoordinateReferenceRecord(4087, (EpsgCoordinateSystemKind)2, 1794); + return true; + case 4093: + cacheIndex = 1822; + reference = new EpsgCoordinateReferenceRecord(4093, (EpsgCoordinateSystemKind)2, 1795); + return true; + case 4094: + cacheIndex = 1823; + reference = new EpsgCoordinateReferenceRecord(4094, (EpsgCoordinateSystemKind)2, 1796); + return true; + case 4095: + cacheIndex = 1824; + reference = new EpsgCoordinateReferenceRecord(4095, (EpsgCoordinateSystemKind)2, 1797); + return true; + case 4096: + cacheIndex = 1825; + reference = new EpsgCoordinateReferenceRecord(4096, (EpsgCoordinateSystemKind)2, 1798); + return true; + case 4120: + cacheIndex = 1826; + reference = new EpsgCoordinateReferenceRecord(4120, (EpsgCoordinateSystemKind)0, 15); + return true; + case 4121: + cacheIndex = 1827; + reference = new EpsgCoordinateReferenceRecord(4121, (EpsgCoordinateSystemKind)0, 16); + return true; + case 4122: + cacheIndex = 1828; + reference = new EpsgCoordinateReferenceRecord(4122, (EpsgCoordinateSystemKind)0, 17); + return true; + case 4123: + cacheIndex = 1829; + reference = new EpsgCoordinateReferenceRecord(4123, (EpsgCoordinateSystemKind)0, 18); + return true; + case 4124: + cacheIndex = 1830; + reference = new EpsgCoordinateReferenceRecord(4124, (EpsgCoordinateSystemKind)0, 19); + return true; + case 4127: + cacheIndex = 1831; + reference = new EpsgCoordinateReferenceRecord(4127, (EpsgCoordinateSystemKind)0, 20); + return true; + case 4128: + cacheIndex = 1832; + reference = new EpsgCoordinateReferenceRecord(4128, (EpsgCoordinateSystemKind)0, 21); + return true; + case 4129: + cacheIndex = 1833; + reference = new EpsgCoordinateReferenceRecord(4129, (EpsgCoordinateSystemKind)0, 22); + return true; + case 4130: + cacheIndex = 1834; + reference = new EpsgCoordinateReferenceRecord(4130, (EpsgCoordinateSystemKind)0, 23); + return true; + case 4131: + cacheIndex = 1835; + reference = new EpsgCoordinateReferenceRecord(4131, (EpsgCoordinateSystemKind)0, 24); + return true; + case 4132: + cacheIndex = 1836; + reference = new EpsgCoordinateReferenceRecord(4132, (EpsgCoordinateSystemKind)0, 25); + return true; + case 4133: + cacheIndex = 1837; + reference = new EpsgCoordinateReferenceRecord(4133, (EpsgCoordinateSystemKind)0, 26); + return true; + case 4134: + cacheIndex = 1838; + reference = new EpsgCoordinateReferenceRecord(4134, (EpsgCoordinateSystemKind)0, 27); + return true; + case 4135: + cacheIndex = 1839; + reference = new EpsgCoordinateReferenceRecord(4135, (EpsgCoordinateSystemKind)0, 28); + return true; + case 4136: + cacheIndex = 1840; + reference = new EpsgCoordinateReferenceRecord(4136, (EpsgCoordinateSystemKind)0, 29); + return true; + case 4137: + cacheIndex = 1841; + reference = new EpsgCoordinateReferenceRecord(4137, (EpsgCoordinateSystemKind)0, 30); + return true; + case 4138: + cacheIndex = 1842; + reference = new EpsgCoordinateReferenceRecord(4138, (EpsgCoordinateSystemKind)0, 31); + return true; + case 4139: + cacheIndex = 1843; + reference = new EpsgCoordinateReferenceRecord(4139, (EpsgCoordinateSystemKind)0, 32); + return true; + case 4141: + cacheIndex = 1844; + reference = new EpsgCoordinateReferenceRecord(4141, (EpsgCoordinateSystemKind)0, 33); + return true; + case 4142: + cacheIndex = 1845; + reference = new EpsgCoordinateReferenceRecord(4142, (EpsgCoordinateSystemKind)0, 34); + return true; + case 4143: + cacheIndex = 1846; + reference = new EpsgCoordinateReferenceRecord(4143, (EpsgCoordinateSystemKind)0, 35); + return true; + case 4144: + cacheIndex = 1847; + reference = new EpsgCoordinateReferenceRecord(4144, (EpsgCoordinateSystemKind)0, 36); + return true; + case 4145: + cacheIndex = 1848; + reference = new EpsgCoordinateReferenceRecord(4145, (EpsgCoordinateSystemKind)0, 37); + return true; + case 4146: + cacheIndex = 1849; + reference = new EpsgCoordinateReferenceRecord(4146, (EpsgCoordinateSystemKind)0, 38); + return true; + case 4147: + cacheIndex = 1850; + reference = new EpsgCoordinateReferenceRecord(4147, (EpsgCoordinateSystemKind)0, 39); + return true; + case 4148: + cacheIndex = 1851; + reference = new EpsgCoordinateReferenceRecord(4148, (EpsgCoordinateSystemKind)0, 40); + return true; + case 4149: + cacheIndex = 1852; + reference = new EpsgCoordinateReferenceRecord(4149, (EpsgCoordinateSystemKind)0, 41); + return true; + case 4150: + cacheIndex = 1853; + reference = new EpsgCoordinateReferenceRecord(4150, (EpsgCoordinateSystemKind)0, 42); + return true; + case 4151: + cacheIndex = 1854; + reference = new EpsgCoordinateReferenceRecord(4151, (EpsgCoordinateSystemKind)0, 43); + return true; + case 4152: + cacheIndex = 1855; + reference = new EpsgCoordinateReferenceRecord(4152, (EpsgCoordinateSystemKind)0, 44); + return true; + case 4153: + cacheIndex = 1856; + reference = new EpsgCoordinateReferenceRecord(4153, (EpsgCoordinateSystemKind)0, 45); + return true; + case 4154: + cacheIndex = 1857; + reference = new EpsgCoordinateReferenceRecord(4154, (EpsgCoordinateSystemKind)0, 46); + return true; + case 4155: + cacheIndex = 1858; + reference = new EpsgCoordinateReferenceRecord(4155, (EpsgCoordinateSystemKind)0, 47); + return true; + case 4156: + cacheIndex = 1859; + reference = new EpsgCoordinateReferenceRecord(4156, (EpsgCoordinateSystemKind)0, 48); + return true; + case 4157: + cacheIndex = 1860; + reference = new EpsgCoordinateReferenceRecord(4157, (EpsgCoordinateSystemKind)0, 49); + return true; + case 4158: + cacheIndex = 1861; + reference = new EpsgCoordinateReferenceRecord(4158, (EpsgCoordinateSystemKind)0, 50); + return true; + case 4159: + cacheIndex = 1862; + reference = new EpsgCoordinateReferenceRecord(4159, (EpsgCoordinateSystemKind)0, 51); + return true; + case 4160: + cacheIndex = 1863; + reference = new EpsgCoordinateReferenceRecord(4160, (EpsgCoordinateSystemKind)0, 52); + return true; + case 4161: + cacheIndex = 1864; + reference = new EpsgCoordinateReferenceRecord(4161, (EpsgCoordinateSystemKind)0, 53); + return true; + case 4162: + cacheIndex = 1865; + reference = new EpsgCoordinateReferenceRecord(4162, (EpsgCoordinateSystemKind)0, 54); + return true; + case 4163: + cacheIndex = 1866; + reference = new EpsgCoordinateReferenceRecord(4163, (EpsgCoordinateSystemKind)0, 55); + return true; + case 4164: + cacheIndex = 1867; + reference = new EpsgCoordinateReferenceRecord(4164, (EpsgCoordinateSystemKind)0, 56); + return true; + case 4165: + cacheIndex = 1868; + reference = new EpsgCoordinateReferenceRecord(4165, (EpsgCoordinateSystemKind)0, 57); + return true; + case 4166: + cacheIndex = 1869; + reference = new EpsgCoordinateReferenceRecord(4166, (EpsgCoordinateSystemKind)0, 58); + return true; + case 4167: + cacheIndex = 1870; + reference = new EpsgCoordinateReferenceRecord(4167, (EpsgCoordinateSystemKind)0, 59); + return true; + case 4168: + cacheIndex = 1871; + reference = new EpsgCoordinateReferenceRecord(4168, (EpsgCoordinateSystemKind)0, 60); + return true; + case 4169: + cacheIndex = 1872; + reference = new EpsgCoordinateReferenceRecord(4169, (EpsgCoordinateSystemKind)0, 61); + return true; + case 4170: + cacheIndex = 1873; + reference = new EpsgCoordinateReferenceRecord(4170, (EpsgCoordinateSystemKind)0, 62); + return true; + case 4171: + cacheIndex = 1874; + reference = new EpsgCoordinateReferenceRecord(4171, (EpsgCoordinateSystemKind)0, 63); + return true; + case 4173: + cacheIndex = 1875; + reference = new EpsgCoordinateReferenceRecord(4173, (EpsgCoordinateSystemKind)0, 64); + return true; + case 4174: + cacheIndex = 1876; + reference = new EpsgCoordinateReferenceRecord(4174, (EpsgCoordinateSystemKind)0, 65); + return true; + case 4175: + cacheIndex = 1877; + reference = new EpsgCoordinateReferenceRecord(4175, (EpsgCoordinateSystemKind)0, 66); + return true; + case 4176: + cacheIndex = 1878; + reference = new EpsgCoordinateReferenceRecord(4176, (EpsgCoordinateSystemKind)0, 67); + return true; + case 4178: + cacheIndex = 1879; + reference = new EpsgCoordinateReferenceRecord(4178, (EpsgCoordinateSystemKind)0, 68); + return true; + case 4179: + cacheIndex = 1880; + reference = new EpsgCoordinateReferenceRecord(4179, (EpsgCoordinateSystemKind)0, 69); + return true; + case 4180: + cacheIndex = 1881; + reference = new EpsgCoordinateReferenceRecord(4180, (EpsgCoordinateSystemKind)0, 70); + return true; + case 4181: + cacheIndex = 1882; + reference = new EpsgCoordinateReferenceRecord(4181, (EpsgCoordinateSystemKind)0, 71); + return true; + case 4182: + cacheIndex = 1883; + reference = new EpsgCoordinateReferenceRecord(4182, (EpsgCoordinateSystemKind)0, 72); + return true; + case 4183: + cacheIndex = 1884; + reference = new EpsgCoordinateReferenceRecord(4183, (EpsgCoordinateSystemKind)0, 73); + return true; + case 4184: + cacheIndex = 1885; + reference = new EpsgCoordinateReferenceRecord(4184, (EpsgCoordinateSystemKind)0, 74); + return true; + case 4188: + cacheIndex = 1886; + reference = new EpsgCoordinateReferenceRecord(4188, (EpsgCoordinateSystemKind)0, 75); + return true; + case 4189: + cacheIndex = 1887; + reference = new EpsgCoordinateReferenceRecord(4189, (EpsgCoordinateSystemKind)0, 76); + return true; + case 4190: + cacheIndex = 1888; + reference = new EpsgCoordinateReferenceRecord(4190, (EpsgCoordinateSystemKind)0, 77); + return true; + case 4191: + cacheIndex = 1889; + reference = new EpsgCoordinateReferenceRecord(4191, (EpsgCoordinateSystemKind)0, 78); + return true; + case 4192: + cacheIndex = 1890; + reference = new EpsgCoordinateReferenceRecord(4192, (EpsgCoordinateSystemKind)0, 79); + return true; + case 4193: + cacheIndex = 1891; + reference = new EpsgCoordinateReferenceRecord(4193, (EpsgCoordinateSystemKind)0, 80); + return true; + case 4194: + cacheIndex = 1892; + reference = new EpsgCoordinateReferenceRecord(4194, (EpsgCoordinateSystemKind)0, 81); + return true; + case 4195: + cacheIndex = 1893; + reference = new EpsgCoordinateReferenceRecord(4195, (EpsgCoordinateSystemKind)0, 82); + return true; + case 4196: + cacheIndex = 1894; + reference = new EpsgCoordinateReferenceRecord(4196, (EpsgCoordinateSystemKind)0, 83); + return true; + case 4197: + cacheIndex = 1895; + reference = new EpsgCoordinateReferenceRecord(4197, (EpsgCoordinateSystemKind)0, 84); + return true; + case 4198: + cacheIndex = 1896; + reference = new EpsgCoordinateReferenceRecord(4198, (EpsgCoordinateSystemKind)0, 85); + return true; + case 4199: + cacheIndex = 1897; + reference = new EpsgCoordinateReferenceRecord(4199, (EpsgCoordinateSystemKind)0, 86); + return true; + case 4200: + cacheIndex = 1898; + reference = new EpsgCoordinateReferenceRecord(4200, (EpsgCoordinateSystemKind)0, 87); + return true; + case 4201: + cacheIndex = 1899; + reference = new EpsgCoordinateReferenceRecord(4201, (EpsgCoordinateSystemKind)0, 88); + return true; + case 4202: + cacheIndex = 1900; + reference = new EpsgCoordinateReferenceRecord(4202, (EpsgCoordinateSystemKind)0, 89); + return true; + case 4203: + cacheIndex = 1901; + reference = new EpsgCoordinateReferenceRecord(4203, (EpsgCoordinateSystemKind)0, 90); + return true; + case 4204: + cacheIndex = 1902; + reference = new EpsgCoordinateReferenceRecord(4204, (EpsgCoordinateSystemKind)0, 91); + return true; + case 4205: + cacheIndex = 1903; + reference = new EpsgCoordinateReferenceRecord(4205, (EpsgCoordinateSystemKind)0, 92); + return true; + case 4206: + cacheIndex = 1904; + reference = new EpsgCoordinateReferenceRecord(4206, (EpsgCoordinateSystemKind)0, 93); + return true; + case 4207: + cacheIndex = 1905; + reference = new EpsgCoordinateReferenceRecord(4207, (EpsgCoordinateSystemKind)0, 94); + return true; + case 4208: + cacheIndex = 1906; + reference = new EpsgCoordinateReferenceRecord(4208, (EpsgCoordinateSystemKind)0, 95); + return true; + case 4209: + cacheIndex = 1907; + reference = new EpsgCoordinateReferenceRecord(4209, (EpsgCoordinateSystemKind)0, 96); + return true; + case 4210: + cacheIndex = 1908; + reference = new EpsgCoordinateReferenceRecord(4210, (EpsgCoordinateSystemKind)0, 97); + return true; + case 4211: + cacheIndex = 1909; + reference = new EpsgCoordinateReferenceRecord(4211, (EpsgCoordinateSystemKind)0, 98); + return true; + case 4212: + cacheIndex = 1910; + reference = new EpsgCoordinateReferenceRecord(4212, (EpsgCoordinateSystemKind)0, 99); + return true; + case 4213: + cacheIndex = 1911; + reference = new EpsgCoordinateReferenceRecord(4213, (EpsgCoordinateSystemKind)0, 100); + return true; + case 4214: + cacheIndex = 1912; + reference = new EpsgCoordinateReferenceRecord(4214, (EpsgCoordinateSystemKind)0, 101); + return true; + case 4215: + cacheIndex = 1913; + reference = new EpsgCoordinateReferenceRecord(4215, (EpsgCoordinateSystemKind)0, 102); + return true; + case 4216: + cacheIndex = 1914; + reference = new EpsgCoordinateReferenceRecord(4216, (EpsgCoordinateSystemKind)0, 103); + return true; + case 4217: + cacheIndex = 1915; + reference = new EpsgCoordinateReferenceRecord(4217, (EpsgCoordinateSystemKind)2, 1799); + return true; + case 4218: + cacheIndex = 1916; + reference = new EpsgCoordinateReferenceRecord(4218, (EpsgCoordinateSystemKind)0, 104); + return true; + case 4219: + cacheIndex = 1917; + reference = new EpsgCoordinateReferenceRecord(4219, (EpsgCoordinateSystemKind)0, 105); + return true; + case 4220: + cacheIndex = 1918; + reference = new EpsgCoordinateReferenceRecord(4220, (EpsgCoordinateSystemKind)0, 106); + return true; + case 4221: + cacheIndex = 1919; + reference = new EpsgCoordinateReferenceRecord(4221, (EpsgCoordinateSystemKind)0, 107); + return true; + case 4222: + cacheIndex = 1920; + reference = new EpsgCoordinateReferenceRecord(4222, (EpsgCoordinateSystemKind)0, 108); + return true; + case 4223: + cacheIndex = 1921; + reference = new EpsgCoordinateReferenceRecord(4223, (EpsgCoordinateSystemKind)0, 109); + return true; + case 4224: + cacheIndex = 1922; + reference = new EpsgCoordinateReferenceRecord(4224, (EpsgCoordinateSystemKind)0, 110); + return true; + case 4225: + cacheIndex = 1923; + reference = new EpsgCoordinateReferenceRecord(4225, (EpsgCoordinateSystemKind)0, 111); + return true; + case 4227: + cacheIndex = 1924; + reference = new EpsgCoordinateReferenceRecord(4227, (EpsgCoordinateSystemKind)0, 112); + return true; + case 4229: + cacheIndex = 1925; + reference = new EpsgCoordinateReferenceRecord(4229, (EpsgCoordinateSystemKind)0, 113); + return true; + case 4230: + cacheIndex = 1926; + reference = new EpsgCoordinateReferenceRecord(4230, (EpsgCoordinateSystemKind)0, 114); + return true; + case 4231: + cacheIndex = 1927; + reference = new EpsgCoordinateReferenceRecord(4231, (EpsgCoordinateSystemKind)0, 115); + return true; + case 4232: + cacheIndex = 1928; + reference = new EpsgCoordinateReferenceRecord(4232, (EpsgCoordinateSystemKind)0, 116); + return true; + case 4236: + cacheIndex = 1929; + reference = new EpsgCoordinateReferenceRecord(4236, (EpsgCoordinateSystemKind)0, 117); + return true; + case 4237: + cacheIndex = 1930; + reference = new EpsgCoordinateReferenceRecord(4237, (EpsgCoordinateSystemKind)0, 118); + return true; + case 4238: + cacheIndex = 1931; + reference = new EpsgCoordinateReferenceRecord(4238, (EpsgCoordinateSystemKind)0, 119); + return true; + case 4239: + cacheIndex = 1932; + reference = new EpsgCoordinateReferenceRecord(4239, (EpsgCoordinateSystemKind)0, 120); + return true; + case 4240: + cacheIndex = 1933; + reference = new EpsgCoordinateReferenceRecord(4240, (EpsgCoordinateSystemKind)0, 121); + return true; + case 4241: + cacheIndex = 1934; + reference = new EpsgCoordinateReferenceRecord(4241, (EpsgCoordinateSystemKind)0, 122); + return true; + case 4242: + cacheIndex = 1935; + reference = new EpsgCoordinateReferenceRecord(4242, (EpsgCoordinateSystemKind)0, 123); + return true; + case 4243: + cacheIndex = 1936; + reference = new EpsgCoordinateReferenceRecord(4243, (EpsgCoordinateSystemKind)0, 124); + return true; + case 4244: + cacheIndex = 1937; + reference = new EpsgCoordinateReferenceRecord(4244, (EpsgCoordinateSystemKind)0, 125); + return true; + case 4245: + cacheIndex = 1938; + reference = new EpsgCoordinateReferenceRecord(4245, (EpsgCoordinateSystemKind)0, 126); + return true; + case 4246: + cacheIndex = 1939; + reference = new EpsgCoordinateReferenceRecord(4246, (EpsgCoordinateSystemKind)0, 127); + return true; + case 4247: + cacheIndex = 1940; + reference = new EpsgCoordinateReferenceRecord(4247, (EpsgCoordinateSystemKind)0, 128); + return true; + case 4248: + cacheIndex = 1941; + reference = new EpsgCoordinateReferenceRecord(4248, (EpsgCoordinateSystemKind)0, 129); + return true; + case 4249: + cacheIndex = 1942; + reference = new EpsgCoordinateReferenceRecord(4249, (EpsgCoordinateSystemKind)0, 130); + return true; + case 4250: + cacheIndex = 1943; + reference = new EpsgCoordinateReferenceRecord(4250, (EpsgCoordinateSystemKind)0, 131); + return true; + case 4251: + cacheIndex = 1944; + reference = new EpsgCoordinateReferenceRecord(4251, (EpsgCoordinateSystemKind)0, 132); + return true; + case 4252: + cacheIndex = 1945; + reference = new EpsgCoordinateReferenceRecord(4252, (EpsgCoordinateSystemKind)0, 133); + return true; + case 4253: + cacheIndex = 1946; + reference = new EpsgCoordinateReferenceRecord(4253, (EpsgCoordinateSystemKind)0, 134); + return true; + case 4254: + cacheIndex = 1947; + reference = new EpsgCoordinateReferenceRecord(4254, (EpsgCoordinateSystemKind)0, 135); + return true; + case 4255: + cacheIndex = 1948; + reference = new EpsgCoordinateReferenceRecord(4255, (EpsgCoordinateSystemKind)0, 136); + return true; + case 4256: + cacheIndex = 1949; + reference = new EpsgCoordinateReferenceRecord(4256, (EpsgCoordinateSystemKind)0, 137); + return true; + case 4257: + cacheIndex = 1950; + reference = new EpsgCoordinateReferenceRecord(4257, (EpsgCoordinateSystemKind)0, 138); + return true; + case 4258: + cacheIndex = 1951; + reference = new EpsgCoordinateReferenceRecord(4258, (EpsgCoordinateSystemKind)0, 139); + return true; + case 4259: + cacheIndex = 1952; + reference = new EpsgCoordinateReferenceRecord(4259, (EpsgCoordinateSystemKind)0, 140); + return true; + case 4261: + cacheIndex = 1953; + reference = new EpsgCoordinateReferenceRecord(4261, (EpsgCoordinateSystemKind)0, 141); + return true; + case 4262: + cacheIndex = 1954; + reference = new EpsgCoordinateReferenceRecord(4262, (EpsgCoordinateSystemKind)0, 142); + return true; + case 4263: + cacheIndex = 1955; + reference = new EpsgCoordinateReferenceRecord(4263, (EpsgCoordinateSystemKind)0, 143); + return true; + case 4265: + cacheIndex = 1956; + reference = new EpsgCoordinateReferenceRecord(4265, (EpsgCoordinateSystemKind)0, 144); + return true; + case 4266: + cacheIndex = 1957; + reference = new EpsgCoordinateReferenceRecord(4266, (EpsgCoordinateSystemKind)0, 145); + return true; + case 4267: + cacheIndex = 1958; + reference = new EpsgCoordinateReferenceRecord(4267, (EpsgCoordinateSystemKind)0, 146); + return true; + case 4269: + cacheIndex = 1959; + reference = new EpsgCoordinateReferenceRecord(4269, (EpsgCoordinateSystemKind)0, 147); + return true; + case 4270: + cacheIndex = 1960; + reference = new EpsgCoordinateReferenceRecord(4270, (EpsgCoordinateSystemKind)0, 148); + return true; + case 4271: + cacheIndex = 1961; + reference = new EpsgCoordinateReferenceRecord(4271, (EpsgCoordinateSystemKind)0, 149); + return true; + case 4272: + cacheIndex = 1962; + reference = new EpsgCoordinateReferenceRecord(4272, (EpsgCoordinateSystemKind)0, 150); + return true; + case 4273: + cacheIndex = 1963; + reference = new EpsgCoordinateReferenceRecord(4273, (EpsgCoordinateSystemKind)0, 151); + return true; + case 4274: + cacheIndex = 1964; + reference = new EpsgCoordinateReferenceRecord(4274, (EpsgCoordinateSystemKind)0, 152); + return true; + case 4275: + cacheIndex = 1965; + reference = new EpsgCoordinateReferenceRecord(4275, (EpsgCoordinateSystemKind)0, 153); + return true; + case 4276: + cacheIndex = 1966; + reference = new EpsgCoordinateReferenceRecord(4276, (EpsgCoordinateSystemKind)0, 154); + return true; + case 4277: + cacheIndex = 1967; + reference = new EpsgCoordinateReferenceRecord(4277, (EpsgCoordinateSystemKind)0, 155); + return true; + case 4278: + cacheIndex = 1968; + reference = new EpsgCoordinateReferenceRecord(4278, (EpsgCoordinateSystemKind)0, 156); + return true; + case 4279: + cacheIndex = 1969; + reference = new EpsgCoordinateReferenceRecord(4279, (EpsgCoordinateSystemKind)0, 157); + return true; + case 4281: + cacheIndex = 1970; + reference = new EpsgCoordinateReferenceRecord(4281, (EpsgCoordinateSystemKind)0, 158); + return true; + case 4282: + cacheIndex = 1971; + reference = new EpsgCoordinateReferenceRecord(4282, (EpsgCoordinateSystemKind)0, 159); + return true; + case 4283: + cacheIndex = 1972; + reference = new EpsgCoordinateReferenceRecord(4283, (EpsgCoordinateSystemKind)0, 160); + return true; + case 4284: + cacheIndex = 1973; + reference = new EpsgCoordinateReferenceRecord(4284, (EpsgCoordinateSystemKind)0, 161); + return true; + case 4285: + cacheIndex = 1974; + reference = new EpsgCoordinateReferenceRecord(4285, (EpsgCoordinateSystemKind)0, 162); + return true; + case 4286: + cacheIndex = 1975; + reference = new EpsgCoordinateReferenceRecord(4286, (EpsgCoordinateSystemKind)0, 163); + return true; + case 4288: + cacheIndex = 1976; + reference = new EpsgCoordinateReferenceRecord(4288, (EpsgCoordinateSystemKind)0, 164); + return true; + case 4289: + cacheIndex = 1977; + reference = new EpsgCoordinateReferenceRecord(4289, (EpsgCoordinateSystemKind)0, 165); + return true; + case 4292: + cacheIndex = 1978; + reference = new EpsgCoordinateReferenceRecord(4292, (EpsgCoordinateSystemKind)0, 166); + return true; + case 4293: + cacheIndex = 1979; + reference = new EpsgCoordinateReferenceRecord(4293, (EpsgCoordinateSystemKind)0, 167); + return true; + case 4295: + cacheIndex = 1980; + reference = new EpsgCoordinateReferenceRecord(4295, (EpsgCoordinateSystemKind)0, 168); + return true; + case 4297: + cacheIndex = 1981; + reference = new EpsgCoordinateReferenceRecord(4297, (EpsgCoordinateSystemKind)0, 169); + return true; + case 4298: + cacheIndex = 1982; + reference = new EpsgCoordinateReferenceRecord(4298, (EpsgCoordinateSystemKind)0, 170); + return true; + case 4299: + cacheIndex = 1983; + reference = new EpsgCoordinateReferenceRecord(4299, (EpsgCoordinateSystemKind)0, 171); + return true; + case 4300: + cacheIndex = 1984; + reference = new EpsgCoordinateReferenceRecord(4300, (EpsgCoordinateSystemKind)0, 172); + return true; + case 4301: + cacheIndex = 1985; + reference = new EpsgCoordinateReferenceRecord(4301, (EpsgCoordinateSystemKind)0, 173); + return true; + case 4302: + cacheIndex = 1986; + reference = new EpsgCoordinateReferenceRecord(4302, (EpsgCoordinateSystemKind)0, 174); + return true; + case 4303: + cacheIndex = 1987; + reference = new EpsgCoordinateReferenceRecord(4303, (EpsgCoordinateSystemKind)0, 175); + return true; + case 4304: + cacheIndex = 1988; + reference = new EpsgCoordinateReferenceRecord(4304, (EpsgCoordinateSystemKind)0, 176); + return true; + case 4306: + cacheIndex = 1989; + reference = new EpsgCoordinateReferenceRecord(4306, (EpsgCoordinateSystemKind)0, 177); + return true; + case 4307: + cacheIndex = 1990; + reference = new EpsgCoordinateReferenceRecord(4307, (EpsgCoordinateSystemKind)0, 178); + return true; + case 4308: + cacheIndex = 1991; + reference = new EpsgCoordinateReferenceRecord(4308, (EpsgCoordinateSystemKind)0, 179); + return true; + case 4309: + cacheIndex = 1992; + reference = new EpsgCoordinateReferenceRecord(4309, (EpsgCoordinateSystemKind)0, 180); + return true; + case 4310: + cacheIndex = 1993; + reference = new EpsgCoordinateReferenceRecord(4310, (EpsgCoordinateSystemKind)0, 181); + return true; + case 4311: + cacheIndex = 1994; + reference = new EpsgCoordinateReferenceRecord(4311, (EpsgCoordinateSystemKind)0, 182); + return true; + case 4312: + cacheIndex = 1995; + reference = new EpsgCoordinateReferenceRecord(4312, (EpsgCoordinateSystemKind)0, 183); + return true; + case 4313: + cacheIndex = 1996; + reference = new EpsgCoordinateReferenceRecord(4313, (EpsgCoordinateSystemKind)0, 184); + return true; + case 4314: + cacheIndex = 1997; + reference = new EpsgCoordinateReferenceRecord(4314, (EpsgCoordinateSystemKind)0, 185); + return true; + case 4315: + cacheIndex = 1998; + reference = new EpsgCoordinateReferenceRecord(4315, (EpsgCoordinateSystemKind)0, 186); + return true; + case 4316: + cacheIndex = 1999; + reference = new EpsgCoordinateReferenceRecord(4316, (EpsgCoordinateSystemKind)0, 187); + return true; + case 4318: + cacheIndex = 2000; + reference = new EpsgCoordinateReferenceRecord(4318, (EpsgCoordinateSystemKind)0, 188); + return true; + case 4319: + cacheIndex = 2001; + reference = new EpsgCoordinateReferenceRecord(4319, (EpsgCoordinateSystemKind)0, 189); + return true; + case 4322: + cacheIndex = 2002; + reference = new EpsgCoordinateReferenceRecord(4322, (EpsgCoordinateSystemKind)0, 190); + return true; + case 4324: + cacheIndex = 2003; + reference = new EpsgCoordinateReferenceRecord(4324, (EpsgCoordinateSystemKind)0, 191); + return true; + case 4326: + cacheIndex = 2004; + reference = new EpsgCoordinateReferenceRecord(4326, (EpsgCoordinateSystemKind)0, 192); + return true; + case 4390: + cacheIndex = 2005; + reference = new EpsgCoordinateReferenceRecord(4390, (EpsgCoordinateSystemKind)2, 1800); + return true; + case 4391: + cacheIndex = 2006; + reference = new EpsgCoordinateReferenceRecord(4391, (EpsgCoordinateSystemKind)2, 1801); + return true; + case 4392: + cacheIndex = 2007; + reference = new EpsgCoordinateReferenceRecord(4392, (EpsgCoordinateSystemKind)2, 1802); + return true; + case 4393: + cacheIndex = 2008; + reference = new EpsgCoordinateReferenceRecord(4393, (EpsgCoordinateSystemKind)2, 1803); + return true; + case 4394: + cacheIndex = 2009; + reference = new EpsgCoordinateReferenceRecord(4394, (EpsgCoordinateSystemKind)2, 1804); + return true; + case 4395: + cacheIndex = 2010; + reference = new EpsgCoordinateReferenceRecord(4395, (EpsgCoordinateSystemKind)2, 1805); + return true; + case 4396: + cacheIndex = 2011; + reference = new EpsgCoordinateReferenceRecord(4396, (EpsgCoordinateSystemKind)2, 1806); + return true; + case 4397: + cacheIndex = 2012; + reference = new EpsgCoordinateReferenceRecord(4397, (EpsgCoordinateSystemKind)2, 1807); + return true; + case 4398: + cacheIndex = 2013; + reference = new EpsgCoordinateReferenceRecord(4398, (EpsgCoordinateSystemKind)2, 1808); + return true; + case 4399: + cacheIndex = 2014; + reference = new EpsgCoordinateReferenceRecord(4399, (EpsgCoordinateSystemKind)2, 1809); + return true; + case 4400: + cacheIndex = 2015; + reference = new EpsgCoordinateReferenceRecord(4400, (EpsgCoordinateSystemKind)2, 1810); + return true; + case 4401: + cacheIndex = 2016; + reference = new EpsgCoordinateReferenceRecord(4401, (EpsgCoordinateSystemKind)2, 1811); + return true; + case 4402: + cacheIndex = 2017; + reference = new EpsgCoordinateReferenceRecord(4402, (EpsgCoordinateSystemKind)2, 1812); + return true; + case 4403: + cacheIndex = 2018; + reference = new EpsgCoordinateReferenceRecord(4403, (EpsgCoordinateSystemKind)2, 1813); + return true; + case 4404: + cacheIndex = 2019; + reference = new EpsgCoordinateReferenceRecord(4404, (EpsgCoordinateSystemKind)2, 1814); + return true; + case 4405: + cacheIndex = 2020; + reference = new EpsgCoordinateReferenceRecord(4405, (EpsgCoordinateSystemKind)2, 1815); + return true; + case 4406: + cacheIndex = 2021; + reference = new EpsgCoordinateReferenceRecord(4406, (EpsgCoordinateSystemKind)2, 1816); + return true; + case 4407: + cacheIndex = 2022; + reference = new EpsgCoordinateReferenceRecord(4407, (EpsgCoordinateSystemKind)2, 1817); + return true; + case 4408: + cacheIndex = 2023; + reference = new EpsgCoordinateReferenceRecord(4408, (EpsgCoordinateSystemKind)2, 1818); + return true; + case 4409: + cacheIndex = 2024; + reference = new EpsgCoordinateReferenceRecord(4409, (EpsgCoordinateSystemKind)2, 1819); + return true; + case 4410: + cacheIndex = 2025; + reference = new EpsgCoordinateReferenceRecord(4410, (EpsgCoordinateSystemKind)2, 1820); + return true; + case 4411: + cacheIndex = 2026; + reference = new EpsgCoordinateReferenceRecord(4411, (EpsgCoordinateSystemKind)2, 1821); + return true; + case 4412: + cacheIndex = 2027; + reference = new EpsgCoordinateReferenceRecord(4412, (EpsgCoordinateSystemKind)2, 1822); + return true; + case 4413: + cacheIndex = 2028; + reference = new EpsgCoordinateReferenceRecord(4413, (EpsgCoordinateSystemKind)2, 1823); + return true; + case 4414: + cacheIndex = 2029; + reference = new EpsgCoordinateReferenceRecord(4414, (EpsgCoordinateSystemKind)2, 1824); + return true; + case 4415: + cacheIndex = 2030; + reference = new EpsgCoordinateReferenceRecord(4415, (EpsgCoordinateSystemKind)2, 1825); + return true; + case 4417: + cacheIndex = 2031; + reference = new EpsgCoordinateReferenceRecord(4417, (EpsgCoordinateSystemKind)2, 1826); + return true; + case 4418: + cacheIndex = 2032; + reference = new EpsgCoordinateReferenceRecord(4418, (EpsgCoordinateSystemKind)2, 1827); + return true; + case 4419: + cacheIndex = 2033; + reference = new EpsgCoordinateReferenceRecord(4419, (EpsgCoordinateSystemKind)2, 1828); + return true; + case 4420: + cacheIndex = 2034; + reference = new EpsgCoordinateReferenceRecord(4420, (EpsgCoordinateSystemKind)2, 1829); + return true; + case 4421: + cacheIndex = 2035; + reference = new EpsgCoordinateReferenceRecord(4421, (EpsgCoordinateSystemKind)2, 1830); + return true; + case 4422: + cacheIndex = 2036; + reference = new EpsgCoordinateReferenceRecord(4422, (EpsgCoordinateSystemKind)2, 1831); + return true; + case 4423: + cacheIndex = 2037; + reference = new EpsgCoordinateReferenceRecord(4423, (EpsgCoordinateSystemKind)2, 1832); + return true; + case 4424: + cacheIndex = 2038; + reference = new EpsgCoordinateReferenceRecord(4424, (EpsgCoordinateSystemKind)2, 1833); + return true; + case 4425: + cacheIndex = 2039; + reference = new EpsgCoordinateReferenceRecord(4425, (EpsgCoordinateSystemKind)2, 1834); + return true; + case 4426: + cacheIndex = 2040; + reference = new EpsgCoordinateReferenceRecord(4426, (EpsgCoordinateSystemKind)2, 1835); + return true; + case 4427: + cacheIndex = 2041; + reference = new EpsgCoordinateReferenceRecord(4427, (EpsgCoordinateSystemKind)2, 1836); + return true; + case 4428: + cacheIndex = 2042; + reference = new EpsgCoordinateReferenceRecord(4428, (EpsgCoordinateSystemKind)2, 1837); + return true; + case 4429: + cacheIndex = 2043; + reference = new EpsgCoordinateReferenceRecord(4429, (EpsgCoordinateSystemKind)2, 1838); + return true; + case 4430: + cacheIndex = 2044; + reference = new EpsgCoordinateReferenceRecord(4430, (EpsgCoordinateSystemKind)2, 1839); + return true; + case 4431: + cacheIndex = 2045; + reference = new EpsgCoordinateReferenceRecord(4431, (EpsgCoordinateSystemKind)2, 1840); + return true; + case 4432: + cacheIndex = 2046; + reference = new EpsgCoordinateReferenceRecord(4432, (EpsgCoordinateSystemKind)2, 1841); + return true; + case 4433: + cacheIndex = 2047; + reference = new EpsgCoordinateReferenceRecord(4433, (EpsgCoordinateSystemKind)2, 1842); + return true; + case 4434: + cacheIndex = 2048; + reference = new EpsgCoordinateReferenceRecord(4434, (EpsgCoordinateSystemKind)2, 1843); + return true; + case 4437: + cacheIndex = 2049; + reference = new EpsgCoordinateReferenceRecord(4437, (EpsgCoordinateSystemKind)2, 1844); + return true; + case 4438: + cacheIndex = 2050; + reference = new EpsgCoordinateReferenceRecord(4438, (EpsgCoordinateSystemKind)2, 1845); + return true; + case 4439: + cacheIndex = 2051; + reference = new EpsgCoordinateReferenceRecord(4439, (EpsgCoordinateSystemKind)2, 1846); + return true; + case 4440: + cacheIndex = 2052; + reference = new EpsgCoordinateReferenceRecord(4440, (EpsgCoordinateSystemKind)3, 3); + return true; + case 4455: + cacheIndex = 2053; + reference = new EpsgCoordinateReferenceRecord(4455, (EpsgCoordinateSystemKind)2, 1847); + return true; + case 4456: + cacheIndex = 2054; + reference = new EpsgCoordinateReferenceRecord(4456, (EpsgCoordinateSystemKind)2, 1848); + return true; + case 4457: + cacheIndex = 2055; + reference = new EpsgCoordinateReferenceRecord(4457, (EpsgCoordinateSystemKind)2, 1849); + return true; + case 4458: + cacheIndex = 2056; + reference = new EpsgCoordinateReferenceRecord(4458, (EpsgCoordinateSystemKind)3, 4); + return true; + case 4462: + cacheIndex = 2057; + reference = new EpsgCoordinateReferenceRecord(4462, (EpsgCoordinateSystemKind)2, 1850); + return true; + case 4463: + cacheIndex = 2058; + reference = new EpsgCoordinateReferenceRecord(4463, (EpsgCoordinateSystemKind)0, 193); + return true; + case 4465: + cacheIndex = 2059; + reference = new EpsgCoordinateReferenceRecord(4465, (EpsgCoordinateSystemKind)1, 6); + return true; + case 4466: + cacheIndex = 2060; + reference = new EpsgCoordinateReferenceRecord(4466, (EpsgCoordinateSystemKind)0, 194); + return true; + case 4467: + cacheIndex = 2061; + reference = new EpsgCoordinateReferenceRecord(4467, (EpsgCoordinateSystemKind)2, 1851); + return true; + case 4468: + cacheIndex = 2062; + reference = new EpsgCoordinateReferenceRecord(4468, (EpsgCoordinateSystemKind)1, 7); + return true; + case 4469: + cacheIndex = 2063; + reference = new EpsgCoordinateReferenceRecord(4469, (EpsgCoordinateSystemKind)0, 195); + return true; + case 4470: + cacheIndex = 2064; + reference = new EpsgCoordinateReferenceRecord(4470, (EpsgCoordinateSystemKind)0, 196); + return true; + case 4471: + cacheIndex = 2065; + reference = new EpsgCoordinateReferenceRecord(4471, (EpsgCoordinateSystemKind)2, 1852); + return true; + case 4472: + cacheIndex = 2066; + reference = new EpsgCoordinateReferenceRecord(4472, (EpsgCoordinateSystemKind)0, 197); + return true; + case 4473: + cacheIndex = 2067; + reference = new EpsgCoordinateReferenceRecord(4473, (EpsgCoordinateSystemKind)1, 8); + return true; + case 4475: + cacheIndex = 2068; + reference = new EpsgCoordinateReferenceRecord(4475, (EpsgCoordinateSystemKind)0, 198); + return true; + case 4479: + cacheIndex = 2069; + reference = new EpsgCoordinateReferenceRecord(4479, (EpsgCoordinateSystemKind)1, 9); + return true; + case 4480: + cacheIndex = 2070; + reference = new EpsgCoordinateReferenceRecord(4480, (EpsgCoordinateSystemKind)0, 199); + return true; + case 4481: + cacheIndex = 2071; + reference = new EpsgCoordinateReferenceRecord(4481, (EpsgCoordinateSystemKind)1, 10); + return true; + case 4482: + cacheIndex = 2072; + reference = new EpsgCoordinateReferenceRecord(4482, (EpsgCoordinateSystemKind)0, 200); + return true; + case 4483: + cacheIndex = 2073; + reference = new EpsgCoordinateReferenceRecord(4483, (EpsgCoordinateSystemKind)0, 201); + return true; + case 4484: + cacheIndex = 2074; + reference = new EpsgCoordinateReferenceRecord(4484, (EpsgCoordinateSystemKind)2, 1853); + return true; + case 4485: + cacheIndex = 2075; + reference = new EpsgCoordinateReferenceRecord(4485, (EpsgCoordinateSystemKind)2, 1854); + return true; + case 4486: + cacheIndex = 2076; + reference = new EpsgCoordinateReferenceRecord(4486, (EpsgCoordinateSystemKind)2, 1855); + return true; + case 4487: + cacheIndex = 2077; + reference = new EpsgCoordinateReferenceRecord(4487, (EpsgCoordinateSystemKind)2, 1856); + return true; + case 4488: + cacheIndex = 2078; + reference = new EpsgCoordinateReferenceRecord(4488, (EpsgCoordinateSystemKind)2, 1857); + return true; + case 4489: + cacheIndex = 2079; + reference = new EpsgCoordinateReferenceRecord(4489, (EpsgCoordinateSystemKind)2, 1858); + return true; + case 4490: + cacheIndex = 2080; + reference = new EpsgCoordinateReferenceRecord(4490, (EpsgCoordinateSystemKind)0, 202); + return true; + case 4491: + cacheIndex = 2081; + reference = new EpsgCoordinateReferenceRecord(4491, (EpsgCoordinateSystemKind)2, 1859); + return true; + case 4492: + cacheIndex = 2082; + reference = new EpsgCoordinateReferenceRecord(4492, (EpsgCoordinateSystemKind)2, 1860); + return true; + case 4493: + cacheIndex = 2083; + reference = new EpsgCoordinateReferenceRecord(4493, (EpsgCoordinateSystemKind)2, 1861); + return true; + case 4494: + cacheIndex = 2084; + reference = new EpsgCoordinateReferenceRecord(4494, (EpsgCoordinateSystemKind)2, 1862); + return true; + case 4495: + cacheIndex = 2085; + reference = new EpsgCoordinateReferenceRecord(4495, (EpsgCoordinateSystemKind)2, 1863); + return true; + case 4496: + cacheIndex = 2086; + reference = new EpsgCoordinateReferenceRecord(4496, (EpsgCoordinateSystemKind)2, 1864); + return true; + case 4497: + cacheIndex = 2087; + reference = new EpsgCoordinateReferenceRecord(4497, (EpsgCoordinateSystemKind)2, 1865); + return true; + case 4498: + cacheIndex = 2088; + reference = new EpsgCoordinateReferenceRecord(4498, (EpsgCoordinateSystemKind)2, 1866); + return true; + case 4499: + cacheIndex = 2089; + reference = new EpsgCoordinateReferenceRecord(4499, (EpsgCoordinateSystemKind)2, 1867); + return true; + case 4500: + cacheIndex = 2090; + reference = new EpsgCoordinateReferenceRecord(4500, (EpsgCoordinateSystemKind)2, 1868); + return true; + case 4501: + cacheIndex = 2091; + reference = new EpsgCoordinateReferenceRecord(4501, (EpsgCoordinateSystemKind)2, 1869); + return true; + case 4502: + cacheIndex = 2092; + reference = new EpsgCoordinateReferenceRecord(4502, (EpsgCoordinateSystemKind)2, 1870); + return true; + case 4503: + cacheIndex = 2093; + reference = new EpsgCoordinateReferenceRecord(4503, (EpsgCoordinateSystemKind)2, 1871); + return true; + case 4504: + cacheIndex = 2094; + reference = new EpsgCoordinateReferenceRecord(4504, (EpsgCoordinateSystemKind)2, 1872); + return true; + case 4505: + cacheIndex = 2095; + reference = new EpsgCoordinateReferenceRecord(4505, (EpsgCoordinateSystemKind)2, 1873); + return true; + case 4506: + cacheIndex = 2096; + reference = new EpsgCoordinateReferenceRecord(4506, (EpsgCoordinateSystemKind)2, 1874); + return true; + case 4507: + cacheIndex = 2097; + reference = new EpsgCoordinateReferenceRecord(4507, (EpsgCoordinateSystemKind)2, 1875); + return true; + case 4508: + cacheIndex = 2098; + reference = new EpsgCoordinateReferenceRecord(4508, (EpsgCoordinateSystemKind)2, 1876); + return true; + case 4509: + cacheIndex = 2099; + reference = new EpsgCoordinateReferenceRecord(4509, (EpsgCoordinateSystemKind)2, 1877); + return true; + case 4510: + cacheIndex = 2100; + reference = new EpsgCoordinateReferenceRecord(4510, (EpsgCoordinateSystemKind)2, 1878); + return true; + case 4511: + cacheIndex = 2101; + reference = new EpsgCoordinateReferenceRecord(4511, (EpsgCoordinateSystemKind)2, 1879); + return true; + case 4512: + cacheIndex = 2102; + reference = new EpsgCoordinateReferenceRecord(4512, (EpsgCoordinateSystemKind)2, 1880); + return true; + case 4513: + cacheIndex = 2103; + reference = new EpsgCoordinateReferenceRecord(4513, (EpsgCoordinateSystemKind)2, 1881); + return true; + case 4514: + cacheIndex = 2104; + reference = new EpsgCoordinateReferenceRecord(4514, (EpsgCoordinateSystemKind)2, 1882); + return true; + case 4515: + cacheIndex = 2105; + reference = new EpsgCoordinateReferenceRecord(4515, (EpsgCoordinateSystemKind)2, 1883); + return true; + case 4516: + cacheIndex = 2106; + reference = new EpsgCoordinateReferenceRecord(4516, (EpsgCoordinateSystemKind)2, 1884); + return true; + case 4517: + cacheIndex = 2107; + reference = new EpsgCoordinateReferenceRecord(4517, (EpsgCoordinateSystemKind)2, 1885); + return true; + case 4518: + cacheIndex = 2108; + reference = new EpsgCoordinateReferenceRecord(4518, (EpsgCoordinateSystemKind)2, 1886); + return true; + case 4519: + cacheIndex = 2109; + reference = new EpsgCoordinateReferenceRecord(4519, (EpsgCoordinateSystemKind)2, 1887); + return true; + case 4520: + cacheIndex = 2110; + reference = new EpsgCoordinateReferenceRecord(4520, (EpsgCoordinateSystemKind)2, 1888); + return true; + case 4521: + cacheIndex = 2111; + reference = new EpsgCoordinateReferenceRecord(4521, (EpsgCoordinateSystemKind)2, 1889); + return true; + case 4522: + cacheIndex = 2112; + reference = new EpsgCoordinateReferenceRecord(4522, (EpsgCoordinateSystemKind)2, 1890); + return true; + case 4523: + cacheIndex = 2113; + reference = new EpsgCoordinateReferenceRecord(4523, (EpsgCoordinateSystemKind)2, 1891); + return true; + case 4524: + cacheIndex = 2114; + reference = new EpsgCoordinateReferenceRecord(4524, (EpsgCoordinateSystemKind)2, 1892); + return true; + case 4525: + cacheIndex = 2115; + reference = new EpsgCoordinateReferenceRecord(4525, (EpsgCoordinateSystemKind)2, 1893); + return true; + case 4526: + cacheIndex = 2116; + reference = new EpsgCoordinateReferenceRecord(4526, (EpsgCoordinateSystemKind)2, 1894); + return true; + case 4527: + cacheIndex = 2117; + reference = new EpsgCoordinateReferenceRecord(4527, (EpsgCoordinateSystemKind)2, 1895); + return true; + case 4528: + cacheIndex = 2118; + reference = new EpsgCoordinateReferenceRecord(4528, (EpsgCoordinateSystemKind)2, 1896); + return true; + case 4529: + cacheIndex = 2119; + reference = new EpsgCoordinateReferenceRecord(4529, (EpsgCoordinateSystemKind)2, 1897); + return true; + case 4530: + cacheIndex = 2120; + reference = new EpsgCoordinateReferenceRecord(4530, (EpsgCoordinateSystemKind)2, 1898); + return true; + case 4531: + cacheIndex = 2121; + reference = new EpsgCoordinateReferenceRecord(4531, (EpsgCoordinateSystemKind)2, 1899); + return true; + case 4532: + cacheIndex = 2122; + reference = new EpsgCoordinateReferenceRecord(4532, (EpsgCoordinateSystemKind)2, 1900); + return true; + case 4533: + cacheIndex = 2123; + reference = new EpsgCoordinateReferenceRecord(4533, (EpsgCoordinateSystemKind)2, 1901); + return true; + case 4534: + cacheIndex = 2124; + reference = new EpsgCoordinateReferenceRecord(4534, (EpsgCoordinateSystemKind)2, 1902); + return true; + case 4535: + cacheIndex = 2125; + reference = new EpsgCoordinateReferenceRecord(4535, (EpsgCoordinateSystemKind)2, 1903); + return true; + case 4536: + cacheIndex = 2126; + reference = new EpsgCoordinateReferenceRecord(4536, (EpsgCoordinateSystemKind)2, 1904); + return true; + case 4537: + cacheIndex = 2127; + reference = new EpsgCoordinateReferenceRecord(4537, (EpsgCoordinateSystemKind)2, 1905); + return true; + case 4538: + cacheIndex = 2128; + reference = new EpsgCoordinateReferenceRecord(4538, (EpsgCoordinateSystemKind)2, 1906); + return true; + case 4539: + cacheIndex = 2129; + reference = new EpsgCoordinateReferenceRecord(4539, (EpsgCoordinateSystemKind)2, 1907); + return true; + case 4540: + cacheIndex = 2130; + reference = new EpsgCoordinateReferenceRecord(4540, (EpsgCoordinateSystemKind)2, 1908); + return true; + case 4541: + cacheIndex = 2131; + reference = new EpsgCoordinateReferenceRecord(4541, (EpsgCoordinateSystemKind)2, 1909); + return true; + case 4542: + cacheIndex = 2132; + reference = new EpsgCoordinateReferenceRecord(4542, (EpsgCoordinateSystemKind)2, 1910); + return true; + case 4543: + cacheIndex = 2133; + reference = new EpsgCoordinateReferenceRecord(4543, (EpsgCoordinateSystemKind)2, 1911); + return true; + case 4544: + cacheIndex = 2134; + reference = new EpsgCoordinateReferenceRecord(4544, (EpsgCoordinateSystemKind)2, 1912); + return true; + case 4545: + cacheIndex = 2135; + reference = new EpsgCoordinateReferenceRecord(4545, (EpsgCoordinateSystemKind)2, 1913); + return true; + case 4546: + cacheIndex = 2136; + reference = new EpsgCoordinateReferenceRecord(4546, (EpsgCoordinateSystemKind)2, 1914); + return true; + case 4547: + cacheIndex = 2137; + reference = new EpsgCoordinateReferenceRecord(4547, (EpsgCoordinateSystemKind)2, 1915); + return true; + case 4548: + cacheIndex = 2138; + reference = new EpsgCoordinateReferenceRecord(4548, (EpsgCoordinateSystemKind)2, 1916); + return true; + case 4549: + cacheIndex = 2139; + reference = new EpsgCoordinateReferenceRecord(4549, (EpsgCoordinateSystemKind)2, 1917); + return true; + case 4550: + cacheIndex = 2140; + reference = new EpsgCoordinateReferenceRecord(4550, (EpsgCoordinateSystemKind)2, 1918); + return true; + case 4551: + cacheIndex = 2141; + reference = new EpsgCoordinateReferenceRecord(4551, (EpsgCoordinateSystemKind)2, 1919); + return true; + case 4552: + cacheIndex = 2142; + reference = new EpsgCoordinateReferenceRecord(4552, (EpsgCoordinateSystemKind)2, 1920); + return true; + case 4553: + cacheIndex = 2143; + reference = new EpsgCoordinateReferenceRecord(4553, (EpsgCoordinateSystemKind)2, 1921); + return true; + case 4554: + cacheIndex = 2144; + reference = new EpsgCoordinateReferenceRecord(4554, (EpsgCoordinateSystemKind)2, 1922); + return true; + case 4555: + cacheIndex = 2145; + reference = new EpsgCoordinateReferenceRecord(4555, (EpsgCoordinateSystemKind)0, 203); + return true; + case 4556: + cacheIndex = 2146; + reference = new EpsgCoordinateReferenceRecord(4556, (EpsgCoordinateSystemKind)1, 11); + return true; + case 4557: + cacheIndex = 2147; + reference = new EpsgCoordinateReferenceRecord(4557, (EpsgCoordinateSystemKind)0, 204); + return true; + case 4558: + cacheIndex = 2148; + reference = new EpsgCoordinateReferenceRecord(4558, (EpsgCoordinateSystemKind)0, 205); + return true; + case 4559: + cacheIndex = 2149; + reference = new EpsgCoordinateReferenceRecord(4559, (EpsgCoordinateSystemKind)2, 1923); + return true; + case 4568: + cacheIndex = 2150; + reference = new EpsgCoordinateReferenceRecord(4568, (EpsgCoordinateSystemKind)2, 1924); + return true; + case 4569: + cacheIndex = 2151; + reference = new EpsgCoordinateReferenceRecord(4569, (EpsgCoordinateSystemKind)2, 1925); + return true; + case 4570: + cacheIndex = 2152; + reference = new EpsgCoordinateReferenceRecord(4570, (EpsgCoordinateSystemKind)2, 1926); + return true; + case 4571: + cacheIndex = 2153; + reference = new EpsgCoordinateReferenceRecord(4571, (EpsgCoordinateSystemKind)2, 1927); + return true; + case 4572: + cacheIndex = 2154; + reference = new EpsgCoordinateReferenceRecord(4572, (EpsgCoordinateSystemKind)2, 1928); + return true; + case 4573: + cacheIndex = 2155; + reference = new EpsgCoordinateReferenceRecord(4573, (EpsgCoordinateSystemKind)2, 1929); + return true; + case 4574: + cacheIndex = 2156; + reference = new EpsgCoordinateReferenceRecord(4574, (EpsgCoordinateSystemKind)2, 1930); + return true; + case 4575: + cacheIndex = 2157; + reference = new EpsgCoordinateReferenceRecord(4575, (EpsgCoordinateSystemKind)2, 1931); + return true; + case 4576: + cacheIndex = 2158; + reference = new EpsgCoordinateReferenceRecord(4576, (EpsgCoordinateSystemKind)2, 1932); + return true; + case 4577: + cacheIndex = 2159; + reference = new EpsgCoordinateReferenceRecord(4577, (EpsgCoordinateSystemKind)2, 1933); + return true; + case 4578: + cacheIndex = 2160; + reference = new EpsgCoordinateReferenceRecord(4578, (EpsgCoordinateSystemKind)2, 1934); + return true; + case 4579: + cacheIndex = 2161; + reference = new EpsgCoordinateReferenceRecord(4579, (EpsgCoordinateSystemKind)2, 1935); + return true; + case 4580: + cacheIndex = 2162; + reference = new EpsgCoordinateReferenceRecord(4580, (EpsgCoordinateSystemKind)2, 1936); + return true; + case 4581: + cacheIndex = 2163; + reference = new EpsgCoordinateReferenceRecord(4581, (EpsgCoordinateSystemKind)2, 1937); + return true; + case 4582: + cacheIndex = 2164; + reference = new EpsgCoordinateReferenceRecord(4582, (EpsgCoordinateSystemKind)2, 1938); + return true; + case 4583: + cacheIndex = 2165; + reference = new EpsgCoordinateReferenceRecord(4583, (EpsgCoordinateSystemKind)2, 1939); + return true; + case 4584: + cacheIndex = 2166; + reference = new EpsgCoordinateReferenceRecord(4584, (EpsgCoordinateSystemKind)2, 1940); + return true; + case 4585: + cacheIndex = 2167; + reference = new EpsgCoordinateReferenceRecord(4585, (EpsgCoordinateSystemKind)2, 1941); + return true; + case 4586: + cacheIndex = 2168; + reference = new EpsgCoordinateReferenceRecord(4586, (EpsgCoordinateSystemKind)2, 1942); + return true; + case 4587: + cacheIndex = 2169; + reference = new EpsgCoordinateReferenceRecord(4587, (EpsgCoordinateSystemKind)2, 1943); + return true; + case 4588: + cacheIndex = 2170; + reference = new EpsgCoordinateReferenceRecord(4588, (EpsgCoordinateSystemKind)2, 1944); + return true; + case 4589: + cacheIndex = 2171; + reference = new EpsgCoordinateReferenceRecord(4589, (EpsgCoordinateSystemKind)2, 1945); + return true; + case 4600: + cacheIndex = 2172; + reference = new EpsgCoordinateReferenceRecord(4600, (EpsgCoordinateSystemKind)0, 206); + return true; + case 4601: + cacheIndex = 2173; + reference = new EpsgCoordinateReferenceRecord(4601, (EpsgCoordinateSystemKind)0, 207); + return true; + case 4602: + cacheIndex = 2174; + reference = new EpsgCoordinateReferenceRecord(4602, (EpsgCoordinateSystemKind)0, 208); + return true; + case 4603: + cacheIndex = 2175; + reference = new EpsgCoordinateReferenceRecord(4603, (EpsgCoordinateSystemKind)0, 209); + return true; + case 4604: + cacheIndex = 2176; + reference = new EpsgCoordinateReferenceRecord(4604, (EpsgCoordinateSystemKind)0, 210); + return true; + case 4605: + cacheIndex = 2177; + reference = new EpsgCoordinateReferenceRecord(4605, (EpsgCoordinateSystemKind)0, 211); + return true; + case 4606: + cacheIndex = 2178; + reference = new EpsgCoordinateReferenceRecord(4606, (EpsgCoordinateSystemKind)0, 212); + return true; + case 4607: + cacheIndex = 2179; + reference = new EpsgCoordinateReferenceRecord(4607, (EpsgCoordinateSystemKind)0, 213); + return true; + case 4608: + cacheIndex = 2180; + reference = new EpsgCoordinateReferenceRecord(4608, (EpsgCoordinateSystemKind)0, 214); + return true; + case 4609: + cacheIndex = 2181; + reference = new EpsgCoordinateReferenceRecord(4609, (EpsgCoordinateSystemKind)0, 215); + return true; + case 4610: + cacheIndex = 2182; + reference = new EpsgCoordinateReferenceRecord(4610, (EpsgCoordinateSystemKind)0, 216); + return true; + case 4611: + cacheIndex = 2183; + reference = new EpsgCoordinateReferenceRecord(4611, (EpsgCoordinateSystemKind)0, 217); + return true; + case 4612: + cacheIndex = 2184; + reference = new EpsgCoordinateReferenceRecord(4612, (EpsgCoordinateSystemKind)0, 218); + return true; + case 4613: + cacheIndex = 2185; + reference = new EpsgCoordinateReferenceRecord(4613, (EpsgCoordinateSystemKind)0, 219); + return true; + case 4614: + cacheIndex = 2186; + reference = new EpsgCoordinateReferenceRecord(4614, (EpsgCoordinateSystemKind)0, 220); + return true; + case 4615: + cacheIndex = 2187; + reference = new EpsgCoordinateReferenceRecord(4615, (EpsgCoordinateSystemKind)0, 221); + return true; + case 4616: + cacheIndex = 2188; + reference = new EpsgCoordinateReferenceRecord(4616, (EpsgCoordinateSystemKind)0, 222); + return true; + case 4617: + cacheIndex = 2189; + reference = new EpsgCoordinateReferenceRecord(4617, (EpsgCoordinateSystemKind)0, 223); + return true; + case 4618: + cacheIndex = 2190; + reference = new EpsgCoordinateReferenceRecord(4618, (EpsgCoordinateSystemKind)0, 224); + return true; + case 4619: + cacheIndex = 2191; + reference = new EpsgCoordinateReferenceRecord(4619, (EpsgCoordinateSystemKind)0, 225); + return true; + case 4620: + cacheIndex = 2192; + reference = new EpsgCoordinateReferenceRecord(4620, (EpsgCoordinateSystemKind)0, 226); + return true; + case 4621: + cacheIndex = 2193; + reference = new EpsgCoordinateReferenceRecord(4621, (EpsgCoordinateSystemKind)0, 227); + return true; + case 4622: + cacheIndex = 2194; + reference = new EpsgCoordinateReferenceRecord(4622, (EpsgCoordinateSystemKind)0, 228); + return true; + case 4623: + cacheIndex = 2195; + reference = new EpsgCoordinateReferenceRecord(4623, (EpsgCoordinateSystemKind)0, 229); + return true; + case 4624: + cacheIndex = 2196; + reference = new EpsgCoordinateReferenceRecord(4624, (EpsgCoordinateSystemKind)0, 230); + return true; + case 4625: + cacheIndex = 2197; + reference = new EpsgCoordinateReferenceRecord(4625, (EpsgCoordinateSystemKind)0, 231); + return true; + case 4626: + cacheIndex = 2198; + reference = new EpsgCoordinateReferenceRecord(4626, (EpsgCoordinateSystemKind)0, 232); + return true; + case 4627: + cacheIndex = 2199; + reference = new EpsgCoordinateReferenceRecord(4627, (EpsgCoordinateSystemKind)0, 233); + return true; + case 4628: + cacheIndex = 2200; + reference = new EpsgCoordinateReferenceRecord(4628, (EpsgCoordinateSystemKind)0, 234); + return true; + case 4629: + cacheIndex = 2201; + reference = new EpsgCoordinateReferenceRecord(4629, (EpsgCoordinateSystemKind)0, 235); + return true; + case 4630: + cacheIndex = 2202; + reference = new EpsgCoordinateReferenceRecord(4630, (EpsgCoordinateSystemKind)0, 236); + return true; + case 4632: + cacheIndex = 2203; + reference = new EpsgCoordinateReferenceRecord(4632, (EpsgCoordinateSystemKind)0, 237); + return true; + case 4633: + cacheIndex = 2204; + reference = new EpsgCoordinateReferenceRecord(4633, (EpsgCoordinateSystemKind)0, 238); + return true; + case 4636: + cacheIndex = 2205; + reference = new EpsgCoordinateReferenceRecord(4636, (EpsgCoordinateSystemKind)0, 239); + return true; + case 4637: + cacheIndex = 2206; + reference = new EpsgCoordinateReferenceRecord(4637, (EpsgCoordinateSystemKind)0, 240); + return true; + case 4638: + cacheIndex = 2207; + reference = new EpsgCoordinateReferenceRecord(4638, (EpsgCoordinateSystemKind)0, 241); + return true; + case 4639: + cacheIndex = 2208; + reference = new EpsgCoordinateReferenceRecord(4639, (EpsgCoordinateSystemKind)0, 242); + return true; + case 4641: + cacheIndex = 2209; + reference = new EpsgCoordinateReferenceRecord(4641, (EpsgCoordinateSystemKind)0, 243); + return true; + case 4642: + cacheIndex = 2210; + reference = new EpsgCoordinateReferenceRecord(4642, (EpsgCoordinateSystemKind)0, 244); + return true; + case 4643: + cacheIndex = 2211; + reference = new EpsgCoordinateReferenceRecord(4643, (EpsgCoordinateSystemKind)0, 245); + return true; + case 4644: + cacheIndex = 2212; + reference = new EpsgCoordinateReferenceRecord(4644, (EpsgCoordinateSystemKind)0, 246); + return true; + case 4646: + cacheIndex = 2213; + reference = new EpsgCoordinateReferenceRecord(4646, (EpsgCoordinateSystemKind)0, 247); + return true; + case 4647: + cacheIndex = 2214; + reference = new EpsgCoordinateReferenceRecord(4647, (EpsgCoordinateSystemKind)2, 1946); + return true; + case 4652: + cacheIndex = 2215; + reference = new EpsgCoordinateReferenceRecord(4652, (EpsgCoordinateSystemKind)2, 1947); + return true; + case 4653: + cacheIndex = 2216; + reference = new EpsgCoordinateReferenceRecord(4653, (EpsgCoordinateSystemKind)2, 1948); + return true; + case 4654: + cacheIndex = 2217; + reference = new EpsgCoordinateReferenceRecord(4654, (EpsgCoordinateSystemKind)2, 1949); + return true; + case 4655: + cacheIndex = 2218; + reference = new EpsgCoordinateReferenceRecord(4655, (EpsgCoordinateSystemKind)2, 1950); + return true; + case 4656: + cacheIndex = 2219; + reference = new EpsgCoordinateReferenceRecord(4656, (EpsgCoordinateSystemKind)2, 1951); + return true; + case 4657: + cacheIndex = 2220; + reference = new EpsgCoordinateReferenceRecord(4657, (EpsgCoordinateSystemKind)0, 248); + return true; + case 4658: + cacheIndex = 2221; + reference = new EpsgCoordinateReferenceRecord(4658, (EpsgCoordinateSystemKind)0, 249); + return true; + case 4659: + cacheIndex = 2222; + reference = new EpsgCoordinateReferenceRecord(4659, (EpsgCoordinateSystemKind)0, 250); + return true; + case 4660: + cacheIndex = 2223; + reference = new EpsgCoordinateReferenceRecord(4660, (EpsgCoordinateSystemKind)0, 251); + return true; + case 4661: + cacheIndex = 2224; + reference = new EpsgCoordinateReferenceRecord(4661, (EpsgCoordinateSystemKind)0, 252); + return true; + case 4662: + cacheIndex = 2225; + reference = new EpsgCoordinateReferenceRecord(4662, (EpsgCoordinateSystemKind)0, 253); + return true; + case 4663: + cacheIndex = 2226; + reference = new EpsgCoordinateReferenceRecord(4663, (EpsgCoordinateSystemKind)0, 254); + return true; + case 4664: + cacheIndex = 2227; + reference = new EpsgCoordinateReferenceRecord(4664, (EpsgCoordinateSystemKind)0, 255); + return true; + case 4665: + cacheIndex = 2228; + reference = new EpsgCoordinateReferenceRecord(4665, (EpsgCoordinateSystemKind)0, 256); + return true; + case 4666: + cacheIndex = 2229; + reference = new EpsgCoordinateReferenceRecord(4666, (EpsgCoordinateSystemKind)0, 257); + return true; + case 4667: + cacheIndex = 2230; + reference = new EpsgCoordinateReferenceRecord(4667, (EpsgCoordinateSystemKind)0, 258); + return true; + case 4668: + cacheIndex = 2231; + reference = new EpsgCoordinateReferenceRecord(4668, (EpsgCoordinateSystemKind)0, 259); + return true; + case 4669: + cacheIndex = 2232; + reference = new EpsgCoordinateReferenceRecord(4669, (EpsgCoordinateSystemKind)0, 260); + return true; + case 4670: + cacheIndex = 2233; + reference = new EpsgCoordinateReferenceRecord(4670, (EpsgCoordinateSystemKind)0, 261); + return true; + case 4671: + cacheIndex = 2234; + reference = new EpsgCoordinateReferenceRecord(4671, (EpsgCoordinateSystemKind)0, 262); + return true; + case 4672: + cacheIndex = 2235; + reference = new EpsgCoordinateReferenceRecord(4672, (EpsgCoordinateSystemKind)0, 263); + return true; + case 4673: + cacheIndex = 2236; + reference = new EpsgCoordinateReferenceRecord(4673, (EpsgCoordinateSystemKind)0, 264); + return true; + case 4674: + cacheIndex = 2237; + reference = new EpsgCoordinateReferenceRecord(4674, (EpsgCoordinateSystemKind)0, 265); + return true; + case 4675: + cacheIndex = 2238; + reference = new EpsgCoordinateReferenceRecord(4675, (EpsgCoordinateSystemKind)0, 266); + return true; + case 4676: + cacheIndex = 2239; + reference = new EpsgCoordinateReferenceRecord(4676, (EpsgCoordinateSystemKind)0, 267); + return true; + case 4677: + cacheIndex = 2240; + reference = new EpsgCoordinateReferenceRecord(4677, (EpsgCoordinateSystemKind)0, 268); + return true; + case 4678: + cacheIndex = 2241; + reference = new EpsgCoordinateReferenceRecord(4678, (EpsgCoordinateSystemKind)0, 269); + return true; + case 4679: + cacheIndex = 2242; + reference = new EpsgCoordinateReferenceRecord(4679, (EpsgCoordinateSystemKind)0, 270); + return true; + case 4680: + cacheIndex = 2243; + reference = new EpsgCoordinateReferenceRecord(4680, (EpsgCoordinateSystemKind)0, 271); + return true; + case 4682: + cacheIndex = 2244; + reference = new EpsgCoordinateReferenceRecord(4682, (EpsgCoordinateSystemKind)0, 272); + return true; + case 4683: + cacheIndex = 2245; + reference = new EpsgCoordinateReferenceRecord(4683, (EpsgCoordinateSystemKind)0, 273); + return true; + case 4684: + cacheIndex = 2246; + reference = new EpsgCoordinateReferenceRecord(4684, (EpsgCoordinateSystemKind)0, 274); + return true; + case 4686: + cacheIndex = 2247; + reference = new EpsgCoordinateReferenceRecord(4686, (EpsgCoordinateSystemKind)0, 275); + return true; + case 4687: + cacheIndex = 2248; + reference = new EpsgCoordinateReferenceRecord(4687, (EpsgCoordinateSystemKind)0, 276); + return true; + case 4688: + cacheIndex = 2249; + reference = new EpsgCoordinateReferenceRecord(4688, (EpsgCoordinateSystemKind)0, 277); + return true; + case 4689: + cacheIndex = 2250; + reference = new EpsgCoordinateReferenceRecord(4689, (EpsgCoordinateSystemKind)0, 278); + return true; + case 4690: + cacheIndex = 2251; + reference = new EpsgCoordinateReferenceRecord(4690, (EpsgCoordinateSystemKind)0, 279); + return true; + case 4691: + cacheIndex = 2252; + reference = new EpsgCoordinateReferenceRecord(4691, (EpsgCoordinateSystemKind)0, 280); + return true; + case 4692: + cacheIndex = 2253; + reference = new EpsgCoordinateReferenceRecord(4692, (EpsgCoordinateSystemKind)0, 281); + return true; + case 4693: + cacheIndex = 2254; + reference = new EpsgCoordinateReferenceRecord(4693, (EpsgCoordinateSystemKind)0, 282); + return true; + case 4694: + cacheIndex = 2255; + reference = new EpsgCoordinateReferenceRecord(4694, (EpsgCoordinateSystemKind)0, 283); + return true; + case 4695: + cacheIndex = 2256; + reference = new EpsgCoordinateReferenceRecord(4695, (EpsgCoordinateSystemKind)0, 284); + return true; + case 4696: + cacheIndex = 2257; + reference = new EpsgCoordinateReferenceRecord(4696, (EpsgCoordinateSystemKind)0, 285); + return true; + case 4697: + cacheIndex = 2258; + reference = new EpsgCoordinateReferenceRecord(4697, (EpsgCoordinateSystemKind)0, 286); + return true; + case 4698: + cacheIndex = 2259; + reference = new EpsgCoordinateReferenceRecord(4698, (EpsgCoordinateSystemKind)0, 287); + return true; + case 4699: + cacheIndex = 2260; + reference = new EpsgCoordinateReferenceRecord(4699, (EpsgCoordinateSystemKind)0, 288); + return true; + case 4700: + cacheIndex = 2261; + reference = new EpsgCoordinateReferenceRecord(4700, (EpsgCoordinateSystemKind)0, 289); + return true; + case 4701: + cacheIndex = 2262; + reference = new EpsgCoordinateReferenceRecord(4701, (EpsgCoordinateSystemKind)0, 290); + return true; + case 4702: + cacheIndex = 2263; + reference = new EpsgCoordinateReferenceRecord(4702, (EpsgCoordinateSystemKind)0, 291); + return true; + case 4703: + cacheIndex = 2264; + reference = new EpsgCoordinateReferenceRecord(4703, (EpsgCoordinateSystemKind)0, 292); + return true; + case 4704: + cacheIndex = 2265; + reference = new EpsgCoordinateReferenceRecord(4704, (EpsgCoordinateSystemKind)0, 293); + return true; + case 4705: + cacheIndex = 2266; + reference = new EpsgCoordinateReferenceRecord(4705, (EpsgCoordinateSystemKind)0, 294); + return true; + case 4706: + cacheIndex = 2267; + reference = new EpsgCoordinateReferenceRecord(4706, (EpsgCoordinateSystemKind)0, 295); + return true; + case 4707: + cacheIndex = 2268; + reference = new EpsgCoordinateReferenceRecord(4707, (EpsgCoordinateSystemKind)0, 296); + return true; + case 4708: + cacheIndex = 2269; + reference = new EpsgCoordinateReferenceRecord(4708, (EpsgCoordinateSystemKind)0, 297); + return true; + case 4709: + cacheIndex = 2270; + reference = new EpsgCoordinateReferenceRecord(4709, (EpsgCoordinateSystemKind)0, 298); + return true; + case 4710: + cacheIndex = 2271; + reference = new EpsgCoordinateReferenceRecord(4710, (EpsgCoordinateSystemKind)0, 299); + return true; + case 4711: + cacheIndex = 2272; + reference = new EpsgCoordinateReferenceRecord(4711, (EpsgCoordinateSystemKind)0, 300); + return true; + case 4712: + cacheIndex = 2273; + reference = new EpsgCoordinateReferenceRecord(4712, (EpsgCoordinateSystemKind)0, 301); + return true; + case 4713: + cacheIndex = 2274; + reference = new EpsgCoordinateReferenceRecord(4713, (EpsgCoordinateSystemKind)0, 302); + return true; + case 4714: + cacheIndex = 2275; + reference = new EpsgCoordinateReferenceRecord(4714, (EpsgCoordinateSystemKind)0, 303); + return true; + case 4715: + cacheIndex = 2276; + reference = new EpsgCoordinateReferenceRecord(4715, (EpsgCoordinateSystemKind)0, 304); + return true; + case 4716: + cacheIndex = 2277; + reference = new EpsgCoordinateReferenceRecord(4716, (EpsgCoordinateSystemKind)0, 305); + return true; + case 4717: + cacheIndex = 2278; + reference = new EpsgCoordinateReferenceRecord(4717, (EpsgCoordinateSystemKind)0, 306); + return true; + case 4718: + cacheIndex = 2279; + reference = new EpsgCoordinateReferenceRecord(4718, (EpsgCoordinateSystemKind)0, 307); + return true; + case 4719: + cacheIndex = 2280; + reference = new EpsgCoordinateReferenceRecord(4719, (EpsgCoordinateSystemKind)0, 308); + return true; + case 4720: + cacheIndex = 2281; + reference = new EpsgCoordinateReferenceRecord(4720, (EpsgCoordinateSystemKind)0, 309); + return true; + case 4721: + cacheIndex = 2282; + reference = new EpsgCoordinateReferenceRecord(4721, (EpsgCoordinateSystemKind)0, 310); + return true; + case 4722: + cacheIndex = 2283; + reference = new EpsgCoordinateReferenceRecord(4722, (EpsgCoordinateSystemKind)0, 311); + return true; + case 4723: + cacheIndex = 2284; + reference = new EpsgCoordinateReferenceRecord(4723, (EpsgCoordinateSystemKind)0, 312); + return true; + case 4724: + cacheIndex = 2285; + reference = new EpsgCoordinateReferenceRecord(4724, (EpsgCoordinateSystemKind)0, 313); + return true; + case 4725: + cacheIndex = 2286; + reference = new EpsgCoordinateReferenceRecord(4725, (EpsgCoordinateSystemKind)0, 314); + return true; + case 4726: + cacheIndex = 2287; + reference = new EpsgCoordinateReferenceRecord(4726, (EpsgCoordinateSystemKind)0, 315); + return true; + case 4727: + cacheIndex = 2288; + reference = new EpsgCoordinateReferenceRecord(4727, (EpsgCoordinateSystemKind)0, 316); + return true; + case 4728: + cacheIndex = 2289; + reference = new EpsgCoordinateReferenceRecord(4728, (EpsgCoordinateSystemKind)0, 317); + return true; + case 4729: + cacheIndex = 2290; + reference = new EpsgCoordinateReferenceRecord(4729, (EpsgCoordinateSystemKind)0, 318); + return true; + case 4730: + cacheIndex = 2291; + reference = new EpsgCoordinateReferenceRecord(4730, (EpsgCoordinateSystemKind)0, 319); + return true; + case 4732: + cacheIndex = 2292; + reference = new EpsgCoordinateReferenceRecord(4732, (EpsgCoordinateSystemKind)0, 320); + return true; + case 4733: + cacheIndex = 2293; + reference = new EpsgCoordinateReferenceRecord(4733, (EpsgCoordinateSystemKind)0, 321); + return true; + case 4734: + cacheIndex = 2294; + reference = new EpsgCoordinateReferenceRecord(4734, (EpsgCoordinateSystemKind)0, 322); + return true; + case 4735: + cacheIndex = 2295; + reference = new EpsgCoordinateReferenceRecord(4735, (EpsgCoordinateSystemKind)0, 323); + return true; + case 4736: + cacheIndex = 2296; + reference = new EpsgCoordinateReferenceRecord(4736, (EpsgCoordinateSystemKind)0, 324); + return true; + case 4737: + cacheIndex = 2297; + reference = new EpsgCoordinateReferenceRecord(4737, (EpsgCoordinateSystemKind)0, 325); + return true; + case 4738: + cacheIndex = 2298; + reference = new EpsgCoordinateReferenceRecord(4738, (EpsgCoordinateSystemKind)0, 326); + return true; + case 4739: + cacheIndex = 2299; + reference = new EpsgCoordinateReferenceRecord(4739, (EpsgCoordinateSystemKind)0, 327); + return true; + case 4740: + cacheIndex = 2300; + reference = new EpsgCoordinateReferenceRecord(4740, (EpsgCoordinateSystemKind)0, 328); + return true; + case 4741: + cacheIndex = 2301; + reference = new EpsgCoordinateReferenceRecord(4741, (EpsgCoordinateSystemKind)0, 329); + return true; + case 4742: + cacheIndex = 2302; + reference = new EpsgCoordinateReferenceRecord(4742, (EpsgCoordinateSystemKind)0, 330); + return true; + case 4743: + cacheIndex = 2303; + reference = new EpsgCoordinateReferenceRecord(4743, (EpsgCoordinateSystemKind)0, 331); + return true; + case 4744: + cacheIndex = 2304; + reference = new EpsgCoordinateReferenceRecord(4744, (EpsgCoordinateSystemKind)0, 332); + return true; + case 4745: + cacheIndex = 2305; + reference = new EpsgCoordinateReferenceRecord(4745, (EpsgCoordinateSystemKind)0, 333); + return true; + case 4746: + cacheIndex = 2306; + reference = new EpsgCoordinateReferenceRecord(4746, (EpsgCoordinateSystemKind)0, 334); + return true; + case 4747: + cacheIndex = 2307; + reference = new EpsgCoordinateReferenceRecord(4747, (EpsgCoordinateSystemKind)0, 335); + return true; + case 4748: + cacheIndex = 2308; + reference = new EpsgCoordinateReferenceRecord(4748, (EpsgCoordinateSystemKind)0, 336); + return true; + case 4749: + cacheIndex = 2309; + reference = new EpsgCoordinateReferenceRecord(4749, (EpsgCoordinateSystemKind)0, 337); + return true; + case 4750: + cacheIndex = 2310; + reference = new EpsgCoordinateReferenceRecord(4750, (EpsgCoordinateSystemKind)0, 338); + return true; + case 4751: + cacheIndex = 2311; + reference = new EpsgCoordinateReferenceRecord(4751, (EpsgCoordinateSystemKind)0, 339); + return true; + case 4752: + cacheIndex = 2312; + reference = new EpsgCoordinateReferenceRecord(4752, (EpsgCoordinateSystemKind)0, 340); + return true; + case 4753: + cacheIndex = 2313; + reference = new EpsgCoordinateReferenceRecord(4753, (EpsgCoordinateSystemKind)0, 341); + return true; + case 4754: + cacheIndex = 2314; + reference = new EpsgCoordinateReferenceRecord(4754, (EpsgCoordinateSystemKind)0, 342); + return true; + case 4755: + cacheIndex = 2315; + reference = new EpsgCoordinateReferenceRecord(4755, (EpsgCoordinateSystemKind)0, 343); + return true; + case 4756: + cacheIndex = 2316; + reference = new EpsgCoordinateReferenceRecord(4756, (EpsgCoordinateSystemKind)0, 344); + return true; + case 4757: + cacheIndex = 2317; + reference = new EpsgCoordinateReferenceRecord(4757, (EpsgCoordinateSystemKind)0, 345); + return true; + case 4758: + cacheIndex = 2318; + reference = new EpsgCoordinateReferenceRecord(4758, (EpsgCoordinateSystemKind)0, 346); + return true; + case 4759: + cacheIndex = 2319; + reference = new EpsgCoordinateReferenceRecord(4759, (EpsgCoordinateSystemKind)0, 347); + return true; + case 4760: + cacheIndex = 2320; + reference = new EpsgCoordinateReferenceRecord(4760, (EpsgCoordinateSystemKind)0, 348); + return true; + case 4761: + cacheIndex = 2321; + reference = new EpsgCoordinateReferenceRecord(4761, (EpsgCoordinateSystemKind)0, 349); + return true; + case 4762: + cacheIndex = 2322; + reference = new EpsgCoordinateReferenceRecord(4762, (EpsgCoordinateSystemKind)0, 350); + return true; + case 4763: + cacheIndex = 2323; + reference = new EpsgCoordinateReferenceRecord(4763, (EpsgCoordinateSystemKind)0, 351); + return true; + case 4764: + cacheIndex = 2324; + reference = new EpsgCoordinateReferenceRecord(4764, (EpsgCoordinateSystemKind)0, 352); + return true; + case 4765: + cacheIndex = 2325; + reference = new EpsgCoordinateReferenceRecord(4765, (EpsgCoordinateSystemKind)0, 353); + return true; + case 4766: + cacheIndex = 2326; + reference = new EpsgCoordinateReferenceRecord(4766, (EpsgCoordinateSystemKind)2, 1952); + return true; + case 4767: + cacheIndex = 2327; + reference = new EpsgCoordinateReferenceRecord(4767, (EpsgCoordinateSystemKind)2, 1953); + return true; + case 4768: + cacheIndex = 2328; + reference = new EpsgCoordinateReferenceRecord(4768, (EpsgCoordinateSystemKind)2, 1954); + return true; + case 4769: + cacheIndex = 2329; + reference = new EpsgCoordinateReferenceRecord(4769, (EpsgCoordinateSystemKind)2, 1955); + return true; + case 4770: + cacheIndex = 2330; + reference = new EpsgCoordinateReferenceRecord(4770, (EpsgCoordinateSystemKind)2, 1956); + return true; + case 4771: + cacheIndex = 2331; + reference = new EpsgCoordinateReferenceRecord(4771, (EpsgCoordinateSystemKind)2, 1957); + return true; + case 4772: + cacheIndex = 2332; + reference = new EpsgCoordinateReferenceRecord(4772, (EpsgCoordinateSystemKind)2, 1958); + return true; + case 4773: + cacheIndex = 2333; + reference = new EpsgCoordinateReferenceRecord(4773, (EpsgCoordinateSystemKind)2, 1959); + return true; + case 4774: + cacheIndex = 2334; + reference = new EpsgCoordinateReferenceRecord(4774, (EpsgCoordinateSystemKind)2, 1960); + return true; + case 4775: + cacheIndex = 2335; + reference = new EpsgCoordinateReferenceRecord(4775, (EpsgCoordinateSystemKind)2, 1961); + return true; + case 4776: + cacheIndex = 2336; + reference = new EpsgCoordinateReferenceRecord(4776, (EpsgCoordinateSystemKind)2, 1962); + return true; + case 4777: + cacheIndex = 2337; + reference = new EpsgCoordinateReferenceRecord(4777, (EpsgCoordinateSystemKind)2, 1963); + return true; + case 4778: + cacheIndex = 2338; + reference = new EpsgCoordinateReferenceRecord(4778, (EpsgCoordinateSystemKind)2, 1964); + return true; + case 4779: + cacheIndex = 2339; + reference = new EpsgCoordinateReferenceRecord(4779, (EpsgCoordinateSystemKind)2, 1965); + return true; + case 4780: + cacheIndex = 2340; + reference = new EpsgCoordinateReferenceRecord(4780, (EpsgCoordinateSystemKind)2, 1966); + return true; + case 4781: + cacheIndex = 2341; + reference = new EpsgCoordinateReferenceRecord(4781, (EpsgCoordinateSystemKind)2, 1967); + return true; + case 4782: + cacheIndex = 2342; + reference = new EpsgCoordinateReferenceRecord(4782, (EpsgCoordinateSystemKind)2, 1968); + return true; + case 4783: + cacheIndex = 2343; + reference = new EpsgCoordinateReferenceRecord(4783, (EpsgCoordinateSystemKind)2, 1969); + return true; + case 4784: + cacheIndex = 2344; + reference = new EpsgCoordinateReferenceRecord(4784, (EpsgCoordinateSystemKind)2, 1970); + return true; + case 4785: + cacheIndex = 2345; + reference = new EpsgCoordinateReferenceRecord(4785, (EpsgCoordinateSystemKind)2, 1971); + return true; + case 4786: + cacheIndex = 2346; + reference = new EpsgCoordinateReferenceRecord(4786, (EpsgCoordinateSystemKind)2, 1972); + return true; + case 4787: + cacheIndex = 2347; + reference = new EpsgCoordinateReferenceRecord(4787, (EpsgCoordinateSystemKind)2, 1973); + return true; + case 4788: + cacheIndex = 2348; + reference = new EpsgCoordinateReferenceRecord(4788, (EpsgCoordinateSystemKind)2, 1974); + return true; + case 4789: + cacheIndex = 2349; + reference = new EpsgCoordinateReferenceRecord(4789, (EpsgCoordinateSystemKind)2, 1975); + return true; + case 4790: + cacheIndex = 2350; + reference = new EpsgCoordinateReferenceRecord(4790, (EpsgCoordinateSystemKind)2, 1976); + return true; + case 4791: + cacheIndex = 2351; + reference = new EpsgCoordinateReferenceRecord(4791, (EpsgCoordinateSystemKind)2, 1977); + return true; + case 4792: + cacheIndex = 2352; + reference = new EpsgCoordinateReferenceRecord(4792, (EpsgCoordinateSystemKind)2, 1978); + return true; + case 4793: + cacheIndex = 2353; + reference = new EpsgCoordinateReferenceRecord(4793, (EpsgCoordinateSystemKind)2, 1979); + return true; + case 4794: + cacheIndex = 2354; + reference = new EpsgCoordinateReferenceRecord(4794, (EpsgCoordinateSystemKind)2, 1980); + return true; + case 4795: + cacheIndex = 2355; + reference = new EpsgCoordinateReferenceRecord(4795, (EpsgCoordinateSystemKind)2, 1981); + return true; + case 4796: + cacheIndex = 2356; + reference = new EpsgCoordinateReferenceRecord(4796, (EpsgCoordinateSystemKind)2, 1982); + return true; + case 4797: + cacheIndex = 2357; + reference = new EpsgCoordinateReferenceRecord(4797, (EpsgCoordinateSystemKind)2, 1983); + return true; + case 4798: + cacheIndex = 2358; + reference = new EpsgCoordinateReferenceRecord(4798, (EpsgCoordinateSystemKind)2, 1984); + return true; + case 4799: + cacheIndex = 2359; + reference = new EpsgCoordinateReferenceRecord(4799, (EpsgCoordinateSystemKind)2, 1985); + return true; + case 4800: + cacheIndex = 2360; + reference = new EpsgCoordinateReferenceRecord(4800, (EpsgCoordinateSystemKind)2, 1986); + return true; + case 4801: + cacheIndex = 2361; + reference = new EpsgCoordinateReferenceRecord(4801, (EpsgCoordinateSystemKind)0, 354); + return true; + case 4802: + cacheIndex = 2362; + reference = new EpsgCoordinateReferenceRecord(4802, (EpsgCoordinateSystemKind)0, 355); + return true; + case 4803: + cacheIndex = 2363; + reference = new EpsgCoordinateReferenceRecord(4803, (EpsgCoordinateSystemKind)0, 356); + return true; + case 4804: + cacheIndex = 2364; + reference = new EpsgCoordinateReferenceRecord(4804, (EpsgCoordinateSystemKind)0, 357); + return true; + case 4805: + cacheIndex = 2365; + reference = new EpsgCoordinateReferenceRecord(4805, (EpsgCoordinateSystemKind)0, 358); + return true; + case 4806: + cacheIndex = 2366; + reference = new EpsgCoordinateReferenceRecord(4806, (EpsgCoordinateSystemKind)0, 359); + return true; + case 4807: + cacheIndex = 2367; + reference = new EpsgCoordinateReferenceRecord(4807, (EpsgCoordinateSystemKind)0, 360); + return true; + case 4809: + cacheIndex = 2368; + reference = new EpsgCoordinateReferenceRecord(4809, (EpsgCoordinateSystemKind)0, 361); + return true; + case 4810: + cacheIndex = 2369; + reference = new EpsgCoordinateReferenceRecord(4810, (EpsgCoordinateSystemKind)0, 362); + return true; + case 4811: + cacheIndex = 2370; + reference = new EpsgCoordinateReferenceRecord(4811, (EpsgCoordinateSystemKind)0, 363); + return true; + case 4812: + cacheIndex = 2371; + reference = new EpsgCoordinateReferenceRecord(4812, (EpsgCoordinateSystemKind)2, 1987); + return true; + case 4813: + cacheIndex = 2372; + reference = new EpsgCoordinateReferenceRecord(4813, (EpsgCoordinateSystemKind)0, 364); + return true; + case 4814: + cacheIndex = 2373; + reference = new EpsgCoordinateReferenceRecord(4814, (EpsgCoordinateSystemKind)0, 365); + return true; + case 4815: + cacheIndex = 2374; + reference = new EpsgCoordinateReferenceRecord(4815, (EpsgCoordinateSystemKind)0, 366); + return true; + case 4816: + cacheIndex = 2375; + reference = new EpsgCoordinateReferenceRecord(4816, (EpsgCoordinateSystemKind)0, 367); + return true; + case 4817: + cacheIndex = 2376; + reference = new EpsgCoordinateReferenceRecord(4817, (EpsgCoordinateSystemKind)0, 368); + return true; + case 4818: + cacheIndex = 2377; + reference = new EpsgCoordinateReferenceRecord(4818, (EpsgCoordinateSystemKind)0, 369); + return true; + case 4820: + cacheIndex = 2378; + reference = new EpsgCoordinateReferenceRecord(4820, (EpsgCoordinateSystemKind)0, 370); + return true; + case 4821: + cacheIndex = 2379; + reference = new EpsgCoordinateReferenceRecord(4821, (EpsgCoordinateSystemKind)0, 371); + return true; + case 4822: + cacheIndex = 2380; + reference = new EpsgCoordinateReferenceRecord(4822, (EpsgCoordinateSystemKind)2, 1988); + return true; + case 4823: + cacheIndex = 2381; + reference = new EpsgCoordinateReferenceRecord(4823, (EpsgCoordinateSystemKind)0, 372); + return true; + case 4824: + cacheIndex = 2382; + reference = new EpsgCoordinateReferenceRecord(4824, (EpsgCoordinateSystemKind)0, 373); + return true; + case 4826: + cacheIndex = 2383; + reference = new EpsgCoordinateReferenceRecord(4826, (EpsgCoordinateSystemKind)2, 1989); + return true; + case 4839: + cacheIndex = 2384; + reference = new EpsgCoordinateReferenceRecord(4839, (EpsgCoordinateSystemKind)2, 1990); + return true; + case 4882: + cacheIndex = 2385; + reference = new EpsgCoordinateReferenceRecord(4882, (EpsgCoordinateSystemKind)1, 12); + return true; + case 4883: + cacheIndex = 2386; + reference = new EpsgCoordinateReferenceRecord(4883, (EpsgCoordinateSystemKind)0, 374); + return true; + case 4884: + cacheIndex = 2387; + reference = new EpsgCoordinateReferenceRecord(4884, (EpsgCoordinateSystemKind)1, 13); + return true; + case 4885: + cacheIndex = 2388; + reference = new EpsgCoordinateReferenceRecord(4885, (EpsgCoordinateSystemKind)0, 375); + return true; + case 4886: + cacheIndex = 2389; + reference = new EpsgCoordinateReferenceRecord(4886, (EpsgCoordinateSystemKind)1, 14); + return true; + case 4887: + cacheIndex = 2390; + reference = new EpsgCoordinateReferenceRecord(4887, (EpsgCoordinateSystemKind)0, 376); + return true; + case 4888: + cacheIndex = 2391; + reference = new EpsgCoordinateReferenceRecord(4888, (EpsgCoordinateSystemKind)1, 15); + return true; + case 4889: + cacheIndex = 2392; + reference = new EpsgCoordinateReferenceRecord(4889, (EpsgCoordinateSystemKind)0, 377); + return true; + case 4890: + cacheIndex = 2393; + reference = new EpsgCoordinateReferenceRecord(4890, (EpsgCoordinateSystemKind)1, 16); + return true; + case 4891: + cacheIndex = 2394; + reference = new EpsgCoordinateReferenceRecord(4891, (EpsgCoordinateSystemKind)0, 378); + return true; + case 4892: + cacheIndex = 2395; + reference = new EpsgCoordinateReferenceRecord(4892, (EpsgCoordinateSystemKind)1, 17); + return true; + case 4893: + cacheIndex = 2396; + reference = new EpsgCoordinateReferenceRecord(4893, (EpsgCoordinateSystemKind)0, 379); + return true; + case 4894: + cacheIndex = 2397; + reference = new EpsgCoordinateReferenceRecord(4894, (EpsgCoordinateSystemKind)1, 18); + return true; + case 4895: + cacheIndex = 2398; + reference = new EpsgCoordinateReferenceRecord(4895, (EpsgCoordinateSystemKind)0, 380); + return true; + case 4896: + cacheIndex = 2399; + reference = new EpsgCoordinateReferenceRecord(4896, (EpsgCoordinateSystemKind)1, 19); + return true; + case 4897: + cacheIndex = 2400; + reference = new EpsgCoordinateReferenceRecord(4897, (EpsgCoordinateSystemKind)1, 20); + return true; + case 4898: + cacheIndex = 2401; + reference = new EpsgCoordinateReferenceRecord(4898, (EpsgCoordinateSystemKind)0, 381); + return true; + case 4899: + cacheIndex = 2402; + reference = new EpsgCoordinateReferenceRecord(4899, (EpsgCoordinateSystemKind)1, 21); + return true; + case 4900: + cacheIndex = 2403; + reference = new EpsgCoordinateReferenceRecord(4900, (EpsgCoordinateSystemKind)0, 382); + return true; + case 4901: + cacheIndex = 2404; + reference = new EpsgCoordinateReferenceRecord(4901, (EpsgCoordinateSystemKind)0, 383); + return true; + case 4903: + cacheIndex = 2405; + reference = new EpsgCoordinateReferenceRecord(4903, (EpsgCoordinateSystemKind)0, 384); + return true; + case 4904: + cacheIndex = 2406; + reference = new EpsgCoordinateReferenceRecord(4904, (EpsgCoordinateSystemKind)0, 385); + return true; + case 4906: + cacheIndex = 2407; + reference = new EpsgCoordinateReferenceRecord(4906, (EpsgCoordinateSystemKind)1, 22); + return true; + case 4907: + cacheIndex = 2408; + reference = new EpsgCoordinateReferenceRecord(4907, (EpsgCoordinateSystemKind)0, 386); + return true; + case 4908: + cacheIndex = 2409; + reference = new EpsgCoordinateReferenceRecord(4908, (EpsgCoordinateSystemKind)1, 23); + return true; + case 4909: + cacheIndex = 2410; + reference = new EpsgCoordinateReferenceRecord(4909, (EpsgCoordinateSystemKind)0, 387); + return true; + case 4910: + cacheIndex = 2411; + reference = new EpsgCoordinateReferenceRecord(4910, (EpsgCoordinateSystemKind)1, 24); + return true; + case 4911: + cacheIndex = 2412; + reference = new EpsgCoordinateReferenceRecord(4911, (EpsgCoordinateSystemKind)1, 25); + return true; + case 4912: + cacheIndex = 2413; + reference = new EpsgCoordinateReferenceRecord(4912, (EpsgCoordinateSystemKind)1, 26); + return true; + case 4913: + cacheIndex = 2414; + reference = new EpsgCoordinateReferenceRecord(4913, (EpsgCoordinateSystemKind)1, 27); + return true; + case 4914: + cacheIndex = 2415; + reference = new EpsgCoordinateReferenceRecord(4914, (EpsgCoordinateSystemKind)1, 28); + return true; + case 4915: + cacheIndex = 2416; + reference = new EpsgCoordinateReferenceRecord(4915, (EpsgCoordinateSystemKind)1, 29); + return true; + case 4916: + cacheIndex = 2417; + reference = new EpsgCoordinateReferenceRecord(4916, (EpsgCoordinateSystemKind)1, 30); + return true; + case 4917: + cacheIndex = 2418; + reference = new EpsgCoordinateReferenceRecord(4917, (EpsgCoordinateSystemKind)1, 31); + return true; + case 4918: + cacheIndex = 2419; + reference = new EpsgCoordinateReferenceRecord(4918, (EpsgCoordinateSystemKind)1, 32); + return true; + case 4919: + cacheIndex = 2420; + reference = new EpsgCoordinateReferenceRecord(4919, (EpsgCoordinateSystemKind)1, 33); + return true; + case 4920: + cacheIndex = 2421; + reference = new EpsgCoordinateReferenceRecord(4920, (EpsgCoordinateSystemKind)1, 34); + return true; + case 4921: + cacheIndex = 2422; + reference = new EpsgCoordinateReferenceRecord(4921, (EpsgCoordinateSystemKind)0, 388); + return true; + case 4922: + cacheIndex = 2423; + reference = new EpsgCoordinateReferenceRecord(4922, (EpsgCoordinateSystemKind)1, 35); + return true; + case 4923: + cacheIndex = 2424; + reference = new EpsgCoordinateReferenceRecord(4923, (EpsgCoordinateSystemKind)0, 389); + return true; + case 4924: + cacheIndex = 2425; + reference = new EpsgCoordinateReferenceRecord(4924, (EpsgCoordinateSystemKind)1, 36); + return true; + case 4925: + cacheIndex = 2426; + reference = new EpsgCoordinateReferenceRecord(4925, (EpsgCoordinateSystemKind)0, 390); + return true; + case 4926: + cacheIndex = 2427; + reference = new EpsgCoordinateReferenceRecord(4926, (EpsgCoordinateSystemKind)1, 37); + return true; + case 4927: + cacheIndex = 2428; + reference = new EpsgCoordinateReferenceRecord(4927, (EpsgCoordinateSystemKind)0, 391); + return true; + case 4928: + cacheIndex = 2429; + reference = new EpsgCoordinateReferenceRecord(4928, (EpsgCoordinateSystemKind)1, 38); + return true; + case 4929: + cacheIndex = 2430; + reference = new EpsgCoordinateReferenceRecord(4929, (EpsgCoordinateSystemKind)0, 392); + return true; + case 4930: + cacheIndex = 2431; + reference = new EpsgCoordinateReferenceRecord(4930, (EpsgCoordinateSystemKind)1, 39); + return true; + case 4931: + cacheIndex = 2432; + reference = new EpsgCoordinateReferenceRecord(4931, (EpsgCoordinateSystemKind)0, 393); + return true; + case 4932: + cacheIndex = 2433; + reference = new EpsgCoordinateReferenceRecord(4932, (EpsgCoordinateSystemKind)1, 40); + return true; + case 4933: + cacheIndex = 2434; + reference = new EpsgCoordinateReferenceRecord(4933, (EpsgCoordinateSystemKind)0, 394); + return true; + case 4934: + cacheIndex = 2435; + reference = new EpsgCoordinateReferenceRecord(4934, (EpsgCoordinateSystemKind)1, 41); + return true; + case 4935: + cacheIndex = 2436; + reference = new EpsgCoordinateReferenceRecord(4935, (EpsgCoordinateSystemKind)0, 395); + return true; + case 4936: + cacheIndex = 2437; + reference = new EpsgCoordinateReferenceRecord(4936, (EpsgCoordinateSystemKind)1, 42); + return true; + case 4937: + cacheIndex = 2438; + reference = new EpsgCoordinateReferenceRecord(4937, (EpsgCoordinateSystemKind)0, 396); + return true; + case 4938: + cacheIndex = 2439; + reference = new EpsgCoordinateReferenceRecord(4938, (EpsgCoordinateSystemKind)1, 43); + return true; + case 4939: + cacheIndex = 2440; + reference = new EpsgCoordinateReferenceRecord(4939, (EpsgCoordinateSystemKind)0, 397); + return true; + case 4940: + cacheIndex = 2441; + reference = new EpsgCoordinateReferenceRecord(4940, (EpsgCoordinateSystemKind)1, 44); + return true; + case 4941: + cacheIndex = 2442; + reference = new EpsgCoordinateReferenceRecord(4941, (EpsgCoordinateSystemKind)0, 398); + return true; + case 4942: + cacheIndex = 2443; + reference = new EpsgCoordinateReferenceRecord(4942, (EpsgCoordinateSystemKind)1, 45); + return true; + case 4943: + cacheIndex = 2444; + reference = new EpsgCoordinateReferenceRecord(4943, (EpsgCoordinateSystemKind)0, 399); + return true; + case 4944: + cacheIndex = 2445; + reference = new EpsgCoordinateReferenceRecord(4944, (EpsgCoordinateSystemKind)1, 46); + return true; + case 4945: + cacheIndex = 2446; + reference = new EpsgCoordinateReferenceRecord(4945, (EpsgCoordinateSystemKind)0, 400); + return true; + case 4946: + cacheIndex = 2447; + reference = new EpsgCoordinateReferenceRecord(4946, (EpsgCoordinateSystemKind)1, 47); + return true; + case 4947: + cacheIndex = 2448; + reference = new EpsgCoordinateReferenceRecord(4947, (EpsgCoordinateSystemKind)0, 401); + return true; + case 4948: + cacheIndex = 2449; + reference = new EpsgCoordinateReferenceRecord(4948, (EpsgCoordinateSystemKind)1, 48); + return true; + case 4949: + cacheIndex = 2450; + reference = new EpsgCoordinateReferenceRecord(4949, (EpsgCoordinateSystemKind)0, 402); + return true; + case 4950: + cacheIndex = 2451; + reference = new EpsgCoordinateReferenceRecord(4950, (EpsgCoordinateSystemKind)1, 49); + return true; + case 4951: + cacheIndex = 2452; + reference = new EpsgCoordinateReferenceRecord(4951, (EpsgCoordinateSystemKind)0, 403); + return true; + case 4952: + cacheIndex = 2453; + reference = new EpsgCoordinateReferenceRecord(4952, (EpsgCoordinateSystemKind)1, 50); + return true; + case 4953: + cacheIndex = 2454; + reference = new EpsgCoordinateReferenceRecord(4953, (EpsgCoordinateSystemKind)0, 404); + return true; + case 4954: + cacheIndex = 2455; + reference = new EpsgCoordinateReferenceRecord(4954, (EpsgCoordinateSystemKind)1, 51); + return true; + case 4955: + cacheIndex = 2456; + reference = new EpsgCoordinateReferenceRecord(4955, (EpsgCoordinateSystemKind)0, 405); + return true; + case 4956: + cacheIndex = 2457; + reference = new EpsgCoordinateReferenceRecord(4956, (EpsgCoordinateSystemKind)1, 52); + return true; + case 4957: + cacheIndex = 2458; + reference = new EpsgCoordinateReferenceRecord(4957, (EpsgCoordinateSystemKind)0, 406); + return true; + case 4958: + cacheIndex = 2459; + reference = new EpsgCoordinateReferenceRecord(4958, (EpsgCoordinateSystemKind)1, 53); + return true; + case 4959: + cacheIndex = 2460; + reference = new EpsgCoordinateReferenceRecord(4959, (EpsgCoordinateSystemKind)0, 407); + return true; + case 4960: + cacheIndex = 2461; + reference = new EpsgCoordinateReferenceRecord(4960, (EpsgCoordinateSystemKind)1, 54); + return true; + case 4961: + cacheIndex = 2462; + reference = new EpsgCoordinateReferenceRecord(4961, (EpsgCoordinateSystemKind)0, 408); + return true; + case 4962: + cacheIndex = 2463; + reference = new EpsgCoordinateReferenceRecord(4962, (EpsgCoordinateSystemKind)1, 55); + return true; + case 4963: + cacheIndex = 2464; + reference = new EpsgCoordinateReferenceRecord(4963, (EpsgCoordinateSystemKind)0, 409); + return true; + case 4964: + cacheIndex = 2465; + reference = new EpsgCoordinateReferenceRecord(4964, (EpsgCoordinateSystemKind)1, 56); + return true; + case 4965: + cacheIndex = 2466; + reference = new EpsgCoordinateReferenceRecord(4965, (EpsgCoordinateSystemKind)0, 410); + return true; + case 4966: + cacheIndex = 2467; + reference = new EpsgCoordinateReferenceRecord(4966, (EpsgCoordinateSystemKind)1, 57); + return true; + case 4967: + cacheIndex = 2468; + reference = new EpsgCoordinateReferenceRecord(4967, (EpsgCoordinateSystemKind)0, 411); + return true; + case 4970: + cacheIndex = 2469; + reference = new EpsgCoordinateReferenceRecord(4970, (EpsgCoordinateSystemKind)1, 58); + return true; + case 4971: + cacheIndex = 2470; + reference = new EpsgCoordinateReferenceRecord(4971, (EpsgCoordinateSystemKind)0, 412); + return true; + case 4974: + cacheIndex = 2471; + reference = new EpsgCoordinateReferenceRecord(4974, (EpsgCoordinateSystemKind)1, 59); + return true; + case 4975: + cacheIndex = 2472; + reference = new EpsgCoordinateReferenceRecord(4975, (EpsgCoordinateSystemKind)0, 413); + return true; + case 4976: + cacheIndex = 2473; + reference = new EpsgCoordinateReferenceRecord(4976, (EpsgCoordinateSystemKind)1, 60); + return true; + case 4977: + cacheIndex = 2474; + reference = new EpsgCoordinateReferenceRecord(4977, (EpsgCoordinateSystemKind)0, 414); + return true; + case 4978: + cacheIndex = 2475; + reference = new EpsgCoordinateReferenceRecord(4978, (EpsgCoordinateSystemKind)1, 61); + return true; + case 4979: + cacheIndex = 2476; + reference = new EpsgCoordinateReferenceRecord(4979, (EpsgCoordinateSystemKind)0, 415); + return true; + case 4980: + cacheIndex = 2477; + reference = new EpsgCoordinateReferenceRecord(4980, (EpsgCoordinateSystemKind)1, 62); + return true; + case 4981: + cacheIndex = 2478; + reference = new EpsgCoordinateReferenceRecord(4981, (EpsgCoordinateSystemKind)0, 416); + return true; + case 4982: + cacheIndex = 2479; + reference = new EpsgCoordinateReferenceRecord(4982, (EpsgCoordinateSystemKind)1, 63); + return true; + case 4983: + cacheIndex = 2480; + reference = new EpsgCoordinateReferenceRecord(4983, (EpsgCoordinateSystemKind)0, 417); + return true; + case 4984: + cacheIndex = 2481; + reference = new EpsgCoordinateReferenceRecord(4984, (EpsgCoordinateSystemKind)1, 64); + return true; + case 4985: + cacheIndex = 2482; + reference = new EpsgCoordinateReferenceRecord(4985, (EpsgCoordinateSystemKind)0, 418); + return true; + case 4986: + cacheIndex = 2483; + reference = new EpsgCoordinateReferenceRecord(4986, (EpsgCoordinateSystemKind)1, 65); + return true; + case 4987: + cacheIndex = 2484; + reference = new EpsgCoordinateReferenceRecord(4987, (EpsgCoordinateSystemKind)0, 419); + return true; + case 4988: + cacheIndex = 2485; + reference = new EpsgCoordinateReferenceRecord(4988, (EpsgCoordinateSystemKind)1, 66); + return true; + case 4989: + cacheIndex = 2486; + reference = new EpsgCoordinateReferenceRecord(4989, (EpsgCoordinateSystemKind)0, 420); + return true; + case 4990: + cacheIndex = 2487; + reference = new EpsgCoordinateReferenceRecord(4990, (EpsgCoordinateSystemKind)1, 67); + return true; + case 4991: + cacheIndex = 2488; + reference = new EpsgCoordinateReferenceRecord(4991, (EpsgCoordinateSystemKind)0, 421); + return true; + case 4992: + cacheIndex = 2489; + reference = new EpsgCoordinateReferenceRecord(4992, (EpsgCoordinateSystemKind)1, 68); + return true; + case 4993: + cacheIndex = 2490; + reference = new EpsgCoordinateReferenceRecord(4993, (EpsgCoordinateSystemKind)0, 422); + return true; + case 4994: + cacheIndex = 2491; + reference = new EpsgCoordinateReferenceRecord(4994, (EpsgCoordinateSystemKind)1, 69); + return true; + case 4995: + cacheIndex = 2492; + reference = new EpsgCoordinateReferenceRecord(4995, (EpsgCoordinateSystemKind)0, 423); + return true; + case 4996: + cacheIndex = 2493; + reference = new EpsgCoordinateReferenceRecord(4996, (EpsgCoordinateSystemKind)1, 70); + return true; + case 4997: + cacheIndex = 2494; + reference = new EpsgCoordinateReferenceRecord(4997, (EpsgCoordinateSystemKind)0, 424); + return true; + case 4998: + cacheIndex = 2495; + reference = new EpsgCoordinateReferenceRecord(4998, (EpsgCoordinateSystemKind)1, 71); + return true; + case 4999: + cacheIndex = 2496; + reference = new EpsgCoordinateReferenceRecord(4999, (EpsgCoordinateSystemKind)0, 425); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket5(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 5011: + cacheIndex = 2497; + reference = new EpsgCoordinateReferenceRecord(5011, (EpsgCoordinateSystemKind)1, 72); + return true; + case 5012: + cacheIndex = 2498; + reference = new EpsgCoordinateReferenceRecord(5012, (EpsgCoordinateSystemKind)0, 426); + return true; + case 5013: + cacheIndex = 2499; + reference = new EpsgCoordinateReferenceRecord(5013, (EpsgCoordinateSystemKind)0, 427); + return true; + case 5014: + cacheIndex = 2500; + reference = new EpsgCoordinateReferenceRecord(5014, (EpsgCoordinateSystemKind)2, 1991); + return true; + case 5015: + cacheIndex = 2501; + reference = new EpsgCoordinateReferenceRecord(5015, (EpsgCoordinateSystemKind)2, 1992); + return true; + case 5016: + cacheIndex = 2502; + reference = new EpsgCoordinateReferenceRecord(5016, (EpsgCoordinateSystemKind)2, 1993); + return true; + case 5017: + cacheIndex = 2503; + reference = new EpsgCoordinateReferenceRecord(5017, (EpsgCoordinateSystemKind)2, 1994); + return true; + case 5018: + cacheIndex = 2504; + reference = new EpsgCoordinateReferenceRecord(5018, (EpsgCoordinateSystemKind)2, 1995); + return true; + case 5041: + cacheIndex = 2505; + reference = new EpsgCoordinateReferenceRecord(5041, (EpsgCoordinateSystemKind)2, 1996); + return true; + case 5042: + cacheIndex = 2506; + reference = new EpsgCoordinateReferenceRecord(5042, (EpsgCoordinateSystemKind)2, 1997); + return true; + case 5048: + cacheIndex = 2507; + reference = new EpsgCoordinateReferenceRecord(5048, (EpsgCoordinateSystemKind)2, 1998); + return true; + case 5069: + cacheIndex = 2508; + reference = new EpsgCoordinateReferenceRecord(5069, (EpsgCoordinateSystemKind)2, 1999); + return true; + case 5070: + cacheIndex = 2509; + reference = new EpsgCoordinateReferenceRecord(5070, (EpsgCoordinateSystemKind)2, 2000); + return true; + case 5071: + cacheIndex = 2510; + reference = new EpsgCoordinateReferenceRecord(5071, (EpsgCoordinateSystemKind)2, 2001); + return true; + case 5072: + cacheIndex = 2511; + reference = new EpsgCoordinateReferenceRecord(5072, (EpsgCoordinateSystemKind)2, 2002); + return true; + case 5105: + cacheIndex = 2512; + reference = new EpsgCoordinateReferenceRecord(5105, (EpsgCoordinateSystemKind)2, 2003); + return true; + case 5106: + cacheIndex = 2513; + reference = new EpsgCoordinateReferenceRecord(5106, (EpsgCoordinateSystemKind)2, 2004); + return true; + case 5107: + cacheIndex = 2514; + reference = new EpsgCoordinateReferenceRecord(5107, (EpsgCoordinateSystemKind)2, 2005); + return true; + case 5108: + cacheIndex = 2515; + reference = new EpsgCoordinateReferenceRecord(5108, (EpsgCoordinateSystemKind)2, 2006); + return true; + case 5109: + cacheIndex = 2516; + reference = new EpsgCoordinateReferenceRecord(5109, (EpsgCoordinateSystemKind)2, 2007); + return true; + case 5110: + cacheIndex = 2517; + reference = new EpsgCoordinateReferenceRecord(5110, (EpsgCoordinateSystemKind)2, 2008); + return true; + case 5111: + cacheIndex = 2518; + reference = new EpsgCoordinateReferenceRecord(5111, (EpsgCoordinateSystemKind)2, 2009); + return true; + case 5112: + cacheIndex = 2519; + reference = new EpsgCoordinateReferenceRecord(5112, (EpsgCoordinateSystemKind)2, 2010); + return true; + case 5113: + cacheIndex = 2520; + reference = new EpsgCoordinateReferenceRecord(5113, (EpsgCoordinateSystemKind)2, 2011); + return true; + case 5114: + cacheIndex = 2521; + reference = new EpsgCoordinateReferenceRecord(5114, (EpsgCoordinateSystemKind)2, 2012); + return true; + case 5115: + cacheIndex = 2522; + reference = new EpsgCoordinateReferenceRecord(5115, (EpsgCoordinateSystemKind)2, 2013); + return true; + case 5116: + cacheIndex = 2523; + reference = new EpsgCoordinateReferenceRecord(5116, (EpsgCoordinateSystemKind)2, 2014); + return true; + case 5117: + cacheIndex = 2524; + reference = new EpsgCoordinateReferenceRecord(5117, (EpsgCoordinateSystemKind)2, 2015); + return true; + case 5118: + cacheIndex = 2525; + reference = new EpsgCoordinateReferenceRecord(5118, (EpsgCoordinateSystemKind)2, 2016); + return true; + case 5119: + cacheIndex = 2526; + reference = new EpsgCoordinateReferenceRecord(5119, (EpsgCoordinateSystemKind)2, 2017); + return true; + case 5120: + cacheIndex = 2527; + reference = new EpsgCoordinateReferenceRecord(5120, (EpsgCoordinateSystemKind)2, 2018); + return true; + case 5121: + cacheIndex = 2528; + reference = new EpsgCoordinateReferenceRecord(5121, (EpsgCoordinateSystemKind)2, 2019); + return true; + case 5122: + cacheIndex = 2529; + reference = new EpsgCoordinateReferenceRecord(5122, (EpsgCoordinateSystemKind)2, 2020); + return true; + case 5123: + cacheIndex = 2530; + reference = new EpsgCoordinateReferenceRecord(5123, (EpsgCoordinateSystemKind)2, 2021); + return true; + case 5124: + cacheIndex = 2531; + reference = new EpsgCoordinateReferenceRecord(5124, (EpsgCoordinateSystemKind)2, 2022); + return true; + case 5125: + cacheIndex = 2532; + reference = new EpsgCoordinateReferenceRecord(5125, (EpsgCoordinateSystemKind)2, 2023); + return true; + case 5126: + cacheIndex = 2533; + reference = new EpsgCoordinateReferenceRecord(5126, (EpsgCoordinateSystemKind)2, 2024); + return true; + case 5127: + cacheIndex = 2534; + reference = new EpsgCoordinateReferenceRecord(5127, (EpsgCoordinateSystemKind)2, 2025); + return true; + case 5128: + cacheIndex = 2535; + reference = new EpsgCoordinateReferenceRecord(5128, (EpsgCoordinateSystemKind)2, 2026); + return true; + case 5129: + cacheIndex = 2536; + reference = new EpsgCoordinateReferenceRecord(5129, (EpsgCoordinateSystemKind)2, 2027); + return true; + case 5130: + cacheIndex = 2537; + reference = new EpsgCoordinateReferenceRecord(5130, (EpsgCoordinateSystemKind)2, 2028); + return true; + case 5132: + cacheIndex = 2538; + reference = new EpsgCoordinateReferenceRecord(5132, (EpsgCoordinateSystemKind)0, 428); + return true; + case 5167: + cacheIndex = 2539; + reference = new EpsgCoordinateReferenceRecord(5167, (EpsgCoordinateSystemKind)2, 2029); + return true; + case 5168: + cacheIndex = 2540; + reference = new EpsgCoordinateReferenceRecord(5168, (EpsgCoordinateSystemKind)2, 2030); + return true; + case 5169: + cacheIndex = 2541; + reference = new EpsgCoordinateReferenceRecord(5169, (EpsgCoordinateSystemKind)2, 2031); + return true; + case 5170: + cacheIndex = 2542; + reference = new EpsgCoordinateReferenceRecord(5170, (EpsgCoordinateSystemKind)2, 2032); + return true; + case 5171: + cacheIndex = 2543; + reference = new EpsgCoordinateReferenceRecord(5171, (EpsgCoordinateSystemKind)2, 2033); + return true; + case 5172: + cacheIndex = 2544; + reference = new EpsgCoordinateReferenceRecord(5172, (EpsgCoordinateSystemKind)2, 2034); + return true; + case 5173: + cacheIndex = 2545; + reference = new EpsgCoordinateReferenceRecord(5173, (EpsgCoordinateSystemKind)2, 2035); + return true; + case 5174: + cacheIndex = 2546; + reference = new EpsgCoordinateReferenceRecord(5174, (EpsgCoordinateSystemKind)2, 2036); + return true; + case 5175: + cacheIndex = 2547; + reference = new EpsgCoordinateReferenceRecord(5175, (EpsgCoordinateSystemKind)2, 2037); + return true; + case 5176: + cacheIndex = 2548; + reference = new EpsgCoordinateReferenceRecord(5176, (EpsgCoordinateSystemKind)2, 2038); + return true; + case 5177: + cacheIndex = 2549; + reference = new EpsgCoordinateReferenceRecord(5177, (EpsgCoordinateSystemKind)2, 2039); + return true; + case 5178: + cacheIndex = 2550; + reference = new EpsgCoordinateReferenceRecord(5178, (EpsgCoordinateSystemKind)2, 2040); + return true; + case 5179: + cacheIndex = 2551; + reference = new EpsgCoordinateReferenceRecord(5179, (EpsgCoordinateSystemKind)2, 2041); + return true; + case 5180: + cacheIndex = 2552; + reference = new EpsgCoordinateReferenceRecord(5180, (EpsgCoordinateSystemKind)2, 2042); + return true; + case 5181: + cacheIndex = 2553; + reference = new EpsgCoordinateReferenceRecord(5181, (EpsgCoordinateSystemKind)2, 2043); + return true; + case 5182: + cacheIndex = 2554; + reference = new EpsgCoordinateReferenceRecord(5182, (EpsgCoordinateSystemKind)2, 2044); + return true; + case 5183: + cacheIndex = 2555; + reference = new EpsgCoordinateReferenceRecord(5183, (EpsgCoordinateSystemKind)2, 2045); + return true; + case 5184: + cacheIndex = 2556; + reference = new EpsgCoordinateReferenceRecord(5184, (EpsgCoordinateSystemKind)2, 2046); + return true; + case 5185: + cacheIndex = 2557; + reference = new EpsgCoordinateReferenceRecord(5185, (EpsgCoordinateSystemKind)2, 2047); + return true; + case 5186: + cacheIndex = 2558; + reference = new EpsgCoordinateReferenceRecord(5186, (EpsgCoordinateSystemKind)2, 2048); + return true; + case 5187: + cacheIndex = 2559; + reference = new EpsgCoordinateReferenceRecord(5187, (EpsgCoordinateSystemKind)2, 2049); + return true; + case 5188: + cacheIndex = 2560; + reference = new EpsgCoordinateReferenceRecord(5188, (EpsgCoordinateSystemKind)2, 2050); + return true; + case 5193: + cacheIndex = 2561; + reference = new EpsgCoordinateReferenceRecord(5193, (EpsgCoordinateSystemKind)3, 5); + return true; + case 5195: + cacheIndex = 2562; + reference = new EpsgCoordinateReferenceRecord(5195, (EpsgCoordinateSystemKind)3, 6); + return true; + case 5214: + cacheIndex = 2563; + reference = new EpsgCoordinateReferenceRecord(5214, (EpsgCoordinateSystemKind)3, 7); + return true; + case 5221: + cacheIndex = 2564; + reference = new EpsgCoordinateReferenceRecord(5221, (EpsgCoordinateSystemKind)2, 2051); + return true; + case 5223: + cacheIndex = 2565; + reference = new EpsgCoordinateReferenceRecord(5223, (EpsgCoordinateSystemKind)2, 2052); + return true; + case 5224: + cacheIndex = 2566; + reference = new EpsgCoordinateReferenceRecord(5224, (EpsgCoordinateSystemKind)2, 2053); + return true; + case 5225: + cacheIndex = 2567; + reference = new EpsgCoordinateReferenceRecord(5225, (EpsgCoordinateSystemKind)2, 2054); + return true; + case 5228: + cacheIndex = 2568; + reference = new EpsgCoordinateReferenceRecord(5228, (EpsgCoordinateSystemKind)0, 429); + return true; + case 5229: + cacheIndex = 2569; + reference = new EpsgCoordinateReferenceRecord(5229, (EpsgCoordinateSystemKind)0, 430); + return true; + case 5233: + cacheIndex = 2570; + reference = new EpsgCoordinateReferenceRecord(5233, (EpsgCoordinateSystemKind)0, 431); + return true; + case 5234: + cacheIndex = 2571; + reference = new EpsgCoordinateReferenceRecord(5234, (EpsgCoordinateSystemKind)2, 2055); + return true; + case 5235: + cacheIndex = 2572; + reference = new EpsgCoordinateReferenceRecord(5235, (EpsgCoordinateSystemKind)2, 2056); + return true; + case 5237: + cacheIndex = 2573; + reference = new EpsgCoordinateReferenceRecord(5237, (EpsgCoordinateSystemKind)3, 8); + return true; + case 5243: + cacheIndex = 2574; + reference = new EpsgCoordinateReferenceRecord(5243, (EpsgCoordinateSystemKind)2, 2057); + return true; + case 5244: + cacheIndex = 2575; + reference = new EpsgCoordinateReferenceRecord(5244, (EpsgCoordinateSystemKind)1, 73); + return true; + case 5245: + cacheIndex = 2576; + reference = new EpsgCoordinateReferenceRecord(5245, (EpsgCoordinateSystemKind)0, 432); + return true; + case 5246: + cacheIndex = 2577; + reference = new EpsgCoordinateReferenceRecord(5246, (EpsgCoordinateSystemKind)0, 433); + return true; + case 5247: + cacheIndex = 2578; + reference = new EpsgCoordinateReferenceRecord(5247, (EpsgCoordinateSystemKind)2, 2058); + return true; + case 5250: + cacheIndex = 2579; + reference = new EpsgCoordinateReferenceRecord(5250, (EpsgCoordinateSystemKind)1, 74); + return true; + case 5251: + cacheIndex = 2580; + reference = new EpsgCoordinateReferenceRecord(5251, (EpsgCoordinateSystemKind)0, 434); + return true; + case 5252: + cacheIndex = 2581; + reference = new EpsgCoordinateReferenceRecord(5252, (EpsgCoordinateSystemKind)0, 435); + return true; + case 5253: + cacheIndex = 2582; + reference = new EpsgCoordinateReferenceRecord(5253, (EpsgCoordinateSystemKind)2, 2059); + return true; + case 5254: + cacheIndex = 2583; + reference = new EpsgCoordinateReferenceRecord(5254, (EpsgCoordinateSystemKind)2, 2060); + return true; + case 5255: + cacheIndex = 2584; + reference = new EpsgCoordinateReferenceRecord(5255, (EpsgCoordinateSystemKind)2, 2061); + return true; + case 5256: + cacheIndex = 2585; + reference = new EpsgCoordinateReferenceRecord(5256, (EpsgCoordinateSystemKind)2, 2062); + return true; + case 5257: + cacheIndex = 2586; + reference = new EpsgCoordinateReferenceRecord(5257, (EpsgCoordinateSystemKind)2, 2063); + return true; + case 5258: + cacheIndex = 2587; + reference = new EpsgCoordinateReferenceRecord(5258, (EpsgCoordinateSystemKind)2, 2064); + return true; + case 5259: + cacheIndex = 2588; + reference = new EpsgCoordinateReferenceRecord(5259, (EpsgCoordinateSystemKind)2, 2065); + return true; + case 5262: + cacheIndex = 2589; + reference = new EpsgCoordinateReferenceRecord(5262, (EpsgCoordinateSystemKind)1, 75); + return true; + case 5263: + cacheIndex = 2590; + reference = new EpsgCoordinateReferenceRecord(5263, (EpsgCoordinateSystemKind)0, 436); + return true; + case 5264: + cacheIndex = 2591; + reference = new EpsgCoordinateReferenceRecord(5264, (EpsgCoordinateSystemKind)0, 437); + return true; + case 5266: + cacheIndex = 2592; + reference = new EpsgCoordinateReferenceRecord(5266, (EpsgCoordinateSystemKind)2, 2066); + return true; + case 5269: + cacheIndex = 2593; + reference = new EpsgCoordinateReferenceRecord(5269, (EpsgCoordinateSystemKind)2, 2067); + return true; + case 5270: + cacheIndex = 2594; + reference = new EpsgCoordinateReferenceRecord(5270, (EpsgCoordinateSystemKind)2, 2068); + return true; + case 5271: + cacheIndex = 2595; + reference = new EpsgCoordinateReferenceRecord(5271, (EpsgCoordinateSystemKind)2, 2069); + return true; + case 5272: + cacheIndex = 2596; + reference = new EpsgCoordinateReferenceRecord(5272, (EpsgCoordinateSystemKind)2, 2070); + return true; + case 5273: + cacheIndex = 2597; + reference = new EpsgCoordinateReferenceRecord(5273, (EpsgCoordinateSystemKind)2, 2071); + return true; + case 5274: + cacheIndex = 2598; + reference = new EpsgCoordinateReferenceRecord(5274, (EpsgCoordinateSystemKind)2, 2072); + return true; + case 5275: + cacheIndex = 2599; + reference = new EpsgCoordinateReferenceRecord(5275, (EpsgCoordinateSystemKind)2, 2073); + return true; + case 5292: + cacheIndex = 2600; + reference = new EpsgCoordinateReferenceRecord(5292, (EpsgCoordinateSystemKind)2, 2074); + return true; + case 5293: + cacheIndex = 2601; + reference = new EpsgCoordinateReferenceRecord(5293, (EpsgCoordinateSystemKind)2, 2075); + return true; + case 5294: + cacheIndex = 2602; + reference = new EpsgCoordinateReferenceRecord(5294, (EpsgCoordinateSystemKind)2, 2076); + return true; + case 5295: + cacheIndex = 2603; + reference = new EpsgCoordinateReferenceRecord(5295, (EpsgCoordinateSystemKind)2, 2077); + return true; + case 5296: + cacheIndex = 2604; + reference = new EpsgCoordinateReferenceRecord(5296, (EpsgCoordinateSystemKind)2, 2078); + return true; + case 5297: + cacheIndex = 2605; + reference = new EpsgCoordinateReferenceRecord(5297, (EpsgCoordinateSystemKind)2, 2079); + return true; + case 5298: + cacheIndex = 2606; + reference = new EpsgCoordinateReferenceRecord(5298, (EpsgCoordinateSystemKind)2, 2080); + return true; + case 5299: + cacheIndex = 2607; + reference = new EpsgCoordinateReferenceRecord(5299, (EpsgCoordinateSystemKind)2, 2081); + return true; + case 5300: + cacheIndex = 2608; + reference = new EpsgCoordinateReferenceRecord(5300, (EpsgCoordinateSystemKind)2, 2082); + return true; + case 5301: + cacheIndex = 2609; + reference = new EpsgCoordinateReferenceRecord(5301, (EpsgCoordinateSystemKind)2, 2083); + return true; + case 5302: + cacheIndex = 2610; + reference = new EpsgCoordinateReferenceRecord(5302, (EpsgCoordinateSystemKind)2, 2084); + return true; + case 5303: + cacheIndex = 2611; + reference = new EpsgCoordinateReferenceRecord(5303, (EpsgCoordinateSystemKind)2, 2085); + return true; + case 5304: + cacheIndex = 2612; + reference = new EpsgCoordinateReferenceRecord(5304, (EpsgCoordinateSystemKind)2, 2086); + return true; + case 5305: + cacheIndex = 2613; + reference = new EpsgCoordinateReferenceRecord(5305, (EpsgCoordinateSystemKind)2, 2087); + return true; + case 5306: + cacheIndex = 2614; + reference = new EpsgCoordinateReferenceRecord(5306, (EpsgCoordinateSystemKind)2, 2088); + return true; + case 5307: + cacheIndex = 2615; + reference = new EpsgCoordinateReferenceRecord(5307, (EpsgCoordinateSystemKind)2, 2089); + return true; + case 5308: + cacheIndex = 2616; + reference = new EpsgCoordinateReferenceRecord(5308, (EpsgCoordinateSystemKind)2, 2090); + return true; + case 5309: + cacheIndex = 2617; + reference = new EpsgCoordinateReferenceRecord(5309, (EpsgCoordinateSystemKind)2, 2091); + return true; + case 5310: + cacheIndex = 2618; + reference = new EpsgCoordinateReferenceRecord(5310, (EpsgCoordinateSystemKind)2, 2092); + return true; + case 5311: + cacheIndex = 2619; + reference = new EpsgCoordinateReferenceRecord(5311, (EpsgCoordinateSystemKind)2, 2093); + return true; + case 5316: + cacheIndex = 2620; + reference = new EpsgCoordinateReferenceRecord(5316, (EpsgCoordinateSystemKind)2, 2094); + return true; + case 5317: + cacheIndex = 2621; + reference = new EpsgCoordinateReferenceRecord(5317, (EpsgCoordinateSystemKind)3, 9); + return true; + case 5318: + cacheIndex = 2622; + reference = new EpsgCoordinateReferenceRecord(5318, (EpsgCoordinateSystemKind)4, 3); + return true; + case 5320: + cacheIndex = 2623; + reference = new EpsgCoordinateReferenceRecord(5320, (EpsgCoordinateSystemKind)2, 2095); + return true; + case 5321: + cacheIndex = 2624; + reference = new EpsgCoordinateReferenceRecord(5321, (EpsgCoordinateSystemKind)2, 2096); + return true; + case 5322: + cacheIndex = 2625; + reference = new EpsgCoordinateReferenceRecord(5322, (EpsgCoordinateSystemKind)1, 76); + return true; + case 5323: + cacheIndex = 2626; + reference = new EpsgCoordinateReferenceRecord(5323, (EpsgCoordinateSystemKind)0, 438); + return true; + case 5324: + cacheIndex = 2627; + reference = new EpsgCoordinateReferenceRecord(5324, (EpsgCoordinateSystemKind)0, 439); + return true; + case 5325: + cacheIndex = 2628; + reference = new EpsgCoordinateReferenceRecord(5325, (EpsgCoordinateSystemKind)2, 2097); + return true; + case 5329: + cacheIndex = 2629; + reference = new EpsgCoordinateReferenceRecord(5329, (EpsgCoordinateSystemKind)2, 2098); + return true; + case 5330: + cacheIndex = 2630; + reference = new EpsgCoordinateReferenceRecord(5330, (EpsgCoordinateSystemKind)2, 2099); + return true; + case 5331: + cacheIndex = 2631; + reference = new EpsgCoordinateReferenceRecord(5331, (EpsgCoordinateSystemKind)2, 2100); + return true; + case 5332: + cacheIndex = 2632; + reference = new EpsgCoordinateReferenceRecord(5332, (EpsgCoordinateSystemKind)1, 77); + return true; + case 5337: + cacheIndex = 2633; + reference = new EpsgCoordinateReferenceRecord(5337, (EpsgCoordinateSystemKind)2, 2101); + return true; + case 5340: + cacheIndex = 2634; + reference = new EpsgCoordinateReferenceRecord(5340, (EpsgCoordinateSystemKind)0, 440); + return true; + case 5341: + cacheIndex = 2635; + reference = new EpsgCoordinateReferenceRecord(5341, (EpsgCoordinateSystemKind)1, 78); + return true; + case 5342: + cacheIndex = 2636; + reference = new EpsgCoordinateReferenceRecord(5342, (EpsgCoordinateSystemKind)0, 441); + return true; + case 5343: + cacheIndex = 2637; + reference = new EpsgCoordinateReferenceRecord(5343, (EpsgCoordinateSystemKind)2, 2102); + return true; + case 5344: + cacheIndex = 2638; + reference = new EpsgCoordinateReferenceRecord(5344, (EpsgCoordinateSystemKind)2, 2103); + return true; + case 5345: + cacheIndex = 2639; + reference = new EpsgCoordinateReferenceRecord(5345, (EpsgCoordinateSystemKind)2, 2104); + return true; + case 5346: + cacheIndex = 2640; + reference = new EpsgCoordinateReferenceRecord(5346, (EpsgCoordinateSystemKind)2, 2105); + return true; + case 5347: + cacheIndex = 2641; + reference = new EpsgCoordinateReferenceRecord(5347, (EpsgCoordinateSystemKind)2, 2106); + return true; + case 5348: + cacheIndex = 2642; + reference = new EpsgCoordinateReferenceRecord(5348, (EpsgCoordinateSystemKind)2, 2107); + return true; + case 5349: + cacheIndex = 2643; + reference = new EpsgCoordinateReferenceRecord(5349, (EpsgCoordinateSystemKind)2, 2108); + return true; + case 5352: + cacheIndex = 2644; + reference = new EpsgCoordinateReferenceRecord(5352, (EpsgCoordinateSystemKind)1, 79); + return true; + case 5353: + cacheIndex = 2645; + reference = new EpsgCoordinateReferenceRecord(5353, (EpsgCoordinateSystemKind)0, 442); + return true; + case 5354: + cacheIndex = 2646; + reference = new EpsgCoordinateReferenceRecord(5354, (EpsgCoordinateSystemKind)0, 443); + return true; + case 5355: + cacheIndex = 2647; + reference = new EpsgCoordinateReferenceRecord(5355, (EpsgCoordinateSystemKind)2, 2109); + return true; + case 5356: + cacheIndex = 2648; + reference = new EpsgCoordinateReferenceRecord(5356, (EpsgCoordinateSystemKind)2, 2110); + return true; + case 5357: + cacheIndex = 2649; + reference = new EpsgCoordinateReferenceRecord(5357, (EpsgCoordinateSystemKind)2, 2111); + return true; + case 5358: + cacheIndex = 2650; + reference = new EpsgCoordinateReferenceRecord(5358, (EpsgCoordinateSystemKind)1, 80); + return true; + case 5359: + cacheIndex = 2651; + reference = new EpsgCoordinateReferenceRecord(5359, (EpsgCoordinateSystemKind)0, 444); + return true; + case 5360: + cacheIndex = 2652; + reference = new EpsgCoordinateReferenceRecord(5360, (EpsgCoordinateSystemKind)0, 445); + return true; + case 5361: + cacheIndex = 2653; + reference = new EpsgCoordinateReferenceRecord(5361, (EpsgCoordinateSystemKind)2, 2112); + return true; + case 5362: + cacheIndex = 2654; + reference = new EpsgCoordinateReferenceRecord(5362, (EpsgCoordinateSystemKind)2, 2113); + return true; + case 5363: + cacheIndex = 2655; + reference = new EpsgCoordinateReferenceRecord(5363, (EpsgCoordinateSystemKind)1, 81); + return true; + case 5364: + cacheIndex = 2656; + reference = new EpsgCoordinateReferenceRecord(5364, (EpsgCoordinateSystemKind)0, 446); + return true; + case 5365: + cacheIndex = 2657; + reference = new EpsgCoordinateReferenceRecord(5365, (EpsgCoordinateSystemKind)0, 447); + return true; + case 5367: + cacheIndex = 2658; + reference = new EpsgCoordinateReferenceRecord(5367, (EpsgCoordinateSystemKind)2, 2114); + return true; + case 5368: + cacheIndex = 2659; + reference = new EpsgCoordinateReferenceRecord(5368, (EpsgCoordinateSystemKind)1, 82); + return true; + case 5369: + cacheIndex = 2660; + reference = new EpsgCoordinateReferenceRecord(5369, (EpsgCoordinateSystemKind)1, 83); + return true; + case 5370: + cacheIndex = 2661; + reference = new EpsgCoordinateReferenceRecord(5370, (EpsgCoordinateSystemKind)0, 448); + return true; + case 5371: + cacheIndex = 2662; + reference = new EpsgCoordinateReferenceRecord(5371, (EpsgCoordinateSystemKind)0, 449); + return true; + case 5372: + cacheIndex = 2663; + reference = new EpsgCoordinateReferenceRecord(5372, (EpsgCoordinateSystemKind)0, 450); + return true; + case 5373: + cacheIndex = 2664; + reference = new EpsgCoordinateReferenceRecord(5373, (EpsgCoordinateSystemKind)0, 451); + return true; + case 5379: + cacheIndex = 2665; + reference = new EpsgCoordinateReferenceRecord(5379, (EpsgCoordinateSystemKind)1, 84); + return true; + case 5380: + cacheIndex = 2666; + reference = new EpsgCoordinateReferenceRecord(5380, (EpsgCoordinateSystemKind)0, 452); + return true; + case 5381: + cacheIndex = 2667; + reference = new EpsgCoordinateReferenceRecord(5381, (EpsgCoordinateSystemKind)0, 453); + return true; + case 5382: + cacheIndex = 2668; + reference = new EpsgCoordinateReferenceRecord(5382, (EpsgCoordinateSystemKind)2, 2115); + return true; + case 5383: + cacheIndex = 2669; + reference = new EpsgCoordinateReferenceRecord(5383, (EpsgCoordinateSystemKind)2, 2116); + return true; + case 5387: + cacheIndex = 2670; + reference = new EpsgCoordinateReferenceRecord(5387, (EpsgCoordinateSystemKind)2, 2117); + return true; + case 5389: + cacheIndex = 2671; + reference = new EpsgCoordinateReferenceRecord(5389, (EpsgCoordinateSystemKind)2, 2118); + return true; + case 5391: + cacheIndex = 2672; + reference = new EpsgCoordinateReferenceRecord(5391, (EpsgCoordinateSystemKind)1, 85); + return true; + case 5392: + cacheIndex = 2673; + reference = new EpsgCoordinateReferenceRecord(5392, (EpsgCoordinateSystemKind)0, 454); + return true; + case 5393: + cacheIndex = 2674; + reference = new EpsgCoordinateReferenceRecord(5393, (EpsgCoordinateSystemKind)0, 455); + return true; + case 5396: + cacheIndex = 2675; + reference = new EpsgCoordinateReferenceRecord(5396, (EpsgCoordinateSystemKind)2, 2119); + return true; + case 5451: + cacheIndex = 2676; + reference = new EpsgCoordinateReferenceRecord(5451, (EpsgCoordinateSystemKind)0, 456); + return true; + case 5456: + cacheIndex = 2677; + reference = new EpsgCoordinateReferenceRecord(5456, (EpsgCoordinateSystemKind)2, 2120); + return true; + case 5457: + cacheIndex = 2678; + reference = new EpsgCoordinateReferenceRecord(5457, (EpsgCoordinateSystemKind)2, 2121); + return true; + case 5459: + cacheIndex = 2679; + reference = new EpsgCoordinateReferenceRecord(5459, (EpsgCoordinateSystemKind)2, 2122); + return true; + case 5460: + cacheIndex = 2680; + reference = new EpsgCoordinateReferenceRecord(5460, (EpsgCoordinateSystemKind)2, 2123); + return true; + case 5461: + cacheIndex = 2681; + reference = new EpsgCoordinateReferenceRecord(5461, (EpsgCoordinateSystemKind)2, 2124); + return true; + case 5462: + cacheIndex = 2682; + reference = new EpsgCoordinateReferenceRecord(5462, (EpsgCoordinateSystemKind)2, 2125); + return true; + case 5463: + cacheIndex = 2683; + reference = new EpsgCoordinateReferenceRecord(5463, (EpsgCoordinateSystemKind)2, 2126); + return true; + case 5464: + cacheIndex = 2684; + reference = new EpsgCoordinateReferenceRecord(5464, (EpsgCoordinateSystemKind)0, 457); + return true; + case 5467: + cacheIndex = 2685; + reference = new EpsgCoordinateReferenceRecord(5467, (EpsgCoordinateSystemKind)0, 458); + return true; + case 5469: + cacheIndex = 2686; + reference = new EpsgCoordinateReferenceRecord(5469, (EpsgCoordinateSystemKind)2, 2127); + return true; + case 5472: + cacheIndex = 2687; + reference = new EpsgCoordinateReferenceRecord(5472, (EpsgCoordinateSystemKind)2, 2128); + return true; + case 5479: + cacheIndex = 2688; + reference = new EpsgCoordinateReferenceRecord(5479, (EpsgCoordinateSystemKind)2, 2129); + return true; + case 5480: + cacheIndex = 2689; + reference = new EpsgCoordinateReferenceRecord(5480, (EpsgCoordinateSystemKind)2, 2130); + return true; + case 5481: + cacheIndex = 2690; + reference = new EpsgCoordinateReferenceRecord(5481, (EpsgCoordinateSystemKind)2, 2131); + return true; + case 5482: + cacheIndex = 2691; + reference = new EpsgCoordinateReferenceRecord(5482, (EpsgCoordinateSystemKind)2, 2132); + return true; + case 5487: + cacheIndex = 2692; + reference = new EpsgCoordinateReferenceRecord(5487, (EpsgCoordinateSystemKind)1, 86); + return true; + case 5488: + cacheIndex = 2693; + reference = new EpsgCoordinateReferenceRecord(5488, (EpsgCoordinateSystemKind)0, 459); + return true; + case 5489: + cacheIndex = 2694; + reference = new EpsgCoordinateReferenceRecord(5489, (EpsgCoordinateSystemKind)0, 460); + return true; + case 5490: + cacheIndex = 2695; + reference = new EpsgCoordinateReferenceRecord(5490, (EpsgCoordinateSystemKind)2, 2133); + return true; + case 5498: + cacheIndex = 2696; + reference = new EpsgCoordinateReferenceRecord(5498, (EpsgCoordinateSystemKind)4, 4); + return true; + case 5499: + cacheIndex = 2697; + reference = new EpsgCoordinateReferenceRecord(5499, (EpsgCoordinateSystemKind)4, 5); + return true; + case 5500: + cacheIndex = 2698; + reference = new EpsgCoordinateReferenceRecord(5500, (EpsgCoordinateSystemKind)4, 6); + return true; + case 5513: + cacheIndex = 2699; + reference = new EpsgCoordinateReferenceRecord(5513, (EpsgCoordinateSystemKind)2, 2134); + return true; + case 5514: + cacheIndex = 2700; + reference = new EpsgCoordinateReferenceRecord(5514, (EpsgCoordinateSystemKind)2, 2135); + return true; + case 5515: + cacheIndex = 2701; + reference = new EpsgCoordinateReferenceRecord(5515, (EpsgCoordinateSystemKind)2, 2136); + return true; + case 5516: + cacheIndex = 2702; + reference = new EpsgCoordinateReferenceRecord(5516, (EpsgCoordinateSystemKind)2, 2137); + return true; + case 5518: + cacheIndex = 2703; + reference = new EpsgCoordinateReferenceRecord(5518, (EpsgCoordinateSystemKind)2, 2138); + return true; + case 5519: + cacheIndex = 2704; + reference = new EpsgCoordinateReferenceRecord(5519, (EpsgCoordinateSystemKind)2, 2139); + return true; + case 5520: + cacheIndex = 2705; + reference = new EpsgCoordinateReferenceRecord(5520, (EpsgCoordinateSystemKind)2, 2140); + return true; + case 5523: + cacheIndex = 2706; + reference = new EpsgCoordinateReferenceRecord(5523, (EpsgCoordinateSystemKind)2, 2141); + return true; + case 5524: + cacheIndex = 2707; + reference = new EpsgCoordinateReferenceRecord(5524, (EpsgCoordinateSystemKind)0, 461); + return true; + case 5527: + cacheIndex = 2708; + reference = new EpsgCoordinateReferenceRecord(5527, (EpsgCoordinateSystemKind)0, 462); + return true; + case 5530: + cacheIndex = 2709; + reference = new EpsgCoordinateReferenceRecord(5530, (EpsgCoordinateSystemKind)2, 2142); + return true; + case 5531: + cacheIndex = 2710; + reference = new EpsgCoordinateReferenceRecord(5531, (EpsgCoordinateSystemKind)2, 2143); + return true; + case 5533: + cacheIndex = 2711; + reference = new EpsgCoordinateReferenceRecord(5533, (EpsgCoordinateSystemKind)2, 2144); + return true; + case 5534: + cacheIndex = 2712; + reference = new EpsgCoordinateReferenceRecord(5534, (EpsgCoordinateSystemKind)2, 2145); + return true; + case 5535: + cacheIndex = 2713; + reference = new EpsgCoordinateReferenceRecord(5535, (EpsgCoordinateSystemKind)2, 2146); + return true; + case 5536: + cacheIndex = 2714; + reference = new EpsgCoordinateReferenceRecord(5536, (EpsgCoordinateSystemKind)2, 2147); + return true; + case 5537: + cacheIndex = 2715; + reference = new EpsgCoordinateReferenceRecord(5537, (EpsgCoordinateSystemKind)2, 2148); + return true; + case 5538: + cacheIndex = 2716; + reference = new EpsgCoordinateReferenceRecord(5538, (EpsgCoordinateSystemKind)2, 2149); + return true; + case 5539: + cacheIndex = 2717; + reference = new EpsgCoordinateReferenceRecord(5539, (EpsgCoordinateSystemKind)2, 2150); + return true; + case 5544: + cacheIndex = 2718; + reference = new EpsgCoordinateReferenceRecord(5544, (EpsgCoordinateSystemKind)1, 87); + return true; + case 5545: + cacheIndex = 2719; + reference = new EpsgCoordinateReferenceRecord(5545, (EpsgCoordinateSystemKind)0, 463); + return true; + case 5546: + cacheIndex = 2720; + reference = new EpsgCoordinateReferenceRecord(5546, (EpsgCoordinateSystemKind)0, 464); + return true; + case 5550: + cacheIndex = 2721; + reference = new EpsgCoordinateReferenceRecord(5550, (EpsgCoordinateSystemKind)2, 2151); + return true; + case 5551: + cacheIndex = 2722; + reference = new EpsgCoordinateReferenceRecord(5551, (EpsgCoordinateSystemKind)2, 2152); + return true; + case 5552: + cacheIndex = 2723; + reference = new EpsgCoordinateReferenceRecord(5552, (EpsgCoordinateSystemKind)2, 2153); + return true; + case 5554: + cacheIndex = 2724; + reference = new EpsgCoordinateReferenceRecord(5554, (EpsgCoordinateSystemKind)4, 7); + return true; + case 5555: + cacheIndex = 2725; + reference = new EpsgCoordinateReferenceRecord(5555, (EpsgCoordinateSystemKind)4, 8); + return true; + case 5556: + cacheIndex = 2726; + reference = new EpsgCoordinateReferenceRecord(5556, (EpsgCoordinateSystemKind)4, 9); + return true; + case 5558: + cacheIndex = 2727; + reference = new EpsgCoordinateReferenceRecord(5558, (EpsgCoordinateSystemKind)1, 88); + return true; + case 5559: + cacheIndex = 2728; + reference = new EpsgCoordinateReferenceRecord(5559, (EpsgCoordinateSystemKind)2, 2154); + return true; + case 5560: + cacheIndex = 2729; + reference = new EpsgCoordinateReferenceRecord(5560, (EpsgCoordinateSystemKind)0, 465); + return true; + case 5561: + cacheIndex = 2730; + reference = new EpsgCoordinateReferenceRecord(5561, (EpsgCoordinateSystemKind)0, 466); + return true; + case 5562: + cacheIndex = 2731; + reference = new EpsgCoordinateReferenceRecord(5562, (EpsgCoordinateSystemKind)2, 2155); + return true; + case 5563: + cacheIndex = 2732; + reference = new EpsgCoordinateReferenceRecord(5563, (EpsgCoordinateSystemKind)2, 2156); + return true; + case 5564: + cacheIndex = 2733; + reference = new EpsgCoordinateReferenceRecord(5564, (EpsgCoordinateSystemKind)2, 2157); + return true; + case 5565: + cacheIndex = 2734; + reference = new EpsgCoordinateReferenceRecord(5565, (EpsgCoordinateSystemKind)2, 2158); + return true; + case 5566: + cacheIndex = 2735; + reference = new EpsgCoordinateReferenceRecord(5566, (EpsgCoordinateSystemKind)2, 2159); + return true; + case 5567: + cacheIndex = 2736; + reference = new EpsgCoordinateReferenceRecord(5567, (EpsgCoordinateSystemKind)2, 2160); + return true; + case 5568: + cacheIndex = 2737; + reference = new EpsgCoordinateReferenceRecord(5568, (EpsgCoordinateSystemKind)2, 2161); + return true; + case 5569: + cacheIndex = 2738; + reference = new EpsgCoordinateReferenceRecord(5569, (EpsgCoordinateSystemKind)2, 2162); + return true; + case 5588: + cacheIndex = 2739; + reference = new EpsgCoordinateReferenceRecord(5588, (EpsgCoordinateSystemKind)2, 2163); + return true; + case 5589: + cacheIndex = 2740; + reference = new EpsgCoordinateReferenceRecord(5589, (EpsgCoordinateSystemKind)2, 2164); + return true; + case 5591: + cacheIndex = 2741; + reference = new EpsgCoordinateReferenceRecord(5591, (EpsgCoordinateSystemKind)1, 89); + return true; + case 5592: + cacheIndex = 2742; + reference = new EpsgCoordinateReferenceRecord(5592, (EpsgCoordinateSystemKind)0, 467); + return true; + case 5593: + cacheIndex = 2743; + reference = new EpsgCoordinateReferenceRecord(5593, (EpsgCoordinateSystemKind)0, 468); + return true; + case 5596: + cacheIndex = 2744; + reference = new EpsgCoordinateReferenceRecord(5596, (EpsgCoordinateSystemKind)2, 2165); + return true; + case 5597: + cacheIndex = 2745; + reference = new EpsgCoordinateReferenceRecord(5597, (EpsgCoordinateSystemKind)3, 10); + return true; + case 5598: + cacheIndex = 2746; + reference = new EpsgCoordinateReferenceRecord(5598, (EpsgCoordinateSystemKind)4, 10); + return true; + case 5600: + cacheIndex = 2747; + reference = new EpsgCoordinateReferenceRecord(5600, (EpsgCoordinateSystemKind)3, 11); + return true; + case 5601: + cacheIndex = 2748; + reference = new EpsgCoordinateReferenceRecord(5601, (EpsgCoordinateSystemKind)3, 12); + return true; + case 5602: + cacheIndex = 2749; + reference = new EpsgCoordinateReferenceRecord(5602, (EpsgCoordinateSystemKind)3, 13); + return true; + case 5603: + cacheIndex = 2750; + reference = new EpsgCoordinateReferenceRecord(5603, (EpsgCoordinateSystemKind)3, 14); + return true; + case 5604: + cacheIndex = 2751; + reference = new EpsgCoordinateReferenceRecord(5604, (EpsgCoordinateSystemKind)3, 15); + return true; + case 5605: + cacheIndex = 2752; + reference = new EpsgCoordinateReferenceRecord(5605, (EpsgCoordinateSystemKind)3, 16); + return true; + case 5606: + cacheIndex = 2753; + reference = new EpsgCoordinateReferenceRecord(5606, (EpsgCoordinateSystemKind)3, 17); + return true; + case 5607: + cacheIndex = 2754; + reference = new EpsgCoordinateReferenceRecord(5607, (EpsgCoordinateSystemKind)3, 18); + return true; + case 5608: + cacheIndex = 2755; + reference = new EpsgCoordinateReferenceRecord(5608, (EpsgCoordinateSystemKind)3, 19); + return true; + case 5609: + cacheIndex = 2756; + reference = new EpsgCoordinateReferenceRecord(5609, (EpsgCoordinateSystemKind)3, 20); + return true; + case 5610: + cacheIndex = 2757; + reference = new EpsgCoordinateReferenceRecord(5610, (EpsgCoordinateSystemKind)3, 21); + return true; + case 5611: + cacheIndex = 2758; + reference = new EpsgCoordinateReferenceRecord(5611, (EpsgCoordinateSystemKind)3, 22); + return true; + case 5613: + cacheIndex = 2759; + reference = new EpsgCoordinateReferenceRecord(5613, (EpsgCoordinateSystemKind)3, 23); + return true; + case 5615: + cacheIndex = 2760; + reference = new EpsgCoordinateReferenceRecord(5615, (EpsgCoordinateSystemKind)3, 24); + return true; + case 5616: + cacheIndex = 2761; + reference = new EpsgCoordinateReferenceRecord(5616, (EpsgCoordinateSystemKind)3, 25); + return true; + case 5617: + cacheIndex = 2762; + reference = new EpsgCoordinateReferenceRecord(5617, (EpsgCoordinateSystemKind)3, 26); + return true; + case 5618: + cacheIndex = 2763; + reference = new EpsgCoordinateReferenceRecord(5618, (EpsgCoordinateSystemKind)3, 27); + return true; + case 5619: + cacheIndex = 2764; + reference = new EpsgCoordinateReferenceRecord(5619, (EpsgCoordinateSystemKind)3, 28); + return true; + case 5620: + cacheIndex = 2765; + reference = new EpsgCoordinateReferenceRecord(5620, (EpsgCoordinateSystemKind)3, 29); + return true; + case 5621: + cacheIndex = 2766; + reference = new EpsgCoordinateReferenceRecord(5621, (EpsgCoordinateSystemKind)3, 30); + return true; + case 5623: + cacheIndex = 2767; + reference = new EpsgCoordinateReferenceRecord(5623, (EpsgCoordinateSystemKind)2, 2166); + return true; + case 5624: + cacheIndex = 2768; + reference = new EpsgCoordinateReferenceRecord(5624, (EpsgCoordinateSystemKind)2, 2167); + return true; + case 5625: + cacheIndex = 2769; + reference = new EpsgCoordinateReferenceRecord(5625, (EpsgCoordinateSystemKind)2, 2168); + return true; + case 5627: + cacheIndex = 2770; + reference = new EpsgCoordinateReferenceRecord(5627, (EpsgCoordinateSystemKind)2, 2169); + return true; + case 5628: + cacheIndex = 2771; + reference = new EpsgCoordinateReferenceRecord(5628, (EpsgCoordinateSystemKind)4, 11); + return true; + case 5629: + cacheIndex = 2772; + reference = new EpsgCoordinateReferenceRecord(5629, (EpsgCoordinateSystemKind)2, 2170); + return true; + case 5631: + cacheIndex = 2773; + reference = new EpsgCoordinateReferenceRecord(5631, (EpsgCoordinateSystemKind)2, 2171); + return true; + case 5632: + cacheIndex = 2774; + reference = new EpsgCoordinateReferenceRecord(5632, (EpsgCoordinateSystemKind)2, 2172); + return true; + case 5633: + cacheIndex = 2775; + reference = new EpsgCoordinateReferenceRecord(5633, (EpsgCoordinateSystemKind)2, 2173); + return true; + case 5634: + cacheIndex = 2776; + reference = new EpsgCoordinateReferenceRecord(5634, (EpsgCoordinateSystemKind)2, 2174); + return true; + case 5635: + cacheIndex = 2777; + reference = new EpsgCoordinateReferenceRecord(5635, (EpsgCoordinateSystemKind)2, 2175); + return true; + case 5636: + cacheIndex = 2778; + reference = new EpsgCoordinateReferenceRecord(5636, (EpsgCoordinateSystemKind)2, 2176); + return true; + case 5637: + cacheIndex = 2779; + reference = new EpsgCoordinateReferenceRecord(5637, (EpsgCoordinateSystemKind)2, 2177); + return true; + case 5638: + cacheIndex = 2780; + reference = new EpsgCoordinateReferenceRecord(5638, (EpsgCoordinateSystemKind)2, 2178); + return true; + case 5639: + cacheIndex = 2781; + reference = new EpsgCoordinateReferenceRecord(5639, (EpsgCoordinateSystemKind)2, 2179); + return true; + case 5641: + cacheIndex = 2782; + reference = new EpsgCoordinateReferenceRecord(5641, (EpsgCoordinateSystemKind)2, 2180); + return true; + case 5643: + cacheIndex = 2783; + reference = new EpsgCoordinateReferenceRecord(5643, (EpsgCoordinateSystemKind)2, 2181); + return true; + case 5644: + cacheIndex = 2784; + reference = new EpsgCoordinateReferenceRecord(5644, (EpsgCoordinateSystemKind)2, 2182); + return true; + case 5646: + cacheIndex = 2785; + reference = new EpsgCoordinateReferenceRecord(5646, (EpsgCoordinateSystemKind)2, 2183); + return true; + case 5649: + cacheIndex = 2786; + reference = new EpsgCoordinateReferenceRecord(5649, (EpsgCoordinateSystemKind)2, 2184); + return true; + case 5650: + cacheIndex = 2787; + reference = new EpsgCoordinateReferenceRecord(5650, (EpsgCoordinateSystemKind)2, 2185); + return true; + case 5651: + cacheIndex = 2788; + reference = new EpsgCoordinateReferenceRecord(5651, (EpsgCoordinateSystemKind)2, 2186); + return true; + case 5652: + cacheIndex = 2789; + reference = new EpsgCoordinateReferenceRecord(5652, (EpsgCoordinateSystemKind)2, 2187); + return true; + case 5653: + cacheIndex = 2790; + reference = new EpsgCoordinateReferenceRecord(5653, (EpsgCoordinateSystemKind)2, 2188); + return true; + case 5654: + cacheIndex = 2791; + reference = new EpsgCoordinateReferenceRecord(5654, (EpsgCoordinateSystemKind)2, 2189); + return true; + case 5655: + cacheIndex = 2792; + reference = new EpsgCoordinateReferenceRecord(5655, (EpsgCoordinateSystemKind)2, 2190); + return true; + case 5659: + cacheIndex = 2793; + reference = new EpsgCoordinateReferenceRecord(5659, (EpsgCoordinateSystemKind)2, 2191); + return true; + case 5663: + cacheIndex = 2794; + reference = new EpsgCoordinateReferenceRecord(5663, (EpsgCoordinateSystemKind)2, 2192); + return true; + case 5664: + cacheIndex = 2795; + reference = new EpsgCoordinateReferenceRecord(5664, (EpsgCoordinateSystemKind)2, 2193); + return true; + case 5665: + cacheIndex = 2796; + reference = new EpsgCoordinateReferenceRecord(5665, (EpsgCoordinateSystemKind)2, 2194); + return true; + case 5666: + cacheIndex = 2797; + reference = new EpsgCoordinateReferenceRecord(5666, (EpsgCoordinateSystemKind)2, 2195); + return true; + case 5667: + cacheIndex = 2798; + reference = new EpsgCoordinateReferenceRecord(5667, (EpsgCoordinateSystemKind)2, 2196); + return true; + case 5668: + cacheIndex = 2799; + reference = new EpsgCoordinateReferenceRecord(5668, (EpsgCoordinateSystemKind)2, 2197); + return true; + case 5669: + cacheIndex = 2800; + reference = new EpsgCoordinateReferenceRecord(5669, (EpsgCoordinateSystemKind)2, 2198); + return true; + case 5670: + cacheIndex = 2801; + reference = new EpsgCoordinateReferenceRecord(5670, (EpsgCoordinateSystemKind)2, 2199); + return true; + case 5671: + cacheIndex = 2802; + reference = new EpsgCoordinateReferenceRecord(5671, (EpsgCoordinateSystemKind)2, 2200); + return true; + case 5672: + cacheIndex = 2803; + reference = new EpsgCoordinateReferenceRecord(5672, (EpsgCoordinateSystemKind)2, 2201); + return true; + case 5673: + cacheIndex = 2804; + reference = new EpsgCoordinateReferenceRecord(5673, (EpsgCoordinateSystemKind)2, 2202); + return true; + case 5674: + cacheIndex = 2805; + reference = new EpsgCoordinateReferenceRecord(5674, (EpsgCoordinateSystemKind)2, 2203); + return true; + case 5675: + cacheIndex = 2806; + reference = new EpsgCoordinateReferenceRecord(5675, (EpsgCoordinateSystemKind)2, 2204); + return true; + case 5676: + cacheIndex = 2807; + reference = new EpsgCoordinateReferenceRecord(5676, (EpsgCoordinateSystemKind)2, 2205); + return true; + case 5677: + cacheIndex = 2808; + reference = new EpsgCoordinateReferenceRecord(5677, (EpsgCoordinateSystemKind)2, 2206); + return true; + case 5678: + cacheIndex = 2809; + reference = new EpsgCoordinateReferenceRecord(5678, (EpsgCoordinateSystemKind)2, 2207); + return true; + case 5679: + cacheIndex = 2810; + reference = new EpsgCoordinateReferenceRecord(5679, (EpsgCoordinateSystemKind)2, 2208); + return true; + case 5680: + cacheIndex = 2811; + reference = new EpsgCoordinateReferenceRecord(5680, (EpsgCoordinateSystemKind)2, 2209); + return true; + case 5681: + cacheIndex = 2812; + reference = new EpsgCoordinateReferenceRecord(5681, (EpsgCoordinateSystemKind)0, 469); + return true; + case 5682: + cacheIndex = 2813; + reference = new EpsgCoordinateReferenceRecord(5682, (EpsgCoordinateSystemKind)2, 2210); + return true; + case 5683: + cacheIndex = 2814; + reference = new EpsgCoordinateReferenceRecord(5683, (EpsgCoordinateSystemKind)2, 2211); + return true; + case 5684: + cacheIndex = 2815; + reference = new EpsgCoordinateReferenceRecord(5684, (EpsgCoordinateSystemKind)2, 2212); + return true; + case 5685: + cacheIndex = 2816; + reference = new EpsgCoordinateReferenceRecord(5685, (EpsgCoordinateSystemKind)2, 2213); + return true; + case 5698: + cacheIndex = 2817; + reference = new EpsgCoordinateReferenceRecord(5698, (EpsgCoordinateSystemKind)4, 12); + return true; + case 5699: + cacheIndex = 2818; + reference = new EpsgCoordinateReferenceRecord(5699, (EpsgCoordinateSystemKind)4, 13); + return true; + case 5700: + cacheIndex = 2819; + reference = new EpsgCoordinateReferenceRecord(5700, (EpsgCoordinateSystemKind)2, 2214); + return true; + case 5701: + cacheIndex = 2820; + reference = new EpsgCoordinateReferenceRecord(5701, (EpsgCoordinateSystemKind)3, 31); + return true; + case 5702: + cacheIndex = 2821; + reference = new EpsgCoordinateReferenceRecord(5702, (EpsgCoordinateSystemKind)3, 32); + return true; + case 5703: + cacheIndex = 2822; + reference = new EpsgCoordinateReferenceRecord(5703, (EpsgCoordinateSystemKind)3, 33); + return true; + case 5705: + cacheIndex = 2823; + reference = new EpsgCoordinateReferenceRecord(5705, (EpsgCoordinateSystemKind)3, 34); + return true; + case 5707: + cacheIndex = 2824; + reference = new EpsgCoordinateReferenceRecord(5707, (EpsgCoordinateSystemKind)4, 14); + return true; + case 5708: + cacheIndex = 2825; + reference = new EpsgCoordinateReferenceRecord(5708, (EpsgCoordinateSystemKind)4, 15); + return true; + case 5709: + cacheIndex = 2826; + reference = new EpsgCoordinateReferenceRecord(5709, (EpsgCoordinateSystemKind)3, 35); + return true; + case 5710: + cacheIndex = 2827; + reference = new EpsgCoordinateReferenceRecord(5710, (EpsgCoordinateSystemKind)3, 36); + return true; + case 5711: + cacheIndex = 2828; + reference = new EpsgCoordinateReferenceRecord(5711, (EpsgCoordinateSystemKind)3, 37); + return true; + case 5712: + cacheIndex = 2829; + reference = new EpsgCoordinateReferenceRecord(5712, (EpsgCoordinateSystemKind)3, 38); + return true; + case 5713: + cacheIndex = 2830; + reference = new EpsgCoordinateReferenceRecord(5713, (EpsgCoordinateSystemKind)3, 39); + return true; + case 5714: + cacheIndex = 2831; + reference = new EpsgCoordinateReferenceRecord(5714, (EpsgCoordinateSystemKind)3, 40); + return true; + case 5716: + cacheIndex = 2832; + reference = new EpsgCoordinateReferenceRecord(5716, (EpsgCoordinateSystemKind)3, 41); + return true; + case 5717: + cacheIndex = 2833; + reference = new EpsgCoordinateReferenceRecord(5717, (EpsgCoordinateSystemKind)3, 42); + return true; + case 5718: + cacheIndex = 2834; + reference = new EpsgCoordinateReferenceRecord(5718, (EpsgCoordinateSystemKind)3, 43); + return true; + case 5719: + cacheIndex = 2835; + reference = new EpsgCoordinateReferenceRecord(5719, (EpsgCoordinateSystemKind)3, 44); + return true; + case 5720: + cacheIndex = 2836; + reference = new EpsgCoordinateReferenceRecord(5720, (EpsgCoordinateSystemKind)3, 45); + return true; + case 5721: + cacheIndex = 2837; + reference = new EpsgCoordinateReferenceRecord(5721, (EpsgCoordinateSystemKind)3, 46); + return true; + case 5722: + cacheIndex = 2838; + reference = new EpsgCoordinateReferenceRecord(5722, (EpsgCoordinateSystemKind)3, 47); + return true; + case 5723: + cacheIndex = 2839; + reference = new EpsgCoordinateReferenceRecord(5723, (EpsgCoordinateSystemKind)3, 48); + return true; + case 5724: + cacheIndex = 2840; + reference = new EpsgCoordinateReferenceRecord(5724, (EpsgCoordinateSystemKind)3, 49); + return true; + case 5725: + cacheIndex = 2841; + reference = new EpsgCoordinateReferenceRecord(5725, (EpsgCoordinateSystemKind)3, 50); + return true; + case 5726: + cacheIndex = 2842; + reference = new EpsgCoordinateReferenceRecord(5726, (EpsgCoordinateSystemKind)3, 51); + return true; + case 5727: + cacheIndex = 2843; + reference = new EpsgCoordinateReferenceRecord(5727, (EpsgCoordinateSystemKind)3, 52); + return true; + case 5728: + cacheIndex = 2844; + reference = new EpsgCoordinateReferenceRecord(5728, (EpsgCoordinateSystemKind)3, 53); + return true; + case 5729: + cacheIndex = 2845; + reference = new EpsgCoordinateReferenceRecord(5729, (EpsgCoordinateSystemKind)3, 54); + return true; + case 5730: + cacheIndex = 2846; + reference = new EpsgCoordinateReferenceRecord(5730, (EpsgCoordinateSystemKind)3, 55); + return true; + case 5731: + cacheIndex = 2847; + reference = new EpsgCoordinateReferenceRecord(5731, (EpsgCoordinateSystemKind)3, 56); + return true; + case 5732: + cacheIndex = 2848; + reference = new EpsgCoordinateReferenceRecord(5732, (EpsgCoordinateSystemKind)3, 57); + return true; + case 5733: + cacheIndex = 2849; + reference = new EpsgCoordinateReferenceRecord(5733, (EpsgCoordinateSystemKind)3, 58); + return true; + case 5735: + cacheIndex = 2850; + reference = new EpsgCoordinateReferenceRecord(5735, (EpsgCoordinateSystemKind)3, 59); + return true; + case 5736: + cacheIndex = 2851; + reference = new EpsgCoordinateReferenceRecord(5736, (EpsgCoordinateSystemKind)3, 60); + return true; + case 5737: + cacheIndex = 2852; + reference = new EpsgCoordinateReferenceRecord(5737, (EpsgCoordinateSystemKind)3, 61); + return true; + case 5738: + cacheIndex = 2853; + reference = new EpsgCoordinateReferenceRecord(5738, (EpsgCoordinateSystemKind)3, 62); + return true; + case 5739: + cacheIndex = 2854; + reference = new EpsgCoordinateReferenceRecord(5739, (EpsgCoordinateSystemKind)3, 63); + return true; + case 5740: + cacheIndex = 2855; + reference = new EpsgCoordinateReferenceRecord(5740, (EpsgCoordinateSystemKind)3, 64); + return true; + case 5741: + cacheIndex = 2856; + reference = new EpsgCoordinateReferenceRecord(5741, (EpsgCoordinateSystemKind)3, 65); + return true; + case 5742: + cacheIndex = 2857; + reference = new EpsgCoordinateReferenceRecord(5742, (EpsgCoordinateSystemKind)3, 66); + return true; + case 5743: + cacheIndex = 2858; + reference = new EpsgCoordinateReferenceRecord(5743, (EpsgCoordinateSystemKind)3, 67); + return true; + case 5744: + cacheIndex = 2859; + reference = new EpsgCoordinateReferenceRecord(5744, (EpsgCoordinateSystemKind)3, 68); + return true; + case 5745: + cacheIndex = 2860; + reference = new EpsgCoordinateReferenceRecord(5745, (EpsgCoordinateSystemKind)3, 69); + return true; + case 5746: + cacheIndex = 2861; + reference = new EpsgCoordinateReferenceRecord(5746, (EpsgCoordinateSystemKind)3, 70); + return true; + case 5747: + cacheIndex = 2862; + reference = new EpsgCoordinateReferenceRecord(5747, (EpsgCoordinateSystemKind)3, 71); + return true; + case 5748: + cacheIndex = 2863; + reference = new EpsgCoordinateReferenceRecord(5748, (EpsgCoordinateSystemKind)3, 72); + return true; + case 5749: + cacheIndex = 2864; + reference = new EpsgCoordinateReferenceRecord(5749, (EpsgCoordinateSystemKind)3, 73); + return true; + case 5750: + cacheIndex = 2865; + reference = new EpsgCoordinateReferenceRecord(5750, (EpsgCoordinateSystemKind)3, 74); + return true; + case 5751: + cacheIndex = 2866; + reference = new EpsgCoordinateReferenceRecord(5751, (EpsgCoordinateSystemKind)3, 75); + return true; + case 5752: + cacheIndex = 2867; + reference = new EpsgCoordinateReferenceRecord(5752, (EpsgCoordinateSystemKind)3, 76); + return true; + case 5753: + cacheIndex = 2868; + reference = new EpsgCoordinateReferenceRecord(5753, (EpsgCoordinateSystemKind)3, 77); + return true; + case 5754: + cacheIndex = 2869; + reference = new EpsgCoordinateReferenceRecord(5754, (EpsgCoordinateSystemKind)3, 78); + return true; + case 5755: + cacheIndex = 2870; + reference = new EpsgCoordinateReferenceRecord(5755, (EpsgCoordinateSystemKind)3, 79); + return true; + case 5756: + cacheIndex = 2871; + reference = new EpsgCoordinateReferenceRecord(5756, (EpsgCoordinateSystemKind)3, 80); + return true; + case 5757: + cacheIndex = 2872; + reference = new EpsgCoordinateReferenceRecord(5757, (EpsgCoordinateSystemKind)3, 81); + return true; + case 5758: + cacheIndex = 2873; + reference = new EpsgCoordinateReferenceRecord(5758, (EpsgCoordinateSystemKind)3, 82); + return true; + case 5759: + cacheIndex = 2874; + reference = new EpsgCoordinateReferenceRecord(5759, (EpsgCoordinateSystemKind)3, 83); + return true; + case 5760: + cacheIndex = 2875; + reference = new EpsgCoordinateReferenceRecord(5760, (EpsgCoordinateSystemKind)3, 84); + return true; + case 5761: + cacheIndex = 2876; + reference = new EpsgCoordinateReferenceRecord(5761, (EpsgCoordinateSystemKind)3, 85); + return true; + case 5762: + cacheIndex = 2877; + reference = new EpsgCoordinateReferenceRecord(5762, (EpsgCoordinateSystemKind)3, 86); + return true; + case 5763: + cacheIndex = 2878; + reference = new EpsgCoordinateReferenceRecord(5763, (EpsgCoordinateSystemKind)3, 87); + return true; + case 5764: + cacheIndex = 2879; + reference = new EpsgCoordinateReferenceRecord(5764, (EpsgCoordinateSystemKind)3, 88); + return true; + case 5765: + cacheIndex = 2880; + reference = new EpsgCoordinateReferenceRecord(5765, (EpsgCoordinateSystemKind)3, 89); + return true; + case 5766: + cacheIndex = 2881; + reference = new EpsgCoordinateReferenceRecord(5766, (EpsgCoordinateSystemKind)3, 90); + return true; + case 5767: + cacheIndex = 2882; + reference = new EpsgCoordinateReferenceRecord(5767, (EpsgCoordinateSystemKind)3, 91); + return true; + case 5768: + cacheIndex = 2883; + reference = new EpsgCoordinateReferenceRecord(5768, (EpsgCoordinateSystemKind)3, 92); + return true; + case 5769: + cacheIndex = 2884; + reference = new EpsgCoordinateReferenceRecord(5769, (EpsgCoordinateSystemKind)3, 93); + return true; + case 5770: + cacheIndex = 2885; + reference = new EpsgCoordinateReferenceRecord(5770, (EpsgCoordinateSystemKind)3, 94); + return true; + case 5771: + cacheIndex = 2886; + reference = new EpsgCoordinateReferenceRecord(5771, (EpsgCoordinateSystemKind)3, 95); + return true; + case 5772: + cacheIndex = 2887; + reference = new EpsgCoordinateReferenceRecord(5772, (EpsgCoordinateSystemKind)3, 96); + return true; + case 5773: + cacheIndex = 2888; + reference = new EpsgCoordinateReferenceRecord(5773, (EpsgCoordinateSystemKind)3, 97); + return true; + case 5774: + cacheIndex = 2889; + reference = new EpsgCoordinateReferenceRecord(5774, (EpsgCoordinateSystemKind)3, 98); + return true; + case 5775: + cacheIndex = 2890; + reference = new EpsgCoordinateReferenceRecord(5775, (EpsgCoordinateSystemKind)3, 99); + return true; + case 5776: + cacheIndex = 2891; + reference = new EpsgCoordinateReferenceRecord(5776, (EpsgCoordinateSystemKind)3, 100); + return true; + case 5777: + cacheIndex = 2892; + reference = new EpsgCoordinateReferenceRecord(5777, (EpsgCoordinateSystemKind)3, 101); + return true; + case 5778: + cacheIndex = 2893; + reference = new EpsgCoordinateReferenceRecord(5778, (EpsgCoordinateSystemKind)3, 102); + return true; + case 5779: + cacheIndex = 2894; + reference = new EpsgCoordinateReferenceRecord(5779, (EpsgCoordinateSystemKind)3, 103); + return true; + case 5780: + cacheIndex = 2895; + reference = new EpsgCoordinateReferenceRecord(5780, (EpsgCoordinateSystemKind)3, 104); + return true; + case 5781: + cacheIndex = 2896; + reference = new EpsgCoordinateReferenceRecord(5781, (EpsgCoordinateSystemKind)3, 105); + return true; + case 5782: + cacheIndex = 2897; + reference = new EpsgCoordinateReferenceRecord(5782, (EpsgCoordinateSystemKind)3, 106); + return true; + case 5783: + cacheIndex = 2898; + reference = new EpsgCoordinateReferenceRecord(5783, (EpsgCoordinateSystemKind)3, 107); + return true; + case 5784: + cacheIndex = 2899; + reference = new EpsgCoordinateReferenceRecord(5784, (EpsgCoordinateSystemKind)3, 108); + return true; + case 5785: + cacheIndex = 2900; + reference = new EpsgCoordinateReferenceRecord(5785, (EpsgCoordinateSystemKind)3, 109); + return true; + case 5786: + cacheIndex = 2901; + reference = new EpsgCoordinateReferenceRecord(5786, (EpsgCoordinateSystemKind)3, 110); + return true; + case 5787: + cacheIndex = 2902; + reference = new EpsgCoordinateReferenceRecord(5787, (EpsgCoordinateSystemKind)3, 111); + return true; + case 5788: + cacheIndex = 2903; + reference = new EpsgCoordinateReferenceRecord(5788, (EpsgCoordinateSystemKind)3, 112); + return true; + case 5790: + cacheIndex = 2904; + reference = new EpsgCoordinateReferenceRecord(5790, (EpsgCoordinateSystemKind)3, 113); + return true; + case 5791: + cacheIndex = 2905; + reference = new EpsgCoordinateReferenceRecord(5791, (EpsgCoordinateSystemKind)3, 114); + return true; + case 5792: + cacheIndex = 2906; + reference = new EpsgCoordinateReferenceRecord(5792, (EpsgCoordinateSystemKind)3, 115); + return true; + case 5793: + cacheIndex = 2907; + reference = new EpsgCoordinateReferenceRecord(5793, (EpsgCoordinateSystemKind)3, 116); + return true; + case 5794: + cacheIndex = 2908; + reference = new EpsgCoordinateReferenceRecord(5794, (EpsgCoordinateSystemKind)3, 117); + return true; + case 5795: + cacheIndex = 2909; + reference = new EpsgCoordinateReferenceRecord(5795, (EpsgCoordinateSystemKind)3, 118); + return true; + case 5796: + cacheIndex = 2910; + reference = new EpsgCoordinateReferenceRecord(5796, (EpsgCoordinateSystemKind)3, 119); + return true; + case 5797: + cacheIndex = 2911; + reference = new EpsgCoordinateReferenceRecord(5797, (EpsgCoordinateSystemKind)3, 120); + return true; + case 5798: + cacheIndex = 2912; + reference = new EpsgCoordinateReferenceRecord(5798, (EpsgCoordinateSystemKind)3, 121); + return true; + case 5825: + cacheIndex = 2913; + reference = new EpsgCoordinateReferenceRecord(5825, (EpsgCoordinateSystemKind)2, 2215); + return true; + case 5828: + cacheIndex = 2914; + reference = new EpsgCoordinateReferenceRecord(5828, (EpsgCoordinateSystemKind)1, 90); + return true; + case 5829: + cacheIndex = 2915; + reference = new EpsgCoordinateReferenceRecord(5829, (EpsgCoordinateSystemKind)3, 122); + return true; + case 5830: + cacheIndex = 2916; + reference = new EpsgCoordinateReferenceRecord(5830, (EpsgCoordinateSystemKind)0, 470); + return true; + case 5836: + cacheIndex = 2917; + reference = new EpsgCoordinateReferenceRecord(5836, (EpsgCoordinateSystemKind)2, 2216); + return true; + case 5837: + cacheIndex = 2918; + reference = new EpsgCoordinateReferenceRecord(5837, (EpsgCoordinateSystemKind)2, 2217); + return true; + case 5839: + cacheIndex = 2919; + reference = new EpsgCoordinateReferenceRecord(5839, (EpsgCoordinateSystemKind)2, 2218); + return true; + case 5842: + cacheIndex = 2920; + reference = new EpsgCoordinateReferenceRecord(5842, (EpsgCoordinateSystemKind)2, 2219); + return true; + case 5843: + cacheIndex = 2921; + reference = new EpsgCoordinateReferenceRecord(5843, (EpsgCoordinateSystemKind)3, 123); + return true; + case 5844: + cacheIndex = 2922; + reference = new EpsgCoordinateReferenceRecord(5844, (EpsgCoordinateSystemKind)2, 2220); + return true; + case 5845: + cacheIndex = 2923; + reference = new EpsgCoordinateReferenceRecord(5845, (EpsgCoordinateSystemKind)4, 16); + return true; + case 5846: + cacheIndex = 2924; + reference = new EpsgCoordinateReferenceRecord(5846, (EpsgCoordinateSystemKind)4, 17); + return true; + case 5847: + cacheIndex = 2925; + reference = new EpsgCoordinateReferenceRecord(5847, (EpsgCoordinateSystemKind)4, 18); + return true; + case 5848: + cacheIndex = 2926; + reference = new EpsgCoordinateReferenceRecord(5848, (EpsgCoordinateSystemKind)4, 19); + return true; + case 5849: + cacheIndex = 2927; + reference = new EpsgCoordinateReferenceRecord(5849, (EpsgCoordinateSystemKind)4, 20); + return true; + case 5850: + cacheIndex = 2928; + reference = new EpsgCoordinateReferenceRecord(5850, (EpsgCoordinateSystemKind)4, 21); + return true; + case 5851: + cacheIndex = 2929; + reference = new EpsgCoordinateReferenceRecord(5851, (EpsgCoordinateSystemKind)4, 22); + return true; + case 5852: + cacheIndex = 2930; + reference = new EpsgCoordinateReferenceRecord(5852, (EpsgCoordinateSystemKind)4, 23); + return true; + case 5853: + cacheIndex = 2931; + reference = new EpsgCoordinateReferenceRecord(5853, (EpsgCoordinateSystemKind)4, 24); + return true; + case 5854: + cacheIndex = 2932; + reference = new EpsgCoordinateReferenceRecord(5854, (EpsgCoordinateSystemKind)4, 25); + return true; + case 5855: + cacheIndex = 2933; + reference = new EpsgCoordinateReferenceRecord(5855, (EpsgCoordinateSystemKind)4, 26); + return true; + case 5856: + cacheIndex = 2934; + reference = new EpsgCoordinateReferenceRecord(5856, (EpsgCoordinateSystemKind)4, 27); + return true; + case 5857: + cacheIndex = 2935; + reference = new EpsgCoordinateReferenceRecord(5857, (EpsgCoordinateSystemKind)4, 28); + return true; + case 5858: + cacheIndex = 2936; + reference = new EpsgCoordinateReferenceRecord(5858, (EpsgCoordinateSystemKind)2, 2221); + return true; + case 5861: + cacheIndex = 2937; + reference = new EpsgCoordinateReferenceRecord(5861, (EpsgCoordinateSystemKind)3, 124); + return true; + case 5862: + cacheIndex = 2938; + reference = new EpsgCoordinateReferenceRecord(5862, (EpsgCoordinateSystemKind)3, 125); + return true; + case 5863: + cacheIndex = 2939; + reference = new EpsgCoordinateReferenceRecord(5863, (EpsgCoordinateSystemKind)3, 126); + return true; + case 5864: + cacheIndex = 2940; + reference = new EpsgCoordinateReferenceRecord(5864, (EpsgCoordinateSystemKind)3, 127); + return true; + case 5865: + cacheIndex = 2941; + reference = new EpsgCoordinateReferenceRecord(5865, (EpsgCoordinateSystemKind)3, 128); + return true; + case 5866: + cacheIndex = 2942; + reference = new EpsgCoordinateReferenceRecord(5866, (EpsgCoordinateSystemKind)3, 129); + return true; + case 5867: + cacheIndex = 2943; + reference = new EpsgCoordinateReferenceRecord(5867, (EpsgCoordinateSystemKind)3, 130); + return true; + case 5868: + cacheIndex = 2944; + reference = new EpsgCoordinateReferenceRecord(5868, (EpsgCoordinateSystemKind)3, 131); + return true; + case 5869: + cacheIndex = 2945; + reference = new EpsgCoordinateReferenceRecord(5869, (EpsgCoordinateSystemKind)3, 132); + return true; + case 5870: + cacheIndex = 2946; + reference = new EpsgCoordinateReferenceRecord(5870, (EpsgCoordinateSystemKind)3, 133); + return true; + case 5871: + cacheIndex = 2947; + reference = new EpsgCoordinateReferenceRecord(5871, (EpsgCoordinateSystemKind)3, 134); + return true; + case 5872: + cacheIndex = 2948; + reference = new EpsgCoordinateReferenceRecord(5872, (EpsgCoordinateSystemKind)3, 135); + return true; + case 5873: + cacheIndex = 2949; + reference = new EpsgCoordinateReferenceRecord(5873, (EpsgCoordinateSystemKind)3, 136); + return true; + case 5874: + cacheIndex = 2950; + reference = new EpsgCoordinateReferenceRecord(5874, (EpsgCoordinateSystemKind)3, 137); + return true; + case 5875: + cacheIndex = 2951; + reference = new EpsgCoordinateReferenceRecord(5875, (EpsgCoordinateSystemKind)2, 2222); + return true; + case 5876: + cacheIndex = 2952; + reference = new EpsgCoordinateReferenceRecord(5876, (EpsgCoordinateSystemKind)2, 2223); + return true; + case 5877: + cacheIndex = 2953; + reference = new EpsgCoordinateReferenceRecord(5877, (EpsgCoordinateSystemKind)2, 2224); + return true; + case 5879: + cacheIndex = 2954; + reference = new EpsgCoordinateReferenceRecord(5879, (EpsgCoordinateSystemKind)2, 2225); + return true; + case 5880: + cacheIndex = 2955; + reference = new EpsgCoordinateReferenceRecord(5880, (EpsgCoordinateSystemKind)2, 2226); + return true; + case 5884: + cacheIndex = 2956; + reference = new EpsgCoordinateReferenceRecord(5884, (EpsgCoordinateSystemKind)1, 91); + return true; + case 5885: + cacheIndex = 2957; + reference = new EpsgCoordinateReferenceRecord(5885, (EpsgCoordinateSystemKind)0, 471); + return true; + case 5886: + cacheIndex = 2958; + reference = new EpsgCoordinateReferenceRecord(5886, (EpsgCoordinateSystemKind)0, 472); + return true; + case 5887: + cacheIndex = 2959; + reference = new EpsgCoordinateReferenceRecord(5887, (EpsgCoordinateSystemKind)2, 2227); + return true; + case 5896: + cacheIndex = 2960; + reference = new EpsgCoordinateReferenceRecord(5896, (EpsgCoordinateSystemKind)2, 2228); + return true; + case 5897: + cacheIndex = 2961; + reference = new EpsgCoordinateReferenceRecord(5897, (EpsgCoordinateSystemKind)2, 2229); + return true; + case 5898: + cacheIndex = 2962; + reference = new EpsgCoordinateReferenceRecord(5898, (EpsgCoordinateSystemKind)2, 2230); + return true; + case 5899: + cacheIndex = 2963; + reference = new EpsgCoordinateReferenceRecord(5899, (EpsgCoordinateSystemKind)2, 2231); + return true; + case 5921: + cacheIndex = 2964; + reference = new EpsgCoordinateReferenceRecord(5921, (EpsgCoordinateSystemKind)2, 2232); + return true; + case 5922: + cacheIndex = 2965; + reference = new EpsgCoordinateReferenceRecord(5922, (EpsgCoordinateSystemKind)2, 2233); + return true; + case 5923: + cacheIndex = 2966; + reference = new EpsgCoordinateReferenceRecord(5923, (EpsgCoordinateSystemKind)2, 2234); + return true; + case 5924: + cacheIndex = 2967; + reference = new EpsgCoordinateReferenceRecord(5924, (EpsgCoordinateSystemKind)2, 2235); + return true; + case 5925: + cacheIndex = 2968; + reference = new EpsgCoordinateReferenceRecord(5925, (EpsgCoordinateSystemKind)2, 2236); + return true; + case 5926: + cacheIndex = 2969; + reference = new EpsgCoordinateReferenceRecord(5926, (EpsgCoordinateSystemKind)2, 2237); + return true; + case 5927: + cacheIndex = 2970; + reference = new EpsgCoordinateReferenceRecord(5927, (EpsgCoordinateSystemKind)2, 2238); + return true; + case 5928: + cacheIndex = 2971; + reference = new EpsgCoordinateReferenceRecord(5928, (EpsgCoordinateSystemKind)2, 2239); + return true; + case 5929: + cacheIndex = 2972; + reference = new EpsgCoordinateReferenceRecord(5929, (EpsgCoordinateSystemKind)2, 2240); + return true; + case 5930: + cacheIndex = 2973; + reference = new EpsgCoordinateReferenceRecord(5930, (EpsgCoordinateSystemKind)2, 2241); + return true; + case 5931: + cacheIndex = 2974; + reference = new EpsgCoordinateReferenceRecord(5931, (EpsgCoordinateSystemKind)2, 2242); + return true; + case 5932: + cacheIndex = 2975; + reference = new EpsgCoordinateReferenceRecord(5932, (EpsgCoordinateSystemKind)2, 2243); + return true; + case 5933: + cacheIndex = 2976; + reference = new EpsgCoordinateReferenceRecord(5933, (EpsgCoordinateSystemKind)2, 2244); + return true; + case 5934: + cacheIndex = 2977; + reference = new EpsgCoordinateReferenceRecord(5934, (EpsgCoordinateSystemKind)2, 2245); + return true; + case 5935: + cacheIndex = 2978; + reference = new EpsgCoordinateReferenceRecord(5935, (EpsgCoordinateSystemKind)2, 2246); + return true; + case 5936: + cacheIndex = 2979; + reference = new EpsgCoordinateReferenceRecord(5936, (EpsgCoordinateSystemKind)2, 2247); + return true; + case 5937: + cacheIndex = 2980; + reference = new EpsgCoordinateReferenceRecord(5937, (EpsgCoordinateSystemKind)2, 2248); + return true; + case 5938: + cacheIndex = 2981; + reference = new EpsgCoordinateReferenceRecord(5938, (EpsgCoordinateSystemKind)2, 2249); + return true; + case 5939: + cacheIndex = 2982; + reference = new EpsgCoordinateReferenceRecord(5939, (EpsgCoordinateSystemKind)2, 2250); + return true; + case 5940: + cacheIndex = 2983; + reference = new EpsgCoordinateReferenceRecord(5940, (EpsgCoordinateSystemKind)2, 2251); + return true; + case 5941: + cacheIndex = 2984; + reference = new EpsgCoordinateReferenceRecord(5941, (EpsgCoordinateSystemKind)3, 138); + return true; + case 5942: + cacheIndex = 2985; + reference = new EpsgCoordinateReferenceRecord(5942, (EpsgCoordinateSystemKind)4, 29); + return true; + case 5945: + cacheIndex = 2986; + reference = new EpsgCoordinateReferenceRecord(5945, (EpsgCoordinateSystemKind)4, 30); + return true; + case 5946: + cacheIndex = 2987; + reference = new EpsgCoordinateReferenceRecord(5946, (EpsgCoordinateSystemKind)4, 31); + return true; + case 5947: + cacheIndex = 2988; + reference = new EpsgCoordinateReferenceRecord(5947, (EpsgCoordinateSystemKind)4, 32); + return true; + case 5948: + cacheIndex = 2989; + reference = new EpsgCoordinateReferenceRecord(5948, (EpsgCoordinateSystemKind)4, 33); + return true; + case 5949: + cacheIndex = 2990; + reference = new EpsgCoordinateReferenceRecord(5949, (EpsgCoordinateSystemKind)4, 34); + return true; + case 5950: + cacheIndex = 2991; + reference = new EpsgCoordinateReferenceRecord(5950, (EpsgCoordinateSystemKind)4, 35); + return true; + case 5951: + cacheIndex = 2992; + reference = new EpsgCoordinateReferenceRecord(5951, (EpsgCoordinateSystemKind)4, 36); + return true; + case 5952: + cacheIndex = 2993; + reference = new EpsgCoordinateReferenceRecord(5952, (EpsgCoordinateSystemKind)4, 37); + return true; + case 5953: + cacheIndex = 2994; + reference = new EpsgCoordinateReferenceRecord(5953, (EpsgCoordinateSystemKind)4, 38); + return true; + case 5954: + cacheIndex = 2995; + reference = new EpsgCoordinateReferenceRecord(5954, (EpsgCoordinateSystemKind)4, 39); + return true; + case 5955: + cacheIndex = 2996; + reference = new EpsgCoordinateReferenceRecord(5955, (EpsgCoordinateSystemKind)4, 40); + return true; + case 5956: + cacheIndex = 2997; + reference = new EpsgCoordinateReferenceRecord(5956, (EpsgCoordinateSystemKind)4, 41); + return true; + case 5957: + cacheIndex = 2998; + reference = new EpsgCoordinateReferenceRecord(5957, (EpsgCoordinateSystemKind)4, 42); + return true; + case 5958: + cacheIndex = 2999; + reference = new EpsgCoordinateReferenceRecord(5958, (EpsgCoordinateSystemKind)4, 43); + return true; + case 5959: + cacheIndex = 3000; + reference = new EpsgCoordinateReferenceRecord(5959, (EpsgCoordinateSystemKind)4, 44); + return true; + case 5960: + cacheIndex = 3001; + reference = new EpsgCoordinateReferenceRecord(5960, (EpsgCoordinateSystemKind)4, 45); + return true; + case 5961: + cacheIndex = 3002; + reference = new EpsgCoordinateReferenceRecord(5961, (EpsgCoordinateSystemKind)4, 46); + return true; + case 5962: + cacheIndex = 3003; + reference = new EpsgCoordinateReferenceRecord(5962, (EpsgCoordinateSystemKind)4, 47); + return true; + case 5963: + cacheIndex = 3004; + reference = new EpsgCoordinateReferenceRecord(5963, (EpsgCoordinateSystemKind)4, 48); + return true; + case 5964: + cacheIndex = 3005; + reference = new EpsgCoordinateReferenceRecord(5964, (EpsgCoordinateSystemKind)4, 49); + return true; + case 5965: + cacheIndex = 3006; + reference = new EpsgCoordinateReferenceRecord(5965, (EpsgCoordinateSystemKind)4, 50); + return true; + case 5966: + cacheIndex = 3007; + reference = new EpsgCoordinateReferenceRecord(5966, (EpsgCoordinateSystemKind)4, 51); + return true; + case 5967: + cacheIndex = 3008; + reference = new EpsgCoordinateReferenceRecord(5967, (EpsgCoordinateSystemKind)4, 52); + return true; + case 5968: + cacheIndex = 3009; + reference = new EpsgCoordinateReferenceRecord(5968, (EpsgCoordinateSystemKind)4, 53); + return true; + case 5969: + cacheIndex = 3010; + reference = new EpsgCoordinateReferenceRecord(5969, (EpsgCoordinateSystemKind)4, 54); + return true; + case 5970: + cacheIndex = 3011; + reference = new EpsgCoordinateReferenceRecord(5970, (EpsgCoordinateSystemKind)4, 55); + return true; + case 5971: + cacheIndex = 3012; + reference = new EpsgCoordinateReferenceRecord(5971, (EpsgCoordinateSystemKind)4, 56); + return true; + case 5972: + cacheIndex = 3013; + reference = new EpsgCoordinateReferenceRecord(5972, (EpsgCoordinateSystemKind)4, 57); + return true; + case 5973: + cacheIndex = 3014; + reference = new EpsgCoordinateReferenceRecord(5973, (EpsgCoordinateSystemKind)4, 58); + return true; + case 5974: + cacheIndex = 3015; + reference = new EpsgCoordinateReferenceRecord(5974, (EpsgCoordinateSystemKind)4, 59); + return true; + case 5975: + cacheIndex = 3016; + reference = new EpsgCoordinateReferenceRecord(5975, (EpsgCoordinateSystemKind)4, 60); + return true; + case 5976: + cacheIndex = 3017; + reference = new EpsgCoordinateReferenceRecord(5976, (EpsgCoordinateSystemKind)4, 61); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket6(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 6050: + cacheIndex = 3018; + reference = new EpsgCoordinateReferenceRecord(6050, (EpsgCoordinateSystemKind)2, 2252); + return true; + case 6051: + cacheIndex = 3019; + reference = new EpsgCoordinateReferenceRecord(6051, (EpsgCoordinateSystemKind)2, 2253); + return true; + case 6052: + cacheIndex = 3020; + reference = new EpsgCoordinateReferenceRecord(6052, (EpsgCoordinateSystemKind)2, 2254); + return true; + case 6053: + cacheIndex = 3021; + reference = new EpsgCoordinateReferenceRecord(6053, (EpsgCoordinateSystemKind)2, 2255); + return true; + case 6054: + cacheIndex = 3022; + reference = new EpsgCoordinateReferenceRecord(6054, (EpsgCoordinateSystemKind)2, 2256); + return true; + case 6055: + cacheIndex = 3023; + reference = new EpsgCoordinateReferenceRecord(6055, (EpsgCoordinateSystemKind)2, 2257); + return true; + case 6056: + cacheIndex = 3024; + reference = new EpsgCoordinateReferenceRecord(6056, (EpsgCoordinateSystemKind)2, 2258); + return true; + case 6057: + cacheIndex = 3025; + reference = new EpsgCoordinateReferenceRecord(6057, (EpsgCoordinateSystemKind)2, 2259); + return true; + case 6058: + cacheIndex = 3026; + reference = new EpsgCoordinateReferenceRecord(6058, (EpsgCoordinateSystemKind)2, 2260); + return true; + case 6059: + cacheIndex = 3027; + reference = new EpsgCoordinateReferenceRecord(6059, (EpsgCoordinateSystemKind)2, 2261); + return true; + case 6060: + cacheIndex = 3028; + reference = new EpsgCoordinateReferenceRecord(6060, (EpsgCoordinateSystemKind)2, 2262); + return true; + case 6061: + cacheIndex = 3029; + reference = new EpsgCoordinateReferenceRecord(6061, (EpsgCoordinateSystemKind)2, 2263); + return true; + case 6062: + cacheIndex = 3030; + reference = new EpsgCoordinateReferenceRecord(6062, (EpsgCoordinateSystemKind)2, 2264); + return true; + case 6063: + cacheIndex = 3031; + reference = new EpsgCoordinateReferenceRecord(6063, (EpsgCoordinateSystemKind)2, 2265); + return true; + case 6064: + cacheIndex = 3032; + reference = new EpsgCoordinateReferenceRecord(6064, (EpsgCoordinateSystemKind)2, 2266); + return true; + case 6065: + cacheIndex = 3033; + reference = new EpsgCoordinateReferenceRecord(6065, (EpsgCoordinateSystemKind)2, 2267); + return true; + case 6066: + cacheIndex = 3034; + reference = new EpsgCoordinateReferenceRecord(6066, (EpsgCoordinateSystemKind)2, 2268); + return true; + case 6067: + cacheIndex = 3035; + reference = new EpsgCoordinateReferenceRecord(6067, (EpsgCoordinateSystemKind)2, 2269); + return true; + case 6068: + cacheIndex = 3036; + reference = new EpsgCoordinateReferenceRecord(6068, (EpsgCoordinateSystemKind)2, 2270); + return true; + case 6069: + cacheIndex = 3037; + reference = new EpsgCoordinateReferenceRecord(6069, (EpsgCoordinateSystemKind)2, 2271); + return true; + case 6070: + cacheIndex = 3038; + reference = new EpsgCoordinateReferenceRecord(6070, (EpsgCoordinateSystemKind)2, 2272); + return true; + case 6071: + cacheIndex = 3039; + reference = new EpsgCoordinateReferenceRecord(6071, (EpsgCoordinateSystemKind)2, 2273); + return true; + case 6072: + cacheIndex = 3040; + reference = new EpsgCoordinateReferenceRecord(6072, (EpsgCoordinateSystemKind)2, 2274); + return true; + case 6073: + cacheIndex = 3041; + reference = new EpsgCoordinateReferenceRecord(6073, (EpsgCoordinateSystemKind)2, 2275); + return true; + case 6074: + cacheIndex = 3042; + reference = new EpsgCoordinateReferenceRecord(6074, (EpsgCoordinateSystemKind)2, 2276); + return true; + case 6075: + cacheIndex = 3043; + reference = new EpsgCoordinateReferenceRecord(6075, (EpsgCoordinateSystemKind)2, 2277); + return true; + case 6076: + cacheIndex = 3044; + reference = new EpsgCoordinateReferenceRecord(6076, (EpsgCoordinateSystemKind)2, 2278); + return true; + case 6077: + cacheIndex = 3045; + reference = new EpsgCoordinateReferenceRecord(6077, (EpsgCoordinateSystemKind)2, 2279); + return true; + case 6078: + cacheIndex = 3046; + reference = new EpsgCoordinateReferenceRecord(6078, (EpsgCoordinateSystemKind)2, 2280); + return true; + case 6079: + cacheIndex = 3047; + reference = new EpsgCoordinateReferenceRecord(6079, (EpsgCoordinateSystemKind)2, 2281); + return true; + case 6080: + cacheIndex = 3048; + reference = new EpsgCoordinateReferenceRecord(6080, (EpsgCoordinateSystemKind)2, 2282); + return true; + case 6081: + cacheIndex = 3049; + reference = new EpsgCoordinateReferenceRecord(6081, (EpsgCoordinateSystemKind)2, 2283); + return true; + case 6082: + cacheIndex = 3050; + reference = new EpsgCoordinateReferenceRecord(6082, (EpsgCoordinateSystemKind)2, 2284); + return true; + case 6083: + cacheIndex = 3051; + reference = new EpsgCoordinateReferenceRecord(6083, (EpsgCoordinateSystemKind)2, 2285); + return true; + case 6084: + cacheIndex = 3052; + reference = new EpsgCoordinateReferenceRecord(6084, (EpsgCoordinateSystemKind)2, 2286); + return true; + case 6085: + cacheIndex = 3053; + reference = new EpsgCoordinateReferenceRecord(6085, (EpsgCoordinateSystemKind)2, 2287); + return true; + case 6086: + cacheIndex = 3054; + reference = new EpsgCoordinateReferenceRecord(6086, (EpsgCoordinateSystemKind)2, 2288); + return true; + case 6087: + cacheIndex = 3055; + reference = new EpsgCoordinateReferenceRecord(6087, (EpsgCoordinateSystemKind)2, 2289); + return true; + case 6088: + cacheIndex = 3056; + reference = new EpsgCoordinateReferenceRecord(6088, (EpsgCoordinateSystemKind)2, 2290); + return true; + case 6089: + cacheIndex = 3057; + reference = new EpsgCoordinateReferenceRecord(6089, (EpsgCoordinateSystemKind)2, 2291); + return true; + case 6090: + cacheIndex = 3058; + reference = new EpsgCoordinateReferenceRecord(6090, (EpsgCoordinateSystemKind)2, 2292); + return true; + case 6091: + cacheIndex = 3059; + reference = new EpsgCoordinateReferenceRecord(6091, (EpsgCoordinateSystemKind)2, 2293); + return true; + case 6092: + cacheIndex = 3060; + reference = new EpsgCoordinateReferenceRecord(6092, (EpsgCoordinateSystemKind)2, 2294); + return true; + case 6093: + cacheIndex = 3061; + reference = new EpsgCoordinateReferenceRecord(6093, (EpsgCoordinateSystemKind)2, 2295); + return true; + case 6094: + cacheIndex = 3062; + reference = new EpsgCoordinateReferenceRecord(6094, (EpsgCoordinateSystemKind)2, 2296); + return true; + case 6095: + cacheIndex = 3063; + reference = new EpsgCoordinateReferenceRecord(6095, (EpsgCoordinateSystemKind)2, 2297); + return true; + case 6096: + cacheIndex = 3064; + reference = new EpsgCoordinateReferenceRecord(6096, (EpsgCoordinateSystemKind)2, 2298); + return true; + case 6097: + cacheIndex = 3065; + reference = new EpsgCoordinateReferenceRecord(6097, (EpsgCoordinateSystemKind)2, 2299); + return true; + case 6098: + cacheIndex = 3066; + reference = new EpsgCoordinateReferenceRecord(6098, (EpsgCoordinateSystemKind)2, 2300); + return true; + case 6099: + cacheIndex = 3067; + reference = new EpsgCoordinateReferenceRecord(6099, (EpsgCoordinateSystemKind)2, 2301); + return true; + case 6100: + cacheIndex = 3068; + reference = new EpsgCoordinateReferenceRecord(6100, (EpsgCoordinateSystemKind)2, 2302); + return true; + case 6101: + cacheIndex = 3069; + reference = new EpsgCoordinateReferenceRecord(6101, (EpsgCoordinateSystemKind)2, 2303); + return true; + case 6102: + cacheIndex = 3070; + reference = new EpsgCoordinateReferenceRecord(6102, (EpsgCoordinateSystemKind)2, 2304); + return true; + case 6103: + cacheIndex = 3071; + reference = new EpsgCoordinateReferenceRecord(6103, (EpsgCoordinateSystemKind)2, 2305); + return true; + case 6104: + cacheIndex = 3072; + reference = new EpsgCoordinateReferenceRecord(6104, (EpsgCoordinateSystemKind)2, 2306); + return true; + case 6105: + cacheIndex = 3073; + reference = new EpsgCoordinateReferenceRecord(6105, (EpsgCoordinateSystemKind)2, 2307); + return true; + case 6106: + cacheIndex = 3074; + reference = new EpsgCoordinateReferenceRecord(6106, (EpsgCoordinateSystemKind)2, 2308); + return true; + case 6107: + cacheIndex = 3075; + reference = new EpsgCoordinateReferenceRecord(6107, (EpsgCoordinateSystemKind)2, 2309); + return true; + case 6108: + cacheIndex = 3076; + reference = new EpsgCoordinateReferenceRecord(6108, (EpsgCoordinateSystemKind)2, 2310); + return true; + case 6109: + cacheIndex = 3077; + reference = new EpsgCoordinateReferenceRecord(6109, (EpsgCoordinateSystemKind)2, 2311); + return true; + case 6110: + cacheIndex = 3078; + reference = new EpsgCoordinateReferenceRecord(6110, (EpsgCoordinateSystemKind)2, 2312); + return true; + case 6111: + cacheIndex = 3079; + reference = new EpsgCoordinateReferenceRecord(6111, (EpsgCoordinateSystemKind)2, 2313); + return true; + case 6112: + cacheIndex = 3080; + reference = new EpsgCoordinateReferenceRecord(6112, (EpsgCoordinateSystemKind)2, 2314); + return true; + case 6113: + cacheIndex = 3081; + reference = new EpsgCoordinateReferenceRecord(6113, (EpsgCoordinateSystemKind)2, 2315); + return true; + case 6114: + cacheIndex = 3082; + reference = new EpsgCoordinateReferenceRecord(6114, (EpsgCoordinateSystemKind)2, 2316); + return true; + case 6115: + cacheIndex = 3083; + reference = new EpsgCoordinateReferenceRecord(6115, (EpsgCoordinateSystemKind)2, 2317); + return true; + case 6116: + cacheIndex = 3084; + reference = new EpsgCoordinateReferenceRecord(6116, (EpsgCoordinateSystemKind)2, 2318); + return true; + case 6117: + cacheIndex = 3085; + reference = new EpsgCoordinateReferenceRecord(6117, (EpsgCoordinateSystemKind)2, 2319); + return true; + case 6118: + cacheIndex = 3086; + reference = new EpsgCoordinateReferenceRecord(6118, (EpsgCoordinateSystemKind)2, 2320); + return true; + case 6119: + cacheIndex = 3087; + reference = new EpsgCoordinateReferenceRecord(6119, (EpsgCoordinateSystemKind)2, 2321); + return true; + case 6120: + cacheIndex = 3088; + reference = new EpsgCoordinateReferenceRecord(6120, (EpsgCoordinateSystemKind)2, 2322); + return true; + case 6121: + cacheIndex = 3089; + reference = new EpsgCoordinateReferenceRecord(6121, (EpsgCoordinateSystemKind)2, 2323); + return true; + case 6122: + cacheIndex = 3090; + reference = new EpsgCoordinateReferenceRecord(6122, (EpsgCoordinateSystemKind)2, 2324); + return true; + case 6123: + cacheIndex = 3091; + reference = new EpsgCoordinateReferenceRecord(6123, (EpsgCoordinateSystemKind)2, 2325); + return true; + case 6124: + cacheIndex = 3092; + reference = new EpsgCoordinateReferenceRecord(6124, (EpsgCoordinateSystemKind)2, 2326); + return true; + case 6125: + cacheIndex = 3093; + reference = new EpsgCoordinateReferenceRecord(6125, (EpsgCoordinateSystemKind)2, 2327); + return true; + case 6128: + cacheIndex = 3094; + reference = new EpsgCoordinateReferenceRecord(6128, (EpsgCoordinateSystemKind)2, 2328); + return true; + case 6129: + cacheIndex = 3095; + reference = new EpsgCoordinateReferenceRecord(6129, (EpsgCoordinateSystemKind)2, 2329); + return true; + case 6130: + cacheIndex = 3096; + reference = new EpsgCoordinateReferenceRecord(6130, (EpsgCoordinateSystemKind)3, 139); + return true; + case 6131: + cacheIndex = 3097; + reference = new EpsgCoordinateReferenceRecord(6131, (EpsgCoordinateSystemKind)3, 140); + return true; + case 6132: + cacheIndex = 3098; + reference = new EpsgCoordinateReferenceRecord(6132, (EpsgCoordinateSystemKind)3, 141); + return true; + case 6133: + cacheIndex = 3099; + reference = new EpsgCoordinateReferenceRecord(6133, (EpsgCoordinateSystemKind)1, 92); + return true; + case 6134: + cacheIndex = 3100; + reference = new EpsgCoordinateReferenceRecord(6134, (EpsgCoordinateSystemKind)0, 473); + return true; + case 6135: + cacheIndex = 3101; + reference = new EpsgCoordinateReferenceRecord(6135, (EpsgCoordinateSystemKind)0, 474); + return true; + case 6144: + cacheIndex = 3102; + reference = new EpsgCoordinateReferenceRecord(6144, (EpsgCoordinateSystemKind)4, 62); + return true; + case 6145: + cacheIndex = 3103; + reference = new EpsgCoordinateReferenceRecord(6145, (EpsgCoordinateSystemKind)4, 63); + return true; + case 6146: + cacheIndex = 3104; + reference = new EpsgCoordinateReferenceRecord(6146, (EpsgCoordinateSystemKind)4, 64); + return true; + case 6147: + cacheIndex = 3105; + reference = new EpsgCoordinateReferenceRecord(6147, (EpsgCoordinateSystemKind)4, 65); + return true; + case 6148: + cacheIndex = 3106; + reference = new EpsgCoordinateReferenceRecord(6148, (EpsgCoordinateSystemKind)4, 66); + return true; + case 6149: + cacheIndex = 3107; + reference = new EpsgCoordinateReferenceRecord(6149, (EpsgCoordinateSystemKind)4, 67); + return true; + case 6150: + cacheIndex = 3108; + reference = new EpsgCoordinateReferenceRecord(6150, (EpsgCoordinateSystemKind)4, 68); + return true; + case 6151: + cacheIndex = 3109; + reference = new EpsgCoordinateReferenceRecord(6151, (EpsgCoordinateSystemKind)4, 69); + return true; + case 6152: + cacheIndex = 3110; + reference = new EpsgCoordinateReferenceRecord(6152, (EpsgCoordinateSystemKind)4, 70); + return true; + case 6153: + cacheIndex = 3111; + reference = new EpsgCoordinateReferenceRecord(6153, (EpsgCoordinateSystemKind)4, 71); + return true; + case 6154: + cacheIndex = 3112; + reference = new EpsgCoordinateReferenceRecord(6154, (EpsgCoordinateSystemKind)4, 72); + return true; + case 6155: + cacheIndex = 3113; + reference = new EpsgCoordinateReferenceRecord(6155, (EpsgCoordinateSystemKind)4, 73); + return true; + case 6156: + cacheIndex = 3114; + reference = new EpsgCoordinateReferenceRecord(6156, (EpsgCoordinateSystemKind)4, 74); + return true; + case 6157: + cacheIndex = 3115; + reference = new EpsgCoordinateReferenceRecord(6157, (EpsgCoordinateSystemKind)4, 75); + return true; + case 6158: + cacheIndex = 3116; + reference = new EpsgCoordinateReferenceRecord(6158, (EpsgCoordinateSystemKind)4, 76); + return true; + case 6159: + cacheIndex = 3117; + reference = new EpsgCoordinateReferenceRecord(6159, (EpsgCoordinateSystemKind)4, 77); + return true; + case 6160: + cacheIndex = 3118; + reference = new EpsgCoordinateReferenceRecord(6160, (EpsgCoordinateSystemKind)4, 78); + return true; + case 6161: + cacheIndex = 3119; + reference = new EpsgCoordinateReferenceRecord(6161, (EpsgCoordinateSystemKind)4, 79); + return true; + case 6162: + cacheIndex = 3120; + reference = new EpsgCoordinateReferenceRecord(6162, (EpsgCoordinateSystemKind)4, 80); + return true; + case 6163: + cacheIndex = 3121; + reference = new EpsgCoordinateReferenceRecord(6163, (EpsgCoordinateSystemKind)4, 81); + return true; + case 6164: + cacheIndex = 3122; + reference = new EpsgCoordinateReferenceRecord(6164, (EpsgCoordinateSystemKind)4, 82); + return true; + case 6165: + cacheIndex = 3123; + reference = new EpsgCoordinateReferenceRecord(6165, (EpsgCoordinateSystemKind)4, 83); + return true; + case 6166: + cacheIndex = 3124; + reference = new EpsgCoordinateReferenceRecord(6166, (EpsgCoordinateSystemKind)4, 84); + return true; + case 6167: + cacheIndex = 3125; + reference = new EpsgCoordinateReferenceRecord(6167, (EpsgCoordinateSystemKind)4, 85); + return true; + case 6168: + cacheIndex = 3126; + reference = new EpsgCoordinateReferenceRecord(6168, (EpsgCoordinateSystemKind)4, 86); + return true; + case 6169: + cacheIndex = 3127; + reference = new EpsgCoordinateReferenceRecord(6169, (EpsgCoordinateSystemKind)4, 87); + return true; + case 6170: + cacheIndex = 3128; + reference = new EpsgCoordinateReferenceRecord(6170, (EpsgCoordinateSystemKind)4, 88); + return true; + case 6171: + cacheIndex = 3129; + reference = new EpsgCoordinateReferenceRecord(6171, (EpsgCoordinateSystemKind)4, 89); + return true; + case 6172: + cacheIndex = 3130; + reference = new EpsgCoordinateReferenceRecord(6172, (EpsgCoordinateSystemKind)4, 90); + return true; + case 6173: + cacheIndex = 3131; + reference = new EpsgCoordinateReferenceRecord(6173, (EpsgCoordinateSystemKind)4, 91); + return true; + case 6174: + cacheIndex = 3132; + reference = new EpsgCoordinateReferenceRecord(6174, (EpsgCoordinateSystemKind)4, 92); + return true; + case 6175: + cacheIndex = 3133; + reference = new EpsgCoordinateReferenceRecord(6175, (EpsgCoordinateSystemKind)4, 93); + return true; + case 6176: + cacheIndex = 3134; + reference = new EpsgCoordinateReferenceRecord(6176, (EpsgCoordinateSystemKind)4, 94); + return true; + case 6178: + cacheIndex = 3135; + reference = new EpsgCoordinateReferenceRecord(6178, (EpsgCoordinateSystemKind)3, 142); + return true; + case 6179: + cacheIndex = 3136; + reference = new EpsgCoordinateReferenceRecord(6179, (EpsgCoordinateSystemKind)3, 143); + return true; + case 6180: + cacheIndex = 3137; + reference = new EpsgCoordinateReferenceRecord(6180, (EpsgCoordinateSystemKind)3, 144); + return true; + case 6181: + cacheIndex = 3138; + reference = new EpsgCoordinateReferenceRecord(6181, (EpsgCoordinateSystemKind)3, 145); + return true; + case 6182: + cacheIndex = 3139; + reference = new EpsgCoordinateReferenceRecord(6182, (EpsgCoordinateSystemKind)3, 146); + return true; + case 6183: + cacheIndex = 3140; + reference = new EpsgCoordinateReferenceRecord(6183, (EpsgCoordinateSystemKind)3, 147); + return true; + case 6184: + cacheIndex = 3141; + reference = new EpsgCoordinateReferenceRecord(6184, (EpsgCoordinateSystemKind)3, 148); + return true; + case 6185: + cacheIndex = 3142; + reference = new EpsgCoordinateReferenceRecord(6185, (EpsgCoordinateSystemKind)3, 149); + return true; + case 6186: + cacheIndex = 3143; + reference = new EpsgCoordinateReferenceRecord(6186, (EpsgCoordinateSystemKind)3, 150); + return true; + case 6187: + cacheIndex = 3144; + reference = new EpsgCoordinateReferenceRecord(6187, (EpsgCoordinateSystemKind)3, 151); + return true; + case 6190: + cacheIndex = 3145; + reference = new EpsgCoordinateReferenceRecord(6190, (EpsgCoordinateSystemKind)4, 95); + return true; + case 6201: + cacheIndex = 3146; + reference = new EpsgCoordinateReferenceRecord(6201, (EpsgCoordinateSystemKind)2, 2330); + return true; + case 6202: + cacheIndex = 3147; + reference = new EpsgCoordinateReferenceRecord(6202, (EpsgCoordinateSystemKind)2, 2331); + return true; + case 6204: + cacheIndex = 3148; + reference = new EpsgCoordinateReferenceRecord(6204, (EpsgCoordinateSystemKind)2, 2332); + return true; + case 6207: + cacheIndex = 3149; + reference = new EpsgCoordinateReferenceRecord(6207, (EpsgCoordinateSystemKind)0, 475); + return true; + case 6210: + cacheIndex = 3150; + reference = new EpsgCoordinateReferenceRecord(6210, (EpsgCoordinateSystemKind)2, 2333); + return true; + case 6211: + cacheIndex = 3151; + reference = new EpsgCoordinateReferenceRecord(6211, (EpsgCoordinateSystemKind)2, 2334); + return true; + case 6244: + cacheIndex = 3152; + reference = new EpsgCoordinateReferenceRecord(6244, (EpsgCoordinateSystemKind)2, 2335); + return true; + case 6245: + cacheIndex = 3153; + reference = new EpsgCoordinateReferenceRecord(6245, (EpsgCoordinateSystemKind)2, 2336); + return true; + case 6246: + cacheIndex = 3154; + reference = new EpsgCoordinateReferenceRecord(6246, (EpsgCoordinateSystemKind)2, 2337); + return true; + case 6247: + cacheIndex = 3155; + reference = new EpsgCoordinateReferenceRecord(6247, (EpsgCoordinateSystemKind)2, 2338); + return true; + case 6248: + cacheIndex = 3156; + reference = new EpsgCoordinateReferenceRecord(6248, (EpsgCoordinateSystemKind)2, 2339); + return true; + case 6249: + cacheIndex = 3157; + reference = new EpsgCoordinateReferenceRecord(6249, (EpsgCoordinateSystemKind)2, 2340); + return true; + case 6250: + cacheIndex = 3158; + reference = new EpsgCoordinateReferenceRecord(6250, (EpsgCoordinateSystemKind)2, 2341); + return true; + case 6251: + cacheIndex = 3159; + reference = new EpsgCoordinateReferenceRecord(6251, (EpsgCoordinateSystemKind)2, 2342); + return true; + case 6252: + cacheIndex = 3160; + reference = new EpsgCoordinateReferenceRecord(6252, (EpsgCoordinateSystemKind)2, 2343); + return true; + case 6253: + cacheIndex = 3161; + reference = new EpsgCoordinateReferenceRecord(6253, (EpsgCoordinateSystemKind)2, 2344); + return true; + case 6254: + cacheIndex = 3162; + reference = new EpsgCoordinateReferenceRecord(6254, (EpsgCoordinateSystemKind)2, 2345); + return true; + case 6255: + cacheIndex = 3163; + reference = new EpsgCoordinateReferenceRecord(6255, (EpsgCoordinateSystemKind)2, 2346); + return true; + case 6256: + cacheIndex = 3164; + reference = new EpsgCoordinateReferenceRecord(6256, (EpsgCoordinateSystemKind)2, 2347); + return true; + case 6257: + cacheIndex = 3165; + reference = new EpsgCoordinateReferenceRecord(6257, (EpsgCoordinateSystemKind)2, 2348); + return true; + case 6258: + cacheIndex = 3166; + reference = new EpsgCoordinateReferenceRecord(6258, (EpsgCoordinateSystemKind)2, 2349); + return true; + case 6259: + cacheIndex = 3167; + reference = new EpsgCoordinateReferenceRecord(6259, (EpsgCoordinateSystemKind)2, 2350); + return true; + case 6260: + cacheIndex = 3168; + reference = new EpsgCoordinateReferenceRecord(6260, (EpsgCoordinateSystemKind)2, 2351); + return true; + case 6261: + cacheIndex = 3169; + reference = new EpsgCoordinateReferenceRecord(6261, (EpsgCoordinateSystemKind)2, 2352); + return true; + case 6262: + cacheIndex = 3170; + reference = new EpsgCoordinateReferenceRecord(6262, (EpsgCoordinateSystemKind)2, 2353); + return true; + case 6263: + cacheIndex = 3171; + reference = new EpsgCoordinateReferenceRecord(6263, (EpsgCoordinateSystemKind)2, 2354); + return true; + case 6264: + cacheIndex = 3172; + reference = new EpsgCoordinateReferenceRecord(6264, (EpsgCoordinateSystemKind)2, 2355); + return true; + case 6265: + cacheIndex = 3173; + reference = new EpsgCoordinateReferenceRecord(6265, (EpsgCoordinateSystemKind)2, 2356); + return true; + case 6266: + cacheIndex = 3174; + reference = new EpsgCoordinateReferenceRecord(6266, (EpsgCoordinateSystemKind)2, 2357); + return true; + case 6267: + cacheIndex = 3175; + reference = new EpsgCoordinateReferenceRecord(6267, (EpsgCoordinateSystemKind)2, 2358); + return true; + case 6268: + cacheIndex = 3176; + reference = new EpsgCoordinateReferenceRecord(6268, (EpsgCoordinateSystemKind)2, 2359); + return true; + case 6269: + cacheIndex = 3177; + reference = new EpsgCoordinateReferenceRecord(6269, (EpsgCoordinateSystemKind)2, 2360); + return true; + case 6270: + cacheIndex = 3178; + reference = new EpsgCoordinateReferenceRecord(6270, (EpsgCoordinateSystemKind)2, 2361); + return true; + case 6271: + cacheIndex = 3179; + reference = new EpsgCoordinateReferenceRecord(6271, (EpsgCoordinateSystemKind)2, 2362); + return true; + case 6272: + cacheIndex = 3180; + reference = new EpsgCoordinateReferenceRecord(6272, (EpsgCoordinateSystemKind)2, 2363); + return true; + case 6273: + cacheIndex = 3181; + reference = new EpsgCoordinateReferenceRecord(6273, (EpsgCoordinateSystemKind)2, 2364); + return true; + case 6274: + cacheIndex = 3182; + reference = new EpsgCoordinateReferenceRecord(6274, (EpsgCoordinateSystemKind)2, 2365); + return true; + case 6275: + cacheIndex = 3183; + reference = new EpsgCoordinateReferenceRecord(6275, (EpsgCoordinateSystemKind)2, 2366); + return true; + case 6307: + cacheIndex = 3184; + reference = new EpsgCoordinateReferenceRecord(6307, (EpsgCoordinateSystemKind)2, 2367); + return true; + case 6309: + cacheIndex = 3185; + reference = new EpsgCoordinateReferenceRecord(6309, (EpsgCoordinateSystemKind)1, 93); + return true; + case 6310: + cacheIndex = 3186; + reference = new EpsgCoordinateReferenceRecord(6310, (EpsgCoordinateSystemKind)0, 476); + return true; + case 6311: + cacheIndex = 3187; + reference = new EpsgCoordinateReferenceRecord(6311, (EpsgCoordinateSystemKind)0, 477); + return true; + case 6312: + cacheIndex = 3188; + reference = new EpsgCoordinateReferenceRecord(6312, (EpsgCoordinateSystemKind)2, 2368); + return true; + case 6316: + cacheIndex = 3189; + reference = new EpsgCoordinateReferenceRecord(6316, (EpsgCoordinateSystemKind)2, 2369); + return true; + case 6317: + cacheIndex = 3190; + reference = new EpsgCoordinateReferenceRecord(6317, (EpsgCoordinateSystemKind)1, 94); + return true; + case 6318: + cacheIndex = 3191; + reference = new EpsgCoordinateReferenceRecord(6318, (EpsgCoordinateSystemKind)0, 478); + return true; + case 6319: + cacheIndex = 3192; + reference = new EpsgCoordinateReferenceRecord(6319, (EpsgCoordinateSystemKind)0, 479); + return true; + case 6320: + cacheIndex = 3193; + reference = new EpsgCoordinateReferenceRecord(6320, (EpsgCoordinateSystemKind)1, 95); + return true; + case 6321: + cacheIndex = 3194; + reference = new EpsgCoordinateReferenceRecord(6321, (EpsgCoordinateSystemKind)0, 480); + return true; + case 6322: + cacheIndex = 3195; + reference = new EpsgCoordinateReferenceRecord(6322, (EpsgCoordinateSystemKind)0, 481); + return true; + case 6323: + cacheIndex = 3196; + reference = new EpsgCoordinateReferenceRecord(6323, (EpsgCoordinateSystemKind)1, 96); + return true; + case 6324: + cacheIndex = 3197; + reference = new EpsgCoordinateReferenceRecord(6324, (EpsgCoordinateSystemKind)0, 482); + return true; + case 6325: + cacheIndex = 3198; + reference = new EpsgCoordinateReferenceRecord(6325, (EpsgCoordinateSystemKind)0, 483); + return true; + case 6328: + cacheIndex = 3199; + reference = new EpsgCoordinateReferenceRecord(6328, (EpsgCoordinateSystemKind)2, 2370); + return true; + case 6329: + cacheIndex = 3200; + reference = new EpsgCoordinateReferenceRecord(6329, (EpsgCoordinateSystemKind)2, 2371); + return true; + case 6330: + cacheIndex = 3201; + reference = new EpsgCoordinateReferenceRecord(6330, (EpsgCoordinateSystemKind)2, 2372); + return true; + case 6331: + cacheIndex = 3202; + reference = new EpsgCoordinateReferenceRecord(6331, (EpsgCoordinateSystemKind)2, 2373); + return true; + case 6332: + cacheIndex = 3203; + reference = new EpsgCoordinateReferenceRecord(6332, (EpsgCoordinateSystemKind)2, 2374); + return true; + case 6333: + cacheIndex = 3204; + reference = new EpsgCoordinateReferenceRecord(6333, (EpsgCoordinateSystemKind)2, 2375); + return true; + case 6334: + cacheIndex = 3205; + reference = new EpsgCoordinateReferenceRecord(6334, (EpsgCoordinateSystemKind)2, 2376); + return true; + case 6335: + cacheIndex = 3206; + reference = new EpsgCoordinateReferenceRecord(6335, (EpsgCoordinateSystemKind)2, 2377); + return true; + case 6336: + cacheIndex = 3207; + reference = new EpsgCoordinateReferenceRecord(6336, (EpsgCoordinateSystemKind)2, 2378); + return true; + case 6337: + cacheIndex = 3208; + reference = new EpsgCoordinateReferenceRecord(6337, (EpsgCoordinateSystemKind)2, 2379); + return true; + case 6338: + cacheIndex = 3209; + reference = new EpsgCoordinateReferenceRecord(6338, (EpsgCoordinateSystemKind)2, 2380); + return true; + case 6339: + cacheIndex = 3210; + reference = new EpsgCoordinateReferenceRecord(6339, (EpsgCoordinateSystemKind)2, 2381); + return true; + case 6340: + cacheIndex = 3211; + reference = new EpsgCoordinateReferenceRecord(6340, (EpsgCoordinateSystemKind)2, 2382); + return true; + case 6341: + cacheIndex = 3212; + reference = new EpsgCoordinateReferenceRecord(6341, (EpsgCoordinateSystemKind)2, 2383); + return true; + case 6342: + cacheIndex = 3213; + reference = new EpsgCoordinateReferenceRecord(6342, (EpsgCoordinateSystemKind)2, 2384); + return true; + case 6343: + cacheIndex = 3214; + reference = new EpsgCoordinateReferenceRecord(6343, (EpsgCoordinateSystemKind)2, 2385); + return true; + case 6344: + cacheIndex = 3215; + reference = new EpsgCoordinateReferenceRecord(6344, (EpsgCoordinateSystemKind)2, 2386); + return true; + case 6345: + cacheIndex = 3216; + reference = new EpsgCoordinateReferenceRecord(6345, (EpsgCoordinateSystemKind)2, 2387); + return true; + case 6346: + cacheIndex = 3217; + reference = new EpsgCoordinateReferenceRecord(6346, (EpsgCoordinateSystemKind)2, 2388); + return true; + case 6347: + cacheIndex = 3218; + reference = new EpsgCoordinateReferenceRecord(6347, (EpsgCoordinateSystemKind)2, 2389); + return true; + case 6348: + cacheIndex = 3219; + reference = new EpsgCoordinateReferenceRecord(6348, (EpsgCoordinateSystemKind)2, 2390); + return true; + case 6349: + cacheIndex = 3220; + reference = new EpsgCoordinateReferenceRecord(6349, (EpsgCoordinateSystemKind)4, 96); + return true; + case 6350: + cacheIndex = 3221; + reference = new EpsgCoordinateReferenceRecord(6350, (EpsgCoordinateSystemKind)2, 2391); + return true; + case 6351: + cacheIndex = 3222; + reference = new EpsgCoordinateReferenceRecord(6351, (EpsgCoordinateSystemKind)2, 2392); + return true; + case 6352: + cacheIndex = 3223; + reference = new EpsgCoordinateReferenceRecord(6352, (EpsgCoordinateSystemKind)2, 2393); + return true; + case 6353: + cacheIndex = 3224; + reference = new EpsgCoordinateReferenceRecord(6353, (EpsgCoordinateSystemKind)2, 2394); + return true; + case 6354: + cacheIndex = 3225; + reference = new EpsgCoordinateReferenceRecord(6354, (EpsgCoordinateSystemKind)2, 2395); + return true; + case 6355: + cacheIndex = 3226; + reference = new EpsgCoordinateReferenceRecord(6355, (EpsgCoordinateSystemKind)2, 2396); + return true; + case 6356: + cacheIndex = 3227; + reference = new EpsgCoordinateReferenceRecord(6356, (EpsgCoordinateSystemKind)2, 2397); + return true; + case 6362: + cacheIndex = 3228; + reference = new EpsgCoordinateReferenceRecord(6362, (EpsgCoordinateSystemKind)2, 2398); + return true; + case 6363: + cacheIndex = 3229; + reference = new EpsgCoordinateReferenceRecord(6363, (EpsgCoordinateSystemKind)1, 97); + return true; + case 6364: + cacheIndex = 3230; + reference = new EpsgCoordinateReferenceRecord(6364, (EpsgCoordinateSystemKind)0, 484); + return true; + case 6365: + cacheIndex = 3231; + reference = new EpsgCoordinateReferenceRecord(6365, (EpsgCoordinateSystemKind)0, 485); + return true; + case 6366: + cacheIndex = 3232; + reference = new EpsgCoordinateReferenceRecord(6366, (EpsgCoordinateSystemKind)2, 2399); + return true; + case 6367: + cacheIndex = 3233; + reference = new EpsgCoordinateReferenceRecord(6367, (EpsgCoordinateSystemKind)2, 2400); + return true; + case 6368: + cacheIndex = 3234; + reference = new EpsgCoordinateReferenceRecord(6368, (EpsgCoordinateSystemKind)2, 2401); + return true; + case 6369: + cacheIndex = 3235; + reference = new EpsgCoordinateReferenceRecord(6369, (EpsgCoordinateSystemKind)2, 2402); + return true; + case 6370: + cacheIndex = 3236; + reference = new EpsgCoordinateReferenceRecord(6370, (EpsgCoordinateSystemKind)2, 2403); + return true; + case 6371: + cacheIndex = 3237; + reference = new EpsgCoordinateReferenceRecord(6371, (EpsgCoordinateSystemKind)2, 2404); + return true; + case 6372: + cacheIndex = 3238; + reference = new EpsgCoordinateReferenceRecord(6372, (EpsgCoordinateSystemKind)2, 2405); + return true; + case 6381: + cacheIndex = 3239; + reference = new EpsgCoordinateReferenceRecord(6381, (EpsgCoordinateSystemKind)2, 2406); + return true; + case 6382: + cacheIndex = 3240; + reference = new EpsgCoordinateReferenceRecord(6382, (EpsgCoordinateSystemKind)2, 2407); + return true; + case 6383: + cacheIndex = 3241; + reference = new EpsgCoordinateReferenceRecord(6383, (EpsgCoordinateSystemKind)2, 2408); + return true; + case 6384: + cacheIndex = 3242; + reference = new EpsgCoordinateReferenceRecord(6384, (EpsgCoordinateSystemKind)2, 2409); + return true; + case 6385: + cacheIndex = 3243; + reference = new EpsgCoordinateReferenceRecord(6385, (EpsgCoordinateSystemKind)2, 2410); + return true; + case 6386: + cacheIndex = 3244; + reference = new EpsgCoordinateReferenceRecord(6386, (EpsgCoordinateSystemKind)2, 2411); + return true; + case 6387: + cacheIndex = 3245; + reference = new EpsgCoordinateReferenceRecord(6387, (EpsgCoordinateSystemKind)2, 2412); + return true; + case 6391: + cacheIndex = 3246; + reference = new EpsgCoordinateReferenceRecord(6391, (EpsgCoordinateSystemKind)2, 2413); + return true; + case 6393: + cacheIndex = 3247; + reference = new EpsgCoordinateReferenceRecord(6393, (EpsgCoordinateSystemKind)2, 2414); + return true; + case 6394: + cacheIndex = 3248; + reference = new EpsgCoordinateReferenceRecord(6394, (EpsgCoordinateSystemKind)2, 2415); + return true; + case 6395: + cacheIndex = 3249; + reference = new EpsgCoordinateReferenceRecord(6395, (EpsgCoordinateSystemKind)2, 2416); + return true; + case 6396: + cacheIndex = 3250; + reference = new EpsgCoordinateReferenceRecord(6396, (EpsgCoordinateSystemKind)2, 2417); + return true; + case 6397: + cacheIndex = 3251; + reference = new EpsgCoordinateReferenceRecord(6397, (EpsgCoordinateSystemKind)2, 2418); + return true; + case 6398: + cacheIndex = 3252; + reference = new EpsgCoordinateReferenceRecord(6398, (EpsgCoordinateSystemKind)2, 2419); + return true; + case 6399: + cacheIndex = 3253; + reference = new EpsgCoordinateReferenceRecord(6399, (EpsgCoordinateSystemKind)2, 2420); + return true; + case 6400: + cacheIndex = 3254; + reference = new EpsgCoordinateReferenceRecord(6400, (EpsgCoordinateSystemKind)2, 2421); + return true; + case 6401: + cacheIndex = 3255; + reference = new EpsgCoordinateReferenceRecord(6401, (EpsgCoordinateSystemKind)2, 2422); + return true; + case 6402: + cacheIndex = 3256; + reference = new EpsgCoordinateReferenceRecord(6402, (EpsgCoordinateSystemKind)2, 2423); + return true; + case 6403: + cacheIndex = 3257; + reference = new EpsgCoordinateReferenceRecord(6403, (EpsgCoordinateSystemKind)2, 2424); + return true; + case 6404: + cacheIndex = 3258; + reference = new EpsgCoordinateReferenceRecord(6404, (EpsgCoordinateSystemKind)2, 2425); + return true; + case 6405: + cacheIndex = 3259; + reference = new EpsgCoordinateReferenceRecord(6405, (EpsgCoordinateSystemKind)2, 2426); + return true; + case 6406: + cacheIndex = 3260; + reference = new EpsgCoordinateReferenceRecord(6406, (EpsgCoordinateSystemKind)2, 2427); + return true; + case 6407: + cacheIndex = 3261; + reference = new EpsgCoordinateReferenceRecord(6407, (EpsgCoordinateSystemKind)2, 2428); + return true; + case 6408: + cacheIndex = 3262; + reference = new EpsgCoordinateReferenceRecord(6408, (EpsgCoordinateSystemKind)2, 2429); + return true; + case 6409: + cacheIndex = 3263; + reference = new EpsgCoordinateReferenceRecord(6409, (EpsgCoordinateSystemKind)2, 2430); + return true; + case 6410: + cacheIndex = 3264; + reference = new EpsgCoordinateReferenceRecord(6410, (EpsgCoordinateSystemKind)2, 2431); + return true; + case 6411: + cacheIndex = 3265; + reference = new EpsgCoordinateReferenceRecord(6411, (EpsgCoordinateSystemKind)2, 2432); + return true; + case 6412: + cacheIndex = 3266; + reference = new EpsgCoordinateReferenceRecord(6412, (EpsgCoordinateSystemKind)2, 2433); + return true; + case 6413: + cacheIndex = 3267; + reference = new EpsgCoordinateReferenceRecord(6413, (EpsgCoordinateSystemKind)2, 2434); + return true; + case 6414: + cacheIndex = 3268; + reference = new EpsgCoordinateReferenceRecord(6414, (EpsgCoordinateSystemKind)2, 2435); + return true; + case 6415: + cacheIndex = 3269; + reference = new EpsgCoordinateReferenceRecord(6415, (EpsgCoordinateSystemKind)2, 2436); + return true; + case 6416: + cacheIndex = 3270; + reference = new EpsgCoordinateReferenceRecord(6416, (EpsgCoordinateSystemKind)2, 2437); + return true; + case 6417: + cacheIndex = 3271; + reference = new EpsgCoordinateReferenceRecord(6417, (EpsgCoordinateSystemKind)2, 2438); + return true; + case 6418: + cacheIndex = 3272; + reference = new EpsgCoordinateReferenceRecord(6418, (EpsgCoordinateSystemKind)2, 2439); + return true; + case 6419: + cacheIndex = 3273; + reference = new EpsgCoordinateReferenceRecord(6419, (EpsgCoordinateSystemKind)2, 2440); + return true; + case 6420: + cacheIndex = 3274; + reference = new EpsgCoordinateReferenceRecord(6420, (EpsgCoordinateSystemKind)2, 2441); + return true; + case 6421: + cacheIndex = 3275; + reference = new EpsgCoordinateReferenceRecord(6421, (EpsgCoordinateSystemKind)2, 2442); + return true; + case 6422: + cacheIndex = 3276; + reference = new EpsgCoordinateReferenceRecord(6422, (EpsgCoordinateSystemKind)2, 2443); + return true; + case 6423: + cacheIndex = 3277; + reference = new EpsgCoordinateReferenceRecord(6423, (EpsgCoordinateSystemKind)2, 2444); + return true; + case 6424: + cacheIndex = 3278; + reference = new EpsgCoordinateReferenceRecord(6424, (EpsgCoordinateSystemKind)2, 2445); + return true; + case 6425: + cacheIndex = 3279; + reference = new EpsgCoordinateReferenceRecord(6425, (EpsgCoordinateSystemKind)2, 2446); + return true; + case 6426: + cacheIndex = 3280; + reference = new EpsgCoordinateReferenceRecord(6426, (EpsgCoordinateSystemKind)2, 2447); + return true; + case 6427: + cacheIndex = 3281; + reference = new EpsgCoordinateReferenceRecord(6427, (EpsgCoordinateSystemKind)2, 2448); + return true; + case 6428: + cacheIndex = 3282; + reference = new EpsgCoordinateReferenceRecord(6428, (EpsgCoordinateSystemKind)2, 2449); + return true; + case 6429: + cacheIndex = 3283; + reference = new EpsgCoordinateReferenceRecord(6429, (EpsgCoordinateSystemKind)2, 2450); + return true; + case 6430: + cacheIndex = 3284; + reference = new EpsgCoordinateReferenceRecord(6430, (EpsgCoordinateSystemKind)2, 2451); + return true; + case 6431: + cacheIndex = 3285; + reference = new EpsgCoordinateReferenceRecord(6431, (EpsgCoordinateSystemKind)2, 2452); + return true; + case 6432: + cacheIndex = 3286; + reference = new EpsgCoordinateReferenceRecord(6432, (EpsgCoordinateSystemKind)2, 2453); + return true; + case 6433: + cacheIndex = 3287; + reference = new EpsgCoordinateReferenceRecord(6433, (EpsgCoordinateSystemKind)2, 2454); + return true; + case 6434: + cacheIndex = 3288; + reference = new EpsgCoordinateReferenceRecord(6434, (EpsgCoordinateSystemKind)2, 2455); + return true; + case 6435: + cacheIndex = 3289; + reference = new EpsgCoordinateReferenceRecord(6435, (EpsgCoordinateSystemKind)2, 2456); + return true; + case 6436: + cacheIndex = 3290; + reference = new EpsgCoordinateReferenceRecord(6436, (EpsgCoordinateSystemKind)2, 2457); + return true; + case 6437: + cacheIndex = 3291; + reference = new EpsgCoordinateReferenceRecord(6437, (EpsgCoordinateSystemKind)2, 2458); + return true; + case 6438: + cacheIndex = 3292; + reference = new EpsgCoordinateReferenceRecord(6438, (EpsgCoordinateSystemKind)2, 2459); + return true; + case 6439: + cacheIndex = 3293; + reference = new EpsgCoordinateReferenceRecord(6439, (EpsgCoordinateSystemKind)2, 2460); + return true; + case 6440: + cacheIndex = 3294; + reference = new EpsgCoordinateReferenceRecord(6440, (EpsgCoordinateSystemKind)2, 2461); + return true; + case 6441: + cacheIndex = 3295; + reference = new EpsgCoordinateReferenceRecord(6441, (EpsgCoordinateSystemKind)2, 2462); + return true; + case 6442: + cacheIndex = 3296; + reference = new EpsgCoordinateReferenceRecord(6442, (EpsgCoordinateSystemKind)2, 2463); + return true; + case 6443: + cacheIndex = 3297; + reference = new EpsgCoordinateReferenceRecord(6443, (EpsgCoordinateSystemKind)2, 2464); + return true; + case 6444: + cacheIndex = 3298; + reference = new EpsgCoordinateReferenceRecord(6444, (EpsgCoordinateSystemKind)2, 2465); + return true; + case 6445: + cacheIndex = 3299; + reference = new EpsgCoordinateReferenceRecord(6445, (EpsgCoordinateSystemKind)2, 2466); + return true; + case 6446: + cacheIndex = 3300; + reference = new EpsgCoordinateReferenceRecord(6446, (EpsgCoordinateSystemKind)2, 2467); + return true; + case 6447: + cacheIndex = 3301; + reference = new EpsgCoordinateReferenceRecord(6447, (EpsgCoordinateSystemKind)2, 2468); + return true; + case 6448: + cacheIndex = 3302; + reference = new EpsgCoordinateReferenceRecord(6448, (EpsgCoordinateSystemKind)2, 2469); + return true; + case 6449: + cacheIndex = 3303; + reference = new EpsgCoordinateReferenceRecord(6449, (EpsgCoordinateSystemKind)2, 2470); + return true; + case 6450: + cacheIndex = 3304; + reference = new EpsgCoordinateReferenceRecord(6450, (EpsgCoordinateSystemKind)2, 2471); + return true; + case 6451: + cacheIndex = 3305; + reference = new EpsgCoordinateReferenceRecord(6451, (EpsgCoordinateSystemKind)2, 2472); + return true; + case 6452: + cacheIndex = 3306; + reference = new EpsgCoordinateReferenceRecord(6452, (EpsgCoordinateSystemKind)2, 2473); + return true; + case 6453: + cacheIndex = 3307; + reference = new EpsgCoordinateReferenceRecord(6453, (EpsgCoordinateSystemKind)2, 2474); + return true; + case 6454: + cacheIndex = 3308; + reference = new EpsgCoordinateReferenceRecord(6454, (EpsgCoordinateSystemKind)2, 2475); + return true; + case 6455: + cacheIndex = 3309; + reference = new EpsgCoordinateReferenceRecord(6455, (EpsgCoordinateSystemKind)2, 2476); + return true; + case 6456: + cacheIndex = 3310; + reference = new EpsgCoordinateReferenceRecord(6456, (EpsgCoordinateSystemKind)2, 2477); + return true; + case 6457: + cacheIndex = 3311; + reference = new EpsgCoordinateReferenceRecord(6457, (EpsgCoordinateSystemKind)2, 2478); + return true; + case 6458: + cacheIndex = 3312; + reference = new EpsgCoordinateReferenceRecord(6458, (EpsgCoordinateSystemKind)2, 2479); + return true; + case 6459: + cacheIndex = 3313; + reference = new EpsgCoordinateReferenceRecord(6459, (EpsgCoordinateSystemKind)2, 2480); + return true; + case 6460: + cacheIndex = 3314; + reference = new EpsgCoordinateReferenceRecord(6460, (EpsgCoordinateSystemKind)2, 2481); + return true; + case 6461: + cacheIndex = 3315; + reference = new EpsgCoordinateReferenceRecord(6461, (EpsgCoordinateSystemKind)2, 2482); + return true; + case 6462: + cacheIndex = 3316; + reference = new EpsgCoordinateReferenceRecord(6462, (EpsgCoordinateSystemKind)2, 2483); + return true; + case 6463: + cacheIndex = 3317; + reference = new EpsgCoordinateReferenceRecord(6463, (EpsgCoordinateSystemKind)2, 2484); + return true; + case 6464: + cacheIndex = 3318; + reference = new EpsgCoordinateReferenceRecord(6464, (EpsgCoordinateSystemKind)2, 2485); + return true; + case 6465: + cacheIndex = 3319; + reference = new EpsgCoordinateReferenceRecord(6465, (EpsgCoordinateSystemKind)2, 2486); + return true; + case 6466: + cacheIndex = 3320; + reference = new EpsgCoordinateReferenceRecord(6466, (EpsgCoordinateSystemKind)2, 2487); + return true; + case 6467: + cacheIndex = 3321; + reference = new EpsgCoordinateReferenceRecord(6467, (EpsgCoordinateSystemKind)2, 2488); + return true; + case 6468: + cacheIndex = 3322; + reference = new EpsgCoordinateReferenceRecord(6468, (EpsgCoordinateSystemKind)2, 2489); + return true; + case 6469: + cacheIndex = 3323; + reference = new EpsgCoordinateReferenceRecord(6469, (EpsgCoordinateSystemKind)2, 2490); + return true; + case 6470: + cacheIndex = 3324; + reference = new EpsgCoordinateReferenceRecord(6470, (EpsgCoordinateSystemKind)2, 2491); + return true; + case 6471: + cacheIndex = 3325; + reference = new EpsgCoordinateReferenceRecord(6471, (EpsgCoordinateSystemKind)2, 2492); + return true; + case 6472: + cacheIndex = 3326; + reference = new EpsgCoordinateReferenceRecord(6472, (EpsgCoordinateSystemKind)2, 2493); + return true; + case 6473: + cacheIndex = 3327; + reference = new EpsgCoordinateReferenceRecord(6473, (EpsgCoordinateSystemKind)2, 2494); + return true; + case 6474: + cacheIndex = 3328; + reference = new EpsgCoordinateReferenceRecord(6474, (EpsgCoordinateSystemKind)2, 2495); + return true; + case 6475: + cacheIndex = 3329; + reference = new EpsgCoordinateReferenceRecord(6475, (EpsgCoordinateSystemKind)2, 2496); + return true; + case 6476: + cacheIndex = 3330; + reference = new EpsgCoordinateReferenceRecord(6476, (EpsgCoordinateSystemKind)2, 2497); + return true; + case 6477: + cacheIndex = 3331; + reference = new EpsgCoordinateReferenceRecord(6477, (EpsgCoordinateSystemKind)2, 2498); + return true; + case 6478: + cacheIndex = 3332; + reference = new EpsgCoordinateReferenceRecord(6478, (EpsgCoordinateSystemKind)2, 2499); + return true; + case 6479: + cacheIndex = 3333; + reference = new EpsgCoordinateReferenceRecord(6479, (EpsgCoordinateSystemKind)2, 2500); + return true; + case 6480: + cacheIndex = 3334; + reference = new EpsgCoordinateReferenceRecord(6480, (EpsgCoordinateSystemKind)2, 2501); + return true; + case 6481: + cacheIndex = 3335; + reference = new EpsgCoordinateReferenceRecord(6481, (EpsgCoordinateSystemKind)2, 2502); + return true; + case 6482: + cacheIndex = 3336; + reference = new EpsgCoordinateReferenceRecord(6482, (EpsgCoordinateSystemKind)2, 2503); + return true; + case 6483: + cacheIndex = 3337; + reference = new EpsgCoordinateReferenceRecord(6483, (EpsgCoordinateSystemKind)2, 2504); + return true; + case 6484: + cacheIndex = 3338; + reference = new EpsgCoordinateReferenceRecord(6484, (EpsgCoordinateSystemKind)2, 2505); + return true; + case 6485: + cacheIndex = 3339; + reference = new EpsgCoordinateReferenceRecord(6485, (EpsgCoordinateSystemKind)2, 2506); + return true; + case 6486: + cacheIndex = 3340; + reference = new EpsgCoordinateReferenceRecord(6486, (EpsgCoordinateSystemKind)2, 2507); + return true; + case 6487: + cacheIndex = 3341; + reference = new EpsgCoordinateReferenceRecord(6487, (EpsgCoordinateSystemKind)2, 2508); + return true; + case 6488: + cacheIndex = 3342; + reference = new EpsgCoordinateReferenceRecord(6488, (EpsgCoordinateSystemKind)2, 2509); + return true; + case 6489: + cacheIndex = 3343; + reference = new EpsgCoordinateReferenceRecord(6489, (EpsgCoordinateSystemKind)2, 2510); + return true; + case 6490: + cacheIndex = 3344; + reference = new EpsgCoordinateReferenceRecord(6490, (EpsgCoordinateSystemKind)2, 2511); + return true; + case 6491: + cacheIndex = 3345; + reference = new EpsgCoordinateReferenceRecord(6491, (EpsgCoordinateSystemKind)2, 2512); + return true; + case 6492: + cacheIndex = 3346; + reference = new EpsgCoordinateReferenceRecord(6492, (EpsgCoordinateSystemKind)2, 2513); + return true; + case 6493: + cacheIndex = 3347; + reference = new EpsgCoordinateReferenceRecord(6493, (EpsgCoordinateSystemKind)2, 2514); + return true; + case 6494: + cacheIndex = 3348; + reference = new EpsgCoordinateReferenceRecord(6494, (EpsgCoordinateSystemKind)2, 2515); + return true; + case 6495: + cacheIndex = 3349; + reference = new EpsgCoordinateReferenceRecord(6495, (EpsgCoordinateSystemKind)2, 2516); + return true; + case 6496: + cacheIndex = 3350; + reference = new EpsgCoordinateReferenceRecord(6496, (EpsgCoordinateSystemKind)2, 2517); + return true; + case 6497: + cacheIndex = 3351; + reference = new EpsgCoordinateReferenceRecord(6497, (EpsgCoordinateSystemKind)2, 2518); + return true; + case 6498: + cacheIndex = 3352; + reference = new EpsgCoordinateReferenceRecord(6498, (EpsgCoordinateSystemKind)2, 2519); + return true; + case 6499: + cacheIndex = 3353; + reference = new EpsgCoordinateReferenceRecord(6499, (EpsgCoordinateSystemKind)2, 2520); + return true; + case 6500: + cacheIndex = 3354; + reference = new EpsgCoordinateReferenceRecord(6500, (EpsgCoordinateSystemKind)2, 2521); + return true; + case 6501: + cacheIndex = 3355; + reference = new EpsgCoordinateReferenceRecord(6501, (EpsgCoordinateSystemKind)2, 2522); + return true; + case 6502: + cacheIndex = 3356; + reference = new EpsgCoordinateReferenceRecord(6502, (EpsgCoordinateSystemKind)2, 2523); + return true; + case 6503: + cacheIndex = 3357; + reference = new EpsgCoordinateReferenceRecord(6503, (EpsgCoordinateSystemKind)2, 2524); + return true; + case 6504: + cacheIndex = 3358; + reference = new EpsgCoordinateReferenceRecord(6504, (EpsgCoordinateSystemKind)2, 2525); + return true; + case 6505: + cacheIndex = 3359; + reference = new EpsgCoordinateReferenceRecord(6505, (EpsgCoordinateSystemKind)2, 2526); + return true; + case 6506: + cacheIndex = 3360; + reference = new EpsgCoordinateReferenceRecord(6506, (EpsgCoordinateSystemKind)2, 2527); + return true; + case 6507: + cacheIndex = 3361; + reference = new EpsgCoordinateReferenceRecord(6507, (EpsgCoordinateSystemKind)2, 2528); + return true; + case 6508: + cacheIndex = 3362; + reference = new EpsgCoordinateReferenceRecord(6508, (EpsgCoordinateSystemKind)2, 2529); + return true; + case 6509: + cacheIndex = 3363; + reference = new EpsgCoordinateReferenceRecord(6509, (EpsgCoordinateSystemKind)2, 2530); + return true; + case 6510: + cacheIndex = 3364; + reference = new EpsgCoordinateReferenceRecord(6510, (EpsgCoordinateSystemKind)2, 2531); + return true; + case 6511: + cacheIndex = 3365; + reference = new EpsgCoordinateReferenceRecord(6511, (EpsgCoordinateSystemKind)2, 2532); + return true; + case 6512: + cacheIndex = 3366; + reference = new EpsgCoordinateReferenceRecord(6512, (EpsgCoordinateSystemKind)2, 2533); + return true; + case 6513: + cacheIndex = 3367; + reference = new EpsgCoordinateReferenceRecord(6513, (EpsgCoordinateSystemKind)2, 2534); + return true; + case 6514: + cacheIndex = 3368; + reference = new EpsgCoordinateReferenceRecord(6514, (EpsgCoordinateSystemKind)2, 2535); + return true; + case 6515: + cacheIndex = 3369; + reference = new EpsgCoordinateReferenceRecord(6515, (EpsgCoordinateSystemKind)2, 2536); + return true; + case 6516: + cacheIndex = 3370; + reference = new EpsgCoordinateReferenceRecord(6516, (EpsgCoordinateSystemKind)2, 2537); + return true; + case 6518: + cacheIndex = 3371; + reference = new EpsgCoordinateReferenceRecord(6518, (EpsgCoordinateSystemKind)2, 2538); + return true; + case 6519: + cacheIndex = 3372; + reference = new EpsgCoordinateReferenceRecord(6519, (EpsgCoordinateSystemKind)2, 2539); + return true; + case 6520: + cacheIndex = 3373; + reference = new EpsgCoordinateReferenceRecord(6520, (EpsgCoordinateSystemKind)2, 2540); + return true; + case 6521: + cacheIndex = 3374; + reference = new EpsgCoordinateReferenceRecord(6521, (EpsgCoordinateSystemKind)2, 2541); + return true; + case 6522: + cacheIndex = 3375; + reference = new EpsgCoordinateReferenceRecord(6522, (EpsgCoordinateSystemKind)2, 2542); + return true; + case 6523: + cacheIndex = 3376; + reference = new EpsgCoordinateReferenceRecord(6523, (EpsgCoordinateSystemKind)2, 2543); + return true; + case 6524: + cacheIndex = 3377; + reference = new EpsgCoordinateReferenceRecord(6524, (EpsgCoordinateSystemKind)2, 2544); + return true; + case 6525: + cacheIndex = 3378; + reference = new EpsgCoordinateReferenceRecord(6525, (EpsgCoordinateSystemKind)2, 2545); + return true; + case 6526: + cacheIndex = 3379; + reference = new EpsgCoordinateReferenceRecord(6526, (EpsgCoordinateSystemKind)2, 2546); + return true; + case 6527: + cacheIndex = 3380; + reference = new EpsgCoordinateReferenceRecord(6527, (EpsgCoordinateSystemKind)2, 2547); + return true; + case 6528: + cacheIndex = 3381; + reference = new EpsgCoordinateReferenceRecord(6528, (EpsgCoordinateSystemKind)2, 2548); + return true; + case 6529: + cacheIndex = 3382; + reference = new EpsgCoordinateReferenceRecord(6529, (EpsgCoordinateSystemKind)2, 2549); + return true; + case 6530: + cacheIndex = 3383; + reference = new EpsgCoordinateReferenceRecord(6530, (EpsgCoordinateSystemKind)2, 2550); + return true; + case 6531: + cacheIndex = 3384; + reference = new EpsgCoordinateReferenceRecord(6531, (EpsgCoordinateSystemKind)2, 2551); + return true; + case 6532: + cacheIndex = 3385; + reference = new EpsgCoordinateReferenceRecord(6532, (EpsgCoordinateSystemKind)2, 2552); + return true; + case 6533: + cacheIndex = 3386; + reference = new EpsgCoordinateReferenceRecord(6533, (EpsgCoordinateSystemKind)2, 2553); + return true; + case 6534: + cacheIndex = 3387; + reference = new EpsgCoordinateReferenceRecord(6534, (EpsgCoordinateSystemKind)2, 2554); + return true; + case 6535: + cacheIndex = 3388; + reference = new EpsgCoordinateReferenceRecord(6535, (EpsgCoordinateSystemKind)2, 2555); + return true; + case 6536: + cacheIndex = 3389; + reference = new EpsgCoordinateReferenceRecord(6536, (EpsgCoordinateSystemKind)2, 2556); + return true; + case 6537: + cacheIndex = 3390; + reference = new EpsgCoordinateReferenceRecord(6537, (EpsgCoordinateSystemKind)2, 2557); + return true; + case 6538: + cacheIndex = 3391; + reference = new EpsgCoordinateReferenceRecord(6538, (EpsgCoordinateSystemKind)2, 2558); + return true; + case 6539: + cacheIndex = 3392; + reference = new EpsgCoordinateReferenceRecord(6539, (EpsgCoordinateSystemKind)2, 2559); + return true; + case 6540: + cacheIndex = 3393; + reference = new EpsgCoordinateReferenceRecord(6540, (EpsgCoordinateSystemKind)2, 2560); + return true; + case 6541: + cacheIndex = 3394; + reference = new EpsgCoordinateReferenceRecord(6541, (EpsgCoordinateSystemKind)2, 2561); + return true; + case 6542: + cacheIndex = 3395; + reference = new EpsgCoordinateReferenceRecord(6542, (EpsgCoordinateSystemKind)2, 2562); + return true; + case 6543: + cacheIndex = 3396; + reference = new EpsgCoordinateReferenceRecord(6543, (EpsgCoordinateSystemKind)2, 2563); + return true; + case 6544: + cacheIndex = 3397; + reference = new EpsgCoordinateReferenceRecord(6544, (EpsgCoordinateSystemKind)2, 2564); + return true; + case 6545: + cacheIndex = 3398; + reference = new EpsgCoordinateReferenceRecord(6545, (EpsgCoordinateSystemKind)2, 2565); + return true; + case 6546: + cacheIndex = 3399; + reference = new EpsgCoordinateReferenceRecord(6546, (EpsgCoordinateSystemKind)2, 2566); + return true; + case 6547: + cacheIndex = 3400; + reference = new EpsgCoordinateReferenceRecord(6547, (EpsgCoordinateSystemKind)2, 2567); + return true; + case 6548: + cacheIndex = 3401; + reference = new EpsgCoordinateReferenceRecord(6548, (EpsgCoordinateSystemKind)2, 2568); + return true; + case 6549: + cacheIndex = 3402; + reference = new EpsgCoordinateReferenceRecord(6549, (EpsgCoordinateSystemKind)2, 2569); + return true; + case 6550: + cacheIndex = 3403; + reference = new EpsgCoordinateReferenceRecord(6550, (EpsgCoordinateSystemKind)2, 2570); + return true; + case 6551: + cacheIndex = 3404; + reference = new EpsgCoordinateReferenceRecord(6551, (EpsgCoordinateSystemKind)2, 2571); + return true; + case 6552: + cacheIndex = 3405; + reference = new EpsgCoordinateReferenceRecord(6552, (EpsgCoordinateSystemKind)2, 2572); + return true; + case 6553: + cacheIndex = 3406; + reference = new EpsgCoordinateReferenceRecord(6553, (EpsgCoordinateSystemKind)2, 2573); + return true; + case 6554: + cacheIndex = 3407; + reference = new EpsgCoordinateReferenceRecord(6554, (EpsgCoordinateSystemKind)2, 2574); + return true; + case 6555: + cacheIndex = 3408; + reference = new EpsgCoordinateReferenceRecord(6555, (EpsgCoordinateSystemKind)2, 2575); + return true; + case 6556: + cacheIndex = 3409; + reference = new EpsgCoordinateReferenceRecord(6556, (EpsgCoordinateSystemKind)2, 2576); + return true; + case 6557: + cacheIndex = 3410; + reference = new EpsgCoordinateReferenceRecord(6557, (EpsgCoordinateSystemKind)2, 2577); + return true; + case 6558: + cacheIndex = 3411; + reference = new EpsgCoordinateReferenceRecord(6558, (EpsgCoordinateSystemKind)2, 2578); + return true; + case 6559: + cacheIndex = 3412; + reference = new EpsgCoordinateReferenceRecord(6559, (EpsgCoordinateSystemKind)2, 2579); + return true; + case 6560: + cacheIndex = 3413; + reference = new EpsgCoordinateReferenceRecord(6560, (EpsgCoordinateSystemKind)2, 2580); + return true; + case 6561: + cacheIndex = 3414; + reference = new EpsgCoordinateReferenceRecord(6561, (EpsgCoordinateSystemKind)2, 2581); + return true; + case 6562: + cacheIndex = 3415; + reference = new EpsgCoordinateReferenceRecord(6562, (EpsgCoordinateSystemKind)2, 2582); + return true; + case 6563: + cacheIndex = 3416; + reference = new EpsgCoordinateReferenceRecord(6563, (EpsgCoordinateSystemKind)2, 2583); + return true; + case 6564: + cacheIndex = 3417; + reference = new EpsgCoordinateReferenceRecord(6564, (EpsgCoordinateSystemKind)2, 2584); + return true; + case 6565: + cacheIndex = 3418; + reference = new EpsgCoordinateReferenceRecord(6565, (EpsgCoordinateSystemKind)2, 2585); + return true; + case 6566: + cacheIndex = 3419; + reference = new EpsgCoordinateReferenceRecord(6566, (EpsgCoordinateSystemKind)2, 2586); + return true; + case 6567: + cacheIndex = 3420; + reference = new EpsgCoordinateReferenceRecord(6567, (EpsgCoordinateSystemKind)2, 2587); + return true; + case 6568: + cacheIndex = 3421; + reference = new EpsgCoordinateReferenceRecord(6568, (EpsgCoordinateSystemKind)2, 2588); + return true; + case 6569: + cacheIndex = 3422; + reference = new EpsgCoordinateReferenceRecord(6569, (EpsgCoordinateSystemKind)2, 2589); + return true; + case 6570: + cacheIndex = 3423; + reference = new EpsgCoordinateReferenceRecord(6570, (EpsgCoordinateSystemKind)2, 2590); + return true; + case 6571: + cacheIndex = 3424; + reference = new EpsgCoordinateReferenceRecord(6571, (EpsgCoordinateSystemKind)2, 2591); + return true; + case 6572: + cacheIndex = 3425; + reference = new EpsgCoordinateReferenceRecord(6572, (EpsgCoordinateSystemKind)2, 2592); + return true; + case 6573: + cacheIndex = 3426; + reference = new EpsgCoordinateReferenceRecord(6573, (EpsgCoordinateSystemKind)2, 2593); + return true; + case 6574: + cacheIndex = 3427; + reference = new EpsgCoordinateReferenceRecord(6574, (EpsgCoordinateSystemKind)2, 2594); + return true; + case 6575: + cacheIndex = 3428; + reference = new EpsgCoordinateReferenceRecord(6575, (EpsgCoordinateSystemKind)2, 2595); + return true; + case 6576: + cacheIndex = 3429; + reference = new EpsgCoordinateReferenceRecord(6576, (EpsgCoordinateSystemKind)2, 2596); + return true; + case 6577: + cacheIndex = 3430; + reference = new EpsgCoordinateReferenceRecord(6577, (EpsgCoordinateSystemKind)2, 2597); + return true; + case 6578: + cacheIndex = 3431; + reference = new EpsgCoordinateReferenceRecord(6578, (EpsgCoordinateSystemKind)2, 2598); + return true; + case 6579: + cacheIndex = 3432; + reference = new EpsgCoordinateReferenceRecord(6579, (EpsgCoordinateSystemKind)2, 2599); + return true; + case 6580: + cacheIndex = 3433; + reference = new EpsgCoordinateReferenceRecord(6580, (EpsgCoordinateSystemKind)2, 2600); + return true; + case 6581: + cacheIndex = 3434; + reference = new EpsgCoordinateReferenceRecord(6581, (EpsgCoordinateSystemKind)2, 2601); + return true; + case 6582: + cacheIndex = 3435; + reference = new EpsgCoordinateReferenceRecord(6582, (EpsgCoordinateSystemKind)2, 2602); + return true; + case 6583: + cacheIndex = 3436; + reference = new EpsgCoordinateReferenceRecord(6583, (EpsgCoordinateSystemKind)2, 2603); + return true; + case 6584: + cacheIndex = 3437; + reference = new EpsgCoordinateReferenceRecord(6584, (EpsgCoordinateSystemKind)2, 2604); + return true; + case 6585: + cacheIndex = 3438; + reference = new EpsgCoordinateReferenceRecord(6585, (EpsgCoordinateSystemKind)2, 2605); + return true; + case 6586: + cacheIndex = 3439; + reference = new EpsgCoordinateReferenceRecord(6586, (EpsgCoordinateSystemKind)2, 2606); + return true; + case 6587: + cacheIndex = 3440; + reference = new EpsgCoordinateReferenceRecord(6587, (EpsgCoordinateSystemKind)2, 2607); + return true; + case 6588: + cacheIndex = 3441; + reference = new EpsgCoordinateReferenceRecord(6588, (EpsgCoordinateSystemKind)2, 2608); + return true; + case 6589: + cacheIndex = 3442; + reference = new EpsgCoordinateReferenceRecord(6589, (EpsgCoordinateSystemKind)2, 2609); + return true; + case 6590: + cacheIndex = 3443; + reference = new EpsgCoordinateReferenceRecord(6590, (EpsgCoordinateSystemKind)2, 2610); + return true; + case 6591: + cacheIndex = 3444; + reference = new EpsgCoordinateReferenceRecord(6591, (EpsgCoordinateSystemKind)2, 2611); + return true; + case 6592: + cacheIndex = 3445; + reference = new EpsgCoordinateReferenceRecord(6592, (EpsgCoordinateSystemKind)2, 2612); + return true; + case 6593: + cacheIndex = 3446; + reference = new EpsgCoordinateReferenceRecord(6593, (EpsgCoordinateSystemKind)2, 2613); + return true; + case 6594: + cacheIndex = 3447; + reference = new EpsgCoordinateReferenceRecord(6594, (EpsgCoordinateSystemKind)2, 2614); + return true; + case 6595: + cacheIndex = 3448; + reference = new EpsgCoordinateReferenceRecord(6595, (EpsgCoordinateSystemKind)2, 2615); + return true; + case 6596: + cacheIndex = 3449; + reference = new EpsgCoordinateReferenceRecord(6596, (EpsgCoordinateSystemKind)2, 2616); + return true; + case 6597: + cacheIndex = 3450; + reference = new EpsgCoordinateReferenceRecord(6597, (EpsgCoordinateSystemKind)2, 2617); + return true; + case 6598: + cacheIndex = 3451; + reference = new EpsgCoordinateReferenceRecord(6598, (EpsgCoordinateSystemKind)2, 2618); + return true; + case 6599: + cacheIndex = 3452; + reference = new EpsgCoordinateReferenceRecord(6599, (EpsgCoordinateSystemKind)2, 2619); + return true; + case 6600: + cacheIndex = 3453; + reference = new EpsgCoordinateReferenceRecord(6600, (EpsgCoordinateSystemKind)2, 2620); + return true; + case 6601: + cacheIndex = 3454; + reference = new EpsgCoordinateReferenceRecord(6601, (EpsgCoordinateSystemKind)2, 2621); + return true; + case 6602: + cacheIndex = 3455; + reference = new EpsgCoordinateReferenceRecord(6602, (EpsgCoordinateSystemKind)2, 2622); + return true; + case 6603: + cacheIndex = 3456; + reference = new EpsgCoordinateReferenceRecord(6603, (EpsgCoordinateSystemKind)2, 2623); + return true; + case 6605: + cacheIndex = 3457; + reference = new EpsgCoordinateReferenceRecord(6605, (EpsgCoordinateSystemKind)2, 2624); + return true; + case 6606: + cacheIndex = 3458; + reference = new EpsgCoordinateReferenceRecord(6606, (EpsgCoordinateSystemKind)2, 2625); + return true; + case 6607: + cacheIndex = 3459; + reference = new EpsgCoordinateReferenceRecord(6607, (EpsgCoordinateSystemKind)2, 2626); + return true; + case 6608: + cacheIndex = 3460; + reference = new EpsgCoordinateReferenceRecord(6608, (EpsgCoordinateSystemKind)2, 2627); + return true; + case 6609: + cacheIndex = 3461; + reference = new EpsgCoordinateReferenceRecord(6609, (EpsgCoordinateSystemKind)2, 2628); + return true; + case 6610: + cacheIndex = 3462; + reference = new EpsgCoordinateReferenceRecord(6610, (EpsgCoordinateSystemKind)2, 2629); + return true; + case 6611: + cacheIndex = 3463; + reference = new EpsgCoordinateReferenceRecord(6611, (EpsgCoordinateSystemKind)2, 2630); + return true; + case 6612: + cacheIndex = 3464; + reference = new EpsgCoordinateReferenceRecord(6612, (EpsgCoordinateSystemKind)2, 2631); + return true; + case 6613: + cacheIndex = 3465; + reference = new EpsgCoordinateReferenceRecord(6613, (EpsgCoordinateSystemKind)2, 2632); + return true; + case 6614: + cacheIndex = 3466; + reference = new EpsgCoordinateReferenceRecord(6614, (EpsgCoordinateSystemKind)2, 2633); + return true; + case 6615: + cacheIndex = 3467; + reference = new EpsgCoordinateReferenceRecord(6615, (EpsgCoordinateSystemKind)2, 2634); + return true; + case 6616: + cacheIndex = 3468; + reference = new EpsgCoordinateReferenceRecord(6616, (EpsgCoordinateSystemKind)2, 2635); + return true; + case 6617: + cacheIndex = 3469; + reference = new EpsgCoordinateReferenceRecord(6617, (EpsgCoordinateSystemKind)2, 2636); + return true; + case 6618: + cacheIndex = 3470; + reference = new EpsgCoordinateReferenceRecord(6618, (EpsgCoordinateSystemKind)2, 2637); + return true; + case 6619: + cacheIndex = 3471; + reference = new EpsgCoordinateReferenceRecord(6619, (EpsgCoordinateSystemKind)2, 2638); + return true; + case 6620: + cacheIndex = 3472; + reference = new EpsgCoordinateReferenceRecord(6620, (EpsgCoordinateSystemKind)2, 2639); + return true; + case 6621: + cacheIndex = 3473; + reference = new EpsgCoordinateReferenceRecord(6621, (EpsgCoordinateSystemKind)2, 2640); + return true; + case 6622: + cacheIndex = 3474; + reference = new EpsgCoordinateReferenceRecord(6622, (EpsgCoordinateSystemKind)2, 2641); + return true; + case 6623: + cacheIndex = 3475; + reference = new EpsgCoordinateReferenceRecord(6623, (EpsgCoordinateSystemKind)2, 2642); + return true; + case 6624: + cacheIndex = 3476; + reference = new EpsgCoordinateReferenceRecord(6624, (EpsgCoordinateSystemKind)2, 2643); + return true; + case 6625: + cacheIndex = 3477; + reference = new EpsgCoordinateReferenceRecord(6625, (EpsgCoordinateSystemKind)2, 2644); + return true; + case 6626: + cacheIndex = 3478; + reference = new EpsgCoordinateReferenceRecord(6626, (EpsgCoordinateSystemKind)2, 2645); + return true; + case 6627: + cacheIndex = 3479; + reference = new EpsgCoordinateReferenceRecord(6627, (EpsgCoordinateSystemKind)2, 2646); + return true; + case 6628: + cacheIndex = 3480; + reference = new EpsgCoordinateReferenceRecord(6628, (EpsgCoordinateSystemKind)2, 2647); + return true; + case 6629: + cacheIndex = 3481; + reference = new EpsgCoordinateReferenceRecord(6629, (EpsgCoordinateSystemKind)2, 2648); + return true; + case 6630: + cacheIndex = 3482; + reference = new EpsgCoordinateReferenceRecord(6630, (EpsgCoordinateSystemKind)2, 2649); + return true; + case 6631: + cacheIndex = 3483; + reference = new EpsgCoordinateReferenceRecord(6631, (EpsgCoordinateSystemKind)2, 2650); + return true; + case 6632: + cacheIndex = 3484; + reference = new EpsgCoordinateReferenceRecord(6632, (EpsgCoordinateSystemKind)2, 2651); + return true; + case 6633: + cacheIndex = 3485; + reference = new EpsgCoordinateReferenceRecord(6633, (EpsgCoordinateSystemKind)2, 2652); + return true; + case 6634: + cacheIndex = 3486; + reference = new EpsgCoordinateReferenceRecord(6634, (EpsgCoordinateSystemKind)2, 2653); + return true; + case 6635: + cacheIndex = 3487; + reference = new EpsgCoordinateReferenceRecord(6635, (EpsgCoordinateSystemKind)2, 2654); + return true; + case 6636: + cacheIndex = 3488; + reference = new EpsgCoordinateReferenceRecord(6636, (EpsgCoordinateSystemKind)2, 2655); + return true; + case 6637: + cacheIndex = 3489; + reference = new EpsgCoordinateReferenceRecord(6637, (EpsgCoordinateSystemKind)2, 2656); + return true; + case 6638: + cacheIndex = 3490; + reference = new EpsgCoordinateReferenceRecord(6638, (EpsgCoordinateSystemKind)3, 152); + return true; + case 6639: + cacheIndex = 3491; + reference = new EpsgCoordinateReferenceRecord(6639, (EpsgCoordinateSystemKind)3, 153); + return true; + case 6640: + cacheIndex = 3492; + reference = new EpsgCoordinateReferenceRecord(6640, (EpsgCoordinateSystemKind)3, 154); + return true; + case 6641: + cacheIndex = 3493; + reference = new EpsgCoordinateReferenceRecord(6641, (EpsgCoordinateSystemKind)3, 155); + return true; + case 6642: + cacheIndex = 3494; + reference = new EpsgCoordinateReferenceRecord(6642, (EpsgCoordinateSystemKind)3, 156); + return true; + case 6643: + cacheIndex = 3495; + reference = new EpsgCoordinateReferenceRecord(6643, (EpsgCoordinateSystemKind)3, 157); + return true; + case 6644: + cacheIndex = 3496; + reference = new EpsgCoordinateReferenceRecord(6644, (EpsgCoordinateSystemKind)3, 158); + return true; + case 6646: + cacheIndex = 3497; + reference = new EpsgCoordinateReferenceRecord(6646, (EpsgCoordinateSystemKind)2, 2657); + return true; + case 6647: + cacheIndex = 3498; + reference = new EpsgCoordinateReferenceRecord(6647, (EpsgCoordinateSystemKind)3, 159); + return true; + case 6649: + cacheIndex = 3499; + reference = new EpsgCoordinateReferenceRecord(6649, (EpsgCoordinateSystemKind)4, 97); + return true; + case 6650: + cacheIndex = 3500; + reference = new EpsgCoordinateReferenceRecord(6650, (EpsgCoordinateSystemKind)4, 98); + return true; + case 6651: + cacheIndex = 3501; + reference = new EpsgCoordinateReferenceRecord(6651, (EpsgCoordinateSystemKind)4, 99); + return true; + case 6652: + cacheIndex = 3502; + reference = new EpsgCoordinateReferenceRecord(6652, (EpsgCoordinateSystemKind)4, 100); + return true; + case 6653: + cacheIndex = 3503; + reference = new EpsgCoordinateReferenceRecord(6653, (EpsgCoordinateSystemKind)4, 101); + return true; + case 6654: + cacheIndex = 3504; + reference = new EpsgCoordinateReferenceRecord(6654, (EpsgCoordinateSystemKind)4, 102); + return true; + case 6655: + cacheIndex = 3505; + reference = new EpsgCoordinateReferenceRecord(6655, (EpsgCoordinateSystemKind)4, 103); + return true; + case 6656: + cacheIndex = 3506; + reference = new EpsgCoordinateReferenceRecord(6656, (EpsgCoordinateSystemKind)4, 104); + return true; + case 6657: + cacheIndex = 3507; + reference = new EpsgCoordinateReferenceRecord(6657, (EpsgCoordinateSystemKind)4, 105); + return true; + case 6658: + cacheIndex = 3508; + reference = new EpsgCoordinateReferenceRecord(6658, (EpsgCoordinateSystemKind)4, 106); + return true; + case 6659: + cacheIndex = 3509; + reference = new EpsgCoordinateReferenceRecord(6659, (EpsgCoordinateSystemKind)4, 107); + return true; + case 6660: + cacheIndex = 3510; + reference = new EpsgCoordinateReferenceRecord(6660, (EpsgCoordinateSystemKind)4, 108); + return true; + case 6661: + cacheIndex = 3511; + reference = new EpsgCoordinateReferenceRecord(6661, (EpsgCoordinateSystemKind)4, 109); + return true; + case 6662: + cacheIndex = 3512; + reference = new EpsgCoordinateReferenceRecord(6662, (EpsgCoordinateSystemKind)4, 110); + return true; + case 6663: + cacheIndex = 3513; + reference = new EpsgCoordinateReferenceRecord(6663, (EpsgCoordinateSystemKind)4, 111); + return true; + case 6664: + cacheIndex = 3514; + reference = new EpsgCoordinateReferenceRecord(6664, (EpsgCoordinateSystemKind)4, 112); + return true; + case 6665: + cacheIndex = 3515; + reference = new EpsgCoordinateReferenceRecord(6665, (EpsgCoordinateSystemKind)4, 113); + return true; + case 6666: + cacheIndex = 3516; + reference = new EpsgCoordinateReferenceRecord(6666, (EpsgCoordinateSystemKind)1, 98); + return true; + case 6667: + cacheIndex = 3517; + reference = new EpsgCoordinateReferenceRecord(6667, (EpsgCoordinateSystemKind)0, 486); + return true; + case 6668: + cacheIndex = 3518; + reference = new EpsgCoordinateReferenceRecord(6668, (EpsgCoordinateSystemKind)0, 487); + return true; + case 6669: + cacheIndex = 3519; + reference = new EpsgCoordinateReferenceRecord(6669, (EpsgCoordinateSystemKind)2, 2658); + return true; + case 6670: + cacheIndex = 3520; + reference = new EpsgCoordinateReferenceRecord(6670, (EpsgCoordinateSystemKind)2, 2659); + return true; + case 6671: + cacheIndex = 3521; + reference = new EpsgCoordinateReferenceRecord(6671, (EpsgCoordinateSystemKind)2, 2660); + return true; + case 6672: + cacheIndex = 3522; + reference = new EpsgCoordinateReferenceRecord(6672, (EpsgCoordinateSystemKind)2, 2661); + return true; + case 6673: + cacheIndex = 3523; + reference = new EpsgCoordinateReferenceRecord(6673, (EpsgCoordinateSystemKind)2, 2662); + return true; + case 6674: + cacheIndex = 3524; + reference = new EpsgCoordinateReferenceRecord(6674, (EpsgCoordinateSystemKind)2, 2663); + return true; + case 6675: + cacheIndex = 3525; + reference = new EpsgCoordinateReferenceRecord(6675, (EpsgCoordinateSystemKind)2, 2664); + return true; + case 6676: + cacheIndex = 3526; + reference = new EpsgCoordinateReferenceRecord(6676, (EpsgCoordinateSystemKind)2, 2665); + return true; + case 6677: + cacheIndex = 3527; + reference = new EpsgCoordinateReferenceRecord(6677, (EpsgCoordinateSystemKind)2, 2666); + return true; + case 6678: + cacheIndex = 3528; + reference = new EpsgCoordinateReferenceRecord(6678, (EpsgCoordinateSystemKind)2, 2667); + return true; + case 6679: + cacheIndex = 3529; + reference = new EpsgCoordinateReferenceRecord(6679, (EpsgCoordinateSystemKind)2, 2668); + return true; + case 6680: + cacheIndex = 3530; + reference = new EpsgCoordinateReferenceRecord(6680, (EpsgCoordinateSystemKind)2, 2669); + return true; + case 6681: + cacheIndex = 3531; + reference = new EpsgCoordinateReferenceRecord(6681, (EpsgCoordinateSystemKind)2, 2670); + return true; + case 6682: + cacheIndex = 3532; + reference = new EpsgCoordinateReferenceRecord(6682, (EpsgCoordinateSystemKind)2, 2671); + return true; + case 6683: + cacheIndex = 3533; + reference = new EpsgCoordinateReferenceRecord(6683, (EpsgCoordinateSystemKind)2, 2672); + return true; + case 6684: + cacheIndex = 3534; + reference = new EpsgCoordinateReferenceRecord(6684, (EpsgCoordinateSystemKind)2, 2673); + return true; + case 6685: + cacheIndex = 3535; + reference = new EpsgCoordinateReferenceRecord(6685, (EpsgCoordinateSystemKind)2, 2674); + return true; + case 6686: + cacheIndex = 3536; + reference = new EpsgCoordinateReferenceRecord(6686, (EpsgCoordinateSystemKind)2, 2675); + return true; + case 6687: + cacheIndex = 3537; + reference = new EpsgCoordinateReferenceRecord(6687, (EpsgCoordinateSystemKind)2, 2676); + return true; + case 6688: + cacheIndex = 3538; + reference = new EpsgCoordinateReferenceRecord(6688, (EpsgCoordinateSystemKind)2, 2677); + return true; + case 6689: + cacheIndex = 3539; + reference = new EpsgCoordinateReferenceRecord(6689, (EpsgCoordinateSystemKind)2, 2678); + return true; + case 6690: + cacheIndex = 3540; + reference = new EpsgCoordinateReferenceRecord(6690, (EpsgCoordinateSystemKind)2, 2679); + return true; + case 6691: + cacheIndex = 3541; + reference = new EpsgCoordinateReferenceRecord(6691, (EpsgCoordinateSystemKind)2, 2680); + return true; + case 6692: + cacheIndex = 3542; + reference = new EpsgCoordinateReferenceRecord(6692, (EpsgCoordinateSystemKind)2, 2681); + return true; + case 6693: + cacheIndex = 3543; + reference = new EpsgCoordinateReferenceRecord(6693, (EpsgCoordinateSystemKind)3, 160); + return true; + case 6694: + cacheIndex = 3544; + reference = new EpsgCoordinateReferenceRecord(6694, (EpsgCoordinateSystemKind)3, 161); + return true; + case 6695: + cacheIndex = 3545; + reference = new EpsgCoordinateReferenceRecord(6695, (EpsgCoordinateSystemKind)3, 162); + return true; + case 6696: + cacheIndex = 3546; + reference = new EpsgCoordinateReferenceRecord(6696, (EpsgCoordinateSystemKind)4, 114); + return true; + case 6697: + cacheIndex = 3547; + reference = new EpsgCoordinateReferenceRecord(6697, (EpsgCoordinateSystemKind)4, 115); + return true; + case 6700: + cacheIndex = 3548; + reference = new EpsgCoordinateReferenceRecord(6700, (EpsgCoordinateSystemKind)4, 116); + return true; + case 6703: + cacheIndex = 3549; + reference = new EpsgCoordinateReferenceRecord(6703, (EpsgCoordinateSystemKind)2, 2682); + return true; + case 6704: + cacheIndex = 3550; + reference = new EpsgCoordinateReferenceRecord(6704, (EpsgCoordinateSystemKind)1, 99); + return true; + case 6705: + cacheIndex = 3551; + reference = new EpsgCoordinateReferenceRecord(6705, (EpsgCoordinateSystemKind)0, 488); + return true; + case 6706: + cacheIndex = 3552; + reference = new EpsgCoordinateReferenceRecord(6706, (EpsgCoordinateSystemKind)0, 489); + return true; + case 6707: + cacheIndex = 3553; + reference = new EpsgCoordinateReferenceRecord(6707, (EpsgCoordinateSystemKind)2, 2683); + return true; + case 6708: + cacheIndex = 3554; + reference = new EpsgCoordinateReferenceRecord(6708, (EpsgCoordinateSystemKind)2, 2684); + return true; + case 6709: + cacheIndex = 3555; + reference = new EpsgCoordinateReferenceRecord(6709, (EpsgCoordinateSystemKind)2, 2685); + return true; + case 6720: + cacheIndex = 3556; + reference = new EpsgCoordinateReferenceRecord(6720, (EpsgCoordinateSystemKind)2, 2686); + return true; + case 6721: + cacheIndex = 3557; + reference = new EpsgCoordinateReferenceRecord(6721, (EpsgCoordinateSystemKind)2, 2687); + return true; + case 6722: + cacheIndex = 3558; + reference = new EpsgCoordinateReferenceRecord(6722, (EpsgCoordinateSystemKind)2, 2688); + return true; + case 6723: + cacheIndex = 3559; + reference = new EpsgCoordinateReferenceRecord(6723, (EpsgCoordinateSystemKind)2, 2689); + return true; + case 6736: + cacheIndex = 3560; + reference = new EpsgCoordinateReferenceRecord(6736, (EpsgCoordinateSystemKind)2, 2690); + return true; + case 6737: + cacheIndex = 3561; + reference = new EpsgCoordinateReferenceRecord(6737, (EpsgCoordinateSystemKind)2, 2691); + return true; + case 6738: + cacheIndex = 3562; + reference = new EpsgCoordinateReferenceRecord(6738, (EpsgCoordinateSystemKind)2, 2692); + return true; + case 6781: + cacheIndex = 3563; + reference = new EpsgCoordinateReferenceRecord(6781, (EpsgCoordinateSystemKind)1, 100); + return true; + case 6782: + cacheIndex = 3564; + reference = new EpsgCoordinateReferenceRecord(6782, (EpsgCoordinateSystemKind)0, 490); + return true; + case 6783: + cacheIndex = 3565; + reference = new EpsgCoordinateReferenceRecord(6783, (EpsgCoordinateSystemKind)0, 491); + return true; + case 6784: + cacheIndex = 3566; + reference = new EpsgCoordinateReferenceRecord(6784, (EpsgCoordinateSystemKind)2, 2693); + return true; + case 6785: + cacheIndex = 3567; + reference = new EpsgCoordinateReferenceRecord(6785, (EpsgCoordinateSystemKind)2, 2694); + return true; + case 6786: + cacheIndex = 3568; + reference = new EpsgCoordinateReferenceRecord(6786, (EpsgCoordinateSystemKind)2, 2695); + return true; + case 6787: + cacheIndex = 3569; + reference = new EpsgCoordinateReferenceRecord(6787, (EpsgCoordinateSystemKind)2, 2696); + return true; + case 6788: + cacheIndex = 3570; + reference = new EpsgCoordinateReferenceRecord(6788, (EpsgCoordinateSystemKind)2, 2697); + return true; + case 6789: + cacheIndex = 3571; + reference = new EpsgCoordinateReferenceRecord(6789, (EpsgCoordinateSystemKind)2, 2698); + return true; + case 6790: + cacheIndex = 3572; + reference = new EpsgCoordinateReferenceRecord(6790, (EpsgCoordinateSystemKind)2, 2699); + return true; + case 6791: + cacheIndex = 3573; + reference = new EpsgCoordinateReferenceRecord(6791, (EpsgCoordinateSystemKind)2, 2700); + return true; + case 6792: + cacheIndex = 3574; + reference = new EpsgCoordinateReferenceRecord(6792, (EpsgCoordinateSystemKind)2, 2701); + return true; + case 6793: + cacheIndex = 3575; + reference = new EpsgCoordinateReferenceRecord(6793, (EpsgCoordinateSystemKind)2, 2702); + return true; + case 6794: + cacheIndex = 3576; + reference = new EpsgCoordinateReferenceRecord(6794, (EpsgCoordinateSystemKind)2, 2703); + return true; + case 6795: + cacheIndex = 3577; + reference = new EpsgCoordinateReferenceRecord(6795, (EpsgCoordinateSystemKind)2, 2704); + return true; + case 6796: + cacheIndex = 3578; + reference = new EpsgCoordinateReferenceRecord(6796, (EpsgCoordinateSystemKind)2, 2705); + return true; + case 6797: + cacheIndex = 3579; + reference = new EpsgCoordinateReferenceRecord(6797, (EpsgCoordinateSystemKind)2, 2706); + return true; + case 6798: + cacheIndex = 3580; + reference = new EpsgCoordinateReferenceRecord(6798, (EpsgCoordinateSystemKind)2, 2707); + return true; + case 6799: + cacheIndex = 3581; + reference = new EpsgCoordinateReferenceRecord(6799, (EpsgCoordinateSystemKind)2, 2708); + return true; + case 6800: + cacheIndex = 3582; + reference = new EpsgCoordinateReferenceRecord(6800, (EpsgCoordinateSystemKind)2, 2709); + return true; + case 6801: + cacheIndex = 3583; + reference = new EpsgCoordinateReferenceRecord(6801, (EpsgCoordinateSystemKind)2, 2710); + return true; + case 6802: + cacheIndex = 3584; + reference = new EpsgCoordinateReferenceRecord(6802, (EpsgCoordinateSystemKind)2, 2711); + return true; + case 6803: + cacheIndex = 3585; + reference = new EpsgCoordinateReferenceRecord(6803, (EpsgCoordinateSystemKind)2, 2712); + return true; + case 6804: + cacheIndex = 3586; + reference = new EpsgCoordinateReferenceRecord(6804, (EpsgCoordinateSystemKind)2, 2713); + return true; + case 6805: + cacheIndex = 3587; + reference = new EpsgCoordinateReferenceRecord(6805, (EpsgCoordinateSystemKind)2, 2714); + return true; + case 6806: + cacheIndex = 3588; + reference = new EpsgCoordinateReferenceRecord(6806, (EpsgCoordinateSystemKind)2, 2715); + return true; + case 6807: + cacheIndex = 3589; + reference = new EpsgCoordinateReferenceRecord(6807, (EpsgCoordinateSystemKind)2, 2716); + return true; + case 6808: + cacheIndex = 3590; + reference = new EpsgCoordinateReferenceRecord(6808, (EpsgCoordinateSystemKind)2, 2717); + return true; + case 6809: + cacheIndex = 3591; + reference = new EpsgCoordinateReferenceRecord(6809, (EpsgCoordinateSystemKind)2, 2718); + return true; + case 6810: + cacheIndex = 3592; + reference = new EpsgCoordinateReferenceRecord(6810, (EpsgCoordinateSystemKind)2, 2719); + return true; + case 6811: + cacheIndex = 3593; + reference = new EpsgCoordinateReferenceRecord(6811, (EpsgCoordinateSystemKind)2, 2720); + return true; + case 6812: + cacheIndex = 3594; + reference = new EpsgCoordinateReferenceRecord(6812, (EpsgCoordinateSystemKind)2, 2721); + return true; + case 6813: + cacheIndex = 3595; + reference = new EpsgCoordinateReferenceRecord(6813, (EpsgCoordinateSystemKind)2, 2722); + return true; + case 6814: + cacheIndex = 3596; + reference = new EpsgCoordinateReferenceRecord(6814, (EpsgCoordinateSystemKind)2, 2723); + return true; + case 6815: + cacheIndex = 3597; + reference = new EpsgCoordinateReferenceRecord(6815, (EpsgCoordinateSystemKind)2, 2724); + return true; + case 6816: + cacheIndex = 3598; + reference = new EpsgCoordinateReferenceRecord(6816, (EpsgCoordinateSystemKind)2, 2725); + return true; + case 6817: + cacheIndex = 3599; + reference = new EpsgCoordinateReferenceRecord(6817, (EpsgCoordinateSystemKind)2, 2726); + return true; + case 6818: + cacheIndex = 3600; + reference = new EpsgCoordinateReferenceRecord(6818, (EpsgCoordinateSystemKind)2, 2727); + return true; + case 6819: + cacheIndex = 3601; + reference = new EpsgCoordinateReferenceRecord(6819, (EpsgCoordinateSystemKind)2, 2728); + return true; + case 6820: + cacheIndex = 3602; + reference = new EpsgCoordinateReferenceRecord(6820, (EpsgCoordinateSystemKind)2, 2729); + return true; + case 6821: + cacheIndex = 3603; + reference = new EpsgCoordinateReferenceRecord(6821, (EpsgCoordinateSystemKind)2, 2730); + return true; + case 6822: + cacheIndex = 3604; + reference = new EpsgCoordinateReferenceRecord(6822, (EpsgCoordinateSystemKind)2, 2731); + return true; + case 6823: + cacheIndex = 3605; + reference = new EpsgCoordinateReferenceRecord(6823, (EpsgCoordinateSystemKind)2, 2732); + return true; + case 6824: + cacheIndex = 3606; + reference = new EpsgCoordinateReferenceRecord(6824, (EpsgCoordinateSystemKind)2, 2733); + return true; + case 6825: + cacheIndex = 3607; + reference = new EpsgCoordinateReferenceRecord(6825, (EpsgCoordinateSystemKind)2, 2734); + return true; + case 6826: + cacheIndex = 3608; + reference = new EpsgCoordinateReferenceRecord(6826, (EpsgCoordinateSystemKind)2, 2735); + return true; + case 6827: + cacheIndex = 3609; + reference = new EpsgCoordinateReferenceRecord(6827, (EpsgCoordinateSystemKind)2, 2736); + return true; + case 6828: + cacheIndex = 3610; + reference = new EpsgCoordinateReferenceRecord(6828, (EpsgCoordinateSystemKind)2, 2737); + return true; + case 6829: + cacheIndex = 3611; + reference = new EpsgCoordinateReferenceRecord(6829, (EpsgCoordinateSystemKind)2, 2738); + return true; + case 6830: + cacheIndex = 3612; + reference = new EpsgCoordinateReferenceRecord(6830, (EpsgCoordinateSystemKind)2, 2739); + return true; + case 6831: + cacheIndex = 3613; + reference = new EpsgCoordinateReferenceRecord(6831, (EpsgCoordinateSystemKind)2, 2740); + return true; + case 6832: + cacheIndex = 3614; + reference = new EpsgCoordinateReferenceRecord(6832, (EpsgCoordinateSystemKind)2, 2741); + return true; + case 6833: + cacheIndex = 3615; + reference = new EpsgCoordinateReferenceRecord(6833, (EpsgCoordinateSystemKind)2, 2742); + return true; + case 6834: + cacheIndex = 3616; + reference = new EpsgCoordinateReferenceRecord(6834, (EpsgCoordinateSystemKind)2, 2743); + return true; + case 6835: + cacheIndex = 3617; + reference = new EpsgCoordinateReferenceRecord(6835, (EpsgCoordinateSystemKind)2, 2744); + return true; + case 6836: + cacheIndex = 3618; + reference = new EpsgCoordinateReferenceRecord(6836, (EpsgCoordinateSystemKind)2, 2745); + return true; + case 6837: + cacheIndex = 3619; + reference = new EpsgCoordinateReferenceRecord(6837, (EpsgCoordinateSystemKind)2, 2746); + return true; + case 6838: + cacheIndex = 3620; + reference = new EpsgCoordinateReferenceRecord(6838, (EpsgCoordinateSystemKind)2, 2747); + return true; + case 6839: + cacheIndex = 3621; + reference = new EpsgCoordinateReferenceRecord(6839, (EpsgCoordinateSystemKind)2, 2748); + return true; + case 6840: + cacheIndex = 3622; + reference = new EpsgCoordinateReferenceRecord(6840, (EpsgCoordinateSystemKind)2, 2749); + return true; + case 6841: + cacheIndex = 3623; + reference = new EpsgCoordinateReferenceRecord(6841, (EpsgCoordinateSystemKind)2, 2750); + return true; + case 6842: + cacheIndex = 3624; + reference = new EpsgCoordinateReferenceRecord(6842, (EpsgCoordinateSystemKind)2, 2751); + return true; + case 6843: + cacheIndex = 3625; + reference = new EpsgCoordinateReferenceRecord(6843, (EpsgCoordinateSystemKind)2, 2752); + return true; + case 6844: + cacheIndex = 3626; + reference = new EpsgCoordinateReferenceRecord(6844, (EpsgCoordinateSystemKind)2, 2753); + return true; + case 6845: + cacheIndex = 3627; + reference = new EpsgCoordinateReferenceRecord(6845, (EpsgCoordinateSystemKind)2, 2754); + return true; + case 6846: + cacheIndex = 3628; + reference = new EpsgCoordinateReferenceRecord(6846, (EpsgCoordinateSystemKind)2, 2755); + return true; + case 6847: + cacheIndex = 3629; + reference = new EpsgCoordinateReferenceRecord(6847, (EpsgCoordinateSystemKind)2, 2756); + return true; + case 6848: + cacheIndex = 3630; + reference = new EpsgCoordinateReferenceRecord(6848, (EpsgCoordinateSystemKind)2, 2757); + return true; + case 6849: + cacheIndex = 3631; + reference = new EpsgCoordinateReferenceRecord(6849, (EpsgCoordinateSystemKind)2, 2758); + return true; + case 6850: + cacheIndex = 3632; + reference = new EpsgCoordinateReferenceRecord(6850, (EpsgCoordinateSystemKind)2, 2759); + return true; + case 6851: + cacheIndex = 3633; + reference = new EpsgCoordinateReferenceRecord(6851, (EpsgCoordinateSystemKind)2, 2760); + return true; + case 6852: + cacheIndex = 3634; + reference = new EpsgCoordinateReferenceRecord(6852, (EpsgCoordinateSystemKind)2, 2761); + return true; + case 6853: + cacheIndex = 3635; + reference = new EpsgCoordinateReferenceRecord(6853, (EpsgCoordinateSystemKind)2, 2762); + return true; + case 6854: + cacheIndex = 3636; + reference = new EpsgCoordinateReferenceRecord(6854, (EpsgCoordinateSystemKind)2, 2763); + return true; + case 6855: + cacheIndex = 3637; + reference = new EpsgCoordinateReferenceRecord(6855, (EpsgCoordinateSystemKind)2, 2764); + return true; + case 6856: + cacheIndex = 3638; + reference = new EpsgCoordinateReferenceRecord(6856, (EpsgCoordinateSystemKind)2, 2765); + return true; + case 6857: + cacheIndex = 3639; + reference = new EpsgCoordinateReferenceRecord(6857, (EpsgCoordinateSystemKind)2, 2766); + return true; + case 6858: + cacheIndex = 3640; + reference = new EpsgCoordinateReferenceRecord(6858, (EpsgCoordinateSystemKind)2, 2767); + return true; + case 6859: + cacheIndex = 3641; + reference = new EpsgCoordinateReferenceRecord(6859, (EpsgCoordinateSystemKind)2, 2768); + return true; + case 6860: + cacheIndex = 3642; + reference = new EpsgCoordinateReferenceRecord(6860, (EpsgCoordinateSystemKind)2, 2769); + return true; + case 6861: + cacheIndex = 3643; + reference = new EpsgCoordinateReferenceRecord(6861, (EpsgCoordinateSystemKind)2, 2770); + return true; + case 6862: + cacheIndex = 3644; + reference = new EpsgCoordinateReferenceRecord(6862, (EpsgCoordinateSystemKind)2, 2771); + return true; + case 6863: + cacheIndex = 3645; + reference = new EpsgCoordinateReferenceRecord(6863, (EpsgCoordinateSystemKind)2, 2772); + return true; + case 6867: + cacheIndex = 3646; + reference = new EpsgCoordinateReferenceRecord(6867, (EpsgCoordinateSystemKind)2, 2773); + return true; + case 6868: + cacheIndex = 3647; + reference = new EpsgCoordinateReferenceRecord(6868, (EpsgCoordinateSystemKind)2, 2774); + return true; + case 6870: + cacheIndex = 3648; + reference = new EpsgCoordinateReferenceRecord(6870, (EpsgCoordinateSystemKind)2, 2775); + return true; + case 6875: + cacheIndex = 3649; + reference = new EpsgCoordinateReferenceRecord(6875, (EpsgCoordinateSystemKind)2, 2776); + return true; + case 6876: + cacheIndex = 3650; + reference = new EpsgCoordinateReferenceRecord(6876, (EpsgCoordinateSystemKind)2, 2777); + return true; + case 6879: + cacheIndex = 3651; + reference = new EpsgCoordinateReferenceRecord(6879, (EpsgCoordinateSystemKind)2, 2778); + return true; + case 6880: + cacheIndex = 3652; + reference = new EpsgCoordinateReferenceRecord(6880, (EpsgCoordinateSystemKind)2, 2779); + return true; + case 6881: + cacheIndex = 3653; + reference = new EpsgCoordinateReferenceRecord(6881, (EpsgCoordinateSystemKind)0, 492); + return true; + case 6882: + cacheIndex = 3654; + reference = new EpsgCoordinateReferenceRecord(6882, (EpsgCoordinateSystemKind)0, 493); + return true; + case 6883: + cacheIndex = 3655; + reference = new EpsgCoordinateReferenceRecord(6883, (EpsgCoordinateSystemKind)0, 494); + return true; + case 6884: + cacheIndex = 3656; + reference = new EpsgCoordinateReferenceRecord(6884, (EpsgCoordinateSystemKind)2, 2780); + return true; + case 6885: + cacheIndex = 3657; + reference = new EpsgCoordinateReferenceRecord(6885, (EpsgCoordinateSystemKind)2, 2781); + return true; + case 6886: + cacheIndex = 3658; + reference = new EpsgCoordinateReferenceRecord(6886, (EpsgCoordinateSystemKind)2, 2782); + return true; + case 6887: + cacheIndex = 3659; + reference = new EpsgCoordinateReferenceRecord(6887, (EpsgCoordinateSystemKind)2, 2783); + return true; + case 6892: + cacheIndex = 3660; + reference = new EpsgCoordinateReferenceRecord(6892, (EpsgCoordinateSystemKind)0, 495); + return true; + case 6893: + cacheIndex = 3661; + reference = new EpsgCoordinateReferenceRecord(6893, (EpsgCoordinateSystemKind)4, 117); + return true; + case 6894: + cacheIndex = 3662; + reference = new EpsgCoordinateReferenceRecord(6894, (EpsgCoordinateSystemKind)0, 496); + return true; + case 6915: + cacheIndex = 3663; + reference = new EpsgCoordinateReferenceRecord(6915, (EpsgCoordinateSystemKind)2, 2784); + return true; + case 6916: + cacheIndex = 3664; + reference = new EpsgCoordinateReferenceRecord(6916, (EpsgCoordinateSystemKind)3, 163); + return true; + case 6917: + cacheIndex = 3665; + reference = new EpsgCoordinateReferenceRecord(6917, (EpsgCoordinateSystemKind)4, 118); + return true; + case 6922: + cacheIndex = 3666; + reference = new EpsgCoordinateReferenceRecord(6922, (EpsgCoordinateSystemKind)2, 2785); + return true; + case 6923: + cacheIndex = 3667; + reference = new EpsgCoordinateReferenceRecord(6923, (EpsgCoordinateSystemKind)2, 2786); + return true; + case 6924: + cacheIndex = 3668; + reference = new EpsgCoordinateReferenceRecord(6924, (EpsgCoordinateSystemKind)2, 2787); + return true; + case 6925: + cacheIndex = 3669; + reference = new EpsgCoordinateReferenceRecord(6925, (EpsgCoordinateSystemKind)2, 2788); + return true; + case 6927: + cacheIndex = 3670; + reference = new EpsgCoordinateReferenceRecord(6927, (EpsgCoordinateSystemKind)4, 119); + return true; + case 6931: + cacheIndex = 3671; + reference = new EpsgCoordinateReferenceRecord(6931, (EpsgCoordinateSystemKind)2, 2789); + return true; + case 6932: + cacheIndex = 3672; + reference = new EpsgCoordinateReferenceRecord(6932, (EpsgCoordinateSystemKind)2, 2790); + return true; + case 6933: + cacheIndex = 3673; + reference = new EpsgCoordinateReferenceRecord(6933, (EpsgCoordinateSystemKind)2, 2791); + return true; + case 6934: + cacheIndex = 3674; + reference = new EpsgCoordinateReferenceRecord(6934, (EpsgCoordinateSystemKind)1, 101); + return true; + case 6962: + cacheIndex = 3675; + reference = new EpsgCoordinateReferenceRecord(6962, (EpsgCoordinateSystemKind)2, 2792); + return true; + case 6966: + cacheIndex = 3676; + reference = new EpsgCoordinateReferenceRecord(6966, (EpsgCoordinateSystemKind)2, 2793); + return true; + case 6981: + cacheIndex = 3677; + reference = new EpsgCoordinateReferenceRecord(6981, (EpsgCoordinateSystemKind)1, 102); + return true; + case 6982: + cacheIndex = 3678; + reference = new EpsgCoordinateReferenceRecord(6982, (EpsgCoordinateSystemKind)0, 497); + return true; + case 6983: + cacheIndex = 3679; + reference = new EpsgCoordinateReferenceRecord(6983, (EpsgCoordinateSystemKind)0, 498); + return true; + case 6984: + cacheIndex = 3680; + reference = new EpsgCoordinateReferenceRecord(6984, (EpsgCoordinateSystemKind)2, 2794); + return true; + case 6988: + cacheIndex = 3681; + reference = new EpsgCoordinateReferenceRecord(6988, (EpsgCoordinateSystemKind)1, 103); + return true; + case 6989: + cacheIndex = 3682; + reference = new EpsgCoordinateReferenceRecord(6989, (EpsgCoordinateSystemKind)0, 499); + return true; + case 6990: + cacheIndex = 3683; + reference = new EpsgCoordinateReferenceRecord(6990, (EpsgCoordinateSystemKind)0, 500); + return true; + case 6991: + cacheIndex = 3684; + reference = new EpsgCoordinateReferenceRecord(6991, (EpsgCoordinateSystemKind)2, 2795); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket7(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 7005: + cacheIndex = 3685; + reference = new EpsgCoordinateReferenceRecord(7005, (EpsgCoordinateSystemKind)2, 2796); + return true; + case 7006: + cacheIndex = 3686; + reference = new EpsgCoordinateReferenceRecord(7006, (EpsgCoordinateSystemKind)2, 2797); + return true; + case 7007: + cacheIndex = 3687; + reference = new EpsgCoordinateReferenceRecord(7007, (EpsgCoordinateSystemKind)2, 2798); + return true; + case 7034: + cacheIndex = 3688; + reference = new EpsgCoordinateReferenceRecord(7034, (EpsgCoordinateSystemKind)0, 501); + return true; + case 7035: + cacheIndex = 3689; + reference = new EpsgCoordinateReferenceRecord(7035, (EpsgCoordinateSystemKind)0, 502); + return true; + case 7036: + cacheIndex = 3690; + reference = new EpsgCoordinateReferenceRecord(7036, (EpsgCoordinateSystemKind)0, 503); + return true; + case 7037: + cacheIndex = 3691; + reference = new EpsgCoordinateReferenceRecord(7037, (EpsgCoordinateSystemKind)0, 504); + return true; + case 7038: + cacheIndex = 3692; + reference = new EpsgCoordinateReferenceRecord(7038, (EpsgCoordinateSystemKind)0, 505); + return true; + case 7039: + cacheIndex = 3693; + reference = new EpsgCoordinateReferenceRecord(7039, (EpsgCoordinateSystemKind)0, 506); + return true; + case 7040: + cacheIndex = 3694; + reference = new EpsgCoordinateReferenceRecord(7040, (EpsgCoordinateSystemKind)0, 507); + return true; + case 7041: + cacheIndex = 3695; + reference = new EpsgCoordinateReferenceRecord(7041, (EpsgCoordinateSystemKind)0, 508); + return true; + case 7042: + cacheIndex = 3696; + reference = new EpsgCoordinateReferenceRecord(7042, (EpsgCoordinateSystemKind)0, 509); + return true; + case 7057: + cacheIndex = 3697; + reference = new EpsgCoordinateReferenceRecord(7057, (EpsgCoordinateSystemKind)2, 2799); + return true; + case 7058: + cacheIndex = 3698; + reference = new EpsgCoordinateReferenceRecord(7058, (EpsgCoordinateSystemKind)2, 2800); + return true; + case 7059: + cacheIndex = 3699; + reference = new EpsgCoordinateReferenceRecord(7059, (EpsgCoordinateSystemKind)2, 2801); + return true; + case 7060: + cacheIndex = 3700; + reference = new EpsgCoordinateReferenceRecord(7060, (EpsgCoordinateSystemKind)2, 2802); + return true; + case 7061: + cacheIndex = 3701; + reference = new EpsgCoordinateReferenceRecord(7061, (EpsgCoordinateSystemKind)2, 2803); + return true; + case 7062: + cacheIndex = 3702; + reference = new EpsgCoordinateReferenceRecord(7062, (EpsgCoordinateSystemKind)2, 2804); + return true; + case 7063: + cacheIndex = 3703; + reference = new EpsgCoordinateReferenceRecord(7063, (EpsgCoordinateSystemKind)2, 2805); + return true; + case 7064: + cacheIndex = 3704; + reference = new EpsgCoordinateReferenceRecord(7064, (EpsgCoordinateSystemKind)2, 2806); + return true; + case 7065: + cacheIndex = 3705; + reference = new EpsgCoordinateReferenceRecord(7065, (EpsgCoordinateSystemKind)2, 2807); + return true; + case 7066: + cacheIndex = 3706; + reference = new EpsgCoordinateReferenceRecord(7066, (EpsgCoordinateSystemKind)2, 2808); + return true; + case 7067: + cacheIndex = 3707; + reference = new EpsgCoordinateReferenceRecord(7067, (EpsgCoordinateSystemKind)2, 2809); + return true; + case 7068: + cacheIndex = 3708; + reference = new EpsgCoordinateReferenceRecord(7068, (EpsgCoordinateSystemKind)2, 2810); + return true; + case 7069: + cacheIndex = 3709; + reference = new EpsgCoordinateReferenceRecord(7069, (EpsgCoordinateSystemKind)2, 2811); + return true; + case 7070: + cacheIndex = 3710; + reference = new EpsgCoordinateReferenceRecord(7070, (EpsgCoordinateSystemKind)2, 2812); + return true; + case 7071: + cacheIndex = 3711; + reference = new EpsgCoordinateReferenceRecord(7071, (EpsgCoordinateSystemKind)1, 104); + return true; + case 7072: + cacheIndex = 3712; + reference = new EpsgCoordinateReferenceRecord(7072, (EpsgCoordinateSystemKind)0, 510); + return true; + case 7073: + cacheIndex = 3713; + reference = new EpsgCoordinateReferenceRecord(7073, (EpsgCoordinateSystemKind)0, 511); + return true; + case 7074: + cacheIndex = 3714; + reference = new EpsgCoordinateReferenceRecord(7074, (EpsgCoordinateSystemKind)2, 2813); + return true; + case 7075: + cacheIndex = 3715; + reference = new EpsgCoordinateReferenceRecord(7075, (EpsgCoordinateSystemKind)2, 2814); + return true; + case 7076: + cacheIndex = 3716; + reference = new EpsgCoordinateReferenceRecord(7076, (EpsgCoordinateSystemKind)2, 2815); + return true; + case 7077: + cacheIndex = 3717; + reference = new EpsgCoordinateReferenceRecord(7077, (EpsgCoordinateSystemKind)2, 2816); + return true; + case 7078: + cacheIndex = 3718; + reference = new EpsgCoordinateReferenceRecord(7078, (EpsgCoordinateSystemKind)2, 2817); + return true; + case 7079: + cacheIndex = 3719; + reference = new EpsgCoordinateReferenceRecord(7079, (EpsgCoordinateSystemKind)2, 2818); + return true; + case 7080: + cacheIndex = 3720; + reference = new EpsgCoordinateReferenceRecord(7080, (EpsgCoordinateSystemKind)2, 2819); + return true; + case 7081: + cacheIndex = 3721; + reference = new EpsgCoordinateReferenceRecord(7081, (EpsgCoordinateSystemKind)2, 2820); + return true; + case 7084: + cacheIndex = 3722; + reference = new EpsgCoordinateReferenceRecord(7084, (EpsgCoordinateSystemKind)0, 512); + return true; + case 7085: + cacheIndex = 3723; + reference = new EpsgCoordinateReferenceRecord(7085, (EpsgCoordinateSystemKind)0, 513); + return true; + case 7086: + cacheIndex = 3724; + reference = new EpsgCoordinateReferenceRecord(7086, (EpsgCoordinateSystemKind)0, 514); + return true; + case 7087: + cacheIndex = 3725; + reference = new EpsgCoordinateReferenceRecord(7087, (EpsgCoordinateSystemKind)0, 515); + return true; + case 7109: + cacheIndex = 3726; + reference = new EpsgCoordinateReferenceRecord(7109, (EpsgCoordinateSystemKind)2, 2821); + return true; + case 7110: + cacheIndex = 3727; + reference = new EpsgCoordinateReferenceRecord(7110, (EpsgCoordinateSystemKind)2, 2822); + return true; + case 7111: + cacheIndex = 3728; + reference = new EpsgCoordinateReferenceRecord(7111, (EpsgCoordinateSystemKind)2, 2823); + return true; + case 7112: + cacheIndex = 3729; + reference = new EpsgCoordinateReferenceRecord(7112, (EpsgCoordinateSystemKind)2, 2824); + return true; + case 7113: + cacheIndex = 3730; + reference = new EpsgCoordinateReferenceRecord(7113, (EpsgCoordinateSystemKind)2, 2825); + return true; + case 7114: + cacheIndex = 3731; + reference = new EpsgCoordinateReferenceRecord(7114, (EpsgCoordinateSystemKind)2, 2826); + return true; + case 7115: + cacheIndex = 3732; + reference = new EpsgCoordinateReferenceRecord(7115, (EpsgCoordinateSystemKind)2, 2827); + return true; + case 7116: + cacheIndex = 3733; + reference = new EpsgCoordinateReferenceRecord(7116, (EpsgCoordinateSystemKind)2, 2828); + return true; + case 7117: + cacheIndex = 3734; + reference = new EpsgCoordinateReferenceRecord(7117, (EpsgCoordinateSystemKind)2, 2829); + return true; + case 7118: + cacheIndex = 3735; + reference = new EpsgCoordinateReferenceRecord(7118, (EpsgCoordinateSystemKind)2, 2830); + return true; + case 7119: + cacheIndex = 3736; + reference = new EpsgCoordinateReferenceRecord(7119, (EpsgCoordinateSystemKind)2, 2831); + return true; + case 7120: + cacheIndex = 3737; + reference = new EpsgCoordinateReferenceRecord(7120, (EpsgCoordinateSystemKind)2, 2832); + return true; + case 7121: + cacheIndex = 3738; + reference = new EpsgCoordinateReferenceRecord(7121, (EpsgCoordinateSystemKind)2, 2833); + return true; + case 7122: + cacheIndex = 3739; + reference = new EpsgCoordinateReferenceRecord(7122, (EpsgCoordinateSystemKind)2, 2834); + return true; + case 7123: + cacheIndex = 3740; + reference = new EpsgCoordinateReferenceRecord(7123, (EpsgCoordinateSystemKind)2, 2835); + return true; + case 7124: + cacheIndex = 3741; + reference = new EpsgCoordinateReferenceRecord(7124, (EpsgCoordinateSystemKind)2, 2836); + return true; + case 7125: + cacheIndex = 3742; + reference = new EpsgCoordinateReferenceRecord(7125, (EpsgCoordinateSystemKind)2, 2837); + return true; + case 7126: + cacheIndex = 3743; + reference = new EpsgCoordinateReferenceRecord(7126, (EpsgCoordinateSystemKind)2, 2838); + return true; + case 7127: + cacheIndex = 3744; + reference = new EpsgCoordinateReferenceRecord(7127, (EpsgCoordinateSystemKind)2, 2839); + return true; + case 7128: + cacheIndex = 3745; + reference = new EpsgCoordinateReferenceRecord(7128, (EpsgCoordinateSystemKind)2, 2840); + return true; + case 7131: + cacheIndex = 3746; + reference = new EpsgCoordinateReferenceRecord(7131, (EpsgCoordinateSystemKind)2, 2841); + return true; + case 7132: + cacheIndex = 3747; + reference = new EpsgCoordinateReferenceRecord(7132, (EpsgCoordinateSystemKind)2, 2842); + return true; + case 7133: + cacheIndex = 3748; + reference = new EpsgCoordinateReferenceRecord(7133, (EpsgCoordinateSystemKind)0, 516); + return true; + case 7134: + cacheIndex = 3749; + reference = new EpsgCoordinateReferenceRecord(7134, (EpsgCoordinateSystemKind)1, 105); + return true; + case 7135: + cacheIndex = 3750; + reference = new EpsgCoordinateReferenceRecord(7135, (EpsgCoordinateSystemKind)0, 517); + return true; + case 7136: + cacheIndex = 3751; + reference = new EpsgCoordinateReferenceRecord(7136, (EpsgCoordinateSystemKind)0, 518); + return true; + case 7137: + cacheIndex = 3752; + reference = new EpsgCoordinateReferenceRecord(7137, (EpsgCoordinateSystemKind)1, 106); + return true; + case 7138: + cacheIndex = 3753; + reference = new EpsgCoordinateReferenceRecord(7138, (EpsgCoordinateSystemKind)0, 519); + return true; + case 7139: + cacheIndex = 3754; + reference = new EpsgCoordinateReferenceRecord(7139, (EpsgCoordinateSystemKind)0, 520); + return true; + case 7142: + cacheIndex = 3755; + reference = new EpsgCoordinateReferenceRecord(7142, (EpsgCoordinateSystemKind)2, 2843); + return true; + case 7257: + cacheIndex = 3756; + reference = new EpsgCoordinateReferenceRecord(7257, (EpsgCoordinateSystemKind)2, 2844); + return true; + case 7258: + cacheIndex = 3757; + reference = new EpsgCoordinateReferenceRecord(7258, (EpsgCoordinateSystemKind)2, 2845); + return true; + case 7259: + cacheIndex = 3758; + reference = new EpsgCoordinateReferenceRecord(7259, (EpsgCoordinateSystemKind)2, 2846); + return true; + case 7260: + cacheIndex = 3759; + reference = new EpsgCoordinateReferenceRecord(7260, (EpsgCoordinateSystemKind)2, 2847); + return true; + case 7261: + cacheIndex = 3760; + reference = new EpsgCoordinateReferenceRecord(7261, (EpsgCoordinateSystemKind)2, 2848); + return true; + case 7262: + cacheIndex = 3761; + reference = new EpsgCoordinateReferenceRecord(7262, (EpsgCoordinateSystemKind)2, 2849); + return true; + case 7263: + cacheIndex = 3762; + reference = new EpsgCoordinateReferenceRecord(7263, (EpsgCoordinateSystemKind)2, 2850); + return true; + case 7264: + cacheIndex = 3763; + reference = new EpsgCoordinateReferenceRecord(7264, (EpsgCoordinateSystemKind)2, 2851); + return true; + case 7265: + cacheIndex = 3764; + reference = new EpsgCoordinateReferenceRecord(7265, (EpsgCoordinateSystemKind)2, 2852); + return true; + case 7266: + cacheIndex = 3765; + reference = new EpsgCoordinateReferenceRecord(7266, (EpsgCoordinateSystemKind)2, 2853); + return true; + case 7267: + cacheIndex = 3766; + reference = new EpsgCoordinateReferenceRecord(7267, (EpsgCoordinateSystemKind)2, 2854); + return true; + case 7268: + cacheIndex = 3767; + reference = new EpsgCoordinateReferenceRecord(7268, (EpsgCoordinateSystemKind)2, 2855); + return true; + case 7269: + cacheIndex = 3768; + reference = new EpsgCoordinateReferenceRecord(7269, (EpsgCoordinateSystemKind)2, 2856); + return true; + case 7270: + cacheIndex = 3769; + reference = new EpsgCoordinateReferenceRecord(7270, (EpsgCoordinateSystemKind)2, 2857); + return true; + case 7271: + cacheIndex = 3770; + reference = new EpsgCoordinateReferenceRecord(7271, (EpsgCoordinateSystemKind)2, 2858); + return true; + case 7272: + cacheIndex = 3771; + reference = new EpsgCoordinateReferenceRecord(7272, (EpsgCoordinateSystemKind)2, 2859); + return true; + case 7273: + cacheIndex = 3772; + reference = new EpsgCoordinateReferenceRecord(7273, (EpsgCoordinateSystemKind)2, 2860); + return true; + case 7274: + cacheIndex = 3773; + reference = new EpsgCoordinateReferenceRecord(7274, (EpsgCoordinateSystemKind)2, 2861); + return true; + case 7275: + cacheIndex = 3774; + reference = new EpsgCoordinateReferenceRecord(7275, (EpsgCoordinateSystemKind)2, 2862); + return true; + case 7276: + cacheIndex = 3775; + reference = new EpsgCoordinateReferenceRecord(7276, (EpsgCoordinateSystemKind)2, 2863); + return true; + case 7277: + cacheIndex = 3776; + reference = new EpsgCoordinateReferenceRecord(7277, (EpsgCoordinateSystemKind)2, 2864); + return true; + case 7278: + cacheIndex = 3777; + reference = new EpsgCoordinateReferenceRecord(7278, (EpsgCoordinateSystemKind)2, 2865); + return true; + case 7279: + cacheIndex = 3778; + reference = new EpsgCoordinateReferenceRecord(7279, (EpsgCoordinateSystemKind)2, 2866); + return true; + case 7280: + cacheIndex = 3779; + reference = new EpsgCoordinateReferenceRecord(7280, (EpsgCoordinateSystemKind)2, 2867); + return true; + case 7281: + cacheIndex = 3780; + reference = new EpsgCoordinateReferenceRecord(7281, (EpsgCoordinateSystemKind)2, 2868); + return true; + case 7282: + cacheIndex = 3781; + reference = new EpsgCoordinateReferenceRecord(7282, (EpsgCoordinateSystemKind)2, 2869); + return true; + case 7283: + cacheIndex = 3782; + reference = new EpsgCoordinateReferenceRecord(7283, (EpsgCoordinateSystemKind)2, 2870); + return true; + case 7284: + cacheIndex = 3783; + reference = new EpsgCoordinateReferenceRecord(7284, (EpsgCoordinateSystemKind)2, 2871); + return true; + case 7285: + cacheIndex = 3784; + reference = new EpsgCoordinateReferenceRecord(7285, (EpsgCoordinateSystemKind)2, 2872); + return true; + case 7286: + cacheIndex = 3785; + reference = new EpsgCoordinateReferenceRecord(7286, (EpsgCoordinateSystemKind)2, 2873); + return true; + case 7287: + cacheIndex = 3786; + reference = new EpsgCoordinateReferenceRecord(7287, (EpsgCoordinateSystemKind)2, 2874); + return true; + case 7288: + cacheIndex = 3787; + reference = new EpsgCoordinateReferenceRecord(7288, (EpsgCoordinateSystemKind)2, 2875); + return true; + case 7289: + cacheIndex = 3788; + reference = new EpsgCoordinateReferenceRecord(7289, (EpsgCoordinateSystemKind)2, 2876); + return true; + case 7290: + cacheIndex = 3789; + reference = new EpsgCoordinateReferenceRecord(7290, (EpsgCoordinateSystemKind)2, 2877); + return true; + case 7291: + cacheIndex = 3790; + reference = new EpsgCoordinateReferenceRecord(7291, (EpsgCoordinateSystemKind)2, 2878); + return true; + case 7292: + cacheIndex = 3791; + reference = new EpsgCoordinateReferenceRecord(7292, (EpsgCoordinateSystemKind)2, 2879); + return true; + case 7293: + cacheIndex = 3792; + reference = new EpsgCoordinateReferenceRecord(7293, (EpsgCoordinateSystemKind)2, 2880); + return true; + case 7294: + cacheIndex = 3793; + reference = new EpsgCoordinateReferenceRecord(7294, (EpsgCoordinateSystemKind)2, 2881); + return true; + case 7295: + cacheIndex = 3794; + reference = new EpsgCoordinateReferenceRecord(7295, (EpsgCoordinateSystemKind)2, 2882); + return true; + case 7296: + cacheIndex = 3795; + reference = new EpsgCoordinateReferenceRecord(7296, (EpsgCoordinateSystemKind)2, 2883); + return true; + case 7297: + cacheIndex = 3796; + reference = new EpsgCoordinateReferenceRecord(7297, (EpsgCoordinateSystemKind)2, 2884); + return true; + case 7298: + cacheIndex = 3797; + reference = new EpsgCoordinateReferenceRecord(7298, (EpsgCoordinateSystemKind)2, 2885); + return true; + case 7299: + cacheIndex = 3798; + reference = new EpsgCoordinateReferenceRecord(7299, (EpsgCoordinateSystemKind)2, 2886); + return true; + case 7300: + cacheIndex = 3799; + reference = new EpsgCoordinateReferenceRecord(7300, (EpsgCoordinateSystemKind)2, 2887); + return true; + case 7301: + cacheIndex = 3800; + reference = new EpsgCoordinateReferenceRecord(7301, (EpsgCoordinateSystemKind)2, 2888); + return true; + case 7302: + cacheIndex = 3801; + reference = new EpsgCoordinateReferenceRecord(7302, (EpsgCoordinateSystemKind)2, 2889); + return true; + case 7303: + cacheIndex = 3802; + reference = new EpsgCoordinateReferenceRecord(7303, (EpsgCoordinateSystemKind)2, 2890); + return true; + case 7304: + cacheIndex = 3803; + reference = new EpsgCoordinateReferenceRecord(7304, (EpsgCoordinateSystemKind)2, 2891); + return true; + case 7305: + cacheIndex = 3804; + reference = new EpsgCoordinateReferenceRecord(7305, (EpsgCoordinateSystemKind)2, 2892); + return true; + case 7306: + cacheIndex = 3805; + reference = new EpsgCoordinateReferenceRecord(7306, (EpsgCoordinateSystemKind)2, 2893); + return true; + case 7307: + cacheIndex = 3806; + reference = new EpsgCoordinateReferenceRecord(7307, (EpsgCoordinateSystemKind)2, 2894); + return true; + case 7308: + cacheIndex = 3807; + reference = new EpsgCoordinateReferenceRecord(7308, (EpsgCoordinateSystemKind)2, 2895); + return true; + case 7309: + cacheIndex = 3808; + reference = new EpsgCoordinateReferenceRecord(7309, (EpsgCoordinateSystemKind)2, 2896); + return true; + case 7310: + cacheIndex = 3809; + reference = new EpsgCoordinateReferenceRecord(7310, (EpsgCoordinateSystemKind)2, 2897); + return true; + case 7311: + cacheIndex = 3810; + reference = new EpsgCoordinateReferenceRecord(7311, (EpsgCoordinateSystemKind)2, 2898); + return true; + case 7312: + cacheIndex = 3811; + reference = new EpsgCoordinateReferenceRecord(7312, (EpsgCoordinateSystemKind)2, 2899); + return true; + case 7313: + cacheIndex = 3812; + reference = new EpsgCoordinateReferenceRecord(7313, (EpsgCoordinateSystemKind)2, 2900); + return true; + case 7314: + cacheIndex = 3813; + reference = new EpsgCoordinateReferenceRecord(7314, (EpsgCoordinateSystemKind)2, 2901); + return true; + case 7315: + cacheIndex = 3814; + reference = new EpsgCoordinateReferenceRecord(7315, (EpsgCoordinateSystemKind)2, 2902); + return true; + case 7316: + cacheIndex = 3815; + reference = new EpsgCoordinateReferenceRecord(7316, (EpsgCoordinateSystemKind)2, 2903); + return true; + case 7317: + cacheIndex = 3816; + reference = new EpsgCoordinateReferenceRecord(7317, (EpsgCoordinateSystemKind)2, 2904); + return true; + case 7318: + cacheIndex = 3817; + reference = new EpsgCoordinateReferenceRecord(7318, (EpsgCoordinateSystemKind)2, 2905); + return true; + case 7319: + cacheIndex = 3818; + reference = new EpsgCoordinateReferenceRecord(7319, (EpsgCoordinateSystemKind)2, 2906); + return true; + case 7320: + cacheIndex = 3819; + reference = new EpsgCoordinateReferenceRecord(7320, (EpsgCoordinateSystemKind)2, 2907); + return true; + case 7321: + cacheIndex = 3820; + reference = new EpsgCoordinateReferenceRecord(7321, (EpsgCoordinateSystemKind)2, 2908); + return true; + case 7322: + cacheIndex = 3821; + reference = new EpsgCoordinateReferenceRecord(7322, (EpsgCoordinateSystemKind)2, 2909); + return true; + case 7323: + cacheIndex = 3822; + reference = new EpsgCoordinateReferenceRecord(7323, (EpsgCoordinateSystemKind)2, 2910); + return true; + case 7324: + cacheIndex = 3823; + reference = new EpsgCoordinateReferenceRecord(7324, (EpsgCoordinateSystemKind)2, 2911); + return true; + case 7325: + cacheIndex = 3824; + reference = new EpsgCoordinateReferenceRecord(7325, (EpsgCoordinateSystemKind)2, 2912); + return true; + case 7326: + cacheIndex = 3825; + reference = new EpsgCoordinateReferenceRecord(7326, (EpsgCoordinateSystemKind)2, 2913); + return true; + case 7327: + cacheIndex = 3826; + reference = new EpsgCoordinateReferenceRecord(7327, (EpsgCoordinateSystemKind)2, 2914); + return true; + case 7328: + cacheIndex = 3827; + reference = new EpsgCoordinateReferenceRecord(7328, (EpsgCoordinateSystemKind)2, 2915); + return true; + case 7329: + cacheIndex = 3828; + reference = new EpsgCoordinateReferenceRecord(7329, (EpsgCoordinateSystemKind)2, 2916); + return true; + case 7330: + cacheIndex = 3829; + reference = new EpsgCoordinateReferenceRecord(7330, (EpsgCoordinateSystemKind)2, 2917); + return true; + case 7331: + cacheIndex = 3830; + reference = new EpsgCoordinateReferenceRecord(7331, (EpsgCoordinateSystemKind)2, 2918); + return true; + case 7332: + cacheIndex = 3831; + reference = new EpsgCoordinateReferenceRecord(7332, (EpsgCoordinateSystemKind)2, 2919); + return true; + case 7333: + cacheIndex = 3832; + reference = new EpsgCoordinateReferenceRecord(7333, (EpsgCoordinateSystemKind)2, 2920); + return true; + case 7334: + cacheIndex = 3833; + reference = new EpsgCoordinateReferenceRecord(7334, (EpsgCoordinateSystemKind)2, 2921); + return true; + case 7335: + cacheIndex = 3834; + reference = new EpsgCoordinateReferenceRecord(7335, (EpsgCoordinateSystemKind)2, 2922); + return true; + case 7336: + cacheIndex = 3835; + reference = new EpsgCoordinateReferenceRecord(7336, (EpsgCoordinateSystemKind)2, 2923); + return true; + case 7337: + cacheIndex = 3836; + reference = new EpsgCoordinateReferenceRecord(7337, (EpsgCoordinateSystemKind)2, 2924); + return true; + case 7338: + cacheIndex = 3837; + reference = new EpsgCoordinateReferenceRecord(7338, (EpsgCoordinateSystemKind)2, 2925); + return true; + case 7339: + cacheIndex = 3838; + reference = new EpsgCoordinateReferenceRecord(7339, (EpsgCoordinateSystemKind)2, 2926); + return true; + case 7340: + cacheIndex = 3839; + reference = new EpsgCoordinateReferenceRecord(7340, (EpsgCoordinateSystemKind)2, 2927); + return true; + case 7341: + cacheIndex = 3840; + reference = new EpsgCoordinateReferenceRecord(7341, (EpsgCoordinateSystemKind)2, 2928); + return true; + case 7342: + cacheIndex = 3841; + reference = new EpsgCoordinateReferenceRecord(7342, (EpsgCoordinateSystemKind)2, 2929); + return true; + case 7343: + cacheIndex = 3842; + reference = new EpsgCoordinateReferenceRecord(7343, (EpsgCoordinateSystemKind)2, 2930); + return true; + case 7344: + cacheIndex = 3843; + reference = new EpsgCoordinateReferenceRecord(7344, (EpsgCoordinateSystemKind)2, 2931); + return true; + case 7345: + cacheIndex = 3844; + reference = new EpsgCoordinateReferenceRecord(7345, (EpsgCoordinateSystemKind)2, 2932); + return true; + case 7346: + cacheIndex = 3845; + reference = new EpsgCoordinateReferenceRecord(7346, (EpsgCoordinateSystemKind)2, 2933); + return true; + case 7347: + cacheIndex = 3846; + reference = new EpsgCoordinateReferenceRecord(7347, (EpsgCoordinateSystemKind)2, 2934); + return true; + case 7348: + cacheIndex = 3847; + reference = new EpsgCoordinateReferenceRecord(7348, (EpsgCoordinateSystemKind)2, 2935); + return true; + case 7349: + cacheIndex = 3848; + reference = new EpsgCoordinateReferenceRecord(7349, (EpsgCoordinateSystemKind)2, 2936); + return true; + case 7350: + cacheIndex = 3849; + reference = new EpsgCoordinateReferenceRecord(7350, (EpsgCoordinateSystemKind)2, 2937); + return true; + case 7351: + cacheIndex = 3850; + reference = new EpsgCoordinateReferenceRecord(7351, (EpsgCoordinateSystemKind)2, 2938); + return true; + case 7352: + cacheIndex = 3851; + reference = new EpsgCoordinateReferenceRecord(7352, (EpsgCoordinateSystemKind)2, 2939); + return true; + case 7353: + cacheIndex = 3852; + reference = new EpsgCoordinateReferenceRecord(7353, (EpsgCoordinateSystemKind)2, 2940); + return true; + case 7354: + cacheIndex = 3853; + reference = new EpsgCoordinateReferenceRecord(7354, (EpsgCoordinateSystemKind)2, 2941); + return true; + case 7355: + cacheIndex = 3854; + reference = new EpsgCoordinateReferenceRecord(7355, (EpsgCoordinateSystemKind)2, 2942); + return true; + case 7356: + cacheIndex = 3855; + reference = new EpsgCoordinateReferenceRecord(7356, (EpsgCoordinateSystemKind)2, 2943); + return true; + case 7357: + cacheIndex = 3856; + reference = new EpsgCoordinateReferenceRecord(7357, (EpsgCoordinateSystemKind)2, 2944); + return true; + case 7358: + cacheIndex = 3857; + reference = new EpsgCoordinateReferenceRecord(7358, (EpsgCoordinateSystemKind)2, 2945); + return true; + case 7359: + cacheIndex = 3858; + reference = new EpsgCoordinateReferenceRecord(7359, (EpsgCoordinateSystemKind)2, 2946); + return true; + case 7360: + cacheIndex = 3859; + reference = new EpsgCoordinateReferenceRecord(7360, (EpsgCoordinateSystemKind)2, 2947); + return true; + case 7361: + cacheIndex = 3860; + reference = new EpsgCoordinateReferenceRecord(7361, (EpsgCoordinateSystemKind)2, 2948); + return true; + case 7362: + cacheIndex = 3861; + reference = new EpsgCoordinateReferenceRecord(7362, (EpsgCoordinateSystemKind)2, 2949); + return true; + case 7363: + cacheIndex = 3862; + reference = new EpsgCoordinateReferenceRecord(7363, (EpsgCoordinateSystemKind)2, 2950); + return true; + case 7364: + cacheIndex = 3863; + reference = new EpsgCoordinateReferenceRecord(7364, (EpsgCoordinateSystemKind)2, 2951); + return true; + case 7365: + cacheIndex = 3864; + reference = new EpsgCoordinateReferenceRecord(7365, (EpsgCoordinateSystemKind)2, 2952); + return true; + case 7366: + cacheIndex = 3865; + reference = new EpsgCoordinateReferenceRecord(7366, (EpsgCoordinateSystemKind)2, 2953); + return true; + case 7367: + cacheIndex = 3866; + reference = new EpsgCoordinateReferenceRecord(7367, (EpsgCoordinateSystemKind)2, 2954); + return true; + case 7368: + cacheIndex = 3867; + reference = new EpsgCoordinateReferenceRecord(7368, (EpsgCoordinateSystemKind)2, 2955); + return true; + case 7369: + cacheIndex = 3868; + reference = new EpsgCoordinateReferenceRecord(7369, (EpsgCoordinateSystemKind)2, 2956); + return true; + case 7370: + cacheIndex = 3869; + reference = new EpsgCoordinateReferenceRecord(7370, (EpsgCoordinateSystemKind)2, 2957); + return true; + case 7371: + cacheIndex = 3870; + reference = new EpsgCoordinateReferenceRecord(7371, (EpsgCoordinateSystemKind)1, 107); + return true; + case 7372: + cacheIndex = 3871; + reference = new EpsgCoordinateReferenceRecord(7372, (EpsgCoordinateSystemKind)0, 521); + return true; + case 7373: + cacheIndex = 3872; + reference = new EpsgCoordinateReferenceRecord(7373, (EpsgCoordinateSystemKind)0, 522); + return true; + case 7374: + cacheIndex = 3873; + reference = new EpsgCoordinateReferenceRecord(7374, (EpsgCoordinateSystemKind)2, 2958); + return true; + case 7375: + cacheIndex = 3874; + reference = new EpsgCoordinateReferenceRecord(7375, (EpsgCoordinateSystemKind)2, 2959); + return true; + case 7376: + cacheIndex = 3875; + reference = new EpsgCoordinateReferenceRecord(7376, (EpsgCoordinateSystemKind)2, 2960); + return true; + case 7400: + cacheIndex = 3876; + reference = new EpsgCoordinateReferenceRecord(7400, (EpsgCoordinateSystemKind)4, 120); + return true; + case 7404: + cacheIndex = 3877; + reference = new EpsgCoordinateReferenceRecord(7404, (EpsgCoordinateSystemKind)4, 121); + return true; + case 7405: + cacheIndex = 3878; + reference = new EpsgCoordinateReferenceRecord(7405, (EpsgCoordinateSystemKind)4, 122); + return true; + case 7406: + cacheIndex = 3879; + reference = new EpsgCoordinateReferenceRecord(7406, (EpsgCoordinateSystemKind)4, 123); + return true; + case 7407: + cacheIndex = 3880; + reference = new EpsgCoordinateReferenceRecord(7407, (EpsgCoordinateSystemKind)4, 124); + return true; + case 7409: + cacheIndex = 3881; + reference = new EpsgCoordinateReferenceRecord(7409, (EpsgCoordinateSystemKind)4, 125); + return true; + case 7410: + cacheIndex = 3882; + reference = new EpsgCoordinateReferenceRecord(7410, (EpsgCoordinateSystemKind)4, 126); + return true; + case 7411: + cacheIndex = 3883; + reference = new EpsgCoordinateReferenceRecord(7411, (EpsgCoordinateSystemKind)4, 127); + return true; + case 7414: + cacheIndex = 3884; + reference = new EpsgCoordinateReferenceRecord(7414, (EpsgCoordinateSystemKind)4, 128); + return true; + case 7415: + cacheIndex = 3885; + reference = new EpsgCoordinateReferenceRecord(7415, (EpsgCoordinateSystemKind)4, 129); + return true; + case 7421: + cacheIndex = 3886; + reference = new EpsgCoordinateReferenceRecord(7421, (EpsgCoordinateSystemKind)4, 130); + return true; + case 7422: + cacheIndex = 3887; + reference = new EpsgCoordinateReferenceRecord(7422, (EpsgCoordinateSystemKind)4, 131); + return true; + case 7423: + cacheIndex = 3888; + reference = new EpsgCoordinateReferenceRecord(7423, (EpsgCoordinateSystemKind)4, 132); + return true; + case 7446: + cacheIndex = 3889; + reference = new EpsgCoordinateReferenceRecord(7446, (EpsgCoordinateSystemKind)3, 164); + return true; + case 7447: + cacheIndex = 3890; + reference = new EpsgCoordinateReferenceRecord(7447, (EpsgCoordinateSystemKind)3, 165); + return true; + case 7528: + cacheIndex = 3891; + reference = new EpsgCoordinateReferenceRecord(7528, (EpsgCoordinateSystemKind)2, 2961); + return true; + case 7529: + cacheIndex = 3892; + reference = new EpsgCoordinateReferenceRecord(7529, (EpsgCoordinateSystemKind)2, 2962); + return true; + case 7530: + cacheIndex = 3893; + reference = new EpsgCoordinateReferenceRecord(7530, (EpsgCoordinateSystemKind)2, 2963); + return true; + case 7531: + cacheIndex = 3894; + reference = new EpsgCoordinateReferenceRecord(7531, (EpsgCoordinateSystemKind)2, 2964); + return true; + case 7532: + cacheIndex = 3895; + reference = new EpsgCoordinateReferenceRecord(7532, (EpsgCoordinateSystemKind)2, 2965); + return true; + case 7533: + cacheIndex = 3896; + reference = new EpsgCoordinateReferenceRecord(7533, (EpsgCoordinateSystemKind)2, 2966); + return true; + case 7534: + cacheIndex = 3897; + reference = new EpsgCoordinateReferenceRecord(7534, (EpsgCoordinateSystemKind)2, 2967); + return true; + case 7535: + cacheIndex = 3898; + reference = new EpsgCoordinateReferenceRecord(7535, (EpsgCoordinateSystemKind)2, 2968); + return true; + case 7536: + cacheIndex = 3899; + reference = new EpsgCoordinateReferenceRecord(7536, (EpsgCoordinateSystemKind)2, 2969); + return true; + case 7537: + cacheIndex = 3900; + reference = new EpsgCoordinateReferenceRecord(7537, (EpsgCoordinateSystemKind)2, 2970); + return true; + case 7538: + cacheIndex = 3901; + reference = new EpsgCoordinateReferenceRecord(7538, (EpsgCoordinateSystemKind)2, 2971); + return true; + case 7539: + cacheIndex = 3902; + reference = new EpsgCoordinateReferenceRecord(7539, (EpsgCoordinateSystemKind)2, 2972); + return true; + case 7540: + cacheIndex = 3903; + reference = new EpsgCoordinateReferenceRecord(7540, (EpsgCoordinateSystemKind)2, 2973); + return true; + case 7541: + cacheIndex = 3904; + reference = new EpsgCoordinateReferenceRecord(7541, (EpsgCoordinateSystemKind)2, 2974); + return true; + case 7542: + cacheIndex = 3905; + reference = new EpsgCoordinateReferenceRecord(7542, (EpsgCoordinateSystemKind)2, 2975); + return true; + case 7543: + cacheIndex = 3906; + reference = new EpsgCoordinateReferenceRecord(7543, (EpsgCoordinateSystemKind)2, 2976); + return true; + case 7544: + cacheIndex = 3907; + reference = new EpsgCoordinateReferenceRecord(7544, (EpsgCoordinateSystemKind)2, 2977); + return true; + case 7545: + cacheIndex = 3908; + reference = new EpsgCoordinateReferenceRecord(7545, (EpsgCoordinateSystemKind)2, 2978); + return true; + case 7546: + cacheIndex = 3909; + reference = new EpsgCoordinateReferenceRecord(7546, (EpsgCoordinateSystemKind)2, 2979); + return true; + case 7547: + cacheIndex = 3910; + reference = new EpsgCoordinateReferenceRecord(7547, (EpsgCoordinateSystemKind)2, 2980); + return true; + case 7548: + cacheIndex = 3911; + reference = new EpsgCoordinateReferenceRecord(7548, (EpsgCoordinateSystemKind)2, 2981); + return true; + case 7549: + cacheIndex = 3912; + reference = new EpsgCoordinateReferenceRecord(7549, (EpsgCoordinateSystemKind)2, 2982); + return true; + case 7550: + cacheIndex = 3913; + reference = new EpsgCoordinateReferenceRecord(7550, (EpsgCoordinateSystemKind)2, 2983); + return true; + case 7551: + cacheIndex = 3914; + reference = new EpsgCoordinateReferenceRecord(7551, (EpsgCoordinateSystemKind)2, 2984); + return true; + case 7552: + cacheIndex = 3915; + reference = new EpsgCoordinateReferenceRecord(7552, (EpsgCoordinateSystemKind)2, 2985); + return true; + case 7553: + cacheIndex = 3916; + reference = new EpsgCoordinateReferenceRecord(7553, (EpsgCoordinateSystemKind)2, 2986); + return true; + case 7554: + cacheIndex = 3917; + reference = new EpsgCoordinateReferenceRecord(7554, (EpsgCoordinateSystemKind)2, 2987); + return true; + case 7555: + cacheIndex = 3918; + reference = new EpsgCoordinateReferenceRecord(7555, (EpsgCoordinateSystemKind)2, 2988); + return true; + case 7556: + cacheIndex = 3919; + reference = new EpsgCoordinateReferenceRecord(7556, (EpsgCoordinateSystemKind)2, 2989); + return true; + case 7557: + cacheIndex = 3920; + reference = new EpsgCoordinateReferenceRecord(7557, (EpsgCoordinateSystemKind)2, 2990); + return true; + case 7558: + cacheIndex = 3921; + reference = new EpsgCoordinateReferenceRecord(7558, (EpsgCoordinateSystemKind)2, 2991); + return true; + case 7559: + cacheIndex = 3922; + reference = new EpsgCoordinateReferenceRecord(7559, (EpsgCoordinateSystemKind)2, 2992); + return true; + case 7560: + cacheIndex = 3923; + reference = new EpsgCoordinateReferenceRecord(7560, (EpsgCoordinateSystemKind)2, 2993); + return true; + case 7561: + cacheIndex = 3924; + reference = new EpsgCoordinateReferenceRecord(7561, (EpsgCoordinateSystemKind)2, 2994); + return true; + case 7562: + cacheIndex = 3925; + reference = new EpsgCoordinateReferenceRecord(7562, (EpsgCoordinateSystemKind)2, 2995); + return true; + case 7563: + cacheIndex = 3926; + reference = new EpsgCoordinateReferenceRecord(7563, (EpsgCoordinateSystemKind)2, 2996); + return true; + case 7564: + cacheIndex = 3927; + reference = new EpsgCoordinateReferenceRecord(7564, (EpsgCoordinateSystemKind)2, 2997); + return true; + case 7565: + cacheIndex = 3928; + reference = new EpsgCoordinateReferenceRecord(7565, (EpsgCoordinateSystemKind)2, 2998); + return true; + case 7566: + cacheIndex = 3929; + reference = new EpsgCoordinateReferenceRecord(7566, (EpsgCoordinateSystemKind)2, 2999); + return true; + case 7567: + cacheIndex = 3930; + reference = new EpsgCoordinateReferenceRecord(7567, (EpsgCoordinateSystemKind)2, 3000); + return true; + case 7568: + cacheIndex = 3931; + reference = new EpsgCoordinateReferenceRecord(7568, (EpsgCoordinateSystemKind)2, 3001); + return true; + case 7569: + cacheIndex = 3932; + reference = new EpsgCoordinateReferenceRecord(7569, (EpsgCoordinateSystemKind)2, 3002); + return true; + case 7570: + cacheIndex = 3933; + reference = new EpsgCoordinateReferenceRecord(7570, (EpsgCoordinateSystemKind)2, 3003); + return true; + case 7571: + cacheIndex = 3934; + reference = new EpsgCoordinateReferenceRecord(7571, (EpsgCoordinateSystemKind)2, 3004); + return true; + case 7572: + cacheIndex = 3935; + reference = new EpsgCoordinateReferenceRecord(7572, (EpsgCoordinateSystemKind)2, 3005); + return true; + case 7573: + cacheIndex = 3936; + reference = new EpsgCoordinateReferenceRecord(7573, (EpsgCoordinateSystemKind)2, 3006); + return true; + case 7574: + cacheIndex = 3937; + reference = new EpsgCoordinateReferenceRecord(7574, (EpsgCoordinateSystemKind)2, 3007); + return true; + case 7575: + cacheIndex = 3938; + reference = new EpsgCoordinateReferenceRecord(7575, (EpsgCoordinateSystemKind)2, 3008); + return true; + case 7576: + cacheIndex = 3939; + reference = new EpsgCoordinateReferenceRecord(7576, (EpsgCoordinateSystemKind)2, 3009); + return true; + case 7577: + cacheIndex = 3940; + reference = new EpsgCoordinateReferenceRecord(7577, (EpsgCoordinateSystemKind)2, 3010); + return true; + case 7578: + cacheIndex = 3941; + reference = new EpsgCoordinateReferenceRecord(7578, (EpsgCoordinateSystemKind)2, 3011); + return true; + case 7579: + cacheIndex = 3942; + reference = new EpsgCoordinateReferenceRecord(7579, (EpsgCoordinateSystemKind)2, 3012); + return true; + case 7580: + cacheIndex = 3943; + reference = new EpsgCoordinateReferenceRecord(7580, (EpsgCoordinateSystemKind)2, 3013); + return true; + case 7581: + cacheIndex = 3944; + reference = new EpsgCoordinateReferenceRecord(7581, (EpsgCoordinateSystemKind)2, 3014); + return true; + case 7582: + cacheIndex = 3945; + reference = new EpsgCoordinateReferenceRecord(7582, (EpsgCoordinateSystemKind)2, 3015); + return true; + case 7583: + cacheIndex = 3946; + reference = new EpsgCoordinateReferenceRecord(7583, (EpsgCoordinateSystemKind)2, 3016); + return true; + case 7584: + cacheIndex = 3947; + reference = new EpsgCoordinateReferenceRecord(7584, (EpsgCoordinateSystemKind)2, 3017); + return true; + case 7585: + cacheIndex = 3948; + reference = new EpsgCoordinateReferenceRecord(7585, (EpsgCoordinateSystemKind)2, 3018); + return true; + case 7586: + cacheIndex = 3949; + reference = new EpsgCoordinateReferenceRecord(7586, (EpsgCoordinateSystemKind)2, 3019); + return true; + case 7587: + cacheIndex = 3950; + reference = new EpsgCoordinateReferenceRecord(7587, (EpsgCoordinateSystemKind)2, 3020); + return true; + case 7588: + cacheIndex = 3951; + reference = new EpsgCoordinateReferenceRecord(7588, (EpsgCoordinateSystemKind)2, 3021); + return true; + case 7589: + cacheIndex = 3952; + reference = new EpsgCoordinateReferenceRecord(7589, (EpsgCoordinateSystemKind)2, 3022); + return true; + case 7590: + cacheIndex = 3953; + reference = new EpsgCoordinateReferenceRecord(7590, (EpsgCoordinateSystemKind)2, 3023); + return true; + case 7591: + cacheIndex = 3954; + reference = new EpsgCoordinateReferenceRecord(7591, (EpsgCoordinateSystemKind)2, 3024); + return true; + case 7592: + cacheIndex = 3955; + reference = new EpsgCoordinateReferenceRecord(7592, (EpsgCoordinateSystemKind)2, 3025); + return true; + case 7593: + cacheIndex = 3956; + reference = new EpsgCoordinateReferenceRecord(7593, (EpsgCoordinateSystemKind)2, 3026); + return true; + case 7594: + cacheIndex = 3957; + reference = new EpsgCoordinateReferenceRecord(7594, (EpsgCoordinateSystemKind)2, 3027); + return true; + case 7595: + cacheIndex = 3958; + reference = new EpsgCoordinateReferenceRecord(7595, (EpsgCoordinateSystemKind)2, 3028); + return true; + case 7596: + cacheIndex = 3959; + reference = new EpsgCoordinateReferenceRecord(7596, (EpsgCoordinateSystemKind)2, 3029); + return true; + case 7597: + cacheIndex = 3960; + reference = new EpsgCoordinateReferenceRecord(7597, (EpsgCoordinateSystemKind)2, 3030); + return true; + case 7598: + cacheIndex = 3961; + reference = new EpsgCoordinateReferenceRecord(7598, (EpsgCoordinateSystemKind)2, 3031); + return true; + case 7599: + cacheIndex = 3962; + reference = new EpsgCoordinateReferenceRecord(7599, (EpsgCoordinateSystemKind)2, 3032); + return true; + case 7600: + cacheIndex = 3963; + reference = new EpsgCoordinateReferenceRecord(7600, (EpsgCoordinateSystemKind)2, 3033); + return true; + case 7601: + cacheIndex = 3964; + reference = new EpsgCoordinateReferenceRecord(7601, (EpsgCoordinateSystemKind)2, 3034); + return true; + case 7602: + cacheIndex = 3965; + reference = new EpsgCoordinateReferenceRecord(7602, (EpsgCoordinateSystemKind)2, 3035); + return true; + case 7603: + cacheIndex = 3966; + reference = new EpsgCoordinateReferenceRecord(7603, (EpsgCoordinateSystemKind)2, 3036); + return true; + case 7604: + cacheIndex = 3967; + reference = new EpsgCoordinateReferenceRecord(7604, (EpsgCoordinateSystemKind)2, 3037); + return true; + case 7605: + cacheIndex = 3968; + reference = new EpsgCoordinateReferenceRecord(7605, (EpsgCoordinateSystemKind)2, 3038); + return true; + case 7606: + cacheIndex = 3969; + reference = new EpsgCoordinateReferenceRecord(7606, (EpsgCoordinateSystemKind)2, 3039); + return true; + case 7607: + cacheIndex = 3970; + reference = new EpsgCoordinateReferenceRecord(7607, (EpsgCoordinateSystemKind)2, 3040); + return true; + case 7608: + cacheIndex = 3971; + reference = new EpsgCoordinateReferenceRecord(7608, (EpsgCoordinateSystemKind)2, 3041); + return true; + case 7609: + cacheIndex = 3972; + reference = new EpsgCoordinateReferenceRecord(7609, (EpsgCoordinateSystemKind)2, 3042); + return true; + case 7610: + cacheIndex = 3973; + reference = new EpsgCoordinateReferenceRecord(7610, (EpsgCoordinateSystemKind)2, 3043); + return true; + case 7611: + cacheIndex = 3974; + reference = new EpsgCoordinateReferenceRecord(7611, (EpsgCoordinateSystemKind)2, 3044); + return true; + case 7612: + cacheIndex = 3975; + reference = new EpsgCoordinateReferenceRecord(7612, (EpsgCoordinateSystemKind)2, 3045); + return true; + case 7613: + cacheIndex = 3976; + reference = new EpsgCoordinateReferenceRecord(7613, (EpsgCoordinateSystemKind)2, 3046); + return true; + case 7614: + cacheIndex = 3977; + reference = new EpsgCoordinateReferenceRecord(7614, (EpsgCoordinateSystemKind)2, 3047); + return true; + case 7615: + cacheIndex = 3978; + reference = new EpsgCoordinateReferenceRecord(7615, (EpsgCoordinateSystemKind)2, 3048); + return true; + case 7616: + cacheIndex = 3979; + reference = new EpsgCoordinateReferenceRecord(7616, (EpsgCoordinateSystemKind)2, 3049); + return true; + case 7617: + cacheIndex = 3980; + reference = new EpsgCoordinateReferenceRecord(7617, (EpsgCoordinateSystemKind)2, 3050); + return true; + case 7618: + cacheIndex = 3981; + reference = new EpsgCoordinateReferenceRecord(7618, (EpsgCoordinateSystemKind)2, 3051); + return true; + case 7619: + cacheIndex = 3982; + reference = new EpsgCoordinateReferenceRecord(7619, (EpsgCoordinateSystemKind)2, 3052); + return true; + case 7620: + cacheIndex = 3983; + reference = new EpsgCoordinateReferenceRecord(7620, (EpsgCoordinateSystemKind)2, 3053); + return true; + case 7621: + cacheIndex = 3984; + reference = new EpsgCoordinateReferenceRecord(7621, (EpsgCoordinateSystemKind)2, 3054); + return true; + case 7622: + cacheIndex = 3985; + reference = new EpsgCoordinateReferenceRecord(7622, (EpsgCoordinateSystemKind)2, 3055); + return true; + case 7623: + cacheIndex = 3986; + reference = new EpsgCoordinateReferenceRecord(7623, (EpsgCoordinateSystemKind)2, 3056); + return true; + case 7624: + cacheIndex = 3987; + reference = new EpsgCoordinateReferenceRecord(7624, (EpsgCoordinateSystemKind)2, 3057); + return true; + case 7625: + cacheIndex = 3988; + reference = new EpsgCoordinateReferenceRecord(7625, (EpsgCoordinateSystemKind)2, 3058); + return true; + case 7626: + cacheIndex = 3989; + reference = new EpsgCoordinateReferenceRecord(7626, (EpsgCoordinateSystemKind)2, 3059); + return true; + case 7627: + cacheIndex = 3990; + reference = new EpsgCoordinateReferenceRecord(7627, (EpsgCoordinateSystemKind)2, 3060); + return true; + case 7628: + cacheIndex = 3991; + reference = new EpsgCoordinateReferenceRecord(7628, (EpsgCoordinateSystemKind)2, 3061); + return true; + case 7629: + cacheIndex = 3992; + reference = new EpsgCoordinateReferenceRecord(7629, (EpsgCoordinateSystemKind)2, 3062); + return true; + case 7630: + cacheIndex = 3993; + reference = new EpsgCoordinateReferenceRecord(7630, (EpsgCoordinateSystemKind)2, 3063); + return true; + case 7631: + cacheIndex = 3994; + reference = new EpsgCoordinateReferenceRecord(7631, (EpsgCoordinateSystemKind)2, 3064); + return true; + case 7632: + cacheIndex = 3995; + reference = new EpsgCoordinateReferenceRecord(7632, (EpsgCoordinateSystemKind)2, 3065); + return true; + case 7633: + cacheIndex = 3996; + reference = new EpsgCoordinateReferenceRecord(7633, (EpsgCoordinateSystemKind)2, 3066); + return true; + case 7634: + cacheIndex = 3997; + reference = new EpsgCoordinateReferenceRecord(7634, (EpsgCoordinateSystemKind)2, 3067); + return true; + case 7635: + cacheIndex = 3998; + reference = new EpsgCoordinateReferenceRecord(7635, (EpsgCoordinateSystemKind)2, 3068); + return true; + case 7636: + cacheIndex = 3999; + reference = new EpsgCoordinateReferenceRecord(7636, (EpsgCoordinateSystemKind)2, 3069); + return true; + case 7637: + cacheIndex = 4000; + reference = new EpsgCoordinateReferenceRecord(7637, (EpsgCoordinateSystemKind)2, 3070); + return true; + case 7638: + cacheIndex = 4001; + reference = new EpsgCoordinateReferenceRecord(7638, (EpsgCoordinateSystemKind)2, 3071); + return true; + case 7639: + cacheIndex = 4002; + reference = new EpsgCoordinateReferenceRecord(7639, (EpsgCoordinateSystemKind)2, 3072); + return true; + case 7640: + cacheIndex = 4003; + reference = new EpsgCoordinateReferenceRecord(7640, (EpsgCoordinateSystemKind)2, 3073); + return true; + case 7641: + cacheIndex = 4004; + reference = new EpsgCoordinateReferenceRecord(7641, (EpsgCoordinateSystemKind)2, 3074); + return true; + case 7642: + cacheIndex = 4005; + reference = new EpsgCoordinateReferenceRecord(7642, (EpsgCoordinateSystemKind)2, 3075); + return true; + case 7643: + cacheIndex = 4006; + reference = new EpsgCoordinateReferenceRecord(7643, (EpsgCoordinateSystemKind)2, 3076); + return true; + case 7644: + cacheIndex = 4007; + reference = new EpsgCoordinateReferenceRecord(7644, (EpsgCoordinateSystemKind)2, 3077); + return true; + case 7645: + cacheIndex = 4008; + reference = new EpsgCoordinateReferenceRecord(7645, (EpsgCoordinateSystemKind)2, 3078); + return true; + case 7651: + cacheIndex = 4009; + reference = new EpsgCoordinateReferenceRecord(7651, (EpsgCoordinateSystemKind)3, 166); + return true; + case 7652: + cacheIndex = 4010; + reference = new EpsgCoordinateReferenceRecord(7652, (EpsgCoordinateSystemKind)3, 167); + return true; + case 7656: + cacheIndex = 4011; + reference = new EpsgCoordinateReferenceRecord(7656, (EpsgCoordinateSystemKind)1, 108); + return true; + case 7657: + cacheIndex = 4012; + reference = new EpsgCoordinateReferenceRecord(7657, (EpsgCoordinateSystemKind)0, 523); + return true; + case 7658: + cacheIndex = 4013; + reference = new EpsgCoordinateReferenceRecord(7658, (EpsgCoordinateSystemKind)1, 109); + return true; + case 7659: + cacheIndex = 4014; + reference = new EpsgCoordinateReferenceRecord(7659, (EpsgCoordinateSystemKind)0, 524); + return true; + case 7660: + cacheIndex = 4015; + reference = new EpsgCoordinateReferenceRecord(7660, (EpsgCoordinateSystemKind)1, 110); + return true; + case 7661: + cacheIndex = 4016; + reference = new EpsgCoordinateReferenceRecord(7661, (EpsgCoordinateSystemKind)0, 525); + return true; + case 7662: + cacheIndex = 4017; + reference = new EpsgCoordinateReferenceRecord(7662, (EpsgCoordinateSystemKind)1, 111); + return true; + case 7663: + cacheIndex = 4018; + reference = new EpsgCoordinateReferenceRecord(7663, (EpsgCoordinateSystemKind)0, 526); + return true; + case 7664: + cacheIndex = 4019; + reference = new EpsgCoordinateReferenceRecord(7664, (EpsgCoordinateSystemKind)1, 112); + return true; + case 7665: + cacheIndex = 4020; + reference = new EpsgCoordinateReferenceRecord(7665, (EpsgCoordinateSystemKind)0, 527); + return true; + case 7677: + cacheIndex = 4021; + reference = new EpsgCoordinateReferenceRecord(7677, (EpsgCoordinateSystemKind)1, 113); + return true; + case 7678: + cacheIndex = 4022; + reference = new EpsgCoordinateReferenceRecord(7678, (EpsgCoordinateSystemKind)0, 528); + return true; + case 7679: + cacheIndex = 4023; + reference = new EpsgCoordinateReferenceRecord(7679, (EpsgCoordinateSystemKind)1, 114); + return true; + case 7680: + cacheIndex = 4024; + reference = new EpsgCoordinateReferenceRecord(7680, (EpsgCoordinateSystemKind)0, 529); + return true; + case 7681: + cacheIndex = 4025; + reference = new EpsgCoordinateReferenceRecord(7681, (EpsgCoordinateSystemKind)1, 115); + return true; + case 7682: + cacheIndex = 4026; + reference = new EpsgCoordinateReferenceRecord(7682, (EpsgCoordinateSystemKind)0, 530); + return true; + case 7683: + cacheIndex = 4027; + reference = new EpsgCoordinateReferenceRecord(7683, (EpsgCoordinateSystemKind)0, 531); + return true; + case 7684: + cacheIndex = 4028; + reference = new EpsgCoordinateReferenceRecord(7684, (EpsgCoordinateSystemKind)1, 116); + return true; + case 7685: + cacheIndex = 4029; + reference = new EpsgCoordinateReferenceRecord(7685, (EpsgCoordinateSystemKind)0, 532); + return true; + case 7686: + cacheIndex = 4030; + reference = new EpsgCoordinateReferenceRecord(7686, (EpsgCoordinateSystemKind)0, 533); + return true; + case 7692: + cacheIndex = 4031; + reference = new EpsgCoordinateReferenceRecord(7692, (EpsgCoordinateSystemKind)2, 3079); + return true; + case 7693: + cacheIndex = 4032; + reference = new EpsgCoordinateReferenceRecord(7693, (EpsgCoordinateSystemKind)2, 3080); + return true; + case 7694: + cacheIndex = 4033; + reference = new EpsgCoordinateReferenceRecord(7694, (EpsgCoordinateSystemKind)2, 3081); + return true; + case 7695: + cacheIndex = 4034; + reference = new EpsgCoordinateReferenceRecord(7695, (EpsgCoordinateSystemKind)2, 3082); + return true; + case 7696: + cacheIndex = 4035; + reference = new EpsgCoordinateReferenceRecord(7696, (EpsgCoordinateSystemKind)2, 3083); + return true; + case 7699: + cacheIndex = 4036; + reference = new EpsgCoordinateReferenceRecord(7699, (EpsgCoordinateSystemKind)3, 168); + return true; + case 7700: + cacheIndex = 4037; + reference = new EpsgCoordinateReferenceRecord(7700, (EpsgCoordinateSystemKind)3, 169); + return true; + case 7707: + cacheIndex = 4038; + reference = new EpsgCoordinateReferenceRecord(7707, (EpsgCoordinateSystemKind)3, 170); + return true; + case 7755: + cacheIndex = 4039; + reference = new EpsgCoordinateReferenceRecord(7755, (EpsgCoordinateSystemKind)2, 3084); + return true; + case 7756: + cacheIndex = 4040; + reference = new EpsgCoordinateReferenceRecord(7756, (EpsgCoordinateSystemKind)2, 3085); + return true; + case 7757: + cacheIndex = 4041; + reference = new EpsgCoordinateReferenceRecord(7757, (EpsgCoordinateSystemKind)2, 3086); + return true; + case 7758: + cacheIndex = 4042; + reference = new EpsgCoordinateReferenceRecord(7758, (EpsgCoordinateSystemKind)2, 3087); + return true; + case 7759: + cacheIndex = 4043; + reference = new EpsgCoordinateReferenceRecord(7759, (EpsgCoordinateSystemKind)2, 3088); + return true; + case 7760: + cacheIndex = 4044; + reference = new EpsgCoordinateReferenceRecord(7760, (EpsgCoordinateSystemKind)2, 3089); + return true; + case 7761: + cacheIndex = 4045; + reference = new EpsgCoordinateReferenceRecord(7761, (EpsgCoordinateSystemKind)2, 3090); + return true; + case 7762: + cacheIndex = 4046; + reference = new EpsgCoordinateReferenceRecord(7762, (EpsgCoordinateSystemKind)2, 3091); + return true; + case 7763: + cacheIndex = 4047; + reference = new EpsgCoordinateReferenceRecord(7763, (EpsgCoordinateSystemKind)2, 3092); + return true; + case 7764: + cacheIndex = 4048; + reference = new EpsgCoordinateReferenceRecord(7764, (EpsgCoordinateSystemKind)2, 3093); + return true; + case 7765: + cacheIndex = 4049; + reference = new EpsgCoordinateReferenceRecord(7765, (EpsgCoordinateSystemKind)2, 3094); + return true; + case 7766: + cacheIndex = 4050; + reference = new EpsgCoordinateReferenceRecord(7766, (EpsgCoordinateSystemKind)2, 3095); + return true; + case 7767: + cacheIndex = 4051; + reference = new EpsgCoordinateReferenceRecord(7767, (EpsgCoordinateSystemKind)2, 3096); + return true; + case 7768: + cacheIndex = 4052; + reference = new EpsgCoordinateReferenceRecord(7768, (EpsgCoordinateSystemKind)2, 3097); + return true; + case 7769: + cacheIndex = 4053; + reference = new EpsgCoordinateReferenceRecord(7769, (EpsgCoordinateSystemKind)2, 3098); + return true; + case 7770: + cacheIndex = 4054; + reference = new EpsgCoordinateReferenceRecord(7770, (EpsgCoordinateSystemKind)2, 3099); + return true; + case 7771: + cacheIndex = 4055; + reference = new EpsgCoordinateReferenceRecord(7771, (EpsgCoordinateSystemKind)2, 3100); + return true; + case 7772: + cacheIndex = 4056; + reference = new EpsgCoordinateReferenceRecord(7772, (EpsgCoordinateSystemKind)2, 3101); + return true; + case 7773: + cacheIndex = 4057; + reference = new EpsgCoordinateReferenceRecord(7773, (EpsgCoordinateSystemKind)2, 3102); + return true; + case 7774: + cacheIndex = 4058; + reference = new EpsgCoordinateReferenceRecord(7774, (EpsgCoordinateSystemKind)2, 3103); + return true; + case 7775: + cacheIndex = 4059; + reference = new EpsgCoordinateReferenceRecord(7775, (EpsgCoordinateSystemKind)2, 3104); + return true; + case 7776: + cacheIndex = 4060; + reference = new EpsgCoordinateReferenceRecord(7776, (EpsgCoordinateSystemKind)2, 3105); + return true; + case 7777: + cacheIndex = 4061; + reference = new EpsgCoordinateReferenceRecord(7777, (EpsgCoordinateSystemKind)2, 3106); + return true; + case 7778: + cacheIndex = 4062; + reference = new EpsgCoordinateReferenceRecord(7778, (EpsgCoordinateSystemKind)2, 3107); + return true; + case 7779: + cacheIndex = 4063; + reference = new EpsgCoordinateReferenceRecord(7779, (EpsgCoordinateSystemKind)2, 3108); + return true; + case 7780: + cacheIndex = 4064; + reference = new EpsgCoordinateReferenceRecord(7780, (EpsgCoordinateSystemKind)2, 3109); + return true; + case 7781: + cacheIndex = 4065; + reference = new EpsgCoordinateReferenceRecord(7781, (EpsgCoordinateSystemKind)2, 3110); + return true; + case 7782: + cacheIndex = 4066; + reference = new EpsgCoordinateReferenceRecord(7782, (EpsgCoordinateSystemKind)2, 3111); + return true; + case 7783: + cacheIndex = 4067; + reference = new EpsgCoordinateReferenceRecord(7783, (EpsgCoordinateSystemKind)2, 3112); + return true; + case 7784: + cacheIndex = 4068; + reference = new EpsgCoordinateReferenceRecord(7784, (EpsgCoordinateSystemKind)2, 3113); + return true; + case 7785: + cacheIndex = 4069; + reference = new EpsgCoordinateReferenceRecord(7785, (EpsgCoordinateSystemKind)2, 3114); + return true; + case 7786: + cacheIndex = 4070; + reference = new EpsgCoordinateReferenceRecord(7786, (EpsgCoordinateSystemKind)2, 3115); + return true; + case 7787: + cacheIndex = 4071; + reference = new EpsgCoordinateReferenceRecord(7787, (EpsgCoordinateSystemKind)2, 3116); + return true; + case 7789: + cacheIndex = 4072; + reference = new EpsgCoordinateReferenceRecord(7789, (EpsgCoordinateSystemKind)1, 117); + return true; + case 7791: + cacheIndex = 4073; + reference = new EpsgCoordinateReferenceRecord(7791, (EpsgCoordinateSystemKind)2, 3117); + return true; + case 7792: + cacheIndex = 4074; + reference = new EpsgCoordinateReferenceRecord(7792, (EpsgCoordinateSystemKind)2, 3118); + return true; + case 7793: + cacheIndex = 4075; + reference = new EpsgCoordinateReferenceRecord(7793, (EpsgCoordinateSystemKind)2, 3119); + return true; + case 7794: + cacheIndex = 4076; + reference = new EpsgCoordinateReferenceRecord(7794, (EpsgCoordinateSystemKind)2, 3120); + return true; + case 7795: + cacheIndex = 4077; + reference = new EpsgCoordinateReferenceRecord(7795, (EpsgCoordinateSystemKind)2, 3121); + return true; + case 7796: + cacheIndex = 4078; + reference = new EpsgCoordinateReferenceRecord(7796, (EpsgCoordinateSystemKind)1, 118); + return true; + case 7797: + cacheIndex = 4079; + reference = new EpsgCoordinateReferenceRecord(7797, (EpsgCoordinateSystemKind)0, 534); + return true; + case 7798: + cacheIndex = 4080; + reference = new EpsgCoordinateReferenceRecord(7798, (EpsgCoordinateSystemKind)0, 535); + return true; + case 7799: + cacheIndex = 4081; + reference = new EpsgCoordinateReferenceRecord(7799, (EpsgCoordinateSystemKind)2, 3122); + return true; + case 7800: + cacheIndex = 4082; + reference = new EpsgCoordinateReferenceRecord(7800, (EpsgCoordinateSystemKind)2, 3123); + return true; + case 7801: + cacheIndex = 4083; + reference = new EpsgCoordinateReferenceRecord(7801, (EpsgCoordinateSystemKind)2, 3124); + return true; + case 7803: + cacheIndex = 4084; + reference = new EpsgCoordinateReferenceRecord(7803, (EpsgCoordinateSystemKind)2, 3125); + return true; + case 7805: + cacheIndex = 4085; + reference = new EpsgCoordinateReferenceRecord(7805, (EpsgCoordinateSystemKind)2, 3126); + return true; + case 7815: + cacheIndex = 4086; + reference = new EpsgCoordinateReferenceRecord(7815, (EpsgCoordinateSystemKind)1, 119); + return true; + case 7816: + cacheIndex = 4087; + reference = new EpsgCoordinateReferenceRecord(7816, (EpsgCoordinateSystemKind)0, 536); + return true; + case 7825: + cacheIndex = 4088; + reference = new EpsgCoordinateReferenceRecord(7825, (EpsgCoordinateSystemKind)2, 3127); + return true; + case 7826: + cacheIndex = 4089; + reference = new EpsgCoordinateReferenceRecord(7826, (EpsgCoordinateSystemKind)2, 3128); + return true; + case 7827: + cacheIndex = 4090; + reference = new EpsgCoordinateReferenceRecord(7827, (EpsgCoordinateSystemKind)2, 3129); + return true; + case 7828: + cacheIndex = 4091; + reference = new EpsgCoordinateReferenceRecord(7828, (EpsgCoordinateSystemKind)2, 3130); + return true; + case 7829: + cacheIndex = 4092; + reference = new EpsgCoordinateReferenceRecord(7829, (EpsgCoordinateSystemKind)2, 3131); + return true; + case 7830: + cacheIndex = 4093; + reference = new EpsgCoordinateReferenceRecord(7830, (EpsgCoordinateSystemKind)2, 3132); + return true; + case 7831: + cacheIndex = 4094; + reference = new EpsgCoordinateReferenceRecord(7831, (EpsgCoordinateSystemKind)2, 3133); + return true; + case 7832: + cacheIndex = 4095; + reference = new EpsgCoordinateReferenceRecord(7832, (EpsgCoordinateSystemKind)3, 171); + return true; + case 7837: + cacheIndex = 4096; + reference = new EpsgCoordinateReferenceRecord(7837, (EpsgCoordinateSystemKind)3, 172); + return true; + case 7839: + cacheIndex = 4097; + reference = new EpsgCoordinateReferenceRecord(7839, (EpsgCoordinateSystemKind)3, 173); + return true; + case 7841: + cacheIndex = 4098; + reference = new EpsgCoordinateReferenceRecord(7841, (EpsgCoordinateSystemKind)3, 174); + return true; + case 7842: + cacheIndex = 4099; + reference = new EpsgCoordinateReferenceRecord(7842, (EpsgCoordinateSystemKind)1, 120); + return true; + case 7843: + cacheIndex = 4100; + reference = new EpsgCoordinateReferenceRecord(7843, (EpsgCoordinateSystemKind)0, 537); + return true; + case 7844: + cacheIndex = 4101; + reference = new EpsgCoordinateReferenceRecord(7844, (EpsgCoordinateSystemKind)0, 538); + return true; + case 7845: + cacheIndex = 4102; + reference = new EpsgCoordinateReferenceRecord(7845, (EpsgCoordinateSystemKind)2, 3134); + return true; + case 7846: + cacheIndex = 4103; + reference = new EpsgCoordinateReferenceRecord(7846, (EpsgCoordinateSystemKind)2, 3135); + return true; + case 7847: + cacheIndex = 4104; + reference = new EpsgCoordinateReferenceRecord(7847, (EpsgCoordinateSystemKind)2, 3136); + return true; + case 7848: + cacheIndex = 4105; + reference = new EpsgCoordinateReferenceRecord(7848, (EpsgCoordinateSystemKind)2, 3137); + return true; + case 7849: + cacheIndex = 4106; + reference = new EpsgCoordinateReferenceRecord(7849, (EpsgCoordinateSystemKind)2, 3138); + return true; + case 7850: + cacheIndex = 4107; + reference = new EpsgCoordinateReferenceRecord(7850, (EpsgCoordinateSystemKind)2, 3139); + return true; + case 7851: + cacheIndex = 4108; + reference = new EpsgCoordinateReferenceRecord(7851, (EpsgCoordinateSystemKind)2, 3140); + return true; + case 7852: + cacheIndex = 4109; + reference = new EpsgCoordinateReferenceRecord(7852, (EpsgCoordinateSystemKind)2, 3141); + return true; + case 7853: + cacheIndex = 4110; + reference = new EpsgCoordinateReferenceRecord(7853, (EpsgCoordinateSystemKind)2, 3142); + return true; + case 7854: + cacheIndex = 4111; + reference = new EpsgCoordinateReferenceRecord(7854, (EpsgCoordinateSystemKind)2, 3143); + return true; + case 7855: + cacheIndex = 4112; + reference = new EpsgCoordinateReferenceRecord(7855, (EpsgCoordinateSystemKind)2, 3144); + return true; + case 7856: + cacheIndex = 4113; + reference = new EpsgCoordinateReferenceRecord(7856, (EpsgCoordinateSystemKind)2, 3145); + return true; + case 7857: + cacheIndex = 4114; + reference = new EpsgCoordinateReferenceRecord(7857, (EpsgCoordinateSystemKind)2, 3146); + return true; + case 7858: + cacheIndex = 4115; + reference = new EpsgCoordinateReferenceRecord(7858, (EpsgCoordinateSystemKind)2, 3147); + return true; + case 7859: + cacheIndex = 4116; + reference = new EpsgCoordinateReferenceRecord(7859, (EpsgCoordinateSystemKind)2, 3148); + return true; + case 7877: + cacheIndex = 4117; + reference = new EpsgCoordinateReferenceRecord(7877, (EpsgCoordinateSystemKind)2, 3149); + return true; + case 7878: + cacheIndex = 4118; + reference = new EpsgCoordinateReferenceRecord(7878, (EpsgCoordinateSystemKind)2, 3150); + return true; + case 7879: + cacheIndex = 4119; + reference = new EpsgCoordinateReferenceRecord(7879, (EpsgCoordinateSystemKind)1, 121); + return true; + case 7880: + cacheIndex = 4120; + reference = new EpsgCoordinateReferenceRecord(7880, (EpsgCoordinateSystemKind)0, 539); + return true; + case 7881: + cacheIndex = 4121; + reference = new EpsgCoordinateReferenceRecord(7881, (EpsgCoordinateSystemKind)0, 540); + return true; + case 7882: + cacheIndex = 4122; + reference = new EpsgCoordinateReferenceRecord(7882, (EpsgCoordinateSystemKind)2, 3151); + return true; + case 7883: + cacheIndex = 4123; + reference = new EpsgCoordinateReferenceRecord(7883, (EpsgCoordinateSystemKind)2, 3152); + return true; + case 7884: + cacheIndex = 4124; + reference = new EpsgCoordinateReferenceRecord(7884, (EpsgCoordinateSystemKind)1, 122); + return true; + case 7885: + cacheIndex = 4125; + reference = new EpsgCoordinateReferenceRecord(7885, (EpsgCoordinateSystemKind)0, 541); + return true; + case 7886: + cacheIndex = 4126; + reference = new EpsgCoordinateReferenceRecord(7886, (EpsgCoordinateSystemKind)0, 542); + return true; + case 7887: + cacheIndex = 4127; + reference = new EpsgCoordinateReferenceRecord(7887, (EpsgCoordinateSystemKind)2, 3153); + return true; + case 7888: + cacheIndex = 4128; + reference = new EpsgCoordinateReferenceRecord(7888, (EpsgCoordinateSystemKind)3, 175); + return true; + case 7889: + cacheIndex = 4129; + reference = new EpsgCoordinateReferenceRecord(7889, (EpsgCoordinateSystemKind)3, 176); + return true; + case 7890: + cacheIndex = 4130; + reference = new EpsgCoordinateReferenceRecord(7890, (EpsgCoordinateSystemKind)3, 177); + return true; + case 7899: + cacheIndex = 4131; + reference = new EpsgCoordinateReferenceRecord(7899, (EpsgCoordinateSystemKind)2, 3154); + return true; + case 7900: + cacheIndex = 4132; + reference = new EpsgCoordinateReferenceRecord(7900, (EpsgCoordinateSystemKind)0, 543); + return true; + case 7901: + cacheIndex = 4133; + reference = new EpsgCoordinateReferenceRecord(7901, (EpsgCoordinateSystemKind)0, 544); + return true; + case 7902: + cacheIndex = 4134; + reference = new EpsgCoordinateReferenceRecord(7902, (EpsgCoordinateSystemKind)0, 545); + return true; + case 7903: + cacheIndex = 4135; + reference = new EpsgCoordinateReferenceRecord(7903, (EpsgCoordinateSystemKind)0, 546); + return true; + case 7904: + cacheIndex = 4136; + reference = new EpsgCoordinateReferenceRecord(7904, (EpsgCoordinateSystemKind)0, 547); + return true; + case 7905: + cacheIndex = 4137; + reference = new EpsgCoordinateReferenceRecord(7905, (EpsgCoordinateSystemKind)0, 548); + return true; + case 7906: + cacheIndex = 4138; + reference = new EpsgCoordinateReferenceRecord(7906, (EpsgCoordinateSystemKind)0, 549); + return true; + case 7907: + cacheIndex = 4139; + reference = new EpsgCoordinateReferenceRecord(7907, (EpsgCoordinateSystemKind)0, 550); + return true; + case 7908: + cacheIndex = 4140; + reference = new EpsgCoordinateReferenceRecord(7908, (EpsgCoordinateSystemKind)0, 551); + return true; + case 7909: + cacheIndex = 4141; + reference = new EpsgCoordinateReferenceRecord(7909, (EpsgCoordinateSystemKind)0, 552); + return true; + case 7910: + cacheIndex = 4142; + reference = new EpsgCoordinateReferenceRecord(7910, (EpsgCoordinateSystemKind)0, 553); + return true; + case 7911: + cacheIndex = 4143; + reference = new EpsgCoordinateReferenceRecord(7911, (EpsgCoordinateSystemKind)0, 554); + return true; + case 7912: + cacheIndex = 4144; + reference = new EpsgCoordinateReferenceRecord(7912, (EpsgCoordinateSystemKind)0, 555); + return true; + case 7914: + cacheIndex = 4145; + reference = new EpsgCoordinateReferenceRecord(7914, (EpsgCoordinateSystemKind)1, 123); + return true; + case 7915: + cacheIndex = 4146; + reference = new EpsgCoordinateReferenceRecord(7915, (EpsgCoordinateSystemKind)0, 556); + return true; + case 7916: + cacheIndex = 4147; + reference = new EpsgCoordinateReferenceRecord(7916, (EpsgCoordinateSystemKind)1, 124); + return true; + case 7917: + cacheIndex = 4148; + reference = new EpsgCoordinateReferenceRecord(7917, (EpsgCoordinateSystemKind)0, 557); + return true; + case 7918: + cacheIndex = 4149; + reference = new EpsgCoordinateReferenceRecord(7918, (EpsgCoordinateSystemKind)1, 125); + return true; + case 7919: + cacheIndex = 4150; + reference = new EpsgCoordinateReferenceRecord(7919, (EpsgCoordinateSystemKind)0, 558); + return true; + case 7920: + cacheIndex = 4151; + reference = new EpsgCoordinateReferenceRecord(7920, (EpsgCoordinateSystemKind)1, 126); + return true; + case 7921: + cacheIndex = 4152; + reference = new EpsgCoordinateReferenceRecord(7921, (EpsgCoordinateSystemKind)0, 559); + return true; + case 7922: + cacheIndex = 4153; + reference = new EpsgCoordinateReferenceRecord(7922, (EpsgCoordinateSystemKind)1, 127); + return true; + case 7923: + cacheIndex = 4154; + reference = new EpsgCoordinateReferenceRecord(7923, (EpsgCoordinateSystemKind)0, 560); + return true; + case 7924: + cacheIndex = 4155; + reference = new EpsgCoordinateReferenceRecord(7924, (EpsgCoordinateSystemKind)1, 128); + return true; + case 7925: + cacheIndex = 4156; + reference = new EpsgCoordinateReferenceRecord(7925, (EpsgCoordinateSystemKind)0, 561); + return true; + case 7926: + cacheIndex = 4157; + reference = new EpsgCoordinateReferenceRecord(7926, (EpsgCoordinateSystemKind)1, 129); + return true; + case 7927: + cacheIndex = 4158; + reference = new EpsgCoordinateReferenceRecord(7927, (EpsgCoordinateSystemKind)0, 562); + return true; + case 7928: + cacheIndex = 4159; + reference = new EpsgCoordinateReferenceRecord(7928, (EpsgCoordinateSystemKind)1, 130); + return true; + case 7929: + cacheIndex = 4160; + reference = new EpsgCoordinateReferenceRecord(7929, (EpsgCoordinateSystemKind)0, 563); + return true; + case 7930: + cacheIndex = 4161; + reference = new EpsgCoordinateReferenceRecord(7930, (EpsgCoordinateSystemKind)1, 131); + return true; + case 7931: + cacheIndex = 4162; + reference = new EpsgCoordinateReferenceRecord(7931, (EpsgCoordinateSystemKind)0, 564); + return true; + case 7954: + cacheIndex = 4163; + reference = new EpsgCoordinateReferenceRecord(7954, (EpsgCoordinateSystemKind)4, 133); + return true; + case 7955: + cacheIndex = 4164; + reference = new EpsgCoordinateReferenceRecord(7955, (EpsgCoordinateSystemKind)4, 134); + return true; + case 7956: + cacheIndex = 4165; + reference = new EpsgCoordinateReferenceRecord(7956, (EpsgCoordinateSystemKind)4, 135); + return true; + case 7979: + cacheIndex = 4166; + reference = new EpsgCoordinateReferenceRecord(7979, (EpsgCoordinateSystemKind)3, 178); + return true; + case 7991: + cacheIndex = 4167; + reference = new EpsgCoordinateReferenceRecord(7991, (EpsgCoordinateSystemKind)2, 3155); + return true; + case 7992: + cacheIndex = 4168; + reference = new EpsgCoordinateReferenceRecord(7992, (EpsgCoordinateSystemKind)2, 3156); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket8(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 8013: + cacheIndex = 4169; + reference = new EpsgCoordinateReferenceRecord(8013, (EpsgCoordinateSystemKind)2, 3157); + return true; + case 8014: + cacheIndex = 4170; + reference = new EpsgCoordinateReferenceRecord(8014, (EpsgCoordinateSystemKind)2, 3158); + return true; + case 8015: + cacheIndex = 4171; + reference = new EpsgCoordinateReferenceRecord(8015, (EpsgCoordinateSystemKind)2, 3159); + return true; + case 8016: + cacheIndex = 4172; + reference = new EpsgCoordinateReferenceRecord(8016, (EpsgCoordinateSystemKind)2, 3160); + return true; + case 8017: + cacheIndex = 4173; + reference = new EpsgCoordinateReferenceRecord(8017, (EpsgCoordinateSystemKind)2, 3161); + return true; + case 8018: + cacheIndex = 4174; + reference = new EpsgCoordinateReferenceRecord(8018, (EpsgCoordinateSystemKind)2, 3162); + return true; + case 8019: + cacheIndex = 4175; + reference = new EpsgCoordinateReferenceRecord(8019, (EpsgCoordinateSystemKind)2, 3163); + return true; + case 8020: + cacheIndex = 4176; + reference = new EpsgCoordinateReferenceRecord(8020, (EpsgCoordinateSystemKind)2, 3164); + return true; + case 8021: + cacheIndex = 4177; + reference = new EpsgCoordinateReferenceRecord(8021, (EpsgCoordinateSystemKind)2, 3165); + return true; + case 8022: + cacheIndex = 4178; + reference = new EpsgCoordinateReferenceRecord(8022, (EpsgCoordinateSystemKind)2, 3166); + return true; + case 8023: + cacheIndex = 4179; + reference = new EpsgCoordinateReferenceRecord(8023, (EpsgCoordinateSystemKind)2, 3167); + return true; + case 8024: + cacheIndex = 4180; + reference = new EpsgCoordinateReferenceRecord(8024, (EpsgCoordinateSystemKind)2, 3168); + return true; + case 8025: + cacheIndex = 4181; + reference = new EpsgCoordinateReferenceRecord(8025, (EpsgCoordinateSystemKind)2, 3169); + return true; + case 8026: + cacheIndex = 4182; + reference = new EpsgCoordinateReferenceRecord(8026, (EpsgCoordinateSystemKind)2, 3170); + return true; + case 8027: + cacheIndex = 4183; + reference = new EpsgCoordinateReferenceRecord(8027, (EpsgCoordinateSystemKind)2, 3171); + return true; + case 8028: + cacheIndex = 4184; + reference = new EpsgCoordinateReferenceRecord(8028, (EpsgCoordinateSystemKind)2, 3172); + return true; + case 8029: + cacheIndex = 4185; + reference = new EpsgCoordinateReferenceRecord(8029, (EpsgCoordinateSystemKind)2, 3173); + return true; + case 8030: + cacheIndex = 4186; + reference = new EpsgCoordinateReferenceRecord(8030, (EpsgCoordinateSystemKind)2, 3174); + return true; + case 8031: + cacheIndex = 4187; + reference = new EpsgCoordinateReferenceRecord(8031, (EpsgCoordinateSystemKind)2, 3175); + return true; + case 8032: + cacheIndex = 4188; + reference = new EpsgCoordinateReferenceRecord(8032, (EpsgCoordinateSystemKind)2, 3176); + return true; + case 8035: + cacheIndex = 4189; + reference = new EpsgCoordinateReferenceRecord(8035, (EpsgCoordinateSystemKind)2, 3177); + return true; + case 8036: + cacheIndex = 4190; + reference = new EpsgCoordinateReferenceRecord(8036, (EpsgCoordinateSystemKind)2, 3178); + return true; + case 8042: + cacheIndex = 4191; + reference = new EpsgCoordinateReferenceRecord(8042, (EpsgCoordinateSystemKind)0, 565); + return true; + case 8043: + cacheIndex = 4192; + reference = new EpsgCoordinateReferenceRecord(8043, (EpsgCoordinateSystemKind)0, 566); + return true; + case 8044: + cacheIndex = 4193; + reference = new EpsgCoordinateReferenceRecord(8044, (EpsgCoordinateSystemKind)2, 3179); + return true; + case 8045: + cacheIndex = 4194; + reference = new EpsgCoordinateReferenceRecord(8045, (EpsgCoordinateSystemKind)2, 3180); + return true; + case 8058: + cacheIndex = 4195; + reference = new EpsgCoordinateReferenceRecord(8058, (EpsgCoordinateSystemKind)2, 3181); + return true; + case 8059: + cacheIndex = 4196; + reference = new EpsgCoordinateReferenceRecord(8059, (EpsgCoordinateSystemKind)2, 3182); + return true; + case 8065: + cacheIndex = 4197; + reference = new EpsgCoordinateReferenceRecord(8065, (EpsgCoordinateSystemKind)2, 3183); + return true; + case 8066: + cacheIndex = 4198; + reference = new EpsgCoordinateReferenceRecord(8066, (EpsgCoordinateSystemKind)2, 3184); + return true; + case 8067: + cacheIndex = 4199; + reference = new EpsgCoordinateReferenceRecord(8067, (EpsgCoordinateSystemKind)2, 3185); + return true; + case 8068: + cacheIndex = 4200; + reference = new EpsgCoordinateReferenceRecord(8068, (EpsgCoordinateSystemKind)2, 3186); + return true; + case 8082: + cacheIndex = 4201; + reference = new EpsgCoordinateReferenceRecord(8082, (EpsgCoordinateSystemKind)2, 3187); + return true; + case 8083: + cacheIndex = 4202; + reference = new EpsgCoordinateReferenceRecord(8083, (EpsgCoordinateSystemKind)2, 3188); + return true; + case 8084: + cacheIndex = 4203; + reference = new EpsgCoordinateReferenceRecord(8084, (EpsgCoordinateSystemKind)1, 132); + return true; + case 8085: + cacheIndex = 4204; + reference = new EpsgCoordinateReferenceRecord(8085, (EpsgCoordinateSystemKind)0, 567); + return true; + case 8086: + cacheIndex = 4205; + reference = new EpsgCoordinateReferenceRecord(8086, (EpsgCoordinateSystemKind)0, 568); + return true; + case 8088: + cacheIndex = 4206; + reference = new EpsgCoordinateReferenceRecord(8088, (EpsgCoordinateSystemKind)2, 3189); + return true; + case 8089: + cacheIndex = 4207; + reference = new EpsgCoordinateReferenceRecord(8089, (EpsgCoordinateSystemKind)3, 179); + return true; + case 8090: + cacheIndex = 4208; + reference = new EpsgCoordinateReferenceRecord(8090, (EpsgCoordinateSystemKind)2, 3190); + return true; + case 8091: + cacheIndex = 4209; + reference = new EpsgCoordinateReferenceRecord(8091, (EpsgCoordinateSystemKind)2, 3191); + return true; + case 8092: + cacheIndex = 4210; + reference = new EpsgCoordinateReferenceRecord(8092, (EpsgCoordinateSystemKind)2, 3192); + return true; + case 8093: + cacheIndex = 4211; + reference = new EpsgCoordinateReferenceRecord(8093, (EpsgCoordinateSystemKind)2, 3193); + return true; + case 8095: + cacheIndex = 4212; + reference = new EpsgCoordinateReferenceRecord(8095, (EpsgCoordinateSystemKind)2, 3194); + return true; + case 8096: + cacheIndex = 4213; + reference = new EpsgCoordinateReferenceRecord(8096, (EpsgCoordinateSystemKind)2, 3195); + return true; + case 8097: + cacheIndex = 4214; + reference = new EpsgCoordinateReferenceRecord(8097, (EpsgCoordinateSystemKind)2, 3196); + return true; + case 8098: + cacheIndex = 4215; + reference = new EpsgCoordinateReferenceRecord(8098, (EpsgCoordinateSystemKind)2, 3197); + return true; + case 8099: + cacheIndex = 4216; + reference = new EpsgCoordinateReferenceRecord(8099, (EpsgCoordinateSystemKind)2, 3198); + return true; + case 8100: + cacheIndex = 4217; + reference = new EpsgCoordinateReferenceRecord(8100, (EpsgCoordinateSystemKind)2, 3199); + return true; + case 8101: + cacheIndex = 4218; + reference = new EpsgCoordinateReferenceRecord(8101, (EpsgCoordinateSystemKind)2, 3200); + return true; + case 8102: + cacheIndex = 4219; + reference = new EpsgCoordinateReferenceRecord(8102, (EpsgCoordinateSystemKind)2, 3201); + return true; + case 8103: + cacheIndex = 4220; + reference = new EpsgCoordinateReferenceRecord(8103, (EpsgCoordinateSystemKind)2, 3202); + return true; + case 8104: + cacheIndex = 4221; + reference = new EpsgCoordinateReferenceRecord(8104, (EpsgCoordinateSystemKind)2, 3203); + return true; + case 8105: + cacheIndex = 4222; + reference = new EpsgCoordinateReferenceRecord(8105, (EpsgCoordinateSystemKind)2, 3204); + return true; + case 8106: + cacheIndex = 4223; + reference = new EpsgCoordinateReferenceRecord(8106, (EpsgCoordinateSystemKind)2, 3205); + return true; + case 8107: + cacheIndex = 4224; + reference = new EpsgCoordinateReferenceRecord(8107, (EpsgCoordinateSystemKind)2, 3206); + return true; + case 8108: + cacheIndex = 4225; + reference = new EpsgCoordinateReferenceRecord(8108, (EpsgCoordinateSystemKind)2, 3207); + return true; + case 8109: + cacheIndex = 4226; + reference = new EpsgCoordinateReferenceRecord(8109, (EpsgCoordinateSystemKind)2, 3208); + return true; + case 8110: + cacheIndex = 4227; + reference = new EpsgCoordinateReferenceRecord(8110, (EpsgCoordinateSystemKind)2, 3209); + return true; + case 8111: + cacheIndex = 4228; + reference = new EpsgCoordinateReferenceRecord(8111, (EpsgCoordinateSystemKind)2, 3210); + return true; + case 8112: + cacheIndex = 4229; + reference = new EpsgCoordinateReferenceRecord(8112, (EpsgCoordinateSystemKind)2, 3211); + return true; + case 8113: + cacheIndex = 4230; + reference = new EpsgCoordinateReferenceRecord(8113, (EpsgCoordinateSystemKind)2, 3212); + return true; + case 8114: + cacheIndex = 4231; + reference = new EpsgCoordinateReferenceRecord(8114, (EpsgCoordinateSystemKind)2, 3213); + return true; + case 8115: + cacheIndex = 4232; + reference = new EpsgCoordinateReferenceRecord(8115, (EpsgCoordinateSystemKind)2, 3214); + return true; + case 8116: + cacheIndex = 4233; + reference = new EpsgCoordinateReferenceRecord(8116, (EpsgCoordinateSystemKind)2, 3215); + return true; + case 8117: + cacheIndex = 4234; + reference = new EpsgCoordinateReferenceRecord(8117, (EpsgCoordinateSystemKind)2, 3216); + return true; + case 8118: + cacheIndex = 4235; + reference = new EpsgCoordinateReferenceRecord(8118, (EpsgCoordinateSystemKind)2, 3217); + return true; + case 8119: + cacheIndex = 4236; + reference = new EpsgCoordinateReferenceRecord(8119, (EpsgCoordinateSystemKind)2, 3218); + return true; + case 8120: + cacheIndex = 4237; + reference = new EpsgCoordinateReferenceRecord(8120, (EpsgCoordinateSystemKind)2, 3219); + return true; + case 8121: + cacheIndex = 4238; + reference = new EpsgCoordinateReferenceRecord(8121, (EpsgCoordinateSystemKind)2, 3220); + return true; + case 8122: + cacheIndex = 4239; + reference = new EpsgCoordinateReferenceRecord(8122, (EpsgCoordinateSystemKind)2, 3221); + return true; + case 8123: + cacheIndex = 4240; + reference = new EpsgCoordinateReferenceRecord(8123, (EpsgCoordinateSystemKind)2, 3222); + return true; + case 8124: + cacheIndex = 4241; + reference = new EpsgCoordinateReferenceRecord(8124, (EpsgCoordinateSystemKind)2, 3223); + return true; + case 8125: + cacheIndex = 4242; + reference = new EpsgCoordinateReferenceRecord(8125, (EpsgCoordinateSystemKind)2, 3224); + return true; + case 8126: + cacheIndex = 4243; + reference = new EpsgCoordinateReferenceRecord(8126, (EpsgCoordinateSystemKind)2, 3225); + return true; + case 8127: + cacheIndex = 4244; + reference = new EpsgCoordinateReferenceRecord(8127, (EpsgCoordinateSystemKind)2, 3226); + return true; + case 8128: + cacheIndex = 4245; + reference = new EpsgCoordinateReferenceRecord(8128, (EpsgCoordinateSystemKind)2, 3227); + return true; + case 8129: + cacheIndex = 4246; + reference = new EpsgCoordinateReferenceRecord(8129, (EpsgCoordinateSystemKind)2, 3228); + return true; + case 8130: + cacheIndex = 4247; + reference = new EpsgCoordinateReferenceRecord(8130, (EpsgCoordinateSystemKind)2, 3229); + return true; + case 8131: + cacheIndex = 4248; + reference = new EpsgCoordinateReferenceRecord(8131, (EpsgCoordinateSystemKind)2, 3230); + return true; + case 8132: + cacheIndex = 4249; + reference = new EpsgCoordinateReferenceRecord(8132, (EpsgCoordinateSystemKind)2, 3231); + return true; + case 8133: + cacheIndex = 4250; + reference = new EpsgCoordinateReferenceRecord(8133, (EpsgCoordinateSystemKind)2, 3232); + return true; + case 8134: + cacheIndex = 4251; + reference = new EpsgCoordinateReferenceRecord(8134, (EpsgCoordinateSystemKind)2, 3233); + return true; + case 8135: + cacheIndex = 4252; + reference = new EpsgCoordinateReferenceRecord(8135, (EpsgCoordinateSystemKind)2, 3234); + return true; + case 8136: + cacheIndex = 4253; + reference = new EpsgCoordinateReferenceRecord(8136, (EpsgCoordinateSystemKind)2, 3235); + return true; + case 8137: + cacheIndex = 4254; + reference = new EpsgCoordinateReferenceRecord(8137, (EpsgCoordinateSystemKind)2, 3236); + return true; + case 8138: + cacheIndex = 4255; + reference = new EpsgCoordinateReferenceRecord(8138, (EpsgCoordinateSystemKind)2, 3237); + return true; + case 8139: + cacheIndex = 4256; + reference = new EpsgCoordinateReferenceRecord(8139, (EpsgCoordinateSystemKind)2, 3238); + return true; + case 8140: + cacheIndex = 4257; + reference = new EpsgCoordinateReferenceRecord(8140, (EpsgCoordinateSystemKind)2, 3239); + return true; + case 8141: + cacheIndex = 4258; + reference = new EpsgCoordinateReferenceRecord(8141, (EpsgCoordinateSystemKind)2, 3240); + return true; + case 8142: + cacheIndex = 4259; + reference = new EpsgCoordinateReferenceRecord(8142, (EpsgCoordinateSystemKind)2, 3241); + return true; + case 8143: + cacheIndex = 4260; + reference = new EpsgCoordinateReferenceRecord(8143, (EpsgCoordinateSystemKind)2, 3242); + return true; + case 8144: + cacheIndex = 4261; + reference = new EpsgCoordinateReferenceRecord(8144, (EpsgCoordinateSystemKind)2, 3243); + return true; + case 8145: + cacheIndex = 4262; + reference = new EpsgCoordinateReferenceRecord(8145, (EpsgCoordinateSystemKind)2, 3244); + return true; + case 8146: + cacheIndex = 4263; + reference = new EpsgCoordinateReferenceRecord(8146, (EpsgCoordinateSystemKind)2, 3245); + return true; + case 8147: + cacheIndex = 4264; + reference = new EpsgCoordinateReferenceRecord(8147, (EpsgCoordinateSystemKind)2, 3246); + return true; + case 8148: + cacheIndex = 4265; + reference = new EpsgCoordinateReferenceRecord(8148, (EpsgCoordinateSystemKind)2, 3247); + return true; + case 8149: + cacheIndex = 4266; + reference = new EpsgCoordinateReferenceRecord(8149, (EpsgCoordinateSystemKind)2, 3248); + return true; + case 8150: + cacheIndex = 4267; + reference = new EpsgCoordinateReferenceRecord(8150, (EpsgCoordinateSystemKind)2, 3249); + return true; + case 8151: + cacheIndex = 4268; + reference = new EpsgCoordinateReferenceRecord(8151, (EpsgCoordinateSystemKind)2, 3250); + return true; + case 8152: + cacheIndex = 4269; + reference = new EpsgCoordinateReferenceRecord(8152, (EpsgCoordinateSystemKind)2, 3251); + return true; + case 8153: + cacheIndex = 4270; + reference = new EpsgCoordinateReferenceRecord(8153, (EpsgCoordinateSystemKind)2, 3252); + return true; + case 8154: + cacheIndex = 4271; + reference = new EpsgCoordinateReferenceRecord(8154, (EpsgCoordinateSystemKind)2, 3253); + return true; + case 8155: + cacheIndex = 4272; + reference = new EpsgCoordinateReferenceRecord(8155, (EpsgCoordinateSystemKind)2, 3254); + return true; + case 8156: + cacheIndex = 4273; + reference = new EpsgCoordinateReferenceRecord(8156, (EpsgCoordinateSystemKind)2, 3255); + return true; + case 8157: + cacheIndex = 4274; + reference = new EpsgCoordinateReferenceRecord(8157, (EpsgCoordinateSystemKind)2, 3256); + return true; + case 8158: + cacheIndex = 4275; + reference = new EpsgCoordinateReferenceRecord(8158, (EpsgCoordinateSystemKind)2, 3257); + return true; + case 8159: + cacheIndex = 4276; + reference = new EpsgCoordinateReferenceRecord(8159, (EpsgCoordinateSystemKind)2, 3258); + return true; + case 8160: + cacheIndex = 4277; + reference = new EpsgCoordinateReferenceRecord(8160, (EpsgCoordinateSystemKind)2, 3259); + return true; + case 8161: + cacheIndex = 4278; + reference = new EpsgCoordinateReferenceRecord(8161, (EpsgCoordinateSystemKind)2, 3260); + return true; + case 8162: + cacheIndex = 4279; + reference = new EpsgCoordinateReferenceRecord(8162, (EpsgCoordinateSystemKind)2, 3261); + return true; + case 8163: + cacheIndex = 4280; + reference = new EpsgCoordinateReferenceRecord(8163, (EpsgCoordinateSystemKind)2, 3262); + return true; + case 8164: + cacheIndex = 4281; + reference = new EpsgCoordinateReferenceRecord(8164, (EpsgCoordinateSystemKind)2, 3263); + return true; + case 8165: + cacheIndex = 4282; + reference = new EpsgCoordinateReferenceRecord(8165, (EpsgCoordinateSystemKind)2, 3264); + return true; + case 8166: + cacheIndex = 4283; + reference = new EpsgCoordinateReferenceRecord(8166, (EpsgCoordinateSystemKind)2, 3265); + return true; + case 8167: + cacheIndex = 4284; + reference = new EpsgCoordinateReferenceRecord(8167, (EpsgCoordinateSystemKind)2, 3266); + return true; + case 8168: + cacheIndex = 4285; + reference = new EpsgCoordinateReferenceRecord(8168, (EpsgCoordinateSystemKind)2, 3267); + return true; + case 8169: + cacheIndex = 4286; + reference = new EpsgCoordinateReferenceRecord(8169, (EpsgCoordinateSystemKind)2, 3268); + return true; + case 8170: + cacheIndex = 4287; + reference = new EpsgCoordinateReferenceRecord(8170, (EpsgCoordinateSystemKind)2, 3269); + return true; + case 8171: + cacheIndex = 4288; + reference = new EpsgCoordinateReferenceRecord(8171, (EpsgCoordinateSystemKind)2, 3270); + return true; + case 8172: + cacheIndex = 4289; + reference = new EpsgCoordinateReferenceRecord(8172, (EpsgCoordinateSystemKind)2, 3271); + return true; + case 8173: + cacheIndex = 4290; + reference = new EpsgCoordinateReferenceRecord(8173, (EpsgCoordinateSystemKind)2, 3272); + return true; + case 8177: + cacheIndex = 4291; + reference = new EpsgCoordinateReferenceRecord(8177, (EpsgCoordinateSystemKind)2, 3273); + return true; + case 8179: + cacheIndex = 4292; + reference = new EpsgCoordinateReferenceRecord(8179, (EpsgCoordinateSystemKind)2, 3274); + return true; + case 8180: + cacheIndex = 4293; + reference = new EpsgCoordinateReferenceRecord(8180, (EpsgCoordinateSystemKind)2, 3275); + return true; + case 8181: + cacheIndex = 4294; + reference = new EpsgCoordinateReferenceRecord(8181, (EpsgCoordinateSystemKind)2, 3276); + return true; + case 8182: + cacheIndex = 4295; + reference = new EpsgCoordinateReferenceRecord(8182, (EpsgCoordinateSystemKind)2, 3277); + return true; + case 8184: + cacheIndex = 4296; + reference = new EpsgCoordinateReferenceRecord(8184, (EpsgCoordinateSystemKind)2, 3278); + return true; + case 8185: + cacheIndex = 4297; + reference = new EpsgCoordinateReferenceRecord(8185, (EpsgCoordinateSystemKind)2, 3279); + return true; + case 8187: + cacheIndex = 4298; + reference = new EpsgCoordinateReferenceRecord(8187, (EpsgCoordinateSystemKind)2, 3280); + return true; + case 8189: + cacheIndex = 4299; + reference = new EpsgCoordinateReferenceRecord(8189, (EpsgCoordinateSystemKind)2, 3281); + return true; + case 8191: + cacheIndex = 4300; + reference = new EpsgCoordinateReferenceRecord(8191, (EpsgCoordinateSystemKind)2, 3282); + return true; + case 8193: + cacheIndex = 4301; + reference = new EpsgCoordinateReferenceRecord(8193, (EpsgCoordinateSystemKind)2, 3283); + return true; + case 8196: + cacheIndex = 4302; + reference = new EpsgCoordinateReferenceRecord(8196, (EpsgCoordinateSystemKind)2, 3284); + return true; + case 8197: + cacheIndex = 4303; + reference = new EpsgCoordinateReferenceRecord(8197, (EpsgCoordinateSystemKind)2, 3285); + return true; + case 8198: + cacheIndex = 4304; + reference = new EpsgCoordinateReferenceRecord(8198, (EpsgCoordinateSystemKind)2, 3286); + return true; + case 8200: + cacheIndex = 4305; + reference = new EpsgCoordinateReferenceRecord(8200, (EpsgCoordinateSystemKind)2, 3287); + return true; + case 8201: + cacheIndex = 4306; + reference = new EpsgCoordinateReferenceRecord(8201, (EpsgCoordinateSystemKind)2, 3288); + return true; + case 8202: + cacheIndex = 4307; + reference = new EpsgCoordinateReferenceRecord(8202, (EpsgCoordinateSystemKind)2, 3289); + return true; + case 8203: + cacheIndex = 4308; + reference = new EpsgCoordinateReferenceRecord(8203, (EpsgCoordinateSystemKind)2, 3290); + return true; + case 8204: + cacheIndex = 4309; + reference = new EpsgCoordinateReferenceRecord(8204, (EpsgCoordinateSystemKind)2, 3291); + return true; + case 8205: + cacheIndex = 4310; + reference = new EpsgCoordinateReferenceRecord(8205, (EpsgCoordinateSystemKind)2, 3292); + return true; + case 8206: + cacheIndex = 4311; + reference = new EpsgCoordinateReferenceRecord(8206, (EpsgCoordinateSystemKind)2, 3293); + return true; + case 8207: + cacheIndex = 4312; + reference = new EpsgCoordinateReferenceRecord(8207, (EpsgCoordinateSystemKind)2, 3294); + return true; + case 8208: + cacheIndex = 4313; + reference = new EpsgCoordinateReferenceRecord(8208, (EpsgCoordinateSystemKind)2, 3295); + return true; + case 8209: + cacheIndex = 4314; + reference = new EpsgCoordinateReferenceRecord(8209, (EpsgCoordinateSystemKind)2, 3296); + return true; + case 8210: + cacheIndex = 4315; + reference = new EpsgCoordinateReferenceRecord(8210, (EpsgCoordinateSystemKind)2, 3297); + return true; + case 8212: + cacheIndex = 4316; + reference = new EpsgCoordinateReferenceRecord(8212, (EpsgCoordinateSystemKind)2, 3298); + return true; + case 8213: + cacheIndex = 4317; + reference = new EpsgCoordinateReferenceRecord(8213, (EpsgCoordinateSystemKind)2, 3299); + return true; + case 8214: + cacheIndex = 4318; + reference = new EpsgCoordinateReferenceRecord(8214, (EpsgCoordinateSystemKind)2, 3300); + return true; + case 8216: + cacheIndex = 4319; + reference = new EpsgCoordinateReferenceRecord(8216, (EpsgCoordinateSystemKind)2, 3301); + return true; + case 8218: + cacheIndex = 4320; + reference = new EpsgCoordinateReferenceRecord(8218, (EpsgCoordinateSystemKind)2, 3302); + return true; + case 8220: + cacheIndex = 4321; + reference = new EpsgCoordinateReferenceRecord(8220, (EpsgCoordinateSystemKind)2, 3303); + return true; + case 8222: + cacheIndex = 4322; + reference = new EpsgCoordinateReferenceRecord(8222, (EpsgCoordinateSystemKind)2, 3304); + return true; + case 8224: + cacheIndex = 4323; + reference = new EpsgCoordinateReferenceRecord(8224, (EpsgCoordinateSystemKind)2, 3305); + return true; + case 8225: + cacheIndex = 4324; + reference = new EpsgCoordinateReferenceRecord(8225, (EpsgCoordinateSystemKind)2, 3306); + return true; + case 8226: + cacheIndex = 4325; + reference = new EpsgCoordinateReferenceRecord(8226, (EpsgCoordinateSystemKind)2, 3307); + return true; + case 8227: + cacheIndex = 4326; + reference = new EpsgCoordinateReferenceRecord(8227, (EpsgCoordinateSystemKind)1, 133); + return true; + case 8230: + cacheIndex = 4327; + reference = new EpsgCoordinateReferenceRecord(8230, (EpsgCoordinateSystemKind)1, 134); + return true; + case 8231: + cacheIndex = 4328; + reference = new EpsgCoordinateReferenceRecord(8231, (EpsgCoordinateSystemKind)0, 569); + return true; + case 8232: + cacheIndex = 4329; + reference = new EpsgCoordinateReferenceRecord(8232, (EpsgCoordinateSystemKind)0, 570); + return true; + case 8233: + cacheIndex = 4330; + reference = new EpsgCoordinateReferenceRecord(8233, (EpsgCoordinateSystemKind)1, 135); + return true; + case 8235: + cacheIndex = 4331; + reference = new EpsgCoordinateReferenceRecord(8235, (EpsgCoordinateSystemKind)0, 571); + return true; + case 8237: + cacheIndex = 4332; + reference = new EpsgCoordinateReferenceRecord(8237, (EpsgCoordinateSystemKind)0, 572); + return true; + case 8238: + cacheIndex = 4333; + reference = new EpsgCoordinateReferenceRecord(8238, (EpsgCoordinateSystemKind)1, 136); + return true; + case 8239: + cacheIndex = 4334; + reference = new EpsgCoordinateReferenceRecord(8239, (EpsgCoordinateSystemKind)0, 573); + return true; + case 8240: + cacheIndex = 4335; + reference = new EpsgCoordinateReferenceRecord(8240, (EpsgCoordinateSystemKind)0, 574); + return true; + case 8242: + cacheIndex = 4336; + reference = new EpsgCoordinateReferenceRecord(8242, (EpsgCoordinateSystemKind)1, 137); + return true; + case 8244: + cacheIndex = 4337; + reference = new EpsgCoordinateReferenceRecord(8244, (EpsgCoordinateSystemKind)0, 575); + return true; + case 8246: + cacheIndex = 4338; + reference = new EpsgCoordinateReferenceRecord(8246, (EpsgCoordinateSystemKind)0, 576); + return true; + case 8247: + cacheIndex = 4339; + reference = new EpsgCoordinateReferenceRecord(8247, (EpsgCoordinateSystemKind)1, 138); + return true; + case 8248: + cacheIndex = 4340; + reference = new EpsgCoordinateReferenceRecord(8248, (EpsgCoordinateSystemKind)0, 577); + return true; + case 8249: + cacheIndex = 4341; + reference = new EpsgCoordinateReferenceRecord(8249, (EpsgCoordinateSystemKind)0, 578); + return true; + case 8250: + cacheIndex = 4342; + reference = new EpsgCoordinateReferenceRecord(8250, (EpsgCoordinateSystemKind)1, 139); + return true; + case 8251: + cacheIndex = 4343; + reference = new EpsgCoordinateReferenceRecord(8251, (EpsgCoordinateSystemKind)0, 579); + return true; + case 8252: + cacheIndex = 4344; + reference = new EpsgCoordinateReferenceRecord(8252, (EpsgCoordinateSystemKind)0, 580); + return true; + case 8253: + cacheIndex = 4345; + reference = new EpsgCoordinateReferenceRecord(8253, (EpsgCoordinateSystemKind)1, 140); + return true; + case 8254: + cacheIndex = 4346; + reference = new EpsgCoordinateReferenceRecord(8254, (EpsgCoordinateSystemKind)0, 581); + return true; + case 8255: + cacheIndex = 4347; + reference = new EpsgCoordinateReferenceRecord(8255, (EpsgCoordinateSystemKind)0, 582); + return true; + case 8266: + cacheIndex = 4348; + reference = new EpsgCoordinateReferenceRecord(8266, (EpsgCoordinateSystemKind)3, 180); + return true; + case 8267: + cacheIndex = 4349; + reference = new EpsgCoordinateReferenceRecord(8267, (EpsgCoordinateSystemKind)3, 181); + return true; + case 8311: + cacheIndex = 4350; + reference = new EpsgCoordinateReferenceRecord(8311, (EpsgCoordinateSystemKind)2, 3308); + return true; + case 8312: + cacheIndex = 4351; + reference = new EpsgCoordinateReferenceRecord(8312, (EpsgCoordinateSystemKind)2, 3309); + return true; + case 8313: + cacheIndex = 4352; + reference = new EpsgCoordinateReferenceRecord(8313, (EpsgCoordinateSystemKind)2, 3310); + return true; + case 8314: + cacheIndex = 4353; + reference = new EpsgCoordinateReferenceRecord(8314, (EpsgCoordinateSystemKind)2, 3311); + return true; + case 8315: + cacheIndex = 4354; + reference = new EpsgCoordinateReferenceRecord(8315, (EpsgCoordinateSystemKind)2, 3312); + return true; + case 8316: + cacheIndex = 4355; + reference = new EpsgCoordinateReferenceRecord(8316, (EpsgCoordinateSystemKind)2, 3313); + return true; + case 8317: + cacheIndex = 4356; + reference = new EpsgCoordinateReferenceRecord(8317, (EpsgCoordinateSystemKind)2, 3314); + return true; + case 8318: + cacheIndex = 4357; + reference = new EpsgCoordinateReferenceRecord(8318, (EpsgCoordinateSystemKind)2, 3315); + return true; + case 8319: + cacheIndex = 4358; + reference = new EpsgCoordinateReferenceRecord(8319, (EpsgCoordinateSystemKind)2, 3316); + return true; + case 8320: + cacheIndex = 4359; + reference = new EpsgCoordinateReferenceRecord(8320, (EpsgCoordinateSystemKind)2, 3317); + return true; + case 8321: + cacheIndex = 4360; + reference = new EpsgCoordinateReferenceRecord(8321, (EpsgCoordinateSystemKind)2, 3318); + return true; + case 8322: + cacheIndex = 4361; + reference = new EpsgCoordinateReferenceRecord(8322, (EpsgCoordinateSystemKind)2, 3319); + return true; + case 8323: + cacheIndex = 4362; + reference = new EpsgCoordinateReferenceRecord(8323, (EpsgCoordinateSystemKind)2, 3320); + return true; + case 8324: + cacheIndex = 4363; + reference = new EpsgCoordinateReferenceRecord(8324, (EpsgCoordinateSystemKind)2, 3321); + return true; + case 8325: + cacheIndex = 4364; + reference = new EpsgCoordinateReferenceRecord(8325, (EpsgCoordinateSystemKind)2, 3322); + return true; + case 8326: + cacheIndex = 4365; + reference = new EpsgCoordinateReferenceRecord(8326, (EpsgCoordinateSystemKind)2, 3323); + return true; + case 8327: + cacheIndex = 4366; + reference = new EpsgCoordinateReferenceRecord(8327, (EpsgCoordinateSystemKind)2, 3324); + return true; + case 8328: + cacheIndex = 4367; + reference = new EpsgCoordinateReferenceRecord(8328, (EpsgCoordinateSystemKind)2, 3325); + return true; + case 8329: + cacheIndex = 4368; + reference = new EpsgCoordinateReferenceRecord(8329, (EpsgCoordinateSystemKind)2, 3326); + return true; + case 8330: + cacheIndex = 4369; + reference = new EpsgCoordinateReferenceRecord(8330, (EpsgCoordinateSystemKind)2, 3327); + return true; + case 8331: + cacheIndex = 4370; + reference = new EpsgCoordinateReferenceRecord(8331, (EpsgCoordinateSystemKind)2, 3328); + return true; + case 8332: + cacheIndex = 4371; + reference = new EpsgCoordinateReferenceRecord(8332, (EpsgCoordinateSystemKind)2, 3329); + return true; + case 8333: + cacheIndex = 4372; + reference = new EpsgCoordinateReferenceRecord(8333, (EpsgCoordinateSystemKind)2, 3330); + return true; + case 8334: + cacheIndex = 4373; + reference = new EpsgCoordinateReferenceRecord(8334, (EpsgCoordinateSystemKind)2, 3331); + return true; + case 8335: + cacheIndex = 4374; + reference = new EpsgCoordinateReferenceRecord(8335, (EpsgCoordinateSystemKind)2, 3332); + return true; + case 8336: + cacheIndex = 4375; + reference = new EpsgCoordinateReferenceRecord(8336, (EpsgCoordinateSystemKind)2, 3333); + return true; + case 8337: + cacheIndex = 4376; + reference = new EpsgCoordinateReferenceRecord(8337, (EpsgCoordinateSystemKind)2, 3334); + return true; + case 8338: + cacheIndex = 4377; + reference = new EpsgCoordinateReferenceRecord(8338, (EpsgCoordinateSystemKind)2, 3335); + return true; + case 8339: + cacheIndex = 4378; + reference = new EpsgCoordinateReferenceRecord(8339, (EpsgCoordinateSystemKind)2, 3336); + return true; + case 8340: + cacheIndex = 4379; + reference = new EpsgCoordinateReferenceRecord(8340, (EpsgCoordinateSystemKind)2, 3337); + return true; + case 8341: + cacheIndex = 4380; + reference = new EpsgCoordinateReferenceRecord(8341, (EpsgCoordinateSystemKind)2, 3338); + return true; + case 8342: + cacheIndex = 4381; + reference = new EpsgCoordinateReferenceRecord(8342, (EpsgCoordinateSystemKind)2, 3339); + return true; + case 8343: + cacheIndex = 4382; + reference = new EpsgCoordinateReferenceRecord(8343, (EpsgCoordinateSystemKind)2, 3340); + return true; + case 8344: + cacheIndex = 4383; + reference = new EpsgCoordinateReferenceRecord(8344, (EpsgCoordinateSystemKind)2, 3341); + return true; + case 8345: + cacheIndex = 4384; + reference = new EpsgCoordinateReferenceRecord(8345, (EpsgCoordinateSystemKind)2, 3342); + return true; + case 8346: + cacheIndex = 4385; + reference = new EpsgCoordinateReferenceRecord(8346, (EpsgCoordinateSystemKind)2, 3343); + return true; + case 8347: + cacheIndex = 4386; + reference = new EpsgCoordinateReferenceRecord(8347, (EpsgCoordinateSystemKind)2, 3344); + return true; + case 8348: + cacheIndex = 4387; + reference = new EpsgCoordinateReferenceRecord(8348, (EpsgCoordinateSystemKind)2, 3345); + return true; + case 8349: + cacheIndex = 4388; + reference = new EpsgCoordinateReferenceRecord(8349, (EpsgCoordinateSystemKind)4, 136); + return true; + case 8350: + cacheIndex = 4389; + reference = new EpsgCoordinateReferenceRecord(8350, (EpsgCoordinateSystemKind)4, 137); + return true; + case 8351: + cacheIndex = 4390; + reference = new EpsgCoordinateReferenceRecord(8351, (EpsgCoordinateSystemKind)0, 583); + return true; + case 8352: + cacheIndex = 4391; + reference = new EpsgCoordinateReferenceRecord(8352, (EpsgCoordinateSystemKind)2, 3346); + return true; + case 8353: + cacheIndex = 4392; + reference = new EpsgCoordinateReferenceRecord(8353, (EpsgCoordinateSystemKind)2, 3347); + return true; + case 8357: + cacheIndex = 4393; + reference = new EpsgCoordinateReferenceRecord(8357, (EpsgCoordinateSystemKind)3, 182); + return true; + case 8370: + cacheIndex = 4394; + reference = new EpsgCoordinateReferenceRecord(8370, (EpsgCoordinateSystemKind)4, 138); + return true; + case 8378: + cacheIndex = 4395; + reference = new EpsgCoordinateReferenceRecord(8378, (EpsgCoordinateSystemKind)3, 183); + return true; + case 8379: + cacheIndex = 4396; + reference = new EpsgCoordinateReferenceRecord(8379, (EpsgCoordinateSystemKind)2, 3348); + return true; + case 8380: + cacheIndex = 4397; + reference = new EpsgCoordinateReferenceRecord(8380, (EpsgCoordinateSystemKind)2, 3349); + return true; + case 8381: + cacheIndex = 4398; + reference = new EpsgCoordinateReferenceRecord(8381, (EpsgCoordinateSystemKind)2, 3350); + return true; + case 8382: + cacheIndex = 4399; + reference = new EpsgCoordinateReferenceRecord(8382, (EpsgCoordinateSystemKind)2, 3351); + return true; + case 8383: + cacheIndex = 4400; + reference = new EpsgCoordinateReferenceRecord(8383, (EpsgCoordinateSystemKind)2, 3352); + return true; + case 8384: + cacheIndex = 4401; + reference = new EpsgCoordinateReferenceRecord(8384, (EpsgCoordinateSystemKind)2, 3353); + return true; + case 8385: + cacheIndex = 4402; + reference = new EpsgCoordinateReferenceRecord(8385, (EpsgCoordinateSystemKind)2, 3354); + return true; + case 8387: + cacheIndex = 4403; + reference = new EpsgCoordinateReferenceRecord(8387, (EpsgCoordinateSystemKind)2, 3355); + return true; + case 8391: + cacheIndex = 4404; + reference = new EpsgCoordinateReferenceRecord(8391, (EpsgCoordinateSystemKind)2, 3356); + return true; + case 8395: + cacheIndex = 4405; + reference = new EpsgCoordinateReferenceRecord(8395, (EpsgCoordinateSystemKind)2, 3357); + return true; + case 8397: + cacheIndex = 4406; + reference = new EpsgCoordinateReferenceRecord(8397, (EpsgCoordinateSystemKind)1, 141); + return true; + case 8399: + cacheIndex = 4407; + reference = new EpsgCoordinateReferenceRecord(8399, (EpsgCoordinateSystemKind)0, 584); + return true; + case 8401: + cacheIndex = 4408; + reference = new EpsgCoordinateReferenceRecord(8401, (EpsgCoordinateSystemKind)1, 142); + return true; + case 8403: + cacheIndex = 4409; + reference = new EpsgCoordinateReferenceRecord(8403, (EpsgCoordinateSystemKind)0, 585); + return true; + case 8425: + cacheIndex = 4410; + reference = new EpsgCoordinateReferenceRecord(8425, (EpsgCoordinateSystemKind)1, 143); + return true; + case 8426: + cacheIndex = 4411; + reference = new EpsgCoordinateReferenceRecord(8426, (EpsgCoordinateSystemKind)0, 586); + return true; + case 8427: + cacheIndex = 4412; + reference = new EpsgCoordinateReferenceRecord(8427, (EpsgCoordinateSystemKind)0, 587); + return true; + case 8428: + cacheIndex = 4413; + reference = new EpsgCoordinateReferenceRecord(8428, (EpsgCoordinateSystemKind)0, 588); + return true; + case 8429: + cacheIndex = 4414; + reference = new EpsgCoordinateReferenceRecord(8429, (EpsgCoordinateSystemKind)1, 144); + return true; + case 8430: + cacheIndex = 4415; + reference = new EpsgCoordinateReferenceRecord(8430, (EpsgCoordinateSystemKind)0, 589); + return true; + case 8431: + cacheIndex = 4416; + reference = new EpsgCoordinateReferenceRecord(8431, (EpsgCoordinateSystemKind)0, 590); + return true; + case 8433: + cacheIndex = 4417; + reference = new EpsgCoordinateReferenceRecord(8433, (EpsgCoordinateSystemKind)2, 3358); + return true; + case 8434: + cacheIndex = 4418; + reference = new EpsgCoordinateReferenceRecord(8434, (EpsgCoordinateSystemKind)3, 184); + return true; + case 8441: + cacheIndex = 4419; + reference = new EpsgCoordinateReferenceRecord(8441, (EpsgCoordinateSystemKind)2, 3359); + return true; + case 8455: + cacheIndex = 4420; + reference = new EpsgCoordinateReferenceRecord(8455, (EpsgCoordinateSystemKind)2, 3360); + return true; + case 8456: + cacheIndex = 4421; + reference = new EpsgCoordinateReferenceRecord(8456, (EpsgCoordinateSystemKind)2, 3361); + return true; + case 8518: + cacheIndex = 4422; + reference = new EpsgCoordinateReferenceRecord(8518, (EpsgCoordinateSystemKind)2, 3362); + return true; + case 8519: + cacheIndex = 4423; + reference = new EpsgCoordinateReferenceRecord(8519, (EpsgCoordinateSystemKind)2, 3363); + return true; + case 8520: + cacheIndex = 4424; + reference = new EpsgCoordinateReferenceRecord(8520, (EpsgCoordinateSystemKind)2, 3364); + return true; + case 8521: + cacheIndex = 4425; + reference = new EpsgCoordinateReferenceRecord(8521, (EpsgCoordinateSystemKind)2, 3365); + return true; + case 8522: + cacheIndex = 4426; + reference = new EpsgCoordinateReferenceRecord(8522, (EpsgCoordinateSystemKind)2, 3366); + return true; + case 8523: + cacheIndex = 4427; + reference = new EpsgCoordinateReferenceRecord(8523, (EpsgCoordinateSystemKind)2, 3367); + return true; + case 8524: + cacheIndex = 4428; + reference = new EpsgCoordinateReferenceRecord(8524, (EpsgCoordinateSystemKind)2, 3368); + return true; + case 8525: + cacheIndex = 4429; + reference = new EpsgCoordinateReferenceRecord(8525, (EpsgCoordinateSystemKind)2, 3369); + return true; + case 8526: + cacheIndex = 4430; + reference = new EpsgCoordinateReferenceRecord(8526, (EpsgCoordinateSystemKind)2, 3370); + return true; + case 8527: + cacheIndex = 4431; + reference = new EpsgCoordinateReferenceRecord(8527, (EpsgCoordinateSystemKind)2, 3371); + return true; + case 8528: + cacheIndex = 4432; + reference = new EpsgCoordinateReferenceRecord(8528, (EpsgCoordinateSystemKind)2, 3372); + return true; + case 8529: + cacheIndex = 4433; + reference = new EpsgCoordinateReferenceRecord(8529, (EpsgCoordinateSystemKind)2, 3373); + return true; + case 8531: + cacheIndex = 4434; + reference = new EpsgCoordinateReferenceRecord(8531, (EpsgCoordinateSystemKind)2, 3374); + return true; + case 8533: + cacheIndex = 4435; + reference = new EpsgCoordinateReferenceRecord(8533, (EpsgCoordinateSystemKind)2, 3375); + return true; + case 8534: + cacheIndex = 4436; + reference = new EpsgCoordinateReferenceRecord(8534, (EpsgCoordinateSystemKind)2, 3376); + return true; + case 8535: + cacheIndex = 4437; + reference = new EpsgCoordinateReferenceRecord(8535, (EpsgCoordinateSystemKind)2, 3377); + return true; + case 8536: + cacheIndex = 4438; + reference = new EpsgCoordinateReferenceRecord(8536, (EpsgCoordinateSystemKind)2, 3378); + return true; + case 8538: + cacheIndex = 4439; + reference = new EpsgCoordinateReferenceRecord(8538, (EpsgCoordinateSystemKind)2, 3379); + return true; + case 8539: + cacheIndex = 4440; + reference = new EpsgCoordinateReferenceRecord(8539, (EpsgCoordinateSystemKind)2, 3380); + return true; + case 8540: + cacheIndex = 4441; + reference = new EpsgCoordinateReferenceRecord(8540, (EpsgCoordinateSystemKind)2, 3381); + return true; + case 8541: + cacheIndex = 4442; + reference = new EpsgCoordinateReferenceRecord(8541, (EpsgCoordinateSystemKind)1, 145); + return true; + case 8542: + cacheIndex = 4443; + reference = new EpsgCoordinateReferenceRecord(8542, (EpsgCoordinateSystemKind)0, 591); + return true; + case 8543: + cacheIndex = 4444; + reference = new EpsgCoordinateReferenceRecord(8543, (EpsgCoordinateSystemKind)1, 146); + return true; + case 8544: + cacheIndex = 4445; + reference = new EpsgCoordinateReferenceRecord(8544, (EpsgCoordinateSystemKind)0, 592); + return true; + case 8545: + cacheIndex = 4446; + reference = new EpsgCoordinateReferenceRecord(8545, (EpsgCoordinateSystemKind)0, 593); + return true; + case 8675: + cacheIndex = 4447; + reference = new EpsgCoordinateReferenceRecord(8675, (EpsgCoordinateSystemKind)3, 185); + return true; + case 8677: + cacheIndex = 4448; + reference = new EpsgCoordinateReferenceRecord(8677, (EpsgCoordinateSystemKind)2, 3382); + return true; + case 8678: + cacheIndex = 4449; + reference = new EpsgCoordinateReferenceRecord(8678, (EpsgCoordinateSystemKind)2, 3383); + return true; + case 8679: + cacheIndex = 4450; + reference = new EpsgCoordinateReferenceRecord(8679, (EpsgCoordinateSystemKind)2, 3384); + return true; + case 8682: + cacheIndex = 4451; + reference = new EpsgCoordinateReferenceRecord(8682, (EpsgCoordinateSystemKind)2, 3385); + return true; + case 8683: + cacheIndex = 4452; + reference = new EpsgCoordinateReferenceRecord(8683, (EpsgCoordinateSystemKind)1, 147); + return true; + case 8684: + cacheIndex = 4453; + reference = new EpsgCoordinateReferenceRecord(8684, (EpsgCoordinateSystemKind)0, 594); + return true; + case 8685: + cacheIndex = 4454; + reference = new EpsgCoordinateReferenceRecord(8685, (EpsgCoordinateSystemKind)0, 595); + return true; + case 8686: + cacheIndex = 4455; + reference = new EpsgCoordinateReferenceRecord(8686, (EpsgCoordinateSystemKind)2, 3386); + return true; + case 8687: + cacheIndex = 4456; + reference = new EpsgCoordinateReferenceRecord(8687, (EpsgCoordinateSystemKind)2, 3387); + return true; + case 8690: + cacheIndex = 4457; + reference = new EpsgCoordinateReferenceRecord(8690, (EpsgCoordinateSystemKind)3, 186); + return true; + case 8691: + cacheIndex = 4458; + reference = new EpsgCoordinateReferenceRecord(8691, (EpsgCoordinateSystemKind)3, 187); + return true; + case 8692: + cacheIndex = 4459; + reference = new EpsgCoordinateReferenceRecord(8692, (EpsgCoordinateSystemKind)2, 3388); + return true; + case 8693: + cacheIndex = 4460; + reference = new EpsgCoordinateReferenceRecord(8693, (EpsgCoordinateSystemKind)2, 3389); + return true; + case 8694: + cacheIndex = 4461; + reference = new EpsgCoordinateReferenceRecord(8694, (EpsgCoordinateSystemKind)0, 596); + return true; + case 8697: + cacheIndex = 4462; + reference = new EpsgCoordinateReferenceRecord(8697, (EpsgCoordinateSystemKind)1, 148); + return true; + case 8698: + cacheIndex = 4463; + reference = new EpsgCoordinateReferenceRecord(8698, (EpsgCoordinateSystemKind)0, 597); + return true; + case 8699: + cacheIndex = 4464; + reference = new EpsgCoordinateReferenceRecord(8699, (EpsgCoordinateSystemKind)0, 598); + return true; + case 8801: + cacheIndex = 4465; + reference = new EpsgCoordinateReferenceRecord(8801, (EpsgCoordinateSystemKind)4, 139); + return true; + case 8802: + cacheIndex = 4466; + reference = new EpsgCoordinateReferenceRecord(8802, (EpsgCoordinateSystemKind)4, 140); + return true; + case 8803: + cacheIndex = 4467; + reference = new EpsgCoordinateReferenceRecord(8803, (EpsgCoordinateSystemKind)4, 141); + return true; + case 8804: + cacheIndex = 4468; + reference = new EpsgCoordinateReferenceRecord(8804, (EpsgCoordinateSystemKind)4, 142); + return true; + case 8805: + cacheIndex = 4469; + reference = new EpsgCoordinateReferenceRecord(8805, (EpsgCoordinateSystemKind)4, 143); + return true; + case 8806: + cacheIndex = 4470; + reference = new EpsgCoordinateReferenceRecord(8806, (EpsgCoordinateSystemKind)4, 144); + return true; + case 8807: + cacheIndex = 4471; + reference = new EpsgCoordinateReferenceRecord(8807, (EpsgCoordinateSystemKind)4, 145); + return true; + case 8808: + cacheIndex = 4472; + reference = new EpsgCoordinateReferenceRecord(8808, (EpsgCoordinateSystemKind)4, 146); + return true; + case 8809: + cacheIndex = 4473; + reference = new EpsgCoordinateReferenceRecord(8809, (EpsgCoordinateSystemKind)4, 147); + return true; + case 8810: + cacheIndex = 4474; + reference = new EpsgCoordinateReferenceRecord(8810, (EpsgCoordinateSystemKind)4, 148); + return true; + case 8811: + cacheIndex = 4475; + reference = new EpsgCoordinateReferenceRecord(8811, (EpsgCoordinateSystemKind)4, 149); + return true; + case 8812: + cacheIndex = 4476; + reference = new EpsgCoordinateReferenceRecord(8812, (EpsgCoordinateSystemKind)4, 150); + return true; + case 8813: + cacheIndex = 4477; + reference = new EpsgCoordinateReferenceRecord(8813, (EpsgCoordinateSystemKind)4, 151); + return true; + case 8814: + cacheIndex = 4478; + reference = new EpsgCoordinateReferenceRecord(8814, (EpsgCoordinateSystemKind)4, 152); + return true; + case 8815: + cacheIndex = 4479; + reference = new EpsgCoordinateReferenceRecord(8815, (EpsgCoordinateSystemKind)4, 153); + return true; + case 8816: + cacheIndex = 4480; + reference = new EpsgCoordinateReferenceRecord(8816, (EpsgCoordinateSystemKind)1, 149); + return true; + case 8817: + cacheIndex = 4481; + reference = new EpsgCoordinateReferenceRecord(8817, (EpsgCoordinateSystemKind)0, 599); + return true; + case 8818: + cacheIndex = 4482; + reference = new EpsgCoordinateReferenceRecord(8818, (EpsgCoordinateSystemKind)0, 600); + return true; + case 8826: + cacheIndex = 4483; + reference = new EpsgCoordinateReferenceRecord(8826, (EpsgCoordinateSystemKind)2, 3390); + return true; + case 8836: + cacheIndex = 4484; + reference = new EpsgCoordinateReferenceRecord(8836, (EpsgCoordinateSystemKind)2, 3391); + return true; + case 8837: + cacheIndex = 4485; + reference = new EpsgCoordinateReferenceRecord(8837, (EpsgCoordinateSystemKind)2, 3392); + return true; + case 8838: + cacheIndex = 4486; + reference = new EpsgCoordinateReferenceRecord(8838, (EpsgCoordinateSystemKind)2, 3393); + return true; + case 8839: + cacheIndex = 4487; + reference = new EpsgCoordinateReferenceRecord(8839, (EpsgCoordinateSystemKind)2, 3394); + return true; + case 8840: + cacheIndex = 4488; + reference = new EpsgCoordinateReferenceRecord(8840, (EpsgCoordinateSystemKind)2, 3395); + return true; + case 8841: + cacheIndex = 4489; + reference = new EpsgCoordinateReferenceRecord(8841, (EpsgCoordinateSystemKind)3, 188); + return true; + case 8857: + cacheIndex = 4490; + reference = new EpsgCoordinateReferenceRecord(8857, (EpsgCoordinateSystemKind)2, 3396); + return true; + case 8858: + cacheIndex = 4491; + reference = new EpsgCoordinateReferenceRecord(8858, (EpsgCoordinateSystemKind)2, 3397); + return true; + case 8859: + cacheIndex = 4492; + reference = new EpsgCoordinateReferenceRecord(8859, (EpsgCoordinateSystemKind)2, 3398); + return true; + case 8860: + cacheIndex = 4493; + reference = new EpsgCoordinateReferenceRecord(8860, (EpsgCoordinateSystemKind)0, 601); + return true; + case 8881: + cacheIndex = 4494; + reference = new EpsgCoordinateReferenceRecord(8881, (EpsgCoordinateSystemKind)3, 189); + return true; + case 8888: + cacheIndex = 4495; + reference = new EpsgCoordinateReferenceRecord(8888, (EpsgCoordinateSystemKind)0, 602); + return true; + case 8897: + cacheIndex = 4496; + reference = new EpsgCoordinateReferenceRecord(8897, (EpsgCoordinateSystemKind)3, 190); + return true; + case 8898: + cacheIndex = 4497; + reference = new EpsgCoordinateReferenceRecord(8898, (EpsgCoordinateSystemKind)1, 150); + return true; + case 8899: + cacheIndex = 4498; + reference = new EpsgCoordinateReferenceRecord(8899, (EpsgCoordinateSystemKind)0, 603); + return true; + case 8900: + cacheIndex = 4499; + reference = new EpsgCoordinateReferenceRecord(8900, (EpsgCoordinateSystemKind)0, 604); + return true; + case 8901: + cacheIndex = 4500; + reference = new EpsgCoordinateReferenceRecord(8901, (EpsgCoordinateSystemKind)0, 605); + return true; + case 8902: + cacheIndex = 4501; + reference = new EpsgCoordinateReferenceRecord(8902, (EpsgCoordinateSystemKind)0, 606); + return true; + case 8903: + cacheIndex = 4502; + reference = new EpsgCoordinateReferenceRecord(8903, (EpsgCoordinateSystemKind)2, 3399); + return true; + case 8904: + cacheIndex = 4503; + reference = new EpsgCoordinateReferenceRecord(8904, (EpsgCoordinateSystemKind)3, 191); + return true; + case 8905: + cacheIndex = 4504; + reference = new EpsgCoordinateReferenceRecord(8905, (EpsgCoordinateSystemKind)1, 151); + return true; + case 8906: + cacheIndex = 4505; + reference = new EpsgCoordinateReferenceRecord(8906, (EpsgCoordinateSystemKind)0, 607); + return true; + case 8907: + cacheIndex = 4506; + reference = new EpsgCoordinateReferenceRecord(8907, (EpsgCoordinateSystemKind)0, 608); + return true; + case 8908: + cacheIndex = 4507; + reference = new EpsgCoordinateReferenceRecord(8908, (EpsgCoordinateSystemKind)2, 3400); + return true; + case 8909: + cacheIndex = 4508; + reference = new EpsgCoordinateReferenceRecord(8909, (EpsgCoordinateSystemKind)2, 3401); + return true; + case 8910: + cacheIndex = 4509; + reference = new EpsgCoordinateReferenceRecord(8910, (EpsgCoordinateSystemKind)2, 3402); + return true; + case 8911: + cacheIndex = 4510; + reference = new EpsgCoordinateReferenceRecord(8911, (EpsgCoordinateSystemKind)3, 192); + return true; + case 8912: + cacheIndex = 4511; + reference = new EpsgCoordinateReferenceRecord(8912, (EpsgCoordinateSystemKind)4, 154); + return true; + case 8915: + cacheIndex = 4512; + reference = new EpsgCoordinateReferenceRecord(8915, (EpsgCoordinateSystemKind)1, 152); + return true; + case 8916: + cacheIndex = 4513; + reference = new EpsgCoordinateReferenceRecord(8916, (EpsgCoordinateSystemKind)0, 609); + return true; + case 8917: + cacheIndex = 4514; + reference = new EpsgCoordinateReferenceRecord(8917, (EpsgCoordinateSystemKind)1, 153); + return true; + case 8918: + cacheIndex = 4515; + reference = new EpsgCoordinateReferenceRecord(8918, (EpsgCoordinateSystemKind)0, 610); + return true; + case 8919: + cacheIndex = 4516; + reference = new EpsgCoordinateReferenceRecord(8919, (EpsgCoordinateSystemKind)1, 154); + return true; + case 8920: + cacheIndex = 4517; + reference = new EpsgCoordinateReferenceRecord(8920, (EpsgCoordinateSystemKind)0, 611); + return true; + case 8921: + cacheIndex = 4518; + reference = new EpsgCoordinateReferenceRecord(8921, (EpsgCoordinateSystemKind)1, 155); + return true; + case 8922: + cacheIndex = 4519; + reference = new EpsgCoordinateReferenceRecord(8922, (EpsgCoordinateSystemKind)0, 612); + return true; + case 8923: + cacheIndex = 4520; + reference = new EpsgCoordinateReferenceRecord(8923, (EpsgCoordinateSystemKind)1, 156); + return true; + case 8924: + cacheIndex = 4521; + reference = new EpsgCoordinateReferenceRecord(8924, (EpsgCoordinateSystemKind)0, 613); + return true; + case 8925: + cacheIndex = 4522; + reference = new EpsgCoordinateReferenceRecord(8925, (EpsgCoordinateSystemKind)1, 157); + return true; + case 8926: + cacheIndex = 4523; + reference = new EpsgCoordinateReferenceRecord(8926, (EpsgCoordinateSystemKind)0, 614); + return true; + case 8927: + cacheIndex = 4524; + reference = new EpsgCoordinateReferenceRecord(8927, (EpsgCoordinateSystemKind)1, 158); + return true; + case 8928: + cacheIndex = 4525; + reference = new EpsgCoordinateReferenceRecord(8928, (EpsgCoordinateSystemKind)0, 615); + return true; + case 8929: + cacheIndex = 4526; + reference = new EpsgCoordinateReferenceRecord(8929, (EpsgCoordinateSystemKind)1, 159); + return true; + case 8930: + cacheIndex = 4527; + reference = new EpsgCoordinateReferenceRecord(8930, (EpsgCoordinateSystemKind)0, 616); + return true; + case 8931: + cacheIndex = 4528; + reference = new EpsgCoordinateReferenceRecord(8931, (EpsgCoordinateSystemKind)1, 160); + return true; + case 8932: + cacheIndex = 4529; + reference = new EpsgCoordinateReferenceRecord(8932, (EpsgCoordinateSystemKind)0, 617); + return true; + case 8933: + cacheIndex = 4530; + reference = new EpsgCoordinateReferenceRecord(8933, (EpsgCoordinateSystemKind)1, 161); + return true; + case 8934: + cacheIndex = 4531; + reference = new EpsgCoordinateReferenceRecord(8934, (EpsgCoordinateSystemKind)0, 618); + return true; + case 8935: + cacheIndex = 4532; + reference = new EpsgCoordinateReferenceRecord(8935, (EpsgCoordinateSystemKind)1, 162); + return true; + case 8936: + cacheIndex = 4533; + reference = new EpsgCoordinateReferenceRecord(8936, (EpsgCoordinateSystemKind)0, 619); + return true; + case 8937: + cacheIndex = 4534; + reference = new EpsgCoordinateReferenceRecord(8937, (EpsgCoordinateSystemKind)1, 163); + return true; + case 8938: + cacheIndex = 4535; + reference = new EpsgCoordinateReferenceRecord(8938, (EpsgCoordinateSystemKind)0, 620); + return true; + case 8939: + cacheIndex = 4536; + reference = new EpsgCoordinateReferenceRecord(8939, (EpsgCoordinateSystemKind)1, 164); + return true; + case 8940: + cacheIndex = 4537; + reference = new EpsgCoordinateReferenceRecord(8940, (EpsgCoordinateSystemKind)0, 621); + return true; + case 8941: + cacheIndex = 4538; + reference = new EpsgCoordinateReferenceRecord(8941, (EpsgCoordinateSystemKind)1, 165); + return true; + case 8942: + cacheIndex = 4539; + reference = new EpsgCoordinateReferenceRecord(8942, (EpsgCoordinateSystemKind)0, 622); + return true; + case 8943: + cacheIndex = 4540; + reference = new EpsgCoordinateReferenceRecord(8943, (EpsgCoordinateSystemKind)1, 166); + return true; + case 8944: + cacheIndex = 4541; + reference = new EpsgCoordinateReferenceRecord(8944, (EpsgCoordinateSystemKind)0, 623); + return true; + case 8945: + cacheIndex = 4542; + reference = new EpsgCoordinateReferenceRecord(8945, (EpsgCoordinateSystemKind)1, 167); + return true; + case 8946: + cacheIndex = 4543; + reference = new EpsgCoordinateReferenceRecord(8946, (EpsgCoordinateSystemKind)0, 624); + return true; + case 8972: + cacheIndex = 4544; + reference = new EpsgCoordinateReferenceRecord(8972, (EpsgCoordinateSystemKind)0, 625); + return true; + case 8973: + cacheIndex = 4545; + reference = new EpsgCoordinateReferenceRecord(8973, (EpsgCoordinateSystemKind)0, 626); + return true; + case 8974: + cacheIndex = 4546; + reference = new EpsgCoordinateReferenceRecord(8974, (EpsgCoordinateSystemKind)0, 627); + return true; + case 8975: + cacheIndex = 4547; + reference = new EpsgCoordinateReferenceRecord(8975, (EpsgCoordinateSystemKind)0, 628); + return true; + case 8976: + cacheIndex = 4548; + reference = new EpsgCoordinateReferenceRecord(8976, (EpsgCoordinateSystemKind)0, 629); + return true; + case 8977: + cacheIndex = 4549; + reference = new EpsgCoordinateReferenceRecord(8977, (EpsgCoordinateSystemKind)0, 630); + return true; + case 8978: + cacheIndex = 4550; + reference = new EpsgCoordinateReferenceRecord(8978, (EpsgCoordinateSystemKind)0, 631); + return true; + case 8979: + cacheIndex = 4551; + reference = new EpsgCoordinateReferenceRecord(8979, (EpsgCoordinateSystemKind)0, 632); + return true; + case 8980: + cacheIndex = 4552; + reference = new EpsgCoordinateReferenceRecord(8980, (EpsgCoordinateSystemKind)0, 633); + return true; + case 8981: + cacheIndex = 4553; + reference = new EpsgCoordinateReferenceRecord(8981, (EpsgCoordinateSystemKind)0, 634); + return true; + case 8982: + cacheIndex = 4554; + reference = new EpsgCoordinateReferenceRecord(8982, (EpsgCoordinateSystemKind)0, 635); + return true; + case 8983: + cacheIndex = 4555; + reference = new EpsgCoordinateReferenceRecord(8983, (EpsgCoordinateSystemKind)0, 636); + return true; + case 8984: + cacheIndex = 4556; + reference = new EpsgCoordinateReferenceRecord(8984, (EpsgCoordinateSystemKind)0, 637); + return true; + case 8985: + cacheIndex = 4557; + reference = new EpsgCoordinateReferenceRecord(8985, (EpsgCoordinateSystemKind)0, 638); + return true; + case 8986: + cacheIndex = 4558; + reference = new EpsgCoordinateReferenceRecord(8986, (EpsgCoordinateSystemKind)0, 639); + return true; + case 8987: + cacheIndex = 4559; + reference = new EpsgCoordinateReferenceRecord(8987, (EpsgCoordinateSystemKind)0, 640); + return true; + case 8988: + cacheIndex = 4560; + reference = new EpsgCoordinateReferenceRecord(8988, (EpsgCoordinateSystemKind)0, 641); + return true; + case 8989: + cacheIndex = 4561; + reference = new EpsgCoordinateReferenceRecord(8989, (EpsgCoordinateSystemKind)0, 642); + return true; + case 8990: + cacheIndex = 4562; + reference = new EpsgCoordinateReferenceRecord(8990, (EpsgCoordinateSystemKind)0, 643); + return true; + case 8991: + cacheIndex = 4563; + reference = new EpsgCoordinateReferenceRecord(8991, (EpsgCoordinateSystemKind)0, 644); + return true; + case 8992: + cacheIndex = 4564; + reference = new EpsgCoordinateReferenceRecord(8992, (EpsgCoordinateSystemKind)0, 645); + return true; + case 8993: + cacheIndex = 4565; + reference = new EpsgCoordinateReferenceRecord(8993, (EpsgCoordinateSystemKind)0, 646); + return true; + case 8994: + cacheIndex = 4566; + reference = new EpsgCoordinateReferenceRecord(8994, (EpsgCoordinateSystemKind)0, 647); + return true; + case 8995: + cacheIndex = 4567; + reference = new EpsgCoordinateReferenceRecord(8995, (EpsgCoordinateSystemKind)0, 648); + return true; + case 8996: + cacheIndex = 4568; + reference = new EpsgCoordinateReferenceRecord(8996, (EpsgCoordinateSystemKind)0, 649); + return true; + case 8997: + cacheIndex = 4569; + reference = new EpsgCoordinateReferenceRecord(8997, (EpsgCoordinateSystemKind)0, 650); + return true; + case 8998: + cacheIndex = 4570; + reference = new EpsgCoordinateReferenceRecord(8998, (EpsgCoordinateSystemKind)0, 651); + return true; + case 8999: + cacheIndex = 4571; + reference = new EpsgCoordinateReferenceRecord(8999, (EpsgCoordinateSystemKind)0, 652); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket9(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 9000: + cacheIndex = 4572; + reference = new EpsgCoordinateReferenceRecord(9000, (EpsgCoordinateSystemKind)0, 653); + return true; + case 9001: + cacheIndex = 4573; + reference = new EpsgCoordinateReferenceRecord(9001, (EpsgCoordinateSystemKind)1, 168); + return true; + case 9002: + cacheIndex = 4574; + reference = new EpsgCoordinateReferenceRecord(9002, (EpsgCoordinateSystemKind)0, 654); + return true; + case 9003: + cacheIndex = 4575; + reference = new EpsgCoordinateReferenceRecord(9003, (EpsgCoordinateSystemKind)0, 655); + return true; + case 9004: + cacheIndex = 4576; + reference = new EpsgCoordinateReferenceRecord(9004, (EpsgCoordinateSystemKind)1, 169); + return true; + case 9005: + cacheIndex = 4577; + reference = new EpsgCoordinateReferenceRecord(9005, (EpsgCoordinateSystemKind)0, 656); + return true; + case 9006: + cacheIndex = 4578; + reference = new EpsgCoordinateReferenceRecord(9006, (EpsgCoordinateSystemKind)0, 657); + return true; + case 9007: + cacheIndex = 4579; + reference = new EpsgCoordinateReferenceRecord(9007, (EpsgCoordinateSystemKind)1, 170); + return true; + case 9008: + cacheIndex = 4580; + reference = new EpsgCoordinateReferenceRecord(9008, (EpsgCoordinateSystemKind)0, 658); + return true; + case 9009: + cacheIndex = 4581; + reference = new EpsgCoordinateReferenceRecord(9009, (EpsgCoordinateSystemKind)0, 659); + return true; + case 9010: + cacheIndex = 4582; + reference = new EpsgCoordinateReferenceRecord(9010, (EpsgCoordinateSystemKind)1, 171); + return true; + case 9011: + cacheIndex = 4583; + reference = new EpsgCoordinateReferenceRecord(9011, (EpsgCoordinateSystemKind)0, 660); + return true; + case 9012: + cacheIndex = 4584; + reference = new EpsgCoordinateReferenceRecord(9012, (EpsgCoordinateSystemKind)0, 661); + return true; + case 9013: + cacheIndex = 4585; + reference = new EpsgCoordinateReferenceRecord(9013, (EpsgCoordinateSystemKind)0, 662); + return true; + case 9014: + cacheIndex = 4586; + reference = new EpsgCoordinateReferenceRecord(9014, (EpsgCoordinateSystemKind)0, 663); + return true; + case 9015: + cacheIndex = 4587; + reference = new EpsgCoordinateReferenceRecord(9015, (EpsgCoordinateSystemKind)1, 172); + return true; + case 9016: + cacheIndex = 4588; + reference = new EpsgCoordinateReferenceRecord(9016, (EpsgCoordinateSystemKind)0, 664); + return true; + case 9017: + cacheIndex = 4589; + reference = new EpsgCoordinateReferenceRecord(9017, (EpsgCoordinateSystemKind)0, 665); + return true; + case 9018: + cacheIndex = 4590; + reference = new EpsgCoordinateReferenceRecord(9018, (EpsgCoordinateSystemKind)0, 666); + return true; + case 9019: + cacheIndex = 4591; + reference = new EpsgCoordinateReferenceRecord(9019, (EpsgCoordinateSystemKind)0, 667); + return true; + case 9039: + cacheIndex = 4592; + reference = new EpsgCoordinateReferenceRecord(9039, (EpsgCoordinateSystemKind)2, 3403); + return true; + case 9040: + cacheIndex = 4593; + reference = new EpsgCoordinateReferenceRecord(9040, (EpsgCoordinateSystemKind)2, 3404); + return true; + case 9053: + cacheIndex = 4594; + reference = new EpsgCoordinateReferenceRecord(9053, (EpsgCoordinateSystemKind)0, 668); + return true; + case 9054: + cacheIndex = 4595; + reference = new EpsgCoordinateReferenceRecord(9054, (EpsgCoordinateSystemKind)0, 669); + return true; + case 9055: + cacheIndex = 4596; + reference = new EpsgCoordinateReferenceRecord(9055, (EpsgCoordinateSystemKind)0, 670); + return true; + case 9056: + cacheIndex = 4597; + reference = new EpsgCoordinateReferenceRecord(9056, (EpsgCoordinateSystemKind)0, 671); + return true; + case 9057: + cacheIndex = 4598; + reference = new EpsgCoordinateReferenceRecord(9057, (EpsgCoordinateSystemKind)0, 672); + return true; + case 9059: + cacheIndex = 4599; + reference = new EpsgCoordinateReferenceRecord(9059, (EpsgCoordinateSystemKind)0, 673); + return true; + case 9060: + cacheIndex = 4600; + reference = new EpsgCoordinateReferenceRecord(9060, (EpsgCoordinateSystemKind)0, 674); + return true; + case 9061: + cacheIndex = 4601; + reference = new EpsgCoordinateReferenceRecord(9061, (EpsgCoordinateSystemKind)0, 675); + return true; + case 9062: + cacheIndex = 4602; + reference = new EpsgCoordinateReferenceRecord(9062, (EpsgCoordinateSystemKind)0, 676); + return true; + case 9063: + cacheIndex = 4603; + reference = new EpsgCoordinateReferenceRecord(9063, (EpsgCoordinateSystemKind)0, 677); + return true; + case 9064: + cacheIndex = 4604; + reference = new EpsgCoordinateReferenceRecord(9064, (EpsgCoordinateSystemKind)0, 678); + return true; + case 9065: + cacheIndex = 4605; + reference = new EpsgCoordinateReferenceRecord(9065, (EpsgCoordinateSystemKind)0, 679); + return true; + case 9066: + cacheIndex = 4606; + reference = new EpsgCoordinateReferenceRecord(9066, (EpsgCoordinateSystemKind)0, 680); + return true; + case 9067: + cacheIndex = 4607; + reference = new EpsgCoordinateReferenceRecord(9067, (EpsgCoordinateSystemKind)0, 681); + return true; + case 9068: + cacheIndex = 4608; + reference = new EpsgCoordinateReferenceRecord(9068, (EpsgCoordinateSystemKind)0, 682); + return true; + case 9069: + cacheIndex = 4609; + reference = new EpsgCoordinateReferenceRecord(9069, (EpsgCoordinateSystemKind)0, 683); + return true; + case 9070: + cacheIndex = 4610; + reference = new EpsgCoordinateReferenceRecord(9070, (EpsgCoordinateSystemKind)1, 173); + return true; + case 9071: + cacheIndex = 4611; + reference = new EpsgCoordinateReferenceRecord(9071, (EpsgCoordinateSystemKind)0, 684); + return true; + case 9072: + cacheIndex = 4612; + reference = new EpsgCoordinateReferenceRecord(9072, (EpsgCoordinateSystemKind)0, 685); + return true; + case 9073: + cacheIndex = 4613; + reference = new EpsgCoordinateReferenceRecord(9073, (EpsgCoordinateSystemKind)1, 174); + return true; + case 9074: + cacheIndex = 4614; + reference = new EpsgCoordinateReferenceRecord(9074, (EpsgCoordinateSystemKind)0, 686); + return true; + case 9075: + cacheIndex = 4615; + reference = new EpsgCoordinateReferenceRecord(9075, (EpsgCoordinateSystemKind)0, 687); + return true; + case 9130: + cacheIndex = 4616; + reference = new EpsgCoordinateReferenceRecord(9130, (EpsgCoordinateSystemKind)3, 193); + return true; + case 9138: + cacheIndex = 4617; + reference = new EpsgCoordinateReferenceRecord(9138, (EpsgCoordinateSystemKind)1, 175); + return true; + case 9139: + cacheIndex = 4618; + reference = new EpsgCoordinateReferenceRecord(9139, (EpsgCoordinateSystemKind)0, 688); + return true; + case 9140: + cacheIndex = 4619; + reference = new EpsgCoordinateReferenceRecord(9140, (EpsgCoordinateSystemKind)0, 689); + return true; + case 9141: + cacheIndex = 4620; + reference = new EpsgCoordinateReferenceRecord(9141, (EpsgCoordinateSystemKind)2, 3405); + return true; + case 9146: + cacheIndex = 4621; + reference = new EpsgCoordinateReferenceRecord(9146, (EpsgCoordinateSystemKind)1, 176); + return true; + case 9147: + cacheIndex = 4622; + reference = new EpsgCoordinateReferenceRecord(9147, (EpsgCoordinateSystemKind)0, 690); + return true; + case 9148: + cacheIndex = 4623; + reference = new EpsgCoordinateReferenceRecord(9148, (EpsgCoordinateSystemKind)0, 691); + return true; + case 9149: + cacheIndex = 4624; + reference = new EpsgCoordinateReferenceRecord(9149, (EpsgCoordinateSystemKind)2, 3406); + return true; + case 9150: + cacheIndex = 4625; + reference = new EpsgCoordinateReferenceRecord(9150, (EpsgCoordinateSystemKind)2, 3407); + return true; + case 9151: + cacheIndex = 4626; + reference = new EpsgCoordinateReferenceRecord(9151, (EpsgCoordinateSystemKind)1, 177); + return true; + case 9152: + cacheIndex = 4627; + reference = new EpsgCoordinateReferenceRecord(9152, (EpsgCoordinateSystemKind)0, 692); + return true; + case 9153: + cacheIndex = 4628; + reference = new EpsgCoordinateReferenceRecord(9153, (EpsgCoordinateSystemKind)0, 693); + return true; + case 9154: + cacheIndex = 4629; + reference = new EpsgCoordinateReferenceRecord(9154, (EpsgCoordinateSystemKind)2, 3408); + return true; + case 9155: + cacheIndex = 4630; + reference = new EpsgCoordinateReferenceRecord(9155, (EpsgCoordinateSystemKind)2, 3409); + return true; + case 9156: + cacheIndex = 4631; + reference = new EpsgCoordinateReferenceRecord(9156, (EpsgCoordinateSystemKind)2, 3410); + return true; + case 9157: + cacheIndex = 4632; + reference = new EpsgCoordinateReferenceRecord(9157, (EpsgCoordinateSystemKind)2, 3411); + return true; + case 9158: + cacheIndex = 4633; + reference = new EpsgCoordinateReferenceRecord(9158, (EpsgCoordinateSystemKind)2, 3412); + return true; + case 9159: + cacheIndex = 4634; + reference = new EpsgCoordinateReferenceRecord(9159, (EpsgCoordinateSystemKind)2, 3413); + return true; + case 9191: + cacheIndex = 4635; + reference = new EpsgCoordinateReferenceRecord(9191, (EpsgCoordinateSystemKind)2, 3414); + return true; + case 9205: + cacheIndex = 4636; + reference = new EpsgCoordinateReferenceRecord(9205, (EpsgCoordinateSystemKind)2, 3415); + return true; + case 9206: + cacheIndex = 4637; + reference = new EpsgCoordinateReferenceRecord(9206, (EpsgCoordinateSystemKind)2, 3416); + return true; + case 9207: + cacheIndex = 4638; + reference = new EpsgCoordinateReferenceRecord(9207, (EpsgCoordinateSystemKind)2, 3417); + return true; + case 9208: + cacheIndex = 4639; + reference = new EpsgCoordinateReferenceRecord(9208, (EpsgCoordinateSystemKind)2, 3418); + return true; + case 9209: + cacheIndex = 4640; + reference = new EpsgCoordinateReferenceRecord(9209, (EpsgCoordinateSystemKind)2, 3419); + return true; + case 9210: + cacheIndex = 4641; + reference = new EpsgCoordinateReferenceRecord(9210, (EpsgCoordinateSystemKind)2, 3420); + return true; + case 9211: + cacheIndex = 4642; + reference = new EpsgCoordinateReferenceRecord(9211, (EpsgCoordinateSystemKind)2, 3421); + return true; + case 9212: + cacheIndex = 4643; + reference = new EpsgCoordinateReferenceRecord(9212, (EpsgCoordinateSystemKind)2, 3422); + return true; + case 9213: + cacheIndex = 4644; + reference = new EpsgCoordinateReferenceRecord(9213, (EpsgCoordinateSystemKind)2, 3423); + return true; + case 9214: + cacheIndex = 4645; + reference = new EpsgCoordinateReferenceRecord(9214, (EpsgCoordinateSystemKind)2, 3424); + return true; + case 9215: + cacheIndex = 4646; + reference = new EpsgCoordinateReferenceRecord(9215, (EpsgCoordinateSystemKind)2, 3425); + return true; + case 9216: + cacheIndex = 4647; + reference = new EpsgCoordinateReferenceRecord(9216, (EpsgCoordinateSystemKind)2, 3426); + return true; + case 9217: + cacheIndex = 4648; + reference = new EpsgCoordinateReferenceRecord(9217, (EpsgCoordinateSystemKind)2, 3427); + return true; + case 9218: + cacheIndex = 4649; + reference = new EpsgCoordinateReferenceRecord(9218, (EpsgCoordinateSystemKind)2, 3428); + return true; + case 9221: + cacheIndex = 4650; + reference = new EpsgCoordinateReferenceRecord(9221, (EpsgCoordinateSystemKind)2, 3429); + return true; + case 9222: + cacheIndex = 4651; + reference = new EpsgCoordinateReferenceRecord(9222, (EpsgCoordinateSystemKind)2, 3430); + return true; + case 9245: + cacheIndex = 4652; + reference = new EpsgCoordinateReferenceRecord(9245, (EpsgCoordinateSystemKind)3, 194); + return true; + case 9248: + cacheIndex = 4653; + reference = new EpsgCoordinateReferenceRecord(9248, (EpsgCoordinateSystemKind)0, 694); + return true; + case 9249: + cacheIndex = 4654; + reference = new EpsgCoordinateReferenceRecord(9249, (EpsgCoordinateSystemKind)2, 3431); + return true; + case 9250: + cacheIndex = 4655; + reference = new EpsgCoordinateReferenceRecord(9250, (EpsgCoordinateSystemKind)2, 3432); + return true; + case 9251: + cacheIndex = 4656; + reference = new EpsgCoordinateReferenceRecord(9251, (EpsgCoordinateSystemKind)0, 695); + return true; + case 9252: + cacheIndex = 4657; + reference = new EpsgCoordinateReferenceRecord(9252, (EpsgCoordinateSystemKind)2, 3433); + return true; + case 9253: + cacheIndex = 4658; + reference = new EpsgCoordinateReferenceRecord(9253, (EpsgCoordinateSystemKind)0, 696); + return true; + case 9254: + cacheIndex = 4659; + reference = new EpsgCoordinateReferenceRecord(9254, (EpsgCoordinateSystemKind)2, 3434); + return true; + case 9255: + cacheIndex = 4660; + reference = new EpsgCoordinateReferenceRecord(9255, (EpsgCoordinateSystemKind)3, 195); + return true; + case 9265: + cacheIndex = 4661; + reference = new EpsgCoordinateReferenceRecord(9265, (EpsgCoordinateSystemKind)2, 3435); + return true; + case 9266: + cacheIndex = 4662; + reference = new EpsgCoordinateReferenceRecord(9266, (EpsgCoordinateSystemKind)1, 178); + return true; + case 9267: + cacheIndex = 4663; + reference = new EpsgCoordinateReferenceRecord(9267, (EpsgCoordinateSystemKind)0, 697); + return true; + case 9271: + cacheIndex = 4664; + reference = new EpsgCoordinateReferenceRecord(9271, (EpsgCoordinateSystemKind)2, 3436); + return true; + case 9272: + cacheIndex = 4665; + reference = new EpsgCoordinateReferenceRecord(9272, (EpsgCoordinateSystemKind)2, 3437); + return true; + case 9273: + cacheIndex = 4666; + reference = new EpsgCoordinateReferenceRecord(9273, (EpsgCoordinateSystemKind)2, 3438); + return true; + case 9274: + cacheIndex = 4667; + reference = new EpsgCoordinateReferenceRecord(9274, (EpsgCoordinateSystemKind)3, 196); + return true; + case 9279: + cacheIndex = 4668; + reference = new EpsgCoordinateReferenceRecord(9279, (EpsgCoordinateSystemKind)3, 197); + return true; + case 9284: + cacheIndex = 4669; + reference = new EpsgCoordinateReferenceRecord(9284, (EpsgCoordinateSystemKind)2, 3439); + return true; + case 9285: + cacheIndex = 4670; + reference = new EpsgCoordinateReferenceRecord(9285, (EpsgCoordinateSystemKind)2, 3440); + return true; + case 9286: + cacheIndex = 4671; + reference = new EpsgCoordinateReferenceRecord(9286, (EpsgCoordinateSystemKind)4, 155); + return true; + case 9287: + cacheIndex = 4672; + reference = new EpsgCoordinateReferenceRecord(9287, (EpsgCoordinateSystemKind)3, 198); + return true; + case 9288: + cacheIndex = 4673; + reference = new EpsgCoordinateReferenceRecord(9288, (EpsgCoordinateSystemKind)3, 199); + return true; + case 9289: + cacheIndex = 4674; + reference = new EpsgCoordinateReferenceRecord(9289, (EpsgCoordinateSystemKind)4, 156); + return true; + case 9290: + cacheIndex = 4675; + reference = new EpsgCoordinateReferenceRecord(9290, (EpsgCoordinateSystemKind)4, 157); + return true; + case 9292: + cacheIndex = 4676; + reference = new EpsgCoordinateReferenceRecord(9292, (EpsgCoordinateSystemKind)1, 179); + return true; + case 9293: + cacheIndex = 4677; + reference = new EpsgCoordinateReferenceRecord(9293, (EpsgCoordinateSystemKind)0, 698); + return true; + case 9294: + cacheIndex = 4678; + reference = new EpsgCoordinateReferenceRecord(9294, (EpsgCoordinateSystemKind)0, 699); + return true; + case 9295: + cacheIndex = 4679; + reference = new EpsgCoordinateReferenceRecord(9295, (EpsgCoordinateSystemKind)2, 3441); + return true; + case 9296: + cacheIndex = 4680; + reference = new EpsgCoordinateReferenceRecord(9296, (EpsgCoordinateSystemKind)2, 3442); + return true; + case 9297: + cacheIndex = 4681; + reference = new EpsgCoordinateReferenceRecord(9297, (EpsgCoordinateSystemKind)2, 3443); + return true; + case 9299: + cacheIndex = 4682; + reference = new EpsgCoordinateReferenceRecord(9299, (EpsgCoordinateSystemKind)0, 700); + return true; + case 9300: + cacheIndex = 4683; + reference = new EpsgCoordinateReferenceRecord(9300, (EpsgCoordinateSystemKind)2, 3444); + return true; + case 9303: + cacheIndex = 4684; + reference = new EpsgCoordinateReferenceRecord(9303, (EpsgCoordinateSystemKind)3, 200); + return true; + case 9306: + cacheIndex = 4685; + reference = new EpsgCoordinateReferenceRecord(9306, (EpsgCoordinateSystemKind)4, 158); + return true; + case 9307: + cacheIndex = 4686; + reference = new EpsgCoordinateReferenceRecord(9307, (EpsgCoordinateSystemKind)1, 180); + return true; + case 9308: + cacheIndex = 4687; + reference = new EpsgCoordinateReferenceRecord(9308, (EpsgCoordinateSystemKind)0, 701); + return true; + case 9309: + cacheIndex = 4688; + reference = new EpsgCoordinateReferenceRecord(9309, (EpsgCoordinateSystemKind)0, 702); + return true; + case 9311: + cacheIndex = 4689; + reference = new EpsgCoordinateReferenceRecord(9311, (EpsgCoordinateSystemKind)2, 3445); + return true; + case 9331: + cacheIndex = 4690; + reference = new EpsgCoordinateReferenceRecord(9331, (EpsgCoordinateSystemKind)1, 181); + return true; + case 9332: + cacheIndex = 4691; + reference = new EpsgCoordinateReferenceRecord(9332, (EpsgCoordinateSystemKind)0, 703); + return true; + case 9333: + cacheIndex = 4692; + reference = new EpsgCoordinateReferenceRecord(9333, (EpsgCoordinateSystemKind)0, 704); + return true; + case 9335: + cacheIndex = 4693; + reference = new EpsgCoordinateReferenceRecord(9335, (EpsgCoordinateSystemKind)3, 201); + return true; + case 9351: + cacheIndex = 4694; + reference = new EpsgCoordinateReferenceRecord(9351, (EpsgCoordinateSystemKind)3, 202); + return true; + case 9354: + cacheIndex = 4695; + reference = new EpsgCoordinateReferenceRecord(9354, (EpsgCoordinateSystemKind)2, 3446); + return true; + case 9356: + cacheIndex = 4696; + reference = new EpsgCoordinateReferenceRecord(9356, (EpsgCoordinateSystemKind)2, 3447); + return true; + case 9357: + cacheIndex = 4697; + reference = new EpsgCoordinateReferenceRecord(9357, (EpsgCoordinateSystemKind)2, 3448); + return true; + case 9358: + cacheIndex = 4698; + reference = new EpsgCoordinateReferenceRecord(9358, (EpsgCoordinateSystemKind)2, 3449); + return true; + case 9359: + cacheIndex = 4699; + reference = new EpsgCoordinateReferenceRecord(9359, (EpsgCoordinateSystemKind)2, 3450); + return true; + case 9360: + cacheIndex = 4700; + reference = new EpsgCoordinateReferenceRecord(9360, (EpsgCoordinateSystemKind)2, 3451); + return true; + case 9364: + cacheIndex = 4701; + reference = new EpsgCoordinateReferenceRecord(9364, (EpsgCoordinateSystemKind)0, 705); + return true; + case 9367: + cacheIndex = 4702; + reference = new EpsgCoordinateReferenceRecord(9367, (EpsgCoordinateSystemKind)2, 3452); + return true; + case 9368: + cacheIndex = 4703; + reference = new EpsgCoordinateReferenceRecord(9368, (EpsgCoordinateSystemKind)4, 159); + return true; + case 9372: + cacheIndex = 4704; + reference = new EpsgCoordinateReferenceRecord(9372, (EpsgCoordinateSystemKind)0, 706); + return true; + case 9373: + cacheIndex = 4705; + reference = new EpsgCoordinateReferenceRecord(9373, (EpsgCoordinateSystemKind)2, 3453); + return true; + case 9374: + cacheIndex = 4706; + reference = new EpsgCoordinateReferenceRecord(9374, (EpsgCoordinateSystemKind)4, 160); + return true; + case 9377: + cacheIndex = 4707; + reference = new EpsgCoordinateReferenceRecord(9377, (EpsgCoordinateSystemKind)2, 3454); + return true; + case 9378: + cacheIndex = 4708; + reference = new EpsgCoordinateReferenceRecord(9378, (EpsgCoordinateSystemKind)1, 182); + return true; + case 9379: + cacheIndex = 4709; + reference = new EpsgCoordinateReferenceRecord(9379, (EpsgCoordinateSystemKind)0, 707); + return true; + case 9380: + cacheIndex = 4710; + reference = new EpsgCoordinateReferenceRecord(9380, (EpsgCoordinateSystemKind)0, 708); + return true; + case 9384: + cacheIndex = 4711; + reference = new EpsgCoordinateReferenceRecord(9384, (EpsgCoordinateSystemKind)0, 709); + return true; + case 9387: + cacheIndex = 4712; + reference = new EpsgCoordinateReferenceRecord(9387, (EpsgCoordinateSystemKind)2, 3455); + return true; + case 9388: + cacheIndex = 4713; + reference = new EpsgCoordinateReferenceRecord(9388, (EpsgCoordinateSystemKind)4, 161); + return true; + case 9389: + cacheIndex = 4714; + reference = new EpsgCoordinateReferenceRecord(9389, (EpsgCoordinateSystemKind)3, 203); + return true; + case 9390: + cacheIndex = 4715; + reference = new EpsgCoordinateReferenceRecord(9390, (EpsgCoordinateSystemKind)3, 204); + return true; + case 9391: + cacheIndex = 4716; + reference = new EpsgCoordinateReferenceRecord(9391, (EpsgCoordinateSystemKind)2, 3456); + return true; + case 9392: + cacheIndex = 4717; + reference = new EpsgCoordinateReferenceRecord(9392, (EpsgCoordinateSystemKind)3, 205); + return true; + case 9393: + cacheIndex = 4718; + reference = new EpsgCoordinateReferenceRecord(9393, (EpsgCoordinateSystemKind)3, 206); + return true; + case 9394: + cacheIndex = 4719; + reference = new EpsgCoordinateReferenceRecord(9394, (EpsgCoordinateSystemKind)3, 207); + return true; + case 9395: + cacheIndex = 4720; + reference = new EpsgCoordinateReferenceRecord(9395, (EpsgCoordinateSystemKind)3, 208); + return true; + case 9396: + cacheIndex = 4721; + reference = new EpsgCoordinateReferenceRecord(9396, (EpsgCoordinateSystemKind)3, 209); + return true; + case 9397: + cacheIndex = 4722; + reference = new EpsgCoordinateReferenceRecord(9397, (EpsgCoordinateSystemKind)3, 210); + return true; + case 9398: + cacheIndex = 4723; + reference = new EpsgCoordinateReferenceRecord(9398, (EpsgCoordinateSystemKind)3, 211); + return true; + case 9399: + cacheIndex = 4724; + reference = new EpsgCoordinateReferenceRecord(9399, (EpsgCoordinateSystemKind)3, 212); + return true; + case 9400: + cacheIndex = 4725; + reference = new EpsgCoordinateReferenceRecord(9400, (EpsgCoordinateSystemKind)3, 213); + return true; + case 9401: + cacheIndex = 4726; + reference = new EpsgCoordinateReferenceRecord(9401, (EpsgCoordinateSystemKind)3, 214); + return true; + case 9402: + cacheIndex = 4727; + reference = new EpsgCoordinateReferenceRecord(9402, (EpsgCoordinateSystemKind)3, 215); + return true; + case 9403: + cacheIndex = 4728; + reference = new EpsgCoordinateReferenceRecord(9403, (EpsgCoordinateSystemKind)0, 710); + return true; + case 9404: + cacheIndex = 4729; + reference = new EpsgCoordinateReferenceRecord(9404, (EpsgCoordinateSystemKind)2, 3457); + return true; + case 9405: + cacheIndex = 4730; + reference = new EpsgCoordinateReferenceRecord(9405, (EpsgCoordinateSystemKind)2, 3458); + return true; + case 9406: + cacheIndex = 4731; + reference = new EpsgCoordinateReferenceRecord(9406, (EpsgCoordinateSystemKind)2, 3459); + return true; + case 9407: + cacheIndex = 4732; + reference = new EpsgCoordinateReferenceRecord(9407, (EpsgCoordinateSystemKind)2, 3460); + return true; + case 9422: + cacheIndex = 4733; + reference = new EpsgCoordinateReferenceRecord(9422, (EpsgCoordinateSystemKind)4, 162); + return true; + case 9423: + cacheIndex = 4734; + reference = new EpsgCoordinateReferenceRecord(9423, (EpsgCoordinateSystemKind)4, 163); + return true; + case 9424: + cacheIndex = 4735; + reference = new EpsgCoordinateReferenceRecord(9424, (EpsgCoordinateSystemKind)4, 164); + return true; + case 9425: + cacheIndex = 4736; + reference = new EpsgCoordinateReferenceRecord(9425, (EpsgCoordinateSystemKind)4, 165); + return true; + case 9426: + cacheIndex = 4737; + reference = new EpsgCoordinateReferenceRecord(9426, (EpsgCoordinateSystemKind)4, 166); + return true; + case 9427: + cacheIndex = 4738; + reference = new EpsgCoordinateReferenceRecord(9427, (EpsgCoordinateSystemKind)4, 167); + return true; + case 9428: + cacheIndex = 4739; + reference = new EpsgCoordinateReferenceRecord(9428, (EpsgCoordinateSystemKind)4, 168); + return true; + case 9429: + cacheIndex = 4740; + reference = new EpsgCoordinateReferenceRecord(9429, (EpsgCoordinateSystemKind)4, 169); + return true; + case 9430: + cacheIndex = 4741; + reference = new EpsgCoordinateReferenceRecord(9430, (EpsgCoordinateSystemKind)4, 170); + return true; + case 9449: + cacheIndex = 4742; + reference = new EpsgCoordinateReferenceRecord(9449, (EpsgCoordinateSystemKind)4, 171); + return true; + case 9450: + cacheIndex = 4743; + reference = new EpsgCoordinateReferenceRecord(9450, (EpsgCoordinateSystemKind)4, 172); + return true; + case 9453: + cacheIndex = 4744; + reference = new EpsgCoordinateReferenceRecord(9453, (EpsgCoordinateSystemKind)0, 711); + return true; + case 9456: + cacheIndex = 4745; + reference = new EpsgCoordinateReferenceRecord(9456, (EpsgCoordinateSystemKind)2, 3461); + return true; + case 9457: + cacheIndex = 4746; + reference = new EpsgCoordinateReferenceRecord(9457, (EpsgCoordinateSystemKind)4, 173); + return true; + case 9458: + cacheIndex = 4747; + reference = new EpsgCoordinateReferenceRecord(9458, (EpsgCoordinateSystemKind)3, 216); + return true; + case 9462: + cacheIndex = 4748; + reference = new EpsgCoordinateReferenceRecord(9462, (EpsgCoordinateSystemKind)4, 174); + return true; + case 9463: + cacheIndex = 4749; + reference = new EpsgCoordinateReferenceRecord(9463, (EpsgCoordinateSystemKind)4, 175); + return true; + case 9464: + cacheIndex = 4750; + reference = new EpsgCoordinateReferenceRecord(9464, (EpsgCoordinateSystemKind)4, 176); + return true; + case 9468: + cacheIndex = 4751; + reference = new EpsgCoordinateReferenceRecord(9468, (EpsgCoordinateSystemKind)1, 183); + return true; + case 9469: + cacheIndex = 4752; + reference = new EpsgCoordinateReferenceRecord(9469, (EpsgCoordinateSystemKind)0, 712); + return true; + case 9470: + cacheIndex = 4753; + reference = new EpsgCoordinateReferenceRecord(9470, (EpsgCoordinateSystemKind)0, 713); + return true; + case 9471: + cacheIndex = 4754; + reference = new EpsgCoordinateReferenceRecord(9471, (EpsgCoordinateSystemKind)3, 217); + return true; + case 9473: + cacheIndex = 4755; + reference = new EpsgCoordinateReferenceRecord(9473, (EpsgCoordinateSystemKind)2, 3462); + return true; + case 9474: + cacheIndex = 4756; + reference = new EpsgCoordinateReferenceRecord(9474, (EpsgCoordinateSystemKind)0, 714); + return true; + case 9475: + cacheIndex = 4757; + reference = new EpsgCoordinateReferenceRecord(9475, (EpsgCoordinateSystemKind)0, 715); + return true; + case 9476: + cacheIndex = 4758; + reference = new EpsgCoordinateReferenceRecord(9476, (EpsgCoordinateSystemKind)2, 3463); + return true; + case 9477: + cacheIndex = 4759; + reference = new EpsgCoordinateReferenceRecord(9477, (EpsgCoordinateSystemKind)2, 3464); + return true; + case 9478: + cacheIndex = 4760; + reference = new EpsgCoordinateReferenceRecord(9478, (EpsgCoordinateSystemKind)2, 3465); + return true; + case 9479: + cacheIndex = 4761; + reference = new EpsgCoordinateReferenceRecord(9479, (EpsgCoordinateSystemKind)2, 3466); + return true; + case 9480: + cacheIndex = 4762; + reference = new EpsgCoordinateReferenceRecord(9480, (EpsgCoordinateSystemKind)2, 3467); + return true; + case 9481: + cacheIndex = 4763; + reference = new EpsgCoordinateReferenceRecord(9481, (EpsgCoordinateSystemKind)2, 3468); + return true; + case 9482: + cacheIndex = 4764; + reference = new EpsgCoordinateReferenceRecord(9482, (EpsgCoordinateSystemKind)2, 3469); + return true; + case 9487: + cacheIndex = 4765; + reference = new EpsgCoordinateReferenceRecord(9487, (EpsgCoordinateSystemKind)2, 3470); + return true; + case 9488: + cacheIndex = 4766; + reference = new EpsgCoordinateReferenceRecord(9488, (EpsgCoordinateSystemKind)2, 3471); + return true; + case 9489: + cacheIndex = 4767; + reference = new EpsgCoordinateReferenceRecord(9489, (EpsgCoordinateSystemKind)2, 3472); + return true; + case 9490: + cacheIndex = 4768; + reference = new EpsgCoordinateReferenceRecord(9490, (EpsgCoordinateSystemKind)2, 3473); + return true; + case 9491: + cacheIndex = 4769; + reference = new EpsgCoordinateReferenceRecord(9491, (EpsgCoordinateSystemKind)2, 3474); + return true; + case 9492: + cacheIndex = 4770; + reference = new EpsgCoordinateReferenceRecord(9492, (EpsgCoordinateSystemKind)2, 3475); + return true; + case 9493: + cacheIndex = 4771; + reference = new EpsgCoordinateReferenceRecord(9493, (EpsgCoordinateSystemKind)2, 3476); + return true; + case 9494: + cacheIndex = 4772; + reference = new EpsgCoordinateReferenceRecord(9494, (EpsgCoordinateSystemKind)2, 3477); + return true; + case 9498: + cacheIndex = 4773; + reference = new EpsgCoordinateReferenceRecord(9498, (EpsgCoordinateSystemKind)2, 3478); + return true; + case 9500: + cacheIndex = 4774; + reference = new EpsgCoordinateReferenceRecord(9500, (EpsgCoordinateSystemKind)4, 177); + return true; + case 9501: + cacheIndex = 4775; + reference = new EpsgCoordinateReferenceRecord(9501, (EpsgCoordinateSystemKind)4, 178); + return true; + case 9502: + cacheIndex = 4776; + reference = new EpsgCoordinateReferenceRecord(9502, (EpsgCoordinateSystemKind)4, 179); + return true; + case 9503: + cacheIndex = 4777; + reference = new EpsgCoordinateReferenceRecord(9503, (EpsgCoordinateSystemKind)4, 180); + return true; + case 9504: + cacheIndex = 4778; + reference = new EpsgCoordinateReferenceRecord(9504, (EpsgCoordinateSystemKind)4, 181); + return true; + case 9505: + cacheIndex = 4779; + reference = new EpsgCoordinateReferenceRecord(9505, (EpsgCoordinateSystemKind)4, 182); + return true; + case 9506: + cacheIndex = 4780; + reference = new EpsgCoordinateReferenceRecord(9506, (EpsgCoordinateSystemKind)4, 183); + return true; + case 9507: + cacheIndex = 4781; + reference = new EpsgCoordinateReferenceRecord(9507, (EpsgCoordinateSystemKind)4, 184); + return true; + case 9508: + cacheIndex = 4782; + reference = new EpsgCoordinateReferenceRecord(9508, (EpsgCoordinateSystemKind)4, 185); + return true; + case 9509: + cacheIndex = 4783; + reference = new EpsgCoordinateReferenceRecord(9509, (EpsgCoordinateSystemKind)4, 186); + return true; + case 9510: + cacheIndex = 4784; + reference = new EpsgCoordinateReferenceRecord(9510, (EpsgCoordinateSystemKind)4, 187); + return true; + case 9511: + cacheIndex = 4785; + reference = new EpsgCoordinateReferenceRecord(9511, (EpsgCoordinateSystemKind)4, 188); + return true; + case 9512: + cacheIndex = 4786; + reference = new EpsgCoordinateReferenceRecord(9512, (EpsgCoordinateSystemKind)4, 189); + return true; + case 9513: + cacheIndex = 4787; + reference = new EpsgCoordinateReferenceRecord(9513, (EpsgCoordinateSystemKind)4, 190); + return true; + case 9514: + cacheIndex = 4788; + reference = new EpsgCoordinateReferenceRecord(9514, (EpsgCoordinateSystemKind)4, 191); + return true; + case 9515: + cacheIndex = 4789; + reference = new EpsgCoordinateReferenceRecord(9515, (EpsgCoordinateSystemKind)4, 192); + return true; + case 9516: + cacheIndex = 4790; + reference = new EpsgCoordinateReferenceRecord(9516, (EpsgCoordinateSystemKind)4, 193); + return true; + case 9517: + cacheIndex = 4791; + reference = new EpsgCoordinateReferenceRecord(9517, (EpsgCoordinateSystemKind)4, 194); + return true; + case 9518: + cacheIndex = 4792; + reference = new EpsgCoordinateReferenceRecord(9518, (EpsgCoordinateSystemKind)4, 195); + return true; + case 9519: + cacheIndex = 4793; + reference = new EpsgCoordinateReferenceRecord(9519, (EpsgCoordinateSystemKind)4, 196); + return true; + case 9520: + cacheIndex = 4794; + reference = new EpsgCoordinateReferenceRecord(9520, (EpsgCoordinateSystemKind)4, 197); + return true; + case 9521: + cacheIndex = 4795; + reference = new EpsgCoordinateReferenceRecord(9521, (EpsgCoordinateSystemKind)4, 198); + return true; + case 9522: + cacheIndex = 4796; + reference = new EpsgCoordinateReferenceRecord(9522, (EpsgCoordinateSystemKind)4, 199); + return true; + case 9523: + cacheIndex = 4797; + reference = new EpsgCoordinateReferenceRecord(9523, (EpsgCoordinateSystemKind)4, 200); + return true; + case 9524: + cacheIndex = 4798; + reference = new EpsgCoordinateReferenceRecord(9524, (EpsgCoordinateSystemKind)4, 201); + return true; + case 9525: + cacheIndex = 4799; + reference = new EpsgCoordinateReferenceRecord(9525, (EpsgCoordinateSystemKind)4, 202); + return true; + case 9526: + cacheIndex = 4800; + reference = new EpsgCoordinateReferenceRecord(9526, (EpsgCoordinateSystemKind)4, 203); + return true; + case 9527: + cacheIndex = 4801; + reference = new EpsgCoordinateReferenceRecord(9527, (EpsgCoordinateSystemKind)4, 204); + return true; + case 9528: + cacheIndex = 4802; + reference = new EpsgCoordinateReferenceRecord(9528, (EpsgCoordinateSystemKind)4, 205); + return true; + case 9529: + cacheIndex = 4803; + reference = new EpsgCoordinateReferenceRecord(9529, (EpsgCoordinateSystemKind)4, 206); + return true; + case 9530: + cacheIndex = 4804; + reference = new EpsgCoordinateReferenceRecord(9530, (EpsgCoordinateSystemKind)4, 207); + return true; + case 9531: + cacheIndex = 4805; + reference = new EpsgCoordinateReferenceRecord(9531, (EpsgCoordinateSystemKind)4, 208); + return true; + case 9532: + cacheIndex = 4806; + reference = new EpsgCoordinateReferenceRecord(9532, (EpsgCoordinateSystemKind)4, 209); + return true; + case 9533: + cacheIndex = 4807; + reference = new EpsgCoordinateReferenceRecord(9533, (EpsgCoordinateSystemKind)4, 210); + return true; + case 9534: + cacheIndex = 4808; + reference = new EpsgCoordinateReferenceRecord(9534, (EpsgCoordinateSystemKind)4, 211); + return true; + case 9535: + cacheIndex = 4809; + reference = new EpsgCoordinateReferenceRecord(9535, (EpsgCoordinateSystemKind)4, 212); + return true; + case 9536: + cacheIndex = 4810; + reference = new EpsgCoordinateReferenceRecord(9536, (EpsgCoordinateSystemKind)4, 213); + return true; + case 9537: + cacheIndex = 4811; + reference = new EpsgCoordinateReferenceRecord(9537, (EpsgCoordinateSystemKind)4, 214); + return true; + case 9538: + cacheIndex = 4812; + reference = new EpsgCoordinateReferenceRecord(9538, (EpsgCoordinateSystemKind)4, 215); + return true; + case 9539: + cacheIndex = 4813; + reference = new EpsgCoordinateReferenceRecord(9539, (EpsgCoordinateSystemKind)4, 216); + return true; + case 9540: + cacheIndex = 4814; + reference = new EpsgCoordinateReferenceRecord(9540, (EpsgCoordinateSystemKind)4, 217); + return true; + case 9541: + cacheIndex = 4815; + reference = new EpsgCoordinateReferenceRecord(9541, (EpsgCoordinateSystemKind)4, 218); + return true; + case 9542: + cacheIndex = 4816; + reference = new EpsgCoordinateReferenceRecord(9542, (EpsgCoordinateSystemKind)4, 219); + return true; + case 9543: + cacheIndex = 4817; + reference = new EpsgCoordinateReferenceRecord(9543, (EpsgCoordinateSystemKind)4, 220); + return true; + case 9544: + cacheIndex = 4818; + reference = new EpsgCoordinateReferenceRecord(9544, (EpsgCoordinateSystemKind)4, 221); + return true; + case 9545: + cacheIndex = 4819; + reference = new EpsgCoordinateReferenceRecord(9545, (EpsgCoordinateSystemKind)1, 184); + return true; + case 9546: + cacheIndex = 4820; + reference = new EpsgCoordinateReferenceRecord(9546, (EpsgCoordinateSystemKind)0, 716); + return true; + case 9547: + cacheIndex = 4821; + reference = new EpsgCoordinateReferenceRecord(9547, (EpsgCoordinateSystemKind)0, 717); + return true; + case 9549: + cacheIndex = 4822; + reference = new EpsgCoordinateReferenceRecord(9549, (EpsgCoordinateSystemKind)2, 3479); + return true; + case 9650: + cacheIndex = 4823; + reference = new EpsgCoordinateReferenceRecord(9650, (EpsgCoordinateSystemKind)3, 218); + return true; + case 9651: + cacheIndex = 4824; + reference = new EpsgCoordinateReferenceRecord(9651, (EpsgCoordinateSystemKind)3, 219); + return true; + case 9656: + cacheIndex = 4825; + reference = new EpsgCoordinateReferenceRecord(9656, (EpsgCoordinateSystemKind)4, 222); + return true; + case 9657: + cacheIndex = 4826; + reference = new EpsgCoordinateReferenceRecord(9657, (EpsgCoordinateSystemKind)4, 223); + return true; + case 9663: + cacheIndex = 4827; + reference = new EpsgCoordinateReferenceRecord(9663, (EpsgCoordinateSystemKind)3, 220); + return true; + case 9666: + cacheIndex = 4828; + reference = new EpsgCoordinateReferenceRecord(9666, (EpsgCoordinateSystemKind)3, 221); + return true; + case 9669: + cacheIndex = 4829; + reference = new EpsgCoordinateReferenceRecord(9669, (EpsgCoordinateSystemKind)3, 222); + return true; + case 9672: + cacheIndex = 4830; + reference = new EpsgCoordinateReferenceRecord(9672, (EpsgCoordinateSystemKind)3, 223); + return true; + case 9674: + cacheIndex = 4831; + reference = new EpsgCoordinateReferenceRecord(9674, (EpsgCoordinateSystemKind)2, 3480); + return true; + case 9675: + cacheIndex = 4832; + reference = new EpsgCoordinateReferenceRecord(9675, (EpsgCoordinateSystemKind)3, 224); + return true; + case 9678: + cacheIndex = 4833; + reference = new EpsgCoordinateReferenceRecord(9678, (EpsgCoordinateSystemKind)2, 3481); + return true; + case 9680: + cacheIndex = 4834; + reference = new EpsgCoordinateReferenceRecord(9680, (EpsgCoordinateSystemKind)2, 3482); + return true; + case 9681: + cacheIndex = 4835; + reference = new EpsgCoordinateReferenceRecord(9681, (EpsgCoordinateSystemKind)3, 225); + return true; + case 9694: + cacheIndex = 4836; + reference = new EpsgCoordinateReferenceRecord(9694, (EpsgCoordinateSystemKind)1, 185); + return true; + case 9695: + cacheIndex = 4837; + reference = new EpsgCoordinateReferenceRecord(9695, (EpsgCoordinateSystemKind)0, 718); + return true; + case 9696: + cacheIndex = 4838; + reference = new EpsgCoordinateReferenceRecord(9696, (EpsgCoordinateSystemKind)0, 719); + return true; + case 9697: + cacheIndex = 4839; + reference = new EpsgCoordinateReferenceRecord(9697, (EpsgCoordinateSystemKind)2, 3483); + return true; + case 9698: + cacheIndex = 4840; + reference = new EpsgCoordinateReferenceRecord(9698, (EpsgCoordinateSystemKind)2, 3484); + return true; + case 9699: + cacheIndex = 4841; + reference = new EpsgCoordinateReferenceRecord(9699, (EpsgCoordinateSystemKind)2, 3485); + return true; + case 9700: + cacheIndex = 4842; + reference = new EpsgCoordinateReferenceRecord(9700, (EpsgCoordinateSystemKind)1, 186); + return true; + case 9701: + cacheIndex = 4843; + reference = new EpsgCoordinateReferenceRecord(9701, (EpsgCoordinateSystemKind)0, 720); + return true; + case 9702: + cacheIndex = 4844; + reference = new EpsgCoordinateReferenceRecord(9702, (EpsgCoordinateSystemKind)0, 721); + return true; + case 9705: + cacheIndex = 4845; + reference = new EpsgCoordinateReferenceRecord(9705, (EpsgCoordinateSystemKind)4, 224); + return true; + case 9707: + cacheIndex = 4846; + reference = new EpsgCoordinateReferenceRecord(9707, (EpsgCoordinateSystemKind)4, 225); + return true; + case 9709: + cacheIndex = 4847; + reference = new EpsgCoordinateReferenceRecord(9709, (EpsgCoordinateSystemKind)2, 3486); + return true; + case 9711: + cacheIndex = 4848; + reference = new EpsgCoordinateReferenceRecord(9711, (EpsgCoordinateSystemKind)4, 226); + return true; + case 9712: + cacheIndex = 4849; + reference = new EpsgCoordinateReferenceRecord(9712, (EpsgCoordinateSystemKind)2, 3487); + return true; + case 9713: + cacheIndex = 4850; + reference = new EpsgCoordinateReferenceRecord(9713, (EpsgCoordinateSystemKind)2, 3488); + return true; + case 9714: + cacheIndex = 4851; + reference = new EpsgCoordinateReferenceRecord(9714, (EpsgCoordinateSystemKind)4, 227); + return true; + case 9715: + cacheIndex = 4852; + reference = new EpsgCoordinateReferenceRecord(9715, (EpsgCoordinateSystemKind)4, 228); + return true; + case 9716: + cacheIndex = 4853; + reference = new EpsgCoordinateReferenceRecord(9716, (EpsgCoordinateSystemKind)2, 3489); + return true; + case 9721: + cacheIndex = 4854; + reference = new EpsgCoordinateReferenceRecord(9721, (EpsgCoordinateSystemKind)3, 226); + return true; + case 9722: + cacheIndex = 4855; + reference = new EpsgCoordinateReferenceRecord(9722, (EpsgCoordinateSystemKind)3, 227); + return true; + case 9723: + cacheIndex = 4856; + reference = new EpsgCoordinateReferenceRecord(9723, (EpsgCoordinateSystemKind)4, 229); + return true; + case 9724: + cacheIndex = 4857; + reference = new EpsgCoordinateReferenceRecord(9724, (EpsgCoordinateSystemKind)4, 230); + return true; + case 9725: + cacheIndex = 4858; + reference = new EpsgCoordinateReferenceRecord(9725, (EpsgCoordinateSystemKind)4, 231); + return true; + case 9739: + cacheIndex = 4859; + reference = new EpsgCoordinateReferenceRecord(9739, (EpsgCoordinateSystemKind)0, 722); + return true; + case 9741: + cacheIndex = 4860; + reference = new EpsgCoordinateReferenceRecord(9741, (EpsgCoordinateSystemKind)2, 3490); + return true; + case 9742: + cacheIndex = 4861; + reference = new EpsgCoordinateReferenceRecord(9742, (EpsgCoordinateSystemKind)4, 232); + return true; + case 9748: + cacheIndex = 4862; + reference = new EpsgCoordinateReferenceRecord(9748, (EpsgCoordinateSystemKind)2, 3491); + return true; + case 9749: + cacheIndex = 4863; + reference = new EpsgCoordinateReferenceRecord(9749, (EpsgCoordinateSystemKind)2, 3492); + return true; + case 9753: + cacheIndex = 4864; + reference = new EpsgCoordinateReferenceRecord(9753, (EpsgCoordinateSystemKind)1, 187); + return true; + case 9754: + cacheIndex = 4865; + reference = new EpsgCoordinateReferenceRecord(9754, (EpsgCoordinateSystemKind)0, 723); + return true; + case 9755: + cacheIndex = 4866; + reference = new EpsgCoordinateReferenceRecord(9755, (EpsgCoordinateSystemKind)0, 724); + return true; + case 9758: + cacheIndex = 4867; + reference = new EpsgCoordinateReferenceRecord(9758, (EpsgCoordinateSystemKind)0, 725); + return true; + case 9761: + cacheIndex = 4868; + reference = new EpsgCoordinateReferenceRecord(9761, (EpsgCoordinateSystemKind)2, 3493); + return true; + case 9762: + cacheIndex = 4869; + reference = new EpsgCoordinateReferenceRecord(9762, (EpsgCoordinateSystemKind)4, 233); + return true; + case 9763: + cacheIndex = 4870; + reference = new EpsgCoordinateReferenceRecord(9763, (EpsgCoordinateSystemKind)0, 726); + return true; + case 9766: + cacheIndex = 4871; + reference = new EpsgCoordinateReferenceRecord(9766, (EpsgCoordinateSystemKind)2, 3494); + return true; + case 9767: + cacheIndex = 4872; + reference = new EpsgCoordinateReferenceRecord(9767, (EpsgCoordinateSystemKind)4, 234); + return true; + case 9775: + cacheIndex = 4873; + reference = new EpsgCoordinateReferenceRecord(9775, (EpsgCoordinateSystemKind)1, 188); + return true; + case 9776: + cacheIndex = 4874; + reference = new EpsgCoordinateReferenceRecord(9776, (EpsgCoordinateSystemKind)0, 727); + return true; + case 9777: + cacheIndex = 4875; + reference = new EpsgCoordinateReferenceRecord(9777, (EpsgCoordinateSystemKind)0, 728); + return true; + case 9778: + cacheIndex = 4876; + reference = new EpsgCoordinateReferenceRecord(9778, (EpsgCoordinateSystemKind)0, 729); + return true; + case 9779: + cacheIndex = 4877; + reference = new EpsgCoordinateReferenceRecord(9779, (EpsgCoordinateSystemKind)0, 730); + return true; + case 9780: + cacheIndex = 4878; + reference = new EpsgCoordinateReferenceRecord(9780, (EpsgCoordinateSystemKind)1, 189); + return true; + case 9781: + cacheIndex = 4879; + reference = new EpsgCoordinateReferenceRecord(9781, (EpsgCoordinateSystemKind)0, 731); + return true; + case 9782: + cacheIndex = 4880; + reference = new EpsgCoordinateReferenceRecord(9782, (EpsgCoordinateSystemKind)0, 732); + return true; + case 9783: + cacheIndex = 4881; + reference = new EpsgCoordinateReferenceRecord(9783, (EpsgCoordinateSystemKind)0, 733); + return true; + case 9784: + cacheIndex = 4882; + reference = new EpsgCoordinateReferenceRecord(9784, (EpsgCoordinateSystemKind)0, 734); + return true; + case 9785: + cacheIndex = 4883; + reference = new EpsgCoordinateReferenceRecord(9785, (EpsgCoordinateSystemKind)4, 235); + return true; + case 9793: + cacheIndex = 4884; + reference = new EpsgCoordinateReferenceRecord(9793, (EpsgCoordinateSystemKind)2, 3495); + return true; + case 9794: + cacheIndex = 4885; + reference = new EpsgCoordinateReferenceRecord(9794, (EpsgCoordinateSystemKind)2, 3496); + return true; + case 9821: + cacheIndex = 4886; + reference = new EpsgCoordinateReferenceRecord(9821, (EpsgCoordinateSystemKind)2, 3497); + return true; + case 9822: + cacheIndex = 4887; + reference = new EpsgCoordinateReferenceRecord(9822, (EpsgCoordinateSystemKind)2, 3498); + return true; + case 9823: + cacheIndex = 4888; + reference = new EpsgCoordinateReferenceRecord(9823, (EpsgCoordinateSystemKind)2, 3499); + return true; + case 9824: + cacheIndex = 4889; + reference = new EpsgCoordinateReferenceRecord(9824, (EpsgCoordinateSystemKind)2, 3500); + return true; + case 9825: + cacheIndex = 4890; + reference = new EpsgCoordinateReferenceRecord(9825, (EpsgCoordinateSystemKind)2, 3501); + return true; + case 9826: + cacheIndex = 4891; + reference = new EpsgCoordinateReferenceRecord(9826, (EpsgCoordinateSystemKind)2, 3502); + return true; + case 9827: + cacheIndex = 4892; + reference = new EpsgCoordinateReferenceRecord(9827, (EpsgCoordinateSystemKind)2, 3503); + return true; + case 9828: + cacheIndex = 4893; + reference = new EpsgCoordinateReferenceRecord(9828, (EpsgCoordinateSystemKind)2, 3504); + return true; + case 9829: + cacheIndex = 4894; + reference = new EpsgCoordinateReferenceRecord(9829, (EpsgCoordinateSystemKind)2, 3505); + return true; + case 9830: + cacheIndex = 4895; + reference = new EpsgCoordinateReferenceRecord(9830, (EpsgCoordinateSystemKind)2, 3506); + return true; + case 9831: + cacheIndex = 4896; + reference = new EpsgCoordinateReferenceRecord(9831, (EpsgCoordinateSystemKind)2, 3507); + return true; + case 9832: + cacheIndex = 4897; + reference = new EpsgCoordinateReferenceRecord(9832, (EpsgCoordinateSystemKind)2, 3508); + return true; + case 9833: + cacheIndex = 4898; + reference = new EpsgCoordinateReferenceRecord(9833, (EpsgCoordinateSystemKind)2, 3509); + return true; + case 9834: + cacheIndex = 4899; + reference = new EpsgCoordinateReferenceRecord(9834, (EpsgCoordinateSystemKind)2, 3510); + return true; + case 9835: + cacheIndex = 4900; + reference = new EpsgCoordinateReferenceRecord(9835, (EpsgCoordinateSystemKind)2, 3511); + return true; + case 9836: + cacheIndex = 4901; + reference = new EpsgCoordinateReferenceRecord(9836, (EpsgCoordinateSystemKind)2, 3512); + return true; + case 9837: + cacheIndex = 4902; + reference = new EpsgCoordinateReferenceRecord(9837, (EpsgCoordinateSystemKind)2, 3513); + return true; + case 9838: + cacheIndex = 4903; + reference = new EpsgCoordinateReferenceRecord(9838, (EpsgCoordinateSystemKind)2, 3514); + return true; + case 9839: + cacheIndex = 4904; + reference = new EpsgCoordinateReferenceRecord(9839, (EpsgCoordinateSystemKind)2, 3515); + return true; + case 9840: + cacheIndex = 4905; + reference = new EpsgCoordinateReferenceRecord(9840, (EpsgCoordinateSystemKind)2, 3516); + return true; + case 9841: + cacheIndex = 4906; + reference = new EpsgCoordinateReferenceRecord(9841, (EpsgCoordinateSystemKind)2, 3517); + return true; + case 9842: + cacheIndex = 4907; + reference = new EpsgCoordinateReferenceRecord(9842, (EpsgCoordinateSystemKind)2, 3518); + return true; + case 9843: + cacheIndex = 4908; + reference = new EpsgCoordinateReferenceRecord(9843, (EpsgCoordinateSystemKind)2, 3519); + return true; + case 9844: + cacheIndex = 4909; + reference = new EpsgCoordinateReferenceRecord(9844, (EpsgCoordinateSystemKind)2, 3520); + return true; + case 9845: + cacheIndex = 4910; + reference = new EpsgCoordinateReferenceRecord(9845, (EpsgCoordinateSystemKind)2, 3521); + return true; + case 9846: + cacheIndex = 4911; + reference = new EpsgCoordinateReferenceRecord(9846, (EpsgCoordinateSystemKind)2, 3522); + return true; + case 9847: + cacheIndex = 4912; + reference = new EpsgCoordinateReferenceRecord(9847, (EpsgCoordinateSystemKind)2, 3523); + return true; + case 9848: + cacheIndex = 4913; + reference = new EpsgCoordinateReferenceRecord(9848, (EpsgCoordinateSystemKind)2, 3524); + return true; + case 9849: + cacheIndex = 4914; + reference = new EpsgCoordinateReferenceRecord(9849, (EpsgCoordinateSystemKind)2, 3525); + return true; + case 9850: + cacheIndex = 4915; + reference = new EpsgCoordinateReferenceRecord(9850, (EpsgCoordinateSystemKind)2, 3526); + return true; + case 9851: + cacheIndex = 4916; + reference = new EpsgCoordinateReferenceRecord(9851, (EpsgCoordinateSystemKind)2, 3527); + return true; + case 9852: + cacheIndex = 4917; + reference = new EpsgCoordinateReferenceRecord(9852, (EpsgCoordinateSystemKind)2, 3528); + return true; + case 9853: + cacheIndex = 4918; + reference = new EpsgCoordinateReferenceRecord(9853, (EpsgCoordinateSystemKind)2, 3529); + return true; + case 9854: + cacheIndex = 4919; + reference = new EpsgCoordinateReferenceRecord(9854, (EpsgCoordinateSystemKind)2, 3530); + return true; + case 9855: + cacheIndex = 4920; + reference = new EpsgCoordinateReferenceRecord(9855, (EpsgCoordinateSystemKind)2, 3531); + return true; + case 9856: + cacheIndex = 4921; + reference = new EpsgCoordinateReferenceRecord(9856, (EpsgCoordinateSystemKind)2, 3532); + return true; + case 9857: + cacheIndex = 4922; + reference = new EpsgCoordinateReferenceRecord(9857, (EpsgCoordinateSystemKind)2, 3533); + return true; + case 9858: + cacheIndex = 4923; + reference = new EpsgCoordinateReferenceRecord(9858, (EpsgCoordinateSystemKind)2, 3534); + return true; + case 9859: + cacheIndex = 4924; + reference = new EpsgCoordinateReferenceRecord(9859, (EpsgCoordinateSystemKind)2, 3535); + return true; + case 9860: + cacheIndex = 4925; + reference = new EpsgCoordinateReferenceRecord(9860, (EpsgCoordinateSystemKind)2, 3536); + return true; + case 9861: + cacheIndex = 4926; + reference = new EpsgCoordinateReferenceRecord(9861, (EpsgCoordinateSystemKind)2, 3537); + return true; + case 9862: + cacheIndex = 4927; + reference = new EpsgCoordinateReferenceRecord(9862, (EpsgCoordinateSystemKind)2, 3538); + return true; + case 9863: + cacheIndex = 4928; + reference = new EpsgCoordinateReferenceRecord(9863, (EpsgCoordinateSystemKind)2, 3539); + return true; + case 9864: + cacheIndex = 4929; + reference = new EpsgCoordinateReferenceRecord(9864, (EpsgCoordinateSystemKind)2, 3540); + return true; + case 9865: + cacheIndex = 4930; + reference = new EpsgCoordinateReferenceRecord(9865, (EpsgCoordinateSystemKind)2, 3541); + return true; + case 9866: + cacheIndex = 4931; + reference = new EpsgCoordinateReferenceRecord(9866, (EpsgCoordinateSystemKind)0, 735); + return true; + case 9869: + cacheIndex = 4932; + reference = new EpsgCoordinateReferenceRecord(9869, (EpsgCoordinateSystemKind)2, 3542); + return true; + case 9870: + cacheIndex = 4933; + reference = new EpsgCoordinateReferenceRecord(9870, (EpsgCoordinateSystemKind)4, 236); + return true; + case 9871: + cacheIndex = 4934; + reference = new EpsgCoordinateReferenceRecord(9871, (EpsgCoordinateSystemKind)0, 736); + return true; + case 9874: + cacheIndex = 4935; + reference = new EpsgCoordinateReferenceRecord(9874, (EpsgCoordinateSystemKind)2, 3543); + return true; + case 9875: + cacheIndex = 4936; + reference = new EpsgCoordinateReferenceRecord(9875, (EpsgCoordinateSystemKind)2, 3544); + return true; + case 9880: + cacheIndex = 4937; + reference = new EpsgCoordinateReferenceRecord(9880, (EpsgCoordinateSystemKind)2, 3545); + return true; + case 9881: + cacheIndex = 4938; + reference = new EpsgCoordinateReferenceRecord(9881, (EpsgCoordinateSystemKind)4, 237); + return true; + case 9883: + cacheIndex = 4939; + reference = new EpsgCoordinateReferenceRecord(9883, (EpsgCoordinateSystemKind)4, 238); + return true; + case 9892: + cacheIndex = 4940; + reference = new EpsgCoordinateReferenceRecord(9892, (EpsgCoordinateSystemKind)1, 190); + return true; + case 9893: + cacheIndex = 4941; + reference = new EpsgCoordinateReferenceRecord(9893, (EpsgCoordinateSystemKind)0, 737); + return true; + case 9895: + cacheIndex = 4942; + reference = new EpsgCoordinateReferenceRecord(9895, (EpsgCoordinateSystemKind)2, 3546); + return true; + case 9897: + cacheIndex = 4943; + reference = new EpsgCoordinateReferenceRecord(9897, (EpsgCoordinateSystemKind)4, 239); + return true; + case 9907: + cacheIndex = 4944; + reference = new EpsgCoordinateReferenceRecord(9907, (EpsgCoordinateSystemKind)4, 240); + return true; + case 9923: + cacheIndex = 4945; + reference = new EpsgCoordinateReferenceRecord(9923, (EpsgCoordinateSystemKind)3, 228); + return true; + case 9924: + cacheIndex = 4946; + reference = new EpsgCoordinateReferenceRecord(9924, (EpsgCoordinateSystemKind)4, 241); + return true; + case 9927: + cacheIndex = 4947; + reference = new EpsgCoordinateReferenceRecord(9927, (EpsgCoordinateSystemKind)3, 229); + return true; + case 9928: + cacheIndex = 4948; + reference = new EpsgCoordinateReferenceRecord(9928, (EpsgCoordinateSystemKind)4, 242); + return true; + case 9929: + cacheIndex = 4949; + reference = new EpsgCoordinateReferenceRecord(9929, (EpsgCoordinateSystemKind)4, 243); + return true; + case 9930: + cacheIndex = 4950; + reference = new EpsgCoordinateReferenceRecord(9930, (EpsgCoordinateSystemKind)4, 244); + return true; + case 9931: + cacheIndex = 4951; + reference = new EpsgCoordinateReferenceRecord(9931, (EpsgCoordinateSystemKind)4, 245); + return true; + case 9932: + cacheIndex = 4952; + reference = new EpsgCoordinateReferenceRecord(9932, (EpsgCoordinateSystemKind)4, 246); + return true; + case 9933: + cacheIndex = 4953; + reference = new EpsgCoordinateReferenceRecord(9933, (EpsgCoordinateSystemKind)4, 247); + return true; + case 9934: + cacheIndex = 4954; + reference = new EpsgCoordinateReferenceRecord(9934, (EpsgCoordinateSystemKind)4, 248); + return true; + case 9935: + cacheIndex = 4955; + reference = new EpsgCoordinateReferenceRecord(9935, (EpsgCoordinateSystemKind)4, 249); + return true; + case 9939: + cacheIndex = 4956; + reference = new EpsgCoordinateReferenceRecord(9939, (EpsgCoordinateSystemKind)0, 738); + return true; + case 9943: + cacheIndex = 4957; + reference = new EpsgCoordinateReferenceRecord(9943, (EpsgCoordinateSystemKind)2, 3547); + return true; + case 9944: + cacheIndex = 4958; + reference = new EpsgCoordinateReferenceRecord(9944, (EpsgCoordinateSystemKind)4, 250); + return true; + case 9945: + cacheIndex = 4959; + reference = new EpsgCoordinateReferenceRecord(9945, (EpsgCoordinateSystemKind)2, 3548); + return true; + case 9947: + cacheIndex = 4960; + reference = new EpsgCoordinateReferenceRecord(9947, (EpsgCoordinateSystemKind)2, 3549); + return true; + case 9948: + cacheIndex = 4961; + reference = new EpsgCoordinateReferenceRecord(9948, (EpsgCoordinateSystemKind)4, 251); + return true; + case 9949: + cacheIndex = 4962; + reference = new EpsgCoordinateReferenceRecord(9949, (EpsgCoordinateSystemKind)4, 252); + return true; + case 9950: + cacheIndex = 4963; + reference = new EpsgCoordinateReferenceRecord(9950, (EpsgCoordinateSystemKind)4, 253); + return true; + case 9951: + cacheIndex = 4964; + reference = new EpsgCoordinateReferenceRecord(9951, (EpsgCoordinateSystemKind)4, 254); + return true; + case 9952: + cacheIndex = 4965; + reference = new EpsgCoordinateReferenceRecord(9952, (EpsgCoordinateSystemKind)4, 255); + return true; + case 9953: + cacheIndex = 4966; + reference = new EpsgCoordinateReferenceRecord(9953, (EpsgCoordinateSystemKind)4, 256); + return true; + case 9964: + cacheIndex = 4967; + reference = new EpsgCoordinateReferenceRecord(9964, (EpsgCoordinateSystemKind)0, 739); + return true; + case 9967: + cacheIndex = 4968; + reference = new EpsgCoordinateReferenceRecord(9967, (EpsgCoordinateSystemKind)2, 3550); + return true; + case 9968: + cacheIndex = 4969; + reference = new EpsgCoordinateReferenceRecord(9968, (EpsgCoordinateSystemKind)4, 257); + return true; + case 9969: + cacheIndex = 4970; + reference = new EpsgCoordinateReferenceRecord(9969, (EpsgCoordinateSystemKind)0, 740); + return true; + case 9972: + cacheIndex = 4971; + reference = new EpsgCoordinateReferenceRecord(9972, (EpsgCoordinateSystemKind)2, 3551); + return true; + case 9973: + cacheIndex = 4972; + reference = new EpsgCoordinateReferenceRecord(9973, (EpsgCoordinateSystemKind)4, 258); + return true; + case 9974: + cacheIndex = 4973; + reference = new EpsgCoordinateReferenceRecord(9974, (EpsgCoordinateSystemKind)0, 741); + return true; + case 9977: + cacheIndex = 4974; + reference = new EpsgCoordinateReferenceRecord(9977, (EpsgCoordinateSystemKind)2, 3552); + return true; + case 9978: + cacheIndex = 4975; + reference = new EpsgCoordinateReferenceRecord(9978, (EpsgCoordinateSystemKind)4, 259); + return true; + case 9988: + cacheIndex = 4976; + reference = new EpsgCoordinateReferenceRecord(9988, (EpsgCoordinateSystemKind)1, 191); + return true; + case 9989: + cacheIndex = 4977; + reference = new EpsgCoordinateReferenceRecord(9989, (EpsgCoordinateSystemKind)0, 742); + return true; + case 9990: + cacheIndex = 4978; + reference = new EpsgCoordinateReferenceRecord(9990, (EpsgCoordinateSystemKind)0, 743); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket10(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 10150: + cacheIndex = 4979; + reference = new EpsgCoordinateReferenceRecord(10150, (EpsgCoordinateSystemKind)3, 230); + return true; + case 10151: + cacheIndex = 4980; + reference = new EpsgCoordinateReferenceRecord(10151, (EpsgCoordinateSystemKind)3, 231); + return true; + case 10156: + cacheIndex = 4981; + reference = new EpsgCoordinateReferenceRecord(10156, (EpsgCoordinateSystemKind)4, 260); + return true; + case 10157: + cacheIndex = 4982; + reference = new EpsgCoordinateReferenceRecord(10157, (EpsgCoordinateSystemKind)4, 261); + return true; + case 10158: + cacheIndex = 4983; + reference = new EpsgCoordinateReferenceRecord(10158, (EpsgCoordinateSystemKind)0, 744); + return true; + case 10160: + cacheIndex = 4984; + reference = new EpsgCoordinateReferenceRecord(10160, (EpsgCoordinateSystemKind)2, 3553); + return true; + case 10162: + cacheIndex = 4985; + reference = new EpsgCoordinateReferenceRecord(10162, (EpsgCoordinateSystemKind)4, 262); + return true; + case 10163: + cacheIndex = 4986; + reference = new EpsgCoordinateReferenceRecord(10163, (EpsgCoordinateSystemKind)4, 263); + return true; + case 10164: + cacheIndex = 4987; + reference = new EpsgCoordinateReferenceRecord(10164, (EpsgCoordinateSystemKind)4, 264); + return true; + case 10165: + cacheIndex = 4988; + reference = new EpsgCoordinateReferenceRecord(10165, (EpsgCoordinateSystemKind)4, 265); + return true; + case 10166: + cacheIndex = 4989; + reference = new EpsgCoordinateReferenceRecord(10166, (EpsgCoordinateSystemKind)4, 266); + return true; + case 10167: + cacheIndex = 4990; + reference = new EpsgCoordinateReferenceRecord(10167, (EpsgCoordinateSystemKind)4, 267); + return true; + case 10168: + cacheIndex = 4991; + reference = new EpsgCoordinateReferenceRecord(10168, (EpsgCoordinateSystemKind)4, 268); + return true; + case 10169: + cacheIndex = 4992; + reference = new EpsgCoordinateReferenceRecord(10169, (EpsgCoordinateSystemKind)4, 269); + return true; + case 10170: + cacheIndex = 4993; + reference = new EpsgCoordinateReferenceRecord(10170, (EpsgCoordinateSystemKind)4, 270); + return true; + case 10171: + cacheIndex = 4994; + reference = new EpsgCoordinateReferenceRecord(10171, (EpsgCoordinateSystemKind)4, 271); + return true; + case 10172: + cacheIndex = 4995; + reference = new EpsgCoordinateReferenceRecord(10172, (EpsgCoordinateSystemKind)4, 272); + return true; + case 10173: + cacheIndex = 4996; + reference = new EpsgCoordinateReferenceRecord(10173, (EpsgCoordinateSystemKind)4, 273); + return true; + case 10174: + cacheIndex = 4997; + reference = new EpsgCoordinateReferenceRecord(10174, (EpsgCoordinateSystemKind)4, 274); + return true; + case 10175: + cacheIndex = 4998; + reference = new EpsgCoordinateReferenceRecord(10175, (EpsgCoordinateSystemKind)0, 745); + return true; + case 10176: + cacheIndex = 4999; + reference = new EpsgCoordinateReferenceRecord(10176, (EpsgCoordinateSystemKind)1, 192); + return true; + case 10177: + cacheIndex = 5000; + reference = new EpsgCoordinateReferenceRecord(10177, (EpsgCoordinateSystemKind)0, 746); + return true; + case 10178: + cacheIndex = 5001; + reference = new EpsgCoordinateReferenceRecord(10178, (EpsgCoordinateSystemKind)0, 747); + return true; + case 10183: + cacheIndex = 5002; + reference = new EpsgCoordinateReferenceRecord(10183, (EpsgCoordinateSystemKind)2, 3554); + return true; + case 10184: + cacheIndex = 5003; + reference = new EpsgCoordinateReferenceRecord(10184, (EpsgCoordinateSystemKind)4, 275); + return true; + case 10185: + cacheIndex = 5004; + reference = new EpsgCoordinateReferenceRecord(10185, (EpsgCoordinateSystemKind)0, 748); + return true; + case 10188: + cacheIndex = 5005; + reference = new EpsgCoordinateReferenceRecord(10188, (EpsgCoordinateSystemKind)2, 3555); + return true; + case 10189: + cacheIndex = 5006; + reference = new EpsgCoordinateReferenceRecord(10189, (EpsgCoordinateSystemKind)4, 276); + return true; + case 10190: + cacheIndex = 5007; + reference = new EpsgCoordinateReferenceRecord(10190, (EpsgCoordinateSystemKind)3, 232); + return true; + case 10191: + cacheIndex = 5008; + reference = new EpsgCoordinateReferenceRecord(10191, (EpsgCoordinateSystemKind)0, 749); + return true; + case 10194: + cacheIndex = 5009; + reference = new EpsgCoordinateReferenceRecord(10194, (EpsgCoordinateSystemKind)2, 3556); + return true; + case 10195: + cacheIndex = 5010; + reference = new EpsgCoordinateReferenceRecord(10195, (EpsgCoordinateSystemKind)4, 277); + return true; + case 10196: + cacheIndex = 5011; + reference = new EpsgCoordinateReferenceRecord(10196, (EpsgCoordinateSystemKind)0, 750); + return true; + case 10199: + cacheIndex = 5012; + reference = new EpsgCoordinateReferenceRecord(10199, (EpsgCoordinateSystemKind)2, 3557); + return true; + case 10200: + cacheIndex = 5013; + reference = new EpsgCoordinateReferenceRecord(10200, (EpsgCoordinateSystemKind)4, 278); + return true; + case 10204: + cacheIndex = 5014; + reference = new EpsgCoordinateReferenceRecord(10204, (EpsgCoordinateSystemKind)0, 751); + return true; + case 10207: + cacheIndex = 5015; + reference = new EpsgCoordinateReferenceRecord(10207, (EpsgCoordinateSystemKind)2, 3558); + return true; + case 10208: + cacheIndex = 5016; + reference = new EpsgCoordinateReferenceRecord(10208, (EpsgCoordinateSystemKind)4, 279); + return true; + case 10209: + cacheIndex = 5017; + reference = new EpsgCoordinateReferenceRecord(10209, (EpsgCoordinateSystemKind)0, 752); + return true; + case 10212: + cacheIndex = 5018; + reference = new EpsgCoordinateReferenceRecord(10212, (EpsgCoordinateSystemKind)2, 3559); + return true; + case 10213: + cacheIndex = 5019; + reference = new EpsgCoordinateReferenceRecord(10213, (EpsgCoordinateSystemKind)4, 280); + return true; + case 10214: + cacheIndex = 5020; + reference = new EpsgCoordinateReferenceRecord(10214, (EpsgCoordinateSystemKind)0, 753); + return true; + case 10217: + cacheIndex = 5021; + reference = new EpsgCoordinateReferenceRecord(10217, (EpsgCoordinateSystemKind)2, 3560); + return true; + case 10218: + cacheIndex = 5022; + reference = new EpsgCoordinateReferenceRecord(10218, (EpsgCoordinateSystemKind)4, 281); + return true; + case 10219: + cacheIndex = 5023; + reference = new EpsgCoordinateReferenceRecord(10219, (EpsgCoordinateSystemKind)0, 754); + return true; + case 10222: + cacheIndex = 5024; + reference = new EpsgCoordinateReferenceRecord(10222, (EpsgCoordinateSystemKind)2, 3561); + return true; + case 10223: + cacheIndex = 5025; + reference = new EpsgCoordinateReferenceRecord(10223, (EpsgCoordinateSystemKind)4, 282); + return true; + case 10224: + cacheIndex = 5026; + reference = new EpsgCoordinateReferenceRecord(10224, (EpsgCoordinateSystemKind)0, 755); + return true; + case 10227: + cacheIndex = 5027; + reference = new EpsgCoordinateReferenceRecord(10227, (EpsgCoordinateSystemKind)2, 3562); + return true; + case 10228: + cacheIndex = 5028; + reference = new EpsgCoordinateReferenceRecord(10228, (EpsgCoordinateSystemKind)4, 283); + return true; + case 10229: + cacheIndex = 5029; + reference = new EpsgCoordinateReferenceRecord(10229, (EpsgCoordinateSystemKind)0, 756); + return true; + case 10235: + cacheIndex = 5030; + reference = new EpsgCoordinateReferenceRecord(10235, (EpsgCoordinateSystemKind)2, 3563); + return true; + case 10236: + cacheIndex = 5031; + reference = new EpsgCoordinateReferenceRecord(10236, (EpsgCoordinateSystemKind)4, 284); + return true; + case 10237: + cacheIndex = 5032; + reference = new EpsgCoordinateReferenceRecord(10237, (EpsgCoordinateSystemKind)0, 757); + return true; + case 10240: + cacheIndex = 5033; + reference = new EpsgCoordinateReferenceRecord(10240, (EpsgCoordinateSystemKind)2, 3564); + return true; + case 10241: + cacheIndex = 5034; + reference = new EpsgCoordinateReferenceRecord(10241, (EpsgCoordinateSystemKind)4, 285); + return true; + case 10245: + cacheIndex = 5035; + reference = new EpsgCoordinateReferenceRecord(10245, (EpsgCoordinateSystemKind)4, 286); + return true; + case 10246: + cacheIndex = 5036; + reference = new EpsgCoordinateReferenceRecord(10246, (EpsgCoordinateSystemKind)4, 287); + return true; + case 10249: + cacheIndex = 5037; + reference = new EpsgCoordinateReferenceRecord(10249, (EpsgCoordinateSystemKind)0, 758); + return true; + case 10250: + cacheIndex = 5038; + reference = new EpsgCoordinateReferenceRecord(10250, (EpsgCoordinateSystemKind)2, 3565); + return true; + case 10252: + cacheIndex = 5039; + reference = new EpsgCoordinateReferenceRecord(10252, (EpsgCoordinateSystemKind)0, 759); + return true; + case 10254: + cacheIndex = 5040; + reference = new EpsgCoordinateReferenceRecord(10254, (EpsgCoordinateSystemKind)2, 3566); + return true; + case 10256: + cacheIndex = 5041; + reference = new EpsgCoordinateReferenceRecord(10256, (EpsgCoordinateSystemKind)0, 760); + return true; + case 10258: + cacheIndex = 5042; + reference = new EpsgCoordinateReferenceRecord(10258, (EpsgCoordinateSystemKind)2, 3567); + return true; + case 10260: + cacheIndex = 5043; + reference = new EpsgCoordinateReferenceRecord(10260, (EpsgCoordinateSystemKind)0, 761); + return true; + case 10262: + cacheIndex = 5044; + reference = new EpsgCoordinateReferenceRecord(10262, (EpsgCoordinateSystemKind)2, 3568); + return true; + case 10265: + cacheIndex = 5045; + reference = new EpsgCoordinateReferenceRecord(10265, (EpsgCoordinateSystemKind)0, 762); + return true; + case 10266: + cacheIndex = 5046; + reference = new EpsgCoordinateReferenceRecord(10266, (EpsgCoordinateSystemKind)2, 3569); + return true; + case 10268: + cacheIndex = 5047; + reference = new EpsgCoordinateReferenceRecord(10268, (EpsgCoordinateSystemKind)0, 763); + return true; + case 10270: + cacheIndex = 5048; + reference = new EpsgCoordinateReferenceRecord(10270, (EpsgCoordinateSystemKind)2, 3570); + return true; + case 10272: + cacheIndex = 5049; + reference = new EpsgCoordinateReferenceRecord(10272, (EpsgCoordinateSystemKind)0, 764); + return true; + case 10275: + cacheIndex = 5050; + reference = new EpsgCoordinateReferenceRecord(10275, (EpsgCoordinateSystemKind)2, 3571); + return true; + case 10276: + cacheIndex = 5051; + reference = new EpsgCoordinateReferenceRecord(10276, (EpsgCoordinateSystemKind)4, 288); + return true; + case 10277: + cacheIndex = 5052; + reference = new EpsgCoordinateReferenceRecord(10277, (EpsgCoordinateSystemKind)0, 765); + return true; + case 10280: + cacheIndex = 5053; + reference = new EpsgCoordinateReferenceRecord(10280, (EpsgCoordinateSystemKind)2, 3572); + return true; + case 10281: + cacheIndex = 5054; + reference = new EpsgCoordinateReferenceRecord(10281, (EpsgCoordinateSystemKind)4, 289); + return true; + case 10282: + cacheIndex = 5055; + reference = new EpsgCoordinateReferenceRecord(10282, (EpsgCoordinateSystemKind)1, 193); + return true; + case 10283: + cacheIndex = 5056; + reference = new EpsgCoordinateReferenceRecord(10283, (EpsgCoordinateSystemKind)0, 766); + return true; + case 10284: + cacheIndex = 5057; + reference = new EpsgCoordinateReferenceRecord(10284, (EpsgCoordinateSystemKind)0, 767); + return true; + case 10285: + cacheIndex = 5058; + reference = new EpsgCoordinateReferenceRecord(10285, (EpsgCoordinateSystemKind)2, 3573); + return true; + case 10286: + cacheIndex = 5059; + reference = new EpsgCoordinateReferenceRecord(10286, (EpsgCoordinateSystemKind)2, 3574); + return true; + case 10287: + cacheIndex = 5060; + reference = new EpsgCoordinateReferenceRecord(10287, (EpsgCoordinateSystemKind)2, 3575); + return true; + case 10288: + cacheIndex = 5061; + reference = new EpsgCoordinateReferenceRecord(10288, (EpsgCoordinateSystemKind)2, 3576); + return true; + case 10289: + cacheIndex = 5062; + reference = new EpsgCoordinateReferenceRecord(10289, (EpsgCoordinateSystemKind)2, 3577); + return true; + case 10290: + cacheIndex = 5063; + reference = new EpsgCoordinateReferenceRecord(10290, (EpsgCoordinateSystemKind)2, 3578); + return true; + case 10291: + cacheIndex = 5064; + reference = new EpsgCoordinateReferenceRecord(10291, (EpsgCoordinateSystemKind)2, 3579); + return true; + case 10293: + cacheIndex = 5065; + reference = new EpsgCoordinateReferenceRecord(10293, (EpsgCoordinateSystemKind)4, 290); + return true; + case 10297: + cacheIndex = 5066; + reference = new EpsgCoordinateReferenceRecord(10297, (EpsgCoordinateSystemKind)1, 194); + return true; + case 10298: + cacheIndex = 5067; + reference = new EpsgCoordinateReferenceRecord(10298, (EpsgCoordinateSystemKind)0, 768); + return true; + case 10299: + cacheIndex = 5068; + reference = new EpsgCoordinateReferenceRecord(10299, (EpsgCoordinateSystemKind)0, 769); + return true; + case 10300: + cacheIndex = 5069; + reference = new EpsgCoordinateReferenceRecord(10300, (EpsgCoordinateSystemKind)0, 770); + return true; + case 10303: + cacheIndex = 5070; + reference = new EpsgCoordinateReferenceRecord(10303, (EpsgCoordinateSystemKind)1, 195); + return true; + case 10304: + cacheIndex = 5071; + reference = new EpsgCoordinateReferenceRecord(10304, (EpsgCoordinateSystemKind)0, 771); + return true; + case 10305: + cacheIndex = 5072; + reference = new EpsgCoordinateReferenceRecord(10305, (EpsgCoordinateSystemKind)0, 772); + return true; + case 10306: + cacheIndex = 5073; + reference = new EpsgCoordinateReferenceRecord(10306, (EpsgCoordinateSystemKind)2, 3580); + return true; + case 10307: + cacheIndex = 5074; + reference = new EpsgCoordinateReferenceRecord(10307, (EpsgCoordinateSystemKind)0, 773); + return true; + case 10308: + cacheIndex = 5075; + reference = new EpsgCoordinateReferenceRecord(10308, (EpsgCoordinateSystemKind)1, 196); + return true; + case 10309: + cacheIndex = 5076; + reference = new EpsgCoordinateReferenceRecord(10309, (EpsgCoordinateSystemKind)0, 774); + return true; + case 10310: + cacheIndex = 5077; + reference = new EpsgCoordinateReferenceRecord(10310, (EpsgCoordinateSystemKind)0, 775); + return true; + case 10311: + cacheIndex = 5078; + reference = new EpsgCoordinateReferenceRecord(10311, (EpsgCoordinateSystemKind)0, 776); + return true; + case 10312: + cacheIndex = 5079; + reference = new EpsgCoordinateReferenceRecord(10312, (EpsgCoordinateSystemKind)0, 777); + return true; + case 10314: + cacheIndex = 5080; + reference = new EpsgCoordinateReferenceRecord(10314, (EpsgCoordinateSystemKind)2, 3581); + return true; + case 10315: + cacheIndex = 5081; + reference = new EpsgCoordinateReferenceRecord(10315, (EpsgCoordinateSystemKind)2, 3582); + return true; + case 10316: + cacheIndex = 5082; + reference = new EpsgCoordinateReferenceRecord(10316, (EpsgCoordinateSystemKind)2, 3583); + return true; + case 10317: + cacheIndex = 5083; + reference = new EpsgCoordinateReferenceRecord(10317, (EpsgCoordinateSystemKind)2, 3584); + return true; + case 10318: + cacheIndex = 5084; + reference = new EpsgCoordinateReferenceRecord(10318, (EpsgCoordinateSystemKind)4, 291); + return true; + case 10326: + cacheIndex = 5085; + reference = new EpsgCoordinateReferenceRecord(10326, (EpsgCoordinateSystemKind)1, 197); + return true; + case 10327: + cacheIndex = 5086; + reference = new EpsgCoordinateReferenceRecord(10327, (EpsgCoordinateSystemKind)0, 778); + return true; + case 10328: + cacheIndex = 5087; + reference = new EpsgCoordinateReferenceRecord(10328, (EpsgCoordinateSystemKind)0, 779); + return true; + case 10329: + cacheIndex = 5088; + reference = new EpsgCoordinateReferenceRecord(10329, (EpsgCoordinateSystemKind)2, 3585); + return true; + case 10345: + cacheIndex = 5089; + reference = new EpsgCoordinateReferenceRecord(10345, (EpsgCoordinateSystemKind)0, 780); + return true; + case 10346: + cacheIndex = 5090; + reference = new EpsgCoordinateReferenceRecord(10346, (EpsgCoordinateSystemKind)0, 781); + return true; + case 10349: + cacheIndex = 5091; + reference = new EpsgCoordinateReferenceRecord(10349, (EpsgCoordinateSystemKind)3, 233); + return true; + case 10352: + cacheIndex = 5092; + reference = new EpsgCoordinateReferenceRecord(10352, (EpsgCoordinateSystemKind)3, 234); + return true; + case 10353: + cacheIndex = 5093; + reference = new EpsgCoordinateReferenceRecord(10353, (EpsgCoordinateSystemKind)3, 235); + return true; + case 10354: + cacheIndex = 5094; + reference = new EpsgCoordinateReferenceRecord(10354, (EpsgCoordinateSystemKind)3, 236); + return true; + case 10355: + cacheIndex = 5095; + reference = new EpsgCoordinateReferenceRecord(10355, (EpsgCoordinateSystemKind)4, 292); + return true; + case 10356: + cacheIndex = 5096; + reference = new EpsgCoordinateReferenceRecord(10356, (EpsgCoordinateSystemKind)4, 293); + return true; + case 10357: + cacheIndex = 5097; + reference = new EpsgCoordinateReferenceRecord(10357, (EpsgCoordinateSystemKind)4, 294); + return true; + case 10365: + cacheIndex = 5098; + reference = new EpsgCoordinateReferenceRecord(10365, (EpsgCoordinateSystemKind)4, 295); + return true; + case 10412: + cacheIndex = 5099; + reference = new EpsgCoordinateReferenceRecord(10412, (EpsgCoordinateSystemKind)1, 198); + return true; + case 10413: + cacheIndex = 5100; + reference = new EpsgCoordinateReferenceRecord(10413, (EpsgCoordinateSystemKind)0, 782); + return true; + case 10414: + cacheIndex = 5101; + reference = new EpsgCoordinateReferenceRecord(10414, (EpsgCoordinateSystemKind)0, 783); + return true; + case 10448: + cacheIndex = 5102; + reference = new EpsgCoordinateReferenceRecord(10448, (EpsgCoordinateSystemKind)2, 3586); + return true; + case 10449: + cacheIndex = 5103; + reference = new EpsgCoordinateReferenceRecord(10449, (EpsgCoordinateSystemKind)2, 3587); + return true; + case 10450: + cacheIndex = 5104; + reference = new EpsgCoordinateReferenceRecord(10450, (EpsgCoordinateSystemKind)2, 3588); + return true; + case 10451: + cacheIndex = 5105; + reference = new EpsgCoordinateReferenceRecord(10451, (EpsgCoordinateSystemKind)2, 3589); + return true; + case 10452: + cacheIndex = 5106; + reference = new EpsgCoordinateReferenceRecord(10452, (EpsgCoordinateSystemKind)2, 3590); + return true; + case 10453: + cacheIndex = 5107; + reference = new EpsgCoordinateReferenceRecord(10453, (EpsgCoordinateSystemKind)2, 3591); + return true; + case 10454: + cacheIndex = 5108; + reference = new EpsgCoordinateReferenceRecord(10454, (EpsgCoordinateSystemKind)2, 3592); + return true; + case 10455: + cacheIndex = 5109; + reference = new EpsgCoordinateReferenceRecord(10455, (EpsgCoordinateSystemKind)2, 3593); + return true; + case 10456: + cacheIndex = 5110; + reference = new EpsgCoordinateReferenceRecord(10456, (EpsgCoordinateSystemKind)2, 3594); + return true; + case 10457: + cacheIndex = 5111; + reference = new EpsgCoordinateReferenceRecord(10457, (EpsgCoordinateSystemKind)2, 3595); + return true; + case 10458: + cacheIndex = 5112; + reference = new EpsgCoordinateReferenceRecord(10458, (EpsgCoordinateSystemKind)2, 3596); + return true; + case 10459: + cacheIndex = 5113; + reference = new EpsgCoordinateReferenceRecord(10459, (EpsgCoordinateSystemKind)2, 3597); + return true; + case 10460: + cacheIndex = 5114; + reference = new EpsgCoordinateReferenceRecord(10460, (EpsgCoordinateSystemKind)2, 3598); + return true; + case 10461: + cacheIndex = 5115; + reference = new EpsgCoordinateReferenceRecord(10461, (EpsgCoordinateSystemKind)2, 3599); + return true; + case 10462: + cacheIndex = 5116; + reference = new EpsgCoordinateReferenceRecord(10462, (EpsgCoordinateSystemKind)2, 3600); + return true; + case 10463: + cacheIndex = 5117; + reference = new EpsgCoordinateReferenceRecord(10463, (EpsgCoordinateSystemKind)2, 3601); + return true; + case 10464: + cacheIndex = 5118; + reference = new EpsgCoordinateReferenceRecord(10464, (EpsgCoordinateSystemKind)2, 3602); + return true; + case 10465: + cacheIndex = 5119; + reference = new EpsgCoordinateReferenceRecord(10465, (EpsgCoordinateSystemKind)2, 3603); + return true; + case 10468: + cacheIndex = 5120; + reference = new EpsgCoordinateReferenceRecord(10468, (EpsgCoordinateSystemKind)0, 784); + return true; + case 10471: + cacheIndex = 5121; + reference = new EpsgCoordinateReferenceRecord(10471, (EpsgCoordinateSystemKind)2, 3604); + return true; + case 10472: + cacheIndex = 5122; + reference = new EpsgCoordinateReferenceRecord(10472, (EpsgCoordinateSystemKind)4, 296); + return true; + case 10473: + cacheIndex = 5123; + reference = new EpsgCoordinateReferenceRecord(10473, (EpsgCoordinateSystemKind)1, 199); + return true; + case 10474: + cacheIndex = 5124; + reference = new EpsgCoordinateReferenceRecord(10474, (EpsgCoordinateSystemKind)0, 785); + return true; + case 10475: + cacheIndex = 5125; + reference = new EpsgCoordinateReferenceRecord(10475, (EpsgCoordinateSystemKind)0, 786); + return true; + case 10477: + cacheIndex = 5126; + reference = new EpsgCoordinateReferenceRecord(10477, (EpsgCoordinateSystemKind)2, 3605); + return true; + case 10481: + cacheIndex = 5127; + reference = new EpsgCoordinateReferenceRecord(10481, (EpsgCoordinateSystemKind)2, 3606); + return true; + case 10482: + cacheIndex = 5128; + reference = new EpsgCoordinateReferenceRecord(10482, (EpsgCoordinateSystemKind)3, 237); + return true; + case 10483: + cacheIndex = 5129; + reference = new EpsgCoordinateReferenceRecord(10483, (EpsgCoordinateSystemKind)3, 238); + return true; + case 10484: + cacheIndex = 5130; + reference = new EpsgCoordinateReferenceRecord(10484, (EpsgCoordinateSystemKind)3, 239); + return true; + case 10485: + cacheIndex = 5131; + reference = new EpsgCoordinateReferenceRecord(10485, (EpsgCoordinateSystemKind)3, 240); + return true; + case 10486: + cacheIndex = 5132; + reference = new EpsgCoordinateReferenceRecord(10486, (EpsgCoordinateSystemKind)4, 297); + return true; + case 10487: + cacheIndex = 5133; + reference = new EpsgCoordinateReferenceRecord(10487, (EpsgCoordinateSystemKind)4, 298); + return true; + case 10488: + cacheIndex = 5134; + reference = new EpsgCoordinateReferenceRecord(10488, (EpsgCoordinateSystemKind)4, 299); + return true; + case 10497: + cacheIndex = 5135; + reference = new EpsgCoordinateReferenceRecord(10497, (EpsgCoordinateSystemKind)4, 300); + return true; + case 10498: + cacheIndex = 5136; + reference = new EpsgCoordinateReferenceRecord(10498, (EpsgCoordinateSystemKind)4, 301); + return true; + case 10499: + cacheIndex = 5137; + reference = new EpsgCoordinateReferenceRecord(10499, (EpsgCoordinateSystemKind)4, 302); + return true; + case 10500: + cacheIndex = 5138; + reference = new EpsgCoordinateReferenceRecord(10500, (EpsgCoordinateSystemKind)4, 303); + return true; + case 10507: + cacheIndex = 5139; + reference = new EpsgCoordinateReferenceRecord(10507, (EpsgCoordinateSystemKind)4, 304); + return true; + case 10516: + cacheIndex = 5140; + reference = new EpsgCoordinateReferenceRecord(10516, (EpsgCoordinateSystemKind)2, 3607); + return true; + case 10545: + cacheIndex = 5141; + reference = new EpsgCoordinateReferenceRecord(10545, (EpsgCoordinateSystemKind)4, 305); + return true; + case 10547: + cacheIndex = 5142; + reference = new EpsgCoordinateReferenceRecord(10547, (EpsgCoordinateSystemKind)3, 241); + return true; + case 10548: + cacheIndex = 5143; + reference = new EpsgCoordinateReferenceRecord(10548, (EpsgCoordinateSystemKind)3, 242); + return true; + case 10549: + cacheIndex = 5144; + reference = new EpsgCoordinateReferenceRecord(10549, (EpsgCoordinateSystemKind)3, 243); + return true; + case 10550: + cacheIndex = 5145; + reference = new EpsgCoordinateReferenceRecord(10550, (EpsgCoordinateSystemKind)3, 244); + return true; + case 10553: + cacheIndex = 5146; + reference = new EpsgCoordinateReferenceRecord(10553, (EpsgCoordinateSystemKind)4, 306); + return true; + case 10554: + cacheIndex = 5147; + reference = new EpsgCoordinateReferenceRecord(10554, (EpsgCoordinateSystemKind)4, 307); + return true; + case 10555: + cacheIndex = 5148; + reference = new EpsgCoordinateReferenceRecord(10555, (EpsgCoordinateSystemKind)4, 308); + return true; + case 10556: + cacheIndex = 5149; + reference = new EpsgCoordinateReferenceRecord(10556, (EpsgCoordinateSystemKind)4, 309); + return true; + case 10565: + cacheIndex = 5150; + reference = new EpsgCoordinateReferenceRecord(10565, (EpsgCoordinateSystemKind)3, 245); + return true; + case 10569: + cacheIndex = 5151; + reference = new EpsgCoordinateReferenceRecord(10569, (EpsgCoordinateSystemKind)1, 200); + return true; + case 10570: + cacheIndex = 5152; + reference = new EpsgCoordinateReferenceRecord(10570, (EpsgCoordinateSystemKind)0, 787); + return true; + case 10571: + cacheIndex = 5153; + reference = new EpsgCoordinateReferenceRecord(10571, (EpsgCoordinateSystemKind)0, 788); + return true; + case 10588: + cacheIndex = 5154; + reference = new EpsgCoordinateReferenceRecord(10588, (EpsgCoordinateSystemKind)3, 246); + return true; + case 10592: + cacheIndex = 5155; + reference = new EpsgCoordinateReferenceRecord(10592, (EpsgCoordinateSystemKind)2, 3608); + return true; + case 10594: + cacheIndex = 5156; + reference = new EpsgCoordinateReferenceRecord(10594, (EpsgCoordinateSystemKind)2, 3609); + return true; + case 10596: + cacheIndex = 5157; + reference = new EpsgCoordinateReferenceRecord(10596, (EpsgCoordinateSystemKind)2, 3610); + return true; + case 10598: + cacheIndex = 5158; + reference = new EpsgCoordinateReferenceRecord(10598, (EpsgCoordinateSystemKind)2, 3611); + return true; + case 10601: + cacheIndex = 5159; + reference = new EpsgCoordinateReferenceRecord(10601, (EpsgCoordinateSystemKind)2, 3612); + return true; + case 10603: + cacheIndex = 5160; + reference = new EpsgCoordinateReferenceRecord(10603, (EpsgCoordinateSystemKind)2, 3613); + return true; + case 10604: + cacheIndex = 5161; + reference = new EpsgCoordinateReferenceRecord(10604, (EpsgCoordinateSystemKind)1, 201); + return true; + case 10605: + cacheIndex = 5162; + reference = new EpsgCoordinateReferenceRecord(10605, (EpsgCoordinateSystemKind)0, 789); + return true; + case 10606: + cacheIndex = 5163; + reference = new EpsgCoordinateReferenceRecord(10606, (EpsgCoordinateSystemKind)0, 790); + return true; + case 10622: + cacheIndex = 5164; + reference = new EpsgCoordinateReferenceRecord(10622, (EpsgCoordinateSystemKind)2, 3614); + return true; + case 10623: + cacheIndex = 5165; + reference = new EpsgCoordinateReferenceRecord(10623, (EpsgCoordinateSystemKind)0, 791); + return true; + case 10626: + cacheIndex = 5166; + reference = new EpsgCoordinateReferenceRecord(10626, (EpsgCoordinateSystemKind)2, 3615); + return true; + case 10627: + cacheIndex = 5167; + reference = new EpsgCoordinateReferenceRecord(10627, (EpsgCoordinateSystemKind)4, 310); + return true; + case 10628: + cacheIndex = 5168; + reference = new EpsgCoordinateReferenceRecord(10628, (EpsgCoordinateSystemKind)0, 792); + return true; + case 10632: + cacheIndex = 5169; + reference = new EpsgCoordinateReferenceRecord(10632, (EpsgCoordinateSystemKind)2, 3616); + return true; + case 10633: + cacheIndex = 5170; + reference = new EpsgCoordinateReferenceRecord(10633, (EpsgCoordinateSystemKind)4, 311); + return true; + case 10634: + cacheIndex = 5171; + reference = new EpsgCoordinateReferenceRecord(10634, (EpsgCoordinateSystemKind)1, 202); + return true; + case 10635: + cacheIndex = 5172; + reference = new EpsgCoordinateReferenceRecord(10635, (EpsgCoordinateSystemKind)0, 793); + return true; + case 10636: + cacheIndex = 5173; + reference = new EpsgCoordinateReferenceRecord(10636, (EpsgCoordinateSystemKind)0, 794); + return true; + case 10637: + cacheIndex = 5174; + reference = new EpsgCoordinateReferenceRecord(10637, (EpsgCoordinateSystemKind)1, 203); + return true; + case 10638: + cacheIndex = 5175; + reference = new EpsgCoordinateReferenceRecord(10638, (EpsgCoordinateSystemKind)0, 795); + return true; + case 10639: + cacheIndex = 5176; + reference = new EpsgCoordinateReferenceRecord(10639, (EpsgCoordinateSystemKind)0, 796); + return true; + case 10641: + cacheIndex = 5177; + reference = new EpsgCoordinateReferenceRecord(10641, (EpsgCoordinateSystemKind)2, 3617); + return true; + case 10642: + cacheIndex = 5178; + reference = new EpsgCoordinateReferenceRecord(10642, (EpsgCoordinateSystemKind)3, 247); + return true; + case 10643: + cacheIndex = 5179; + reference = new EpsgCoordinateReferenceRecord(10643, (EpsgCoordinateSystemKind)4, 312); + return true; + case 10644: + cacheIndex = 5180; + reference = new EpsgCoordinateReferenceRecord(10644, (EpsgCoordinateSystemKind)4, 313); + return true; + case 10645: + cacheIndex = 5181; + reference = new EpsgCoordinateReferenceRecord(10645, (EpsgCoordinateSystemKind)4, 314); + return true; + case 10649: + cacheIndex = 5182; + reference = new EpsgCoordinateReferenceRecord(10649, (EpsgCoordinateSystemKind)3, 248); + return true; + case 10650: + cacheIndex = 5183; + reference = new EpsgCoordinateReferenceRecord(10650, (EpsgCoordinateSystemKind)3, 249); + return true; + case 10651: + cacheIndex = 5184; + reference = new EpsgCoordinateReferenceRecord(10651, (EpsgCoordinateSystemKind)4, 315); + return true; + case 10652: + cacheIndex = 5185; + reference = new EpsgCoordinateReferenceRecord(10652, (EpsgCoordinateSystemKind)4, 316); + return true; + case 10659: + cacheIndex = 5186; + reference = new EpsgCoordinateReferenceRecord(10659, (EpsgCoordinateSystemKind)4, 317); + return true; + case 10660: + cacheIndex = 5187; + reference = new EpsgCoordinateReferenceRecord(10660, (EpsgCoordinateSystemKind)4, 318); + return true; + case 10665: + cacheIndex = 5188; + reference = new EpsgCoordinateReferenceRecord(10665, (EpsgCoordinateSystemKind)2, 3618); + return true; + case 10669: + cacheIndex = 5189; + reference = new EpsgCoordinateReferenceRecord(10669, (EpsgCoordinateSystemKind)1, 204); + return true; + case 10670: + cacheIndex = 5190; + reference = new EpsgCoordinateReferenceRecord(10670, (EpsgCoordinateSystemKind)0, 797); + return true; + case 10671: + cacheIndex = 5191; + reference = new EpsgCoordinateReferenceRecord(10671, (EpsgCoordinateSystemKind)0, 798); + return true; + case 10672: + cacheIndex = 5192; + reference = new EpsgCoordinateReferenceRecord(10672, (EpsgCoordinateSystemKind)0, 799); + return true; + case 10673: + cacheIndex = 5193; + reference = new EpsgCoordinateReferenceRecord(10673, (EpsgCoordinateSystemKind)0, 800); + return true; + case 10674: + cacheIndex = 5194; + reference = new EpsgCoordinateReferenceRecord(10674, (EpsgCoordinateSystemKind)2, 3619); + return true; + case 10678: + cacheIndex = 5195; + reference = new EpsgCoordinateReferenceRecord(10678, (EpsgCoordinateSystemKind)3, 250); + return true; + case 10679: + cacheIndex = 5196; + reference = new EpsgCoordinateReferenceRecord(10679, (EpsgCoordinateSystemKind)4, 319); + return true; + case 10686: + cacheIndex = 5197; + reference = new EpsgCoordinateReferenceRecord(10686, (EpsgCoordinateSystemKind)4, 320); + return true; + case 10687: + cacheIndex = 5198; + reference = new EpsgCoordinateReferenceRecord(10687, (EpsgCoordinateSystemKind)4, 321); + return true; + case 10688: + cacheIndex = 5199; + reference = new EpsgCoordinateReferenceRecord(10688, (EpsgCoordinateSystemKind)1, 205); + return true; + case 10689: + cacheIndex = 5200; + reference = new EpsgCoordinateReferenceRecord(10689, (EpsgCoordinateSystemKind)0, 801); + return true; + case 10690: + cacheIndex = 5201; + reference = new EpsgCoordinateReferenceRecord(10690, (EpsgCoordinateSystemKind)0, 802); + return true; + case 10691: + cacheIndex = 5202; + reference = new EpsgCoordinateReferenceRecord(10691, (EpsgCoordinateSystemKind)4, 322); + return true; + case 10692: + cacheIndex = 5203; + reference = new EpsgCoordinateReferenceRecord(10692, (EpsgCoordinateSystemKind)4, 323); + return true; + case 10699: + cacheIndex = 5204; + reference = new EpsgCoordinateReferenceRecord(10699, (EpsgCoordinateSystemKind)2, 3620); + return true; + case 10702: + cacheIndex = 5205; + reference = new EpsgCoordinateReferenceRecord(10702, (EpsgCoordinateSystemKind)2, 3621); + return true; + case 10723: + cacheIndex = 5206; + reference = new EpsgCoordinateReferenceRecord(10723, (EpsgCoordinateSystemKind)1, 206); + return true; + case 10724: + cacheIndex = 5207; + reference = new EpsgCoordinateReferenceRecord(10724, (EpsgCoordinateSystemKind)0, 803); + return true; + case 10725: + cacheIndex = 5208; + reference = new EpsgCoordinateReferenceRecord(10725, (EpsgCoordinateSystemKind)0, 804); + return true; + case 10726: + cacheIndex = 5209; + reference = new EpsgCoordinateReferenceRecord(10726, (EpsgCoordinateSystemKind)2, 3622); + return true; + case 10727: + cacheIndex = 5210; + reference = new EpsgCoordinateReferenceRecord(10727, (EpsgCoordinateSystemKind)2, 3623); + return true; + case 10728: + cacheIndex = 5211; + reference = new EpsgCoordinateReferenceRecord(10728, (EpsgCoordinateSystemKind)2, 3624); + return true; + case 10729: + cacheIndex = 5212; + reference = new EpsgCoordinateReferenceRecord(10729, (EpsgCoordinateSystemKind)2, 3625); + return true; + case 10731: + cacheIndex = 5213; + reference = new EpsgCoordinateReferenceRecord(10731, (EpsgCoordinateSystemKind)2, 3626); + return true; + case 10732: + cacheIndex = 5214; + reference = new EpsgCoordinateReferenceRecord(10732, (EpsgCoordinateSystemKind)2, 3627); + return true; + case 10733: + cacheIndex = 5215; + reference = new EpsgCoordinateReferenceRecord(10733, (EpsgCoordinateSystemKind)2, 3628); + return true; + case 10734: + cacheIndex = 5216; + reference = new EpsgCoordinateReferenceRecord(10734, (EpsgCoordinateSystemKind)1, 207); + return true; + case 10735: + cacheIndex = 5217; + reference = new EpsgCoordinateReferenceRecord(10735, (EpsgCoordinateSystemKind)0, 805); + return true; + case 10736: + cacheIndex = 5218; + reference = new EpsgCoordinateReferenceRecord(10736, (EpsgCoordinateSystemKind)0, 806); + return true; + case 10737: + cacheIndex = 5219; + reference = new EpsgCoordinateReferenceRecord(10737, (EpsgCoordinateSystemKind)1, 208); + return true; + case 10738: + cacheIndex = 5220; + reference = new EpsgCoordinateReferenceRecord(10738, (EpsgCoordinateSystemKind)0, 807); + return true; + case 10739: + cacheIndex = 5221; + reference = new EpsgCoordinateReferenceRecord(10739, (EpsgCoordinateSystemKind)0, 808); + return true; + case 10740: + cacheIndex = 5222; + reference = new EpsgCoordinateReferenceRecord(10740, (EpsgCoordinateSystemKind)3, 251); + return true; + case 10741: + cacheIndex = 5223; + reference = new EpsgCoordinateReferenceRecord(10741, (EpsgCoordinateSystemKind)4, 324); + return true; + case 10742: + cacheIndex = 5224; + reference = new EpsgCoordinateReferenceRecord(10742, (EpsgCoordinateSystemKind)4, 325); + return true; + case 10744: + cacheIndex = 5225; + reference = new EpsgCoordinateReferenceRecord(10744, (EpsgCoordinateSystemKind)2, 3629); + return true; + case 10745: + cacheIndex = 5226; + reference = new EpsgCoordinateReferenceRecord(10745, (EpsgCoordinateSystemKind)2, 3630); + return true; + case 10746: + cacheIndex = 5227; + reference = new EpsgCoordinateReferenceRecord(10746, (EpsgCoordinateSystemKind)4, 326); + return true; + case 10747: + cacheIndex = 5228; + reference = new EpsgCoordinateReferenceRecord(10747, (EpsgCoordinateSystemKind)4, 327); + return true; + case 10758: + cacheIndex = 5229; + reference = new EpsgCoordinateReferenceRecord(10758, (EpsgCoordinateSystemKind)0, 809); + return true; + case 10759: + cacheIndex = 5230; + reference = new EpsgCoordinateReferenceRecord(10759, (EpsgCoordinateSystemKind)2, 3631); + return true; + case 10760: + cacheIndex = 5231; + reference = new EpsgCoordinateReferenceRecord(10760, (EpsgCoordinateSystemKind)1, 209); + return true; + case 10761: + cacheIndex = 5232; + reference = new EpsgCoordinateReferenceRecord(10761, (EpsgCoordinateSystemKind)0, 810); + return true; + case 10762: + cacheIndex = 5233; + reference = new EpsgCoordinateReferenceRecord(10762, (EpsgCoordinateSystemKind)0, 811); + return true; + case 10763: + cacheIndex = 5234; + reference = new EpsgCoordinateReferenceRecord(10763, (EpsgCoordinateSystemKind)3, 252); + return true; + case 10764: + cacheIndex = 5235; + reference = new EpsgCoordinateReferenceRecord(10764, (EpsgCoordinateSystemKind)4, 328); + return true; + case 10765: + cacheIndex = 5236; + reference = new EpsgCoordinateReferenceRecord(10765, (EpsgCoordinateSystemKind)4, 329); + return true; + case 10773: + cacheIndex = 5237; + reference = new EpsgCoordinateReferenceRecord(10773, (EpsgCoordinateSystemKind)2, 3632); + return true; + case 10774: + cacheIndex = 5238; + reference = new EpsgCoordinateReferenceRecord(10774, (EpsgCoordinateSystemKind)4, 330); + return true; + case 10779: + cacheIndex = 5239; + reference = new EpsgCoordinateReferenceRecord(10779, (EpsgCoordinateSystemKind)1, 210); + return true; + case 10780: + cacheIndex = 5240; + reference = new EpsgCoordinateReferenceRecord(10780, (EpsgCoordinateSystemKind)0, 812); + return true; + case 10781: + cacheIndex = 5241; + reference = new EpsgCoordinateReferenceRecord(10781, (EpsgCoordinateSystemKind)0, 813); + return true; + case 10783: + cacheIndex = 5242; + reference = new EpsgCoordinateReferenceRecord(10783, (EpsgCoordinateSystemKind)1, 211); + return true; + case 10784: + cacheIndex = 5243; + reference = new EpsgCoordinateReferenceRecord(10784, (EpsgCoordinateSystemKind)0, 814); + return true; + case 10785: + cacheIndex = 5244; + reference = new EpsgCoordinateReferenceRecord(10785, (EpsgCoordinateSystemKind)0, 815); + return true; + case 10789: + cacheIndex = 5245; + reference = new EpsgCoordinateReferenceRecord(10789, (EpsgCoordinateSystemKind)1, 212); + return true; + case 10790: + cacheIndex = 5246; + reference = new EpsgCoordinateReferenceRecord(10790, (EpsgCoordinateSystemKind)0, 816); + return true; + case 10791: + cacheIndex = 5247; + reference = new EpsgCoordinateReferenceRecord(10791, (EpsgCoordinateSystemKind)0, 817); + return true; + case 10792: + cacheIndex = 5248; + reference = new EpsgCoordinateReferenceRecord(10792, (EpsgCoordinateSystemKind)2, 3633); + return true; + case 10793: + cacheIndex = 5249; + reference = new EpsgCoordinateReferenceRecord(10793, (EpsgCoordinateSystemKind)2, 3634); + return true; + case 10794: + cacheIndex = 5250; + reference = new EpsgCoordinateReferenceRecord(10794, (EpsgCoordinateSystemKind)2, 3635); + return true; + case 10795: + cacheIndex = 5251; + reference = new EpsgCoordinateReferenceRecord(10795, (EpsgCoordinateSystemKind)2, 3636); + return true; + case 10798: + cacheIndex = 5252; + reference = new EpsgCoordinateReferenceRecord(10798, (EpsgCoordinateSystemKind)1, 213); + return true; + case 10799: + cacheIndex = 5253; + reference = new EpsgCoordinateReferenceRecord(10799, (EpsgCoordinateSystemKind)0, 818); + return true; + case 10800: + cacheIndex = 5254; + reference = new EpsgCoordinateReferenceRecord(10800, (EpsgCoordinateSystemKind)0, 819); + return true; + case 10801: + cacheIndex = 5255; + reference = new EpsgCoordinateReferenceRecord(10801, (EpsgCoordinateSystemKind)2, 3637); + return true; + case 10802: + cacheIndex = 5256; + reference = new EpsgCoordinateReferenceRecord(10802, (EpsgCoordinateSystemKind)2, 3638); + return true; + case 10805: + cacheIndex = 5257; + reference = new EpsgCoordinateReferenceRecord(10805, (EpsgCoordinateSystemKind)1, 214); + return true; + case 10806: + cacheIndex = 5258; + reference = new EpsgCoordinateReferenceRecord(10806, (EpsgCoordinateSystemKind)0, 820); + return true; + case 10807: + cacheIndex = 5259; + reference = new EpsgCoordinateReferenceRecord(10807, (EpsgCoordinateSystemKind)0, 821); + return true; + case 10820: + cacheIndex = 5260; + reference = new EpsgCoordinateReferenceRecord(10820, (EpsgCoordinateSystemKind)2, 3639); + return true; + case 10826: + cacheIndex = 5261; + reference = new EpsgCoordinateReferenceRecord(10826, (EpsgCoordinateSystemKind)4, 331); + return true; + case 10829: + cacheIndex = 5262; + reference = new EpsgCoordinateReferenceRecord(10829, (EpsgCoordinateSystemKind)1, 215); + return true; + case 10830: + cacheIndex = 5263; + reference = new EpsgCoordinateReferenceRecord(10830, (EpsgCoordinateSystemKind)0, 822); + return true; + case 10831: + cacheIndex = 5264; + reference = new EpsgCoordinateReferenceRecord(10831, (EpsgCoordinateSystemKind)0, 823); + return true; + case 10833: + cacheIndex = 5265; + reference = new EpsgCoordinateReferenceRecord(10833, (EpsgCoordinateSystemKind)2, 3640); + return true; + case 10836: + cacheIndex = 5266; + reference = new EpsgCoordinateReferenceRecord(10836, (EpsgCoordinateSystemKind)2, 3641); + return true; + case 10837: + cacheIndex = 5267; + reference = new EpsgCoordinateReferenceRecord(10837, (EpsgCoordinateSystemKind)2, 3642); + return true; + case 10839: + cacheIndex = 5268; + reference = new EpsgCoordinateReferenceRecord(10839, (EpsgCoordinateSystemKind)4, 332); + return true; + case 10849: + cacheIndex = 5269; + reference = new EpsgCoordinateReferenceRecord(10849, (EpsgCoordinateSystemKind)0, 824); + return true; + case 10851: + cacheIndex = 5270; + reference = new EpsgCoordinateReferenceRecord(10851, (EpsgCoordinateSystemKind)2, 3643); + return true; + case 10852: + cacheIndex = 5271; + reference = new EpsgCoordinateReferenceRecord(10852, (EpsgCoordinateSystemKind)4, 333); + return true; + case 10857: + cacheIndex = 5272; + reference = new EpsgCoordinateReferenceRecord(10857, (EpsgCoordinateSystemKind)2, 3644); + return true; + case 10860: + cacheIndex = 5273; + reference = new EpsgCoordinateReferenceRecord(10860, (EpsgCoordinateSystemKind)0, 825); + return true; + case 10863: + cacheIndex = 5274; + reference = new EpsgCoordinateReferenceRecord(10863, (EpsgCoordinateSystemKind)2, 3645); + return true; + case 10864: + cacheIndex = 5275; + reference = new EpsgCoordinateReferenceRecord(10864, (EpsgCoordinateSystemKind)4, 334); + return true; + case 10865: + cacheIndex = 5276; + reference = new EpsgCoordinateReferenceRecord(10865, (EpsgCoordinateSystemKind)4, 335); + return true; + case 10873: + cacheIndex = 5277; + reference = new EpsgCoordinateReferenceRecord(10873, (EpsgCoordinateSystemKind)1, 216); + return true; + case 10874: + cacheIndex = 5278; + reference = new EpsgCoordinateReferenceRecord(10874, (EpsgCoordinateSystemKind)0, 826); + return true; + case 10875: + cacheIndex = 5279; + reference = new EpsgCoordinateReferenceRecord(10875, (EpsgCoordinateSystemKind)0, 827); + return true; + case 10890: + cacheIndex = 5280; + reference = new EpsgCoordinateReferenceRecord(10890, (EpsgCoordinateSystemKind)1, 217); + return true; + case 10891: + cacheIndex = 5281; + reference = new EpsgCoordinateReferenceRecord(10891, (EpsgCoordinateSystemKind)0, 828); + return true; + case 10892: + cacheIndex = 5282; + reference = new EpsgCoordinateReferenceRecord(10892, (EpsgCoordinateSystemKind)0, 829); + return true; + case 10898: + cacheIndex = 5283; + reference = new EpsgCoordinateReferenceRecord(10898, (EpsgCoordinateSystemKind)0, 830); + return true; + case 10899: + cacheIndex = 5284; + reference = new EpsgCoordinateReferenceRecord(10899, (EpsgCoordinateSystemKind)2, 3646); + return true; + case 10900: + cacheIndex = 5285; + reference = new EpsgCoordinateReferenceRecord(10900, (EpsgCoordinateSystemKind)3, 253); + return true; + case 10904: + cacheIndex = 5286; + reference = new EpsgCoordinateReferenceRecord(10904, (EpsgCoordinateSystemKind)4, 336); + return true; + case 10906: + cacheIndex = 5287; + reference = new EpsgCoordinateReferenceRecord(10906, (EpsgCoordinateSystemKind)4, 337); + return true; + case 10908: + cacheIndex = 5288; + reference = new EpsgCoordinateReferenceRecord(10908, (EpsgCoordinateSystemKind)1, 218); + return true; + case 10909: + cacheIndex = 5289; + reference = new EpsgCoordinateReferenceRecord(10909, (EpsgCoordinateSystemKind)0, 831); + return true; + case 10910: + cacheIndex = 5290; + reference = new EpsgCoordinateReferenceRecord(10910, (EpsgCoordinateSystemKind)0, 832); + return true; + case 10911: + cacheIndex = 5291; + reference = new EpsgCoordinateReferenceRecord(10911, (EpsgCoordinateSystemKind)2, 3647); + return true; + case 10912: + cacheIndex = 5292; + reference = new EpsgCoordinateReferenceRecord(10912, (EpsgCoordinateSystemKind)2, 3648); + return true; + case 10913: + cacheIndex = 5293; + reference = new EpsgCoordinateReferenceRecord(10913, (EpsgCoordinateSystemKind)2, 3649); + return true; + case 10914: + cacheIndex = 5294; + reference = new EpsgCoordinateReferenceRecord(10914, (EpsgCoordinateSystemKind)2, 3650); + return true; + case 10915: + cacheIndex = 5295; + reference = new EpsgCoordinateReferenceRecord(10915, (EpsgCoordinateSystemKind)2, 3651); + return true; + case 10916: + cacheIndex = 5296; + reference = new EpsgCoordinateReferenceRecord(10916, (EpsgCoordinateSystemKind)2, 3652); + return true; + case 10917: + cacheIndex = 5297; + reference = new EpsgCoordinateReferenceRecord(10917, (EpsgCoordinateSystemKind)2, 3653); + return true; + case 10918: + cacheIndex = 5298; + reference = new EpsgCoordinateReferenceRecord(10918, (EpsgCoordinateSystemKind)3, 254); + return true; + case 10920: + cacheIndex = 5299; + reference = new EpsgCoordinateReferenceRecord(10920, (EpsgCoordinateSystemKind)4, 338); + return true; + case 10921: + cacheIndex = 5300; + reference = new EpsgCoordinateReferenceRecord(10921, (EpsgCoordinateSystemKind)2, 3654); + return true; + case 10922: + cacheIndex = 5301; + reference = new EpsgCoordinateReferenceRecord(10922, (EpsgCoordinateSystemKind)2, 3655); + return true; + case 10923: + cacheIndex = 5302; + reference = new EpsgCoordinateReferenceRecord(10923, (EpsgCoordinateSystemKind)2, 3656); + return true; + case 10924: + cacheIndex = 5303; + reference = new EpsgCoordinateReferenceRecord(10924, (EpsgCoordinateSystemKind)2, 3657); + return true; + case 10925: + cacheIndex = 5304; + reference = new EpsgCoordinateReferenceRecord(10925, (EpsgCoordinateSystemKind)2, 3658); + return true; + case 10926: + cacheIndex = 5305; + reference = new EpsgCoordinateReferenceRecord(10926, (EpsgCoordinateSystemKind)2, 3659); + return true; + case 10939: + cacheIndex = 5306; + reference = new EpsgCoordinateReferenceRecord(10939, (EpsgCoordinateSystemKind)1, 219); + return true; + case 10940: + cacheIndex = 5307; + reference = new EpsgCoordinateReferenceRecord(10940, (EpsgCoordinateSystemKind)0, 833); + return true; + case 10941: + cacheIndex = 5308; + reference = new EpsgCoordinateReferenceRecord(10941, (EpsgCoordinateSystemKind)0, 834); + return true; + case 10942: + cacheIndex = 5309; + reference = new EpsgCoordinateReferenceRecord(10942, (EpsgCoordinateSystemKind)2, 3660); + return true; + case 10943: + cacheIndex = 5310; + reference = new EpsgCoordinateReferenceRecord(10943, (EpsgCoordinateSystemKind)2, 3661); + return true; + case 10944: + cacheIndex = 5311; + reference = new EpsgCoordinateReferenceRecord(10944, (EpsgCoordinateSystemKind)2, 3662); + return true; + case 10945: + cacheIndex = 5312; + reference = new EpsgCoordinateReferenceRecord(10945, (EpsgCoordinateSystemKind)2, 3663); + return true; + case 10946: + cacheIndex = 5313; + reference = new EpsgCoordinateReferenceRecord(10946, (EpsgCoordinateSystemKind)2, 3664); + return true; + case 10947: + cacheIndex = 5314; + reference = new EpsgCoordinateReferenceRecord(10947, (EpsgCoordinateSystemKind)2, 3665); + return true; + case 10948: + cacheIndex = 5315; + reference = new EpsgCoordinateReferenceRecord(10948, (EpsgCoordinateSystemKind)2, 3666); + return true; + case 10949: + cacheIndex = 5316; + reference = new EpsgCoordinateReferenceRecord(10949, (EpsgCoordinateSystemKind)2, 3667); + return true; + case 10950: + cacheIndex = 5317; + reference = new EpsgCoordinateReferenceRecord(10950, (EpsgCoordinateSystemKind)1, 220); + return true; + case 10951: + cacheIndex = 5318; + reference = new EpsgCoordinateReferenceRecord(10951, (EpsgCoordinateSystemKind)0, 835); + return true; + case 10952: + cacheIndex = 5319; + reference = new EpsgCoordinateReferenceRecord(10952, (EpsgCoordinateSystemKind)0, 836); + return true; + case 10954: + cacheIndex = 5320; + reference = new EpsgCoordinateReferenceRecord(10954, (EpsgCoordinateSystemKind)1, 221); + return true; + case 10955: + cacheIndex = 5321; + reference = new EpsgCoordinateReferenceRecord(10955, (EpsgCoordinateSystemKind)0, 837); + return true; + case 10956: + cacheIndex = 5322; + reference = new EpsgCoordinateReferenceRecord(10956, (EpsgCoordinateSystemKind)0, 838); + return true; + case 10957: + cacheIndex = 5323; + reference = new EpsgCoordinateReferenceRecord(10957, (EpsgCoordinateSystemKind)1, 222); + return true; + case 10958: + cacheIndex = 5324; + reference = new EpsgCoordinateReferenceRecord(10958, (EpsgCoordinateSystemKind)0, 839); + return true; + case 10959: + cacheIndex = 5325; + reference = new EpsgCoordinateReferenceRecord(10959, (EpsgCoordinateSystemKind)0, 840); + return true; + case 10966: + cacheIndex = 5326; + reference = new EpsgCoordinateReferenceRecord(10966, (EpsgCoordinateSystemKind)1, 223); + return true; + case 10967: + cacheIndex = 5327; + reference = new EpsgCoordinateReferenceRecord(10967, (EpsgCoordinateSystemKind)0, 841); + return true; + case 10968: + cacheIndex = 5328; + reference = new EpsgCoordinateReferenceRecord(10968, (EpsgCoordinateSystemKind)0, 842); + return true; + case 10979: + cacheIndex = 5329; + reference = new EpsgCoordinateReferenceRecord(10979, (EpsgCoordinateSystemKind)2, 3668); + return true; + case 10980: + cacheIndex = 5330; + reference = new EpsgCoordinateReferenceRecord(10980, (EpsgCoordinateSystemKind)2, 3669); + return true; + case 10981: + cacheIndex = 5331; + reference = new EpsgCoordinateReferenceRecord(10981, (EpsgCoordinateSystemKind)2, 3670); + return true; + case 10982: + cacheIndex = 5332; + reference = new EpsgCoordinateReferenceRecord(10982, (EpsgCoordinateSystemKind)2, 3671); + return true; + case 10983: + cacheIndex = 5333; + reference = new EpsgCoordinateReferenceRecord(10983, (EpsgCoordinateSystemKind)2, 3672); + return true; + case 10984: + cacheIndex = 5334; + reference = new EpsgCoordinateReferenceRecord(10984, (EpsgCoordinateSystemKind)2, 3673); + return true; + case 10985: + cacheIndex = 5335; + reference = new EpsgCoordinateReferenceRecord(10985, (EpsgCoordinateSystemKind)2, 3674); + return true; + case 10986: + cacheIndex = 5336; + reference = new EpsgCoordinateReferenceRecord(10986, (EpsgCoordinateSystemKind)2, 3675); + return true; + case 10987: + cacheIndex = 5337; + reference = new EpsgCoordinateReferenceRecord(10987, (EpsgCoordinateSystemKind)2, 3676); + return true; + case 10989: + cacheIndex = 5338; + reference = new EpsgCoordinateReferenceRecord(10989, (EpsgCoordinateSystemKind)3, 255); + return true; + case 10991: + cacheIndex = 5339; + reference = new EpsgCoordinateReferenceRecord(10991, (EpsgCoordinateSystemKind)1, 224); + return true; + case 10992: + cacheIndex = 5340; + reference = new EpsgCoordinateReferenceRecord(10992, (EpsgCoordinateSystemKind)0, 843); + return true; + case 10993: + cacheIndex = 5341; + reference = new EpsgCoordinateReferenceRecord(10993, (EpsgCoordinateSystemKind)0, 844); + return true; + case 10995: + cacheIndex = 5342; + reference = new EpsgCoordinateReferenceRecord(10995, (EpsgCoordinateSystemKind)2, 3677); + return true; + case 10997: + cacheIndex = 5343; + reference = new EpsgCoordinateReferenceRecord(10997, (EpsgCoordinateSystemKind)4, 339); + return true; + case 10999: + cacheIndex = 5344; + reference = new EpsgCoordinateReferenceRecord(10999, (EpsgCoordinateSystemKind)3, 256); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket11(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 11000: + cacheIndex = 5345; + reference = new EpsgCoordinateReferenceRecord(11000, (EpsgCoordinateSystemKind)4, 340); + return true; + case 11006: + cacheIndex = 5346; + reference = new EpsgCoordinateReferenceRecord(11006, (EpsgCoordinateSystemKind)4, 341); + return true; + case 11007: + cacheIndex = 5347; + reference = new EpsgCoordinateReferenceRecord(11007, (EpsgCoordinateSystemKind)1, 225); + return true; + case 11008: + cacheIndex = 5348; + reference = new EpsgCoordinateReferenceRecord(11008, (EpsgCoordinateSystemKind)0, 845); + return true; + case 11009: + cacheIndex = 5349; + reference = new EpsgCoordinateReferenceRecord(11009, (EpsgCoordinateSystemKind)0, 846); + return true; + case 11012: + cacheIndex = 5350; + reference = new EpsgCoordinateReferenceRecord(11012, (EpsgCoordinateSystemKind)2, 3678); + return true; + case 11013: + cacheIndex = 5351; + reference = new EpsgCoordinateReferenceRecord(11013, (EpsgCoordinateSystemKind)2, 3679); + return true; + case 11014: + cacheIndex = 5352; + reference = new EpsgCoordinateReferenceRecord(11014, (EpsgCoordinateSystemKind)2, 3680); + return true; + case 11015: + cacheIndex = 5353; + reference = new EpsgCoordinateReferenceRecord(11015, (EpsgCoordinateSystemKind)2, 3681); + return true; + case 11016: + cacheIndex = 5354; + reference = new EpsgCoordinateReferenceRecord(11016, (EpsgCoordinateSystemKind)2, 3682); + return true; + case 11017: + cacheIndex = 5355; + reference = new EpsgCoordinateReferenceRecord(11017, (EpsgCoordinateSystemKind)2, 3683); + return true; + case 11018: + cacheIndex = 5356; + reference = new EpsgCoordinateReferenceRecord(11018, (EpsgCoordinateSystemKind)2, 3684); + return true; + case 11019: + cacheIndex = 5357; + reference = new EpsgCoordinateReferenceRecord(11019, (EpsgCoordinateSystemKind)2, 3685); + return true; + case 11020: + cacheIndex = 5358; + reference = new EpsgCoordinateReferenceRecord(11020, (EpsgCoordinateSystemKind)2, 3686); + return true; + case 11021: + cacheIndex = 5359; + reference = new EpsgCoordinateReferenceRecord(11021, (EpsgCoordinateSystemKind)2, 3687); + return true; + case 11022: + cacheIndex = 5360; + reference = new EpsgCoordinateReferenceRecord(11022, (EpsgCoordinateSystemKind)2, 3688); + return true; + case 11023: + cacheIndex = 5361; + reference = new EpsgCoordinateReferenceRecord(11023, (EpsgCoordinateSystemKind)2, 3689); + return true; + case 11024: + cacheIndex = 5362; + reference = new EpsgCoordinateReferenceRecord(11024, (EpsgCoordinateSystemKind)2, 3690); + return true; + case 11025: + cacheIndex = 5363; + reference = new EpsgCoordinateReferenceRecord(11025, (EpsgCoordinateSystemKind)2, 3691); + return true; + case 11026: + cacheIndex = 5364; + reference = new EpsgCoordinateReferenceRecord(11026, (EpsgCoordinateSystemKind)2, 3692); + return true; + case 11027: + cacheIndex = 5365; + reference = new EpsgCoordinateReferenceRecord(11027, (EpsgCoordinateSystemKind)2, 3693); + return true; + case 11029: + cacheIndex = 5366; + reference = new EpsgCoordinateReferenceRecord(11029, (EpsgCoordinateSystemKind)1, 226); + return true; + case 11030: + cacheIndex = 5367; + reference = new EpsgCoordinateReferenceRecord(11030, (EpsgCoordinateSystemKind)0, 847); + return true; + case 11033: + cacheIndex = 5368; + reference = new EpsgCoordinateReferenceRecord(11033, (EpsgCoordinateSystemKind)0, 848); + return true; + case 11035: + cacheIndex = 5369; + reference = new EpsgCoordinateReferenceRecord(11035, (EpsgCoordinateSystemKind)1, 227); + return true; + case 11036: + cacheIndex = 5370; + reference = new EpsgCoordinateReferenceRecord(11036, (EpsgCoordinateSystemKind)0, 849); + return true; + case 11037: + cacheIndex = 5371; + reference = new EpsgCoordinateReferenceRecord(11037, (EpsgCoordinateSystemKind)0, 850); + return true; + case 11041: + cacheIndex = 5372; + reference = new EpsgCoordinateReferenceRecord(11041, (EpsgCoordinateSystemKind)1, 228); + return true; + case 11042: + cacheIndex = 5373; + reference = new EpsgCoordinateReferenceRecord(11042, (EpsgCoordinateSystemKind)0, 851); + return true; + case 11043: + cacheIndex = 5374; + reference = new EpsgCoordinateReferenceRecord(11043, (EpsgCoordinateSystemKind)0, 852); + return true; + case 11045: + cacheIndex = 5375; + reference = new EpsgCoordinateReferenceRecord(11045, (EpsgCoordinateSystemKind)1, 229); + return true; + case 11046: + cacheIndex = 5376; + reference = new EpsgCoordinateReferenceRecord(11046, (EpsgCoordinateSystemKind)0, 853); + return true; + case 11047: + cacheIndex = 5377; + reference = new EpsgCoordinateReferenceRecord(11047, (EpsgCoordinateSystemKind)0, 854); + return true; + case 11051: + cacheIndex = 5378; + reference = new EpsgCoordinateReferenceRecord(11051, (EpsgCoordinateSystemKind)1, 230); + return true; + case 11052: + cacheIndex = 5379; + reference = new EpsgCoordinateReferenceRecord(11052, (EpsgCoordinateSystemKind)0, 855); + return true; + case 11053: + cacheIndex = 5380; + reference = new EpsgCoordinateReferenceRecord(11053, (EpsgCoordinateSystemKind)0, 856); + return true; + case 11055: + cacheIndex = 5381; + reference = new EpsgCoordinateReferenceRecord(11055, (EpsgCoordinateSystemKind)1, 231); + return true; + case 11056: + cacheIndex = 5382; + reference = new EpsgCoordinateReferenceRecord(11056, (EpsgCoordinateSystemKind)0, 857); + return true; + case 11057: + cacheIndex = 5383; + reference = new EpsgCoordinateReferenceRecord(11057, (EpsgCoordinateSystemKind)0, 858); + return true; + case 11061: + cacheIndex = 5384; + reference = new EpsgCoordinateReferenceRecord(11061, (EpsgCoordinateSystemKind)1, 232); + return true; + case 11062: + cacheIndex = 5385; + reference = new EpsgCoordinateReferenceRecord(11062, (EpsgCoordinateSystemKind)0, 859); + return true; + case 11063: + cacheIndex = 5386; + reference = new EpsgCoordinateReferenceRecord(11063, (EpsgCoordinateSystemKind)0, 860); + return true; + case 11068: + cacheIndex = 5387; + reference = new EpsgCoordinateReferenceRecord(11068, (EpsgCoordinateSystemKind)1, 233); + return true; + case 11069: + cacheIndex = 5388; + reference = new EpsgCoordinateReferenceRecord(11069, (EpsgCoordinateSystemKind)0, 861); + return true; + case 11070: + cacheIndex = 5389; + reference = new EpsgCoordinateReferenceRecord(11070, (EpsgCoordinateSystemKind)0, 862); + return true; + case 11074: + cacheIndex = 5390; + reference = new EpsgCoordinateReferenceRecord(11074, (EpsgCoordinateSystemKind)1, 234); + return true; + case 11075: + cacheIndex = 5391; + reference = new EpsgCoordinateReferenceRecord(11075, (EpsgCoordinateSystemKind)0, 863); + return true; + case 11076: + cacheIndex = 5392; + reference = new EpsgCoordinateReferenceRecord(11076, (EpsgCoordinateSystemKind)0, 864); + return true; + case 11077: + cacheIndex = 5393; + reference = new EpsgCoordinateReferenceRecord(11077, (EpsgCoordinateSystemKind)1, 235); + return true; + case 11078: + cacheIndex = 5394; + reference = new EpsgCoordinateReferenceRecord(11078, (EpsgCoordinateSystemKind)0, 865); + return true; + case 11079: + cacheIndex = 5395; + reference = new EpsgCoordinateReferenceRecord(11079, (EpsgCoordinateSystemKind)0, 866); + return true; + case 11085: + cacheIndex = 5396; + reference = new EpsgCoordinateReferenceRecord(11085, (EpsgCoordinateSystemKind)1, 236); + return true; + case 11086: + cacheIndex = 5397; + reference = new EpsgCoordinateReferenceRecord(11086, (EpsgCoordinateSystemKind)0, 867); + return true; + case 11087: + cacheIndex = 5398; + reference = new EpsgCoordinateReferenceRecord(11087, (EpsgCoordinateSystemKind)0, 868); + return true; + case 11091: + cacheIndex = 5399; + reference = new EpsgCoordinateReferenceRecord(11091, (EpsgCoordinateSystemKind)1, 237); + return true; + case 11092: + cacheIndex = 5400; + reference = new EpsgCoordinateReferenceRecord(11092, (EpsgCoordinateSystemKind)0, 869); + return true; + case 11093: + cacheIndex = 5401; + reference = new EpsgCoordinateReferenceRecord(11093, (EpsgCoordinateSystemKind)0, 870); + return true; + case 11097: + cacheIndex = 5402; + reference = new EpsgCoordinateReferenceRecord(11097, (EpsgCoordinateSystemKind)1, 238); + return true; + case 11098: + cacheIndex = 5403; + reference = new EpsgCoordinateReferenceRecord(11098, (EpsgCoordinateSystemKind)0, 871); + return true; + case 11099: + cacheIndex = 5404; + reference = new EpsgCoordinateReferenceRecord(11099, (EpsgCoordinateSystemKind)0, 872); + return true; + case 11106: + cacheIndex = 5405; + reference = new EpsgCoordinateReferenceRecord(11106, (EpsgCoordinateSystemKind)1, 239); + return true; + case 11107: + cacheIndex = 5406; + reference = new EpsgCoordinateReferenceRecord(11107, (EpsgCoordinateSystemKind)0, 873); + return true; + case 11108: + cacheIndex = 5407; + reference = new EpsgCoordinateReferenceRecord(11108, (EpsgCoordinateSystemKind)0, 874); + return true; + case 11112: + cacheIndex = 5408; + reference = new EpsgCoordinateReferenceRecord(11112, (EpsgCoordinateSystemKind)1, 240); + return true; + case 11113: + cacheIndex = 5409; + reference = new EpsgCoordinateReferenceRecord(11113, (EpsgCoordinateSystemKind)0, 875); + return true; + case 11114: + cacheIndex = 5410; + reference = new EpsgCoordinateReferenceRecord(11114, (EpsgCoordinateSystemKind)2, 3694); + return true; + case 11115: + cacheIndex = 5411; + reference = new EpsgCoordinateReferenceRecord(11115, (EpsgCoordinateSystemKind)2, 3695); + return true; + case 11116: + cacheIndex = 5412; + reference = new EpsgCoordinateReferenceRecord(11116, (EpsgCoordinateSystemKind)2, 3696); + return true; + case 11117: + cacheIndex = 5413; + reference = new EpsgCoordinateReferenceRecord(11117, (EpsgCoordinateSystemKind)2, 3697); + return true; + case 11118: + cacheIndex = 5414; + reference = new EpsgCoordinateReferenceRecord(11118, (EpsgCoordinateSystemKind)2, 3698); + return true; + case 11119: + cacheIndex = 5415; + reference = new EpsgCoordinateReferenceRecord(11119, (EpsgCoordinateSystemKind)0, 876); + return true; + case 11120: + cacheIndex = 5416; + reference = new EpsgCoordinateReferenceRecord(11120, (EpsgCoordinateSystemKind)4, 342); + return true; + case 11126: + cacheIndex = 5417; + reference = new EpsgCoordinateReferenceRecord(11126, (EpsgCoordinateSystemKind)1, 241); + return true; + case 11127: + cacheIndex = 5418; + reference = new EpsgCoordinateReferenceRecord(11127, (EpsgCoordinateSystemKind)0, 877); + return true; + case 11128: + cacheIndex = 5419; + reference = new EpsgCoordinateReferenceRecord(11128, (EpsgCoordinateSystemKind)0, 878); + return true; + case 11129: + cacheIndex = 5420; + reference = new EpsgCoordinateReferenceRecord(11129, (EpsgCoordinateSystemKind)1, 242); + return true; + case 11130: + cacheIndex = 5421; + reference = new EpsgCoordinateReferenceRecord(11130, (EpsgCoordinateSystemKind)0, 879); + return true; + case 11134: + cacheIndex = 5422; + reference = new EpsgCoordinateReferenceRecord(11134, (EpsgCoordinateSystemKind)0, 880); + return true; + case 11141: + cacheIndex = 5423; + reference = new EpsgCoordinateReferenceRecord(11141, (EpsgCoordinateSystemKind)2, 3699); + return true; + case 11142: + cacheIndex = 5424; + reference = new EpsgCoordinateReferenceRecord(11142, (EpsgCoordinateSystemKind)2, 3700); + return true; + case 11143: + cacheIndex = 5425; + reference = new EpsgCoordinateReferenceRecord(11143, (EpsgCoordinateSystemKind)2, 3701); + return true; + case 11144: + cacheIndex = 5426; + reference = new EpsgCoordinateReferenceRecord(11144, (EpsgCoordinateSystemKind)2, 3702); + return true; + case 11145: + cacheIndex = 5427; + reference = new EpsgCoordinateReferenceRecord(11145, (EpsgCoordinateSystemKind)2, 3703); + return true; + case 11146: + cacheIndex = 5428; + reference = new EpsgCoordinateReferenceRecord(11146, (EpsgCoordinateSystemKind)2, 3704); + return true; + case 11147: + cacheIndex = 5429; + reference = new EpsgCoordinateReferenceRecord(11147, (EpsgCoordinateSystemKind)2, 3705); + return true; + case 11148: + cacheIndex = 5430; + reference = new EpsgCoordinateReferenceRecord(11148, (EpsgCoordinateSystemKind)2, 3706); + return true; + case 11157: + cacheIndex = 5431; + reference = new EpsgCoordinateReferenceRecord(11157, (EpsgCoordinateSystemKind)3, 257); + return true; + case 11158: + cacheIndex = 5432; + reference = new EpsgCoordinateReferenceRecord(11158, (EpsgCoordinateSystemKind)4, 343); + return true; + case 11161: + cacheIndex = 5433; + reference = new EpsgCoordinateReferenceRecord(11161, (EpsgCoordinateSystemKind)1, 243); + return true; + case 11162: + cacheIndex = 5434; + reference = new EpsgCoordinateReferenceRecord(11162, (EpsgCoordinateSystemKind)0, 881); + return true; + case 11163: + cacheIndex = 5435; + reference = new EpsgCoordinateReferenceRecord(11163, (EpsgCoordinateSystemKind)0, 882); + return true; + case 11169: + cacheIndex = 5436; + reference = new EpsgCoordinateReferenceRecord(11169, (EpsgCoordinateSystemKind)4, 344); + return true; + case 11170: + cacheIndex = 5437; + reference = new EpsgCoordinateReferenceRecord(11170, (EpsgCoordinateSystemKind)4, 345); + return true; + case 11171: + cacheIndex = 5438; + reference = new EpsgCoordinateReferenceRecord(11171, (EpsgCoordinateSystemKind)4, 346); + return true; + case 11172: + cacheIndex = 5439; + reference = new EpsgCoordinateReferenceRecord(11172, (EpsgCoordinateSystemKind)4, 347); + return true; + case 11173: + cacheIndex = 5440; + reference = new EpsgCoordinateReferenceRecord(11173, (EpsgCoordinateSystemKind)4, 348); + return true; + case 11174: + cacheIndex = 5441; + reference = new EpsgCoordinateReferenceRecord(11174, (EpsgCoordinateSystemKind)4, 349); + return true; + case 11175: + cacheIndex = 5442; + reference = new EpsgCoordinateReferenceRecord(11175, (EpsgCoordinateSystemKind)4, 350); + return true; + case 11176: + cacheIndex = 5443; + reference = new EpsgCoordinateReferenceRecord(11176, (EpsgCoordinateSystemKind)4, 351); + return true; + case 11177: + cacheIndex = 5444; + reference = new EpsgCoordinateReferenceRecord(11177, (EpsgCoordinateSystemKind)4, 352); + return true; + case 11178: + cacheIndex = 5445; + reference = new EpsgCoordinateReferenceRecord(11178, (EpsgCoordinateSystemKind)4, 353); + return true; + case 11179: + cacheIndex = 5446; + reference = new EpsgCoordinateReferenceRecord(11179, (EpsgCoordinateSystemKind)4, 354); + return true; + case 11180: + cacheIndex = 5447; + reference = new EpsgCoordinateReferenceRecord(11180, (EpsgCoordinateSystemKind)4, 355); + return true; + case 11181: + cacheIndex = 5448; + reference = new EpsgCoordinateReferenceRecord(11181, (EpsgCoordinateSystemKind)4, 356); + return true; + case 11187: + cacheIndex = 5449; + reference = new EpsgCoordinateReferenceRecord(11187, (EpsgCoordinateSystemKind)1, 244); + return true; + case 11188: + cacheIndex = 5450; + reference = new EpsgCoordinateReferenceRecord(11188, (EpsgCoordinateSystemKind)0, 883); + return true; + case 11189: + cacheIndex = 5451; + reference = new EpsgCoordinateReferenceRecord(11189, (EpsgCoordinateSystemKind)0, 884); + return true; + case 11197: + cacheIndex = 5452; + reference = new EpsgCoordinateReferenceRecord(11197, (EpsgCoordinateSystemKind)1, 245); + return true; + case 11198: + cacheIndex = 5453; + reference = new EpsgCoordinateReferenceRecord(11198, (EpsgCoordinateSystemKind)0, 885); + return true; + case 11199: + cacheIndex = 5454; + reference = new EpsgCoordinateReferenceRecord(11199, (EpsgCoordinateSystemKind)0, 886); + return true; + case 11213: + cacheIndex = 5455; + reference = new EpsgCoordinateReferenceRecord(11213, (EpsgCoordinateSystemKind)1, 246); + return true; + case 11214: + cacheIndex = 5456; + reference = new EpsgCoordinateReferenceRecord(11214, (EpsgCoordinateSystemKind)0, 887); + return true; + case 11215: + cacheIndex = 5457; + reference = new EpsgCoordinateReferenceRecord(11215, (EpsgCoordinateSystemKind)0, 888); + return true; + case 11219: + cacheIndex = 5458; + reference = new EpsgCoordinateReferenceRecord(11219, (EpsgCoordinateSystemKind)2, 3707); + return true; + case 11222: + cacheIndex = 5459; + reference = new EpsgCoordinateReferenceRecord(11222, (EpsgCoordinateSystemKind)1, 247); + return true; + case 11223: + cacheIndex = 5460; + reference = new EpsgCoordinateReferenceRecord(11223, (EpsgCoordinateSystemKind)0, 889); + return true; + case 11224: + cacheIndex = 5461; + reference = new EpsgCoordinateReferenceRecord(11224, (EpsgCoordinateSystemKind)1, 248); + return true; + case 11225: + cacheIndex = 5462; + reference = new EpsgCoordinateReferenceRecord(11225, (EpsgCoordinateSystemKind)0, 890); + return true; + case 11226: + cacheIndex = 5463; + reference = new EpsgCoordinateReferenceRecord(11226, (EpsgCoordinateSystemKind)0, 891); + return true; + case 11266: + cacheIndex = 5464; + reference = new EpsgCoordinateReferenceRecord(11266, (EpsgCoordinateSystemKind)2, 3708); + return true; + case 11267: + cacheIndex = 5465; + reference = new EpsgCoordinateReferenceRecord(11267, (EpsgCoordinateSystemKind)2, 3709); + return true; + case 11268: + cacheIndex = 5466; + reference = new EpsgCoordinateReferenceRecord(11268, (EpsgCoordinateSystemKind)2, 3710); + return true; + case 11269: + cacheIndex = 5467; + reference = new EpsgCoordinateReferenceRecord(11269, (EpsgCoordinateSystemKind)2, 3711); + return true; + case 11270: + cacheIndex = 5468; + reference = new EpsgCoordinateReferenceRecord(11270, (EpsgCoordinateSystemKind)2, 3712); + return true; + case 11271: + cacheIndex = 5469; + reference = new EpsgCoordinateReferenceRecord(11271, (EpsgCoordinateSystemKind)2, 3713); + return true; + case 11272: + cacheIndex = 5470; + reference = new EpsgCoordinateReferenceRecord(11272, (EpsgCoordinateSystemKind)2, 3714); + return true; + case 11274: + cacheIndex = 5471; + reference = new EpsgCoordinateReferenceRecord(11274, (EpsgCoordinateSystemKind)4, 357); + return true; + case 11277: + cacheIndex = 5472; + reference = new EpsgCoordinateReferenceRecord(11277, (EpsgCoordinateSystemKind)2, 3715); + return true; + case 11278: + cacheIndex = 5473; + reference = new EpsgCoordinateReferenceRecord(11278, (EpsgCoordinateSystemKind)2, 3716); + return true; + case 11279: + cacheIndex = 5474; + reference = new EpsgCoordinateReferenceRecord(11279, (EpsgCoordinateSystemKind)2, 3717); + return true; + case 11280: + cacheIndex = 5475; + reference = new EpsgCoordinateReferenceRecord(11280, (EpsgCoordinateSystemKind)2, 3718); + return true; + case 11281: + cacheIndex = 5476; + reference = new EpsgCoordinateReferenceRecord(11281, (EpsgCoordinateSystemKind)2, 3719); + return true; + case 11282: + cacheIndex = 5477; + reference = new EpsgCoordinateReferenceRecord(11282, (EpsgCoordinateSystemKind)2, 3720); + return true; + case 11283: + cacheIndex = 5478; + reference = new EpsgCoordinateReferenceRecord(11283, (EpsgCoordinateSystemKind)2, 3721); + return true; + case 11284: + cacheIndex = 5479; + reference = new EpsgCoordinateReferenceRecord(11284, (EpsgCoordinateSystemKind)2, 3722); + return true; + case 11296: + cacheIndex = 5480; + reference = new EpsgCoordinateReferenceRecord(11296, (EpsgCoordinateSystemKind)2, 3723); + return true; + case 11297: + cacheIndex = 5481; + reference = new EpsgCoordinateReferenceRecord(11297, (EpsgCoordinateSystemKind)2, 3724); + return true; + case 11298: + cacheIndex = 5482; + reference = new EpsgCoordinateReferenceRecord(11298, (EpsgCoordinateSystemKind)2, 3725); + return true; + case 11299: + cacheIndex = 5483; + reference = new EpsgCoordinateReferenceRecord(11299, (EpsgCoordinateSystemKind)2, 3726); + return true; + case 11300: + cacheIndex = 5484; + reference = new EpsgCoordinateReferenceRecord(11300, (EpsgCoordinateSystemKind)2, 3727); + return true; + case 11303: + cacheIndex = 5485; + reference = new EpsgCoordinateReferenceRecord(11303, (EpsgCoordinateSystemKind)2, 3728); + return true; + case 11304: + cacheIndex = 5486; + reference = new EpsgCoordinateReferenceRecord(11304, (EpsgCoordinateSystemKind)2, 3729); + return true; + case 11305: + cacheIndex = 5487; + reference = new EpsgCoordinateReferenceRecord(11305, (EpsgCoordinateSystemKind)2, 3730); + return true; + case 11306: + cacheIndex = 5488; + reference = new EpsgCoordinateReferenceRecord(11306, (EpsgCoordinateSystemKind)2, 3731); + return true; + case 11307: + cacheIndex = 5489; + reference = new EpsgCoordinateReferenceRecord(11307, (EpsgCoordinateSystemKind)0, 892); + return true; + case 11311: + cacheIndex = 5490; + reference = new EpsgCoordinateReferenceRecord(11311, (EpsgCoordinateSystemKind)4, 358); + return true; + case 11312: + cacheIndex = 5491; + reference = new EpsgCoordinateReferenceRecord(11312, (EpsgCoordinateSystemKind)4, 359); + return true; + case 11314: + cacheIndex = 5492; + reference = new EpsgCoordinateReferenceRecord(11314, (EpsgCoordinateSystemKind)4, 360); + return true; + case 11338: + cacheIndex = 5493; + reference = new EpsgCoordinateReferenceRecord(11338, (EpsgCoordinateSystemKind)3, 258); + return true; + case 11341: + cacheIndex = 5494; + reference = new EpsgCoordinateReferenceRecord(11341, (EpsgCoordinateSystemKind)2, 3732); + return true; + case 11360: + cacheIndex = 5495; + reference = new EpsgCoordinateReferenceRecord(11360, (EpsgCoordinateSystemKind)2, 3733); + return true; + case 11361: + cacheIndex = 5496; + reference = new EpsgCoordinateReferenceRecord(11361, (EpsgCoordinateSystemKind)2, 3734); + return true; + case 11362: + cacheIndex = 5497; + reference = new EpsgCoordinateReferenceRecord(11362, (EpsgCoordinateSystemKind)2, 3735); + return true; + case 11363: + cacheIndex = 5498; + reference = new EpsgCoordinateReferenceRecord(11363, (EpsgCoordinateSystemKind)2, 3736); + return true; + case 11364: + cacheIndex = 5499; + reference = new EpsgCoordinateReferenceRecord(11364, (EpsgCoordinateSystemKind)2, 3737); + return true; + case 11365: + cacheIndex = 5500; + reference = new EpsgCoordinateReferenceRecord(11365, (EpsgCoordinateSystemKind)2, 3738); + return true; + case 11366: + cacheIndex = 5501; + reference = new EpsgCoordinateReferenceRecord(11366, (EpsgCoordinateSystemKind)2, 3739); + return true; + case 11367: + cacheIndex = 5502; + reference = new EpsgCoordinateReferenceRecord(11367, (EpsgCoordinateSystemKind)2, 3740); + return true; + case 11368: + cacheIndex = 5503; + reference = new EpsgCoordinateReferenceRecord(11368, (EpsgCoordinateSystemKind)2, 3741); + return true; + case 11369: + cacheIndex = 5504; + reference = new EpsgCoordinateReferenceRecord(11369, (EpsgCoordinateSystemKind)2, 3742); + return true; + case 11370: + cacheIndex = 5505; + reference = new EpsgCoordinateReferenceRecord(11370, (EpsgCoordinateSystemKind)2, 3743); + return true; + case 11371: + cacheIndex = 5506; + reference = new EpsgCoordinateReferenceRecord(11371, (EpsgCoordinateSystemKind)2, 3744); + return true; + case 11372: + cacheIndex = 5507; + reference = new EpsgCoordinateReferenceRecord(11372, (EpsgCoordinateSystemKind)2, 3745); + return true; + case 11373: + cacheIndex = 5508; + reference = new EpsgCoordinateReferenceRecord(11373, (EpsgCoordinateSystemKind)2, 3746); + return true; + case 11374: + cacheIndex = 5509; + reference = new EpsgCoordinateReferenceRecord(11374, (EpsgCoordinateSystemKind)2, 3747); + return true; + case 11375: + cacheIndex = 5510; + reference = new EpsgCoordinateReferenceRecord(11375, (EpsgCoordinateSystemKind)2, 3748); + return true; + case 11376: + cacheIndex = 5511; + reference = new EpsgCoordinateReferenceRecord(11376, (EpsgCoordinateSystemKind)2, 3749); + return true; + case 11377: + cacheIndex = 5512; + reference = new EpsgCoordinateReferenceRecord(11377, (EpsgCoordinateSystemKind)2, 3750); + return true; + case 11383: + cacheIndex = 5513; + reference = new EpsgCoordinateReferenceRecord(11383, (EpsgCoordinateSystemKind)4, 361); + return true; + case 11385: + cacheIndex = 5514; + reference = new EpsgCoordinateReferenceRecord(11385, (EpsgCoordinateSystemKind)4, 362); + return true; + case 11390: + cacheIndex = 5515; + reference = new EpsgCoordinateReferenceRecord(11390, (EpsgCoordinateSystemKind)2, 3751); + return true; + case 11391: + cacheIndex = 5516; + reference = new EpsgCoordinateReferenceRecord(11391, (EpsgCoordinateSystemKind)1, 249); + return true; + case 11392: + cacheIndex = 5517; + reference = new EpsgCoordinateReferenceRecord(11392, (EpsgCoordinateSystemKind)0, 893); + return true; + case 11393: + cacheIndex = 5518; + reference = new EpsgCoordinateReferenceRecord(11393, (EpsgCoordinateSystemKind)0, 894); + return true; + case 11394: + cacheIndex = 5519; + reference = new EpsgCoordinateReferenceRecord(11394, (EpsgCoordinateSystemKind)3, 259); + return true; + case 11399: + cacheIndex = 5520; + reference = new EpsgCoordinateReferenceRecord(11399, (EpsgCoordinateSystemKind)4, 363); + return true; + case 11400: + cacheIndex = 5521; + reference = new EpsgCoordinateReferenceRecord(11400, (EpsgCoordinateSystemKind)4, 364); + return true; + case 11403: + cacheIndex = 5522; + reference = new EpsgCoordinateReferenceRecord(11403, (EpsgCoordinateSystemKind)4, 365); + return true; + case 11404: + cacheIndex = 5523; + reference = new EpsgCoordinateReferenceRecord(11404, (EpsgCoordinateSystemKind)4, 366); + return true; + case 11405: + cacheIndex = 5524; + reference = new EpsgCoordinateReferenceRecord(11405, (EpsgCoordinateSystemKind)4, 367); + return true; + case 11406: + cacheIndex = 5525; + reference = new EpsgCoordinateReferenceRecord(11406, (EpsgCoordinateSystemKind)4, 368); + return true; + case 11407: + cacheIndex = 5526; + reference = new EpsgCoordinateReferenceRecord(11407, (EpsgCoordinateSystemKind)4, 369); + return true; + case 11408: + cacheIndex = 5527; + reference = new EpsgCoordinateReferenceRecord(11408, (EpsgCoordinateSystemKind)4, 370); + return true; + case 11409: + cacheIndex = 5528; + reference = new EpsgCoordinateReferenceRecord(11409, (EpsgCoordinateSystemKind)4, 371); + return true; + case 11410: + cacheIndex = 5529; + reference = new EpsgCoordinateReferenceRecord(11410, (EpsgCoordinateSystemKind)4, 372); + return true; + case 11411: + cacheIndex = 5530; + reference = new EpsgCoordinateReferenceRecord(11411, (EpsgCoordinateSystemKind)4, 373); + return true; + case 11412: + cacheIndex = 5531; + reference = new EpsgCoordinateReferenceRecord(11412, (EpsgCoordinateSystemKind)4, 374); + return true; + case 11413: + cacheIndex = 5532; + reference = new EpsgCoordinateReferenceRecord(11413, (EpsgCoordinateSystemKind)4, 375); + return true; + case 11414: + cacheIndex = 5533; + reference = new EpsgCoordinateReferenceRecord(11414, (EpsgCoordinateSystemKind)4, 376); + return true; + case 11415: + cacheIndex = 5534; + reference = new EpsgCoordinateReferenceRecord(11415, (EpsgCoordinateSystemKind)4, 377); + return true; + case 11416: + cacheIndex = 5535; + reference = new EpsgCoordinateReferenceRecord(11416, (EpsgCoordinateSystemKind)4, 378); + return true; + case 11417: + cacheIndex = 5536; + reference = new EpsgCoordinateReferenceRecord(11417, (EpsgCoordinateSystemKind)4, 379); + return true; + case 11418: + cacheIndex = 5537; + reference = new EpsgCoordinateReferenceRecord(11418, (EpsgCoordinateSystemKind)4, 380); + return true; + case 11419: + cacheIndex = 5538; + reference = new EpsgCoordinateReferenceRecord(11419, (EpsgCoordinateSystemKind)4, 381); + return true; + case 11420: + cacheIndex = 5539; + reference = new EpsgCoordinateReferenceRecord(11420, (EpsgCoordinateSystemKind)4, 382); + return true; + case 11421: + cacheIndex = 5540; + reference = new EpsgCoordinateReferenceRecord(11421, (EpsgCoordinateSystemKind)4, 383); + return true; + case 11422: + cacheIndex = 5541; + reference = new EpsgCoordinateReferenceRecord(11422, (EpsgCoordinateSystemKind)4, 384); + return true; + case 11423: + cacheIndex = 5542; + reference = new EpsgCoordinateReferenceRecord(11423, (EpsgCoordinateSystemKind)4, 385); + return true; + case 11424: + cacheIndex = 5543; + reference = new EpsgCoordinateReferenceRecord(11424, (EpsgCoordinateSystemKind)4, 386); + return true; + case 11425: + cacheIndex = 5544; + reference = new EpsgCoordinateReferenceRecord(11425, (EpsgCoordinateSystemKind)4, 387); + return true; + case 11426: + cacheIndex = 5545; + reference = new EpsgCoordinateReferenceRecord(11426, (EpsgCoordinateSystemKind)4, 388); + return true; + case 11427: + cacheIndex = 5546; + reference = new EpsgCoordinateReferenceRecord(11427, (EpsgCoordinateSystemKind)4, 389); + return true; + case 11428: + cacheIndex = 5547; + reference = new EpsgCoordinateReferenceRecord(11428, (EpsgCoordinateSystemKind)4, 390); + return true; + case 11429: + cacheIndex = 5548; + reference = new EpsgCoordinateReferenceRecord(11429, (EpsgCoordinateSystemKind)4, 391); + return true; + case 11430: + cacheIndex = 5549; + reference = new EpsgCoordinateReferenceRecord(11430, (EpsgCoordinateSystemKind)4, 392); + return true; + case 11435: + cacheIndex = 5550; + reference = new EpsgCoordinateReferenceRecord(11435, (EpsgCoordinateSystemKind)4, 393); + return true; + case 11436: + cacheIndex = 5551; + reference = new EpsgCoordinateReferenceRecord(11436, (EpsgCoordinateSystemKind)4, 394); + return true; + case 11437: + cacheIndex = 5552; + reference = new EpsgCoordinateReferenceRecord(11437, (EpsgCoordinateSystemKind)4, 395); + return true; + case 11446: + cacheIndex = 5553; + reference = new EpsgCoordinateReferenceRecord(11446, (EpsgCoordinateSystemKind)3, 260); + return true; + case 11447: + cacheIndex = 5554; + reference = new EpsgCoordinateReferenceRecord(11447, (EpsgCoordinateSystemKind)4, 396); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket20(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 20000: + cacheIndex = 5555; + reference = new EpsgCoordinateReferenceRecord(20000, (EpsgCoordinateSystemKind)3, 261); + return true; + case 20001: + cacheIndex = 5556; + reference = new EpsgCoordinateReferenceRecord(20001, (EpsgCoordinateSystemKind)4, 397); + return true; + case 20002: + cacheIndex = 5557; + reference = new EpsgCoordinateReferenceRecord(20002, (EpsgCoordinateSystemKind)2, 3752); + return true; + case 20003: + cacheIndex = 5558; + reference = new EpsgCoordinateReferenceRecord(20003, (EpsgCoordinateSystemKind)4, 398); + return true; + case 20004: + cacheIndex = 5559; + reference = new EpsgCoordinateReferenceRecord(20004, (EpsgCoordinateSystemKind)2, 3753); + return true; + case 20005: + cacheIndex = 5560; + reference = new EpsgCoordinateReferenceRecord(20005, (EpsgCoordinateSystemKind)2, 3754); + return true; + case 20006: + cacheIndex = 5561; + reference = new EpsgCoordinateReferenceRecord(20006, (EpsgCoordinateSystemKind)2, 3755); + return true; + case 20007: + cacheIndex = 5562; + reference = new EpsgCoordinateReferenceRecord(20007, (EpsgCoordinateSystemKind)2, 3756); + return true; + case 20008: + cacheIndex = 5563; + reference = new EpsgCoordinateReferenceRecord(20008, (EpsgCoordinateSystemKind)2, 3757); + return true; + case 20009: + cacheIndex = 5564; + reference = new EpsgCoordinateReferenceRecord(20009, (EpsgCoordinateSystemKind)2, 3758); + return true; + case 20010: + cacheIndex = 5565; + reference = new EpsgCoordinateReferenceRecord(20010, (EpsgCoordinateSystemKind)2, 3759); + return true; + case 20011: + cacheIndex = 5566; + reference = new EpsgCoordinateReferenceRecord(20011, (EpsgCoordinateSystemKind)2, 3760); + return true; + case 20012: + cacheIndex = 5567; + reference = new EpsgCoordinateReferenceRecord(20012, (EpsgCoordinateSystemKind)2, 3761); + return true; + case 20013: + cacheIndex = 5568; + reference = new EpsgCoordinateReferenceRecord(20013, (EpsgCoordinateSystemKind)2, 3762); + return true; + case 20014: + cacheIndex = 5569; + reference = new EpsgCoordinateReferenceRecord(20014, (EpsgCoordinateSystemKind)2, 3763); + return true; + case 20015: + cacheIndex = 5570; + reference = new EpsgCoordinateReferenceRecord(20015, (EpsgCoordinateSystemKind)2, 3764); + return true; + case 20016: + cacheIndex = 5571; + reference = new EpsgCoordinateReferenceRecord(20016, (EpsgCoordinateSystemKind)2, 3765); + return true; + case 20017: + cacheIndex = 5572; + reference = new EpsgCoordinateReferenceRecord(20017, (EpsgCoordinateSystemKind)2, 3766); + return true; + case 20018: + cacheIndex = 5573; + reference = new EpsgCoordinateReferenceRecord(20018, (EpsgCoordinateSystemKind)2, 3767); + return true; + case 20019: + cacheIndex = 5574; + reference = new EpsgCoordinateReferenceRecord(20019, (EpsgCoordinateSystemKind)2, 3768); + return true; + case 20020: + cacheIndex = 5575; + reference = new EpsgCoordinateReferenceRecord(20020, (EpsgCoordinateSystemKind)2, 3769); + return true; + case 20021: + cacheIndex = 5576; + reference = new EpsgCoordinateReferenceRecord(20021, (EpsgCoordinateSystemKind)2, 3770); + return true; + case 20022: + cacheIndex = 5577; + reference = new EpsgCoordinateReferenceRecord(20022, (EpsgCoordinateSystemKind)2, 3771); + return true; + case 20023: + cacheIndex = 5578; + reference = new EpsgCoordinateReferenceRecord(20023, (EpsgCoordinateSystemKind)2, 3772); + return true; + case 20024: + cacheIndex = 5579; + reference = new EpsgCoordinateReferenceRecord(20024, (EpsgCoordinateSystemKind)2, 3773); + return true; + case 20025: + cacheIndex = 5580; + reference = new EpsgCoordinateReferenceRecord(20025, (EpsgCoordinateSystemKind)2, 3774); + return true; + case 20026: + cacheIndex = 5581; + reference = new EpsgCoordinateReferenceRecord(20026, (EpsgCoordinateSystemKind)2, 3775); + return true; + case 20027: + cacheIndex = 5582; + reference = new EpsgCoordinateReferenceRecord(20027, (EpsgCoordinateSystemKind)2, 3776); + return true; + case 20028: + cacheIndex = 5583; + reference = new EpsgCoordinateReferenceRecord(20028, (EpsgCoordinateSystemKind)2, 3777); + return true; + case 20029: + cacheIndex = 5584; + reference = new EpsgCoordinateReferenceRecord(20029, (EpsgCoordinateSystemKind)2, 3778); + return true; + case 20030: + cacheIndex = 5585; + reference = new EpsgCoordinateReferenceRecord(20030, (EpsgCoordinateSystemKind)2, 3779); + return true; + case 20031: + cacheIndex = 5586; + reference = new EpsgCoordinateReferenceRecord(20031, (EpsgCoordinateSystemKind)2, 3780); + return true; + case 20032: + cacheIndex = 5587; + reference = new EpsgCoordinateReferenceRecord(20032, (EpsgCoordinateSystemKind)2, 3781); + return true; + case 20033: + cacheIndex = 5588; + reference = new EpsgCoordinateReferenceRecord(20033, (EpsgCoordinateSystemKind)0, 895); + return true; + case 20034: + cacheIndex = 5589; + reference = new EpsgCoordinateReferenceRecord(20034, (EpsgCoordinateSystemKind)3, 262); + return true; + case 20035: + cacheIndex = 5590; + reference = new EpsgCoordinateReferenceRecord(20035, (EpsgCoordinateSystemKind)3, 263); + return true; + case 20036: + cacheIndex = 5591; + reference = new EpsgCoordinateReferenceRecord(20036, (EpsgCoordinateSystemKind)3, 264); + return true; + case 20037: + cacheIndex = 5592; + reference = new EpsgCoordinateReferenceRecord(20037, (EpsgCoordinateSystemKind)4, 399); + return true; + case 20038: + cacheIndex = 5593; + reference = new EpsgCoordinateReferenceRecord(20038, (EpsgCoordinateSystemKind)4, 400); + return true; + case 20039: + cacheIndex = 5594; + reference = new EpsgCoordinateReferenceRecord(20039, (EpsgCoordinateSystemKind)1, 250); + return true; + case 20040: + cacheIndex = 5595; + reference = new EpsgCoordinateReferenceRecord(20040, (EpsgCoordinateSystemKind)0, 896); + return true; + case 20041: + cacheIndex = 5596; + reference = new EpsgCoordinateReferenceRecord(20041, (EpsgCoordinateSystemKind)0, 897); + return true; + case 20042: + cacheIndex = 5597; + reference = new EpsgCoordinateReferenceRecord(20042, (EpsgCoordinateSystemKind)2, 3782); + return true; + case 20043: + cacheIndex = 5598; + reference = new EpsgCoordinateReferenceRecord(20043, (EpsgCoordinateSystemKind)4, 401); + return true; + case 20044: + cacheIndex = 5599; + reference = new EpsgCoordinateReferenceRecord(20044, (EpsgCoordinateSystemKind)1, 251); + return true; + case 20045: + cacheIndex = 5600; + reference = new EpsgCoordinateReferenceRecord(20045, (EpsgCoordinateSystemKind)0, 898); + return true; + case 20046: + cacheIndex = 5601; + reference = new EpsgCoordinateReferenceRecord(20046, (EpsgCoordinateSystemKind)0, 899); + return true; + case 20047: + cacheIndex = 5602; + reference = new EpsgCoordinateReferenceRecord(20047, (EpsgCoordinateSystemKind)2, 3783); + return true; + case 20048: + cacheIndex = 5603; + reference = new EpsgCoordinateReferenceRecord(20048, (EpsgCoordinateSystemKind)2, 3784); + return true; + case 20049: + cacheIndex = 5604; + reference = new EpsgCoordinateReferenceRecord(20049, (EpsgCoordinateSystemKind)2, 3785); + return true; + case 20050: + cacheIndex = 5605; + reference = new EpsgCoordinateReferenceRecord(20050, (EpsgCoordinateSystemKind)2, 3786); + return true; + case 20135: + cacheIndex = 5606; + reference = new EpsgCoordinateReferenceRecord(20135, (EpsgCoordinateSystemKind)2, 3787); + return true; + case 20136: + cacheIndex = 5607; + reference = new EpsgCoordinateReferenceRecord(20136, (EpsgCoordinateSystemKind)2, 3788); + return true; + case 20137: + cacheIndex = 5608; + reference = new EpsgCoordinateReferenceRecord(20137, (EpsgCoordinateSystemKind)2, 3789); + return true; + case 20138: + cacheIndex = 5609; + reference = new EpsgCoordinateReferenceRecord(20138, (EpsgCoordinateSystemKind)2, 3790); + return true; + case 20249: + cacheIndex = 5610; + reference = new EpsgCoordinateReferenceRecord(20249, (EpsgCoordinateSystemKind)2, 3791); + return true; + case 20250: + cacheIndex = 5611; + reference = new EpsgCoordinateReferenceRecord(20250, (EpsgCoordinateSystemKind)2, 3792); + return true; + case 20251: + cacheIndex = 5612; + reference = new EpsgCoordinateReferenceRecord(20251, (EpsgCoordinateSystemKind)2, 3793); + return true; + case 20252: + cacheIndex = 5613; + reference = new EpsgCoordinateReferenceRecord(20252, (EpsgCoordinateSystemKind)2, 3794); + return true; + case 20253: + cacheIndex = 5614; + reference = new EpsgCoordinateReferenceRecord(20253, (EpsgCoordinateSystemKind)2, 3795); + return true; + case 20254: + cacheIndex = 5615; + reference = new EpsgCoordinateReferenceRecord(20254, (EpsgCoordinateSystemKind)2, 3796); + return true; + case 20255: + cacheIndex = 5616; + reference = new EpsgCoordinateReferenceRecord(20255, (EpsgCoordinateSystemKind)2, 3797); + return true; + case 20256: + cacheIndex = 5617; + reference = new EpsgCoordinateReferenceRecord(20256, (EpsgCoordinateSystemKind)2, 3798); + return true; + case 20257: + cacheIndex = 5618; + reference = new EpsgCoordinateReferenceRecord(20257, (EpsgCoordinateSystemKind)2, 3799); + return true; + case 20258: + cacheIndex = 5619; + reference = new EpsgCoordinateReferenceRecord(20258, (EpsgCoordinateSystemKind)2, 3800); + return true; + case 20349: + cacheIndex = 5620; + reference = new EpsgCoordinateReferenceRecord(20349, (EpsgCoordinateSystemKind)2, 3801); + return true; + case 20350: + cacheIndex = 5621; + reference = new EpsgCoordinateReferenceRecord(20350, (EpsgCoordinateSystemKind)2, 3802); + return true; + case 20351: + cacheIndex = 5622; + reference = new EpsgCoordinateReferenceRecord(20351, (EpsgCoordinateSystemKind)2, 3803); + return true; + case 20352: + cacheIndex = 5623; + reference = new EpsgCoordinateReferenceRecord(20352, (EpsgCoordinateSystemKind)2, 3804); + return true; + case 20353: + cacheIndex = 5624; + reference = new EpsgCoordinateReferenceRecord(20353, (EpsgCoordinateSystemKind)2, 3805); + return true; + case 20354: + cacheIndex = 5625; + reference = new EpsgCoordinateReferenceRecord(20354, (EpsgCoordinateSystemKind)2, 3806); + return true; + case 20355: + cacheIndex = 5626; + reference = new EpsgCoordinateReferenceRecord(20355, (EpsgCoordinateSystemKind)2, 3807); + return true; + case 20356: + cacheIndex = 5627; + reference = new EpsgCoordinateReferenceRecord(20356, (EpsgCoordinateSystemKind)2, 3808); + return true; + case 20436: + cacheIndex = 5628; + reference = new EpsgCoordinateReferenceRecord(20436, (EpsgCoordinateSystemKind)2, 3809); + return true; + case 20437: + cacheIndex = 5629; + reference = new EpsgCoordinateReferenceRecord(20437, (EpsgCoordinateSystemKind)2, 3810); + return true; + case 20438: + cacheIndex = 5630; + reference = new EpsgCoordinateReferenceRecord(20438, (EpsgCoordinateSystemKind)2, 3811); + return true; + case 20439: + cacheIndex = 5631; + reference = new EpsgCoordinateReferenceRecord(20439, (EpsgCoordinateSystemKind)2, 3812); + return true; + case 20440: + cacheIndex = 5632; + reference = new EpsgCoordinateReferenceRecord(20440, (EpsgCoordinateSystemKind)2, 3813); + return true; + case 20499: + cacheIndex = 5633; + reference = new EpsgCoordinateReferenceRecord(20499, (EpsgCoordinateSystemKind)2, 3814); + return true; + case 20538: + cacheIndex = 5634; + reference = new EpsgCoordinateReferenceRecord(20538, (EpsgCoordinateSystemKind)2, 3815); + return true; + case 20539: + cacheIndex = 5635; + reference = new EpsgCoordinateReferenceRecord(20539, (EpsgCoordinateSystemKind)2, 3816); + return true; + case 20790: + cacheIndex = 5636; + reference = new EpsgCoordinateReferenceRecord(20790, (EpsgCoordinateSystemKind)2, 3817); + return true; + case 20791: + cacheIndex = 5637; + reference = new EpsgCoordinateReferenceRecord(20791, (EpsgCoordinateSystemKind)2, 3818); + return true; + case 20822: + cacheIndex = 5638; + reference = new EpsgCoordinateReferenceRecord(20822, (EpsgCoordinateSystemKind)2, 3819); + return true; + case 20823: + cacheIndex = 5639; + reference = new EpsgCoordinateReferenceRecord(20823, (EpsgCoordinateSystemKind)2, 3820); + return true; + case 20824: + cacheIndex = 5640; + reference = new EpsgCoordinateReferenceRecord(20824, (EpsgCoordinateSystemKind)2, 3821); + return true; + case 20904: + cacheIndex = 5641; + reference = new EpsgCoordinateReferenceRecord(20904, (EpsgCoordinateSystemKind)2, 3822); + return true; + case 20905: + cacheIndex = 5642; + reference = new EpsgCoordinateReferenceRecord(20905, (EpsgCoordinateSystemKind)2, 3823); + return true; + case 20906: + cacheIndex = 5643; + reference = new EpsgCoordinateReferenceRecord(20906, (EpsgCoordinateSystemKind)2, 3824); + return true; + case 20907: + cacheIndex = 5644; + reference = new EpsgCoordinateReferenceRecord(20907, (EpsgCoordinateSystemKind)2, 3825); + return true; + case 20908: + cacheIndex = 5645; + reference = new EpsgCoordinateReferenceRecord(20908, (EpsgCoordinateSystemKind)2, 3826); + return true; + case 20909: + cacheIndex = 5646; + reference = new EpsgCoordinateReferenceRecord(20909, (EpsgCoordinateSystemKind)2, 3827); + return true; + case 20910: + cacheIndex = 5647; + reference = new EpsgCoordinateReferenceRecord(20910, (EpsgCoordinateSystemKind)2, 3828); + return true; + case 20911: + cacheIndex = 5648; + reference = new EpsgCoordinateReferenceRecord(20911, (EpsgCoordinateSystemKind)2, 3829); + return true; + case 20912: + cacheIndex = 5649; + reference = new EpsgCoordinateReferenceRecord(20912, (EpsgCoordinateSystemKind)2, 3830); + return true; + case 20913: + cacheIndex = 5650; + reference = new EpsgCoordinateReferenceRecord(20913, (EpsgCoordinateSystemKind)2, 3831); + return true; + case 20914: + cacheIndex = 5651; + reference = new EpsgCoordinateReferenceRecord(20914, (EpsgCoordinateSystemKind)2, 3832); + return true; + case 20915: + cacheIndex = 5652; + reference = new EpsgCoordinateReferenceRecord(20915, (EpsgCoordinateSystemKind)2, 3833); + return true; + case 20916: + cacheIndex = 5653; + reference = new EpsgCoordinateReferenceRecord(20916, (EpsgCoordinateSystemKind)2, 3834); + return true; + case 20917: + cacheIndex = 5654; + reference = new EpsgCoordinateReferenceRecord(20917, (EpsgCoordinateSystemKind)2, 3835); + return true; + case 20918: + cacheIndex = 5655; + reference = new EpsgCoordinateReferenceRecord(20918, (EpsgCoordinateSystemKind)2, 3836); + return true; + case 20919: + cacheIndex = 5656; + reference = new EpsgCoordinateReferenceRecord(20919, (EpsgCoordinateSystemKind)2, 3837); + return true; + case 20920: + cacheIndex = 5657; + reference = new EpsgCoordinateReferenceRecord(20920, (EpsgCoordinateSystemKind)2, 3838); + return true; + case 20921: + cacheIndex = 5658; + reference = new EpsgCoordinateReferenceRecord(20921, (EpsgCoordinateSystemKind)2, 3839); + return true; + case 20922: + cacheIndex = 5659; + reference = new EpsgCoordinateReferenceRecord(20922, (EpsgCoordinateSystemKind)2, 3840); + return true; + case 20923: + cacheIndex = 5660; + reference = new EpsgCoordinateReferenceRecord(20923, (EpsgCoordinateSystemKind)2, 3841); + return true; + case 20924: + cacheIndex = 5661; + reference = new EpsgCoordinateReferenceRecord(20924, (EpsgCoordinateSystemKind)2, 3842); + return true; + case 20925: + cacheIndex = 5662; + reference = new EpsgCoordinateReferenceRecord(20925, (EpsgCoordinateSystemKind)2, 3843); + return true; + case 20926: + cacheIndex = 5663; + reference = new EpsgCoordinateReferenceRecord(20926, (EpsgCoordinateSystemKind)2, 3844); + return true; + case 20927: + cacheIndex = 5664; + reference = new EpsgCoordinateReferenceRecord(20927, (EpsgCoordinateSystemKind)2, 3845); + return true; + case 20928: + cacheIndex = 5665; + reference = new EpsgCoordinateReferenceRecord(20928, (EpsgCoordinateSystemKind)2, 3846); + return true; + case 20929: + cacheIndex = 5666; + reference = new EpsgCoordinateReferenceRecord(20929, (EpsgCoordinateSystemKind)2, 3847); + return true; + case 20930: + cacheIndex = 5667; + reference = new EpsgCoordinateReferenceRecord(20930, (EpsgCoordinateSystemKind)2, 3848); + return true; + case 20931: + cacheIndex = 5668; + reference = new EpsgCoordinateReferenceRecord(20931, (EpsgCoordinateSystemKind)2, 3849); + return true; + case 20932: + cacheIndex = 5669; + reference = new EpsgCoordinateReferenceRecord(20932, (EpsgCoordinateSystemKind)2, 3850); + return true; + case 20934: + cacheIndex = 5670; + reference = new EpsgCoordinateReferenceRecord(20934, (EpsgCoordinateSystemKind)2, 3851); + return true; + case 20935: + cacheIndex = 5671; + reference = new EpsgCoordinateReferenceRecord(20935, (EpsgCoordinateSystemKind)2, 3852); + return true; + case 20936: + cacheIndex = 5672; + reference = new EpsgCoordinateReferenceRecord(20936, (EpsgCoordinateSystemKind)2, 3853); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket21(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 21004: + cacheIndex = 5673; + reference = new EpsgCoordinateReferenceRecord(21004, (EpsgCoordinateSystemKind)2, 3854); + return true; + case 21005: + cacheIndex = 5674; + reference = new EpsgCoordinateReferenceRecord(21005, (EpsgCoordinateSystemKind)2, 3855); + return true; + case 21006: + cacheIndex = 5675; + reference = new EpsgCoordinateReferenceRecord(21006, (EpsgCoordinateSystemKind)2, 3856); + return true; + case 21007: + cacheIndex = 5676; + reference = new EpsgCoordinateReferenceRecord(21007, (EpsgCoordinateSystemKind)2, 3857); + return true; + case 21008: + cacheIndex = 5677; + reference = new EpsgCoordinateReferenceRecord(21008, (EpsgCoordinateSystemKind)2, 3858); + return true; + case 21009: + cacheIndex = 5678; + reference = new EpsgCoordinateReferenceRecord(21009, (EpsgCoordinateSystemKind)2, 3859); + return true; + case 21010: + cacheIndex = 5679; + reference = new EpsgCoordinateReferenceRecord(21010, (EpsgCoordinateSystemKind)2, 3860); + return true; + case 21011: + cacheIndex = 5680; + reference = new EpsgCoordinateReferenceRecord(21011, (EpsgCoordinateSystemKind)2, 3861); + return true; + case 21012: + cacheIndex = 5681; + reference = new EpsgCoordinateReferenceRecord(21012, (EpsgCoordinateSystemKind)2, 3862); + return true; + case 21013: + cacheIndex = 5682; + reference = new EpsgCoordinateReferenceRecord(21013, (EpsgCoordinateSystemKind)2, 3863); + return true; + case 21014: + cacheIndex = 5683; + reference = new EpsgCoordinateReferenceRecord(21014, (EpsgCoordinateSystemKind)2, 3864); + return true; + case 21015: + cacheIndex = 5684; + reference = new EpsgCoordinateReferenceRecord(21015, (EpsgCoordinateSystemKind)2, 3865); + return true; + case 21016: + cacheIndex = 5685; + reference = new EpsgCoordinateReferenceRecord(21016, (EpsgCoordinateSystemKind)2, 3866); + return true; + case 21017: + cacheIndex = 5686; + reference = new EpsgCoordinateReferenceRecord(21017, (EpsgCoordinateSystemKind)2, 3867); + return true; + case 21018: + cacheIndex = 5687; + reference = new EpsgCoordinateReferenceRecord(21018, (EpsgCoordinateSystemKind)2, 3868); + return true; + case 21019: + cacheIndex = 5688; + reference = new EpsgCoordinateReferenceRecord(21019, (EpsgCoordinateSystemKind)2, 3869); + return true; + case 21020: + cacheIndex = 5689; + reference = new EpsgCoordinateReferenceRecord(21020, (EpsgCoordinateSystemKind)2, 3870); + return true; + case 21021: + cacheIndex = 5690; + reference = new EpsgCoordinateReferenceRecord(21021, (EpsgCoordinateSystemKind)2, 3871); + return true; + case 21022: + cacheIndex = 5691; + reference = new EpsgCoordinateReferenceRecord(21022, (EpsgCoordinateSystemKind)2, 3872); + return true; + case 21023: + cacheIndex = 5692; + reference = new EpsgCoordinateReferenceRecord(21023, (EpsgCoordinateSystemKind)2, 3873); + return true; + case 21024: + cacheIndex = 5693; + reference = new EpsgCoordinateReferenceRecord(21024, (EpsgCoordinateSystemKind)2, 3874); + return true; + case 21025: + cacheIndex = 5694; + reference = new EpsgCoordinateReferenceRecord(21025, (EpsgCoordinateSystemKind)2, 3875); + return true; + case 21026: + cacheIndex = 5695; + reference = new EpsgCoordinateReferenceRecord(21026, (EpsgCoordinateSystemKind)2, 3876); + return true; + case 21027: + cacheIndex = 5696; + reference = new EpsgCoordinateReferenceRecord(21027, (EpsgCoordinateSystemKind)2, 3877); + return true; + case 21028: + cacheIndex = 5697; + reference = new EpsgCoordinateReferenceRecord(21028, (EpsgCoordinateSystemKind)2, 3878); + return true; + case 21029: + cacheIndex = 5698; + reference = new EpsgCoordinateReferenceRecord(21029, (EpsgCoordinateSystemKind)2, 3879); + return true; + case 21030: + cacheIndex = 5699; + reference = new EpsgCoordinateReferenceRecord(21030, (EpsgCoordinateSystemKind)2, 3880); + return true; + case 21031: + cacheIndex = 5700; + reference = new EpsgCoordinateReferenceRecord(21031, (EpsgCoordinateSystemKind)2, 3881); + return true; + case 21032: + cacheIndex = 5701; + reference = new EpsgCoordinateReferenceRecord(21032, (EpsgCoordinateSystemKind)2, 3882); + return true; + case 21035: + cacheIndex = 5702; + reference = new EpsgCoordinateReferenceRecord(21035, (EpsgCoordinateSystemKind)2, 3883); + return true; + case 21036: + cacheIndex = 5703; + reference = new EpsgCoordinateReferenceRecord(21036, (EpsgCoordinateSystemKind)2, 3884); + return true; + case 21037: + cacheIndex = 5704; + reference = new EpsgCoordinateReferenceRecord(21037, (EpsgCoordinateSystemKind)2, 3885); + return true; + case 21095: + cacheIndex = 5705; + reference = new EpsgCoordinateReferenceRecord(21095, (EpsgCoordinateSystemKind)2, 3886); + return true; + case 21096: + cacheIndex = 5706; + reference = new EpsgCoordinateReferenceRecord(21096, (EpsgCoordinateSystemKind)2, 3887); + return true; + case 21097: + cacheIndex = 5707; + reference = new EpsgCoordinateReferenceRecord(21097, (EpsgCoordinateSystemKind)2, 3888); + return true; + case 21148: + cacheIndex = 5708; + reference = new EpsgCoordinateReferenceRecord(21148, (EpsgCoordinateSystemKind)2, 3889); + return true; + case 21149: + cacheIndex = 5709; + reference = new EpsgCoordinateReferenceRecord(21149, (EpsgCoordinateSystemKind)2, 3890); + return true; + case 21150: + cacheIndex = 5710; + reference = new EpsgCoordinateReferenceRecord(21150, (EpsgCoordinateSystemKind)2, 3891); + return true; + case 21207: + cacheIndex = 5711; + reference = new EpsgCoordinateReferenceRecord(21207, (EpsgCoordinateSystemKind)2, 3892); + return true; + case 21208: + cacheIndex = 5712; + reference = new EpsgCoordinateReferenceRecord(21208, (EpsgCoordinateSystemKind)2, 3893); + return true; + case 21209: + cacheIndex = 5713; + reference = new EpsgCoordinateReferenceRecord(21209, (EpsgCoordinateSystemKind)2, 3894); + return true; + case 21210: + cacheIndex = 5714; + reference = new EpsgCoordinateReferenceRecord(21210, (EpsgCoordinateSystemKind)2, 3895); + return true; + case 21211: + cacheIndex = 5715; + reference = new EpsgCoordinateReferenceRecord(21211, (EpsgCoordinateSystemKind)2, 3896); + return true; + case 21212: + cacheIndex = 5716; + reference = new EpsgCoordinateReferenceRecord(21212, (EpsgCoordinateSystemKind)2, 3897); + return true; + case 21213: + cacheIndex = 5717; + reference = new EpsgCoordinateReferenceRecord(21213, (EpsgCoordinateSystemKind)2, 3898); + return true; + case 21214: + cacheIndex = 5718; + reference = new EpsgCoordinateReferenceRecord(21214, (EpsgCoordinateSystemKind)2, 3899); + return true; + case 21215: + cacheIndex = 5719; + reference = new EpsgCoordinateReferenceRecord(21215, (EpsgCoordinateSystemKind)2, 3900); + return true; + case 21216: + cacheIndex = 5720; + reference = new EpsgCoordinateReferenceRecord(21216, (EpsgCoordinateSystemKind)2, 3901); + return true; + case 21217: + cacheIndex = 5721; + reference = new EpsgCoordinateReferenceRecord(21217, (EpsgCoordinateSystemKind)2, 3902); + return true; + case 21218: + cacheIndex = 5722; + reference = new EpsgCoordinateReferenceRecord(21218, (EpsgCoordinateSystemKind)2, 3903); + return true; + case 21219: + cacheIndex = 5723; + reference = new EpsgCoordinateReferenceRecord(21219, (EpsgCoordinateSystemKind)2, 3904); + return true; + case 21220: + cacheIndex = 5724; + reference = new EpsgCoordinateReferenceRecord(21220, (EpsgCoordinateSystemKind)2, 3905); + return true; + case 21221: + cacheIndex = 5725; + reference = new EpsgCoordinateReferenceRecord(21221, (EpsgCoordinateSystemKind)2, 3906); + return true; + case 21222: + cacheIndex = 5726; + reference = new EpsgCoordinateReferenceRecord(21222, (EpsgCoordinateSystemKind)2, 3907); + return true; + case 21223: + cacheIndex = 5727; + reference = new EpsgCoordinateReferenceRecord(21223, (EpsgCoordinateSystemKind)2, 3908); + return true; + case 21224: + cacheIndex = 5728; + reference = new EpsgCoordinateReferenceRecord(21224, (EpsgCoordinateSystemKind)2, 3909); + return true; + case 21225: + cacheIndex = 5729; + reference = new EpsgCoordinateReferenceRecord(21225, (EpsgCoordinateSystemKind)2, 3910); + return true; + case 21226: + cacheIndex = 5730; + reference = new EpsgCoordinateReferenceRecord(21226, (EpsgCoordinateSystemKind)2, 3911); + return true; + case 21227: + cacheIndex = 5731; + reference = new EpsgCoordinateReferenceRecord(21227, (EpsgCoordinateSystemKind)2, 3912); + return true; + case 21228: + cacheIndex = 5732; + reference = new EpsgCoordinateReferenceRecord(21228, (EpsgCoordinateSystemKind)2, 3913); + return true; + case 21229: + cacheIndex = 5733; + reference = new EpsgCoordinateReferenceRecord(21229, (EpsgCoordinateSystemKind)2, 3914); + return true; + case 21230: + cacheIndex = 5734; + reference = new EpsgCoordinateReferenceRecord(21230, (EpsgCoordinateSystemKind)2, 3915); + return true; + case 21231: + cacheIndex = 5735; + reference = new EpsgCoordinateReferenceRecord(21231, (EpsgCoordinateSystemKind)2, 3916); + return true; + case 21232: + cacheIndex = 5736; + reference = new EpsgCoordinateReferenceRecord(21232, (EpsgCoordinateSystemKind)2, 3917); + return true; + case 21233: + cacheIndex = 5737; + reference = new EpsgCoordinateReferenceRecord(21233, (EpsgCoordinateSystemKind)2, 3918); + return true; + case 21234: + cacheIndex = 5738; + reference = new EpsgCoordinateReferenceRecord(21234, (EpsgCoordinateSystemKind)2, 3919); + return true; + case 21235: + cacheIndex = 5739; + reference = new EpsgCoordinateReferenceRecord(21235, (EpsgCoordinateSystemKind)2, 3920); + return true; + case 21236: + cacheIndex = 5740; + reference = new EpsgCoordinateReferenceRecord(21236, (EpsgCoordinateSystemKind)2, 3921); + return true; + case 21237: + cacheIndex = 5741; + reference = new EpsgCoordinateReferenceRecord(21237, (EpsgCoordinateSystemKind)2, 3922); + return true; + case 21238: + cacheIndex = 5742; + reference = new EpsgCoordinateReferenceRecord(21238, (EpsgCoordinateSystemKind)2, 3923); + return true; + case 21239: + cacheIndex = 5743; + reference = new EpsgCoordinateReferenceRecord(21239, (EpsgCoordinateSystemKind)2, 3924); + return true; + case 21240: + cacheIndex = 5744; + reference = new EpsgCoordinateReferenceRecord(21240, (EpsgCoordinateSystemKind)2, 3925); + return true; + case 21241: + cacheIndex = 5745; + reference = new EpsgCoordinateReferenceRecord(21241, (EpsgCoordinateSystemKind)2, 3926); + return true; + case 21242: + cacheIndex = 5746; + reference = new EpsgCoordinateReferenceRecord(21242, (EpsgCoordinateSystemKind)2, 3927); + return true; + case 21243: + cacheIndex = 5747; + reference = new EpsgCoordinateReferenceRecord(21243, (EpsgCoordinateSystemKind)2, 3928); + return true; + case 21244: + cacheIndex = 5748; + reference = new EpsgCoordinateReferenceRecord(21244, (EpsgCoordinateSystemKind)2, 3929); + return true; + case 21245: + cacheIndex = 5749; + reference = new EpsgCoordinateReferenceRecord(21245, (EpsgCoordinateSystemKind)2, 3930); + return true; + case 21246: + cacheIndex = 5750; + reference = new EpsgCoordinateReferenceRecord(21246, (EpsgCoordinateSystemKind)2, 3931); + return true; + case 21247: + cacheIndex = 5751; + reference = new EpsgCoordinateReferenceRecord(21247, (EpsgCoordinateSystemKind)2, 3932); + return true; + case 21248: + cacheIndex = 5752; + reference = new EpsgCoordinateReferenceRecord(21248, (EpsgCoordinateSystemKind)2, 3933); + return true; + case 21249: + cacheIndex = 5753; + reference = new EpsgCoordinateReferenceRecord(21249, (EpsgCoordinateSystemKind)2, 3934); + return true; + case 21250: + cacheIndex = 5754; + reference = new EpsgCoordinateReferenceRecord(21250, (EpsgCoordinateSystemKind)2, 3935); + return true; + case 21251: + cacheIndex = 5755; + reference = new EpsgCoordinateReferenceRecord(21251, (EpsgCoordinateSystemKind)2, 3936); + return true; + case 21252: + cacheIndex = 5756; + reference = new EpsgCoordinateReferenceRecord(21252, (EpsgCoordinateSystemKind)2, 3937); + return true; + case 21253: + cacheIndex = 5757; + reference = new EpsgCoordinateReferenceRecord(21253, (EpsgCoordinateSystemKind)2, 3938); + return true; + case 21254: + cacheIndex = 5758; + reference = new EpsgCoordinateReferenceRecord(21254, (EpsgCoordinateSystemKind)2, 3939); + return true; + case 21255: + cacheIndex = 5759; + reference = new EpsgCoordinateReferenceRecord(21255, (EpsgCoordinateSystemKind)2, 3940); + return true; + case 21256: + cacheIndex = 5760; + reference = new EpsgCoordinateReferenceRecord(21256, (EpsgCoordinateSystemKind)2, 3941); + return true; + case 21257: + cacheIndex = 5761; + reference = new EpsgCoordinateReferenceRecord(21257, (EpsgCoordinateSystemKind)2, 3942); + return true; + case 21258: + cacheIndex = 5762; + reference = new EpsgCoordinateReferenceRecord(21258, (EpsgCoordinateSystemKind)2, 3943); + return true; + case 21259: + cacheIndex = 5763; + reference = new EpsgCoordinateReferenceRecord(21259, (EpsgCoordinateSystemKind)2, 3944); + return true; + case 21260: + cacheIndex = 5764; + reference = new EpsgCoordinateReferenceRecord(21260, (EpsgCoordinateSystemKind)2, 3945); + return true; + case 21261: + cacheIndex = 5765; + reference = new EpsgCoordinateReferenceRecord(21261, (EpsgCoordinateSystemKind)2, 3946); + return true; + case 21262: + cacheIndex = 5766; + reference = new EpsgCoordinateReferenceRecord(21262, (EpsgCoordinateSystemKind)2, 3947); + return true; + case 21263: + cacheIndex = 5767; + reference = new EpsgCoordinateReferenceRecord(21263, (EpsgCoordinateSystemKind)2, 3948); + return true; + case 21264: + cacheIndex = 5768; + reference = new EpsgCoordinateReferenceRecord(21264, (EpsgCoordinateSystemKind)2, 3949); + return true; + case 21291: + cacheIndex = 5769; + reference = new EpsgCoordinateReferenceRecord(21291, (EpsgCoordinateSystemKind)2, 3950); + return true; + case 21292: + cacheIndex = 5770; + reference = new EpsgCoordinateReferenceRecord(21292, (EpsgCoordinateSystemKind)2, 3951); + return true; + case 21307: + cacheIndex = 5771; + reference = new EpsgCoordinateReferenceRecord(21307, (EpsgCoordinateSystemKind)2, 3952); + return true; + case 21308: + cacheIndex = 5772; + reference = new EpsgCoordinateReferenceRecord(21308, (EpsgCoordinateSystemKind)2, 3953); + return true; + case 21309: + cacheIndex = 5773; + reference = new EpsgCoordinateReferenceRecord(21309, (EpsgCoordinateSystemKind)2, 3954); + return true; + case 21310: + cacheIndex = 5774; + reference = new EpsgCoordinateReferenceRecord(21310, (EpsgCoordinateSystemKind)2, 3955); + return true; + case 21311: + cacheIndex = 5775; + reference = new EpsgCoordinateReferenceRecord(21311, (EpsgCoordinateSystemKind)2, 3956); + return true; + case 21312: + cacheIndex = 5776; + reference = new EpsgCoordinateReferenceRecord(21312, (EpsgCoordinateSystemKind)2, 3957); + return true; + case 21313: + cacheIndex = 5777; + reference = new EpsgCoordinateReferenceRecord(21313, (EpsgCoordinateSystemKind)2, 3958); + return true; + case 21314: + cacheIndex = 5778; + reference = new EpsgCoordinateReferenceRecord(21314, (EpsgCoordinateSystemKind)2, 3959); + return true; + case 21315: + cacheIndex = 5779; + reference = new EpsgCoordinateReferenceRecord(21315, (EpsgCoordinateSystemKind)2, 3960); + return true; + case 21316: + cacheIndex = 5780; + reference = new EpsgCoordinateReferenceRecord(21316, (EpsgCoordinateSystemKind)2, 3961); + return true; + case 21317: + cacheIndex = 5781; + reference = new EpsgCoordinateReferenceRecord(21317, (EpsgCoordinateSystemKind)2, 3962); + return true; + case 21318: + cacheIndex = 5782; + reference = new EpsgCoordinateReferenceRecord(21318, (EpsgCoordinateSystemKind)2, 3963); + return true; + case 21319: + cacheIndex = 5783; + reference = new EpsgCoordinateReferenceRecord(21319, (EpsgCoordinateSystemKind)2, 3964); + return true; + case 21320: + cacheIndex = 5784; + reference = new EpsgCoordinateReferenceRecord(21320, (EpsgCoordinateSystemKind)2, 3965); + return true; + case 21321: + cacheIndex = 5785; + reference = new EpsgCoordinateReferenceRecord(21321, (EpsgCoordinateSystemKind)2, 3966); + return true; + case 21322: + cacheIndex = 5786; + reference = new EpsgCoordinateReferenceRecord(21322, (EpsgCoordinateSystemKind)2, 3967); + return true; + case 21323: + cacheIndex = 5787; + reference = new EpsgCoordinateReferenceRecord(21323, (EpsgCoordinateSystemKind)2, 3968); + return true; + case 21324: + cacheIndex = 5788; + reference = new EpsgCoordinateReferenceRecord(21324, (EpsgCoordinateSystemKind)2, 3969); + return true; + case 21325: + cacheIndex = 5789; + reference = new EpsgCoordinateReferenceRecord(21325, (EpsgCoordinateSystemKind)2, 3970); + return true; + case 21326: + cacheIndex = 5790; + reference = new EpsgCoordinateReferenceRecord(21326, (EpsgCoordinateSystemKind)2, 3971); + return true; + case 21327: + cacheIndex = 5791; + reference = new EpsgCoordinateReferenceRecord(21327, (EpsgCoordinateSystemKind)2, 3972); + return true; + case 21328: + cacheIndex = 5792; + reference = new EpsgCoordinateReferenceRecord(21328, (EpsgCoordinateSystemKind)2, 3973); + return true; + case 21329: + cacheIndex = 5793; + reference = new EpsgCoordinateReferenceRecord(21329, (EpsgCoordinateSystemKind)2, 3974); + return true; + case 21330: + cacheIndex = 5794; + reference = new EpsgCoordinateReferenceRecord(21330, (EpsgCoordinateSystemKind)2, 3975); + return true; + case 21331: + cacheIndex = 5795; + reference = new EpsgCoordinateReferenceRecord(21331, (EpsgCoordinateSystemKind)2, 3976); + return true; + case 21332: + cacheIndex = 5796; + reference = new EpsgCoordinateReferenceRecord(21332, (EpsgCoordinateSystemKind)2, 3977); + return true; + case 21333: + cacheIndex = 5797; + reference = new EpsgCoordinateReferenceRecord(21333, (EpsgCoordinateSystemKind)2, 3978); + return true; + case 21334: + cacheIndex = 5798; + reference = new EpsgCoordinateReferenceRecord(21334, (EpsgCoordinateSystemKind)2, 3979); + return true; + case 21335: + cacheIndex = 5799; + reference = new EpsgCoordinateReferenceRecord(21335, (EpsgCoordinateSystemKind)2, 3980); + return true; + case 21336: + cacheIndex = 5800; + reference = new EpsgCoordinateReferenceRecord(21336, (EpsgCoordinateSystemKind)2, 3981); + return true; + case 21337: + cacheIndex = 5801; + reference = new EpsgCoordinateReferenceRecord(21337, (EpsgCoordinateSystemKind)2, 3982); + return true; + case 21338: + cacheIndex = 5802; + reference = new EpsgCoordinateReferenceRecord(21338, (EpsgCoordinateSystemKind)2, 3983); + return true; + case 21339: + cacheIndex = 5803; + reference = new EpsgCoordinateReferenceRecord(21339, (EpsgCoordinateSystemKind)2, 3984); + return true; + case 21340: + cacheIndex = 5804; + reference = new EpsgCoordinateReferenceRecord(21340, (EpsgCoordinateSystemKind)2, 3985); + return true; + case 21341: + cacheIndex = 5805; + reference = new EpsgCoordinateReferenceRecord(21341, (EpsgCoordinateSystemKind)2, 3986); + return true; + case 21342: + cacheIndex = 5806; + reference = new EpsgCoordinateReferenceRecord(21342, (EpsgCoordinateSystemKind)2, 3987); + return true; + case 21343: + cacheIndex = 5807; + reference = new EpsgCoordinateReferenceRecord(21343, (EpsgCoordinateSystemKind)2, 3988); + return true; + case 21344: + cacheIndex = 5808; + reference = new EpsgCoordinateReferenceRecord(21344, (EpsgCoordinateSystemKind)2, 3989); + return true; + case 21345: + cacheIndex = 5809; + reference = new EpsgCoordinateReferenceRecord(21345, (EpsgCoordinateSystemKind)2, 3990); + return true; + case 21346: + cacheIndex = 5810; + reference = new EpsgCoordinateReferenceRecord(21346, (EpsgCoordinateSystemKind)2, 3991); + return true; + case 21347: + cacheIndex = 5811; + reference = new EpsgCoordinateReferenceRecord(21347, (EpsgCoordinateSystemKind)2, 3992); + return true; + case 21348: + cacheIndex = 5812; + reference = new EpsgCoordinateReferenceRecord(21348, (EpsgCoordinateSystemKind)2, 3993); + return true; + case 21349: + cacheIndex = 5813; + reference = new EpsgCoordinateReferenceRecord(21349, (EpsgCoordinateSystemKind)2, 3994); + return true; + case 21350: + cacheIndex = 5814; + reference = new EpsgCoordinateReferenceRecord(21350, (EpsgCoordinateSystemKind)2, 3995); + return true; + case 21351: + cacheIndex = 5815; + reference = new EpsgCoordinateReferenceRecord(21351, (EpsgCoordinateSystemKind)2, 3996); + return true; + case 21352: + cacheIndex = 5816; + reference = new EpsgCoordinateReferenceRecord(21352, (EpsgCoordinateSystemKind)2, 3997); + return true; + case 21353: + cacheIndex = 5817; + reference = new EpsgCoordinateReferenceRecord(21353, (EpsgCoordinateSystemKind)2, 3998); + return true; + case 21354: + cacheIndex = 5818; + reference = new EpsgCoordinateReferenceRecord(21354, (EpsgCoordinateSystemKind)2, 3999); + return true; + case 21355: + cacheIndex = 5819; + reference = new EpsgCoordinateReferenceRecord(21355, (EpsgCoordinateSystemKind)2, 4000); + return true; + case 21356: + cacheIndex = 5820; + reference = new EpsgCoordinateReferenceRecord(21356, (EpsgCoordinateSystemKind)2, 4001); + return true; + case 21357: + cacheIndex = 5821; + reference = new EpsgCoordinateReferenceRecord(21357, (EpsgCoordinateSystemKind)2, 4002); + return true; + case 21358: + cacheIndex = 5822; + reference = new EpsgCoordinateReferenceRecord(21358, (EpsgCoordinateSystemKind)2, 4003); + return true; + case 21359: + cacheIndex = 5823; + reference = new EpsgCoordinateReferenceRecord(21359, (EpsgCoordinateSystemKind)2, 4004); + return true; + case 21360: + cacheIndex = 5824; + reference = new EpsgCoordinateReferenceRecord(21360, (EpsgCoordinateSystemKind)2, 4005); + return true; + case 21361: + cacheIndex = 5825; + reference = new EpsgCoordinateReferenceRecord(21361, (EpsgCoordinateSystemKind)2, 4006); + return true; + case 21362: + cacheIndex = 5826; + reference = new EpsgCoordinateReferenceRecord(21362, (EpsgCoordinateSystemKind)2, 4007); + return true; + case 21363: + cacheIndex = 5827; + reference = new EpsgCoordinateReferenceRecord(21363, (EpsgCoordinateSystemKind)2, 4008); + return true; + case 21364: + cacheIndex = 5828; + reference = new EpsgCoordinateReferenceRecord(21364, (EpsgCoordinateSystemKind)2, 4009); + return true; + case 21413: + cacheIndex = 5829; + reference = new EpsgCoordinateReferenceRecord(21413, (EpsgCoordinateSystemKind)2, 4010); + return true; + case 21414: + cacheIndex = 5830; + reference = new EpsgCoordinateReferenceRecord(21414, (EpsgCoordinateSystemKind)2, 4011); + return true; + case 21415: + cacheIndex = 5831; + reference = new EpsgCoordinateReferenceRecord(21415, (EpsgCoordinateSystemKind)2, 4012); + return true; + case 21416: + cacheIndex = 5832; + reference = new EpsgCoordinateReferenceRecord(21416, (EpsgCoordinateSystemKind)2, 4013); + return true; + case 21417: + cacheIndex = 5833; + reference = new EpsgCoordinateReferenceRecord(21417, (EpsgCoordinateSystemKind)2, 4014); + return true; + case 21418: + cacheIndex = 5834; + reference = new EpsgCoordinateReferenceRecord(21418, (EpsgCoordinateSystemKind)2, 4015); + return true; + case 21419: + cacheIndex = 5835; + reference = new EpsgCoordinateReferenceRecord(21419, (EpsgCoordinateSystemKind)2, 4016); + return true; + case 21420: + cacheIndex = 5836; + reference = new EpsgCoordinateReferenceRecord(21420, (EpsgCoordinateSystemKind)2, 4017); + return true; + case 21421: + cacheIndex = 5837; + reference = new EpsgCoordinateReferenceRecord(21421, (EpsgCoordinateSystemKind)2, 4018); + return true; + case 21422: + cacheIndex = 5838; + reference = new EpsgCoordinateReferenceRecord(21422, (EpsgCoordinateSystemKind)2, 4019); + return true; + case 21423: + cacheIndex = 5839; + reference = new EpsgCoordinateReferenceRecord(21423, (EpsgCoordinateSystemKind)2, 4020); + return true; + case 21453: + cacheIndex = 5840; + reference = new EpsgCoordinateReferenceRecord(21453, (EpsgCoordinateSystemKind)2, 4021); + return true; + case 21454: + cacheIndex = 5841; + reference = new EpsgCoordinateReferenceRecord(21454, (EpsgCoordinateSystemKind)2, 4022); + return true; + case 21455: + cacheIndex = 5842; + reference = new EpsgCoordinateReferenceRecord(21455, (EpsgCoordinateSystemKind)2, 4023); + return true; + case 21456: + cacheIndex = 5843; + reference = new EpsgCoordinateReferenceRecord(21456, (EpsgCoordinateSystemKind)2, 4024); + return true; + case 21457: + cacheIndex = 5844; + reference = new EpsgCoordinateReferenceRecord(21457, (EpsgCoordinateSystemKind)2, 4025); + return true; + case 21458: + cacheIndex = 5845; + reference = new EpsgCoordinateReferenceRecord(21458, (EpsgCoordinateSystemKind)2, 4026); + return true; + case 21459: + cacheIndex = 5846; + reference = new EpsgCoordinateReferenceRecord(21459, (EpsgCoordinateSystemKind)2, 4027); + return true; + case 21460: + cacheIndex = 5847; + reference = new EpsgCoordinateReferenceRecord(21460, (EpsgCoordinateSystemKind)2, 4028); + return true; + case 21461: + cacheIndex = 5848; + reference = new EpsgCoordinateReferenceRecord(21461, (EpsgCoordinateSystemKind)2, 4029); + return true; + case 21462: + cacheIndex = 5849; + reference = new EpsgCoordinateReferenceRecord(21462, (EpsgCoordinateSystemKind)2, 4030); + return true; + case 21463: + cacheIndex = 5850; + reference = new EpsgCoordinateReferenceRecord(21463, (EpsgCoordinateSystemKind)2, 4031); + return true; + case 21500: + cacheIndex = 5851; + reference = new EpsgCoordinateReferenceRecord(21500, (EpsgCoordinateSystemKind)2, 4032); + return true; + case 21780: + cacheIndex = 5852; + reference = new EpsgCoordinateReferenceRecord(21780, (EpsgCoordinateSystemKind)2, 4033); + return true; + case 21781: + cacheIndex = 5853; + reference = new EpsgCoordinateReferenceRecord(21781, (EpsgCoordinateSystemKind)2, 4034); + return true; + case 21782: + cacheIndex = 5854; + reference = new EpsgCoordinateReferenceRecord(21782, (EpsgCoordinateSystemKind)2, 4035); + return true; + case 21818: + cacheIndex = 5855; + reference = new EpsgCoordinateReferenceRecord(21818, (EpsgCoordinateSystemKind)2, 4036); + return true; + case 21896: + cacheIndex = 5856; + reference = new EpsgCoordinateReferenceRecord(21896, (EpsgCoordinateSystemKind)2, 4037); + return true; + case 21897: + cacheIndex = 5857; + reference = new EpsgCoordinateReferenceRecord(21897, (EpsgCoordinateSystemKind)2, 4038); + return true; + case 21898: + cacheIndex = 5858; + reference = new EpsgCoordinateReferenceRecord(21898, (EpsgCoordinateSystemKind)2, 4039); + return true; + case 21899: + cacheIndex = 5859; + reference = new EpsgCoordinateReferenceRecord(21899, (EpsgCoordinateSystemKind)2, 4040); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket22(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 22032: + cacheIndex = 5860; + reference = new EpsgCoordinateReferenceRecord(22032, (EpsgCoordinateSystemKind)2, 4041); + return true; + case 22033: + cacheIndex = 5861; + reference = new EpsgCoordinateReferenceRecord(22033, (EpsgCoordinateSystemKind)2, 4042); + return true; + case 22091: + cacheIndex = 5862; + reference = new EpsgCoordinateReferenceRecord(22091, (EpsgCoordinateSystemKind)2, 4043); + return true; + case 22092: + cacheIndex = 5863; + reference = new EpsgCoordinateReferenceRecord(22092, (EpsgCoordinateSystemKind)2, 4044); + return true; + case 22171: + cacheIndex = 5864; + reference = new EpsgCoordinateReferenceRecord(22171, (EpsgCoordinateSystemKind)2, 4045); + return true; + case 22172: + cacheIndex = 5865; + reference = new EpsgCoordinateReferenceRecord(22172, (EpsgCoordinateSystemKind)2, 4046); + return true; + case 22173: + cacheIndex = 5866; + reference = new EpsgCoordinateReferenceRecord(22173, (EpsgCoordinateSystemKind)2, 4047); + return true; + case 22174: + cacheIndex = 5867; + reference = new EpsgCoordinateReferenceRecord(22174, (EpsgCoordinateSystemKind)2, 4048); + return true; + case 22175: + cacheIndex = 5868; + reference = new EpsgCoordinateReferenceRecord(22175, (EpsgCoordinateSystemKind)2, 4049); + return true; + case 22176: + cacheIndex = 5869; + reference = new EpsgCoordinateReferenceRecord(22176, (EpsgCoordinateSystemKind)2, 4050); + return true; + case 22177: + cacheIndex = 5870; + reference = new EpsgCoordinateReferenceRecord(22177, (EpsgCoordinateSystemKind)2, 4051); + return true; + case 22181: + cacheIndex = 5871; + reference = new EpsgCoordinateReferenceRecord(22181, (EpsgCoordinateSystemKind)2, 4052); + return true; + case 22182: + cacheIndex = 5872; + reference = new EpsgCoordinateReferenceRecord(22182, (EpsgCoordinateSystemKind)2, 4053); + return true; + case 22183: + cacheIndex = 5873; + reference = new EpsgCoordinateReferenceRecord(22183, (EpsgCoordinateSystemKind)2, 4054); + return true; + case 22184: + cacheIndex = 5874; + reference = new EpsgCoordinateReferenceRecord(22184, (EpsgCoordinateSystemKind)2, 4055); + return true; + case 22185: + cacheIndex = 5875; + reference = new EpsgCoordinateReferenceRecord(22185, (EpsgCoordinateSystemKind)2, 4056); + return true; + case 22186: + cacheIndex = 5876; + reference = new EpsgCoordinateReferenceRecord(22186, (EpsgCoordinateSystemKind)2, 4057); + return true; + case 22187: + cacheIndex = 5877; + reference = new EpsgCoordinateReferenceRecord(22187, (EpsgCoordinateSystemKind)2, 4058); + return true; + case 22191: + cacheIndex = 5878; + reference = new EpsgCoordinateReferenceRecord(22191, (EpsgCoordinateSystemKind)2, 4059); + return true; + case 22192: + cacheIndex = 5879; + reference = new EpsgCoordinateReferenceRecord(22192, (EpsgCoordinateSystemKind)2, 4060); + return true; + case 22193: + cacheIndex = 5880; + reference = new EpsgCoordinateReferenceRecord(22193, (EpsgCoordinateSystemKind)2, 4061); + return true; + case 22194: + cacheIndex = 5881; + reference = new EpsgCoordinateReferenceRecord(22194, (EpsgCoordinateSystemKind)2, 4062); + return true; + case 22195: + cacheIndex = 5882; + reference = new EpsgCoordinateReferenceRecord(22195, (EpsgCoordinateSystemKind)2, 4063); + return true; + case 22196: + cacheIndex = 5883; + reference = new EpsgCoordinateReferenceRecord(22196, (EpsgCoordinateSystemKind)2, 4064); + return true; + case 22197: + cacheIndex = 5884; + reference = new EpsgCoordinateReferenceRecord(22197, (EpsgCoordinateSystemKind)2, 4065); + return true; + case 22207: + cacheIndex = 5885; + reference = new EpsgCoordinateReferenceRecord(22207, (EpsgCoordinateSystemKind)2, 4066); + return true; + case 22208: + cacheIndex = 5886; + reference = new EpsgCoordinateReferenceRecord(22208, (EpsgCoordinateSystemKind)2, 4067); + return true; + case 22209: + cacheIndex = 5887; + reference = new EpsgCoordinateReferenceRecord(22209, (EpsgCoordinateSystemKind)2, 4068); + return true; + case 22210: + cacheIndex = 5888; + reference = new EpsgCoordinateReferenceRecord(22210, (EpsgCoordinateSystemKind)2, 4069); + return true; + case 22211: + cacheIndex = 5889; + reference = new EpsgCoordinateReferenceRecord(22211, (EpsgCoordinateSystemKind)2, 4070); + return true; + case 22212: + cacheIndex = 5890; + reference = new EpsgCoordinateReferenceRecord(22212, (EpsgCoordinateSystemKind)2, 4071); + return true; + case 22213: + cacheIndex = 5891; + reference = new EpsgCoordinateReferenceRecord(22213, (EpsgCoordinateSystemKind)2, 4072); + return true; + case 22214: + cacheIndex = 5892; + reference = new EpsgCoordinateReferenceRecord(22214, (EpsgCoordinateSystemKind)2, 4073); + return true; + case 22215: + cacheIndex = 5893; + reference = new EpsgCoordinateReferenceRecord(22215, (EpsgCoordinateSystemKind)2, 4074); + return true; + case 22216: + cacheIndex = 5894; + reference = new EpsgCoordinateReferenceRecord(22216, (EpsgCoordinateSystemKind)2, 4075); + return true; + case 22217: + cacheIndex = 5895; + reference = new EpsgCoordinateReferenceRecord(22217, (EpsgCoordinateSystemKind)2, 4076); + return true; + case 22218: + cacheIndex = 5896; + reference = new EpsgCoordinateReferenceRecord(22218, (EpsgCoordinateSystemKind)2, 4077); + return true; + case 22219: + cacheIndex = 5897; + reference = new EpsgCoordinateReferenceRecord(22219, (EpsgCoordinateSystemKind)2, 4078); + return true; + case 22220: + cacheIndex = 5898; + reference = new EpsgCoordinateReferenceRecord(22220, (EpsgCoordinateSystemKind)2, 4079); + return true; + case 22221: + cacheIndex = 5899; + reference = new EpsgCoordinateReferenceRecord(22221, (EpsgCoordinateSystemKind)2, 4080); + return true; + case 22222: + cacheIndex = 5900; + reference = new EpsgCoordinateReferenceRecord(22222, (EpsgCoordinateSystemKind)2, 4081); + return true; + case 22229: + cacheIndex = 5901; + reference = new EpsgCoordinateReferenceRecord(22229, (EpsgCoordinateSystemKind)2, 4082); + return true; + case 22230: + cacheIndex = 5902; + reference = new EpsgCoordinateReferenceRecord(22230, (EpsgCoordinateSystemKind)2, 4083); + return true; + case 22231: + cacheIndex = 5903; + reference = new EpsgCoordinateReferenceRecord(22231, (EpsgCoordinateSystemKind)2, 4084); + return true; + case 22232: + cacheIndex = 5904; + reference = new EpsgCoordinateReferenceRecord(22232, (EpsgCoordinateSystemKind)2, 4085); + return true; + case 22234: + cacheIndex = 5905; + reference = new EpsgCoordinateReferenceRecord(22234, (EpsgCoordinateSystemKind)2, 4086); + return true; + case 22235: + cacheIndex = 5906; + reference = new EpsgCoordinateReferenceRecord(22235, (EpsgCoordinateSystemKind)2, 4087); + return true; + case 22239: + cacheIndex = 5907; + reference = new EpsgCoordinateReferenceRecord(22239, (EpsgCoordinateSystemKind)2, 4088); + return true; + case 22240: + cacheIndex = 5908; + reference = new EpsgCoordinateReferenceRecord(22240, (EpsgCoordinateSystemKind)2, 4089); + return true; + case 22243: + cacheIndex = 5909; + reference = new EpsgCoordinateReferenceRecord(22243, (EpsgCoordinateSystemKind)2, 4090); + return true; + case 22244: + cacheIndex = 5910; + reference = new EpsgCoordinateReferenceRecord(22244, (EpsgCoordinateSystemKind)2, 4091); + return true; + case 22245: + cacheIndex = 5911; + reference = new EpsgCoordinateReferenceRecord(22245, (EpsgCoordinateSystemKind)2, 4092); + return true; + case 22246: + cacheIndex = 5912; + reference = new EpsgCoordinateReferenceRecord(22246, (EpsgCoordinateSystemKind)2, 4093); + return true; + case 22247: + cacheIndex = 5913; + reference = new EpsgCoordinateReferenceRecord(22247, (EpsgCoordinateSystemKind)2, 4094); + return true; + case 22248: + cacheIndex = 5914; + reference = new EpsgCoordinateReferenceRecord(22248, (EpsgCoordinateSystemKind)2, 4095); + return true; + case 22249: + cacheIndex = 5915; + reference = new EpsgCoordinateReferenceRecord(22249, (EpsgCoordinateSystemKind)2, 4096); + return true; + case 22250: + cacheIndex = 5916; + reference = new EpsgCoordinateReferenceRecord(22250, (EpsgCoordinateSystemKind)2, 4097); + return true; + case 22262: + cacheIndex = 5917; + reference = new EpsgCoordinateReferenceRecord(22262, (EpsgCoordinateSystemKind)2, 4098); + return true; + case 22263: + cacheIndex = 5918; + reference = new EpsgCoordinateReferenceRecord(22263, (EpsgCoordinateSystemKind)2, 4099); + return true; + case 22264: + cacheIndex = 5919; + reference = new EpsgCoordinateReferenceRecord(22264, (EpsgCoordinateSystemKind)2, 4100); + return true; + case 22265: + cacheIndex = 5920; + reference = new EpsgCoordinateReferenceRecord(22265, (EpsgCoordinateSystemKind)2, 4101); + return true; + case 22275: + cacheIndex = 5921; + reference = new EpsgCoordinateReferenceRecord(22275, (EpsgCoordinateSystemKind)2, 4102); + return true; + case 22277: + cacheIndex = 5922; + reference = new EpsgCoordinateReferenceRecord(22277, (EpsgCoordinateSystemKind)2, 4103); + return true; + case 22279: + cacheIndex = 5923; + reference = new EpsgCoordinateReferenceRecord(22279, (EpsgCoordinateSystemKind)2, 4104); + return true; + case 22281: + cacheIndex = 5924; + reference = new EpsgCoordinateReferenceRecord(22281, (EpsgCoordinateSystemKind)2, 4105); + return true; + case 22283: + cacheIndex = 5925; + reference = new EpsgCoordinateReferenceRecord(22283, (EpsgCoordinateSystemKind)2, 4106); + return true; + case 22285: + cacheIndex = 5926; + reference = new EpsgCoordinateReferenceRecord(22285, (EpsgCoordinateSystemKind)2, 4107); + return true; + case 22287: + cacheIndex = 5927; + reference = new EpsgCoordinateReferenceRecord(22287, (EpsgCoordinateSystemKind)2, 4108); + return true; + case 22289: + cacheIndex = 5928; + reference = new EpsgCoordinateReferenceRecord(22289, (EpsgCoordinateSystemKind)2, 4109); + return true; + case 22291: + cacheIndex = 5929; + reference = new EpsgCoordinateReferenceRecord(22291, (EpsgCoordinateSystemKind)2, 4110); + return true; + case 22293: + cacheIndex = 5930; + reference = new EpsgCoordinateReferenceRecord(22293, (EpsgCoordinateSystemKind)2, 4111); + return true; + case 22300: + cacheIndex = 5931; + reference = new EpsgCoordinateReferenceRecord(22300, (EpsgCoordinateSystemKind)2, 4112); + return true; + case 22307: + cacheIndex = 5932; + reference = new EpsgCoordinateReferenceRecord(22307, (EpsgCoordinateSystemKind)2, 4113); + return true; + case 22308: + cacheIndex = 5933; + reference = new EpsgCoordinateReferenceRecord(22308, (EpsgCoordinateSystemKind)2, 4114); + return true; + case 22309: + cacheIndex = 5934; + reference = new EpsgCoordinateReferenceRecord(22309, (EpsgCoordinateSystemKind)2, 4115); + return true; + case 22310: + cacheIndex = 5935; + reference = new EpsgCoordinateReferenceRecord(22310, (EpsgCoordinateSystemKind)2, 4116); + return true; + case 22311: + cacheIndex = 5936; + reference = new EpsgCoordinateReferenceRecord(22311, (EpsgCoordinateSystemKind)2, 4117); + return true; + case 22312: + cacheIndex = 5937; + reference = new EpsgCoordinateReferenceRecord(22312, (EpsgCoordinateSystemKind)2, 4118); + return true; + case 22313: + cacheIndex = 5938; + reference = new EpsgCoordinateReferenceRecord(22313, (EpsgCoordinateSystemKind)2, 4119); + return true; + case 22314: + cacheIndex = 5939; + reference = new EpsgCoordinateReferenceRecord(22314, (EpsgCoordinateSystemKind)2, 4120); + return true; + case 22315: + cacheIndex = 5940; + reference = new EpsgCoordinateReferenceRecord(22315, (EpsgCoordinateSystemKind)2, 4121); + return true; + case 22316: + cacheIndex = 5941; + reference = new EpsgCoordinateReferenceRecord(22316, (EpsgCoordinateSystemKind)2, 4122); + return true; + case 22317: + cacheIndex = 5942; + reference = new EpsgCoordinateReferenceRecord(22317, (EpsgCoordinateSystemKind)2, 4123); + return true; + case 22318: + cacheIndex = 5943; + reference = new EpsgCoordinateReferenceRecord(22318, (EpsgCoordinateSystemKind)2, 4124); + return true; + case 22319: + cacheIndex = 5944; + reference = new EpsgCoordinateReferenceRecord(22319, (EpsgCoordinateSystemKind)2, 4125); + return true; + case 22320: + cacheIndex = 5945; + reference = new EpsgCoordinateReferenceRecord(22320, (EpsgCoordinateSystemKind)2, 4126); + return true; + case 22321: + cacheIndex = 5946; + reference = new EpsgCoordinateReferenceRecord(22321, (EpsgCoordinateSystemKind)2, 4127); + return true; + case 22322: + cacheIndex = 5947; + reference = new EpsgCoordinateReferenceRecord(22322, (EpsgCoordinateSystemKind)2, 4128); + return true; + case 22332: + cacheIndex = 5948; + reference = new EpsgCoordinateReferenceRecord(22332, (EpsgCoordinateSystemKind)2, 4129); + return true; + case 22337: + cacheIndex = 5949; + reference = new EpsgCoordinateReferenceRecord(22337, (EpsgCoordinateSystemKind)2, 4130); + return true; + case 22338: + cacheIndex = 5950; + reference = new EpsgCoordinateReferenceRecord(22338, (EpsgCoordinateSystemKind)2, 4131); + return true; + case 22348: + cacheIndex = 5951; + reference = new EpsgCoordinateReferenceRecord(22348, (EpsgCoordinateSystemKind)2, 4132); + return true; + case 22349: + cacheIndex = 5952; + reference = new EpsgCoordinateReferenceRecord(22349, (EpsgCoordinateSystemKind)2, 4133); + return true; + case 22350: + cacheIndex = 5953; + reference = new EpsgCoordinateReferenceRecord(22350, (EpsgCoordinateSystemKind)2, 4134); + return true; + case 22351: + cacheIndex = 5954; + reference = new EpsgCoordinateReferenceRecord(22351, (EpsgCoordinateSystemKind)2, 4135); + return true; + case 22352: + cacheIndex = 5955; + reference = new EpsgCoordinateReferenceRecord(22352, (EpsgCoordinateSystemKind)2, 4136); + return true; + case 22353: + cacheIndex = 5956; + reference = new EpsgCoordinateReferenceRecord(22353, (EpsgCoordinateSystemKind)2, 4137); + return true; + case 22354: + cacheIndex = 5957; + reference = new EpsgCoordinateReferenceRecord(22354, (EpsgCoordinateSystemKind)2, 4138); + return true; + case 22355: + cacheIndex = 5958; + reference = new EpsgCoordinateReferenceRecord(22355, (EpsgCoordinateSystemKind)2, 4139); + return true; + case 22356: + cacheIndex = 5959; + reference = new EpsgCoordinateReferenceRecord(22356, (EpsgCoordinateSystemKind)2, 4140); + return true; + case 22357: + cacheIndex = 5960; + reference = new EpsgCoordinateReferenceRecord(22357, (EpsgCoordinateSystemKind)2, 4141); + return true; + case 22391: + cacheIndex = 5961; + reference = new EpsgCoordinateReferenceRecord(22391, (EpsgCoordinateSystemKind)2, 4142); + return true; + case 22392: + cacheIndex = 5962; + reference = new EpsgCoordinateReferenceRecord(22392, (EpsgCoordinateSystemKind)2, 4143); + return true; + case 22407: + cacheIndex = 5963; + reference = new EpsgCoordinateReferenceRecord(22407, (EpsgCoordinateSystemKind)2, 4144); + return true; + case 22408: + cacheIndex = 5964; + reference = new EpsgCoordinateReferenceRecord(22408, (EpsgCoordinateSystemKind)2, 4145); + return true; + case 22409: + cacheIndex = 5965; + reference = new EpsgCoordinateReferenceRecord(22409, (EpsgCoordinateSystemKind)2, 4146); + return true; + case 22410: + cacheIndex = 5966; + reference = new EpsgCoordinateReferenceRecord(22410, (EpsgCoordinateSystemKind)2, 4147); + return true; + case 22411: + cacheIndex = 5967; + reference = new EpsgCoordinateReferenceRecord(22411, (EpsgCoordinateSystemKind)2, 4148); + return true; + case 22412: + cacheIndex = 5968; + reference = new EpsgCoordinateReferenceRecord(22412, (EpsgCoordinateSystemKind)2, 4149); + return true; + case 22413: + cacheIndex = 5969; + reference = new EpsgCoordinateReferenceRecord(22413, (EpsgCoordinateSystemKind)2, 4150); + return true; + case 22414: + cacheIndex = 5970; + reference = new EpsgCoordinateReferenceRecord(22414, (EpsgCoordinateSystemKind)2, 4151); + return true; + case 22415: + cacheIndex = 5971; + reference = new EpsgCoordinateReferenceRecord(22415, (EpsgCoordinateSystemKind)2, 4152); + return true; + case 22416: + cacheIndex = 5972; + reference = new EpsgCoordinateReferenceRecord(22416, (EpsgCoordinateSystemKind)2, 4153); + return true; + case 22417: + cacheIndex = 5973; + reference = new EpsgCoordinateReferenceRecord(22417, (EpsgCoordinateSystemKind)2, 4154); + return true; + case 22418: + cacheIndex = 5974; + reference = new EpsgCoordinateReferenceRecord(22418, (EpsgCoordinateSystemKind)2, 4155); + return true; + case 22419: + cacheIndex = 5975; + reference = new EpsgCoordinateReferenceRecord(22419, (EpsgCoordinateSystemKind)2, 4156); + return true; + case 22420: + cacheIndex = 5976; + reference = new EpsgCoordinateReferenceRecord(22420, (EpsgCoordinateSystemKind)2, 4157); + return true; + case 22421: + cacheIndex = 5977; + reference = new EpsgCoordinateReferenceRecord(22421, (EpsgCoordinateSystemKind)2, 4158); + return true; + case 22422: + cacheIndex = 5978; + reference = new EpsgCoordinateReferenceRecord(22422, (EpsgCoordinateSystemKind)2, 4159); + return true; + case 22462: + cacheIndex = 5979; + reference = new EpsgCoordinateReferenceRecord(22462, (EpsgCoordinateSystemKind)2, 4160); + return true; + case 22463: + cacheIndex = 5980; + reference = new EpsgCoordinateReferenceRecord(22463, (EpsgCoordinateSystemKind)2, 4161); + return true; + case 22464: + cacheIndex = 5981; + reference = new EpsgCoordinateReferenceRecord(22464, (EpsgCoordinateSystemKind)2, 4162); + return true; + case 22465: + cacheIndex = 5982; + reference = new EpsgCoordinateReferenceRecord(22465, (EpsgCoordinateSystemKind)2, 4163); + return true; + case 22521: + cacheIndex = 5983; + reference = new EpsgCoordinateReferenceRecord(22521, (EpsgCoordinateSystemKind)2, 4164); + return true; + case 22522: + cacheIndex = 5984; + reference = new EpsgCoordinateReferenceRecord(22522, (EpsgCoordinateSystemKind)2, 4165); + return true; + case 22523: + cacheIndex = 5985; + reference = new EpsgCoordinateReferenceRecord(22523, (EpsgCoordinateSystemKind)2, 4166); + return true; + case 22524: + cacheIndex = 5986; + reference = new EpsgCoordinateReferenceRecord(22524, (EpsgCoordinateSystemKind)2, 4167); + return true; + case 22525: + cacheIndex = 5987; + reference = new EpsgCoordinateReferenceRecord(22525, (EpsgCoordinateSystemKind)2, 4168); + return true; + case 22607: + cacheIndex = 5988; + reference = new EpsgCoordinateReferenceRecord(22607, (EpsgCoordinateSystemKind)2, 4169); + return true; + case 22608: + cacheIndex = 5989; + reference = new EpsgCoordinateReferenceRecord(22608, (EpsgCoordinateSystemKind)2, 4170); + return true; + case 22609: + cacheIndex = 5990; + reference = new EpsgCoordinateReferenceRecord(22609, (EpsgCoordinateSystemKind)2, 4171); + return true; + case 22610: + cacheIndex = 5991; + reference = new EpsgCoordinateReferenceRecord(22610, (EpsgCoordinateSystemKind)2, 4172); + return true; + case 22611: + cacheIndex = 5992; + reference = new EpsgCoordinateReferenceRecord(22611, (EpsgCoordinateSystemKind)2, 4173); + return true; + case 22612: + cacheIndex = 5993; + reference = new EpsgCoordinateReferenceRecord(22612, (EpsgCoordinateSystemKind)2, 4174); + return true; + case 22613: + cacheIndex = 5994; + reference = new EpsgCoordinateReferenceRecord(22613, (EpsgCoordinateSystemKind)2, 4175); + return true; + case 22614: + cacheIndex = 5995; + reference = new EpsgCoordinateReferenceRecord(22614, (EpsgCoordinateSystemKind)2, 4176); + return true; + case 22615: + cacheIndex = 5996; + reference = new EpsgCoordinateReferenceRecord(22615, (EpsgCoordinateSystemKind)2, 4177); + return true; + case 22616: + cacheIndex = 5997; + reference = new EpsgCoordinateReferenceRecord(22616, (EpsgCoordinateSystemKind)2, 4178); + return true; + case 22617: + cacheIndex = 5998; + reference = new EpsgCoordinateReferenceRecord(22617, (EpsgCoordinateSystemKind)2, 4179); + return true; + case 22618: + cacheIndex = 5999; + reference = new EpsgCoordinateReferenceRecord(22618, (EpsgCoordinateSystemKind)2, 4180); + return true; + case 22619: + cacheIndex = 6000; + reference = new EpsgCoordinateReferenceRecord(22619, (EpsgCoordinateSystemKind)2, 4181); + return true; + case 22620: + cacheIndex = 6001; + reference = new EpsgCoordinateReferenceRecord(22620, (EpsgCoordinateSystemKind)2, 4182); + return true; + case 22621: + cacheIndex = 6002; + reference = new EpsgCoordinateReferenceRecord(22621, (EpsgCoordinateSystemKind)2, 4183); + return true; + case 22622: + cacheIndex = 6003; + reference = new EpsgCoordinateReferenceRecord(22622, (EpsgCoordinateSystemKind)2, 4184); + return true; + case 22639: + cacheIndex = 6004; + reference = new EpsgCoordinateReferenceRecord(22639, (EpsgCoordinateSystemKind)2, 4185); + return true; + case 22641: + cacheIndex = 6005; + reference = new EpsgCoordinateReferenceRecord(22641, (EpsgCoordinateSystemKind)2, 4186); + return true; + case 22642: + cacheIndex = 6006; + reference = new EpsgCoordinateReferenceRecord(22642, (EpsgCoordinateSystemKind)2, 4187); + return true; + case 22643: + cacheIndex = 6007; + reference = new EpsgCoordinateReferenceRecord(22643, (EpsgCoordinateSystemKind)2, 4188); + return true; + case 22644: + cacheIndex = 6008; + reference = new EpsgCoordinateReferenceRecord(22644, (EpsgCoordinateSystemKind)2, 4189); + return true; + case 22645: + cacheIndex = 6009; + reference = new EpsgCoordinateReferenceRecord(22645, (EpsgCoordinateSystemKind)2, 4190); + return true; + case 22646: + cacheIndex = 6010; + reference = new EpsgCoordinateReferenceRecord(22646, (EpsgCoordinateSystemKind)2, 4191); + return true; + case 22648: + cacheIndex = 6011; + reference = new EpsgCoordinateReferenceRecord(22648, (EpsgCoordinateSystemKind)2, 4192); + return true; + case 22649: + cacheIndex = 6012; + reference = new EpsgCoordinateReferenceRecord(22649, (EpsgCoordinateSystemKind)2, 4193); + return true; + case 22650: + cacheIndex = 6013; + reference = new EpsgCoordinateReferenceRecord(22650, (EpsgCoordinateSystemKind)2, 4194); + return true; + case 22651: + cacheIndex = 6014; + reference = new EpsgCoordinateReferenceRecord(22651, (EpsgCoordinateSystemKind)2, 4195); + return true; + case 22652: + cacheIndex = 6015; + reference = new EpsgCoordinateReferenceRecord(22652, (EpsgCoordinateSystemKind)2, 4196); + return true; + case 22653: + cacheIndex = 6016; + reference = new EpsgCoordinateReferenceRecord(22653, (EpsgCoordinateSystemKind)2, 4197); + return true; + case 22654: + cacheIndex = 6017; + reference = new EpsgCoordinateReferenceRecord(22654, (EpsgCoordinateSystemKind)2, 4198); + return true; + case 22655: + cacheIndex = 6018; + reference = new EpsgCoordinateReferenceRecord(22655, (EpsgCoordinateSystemKind)2, 4199); + return true; + case 22656: + cacheIndex = 6019; + reference = new EpsgCoordinateReferenceRecord(22656, (EpsgCoordinateSystemKind)2, 4200); + return true; + case 22657: + cacheIndex = 6020; + reference = new EpsgCoordinateReferenceRecord(22657, (EpsgCoordinateSystemKind)2, 4201); + return true; + case 22700: + cacheIndex = 6021; + reference = new EpsgCoordinateReferenceRecord(22700, (EpsgCoordinateSystemKind)2, 4202); + return true; + case 22707: + cacheIndex = 6022; + reference = new EpsgCoordinateReferenceRecord(22707, (EpsgCoordinateSystemKind)2, 4203); + return true; + case 22708: + cacheIndex = 6023; + reference = new EpsgCoordinateReferenceRecord(22708, (EpsgCoordinateSystemKind)2, 4204); + return true; + case 22709: + cacheIndex = 6024; + reference = new EpsgCoordinateReferenceRecord(22709, (EpsgCoordinateSystemKind)2, 4205); + return true; + case 22710: + cacheIndex = 6025; + reference = new EpsgCoordinateReferenceRecord(22710, (EpsgCoordinateSystemKind)2, 4206); + return true; + case 22711: + cacheIndex = 6026; + reference = new EpsgCoordinateReferenceRecord(22711, (EpsgCoordinateSystemKind)2, 4207); + return true; + case 22712: + cacheIndex = 6027; + reference = new EpsgCoordinateReferenceRecord(22712, (EpsgCoordinateSystemKind)2, 4208); + return true; + case 22713: + cacheIndex = 6028; + reference = new EpsgCoordinateReferenceRecord(22713, (EpsgCoordinateSystemKind)2, 4209); + return true; + case 22714: + cacheIndex = 6029; + reference = new EpsgCoordinateReferenceRecord(22714, (EpsgCoordinateSystemKind)2, 4210); + return true; + case 22715: + cacheIndex = 6030; + reference = new EpsgCoordinateReferenceRecord(22715, (EpsgCoordinateSystemKind)2, 4211); + return true; + case 22716: + cacheIndex = 6031; + reference = new EpsgCoordinateReferenceRecord(22716, (EpsgCoordinateSystemKind)2, 4212); + return true; + case 22717: + cacheIndex = 6032; + reference = new EpsgCoordinateReferenceRecord(22717, (EpsgCoordinateSystemKind)2, 4213); + return true; + case 22718: + cacheIndex = 6033; + reference = new EpsgCoordinateReferenceRecord(22718, (EpsgCoordinateSystemKind)2, 4214); + return true; + case 22719: + cacheIndex = 6034; + reference = new EpsgCoordinateReferenceRecord(22719, (EpsgCoordinateSystemKind)2, 4215); + return true; + case 22720: + cacheIndex = 6035; + reference = new EpsgCoordinateReferenceRecord(22720, (EpsgCoordinateSystemKind)2, 4216); + return true; + case 22721: + cacheIndex = 6036; + reference = new EpsgCoordinateReferenceRecord(22721, (EpsgCoordinateSystemKind)2, 4217); + return true; + case 22722: + cacheIndex = 6037; + reference = new EpsgCoordinateReferenceRecord(22722, (EpsgCoordinateSystemKind)2, 4218); + return true; + case 22739: + cacheIndex = 6038; + reference = new EpsgCoordinateReferenceRecord(22739, (EpsgCoordinateSystemKind)2, 4219); + return true; + case 22762: + cacheIndex = 6039; + reference = new EpsgCoordinateReferenceRecord(22762, (EpsgCoordinateSystemKind)2, 4220); + return true; + case 22763: + cacheIndex = 6040; + reference = new EpsgCoordinateReferenceRecord(22763, (EpsgCoordinateSystemKind)2, 4221); + return true; + case 22764: + cacheIndex = 6041; + reference = new EpsgCoordinateReferenceRecord(22764, (EpsgCoordinateSystemKind)2, 4222); + return true; + case 22765: + cacheIndex = 6042; + reference = new EpsgCoordinateReferenceRecord(22765, (EpsgCoordinateSystemKind)2, 4223); + return true; + case 22770: + cacheIndex = 6043; + reference = new EpsgCoordinateReferenceRecord(22770, (EpsgCoordinateSystemKind)2, 4224); + return true; + case 22780: + cacheIndex = 6044; + reference = new EpsgCoordinateReferenceRecord(22780, (EpsgCoordinateSystemKind)2, 4225); + return true; + case 22807: + cacheIndex = 6045; + reference = new EpsgCoordinateReferenceRecord(22807, (EpsgCoordinateSystemKind)2, 4226); + return true; + case 22808: + cacheIndex = 6046; + reference = new EpsgCoordinateReferenceRecord(22808, (EpsgCoordinateSystemKind)2, 4227); + return true; + case 22809: + cacheIndex = 6047; + reference = new EpsgCoordinateReferenceRecord(22809, (EpsgCoordinateSystemKind)2, 4228); + return true; + case 22810: + cacheIndex = 6048; + reference = new EpsgCoordinateReferenceRecord(22810, (EpsgCoordinateSystemKind)2, 4229); + return true; + case 22811: + cacheIndex = 6049; + reference = new EpsgCoordinateReferenceRecord(22811, (EpsgCoordinateSystemKind)2, 4230); + return true; + case 22812: + cacheIndex = 6050; + reference = new EpsgCoordinateReferenceRecord(22812, (EpsgCoordinateSystemKind)2, 4231); + return true; + case 22813: + cacheIndex = 6051; + reference = new EpsgCoordinateReferenceRecord(22813, (EpsgCoordinateSystemKind)2, 4232); + return true; + case 22814: + cacheIndex = 6052; + reference = new EpsgCoordinateReferenceRecord(22814, (EpsgCoordinateSystemKind)2, 4233); + return true; + case 22815: + cacheIndex = 6053; + reference = new EpsgCoordinateReferenceRecord(22815, (EpsgCoordinateSystemKind)2, 4234); + return true; + case 22816: + cacheIndex = 6054; + reference = new EpsgCoordinateReferenceRecord(22816, (EpsgCoordinateSystemKind)2, 4235); + return true; + case 22817: + cacheIndex = 6055; + reference = new EpsgCoordinateReferenceRecord(22817, (EpsgCoordinateSystemKind)2, 4236); + return true; + case 22818: + cacheIndex = 6056; + reference = new EpsgCoordinateReferenceRecord(22818, (EpsgCoordinateSystemKind)2, 4237); + return true; + case 22819: + cacheIndex = 6057; + reference = new EpsgCoordinateReferenceRecord(22819, (EpsgCoordinateSystemKind)2, 4238); + return true; + case 22820: + cacheIndex = 6058; + reference = new EpsgCoordinateReferenceRecord(22820, (EpsgCoordinateSystemKind)2, 4239); + return true; + case 22821: + cacheIndex = 6059; + reference = new EpsgCoordinateReferenceRecord(22821, (EpsgCoordinateSystemKind)2, 4240); + return true; + case 22822: + cacheIndex = 6060; + reference = new EpsgCoordinateReferenceRecord(22822, (EpsgCoordinateSystemKind)2, 4241); + return true; + case 22991: + cacheIndex = 6061; + reference = new EpsgCoordinateReferenceRecord(22991, (EpsgCoordinateSystemKind)2, 4242); + return true; + case 22992: + cacheIndex = 6062; + reference = new EpsgCoordinateReferenceRecord(22992, (EpsgCoordinateSystemKind)2, 4243); + return true; + case 22993: + cacheIndex = 6063; + reference = new EpsgCoordinateReferenceRecord(22993, (EpsgCoordinateSystemKind)2, 4244); + return true; + case 22994: + cacheIndex = 6064; + reference = new EpsgCoordinateReferenceRecord(22994, (EpsgCoordinateSystemKind)2, 4245); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket23(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 23028: + cacheIndex = 6065; + reference = new EpsgCoordinateReferenceRecord(23028, (EpsgCoordinateSystemKind)2, 4246); + return true; + case 23029: + cacheIndex = 6066; + reference = new EpsgCoordinateReferenceRecord(23029, (EpsgCoordinateSystemKind)2, 4247); + return true; + case 23030: + cacheIndex = 6067; + reference = new EpsgCoordinateReferenceRecord(23030, (EpsgCoordinateSystemKind)2, 4248); + return true; + case 23031: + cacheIndex = 6068; + reference = new EpsgCoordinateReferenceRecord(23031, (EpsgCoordinateSystemKind)2, 4249); + return true; + case 23032: + cacheIndex = 6069; + reference = new EpsgCoordinateReferenceRecord(23032, (EpsgCoordinateSystemKind)2, 4250); + return true; + case 23033: + cacheIndex = 6070; + reference = new EpsgCoordinateReferenceRecord(23033, (EpsgCoordinateSystemKind)2, 4251); + return true; + case 23034: + cacheIndex = 6071; + reference = new EpsgCoordinateReferenceRecord(23034, (EpsgCoordinateSystemKind)2, 4252); + return true; + case 23035: + cacheIndex = 6072; + reference = new EpsgCoordinateReferenceRecord(23035, (EpsgCoordinateSystemKind)2, 4253); + return true; + case 23036: + cacheIndex = 6073; + reference = new EpsgCoordinateReferenceRecord(23036, (EpsgCoordinateSystemKind)2, 4254); + return true; + case 23037: + cacheIndex = 6074; + reference = new EpsgCoordinateReferenceRecord(23037, (EpsgCoordinateSystemKind)2, 4255); + return true; + case 23038: + cacheIndex = 6075; + reference = new EpsgCoordinateReferenceRecord(23038, (EpsgCoordinateSystemKind)2, 4256); + return true; + case 23090: + cacheIndex = 6076; + reference = new EpsgCoordinateReferenceRecord(23090, (EpsgCoordinateSystemKind)2, 4257); + return true; + case 23095: + cacheIndex = 6077; + reference = new EpsgCoordinateReferenceRecord(23095, (EpsgCoordinateSystemKind)2, 4258); + return true; + case 23239: + cacheIndex = 6078; + reference = new EpsgCoordinateReferenceRecord(23239, (EpsgCoordinateSystemKind)2, 4259); + return true; + case 23240: + cacheIndex = 6079; + reference = new EpsgCoordinateReferenceRecord(23240, (EpsgCoordinateSystemKind)2, 4260); + return true; + case 23301: + cacheIndex = 6080; + reference = new EpsgCoordinateReferenceRecord(23301, (EpsgCoordinateSystemKind)2, 4261); + return true; + case 23302: + cacheIndex = 6081; + reference = new EpsgCoordinateReferenceRecord(23302, (EpsgCoordinateSystemKind)2, 4262); + return true; + case 23303: + cacheIndex = 6082; + reference = new EpsgCoordinateReferenceRecord(23303, (EpsgCoordinateSystemKind)2, 4263); + return true; + case 23304: + cacheIndex = 6083; + reference = new EpsgCoordinateReferenceRecord(23304, (EpsgCoordinateSystemKind)2, 4264); + return true; + case 23305: + cacheIndex = 6084; + reference = new EpsgCoordinateReferenceRecord(23305, (EpsgCoordinateSystemKind)2, 4265); + return true; + case 23306: + cacheIndex = 6085; + reference = new EpsgCoordinateReferenceRecord(23306, (EpsgCoordinateSystemKind)2, 4266); + return true; + case 23307: + cacheIndex = 6086; + reference = new EpsgCoordinateReferenceRecord(23307, (EpsgCoordinateSystemKind)2, 4267); + return true; + case 23308: + cacheIndex = 6087; + reference = new EpsgCoordinateReferenceRecord(23308, (EpsgCoordinateSystemKind)2, 4268); + return true; + case 23309: + cacheIndex = 6088; + reference = new EpsgCoordinateReferenceRecord(23309, (EpsgCoordinateSystemKind)2, 4269); + return true; + case 23310: + cacheIndex = 6089; + reference = new EpsgCoordinateReferenceRecord(23310, (EpsgCoordinateSystemKind)2, 4270); + return true; + case 23311: + cacheIndex = 6090; + reference = new EpsgCoordinateReferenceRecord(23311, (EpsgCoordinateSystemKind)2, 4271); + return true; + case 23312: + cacheIndex = 6091; + reference = new EpsgCoordinateReferenceRecord(23312, (EpsgCoordinateSystemKind)2, 4272); + return true; + case 23313: + cacheIndex = 6092; + reference = new EpsgCoordinateReferenceRecord(23313, (EpsgCoordinateSystemKind)2, 4273); + return true; + case 23314: + cacheIndex = 6093; + reference = new EpsgCoordinateReferenceRecord(23314, (EpsgCoordinateSystemKind)2, 4274); + return true; + case 23315: + cacheIndex = 6094; + reference = new EpsgCoordinateReferenceRecord(23315, (EpsgCoordinateSystemKind)2, 4275); + return true; + case 23316: + cacheIndex = 6095; + reference = new EpsgCoordinateReferenceRecord(23316, (EpsgCoordinateSystemKind)2, 4276); + return true; + case 23317: + cacheIndex = 6096; + reference = new EpsgCoordinateReferenceRecord(23317, (EpsgCoordinateSystemKind)2, 4277); + return true; + case 23318: + cacheIndex = 6097; + reference = new EpsgCoordinateReferenceRecord(23318, (EpsgCoordinateSystemKind)2, 4278); + return true; + case 23319: + cacheIndex = 6098; + reference = new EpsgCoordinateReferenceRecord(23319, (EpsgCoordinateSystemKind)2, 4279); + return true; + case 23320: + cacheIndex = 6099; + reference = new EpsgCoordinateReferenceRecord(23320, (EpsgCoordinateSystemKind)2, 4280); + return true; + case 23321: + cacheIndex = 6100; + reference = new EpsgCoordinateReferenceRecord(23321, (EpsgCoordinateSystemKind)2, 4281); + return true; + case 23322: + cacheIndex = 6101; + reference = new EpsgCoordinateReferenceRecord(23322, (EpsgCoordinateSystemKind)2, 4282); + return true; + case 23323: + cacheIndex = 6102; + reference = new EpsgCoordinateReferenceRecord(23323, (EpsgCoordinateSystemKind)2, 4283); + return true; + case 23324: + cacheIndex = 6103; + reference = new EpsgCoordinateReferenceRecord(23324, (EpsgCoordinateSystemKind)2, 4284); + return true; + case 23325: + cacheIndex = 6104; + reference = new EpsgCoordinateReferenceRecord(23325, (EpsgCoordinateSystemKind)2, 4285); + return true; + case 23326: + cacheIndex = 6105; + reference = new EpsgCoordinateReferenceRecord(23326, (EpsgCoordinateSystemKind)2, 4286); + return true; + case 23327: + cacheIndex = 6106; + reference = new EpsgCoordinateReferenceRecord(23327, (EpsgCoordinateSystemKind)2, 4287); + return true; + case 23328: + cacheIndex = 6107; + reference = new EpsgCoordinateReferenceRecord(23328, (EpsgCoordinateSystemKind)2, 4288); + return true; + case 23329: + cacheIndex = 6108; + reference = new EpsgCoordinateReferenceRecord(23329, (EpsgCoordinateSystemKind)2, 4289); + return true; + case 23330: + cacheIndex = 6109; + reference = new EpsgCoordinateReferenceRecord(23330, (EpsgCoordinateSystemKind)2, 4290); + return true; + case 23331: + cacheIndex = 6110; + reference = new EpsgCoordinateReferenceRecord(23331, (EpsgCoordinateSystemKind)2, 4291); + return true; + case 23332: + cacheIndex = 6111; + reference = new EpsgCoordinateReferenceRecord(23332, (EpsgCoordinateSystemKind)2, 4292); + return true; + case 23333: + cacheIndex = 6112; + reference = new EpsgCoordinateReferenceRecord(23333, (EpsgCoordinateSystemKind)2, 4293); + return true; + case 23700: + cacheIndex = 6113; + reference = new EpsgCoordinateReferenceRecord(23700, (EpsgCoordinateSystemKind)2, 4294); + return true; + case 23830: + cacheIndex = 6114; + reference = new EpsgCoordinateReferenceRecord(23830, (EpsgCoordinateSystemKind)2, 4295); + return true; + case 23831: + cacheIndex = 6115; + reference = new EpsgCoordinateReferenceRecord(23831, (EpsgCoordinateSystemKind)2, 4296); + return true; + case 23832: + cacheIndex = 6116; + reference = new EpsgCoordinateReferenceRecord(23832, (EpsgCoordinateSystemKind)2, 4297); + return true; + case 23833: + cacheIndex = 6117; + reference = new EpsgCoordinateReferenceRecord(23833, (EpsgCoordinateSystemKind)2, 4298); + return true; + case 23834: + cacheIndex = 6118; + reference = new EpsgCoordinateReferenceRecord(23834, (EpsgCoordinateSystemKind)2, 4299); + return true; + case 23835: + cacheIndex = 6119; + reference = new EpsgCoordinateReferenceRecord(23835, (EpsgCoordinateSystemKind)2, 4300); + return true; + case 23836: + cacheIndex = 6120; + reference = new EpsgCoordinateReferenceRecord(23836, (EpsgCoordinateSystemKind)2, 4301); + return true; + case 23837: + cacheIndex = 6121; + reference = new EpsgCoordinateReferenceRecord(23837, (EpsgCoordinateSystemKind)2, 4302); + return true; + case 23838: + cacheIndex = 6122; + reference = new EpsgCoordinateReferenceRecord(23838, (EpsgCoordinateSystemKind)2, 4303); + return true; + case 23839: + cacheIndex = 6123; + reference = new EpsgCoordinateReferenceRecord(23839, (EpsgCoordinateSystemKind)2, 4304); + return true; + case 23840: + cacheIndex = 6124; + reference = new EpsgCoordinateReferenceRecord(23840, (EpsgCoordinateSystemKind)2, 4305); + return true; + case 23841: + cacheIndex = 6125; + reference = new EpsgCoordinateReferenceRecord(23841, (EpsgCoordinateSystemKind)2, 4306); + return true; + case 23842: + cacheIndex = 6126; + reference = new EpsgCoordinateReferenceRecord(23842, (EpsgCoordinateSystemKind)2, 4307); + return true; + case 23843: + cacheIndex = 6127; + reference = new EpsgCoordinateReferenceRecord(23843, (EpsgCoordinateSystemKind)2, 4308); + return true; + case 23844: + cacheIndex = 6128; + reference = new EpsgCoordinateReferenceRecord(23844, (EpsgCoordinateSystemKind)2, 4309); + return true; + case 23845: + cacheIndex = 6129; + reference = new EpsgCoordinateReferenceRecord(23845, (EpsgCoordinateSystemKind)2, 4310); + return true; + case 23846: + cacheIndex = 6130; + reference = new EpsgCoordinateReferenceRecord(23846, (EpsgCoordinateSystemKind)2, 4311); + return true; + case 23847: + cacheIndex = 6131; + reference = new EpsgCoordinateReferenceRecord(23847, (EpsgCoordinateSystemKind)2, 4312); + return true; + case 23848: + cacheIndex = 6132; + reference = new EpsgCoordinateReferenceRecord(23848, (EpsgCoordinateSystemKind)2, 4313); + return true; + case 23849: + cacheIndex = 6133; + reference = new EpsgCoordinateReferenceRecord(23849, (EpsgCoordinateSystemKind)2, 4314); + return true; + case 23850: + cacheIndex = 6134; + reference = new EpsgCoordinateReferenceRecord(23850, (EpsgCoordinateSystemKind)2, 4315); + return true; + case 23851: + cacheIndex = 6135; + reference = new EpsgCoordinateReferenceRecord(23851, (EpsgCoordinateSystemKind)2, 4316); + return true; + case 23852: + cacheIndex = 6136; + reference = new EpsgCoordinateReferenceRecord(23852, (EpsgCoordinateSystemKind)2, 4317); + return true; + case 23866: + cacheIndex = 6137; + reference = new EpsgCoordinateReferenceRecord(23866, (EpsgCoordinateSystemKind)2, 4318); + return true; + case 23867: + cacheIndex = 6138; + reference = new EpsgCoordinateReferenceRecord(23867, (EpsgCoordinateSystemKind)2, 4319); + return true; + case 23868: + cacheIndex = 6139; + reference = new EpsgCoordinateReferenceRecord(23868, (EpsgCoordinateSystemKind)2, 4320); + return true; + case 23869: + cacheIndex = 6140; + reference = new EpsgCoordinateReferenceRecord(23869, (EpsgCoordinateSystemKind)2, 4321); + return true; + case 23870: + cacheIndex = 6141; + reference = new EpsgCoordinateReferenceRecord(23870, (EpsgCoordinateSystemKind)2, 4322); + return true; + case 23871: + cacheIndex = 6142; + reference = new EpsgCoordinateReferenceRecord(23871, (EpsgCoordinateSystemKind)2, 4323); + return true; + case 23872: + cacheIndex = 6143; + reference = new EpsgCoordinateReferenceRecord(23872, (EpsgCoordinateSystemKind)2, 4324); + return true; + case 23877: + cacheIndex = 6144; + reference = new EpsgCoordinateReferenceRecord(23877, (EpsgCoordinateSystemKind)2, 4325); + return true; + case 23878: + cacheIndex = 6145; + reference = new EpsgCoordinateReferenceRecord(23878, (EpsgCoordinateSystemKind)2, 4326); + return true; + case 23879: + cacheIndex = 6146; + reference = new EpsgCoordinateReferenceRecord(23879, (EpsgCoordinateSystemKind)2, 4327); + return true; + case 23880: + cacheIndex = 6147; + reference = new EpsgCoordinateReferenceRecord(23880, (EpsgCoordinateSystemKind)2, 4328); + return true; + case 23881: + cacheIndex = 6148; + reference = new EpsgCoordinateReferenceRecord(23881, (EpsgCoordinateSystemKind)2, 4329); + return true; + case 23882: + cacheIndex = 6149; + reference = new EpsgCoordinateReferenceRecord(23882, (EpsgCoordinateSystemKind)2, 4330); + return true; + case 23883: + cacheIndex = 6150; + reference = new EpsgCoordinateReferenceRecord(23883, (EpsgCoordinateSystemKind)2, 4331); + return true; + case 23884: + cacheIndex = 6151; + reference = new EpsgCoordinateReferenceRecord(23884, (EpsgCoordinateSystemKind)2, 4332); + return true; + case 23887: + cacheIndex = 6152; + reference = new EpsgCoordinateReferenceRecord(23887, (EpsgCoordinateSystemKind)2, 4333); + return true; + case 23888: + cacheIndex = 6153; + reference = new EpsgCoordinateReferenceRecord(23888, (EpsgCoordinateSystemKind)2, 4334); + return true; + case 23889: + cacheIndex = 6154; + reference = new EpsgCoordinateReferenceRecord(23889, (EpsgCoordinateSystemKind)2, 4335); + return true; + case 23890: + cacheIndex = 6155; + reference = new EpsgCoordinateReferenceRecord(23890, (EpsgCoordinateSystemKind)2, 4336); + return true; + case 23891: + cacheIndex = 6156; + reference = new EpsgCoordinateReferenceRecord(23891, (EpsgCoordinateSystemKind)2, 4337); + return true; + case 23892: + cacheIndex = 6157; + reference = new EpsgCoordinateReferenceRecord(23892, (EpsgCoordinateSystemKind)2, 4338); + return true; + case 23893: + cacheIndex = 6158; + reference = new EpsgCoordinateReferenceRecord(23893, (EpsgCoordinateSystemKind)2, 4339); + return true; + case 23894: + cacheIndex = 6159; + reference = new EpsgCoordinateReferenceRecord(23894, (EpsgCoordinateSystemKind)2, 4340); + return true; + case 23946: + cacheIndex = 6160; + reference = new EpsgCoordinateReferenceRecord(23946, (EpsgCoordinateSystemKind)2, 4341); + return true; + case 23947: + cacheIndex = 6161; + reference = new EpsgCoordinateReferenceRecord(23947, (EpsgCoordinateSystemKind)2, 4342); + return true; + case 23948: + cacheIndex = 6162; + reference = new EpsgCoordinateReferenceRecord(23948, (EpsgCoordinateSystemKind)2, 4343); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket24(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 24047: + cacheIndex = 6163; + reference = new EpsgCoordinateReferenceRecord(24047, (EpsgCoordinateSystemKind)2, 4344); + return true; + case 24048: + cacheIndex = 6164; + reference = new EpsgCoordinateReferenceRecord(24048, (EpsgCoordinateSystemKind)2, 4345); + return true; + case 24100: + cacheIndex = 6165; + reference = new EpsgCoordinateReferenceRecord(24100, (EpsgCoordinateSystemKind)2, 4346); + return true; + case 24200: + cacheIndex = 6166; + reference = new EpsgCoordinateReferenceRecord(24200, (EpsgCoordinateSystemKind)2, 4347); + return true; + case 24305: + cacheIndex = 6167; + reference = new EpsgCoordinateReferenceRecord(24305, (EpsgCoordinateSystemKind)2, 4348); + return true; + case 24306: + cacheIndex = 6168; + reference = new EpsgCoordinateReferenceRecord(24306, (EpsgCoordinateSystemKind)2, 4349); + return true; + case 24311: + cacheIndex = 6169; + reference = new EpsgCoordinateReferenceRecord(24311, (EpsgCoordinateSystemKind)2, 4350); + return true; + case 24312: + cacheIndex = 6170; + reference = new EpsgCoordinateReferenceRecord(24312, (EpsgCoordinateSystemKind)2, 4351); + return true; + case 24313: + cacheIndex = 6171; + reference = new EpsgCoordinateReferenceRecord(24313, (EpsgCoordinateSystemKind)2, 4352); + return true; + case 24342: + cacheIndex = 6172; + reference = new EpsgCoordinateReferenceRecord(24342, (EpsgCoordinateSystemKind)2, 4353); + return true; + case 24343: + cacheIndex = 6173; + reference = new EpsgCoordinateReferenceRecord(24343, (EpsgCoordinateSystemKind)2, 4354); + return true; + case 24344: + cacheIndex = 6174; + reference = new EpsgCoordinateReferenceRecord(24344, (EpsgCoordinateSystemKind)2, 4355); + return true; + case 24345: + cacheIndex = 6175; + reference = new EpsgCoordinateReferenceRecord(24345, (EpsgCoordinateSystemKind)2, 4356); + return true; + case 24346: + cacheIndex = 6176; + reference = new EpsgCoordinateReferenceRecord(24346, (EpsgCoordinateSystemKind)2, 4357); + return true; + case 24347: + cacheIndex = 6177; + reference = new EpsgCoordinateReferenceRecord(24347, (EpsgCoordinateSystemKind)2, 4358); + return true; + case 24370: + cacheIndex = 6178; + reference = new EpsgCoordinateReferenceRecord(24370, (EpsgCoordinateSystemKind)2, 4359); + return true; + case 24371: + cacheIndex = 6179; + reference = new EpsgCoordinateReferenceRecord(24371, (EpsgCoordinateSystemKind)2, 4360); + return true; + case 24372: + cacheIndex = 6180; + reference = new EpsgCoordinateReferenceRecord(24372, (EpsgCoordinateSystemKind)2, 4361); + return true; + case 24373: + cacheIndex = 6181; + reference = new EpsgCoordinateReferenceRecord(24373, (EpsgCoordinateSystemKind)2, 4362); + return true; + case 24374: + cacheIndex = 6182; + reference = new EpsgCoordinateReferenceRecord(24374, (EpsgCoordinateSystemKind)2, 4363); + return true; + case 24375: + cacheIndex = 6183; + reference = new EpsgCoordinateReferenceRecord(24375, (EpsgCoordinateSystemKind)2, 4364); + return true; + case 24376: + cacheIndex = 6184; + reference = new EpsgCoordinateReferenceRecord(24376, (EpsgCoordinateSystemKind)2, 4365); + return true; + case 24377: + cacheIndex = 6185; + reference = new EpsgCoordinateReferenceRecord(24377, (EpsgCoordinateSystemKind)2, 4366); + return true; + case 24378: + cacheIndex = 6186; + reference = new EpsgCoordinateReferenceRecord(24378, (EpsgCoordinateSystemKind)2, 4367); + return true; + case 24379: + cacheIndex = 6187; + reference = new EpsgCoordinateReferenceRecord(24379, (EpsgCoordinateSystemKind)2, 4368); + return true; + case 24380: + cacheIndex = 6188; + reference = new EpsgCoordinateReferenceRecord(24380, (EpsgCoordinateSystemKind)2, 4369); + return true; + case 24381: + cacheIndex = 6189; + reference = new EpsgCoordinateReferenceRecord(24381, (EpsgCoordinateSystemKind)2, 4370); + return true; + case 24382: + cacheIndex = 6190; + reference = new EpsgCoordinateReferenceRecord(24382, (EpsgCoordinateSystemKind)2, 4371); + return true; + case 24383: + cacheIndex = 6191; + reference = new EpsgCoordinateReferenceRecord(24383, (EpsgCoordinateSystemKind)2, 4372); + return true; + case 24500: + cacheIndex = 6192; + reference = new EpsgCoordinateReferenceRecord(24500, (EpsgCoordinateSystemKind)2, 4373); + return true; + case 24547: + cacheIndex = 6193; + reference = new EpsgCoordinateReferenceRecord(24547, (EpsgCoordinateSystemKind)2, 4374); + return true; + case 24548: + cacheIndex = 6194; + reference = new EpsgCoordinateReferenceRecord(24548, (EpsgCoordinateSystemKind)2, 4375); + return true; + case 24600: + cacheIndex = 6195; + reference = new EpsgCoordinateReferenceRecord(24600, (EpsgCoordinateSystemKind)2, 4376); + return true; + case 24718: + cacheIndex = 6196; + reference = new EpsgCoordinateReferenceRecord(24718, (EpsgCoordinateSystemKind)2, 4377); + return true; + case 24719: + cacheIndex = 6197; + reference = new EpsgCoordinateReferenceRecord(24719, (EpsgCoordinateSystemKind)2, 4378); + return true; + case 24720: + cacheIndex = 6198; + reference = new EpsgCoordinateReferenceRecord(24720, (EpsgCoordinateSystemKind)2, 4379); + return true; + case 24817: + cacheIndex = 6199; + reference = new EpsgCoordinateReferenceRecord(24817, (EpsgCoordinateSystemKind)2, 4380); + return true; + case 24818: + cacheIndex = 6200; + reference = new EpsgCoordinateReferenceRecord(24818, (EpsgCoordinateSystemKind)2, 4381); + return true; + case 24819: + cacheIndex = 6201; + reference = new EpsgCoordinateReferenceRecord(24819, (EpsgCoordinateSystemKind)2, 4382); + return true; + case 24820: + cacheIndex = 6202; + reference = new EpsgCoordinateReferenceRecord(24820, (EpsgCoordinateSystemKind)2, 4383); + return true; + case 24821: + cacheIndex = 6203; + reference = new EpsgCoordinateReferenceRecord(24821, (EpsgCoordinateSystemKind)2, 4384); + return true; + case 24877: + cacheIndex = 6204; + reference = new EpsgCoordinateReferenceRecord(24877, (EpsgCoordinateSystemKind)2, 4385); + return true; + case 24878: + cacheIndex = 6205; + reference = new EpsgCoordinateReferenceRecord(24878, (EpsgCoordinateSystemKind)2, 4386); + return true; + case 24879: + cacheIndex = 6206; + reference = new EpsgCoordinateReferenceRecord(24879, (EpsgCoordinateSystemKind)2, 4387); + return true; + case 24880: + cacheIndex = 6207; + reference = new EpsgCoordinateReferenceRecord(24880, (EpsgCoordinateSystemKind)2, 4388); + return true; + case 24881: + cacheIndex = 6208; + reference = new EpsgCoordinateReferenceRecord(24881, (EpsgCoordinateSystemKind)2, 4389); + return true; + case 24882: + cacheIndex = 6209; + reference = new EpsgCoordinateReferenceRecord(24882, (EpsgCoordinateSystemKind)2, 4390); + return true; + case 24891: + cacheIndex = 6210; + reference = new EpsgCoordinateReferenceRecord(24891, (EpsgCoordinateSystemKind)2, 4391); + return true; + case 24892: + cacheIndex = 6211; + reference = new EpsgCoordinateReferenceRecord(24892, (EpsgCoordinateSystemKind)2, 4392); + return true; + case 24893: + cacheIndex = 6212; + reference = new EpsgCoordinateReferenceRecord(24893, (EpsgCoordinateSystemKind)2, 4393); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket25(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 25000: + cacheIndex = 6213; + reference = new EpsgCoordinateReferenceRecord(25000, (EpsgCoordinateSystemKind)2, 4394); + return true; + case 25231: + cacheIndex = 6214; + reference = new EpsgCoordinateReferenceRecord(25231, (EpsgCoordinateSystemKind)2, 4395); + return true; + case 25391: + cacheIndex = 6215; + reference = new EpsgCoordinateReferenceRecord(25391, (EpsgCoordinateSystemKind)2, 4396); + return true; + case 25392: + cacheIndex = 6216; + reference = new EpsgCoordinateReferenceRecord(25392, (EpsgCoordinateSystemKind)2, 4397); + return true; + case 25393: + cacheIndex = 6217; + reference = new EpsgCoordinateReferenceRecord(25393, (EpsgCoordinateSystemKind)2, 4398); + return true; + case 25394: + cacheIndex = 6218; + reference = new EpsgCoordinateReferenceRecord(25394, (EpsgCoordinateSystemKind)2, 4399); + return true; + case 25395: + cacheIndex = 6219; + reference = new EpsgCoordinateReferenceRecord(25395, (EpsgCoordinateSystemKind)2, 4400); + return true; + case 25828: + cacheIndex = 6220; + reference = new EpsgCoordinateReferenceRecord(25828, (EpsgCoordinateSystemKind)2, 4401); + return true; + case 25829: + cacheIndex = 6221; + reference = new EpsgCoordinateReferenceRecord(25829, (EpsgCoordinateSystemKind)2, 4402); + return true; + case 25830: + cacheIndex = 6222; + reference = new EpsgCoordinateReferenceRecord(25830, (EpsgCoordinateSystemKind)2, 4403); + return true; + case 25831: + cacheIndex = 6223; + reference = new EpsgCoordinateReferenceRecord(25831, (EpsgCoordinateSystemKind)2, 4404); + return true; + case 25832: + cacheIndex = 6224; + reference = new EpsgCoordinateReferenceRecord(25832, (EpsgCoordinateSystemKind)2, 4405); + return true; + case 25833: + cacheIndex = 6225; + reference = new EpsgCoordinateReferenceRecord(25833, (EpsgCoordinateSystemKind)2, 4406); + return true; + case 25834: + cacheIndex = 6226; + reference = new EpsgCoordinateReferenceRecord(25834, (EpsgCoordinateSystemKind)2, 4407); + return true; + case 25835: + cacheIndex = 6227; + reference = new EpsgCoordinateReferenceRecord(25835, (EpsgCoordinateSystemKind)2, 4408); + return true; + case 25836: + cacheIndex = 6228; + reference = new EpsgCoordinateReferenceRecord(25836, (EpsgCoordinateSystemKind)2, 4409); + return true; + case 25837: + cacheIndex = 6229; + reference = new EpsgCoordinateReferenceRecord(25837, (EpsgCoordinateSystemKind)2, 4410); + return true; + case 25884: + cacheIndex = 6230; + reference = new EpsgCoordinateReferenceRecord(25884, (EpsgCoordinateSystemKind)2, 4411); + return true; + case 25932: + cacheIndex = 6231; + reference = new EpsgCoordinateReferenceRecord(25932, (EpsgCoordinateSystemKind)2, 4412); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket26(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 26191: + cacheIndex = 6232; + reference = new EpsgCoordinateReferenceRecord(26191, (EpsgCoordinateSystemKind)2, 4413); + return true; + case 26192: + cacheIndex = 6233; + reference = new EpsgCoordinateReferenceRecord(26192, (EpsgCoordinateSystemKind)2, 4414); + return true; + case 26194: + cacheIndex = 6234; + reference = new EpsgCoordinateReferenceRecord(26194, (EpsgCoordinateSystemKind)2, 4415); + return true; + case 26195: + cacheIndex = 6235; + reference = new EpsgCoordinateReferenceRecord(26195, (EpsgCoordinateSystemKind)2, 4416); + return true; + case 26237: + cacheIndex = 6236; + reference = new EpsgCoordinateReferenceRecord(26237, (EpsgCoordinateSystemKind)2, 4417); + return true; + case 26331: + cacheIndex = 6237; + reference = new EpsgCoordinateReferenceRecord(26331, (EpsgCoordinateSystemKind)2, 4418); + return true; + case 26332: + cacheIndex = 6238; + reference = new EpsgCoordinateReferenceRecord(26332, (EpsgCoordinateSystemKind)2, 4419); + return true; + case 26391: + cacheIndex = 6239; + reference = new EpsgCoordinateReferenceRecord(26391, (EpsgCoordinateSystemKind)2, 4420); + return true; + case 26392: + cacheIndex = 6240; + reference = new EpsgCoordinateReferenceRecord(26392, (EpsgCoordinateSystemKind)2, 4421); + return true; + case 26393: + cacheIndex = 6241; + reference = new EpsgCoordinateReferenceRecord(26393, (EpsgCoordinateSystemKind)2, 4422); + return true; + case 26632: + cacheIndex = 6242; + reference = new EpsgCoordinateReferenceRecord(26632, (EpsgCoordinateSystemKind)2, 4423); + return true; + case 26692: + cacheIndex = 6243; + reference = new EpsgCoordinateReferenceRecord(26692, (EpsgCoordinateSystemKind)2, 4424); + return true; + case 26701: + cacheIndex = 6244; + reference = new EpsgCoordinateReferenceRecord(26701, (EpsgCoordinateSystemKind)2, 4425); + return true; + case 26702: + cacheIndex = 6245; + reference = new EpsgCoordinateReferenceRecord(26702, (EpsgCoordinateSystemKind)2, 4426); + return true; + case 26703: + cacheIndex = 6246; + reference = new EpsgCoordinateReferenceRecord(26703, (EpsgCoordinateSystemKind)2, 4427); + return true; + case 26704: + cacheIndex = 6247; + reference = new EpsgCoordinateReferenceRecord(26704, (EpsgCoordinateSystemKind)2, 4428); + return true; + case 26705: + cacheIndex = 6248; + reference = new EpsgCoordinateReferenceRecord(26705, (EpsgCoordinateSystemKind)2, 4429); + return true; + case 26706: + cacheIndex = 6249; + reference = new EpsgCoordinateReferenceRecord(26706, (EpsgCoordinateSystemKind)2, 4430); + return true; + case 26707: + cacheIndex = 6250; + reference = new EpsgCoordinateReferenceRecord(26707, (EpsgCoordinateSystemKind)2, 4431); + return true; + case 26708: + cacheIndex = 6251; + reference = new EpsgCoordinateReferenceRecord(26708, (EpsgCoordinateSystemKind)2, 4432); + return true; + case 26709: + cacheIndex = 6252; + reference = new EpsgCoordinateReferenceRecord(26709, (EpsgCoordinateSystemKind)2, 4433); + return true; + case 26710: + cacheIndex = 6253; + reference = new EpsgCoordinateReferenceRecord(26710, (EpsgCoordinateSystemKind)2, 4434); + return true; + case 26711: + cacheIndex = 6254; + reference = new EpsgCoordinateReferenceRecord(26711, (EpsgCoordinateSystemKind)2, 4435); + return true; + case 26712: + cacheIndex = 6255; + reference = new EpsgCoordinateReferenceRecord(26712, (EpsgCoordinateSystemKind)2, 4436); + return true; + case 26713: + cacheIndex = 6256; + reference = new EpsgCoordinateReferenceRecord(26713, (EpsgCoordinateSystemKind)2, 4437); + return true; + case 26714: + cacheIndex = 6257; + reference = new EpsgCoordinateReferenceRecord(26714, (EpsgCoordinateSystemKind)2, 4438); + return true; + case 26715: + cacheIndex = 6258; + reference = new EpsgCoordinateReferenceRecord(26715, (EpsgCoordinateSystemKind)2, 4439); + return true; + case 26716: + cacheIndex = 6259; + reference = new EpsgCoordinateReferenceRecord(26716, (EpsgCoordinateSystemKind)2, 4440); + return true; + case 26717: + cacheIndex = 6260; + reference = new EpsgCoordinateReferenceRecord(26717, (EpsgCoordinateSystemKind)2, 4441); + return true; + case 26718: + cacheIndex = 6261; + reference = new EpsgCoordinateReferenceRecord(26718, (EpsgCoordinateSystemKind)2, 4442); + return true; + case 26719: + cacheIndex = 6262; + reference = new EpsgCoordinateReferenceRecord(26719, (EpsgCoordinateSystemKind)2, 4443); + return true; + case 26720: + cacheIndex = 6263; + reference = new EpsgCoordinateReferenceRecord(26720, (EpsgCoordinateSystemKind)2, 4444); + return true; + case 26721: + cacheIndex = 6264; + reference = new EpsgCoordinateReferenceRecord(26721, (EpsgCoordinateSystemKind)2, 4445); + return true; + case 26722: + cacheIndex = 6265; + reference = new EpsgCoordinateReferenceRecord(26722, (EpsgCoordinateSystemKind)2, 4446); + return true; + case 26729: + cacheIndex = 6266; + reference = new EpsgCoordinateReferenceRecord(26729, (EpsgCoordinateSystemKind)2, 4447); + return true; + case 26730: + cacheIndex = 6267; + reference = new EpsgCoordinateReferenceRecord(26730, (EpsgCoordinateSystemKind)2, 4448); + return true; + case 26731: + cacheIndex = 6268; + reference = new EpsgCoordinateReferenceRecord(26731, (EpsgCoordinateSystemKind)2, 4449); + return true; + case 26732: + cacheIndex = 6269; + reference = new EpsgCoordinateReferenceRecord(26732, (EpsgCoordinateSystemKind)2, 4450); + return true; + case 26733: + cacheIndex = 6270; + reference = new EpsgCoordinateReferenceRecord(26733, (EpsgCoordinateSystemKind)2, 4451); + return true; + case 26734: + cacheIndex = 6271; + reference = new EpsgCoordinateReferenceRecord(26734, (EpsgCoordinateSystemKind)2, 4452); + return true; + case 26735: + cacheIndex = 6272; + reference = new EpsgCoordinateReferenceRecord(26735, (EpsgCoordinateSystemKind)2, 4453); + return true; + case 26736: + cacheIndex = 6273; + reference = new EpsgCoordinateReferenceRecord(26736, (EpsgCoordinateSystemKind)2, 4454); + return true; + case 26737: + cacheIndex = 6274; + reference = new EpsgCoordinateReferenceRecord(26737, (EpsgCoordinateSystemKind)2, 4455); + return true; + case 26738: + cacheIndex = 6275; + reference = new EpsgCoordinateReferenceRecord(26738, (EpsgCoordinateSystemKind)2, 4456); + return true; + case 26739: + cacheIndex = 6276; + reference = new EpsgCoordinateReferenceRecord(26739, (EpsgCoordinateSystemKind)2, 4457); + return true; + case 26740: + cacheIndex = 6277; + reference = new EpsgCoordinateReferenceRecord(26740, (EpsgCoordinateSystemKind)2, 4458); + return true; + case 26741: + cacheIndex = 6278; + reference = new EpsgCoordinateReferenceRecord(26741, (EpsgCoordinateSystemKind)2, 4459); + return true; + case 26742: + cacheIndex = 6279; + reference = new EpsgCoordinateReferenceRecord(26742, (EpsgCoordinateSystemKind)2, 4460); + return true; + case 26743: + cacheIndex = 6280; + reference = new EpsgCoordinateReferenceRecord(26743, (EpsgCoordinateSystemKind)2, 4461); + return true; + case 26744: + cacheIndex = 6281; + reference = new EpsgCoordinateReferenceRecord(26744, (EpsgCoordinateSystemKind)2, 4462); + return true; + case 26745: + cacheIndex = 6282; + reference = new EpsgCoordinateReferenceRecord(26745, (EpsgCoordinateSystemKind)2, 4463); + return true; + case 26746: + cacheIndex = 6283; + reference = new EpsgCoordinateReferenceRecord(26746, (EpsgCoordinateSystemKind)2, 4464); + return true; + case 26748: + cacheIndex = 6284; + reference = new EpsgCoordinateReferenceRecord(26748, (EpsgCoordinateSystemKind)2, 4465); + return true; + case 26749: + cacheIndex = 6285; + reference = new EpsgCoordinateReferenceRecord(26749, (EpsgCoordinateSystemKind)2, 4466); + return true; + case 26750: + cacheIndex = 6286; + reference = new EpsgCoordinateReferenceRecord(26750, (EpsgCoordinateSystemKind)2, 4467); + return true; + case 26751: + cacheIndex = 6287; + reference = new EpsgCoordinateReferenceRecord(26751, (EpsgCoordinateSystemKind)2, 4468); + return true; + case 26752: + cacheIndex = 6288; + reference = new EpsgCoordinateReferenceRecord(26752, (EpsgCoordinateSystemKind)2, 4469); + return true; + case 26753: + cacheIndex = 6289; + reference = new EpsgCoordinateReferenceRecord(26753, (EpsgCoordinateSystemKind)2, 4470); + return true; + case 26754: + cacheIndex = 6290; + reference = new EpsgCoordinateReferenceRecord(26754, (EpsgCoordinateSystemKind)2, 4471); + return true; + case 26755: + cacheIndex = 6291; + reference = new EpsgCoordinateReferenceRecord(26755, (EpsgCoordinateSystemKind)2, 4472); + return true; + case 26756: + cacheIndex = 6292; + reference = new EpsgCoordinateReferenceRecord(26756, (EpsgCoordinateSystemKind)2, 4473); + return true; + case 26757: + cacheIndex = 6293; + reference = new EpsgCoordinateReferenceRecord(26757, (EpsgCoordinateSystemKind)2, 4474); + return true; + case 26758: + cacheIndex = 6294; + reference = new EpsgCoordinateReferenceRecord(26758, (EpsgCoordinateSystemKind)2, 4475); + return true; + case 26759: + cacheIndex = 6295; + reference = new EpsgCoordinateReferenceRecord(26759, (EpsgCoordinateSystemKind)2, 4476); + return true; + case 26760: + cacheIndex = 6296; + reference = new EpsgCoordinateReferenceRecord(26760, (EpsgCoordinateSystemKind)2, 4477); + return true; + case 26766: + cacheIndex = 6297; + reference = new EpsgCoordinateReferenceRecord(26766, (EpsgCoordinateSystemKind)2, 4478); + return true; + case 26767: + cacheIndex = 6298; + reference = new EpsgCoordinateReferenceRecord(26767, (EpsgCoordinateSystemKind)2, 4479); + return true; + case 26768: + cacheIndex = 6299; + reference = new EpsgCoordinateReferenceRecord(26768, (EpsgCoordinateSystemKind)2, 4480); + return true; + case 26769: + cacheIndex = 6300; + reference = new EpsgCoordinateReferenceRecord(26769, (EpsgCoordinateSystemKind)2, 4481); + return true; + case 26770: + cacheIndex = 6301; + reference = new EpsgCoordinateReferenceRecord(26770, (EpsgCoordinateSystemKind)2, 4482); + return true; + case 26771: + cacheIndex = 6302; + reference = new EpsgCoordinateReferenceRecord(26771, (EpsgCoordinateSystemKind)2, 4483); + return true; + case 26772: + cacheIndex = 6303; + reference = new EpsgCoordinateReferenceRecord(26772, (EpsgCoordinateSystemKind)2, 4484); + return true; + case 26773: + cacheIndex = 6304; + reference = new EpsgCoordinateReferenceRecord(26773, (EpsgCoordinateSystemKind)2, 4485); + return true; + case 26774: + cacheIndex = 6305; + reference = new EpsgCoordinateReferenceRecord(26774, (EpsgCoordinateSystemKind)2, 4486); + return true; + case 26775: + cacheIndex = 6306; + reference = new EpsgCoordinateReferenceRecord(26775, (EpsgCoordinateSystemKind)2, 4487); + return true; + case 26776: + cacheIndex = 6307; + reference = new EpsgCoordinateReferenceRecord(26776, (EpsgCoordinateSystemKind)2, 4488); + return true; + case 26777: + cacheIndex = 6308; + reference = new EpsgCoordinateReferenceRecord(26777, (EpsgCoordinateSystemKind)2, 4489); + return true; + case 26778: + cacheIndex = 6309; + reference = new EpsgCoordinateReferenceRecord(26778, (EpsgCoordinateSystemKind)2, 4490); + return true; + case 26779: + cacheIndex = 6310; + reference = new EpsgCoordinateReferenceRecord(26779, (EpsgCoordinateSystemKind)2, 4491); + return true; + case 26780: + cacheIndex = 6311; + reference = new EpsgCoordinateReferenceRecord(26780, (EpsgCoordinateSystemKind)2, 4492); + return true; + case 26781: + cacheIndex = 6312; + reference = new EpsgCoordinateReferenceRecord(26781, (EpsgCoordinateSystemKind)2, 4493); + return true; + case 26782: + cacheIndex = 6313; + reference = new EpsgCoordinateReferenceRecord(26782, (EpsgCoordinateSystemKind)2, 4494); + return true; + case 26783: + cacheIndex = 6314; + reference = new EpsgCoordinateReferenceRecord(26783, (EpsgCoordinateSystemKind)2, 4495); + return true; + case 26784: + cacheIndex = 6315; + reference = new EpsgCoordinateReferenceRecord(26784, (EpsgCoordinateSystemKind)2, 4496); + return true; + case 26785: + cacheIndex = 6316; + reference = new EpsgCoordinateReferenceRecord(26785, (EpsgCoordinateSystemKind)2, 4497); + return true; + case 26786: + cacheIndex = 6317; + reference = new EpsgCoordinateReferenceRecord(26786, (EpsgCoordinateSystemKind)2, 4498); + return true; + case 26787: + cacheIndex = 6318; + reference = new EpsgCoordinateReferenceRecord(26787, (EpsgCoordinateSystemKind)2, 4499); + return true; + case 26791: + cacheIndex = 6319; + reference = new EpsgCoordinateReferenceRecord(26791, (EpsgCoordinateSystemKind)2, 4500); + return true; + case 26792: + cacheIndex = 6320; + reference = new EpsgCoordinateReferenceRecord(26792, (EpsgCoordinateSystemKind)2, 4501); + return true; + case 26793: + cacheIndex = 6321; + reference = new EpsgCoordinateReferenceRecord(26793, (EpsgCoordinateSystemKind)2, 4502); + return true; + case 26794: + cacheIndex = 6322; + reference = new EpsgCoordinateReferenceRecord(26794, (EpsgCoordinateSystemKind)2, 4503); + return true; + case 26795: + cacheIndex = 6323; + reference = new EpsgCoordinateReferenceRecord(26795, (EpsgCoordinateSystemKind)2, 4504); + return true; + case 26796: + cacheIndex = 6324; + reference = new EpsgCoordinateReferenceRecord(26796, (EpsgCoordinateSystemKind)2, 4505); + return true; + case 26797: + cacheIndex = 6325; + reference = new EpsgCoordinateReferenceRecord(26797, (EpsgCoordinateSystemKind)2, 4506); + return true; + case 26798: + cacheIndex = 6326; + reference = new EpsgCoordinateReferenceRecord(26798, (EpsgCoordinateSystemKind)2, 4507); + return true; + case 26799: + cacheIndex = 6327; + reference = new EpsgCoordinateReferenceRecord(26799, (EpsgCoordinateSystemKind)2, 4508); + return true; + case 26847: + cacheIndex = 6328; + reference = new EpsgCoordinateReferenceRecord(26847, (EpsgCoordinateSystemKind)2, 4509); + return true; + case 26848: + cacheIndex = 6329; + reference = new EpsgCoordinateReferenceRecord(26848, (EpsgCoordinateSystemKind)2, 4510); + return true; + case 26849: + cacheIndex = 6330; + reference = new EpsgCoordinateReferenceRecord(26849, (EpsgCoordinateSystemKind)2, 4511); + return true; + case 26850: + cacheIndex = 6331; + reference = new EpsgCoordinateReferenceRecord(26850, (EpsgCoordinateSystemKind)2, 4512); + return true; + case 26851: + cacheIndex = 6332; + reference = new EpsgCoordinateReferenceRecord(26851, (EpsgCoordinateSystemKind)2, 4513); + return true; + case 26852: + cacheIndex = 6333; + reference = new EpsgCoordinateReferenceRecord(26852, (EpsgCoordinateSystemKind)2, 4514); + return true; + case 26853: + cacheIndex = 6334; + reference = new EpsgCoordinateReferenceRecord(26853, (EpsgCoordinateSystemKind)2, 4515); + return true; + case 26854: + cacheIndex = 6335; + reference = new EpsgCoordinateReferenceRecord(26854, (EpsgCoordinateSystemKind)2, 4516); + return true; + case 26855: + cacheIndex = 6336; + reference = new EpsgCoordinateReferenceRecord(26855, (EpsgCoordinateSystemKind)2, 4517); + return true; + case 26856: + cacheIndex = 6337; + reference = new EpsgCoordinateReferenceRecord(26856, (EpsgCoordinateSystemKind)2, 4518); + return true; + case 26857: + cacheIndex = 6338; + reference = new EpsgCoordinateReferenceRecord(26857, (EpsgCoordinateSystemKind)2, 4519); + return true; + case 26858: + cacheIndex = 6339; + reference = new EpsgCoordinateReferenceRecord(26858, (EpsgCoordinateSystemKind)2, 4520); + return true; + case 26859: + cacheIndex = 6340; + reference = new EpsgCoordinateReferenceRecord(26859, (EpsgCoordinateSystemKind)2, 4521); + return true; + case 26860: + cacheIndex = 6341; + reference = new EpsgCoordinateReferenceRecord(26860, (EpsgCoordinateSystemKind)2, 4522); + return true; + case 26861: + cacheIndex = 6342; + reference = new EpsgCoordinateReferenceRecord(26861, (EpsgCoordinateSystemKind)2, 4523); + return true; + case 26862: + cacheIndex = 6343; + reference = new EpsgCoordinateReferenceRecord(26862, (EpsgCoordinateSystemKind)2, 4524); + return true; + case 26863: + cacheIndex = 6344; + reference = new EpsgCoordinateReferenceRecord(26863, (EpsgCoordinateSystemKind)2, 4525); + return true; + case 26864: + cacheIndex = 6345; + reference = new EpsgCoordinateReferenceRecord(26864, (EpsgCoordinateSystemKind)2, 4526); + return true; + case 26865: + cacheIndex = 6346; + reference = new EpsgCoordinateReferenceRecord(26865, (EpsgCoordinateSystemKind)2, 4527); + return true; + case 26866: + cacheIndex = 6347; + reference = new EpsgCoordinateReferenceRecord(26866, (EpsgCoordinateSystemKind)2, 4528); + return true; + case 26867: + cacheIndex = 6348; + reference = new EpsgCoordinateReferenceRecord(26867, (EpsgCoordinateSystemKind)2, 4529); + return true; + case 26868: + cacheIndex = 6349; + reference = new EpsgCoordinateReferenceRecord(26868, (EpsgCoordinateSystemKind)2, 4530); + return true; + case 26869: + cacheIndex = 6350; + reference = new EpsgCoordinateReferenceRecord(26869, (EpsgCoordinateSystemKind)2, 4531); + return true; + case 26870: + cacheIndex = 6351; + reference = new EpsgCoordinateReferenceRecord(26870, (EpsgCoordinateSystemKind)2, 4532); + return true; + case 26891: + cacheIndex = 6352; + reference = new EpsgCoordinateReferenceRecord(26891, (EpsgCoordinateSystemKind)2, 4533); + return true; + case 26892: + cacheIndex = 6353; + reference = new EpsgCoordinateReferenceRecord(26892, (EpsgCoordinateSystemKind)2, 4534); + return true; + case 26893: + cacheIndex = 6354; + reference = new EpsgCoordinateReferenceRecord(26893, (EpsgCoordinateSystemKind)2, 4535); + return true; + case 26894: + cacheIndex = 6355; + reference = new EpsgCoordinateReferenceRecord(26894, (EpsgCoordinateSystemKind)2, 4536); + return true; + case 26895: + cacheIndex = 6356; + reference = new EpsgCoordinateReferenceRecord(26895, (EpsgCoordinateSystemKind)2, 4537); + return true; + case 26896: + cacheIndex = 6357; + reference = new EpsgCoordinateReferenceRecord(26896, (EpsgCoordinateSystemKind)2, 4538); + return true; + case 26897: + cacheIndex = 6358; + reference = new EpsgCoordinateReferenceRecord(26897, (EpsgCoordinateSystemKind)2, 4539); + return true; + case 26898: + cacheIndex = 6359; + reference = new EpsgCoordinateReferenceRecord(26898, (EpsgCoordinateSystemKind)2, 4540); + return true; + case 26899: + cacheIndex = 6360; + reference = new EpsgCoordinateReferenceRecord(26899, (EpsgCoordinateSystemKind)2, 4541); + return true; + case 26901: + cacheIndex = 6361; + reference = new EpsgCoordinateReferenceRecord(26901, (EpsgCoordinateSystemKind)2, 4542); + return true; + case 26902: + cacheIndex = 6362; + reference = new EpsgCoordinateReferenceRecord(26902, (EpsgCoordinateSystemKind)2, 4543); + return true; + case 26903: + cacheIndex = 6363; + reference = new EpsgCoordinateReferenceRecord(26903, (EpsgCoordinateSystemKind)2, 4544); + return true; + case 26904: + cacheIndex = 6364; + reference = new EpsgCoordinateReferenceRecord(26904, (EpsgCoordinateSystemKind)2, 4545); + return true; + case 26905: + cacheIndex = 6365; + reference = new EpsgCoordinateReferenceRecord(26905, (EpsgCoordinateSystemKind)2, 4546); + return true; + case 26906: + cacheIndex = 6366; + reference = new EpsgCoordinateReferenceRecord(26906, (EpsgCoordinateSystemKind)2, 4547); + return true; + case 26907: + cacheIndex = 6367; + reference = new EpsgCoordinateReferenceRecord(26907, (EpsgCoordinateSystemKind)2, 4548); + return true; + case 26908: + cacheIndex = 6368; + reference = new EpsgCoordinateReferenceRecord(26908, (EpsgCoordinateSystemKind)2, 4549); + return true; + case 26909: + cacheIndex = 6369; + reference = new EpsgCoordinateReferenceRecord(26909, (EpsgCoordinateSystemKind)2, 4550); + return true; + case 26910: + cacheIndex = 6370; + reference = new EpsgCoordinateReferenceRecord(26910, (EpsgCoordinateSystemKind)2, 4551); + return true; + case 26911: + cacheIndex = 6371; + reference = new EpsgCoordinateReferenceRecord(26911, (EpsgCoordinateSystemKind)2, 4552); + return true; + case 26912: + cacheIndex = 6372; + reference = new EpsgCoordinateReferenceRecord(26912, (EpsgCoordinateSystemKind)2, 4553); + return true; + case 26913: + cacheIndex = 6373; + reference = new EpsgCoordinateReferenceRecord(26913, (EpsgCoordinateSystemKind)2, 4554); + return true; + case 26914: + cacheIndex = 6374; + reference = new EpsgCoordinateReferenceRecord(26914, (EpsgCoordinateSystemKind)2, 4555); + return true; + case 26915: + cacheIndex = 6375; + reference = new EpsgCoordinateReferenceRecord(26915, (EpsgCoordinateSystemKind)2, 4556); + return true; + case 26916: + cacheIndex = 6376; + reference = new EpsgCoordinateReferenceRecord(26916, (EpsgCoordinateSystemKind)2, 4557); + return true; + case 26917: + cacheIndex = 6377; + reference = new EpsgCoordinateReferenceRecord(26917, (EpsgCoordinateSystemKind)2, 4558); + return true; + case 26918: + cacheIndex = 6378; + reference = new EpsgCoordinateReferenceRecord(26918, (EpsgCoordinateSystemKind)2, 4559); + return true; + case 26919: + cacheIndex = 6379; + reference = new EpsgCoordinateReferenceRecord(26919, (EpsgCoordinateSystemKind)2, 4560); + return true; + case 26920: + cacheIndex = 6380; + reference = new EpsgCoordinateReferenceRecord(26920, (EpsgCoordinateSystemKind)2, 4561); + return true; + case 26921: + cacheIndex = 6381; + reference = new EpsgCoordinateReferenceRecord(26921, (EpsgCoordinateSystemKind)2, 4562); + return true; + case 26922: + cacheIndex = 6382; + reference = new EpsgCoordinateReferenceRecord(26922, (EpsgCoordinateSystemKind)2, 4563); + return true; + case 26923: + cacheIndex = 6383; + reference = new EpsgCoordinateReferenceRecord(26923, (EpsgCoordinateSystemKind)2, 4564); + return true; + case 26929: + cacheIndex = 6384; + reference = new EpsgCoordinateReferenceRecord(26929, (EpsgCoordinateSystemKind)2, 4565); + return true; + case 26930: + cacheIndex = 6385; + reference = new EpsgCoordinateReferenceRecord(26930, (EpsgCoordinateSystemKind)2, 4566); + return true; + case 26931: + cacheIndex = 6386; + reference = new EpsgCoordinateReferenceRecord(26931, (EpsgCoordinateSystemKind)2, 4567); + return true; + case 26932: + cacheIndex = 6387; + reference = new EpsgCoordinateReferenceRecord(26932, (EpsgCoordinateSystemKind)2, 4568); + return true; + case 26933: + cacheIndex = 6388; + reference = new EpsgCoordinateReferenceRecord(26933, (EpsgCoordinateSystemKind)2, 4569); + return true; + case 26934: + cacheIndex = 6389; + reference = new EpsgCoordinateReferenceRecord(26934, (EpsgCoordinateSystemKind)2, 4570); + return true; + case 26935: + cacheIndex = 6390; + reference = new EpsgCoordinateReferenceRecord(26935, (EpsgCoordinateSystemKind)2, 4571); + return true; + case 26936: + cacheIndex = 6391; + reference = new EpsgCoordinateReferenceRecord(26936, (EpsgCoordinateSystemKind)2, 4572); + return true; + case 26937: + cacheIndex = 6392; + reference = new EpsgCoordinateReferenceRecord(26937, (EpsgCoordinateSystemKind)2, 4573); + return true; + case 26938: + cacheIndex = 6393; + reference = new EpsgCoordinateReferenceRecord(26938, (EpsgCoordinateSystemKind)2, 4574); + return true; + case 26939: + cacheIndex = 6394; + reference = new EpsgCoordinateReferenceRecord(26939, (EpsgCoordinateSystemKind)2, 4575); + return true; + case 26940: + cacheIndex = 6395; + reference = new EpsgCoordinateReferenceRecord(26940, (EpsgCoordinateSystemKind)2, 4576); + return true; + case 26941: + cacheIndex = 6396; + reference = new EpsgCoordinateReferenceRecord(26941, (EpsgCoordinateSystemKind)2, 4577); + return true; + case 26942: + cacheIndex = 6397; + reference = new EpsgCoordinateReferenceRecord(26942, (EpsgCoordinateSystemKind)2, 4578); + return true; + case 26943: + cacheIndex = 6398; + reference = new EpsgCoordinateReferenceRecord(26943, (EpsgCoordinateSystemKind)2, 4579); + return true; + case 26944: + cacheIndex = 6399; + reference = new EpsgCoordinateReferenceRecord(26944, (EpsgCoordinateSystemKind)2, 4580); + return true; + case 26945: + cacheIndex = 6400; + reference = new EpsgCoordinateReferenceRecord(26945, (EpsgCoordinateSystemKind)2, 4581); + return true; + case 26946: + cacheIndex = 6401; + reference = new EpsgCoordinateReferenceRecord(26946, (EpsgCoordinateSystemKind)2, 4582); + return true; + case 26948: + cacheIndex = 6402; + reference = new EpsgCoordinateReferenceRecord(26948, (EpsgCoordinateSystemKind)2, 4583); + return true; + case 26949: + cacheIndex = 6403; + reference = new EpsgCoordinateReferenceRecord(26949, (EpsgCoordinateSystemKind)2, 4584); + return true; + case 26950: + cacheIndex = 6404; + reference = new EpsgCoordinateReferenceRecord(26950, (EpsgCoordinateSystemKind)2, 4585); + return true; + case 26951: + cacheIndex = 6405; + reference = new EpsgCoordinateReferenceRecord(26951, (EpsgCoordinateSystemKind)2, 4586); + return true; + case 26952: + cacheIndex = 6406; + reference = new EpsgCoordinateReferenceRecord(26952, (EpsgCoordinateSystemKind)2, 4587); + return true; + case 26953: + cacheIndex = 6407; + reference = new EpsgCoordinateReferenceRecord(26953, (EpsgCoordinateSystemKind)2, 4588); + return true; + case 26954: + cacheIndex = 6408; + reference = new EpsgCoordinateReferenceRecord(26954, (EpsgCoordinateSystemKind)2, 4589); + return true; + case 26955: + cacheIndex = 6409; + reference = new EpsgCoordinateReferenceRecord(26955, (EpsgCoordinateSystemKind)2, 4590); + return true; + case 26956: + cacheIndex = 6410; + reference = new EpsgCoordinateReferenceRecord(26956, (EpsgCoordinateSystemKind)2, 4591); + return true; + case 26957: + cacheIndex = 6411; + reference = new EpsgCoordinateReferenceRecord(26957, (EpsgCoordinateSystemKind)2, 4592); + return true; + case 26958: + cacheIndex = 6412; + reference = new EpsgCoordinateReferenceRecord(26958, (EpsgCoordinateSystemKind)2, 4593); + return true; + case 26959: + cacheIndex = 6413; + reference = new EpsgCoordinateReferenceRecord(26959, (EpsgCoordinateSystemKind)2, 4594); + return true; + case 26960: + cacheIndex = 6414; + reference = new EpsgCoordinateReferenceRecord(26960, (EpsgCoordinateSystemKind)2, 4595); + return true; + case 26961: + cacheIndex = 6415; + reference = new EpsgCoordinateReferenceRecord(26961, (EpsgCoordinateSystemKind)2, 4596); + return true; + case 26962: + cacheIndex = 6416; + reference = new EpsgCoordinateReferenceRecord(26962, (EpsgCoordinateSystemKind)2, 4597); + return true; + case 26963: + cacheIndex = 6417; + reference = new EpsgCoordinateReferenceRecord(26963, (EpsgCoordinateSystemKind)2, 4598); + return true; + case 26964: + cacheIndex = 6418; + reference = new EpsgCoordinateReferenceRecord(26964, (EpsgCoordinateSystemKind)2, 4599); + return true; + case 26965: + cacheIndex = 6419; + reference = new EpsgCoordinateReferenceRecord(26965, (EpsgCoordinateSystemKind)2, 4600); + return true; + case 26966: + cacheIndex = 6420; + reference = new EpsgCoordinateReferenceRecord(26966, (EpsgCoordinateSystemKind)2, 4601); + return true; + case 26967: + cacheIndex = 6421; + reference = new EpsgCoordinateReferenceRecord(26967, (EpsgCoordinateSystemKind)2, 4602); + return true; + case 26968: + cacheIndex = 6422; + reference = new EpsgCoordinateReferenceRecord(26968, (EpsgCoordinateSystemKind)2, 4603); + return true; + case 26969: + cacheIndex = 6423; + reference = new EpsgCoordinateReferenceRecord(26969, (EpsgCoordinateSystemKind)2, 4604); + return true; + case 26970: + cacheIndex = 6424; + reference = new EpsgCoordinateReferenceRecord(26970, (EpsgCoordinateSystemKind)2, 4605); + return true; + case 26971: + cacheIndex = 6425; + reference = new EpsgCoordinateReferenceRecord(26971, (EpsgCoordinateSystemKind)2, 4606); + return true; + case 26972: + cacheIndex = 6426; + reference = new EpsgCoordinateReferenceRecord(26972, (EpsgCoordinateSystemKind)2, 4607); + return true; + case 26973: + cacheIndex = 6427; + reference = new EpsgCoordinateReferenceRecord(26973, (EpsgCoordinateSystemKind)2, 4608); + return true; + case 26974: + cacheIndex = 6428; + reference = new EpsgCoordinateReferenceRecord(26974, (EpsgCoordinateSystemKind)2, 4609); + return true; + case 26975: + cacheIndex = 6429; + reference = new EpsgCoordinateReferenceRecord(26975, (EpsgCoordinateSystemKind)2, 4610); + return true; + case 26976: + cacheIndex = 6430; + reference = new EpsgCoordinateReferenceRecord(26976, (EpsgCoordinateSystemKind)2, 4611); + return true; + case 26977: + cacheIndex = 6431; + reference = new EpsgCoordinateReferenceRecord(26977, (EpsgCoordinateSystemKind)2, 4612); + return true; + case 26978: + cacheIndex = 6432; + reference = new EpsgCoordinateReferenceRecord(26978, (EpsgCoordinateSystemKind)2, 4613); + return true; + case 26980: + cacheIndex = 6433; + reference = new EpsgCoordinateReferenceRecord(26980, (EpsgCoordinateSystemKind)2, 4614); + return true; + case 26981: + cacheIndex = 6434; + reference = new EpsgCoordinateReferenceRecord(26981, (EpsgCoordinateSystemKind)2, 4615); + return true; + case 26982: + cacheIndex = 6435; + reference = new EpsgCoordinateReferenceRecord(26982, (EpsgCoordinateSystemKind)2, 4616); + return true; + case 26983: + cacheIndex = 6436; + reference = new EpsgCoordinateReferenceRecord(26983, (EpsgCoordinateSystemKind)2, 4617); + return true; + case 26984: + cacheIndex = 6437; + reference = new EpsgCoordinateReferenceRecord(26984, (EpsgCoordinateSystemKind)2, 4618); + return true; + case 26985: + cacheIndex = 6438; + reference = new EpsgCoordinateReferenceRecord(26985, (EpsgCoordinateSystemKind)2, 4619); + return true; + case 26986: + cacheIndex = 6439; + reference = new EpsgCoordinateReferenceRecord(26986, (EpsgCoordinateSystemKind)2, 4620); + return true; + case 26987: + cacheIndex = 6440; + reference = new EpsgCoordinateReferenceRecord(26987, (EpsgCoordinateSystemKind)2, 4621); + return true; + case 26988: + cacheIndex = 6441; + reference = new EpsgCoordinateReferenceRecord(26988, (EpsgCoordinateSystemKind)2, 4622); + return true; + case 26989: + cacheIndex = 6442; + reference = new EpsgCoordinateReferenceRecord(26989, (EpsgCoordinateSystemKind)2, 4623); + return true; + case 26990: + cacheIndex = 6443; + reference = new EpsgCoordinateReferenceRecord(26990, (EpsgCoordinateSystemKind)2, 4624); + return true; + case 26991: + cacheIndex = 6444; + reference = new EpsgCoordinateReferenceRecord(26991, (EpsgCoordinateSystemKind)2, 4625); + return true; + case 26992: + cacheIndex = 6445; + reference = new EpsgCoordinateReferenceRecord(26992, (EpsgCoordinateSystemKind)2, 4626); + return true; + case 26993: + cacheIndex = 6446; + reference = new EpsgCoordinateReferenceRecord(26993, (EpsgCoordinateSystemKind)2, 4627); + return true; + case 26994: + cacheIndex = 6447; + reference = new EpsgCoordinateReferenceRecord(26994, (EpsgCoordinateSystemKind)2, 4628); + return true; + case 26995: + cacheIndex = 6448; + reference = new EpsgCoordinateReferenceRecord(26995, (EpsgCoordinateSystemKind)2, 4629); + return true; + case 26996: + cacheIndex = 6449; + reference = new EpsgCoordinateReferenceRecord(26996, (EpsgCoordinateSystemKind)2, 4630); + return true; + case 26997: + cacheIndex = 6450; + reference = new EpsgCoordinateReferenceRecord(26997, (EpsgCoordinateSystemKind)2, 4631); + return true; + case 26998: + cacheIndex = 6451; + reference = new EpsgCoordinateReferenceRecord(26998, (EpsgCoordinateSystemKind)2, 4632); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket27(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 27039: + cacheIndex = 6452; + reference = new EpsgCoordinateReferenceRecord(27039, (EpsgCoordinateSystemKind)2, 4633); + return true; + case 27040: + cacheIndex = 6453; + reference = new EpsgCoordinateReferenceRecord(27040, (EpsgCoordinateSystemKind)2, 4634); + return true; + case 27120: + cacheIndex = 6454; + reference = new EpsgCoordinateReferenceRecord(27120, (EpsgCoordinateSystemKind)2, 4635); + return true; + case 27200: + cacheIndex = 6455; + reference = new EpsgCoordinateReferenceRecord(27200, (EpsgCoordinateSystemKind)2, 4636); + return true; + case 27205: + cacheIndex = 6456; + reference = new EpsgCoordinateReferenceRecord(27205, (EpsgCoordinateSystemKind)2, 4637); + return true; + case 27206: + cacheIndex = 6457; + reference = new EpsgCoordinateReferenceRecord(27206, (EpsgCoordinateSystemKind)2, 4638); + return true; + case 27207: + cacheIndex = 6458; + reference = new EpsgCoordinateReferenceRecord(27207, (EpsgCoordinateSystemKind)2, 4639); + return true; + case 27208: + cacheIndex = 6459; + reference = new EpsgCoordinateReferenceRecord(27208, (EpsgCoordinateSystemKind)2, 4640); + return true; + case 27209: + cacheIndex = 6460; + reference = new EpsgCoordinateReferenceRecord(27209, (EpsgCoordinateSystemKind)2, 4641); + return true; + case 27210: + cacheIndex = 6461; + reference = new EpsgCoordinateReferenceRecord(27210, (EpsgCoordinateSystemKind)2, 4642); + return true; + case 27211: + cacheIndex = 6462; + reference = new EpsgCoordinateReferenceRecord(27211, (EpsgCoordinateSystemKind)2, 4643); + return true; + case 27212: + cacheIndex = 6463; + reference = new EpsgCoordinateReferenceRecord(27212, (EpsgCoordinateSystemKind)2, 4644); + return true; + case 27213: + cacheIndex = 6464; + reference = new EpsgCoordinateReferenceRecord(27213, (EpsgCoordinateSystemKind)2, 4645); + return true; + case 27214: + cacheIndex = 6465; + reference = new EpsgCoordinateReferenceRecord(27214, (EpsgCoordinateSystemKind)2, 4646); + return true; + case 27215: + cacheIndex = 6466; + reference = new EpsgCoordinateReferenceRecord(27215, (EpsgCoordinateSystemKind)2, 4647); + return true; + case 27216: + cacheIndex = 6467; + reference = new EpsgCoordinateReferenceRecord(27216, (EpsgCoordinateSystemKind)2, 4648); + return true; + case 27217: + cacheIndex = 6468; + reference = new EpsgCoordinateReferenceRecord(27217, (EpsgCoordinateSystemKind)2, 4649); + return true; + case 27218: + cacheIndex = 6469; + reference = new EpsgCoordinateReferenceRecord(27218, (EpsgCoordinateSystemKind)2, 4650); + return true; + case 27219: + cacheIndex = 6470; + reference = new EpsgCoordinateReferenceRecord(27219, (EpsgCoordinateSystemKind)2, 4651); + return true; + case 27220: + cacheIndex = 6471; + reference = new EpsgCoordinateReferenceRecord(27220, (EpsgCoordinateSystemKind)2, 4652); + return true; + case 27221: + cacheIndex = 6472; + reference = new EpsgCoordinateReferenceRecord(27221, (EpsgCoordinateSystemKind)2, 4653); + return true; + case 27222: + cacheIndex = 6473; + reference = new EpsgCoordinateReferenceRecord(27222, (EpsgCoordinateSystemKind)2, 4654); + return true; + case 27223: + cacheIndex = 6474; + reference = new EpsgCoordinateReferenceRecord(27223, (EpsgCoordinateSystemKind)2, 4655); + return true; + case 27224: + cacheIndex = 6475; + reference = new EpsgCoordinateReferenceRecord(27224, (EpsgCoordinateSystemKind)2, 4656); + return true; + case 27225: + cacheIndex = 6476; + reference = new EpsgCoordinateReferenceRecord(27225, (EpsgCoordinateSystemKind)2, 4657); + return true; + case 27226: + cacheIndex = 6477; + reference = new EpsgCoordinateReferenceRecord(27226, (EpsgCoordinateSystemKind)2, 4658); + return true; + case 27227: + cacheIndex = 6478; + reference = new EpsgCoordinateReferenceRecord(27227, (EpsgCoordinateSystemKind)2, 4659); + return true; + case 27228: + cacheIndex = 6479; + reference = new EpsgCoordinateReferenceRecord(27228, (EpsgCoordinateSystemKind)2, 4660); + return true; + case 27229: + cacheIndex = 6480; + reference = new EpsgCoordinateReferenceRecord(27229, (EpsgCoordinateSystemKind)2, 4661); + return true; + case 27230: + cacheIndex = 6481; + reference = new EpsgCoordinateReferenceRecord(27230, (EpsgCoordinateSystemKind)2, 4662); + return true; + case 27231: + cacheIndex = 6482; + reference = new EpsgCoordinateReferenceRecord(27231, (EpsgCoordinateSystemKind)2, 4663); + return true; + case 27232: + cacheIndex = 6483; + reference = new EpsgCoordinateReferenceRecord(27232, (EpsgCoordinateSystemKind)2, 4664); + return true; + case 27258: + cacheIndex = 6484; + reference = new EpsgCoordinateReferenceRecord(27258, (EpsgCoordinateSystemKind)2, 4665); + return true; + case 27259: + cacheIndex = 6485; + reference = new EpsgCoordinateReferenceRecord(27259, (EpsgCoordinateSystemKind)2, 4666); + return true; + case 27260: + cacheIndex = 6486; + reference = new EpsgCoordinateReferenceRecord(27260, (EpsgCoordinateSystemKind)2, 4667); + return true; + case 27291: + cacheIndex = 6487; + reference = new EpsgCoordinateReferenceRecord(27291, (EpsgCoordinateSystemKind)2, 4668); + return true; + case 27292: + cacheIndex = 6488; + reference = new EpsgCoordinateReferenceRecord(27292, (EpsgCoordinateSystemKind)2, 4669); + return true; + case 27391: + cacheIndex = 6489; + reference = new EpsgCoordinateReferenceRecord(27391, (EpsgCoordinateSystemKind)2, 4670); + return true; + case 27392: + cacheIndex = 6490; + reference = new EpsgCoordinateReferenceRecord(27392, (EpsgCoordinateSystemKind)2, 4671); + return true; + case 27393: + cacheIndex = 6491; + reference = new EpsgCoordinateReferenceRecord(27393, (EpsgCoordinateSystemKind)2, 4672); + return true; + case 27394: + cacheIndex = 6492; + reference = new EpsgCoordinateReferenceRecord(27394, (EpsgCoordinateSystemKind)2, 4673); + return true; + case 27395: + cacheIndex = 6493; + reference = new EpsgCoordinateReferenceRecord(27395, (EpsgCoordinateSystemKind)2, 4674); + return true; + case 27396: + cacheIndex = 6494; + reference = new EpsgCoordinateReferenceRecord(27396, (EpsgCoordinateSystemKind)2, 4675); + return true; + case 27397: + cacheIndex = 6495; + reference = new EpsgCoordinateReferenceRecord(27397, (EpsgCoordinateSystemKind)2, 4676); + return true; + case 27398: + cacheIndex = 6496; + reference = new EpsgCoordinateReferenceRecord(27398, (EpsgCoordinateSystemKind)2, 4677); + return true; + case 27429: + cacheIndex = 6497; + reference = new EpsgCoordinateReferenceRecord(27429, (EpsgCoordinateSystemKind)2, 4678); + return true; + case 27493: + cacheIndex = 6498; + reference = new EpsgCoordinateReferenceRecord(27493, (EpsgCoordinateSystemKind)2, 4679); + return true; + case 27500: + cacheIndex = 6499; + reference = new EpsgCoordinateReferenceRecord(27500, (EpsgCoordinateSystemKind)2, 4680); + return true; + case 27561: + cacheIndex = 6500; + reference = new EpsgCoordinateReferenceRecord(27561, (EpsgCoordinateSystemKind)2, 4681); + return true; + case 27562: + cacheIndex = 6501; + reference = new EpsgCoordinateReferenceRecord(27562, (EpsgCoordinateSystemKind)2, 4682); + return true; + case 27563: + cacheIndex = 6502; + reference = new EpsgCoordinateReferenceRecord(27563, (EpsgCoordinateSystemKind)2, 4683); + return true; + case 27564: + cacheIndex = 6503; + reference = new EpsgCoordinateReferenceRecord(27564, (EpsgCoordinateSystemKind)2, 4684); + return true; + case 27571: + cacheIndex = 6504; + reference = new EpsgCoordinateReferenceRecord(27571, (EpsgCoordinateSystemKind)2, 4685); + return true; + case 27572: + cacheIndex = 6505; + reference = new EpsgCoordinateReferenceRecord(27572, (EpsgCoordinateSystemKind)2, 4686); + return true; + case 27573: + cacheIndex = 6506; + reference = new EpsgCoordinateReferenceRecord(27573, (EpsgCoordinateSystemKind)2, 4687); + return true; + case 27574: + cacheIndex = 6507; + reference = new EpsgCoordinateReferenceRecord(27574, (EpsgCoordinateSystemKind)2, 4688); + return true; + case 27700: + cacheIndex = 6508; + reference = new EpsgCoordinateReferenceRecord(27700, (EpsgCoordinateSystemKind)2, 4689); + return true; + case 27701: + cacheIndex = 6509; + reference = new EpsgCoordinateReferenceRecord(27701, (EpsgCoordinateSystemKind)2, 4690); + return true; + case 27702: + cacheIndex = 6510; + reference = new EpsgCoordinateReferenceRecord(27702, (EpsgCoordinateSystemKind)2, 4691); + return true; + case 27703: + cacheIndex = 6511; + reference = new EpsgCoordinateReferenceRecord(27703, (EpsgCoordinateSystemKind)2, 4692); + return true; + case 27704: + cacheIndex = 6512; + reference = new EpsgCoordinateReferenceRecord(27704, (EpsgCoordinateSystemKind)2, 4693); + return true; + case 27705: + cacheIndex = 6513; + reference = new EpsgCoordinateReferenceRecord(27705, (EpsgCoordinateSystemKind)2, 4694); + return true; + case 27706: + cacheIndex = 6514; + reference = new EpsgCoordinateReferenceRecord(27706, (EpsgCoordinateSystemKind)2, 4695); + return true; + case 27707: + cacheIndex = 6515; + reference = new EpsgCoordinateReferenceRecord(27707, (EpsgCoordinateSystemKind)2, 4696); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket28(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 28191: + cacheIndex = 6516; + reference = new EpsgCoordinateReferenceRecord(28191, (EpsgCoordinateSystemKind)2, 4697); + return true; + case 28192: + cacheIndex = 6517; + reference = new EpsgCoordinateReferenceRecord(28192, (EpsgCoordinateSystemKind)2, 4698); + return true; + case 28193: + cacheIndex = 6518; + reference = new EpsgCoordinateReferenceRecord(28193, (EpsgCoordinateSystemKind)2, 4699); + return true; + case 28232: + cacheIndex = 6519; + reference = new EpsgCoordinateReferenceRecord(28232, (EpsgCoordinateSystemKind)2, 4700); + return true; + case 28348: + cacheIndex = 6520; + reference = new EpsgCoordinateReferenceRecord(28348, (EpsgCoordinateSystemKind)2, 4701); + return true; + case 28349: + cacheIndex = 6521; + reference = new EpsgCoordinateReferenceRecord(28349, (EpsgCoordinateSystemKind)2, 4702); + return true; + case 28350: + cacheIndex = 6522; + reference = new EpsgCoordinateReferenceRecord(28350, (EpsgCoordinateSystemKind)2, 4703); + return true; + case 28351: + cacheIndex = 6523; + reference = new EpsgCoordinateReferenceRecord(28351, (EpsgCoordinateSystemKind)2, 4704); + return true; + case 28352: + cacheIndex = 6524; + reference = new EpsgCoordinateReferenceRecord(28352, (EpsgCoordinateSystemKind)2, 4705); + return true; + case 28353: + cacheIndex = 6525; + reference = new EpsgCoordinateReferenceRecord(28353, (EpsgCoordinateSystemKind)2, 4706); + return true; + case 28354: + cacheIndex = 6526; + reference = new EpsgCoordinateReferenceRecord(28354, (EpsgCoordinateSystemKind)2, 4707); + return true; + case 28355: + cacheIndex = 6527; + reference = new EpsgCoordinateReferenceRecord(28355, (EpsgCoordinateSystemKind)2, 4708); + return true; + case 28356: + cacheIndex = 6528; + reference = new EpsgCoordinateReferenceRecord(28356, (EpsgCoordinateSystemKind)2, 4709); + return true; + case 28357: + cacheIndex = 6529; + reference = new EpsgCoordinateReferenceRecord(28357, (EpsgCoordinateSystemKind)2, 4710); + return true; + case 28358: + cacheIndex = 6530; + reference = new EpsgCoordinateReferenceRecord(28358, (EpsgCoordinateSystemKind)2, 4711); + return true; + case 28404: + cacheIndex = 6531; + reference = new EpsgCoordinateReferenceRecord(28404, (EpsgCoordinateSystemKind)2, 4712); + return true; + case 28405: + cacheIndex = 6532; + reference = new EpsgCoordinateReferenceRecord(28405, (EpsgCoordinateSystemKind)2, 4713); + return true; + case 28406: + cacheIndex = 6533; + reference = new EpsgCoordinateReferenceRecord(28406, (EpsgCoordinateSystemKind)2, 4714); + return true; + case 28407: + cacheIndex = 6534; + reference = new EpsgCoordinateReferenceRecord(28407, (EpsgCoordinateSystemKind)2, 4715); + return true; + case 28408: + cacheIndex = 6535; + reference = new EpsgCoordinateReferenceRecord(28408, (EpsgCoordinateSystemKind)2, 4716); + return true; + case 28409: + cacheIndex = 6536; + reference = new EpsgCoordinateReferenceRecord(28409, (EpsgCoordinateSystemKind)2, 4717); + return true; + case 28410: + cacheIndex = 6537; + reference = new EpsgCoordinateReferenceRecord(28410, (EpsgCoordinateSystemKind)2, 4718); + return true; + case 28411: + cacheIndex = 6538; + reference = new EpsgCoordinateReferenceRecord(28411, (EpsgCoordinateSystemKind)2, 4719); + return true; + case 28412: + cacheIndex = 6539; + reference = new EpsgCoordinateReferenceRecord(28412, (EpsgCoordinateSystemKind)2, 4720); + return true; + case 28413: + cacheIndex = 6540; + reference = new EpsgCoordinateReferenceRecord(28413, (EpsgCoordinateSystemKind)2, 4721); + return true; + case 28414: + cacheIndex = 6541; + reference = new EpsgCoordinateReferenceRecord(28414, (EpsgCoordinateSystemKind)2, 4722); + return true; + case 28415: + cacheIndex = 6542; + reference = new EpsgCoordinateReferenceRecord(28415, (EpsgCoordinateSystemKind)2, 4723); + return true; + case 28416: + cacheIndex = 6543; + reference = new EpsgCoordinateReferenceRecord(28416, (EpsgCoordinateSystemKind)2, 4724); + return true; + case 28417: + cacheIndex = 6544; + reference = new EpsgCoordinateReferenceRecord(28417, (EpsgCoordinateSystemKind)2, 4725); + return true; + case 28418: + cacheIndex = 6545; + reference = new EpsgCoordinateReferenceRecord(28418, (EpsgCoordinateSystemKind)2, 4726); + return true; + case 28419: + cacheIndex = 6546; + reference = new EpsgCoordinateReferenceRecord(28419, (EpsgCoordinateSystemKind)2, 4727); + return true; + case 28420: + cacheIndex = 6547; + reference = new EpsgCoordinateReferenceRecord(28420, (EpsgCoordinateSystemKind)2, 4728); + return true; + case 28421: + cacheIndex = 6548; + reference = new EpsgCoordinateReferenceRecord(28421, (EpsgCoordinateSystemKind)2, 4729); + return true; + case 28422: + cacheIndex = 6549; + reference = new EpsgCoordinateReferenceRecord(28422, (EpsgCoordinateSystemKind)2, 4730); + return true; + case 28423: + cacheIndex = 6550; + reference = new EpsgCoordinateReferenceRecord(28423, (EpsgCoordinateSystemKind)2, 4731); + return true; + case 28424: + cacheIndex = 6551; + reference = new EpsgCoordinateReferenceRecord(28424, (EpsgCoordinateSystemKind)2, 4732); + return true; + case 28425: + cacheIndex = 6552; + reference = new EpsgCoordinateReferenceRecord(28425, (EpsgCoordinateSystemKind)2, 4733); + return true; + case 28426: + cacheIndex = 6553; + reference = new EpsgCoordinateReferenceRecord(28426, (EpsgCoordinateSystemKind)2, 4734); + return true; + case 28427: + cacheIndex = 6554; + reference = new EpsgCoordinateReferenceRecord(28427, (EpsgCoordinateSystemKind)2, 4735); + return true; + case 28428: + cacheIndex = 6555; + reference = new EpsgCoordinateReferenceRecord(28428, (EpsgCoordinateSystemKind)2, 4736); + return true; + case 28429: + cacheIndex = 6556; + reference = new EpsgCoordinateReferenceRecord(28429, (EpsgCoordinateSystemKind)2, 4737); + return true; + case 28430: + cacheIndex = 6557; + reference = new EpsgCoordinateReferenceRecord(28430, (EpsgCoordinateSystemKind)2, 4738); + return true; + case 28431: + cacheIndex = 6558; + reference = new EpsgCoordinateReferenceRecord(28431, (EpsgCoordinateSystemKind)2, 4739); + return true; + case 28432: + cacheIndex = 6559; + reference = new EpsgCoordinateReferenceRecord(28432, (EpsgCoordinateSystemKind)2, 4740); + return true; + case 28600: + cacheIndex = 6560; + reference = new EpsgCoordinateReferenceRecord(28600, (EpsgCoordinateSystemKind)2, 4741); + return true; + case 28991: + cacheIndex = 6561; + reference = new EpsgCoordinateReferenceRecord(28991, (EpsgCoordinateSystemKind)2, 4742); + return true; + case 28992: + cacheIndex = 6562; + reference = new EpsgCoordinateReferenceRecord(28992, (EpsgCoordinateSystemKind)2, 4743); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket29(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 29101: + cacheIndex = 6563; + reference = new EpsgCoordinateReferenceRecord(29101, (EpsgCoordinateSystemKind)2, 4744); + return true; + case 29168: + cacheIndex = 6564; + reference = new EpsgCoordinateReferenceRecord(29168, (EpsgCoordinateSystemKind)2, 4745); + return true; + case 29169: + cacheIndex = 6565; + reference = new EpsgCoordinateReferenceRecord(29169, (EpsgCoordinateSystemKind)2, 4746); + return true; + case 29170: + cacheIndex = 6566; + reference = new EpsgCoordinateReferenceRecord(29170, (EpsgCoordinateSystemKind)2, 4747); + return true; + case 29171: + cacheIndex = 6567; + reference = new EpsgCoordinateReferenceRecord(29171, (EpsgCoordinateSystemKind)2, 4748); + return true; + case 29172: + cacheIndex = 6568; + reference = new EpsgCoordinateReferenceRecord(29172, (EpsgCoordinateSystemKind)2, 4749); + return true; + case 29187: + cacheIndex = 6569; + reference = new EpsgCoordinateReferenceRecord(29187, (EpsgCoordinateSystemKind)2, 4750); + return true; + case 29188: + cacheIndex = 6570; + reference = new EpsgCoordinateReferenceRecord(29188, (EpsgCoordinateSystemKind)2, 4751); + return true; + case 29189: + cacheIndex = 6571; + reference = new EpsgCoordinateReferenceRecord(29189, (EpsgCoordinateSystemKind)2, 4752); + return true; + case 29190: + cacheIndex = 6572; + reference = new EpsgCoordinateReferenceRecord(29190, (EpsgCoordinateSystemKind)2, 4753); + return true; + case 29191: + cacheIndex = 6573; + reference = new EpsgCoordinateReferenceRecord(29191, (EpsgCoordinateSystemKind)2, 4754); + return true; + case 29192: + cacheIndex = 6574; + reference = new EpsgCoordinateReferenceRecord(29192, (EpsgCoordinateSystemKind)2, 4755); + return true; + case 29193: + cacheIndex = 6575; + reference = new EpsgCoordinateReferenceRecord(29193, (EpsgCoordinateSystemKind)2, 4756); + return true; + case 29194: + cacheIndex = 6576; + reference = new EpsgCoordinateReferenceRecord(29194, (EpsgCoordinateSystemKind)2, 4757); + return true; + case 29195: + cacheIndex = 6577; + reference = new EpsgCoordinateReferenceRecord(29195, (EpsgCoordinateSystemKind)2, 4758); + return true; + case 29220: + cacheIndex = 6578; + reference = new EpsgCoordinateReferenceRecord(29220, (EpsgCoordinateSystemKind)2, 4759); + return true; + case 29221: + cacheIndex = 6579; + reference = new EpsgCoordinateReferenceRecord(29221, (EpsgCoordinateSystemKind)2, 4760); + return true; + case 29333: + cacheIndex = 6580; + reference = new EpsgCoordinateReferenceRecord(29333, (EpsgCoordinateSystemKind)2, 4761); + return true; + case 29371: + cacheIndex = 6581; + reference = new EpsgCoordinateReferenceRecord(29371, (EpsgCoordinateSystemKind)2, 4762); + return true; + case 29373: + cacheIndex = 6582; + reference = new EpsgCoordinateReferenceRecord(29373, (EpsgCoordinateSystemKind)2, 4763); + return true; + case 29375: + cacheIndex = 6583; + reference = new EpsgCoordinateReferenceRecord(29375, (EpsgCoordinateSystemKind)2, 4764); + return true; + case 29377: + cacheIndex = 6584; + reference = new EpsgCoordinateReferenceRecord(29377, (EpsgCoordinateSystemKind)2, 4765); + return true; + case 29379: + cacheIndex = 6585; + reference = new EpsgCoordinateReferenceRecord(29379, (EpsgCoordinateSystemKind)2, 4766); + return true; + case 29381: + cacheIndex = 6586; + reference = new EpsgCoordinateReferenceRecord(29381, (EpsgCoordinateSystemKind)2, 4767); + return true; + case 29383: + cacheIndex = 6587; + reference = new EpsgCoordinateReferenceRecord(29383, (EpsgCoordinateSystemKind)2, 4768); + return true; + case 29385: + cacheIndex = 6588; + reference = new EpsgCoordinateReferenceRecord(29385, (EpsgCoordinateSystemKind)2, 4769); + return true; + case 29701: + cacheIndex = 6589; + reference = new EpsgCoordinateReferenceRecord(29701, (EpsgCoordinateSystemKind)2, 4770); + return true; + case 29702: + cacheIndex = 6590; + reference = new EpsgCoordinateReferenceRecord(29702, (EpsgCoordinateSystemKind)2, 4771); + return true; + case 29738: + cacheIndex = 6591; + reference = new EpsgCoordinateReferenceRecord(29738, (EpsgCoordinateSystemKind)2, 4772); + return true; + case 29739: + cacheIndex = 6592; + reference = new EpsgCoordinateReferenceRecord(29739, (EpsgCoordinateSystemKind)2, 4773); + return true; + case 29849: + cacheIndex = 6593; + reference = new EpsgCoordinateReferenceRecord(29849, (EpsgCoordinateSystemKind)2, 4774); + return true; + case 29850: + cacheIndex = 6594; + reference = new EpsgCoordinateReferenceRecord(29850, (EpsgCoordinateSystemKind)2, 4775); + return true; + case 29871: + cacheIndex = 6595; + reference = new EpsgCoordinateReferenceRecord(29871, (EpsgCoordinateSystemKind)2, 4776); + return true; + case 29872: + cacheIndex = 6596; + reference = new EpsgCoordinateReferenceRecord(29872, (EpsgCoordinateSystemKind)2, 4777); + return true; + case 29873: + cacheIndex = 6597; + reference = new EpsgCoordinateReferenceRecord(29873, (EpsgCoordinateSystemKind)2, 4778); + return true; + case 29874: + cacheIndex = 6598; + reference = new EpsgCoordinateReferenceRecord(29874, (EpsgCoordinateSystemKind)2, 4779); + return true; + case 29901: + cacheIndex = 6599; + reference = new EpsgCoordinateReferenceRecord(29901, (EpsgCoordinateSystemKind)2, 4780); + return true; + case 29902: + cacheIndex = 6600; + reference = new EpsgCoordinateReferenceRecord(29902, (EpsgCoordinateSystemKind)2, 4781); + return true; + case 29903: + cacheIndex = 6601; + reference = new EpsgCoordinateReferenceRecord(29903, (EpsgCoordinateSystemKind)2, 4782); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket30(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 30161: + cacheIndex = 6602; + reference = new EpsgCoordinateReferenceRecord(30161, (EpsgCoordinateSystemKind)2, 4783); + return true; + case 30162: + cacheIndex = 6603; + reference = new EpsgCoordinateReferenceRecord(30162, (EpsgCoordinateSystemKind)2, 4784); + return true; + case 30163: + cacheIndex = 6604; + reference = new EpsgCoordinateReferenceRecord(30163, (EpsgCoordinateSystemKind)2, 4785); + return true; + case 30164: + cacheIndex = 6605; + reference = new EpsgCoordinateReferenceRecord(30164, (EpsgCoordinateSystemKind)2, 4786); + return true; + case 30165: + cacheIndex = 6606; + reference = new EpsgCoordinateReferenceRecord(30165, (EpsgCoordinateSystemKind)2, 4787); + return true; + case 30166: + cacheIndex = 6607; + reference = new EpsgCoordinateReferenceRecord(30166, (EpsgCoordinateSystemKind)2, 4788); + return true; + case 30167: + cacheIndex = 6608; + reference = new EpsgCoordinateReferenceRecord(30167, (EpsgCoordinateSystemKind)2, 4789); + return true; + case 30168: + cacheIndex = 6609; + reference = new EpsgCoordinateReferenceRecord(30168, (EpsgCoordinateSystemKind)2, 4790); + return true; + case 30169: + cacheIndex = 6610; + reference = new EpsgCoordinateReferenceRecord(30169, (EpsgCoordinateSystemKind)2, 4791); + return true; + case 30170: + cacheIndex = 6611; + reference = new EpsgCoordinateReferenceRecord(30170, (EpsgCoordinateSystemKind)2, 4792); + return true; + case 30171: + cacheIndex = 6612; + reference = new EpsgCoordinateReferenceRecord(30171, (EpsgCoordinateSystemKind)2, 4793); + return true; + case 30172: + cacheIndex = 6613; + reference = new EpsgCoordinateReferenceRecord(30172, (EpsgCoordinateSystemKind)2, 4794); + return true; + case 30173: + cacheIndex = 6614; + reference = new EpsgCoordinateReferenceRecord(30173, (EpsgCoordinateSystemKind)2, 4795); + return true; + case 30174: + cacheIndex = 6615; + reference = new EpsgCoordinateReferenceRecord(30174, (EpsgCoordinateSystemKind)2, 4796); + return true; + case 30175: + cacheIndex = 6616; + reference = new EpsgCoordinateReferenceRecord(30175, (EpsgCoordinateSystemKind)2, 4797); + return true; + case 30176: + cacheIndex = 6617; + reference = new EpsgCoordinateReferenceRecord(30176, (EpsgCoordinateSystemKind)2, 4798); + return true; + case 30177: + cacheIndex = 6618; + reference = new EpsgCoordinateReferenceRecord(30177, (EpsgCoordinateSystemKind)2, 4799); + return true; + case 30178: + cacheIndex = 6619; + reference = new EpsgCoordinateReferenceRecord(30178, (EpsgCoordinateSystemKind)2, 4800); + return true; + case 30179: + cacheIndex = 6620; + reference = new EpsgCoordinateReferenceRecord(30179, (EpsgCoordinateSystemKind)2, 4801); + return true; + case 30200: + cacheIndex = 6621; + reference = new EpsgCoordinateReferenceRecord(30200, (EpsgCoordinateSystemKind)2, 4802); + return true; + case 30339: + cacheIndex = 6622; + reference = new EpsgCoordinateReferenceRecord(30339, (EpsgCoordinateSystemKind)2, 4803); + return true; + case 30340: + cacheIndex = 6623; + reference = new EpsgCoordinateReferenceRecord(30340, (EpsgCoordinateSystemKind)2, 4804); + return true; + case 30491: + cacheIndex = 6624; + reference = new EpsgCoordinateReferenceRecord(30491, (EpsgCoordinateSystemKind)2, 4805); + return true; + case 30492: + cacheIndex = 6625; + reference = new EpsgCoordinateReferenceRecord(30492, (EpsgCoordinateSystemKind)2, 4806); + return true; + case 30493: + cacheIndex = 6626; + reference = new EpsgCoordinateReferenceRecord(30493, (EpsgCoordinateSystemKind)2, 4807); + return true; + case 30494: + cacheIndex = 6627; + reference = new EpsgCoordinateReferenceRecord(30494, (EpsgCoordinateSystemKind)2, 4808); + return true; + case 30729: + cacheIndex = 6628; + reference = new EpsgCoordinateReferenceRecord(30729, (EpsgCoordinateSystemKind)2, 4809); + return true; + case 30730: + cacheIndex = 6629; + reference = new EpsgCoordinateReferenceRecord(30730, (EpsgCoordinateSystemKind)2, 4810); + return true; + case 30731: + cacheIndex = 6630; + reference = new EpsgCoordinateReferenceRecord(30731, (EpsgCoordinateSystemKind)2, 4811); + return true; + case 30732: + cacheIndex = 6631; + reference = new EpsgCoordinateReferenceRecord(30732, (EpsgCoordinateSystemKind)2, 4812); + return true; + case 30791: + cacheIndex = 6632; + reference = new EpsgCoordinateReferenceRecord(30791, (EpsgCoordinateSystemKind)2, 4813); + return true; + case 30792: + cacheIndex = 6633; + reference = new EpsgCoordinateReferenceRecord(30792, (EpsgCoordinateSystemKind)2, 4814); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket31(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 31028: + cacheIndex = 6634; + reference = new EpsgCoordinateReferenceRecord(31028, (EpsgCoordinateSystemKind)2, 4815); + return true; + case 31121: + cacheIndex = 6635; + reference = new EpsgCoordinateReferenceRecord(31121, (EpsgCoordinateSystemKind)2, 4816); + return true; + case 31154: + cacheIndex = 6636; + reference = new EpsgCoordinateReferenceRecord(31154, (EpsgCoordinateSystemKind)2, 4817); + return true; + case 31170: + cacheIndex = 6637; + reference = new EpsgCoordinateReferenceRecord(31170, (EpsgCoordinateSystemKind)2, 4818); + return true; + case 31171: + cacheIndex = 6638; + reference = new EpsgCoordinateReferenceRecord(31171, (EpsgCoordinateSystemKind)2, 4819); + return true; + case 31251: + cacheIndex = 6639; + reference = new EpsgCoordinateReferenceRecord(31251, (EpsgCoordinateSystemKind)2, 4820); + return true; + case 31252: + cacheIndex = 6640; + reference = new EpsgCoordinateReferenceRecord(31252, (EpsgCoordinateSystemKind)2, 4821); + return true; + case 31253: + cacheIndex = 6641; + reference = new EpsgCoordinateReferenceRecord(31253, (EpsgCoordinateSystemKind)2, 4822); + return true; + case 31254: + cacheIndex = 6642; + reference = new EpsgCoordinateReferenceRecord(31254, (EpsgCoordinateSystemKind)2, 4823); + return true; + case 31255: + cacheIndex = 6643; + reference = new EpsgCoordinateReferenceRecord(31255, (EpsgCoordinateSystemKind)2, 4824); + return true; + case 31256: + cacheIndex = 6644; + reference = new EpsgCoordinateReferenceRecord(31256, (EpsgCoordinateSystemKind)2, 4825); + return true; + case 31257: + cacheIndex = 6645; + reference = new EpsgCoordinateReferenceRecord(31257, (EpsgCoordinateSystemKind)2, 4826); + return true; + case 31258: + cacheIndex = 6646; + reference = new EpsgCoordinateReferenceRecord(31258, (EpsgCoordinateSystemKind)2, 4827); + return true; + case 31259: + cacheIndex = 6647; + reference = new EpsgCoordinateReferenceRecord(31259, (EpsgCoordinateSystemKind)2, 4828); + return true; + case 31281: + cacheIndex = 6648; + reference = new EpsgCoordinateReferenceRecord(31281, (EpsgCoordinateSystemKind)2, 4829); + return true; + case 31282: + cacheIndex = 6649; + reference = new EpsgCoordinateReferenceRecord(31282, (EpsgCoordinateSystemKind)2, 4830); + return true; + case 31283: + cacheIndex = 6650; + reference = new EpsgCoordinateReferenceRecord(31283, (EpsgCoordinateSystemKind)2, 4831); + return true; + case 31284: + cacheIndex = 6651; + reference = new EpsgCoordinateReferenceRecord(31284, (EpsgCoordinateSystemKind)2, 4832); + return true; + case 31285: + cacheIndex = 6652; + reference = new EpsgCoordinateReferenceRecord(31285, (EpsgCoordinateSystemKind)2, 4833); + return true; + case 31286: + cacheIndex = 6653; + reference = new EpsgCoordinateReferenceRecord(31286, (EpsgCoordinateSystemKind)2, 4834); + return true; + case 31287: + cacheIndex = 6654; + reference = new EpsgCoordinateReferenceRecord(31287, (EpsgCoordinateSystemKind)2, 4835); + return true; + case 31288: + cacheIndex = 6655; + reference = new EpsgCoordinateReferenceRecord(31288, (EpsgCoordinateSystemKind)2, 4836); + return true; + case 31289: + cacheIndex = 6656; + reference = new EpsgCoordinateReferenceRecord(31289, (EpsgCoordinateSystemKind)2, 4837); + return true; + case 31290: + cacheIndex = 6657; + reference = new EpsgCoordinateReferenceRecord(31290, (EpsgCoordinateSystemKind)2, 4838); + return true; + case 31300: + cacheIndex = 6658; + reference = new EpsgCoordinateReferenceRecord(31300, (EpsgCoordinateSystemKind)2, 4839); + return true; + case 31370: + cacheIndex = 6659; + reference = new EpsgCoordinateReferenceRecord(31370, (EpsgCoordinateSystemKind)2, 4840); + return true; + case 31466: + cacheIndex = 6660; + reference = new EpsgCoordinateReferenceRecord(31466, (EpsgCoordinateSystemKind)2, 4841); + return true; + case 31467: + cacheIndex = 6661; + reference = new EpsgCoordinateReferenceRecord(31467, (EpsgCoordinateSystemKind)2, 4842); + return true; + case 31468: + cacheIndex = 6662; + reference = new EpsgCoordinateReferenceRecord(31468, (EpsgCoordinateSystemKind)2, 4843); + return true; + case 31469: + cacheIndex = 6663; + reference = new EpsgCoordinateReferenceRecord(31469, (EpsgCoordinateSystemKind)2, 4844); + return true; + case 31528: + cacheIndex = 6664; + reference = new EpsgCoordinateReferenceRecord(31528, (EpsgCoordinateSystemKind)2, 4845); + return true; + case 31529: + cacheIndex = 6665; + reference = new EpsgCoordinateReferenceRecord(31529, (EpsgCoordinateSystemKind)2, 4846); + return true; + case 31600: + cacheIndex = 6666; + reference = new EpsgCoordinateReferenceRecord(31600, (EpsgCoordinateSystemKind)2, 4847); + return true; + case 31838: + cacheIndex = 6667; + reference = new EpsgCoordinateReferenceRecord(31838, (EpsgCoordinateSystemKind)2, 4848); + return true; + case 31839: + cacheIndex = 6668; + reference = new EpsgCoordinateReferenceRecord(31839, (EpsgCoordinateSystemKind)2, 4849); + return true; + case 31901: + cacheIndex = 6669; + reference = new EpsgCoordinateReferenceRecord(31901, (EpsgCoordinateSystemKind)2, 4850); + return true; + case 31965: + cacheIndex = 6670; + reference = new EpsgCoordinateReferenceRecord(31965, (EpsgCoordinateSystemKind)2, 4851); + return true; + case 31966: + cacheIndex = 6671; + reference = new EpsgCoordinateReferenceRecord(31966, (EpsgCoordinateSystemKind)2, 4852); + return true; + case 31967: + cacheIndex = 6672; + reference = new EpsgCoordinateReferenceRecord(31967, (EpsgCoordinateSystemKind)2, 4853); + return true; + case 31968: + cacheIndex = 6673; + reference = new EpsgCoordinateReferenceRecord(31968, (EpsgCoordinateSystemKind)2, 4854); + return true; + case 31969: + cacheIndex = 6674; + reference = new EpsgCoordinateReferenceRecord(31969, (EpsgCoordinateSystemKind)2, 4855); + return true; + case 31970: + cacheIndex = 6675; + reference = new EpsgCoordinateReferenceRecord(31970, (EpsgCoordinateSystemKind)2, 4856); + return true; + case 31971: + cacheIndex = 6676; + reference = new EpsgCoordinateReferenceRecord(31971, (EpsgCoordinateSystemKind)2, 4857); + return true; + case 31972: + cacheIndex = 6677; + reference = new EpsgCoordinateReferenceRecord(31972, (EpsgCoordinateSystemKind)2, 4858); + return true; + case 31973: + cacheIndex = 6678; + reference = new EpsgCoordinateReferenceRecord(31973, (EpsgCoordinateSystemKind)2, 4859); + return true; + case 31974: + cacheIndex = 6679; + reference = new EpsgCoordinateReferenceRecord(31974, (EpsgCoordinateSystemKind)2, 4860); + return true; + case 31975: + cacheIndex = 6680; + reference = new EpsgCoordinateReferenceRecord(31975, (EpsgCoordinateSystemKind)2, 4861); + return true; + case 31976: + cacheIndex = 6681; + reference = new EpsgCoordinateReferenceRecord(31976, (EpsgCoordinateSystemKind)2, 4862); + return true; + case 31977: + cacheIndex = 6682; + reference = new EpsgCoordinateReferenceRecord(31977, (EpsgCoordinateSystemKind)2, 4863); + return true; + case 31978: + cacheIndex = 6683; + reference = new EpsgCoordinateReferenceRecord(31978, (EpsgCoordinateSystemKind)2, 4864); + return true; + case 31979: + cacheIndex = 6684; + reference = new EpsgCoordinateReferenceRecord(31979, (EpsgCoordinateSystemKind)2, 4865); + return true; + case 31980: + cacheIndex = 6685; + reference = new EpsgCoordinateReferenceRecord(31980, (EpsgCoordinateSystemKind)2, 4866); + return true; + case 31981: + cacheIndex = 6686; + reference = new EpsgCoordinateReferenceRecord(31981, (EpsgCoordinateSystemKind)2, 4867); + return true; + case 31982: + cacheIndex = 6687; + reference = new EpsgCoordinateReferenceRecord(31982, (EpsgCoordinateSystemKind)2, 4868); + return true; + case 31983: + cacheIndex = 6688; + reference = new EpsgCoordinateReferenceRecord(31983, (EpsgCoordinateSystemKind)2, 4869); + return true; + case 31984: + cacheIndex = 6689; + reference = new EpsgCoordinateReferenceRecord(31984, (EpsgCoordinateSystemKind)2, 4870); + return true; + case 31985: + cacheIndex = 6690; + reference = new EpsgCoordinateReferenceRecord(31985, (EpsgCoordinateSystemKind)2, 4871); + return true; + case 31986: + cacheIndex = 6691; + reference = new EpsgCoordinateReferenceRecord(31986, (EpsgCoordinateSystemKind)2, 4872); + return true; + case 31987: + cacheIndex = 6692; + reference = new EpsgCoordinateReferenceRecord(31987, (EpsgCoordinateSystemKind)2, 4873); + return true; + case 31988: + cacheIndex = 6693; + reference = new EpsgCoordinateReferenceRecord(31988, (EpsgCoordinateSystemKind)2, 4874); + return true; + case 31989: + cacheIndex = 6694; + reference = new EpsgCoordinateReferenceRecord(31989, (EpsgCoordinateSystemKind)2, 4875); + return true; + case 31990: + cacheIndex = 6695; + reference = new EpsgCoordinateReferenceRecord(31990, (EpsgCoordinateSystemKind)2, 4876); + return true; + case 31991: + cacheIndex = 6696; + reference = new EpsgCoordinateReferenceRecord(31991, (EpsgCoordinateSystemKind)2, 4877); + return true; + case 31992: + cacheIndex = 6697; + reference = new EpsgCoordinateReferenceRecord(31992, (EpsgCoordinateSystemKind)2, 4878); + return true; + case 31993: + cacheIndex = 6698; + reference = new EpsgCoordinateReferenceRecord(31993, (EpsgCoordinateSystemKind)2, 4879); + return true; + case 31994: + cacheIndex = 6699; + reference = new EpsgCoordinateReferenceRecord(31994, (EpsgCoordinateSystemKind)2, 4880); + return true; + case 31995: + cacheIndex = 6700; + reference = new EpsgCoordinateReferenceRecord(31995, (EpsgCoordinateSystemKind)2, 4881); + return true; + case 31996: + cacheIndex = 6701; + reference = new EpsgCoordinateReferenceRecord(31996, (EpsgCoordinateSystemKind)2, 4882); + return true; + case 31997: + cacheIndex = 6702; + reference = new EpsgCoordinateReferenceRecord(31997, (EpsgCoordinateSystemKind)2, 4883); + return true; + case 31998: + cacheIndex = 6703; + reference = new EpsgCoordinateReferenceRecord(31998, (EpsgCoordinateSystemKind)2, 4884); + return true; + case 31999: + cacheIndex = 6704; + reference = new EpsgCoordinateReferenceRecord(31999, (EpsgCoordinateSystemKind)2, 4885); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + private static bool TryGetCoordinateReferenceBucket32(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex) + { + switch (srid) + { + case 32000: + cacheIndex = 6705; + reference = new EpsgCoordinateReferenceRecord(32000, (EpsgCoordinateSystemKind)2, 4886); + return true; + case 32001: + cacheIndex = 6706; + reference = new EpsgCoordinateReferenceRecord(32001, (EpsgCoordinateSystemKind)2, 4887); + return true; + case 32002: + cacheIndex = 6707; + reference = new EpsgCoordinateReferenceRecord(32002, (EpsgCoordinateSystemKind)2, 4888); + return true; + case 32003: + cacheIndex = 6708; + reference = new EpsgCoordinateReferenceRecord(32003, (EpsgCoordinateSystemKind)2, 4889); + return true; + case 32005: + cacheIndex = 6709; + reference = new EpsgCoordinateReferenceRecord(32005, (EpsgCoordinateSystemKind)2, 4890); + return true; + case 32006: + cacheIndex = 6710; + reference = new EpsgCoordinateReferenceRecord(32006, (EpsgCoordinateSystemKind)2, 4891); + return true; + case 32007: + cacheIndex = 6711; + reference = new EpsgCoordinateReferenceRecord(32007, (EpsgCoordinateSystemKind)2, 4892); + return true; + case 32008: + cacheIndex = 6712; + reference = new EpsgCoordinateReferenceRecord(32008, (EpsgCoordinateSystemKind)2, 4893); + return true; + case 32009: + cacheIndex = 6713; + reference = new EpsgCoordinateReferenceRecord(32009, (EpsgCoordinateSystemKind)2, 4894); + return true; + case 32010: + cacheIndex = 6714; + reference = new EpsgCoordinateReferenceRecord(32010, (EpsgCoordinateSystemKind)2, 4895); + return true; + case 32011: + cacheIndex = 6715; + reference = new EpsgCoordinateReferenceRecord(32011, (EpsgCoordinateSystemKind)2, 4896); + return true; + case 32012: + cacheIndex = 6716; + reference = new EpsgCoordinateReferenceRecord(32012, (EpsgCoordinateSystemKind)2, 4897); + return true; + case 32013: + cacheIndex = 6717; + reference = new EpsgCoordinateReferenceRecord(32013, (EpsgCoordinateSystemKind)2, 4898); + return true; + case 32014: + cacheIndex = 6718; + reference = new EpsgCoordinateReferenceRecord(32014, (EpsgCoordinateSystemKind)2, 4899); + return true; + case 32015: + cacheIndex = 6719; + reference = new EpsgCoordinateReferenceRecord(32015, (EpsgCoordinateSystemKind)2, 4900); + return true; + case 32016: + cacheIndex = 6720; + reference = new EpsgCoordinateReferenceRecord(32016, (EpsgCoordinateSystemKind)2, 4901); + return true; + case 32017: + cacheIndex = 6721; + reference = new EpsgCoordinateReferenceRecord(32017, (EpsgCoordinateSystemKind)2, 4902); + return true; + case 32019: + cacheIndex = 6722; + reference = new EpsgCoordinateReferenceRecord(32019, (EpsgCoordinateSystemKind)2, 4903); + return true; + case 32020: + cacheIndex = 6723; + reference = new EpsgCoordinateReferenceRecord(32020, (EpsgCoordinateSystemKind)2, 4904); + return true; + case 32021: + cacheIndex = 6724; + reference = new EpsgCoordinateReferenceRecord(32021, (EpsgCoordinateSystemKind)2, 4905); + return true; + case 32022: + cacheIndex = 6725; + reference = new EpsgCoordinateReferenceRecord(32022, (EpsgCoordinateSystemKind)2, 4906); + return true; + case 32023: + cacheIndex = 6726; + reference = new EpsgCoordinateReferenceRecord(32023, (EpsgCoordinateSystemKind)2, 4907); + return true; + case 32024: + cacheIndex = 6727; + reference = new EpsgCoordinateReferenceRecord(32024, (EpsgCoordinateSystemKind)2, 4908); + return true; + case 32025: + cacheIndex = 6728; + reference = new EpsgCoordinateReferenceRecord(32025, (EpsgCoordinateSystemKind)2, 4909); + return true; + case 32026: + cacheIndex = 6729; + reference = new EpsgCoordinateReferenceRecord(32026, (EpsgCoordinateSystemKind)2, 4910); + return true; + case 32027: + cacheIndex = 6730; + reference = new EpsgCoordinateReferenceRecord(32027, (EpsgCoordinateSystemKind)2, 4911); + return true; + case 32028: + cacheIndex = 6731; + reference = new EpsgCoordinateReferenceRecord(32028, (EpsgCoordinateSystemKind)2, 4912); + return true; + case 32030: + cacheIndex = 6732; + reference = new EpsgCoordinateReferenceRecord(32030, (EpsgCoordinateSystemKind)2, 4913); + return true; + case 32031: + cacheIndex = 6733; + reference = new EpsgCoordinateReferenceRecord(32031, (EpsgCoordinateSystemKind)2, 4914); + return true; + case 32033: + cacheIndex = 6734; + reference = new EpsgCoordinateReferenceRecord(32033, (EpsgCoordinateSystemKind)2, 4915); + return true; + case 32034: + cacheIndex = 6735; + reference = new EpsgCoordinateReferenceRecord(32034, (EpsgCoordinateSystemKind)2, 4916); + return true; + case 32035: + cacheIndex = 6736; + reference = new EpsgCoordinateReferenceRecord(32035, (EpsgCoordinateSystemKind)2, 4917); + return true; + case 32037: + cacheIndex = 6737; + reference = new EpsgCoordinateReferenceRecord(32037, (EpsgCoordinateSystemKind)2, 4918); + return true; + case 32038: + cacheIndex = 6738; + reference = new EpsgCoordinateReferenceRecord(32038, (EpsgCoordinateSystemKind)2, 4919); + return true; + case 32039: + cacheIndex = 6739; + reference = new EpsgCoordinateReferenceRecord(32039, (EpsgCoordinateSystemKind)2, 4920); + return true; + case 32040: + cacheIndex = 6740; + reference = new EpsgCoordinateReferenceRecord(32040, (EpsgCoordinateSystemKind)2, 4921); + return true; + case 32041: + cacheIndex = 6741; + reference = new EpsgCoordinateReferenceRecord(32041, (EpsgCoordinateSystemKind)2, 4922); + return true; + case 32042: + cacheIndex = 6742; + reference = new EpsgCoordinateReferenceRecord(32042, (EpsgCoordinateSystemKind)2, 4923); + return true; + case 32043: + cacheIndex = 6743; + reference = new EpsgCoordinateReferenceRecord(32043, (EpsgCoordinateSystemKind)2, 4924); + return true; + case 32044: + cacheIndex = 6744; + reference = new EpsgCoordinateReferenceRecord(32044, (EpsgCoordinateSystemKind)2, 4925); + return true; + case 32045: + cacheIndex = 6745; + reference = new EpsgCoordinateReferenceRecord(32045, (EpsgCoordinateSystemKind)2, 4926); + return true; + case 32046: + cacheIndex = 6746; + reference = new EpsgCoordinateReferenceRecord(32046, (EpsgCoordinateSystemKind)2, 4927); + return true; + case 32047: + cacheIndex = 6747; + reference = new EpsgCoordinateReferenceRecord(32047, (EpsgCoordinateSystemKind)2, 4928); + return true; + case 32048: + cacheIndex = 6748; + reference = new EpsgCoordinateReferenceRecord(32048, (EpsgCoordinateSystemKind)2, 4929); + return true; + case 32049: + cacheIndex = 6749; + reference = new EpsgCoordinateReferenceRecord(32049, (EpsgCoordinateSystemKind)2, 4930); + return true; + case 32050: + cacheIndex = 6750; + reference = new EpsgCoordinateReferenceRecord(32050, (EpsgCoordinateSystemKind)2, 4931); + return true; + case 32051: + cacheIndex = 6751; + reference = new EpsgCoordinateReferenceRecord(32051, (EpsgCoordinateSystemKind)2, 4932); + return true; + case 32052: + cacheIndex = 6752; + reference = new EpsgCoordinateReferenceRecord(32052, (EpsgCoordinateSystemKind)2, 4933); + return true; + case 32053: + cacheIndex = 6753; + reference = new EpsgCoordinateReferenceRecord(32053, (EpsgCoordinateSystemKind)2, 4934); + return true; + case 32054: + cacheIndex = 6754; + reference = new EpsgCoordinateReferenceRecord(32054, (EpsgCoordinateSystemKind)2, 4935); + return true; + case 32055: + cacheIndex = 6755; + reference = new EpsgCoordinateReferenceRecord(32055, (EpsgCoordinateSystemKind)2, 4936); + return true; + case 32056: + cacheIndex = 6756; + reference = new EpsgCoordinateReferenceRecord(32056, (EpsgCoordinateSystemKind)2, 4937); + return true; + case 32057: + cacheIndex = 6757; + reference = new EpsgCoordinateReferenceRecord(32057, (EpsgCoordinateSystemKind)2, 4938); + return true; + case 32058: + cacheIndex = 6758; + reference = new EpsgCoordinateReferenceRecord(32058, (EpsgCoordinateSystemKind)2, 4939); + return true; + case 32064: + cacheIndex = 6759; + reference = new EpsgCoordinateReferenceRecord(32064, (EpsgCoordinateSystemKind)2, 4940); + return true; + case 32065: + cacheIndex = 6760; + reference = new EpsgCoordinateReferenceRecord(32065, (EpsgCoordinateSystemKind)2, 4941); + return true; + case 32066: + cacheIndex = 6761; + reference = new EpsgCoordinateReferenceRecord(32066, (EpsgCoordinateSystemKind)2, 4942); + return true; + case 32067: + cacheIndex = 6762; + reference = new EpsgCoordinateReferenceRecord(32067, (EpsgCoordinateSystemKind)2, 4943); + return true; + case 32081: + cacheIndex = 6763; + reference = new EpsgCoordinateReferenceRecord(32081, (EpsgCoordinateSystemKind)2, 4944); + return true; + case 32082: + cacheIndex = 6764; + reference = new EpsgCoordinateReferenceRecord(32082, (EpsgCoordinateSystemKind)2, 4945); + return true; + case 32083: + cacheIndex = 6765; + reference = new EpsgCoordinateReferenceRecord(32083, (EpsgCoordinateSystemKind)2, 4946); + return true; + case 32084: + cacheIndex = 6766; + reference = new EpsgCoordinateReferenceRecord(32084, (EpsgCoordinateSystemKind)2, 4947); + return true; + case 32085: + cacheIndex = 6767; + reference = new EpsgCoordinateReferenceRecord(32085, (EpsgCoordinateSystemKind)2, 4948); + return true; + case 32086: + cacheIndex = 6768; + reference = new EpsgCoordinateReferenceRecord(32086, (EpsgCoordinateSystemKind)2, 4949); + return true; + case 32098: + cacheIndex = 6769; + reference = new EpsgCoordinateReferenceRecord(32098, (EpsgCoordinateSystemKind)2, 4950); + return true; + case 32099: + cacheIndex = 6770; + reference = new EpsgCoordinateReferenceRecord(32099, (EpsgCoordinateSystemKind)2, 4951); + return true; + case 32100: + cacheIndex = 6771; + reference = new EpsgCoordinateReferenceRecord(32100, (EpsgCoordinateSystemKind)2, 4952); + return true; + case 32104: + cacheIndex = 6772; + reference = new EpsgCoordinateReferenceRecord(32104, (EpsgCoordinateSystemKind)2, 4953); + return true; + case 32107: + cacheIndex = 6773; + reference = new EpsgCoordinateReferenceRecord(32107, (EpsgCoordinateSystemKind)2, 4954); + return true; + case 32108: + cacheIndex = 6774; + reference = new EpsgCoordinateReferenceRecord(32108, (EpsgCoordinateSystemKind)2, 4955); + return true; + case 32109: + cacheIndex = 6775; + reference = new EpsgCoordinateReferenceRecord(32109, (EpsgCoordinateSystemKind)2, 4956); + return true; + case 32110: + cacheIndex = 6776; + reference = new EpsgCoordinateReferenceRecord(32110, (EpsgCoordinateSystemKind)2, 4957); + return true; + case 32111: + cacheIndex = 6777; + reference = new EpsgCoordinateReferenceRecord(32111, (EpsgCoordinateSystemKind)2, 4958); + return true; + case 32112: + cacheIndex = 6778; + reference = new EpsgCoordinateReferenceRecord(32112, (EpsgCoordinateSystemKind)2, 4959); + return true; + case 32113: + cacheIndex = 6779; + reference = new EpsgCoordinateReferenceRecord(32113, (EpsgCoordinateSystemKind)2, 4960); + return true; + case 32114: + cacheIndex = 6780; + reference = new EpsgCoordinateReferenceRecord(32114, (EpsgCoordinateSystemKind)2, 4961); + return true; + case 32115: + cacheIndex = 6781; + reference = new EpsgCoordinateReferenceRecord(32115, (EpsgCoordinateSystemKind)2, 4962); + return true; + case 32116: + cacheIndex = 6782; + reference = new EpsgCoordinateReferenceRecord(32116, (EpsgCoordinateSystemKind)2, 4963); + return true; + case 32117: + cacheIndex = 6783; + reference = new EpsgCoordinateReferenceRecord(32117, (EpsgCoordinateSystemKind)2, 4964); + return true; + case 32118: + cacheIndex = 6784; + reference = new EpsgCoordinateReferenceRecord(32118, (EpsgCoordinateSystemKind)2, 4965); + return true; + case 32119: + cacheIndex = 6785; + reference = new EpsgCoordinateReferenceRecord(32119, (EpsgCoordinateSystemKind)2, 4966); + return true; + case 32120: + cacheIndex = 6786; + reference = new EpsgCoordinateReferenceRecord(32120, (EpsgCoordinateSystemKind)2, 4967); + return true; + case 32121: + cacheIndex = 6787; + reference = new EpsgCoordinateReferenceRecord(32121, (EpsgCoordinateSystemKind)2, 4968); + return true; + case 32122: + cacheIndex = 6788; + reference = new EpsgCoordinateReferenceRecord(32122, (EpsgCoordinateSystemKind)2, 4969); + return true; + case 32123: + cacheIndex = 6789; + reference = new EpsgCoordinateReferenceRecord(32123, (EpsgCoordinateSystemKind)2, 4970); + return true; + case 32124: + cacheIndex = 6790; + reference = new EpsgCoordinateReferenceRecord(32124, (EpsgCoordinateSystemKind)2, 4971); + return true; + case 32125: + cacheIndex = 6791; + reference = new EpsgCoordinateReferenceRecord(32125, (EpsgCoordinateSystemKind)2, 4972); + return true; + case 32126: + cacheIndex = 6792; + reference = new EpsgCoordinateReferenceRecord(32126, (EpsgCoordinateSystemKind)2, 4973); + return true; + case 32127: + cacheIndex = 6793; + reference = new EpsgCoordinateReferenceRecord(32127, (EpsgCoordinateSystemKind)2, 4974); + return true; + case 32128: + cacheIndex = 6794; + reference = new EpsgCoordinateReferenceRecord(32128, (EpsgCoordinateSystemKind)2, 4975); + return true; + case 32129: + cacheIndex = 6795; + reference = new EpsgCoordinateReferenceRecord(32129, (EpsgCoordinateSystemKind)2, 4976); + return true; + case 32130: + cacheIndex = 6796; + reference = new EpsgCoordinateReferenceRecord(32130, (EpsgCoordinateSystemKind)2, 4977); + return true; + case 32133: + cacheIndex = 6797; + reference = new EpsgCoordinateReferenceRecord(32133, (EpsgCoordinateSystemKind)2, 4978); + return true; + case 32134: + cacheIndex = 6798; + reference = new EpsgCoordinateReferenceRecord(32134, (EpsgCoordinateSystemKind)2, 4979); + return true; + case 32135: + cacheIndex = 6799; + reference = new EpsgCoordinateReferenceRecord(32135, (EpsgCoordinateSystemKind)2, 4980); + return true; + case 32136: + cacheIndex = 6800; + reference = new EpsgCoordinateReferenceRecord(32136, (EpsgCoordinateSystemKind)2, 4981); + return true; + case 32137: + cacheIndex = 6801; + reference = new EpsgCoordinateReferenceRecord(32137, (EpsgCoordinateSystemKind)2, 4982); + return true; + case 32138: + cacheIndex = 6802; + reference = new EpsgCoordinateReferenceRecord(32138, (EpsgCoordinateSystemKind)2, 4983); + return true; + case 32139: + cacheIndex = 6803; + reference = new EpsgCoordinateReferenceRecord(32139, (EpsgCoordinateSystemKind)2, 4984); + return true; + case 32140: + cacheIndex = 6804; + reference = new EpsgCoordinateReferenceRecord(32140, (EpsgCoordinateSystemKind)2, 4985); + return true; + case 32141: + cacheIndex = 6805; + reference = new EpsgCoordinateReferenceRecord(32141, (EpsgCoordinateSystemKind)2, 4986); + return true; + case 32142: + cacheIndex = 6806; + reference = new EpsgCoordinateReferenceRecord(32142, (EpsgCoordinateSystemKind)2, 4987); + return true; + case 32143: + cacheIndex = 6807; + reference = new EpsgCoordinateReferenceRecord(32143, (EpsgCoordinateSystemKind)2, 4988); + return true; + case 32144: + cacheIndex = 6808; + reference = new EpsgCoordinateReferenceRecord(32144, (EpsgCoordinateSystemKind)2, 4989); + return true; + case 32145: + cacheIndex = 6809; + reference = new EpsgCoordinateReferenceRecord(32145, (EpsgCoordinateSystemKind)2, 4990); + return true; + case 32146: + cacheIndex = 6810; + reference = new EpsgCoordinateReferenceRecord(32146, (EpsgCoordinateSystemKind)2, 4991); + return true; + case 32147: + cacheIndex = 6811; + reference = new EpsgCoordinateReferenceRecord(32147, (EpsgCoordinateSystemKind)2, 4992); + return true; + case 32148: + cacheIndex = 6812; + reference = new EpsgCoordinateReferenceRecord(32148, (EpsgCoordinateSystemKind)2, 4993); + return true; + case 32149: + cacheIndex = 6813; + reference = new EpsgCoordinateReferenceRecord(32149, (EpsgCoordinateSystemKind)2, 4994); + return true; + case 32150: + cacheIndex = 6814; + reference = new EpsgCoordinateReferenceRecord(32150, (EpsgCoordinateSystemKind)2, 4995); + return true; + case 32151: + cacheIndex = 6815; + reference = new EpsgCoordinateReferenceRecord(32151, (EpsgCoordinateSystemKind)2, 4996); + return true; + case 32152: + cacheIndex = 6816; + reference = new EpsgCoordinateReferenceRecord(32152, (EpsgCoordinateSystemKind)2, 4997); + return true; + case 32153: + cacheIndex = 6817; + reference = new EpsgCoordinateReferenceRecord(32153, (EpsgCoordinateSystemKind)2, 4998); + return true; + case 32154: + cacheIndex = 6818; + reference = new EpsgCoordinateReferenceRecord(32154, (EpsgCoordinateSystemKind)2, 4999); + return true; + case 32155: + cacheIndex = 6819; + reference = new EpsgCoordinateReferenceRecord(32155, (EpsgCoordinateSystemKind)2, 5000); + return true; + case 32156: + cacheIndex = 6820; + reference = new EpsgCoordinateReferenceRecord(32156, (EpsgCoordinateSystemKind)2, 5001); + return true; + case 32157: + cacheIndex = 6821; + reference = new EpsgCoordinateReferenceRecord(32157, (EpsgCoordinateSystemKind)2, 5002); + return true; + case 32158: + cacheIndex = 6822; + reference = new EpsgCoordinateReferenceRecord(32158, (EpsgCoordinateSystemKind)2, 5003); + return true; + case 32159: + cacheIndex = 6823; + reference = new EpsgCoordinateReferenceRecord(32159, (EpsgCoordinateSystemKind)2, 5004); + return true; + case 32161: + cacheIndex = 6824; + reference = new EpsgCoordinateReferenceRecord(32161, (EpsgCoordinateSystemKind)2, 5005); + return true; + case 32164: + cacheIndex = 6825; + reference = new EpsgCoordinateReferenceRecord(32164, (EpsgCoordinateSystemKind)2, 5006); + return true; + case 32165: + cacheIndex = 6826; + reference = new EpsgCoordinateReferenceRecord(32165, (EpsgCoordinateSystemKind)2, 5007); + return true; + case 32166: + cacheIndex = 6827; + reference = new EpsgCoordinateReferenceRecord(32166, (EpsgCoordinateSystemKind)2, 5008); + return true; + case 32167: + cacheIndex = 6828; + reference = new EpsgCoordinateReferenceRecord(32167, (EpsgCoordinateSystemKind)2, 5009); + return true; + case 32181: + cacheIndex = 6829; + reference = new EpsgCoordinateReferenceRecord(32181, (EpsgCoordinateSystemKind)2, 5010); + return true; + case 32182: + cacheIndex = 6830; + reference = new EpsgCoordinateReferenceRecord(32182, (EpsgCoordinateSystemKind)2, 5011); + return true; + case 32183: + cacheIndex = 6831; + reference = new EpsgCoordinateReferenceRecord(32183, (EpsgCoordinateSystemKind)2, 5012); + return true; + case 32184: + cacheIndex = 6832; + reference = new EpsgCoordinateReferenceRecord(32184, (EpsgCoordinateSystemKind)2, 5013); + return true; + case 32185: + cacheIndex = 6833; + reference = new EpsgCoordinateReferenceRecord(32185, (EpsgCoordinateSystemKind)2, 5014); + return true; + case 32186: + cacheIndex = 6834; + reference = new EpsgCoordinateReferenceRecord(32186, (EpsgCoordinateSystemKind)2, 5015); + return true; + case 32187: + cacheIndex = 6835; + reference = new EpsgCoordinateReferenceRecord(32187, (EpsgCoordinateSystemKind)2, 5016); + return true; + case 32188: + cacheIndex = 6836; + reference = new EpsgCoordinateReferenceRecord(32188, (EpsgCoordinateSystemKind)2, 5017); + return true; + case 32189: + cacheIndex = 6837; + reference = new EpsgCoordinateReferenceRecord(32189, (EpsgCoordinateSystemKind)2, 5018); + return true; + case 32190: + cacheIndex = 6838; + reference = new EpsgCoordinateReferenceRecord(32190, (EpsgCoordinateSystemKind)2, 5019); + return true; + case 32191: + cacheIndex = 6839; + reference = new EpsgCoordinateReferenceRecord(32191, (EpsgCoordinateSystemKind)2, 5020); + return true; + case 32192: + cacheIndex = 6840; + reference = new EpsgCoordinateReferenceRecord(32192, (EpsgCoordinateSystemKind)2, 5021); + return true; + case 32193: + cacheIndex = 6841; + reference = new EpsgCoordinateReferenceRecord(32193, (EpsgCoordinateSystemKind)2, 5022); + return true; + case 32194: + cacheIndex = 6842; + reference = new EpsgCoordinateReferenceRecord(32194, (EpsgCoordinateSystemKind)2, 5023); + return true; + case 32195: + cacheIndex = 6843; + reference = new EpsgCoordinateReferenceRecord(32195, (EpsgCoordinateSystemKind)2, 5024); + return true; + case 32196: + cacheIndex = 6844; + reference = new EpsgCoordinateReferenceRecord(32196, (EpsgCoordinateSystemKind)2, 5025); + return true; + case 32197: + cacheIndex = 6845; + reference = new EpsgCoordinateReferenceRecord(32197, (EpsgCoordinateSystemKind)2, 5026); + return true; + case 32198: + cacheIndex = 6846; + reference = new EpsgCoordinateReferenceRecord(32198, (EpsgCoordinateSystemKind)2, 5027); + return true; + case 32199: + cacheIndex = 6847; + reference = new EpsgCoordinateReferenceRecord(32199, (EpsgCoordinateSystemKind)2, 5028); + return true; + case 32201: + cacheIndex = 6848; + reference = new EpsgCoordinateReferenceRecord(32201, (EpsgCoordinateSystemKind)2, 5029); + return true; + case 32202: + cacheIndex = 6849; + reference = new EpsgCoordinateReferenceRecord(32202, (EpsgCoordinateSystemKind)2, 5030); + return true; + case 32203: + cacheIndex = 6850; + reference = new EpsgCoordinateReferenceRecord(32203, (EpsgCoordinateSystemKind)2, 5031); + return true; + case 32204: + cacheIndex = 6851; + reference = new EpsgCoordinateReferenceRecord(32204, (EpsgCoordinateSystemKind)2, 5032); + return true; + case 32205: + cacheIndex = 6852; + reference = new EpsgCoordinateReferenceRecord(32205, (EpsgCoordinateSystemKind)2, 5033); + return true; + case 32206: + cacheIndex = 6853; + reference = new EpsgCoordinateReferenceRecord(32206, (EpsgCoordinateSystemKind)2, 5034); + return true; + case 32207: + cacheIndex = 6854; + reference = new EpsgCoordinateReferenceRecord(32207, (EpsgCoordinateSystemKind)2, 5035); + return true; + case 32208: + cacheIndex = 6855; + reference = new EpsgCoordinateReferenceRecord(32208, (EpsgCoordinateSystemKind)2, 5036); + return true; + case 32209: + cacheIndex = 6856; + reference = new EpsgCoordinateReferenceRecord(32209, (EpsgCoordinateSystemKind)2, 5037); + return true; + case 32210: + cacheIndex = 6857; + reference = new EpsgCoordinateReferenceRecord(32210, (EpsgCoordinateSystemKind)2, 5038); + return true; + case 32211: + cacheIndex = 6858; + reference = new EpsgCoordinateReferenceRecord(32211, (EpsgCoordinateSystemKind)2, 5039); + return true; + case 32212: + cacheIndex = 6859; + reference = new EpsgCoordinateReferenceRecord(32212, (EpsgCoordinateSystemKind)2, 5040); + return true; + case 32213: + cacheIndex = 6860; + reference = new EpsgCoordinateReferenceRecord(32213, (EpsgCoordinateSystemKind)2, 5041); + return true; + case 32214: + cacheIndex = 6861; + reference = new EpsgCoordinateReferenceRecord(32214, (EpsgCoordinateSystemKind)2, 5042); + return true; + case 32215: + cacheIndex = 6862; + reference = new EpsgCoordinateReferenceRecord(32215, (EpsgCoordinateSystemKind)2, 5043); + return true; + case 32216: + cacheIndex = 6863; + reference = new EpsgCoordinateReferenceRecord(32216, (EpsgCoordinateSystemKind)2, 5044); + return true; + case 32217: + cacheIndex = 6864; + reference = new EpsgCoordinateReferenceRecord(32217, (EpsgCoordinateSystemKind)2, 5045); + return true; + case 32218: + cacheIndex = 6865; + reference = new EpsgCoordinateReferenceRecord(32218, (EpsgCoordinateSystemKind)2, 5046); + return true; + case 32219: + cacheIndex = 6866; + reference = new EpsgCoordinateReferenceRecord(32219, (EpsgCoordinateSystemKind)2, 5047); + return true; + case 32220: + cacheIndex = 6867; + reference = new EpsgCoordinateReferenceRecord(32220, (EpsgCoordinateSystemKind)2, 5048); + return true; + case 32221: + cacheIndex = 6868; + reference = new EpsgCoordinateReferenceRecord(32221, (EpsgCoordinateSystemKind)2, 5049); + return true; + case 32222: + cacheIndex = 6869; + reference = new EpsgCoordinateReferenceRecord(32222, (EpsgCoordinateSystemKind)2, 5050); + return true; + case 32223: + cacheIndex = 6870; + reference = new EpsgCoordinateReferenceRecord(32223, (EpsgCoordinateSystemKind)2, 5051); + return true; + case 32224: + cacheIndex = 6871; + reference = new EpsgCoordinateReferenceRecord(32224, (EpsgCoordinateSystemKind)2, 5052); + return true; + case 32225: + cacheIndex = 6872; + reference = new EpsgCoordinateReferenceRecord(32225, (EpsgCoordinateSystemKind)2, 5053); + return true; + case 32226: + cacheIndex = 6873; + reference = new EpsgCoordinateReferenceRecord(32226, (EpsgCoordinateSystemKind)2, 5054); + return true; + case 32227: + cacheIndex = 6874; + reference = new EpsgCoordinateReferenceRecord(32227, (EpsgCoordinateSystemKind)2, 5055); + return true; + case 32228: + cacheIndex = 6875; + reference = new EpsgCoordinateReferenceRecord(32228, (EpsgCoordinateSystemKind)2, 5056); + return true; + case 32229: + cacheIndex = 6876; + reference = new EpsgCoordinateReferenceRecord(32229, (EpsgCoordinateSystemKind)2, 5057); + return true; + case 32230: + cacheIndex = 6877; + reference = new EpsgCoordinateReferenceRecord(32230, (EpsgCoordinateSystemKind)2, 5058); + return true; + case 32231: + cacheIndex = 6878; + reference = new EpsgCoordinateReferenceRecord(32231, (EpsgCoordinateSystemKind)2, 5059); + return true; + case 32232: + cacheIndex = 6879; + reference = new EpsgCoordinateReferenceRecord(32232, (EpsgCoordinateSystemKind)2, 5060); + return true; + case 32233: + cacheIndex = 6880; + reference = new EpsgCoordinateReferenceRecord(32233, (EpsgCoordinateSystemKind)2, 5061); + return true; + case 32234: + cacheIndex = 6881; + reference = new EpsgCoordinateReferenceRecord(32234, (EpsgCoordinateSystemKind)2, 5062); + return true; + case 32235: + cacheIndex = 6882; + reference = new EpsgCoordinateReferenceRecord(32235, (EpsgCoordinateSystemKind)2, 5063); + return true; + case 32236: + cacheIndex = 6883; + reference = new EpsgCoordinateReferenceRecord(32236, (EpsgCoordinateSystemKind)2, 5064); + return true; + case 32237: + cacheIndex = 6884; + reference = new EpsgCoordinateReferenceRecord(32237, (EpsgCoordinateSystemKind)2, 5065); + return true; + case 32238: + cacheIndex = 6885; + reference = new EpsgCoordinateReferenceRecord(32238, (EpsgCoordinateSystemKind)2, 5066); + return true; + case 32239: + cacheIndex = 6886; + reference = new EpsgCoordinateReferenceRecord(32239, (EpsgCoordinateSystemKind)2, 5067); + return true; + case 32240: + cacheIndex = 6887; + reference = new EpsgCoordinateReferenceRecord(32240, (EpsgCoordinateSystemKind)2, 5068); + return true; + case 32241: + cacheIndex = 6888; + reference = new EpsgCoordinateReferenceRecord(32241, (EpsgCoordinateSystemKind)2, 5069); + return true; + case 32242: + cacheIndex = 6889; + reference = new EpsgCoordinateReferenceRecord(32242, (EpsgCoordinateSystemKind)2, 5070); + return true; + case 32243: + cacheIndex = 6890; + reference = new EpsgCoordinateReferenceRecord(32243, (EpsgCoordinateSystemKind)2, 5071); + return true; + case 32244: + cacheIndex = 6891; + reference = new EpsgCoordinateReferenceRecord(32244, (EpsgCoordinateSystemKind)2, 5072); + return true; + case 32245: + cacheIndex = 6892; + reference = new EpsgCoordinateReferenceRecord(32245, (EpsgCoordinateSystemKind)2, 5073); + return true; + case 32246: + cacheIndex = 6893; + reference = new EpsgCoordinateReferenceRecord(32246, (EpsgCoordinateSystemKind)2, 5074); + return true; + case 32247: + cacheIndex = 6894; + reference = new EpsgCoordinateReferenceRecord(32247, (EpsgCoordinateSystemKind)2, 5075); + return true; + case 32248: + cacheIndex = 6895; + reference = new EpsgCoordinateReferenceRecord(32248, (EpsgCoordinateSystemKind)2, 5076); + return true; + case 32249: + cacheIndex = 6896; + reference = new EpsgCoordinateReferenceRecord(32249, (EpsgCoordinateSystemKind)2, 5077); + return true; + case 32250: + cacheIndex = 6897; + reference = new EpsgCoordinateReferenceRecord(32250, (EpsgCoordinateSystemKind)2, 5078); + return true; + case 32251: + cacheIndex = 6898; + reference = new EpsgCoordinateReferenceRecord(32251, (EpsgCoordinateSystemKind)2, 5079); + return true; + case 32252: + cacheIndex = 6899; + reference = new EpsgCoordinateReferenceRecord(32252, (EpsgCoordinateSystemKind)2, 5080); + return true; + case 32253: + cacheIndex = 6900; + reference = new EpsgCoordinateReferenceRecord(32253, (EpsgCoordinateSystemKind)2, 5081); + return true; + case 32254: + cacheIndex = 6901; + reference = new EpsgCoordinateReferenceRecord(32254, (EpsgCoordinateSystemKind)2, 5082); + return true; + case 32255: + cacheIndex = 6902; + reference = new EpsgCoordinateReferenceRecord(32255, (EpsgCoordinateSystemKind)2, 5083); + return true; + case 32256: + cacheIndex = 6903; + reference = new EpsgCoordinateReferenceRecord(32256, (EpsgCoordinateSystemKind)2, 5084); + return true; + case 32257: + cacheIndex = 6904; + reference = new EpsgCoordinateReferenceRecord(32257, (EpsgCoordinateSystemKind)2, 5085); + return true; + case 32258: + cacheIndex = 6905; + reference = new EpsgCoordinateReferenceRecord(32258, (EpsgCoordinateSystemKind)2, 5086); + return true; + case 32259: + cacheIndex = 6906; + reference = new EpsgCoordinateReferenceRecord(32259, (EpsgCoordinateSystemKind)2, 5087); + return true; + case 32260: + cacheIndex = 6907; + reference = new EpsgCoordinateReferenceRecord(32260, (EpsgCoordinateSystemKind)2, 5088); + return true; + case 32301: + cacheIndex = 6908; + reference = new EpsgCoordinateReferenceRecord(32301, (EpsgCoordinateSystemKind)2, 5089); + return true; + case 32302: + cacheIndex = 6909; + reference = new EpsgCoordinateReferenceRecord(32302, (EpsgCoordinateSystemKind)2, 5090); + return true; + case 32303: + cacheIndex = 6910; + reference = new EpsgCoordinateReferenceRecord(32303, (EpsgCoordinateSystemKind)2, 5091); + return true; + case 32304: + cacheIndex = 6911; + reference = new EpsgCoordinateReferenceRecord(32304, (EpsgCoordinateSystemKind)2, 5092); + return true; + case 32305: + cacheIndex = 6912; + reference = new EpsgCoordinateReferenceRecord(32305, (EpsgCoordinateSystemKind)2, 5093); + return true; + case 32306: + cacheIndex = 6913; + reference = new EpsgCoordinateReferenceRecord(32306, (EpsgCoordinateSystemKind)2, 5094); + return true; + case 32307: + cacheIndex = 6914; + reference = new EpsgCoordinateReferenceRecord(32307, (EpsgCoordinateSystemKind)2, 5095); + return true; + case 32308: + cacheIndex = 6915; + reference = new EpsgCoordinateReferenceRecord(32308, (EpsgCoordinateSystemKind)2, 5096); + return true; + case 32309: + cacheIndex = 6916; + reference = new EpsgCoordinateReferenceRecord(32309, (EpsgCoordinateSystemKind)2, 5097); + return true; + case 32310: + cacheIndex = 6917; + reference = new EpsgCoordinateReferenceRecord(32310, (EpsgCoordinateSystemKind)2, 5098); + return true; + case 32311: + cacheIndex = 6918; + reference = new EpsgCoordinateReferenceRecord(32311, (EpsgCoordinateSystemKind)2, 5099); + return true; + case 32312: + cacheIndex = 6919; + reference = new EpsgCoordinateReferenceRecord(32312, (EpsgCoordinateSystemKind)2, 5100); + return true; + case 32313: + cacheIndex = 6920; + reference = new EpsgCoordinateReferenceRecord(32313, (EpsgCoordinateSystemKind)2, 5101); + return true; + case 32314: + cacheIndex = 6921; + reference = new EpsgCoordinateReferenceRecord(32314, (EpsgCoordinateSystemKind)2, 5102); + return true; + case 32315: + cacheIndex = 6922; + reference = new EpsgCoordinateReferenceRecord(32315, (EpsgCoordinateSystemKind)2, 5103); + return true; + case 32316: + cacheIndex = 6923; + reference = new EpsgCoordinateReferenceRecord(32316, (EpsgCoordinateSystemKind)2, 5104); + return true; + case 32317: + cacheIndex = 6924; + reference = new EpsgCoordinateReferenceRecord(32317, (EpsgCoordinateSystemKind)2, 5105); + return true; + case 32318: + cacheIndex = 6925; + reference = new EpsgCoordinateReferenceRecord(32318, (EpsgCoordinateSystemKind)2, 5106); + return true; + case 32319: + cacheIndex = 6926; + reference = new EpsgCoordinateReferenceRecord(32319, (EpsgCoordinateSystemKind)2, 5107); + return true; + case 32320: + cacheIndex = 6927; + reference = new EpsgCoordinateReferenceRecord(32320, (EpsgCoordinateSystemKind)2, 5108); + return true; + case 32321: + cacheIndex = 6928; + reference = new EpsgCoordinateReferenceRecord(32321, (EpsgCoordinateSystemKind)2, 5109); + return true; + case 32322: + cacheIndex = 6929; + reference = new EpsgCoordinateReferenceRecord(32322, (EpsgCoordinateSystemKind)2, 5110); + return true; + case 32323: + cacheIndex = 6930; + reference = new EpsgCoordinateReferenceRecord(32323, (EpsgCoordinateSystemKind)2, 5111); + return true; + case 32324: + cacheIndex = 6931; + reference = new EpsgCoordinateReferenceRecord(32324, (EpsgCoordinateSystemKind)2, 5112); + return true; + case 32325: + cacheIndex = 6932; + reference = new EpsgCoordinateReferenceRecord(32325, (EpsgCoordinateSystemKind)2, 5113); + return true; + case 32326: + cacheIndex = 6933; + reference = new EpsgCoordinateReferenceRecord(32326, (EpsgCoordinateSystemKind)2, 5114); + return true; + case 32327: + cacheIndex = 6934; + reference = new EpsgCoordinateReferenceRecord(32327, (EpsgCoordinateSystemKind)2, 5115); + return true; + case 32328: + cacheIndex = 6935; + reference = new EpsgCoordinateReferenceRecord(32328, (EpsgCoordinateSystemKind)2, 5116); + return true; + case 32329: + cacheIndex = 6936; + reference = new EpsgCoordinateReferenceRecord(32329, (EpsgCoordinateSystemKind)2, 5117); + return true; + case 32330: + cacheIndex = 6937; + reference = new EpsgCoordinateReferenceRecord(32330, (EpsgCoordinateSystemKind)2, 5118); + return true; + case 32331: + cacheIndex = 6938; + reference = new EpsgCoordinateReferenceRecord(32331, (EpsgCoordinateSystemKind)2, 5119); + return true; + case 32332: + cacheIndex = 6939; + reference = new EpsgCoordinateReferenceRecord(32332, (EpsgCoordinateSystemKind)2, 5120); + return true; + case 32333: + cacheIndex = 6940; + reference = new EpsgCoordinateReferenceRecord(32333, (EpsgCoordinateSystemKind)2, 5121); + return true; + case 32334: + cacheIndex = 6941; + reference = new EpsgCoordinateReferenceRecord(32334, (EpsgCoordinateSystemKind)2, 5122); + return true; + case 32335: + cacheIndex = 6942; + reference = new EpsgCoordinateReferenceRecord(32335, (EpsgCoordinateSystemKind)2, 5123); + return true; + case 32336: + cacheIndex = 6943; + reference = new EpsgCoordinateReferenceRecord(32336, (EpsgCoordinateSystemKind)2, 5124); + return true; + case 32337: + cacheIndex = 6944; + reference = new EpsgCoordinateReferenceRecord(32337, (EpsgCoordinateSystemKind)2, 5125); + return true; + case 32338: + cacheIndex = 6945; + reference = new EpsgCoordinateReferenceRecord(32338, (EpsgCoordinateSystemKind)2, 5126); + return true; + case 32339: + cacheIndex = 6946; + reference = new EpsgCoordinateReferenceRecord(32339, (EpsgCoordinateSystemKind)2, 5127); + return true; + case 32340: + cacheIndex = 6947; + reference = new EpsgCoordinateReferenceRecord(32340, (EpsgCoordinateSystemKind)2, 5128); + return true; + case 32341: + cacheIndex = 6948; + reference = new EpsgCoordinateReferenceRecord(32341, (EpsgCoordinateSystemKind)2, 5129); + return true; + case 32342: + cacheIndex = 6949; + reference = new EpsgCoordinateReferenceRecord(32342, (EpsgCoordinateSystemKind)2, 5130); + return true; + case 32343: + cacheIndex = 6950; + reference = new EpsgCoordinateReferenceRecord(32343, (EpsgCoordinateSystemKind)2, 5131); + return true; + case 32344: + cacheIndex = 6951; + reference = new EpsgCoordinateReferenceRecord(32344, (EpsgCoordinateSystemKind)2, 5132); + return true; + case 32345: + cacheIndex = 6952; + reference = new EpsgCoordinateReferenceRecord(32345, (EpsgCoordinateSystemKind)2, 5133); + return true; + case 32346: + cacheIndex = 6953; + reference = new EpsgCoordinateReferenceRecord(32346, (EpsgCoordinateSystemKind)2, 5134); + return true; + case 32347: + cacheIndex = 6954; + reference = new EpsgCoordinateReferenceRecord(32347, (EpsgCoordinateSystemKind)2, 5135); + return true; + case 32348: + cacheIndex = 6955; + reference = new EpsgCoordinateReferenceRecord(32348, (EpsgCoordinateSystemKind)2, 5136); + return true; + case 32349: + cacheIndex = 6956; + reference = new EpsgCoordinateReferenceRecord(32349, (EpsgCoordinateSystemKind)2, 5137); + return true; + case 32350: + cacheIndex = 6957; + reference = new EpsgCoordinateReferenceRecord(32350, (EpsgCoordinateSystemKind)2, 5138); + return true; + case 32351: + cacheIndex = 6958; + reference = new EpsgCoordinateReferenceRecord(32351, (EpsgCoordinateSystemKind)2, 5139); + return true; + case 32352: + cacheIndex = 6959; + reference = new EpsgCoordinateReferenceRecord(32352, (EpsgCoordinateSystemKind)2, 5140); + return true; + case 32353: + cacheIndex = 6960; + reference = new EpsgCoordinateReferenceRecord(32353, (EpsgCoordinateSystemKind)2, 5141); + return true; + case 32354: + cacheIndex = 6961; + reference = new EpsgCoordinateReferenceRecord(32354, (EpsgCoordinateSystemKind)2, 5142); + return true; + case 32355: + cacheIndex = 6962; + reference = new EpsgCoordinateReferenceRecord(32355, (EpsgCoordinateSystemKind)2, 5143); + return true; + case 32356: + cacheIndex = 6963; + reference = new EpsgCoordinateReferenceRecord(32356, (EpsgCoordinateSystemKind)2, 5144); + return true; + case 32357: + cacheIndex = 6964; + reference = new EpsgCoordinateReferenceRecord(32357, (EpsgCoordinateSystemKind)2, 5145); + return true; + case 32358: + cacheIndex = 6965; + reference = new EpsgCoordinateReferenceRecord(32358, (EpsgCoordinateSystemKind)2, 5146); + return true; + case 32359: + cacheIndex = 6966; + reference = new EpsgCoordinateReferenceRecord(32359, (EpsgCoordinateSystemKind)2, 5147); + return true; + case 32360: + cacheIndex = 6967; + reference = new EpsgCoordinateReferenceRecord(32360, (EpsgCoordinateSystemKind)2, 5148); + return true; + case 32401: + cacheIndex = 6968; + reference = new EpsgCoordinateReferenceRecord(32401, (EpsgCoordinateSystemKind)2, 5149); + return true; + case 32402: + cacheIndex = 6969; + reference = new EpsgCoordinateReferenceRecord(32402, (EpsgCoordinateSystemKind)2, 5150); + return true; + case 32403: + cacheIndex = 6970; + reference = new EpsgCoordinateReferenceRecord(32403, (EpsgCoordinateSystemKind)2, 5151); + return true; + case 32404: + cacheIndex = 6971; + reference = new EpsgCoordinateReferenceRecord(32404, (EpsgCoordinateSystemKind)2, 5152); + return true; + case 32405: + cacheIndex = 6972; + reference = new EpsgCoordinateReferenceRecord(32405, (EpsgCoordinateSystemKind)2, 5153); + return true; + case 32406: + cacheIndex = 6973; + reference = new EpsgCoordinateReferenceRecord(32406, (EpsgCoordinateSystemKind)2, 5154); + return true; + case 32407: + cacheIndex = 6974; + reference = new EpsgCoordinateReferenceRecord(32407, (EpsgCoordinateSystemKind)2, 5155); + return true; + case 32408: + cacheIndex = 6975; + reference = new EpsgCoordinateReferenceRecord(32408, (EpsgCoordinateSystemKind)2, 5156); + return true; + case 32409: + cacheIndex = 6976; + reference = new EpsgCoordinateReferenceRecord(32409, (EpsgCoordinateSystemKind)2, 5157); + return true; + case 32410: + cacheIndex = 6977; + reference = new EpsgCoordinateReferenceRecord(32410, (EpsgCoordinateSystemKind)2, 5158); + return true; + case 32411: + cacheIndex = 6978; + reference = new EpsgCoordinateReferenceRecord(32411, (EpsgCoordinateSystemKind)2, 5159); + return true; + case 32412: + cacheIndex = 6979; + reference = new EpsgCoordinateReferenceRecord(32412, (EpsgCoordinateSystemKind)2, 5160); + return true; + case 32413: + cacheIndex = 6980; + reference = new EpsgCoordinateReferenceRecord(32413, (EpsgCoordinateSystemKind)2, 5161); + return true; + case 32414: + cacheIndex = 6981; + reference = new EpsgCoordinateReferenceRecord(32414, (EpsgCoordinateSystemKind)2, 5162); + return true; + case 32415: + cacheIndex = 6982; + reference = new EpsgCoordinateReferenceRecord(32415, (EpsgCoordinateSystemKind)2, 5163); + return true; + case 32416: + cacheIndex = 6983; + reference = new EpsgCoordinateReferenceRecord(32416, (EpsgCoordinateSystemKind)2, 5164); + return true; + case 32417: + cacheIndex = 6984; + reference = new EpsgCoordinateReferenceRecord(32417, (EpsgCoordinateSystemKind)2, 5165); + return true; + case 32418: + cacheIndex = 6985; + reference = new EpsgCoordinateReferenceRecord(32418, (EpsgCoordinateSystemKind)2, 5166); + return true; + case 32419: + cacheIndex = 6986; + reference = new EpsgCoordinateReferenceRecord(32419, (EpsgCoordinateSystemKind)2, 5167); + return true; + case 32420: + cacheIndex = 6987; + reference = new EpsgCoordinateReferenceRecord(32420, (EpsgCoordinateSystemKind)2, 5168); + return true; + case 32421: + cacheIndex = 6988; + reference = new EpsgCoordinateReferenceRecord(32421, (EpsgCoordinateSystemKind)2, 5169); + return true; + case 32422: + cacheIndex = 6989; + reference = new EpsgCoordinateReferenceRecord(32422, (EpsgCoordinateSystemKind)2, 5170); + return true; + case 32423: + cacheIndex = 6990; + reference = new EpsgCoordinateReferenceRecord(32423, (EpsgCoordinateSystemKind)2, 5171); + return true; + case 32424: + cacheIndex = 6991; + reference = new EpsgCoordinateReferenceRecord(32424, (EpsgCoordinateSystemKind)2, 5172); + return true; + case 32425: + cacheIndex = 6992; + reference = new EpsgCoordinateReferenceRecord(32425, (EpsgCoordinateSystemKind)2, 5173); + return true; + case 32426: + cacheIndex = 6993; + reference = new EpsgCoordinateReferenceRecord(32426, (EpsgCoordinateSystemKind)2, 5174); + return true; + case 32427: + cacheIndex = 6994; + reference = new EpsgCoordinateReferenceRecord(32427, (EpsgCoordinateSystemKind)2, 5175); + return true; + case 32428: + cacheIndex = 6995; + reference = new EpsgCoordinateReferenceRecord(32428, (EpsgCoordinateSystemKind)2, 5176); + return true; + case 32429: + cacheIndex = 6996; + reference = new EpsgCoordinateReferenceRecord(32429, (EpsgCoordinateSystemKind)2, 5177); + return true; + case 32430: + cacheIndex = 6997; + reference = new EpsgCoordinateReferenceRecord(32430, (EpsgCoordinateSystemKind)2, 5178); + return true; + case 32431: + cacheIndex = 6998; + reference = new EpsgCoordinateReferenceRecord(32431, (EpsgCoordinateSystemKind)2, 5179); + return true; + case 32432: + cacheIndex = 6999; + reference = new EpsgCoordinateReferenceRecord(32432, (EpsgCoordinateSystemKind)2, 5180); + return true; + case 32433: + cacheIndex = 7000; + reference = new EpsgCoordinateReferenceRecord(32433, (EpsgCoordinateSystemKind)2, 5181); + return true; + case 32434: + cacheIndex = 7001; + reference = new EpsgCoordinateReferenceRecord(32434, (EpsgCoordinateSystemKind)2, 5182); + return true; + case 32435: + cacheIndex = 7002; + reference = new EpsgCoordinateReferenceRecord(32435, (EpsgCoordinateSystemKind)2, 5183); + return true; + case 32436: + cacheIndex = 7003; + reference = new EpsgCoordinateReferenceRecord(32436, (EpsgCoordinateSystemKind)2, 5184); + return true; + case 32437: + cacheIndex = 7004; + reference = new EpsgCoordinateReferenceRecord(32437, (EpsgCoordinateSystemKind)2, 5185); + return true; + case 32438: + cacheIndex = 7005; + reference = new EpsgCoordinateReferenceRecord(32438, (EpsgCoordinateSystemKind)2, 5186); + return true; + case 32439: + cacheIndex = 7006; + reference = new EpsgCoordinateReferenceRecord(32439, (EpsgCoordinateSystemKind)2, 5187); + return true; + case 32440: + cacheIndex = 7007; + reference = new EpsgCoordinateReferenceRecord(32440, (EpsgCoordinateSystemKind)2, 5188); + return true; + case 32441: + cacheIndex = 7008; + reference = new EpsgCoordinateReferenceRecord(32441, (EpsgCoordinateSystemKind)2, 5189); + return true; + case 32442: + cacheIndex = 7009; + reference = new EpsgCoordinateReferenceRecord(32442, (EpsgCoordinateSystemKind)2, 5190); + return true; + case 32443: + cacheIndex = 7010; + reference = new EpsgCoordinateReferenceRecord(32443, (EpsgCoordinateSystemKind)2, 5191); + return true; + case 32444: + cacheIndex = 7011; + reference = new EpsgCoordinateReferenceRecord(32444, (EpsgCoordinateSystemKind)2, 5192); + return true; + case 32445: + cacheIndex = 7012; + reference = new EpsgCoordinateReferenceRecord(32445, (EpsgCoordinateSystemKind)2, 5193); + return true; + case 32446: + cacheIndex = 7013; + reference = new EpsgCoordinateReferenceRecord(32446, (EpsgCoordinateSystemKind)2, 5194); + return true; + case 32447: + cacheIndex = 7014; + reference = new EpsgCoordinateReferenceRecord(32447, (EpsgCoordinateSystemKind)2, 5195); + return true; + case 32448: + cacheIndex = 7015; + reference = new EpsgCoordinateReferenceRecord(32448, (EpsgCoordinateSystemKind)2, 5196); + return true; + case 32449: + cacheIndex = 7016; + reference = new EpsgCoordinateReferenceRecord(32449, (EpsgCoordinateSystemKind)2, 5197); + return true; + case 32450: + cacheIndex = 7017; + reference = new EpsgCoordinateReferenceRecord(32450, (EpsgCoordinateSystemKind)2, 5198); + return true; + case 32451: + cacheIndex = 7018; + reference = new EpsgCoordinateReferenceRecord(32451, (EpsgCoordinateSystemKind)2, 5199); + return true; + case 32452: + cacheIndex = 7019; + reference = new EpsgCoordinateReferenceRecord(32452, (EpsgCoordinateSystemKind)2, 5200); + return true; + case 32453: + cacheIndex = 7020; + reference = new EpsgCoordinateReferenceRecord(32453, (EpsgCoordinateSystemKind)2, 5201); + return true; + case 32454: + cacheIndex = 7021; + reference = new EpsgCoordinateReferenceRecord(32454, (EpsgCoordinateSystemKind)2, 5202); + return true; + case 32455: + cacheIndex = 7022; + reference = new EpsgCoordinateReferenceRecord(32455, (EpsgCoordinateSystemKind)2, 5203); + return true; + case 32456: + cacheIndex = 7023; + reference = new EpsgCoordinateReferenceRecord(32456, (EpsgCoordinateSystemKind)2, 5204); + return true; + case 32457: + cacheIndex = 7024; + reference = new EpsgCoordinateReferenceRecord(32457, (EpsgCoordinateSystemKind)2, 5205); + return true; + case 32458: + cacheIndex = 7025; + reference = new EpsgCoordinateReferenceRecord(32458, (EpsgCoordinateSystemKind)2, 5206); + return true; + case 32459: + cacheIndex = 7026; + reference = new EpsgCoordinateReferenceRecord(32459, (EpsgCoordinateSystemKind)2, 5207); + return true; + case 32460: + cacheIndex = 7027; + reference = new EpsgCoordinateReferenceRecord(32460, (EpsgCoordinateSystemKind)2, 5208); + return true; + case 32501: + cacheIndex = 7028; + reference = new EpsgCoordinateReferenceRecord(32501, (EpsgCoordinateSystemKind)2, 5209); + return true; + case 32502: + cacheIndex = 7029; + reference = new EpsgCoordinateReferenceRecord(32502, (EpsgCoordinateSystemKind)2, 5210); + return true; + case 32503: + cacheIndex = 7030; + reference = new EpsgCoordinateReferenceRecord(32503, (EpsgCoordinateSystemKind)2, 5211); + return true; + case 32504: + cacheIndex = 7031; + reference = new EpsgCoordinateReferenceRecord(32504, (EpsgCoordinateSystemKind)2, 5212); + return true; + case 32505: + cacheIndex = 7032; + reference = new EpsgCoordinateReferenceRecord(32505, (EpsgCoordinateSystemKind)2, 5213); + return true; + case 32506: + cacheIndex = 7033; + reference = new EpsgCoordinateReferenceRecord(32506, (EpsgCoordinateSystemKind)2, 5214); + return true; + case 32507: + cacheIndex = 7034; + reference = new EpsgCoordinateReferenceRecord(32507, (EpsgCoordinateSystemKind)2, 5215); + return true; + case 32508: + cacheIndex = 7035; + reference = new EpsgCoordinateReferenceRecord(32508, (EpsgCoordinateSystemKind)2, 5216); + return true; + case 32509: + cacheIndex = 7036; + reference = new EpsgCoordinateReferenceRecord(32509, (EpsgCoordinateSystemKind)2, 5217); + return true; + case 32510: + cacheIndex = 7037; + reference = new EpsgCoordinateReferenceRecord(32510, (EpsgCoordinateSystemKind)2, 5218); + return true; + case 32511: + cacheIndex = 7038; + reference = new EpsgCoordinateReferenceRecord(32511, (EpsgCoordinateSystemKind)2, 5219); + return true; + case 32512: + cacheIndex = 7039; + reference = new EpsgCoordinateReferenceRecord(32512, (EpsgCoordinateSystemKind)2, 5220); + return true; + case 32513: + cacheIndex = 7040; + reference = new EpsgCoordinateReferenceRecord(32513, (EpsgCoordinateSystemKind)2, 5221); + return true; + case 32514: + cacheIndex = 7041; + reference = new EpsgCoordinateReferenceRecord(32514, (EpsgCoordinateSystemKind)2, 5222); + return true; + case 32515: + cacheIndex = 7042; + reference = new EpsgCoordinateReferenceRecord(32515, (EpsgCoordinateSystemKind)2, 5223); + return true; + case 32516: + cacheIndex = 7043; + reference = new EpsgCoordinateReferenceRecord(32516, (EpsgCoordinateSystemKind)2, 5224); + return true; + case 32517: + cacheIndex = 7044; + reference = new EpsgCoordinateReferenceRecord(32517, (EpsgCoordinateSystemKind)2, 5225); + return true; + case 32518: + cacheIndex = 7045; + reference = new EpsgCoordinateReferenceRecord(32518, (EpsgCoordinateSystemKind)2, 5226); + return true; + case 32519: + cacheIndex = 7046; + reference = new EpsgCoordinateReferenceRecord(32519, (EpsgCoordinateSystemKind)2, 5227); + return true; + case 32520: + cacheIndex = 7047; + reference = new EpsgCoordinateReferenceRecord(32520, (EpsgCoordinateSystemKind)2, 5228); + return true; + case 32521: + cacheIndex = 7048; + reference = new EpsgCoordinateReferenceRecord(32521, (EpsgCoordinateSystemKind)2, 5229); + return true; + case 32522: + cacheIndex = 7049; + reference = new EpsgCoordinateReferenceRecord(32522, (EpsgCoordinateSystemKind)2, 5230); + return true; + case 32523: + cacheIndex = 7050; + reference = new EpsgCoordinateReferenceRecord(32523, (EpsgCoordinateSystemKind)2, 5231); + return true; + case 32524: + cacheIndex = 7051; + reference = new EpsgCoordinateReferenceRecord(32524, (EpsgCoordinateSystemKind)2, 5232); + return true; + case 32525: + cacheIndex = 7052; + reference = new EpsgCoordinateReferenceRecord(32525, (EpsgCoordinateSystemKind)2, 5233); + return true; + case 32526: + cacheIndex = 7053; + reference = new EpsgCoordinateReferenceRecord(32526, (EpsgCoordinateSystemKind)2, 5234); + return true; + case 32527: + cacheIndex = 7054; + reference = new EpsgCoordinateReferenceRecord(32527, (EpsgCoordinateSystemKind)2, 5235); + return true; + case 32528: + cacheIndex = 7055; + reference = new EpsgCoordinateReferenceRecord(32528, (EpsgCoordinateSystemKind)2, 5236); + return true; + case 32529: + cacheIndex = 7056; + reference = new EpsgCoordinateReferenceRecord(32529, (EpsgCoordinateSystemKind)2, 5237); + return true; + case 32530: + cacheIndex = 7057; + reference = new EpsgCoordinateReferenceRecord(32530, (EpsgCoordinateSystemKind)2, 5238); + return true; + case 32531: + cacheIndex = 7058; + reference = new EpsgCoordinateReferenceRecord(32531, (EpsgCoordinateSystemKind)2, 5239); + return true; + case 32532: + cacheIndex = 7059; + reference = new EpsgCoordinateReferenceRecord(32532, (EpsgCoordinateSystemKind)2, 5240); + return true; + case 32533: + cacheIndex = 7060; + reference = new EpsgCoordinateReferenceRecord(32533, (EpsgCoordinateSystemKind)2, 5241); + return true; + case 32534: + cacheIndex = 7061; + reference = new EpsgCoordinateReferenceRecord(32534, (EpsgCoordinateSystemKind)2, 5242); + return true; + case 32535: + cacheIndex = 7062; + reference = new EpsgCoordinateReferenceRecord(32535, (EpsgCoordinateSystemKind)2, 5243); + return true; + case 32536: + cacheIndex = 7063; + reference = new EpsgCoordinateReferenceRecord(32536, (EpsgCoordinateSystemKind)2, 5244); + return true; + case 32537: + cacheIndex = 7064; + reference = new EpsgCoordinateReferenceRecord(32537, (EpsgCoordinateSystemKind)2, 5245); + return true; + case 32538: + cacheIndex = 7065; + reference = new EpsgCoordinateReferenceRecord(32538, (EpsgCoordinateSystemKind)2, 5246); + return true; + case 32539: + cacheIndex = 7066; + reference = new EpsgCoordinateReferenceRecord(32539, (EpsgCoordinateSystemKind)2, 5247); + return true; + case 32540: + cacheIndex = 7067; + reference = new EpsgCoordinateReferenceRecord(32540, (EpsgCoordinateSystemKind)2, 5248); + return true; + case 32541: + cacheIndex = 7068; + reference = new EpsgCoordinateReferenceRecord(32541, (EpsgCoordinateSystemKind)2, 5249); + return true; + case 32542: + cacheIndex = 7069; + reference = new EpsgCoordinateReferenceRecord(32542, (EpsgCoordinateSystemKind)2, 5250); + return true; + case 32543: + cacheIndex = 7070; + reference = new EpsgCoordinateReferenceRecord(32543, (EpsgCoordinateSystemKind)2, 5251); + return true; + case 32544: + cacheIndex = 7071; + reference = new EpsgCoordinateReferenceRecord(32544, (EpsgCoordinateSystemKind)2, 5252); + return true; + case 32545: + cacheIndex = 7072; + reference = new EpsgCoordinateReferenceRecord(32545, (EpsgCoordinateSystemKind)2, 5253); + return true; + case 32546: + cacheIndex = 7073; + reference = new EpsgCoordinateReferenceRecord(32546, (EpsgCoordinateSystemKind)2, 5254); + return true; + case 32547: + cacheIndex = 7074; + reference = new EpsgCoordinateReferenceRecord(32547, (EpsgCoordinateSystemKind)2, 5255); + return true; + case 32548: + cacheIndex = 7075; + reference = new EpsgCoordinateReferenceRecord(32548, (EpsgCoordinateSystemKind)2, 5256); + return true; + case 32549: + cacheIndex = 7076; + reference = new EpsgCoordinateReferenceRecord(32549, (EpsgCoordinateSystemKind)2, 5257); + return true; + case 32550: + cacheIndex = 7077; + reference = new EpsgCoordinateReferenceRecord(32550, (EpsgCoordinateSystemKind)2, 5258); + return true; + case 32551: + cacheIndex = 7078; + reference = new EpsgCoordinateReferenceRecord(32551, (EpsgCoordinateSystemKind)2, 5259); + return true; + case 32552: + cacheIndex = 7079; + reference = new EpsgCoordinateReferenceRecord(32552, (EpsgCoordinateSystemKind)2, 5260); + return true; + case 32553: + cacheIndex = 7080; + reference = new EpsgCoordinateReferenceRecord(32553, (EpsgCoordinateSystemKind)2, 5261); + return true; + case 32554: + cacheIndex = 7081; + reference = new EpsgCoordinateReferenceRecord(32554, (EpsgCoordinateSystemKind)2, 5262); + return true; + case 32555: + cacheIndex = 7082; + reference = new EpsgCoordinateReferenceRecord(32555, (EpsgCoordinateSystemKind)2, 5263); + return true; + case 32556: + cacheIndex = 7083; + reference = new EpsgCoordinateReferenceRecord(32556, (EpsgCoordinateSystemKind)2, 5264); + return true; + case 32557: + cacheIndex = 7084; + reference = new EpsgCoordinateReferenceRecord(32557, (EpsgCoordinateSystemKind)2, 5265); + return true; + case 32558: + cacheIndex = 7085; + reference = new EpsgCoordinateReferenceRecord(32558, (EpsgCoordinateSystemKind)2, 5266); + return true; + case 32559: + cacheIndex = 7086; + reference = new EpsgCoordinateReferenceRecord(32559, (EpsgCoordinateSystemKind)2, 5267); + return true; + case 32560: + cacheIndex = 7087; + reference = new EpsgCoordinateReferenceRecord(32560, (EpsgCoordinateSystemKind)2, 5268); + return true; + case 32600: + cacheIndex = 7088; + reference = new EpsgCoordinateReferenceRecord(32600, (EpsgCoordinateSystemKind)2, 5269); + return true; + case 32601: + cacheIndex = 7089; + reference = new EpsgCoordinateReferenceRecord(32601, (EpsgCoordinateSystemKind)2, 5270); + return true; + case 32602: + cacheIndex = 7090; + reference = new EpsgCoordinateReferenceRecord(32602, (EpsgCoordinateSystemKind)2, 5271); + return true; + case 32603: + cacheIndex = 7091; + reference = new EpsgCoordinateReferenceRecord(32603, (EpsgCoordinateSystemKind)2, 5272); + return true; + case 32604: + cacheIndex = 7092; + reference = new EpsgCoordinateReferenceRecord(32604, (EpsgCoordinateSystemKind)2, 5273); + return true; + case 32605: + cacheIndex = 7093; + reference = new EpsgCoordinateReferenceRecord(32605, (EpsgCoordinateSystemKind)2, 5274); + return true; + case 32606: + cacheIndex = 7094; + reference = new EpsgCoordinateReferenceRecord(32606, (EpsgCoordinateSystemKind)2, 5275); + return true; + case 32607: + cacheIndex = 7095; + reference = new EpsgCoordinateReferenceRecord(32607, (EpsgCoordinateSystemKind)2, 5276); + return true; + case 32608: + cacheIndex = 7096; + reference = new EpsgCoordinateReferenceRecord(32608, (EpsgCoordinateSystemKind)2, 5277); + return true; + case 32609: + cacheIndex = 7097; + reference = new EpsgCoordinateReferenceRecord(32609, (EpsgCoordinateSystemKind)2, 5278); + return true; + case 32610: + cacheIndex = 7098; + reference = new EpsgCoordinateReferenceRecord(32610, (EpsgCoordinateSystemKind)2, 5279); + return true; + case 32611: + cacheIndex = 7099; + reference = new EpsgCoordinateReferenceRecord(32611, (EpsgCoordinateSystemKind)2, 5280); + return true; + case 32612: + cacheIndex = 7100; + reference = new EpsgCoordinateReferenceRecord(32612, (EpsgCoordinateSystemKind)2, 5281); + return true; + case 32613: + cacheIndex = 7101; + reference = new EpsgCoordinateReferenceRecord(32613, (EpsgCoordinateSystemKind)2, 5282); + return true; + case 32614: + cacheIndex = 7102; + reference = new EpsgCoordinateReferenceRecord(32614, (EpsgCoordinateSystemKind)2, 5283); + return true; + case 32615: + cacheIndex = 7103; + reference = new EpsgCoordinateReferenceRecord(32615, (EpsgCoordinateSystemKind)2, 5284); + return true; + case 32616: + cacheIndex = 7104; + reference = new EpsgCoordinateReferenceRecord(32616, (EpsgCoordinateSystemKind)2, 5285); + return true; + case 32617: + cacheIndex = 7105; + reference = new EpsgCoordinateReferenceRecord(32617, (EpsgCoordinateSystemKind)2, 5286); + return true; + case 32618: + cacheIndex = 7106; + reference = new EpsgCoordinateReferenceRecord(32618, (EpsgCoordinateSystemKind)2, 5287); + return true; + case 32619: + cacheIndex = 7107; + reference = new EpsgCoordinateReferenceRecord(32619, (EpsgCoordinateSystemKind)2, 5288); + return true; + case 32620: + cacheIndex = 7108; + reference = new EpsgCoordinateReferenceRecord(32620, (EpsgCoordinateSystemKind)2, 5289); + return true; + case 32621: + cacheIndex = 7109; + reference = new EpsgCoordinateReferenceRecord(32621, (EpsgCoordinateSystemKind)2, 5290); + return true; + case 32622: + cacheIndex = 7110; + reference = new EpsgCoordinateReferenceRecord(32622, (EpsgCoordinateSystemKind)2, 5291); + return true; + case 32623: + cacheIndex = 7111; + reference = new EpsgCoordinateReferenceRecord(32623, (EpsgCoordinateSystemKind)2, 5292); + return true; + case 32624: + cacheIndex = 7112; + reference = new EpsgCoordinateReferenceRecord(32624, (EpsgCoordinateSystemKind)2, 5293); + return true; + case 32625: + cacheIndex = 7113; + reference = new EpsgCoordinateReferenceRecord(32625, (EpsgCoordinateSystemKind)2, 5294); + return true; + case 32626: + cacheIndex = 7114; + reference = new EpsgCoordinateReferenceRecord(32626, (EpsgCoordinateSystemKind)2, 5295); + return true; + case 32627: + cacheIndex = 7115; + reference = new EpsgCoordinateReferenceRecord(32627, (EpsgCoordinateSystemKind)2, 5296); + return true; + case 32628: + cacheIndex = 7116; + reference = new EpsgCoordinateReferenceRecord(32628, (EpsgCoordinateSystemKind)2, 5297); + return true; + case 32629: + cacheIndex = 7117; + reference = new EpsgCoordinateReferenceRecord(32629, (EpsgCoordinateSystemKind)2, 5298); + return true; + case 32630: + cacheIndex = 7118; + reference = new EpsgCoordinateReferenceRecord(32630, (EpsgCoordinateSystemKind)2, 5299); + return true; + case 32631: + cacheIndex = 7119; + reference = new EpsgCoordinateReferenceRecord(32631, (EpsgCoordinateSystemKind)2, 5300); + return true; + case 32632: + cacheIndex = 7120; + reference = new EpsgCoordinateReferenceRecord(32632, (EpsgCoordinateSystemKind)2, 5301); + return true; + case 32633: + cacheIndex = 7121; + reference = new EpsgCoordinateReferenceRecord(32633, (EpsgCoordinateSystemKind)2, 5302); + return true; + case 32634: + cacheIndex = 7122; + reference = new EpsgCoordinateReferenceRecord(32634, (EpsgCoordinateSystemKind)2, 5303); + return true; + case 32635: + cacheIndex = 7123; + reference = new EpsgCoordinateReferenceRecord(32635, (EpsgCoordinateSystemKind)2, 5304); + return true; + case 32636: + cacheIndex = 7124; + reference = new EpsgCoordinateReferenceRecord(32636, (EpsgCoordinateSystemKind)2, 5305); + return true; + case 32637: + cacheIndex = 7125; + reference = new EpsgCoordinateReferenceRecord(32637, (EpsgCoordinateSystemKind)2, 5306); + return true; + case 32638: + cacheIndex = 7126; + reference = new EpsgCoordinateReferenceRecord(32638, (EpsgCoordinateSystemKind)2, 5307); + return true; + case 32639: + cacheIndex = 7127; + reference = new EpsgCoordinateReferenceRecord(32639, (EpsgCoordinateSystemKind)2, 5308); + return true; + case 32640: + cacheIndex = 7128; + reference = new EpsgCoordinateReferenceRecord(32640, (EpsgCoordinateSystemKind)2, 5309); + return true; + case 32641: + cacheIndex = 7129; + reference = new EpsgCoordinateReferenceRecord(32641, (EpsgCoordinateSystemKind)2, 5310); + return true; + case 32642: + cacheIndex = 7130; + reference = new EpsgCoordinateReferenceRecord(32642, (EpsgCoordinateSystemKind)2, 5311); + return true; + case 32643: + cacheIndex = 7131; + reference = new EpsgCoordinateReferenceRecord(32643, (EpsgCoordinateSystemKind)2, 5312); + return true; + case 32644: + cacheIndex = 7132; + reference = new EpsgCoordinateReferenceRecord(32644, (EpsgCoordinateSystemKind)2, 5313); + return true; + case 32645: + cacheIndex = 7133; + reference = new EpsgCoordinateReferenceRecord(32645, (EpsgCoordinateSystemKind)2, 5314); + return true; + case 32646: + cacheIndex = 7134; + reference = new EpsgCoordinateReferenceRecord(32646, (EpsgCoordinateSystemKind)2, 5315); + return true; + case 32647: + cacheIndex = 7135; + reference = new EpsgCoordinateReferenceRecord(32647, (EpsgCoordinateSystemKind)2, 5316); + return true; + case 32648: + cacheIndex = 7136; + reference = new EpsgCoordinateReferenceRecord(32648, (EpsgCoordinateSystemKind)2, 5317); + return true; + case 32649: + cacheIndex = 7137; + reference = new EpsgCoordinateReferenceRecord(32649, (EpsgCoordinateSystemKind)2, 5318); + return true; + case 32650: + cacheIndex = 7138; + reference = new EpsgCoordinateReferenceRecord(32650, (EpsgCoordinateSystemKind)2, 5319); + return true; + case 32651: + cacheIndex = 7139; + reference = new EpsgCoordinateReferenceRecord(32651, (EpsgCoordinateSystemKind)2, 5320); + return true; + case 32652: + cacheIndex = 7140; + reference = new EpsgCoordinateReferenceRecord(32652, (EpsgCoordinateSystemKind)2, 5321); + return true; + case 32653: + cacheIndex = 7141; + reference = new EpsgCoordinateReferenceRecord(32653, (EpsgCoordinateSystemKind)2, 5322); + return true; + case 32654: + cacheIndex = 7142; + reference = new EpsgCoordinateReferenceRecord(32654, (EpsgCoordinateSystemKind)2, 5323); + return true; + case 32655: + cacheIndex = 7143; + reference = new EpsgCoordinateReferenceRecord(32655, (EpsgCoordinateSystemKind)2, 5324); + return true; + case 32656: + cacheIndex = 7144; + reference = new EpsgCoordinateReferenceRecord(32656, (EpsgCoordinateSystemKind)2, 5325); + return true; + case 32657: + cacheIndex = 7145; + reference = new EpsgCoordinateReferenceRecord(32657, (EpsgCoordinateSystemKind)2, 5326); + return true; + case 32658: + cacheIndex = 7146; + reference = new EpsgCoordinateReferenceRecord(32658, (EpsgCoordinateSystemKind)2, 5327); + return true; + case 32659: + cacheIndex = 7147; + reference = new EpsgCoordinateReferenceRecord(32659, (EpsgCoordinateSystemKind)2, 5328); + return true; + case 32660: + cacheIndex = 7148; + reference = new EpsgCoordinateReferenceRecord(32660, (EpsgCoordinateSystemKind)2, 5329); + return true; + case 32661: + cacheIndex = 7149; + reference = new EpsgCoordinateReferenceRecord(32661, (EpsgCoordinateSystemKind)2, 5330); + return true; + case 32664: + cacheIndex = 7150; + reference = new EpsgCoordinateReferenceRecord(32664, (EpsgCoordinateSystemKind)2, 5331); + return true; + case 32665: + cacheIndex = 7151; + reference = new EpsgCoordinateReferenceRecord(32665, (EpsgCoordinateSystemKind)2, 5332); + return true; + case 32666: + cacheIndex = 7152; + reference = new EpsgCoordinateReferenceRecord(32666, (EpsgCoordinateSystemKind)2, 5333); + return true; + case 32667: + cacheIndex = 7153; + reference = new EpsgCoordinateReferenceRecord(32667, (EpsgCoordinateSystemKind)2, 5334); + return true; + case 32700: + cacheIndex = 7154; + reference = new EpsgCoordinateReferenceRecord(32700, (EpsgCoordinateSystemKind)2, 5335); + return true; + case 32701: + cacheIndex = 7155; + reference = new EpsgCoordinateReferenceRecord(32701, (EpsgCoordinateSystemKind)2, 5336); + return true; + case 32702: + cacheIndex = 7156; + reference = new EpsgCoordinateReferenceRecord(32702, (EpsgCoordinateSystemKind)2, 5337); + return true; + case 32703: + cacheIndex = 7157; + reference = new EpsgCoordinateReferenceRecord(32703, (EpsgCoordinateSystemKind)2, 5338); + return true; + case 32704: + cacheIndex = 7158; + reference = new EpsgCoordinateReferenceRecord(32704, (EpsgCoordinateSystemKind)2, 5339); + return true; + case 32705: + cacheIndex = 7159; + reference = new EpsgCoordinateReferenceRecord(32705, (EpsgCoordinateSystemKind)2, 5340); + return true; + case 32706: + cacheIndex = 7160; + reference = new EpsgCoordinateReferenceRecord(32706, (EpsgCoordinateSystemKind)2, 5341); + return true; + case 32707: + cacheIndex = 7161; + reference = new EpsgCoordinateReferenceRecord(32707, (EpsgCoordinateSystemKind)2, 5342); + return true; + case 32708: + cacheIndex = 7162; + reference = new EpsgCoordinateReferenceRecord(32708, (EpsgCoordinateSystemKind)2, 5343); + return true; + case 32709: + cacheIndex = 7163; + reference = new EpsgCoordinateReferenceRecord(32709, (EpsgCoordinateSystemKind)2, 5344); + return true; + case 32710: + cacheIndex = 7164; + reference = new EpsgCoordinateReferenceRecord(32710, (EpsgCoordinateSystemKind)2, 5345); + return true; + case 32711: + cacheIndex = 7165; + reference = new EpsgCoordinateReferenceRecord(32711, (EpsgCoordinateSystemKind)2, 5346); + return true; + case 32712: + cacheIndex = 7166; + reference = new EpsgCoordinateReferenceRecord(32712, (EpsgCoordinateSystemKind)2, 5347); + return true; + case 32713: + cacheIndex = 7167; + reference = new EpsgCoordinateReferenceRecord(32713, (EpsgCoordinateSystemKind)2, 5348); + return true; + case 32714: + cacheIndex = 7168; + reference = new EpsgCoordinateReferenceRecord(32714, (EpsgCoordinateSystemKind)2, 5349); + return true; + case 32715: + cacheIndex = 7169; + reference = new EpsgCoordinateReferenceRecord(32715, (EpsgCoordinateSystemKind)2, 5350); + return true; + case 32716: + cacheIndex = 7170; + reference = new EpsgCoordinateReferenceRecord(32716, (EpsgCoordinateSystemKind)2, 5351); + return true; + case 32717: + cacheIndex = 7171; + reference = new EpsgCoordinateReferenceRecord(32717, (EpsgCoordinateSystemKind)2, 5352); + return true; + case 32718: + cacheIndex = 7172; + reference = new EpsgCoordinateReferenceRecord(32718, (EpsgCoordinateSystemKind)2, 5353); + return true; + case 32719: + cacheIndex = 7173; + reference = new EpsgCoordinateReferenceRecord(32719, (EpsgCoordinateSystemKind)2, 5354); + return true; + case 32720: + cacheIndex = 7174; + reference = new EpsgCoordinateReferenceRecord(32720, (EpsgCoordinateSystemKind)2, 5355); + return true; + case 32721: + cacheIndex = 7175; + reference = new EpsgCoordinateReferenceRecord(32721, (EpsgCoordinateSystemKind)2, 5356); + return true; + case 32722: + cacheIndex = 7176; + reference = new EpsgCoordinateReferenceRecord(32722, (EpsgCoordinateSystemKind)2, 5357); + return true; + case 32723: + cacheIndex = 7177; + reference = new EpsgCoordinateReferenceRecord(32723, (EpsgCoordinateSystemKind)2, 5358); + return true; + case 32724: + cacheIndex = 7178; + reference = new EpsgCoordinateReferenceRecord(32724, (EpsgCoordinateSystemKind)2, 5359); + return true; + case 32725: + cacheIndex = 7179; + reference = new EpsgCoordinateReferenceRecord(32725, (EpsgCoordinateSystemKind)2, 5360); + return true; + case 32726: + cacheIndex = 7180; + reference = new EpsgCoordinateReferenceRecord(32726, (EpsgCoordinateSystemKind)2, 5361); + return true; + case 32727: + cacheIndex = 7181; + reference = new EpsgCoordinateReferenceRecord(32727, (EpsgCoordinateSystemKind)2, 5362); + return true; + case 32728: + cacheIndex = 7182; + reference = new EpsgCoordinateReferenceRecord(32728, (EpsgCoordinateSystemKind)2, 5363); + return true; + case 32729: + cacheIndex = 7183; + reference = new EpsgCoordinateReferenceRecord(32729, (EpsgCoordinateSystemKind)2, 5364); + return true; + case 32730: + cacheIndex = 7184; + reference = new EpsgCoordinateReferenceRecord(32730, (EpsgCoordinateSystemKind)2, 5365); + return true; + case 32731: + cacheIndex = 7185; + reference = new EpsgCoordinateReferenceRecord(32731, (EpsgCoordinateSystemKind)2, 5366); + return true; + case 32732: + cacheIndex = 7186; + reference = new EpsgCoordinateReferenceRecord(32732, (EpsgCoordinateSystemKind)2, 5367); + return true; + case 32733: + cacheIndex = 7187; + reference = new EpsgCoordinateReferenceRecord(32733, (EpsgCoordinateSystemKind)2, 5368); + return true; + case 32734: + cacheIndex = 7188; + reference = new EpsgCoordinateReferenceRecord(32734, (EpsgCoordinateSystemKind)2, 5369); + return true; + case 32735: + cacheIndex = 7189; + reference = new EpsgCoordinateReferenceRecord(32735, (EpsgCoordinateSystemKind)2, 5370); + return true; + case 32736: + cacheIndex = 7190; + reference = new EpsgCoordinateReferenceRecord(32736, (EpsgCoordinateSystemKind)2, 5371); + return true; + case 32737: + cacheIndex = 7191; + reference = new EpsgCoordinateReferenceRecord(32737, (EpsgCoordinateSystemKind)2, 5372); + return true; + case 32738: + cacheIndex = 7192; + reference = new EpsgCoordinateReferenceRecord(32738, (EpsgCoordinateSystemKind)2, 5373); + return true; + case 32739: + cacheIndex = 7193; + reference = new EpsgCoordinateReferenceRecord(32739, (EpsgCoordinateSystemKind)2, 5374); + return true; + case 32740: + cacheIndex = 7194; + reference = new EpsgCoordinateReferenceRecord(32740, (EpsgCoordinateSystemKind)2, 5375); + return true; + case 32741: + cacheIndex = 7195; + reference = new EpsgCoordinateReferenceRecord(32741, (EpsgCoordinateSystemKind)2, 5376); + return true; + case 32742: + cacheIndex = 7196; + reference = new EpsgCoordinateReferenceRecord(32742, (EpsgCoordinateSystemKind)2, 5377); + return true; + case 32743: + cacheIndex = 7197; + reference = new EpsgCoordinateReferenceRecord(32743, (EpsgCoordinateSystemKind)2, 5378); + return true; + case 32744: + cacheIndex = 7198; + reference = new EpsgCoordinateReferenceRecord(32744, (EpsgCoordinateSystemKind)2, 5379); + return true; + case 32745: + cacheIndex = 7199; + reference = new EpsgCoordinateReferenceRecord(32745, (EpsgCoordinateSystemKind)2, 5380); + return true; + case 32746: + cacheIndex = 7200; + reference = new EpsgCoordinateReferenceRecord(32746, (EpsgCoordinateSystemKind)2, 5381); + return true; + case 32747: + cacheIndex = 7201; + reference = new EpsgCoordinateReferenceRecord(32747, (EpsgCoordinateSystemKind)2, 5382); + return true; + case 32748: + cacheIndex = 7202; + reference = new EpsgCoordinateReferenceRecord(32748, (EpsgCoordinateSystemKind)2, 5383); + return true; + case 32749: + cacheIndex = 7203; + reference = new EpsgCoordinateReferenceRecord(32749, (EpsgCoordinateSystemKind)2, 5384); + return true; + case 32750: + cacheIndex = 7204; + reference = new EpsgCoordinateReferenceRecord(32750, (EpsgCoordinateSystemKind)2, 5385); + return true; + case 32751: + cacheIndex = 7205; + reference = new EpsgCoordinateReferenceRecord(32751, (EpsgCoordinateSystemKind)2, 5386); + return true; + case 32752: + cacheIndex = 7206; + reference = new EpsgCoordinateReferenceRecord(32752, (EpsgCoordinateSystemKind)2, 5387); + return true; + case 32753: + cacheIndex = 7207; + reference = new EpsgCoordinateReferenceRecord(32753, (EpsgCoordinateSystemKind)2, 5388); + return true; + case 32754: + cacheIndex = 7208; + reference = new EpsgCoordinateReferenceRecord(32754, (EpsgCoordinateSystemKind)2, 5389); + return true; + case 32755: + cacheIndex = 7209; + reference = new EpsgCoordinateReferenceRecord(32755, (EpsgCoordinateSystemKind)2, 5390); + return true; + case 32756: + cacheIndex = 7210; + reference = new EpsgCoordinateReferenceRecord(32756, (EpsgCoordinateSystemKind)2, 5391); + return true; + case 32757: + cacheIndex = 7211; + reference = new EpsgCoordinateReferenceRecord(32757, (EpsgCoordinateSystemKind)2, 5392); + return true; + case 32758: + cacheIndex = 7212; + reference = new EpsgCoordinateReferenceRecord(32758, (EpsgCoordinateSystemKind)2, 5393); + return true; + case 32759: + cacheIndex = 7213; + reference = new EpsgCoordinateReferenceRecord(32759, (EpsgCoordinateSystemKind)2, 5394); + return true; + case 32760: + cacheIndex = 7214; + reference = new EpsgCoordinateReferenceRecord(32760, (EpsgCoordinateSystemKind)2, 5395); + return true; + case 32761: + cacheIndex = 7215; + reference = new EpsgCoordinateReferenceRecord(32761, (EpsgCoordinateSystemKind)2, 5396); + return true; + case 32766: + cacheIndex = 7216; + reference = new EpsgCoordinateReferenceRecord(32766, (EpsgCoordinateSystemKind)2, 5397); + return true; + default: + cacheIndex = -1; + reference = default; + return false; + } + } + + internal static bool TryGetCoordinateSridByCacheIndex(int cacheIndex, out int srid) + { + if ((uint)cacheIndex < (uint)CoordinateSridByCacheIndex.Length) + { + srid = CoordinateSridByCacheIndex[cacheIndex]; + return true; + } + + srid = -1; + return false; + } + } +} diff --git a/src/ProjNet/Data/ICoordinateOperationDefinitionProvider.cs b/src/ProjNet/Data/ICoordinateOperationDefinitionProvider.cs new file mode 100644 index 00000000..542528c9 --- /dev/null +++ b/src/ProjNet/Data/ICoordinateOperationDefinitionProvider.cs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +using System.Collections.Generic; + +/// +/// Provides coordinate operation definitions from a backing catalog. +/// +internal interface ICoordinateOperationDefinitionProvider +{ + /// + /// Gets the coordinate operation definitions. + /// + /// A sequence of coordinate operation definitions. + IEnumerable GetDefinitions(); +} diff --git a/src/ProjNet/Data/ICoordinateSystemDefinitionProvider.cs b/src/ProjNet/Data/ICoordinateSystemDefinitionProvider.cs new file mode 100644 index 00000000..c1c915d8 --- /dev/null +++ b/src/ProjNet/Data/ICoordinateSystemDefinitionProvider.cs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +using System.Collections.Generic; + +/// +/// Provides managed coordinate system definitions used to initialize . +/// +public interface ICoordinateSystemDefinitionProvider +{ + /// + /// Gets coordinate system definitions keyed by SRID. + /// + /// Coordinate system definitions. + IEnumerable GetDefinitions(); +} diff --git a/src/ProjNet/Data/IManagedCoordinateSystemProvider.cs b/src/ProjNet/Data/IManagedCoordinateSystemProvider.cs new file mode 100644 index 00000000..e3c64fec --- /dev/null +++ b/src/ProjNet/Data/IManagedCoordinateSystemProvider.cs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +using System.Collections.Generic; + +/// +/// Internal provider contract for managed coordinate systems emitted as structured objects. +/// +internal interface IManagedCoordinateSystemProvider +{ + /// + /// Gets coordinate system objects keyed by SRID. + /// + /// Coordinate system objects. + IEnumerable GetCoordinateSystems(); +} diff --git a/src/ProjNet/Data/ManagedCoordinateOperationDefinitionProvider.cs b/src/ProjNet/Data/ManagedCoordinateOperationDefinitionProvider.cs new file mode 100644 index 00000000..66343597 --- /dev/null +++ b/src/ProjNet/Data/ManagedCoordinateOperationDefinitionProvider.cs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +using System.Collections.Generic; +using ProjNet.Data.Generated; + +/// +/// Provides managed coordinate operation definitions from generated catalog data. +/// +internal sealed class ManagedCoordinateOperationDefinitionProvider : ICoordinateOperationDefinitionProvider +{ + /// + /// Enumerates all coordinate operation definitions from the generated EPSG catalog. + /// + /// A sequence of instances from the EPSG catalog. + public IEnumerable GetDefinitions() + { + EpsgOperationRecord[] records = EpsgGeneratedOperationsCatalog.Operations; + + for (int i = 0; i < records.Length; i++) + { + EpsgOperationRecord operation = records[i]; + yield return new CoordinateOperationDefinition( + (CoordinateOperationKind)operation.OperationType, + operation.OperationCode, + operation.SourceSrid, + operation.TargetSrid, + operation.Accuracy, + operation.MethodName, + operation.ParameterFileName, + operation.AreaSouthLatitude, + operation.AreaNorthLatitude, + operation.AreaWestLongitude, + operation.AreaEastLongitude); + } + } +} diff --git a/src/ProjNet/Data/ManagedCoordinateSystemDefinitionProvider.cs b/src/ProjNet/Data/ManagedCoordinateSystemDefinitionProvider.cs new file mode 100644 index 00000000..f09610ad --- /dev/null +++ b/src/ProjNet/Data/ManagedCoordinateSystemDefinitionProvider.cs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Data; + +using System.Collections.Generic; +using ProjNet.Data.Generated; + +/// +/// Provides managed, runtime-independent defaults for core coordinate system definitions. +/// +/// +/// This provider intentionally avoids runtime SQLite/native dependencies. +/// It is the baseline managed packaging implementation and can be replaced by a generated provider in later phases. +/// +public sealed class ManagedCoordinateSystemDefinitionProvider : ICoordinateSystemDefinitionProvider, IManagedCoordinateSystemProvider +{ + /// + public IEnumerable GetCoordinateSystems() + { + return GetManagedCoordinateSystems(); + } + + /// + /// Enumerates all coordinate system definitions as WKT-encoded instances. + /// + /// A sequence of instances for all known coordinate systems. + public IEnumerable GetDefinitions() + { + foreach (CoordinateSystemEntry entry in GetManagedCoordinateSystems()) + { + yield return new CoordinateSystemDefinition(entry.Srid, entry.CoordinateSystem.WKT); + } + } + + private static IEnumerable GetManagedCoordinateSystems() + { + var yieldedSrids = new HashSet(); + foreach (CoordinateSystemEntry entry in EpsgCoordinateSystemFactory.GetCoordinateSystems()) + { + if (!yieldedSrids.Add(entry.Srid)) + { + continue; + } + + yield return entry; + } + } +} diff --git a/src/ProjNet/ENGINEERING_GOVERNANCE.md b/src/ProjNet/ENGINEERING_GOVERNANCE.md new file mode 100644 index 00000000..10d2fbdd --- /dev/null +++ b/src/ProjNet/ENGINEERING_GOVERNANCE.md @@ -0,0 +1,96 @@ +# Engineering governance + +This document defines the active engineering and quality gates for `ProjNET`. + +## Public API baseline policy + +`src/ProjNet/PublicAPI.Shipped.txt` is the canonical public API baseline for the main `ProjNET` library. + +- Verification runs in `test/ProjNet.Tests/CodeQuality/PublicApiBaselineTests.cs`. +- The baseline gate must stay green in regular validation. +- Intentional API surface changes must update the shipped baseline in a reviewed commit. +- `dotnet pack` also runs SDK package validation against `PackageValidationBaselineVersion`. +- Intentional baseline deltas that remain accepted for the active prerelease line must be tracked in `src/ProjNet/CompatibilitySuppressions.xml` and reviewed together with the corresponding API change. + +### Approved baseline update flow + +Use this only when a public API change is intentional and approved: + +1. Run baseline update: + - PowerShell: + `$env:PROJNET_UPDATE_PUBLIC_API_BASELINE='1'; dotnet test --project .\test\ProjNet.Tests\ProjNET.Tests.csproj --filter-class ProjNet.Tests.PublicApiBaselineTests` +2. Inspect and review changes in: + - `src/ProjNet/PublicAPI.Shipped.txt` + - ProjNET uses `PublicApiBaselineTests` with `PublicApiGenerator`; there is no `PublicAPI.Unshipped.txt` file in this repository. +3. Re-run without update variable: + - `Remove-Item Env:PROJNET_UPDATE_PUBLIC_API_BASELINE -ErrorAction Ignore` + - `dotnet test --project .\test\ProjNet.Tests\ProjNET.Tests.csproj --filter-class ProjNet.Tests.PublicApiBaselineTests` + +## Target framework policy + +`ProjNET` must continue to ship `netstandard2.0`. + +Approved target frameworks: + +- `netstandard2.0` (required shipping target) +- `netstandard2.1` +- `net8.0` + +Build policy is enforced in `src/ProjNet/ProjNET.csproj` via `ValidateTargetFrameworkPolicy`. + +## Testing policy + +- Unit and integration tests run on xUnit v3. +- Default validation command: + - `dotnet test --project .\test\ProjNet.Tests\ProjNET.Tests.csproj` + +## CI/CD pipeline + +- `/.github/workflows/full-ci.yml` is the main validation pipeline for push, pull request, and manual runs. It builds the solution, runs pull-request dependency review, collects Cobertura coverage, executes the API/parity/benchmark smoke gates, packs artifacts, publishes to MyGet from `develop` and `master`, and publishes to NuGet from `master`. +- `/.github/workflows/benchmarks.yml` runs the curated BenchmarkDotNet suite on a weekly schedule and on manual demand. The default branch persists benchmark history to the `benchmark-data` branch; non-default refs run compare-only checks against that stored baseline. +- `/.github/workflows/codeql.yml` runs the dedicated C# CodeQL security analysis workflow on push, pull request, and weekly schedule. +- `/.github/workflows/mutation-tests.yml` runs the Stryker mutation suite for manual runs and for `develop` pushes that touch the configured source, test, tooling, or workflow paths. +- `/.github/dependabot.yml` manages weekly NuGet and GitHub Actions dependency updates. + +### Coverage policy + +- Coverage is collected in CI via `dotnet-coverage` and published as Cobertura output plus markdown summaries. +- Same-repository non-bot pull requests receive the current coverage summary directly in the PR discussion. +- There is no hard fail threshold at this stage; coverage is tracked for visibility and regression monitoring. + +### Benchmark policy + +- The benchmark pipeline uses a curated benchmark set defined in `/.github/scripts/Get-CuratedBenchmarkConfiguration.ps1`. +- `full-ci.yml` runs the curated smoke path so benchmark execution, report conversion, and curated dataset completeness fail fast during normal CI. +- `benchmarks.yml` runs the full curated suite, converts BenchmarkDotNet reports into the regression dataset consumed by `benchmark-action`, and raises alerts at a `150%` regression threshold without failing the workflow. + +## Code style and analyzers + +- `.editorconfig` is the primary style source of truth. +- StyleCop analyzers are enabled repository-wide. +- File-header diagnostics `SA1633`-`SA1638` are intentionally disabled to allow provenance-specific SPDX headers. +- `stylecop.json` keeps XML header enforcement disabled (`xmlHeader: false`) for variable attribution scenarios. +- Null checks should use pattern-style comparisons (`is null` / `is not null`) in new and touched code. +- `StringSyntaxAttribute` annotations should be added only when APIs accept caller-supplied regex or format strings; current handwritten code primarily uses inline patterns and `GeneratedRegex`. +- Prefer nullable flow attributes (`NotNull`, `NotNullWhen`, `MemberNotNull`, etc.) and throw-flow attributes (`DoesNotReturn`, `DoesNotReturnIf`) on shared guard helpers where applicable. +- Reflection/AOT-related attributes (`DynamicallyAccessedMembers`, `RequiresUnreferencedCode`, `RequiresDynamicCode`) are audited together with trim warnings in the dedicated AOT milestone. + +## Documentation policy + +- Public API changes should include XML documentation updates when applicable. +- External web URLs should not be embedded in XML API docs unless required for legal/provenance context. + +## Licensing and attribution policy + +- Project-level licensing and attribution references are maintained in: + - `LICENSES/` + - `NOTICE.md` +- Source files use SPDX-style headers with provenance-specific attribution. + +## Deprecation and compatibility policy + +- Compatibility-first is the default: avoid breaking removals in active release lines. +- Obsolete APIs are acceptable when: + - replacement guidance is explicit, + - behavior remains functional during deprecation window, + - removals are deferred to a major-version decision. diff --git a/src/ProjNet/Geometries/XY.cs b/src/ProjNet/Geometries/XY.cs index 2efd2291..ca009c1f 100644 --- a/src/ProjNet/Geometries/XY.cs +++ b/src/ProjNet/Geometries/XY.cs @@ -1,42 +1,61 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Geometries; + using System; using System.Runtime.InteropServices; -namespace ProjNet.Geometries +/// +/// A pair of X- and Y-ordinates, laid out in that order. +/// +[StructLayout(LayoutKind.Sequential)] +public struct XY : IEquatable { /// - /// A pair of X- and Y-ordinates, laid out in that order. + /// The x-ordinate value. + /// + public double X; + + /// + /// The y-ordinate value. + /// + public double Y; + + /// + /// Initializes a new instance of the struct. + /// + /// The value for . + /// The value for . + public XY(double x, double y) => + (this.X, this.Y) = (x, y); + + /// + /// Compares two values for equality. + /// + /// The left operand. + /// The right operand. + /// when both values are equal; otherwise . + public static bool operator ==(XY left, XY right) => left.Equals(right); + + /// + /// Compares two values for inequality. /// - [StructLayout(LayoutKind.Sequential)] - public struct XY : IEquatable - { - /// - /// The x-ordinate value - /// - public double X; - - /// - /// The y-ordinate value - /// - public double Y; - - /// - /// Initializes a new instance of the struct. - /// - /// The value for . - /// The value for . - public XY(double x, double y) => - (X, Y) = (x, y); - - /// - public override bool Equals(object obj) => obj is XY other && Equals(other); - - /// - public bool Equals(XY other) => (X, Y).Equals((other.X, other.Y)); - - /// - public override int GetHashCode() => (X, Y).GetHashCode(); - - /// - public override string ToString() => $"({X}, {Y})"; - } + /// The left operand. + /// The right operand. + /// when values differ; otherwise . + public static bool operator !=(XY left, XY right) => !left.Equals(right); + + /// + public override readonly bool Equals(object? obj) => obj is XY other && this.Equals(other); + + /// + public readonly bool Equals(XY other) => (this.X, this.Y).Equals((other.X, other.Y)); + + /// + public override readonly int GetHashCode() => (this.X, this.Y).GetHashCode(); + + /// + public override readonly string ToString() => $"({this.X}, {this.Y})"; } diff --git a/src/ProjNet/Geometries/XYZ.cs b/src/ProjNet/Geometries/XYZ.cs index e7e1ff71..dce82d2d 100644 --- a/src/ProjNet/Geometries/XYZ.cs +++ b/src/ProjNet/Geometries/XYZ.cs @@ -1,48 +1,67 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Geometries; + using System; using System.Runtime.InteropServices; -namespace ProjNet.Geometries +/// +/// A tuple of X-, Y- and Z-ordinate values, laid out in that order. +/// +[StructLayout(LayoutKind.Sequential)] +public struct XYZ : IEquatable { /// - /// A tuple of X-, Y- and Z-ordinate values, laid out in that order. + /// The X-ordinate value. + /// + public double X; + + /// + /// The Y-ordinate value. + /// + public double Y; + + /// + /// The Z-ordinate value. + /// + public double Z; + + /// + /// Initializes a new instance of the struct. /// - [StructLayout(LayoutKind.Sequential)] - public struct XYZ : IEquatable - { - /// - /// The X-ordinate value - /// - public double X; - - /// - /// The Y-ordinate value - /// - public double Y; - - /// - /// The Z-ordinate value - /// - public double Z; - - /// - /// Initializes a new instance of the struct. - /// - /// The value for . - /// The value for . - /// The value for . - public XYZ(double x, double y, double z) => - (X, Y, Z) = (x, y, z); - - /// - public override bool Equals(object obj) => obj is XYZ other && Equals(other); - - /// - public bool Equals(XYZ other) => (X, Y, Z).Equals((other.X, other.Y, other.Z)); - - /// - public override int GetHashCode() => (X, Y, Z).GetHashCode(); - - /// - public override string ToString() => $"({X}, {Y}, {Z})"; - } + /// The value for . + /// The value for . + /// The value for . + public XYZ(double x, double y, double z) => + (this.X, this.Y, this.Z) = (x, y, z); + + /// + /// Compares two values for equality. + /// + /// The left operand. + /// The right operand. + /// when both values are equal; otherwise . + public static bool operator ==(XYZ left, XYZ right) => left.Equals(right); + + /// + /// Compares two values for inequality. + /// + /// The left operand. + /// The right operand. + /// when values differ; otherwise . + public static bool operator !=(XYZ left, XYZ right) => !left.Equals(right); + + /// + public override readonly bool Equals(object? obj) => obj is XYZ other && this.Equals(other); + + /// + public readonly bool Equals(XYZ other) => (this.X, this.Y, this.Z).Equals((other.X, other.Y, other.Z)); + + /// + public override readonly int GetHashCode() => (this.X, this.Y, this.Z).GetHashCode(); + + /// + public override readonly string ToString() => $"({this.X}, {this.Y}, {this.Z})"; } diff --git a/src/ProjNet/GlobalSuppressions.cs b/src/ProjNet/GlobalSuppressions.cs new file mode 100644 index 00000000..20a65b3d --- /dev/null +++ b/src/ProjNet/GlobalSuppressions.cs @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Legacy API naming retained for compatibility.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WGS84_UTM(System.Int32,System.Boolean)")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Legacy PROJ-derived helper naming retained in projection base class.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.Projections.MapProjection.Lon_origin")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Legacy PROJ-derived helper naming retained in projection base class.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.Projections.MapProjection.Central_parallel")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Legacy PROJ-derived helper naming retained in projection base class.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.Projections.MapProjection.Adjust_lon(System.Double)")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Legacy PROJ-derived helper naming retained in projection base class.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.Projections.MapProjection.Inv_mlfn(System.Double)")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Legacy PROJ-derived helper naming retained in projection base class.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.Projections.MapProjection.Qsfn(System.Double,System.Double,System.Double)")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Legacy PROJ-derived helper naming retained in projection base class.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.Projections.MapProjection.Sincos(System.Double,System.Double@,System.Double@)")] +[assembly: SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Legacy public type name retained for API compatibility.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.AxisOrientationEnum")] +[assembly: SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Legacy public enum name retained for API compatibility.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.DomainFlags")] +[assembly: SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Legacy Bursa-Wolf parameter field names are retained for API compatibility.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.Wgs84ConversionInfo.Ex")] +[assembly: SuppressMessage("Design", "CA1008:Enums should have zero value", Justification = "Legacy datum enum uses historical EPSG-aligned numeric ranges; introducing a synthetic zero value risks semantic ambiguity.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.DatumType")] +[assembly: SuppressMessage("Design", "CA1008:Enums should have zero value", Justification = "Legacy domain flags enum intentionally starts at bit 1 for historical API compatibility.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.DomainFlags")] +[assembly: SuppressMessage("Design", "CA1028:Enum Storage should be Int32", Justification = "Legacy public enum storage type is retained for API and serialization compatibility.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.AxisOrientationEnum")] +[assembly: SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Registry keeps an instance shape for legacy extension and compatibility-safe minimal churn.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.ProjectionsRegistry")] +[assembly: SuppressMessage("Naming", "CA1716:Identifiers should not match keywords", Justification = "Alias is a long-standing metadata property in the public API and changing it would be a breaking change.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.IInfo.Alias")] +[assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "DefaultEnvelope is a long-standing public API shape and changing to a collection would be breaking.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.CoordinateSystem.DefaultEnvelope")] +[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1303:Const field names should begin with upper-case letter", Justification = "Legacy PROJ-derived const naming retained for compatibility and source continuity.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.Projections.MapProjection.prjMAXLONG")] +[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1303:Const field names should begin with upper-case letter", Justification = "Legacy projection math symbol naming retained for source continuity.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.Projections.MercatorAuxiliarySphere.k0")] + +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.HD_Min")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.HD_Other")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.HD_Classic")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.HD_Geocentric")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.HD_Max")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_Min")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_Other")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_Orthometric")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_Ellipsoidal")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_AltitudeBarometric")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_Normal")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_GeoidModelDerived")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_Depth")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.VD_Max")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.LD_Min")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.LD_Other")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.LD_Engineering")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.LD_Max")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.TD_Min")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.TD_Other")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.TD_Max")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.PD_Min")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.PD_Other")] +[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Enum names are long-standing public API values.", Scope = "member", Target = "~F:ProjNet.CoordinateSystems.DatumType.PD_Max")] + +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.CoordinateSystem")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.GeographicCoordinateSystem")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Wgs84ConversionInfo")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.IO.CoordinateSystems.WktStreamTokenizer")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.DatumTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Runtime transform keeps parsing and inversion helpers grouped with projection-specific math for readability and minimal churn.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.AffineRuntimeMathTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.GeocentricTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.PrimeMeridianTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.AlbersProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.KrovakProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.LambertAzimuthalEqualAreaProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.LambertConformalConic2SP")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.Mercator")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.MercatorAuxiliarySphere")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.ObliqueStereographicProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.OrthographicProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.PolarStereographicProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.PolyconicProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.PseudoMercator")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.TransverseMercator")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:Constant fields should appear before non-constant fields", Justification = "Legacy projection member layout is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.KrovakProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:Constant fields should appear before non-constant fields", Justification = "Legacy projection member layout is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.TransverseMercator")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:Constant fields should appear before non-constant fields", Justification = "Legacy projection member layout is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.HealpixProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Legacy projection base class groups specialized properties near related transform methods for maintainability and compatibility-safe minimal churn.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.MapProjection")] + +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Instance API shape is retained for compatibility and consistency with long-standing factory patterns.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.CoordinateSystemFactory")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Instance API shape is retained for compatibility and consistency with long-standing factory patterns.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.ParameterInfo")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Instance API shape is retained for compatibility and consistency with long-standing factory patterns.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Projection implementation kept consistent with existing inheritance and override patterns.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.ObliqueStereographicProjection")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Projection implementation kept consistent with existing inheritance and override patterns.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.PolarStereographicProjection")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Lowercase canonicalization is intentional for EPSG/projection key matching and legacy compatibility.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.ProjectionParameterSet")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Lowercase canonicalization is intentional for EPSG/projection key matching and legacy compatibility.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Lowercase canonicalization is intentional for EPSG/projection key matching and legacy compatibility.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.ProjectionsRegistry")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Generated EPSG normalization intentionally uses lowercase aliases for stable map keys.", Scope = "type", Target = "~T:ProjNet.Data.Generated.EpsgCoordinateSystemFactory")] +[assembly: SuppressMessage("Security", "CA5362:Potential reference cycle in deserialized object graph", Justification = "Cached inverse link is an intentional runtime graph edge in transform composition and is not used for untrusted deserialization.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.ConcatenatedTransform")] +[assembly: SuppressMessage("Security", "CA5362:Potential reference cycle in deserialized object graph", Justification = "NTv2 child-grid hierarchy is an intentional in-memory graph and not part of untrusted deserialization flows.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.Ntv2HGridShiftMathTransform.Ntv2Grid")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.IO.CoordinateSystems.StreamTokenizer")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.VerticalCoordinateSystem")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.HealpixProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.MapProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.ObliqueStereographicProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Projections.PolarStereographicProjection")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.AffineTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.GeoTiffGridShiftMathTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.GeoTiffGridLoader")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.GtxVGridShiftMathTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.MathTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Pipeline stack transfer keeps transform overrides grouped while retaining internal parser factories in the same type.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.PipelineStackTransferMathTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy member ordering is retained to avoid broad high-risk refactors.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.GeoTiffGridShiftMathTransform.Header")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "GeoTIFF grid transform and loader helpers are intentionally co-located for tightly coupled parsing/runtime behavior.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.GeoTiffVGridShiftMathTransform")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "GeoTIFF grid transform and loader helpers are intentionally co-located for tightly coupled parsing/runtime behavior.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.GeoTiffGridLoader")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "GeoTIFF grid transform and loader helpers are intentionally co-located for tightly coupled parsing/runtime behavior.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.BaseGeoGrid")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Grid resolver options remain co-located with resolver implementation for small internal resource-resolution API cohesion.", Scope = "type", Target = "~T:ProjNet.Resources.GridResourceResolverOptions")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1649:File name should match first type name", Justification = "Legacy multi-type source layout is intentionally retained to avoid large compatibility-risky file split churn.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.SampleEncoding")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1649:File name should match first type name", Justification = "Legacy file keeps interface and adapter definitions co-located for public API continuity.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.ICoordinateTransformationCore")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1649:File name should match first type name", Justification = "Legacy resource resolver file intentionally co-locates small support types with resolver implementation.", Scope = "type", Target = "~T:ProjNet.Resources.GridResourceResolutionMode")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1649:File name should match first type name", Justification = "Legacy resource resolver file intentionally co-locates small support types with resolver implementation.", Scope = "type", Target = "~T:ProjNet.Resources.IGridResourceFetchClient")] +[assembly: SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional", Justification = "Legacy matrix-style APIs and parsing shapes are retained for compatibility and readability.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.AffineTransform")] +[assembly: SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional", Justification = "Legacy matrix-style APIs and parsing shapes are retained for compatibility and readability.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.MathTransform")] +[assembly: SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional", Justification = "Legacy matrix-style APIs and parsing shapes are retained for compatibility and readability.", Scope = "type", Target = "~T:ProjNet.IO.CoordinateSystems.MathTransformWktReader")] +[assembly: SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Legacy properties intentionally remain unavailable and preserve long-standing behavior for unsupported XML/WKT representations.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.FittedCoordinateSystem.XML")] +[assembly: SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Legacy properties intentionally remain unavailable and preserve long-standing behavior for unsupported XML/WKT representations.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.Transformations.AffineTransform.XML")] +[assembly: SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Legacy properties intentionally remain unavailable and preserve long-standing behavior for unsupported XML/WKT representations.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.Transformations.GeographicTransform.WKT")] +[assembly: SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Legacy properties intentionally remain unavailable and preserve long-standing behavior for unsupported XML/WKT representations.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.Transformations.GeographicTransform.XML")] +[assembly: SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Legacy properties intentionally remain unavailable and preserve long-standing behavior for unsupported XML/WKT representations.", Scope = "member", Target = "~P:ProjNet.CoordinateSystems.Unit.XML")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Invalid or unsupported WKT rows are intentionally skipped during bulk coordinate-system loading to preserve resilient legacy enumeration behavior.", Scope = "member", Target = "~M:ProjNet.CoordinateSystemServices.CreateCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystemFactory,System.String)~ProjNet.CoordinateSystems.CoordinateSystem")] +[assembly: SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "CoordinateSystemServices lifecycle is application-scoped legacy API; disposing the internal initialization event is intentionally omitted for compatibility and to avoid teardown races.", Scope = "type", Target = "~T:ProjNet.CoordinateSystemServices")] +[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Legacy API signatures are retained for compatibility with existing consumers and extension patterns.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.Transformations.MathTransform.GetCodomainConvexHull(System.Collections.Generic.List{System.Double})")] +[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Legacy API signatures are retained for compatibility with existing consumers and extension patterns.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.Transformations.MathTransform.GetDomainFlags(System.Collections.Generic.List{System.Double})")] +[assembly: SuppressMessage("Maintainability", "CA1508:Avoid dead conditional code", Justification = "Candidate-selection and generated catalog creation paths intentionally retain defensive checks for clarity across legacy and generated code flows.", Scope = "type", Target = "~T:ProjNet.CoordinateSystems.Transformations.CoordinateOperationResolver")] +[assembly: SuppressMessage("Maintainability", "CA1508:Avoid dead conditional code", Justification = "Generated EPSG factory keeps explicit cache/create guard branches for readability and deterministic generation output.", Scope = "type", Target = "~T:ProjNet.Data.Generated.EpsgCoordinateSystemFactory")] +[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Legacy factory API signatures are retained for compatibility with existing consumers.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.CoordinateSystemFactory.CreateFittedCoordinateSystem(System.String,ProjNet.CoordinateSystems.CoordinateSystem,System.String,System.Collections.Generic.List{ProjNet.CoordinateSystems.AxisInfo})")] +[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Legacy factory API signatures are retained for compatibility with existing consumers.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.CoordinateSystemFactory.CreateFittedCoordinateSystem(System.String,ProjNet.CoordinateSystems.CoordinateSystem,ProjNet.CoordinateSystems.Transformations.MathTransform,System.Collections.Generic.List{ProjNet.CoordinateSystems.AxisInfo})")] +[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Legacy factory API signatures are retained for compatibility with existing consumers.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.CoordinateSystemFactory.CreateProjection(System.String,System.String,System.Collections.Generic.List{ProjNet.CoordinateSystems.ProjectionParameter})")] +[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Projection parameter cloning keeps list semantics for compatibility with existing derived implementations.", Scope = "member", Target = "~M:ProjNet.CoordinateSystems.Projections.MapProjection.CloneParametersList(System.Collections.Generic.IEnumerable{ProjNet.CoordinateSystems.ProjectionParameter})")] diff --git a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.Wkt1.cs b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.Wkt1.cs new file mode 100644 index 00000000..17de3088 --- /dev/null +++ b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.Wkt1.cs @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.Wkt; + +/// +/// Creates an object based on the supplied Well Known Text (WKT). +/// +public static partial class CoordinateSystemWktReader +{ + /// + /// Returns a from a WKT node. + /// + /// The parsed unit node. + /// An object that implements the IUnit interface. + private static Unit ReadUnit(WktKeywordNode node) + { + return ReadWkt1UnitFromNode( + node, + static (unitsPerUnit, unitName, authority, authorityCode) => new Unit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + /// + /// Returns a from a WKT node. + /// + /// The parsed unit node. + /// An object that implements the IUnit interface. + private static LinearUnit ReadLinearUnit(WktKeywordNode node) + { + return ReadWkt1UnitFromNode( + node, + static (unitsPerUnit, unitName, authority, authorityCode) => new LinearUnit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + /// + /// Returns a from a WKT node. + /// + /// The parsed unit node. + /// An object that implements the IUnit interface. + private static AngularUnit ReadAngularUnit(WktKeywordNode node) + { + return ReadWkt1UnitFromNode( + node, + static (unitsPerUnit, unitName, authority, authorityCode) => new AngularUnit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + /// + /// Returns a from a WKT node. + /// + /// The parsed axis node. + /// An AxisInfo object. + private static AxisInfo ReadAxis(WktKeywordNode node) + { + string axisName = node.GetStringChild(0); + string unitname = node.GetIdentifierChild(1); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is WktKeywordNode keywordChild) + { + throw new NotSupportedException($"WKT1 AXIS keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return unitname.ToUpperInvariant() switch + { + "DOWN" => new AxisInfo(axisName, AxisOrientationEnum.Down), + "EAST" => new AxisInfo(axisName, AxisOrientationEnum.East), + "NORTH" => new AxisInfo(axisName, AxisOrientationEnum.North), + "OTHER" => new AxisInfo(axisName, AxisOrientationEnum.Other), + "SOUTH" => new AxisInfo(axisName, AxisOrientationEnum.South), + "UP" => new AxisInfo(axisName, AxisOrientationEnum.Up), + "WEST" => new AxisInfo(axisName, AxisOrientationEnum.West), + _ => ThrowWktParseException($"Invalid axis name '{unitname}' in WKT"), + }; + } + + private static TUnit ReadWkt1UnitFromNode(WktKeywordNode node, Func factory) + { + string unitName = node.GetStringChild(0); + double unitsPerUnit = node.GetNumberChild(1); + string authority = string.Empty; + long authorityCode = -1; + + (string Authority, string Code)? authorityNode = node.GetAuthority(); + if (authorityNode.HasValue) + { + authority = authorityNode.Value.Authority; + authorityCode = long.TryParse(authorityNode.Value.Code, NumberStyles.Any, CultureInfo.InvariantCulture, out long parsedCode) + ? parsedCode + : -1; + } + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is WktKeywordNode keywordChild && !string.Equals(keywordChild.Keyword, "AUTHORITY", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"WKT1 {node.Keyword} keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return factory(unitsPerUnit, unitName, authority, authorityCode); + } + + // Reads either 3, 6 or 7 parameter Bursa-Wolf values from TOWGS84 token + private static Wgs84ConversionInfo ReadWGS84ConversionInfo(WktKeywordNode node) + { + IReadOnlyList values = node.GetAllNumbers(); + if (values.Count is not 3 and not 6 and not 7) + { + ThrowWktParseException("WKT1 TOWGS84 must contain 3, 6, or 7 numeric values."); + } + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is WktKeywordNode keywordChild) + { + throw new NotSupportedException($"WKT1 TOWGS84 keyword '{keywordChild.Keyword}' is not supported."); + } + } + + var info = new Wgs84ConversionInfo + { + Dx = values[0], + Dy = values[1], + Dz = values[2], + }; + + if (values.Count >= 6) + { + info.Ex = values[3]; + info.Ey = values[4]; + info.Ez = values[5]; + } + + if (values.Count == 7) + { + info.Ppm = values[6]; + } + + return info; + } + + private static Ellipsoid ReadEllipsoid(WktKeywordNode node) + { + string name = node.GetStringChild(0); + double majorAxis = node.GetNumberChild(1); + double e = node.GetNumberChild(2); + ReadWkt1Authority(node, out string authority, out long authorityCode); + + return new Ellipsoid(majorAxis, 0.0, e, true, LinearUnit.Metre, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static Projection ReadProjection(WktKeywordNode projectionNode, List parameterNodes) + { + string projectionName = projectionNode.GetStringChild(0); + ReadWkt1Authority(projectionNode, out string authority, out long authorityCode); + + var paramList = new List(parameterNodes.Count); + for (int i = 0; i < parameterNodes.Count; i++) + { + paramList.Add(ReadWkt1ProjectionParameter(parameterNodes[i])); + } + + return new Projection(projectionName, paramList, projectionName, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static ProjectionParameter ReadWkt1ProjectionParameter(WktKeywordNode node) + { + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is WktKeywordNode keywordChild) + { + throw new NotSupportedException($"WKT1 PARAMETER keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return new ProjectionParameter(node.GetStringChild(0), node.GetNumberChild(1)); + } + + private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktKeywordNode node) + { + string name = node.GetStringChild(0); + GeographicCoordinateSystem? geographicCS = null; + LinearUnit? linearUnit = null; + WktKeywordNode? projectionNode = null; + var parameterNodes = new List(); + var axisInfo = new List(2); + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + switch (keywordChild.Keyword) + { + case "GEOGCS": + geographicCS = ReadGeographicCoordinateSystem(keywordChild); + break; + case "UNIT": + linearUnit = ReadLinearUnit(keywordChild); + break; + case "PROJECTION": + projectionNode = keywordChild; + break; + case "PARAMETER": + parameterNodes.Add(keywordChild); + break; + case "AXIS": + axisInfo.Add(ReadAxis(keywordChild)); + break; + case "AUTHORITY": + ReadWkt1Authority(keywordChild, out authority, out authorityCode); + break; + default: + break; + } + } + + // This is default axis values if not specified. + if (axisInfo.Count == 0) + { + axisInfo.Add(new AxisInfo("X", AxisOrientationEnum.East)); + axisInfo.Add(new AxisInfo("Y", AxisOrientationEnum.North)); + } + + geographicCS = ArgumentGuard.ThrowIfNull(geographicCS, nameof(geographicCS)); + linearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + Projection projection = ReadProjection(ArgumentGuard.ThrowIfNull(projectionNode, nameof(projectionNode)), parameterNodes); + return new ProjectedCoordinateSystem(geographicCS.HorizontalDatum, geographicCS, linearUnit, projection, axisInfo, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static VerticalCoordinateSystem ReadVerticalCoordinateSystem(WktKeywordNode node) + { + string name = node.GetStringChild(0); + VerticalDatum? verticalDatum = null; + LinearUnit? linearUnit = null; + string authority = string.Empty; + long authorityCode = -1; + AxisInfo? info = null; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + switch (keywordChild.Keyword) + { + case "VERT_DATUM": + verticalDatum = ReadVerticalDatum(keywordChild); + break; + case "UNIT": + linearUnit = ReadLinearUnit(keywordChild); + break; + case "AXIS": + info = ReadAxis(keywordChild); + break; + case "AUTHORITY": + ReadWkt1Authority(keywordChild, out authority, out authorityCode); + break; + default: + break; + } + } + + // This is default axis values if not specified. + info ??= new AxisInfo("Up", AxisOrientationEnum.Up); + + return new VerticalCoordinateSystem( + ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)), + ArgumentGuard.ThrowIfNull(verticalDatum, nameof(verticalDatum)), + info, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static CompoundCoordinateSystem ReadCompoundCoordinateSystem(WktKeywordNode node) + { + string name = node.GetStringChild(0); + CoordinateSystem? headcs = null; + CoordinateSystem? tailcs = null; + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (string.Equals(keywordChild.Keyword, "AUTHORITY", StringComparison.OrdinalIgnoreCase)) + { + ReadWkt1Authority(keywordChild, out authority, out authorityCode); + } + else if (IsCoordinateSystemKeyword(keywordChild.Keyword)) + { + if (headcs is null) + { + headcs = ReadCoordinateSystemNode(keywordChild); + } + else if (tailcs is null) + { + tailcs = ReadCoordinateSystemNode(keywordChild); + } + } + } + + return new CompoundCoordinateSystem( + ArgumentGuard.ThrowIfNull(headcs, nameof(headcs)), + ArgumentGuard.ThrowIfNull(tailcs, nameof(tailcs)), + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static GeocentricCoordinateSystem ReadGeocentricCoordinateSystem(WktKeywordNode node) + { + string name = node.GetStringChild(0); + HorizontalDatum? horizontalDatum = null; + PrimeMeridian? primeMeridian = null; + LinearUnit? linearUnit = null; + string authority = string.Empty; + long authorityCode = -1; + var info = new List(3); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + switch (keywordChild.Keyword) + { + case "DATUM": + horizontalDatum = ReadHorizontalDatum(keywordChild); + break; + case "PRIMEM": + primeMeridian = ReadPrimeMeridian(keywordChild); + break; + case "UNIT": + linearUnit = ReadLinearUnit(keywordChild); + break; + case "AXIS": + info.Add(ReadAxis(keywordChild)); + break; + case "AUTHORITY": + ReadWkt1Authority(keywordChild, out authority, out authorityCode); + break; + default: + break; + } + } + + // This is default axis values if not specified. + if (info.Count == 0) + { + info.Add(new AxisInfo("Geocentric X", AxisOrientationEnum.Other)); + info.Add(new AxisInfo("Geocentric Y", AxisOrientationEnum.Other)); + info.Add(new AxisInfo("Geocentric Z", AxisOrientationEnum.North)); + } + + return new GeocentricCoordinateSystem( + ArgumentGuard.ThrowIfNull(horizontalDatum, nameof(horizontalDatum)), + ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)), + ArgumentGuard.ThrowIfNull(primeMeridian, nameof(primeMeridian)), + info, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static GeographicCoordinateSystem ReadGeographicCoordinateSystem(WktKeywordNode node) + { + string name = node.GetStringChild(0); + HorizontalDatum? horizontalDatum = null; + PrimeMeridian? primeMeridian = null; + AngularUnit? angularUnit = null; + string authority = string.Empty; + long authorityCode = -1; + var info = new List(2); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + switch (keywordChild.Keyword) + { + case "DATUM": + horizontalDatum = ReadHorizontalDatum(keywordChild); + break; + case "PRIMEM": + primeMeridian = ReadPrimeMeridian(keywordChild); + break; + case "UNIT": + angularUnit = ReadAngularUnit(keywordChild); + break; + case "AXIS": + info.Add(ReadAxis(keywordChild)); + break; + case "AUTHORITY": + ReadWkt1Authority(keywordChild, out authority, out authorityCode); + break; + default: + break; + } + } + + // This is default axis values if not specified. + if (info.Count == 0) + { + info.Add(new AxisInfo("Lon", AxisOrientationEnum.East)); + info.Add(new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + return new GeographicCoordinateSystem( + ArgumentGuard.ThrowIfNull(angularUnit, nameof(angularUnit)), + ArgumentGuard.ThrowIfNull(horizontalDatum, nameof(horizontalDatum)), + ArgumentGuard.ThrowIfNull(primeMeridian, nameof(primeMeridian)), + info, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static HorizontalDatum ReadHorizontalDatum(WktKeywordNode node) + { + string name = node.GetStringChild(0); + Wgs84ConversionInfo? wgsInfo = null; + string authority = string.Empty; + long authorityCode = -1; + Ellipsoid? ellipsoid = null; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + switch (keywordChild.Keyword) + { + case "SPHEROID": + ellipsoid = ReadEllipsoid(keywordChild); + break; + case "TOWGS84": + wgsInfo = ReadWGS84ConversionInfo(keywordChild); + break; + case "AUTHORITY": + ReadWkt1Authority(keywordChild, out authority, out authorityCode); + break; + default: + break; + } + } + + // make an assumption about the datum type. + return new HorizontalDatum( + ArgumentGuard.ThrowIfNull(ellipsoid, nameof(ellipsoid)), + wgsInfo, + DatumType.HD_Geocentric, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static VerticalDatum ReadVerticalDatum(WktKeywordNode node) + { + string name = node.GetStringChild(0); + var datumType = (DatumType)node.GetNumberChild(1); + ReadWkt1Authority(node, out string authority, out long authorityCode); + + return new VerticalDatum(datumType, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static PrimeMeridian ReadPrimeMeridian(WktKeywordNode node) + { + string name = node.GetStringChild(0); + double longitude = node.GetNumberChild(1); + ReadWkt1Authority(node, out string authority, out long authorityCode); + + // make an assumption about the Angular units - degrees. + return new PrimeMeridian(longitude, AngularUnit.Degrees, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static void ReadWkt1Authority(WktKeywordNode node, out string authority, out long authorityCode) + { + authority = string.Empty; + authorityCode = -1; + + if (node.KeywordEquals("AUTHORITY")) + { + ReadOnlySpan children = node.GetChildrenSpan(); + if (children.Length < 2) + { + return; + } + + authority = node.GetLeafTextChild(0); + authorityCode = long.TryParse(node.GetLeafTextChild(1), NumberStyles.Any, CultureInfo.InvariantCulture, out long directCode) + ? directCode + : -1; + return; + } + + (string Authority, string Code)? authorityNode = node.GetAuthority(); + if (!authorityNode.HasValue) + { + return; + } + + authority = authorityNode.Value.Authority; + authorityCode = long.TryParse(authorityNode.Value.Code, NumberStyles.Any, CultureInfo.InvariantCulture, out long parsedCode) + ? parsedCode + : -1; + } + + private static FittedCoordinateSystem ReadFittedCoordinateSystem(WktKeywordNode node) + { + string name = node.GetStringChild(0); + MathTransform? toBaseTransform = null; + CoordinateSystem? baseCS = null; + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (string.Equals(keywordChild.Keyword, "PARAM_MT", StringComparison.OrdinalIgnoreCase)) + { + toBaseTransform = MathTransformWktReader.ReadMathTransform(keywordChild); + } + else if (string.Equals(keywordChild.Keyword, "AUTHORITY", StringComparison.OrdinalIgnoreCase)) + { + ReadWkt1Authority(keywordChild, out authority, out authorityCode); + } + else if (baseCS is null && IsCoordinateSystemKeyword(keywordChild.Keyword)) + { + baseCS = ReadCoordinateSystemNode(keywordChild); + } + } + + return new FittedCoordinateSystem( + ArgumentGuard.ThrowIfNull(baseCS, nameof(baseCS)), + ArgumentGuard.ThrowIfNull(toBaseTransform, nameof(toBaseTransform)), + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } +} diff --git a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.Wkt2.cs b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.Wkt2.cs new file mode 100644 index 00000000..784a6ac7 --- /dev/null +++ b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.Wkt2.cs @@ -0,0 +1,2360 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.Wkt; + +/// +/// Creates an object based on the supplied Well Known Text (WKT). +/// +public static partial class CoordinateSystemWktReader +{ + private static CoordinateSystem ReadWkt2GeodeticCoordinateReferenceSystem(WktKeywordNode node) + { + string rootKeyword = node.Keyword; + string name = node.GetStringChild(0); + + HorizontalDatum? horizontalDatum = null; + GeographicCoordinateSystem? baseGeographicCoordinateSystem = null; + Projection? derivingConversion = null; + PrimeMeridian? primeMeridian = null; + AngularUnit? angularUnit = null; + LinearUnit? linearUnit = null; + string? coordinateSystemType = null; + int coordinateSystemDimension = 0; + string authority = string.Empty; + long authorityCode = -1; + var axisInfo = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("DATUM")) + { + horizontalDatum = ReadWkt2HorizontalDatum(keywordChild); + } + else if (keywordChild.KeywordEquals("ENSEMBLE")) + { + horizontalDatum = ReadWkt2HorizontalDatumEnsemble(keywordChild); + } + else if (keywordChild.KeywordEquals("BASEGEOGCRS") || keywordChild.KeywordEquals("BASEGEODCRS")) + { + baseGeographicCoordinateSystem = ReadWkt2BaseGeographicCoordinateSystem(keywordChild); + } + else if (keywordChild.KeywordEquals("DERIVINGCONVERSION")) + { + derivingConversion = ReadWkt2DerivingConversion(keywordChild, out AngularUnit? derivingAngularUnit); + angularUnit = MergeAxisAngularUnit(angularUnit, derivingAngularUnit); + } + else if (keywordChild.KeywordEquals("PRIMEM")) + { + primeMeridian = ReadWkt2PrimeMeridian(keywordChild); + } + else if (keywordChild.KeywordEquals("CS")) + { + (coordinateSystemType, coordinateSystemDimension) = ReadWkt2CoordinateSystemDefinition(keywordChild); + } + else if (keywordChild.KeywordEquals("AXIS")) + { + axisInfo.Add(ReadWkt2Axis(keywordChild, out AngularUnit? axisAngularUnit, out LinearUnit? axisLinearUnit)); + angularUnit = MergeAxisAngularUnit(angularUnit, axisAngularUnit); + linearUnit = MergeAxisLinearUnit(linearUnit, axisLinearUnit); + } + else if (keywordChild.KeywordEquals("ANGLEUNIT")) + { + angularUnit = ReadWkt2AngularUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("LENGTHUNIT")) + { + linearUnit = ReadWkt2LinearUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + bool isDerived = baseGeographicCoordinateSystem is not null || derivingConversion is not null; + if (isDerived) + { + if (horizontalDatum is not null) + { + ThrowWktParseException("WKT2 derived geodetic CRS must use BASEGEOGCRS or BASEGEODCRS instead of a top-level DATUM block."); + } + + if (baseGeographicCoordinateSystem is null) + { + ThrowWktParseException("WKT2 derived geodetic CRS is missing a BASEGEOGCRS or BASEGEODCRS block."); + } + + if (derivingConversion is null) + { + ThrowWktParseException("WKT2 derived geodetic CRS is missing a DERIVINGCONVERSION block."); + } + + if (string.IsNullOrWhiteSpace(coordinateSystemType)) + { + ThrowWktParseException("WKT2 derived geodetic CRS is missing a CS block."); + } + + if (!string.Equals(coordinateSystemType, "ellipsoidal", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"WKT2 derived geodetic coordinate system type '{coordinateSystemType}' is not supported."); + } + + if (coordinateSystemDimension != 2) + { + throw new NotSupportedException("WKT2 derived geodetic CRS dimensions other than 2 are not supported."); + } + + if (axisInfo.Count != coordinateSystemDimension) + { + ThrowWktParseException($"WKT2 derived geodetic CRS declared dimension {coordinateSystemDimension}, but provided {axisInfo.Count} AXIS blocks."); + } + + if (angularUnit is null) + { + ThrowWktParseException("WKT2 derived geodetic CRS is missing an ANGLEUNIT block."); + } + + AffineTransform transform = DerivedCoordinateSystemSupport.CreateAffineTransform(derivingConversion); + var fittedCoordinateSystem = new FittedCoordinateSystem( + baseGeographicCoordinateSystem, + transform, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty, + axisInfo); + return fittedCoordinateSystem; + } + + if (horizontalDatum is null) + { + ThrowWktParseException("WKT2 geodetic CRS is missing a DATUM block."); + } + + if (string.IsNullOrWhiteSpace(coordinateSystemType)) + { + ThrowWktParseException("WKT2 geodetic CRS is missing a CS block."); + } + + if (axisInfo.Count != coordinateSystemDimension) + { + ThrowWktParseException($"WKT2 geodetic CRS declared dimension {coordinateSystemDimension}, but provided {axisInfo.Count} AXIS blocks."); + } + + if (string.Equals(coordinateSystemType, "ellipsoidal", StringComparison.OrdinalIgnoreCase)) + { + if (coordinateSystemDimension == 3) + { + if (angularUnit is null) + { + ThrowWktParseException("WKT2 ellipsoidal CRS is missing ANGLEUNIT metadata."); + } + + if (linearUnit is null) + { + ThrowWktParseException("WKT2 three-dimensional ellipsoidal CRS is missing LENGTHUNIT metadata."); + } + + primeMeridian ??= PrimeMeridian.Greenwich; + return CreateOperationalWkt2EllipsoidalHeightCompoundCoordinateSystem( + name, + authority, + authorityCode, + horizontalDatum, + primeMeridian, + angularUnit, + linearUnit, + axisInfo); + } + + if (coordinateSystemDimension != 2) + { + throw new NotSupportedException("WKT2 ellipsoidal CRS dimensions other than 2 are not supported."); + } + + if (angularUnit is null) + { + ThrowWktParseException("WKT2 ellipsoidal CRS is missing ANGLEUNIT metadata."); + } + + primeMeridian ??= PrimeMeridian.Greenwich; + return new GeographicCoordinateSystem( + angularUnit, + horizontalDatum, + primeMeridian, + axisInfo, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + if (string.Equals(coordinateSystemType, "cartesian", StringComparison.OrdinalIgnoreCase)) + { + if (coordinateSystemDimension != 3) + { + throw new NotSupportedException("WKT2 cartesian geodetic CRS dimensions other than 3 are not supported."); + } + + if (linearUnit is null) + { + ThrowWktParseException("WKT2 cartesian geodetic CRS is missing LENGTHUNIT metadata."); + } + + primeMeridian ??= PrimeMeridian.Greenwich; + return new GeocentricCoordinateSystem( + horizontalDatum, + linearUnit, + primeMeridian, + axisInfo, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + throw new NotSupportedException($"WKT2 coordinate system type '{coordinateSystemType}' is not supported."); + } + + private static CompoundCoordinateSystem CreateOperationalWkt2EllipsoidalHeightCompoundCoordinateSystem( + string name, + string authority, + long authorityCode, + HorizontalDatum horizontalDatum, + PrimeMeridian primeMeridian, + AngularUnit angularUnit, + LinearUnit linearUnit, + List axisInfo) + { + var head = new GeographicCoordinateSystem( + angularUnit, + horizontalDatum, + primeMeridian, + [new AxisInfo(axisInfo[0]), new AxisInfo(axisInfo[1])], + name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + var tail = new VerticalCoordinateSystem( + linearUnit, + new VerticalDatum(DatumType.VD_Ellipsoidal, "Ellipsoidal height datum", string.Empty, -1, string.Empty, string.Empty, string.Empty), + new AxisInfo(axisInfo[2]), + axisInfo[2].Name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + return new CompoundCoordinateSystem(head, tail, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static (string Type, int Dimension) ReadWkt2CoordinateSystemDefinition(WktKeywordNode node) + { + if (!node.KeywordEquals("CS")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in CS."); + } + + string coordinateSystemType = node.GetIdentifierChild(0); + int dimension = checked((int)node.GetNumberChild(1)); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ID") || + ShouldSkipWkt2MetadataNode(keywordChild)) + { + continue; + } + + throw new NotSupportedException($"WKT2 CS keyword '{keywordChild.Keyword}' is not supported."); + } + + return (coordinateSystemType, dimension); + } + + private static AxisInfo ReadWkt2Axis(WktKeywordNode node, out AngularUnit? angularUnit, out LinearUnit? linearUnit) + { + (AxisInfo axis, IUnit? unit) = ReadWkt2AxisDefinition(node); + WktKeywordNode? unitNode = node.FindChild("ANGLEUNIT", "LENGTHUNIT", "SCALEUNIT", "TIMEUNIT", "PARAMETRICUNIT"); + + if (unit is not null && unit is not AngularUnit && unit is not LinearUnit) + { + throw new NotSupportedException($"WKT2 AXIS keyword '{ArgumentGuard.ThrowIfNull(unitNode, nameof(unitNode)).Keyword}' is not supported."); + } + + angularUnit = unit as AngularUnit; + linearUnit = unit as LinearUnit; + return axis; + } + + private static AxisOrientationEnum ParseWkt2AxisOrientation(string orientationToken) + { + if (string.Equals(orientationToken, "NORTH", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.North; + } + + if (string.Equals(orientationToken, "SOUTH", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.South; + } + + if (string.Equals(orientationToken, "EAST", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.East; + } + + if (string.Equals(orientationToken, "WEST", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.West; + } + + if (string.Equals(orientationToken, "UP", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.Up; + } + + if (string.Equals(orientationToken, "DOWN", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.Down; + } + + if (string.Equals(orientationToken, "OTHER", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.Other; + } + + if (string.Equals(orientationToken, "GEOCENTRICX", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.Other; + } + + if (string.Equals(orientationToken, "GEOCENTRICY", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.East; + } + + if (string.Equals(orientationToken, "GEOCENTRICZ", StringComparison.OrdinalIgnoreCase)) + { + return AxisOrientationEnum.North; + } + + return ThrowWktParseException($"Invalid WKT2 axis orientation '{orientationToken}'."); + } + + private static (AxisInfo Axis, IUnit? Unit) ReadWkt2AxisDefinition(WktKeywordNode node) + { + if (!node.KeywordEquals("AXIS")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in AXIS."); + } + + IUnit? unit = null; + string axisName = node.GetStringChild(0); + AxisOrientationEnum orientation = ParseWkt2AxisOrientation(node.GetIdentifierChild(1)); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ANGLEUNIT") + || keywordChild.KeywordEquals("LENGTHUNIT") + || keywordChild.KeywordEquals("SCALEUNIT") + || keywordChild.KeywordEquals("TIMEUNIT") + || keywordChild.KeywordEquals("PARAMETRICUNIT")) + { + unit = ReadWkt2Unit(keywordChild); + } + else if (!keywordChild.KeywordEquals("ID")) + { + if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 AXIS keyword '{keywordChild.Keyword}' is not supported."); + } + } + } + + return (new AxisInfo(axisName, orientation), unit); + } + + private static HorizontalDatum ReadWkt2HorizontalDatum(WktKeywordNode node) + { + if (!node.KeywordEquals("DATUM")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in DATUM."); + } + + string name = node.GetStringChild(0); + string authority = string.Empty; + long authorityCode = -1; + Ellipsoid? ellipsoid = null; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ELLIPSOID")) + { + ellipsoid = ReadWkt2Ellipsoid(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 DATUM keyword '{keywordChild.Keyword}' is not supported."); + } + } + + if (ellipsoid is null) + { + ThrowWktParseException("WKT2 DATUM is missing an ELLIPSOID block."); + } + + return new HorizontalDatum(ellipsoid, null, DatumType.HD_Geocentric, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static HorizontalDatum ReadWkt2HorizontalDatumEnsemble(WktKeywordNode node) + { + DatumEnsemble ensemble = ReadWkt2DatumEnsemble(node, requireEllipsoid: true); + Ellipsoid ellipsoid = ArgumentGuard.ThrowIfNull(ensemble.Ellipsoid, nameof(ensemble)); + return new HorizontalDatum(ellipsoid, null, DatumType.HD_Geocentric, ensemble.Name, ensemble.Authority, ensemble.AuthorityCode, string.Empty, string.Empty, string.Empty, ensemble); + } + + private static DatumEnsemble ReadWkt2DatumEnsemble(WktKeywordNode node, bool requireEllipsoid) + { + if (!node.KeywordEquals("ENSEMBLE")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in ENSEMBLE."); + } + + string name = node.GetStringChild(0); + var members = new List(); + Ellipsoid? ellipsoid = null; + double? accuracy = null; + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("MEMBER")) + { + members.Add(ReadWkt2DatumEnsembleMember(keywordChild)); + } + else if (keywordChild.KeywordEquals("ELLIPSOID")) + { + ellipsoid = ReadWkt2Ellipsoid(keywordChild); + } + else if (keywordChild.KeywordEquals("ENSEMBLEACCURACY")) + { + accuracy = ReadWkt2DatumEnsembleAccuracy(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 ENSEMBLE keyword '{keywordChild.Keyword}' is not supported."); + } + } + + if (members.Count == 0) + { + ThrowWktParseException("WKT2 ENSEMBLE is missing MEMBER blocks."); + } + + if (requireEllipsoid && ellipsoid is null) + { + ThrowWktParseException("WKT2 ENSEMBLE is missing an ELLIPSOID block."); + } + + if (accuracy is null) + { + ThrowWktParseException("WKT2 ENSEMBLE is missing an ENSEMBLEACCURACY block."); + } + + return new DatumEnsemble(name, members, accuracy.Value, ellipsoid, authority, authorityCode); + } + + private static DatumEnsembleMember ReadWkt2DatumEnsembleMember(WktKeywordNode node) + { + if (!node.KeywordEquals("MEMBER")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in MEMBER."); + } + + string name = node.GetStringChild(0); + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 MEMBER keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return new DatumEnsembleMember(name, authority, authorityCode); + } + + private static double ReadWkt2DatumEnsembleAccuracy(WktKeywordNode node) + { + if (!node.KeywordEquals("ENSEMBLEACCURACY")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in ENSEMBLEACCURACY."); + } + + return node.GetNumberChild(0); + } + + private static Ellipsoid ReadWkt2Ellipsoid(WktKeywordNode node) + { + if (!node.KeywordEquals("ELLIPSOID")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in ELLIPSOID."); + } + + string name = node.GetStringChild(0); + double semiMajorAxis = node.GetNumberChild(1); + double inverseFlattening = node.GetNumberChild(2); + string authority = string.Empty; + long authorityCode = -1; + LinearUnit? axisUnit = null; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("LENGTHUNIT")) + { + axisUnit = ReadWkt2LinearUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 ELLIPSOID keyword '{keywordChild.Keyword}' is not supported."); + } + } + + if (axisUnit is null) + { + ThrowWktParseException("WKT2 ELLIPSOID is missing a LENGTHUNIT block."); + } + + return new Ellipsoid(semiMajorAxis, 0d, inverseFlattening, true, axisUnit, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static PrimeMeridian ReadWkt2PrimeMeridian(WktKeywordNode node) + { + if (!node.KeywordEquals("PRIMEM")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in PRIMEM."); + } + + string name = node.GetStringChild(0); + double longitude = node.GetNumberChild(1); + string authority = string.Empty; + long authorityCode = -1; + AngularUnit angularUnit = AngularUnit.Degrees; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ANGLEUNIT")) + { + angularUnit = ReadWkt2AngularUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 PRIMEM keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return new PrimeMeridian(longitude, angularUnit, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static AngularUnit ReadWkt2AngularUnit(WktKeywordNode node) + { + return ReadWkt2UnitFromNode( + node, + "ANGLEUNIT", + static (conversionFactor, name, authority, authorityCode) => new AngularUnit(conversionFactor, name, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + private static LinearUnit ReadWkt2LinearUnit(WktKeywordNode node) + { + return ReadWkt2UnitFromNode( + node, + "LENGTHUNIT", + static (conversionFactor, name, authority, authorityCode) => new LinearUnit(conversionFactor, name, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + private static Unit ReadWkt2ScaleUnit(WktKeywordNode node) + { + return ReadWkt2UnitFromNode( + node, + "SCALEUNIT", + static (conversionFactor, name, authority, authorityCode) => new Unit(conversionFactor, name, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + private static TimeUnit ReadWkt2TimeUnit(WktKeywordNode node) + { + return ReadWkt2UnitFromNode( + node, + "TIMEUNIT", + static (conversionFactor, name, authority, authorityCode) => new TimeUnit(conversionFactor, name, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + private static ParametricUnit ReadWkt2ParametricUnit(WktKeywordNode node) + { + return ReadWkt2UnitFromNode( + node, + "PARAMETRICUNIT", + static (conversionFactor, name, authority, authorityCode) => new ParametricUnit(conversionFactor, name, authority, authorityCode, string.Empty, string.Empty, string.Empty)); + } + + private static IUnit ReadWkt2Unit(WktKeywordNode node) + { + if (node.KeywordEquals("ANGLEUNIT")) + { + return ReadWkt2AngularUnit(node); + } + + if (node.KeywordEquals("LENGTHUNIT")) + { + return ReadWkt2LinearUnit(node); + } + + if (node.KeywordEquals("SCALEUNIT")) + { + return ReadWkt2ScaleUnit(node); + } + + if (node.KeywordEquals("TIMEUNIT")) + { + return ReadWkt2TimeUnit(node); + } + + if (node.KeywordEquals("PARAMETRICUNIT")) + { + return ReadWkt2ParametricUnit(node); + } + + throw new NotSupportedException($"WKT2 unit keyword '{node.Keyword}' is not supported."); + } + + private static TUnit ReadWkt2UnitFromNode(WktKeywordNode node, string expectedKeyword, Func factory) + { + if (!node.KeywordEquals(expectedKeyword)) + { + throw new NotSupportedException($"WKT2 unit keyword '{node.Keyword}' is not supported."); + } + + string name = node.GetStringChild(0); + double conversionFactor = node.GetNumberChild(1); + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + continue; + } + + if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 {expectedKeyword} keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return factory(conversionFactor, name, authority, authorityCode); + } + + private static bool Wkt2UnitsEqual(IUnit left, IUnit right) + { + return left.GetType() == right.GetType() && left.EqualParams(right); + } + + private static List ResolveWkt2CoordinateSystemUnits(IUnit? rootUnit, List axisUnits, int dimension, string context, bool allowMixedUnits) + { + if (axisUnits.Count != dimension) + { + ThrowWktParseException($"{context} declared dimension {dimension}, but provided {axisUnits.Count} AXIS blocks."); + } + + var resolvedUnits = new List(dimension); + for (int i = 0; i < axisUnits.Count; i++) + { + IUnit? axisUnit = axisUnits[i]; + if (axisUnit is null) + { + if (rootUnit is null) + { + ThrowWktParseException($"{context} axis {i.ToString(CultureInfo.InvariantCulture)} is missing a unit definition."); + } + + axisUnit = rootUnit; + } + else if (rootUnit is not null && !Wkt2UnitsEqual(rootUnit, axisUnit) && !allowMixedUnits) + { + throw new NotSupportedException($"{context} axis-specific units must match the root unit."); + } + + resolvedUnits.Add(ArgumentGuard.ThrowIfNull(axisUnit, nameof(axisUnit))); + } + + return resolvedUnits; + } + + private static EngineeringDatum ReadWkt2EngineeringDatum(WktKeywordNode node) + { + if (!node.KeywordEquals("EDATUM") && + !node.KeywordEquals("ENGINEERINGDATUM")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in engineering datum."); + } + + string name = node.GetStringChild(0); + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 {node.Keyword} keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return new EngineeringDatum(name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static TemporalDatum ReadWkt2TemporalDatum(WktKeywordNode node) + { + if (!node.KeywordEquals("TDATUM") && + !node.KeywordEquals("TIMEDATUM")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in temporal datum."); + } + + string name = node.GetStringChild(0); + string timeOrigin = string.Empty; + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("TIMEORIGIN")) + { + timeOrigin = keywordChild.GetStringChild(0); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 {node.Keyword} keyword '{keywordChild.Keyword}' is not supported."); + } + } + + if (string.IsNullOrWhiteSpace(timeOrigin)) + { + ThrowWktParseException("WKT2 temporal datum is missing a TIMEORIGIN block."); + } + + return new TemporalDatum(timeOrigin, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static ParametricDatum ReadWkt2ParametricDatum(WktKeywordNode node) + { + if (!node.KeywordEquals("PDATUM") && + !node.KeywordEquals("PARAMETRICDATUM")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in parametric datum."); + } + + string name = node.GetStringChild(0); + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 {node.Keyword} keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return new ParametricDatum(name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static EngineeringCoordinateSystem ReadWkt2EngineeringCoordinateSystem(WktKeywordNode node) + { + string rootKeyword = node.Keyword; + string name = node.GetStringChild(0); + + EngineeringDatum? engineeringDatum = null; + string? coordinateSystemType = null; + int coordinateSystemDimension = 0; + IUnit? rootUnit = null; + string authority = string.Empty; + long authorityCode = -1; + var axisInfo = new List(); + var axisUnits = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("EDATUM") || keywordChild.KeywordEquals("ENGINEERINGDATUM")) + { + engineeringDatum = ReadWkt2EngineeringDatum(keywordChild); + } + else if (keywordChild.KeywordEquals("CS")) + { + (coordinateSystemType, coordinateSystemDimension) = ReadWkt2CoordinateSystemDefinition(keywordChild); + } + else if (keywordChild.KeywordEquals("AXIS")) + { + (AxisInfo axis, IUnit? unit) = ReadWkt2AxisDefinition(keywordChild); + axisInfo.Add(axis); + axisUnits.Add(unit); + } + else if (keywordChild.KeywordEquals("ANGLEUNIT") + || keywordChild.KeywordEquals("LENGTHUNIT") + || keywordChild.KeywordEquals("SCALEUNIT") + || keywordChild.KeywordEquals("TIMEUNIT") + || keywordChild.KeywordEquals("PARAMETRICUNIT")) + { + rootUnit = ReadWkt2Unit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + if (engineeringDatum is null) + { + ThrowWktParseException("WKT2 engineering CRS is missing an EDATUM or ENGINEERINGDATUM block."); + } + + if (string.IsNullOrWhiteSpace(coordinateSystemType)) + { + ThrowWktParseException("WKT2 engineering CRS is missing a CS block."); + } + + List resolvedUnits = ResolveWkt2CoordinateSystemUnits(rootUnit, axisUnits, coordinateSystemDimension, "WKT2 engineering CRS", allowMixedUnits: true); + engineeringDatum = ArgumentGuard.ThrowIfNull(engineeringDatum, nameof(engineeringDatum)); + return new EngineeringCoordinateSystem( + engineeringDatum, + ArgumentGuard.ThrowIfNull(coordinateSystemType, nameof(coordinateSystemType)), + axisInfo, + resolvedUnits, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static TemporalCoordinateSystem ReadWkt2TemporalCoordinateSystem(WktKeywordNode node) + { + const string rootKeyword = "TIMECRS"; + string name = node.GetStringChild(0); + + TemporalDatum? temporalDatum = null; + string? coordinateSystemType = null; + int coordinateSystemDimension = 0; + IUnit? rootUnit = null; + string authority = string.Empty; + long authorityCode = -1; + var axisInfo = new List(); + var axisUnits = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("TDATUM") || keywordChild.KeywordEquals("TIMEDATUM")) + { + temporalDatum = ReadWkt2TemporalDatum(keywordChild); + } + else if (keywordChild.KeywordEquals("CS")) + { + (coordinateSystemType, coordinateSystemDimension) = ReadWkt2CoordinateSystemDefinition(keywordChild); + } + else if (keywordChild.KeywordEquals("AXIS")) + { + (AxisInfo axis, IUnit? unit) = ReadWkt2AxisDefinition(keywordChild); + axisInfo.Add(axis); + axisUnits.Add(unit); + } + else if (keywordChild.KeywordEquals("TIMEUNIT")) + { + rootUnit = ReadWkt2TimeUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + if (temporalDatum is null) + { + ThrowWktParseException("WKT2 temporal CRS is missing a TDATUM or TIMEDATUM block."); + } + + if (!string.Equals(coordinateSystemType, "temporal", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"WKT2 temporal coordinate system type '{coordinateSystemType}' is not supported."); + } + + List resolvedUnits = ResolveWkt2CoordinateSystemUnits(rootUnit, axisUnits, coordinateSystemDimension, "WKT2 temporal CRS", allowMixedUnits: false); + if (resolvedUnits.Count != 1 || resolvedUnits[0] is not TimeUnit timeUnit) + { + throw new NotSupportedException("WKT2 temporal CRS requires TIMEUNIT metadata."); + } + + temporalDatum = ArgumentGuard.ThrowIfNull(temporalDatum, nameof(temporalDatum)); + return new TemporalCoordinateSystem( + timeUnit, + temporalDatum, + axisInfo[0], + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static ParametricCoordinateSystem ReadWkt2ParametricCoordinateSystem(WktKeywordNode node) + { + const string rootKeyword = "PARAMETRICCRS"; + string name = node.GetStringChild(0); + + ParametricDatum? parametricDatum = null; + string? coordinateSystemType = null; + int coordinateSystemDimension = 0; + IUnit? rootUnit = null; + string authority = string.Empty; + long authorityCode = -1; + var axisInfo = new List(); + var axisUnits = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("PDATUM") || keywordChild.KeywordEquals("PARAMETRICDATUM")) + { + parametricDatum = ReadWkt2ParametricDatum(keywordChild); + } + else if (keywordChild.KeywordEquals("CS")) + { + (coordinateSystemType, coordinateSystemDimension) = ReadWkt2CoordinateSystemDefinition(keywordChild); + } + else if (keywordChild.KeywordEquals("AXIS")) + { + (AxisInfo axis, IUnit? unit) = ReadWkt2AxisDefinition(keywordChild); + axisInfo.Add(axis); + axisUnits.Add(unit); + } + else if (keywordChild.KeywordEquals("PARAMETRICUNIT")) + { + rootUnit = ReadWkt2ParametricUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + if (parametricDatum is null) + { + ThrowWktParseException("WKT2 parametric CRS is missing a PDATUM or PARAMETRICDATUM block."); + } + + if (!string.Equals(coordinateSystemType, "parametric", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"WKT2 parametric coordinate system type '{coordinateSystemType}' is not supported."); + } + + List resolvedUnits = ResolveWkt2CoordinateSystemUnits(rootUnit, axisUnits, coordinateSystemDimension, "WKT2 parametric CRS", allowMixedUnits: false); + if (resolvedUnits.Count != 1 || resolvedUnits[0] is not ParametricUnit parametricUnit) + { + throw new NotSupportedException("WKT2 parametric CRS requires PARAMETRICUNIT metadata."); + } + + parametricDatum = ArgumentGuard.ThrowIfNull(parametricDatum, nameof(parametricDatum)); + return new ParametricCoordinateSystem( + parametricUnit, + parametricDatum, + axisInfo[0], + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static CoordinateOperation ReadWkt2CoordinateOperation(WktKeywordNode node) + { + string name = node.GetStringChild(0); + + CoordinateSystem? sourceCoordinateSystem = null; + CoordinateSystem? targetCoordinateSystem = null; + string methodName = string.Empty; + string authority = string.Empty; + long authorityCode = -1; + var parameters = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("SOURCECRS")) + { + sourceCoordinateSystem = ReadWkt2BoundCoordinateSystemComponent(keywordChild); + } + else if (keywordChild.KeywordEquals("TARGETCRS")) + { + targetCoordinateSystem = ReadWkt2BoundCoordinateSystemComponent(keywordChild); + } + else if (keywordChild.KeywordEquals("METHOD")) + { + methodName = ReadWkt2ProjectionMethod(keywordChild); + } + else if (keywordChild.KeywordEquals("PARAMETER")) + { + parameters.Add(ReadWkt2CoordinateOperationParameter(keywordChild)); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 COORDINATEOPERATION keyword '{keywordChild.Keyword}' is not supported."); + } + } + + if (sourceCoordinateSystem is null) + { + ThrowWktParseException("WKT2 coordinate operation is missing a SOURCECRS block."); + } + + if (targetCoordinateSystem is null) + { + ThrowWktParseException("WKT2 coordinate operation is missing a TARGETCRS block."); + } + + if (string.IsNullOrWhiteSpace(methodName)) + { + ThrowWktParseException("WKT2 coordinate operation is missing a METHOD block."); + } + + sourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + targetCoordinateSystem = ArgumentGuard.ThrowIfNull(targetCoordinateSystem, nameof(targetCoordinateSystem)); + return new CoordinateOperation( + methodName, + parameters, + sourceCoordinateSystem, + targetCoordinateSystem, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static Parameter ReadWkt2CoordinateOperationParameter(WktKeywordNode node) + { + string parameterName = node.GetStringChild(0); + double value = node.GetNumberChild(1); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (!keywordChild.KeywordEquals("ANGLEUNIT") + && !keywordChild.KeywordEquals("LENGTHUNIT") + && !keywordChild.KeywordEquals("SCALEUNIT") + && !keywordChild.KeywordEquals("TIMEUNIT") + && !keywordChild.KeywordEquals("PARAMETRICUNIT") + && !keywordChild.KeywordEquals("ID") + && !ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 COORDINATEOPERATION PARAMETER keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return new Parameter(parameterName, value); + } + + private static ConcatenatedOperation ReadWkt2ConcatenatedOperation(WktKeywordNode node) + { + string name = node.GetStringChild(0); + + CoordinateSystem? sourceCoordinateSystem = null; + CoordinateSystem? targetCoordinateSystem = null; + string authority = string.Empty; + long authorityCode = -1; + var steps = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("SOURCECRS")) + { + sourceCoordinateSystem = ReadWkt2BoundCoordinateSystemComponent(keywordChild); + } + else if (keywordChild.KeywordEquals("TARGETCRS")) + { + targetCoordinateSystem = ReadWkt2BoundCoordinateSystemComponent(keywordChild); + } + else if (keywordChild.KeywordEquals("STEP")) + { + steps.Add(ReadWkt2ConcatenatedOperationStep(keywordChild)); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 CONCATENATEDOPERATION keyword '{keywordChild.Keyword}' is not supported."); + } + } + + if (sourceCoordinateSystem is null) + { + ThrowWktParseException("WKT2 concatenated operation is missing a SOURCECRS block."); + } + + if (targetCoordinateSystem is null) + { + ThrowWktParseException("WKT2 concatenated operation is missing a TARGETCRS block."); + } + + sourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + targetCoordinateSystem = ArgumentGuard.ThrowIfNull(targetCoordinateSystem, nameof(targetCoordinateSystem)); + return new ConcatenatedOperation( + steps, + sourceCoordinateSystem, + targetCoordinateSystem, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static CoordinateOperation ReadWkt2ConcatenatedOperationStep(WktKeywordNode node) + { + WktKeywordNode? operationNode = node.FindChild("COORDINATEOPERATION"); + if (operationNode is null) + { + WktKeywordNode? firstChild = null; + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is WktKeywordNode keywordChild) + { + firstChild = keywordChild; + break; + } + } + + throw new NotSupportedException($"WKT2 STEP keyword '{firstChild?.Keyword ?? string.Empty}' is not supported."); + } + + return ReadWkt2CoordinateOperation(operationNode); + } + + private static void ReadIdentifierWithUnknownCode(WktKeywordNode node, out string authority, out long authorityCode) + { + if (!node.KeywordEquals("ID")) + { + throw new NotSupportedException($"WKT2 identifier keyword '{node.Keyword}' is not supported."); + } + + ReadOnlySpan children = node.GetChildrenSpan(); + if (children.Length < 2) + { + ThrowWktParseException("WKT2 ID is missing an authority code."); + } + + authority = node.GetLeafTextChild(0); + authorityCode = long.TryParse(node.GetLeafTextChild(1), NumberStyles.Any, CultureInfo.InvariantCulture, out long parsedCode) + ? parsedCode + : -1; + } + + private static bool ShouldSkipWkt2MetadataNode(WktKeywordNode node) + { + return node.KeywordEquals("ANCHOR") + || node.KeywordEquals("ANCHOREPOCH") + || node.KeywordEquals("AREA") + || node.KeywordEquals("BBOX") + || node.KeywordEquals("DEFININGTRANSFORMATION") + || node.KeywordEquals("DYNAMIC") + || node.KeywordEquals("GEOIDMODEL") + || node.KeywordEquals("MERIDIAN") + || node.KeywordEquals("ORDER") + || node.KeywordEquals("REMARK") + || node.KeywordEquals("SCOPE") + || node.KeywordEquals("VERSION") + || node.KeywordEquals("USAGE"); + } + + private static AngularUnit? MergeAxisAngularUnit(AngularUnit? current, AngularUnit? candidate) + { + if (candidate is null) + { + return current; + } + + if (current is null || current.EqualParams(candidate)) + { + return candidate; + } + + throw new NotSupportedException("WKT2 axis-specific ANGLEUNIT values must match within the same CRS."); + } + + private static GeographicCoordinateSystem OverrideGeographicAngularUnit(GeographicCoordinateSystem geographicCoordinateSystem, AngularUnit? angularUnit) + { + geographicCoordinateSystem = ArgumentGuard.ThrowIfNull(geographicCoordinateSystem, nameof(geographicCoordinateSystem)); + if (angularUnit is null || geographicCoordinateSystem.AngularUnit.EqualParams(angularUnit)) + { + return geographicCoordinateSystem; + } + + return new GeographicCoordinateSystem( + angularUnit, + geographicCoordinateSystem.HorizontalDatum, + geographicCoordinateSystem.PrimeMeridian, + CloneAxisInfoList(geographicCoordinateSystem.AxisInfo), + geographicCoordinateSystem.Name, + geographicCoordinateSystem.Authority, + geographicCoordinateSystem.AuthorityCode, + geographicCoordinateSystem.Alias, + geographicCoordinateSystem.Abbreviation, + geographicCoordinateSystem.Remarks); + } + + private static List CloneAxisInfoList(List axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + + var clone = new List(axisInfo.Count); + for (int i = 0; i < axisInfo.Count; i++) + { + clone.Add(new AxisInfo(axisInfo[i])); + } + + return clone; + } + + private static LinearUnit? MergeAxisLinearUnit(LinearUnit? current, LinearUnit? candidate) + { + if (candidate is null) + { + return current; + } + + if (current is null || current.EqualParams(candidate)) + { + return candidate; + } + + throw new NotSupportedException("WKT2 axis-specific LENGTHUNIT values must match within the same CRS."); + } + + private static CoordinateSystem ReadWkt2ProjectedCoordinateSystem(WktKeywordNode node) => + ReadWkt2ProjectedCoordinateSystemCore(node, "PROJCRS", "projected CRS", "projected coordinate system", allowOperationalEllipsoidalHeightCompound: true); + + private static FittedCoordinateSystem ReadWkt2DerivedProjectedCoordinateSystem(WktKeywordNode node) + { + const string rootKeyword = "DERIVEDPROJCRS"; + + ProjectedCoordinateSystem? baseProjectedCoordinateSystem = null; + Projection? derivingConversion = null; + LinearUnit? linearUnit = null; + string? coordinateSystemType = null; + int coordinateSystemDimension = 0; + string authority = string.Empty; + long authorityCode = -1; + var axisInfo = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("BASEPROJCRS")) + { + baseProjectedCoordinateSystem = ReadWkt2BaseProjectedCoordinateSystem(keywordChild); + } + else if (keywordChild.KeywordEquals("DERIVINGCONVERSION")) + { + derivingConversion = ReadWkt2DerivingConversion(keywordChild, out _); + } + else if (keywordChild.KeywordEquals("CS")) + { + (coordinateSystemType, coordinateSystemDimension) = ReadWkt2CoordinateSystemDefinition(keywordChild); + } + else if (keywordChild.KeywordEquals("AXIS")) + { + axisInfo.Add(ReadWkt2Axis(keywordChild, out _, out LinearUnit? axisLinearUnit)); + linearUnit = MergeAxisLinearUnit(linearUnit, axisLinearUnit); + } + else if (keywordChild.KeywordEquals("LENGTHUNIT")) + { + linearUnit = ReadWkt2LinearUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + string name = node.GetStringChild(0); + if (baseProjectedCoordinateSystem is null) + { + ThrowWktParseException("WKT2 derived projected CRS is missing a BASEPROJCRS block."); + } + + if (derivingConversion is null) + { + ThrowWktParseException("WKT2 derived projected CRS is missing a DERIVINGCONVERSION block."); + } + + if (string.IsNullOrWhiteSpace(coordinateSystemType)) + { + ThrowWktParseException("WKT2 derived projected CRS is missing a CS block."); + } + + if (!string.Equals(coordinateSystemType, "cartesian", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"WKT2 derived projected coordinate system type '{coordinateSystemType}' is not supported."); + } + + if (coordinateSystemDimension != 2) + { + throw new NotSupportedException("WKT2 derived projected CRS dimensions other than 2 are not supported."); + } + + if (linearUnit is null) + { + ThrowWktParseException("WKT2 derived projected CRS is missing a LENGTHUNIT block."); + } + + if (axisInfo.Count != coordinateSystemDimension) + { + ThrowWktParseException($"WKT2 derived projected CRS declared dimension {coordinateSystemDimension}, but provided {axisInfo.Count} AXIS blocks."); + } + + baseProjectedCoordinateSystem = ArgumentGuard.ThrowIfNull(baseProjectedCoordinateSystem, nameof(baseProjectedCoordinateSystem)); + derivingConversion = ArgumentGuard.ThrowIfNull(derivingConversion, nameof(derivingConversion)); + AffineTransform transform = DerivedCoordinateSystemSupport.CreateAffineTransform(derivingConversion); + var fittedCoordinateSystem = new FittedCoordinateSystem( + baseProjectedCoordinateSystem, + transform, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty, + axisInfo); + return fittedCoordinateSystem; + } + + private static ProjectedCoordinateSystem ReadWkt2BaseProjectedCoordinateSystem(WktKeywordNode node) => + (ProjectedCoordinateSystem)ReadWkt2ProjectedCoordinateSystemCore( + node, + "BASEPROJCRS", + "base projected CRS", + "base projected coordinate system", + allowOperationalEllipsoidalHeightCompound: false); + + private static CoordinateSystem ReadWkt2ProjectedCoordinateSystemCore( + WktKeywordNode node, + string rootKeyword, + string crsContext, + string coordinateSystemContext, + bool allowOperationalEllipsoidalHeightCompound) + { + if (!node.KeywordEquals(rootKeyword)) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in {rootKeyword}."); + } + + GeographicCoordinateSystem? geographicCS = null; + Projection? projection = null; + AngularUnit? baseAngularUnit = null; + LinearUnit? linearUnit = null; + string? coordinateSystemType = null; + int coordinateSystemDimension = 0; + string authority = string.Empty; + long authorityCode = -1; + var axisInfo = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("BASEGEOGCRS") || keywordChild.KeywordEquals("BASEGEODCRS")) + { + geographicCS = ReadWkt2BaseGeographicCoordinateSystem(keywordChild); + } + else if (keywordChild.KeywordEquals("CONVERSION")) + { + projection = ReadWkt2Conversion(keywordChild, out AngularUnit? conversionAngularUnit); + baseAngularUnit = MergeAxisAngularUnit(baseAngularUnit, conversionAngularUnit); + } + else if (keywordChild.KeywordEquals("CS")) + { + (coordinateSystemType, coordinateSystemDimension) = ReadWkt2CoordinateSystemDefinition(keywordChild); + } + else if (keywordChild.KeywordEquals("AXIS")) + { + axisInfo.Add(ReadWkt2Axis(keywordChild, out _, out LinearUnit? axisLinearUnit)); + linearUnit = MergeAxisLinearUnit(linearUnit, axisLinearUnit); + } + else if (keywordChild.KeywordEquals("LENGTHUNIT")) + { + linearUnit = ReadWkt2LinearUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (keywordChild.KeywordEquals("ENSEMBLE")) + { + throw new NotSupportedException("WKT2 datum ensembles are not supported."); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + string name = node.GetStringChild(0); + if (geographicCS is null) + { + ThrowWktParseException($"WKT2 {crsContext} is missing a BASEGEOGCRS block."); + } + + if (projection is null) + { + ThrowWktParseException($"WKT2 {crsContext} is missing a CONVERSION block."); + } + + if (string.IsNullOrWhiteSpace(coordinateSystemType)) + { + ThrowWktParseException($"WKT2 {crsContext} is missing a CS block."); + } + + if (!string.Equals(coordinateSystemType, "cartesian", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"WKT2 {coordinateSystemContext} type '{coordinateSystemType}' is not supported."); + } + + if (coordinateSystemDimension != 2 && coordinateSystemDimension != 3) + { + throw new NotSupportedException($"WKT2 {crsContext} dimensions other than 2 or 3 are not supported."); + } + + if (linearUnit is null) + { + ThrowWktParseException($"WKT2 {crsContext} is missing a LENGTHUNIT block."); + } + + if (axisInfo.Count != coordinateSystemDimension) + { + ThrowWktParseException($"WKT2 {crsContext} declared dimension {coordinateSystemDimension}, but provided {axisInfo.Count} AXIS blocks."); + } + + geographicCS = ArgumentGuard.ThrowIfNull(geographicCS, nameof(geographicCS)); + projection = ArgumentGuard.ThrowIfNull(projection, nameof(projection)); + linearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + geographicCS = OverrideGeographicAngularUnit(geographicCS, baseAngularUnit); + if (coordinateSystemDimension == 3) + { + if (!allowOperationalEllipsoidalHeightCompound) + { + throw new NotSupportedException($"WKT2 {crsContext} dimensions other than 2 are not supported."); + } + + return CreateOperationalWkt2ProjectedEllipsoidalHeightCompoundCoordinateSystem( + name, + authority, + authorityCode, + geographicCS, + linearUnit, + projection, + axisInfo); + } + + return new ProjectedCoordinateSystem( + geographicCS.HorizontalDatum, + geographicCS, + linearUnit, + projection, + axisInfo, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static CompoundCoordinateSystem CreateOperationalWkt2ProjectedEllipsoidalHeightCompoundCoordinateSystem( + string name, + string authority, + long authorityCode, + GeographicCoordinateSystem geographicCoordinateSystem, + LinearUnit linearUnit, + Projection projection, + List axisInfo) + { + var head = new ProjectedCoordinateSystem( + geographicCoordinateSystem.HorizontalDatum, + geographicCoordinateSystem, + linearUnit, + projection, + [new AxisInfo(axisInfo[0]), new AxisInfo(axisInfo[1])], + name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + var tail = new VerticalCoordinateSystem( + linearUnit, + new VerticalDatum(DatumType.VD_Ellipsoidal, "Ellipsoidal height datum", string.Empty, -1, string.Empty, string.Empty, string.Empty), + new AxisInfo(axisInfo[2]), + axisInfo[2].Name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + return new CompoundCoordinateSystem(head, tail, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static GeographicCoordinateSystem ReadWkt2BaseGeographicCoordinateSystem(WktKeywordNode node) + { + string rootKeyword = node.Keyword; + string name = node.GetStringChild(0); + HorizontalDatum? horizontalDatum = null; + PrimeMeridian? primeMeridian = null; + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("DATUM")) + { + horizontalDatum = ReadWkt2HorizontalDatum(keywordChild); + } + else if (keywordChild.KeywordEquals("ENSEMBLE")) + { + horizontalDatum = ReadWkt2HorizontalDatumEnsemble(keywordChild); + } + else if (keywordChild.KeywordEquals("PRIMEM")) + { + primeMeridian = ReadWkt2PrimeMeridian(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + if (horizontalDatum is null) + { + ThrowWktParseException($"WKT2 {rootKeyword} is missing a DATUM block."); + } + + horizontalDatum = ArgumentGuard.ThrowIfNull(horizontalDatum, nameof(horizontalDatum)); + primeMeridian ??= PrimeMeridian.Greenwich; + return new GeographicCoordinateSystem( + AngularUnit.Degrees, + horizontalDatum, + primeMeridian, + new List + { + new("Geodetic latitude (Lat)", AxisOrientationEnum.North), + new("Geodetic longitude (Lon)", AxisOrientationEnum.East), + }, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static Projection ReadWkt2Conversion(WktKeywordNode node, out AngularUnit? angularUnit) + { + return ReadWkt2Conversion(node, "CONVERSION", out angularUnit); + } + + private static Projection ReadWkt2DerivingConversion(WktKeywordNode node, out AngularUnit? angularUnit) + { + return ReadWkt2Conversion(node, "DERIVINGCONVERSION", out angularUnit); + } + + private static Projection ReadWkt2Conversion(WktKeywordNode node, string keyword, out AngularUnit? angularUnit) + { + string conversionName = node.GetStringChild(0); + + string methodName = string.Empty; + string authority = string.Empty; + long authorityCode = -1; + angularUnit = null; + var parameters = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("METHOD")) + { + methodName = ReadWkt2ProjectionMethod(keywordChild); + } + else if (keywordChild.KeywordEquals("PARAMETER")) + { + parameters.Add(ReadWkt2ProjectionParameter(keywordChild, out AngularUnit? parameterAngularUnit)); + angularUnit = MergeAxisAngularUnit(angularUnit, parameterAngularUnit); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 {keyword} keyword '{keywordChild.Keyword}' is not supported."); + } + } + + if (string.IsNullOrWhiteSpace(methodName)) + { + ThrowWktParseException($"WKT2 {keyword} is missing a METHOD block."); + } + + return new Projection(methodName, parameters, conversionName, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static string ReadWkt2ProjectionMethod(WktKeywordNode node) + { + string methodName = node.GetStringChild(0); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (!keywordChild.KeywordEquals("ID") && !ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 METHOD keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return methodName; + } + + private static ProjectionParameter ReadWkt2ProjectionParameter(WktKeywordNode node, out AngularUnit? angularUnit) + { + string parameterName = NormalizeWkt2ProjectionParameterName(node.GetStringChild(0)); + double value = node.GetNumberChild(1); + angularUnit = null; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ANGLEUNIT")) + { + angularUnit = ReadWkt2AngularUnit(keywordChild); + } + else if (!keywordChild.KeywordEquals("ID") + && !keywordChild.KeywordEquals("LENGTHUNIT") + && !keywordChild.KeywordEquals("SCALEUNIT")) + { + if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 PARAMETER keyword '{keywordChild.Keyword}' is not supported."); + } + } + } + + return new ProjectionParameter(parameterName, value); + } + + private static string NormalizeWkt2ProjectionParameterName(string parameterName) => ProjectionParameterNameNormalizer.Normalize(parameterName); + + private static VerticalCoordinateSystem ReadWkt2VerticalCoordinateSystem(WktKeywordNode node) + { + const string rootKeyword = "VERTCRS"; + + VerticalDatum? verticalDatum = null; + LinearUnit? linearUnit = null; + string? coordinateSystemType = null; + int coordinateSystemDimension = 0; + string authority = string.Empty; + long authorityCode = -1; + var axisInfo = new List(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("VDATUM")) + { + verticalDatum = ReadWkt2VerticalDatum(keywordChild); + } + else if (keywordChild.KeywordEquals("ENSEMBLE")) + { + verticalDatum = ReadWkt2VerticalDatumEnsemble(keywordChild); + } + else if (keywordChild.KeywordEquals("CS")) + { + (coordinateSystemType, coordinateSystemDimension) = ReadWkt2CoordinateSystemDefinition(keywordChild); + } + else if (keywordChild.KeywordEquals("AXIS")) + { + axisInfo.Add(ReadWkt2Axis(keywordChild, out _, out LinearUnit? axisLinearUnit)); + linearUnit = MergeAxisLinearUnit(linearUnit, axisLinearUnit); + } + else if (keywordChild.KeywordEquals("LENGTHUNIT")) + { + linearUnit = ReadWkt2LinearUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + string name = node.GetStringChild(0); + if (verticalDatum is null) + { + ThrowWktParseException("WKT2 vertical CRS is missing a VDATUM or ENSEMBLE block."); + } + + if (string.IsNullOrWhiteSpace(coordinateSystemType)) + { + ThrowWktParseException("WKT2 vertical CRS is missing a CS block."); + } + + if (!string.Equals(coordinateSystemType, "vertical", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"WKT2 vertical coordinate system type '{coordinateSystemType}' is not supported."); + } + + if (coordinateSystemDimension != 1) + { + throw new NotSupportedException("WKT2 vertical CRS dimensions other than 1 are not supported."); + } + + if (linearUnit is null) + { + ThrowWktParseException("WKT2 vertical CRS is missing a LENGTHUNIT block."); + } + + if (axisInfo.Count != coordinateSystemDimension) + { + ThrowWktParseException($"WKT2 vertical CRS declared dimension {coordinateSystemDimension}, but provided {axisInfo.Count} AXIS blocks."); + } + + verticalDatum = ApplyVerticalDatumTypeForAxis(ArgumentGuard.ThrowIfNull(verticalDatum, nameof(verticalDatum)), axisInfo[0]); + linearUnit = ArgumentGuard.ThrowIfNull(linearUnit, nameof(linearUnit)); + return new VerticalCoordinateSystem( + linearUnit, + verticalDatum, + axisInfo[0], + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static VerticalDatum ReadWkt2VerticalDatum(WktKeywordNode node) + { + if (!node.KeywordEquals("VDATUM")) + { + throw new NotSupportedException($"WKT2 keyword '{node.Keyword}' is not supported in VDATUM."); + } + + string name = node.GetStringChild(0); + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 VDATUM keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return new VerticalDatum(DatumType.VD_GeoidModelDerived, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static VerticalDatum ReadWkt2VerticalDatumEnsemble(WktKeywordNode node) + { + DatumEnsemble ensemble = ReadWkt2DatumEnsemble(node, requireEllipsoid: false); + return new VerticalDatum(DatumType.VD_GeoidModelDerived, ensemble.Name, ensemble.Authority, ensemble.AuthorityCode, string.Empty, string.Empty, string.Empty, ensemble); + } + + private static VerticalDatum ApplyVerticalDatumTypeForAxis(VerticalDatum verticalDatum, AxisInfo axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + + DatumType datumType = axisInfo.Orientation == AxisOrientationEnum.Down + ? DatumType.VD_Depth + : DatumType.VD_GeoidModelDerived; + if (verticalDatum.DatumType == datumType) + { + return verticalDatum; + } + + return new VerticalDatum( + datumType, + verticalDatum.Name, + verticalDatum.Authority, + verticalDatum.AuthorityCode, + verticalDatum.Alias, + verticalDatum.Remarks, + verticalDatum.Abbreviation, + verticalDatum.Ensemble); + } + + private static CompoundCoordinateSystem ReadWkt2CompoundCoordinateSystem(WktKeywordNode node) + { + const string rootKeyword = "COMPOUNDCRS"; + string name = node.GetStringChild(0); + + CoordinateSystem? headCoordinateSystem = null; + CoordinateSystem? tailCoordinateSystem = null; + string authority = string.Empty; + long authorityCode = -1; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (headCoordinateSystem is null) + { + headCoordinateSystem = ReadWkt2CoordinateSystemNode(keywordChild); + } + else if (tailCoordinateSystem is null) + { + tailCoordinateSystem = ReadWkt2CoordinateSystemNode(keywordChild); + } + else if (keywordChild.KeywordEquals("ID")) + { + ReadIdentifierWithUnknownCode(keywordChild, out authority, out authorityCode); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + headCoordinateSystem = ArgumentGuard.ThrowIfNull(headCoordinateSystem, nameof(headCoordinateSystem)); + tailCoordinateSystem = ArgumentGuard.ThrowIfNull(tailCoordinateSystem, nameof(tailCoordinateSystem)); + return new CompoundCoordinateSystem(headCoordinateSystem, tailCoordinateSystem, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static BoundCoordinateSystem ReadWkt2BoundCoordinateSystem(WktKeywordNode node) + { + const string rootKeyword = "BOUNDCRS"; + + CoordinateSystem? sourceCoordinateSystem = null; + CoordinateSystem? targetCoordinateSystem = null; + BoundTransformation? transformation = null; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("SOURCECRS")) + { + sourceCoordinateSystem = ReadWkt2BoundCoordinateSystemComponent(keywordChild); + EnsureSupportedWkt2BoundSourceCoordinateSystem(sourceCoordinateSystem); + } + else if (keywordChild.KeywordEquals("TARGETCRS")) + { + targetCoordinateSystem = ReadWkt2BoundCoordinateSystemComponent(keywordChild); + } + else if (keywordChild.KeywordEquals("ABRIDGEDTRANSFORMATION")) + { + transformation = ReadWkt2AbridgedTransformationDefinition(keywordChild); + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {rootKeyword}."); + } + } + + sourceCoordinateSystem = ArgumentGuard.ThrowIfNull(sourceCoordinateSystem, nameof(sourceCoordinateSystem)); + targetCoordinateSystem = ArgumentGuard.ThrowIfNull(targetCoordinateSystem, nameof(targetCoordinateSystem)); + transformation = ArgumentGuard.ThrowIfNull(transformation, nameof(transformation)); + + return new BoundCoordinateSystem( + sourceCoordinateSystem, + targetCoordinateSystem, + transformation, + sourceCoordinateSystem.Name, + sourceCoordinateSystem.Authority, + sourceCoordinateSystem.AuthorityCode, + sourceCoordinateSystem.Alias, + sourceCoordinateSystem.Abbreviation, + sourceCoordinateSystem.Remarks); + } + + private static CoordinateSystem ReadWkt2BoundCoordinateSystemComponent(WktKeywordNode node) + { + WktKeywordNode? coordinateSystemNode = null; + bool foundCoordinateSystemNode = false; + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (!foundCoordinateSystemNode) + { + coordinateSystemNode = keywordChild; + foundCoordinateSystemNode = true; + } + else if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 keyword '{keywordChild.Keyword}' is not supported in {node.Keyword}."); + } + } + + return ReadWkt2CoordinateSystemNode(ArgumentGuard.ThrowIfNull(coordinateSystemNode, nameof(coordinateSystemNode))); + } + + private static void EnsureSupportedWkt2BoundSourceCoordinateSystem(CoordinateSystem coordinateSystem) + { + if (coordinateSystem is BoundCoordinateSystem boundCoordinateSystem) + { + EnsureSupportedWkt2BoundSourceCoordinateSystem(boundCoordinateSystem.SourceCoordinateSystem); + return; + } + + if (coordinateSystem is VerticalCoordinateSystem || BoundCoordinateSystemSupport.TryGetHorizontalDatum(coordinateSystem, out _)) + { + return; + } + + throw new NotSupportedException( + $"WKT2 BOUNDCRS source coordinate system type '{BoundCoordinateSystemSupport.GetCoordinateSystemKeyword(coordinateSystem)}' is not supported."); + } + + private static BoundTransformation ReadWkt2AbridgedTransformationDefinition(WktKeywordNode node) + { + _ = node.GetStringChild(0); + + string methodName = string.Empty; + string? parameterFileName = null; + var parameters = new Wgs84ConversionInfo(); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("METHOD")) + { + methodName = ReadWkt2ProjectionMethod(keywordChild); + } + else if (keywordChild.KeywordEquals("PARAMETER")) + { + ReadWkt2AbridgedTransformationParameter(keywordChild, parameters); + } + else if (keywordChild.KeywordEquals("PARAMETERFILE")) + { + parameterFileName = ReadWkt2AbridgedTransformationParameterFile(keywordChild); + } + else if (!keywordChild.KeywordEquals("ID")) + { + if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 ABRIDGEDTRANSFORMATION keyword '{keywordChild.Keyword}' is not supported."); + } + } + } + + if (string.IsNullOrWhiteSpace(methodName)) + { + ThrowWktParseException("WKT2 ABRIDGEDTRANSFORMATION is missing a METHOD block."); + } + + return BoundCoordinateSystemSupport.CreateBoundTransformation( + methodName, + string.IsNullOrWhiteSpace(parameterFileName) ? parameters : null, + parameterFileName); + } + + private static void ReadWkt2AbridgedTransformationParameter(WktKeywordNode node, Wgs84ConversionInfo parameters) + { + string parameterName = NormalizeWkt2BoundTransformationParameterName(node.GetStringChild(0)); + double value = node.GetNumberChild(1); + + AngularUnit? angularUnit = null; + LinearUnit? linearUnit = null; + double? scaleUnitFactor = null; + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (keywordChild.KeywordEquals("ANGLEUNIT")) + { + angularUnit = ReadWkt2AngularUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("LENGTHUNIT")) + { + linearUnit = ReadWkt2LinearUnit(keywordChild); + } + else if (keywordChild.KeywordEquals("SCALEUNIT")) + { + scaleUnitFactor = ReadWkt2ScaleUnitFactor(keywordChild); + } + else if (!keywordChild.KeywordEquals("ID")) + { + if (!ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 ABRIDGEDTRANSFORMATION parameter keyword '{keywordChild.Keyword}' is not supported."); + } + } + } + + ApplyWkt2BoundTransformationParameter( + parameters, + parameterName, + NormalizeWkt2BoundTransformationParameterValue(parameterName, value, angularUnit, linearUnit, scaleUnitFactor)); + } + + private static string ReadWkt2AbridgedTransformationParameterFile(WktKeywordNode node) + { + _ = node.GetStringChild(0); + string parameterFileName = node.GetStringChild(1); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (!keywordChild.KeywordEquals("ID") && !ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 PARAMETERFILE keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return parameterFileName; + } + + private static double ReadWkt2ScaleUnitFactor(WktKeywordNode node) + { + _ = node.GetStringChild(0); + double unitFactor = node.GetNumberChild(1); + + ReadOnlySpan children = node.GetChildrenSpan(); + for (int i = 0; i < children.Length; i++) + { + if (children[i] is not WktKeywordNode keywordChild) + { + continue; + } + + if (!keywordChild.KeywordEquals("ID") && !ShouldSkipWkt2MetadataNode(keywordChild)) + { + throw new NotSupportedException($"WKT2 SCALEUNIT keyword '{keywordChild.Keyword}' is not supported."); + } + } + + return unitFactor; + } + + private static string NormalizeWkt2BoundTransformationParameterName(string parameterName) + { + string normalized = ProjectionParameterNameNormalizer.NormalizeLookupToken(parameterName); + + return normalized switch + { + "X_AXIS_TRANSLATION" => "dx", + "Y_AXIS_TRANSLATION" => "dy", + "Z_AXIS_TRANSLATION" => "dz", + "X_AXIS_ROTATION" => "ex", + "Y_AXIS_ROTATION" => "ey", + "Z_AXIS_ROTATION" => "ez", + "SCALE_DIFFERENCE" => "ppm", + _ => normalized, + }; + } + + private static double NormalizeWkt2BoundTransformationParameterValue( + string parameterName, + double value, + AngularUnit? angularUnit, + LinearUnit? linearUnit, + double? scaleUnitFactor) + { + return parameterName switch + { + "dx" or "dy" or "dz" => linearUnit is null ? value : value * linearUnit.MetersPerUnit, + "ex" or "ey" or "ez" => angularUnit is null ? value : (value * angularUnit.RadiansPerUnit) / RadiansPerArcSecond, + "ppm" => scaleUnitFactor.HasValue ? value * scaleUnitFactor.Value * 1000000d : value, + _ => throw new NotSupportedException($"WKT2 BOUNDCRS transformation parameter '{parameterName}' is not supported."), + }; + } + + private static void ApplyWkt2BoundTransformationParameter(Wgs84ConversionInfo parameters, string parameterName, double value) + { + BoundCoordinateSystemSupport.AssignTransformationParameter(parameterName, value, parameters); + } +} diff --git a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs index 01fc3353..e80d7c35 100644 --- a/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs +++ b/src/ProjNet/IO/CoordinateSystems/CoordinateSystemWktReader.cs @@ -1,735 +1,441 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.IO.CoordinateSystems; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; -using System.Linq; -using System.Runtime.InteropServices.ComTypes; using System.Text; +using ProjNet; using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.Wkt; -namespace ProjNet.IO.CoordinateSystems +/// +/// Creates an object based on the supplied Well Known Text (WKT). +/// +public static partial class CoordinateSystemWktReader { + private const double RadiansPerArcSecond = 4.84813681109535993589914102357e-6d; + /// - /// Creates an object based on the supplied Well Known Text (WKT). + /// Reads and parses a WKT-formatted projection string. /// - public static class CoordinateSystemWktReader + /// String containing WKT. + /// Object representation of the WKT. + /// If a token is not recognized. + public static IInfo Parse(string wkt) { - /// - /// Reads and parses a WKT-formatted projection string. - /// - /// String containing WKT. - /// Object representation of the WKT. - /// If a token is not recognized. - public static IInfo Parse(string wkt) + if (string.IsNullOrWhiteSpace(wkt)) { - if (string.IsNullOrWhiteSpace(wkt)) - throw new ArgumentNullException("wkt"); + ArgumentGuard.ThrowArgumentNull(nameof(wkt)); + } - using (TextReader reader = new StringReader(wkt)) - { - var tokenizer = new WktStreamTokenizer(reader); - tokenizer.NextToken(); - string objectName = tokenizer.GetStringValue(); - switch (objectName) - { - case "UNIT": - return ReadUnit(tokenizer); - case "SPHEROID": - return ReadEllipsoid(tokenizer); - case "DATUM": - return ReadHorizontalDatum(tokenizer); - case "PRIMEM": - return ReadPrimeMeridian(tokenizer); - case "VERT_CS": - case "GEOGCS": - case "PROJCS": - case "COMPD_CS": - case "GEOCCS": - case "FITTED_CS": - case "LOCAL_CS": - return ReadCoordinateSystem(wkt, tokenizer); - default: - throw new ArgumentException($"'{objectName}' is not recognized."); - } - } + return ParseCore(wkt.AsSpan(), wkt); + } + + /// + /// Reads and parses a WKT-formatted projection text from a character span. + /// + /// Character span containing WKT. + /// Object representation of the WKT. + /// If a token is not recognized. + public static IInfo Parse(ReadOnlySpan wkt) + { + if (wkt.IsEmpty || IsWhitespaceOnly(wkt)) + { + ArgumentGuard.ThrowArgumentNull(nameof(wkt)); } - /// - /// Returns a IUnit given a piece of WKT. - /// - /// WktStreamTokenizer that has the WKT. - /// An object that implements the IUnit interface. - private static IUnit ReadUnit(WktStreamTokenizer tokenizer) + return ParseCore(wkt, sourceText: null); + } + + [DoesNotReturn] + private static void ThrowWktParseException(string message) + { + throw new WktParseException(message); + } + + [DoesNotReturn] + private static T ThrowWktParseException(string message) + { + throw new WktParseException(message); + } + + private static bool IsWhitespaceOnly(ReadOnlySpan value) + { + for (int i = 0; i < value.Length; i++) { - var bracket = tokenizer.ReadOpener(); - string unitName = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double unitsPerUnit = tokenizer.GetNumericValue(); - string authority = string.Empty; - long authorityCode = -1; - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") + if (!char.IsWhiteSpace(value[i])) { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); + return false; } - else - tokenizer.CheckCloser(bracket); - - return new Unit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty); } - /// - /// Returns a given a piece of WKT. - /// - /// WktStreamTokenizer that has the WKT. - /// An object that implements the IUnit interface. - private static LinearUnit ReadLinearUnit(WktStreamTokenizer tokenizer) + + return true; + } + + private static IInfo ParseCore(ReadOnlySpan wkt, string? sourceText) + { + if (ShouldBypassNativeWkt2(wkt)) { - var bracket = tokenizer.ReadOpener(); - - string unitName = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double unitsPerUnit = tokenizer.GetNumericValue(); - string authority = string.Empty; - long authorityCode = -1; - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } - else - tokenizer.CheckCloser(bracket); + return ParseNormalizedWkt(sourceText ?? wkt.ToString()); + } - return new LinearUnit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty); + string wktText = sourceText ?? wkt.ToString(); + if (TryParseNativeWkt2(wktText, out IInfo? nativeWkt2Info)) + { + return ArgumentGuard.ThrowIfNull(nativeWkt2Info, nameof(nativeWkt2Info)); } - /// - /// Returns a given a piece of WKT. - /// - /// WktStreamTokenizer that has the WKT. - /// An object that implements the IUnit interface. - private static AngularUnit ReadAngularUnit(WktStreamTokenizer tokenizer) + + string normalizedWkt = NormalizeWkt(wktText); + return ParseNormalizedWkt(normalizedWkt); + } + + private static bool ShouldBypassNativeWkt2(ReadOnlySpan wkt) + { + return TryGetRootKeyword(wkt, out ReadOnlySpan keyword) + && IsWkt1OnlyRootKeyword(keyword); + } + + private static bool TryGetRootKeyword(ReadOnlySpan wkt, out ReadOnlySpan keyword) + { + int index = 0; + while (index < wkt.Length && char.IsWhiteSpace(wkt[index])) { - var bracket = tokenizer.ReadOpener(); - - string unitName = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double unitsPerUnit = tokenizer.GetNumericValue(); - string authority = string.Empty; - long authorityCode = -1; - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } - else - { - tokenizer.CheckCloser(bracket); - } - return new AngularUnit(unitsPerUnit, unitName, authority, authorityCode, string.Empty, string.Empty, string.Empty); + index++; } - /// - /// Returns a given a piece of WKT. - /// - /// WktStreamTokenizer that has the WKT. - /// An AxisInfo object. - private static AxisInfo ReadAxis(WktStreamTokenizer tokenizer) + int start = index; + while (index < wkt.Length) { - if (tokenizer.GetStringValue() != "AXIS") - tokenizer.ReadToken("AXIS"); - var bracket = tokenizer.ReadOpener(); - string axisName = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - string unitname = tokenizer.GetStringValue(); - tokenizer.ReadCloser(bracket); - switch (unitname.ToUpperInvariant()) + char current = wkt[index]; + if ((current >= 'A' && current <= 'Z') || + (current >= 'a' && current <= 'z') || + (current >= '0' && current <= '9') || + current == '_') { - case "DOWN": return new AxisInfo(axisName, AxisOrientationEnum.Down); - case "EAST": return new AxisInfo(axisName, AxisOrientationEnum.East); - case "NORTH": return new AxisInfo(axisName, AxisOrientationEnum.North); - case "OTHER": return new AxisInfo(axisName, AxisOrientationEnum.Other); - case "SOUTH": return new AxisInfo(axisName, AxisOrientationEnum.South); - case "UP": return new AxisInfo(axisName, AxisOrientationEnum.Up); - case "WEST": return new AxisInfo(axisName, AxisOrientationEnum.West); - default: - throw new ArgumentException("Invalid axis name '" + unitname + "' in WKT"); + index++; + continue; } + + break; } - private static CoordinateSystem ReadCoordinateSystem(string coordinateSystem, WktStreamTokenizer tokenizer) + if (index == start) { - switch (tokenizer.GetStringValue()) - { - case "GEOGCS": - return ReadGeographicCoordinateSystem(tokenizer); - case "PROJCS": - return ReadProjectedCoordinateSystem(tokenizer); - case "FITTED_CS": - return ReadFittedCoordinateSystem (tokenizer); - case "GEOCCS": - return ReadGeocentricCoordinateSystem(tokenizer); - case "COMPD_CS": - return ReadCompoundCoordinateSystem(tokenizer); - case "VERT_CS": - return ReadVerticalCoordinateSystem(tokenizer); - case "LOCAL_CS": - throw new NotSupportedException($"{coordinateSystem} coordinate system is not supported."); - default: - throw new InvalidOperationException($"{coordinateSystem} coordinate system is not recognized."); - } + keyword = default; + return false; } - // Reads either 3, 6 or 7 parameter Bursa-Wolf values from TOWGS84 token - private static Wgs84ConversionInfo ReadWGS84ConversionInfo(WktStreamTokenizer tokenizer) + keyword = wkt.Slice(start, index - start); + while (index < wkt.Length && char.IsWhiteSpace(wkt[index])) { - //TOWGS84[0,0,0,0,0,0,0] - var bracket = tokenizer.ReadOpener(); - var info = new Wgs84ConversionInfo(); - tokenizer.NextToken(); - info.Dx = tokenizer.GetNumericValue(); - tokenizer.ReadToken(","); - - tokenizer.NextToken(); - info.Dy = tokenizer.GetNumericValue(); - tokenizer.ReadToken(","); - - tokenizer.NextToken(); - info.Dz = tokenizer.GetNumericValue(); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - info.Ex = tokenizer.GetNumericValue(); - - tokenizer.ReadToken(","); - tokenizer.NextToken(); - info.Ey = tokenizer.GetNumericValue(); - - tokenizer.ReadToken(","); - tokenizer.NextToken(); - info.Ez = tokenizer.GetNumericValue(); - - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - info.Ppm = tokenizer.GetNumericValue(); - } - } - if (tokenizer.GetStringValue() != "]") - tokenizer.ReadCloser(bracket); - return info; + index++; } - private static Ellipsoid ReadEllipsoid(WktStreamTokenizer tokenizer) + return index < wkt.Length && (wkt[index] == '[' || wkt[index] == '('); + } + + private static bool IsWkt1OnlyRootKeyword(ReadOnlySpan keyword) + { + return KeywordEqualsOrdinalIgnoreCase(keyword, "UNIT") + || KeywordEqualsOrdinalIgnoreCase(keyword, "SPHEROID") + || KeywordEqualsOrdinalIgnoreCase(keyword, "DATUM") + || KeywordEqualsOrdinalIgnoreCase(keyword, "PRIMEM") + || KeywordEqualsOrdinalIgnoreCase(keyword, "GEOGCS") + || KeywordEqualsOrdinalIgnoreCase(keyword, "PROJCS") + || KeywordEqualsOrdinalIgnoreCase(keyword, "GEOCCS") + || KeywordEqualsOrdinalIgnoreCase(keyword, "COMPD_CS") + || KeywordEqualsOrdinalIgnoreCase(keyword, "VERT_CS") + || KeywordEqualsOrdinalIgnoreCase(keyword, "FITTED_CS") + || KeywordEqualsOrdinalIgnoreCase(keyword, "LOCAL_CS"); + } + + private static bool KeywordEqualsOrdinalIgnoreCase(ReadOnlySpan keyword, string expected) + { + if (keyword.Length != expected.Length) { - //SPHEROID["Airy 1830",6377563.396,299.3249646,AUTHORITY["EPSG","7001"]] - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double majorAxis = tokenizer.GetNumericValue(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double e = tokenizer.GetNumericValue(); - tokenizer.NextToken(); - string authority = string.Empty; - long authorityCode = -1; - if (tokenizer.GetStringValue() == ",") //Read authority - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } - var ellipsoid = new Ellipsoid(majorAxis, 0.0, e, true, LinearUnit.Metre, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return ellipsoid; + return false; } - private static IProjection ReadProjection(WktStreamTokenizer tokenizer) + for (int i = 0; i < keyword.Length; i++) { - if (tokenizer.GetStringValue() != "PROJECTION") - tokenizer.ReadToken("PROJECTION"); - var bracket = tokenizer.ReadOpener(); - string projectionName = tokenizer.ReadDoubleQuotedWord(); - string authority = string.Empty; - long authorityCode = -1L; - - tokenizer.NextToken(true); - if (tokenizer.GetStringValue() == ",") + char current = keyword[i]; + if (current >= 'a' && current <= 'z') { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); + current = (char)(current - ('a' - 'A')); } - else - tokenizer.CheckCloser(bracket); - tokenizer.ReadToken(",");//, - tokenizer.ReadToken("PARAMETER"); - var paramList = new List(); - while (tokenizer.GetStringValue() == "PARAMETER") + if (current != expected[i]) { - bracket = tokenizer.ReadOpener(); - string paramName = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double paramValue = tokenizer.GetNumericValue(); - tokenizer.ReadCloser(bracket); - paramList.Add(new ProjectionParameter(paramName, paramValue)); - //tokenizer.ReadToken(","); - //tokenizer.NextToken(); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - } - else - { - break; - } + return false; } - var projection = new Projection(projectionName, paramList, projectionName, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return projection; } - private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(WktStreamTokenizer tokenizer) + return true; + } + + private static bool TryParseNativeWkt2(string wkt, out IInfo? info) + { + var tokenizer = new WktTokenizer(wkt); + WktKeywordNode rootNode; + try { - /*PROJCS[ - "OSGB 1936 / British National Grid", - GEOGCS[ - "OSGB 1936", - DATUM[...] - PRIMEM[...] - AXIS["Geodetic latitude","NORTH"] - AXIS["Geodetic longitude","EAST"] - AUTHORITY["EPSG","4277"] - ], - PROJECTION["Transverse Mercator"], - PARAMETER["latitude_of_natural_origin",49], - PARAMETER["longitude_of_natural_origin",-2], - PARAMETER["scale_factor_at_natural_origin",0.999601272], - PARAMETER["false_easting",400000], - PARAMETER["false_northing",-100000], - AXIS["Easting","EAST"], - AXIS["Northing","NORTH"], - AUTHORITY["EPSG","27700"] - ] - */ - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.ReadToken("GEOGCS"); - var geographicCS = ReadGeographicCoordinateSystem(tokenizer); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - - LinearUnit linearUnit = null; - - if (tokenizer.GetStringValue().Equals("UNIT", StringComparison.OrdinalIgnoreCase)) - { - linearUnit = ReadLinearUnit(tokenizer); - tokenizer.ReadToken(","); - } - var projection = ReadProjection(tokenizer); - var unit = linearUnit ?? ReadLinearUnit(tokenizer); - var axisInfo = new List(2); - string authority = string.Empty; - long authorityCode = -1; - - var ct = tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - while (tokenizer.GetStringValue() == "AXIS") - { - axisInfo.Add(ReadAxis(tokenizer)); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - } - - while (ct != TokenType.Eol && ct != TokenType.Eof) - { - if (tokenizer.GetStringValue() == "AUTHORITY") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - break; - } - else - { - ct = tokenizer.NextToken(); - } - } - } - //This is default axis values if not specified. - if (axisInfo.Count == 0) - { - axisInfo.Add(new AxisInfo("X", AxisOrientationEnum.East)); - axisInfo.Add(new AxisInfo("Y", AxisOrientationEnum.North)); - } - var projectedCS = new ProjectedCoordinateSystem(geographicCS.HorizontalDatum, geographicCS, unit as LinearUnit, projection, axisInfo, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return projectedCS; + rootNode = WktKeywordNode.ParseTree(tokenizer); } - - private static VerticalCoordinateSystem ReadVerticalCoordinateSystem(WktStreamTokenizer tokenizer) + catch (WktParseException) { - // VERT_CS["", , , {,} {,< authority >}] - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.ReadToken("VERT_DATUM"); - var verticalDatum = ReadVerticalDatum(tokenizer); - tokenizer.ReadToken(","); - tokenizer.ReadToken("UNIT"); - var linearUnit = ReadLinearUnit(tokenizer); - - string authority = string.Empty; - long authorityCode = -1; - tokenizer.NextToken(); - AxisInfo info = null; - if (tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "AXIS") - { - info = ReadAxis(tokenizer); - tokenizer.NextToken(); - } - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "AUTHORITY") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } - } - - //This is default axis values if not specified. - if (info == null) - { - info = new AxisInfo("Up", AxisOrientationEnum.Up); - } - var verticalCs = new VerticalCoordinateSystem(linearUnit, verticalDatum, info, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return verticalCs; + info = null; + return false; } - private static CompoundCoordinateSystem ReadCompoundCoordinateSystem(WktStreamTokenizer tokenizer) + info = rootNode.Keyword switch { - // = COMPD_CS["", , {,}] - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - var headcs = ReadCoordinateSystem(null, tokenizer); - - var ct = tokenizer.NextToken(); - while (ct != TokenType.Eol && ct != TokenType.Eof && new[] { ",", "]"}.Contains(tokenizer.GetStringValue())) - { - ct = tokenizer.NextToken(); + "GEOGCRS" or "GEODCRS" or "GEODETICCRS" when rootNode.FindChild("CS") is not null + => ReadWkt2GeodeticCoordinateReferenceSystem(rootNode), + "PROJCRS" when rootNode.FindChild("CONVERSION") is not null && rootNode.FindChild("CS") is not null + => ReadWkt2ProjectedCoordinateSystem(rootNode), + "DERIVEDPROJCRS" when rootNode.FindChild("DERIVINGCONVERSION") is not null && rootNode.FindChild("CS") is not null + => ReadWkt2DerivedProjectedCoordinateSystem(rootNode), + "VERTCRS" when rootNode.FindChild("VDATUM", "ENSEMBLE") is not null && rootNode.FindChild("CS") is not null + => ReadWkt2VerticalCoordinateSystem(rootNode), + "ENGCRS" or "ENGINEERINGCRS" => ReadWkt2EngineeringCoordinateSystem(rootNode), + "TIMECRS" => ReadWkt2TemporalCoordinateSystem(rootNode), + "PARAMETRICCRS" => ReadWkt2ParametricCoordinateSystem(rootNode), + "COORDINATEOPERATION" => ReadWkt2CoordinateOperation(rootNode), + "CONCATENATEDOPERATION" => ReadWkt2ConcatenatedOperation(rootNode), + "COMPOUNDCRS" => ReadWkt2CompoundCoordinateSystem(rootNode), + "BOUNDCRS" when rootNode.FindChild("SOURCECRS") is not null + && rootNode.FindChild("TARGETCRS") is not null + && rootNode.FindChild("ABRIDGEDTRANSFORMATION") is not null + => ReadWkt2BoundCoordinateSystem(rootNode), + _ => null, + }; + + return info is not null; + } - } - var tailcs = ReadCoordinateSystem(null, tokenizer); + private static CoordinateSystem ReadCoordinateSystemNode(WktKeywordNode node) + { + return node.Keyword switch + { + "GEOGCRS" or "GEODCRS" or "GEODETICCRS" => ReadWkt2GeodeticCoordinateReferenceSystem(node), + "PROJCRS" => ReadWkt2ProjectedCoordinateSystem(node), + "DERIVEDPROJCRS" => ReadWkt2DerivedProjectedCoordinateSystem(node), + "VERTCRS" => ReadWkt2VerticalCoordinateSystem(node), + "ENGCRS" or "ENGINEERINGCRS" => ReadWkt2EngineeringCoordinateSystem(node), + "TIMECRS" => ReadWkt2TemporalCoordinateSystem(node), + "PARAMETRICCRS" => ReadWkt2ParametricCoordinateSystem(node), + "COMPOUNDCRS" => ReadWkt2CompoundCoordinateSystem(node), + "BOUNDCRS" => ReadWkt2BoundCoordinateSystem(node), + "GEOGCS" => ReadGeographicCoordinateSystem(node), + "PROJCS" => ReadProjectedCoordinateSystem(node), + "FITTED_CS" => ReadFittedCoordinateSystem(node), + "GEOCCS" => ReadGeocentricCoordinateSystem(node), + "COMPD_CS" => ReadCompoundCoordinateSystem(node), + "VERT_CS" => ReadVerticalCoordinateSystem(node), + _ => ThrowWktParseException($"'{node.Keyword}' is not recognized."), + }; + } - string authority = string.Empty; - long authorityCode = -1; - tokenizer.NextToken(); + private static CoordinateSystem ReadWkt2CoordinateSystemNode(WktKeywordNode node) + { + return ReadCoordinateSystemNode(node); + } - if ( tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - if(tokenizer.GetStringValue() == "AUTHORITY") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - } - } + private static bool IsCoordinateSystemKeyword(string keyword) + { + return keyword is "GEOGCRS" + or "GEODCRS" + or "GEODETICCRS" + or "PROJCRS" + or "DERIVEDPROJCRS" + or "VERTCRS" + or "ENGCRS" + or "ENGINEERINGCRS" + or "TIMECRS" + or "PARAMETRICCRS" + or "COMPOUNDCRS" + or "BOUNDCRS" + or "GEOGCS" + or "PROJCS" + or "FITTED_CS" + or "GEOCCS" + or "COMPD_CS" + or "VERT_CS" + or "LOCAL_CS"; + } - return new CompoundCoordinateSystem(headcs, tailcs, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - } - private static GeocentricCoordinateSystem ReadGeocentricCoordinateSystem(WktStreamTokenizer tokenizer) + // The WKT1 fallback still accepts historical hybrid inputs such as PROJECTEDCRS plus + // PROJECTION/PARAMETER siblings and spaced ID[...] metadata that do not satisfy the native WKT2 path. + private static string NormalizeWkt(string wkt) + { + StringBuilder? builder = null; + int copyStart = 0; + int index = 0; + while (index < wkt.Length) { - /* - * GEOCCS["", , , {,, , } {,}] - */ - - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.ReadToken("DATUM"); - var horizontalDatum = ReadHorizontalDatum(tokenizer); - tokenizer.ReadToken(","); - tokenizer.ReadToken("PRIMEM"); - var primeMeridian = ReadPrimeMeridian(tokenizer); - tokenizer.ReadToken(","); - tokenizer.ReadToken("UNIT"); - var linearUnit = ReadLinearUnit(tokenizer); - - string authority = string.Empty; - long authorityCode = -1; - tokenizer.NextToken(); - - var info = new List(3); - if (tokenizer.GetStringValue() == ",") + if (!IsWktKeywordCharacter(wkt[index])) { - tokenizer.NextToken(); - while (tokenizer.GetStringValue() == "AXIS") - { - info.Add(ReadAxis(tokenizer)); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - } - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "AUTHORITY") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } + index++; + continue; } - //This is default axis values if not specified. - if (info.Count == 0) + int keywordStart = index; + while (index < wkt.Length && IsWktKeywordCharacter(wkt[index])) { - info.Add(new AxisInfo("Geocentric X", AxisOrientationEnum.Other)); - info.Add(new AxisInfo("Geocentric Y", AxisOrientationEnum.Other)); - info.Add(new AxisInfo("Geocentric Z", AxisOrientationEnum.North)); + index++; } - return new GeocentricCoordinateSystem(horizontalDatum, linearUnit, primeMeridian, info, name, authority, authorityCode, - string.Empty, string.Empty, string.Empty); - } + ReadOnlySpan keyword = wkt.AsSpan(keywordStart, index - keywordStart); + int openerIndex = index; + while (openerIndex < wkt.Length && char.IsWhiteSpace(wkt[openerIndex])) + { + openerIndex++; + } - private static GeographicCoordinateSystem ReadGeographicCoordinateSystem(WktStreamTokenizer tokenizer) - { - /* - GEOGCS["OSGB 1936", - DATUM["OSGB 1936",SPHEROID["Airy 1830",6377563.396,299.3249646,AUTHORITY["EPSG","7001"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6277"]] - PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]] - AXIS["Geodetic latitude","NORTH"] - AXIS["Geodetic longitude","EAST"] - AUTHORITY["EPSG","4277"] - ] - */ - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.ReadToken("DATUM"); - var horizontalDatum = ReadHorizontalDatum(tokenizer); - tokenizer.ReadToken(","); - tokenizer.ReadToken("PRIMEM"); - var primeMeridian = ReadPrimeMeridian(tokenizer); - tokenizer.ReadToken(","); - tokenizer.ReadToken("UNIT"); - var angularUnit = ReadAngularUnit(tokenizer); - - string authority = string.Empty; - long authorityCode = -1; - tokenizer.NextToken(); - var info = new List(2); - if (tokenizer.GetStringValue() == ",") + if (openerIndex >= wkt.Length || (wkt[openerIndex] != '[' && wkt[openerIndex] != '(')) { - tokenizer.NextToken(); - while (tokenizer.GetStringValue() == "AXIS") - { - info.Add(ReadAxis(tokenizer)); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - } - if (tokenizer.GetStringValue() == ",") tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "AUTHORITY") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } + continue; } - //This is default axis values if not specified. - if (info.Count == 0) + if (!TryGetNormalizedWktKeyword(keyword, wkt.AsSpan(openerIndex), openerIndex != index, out string? normalizedKeyword)) { - info.Add(new AxisInfo("Lon", AxisOrientationEnum.East)); - info.Add(new AxisInfo("Lat", AxisOrientationEnum.North)); + continue; } - var geographicCS = new GeographicCoordinateSystem(angularUnit, horizontalDatum, - primeMeridian, info, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return geographicCS; + + builder ??= new StringBuilder(wkt.Length); + builder.Append(wkt, copyStart, keywordStart - copyStart); + builder.Append(normalizedKeyword); + builder.Append(wkt[openerIndex]); + copyStart = openerIndex + 1; + index = copyStart; } - private static HorizontalDatum ReadHorizontalDatum(WktStreamTokenizer tokenizer) + if (builder is null) { - //DATUM["OSGB 1936",SPHEROID["Airy 1830",6377563.396,299.3249646,AUTHORITY["EPSG","7001"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6277"]] - Wgs84ConversionInfo wgsInfo = null; - string authority = string.Empty; - long authorityCode = -1; - - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.ReadToken("SPHEROID"); - var ellipsoid = ReadEllipsoid(tokenizer); - tokenizer.NextToken(); - while (tokenizer.GetStringValue() == ",") - { - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "TOWGS84") - { - wgsInfo = ReadWGS84ConversionInfo(tokenizer); - tokenizer.NextToken(); - } - else if (tokenizer.GetStringValue() == "AUTHORITY") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } - } - // make an assumption about the datum type. - var horizontalDatum = new HorizontalDatum(ellipsoid, wgsInfo, DatumType.HD_Geocentric, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + return wkt; + } + + builder.Append(wkt, copyStart, wkt.Length - copyStart); + return builder.ToString(); + } - return horizontalDatum; + private static bool TryGetNormalizedWktKeyword(ReadOnlySpan keyword, ReadOnlySpan openerAndRemainder, bool hadWhitespaceBeforeOpener, out string? normalizedKeyword) + { + normalizedKeyword = null; + bool requiresQuotedFirstValue = false; + + if (KeywordEqualsOrdinalIgnoreCase(keyword, "ELLIPSOID")) + { + normalizedKeyword = "SPHEROID"; + } + else if (KeywordEqualsOrdinalIgnoreCase(keyword, "ID")) + { + normalizedKeyword = "AUTHORITY"; + requiresQuotedFirstValue = true; + } + else if (KeywordEqualsOrdinalIgnoreCase(keyword, "GEODETICCRS") || KeywordEqualsOrdinalIgnoreCase(keyword, "GEODCRS")) + { + normalizedKeyword = "GEOGCS"; + } + else if (KeywordEqualsOrdinalIgnoreCase(keyword, "BASEGEODCRS") || KeywordEqualsOrdinalIgnoreCase(keyword, "BASEGEOGCRS")) + { + normalizedKeyword = "GEOGCS"; + } + else if (KeywordEqualsOrdinalIgnoreCase(keyword, "PROJECTEDCRS") || KeywordEqualsOrdinalIgnoreCase(keyword, "PROJCRS")) + { + normalizedKeyword = "PROJCS"; + } + else if (KeywordEqualsOrdinalIgnoreCase(keyword, "VERTCRS")) + { + normalizedKeyword = "VERT_CS"; + } + else if (KeywordEqualsOrdinalIgnoreCase(keyword, "COMPOUNDCRS")) + { + normalizedKeyword = "COMPD_CS"; + } + else if (KeywordEqualsOrdinalIgnoreCase(keyword, "BOUNDCRS")) + { + normalizedKeyword = "BOUNDCRS"; + } + else + { + return false; } - private static VerticalDatum ReadVerticalDatum(WktStreamTokenizer tokenizer) + if (requiresQuotedFirstValue) { - // = VERT_DATUM["", {,}] - string authority = string.Empty; - long authorityCode = -1; - - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - var datumType = (DatumType) tokenizer.GetNumericValue(); - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == ",") + int valueIndex = 1; + while (valueIndex < openerAndRemainder.Length && char.IsWhiteSpace(openerAndRemainder[valueIndex])) { - tokenizer.NextToken(); - if (tokenizer.GetStringValue() == "AUTHORITY") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } + valueIndex++; } - var verticalDatum = new VerticalDatum( datumType, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return verticalDatum; + if (valueIndex >= openerAndRemainder.Length || openerAndRemainder[valueIndex] != '"') + { + normalizedKeyword = null; + return false; + } } - private static PrimeMeridian ReadPrimeMeridian(WktStreamTokenizer tokenizer) + if (!hadWhitespaceBeforeOpener && KeywordEqualsOrdinal(keyword, normalizedKeyword)) { - //PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]] - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord(); - tokenizer.ReadToken(","); - tokenizer.NextToken(); - double longitude = tokenizer.GetNumericValue(); - - tokenizer.NextToken(); - string authority = string.Empty; - long authorityCode = -1; - if (tokenizer.GetStringValue() == ",") - { - tokenizer.ReadAuthority(out authority, out authorityCode); - tokenizer.ReadCloser(bracket); - } - else - tokenizer.CheckCloser(bracket); + normalizedKeyword = null; + return false; + } - // make an assumption about the Angular units - degrees. - var primeMeridian = new PrimeMeridian(longitude, AngularUnit.Degrees, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + return true; + } + + private static bool IsWktKeywordCharacter(char value) + { + return (value >= 'A' && value <= 'Z') + || (value >= 'a' && value <= 'z') + || (value >= '0' && value <= '9') + || value == '_'; + } - return primeMeridian; + private static bool KeywordEqualsOrdinal(ReadOnlySpan keyword, string expected) + { + if (keyword.Length != expected.Length) + { + return false; } - private static FittedCoordinateSystem ReadFittedCoordinateSystem (WktStreamTokenizer tokenizer) + for (int i = 0; i < keyword.Length; i++) { - /* - FITTED_CS[ - "Local coordinate system MNAU (based on Gauss-Krueger)", - PARAM_MT[ - "Affine", - PARAMETER["num_row",3], - PARAMETER["num_col",3], - PARAMETER["elt_0_0", 0.883485346527455], - PARAMETER["elt_0_1", -0.468458794848877], - PARAMETER["elt_0_2", 3455869.17937689], - PARAMETER["elt_1_0", 0.468458794848877], - PARAMETER["elt_1_1", 0.883485346527455], - PARAMETER["elt_1_2", 5478710.88035753], - PARAMETER["elt_2_2", 1], - ], - PROJCS["DHDN / Gauss-Kruger zone 3", GEOGCS["DHDN", DATUM["Deutsches_Hauptdreiecksnetz", SPHEROID["Bessel 1841", 6377397.155, 299.1528128, AUTHORITY["EPSG", "7004"]], TOWGS84[612.4, 77, 440.2, -0.054, 0.057, -2.797, 0.525975255930096], AUTHORITY["EPSG", "6314"]], PRIMEM["Greenwich", 0, AUTHORITY["EPSG", "8901"]], UNIT["degree", 0.0174532925199433, AUTHORITY["EPSG", "9122"]], AUTHORITY["EPSG", "4314"]], UNIT["metre", 1, AUTHORITY["EPSG", "9001"]], PROJECTION["Transverse_Mercator"], PARAMETER["latitude_of_origin", 0], PARAMETER["central_meridian", 9], PARAMETER["scale_factor", 1], PARAMETER["false_easting", 3500000], PARAMETER["false_northing", 0], AUTHORITY["EPSG", "31467"]] - AUTHORITY["CUSTOM","12345"] - ] - */ - var bracket = tokenizer.ReadOpener(); - string name = tokenizer.ReadDoubleQuotedWord (); - tokenizer.ReadToken (","); - tokenizer.ReadToken ("PARAM_MT"); - var toBaseTransform = MathTransformWktReader.ReadMathTransform (tokenizer); - tokenizer.ReadToken (","); - tokenizer.NextToken (); - var baseCS = ReadCoordinateSystem (null, tokenizer); - - string authority = string.Empty; - long authorityCode = -1; - - var ct = tokenizer.NextToken (); - while (ct != TokenType.Eol && ct != TokenType.Eof) + if (keyword[i] != expected[i]) { - switch (tokenizer.GetStringValue ()) - { - case ",": - break; - case "]": - case ")": - tokenizer.CheckCloser(bracket); - - break; - case "AUTHORITY": - tokenizer.ReadAuthority (out authority, out authorityCode); - //tokenizer.ReadCloser(bracket); - break; - } - ct = tokenizer.NextToken (); + return false; } - - var fittedCS = new FittedCoordinateSystem (baseCS, toBaseTransform, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); - return fittedCS; } + + return true; + } + + private static IInfo ParseNormalizedWkt(string normalizedWkt) + { + var tokenizer = new WktTokenizer(normalizedWkt); + tokenizer.NextToken(); + var rootNode = WktKeywordNode.ParseSubtree(tokenizer); + return rootNode.Keyword switch + { + "UNIT" => ReadUnit(rootNode), + "SPHEROID" => ReadEllipsoid(rootNode), + "DATUM" => ReadHorizontalDatum(rootNode), + "PRIMEM" => ReadPrimeMeridian(rootNode), + "VERT_CS" or "GEOGCS" or "PROJCS" or "COMPD_CS" or "GEOCCS" or "FITTED_CS" or "LOCAL_CS" + => ReadCoordinateSystemNode(rootNode), + "BOUNDCRS" => throw new NotSupportedException("BOUNDCRS coordinate system is not supported."), + _ => ThrowWktParseException($"'{rootNode.Keyword}' is not recognized."), + }; } } diff --git a/src/ProjNet/IO/CoordinateSystems/MathTransformWktReader.cs b/src/ProjNet/IO/CoordinateSystems/MathTransformWktReader.cs index d417dce5..799afe65 100644 --- a/src/ProjNet/IO/CoordinateSystems/MathTransformWktReader.cs +++ b/src/ProjNet/IO/CoordinateSystems/MathTransformWktReader.cs @@ -1,241 +1,545 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.IO.CoordinateSystems; using System; using System.Collections.Generic; -using System.IO; +using System.Diagnostics.CodeAnalysis; using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.Wkt; -namespace ProjNet.IO.CoordinateSystems +/// +/// Creates a from a Well Known Text (WKT) string. +/// +public static class MathTransformWktReader { /// - /// Creates an math transform based on the supplied Well Known Text (WKT). + /// Reads and parses a WKT-formatted projection string. + /// + /// String containing WKT. + /// Object representation of the WKT. + /// If a token is not recognised. + public static MathTransform Parse(string wkt) + { + if (string.IsNullOrWhiteSpace(wkt)) + { + ArgumentGuard.ThrowArgument("WKT text must not be empty or whitespace.", nameof(wkt)); + } + + var tokenizer = new WktTokenizer(wkt); + tokenizer.NextToken(); + string objectName = tokenizer.GetStringValue(); + return objectName switch + { + "PARAM_MT" => ReadMathTransform(tokenizer), + "INVERSE_MT" => ReadInverseMathTransform(tokenizer), + _ => ThrowWktParseException($"'{objectName}' is not recognized."), + }; + } + + /// + /// Reads a math transform from the current position of the specified tokenizer. + /// + /// The tokenizer positioned at or before a PARAM_MT token. + /// The parsed . + internal static MathTransform ReadMathTransform(WktTokenizer tokenizer) + { + if (tokenizer.GetStringValue() != "PARAM_MT") + { + tokenizer.ReadToken("PARAM_MT"); + } + + tokenizer.ReadToken("["); + string transformName = tokenizer.ReadDoubleQuotedWord(); + tokenizer.ReadToken(","); + + return transformName.ToUpperInvariant() switch + { + "AFFINE" => ReadAffineTransform(tokenizer), + "IDENTITY" => ReadIdentityTransform(tokenizer), + _ => ReadProjectionTransform(tokenizer, transformName), + }; + } + + /// + /// Reads a math transform from a parsed PARAM_MT node. + /// + /// The parsed math-transform node. + /// The parsed . + internal static MathTransform ReadMathTransform(WktKeywordNode node) + { + ArgumentGuard.ThrowIfNull(node, nameof(node)); + if (!string.Equals(node.Keyword, "PARAM_MT", StringComparison.OrdinalIgnoreCase)) + { + ArgumentGuard.ThrowArgument($"Expected 'PARAM_MT' but found '{node.Keyword}'.", nameof(node)); + } + + string transformName = node.GetString(0); + return transformName.ToUpperInvariant() switch + { + "AFFINE" => ReadAffineTransform(node), + "IDENTITY" => ReadIdentityTransform(node), + _ => ReadProjectionTransform(node, transformName), + }; + } + + /// + /// Reads an inverse math transform from the current position of the specified tokenizer. + /// + /// The tokenizer positioned at or before an INVERSE_MT token. + /// The parsed inverse . + internal static MathTransform ReadInverseMathTransform(WktTokenizer tokenizer) + { + if (tokenizer.GetStringValue() != "INVERSE_MT") + { + tokenizer.ReadToken("INVERSE_MT"); + } + + tokenizer.ReadToken("["); + tokenizer.NextToken(); + + MathTransform transform = tokenizer.GetStringValue() switch + { + "PARAM_MT" => ReadMathTransform(tokenizer), + "INVERSE_MT" => ReadInverseMathTransform(tokenizer), + _ => throw new NotSupportedException($"Transform not supported '{tokenizer.GetStringValue()}'"), + }; + + if (tokenizer.GetStringValue() != "]") + { + tokenizer.ReadToken("]"); + } + + return transform.Inverse(); + } + + /// + /// Reads an inverse math transform from a parsed INVERSE_MT node. /// - public static class MathTransformWktReader + /// The parsed inverse-math-transform node. + /// The parsed inverse . + internal static MathTransform ReadInverseMathTransform(WktKeywordNode node) { - /// - /// Reads and parses a WKT-formatted projection string. - /// - /// String containing WKT. - /// Object representation of the WKT. - /// If a token is not recognised. - public static MathTransform Parse (string wkt) - { - if (string.IsNullOrWhiteSpace (wkt)) - throw new ArgumentNullException ("wkt"); - - using (TextReader reader = new StringReader (wkt)) + ArgumentGuard.ThrowIfNull(node, nameof(node)); + if (!string.Equals(node.Keyword, "INVERSE_MT", StringComparison.OrdinalIgnoreCase)) + { + ArgumentGuard.ThrowArgument($"Expected 'INVERSE_MT' but found '{node.Keyword}'.", nameof(node)); + } + + foreach (WktNode child in node.Children) + { + if (child is not WktKeywordNode keywordChild) { - var tokenizer = new WktStreamTokenizer (reader); - tokenizer.NextToken (); - string objectName = tokenizer.GetStringValue (); - switch (objectName) - { - case "PARAM_MT": - return ReadMathTransform (tokenizer); - default: - throw new ArgumentException ($"'{objectName}' is not recognized."); - } + continue; } + + MathTransform transform = keywordChild.Keyword switch + { + "PARAM_MT" => ReadMathTransform(keywordChild), + "INVERSE_MT" => ReadInverseMathTransform(keywordChild), + _ => throw new NotSupportedException($"Transform not supported '{keywordChild.Keyword}'"), + }; + + return transform.Inverse(); } - /// - /// Reads math transform from using current token from the specified tokenizer - /// - /// - /// - internal static MathTransform ReadMathTransform (WktStreamTokenizer tokenizer) + return ThrowWktParseException("INVERSE_MT does not contain a nested math transform."); + } + + private static ParameterInfo ReadParameters(WktTokenizer tokenizer) + { + var paramList = new List(); + while (tokenizer.GetStringValue() == "PARAMETER") { - if (tokenizer.GetStringValue () != "PARAM_MT") - tokenizer.ReadToken ("PARAM_MT"); - tokenizer.ReadToken ("["); - string transformName = tokenizer.ReadDoubleQuotedWord (); - tokenizer.ReadToken (","); + tokenizer.ReadToken("["); + string paramName = tokenizer.ReadDoubleQuotedWord(); + tokenizer.ReadToken(","); + tokenizer.NextToken(); + double paramValue = tokenizer.GetNumericValue(); + tokenizer.ReadToken("]"); - switch (transformName.ToUpperInvariant ()) + // test, whether next parameter is delimited by comma + tokenizer.NextToken(); + if (tokenizer.GetStringValue() != "]") { - case "AFFINE": - return ReadAffineTransform (tokenizer); - default: - throw new NotSupportedException ("Transform not supported '" + transformName + "'"); + tokenizer.NextToken(); } + + paramList.Add(new Parameter(paramName, paramValue)); } - private static ParameterInfo ReadParameters (WktStreamTokenizer tokenizer) + var info = new ParameterInfo() { Parameters = paramList }; + return info; + } + + private static ParameterInfo ReadParameters(WktKeywordNode node) + { + ArgumentGuard.ThrowIfNull(node, nameof(node)); + + var paramList = new List(); + foreach (WktNode child in node.Children) { - var paramList = new List (); - while (tokenizer.GetStringValue () == "PARAMETER") + if (child is not WktKeywordNode keywordChild) { - tokenizer.ReadToken ("["); - string paramName = tokenizer.ReadDoubleQuotedWord (); - tokenizer.ReadToken (","); - tokenizer.NextToken (); - double paramValue = tokenizer.GetNumericValue (); - tokenizer.ReadToken ("]"); - //test, whether next parameter is delimited by comma - tokenizer.NextToken (); - if (tokenizer.GetStringValue () != "]") - tokenizer.NextToken (); - paramList.Add (new Parameter (paramName, paramValue)); + continue; } - var info = new ParameterInfo () { Parameters = paramList }; - return info; - } - - private static MathTransform ReadAffineTransform (WktStreamTokenizer tokenizer) - { - /* - PARAM_MT[ - "Affine", - PARAMETER["num_row",3], - PARAMETER["num_col",3], - PARAMETER["elt_0_0", 0.883485346527455], - PARAMETER["elt_0_1", -0.468458794848877], - PARAMETER["elt_0_2", 3455869.17937689], - PARAMETER["elt_1_0", 0.468458794848877], - PARAMETER["elt_1_1", 0.883485346527455], - PARAMETER["elt_1_2", 5478710.88035753], - PARAMETER["elt_2_2", 1] - ] - */ - //tokenizer stands on the first PARAMETER - if (tokenizer.GetStringValue () != "PARAMETER") - tokenizer.ReadToken ("PARAMETER"); - - var paramInfo = ReadParameters (tokenizer); - //manage required parameters - row, col - var rowParam = paramInfo.GetParameterByName ("num_row"); - var colParam = paramInfo.GetParameterByName ("num_col"); - - if (rowParam == null) + + if (!string.Equals(keywordChild.Keyword, "PARAMETER", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + paramList.Add(new Parameter(keywordChild.GetString(0), keywordChild.GetNumber(0))); + } + + return new ParameterInfo() { Parameters = paramList }; + } + + private static AffineTransform ReadAffineTransform(WktTokenizer tokenizer) + { + // PARAM_MT[ + // "Affine", + // PARAMETER["num_row",3], + // PARAMETER["num_col",3], + // PARAMETER["elt_0_0", 0.883485346527455], + // PARAMETER["elt_0_1", -0.468458794848877], + // PARAMETER["elt_0_2", 3455869.17937689], + // PARAMETER["elt_1_0", 0.468458794848877], + // PARAMETER["elt_1_1", 0.883485346527455], + // PARAMETER["elt_1_2", 5478710.88035753], + // PARAMETER["elt_2_2", 1] + // ] + // tokenizer stands on the first PARAMETER + if (tokenizer.GetStringValue() != "PARAMETER") + { + tokenizer.ReadToken("PARAMETER"); + } + + ParameterInfo paramInfo = ReadParameters(tokenizer); + + // manage required parameters - row, col + Parameter? rowParamCandidate = paramInfo.GetParameterByName("num_row"); + Parameter? colParamCandidate = paramInfo.GetParameterByName("num_col"); + + if (rowParamCandidate is null) + { + ThrowWktParseException("Affine transform does not contain 'num_row' parameter"); + } + + if (colParamCandidate is null) + { + ThrowWktParseException("Affine transform does not contain 'num_col' parameter"); + } + + Parameter rowParam = ArgumentGuard.ThrowIfNull(rowParamCandidate, nameof(rowParamCandidate)); + Parameter colParam = ArgumentGuard.ThrowIfNull(colParamCandidate, nameof(colParamCandidate)); + IList? parametersCandidate = paramInfo.Parameters; + IList parameters = ArgumentGuard.ThrowIfNull(parametersCandidate, nameof(parametersCandidate)); + + int rowVal = (int)rowParam.Value; + int colVal = (int)colParam.Value; + + if (rowVal <= 0) + { + ThrowWktParseException("Affine transform contains invalid value of 'num_row' parameter"); + } + + if (colVal <= 0) + { + ThrowWktParseException("Affine transform contains invalid value of 'num_col' parameter"); + } + + // creates working matrix; + double[,] matrix = new double[rowVal, colVal]; + + // simply process matrix values - no elt_ROW_COL parsing + foreach (Parameter? param in parameters) + { + if (param is null || param.Name is null) { - throw new ArgumentNullException (nameof(rowParam), "Affine transform does not contain 'num_row' parameter"); + continue; } - if (colParam == null) + + switch (param.Name) { - throw new ArgumentNullException (nameof(colParam), "Affine transform does not contain 'num_col' parameter"); + case "num_row": + case "num_col": + break; + case "elt_0_0": + matrix[0, 0] = param.Value; + break; + case "elt_0_1": + matrix[0, 1] = param.Value; + break; + case "elt_0_2": + matrix[0, 2] = param.Value; + break; + case "elt_0_3": + matrix[0, 3] = param.Value; + break; + case "elt_1_0": + matrix[1, 0] = param.Value; + break; + case "elt_1_1": + matrix[1, 1] = param.Value; + break; + case "elt_1_2": + matrix[1, 2] = param.Value; + break; + case "elt_1_3": + matrix[1, 3] = param.Value; + break; + case "elt_2_0": + matrix[2, 0] = param.Value; + break; + case "elt_2_1": + matrix[2, 1] = param.Value; + break; + case "elt_2_2": + matrix[2, 2] = param.Value; + break; + case "elt_2_3": + matrix[2, 3] = param.Value; + break; + case "elt_3_0": + matrix[3, 0] = param.Value; + break; + case "elt_3_1": + matrix[3, 1] = param.Value; + break; + case "elt_3_2": + matrix[3, 2] = param.Value; + break; + case "elt_3_3": + matrix[3, 3] = param.Value; + break; } - int rowVal = (int)rowParam.Value; - int colVal = (int)colParam.Value; + } + + // read rest of WKT + if (tokenizer.GetStringValue() != "]") + { + tokenizer.ReadToken("]"); + } - if (rowVal <= 0) + // use "matrix" constructor to create transformation matrix + var affineTransform = new AffineTransform(matrix); + return affineTransform; + } + + private static AffineTransform ReadAffineTransform(WktKeywordNode node) + { + ParameterInfo paramInfo = ReadParameters(node); + + Parameter? rowParamCandidate = paramInfo.GetParameterByName("num_row"); + Parameter? colParamCandidate = paramInfo.GetParameterByName("num_col"); + + if (rowParamCandidate is null) + { + ThrowWktParseException("Affine transform does not contain 'num_row' parameter"); + } + + if (colParamCandidate is null) + { + ThrowWktParseException("Affine transform does not contain 'num_col' parameter"); + } + + Parameter rowParam = ArgumentGuard.ThrowIfNull(rowParamCandidate, nameof(rowParamCandidate)); + Parameter colParam = ArgumentGuard.ThrowIfNull(colParamCandidate, nameof(colParamCandidate)); + IList? parametersCandidate = paramInfo.Parameters; + IList parameters = ArgumentGuard.ThrowIfNull(parametersCandidate, nameof(parametersCandidate)); + + int rowVal = (int)rowParam.Value; + int colVal = (int)colParam.Value; + + if (rowVal <= 0) + { + ThrowWktParseException("Affine transform contains invalid value of 'num_row' parameter"); + } + + if (colVal <= 0) + { + ThrowWktParseException("Affine transform contains invalid value of 'num_col' parameter"); + } + + double[,] matrix = new double[rowVal, colVal]; + foreach (Parameter? param in parameters) + { + if (param is null || param.Name is null) { - throw new ArgumentException ("Affine transform contains invalid value of 'num_row' parameter"); + continue; } - if (colVal <= 0) + switch (param.Name) { - throw new ArgumentException ("Affine transform contains invalid value of 'num_col' parameter"); + case "num_row": + case "num_col": + break; + case "elt_0_0": + matrix[0, 0] = param.Value; + break; + case "elt_0_1": + matrix[0, 1] = param.Value; + break; + case "elt_0_2": + matrix[0, 2] = param.Value; + break; + case "elt_0_3": + matrix[0, 3] = param.Value; + break; + case "elt_1_0": + matrix[1, 0] = param.Value; + break; + case "elt_1_1": + matrix[1, 1] = param.Value; + break; + case "elt_1_2": + matrix[1, 2] = param.Value; + break; + case "elt_1_3": + matrix[1, 3] = param.Value; + break; + case "elt_2_0": + matrix[2, 0] = param.Value; + break; + case "elt_2_1": + matrix[2, 1] = param.Value; + break; + case "elt_2_2": + matrix[2, 2] = param.Value; + break; + case "elt_2_3": + matrix[2, 3] = param.Value; + break; + case "elt_3_0": + matrix[3, 0] = param.Value; + break; + case "elt_3_1": + matrix[3, 1] = param.Value; + break; + case "elt_3_2": + matrix[3, 2] = param.Value; + break; + case "elt_3_3": + matrix[3, 3] = param.Value; + break; } + } + + return new AffineTransform(matrix); + } + + private static IdentityMathTransform ReadIdentityTransform(WktTokenizer tokenizer) + { + if (tokenizer.GetStringValue() != "PARAMETER") + { + tokenizer.ReadToken("PARAMETER"); + } + + ParameterInfo paramInfo = ReadParameters(tokenizer); + Parameter? dimensionParam = paramInfo.GetParameterByName("dimension"); + if (dimensionParam is null) + { + ThrowWktParseException("Identity transform does not contain 'dimension' parameter"); + } - //creates working matrix; - double[,] matrix = new double[rowVal, colVal]; + int dimension = (int)dimensionParam.Value; + if (dimension <= 0) + { + ThrowWktParseException("Identity transform contains invalid value of 'dimension' parameter"); + } + + if (tokenizer.GetStringValue() != "]") + { + tokenizer.ReadToken("]"); + } + + return new IdentityMathTransform(dimension); + } + + private static IdentityMathTransform ReadIdentityTransform(WktKeywordNode node) + { + ParameterInfo paramInfo = ReadParameters(node); + Parameter? dimensionParam = paramInfo.GetParameterByName("dimension"); + if (dimensionParam is null) + { + ThrowWktParseException("Identity transform does not contain 'dimension' parameter"); + } + + int dimension = (int)dimensionParam.Value; + if (dimension <= 0) + { + ThrowWktParseException("Identity transform contains invalid value of 'dimension' parameter"); + } + + return new IdentityMathTransform(dimension); + } + + private static MathTransform ReadProjectionTransform(WktTokenizer tokenizer, string transformName) + { + if (tokenizer.GetStringValue() != "PARAMETER") + { + tokenizer.ReadToken("PARAMETER"); + } + + ParameterInfo paramInfo = ReadParameters(tokenizer); + IList? parametersCandidate = paramInfo.Parameters; + IList parameters = ArgumentGuard.ThrowIfNull(parametersCandidate, nameof(parametersCandidate)); + var projectionParameters = new List(parameters.Count); - //simply process matrix values - no elt_ROW_COL parsing - foreach (var param in paramInfo.Parameters) + foreach (Parameter? parameter in parameters) + { + if (parameter is null || parameter.Name is null) { - if (param == null || param.Name == null) - { - continue; - } - switch (param.Name) - { - case "num_row": - case "num_col": - break; - case "elt_0_0": - matrix[0, 0] = param.Value; - break; - case "elt_0_1": - matrix[0, 1] = param.Value; - break; - case "elt_0_2": - matrix[0, 2] = param.Value; - break; - case "elt_0_3": - matrix[0, 3] = param.Value; - break; - case "elt_1_0": - matrix[1, 0] = param.Value; - break; - case "elt_1_1": - matrix[1, 1] = param.Value; - break; - case "elt_1_2": - matrix[1, 2] = param.Value; - break; - case "elt_1_3": - matrix[1, 3] = param.Value; - break; - case "elt_2_0": - matrix[2, 0] = param.Value; - break; - case "elt_2_1": - matrix[2, 1] = param.Value; - break; - case "elt_2_2": - matrix[2, 2] = param.Value; - break; - case "elt_2_3": - matrix[2, 3] = param.Value; - break; - case "elt_3_0": - matrix[3, 0] = param.Value; - break; - case "elt_3_1": - matrix[3, 1] = param.Value; - break; - case "elt_3_2": - matrix[3, 2] = param.Value; - break; - case "elt_3_3": - matrix[3, 3] = param.Value; - break; - } + continue; } - //read rest of WKT - if (tokenizer.GetStringValue () != "]") - tokenizer.ReadToken ("]"); + projectionParameters.Add(new ProjectionParameter(parameter.Name, parameter.Value)); + } + + if (tokenizer.GetStringValue() != "]") + { + tokenizer.ReadToken("]"); + } + + return ProjectionsRegistry.CreateProjection(transformName, projectionParameters); + } + + private static MathTransform ReadProjectionTransform(WktKeywordNode node, string transformName) + { + ParameterInfo paramInfo = ReadParameters(node); + IList? parametersCandidate = paramInfo.Parameters; + IList parameters = ArgumentGuard.ThrowIfNull(parametersCandidate, nameof(parametersCandidate)); + var projectionParameters = new List(parameters.Count); + + foreach (Parameter? parameter in parameters) + { + if (parameter is null || parameter.Name is null) + { + continue; + } - //use "matrix" constructor to create transformation matrix - var affineTransform = new AffineTransform (matrix); - return affineTransform; + projectionParameters.Add(new ProjectionParameter(parameter.Name, parameter.Value)); } + + return ProjectionsRegistry.CreateProjection(transformName, projectionParameters); + } + + [DoesNotReturn] + private static void ThrowWktParseException(string message) + { + throw new WktParseException(message); + } + + [DoesNotReturn] + private static T ThrowWktParseException(string message) + { + throw new WktParseException(message); } } diff --git a/src/ProjNet/IO/CoordinateSystems/ProjJsonReader.cs b/src/ProjNet/IO/CoordinateSystems/ProjJsonReader.cs new file mode 100644 index 00000000..28bd6c91 --- /dev/null +++ b/src/ProjNet/IO/CoordinateSystems/ProjJsonReader.cs @@ -0,0 +1,1016 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.Json; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; + +/// +/// Creates an object based on the supplied PROJJSON text. +/// +public static class ProjJsonReader +{ + /// + /// Reads and parses a PROJJSON-formatted text. + /// + /// String containing PROJJSON. + /// Object representation of the PROJJSON text. + public static IInfo Parse(string json) => Parse(json.AsSpan()); + + /// + /// Reads and parses a PROJJSON-formatted text from a character span. + /// + /// Character span containing PROJJSON. + /// Object representation of the PROJJSON text. + public static IInfo Parse(ReadOnlySpan json) + { + if (json.IsEmpty || IsWhitespaceOnly(json)) + { + ArgumentGuard.ThrowArgumentNull(nameof(json)); + } + + using var document = JsonDocument.Parse(json.ToString()); + if (document.RootElement.ValueKind != JsonValueKind.Object) + { + ArgumentGuard.ThrowArgument("PROJJSON root must be an object.", nameof(json)); + } + + return ReadInfo(document.RootElement); + } + + private static bool IsWhitespaceOnly(ReadOnlySpan value) + { + for (int i = 0; i < value.Length; i++) + { + if (!char.IsWhiteSpace(value[i])) + { + return false; + } + } + + return true; + } + + private static IInfo ReadInfo(JsonElement element) + { + string type = GetRequiredString(element, "type"); + if (string.Equals(type, "GeographicCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadGeographicCoordinateSystem(element); + } + + if (string.Equals(type, "GeodeticCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadGeodeticCoordinateSystem(element); + } + + if (string.Equals(type, "ProjectedCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadProjectedCoordinateSystem(element); + } + + if (string.Equals(type, "DerivedGeographicCRS", StringComparison.OrdinalIgnoreCase) + || string.Equals(type, "DerivedGeodeticCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadDerivedGeodeticCoordinateSystem(element); + } + + if (string.Equals(type, "DerivedProjectedCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadDerivedProjectedCoordinateSystem(element); + } + + if (string.Equals(type, "BoundCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadBoundCoordinateSystem(element); + } + + if (string.Equals(type, "VerticalCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadVerticalCoordinateSystem(element); + } + + if (string.Equals(type, "CompoundCRS", StringComparison.OrdinalIgnoreCase)) + { + return ReadCompoundCoordinateSystem(element); + } + + throw new NotSupportedException($"PROJJSON type '{type}' is not supported."); + } + + private static GeographicCoordinateSystem ReadGeographicCoordinateSystem(JsonElement element) + { + string name = GetRequiredString(element, "name"); + HorizontalDatum horizontalDatum = ReadHorizontalDatumOrEnsemble(element); + PrimeMeridian primeMeridian = element.TryGetProperty("prime_meridian", out JsonElement primeMeridianElement) + ? ReadPrimeMeridian(primeMeridianElement) + : PrimeMeridian.Greenwich; + + ReadCoordinateSystemDefinition( + GetRequiredProperty(element, "coordinate_system"), + out string coordinateSystemType, + out int coordinateSystemDimension, + out List axisInfo, + out AngularUnit? angularUnit, + out _); + + if (!string.Equals(coordinateSystemType, "ellipsoidal", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON coordinate system subtype '{coordinateSystemType}' is not supported for GeographicCRS."); + } + + if (coordinateSystemDimension != 2) + { + throw new NotSupportedException("PROJJSON GeographicCRS dimensions other than 2 are not supported."); + } + + if (angularUnit is null) + { + ArgumentGuard.ThrowArgument("PROJJSON GeographicCRS is missing axis angular units.", nameof(element)); + } + + ReadIdentifier(element, out string authority, out long authorityCode); + return new GeographicCoordinateSystem( + angularUnit, + horizontalDatum, + primeMeridian, + axisInfo, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static CoordinateSystem ReadGeodeticCoordinateSystem(JsonElement element) + { + JsonElement coordinateSystemElement = GetRequiredProperty(element, "coordinate_system"); + string coordinateSystemType = GetRequiredString(coordinateSystemElement, "subtype"); + if (string.Equals(coordinateSystemType, "ellipsoidal", StringComparison.OrdinalIgnoreCase)) + { + return ReadGeographicCoordinateSystem(element); + } + + if (string.Equals(coordinateSystemType, "cartesian", StringComparison.OrdinalIgnoreCase)) + { + return ReadGeocentricCoordinateSystem(element); + } + + throw new NotSupportedException($"PROJJSON GeodeticCRS coordinate system subtype '{coordinateSystemType}' is not supported."); + } + + private static GeocentricCoordinateSystem ReadGeocentricCoordinateSystem(JsonElement element) + { + string name = GetRequiredString(element, "name"); + HorizontalDatum horizontalDatum = ReadHorizontalDatumOrEnsemble(element); + PrimeMeridian primeMeridian = element.TryGetProperty("prime_meridian", out JsonElement primeMeridianElement) + ? ReadPrimeMeridian(primeMeridianElement) + : PrimeMeridian.Greenwich; + + ReadCoordinateSystemDefinition( + GetRequiredProperty(element, "coordinate_system"), + out string coordinateSystemType, + out int coordinateSystemDimension, + out List axisInfo, + out _, + out LinearUnit? linearUnit); + + if (!string.Equals(coordinateSystemType, "cartesian", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON coordinate system subtype '{coordinateSystemType}' is not supported for geocentric GeodeticCRS."); + } + + if (coordinateSystemDimension != 3) + { + throw new NotSupportedException("PROJJSON cartesian GeodeticCRS dimensions other than 3 are not supported."); + } + + if (linearUnit is null) + { + ArgumentGuard.ThrowArgument("PROJJSON geocentric GeodeticCRS is missing axis linear units.", nameof(element)); + } + + ReadIdentifier(element, out string authority, out long authorityCode); + return new GeocentricCoordinateSystem( + horizontalDatum, + linearUnit, + primeMeridian, + axisInfo, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static ProjectedCoordinateSystem ReadProjectedCoordinateSystem(JsonElement element) + { + string name = GetRequiredString(element, "name"); + GeographicCoordinateSystem baseCrs = ReadGeographicCoordinateSystem(GetRequiredProperty(element, "base_crs")); + Projection conversion = ReadConversion(GetRequiredProperty(element, "conversion")); + + ReadCoordinateSystemDefinition( + GetRequiredProperty(element, "coordinate_system"), + out string coordinateSystemType, + out int coordinateSystemDimension, + out List axisInfo, + out _, + out LinearUnit? linearUnit); + + if (!string.Equals(coordinateSystemType, "cartesian", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON coordinate system subtype '{coordinateSystemType}' is not supported for ProjectedCRS."); + } + + if (coordinateSystemDimension != 2) + { + throw new NotSupportedException("PROJJSON ProjectedCRS dimensions other than 2 are not supported."); + } + + if (linearUnit is null) + { + ArgumentGuard.ThrowArgument("PROJJSON ProjectedCRS is missing axis linear units.", nameof(element)); + } + + ReadIdentifier(element, out string authority, out long authorityCode); + return new ProjectedCoordinateSystem( + baseCrs.HorizontalDatum, + baseCrs, + linearUnit, + conversion, + axisInfo, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static FittedCoordinateSystem ReadDerivedGeodeticCoordinateSystem(JsonElement element) + { + string name = GetRequiredString(element, "name"); + CoordinateSystem baseCoordinateSystem = ReadCoordinateSystemElement(GetRequiredProperty(element, "base_crs"), "base_crs"); + if (baseCoordinateSystem is not GeographicCoordinateSystem baseGeographicCoordinateSystem) + { + throw new NotSupportedException("PROJJSON derived geodetic CRS currently supports only geographic base CRS definitions."); + } + + Projection conversion = ReadConversion(GetRequiredProperty(element, "conversion")); + AffineTransform transform = DerivedCoordinateSystemSupport.CreateAffineTransform(conversion); + + ReadCoordinateSystemDefinition( + GetRequiredProperty(element, "coordinate_system"), + out string coordinateSystemType, + out int coordinateSystemDimension, + out List axisInfo, + out AngularUnit? angularUnit, + out _); + + if (!string.Equals(coordinateSystemType, "ellipsoidal", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON derived geodetic coordinate-system subtype '{coordinateSystemType}' is not supported."); + } + + if (coordinateSystemDimension != 2) + { + throw new NotSupportedException("PROJJSON derived geodetic CRS dimensions other than 2 are not supported."); + } + + if (angularUnit is null) + { + ArgumentGuard.ThrowArgument("PROJJSON derived geodetic CRS is missing axis angular units.", nameof(element)); + } + + ReadIdentifier(element, out string authority, out long authorityCode); + return CreateDerivedCoordinateSystem(name, baseGeographicCoordinateSystem, transform, axisInfo, authority, authorityCode); + } + + private static FittedCoordinateSystem ReadDerivedProjectedCoordinateSystem(JsonElement element) + { + string name = GetRequiredString(element, "name"); + CoordinateSystem baseCoordinateSystem = ReadCoordinateSystemElement(GetRequiredProperty(element, "base_crs"), "base_crs"); + if (baseCoordinateSystem is not ProjectedCoordinateSystem baseProjectedCoordinateSystem) + { + throw new NotSupportedException("PROJJSON derived projected CRS currently supports only projected base CRS definitions."); + } + + Projection conversion = ReadConversion(GetRequiredProperty(element, "conversion")); + AffineTransform transform = DerivedCoordinateSystemSupport.CreateAffineTransform(conversion); + + ReadCoordinateSystemDefinition( + GetRequiredProperty(element, "coordinate_system"), + out string coordinateSystemType, + out int coordinateSystemDimension, + out List axisInfo, + out _, + out LinearUnit? linearUnit); + + if (!string.Equals(coordinateSystemType, "cartesian", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON derived projected coordinate-system subtype '{coordinateSystemType}' is not supported."); + } + + if (coordinateSystemDimension != 2) + { + throw new NotSupportedException("PROJJSON derived projected CRS dimensions other than 2 are not supported."); + } + + if (linearUnit is null) + { + ArgumentGuard.ThrowArgument("PROJJSON derived projected CRS is missing axis linear units.", nameof(element)); + } + + ReadIdentifier(element, out string authority, out long authorityCode); + return CreateDerivedCoordinateSystem(name, baseProjectedCoordinateSystem, transform, axisInfo, authority, authorityCode); + } + + private static BoundCoordinateSystem ReadBoundCoordinateSystem(JsonElement element) + { + CoordinateSystem sourceCoordinateSystem = ReadCoordinateSystemElement(GetRequiredProperty(element, "source_crs"), "source_crs"); + CoordinateSystem targetCoordinateSystem = ReadCoordinateSystemElement(GetRequiredProperty(element, "target_crs"), "target_crs"); + BoundTransformation transformation = ReadBoundTransformation(GetRequiredProperty(element, "transformation"), sourceCoordinateSystem); + + string name = GetOptionalString(element, "name") ?? sourceCoordinateSystem.Name; + ReadIdentifier(element, out string authority, out long authorityCode); + return new BoundCoordinateSystem( + sourceCoordinateSystem, + targetCoordinateSystem, + transformation, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static VerticalCoordinateSystem ReadVerticalCoordinateSystem(JsonElement element) + { + string name = GetRequiredString(element, "name"); + VerticalDatum verticalDatum = ReadVerticalDatumOrEnsemble(element); + + ReadCoordinateSystemDefinition( + GetRequiredProperty(element, "coordinate_system"), + out string coordinateSystemType, + out int coordinateSystemDimension, + out List axisInfo, + out _, + out LinearUnit? linearUnit); + + if (!string.Equals(coordinateSystemType, "vertical", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON coordinate system subtype '{coordinateSystemType}' is not supported for VerticalCRS."); + } + + if (coordinateSystemDimension != 1 || axisInfo.Count != 1) + { + throw new NotSupportedException("PROJJSON VerticalCRS dimensions other than 1 are not supported."); + } + + if (linearUnit is null) + { + ArgumentGuard.ThrowArgument("PROJJSON VerticalCRS is missing axis linear units.", nameof(element)); + } + + verticalDatum = ApplyVerticalDatumTypeForAxis(verticalDatum, axisInfo[0]); + ReadIdentifier(element, out string authority, out long authorityCode); + return new VerticalCoordinateSystem( + linearUnit, + verticalDatum, + axisInfo[0], + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static CompoundCoordinateSystem ReadCompoundCoordinateSystem(JsonElement element) + { + string name = GetRequiredString(element, "name"); + JsonElement componentsElement = GetRequiredProperty(element, "components"); + if (componentsElement.ValueKind != JsonValueKind.Array) + { + ArgumentGuard.ThrowArgument("PROJJSON CompoundCRS components must be an array.", nameof(element)); + } + + var components = new List(); + foreach (JsonElement componentElement in componentsElement.EnumerateArray()) + { + if (ReadInfo(componentElement) is not CoordinateSystem coordinateSystem) + { + throw new NotSupportedException("PROJJSON CompoundCRS components must be coordinate reference systems."); + } + + components.Add(coordinateSystem); + } + + if (components.Count < 2) + { + ArgumentGuard.ThrowArgument("PROJJSON CompoundCRS must contain at least two components.", nameof(element)); + } + + ReadIdentifier(element, out string authority, out long authorityCode); + CoordinateSystem head = components[0]; + CoordinateSystem tail = components[1]; + var combined = new CompoundCoordinateSystem(head, tail, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + for (int i = 2; i < components.Count; i++) + { + combined = new CompoundCoordinateSystem(combined, components[i], name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + return combined; + } + + private static BoundTransformation ReadBoundTransformation(JsonElement element, CoordinateSystem sourceCoordinateSystem) + { + if (element.TryGetProperty("source_crs", out JsonElement transformationSourceCrsElement)) + { + CoordinateSystem transformationSourceCoordinateSystem = ReadCoordinateSystemElement(transformationSourceCrsElement, "transformation.source_crs"); + if (!transformationSourceCoordinateSystem.EqualParams(sourceCoordinateSystem)) + { + throw new NotSupportedException("PROJJSON BoundCRS transformations with an overriding source_crs are not supported."); + } + } + + string methodName = GetRequiredString(GetRequiredProperty(element, "method"), "name"); + JsonElement parametersElement = GetRequiredProperty(element, "parameters"); + if (parametersElement.ValueKind != JsonValueKind.Array) + { + ArgumentGuard.ThrowArgument("PROJJSON BoundCRS transformation parameters must be an array.", nameof(element)); + } + + var parameters = new Wgs84ConversionInfo(); + bool hasNumericParameters = false; + string? parameterFileName = null; + + foreach (JsonElement parameterElement in parametersElement.EnumerateArray()) + { + string parameterName = GetRequiredString(parameterElement, "name"); + JsonElement valueElement = GetRequiredProperty(parameterElement, "value"); + + if (valueElement.ValueKind == JsonValueKind.Number) + { + BoundCoordinateSystemSupport.AssignTransformationParameter(parameterName, valueElement.GetDouble(), parameters); + hasNumericParameters = true; + continue; + } + + if (valueElement.ValueKind != JsonValueKind.String) + { + ArgumentGuard.ThrowArgument("PROJJSON BoundCRS transformation parameter values must be numbers or strings.", nameof(element)); + } + + string candidateParameterFileName = valueElement.GetString() ?? string.Empty; + if (string.IsNullOrWhiteSpace(candidateParameterFileName)) + { + ArgumentGuard.ThrowArgument("PROJJSON BoundCRS transformation parameter file references must be non-empty.", nameof(element)); + } + + if (parameterFileName is not null + && !BoundCoordinateSystemSupport.AreEquivalentParameterFileReferences(parameterFileName, candidateParameterFileName)) + { + throw new NotSupportedException("PROJJSON BoundCRS transformations with multiple parameter files are not supported."); + } + + parameterFileName = candidateParameterFileName; + } + + return BoundCoordinateSystemSupport.CreateBoundTransformation( + methodName, + hasNumericParameters ? parameters : null, + parameterFileName); + } + + private static Projection ReadConversion(JsonElement element) + { + string name = GetRequiredString(element, "name"); + string className = GetRequiredString(GetRequiredProperty(element, "method"), "name"); + + var parameters = new List(); + if (element.TryGetProperty("parameters", out JsonElement parametersElement)) + { + if (parametersElement.ValueKind != JsonValueKind.Array) + { + ArgumentGuard.ThrowArgument("PROJJSON conversion parameters must be an array.", nameof(element)); + } + + foreach (JsonElement parameterElement in parametersElement.EnumerateArray()) + { + string parameterName = NormalizeProjectionParameterName(GetRequiredString(parameterElement, "name")); + double value = GetRequiredDouble(parameterElement, "value"); + parameters.Add(new ProjectionParameter(parameterName, value)); + } + } + + ReadIdentifier(element, out string authority, out long authorityCode); + return new Projection(className, parameters, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static CoordinateSystem ReadCoordinateSystemElement(JsonElement element, string propertyName) + { + if (ReadInfo(element) is not CoordinateSystem coordinateSystem) + { + throw new NotSupportedException($"PROJJSON {propertyName} must be a coordinate reference system."); + } + + return coordinateSystem; + } + + private static FittedCoordinateSystem CreateDerivedCoordinateSystem( + string name, + CoordinateSystem baseCoordinateSystem, + AffineTransform transform, + List axisInfo, + string authority, + long authorityCode) + { + var fittedCoordinateSystem = new FittedCoordinateSystem( + baseCoordinateSystem, + transform, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty, + new List(axisInfo)); + return fittedCoordinateSystem; + } + + private static void ReadCoordinateSystemDefinition( + JsonElement element, + out string coordinateSystemType, + out int coordinateSystemDimension, + out List axisInfo, + out AngularUnit? angularUnit, + out LinearUnit? linearUnit) + { + coordinateSystemType = GetRequiredString(element, "subtype"); + angularUnit = null; + linearUnit = null; + axisInfo = []; + + JsonElement axisArray = GetRequiredProperty(element, "axis"); + if (axisArray.ValueKind != JsonValueKind.Array) + { + ArgumentGuard.ThrowArgument("PROJJSON coordinate system axis definition must be an array.", nameof(element)); + } + + foreach (JsonElement axisElement in axisArray.EnumerateArray()) + { + axisInfo.Add(ReadAxis(axisElement, out AngularUnit? axisAngularUnit, out LinearUnit? axisLinearUnit)); + angularUnit = MergeAngularUnit(angularUnit, axisAngularUnit); + linearUnit = MergeLinearUnit(linearUnit, axisLinearUnit); + } + + coordinateSystemDimension = axisInfo.Count; + } + + private static AxisInfo ReadAxis(JsonElement element, out AngularUnit? angularUnit, out LinearUnit? linearUnit) + { + string axisName = GetRequiredString(element, "name"); + AxisOrientationEnum orientation = ParseAxisOrientation(GetRequiredString(element, "direction")); + angularUnit = null; + linearUnit = null; + + if (element.TryGetProperty("unit", out JsonElement unitElement)) + { + if (IsAngularUnit(unitElement)) + { + angularUnit = ReadAngularUnit(unitElement); + } + else if (IsLinearUnit(unitElement)) + { + linearUnit = ReadLinearUnit(unitElement); + } + } + + return new AxisInfo(axisName, orientation); + } + + private static HorizontalDatum ReadHorizontalDatumOrEnsemble(JsonElement element) + { + bool hasDatum = element.TryGetProperty("datum", out JsonElement datumElement); + bool hasDatumEnsemble = element.TryGetProperty("datum_ensemble", out JsonElement datumEnsembleElement); + if (hasDatum == hasDatumEnsemble) + { + ArgumentGuard.ThrowArgument("PROJJSON geodetic CRS must contain exactly one of datum or datum_ensemble.", nameof(element)); + } + + return hasDatum + ? ReadHorizontalDatum(datumElement) + : ReadHorizontalDatumEnsemble(datumEnsembleElement); + } + + private static VerticalDatum ReadVerticalDatumOrEnsemble(JsonElement element) + { + bool hasDatum = element.TryGetProperty("datum", out JsonElement datumElement); + bool hasDatumEnsemble = element.TryGetProperty("datum_ensemble", out JsonElement datumEnsembleElement); + if (hasDatum == hasDatumEnsemble) + { + ArgumentGuard.ThrowArgument("PROJJSON vertical CRS must contain exactly one of datum or datum_ensemble.", nameof(element)); + } + + return hasDatum + ? ReadVerticalDatum(datumElement) + : ReadVerticalDatumEnsemble(datumEnsembleElement); + } + + private static HorizontalDatum ReadHorizontalDatum(JsonElement element) + { + string datumType = GetOptionalString(element, "type") ?? "GeodeticReferenceFrame"; + if (!string.Equals(datumType, "GeodeticReferenceFrame", StringComparison.OrdinalIgnoreCase) + && !string.Equals(datumType, "DynamicGeodeticReferenceFrame", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON datum type '{datumType}' is not supported for geodetic CRS."); + } + + string name = GetRequiredString(element, "name"); + Ellipsoid ellipsoid = ReadEllipsoid(GetRequiredProperty(element, "ellipsoid")); + ReadIdentifier(element, out string authority, out long authorityCode); + return new HorizontalDatum(ellipsoid, null, DatumType.HD_Geocentric, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static HorizontalDatum ReadHorizontalDatumEnsemble(JsonElement element) + { + DatumEnsemble ensemble = ReadDatumEnsemble(element, requireEllipsoid: true, "geodetic CRS"); + Ellipsoid ellipsoid = ArgumentGuard.ThrowIfNull(ensemble.Ellipsoid, nameof(ensemble)); + return new HorizontalDatum(ellipsoid, null, DatumType.HD_Geocentric, ensemble.Name, ensemble.Authority, ensemble.AuthorityCode, string.Empty, string.Empty, string.Empty, ensemble); + } + + private static VerticalDatum ReadVerticalDatum(JsonElement element) + { + string datumType = GetOptionalString(element, "type") ?? "VerticalReferenceFrame"; + if (!string.Equals(datumType, "VerticalReferenceFrame", StringComparison.OrdinalIgnoreCase) + && !string.Equals(datumType, "DynamicVerticalReferenceFrame", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON datum type '{datumType}' is not supported for vertical CRS."); + } + + string name = GetRequiredString(element, "name"); + ReadIdentifier(element, out string authority, out long authorityCode); + return new VerticalDatum(DatumType.VD_GeoidModelDerived, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static VerticalDatum ReadVerticalDatumEnsemble(JsonElement element) + { + DatumEnsemble ensemble = ReadDatumEnsemble(element, requireEllipsoid: false, "vertical CRS"); + return new VerticalDatum(DatumType.VD_GeoidModelDerived, ensemble.Name, ensemble.Authority, ensemble.AuthorityCode, string.Empty, string.Empty, string.Empty, ensemble); + } + + private static VerticalDatum ApplyVerticalDatumTypeForAxis(VerticalDatum verticalDatum, AxisInfo axisInfo) + { + axisInfo = ArgumentGuard.ThrowIfNull(axisInfo, nameof(axisInfo)); + + DatumType datumType = axisInfo.Orientation == AxisOrientationEnum.Down + ? DatumType.VD_Depth + : DatumType.VD_GeoidModelDerived; + if (verticalDatum.DatumType == datumType) + { + return verticalDatum; + } + + return new VerticalDatum( + datumType, + verticalDatum.Name, + verticalDatum.Authority, + verticalDatum.AuthorityCode, + verticalDatum.Alias, + verticalDatum.Remarks, + verticalDatum.Abbreviation, + verticalDatum.Ensemble); + } + + private static DatumEnsemble ReadDatumEnsemble(JsonElement element, bool requireEllipsoid, string context) + { + string datumType = GetOptionalString(element, "type") ?? "DatumEnsemble"; + if (!string.Equals(datumType, "DatumEnsemble", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON datum ensemble type '{datumType}' is not supported for {context}."); + } + + string name = GetRequiredString(element, "name"); + JsonElement membersElement = GetRequiredProperty(element, "members"); + if (membersElement.ValueKind != JsonValueKind.Array) + { + ArgumentGuard.ThrowArgument("PROJJSON datum_ensemble members must be an array.", nameof(element)); + } + + var members = new List(); + foreach (JsonElement memberElement in membersElement.EnumerateArray()) + { + members.Add(ReadDatumEnsembleMember(memberElement)); + } + + Ellipsoid? ellipsoid = element.TryGetProperty("ellipsoid", out JsonElement ellipsoidElement) + ? ReadEllipsoid(ellipsoidElement) + : null; + if (requireEllipsoid && ellipsoid is null) + { + ArgumentGuard.ThrowArgument("PROJJSON datum_ensemble for geodetic CRS is missing an ellipsoid.", nameof(element)); + } + + string accuracyToken = GetRequiredString(element, "accuracy"); + if (!double.TryParse(accuracyToken, NumberStyles.Any, CultureInfo.InvariantCulture, out double accuracy)) + { + ArgumentGuard.ThrowArgument($"Invalid PROJJSON datum_ensemble accuracy '{accuracyToken}'.", nameof(element)); + } + + ArgumentGuard.ThrowIfNotFinite(accuracy, nameof(element), "PROJJSON datum_ensemble accuracy must be finite."); + ReadIdentifier(element, out string authority, out long authorityCode); + return new DatumEnsemble(name, members, accuracy, ellipsoid, authority, authorityCode); + } + + private static DatumEnsembleMember ReadDatumEnsembleMember(JsonElement element) + { + string name = GetRequiredString(element, "name"); + ReadIdentifier(element, out string authority, out long authorityCode); + return new DatumEnsembleMember(name, authority, authorityCode); + } + + private static Ellipsoid ReadEllipsoid(JsonElement element) + { + string name = GetRequiredString(element, "name"); + double semiMajorAxis = GetRequiredDouble(element, "semi_major_axis"); + bool hasInverseFlattening = element.TryGetProperty("inverse_flattening", out JsonElement inverseFlatteningElement); + bool hasSemiMinorAxis = element.TryGetProperty("semi_minor_axis", out JsonElement semiMinorAxisElement); + if (!hasInverseFlattening && !hasSemiMinorAxis) + { + ArgumentGuard.ThrowArgument("PROJJSON ellipsoid requires either inverse_flattening or semi_minor_axis.", nameof(element)); + } + + LinearUnit axisUnit = element.TryGetProperty("unit", out JsonElement unitElement) + ? ReadLinearUnit(unitElement) + : LinearUnit.Metre; + double inverseFlattening = hasInverseFlattening ? GetDouble(inverseFlatteningElement) : 0d; + double semiMinorAxis = hasSemiMinorAxis ? GetDouble(semiMinorAxisElement) : 0d; + bool isIvfDefinitive = hasInverseFlattening; + + ReadIdentifier(element, out string authority, out long authorityCode); + return new Ellipsoid( + semiMajorAxis, + semiMinorAxis, + inverseFlattening, + isIvfDefinitive, + axisUnit, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static PrimeMeridian ReadPrimeMeridian(JsonElement element) + { + string name = GetRequiredString(element, "name"); + double longitude = GetRequiredDouble(element, "longitude"); + AngularUnit angularUnit = element.TryGetProperty("unit", out JsonElement unitElement) + ? ReadAngularUnit(unitElement) + : AngularUnit.Degrees; + + ReadIdentifier(element, out string authority, out long authorityCode); + return new PrimeMeridian(longitude, angularUnit, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static AngularUnit ReadAngularUnit(JsonElement element) + { + if (element.ValueKind == JsonValueKind.String) + { + string unitName = element.GetString() ?? string.Empty; + return unitName.ToUpperInvariant() switch + { + "DEGREE" => AngularUnit.Degrees, + _ => throw new NotSupportedException($"PROJJSON angular unit '{unitName}' is not supported."), + }; + } + + string type = GetOptionalString(element, "type") ?? "AngularUnit"; + if (!string.Equals(type, "AngularUnit", StringComparison.OrdinalIgnoreCase) + && !string.Equals(type, "Unit", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON angular unit type '{type}' is not supported."); + } + + string name = GetRequiredString(element, "name"); + double radiansPerUnit = GetRequiredDouble(element, "conversion_factor"); + ReadIdentifier(element, out string authority, out long authorityCode); + return new AngularUnit(radiansPerUnit, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static LinearUnit ReadLinearUnit(JsonElement element) + { + if (element.ValueKind == JsonValueKind.String) + { + string unitName = element.GetString() ?? string.Empty; + return unitName.ToUpperInvariant() switch + { + "METRE" or "METER" => LinearUnit.Metre, + _ => throw new NotSupportedException($"PROJJSON linear unit '{unitName}' is not supported."), + }; + } + + string type = GetOptionalString(element, "type") ?? "LinearUnit"; + if (!string.Equals(type, "LinearUnit", StringComparison.OrdinalIgnoreCase) + && !string.Equals(type, "Unit", StringComparison.OrdinalIgnoreCase)) + { + throw new NotSupportedException($"PROJJSON linear unit type '{type}' is not supported."); + } + + string name = GetRequiredString(element, "name"); + double metersPerUnit = GetRequiredDouble(element, "conversion_factor"); + ReadIdentifier(element, out string authority, out long authorityCode); + return new LinearUnit(metersPerUnit, name, authority, authorityCode, string.Empty, string.Empty, string.Empty); + } + + private static void ReadIdentifier(JsonElement element, out string authority, out long authorityCode) + { + authority = string.Empty; + authorityCode = -1; + + if (element.TryGetProperty("id", out JsonElement idElement)) + { + ReadSingleIdentifier(idElement, out authority, out authorityCode); + return; + } + + if (!element.TryGetProperty("ids", out JsonElement idsElement) || idsElement.ValueKind != JsonValueKind.Array) + { + return; + } + + JsonElement? selectedIdentifier = null; + foreach (JsonElement candidate in idsElement.EnumerateArray()) + { + selectedIdentifier ??= candidate; + string? candidateAuthority = GetOptionalString(candidate, "authority"); + if (string.Equals(candidateAuthority, "EPSG", StringComparison.OrdinalIgnoreCase)) + { + selectedIdentifier = candidate; + break; + } + } + + if (selectedIdentifier.HasValue) + { + ReadSingleIdentifier(selectedIdentifier.Value, out authority, out authorityCode); + } + } + + private static void ReadSingleIdentifier(JsonElement element, out string authority, out long authorityCode) + { + authority = GetRequiredString(element, "authority"); + JsonElement codeElement = GetRequiredProperty(element, "code"); + authorityCode = codeElement.ValueKind switch + { + JsonValueKind.Number => codeElement.GetInt64(), + JsonValueKind.String => long.TryParse(codeElement.GetString(), NumberStyles.Any, CultureInfo.InvariantCulture, out long parsedCode) + ? parsedCode + : -1, + _ => -1, + }; + } + + private static string NormalizeProjectionParameterName(string parameterName) => ProjectionParameterNameNormalizer.Normalize(parameterName); + + private static AxisOrientationEnum ParseAxisOrientation(string orientationToken) + { + return orientationToken.ToUpperInvariant() switch + { + "NORTH" => AxisOrientationEnum.North, + "SOUTH" => AxisOrientationEnum.South, + "EAST" => AxisOrientationEnum.East, + "WEST" => AxisOrientationEnum.West, + "UP" => AxisOrientationEnum.Up, + "DOWN" => AxisOrientationEnum.Down, + "GEOCENTRICX" => AxisOrientationEnum.Other, + "GEOCENTRICY" => AxisOrientationEnum.East, + "GEOCENTRICZ" => AxisOrientationEnum.North, + _ => ArgumentGuard.ThrowArgument($"Invalid PROJJSON axis orientation '{orientationToken}'.", nameof(orientationToken)), + }; + } + + private static AngularUnit? MergeAngularUnit(AngularUnit? current, AngularUnit? candidate) + { + if (current is null) + { + return candidate; + } + + if (candidate is null) + { + return current; + } + + if (!current.EqualParams(candidate)) + { + ArgumentGuard.ThrowArgument("PROJJSON axis angular units must match.", nameof(candidate)); + } + + return current; + } + + private static LinearUnit? MergeLinearUnit(LinearUnit? current, LinearUnit? candidate) + { + if (current is null) + { + return candidate; + } + + if (candidate is null) + { + return current; + } + + if (!current.EqualParams(candidate)) + { + ArgumentGuard.ThrowArgument("PROJJSON axis linear units must match.", nameof(candidate)); + } + + return current; + } + + private static bool IsAngularUnit(JsonElement element) + { + if (element.ValueKind == JsonValueKind.String) + { + return string.Equals(element.GetString(), "degree", StringComparison.OrdinalIgnoreCase); + } + + string type = GetOptionalString(element, "type") ?? string.Empty; + return string.Equals(type, "AngularUnit", StringComparison.OrdinalIgnoreCase) + || string.Equals(type, "Unit", StringComparison.OrdinalIgnoreCase); + } + + private static bool IsLinearUnit(JsonElement element) + { + if (element.ValueKind == JsonValueKind.String) + { + return string.Equals(element.GetString(), "metre", StringComparison.OrdinalIgnoreCase) + || string.Equals(element.GetString(), "meter", StringComparison.OrdinalIgnoreCase); + } + + string type = GetOptionalString(element, "type") ?? string.Empty; + return string.Equals(type, "LinearUnit", StringComparison.OrdinalIgnoreCase) + || string.Equals(type, "Unit", StringComparison.OrdinalIgnoreCase); + } + + private static string GetRequiredString(JsonElement element, string propertyName) + { + JsonElement property = GetRequiredProperty(element, propertyName); + if (property.ValueKind != JsonValueKind.String) + { + ArgumentGuard.ThrowArgument($"PROJJSON property '{propertyName}' must be a string.", nameof(element)); + } + + return property.GetString() ?? string.Empty; + } + + private static string? GetOptionalString(JsonElement element, string propertyName) + { + if (!element.TryGetProperty(propertyName, out JsonElement property)) + { + return null; + } + + return property.ValueKind == JsonValueKind.String + ? property.GetString() + : null; + } + + private static double GetRequiredDouble(JsonElement element, string propertyName) + { + return GetDouble(GetRequiredProperty(element, propertyName)); + } + + private static double GetDouble(JsonElement element) + { + if (element.ValueKind != JsonValueKind.Number) + { + ArgumentGuard.ThrowArgument("PROJJSON numeric property must be a number.", nameof(element)); + } + + return element.GetDouble(); + } + + private static JsonElement GetRequiredProperty(JsonElement element, string propertyName) + { + if (!element.TryGetProperty(propertyName, out JsonElement property)) + { + ArgumentGuard.ThrowArgument($"PROJJSON property '{propertyName}' is required.", nameof(element)); + } + + return property; + } +} diff --git a/src/ProjNet/IO/CoordinateSystems/ProjJsonWriter.cs b/src/ProjNet/IO/CoordinateSystems/ProjJsonWriter.cs new file mode 100644 index 00000000..b496665e --- /dev/null +++ b/src/ProjNet/IO/CoordinateSystems/ProjJsonWriter.cs @@ -0,0 +1,758 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.CoordinateSystems; + +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.Json; +using ProjNet; +using ProjNet.CoordinateSystems; + +/// +/// Writes coordinate systems as PROJJSON. +/// +public static class ProjJsonWriter +{ + private static readonly AngularUnit ArcSecondUnit = new(4.84813681109535993589914102357e-6d, "arc-second", "EPSG", 9104, "arcsec", string.Empty, "=pi/648000 radians."); + + /// + /// Writes a coordinate system to an existing . + /// + /// The JSON writer to write to. + /// The coordinate system to serialize. + public static void WriteTo(Utf8JsonWriter writer, CoordinateSystem coordinateSystem) + { + ArgumentGuard.ThrowIfNull(writer, nameof(writer)); + ArgumentGuard.ThrowIfNull(coordinateSystem, nameof(coordinateSystem)); + + WriteCoordinateSystem(writer, coordinateSystem); + } + + /// + /// Serializes a coordinate system to PROJJSON text. + /// + /// The coordinate system to serialize. + /// The serialized PROJJSON text. + public static string ToJson(CoordinateSystem coordinateSystem) + { + ArgumentGuard.ThrowIfNull(coordinateSystem, nameof(coordinateSystem)); + + using var stream = new MemoryStream(); + using (var writer = new Utf8JsonWriter(stream)) + { + WriteCoordinateSystem(writer, coordinateSystem); + writer.Flush(); + } + + return Encoding.UTF8.GetString(stream.ToArray()); + } + + private static void WriteCoordinateSystem(Utf8JsonWriter writer, CoordinateSystem coordinateSystem) + { + switch (coordinateSystem) + { + case BoundCoordinateSystem boundCoordinateSystem: + WriteBoundCoordinateSystem(writer, boundCoordinateSystem); + break; + case GeographicCoordinateSystem geographicCoordinateSystem: + WriteGeographicCoordinateSystem(writer, geographicCoordinateSystem); + break; + case GeocentricCoordinateSystem geocentricCoordinateSystem: + WriteGeocentricCoordinateSystem(writer, geocentricCoordinateSystem); + break; + case ProjectedCoordinateSystem projectedCoordinateSystem: + WriteProjectedCoordinateSystem(writer, projectedCoordinateSystem); + break; + case VerticalCoordinateSystem verticalCoordinateSystem: + WriteVerticalCoordinateSystem(writer, verticalCoordinateSystem); + break; + case CompoundCoordinateSystem compoundCoordinateSystem: + WriteCompoundCoordinateSystem(writer, compoundCoordinateSystem); + break; + case FittedCoordinateSystem fittedCoordinateSystem: + WriteDerivedCoordinateSystem(writer, fittedCoordinateSystem); + break; + default: + throw new NotSupportedException($"PROJJSON writing is not supported for coordinate system type '{coordinateSystem.GetType().Name}'."); + } + } + + private static void WriteGeographicCoordinateSystem(Utf8JsonWriter writer, GeographicCoordinateSystem coordinateSystem) + { + if (TryWriteLegacyBoundCoordinateSystem(writer, coordinateSystem)) + { + return; + } + + writer.WriteStartObject(); + writer.WriteString("type", "GeographicCRS"); + writer.WriteString("name", coordinateSystem.Name); + + WriteHorizontalDatumProperty(writer, coordinateSystem.HorizontalDatum); + + writer.WritePropertyName("prime_meridian"); + WritePrimeMeridian(writer, coordinateSystem.PrimeMeridian); + + writer.WritePropertyName("coordinate_system"); + WriteCoordinateSystemDefinition(writer, "ellipsoidal", coordinateSystem); + + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + } + + private static void WriteGeocentricCoordinateSystem(Utf8JsonWriter writer, GeocentricCoordinateSystem coordinateSystem) + { + if (TryWriteLegacyBoundCoordinateSystem(writer, coordinateSystem)) + { + return; + } + + writer.WriteStartObject(); + writer.WriteString("type", "GeodeticCRS"); + writer.WriteString("name", coordinateSystem.Name); + + WriteHorizontalDatumProperty(writer, coordinateSystem.HorizontalDatum); + + writer.WritePropertyName("prime_meridian"); + WritePrimeMeridian(writer, coordinateSystem.PrimeMeridian); + + writer.WritePropertyName("coordinate_system"); + WriteCoordinateSystemDefinition(writer, "Cartesian", coordinateSystem); + + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + } + + private static void WriteProjectedCoordinateSystem(Utf8JsonWriter writer, ProjectedCoordinateSystem coordinateSystem) + { + if (TryWriteLegacyBoundCoordinateSystem(writer, coordinateSystem)) + { + return; + } + + writer.WriteStartObject(); + writer.WriteString("type", "ProjectedCRS"); + writer.WriteString("name", coordinateSystem.Name); + + writer.WritePropertyName("base_crs"); + WriteGeographicCoordinateSystem(writer, coordinateSystem.GeographicCoordinateSystem); + + writer.WritePropertyName("conversion"); + WriteConversion( + writer, + coordinateSystem.Projection, + coordinateSystem.GeographicCoordinateSystem.AngularUnit, + coordinateSystem.LinearUnit); + + writer.WritePropertyName("coordinate_system"); + WriteCoordinateSystemDefinition(writer, "Cartesian", coordinateSystem); + + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + } + + private static void WriteDerivedCoordinateSystem(Utf8JsonWriter writer, FittedCoordinateSystem coordinateSystem) + { + Projection derivingConversion = DerivedCoordinateSystemSupport.CreateAffineConversion( + coordinateSystem.ToBaseTransform, + DerivedCoordinateSystemSupport.DefaultDerivingConversionName); + + switch (coordinateSystem.BaseCoordinateSystem) + { + case GeographicCoordinateSystem geographicCoordinateSystem: + if (BoundCoordinateSystemSupport.CreateLegacyBoundCoordinateSystemForSerialization(geographicCoordinateSystem) is not null) + { + throw new NotSupportedException("PROJJSON derived geographic CRS output does not support base CRS definitions that expand to BoundCRS."); + } + + writer.WriteStartObject(); + writer.WriteString("type", "DerivedGeographicCRS"); + writer.WriteString("name", coordinateSystem.Name); + writer.WritePropertyName("base_crs"); + WriteGeographicCoordinateSystem(writer, geographicCoordinateSystem); + writer.WritePropertyName("conversion"); + WriteDerivedAffineConversion(writer, derivingConversion, geographicCoordinateSystem.AngularUnit, null); + writer.WritePropertyName("coordinate_system"); + WriteCoordinateSystemDefinition(writer, "ellipsoidal", coordinateSystem); + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + break; + case ProjectedCoordinateSystem projectedCoordinateSystem: + if (BoundCoordinateSystemSupport.CreateLegacyBoundCoordinateSystemForSerialization(projectedCoordinateSystem) is not null) + { + throw new NotSupportedException("PROJJSON derived projected CRS output does not support base CRS definitions that expand to BoundCRS."); + } + + writer.WriteStartObject(); + writer.WriteString("type", "DerivedProjectedCRS"); + writer.WriteString("name", coordinateSystem.Name); + writer.WritePropertyName("base_crs"); + WriteProjectedCoordinateSystem(writer, projectedCoordinateSystem); + writer.WritePropertyName("conversion"); + WriteDerivedAffineConversion(writer, derivingConversion, null, projectedCoordinateSystem.LinearUnit); + writer.WritePropertyName("coordinate_system"); + WriteCoordinateSystemDefinition(writer, "Cartesian", coordinateSystem); + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + break; + default: + throw new NotSupportedException("PROJJSON derived CRS writing currently supports only affine transforms based on two-dimensional geographic or projected coordinate systems."); + } + } + + private static void WriteVerticalCoordinateSystem(Utf8JsonWriter writer, VerticalCoordinateSystem coordinateSystem) + { + if (TryWriteLegacyBoundCoordinateSystem(writer, coordinateSystem)) + { + return; + } + + writer.WriteStartObject(); + writer.WriteString("type", "VerticalCRS"); + writer.WriteString("name", coordinateSystem.Name); + + WriteVerticalDatumProperty(writer, coordinateSystem.VerticalDatum); + + writer.WritePropertyName("coordinate_system"); + WriteCoordinateSystemDefinition(writer, "vertical", coordinateSystem); + + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + } + + private static bool TryWriteLegacyBoundCoordinateSystem(Utf8JsonWriter writer, CoordinateSystem coordinateSystem) + { + BoundCoordinateSystem? boundCoordinateSystem = BoundCoordinateSystemSupport.CreateLegacyBoundCoordinateSystemForSerialization(coordinateSystem); + if (boundCoordinateSystem is null) + { + return false; + } + + WriteBoundCoordinateSystem(writer, boundCoordinateSystem); + return true; + } + + private static void WriteBoundCoordinateSystem(Utf8JsonWriter writer, BoundCoordinateSystem coordinateSystem) + { + writer.WriteStartObject(); + writer.WriteString("type", "BoundCRS"); + writer.WriteString("name", coordinateSystem.Name); + + writer.WritePropertyName("source_crs"); + WriteBoundCoordinateSystemComponent(writer, coordinateSystem.SourceCoordinateSystem); + + writer.WritePropertyName("target_crs"); + WriteBoundCoordinateSystemComponent(writer, coordinateSystem.TargetCoordinateSystem); + + writer.WritePropertyName("transformation"); + WriteBoundTransformation(writer, coordinateSystem.SourceCoordinateSystem, coordinateSystem.TargetCoordinateSystem, coordinateSystem.Transformation); + + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + } + + private static void WriteBoundCoordinateSystemComponent(Utf8JsonWriter writer, CoordinateSystem coordinateSystem) + { + if (coordinateSystem is BoundCoordinateSystem boundCoordinateSystem) + { + WriteBoundCoordinateSystem(writer, boundCoordinateSystem); + return; + } + + WriteCoordinateSystem(writer, BoundCoordinateSystemSupport.CreateCoordinateSystemWithoutLegacyBoundMetadata(coordinateSystem)); + } + + private static void WriteBoundTransformation( + Utf8JsonWriter writer, + CoordinateSystem sourceCoordinateSystem, + CoordinateSystem targetCoordinateSystem, + BoundTransformation transformation) + { + writer.WriteStartObject(); + writer.WriteString("type", "AbridgedTransformation"); + writer.WriteString("name", $"{sourceCoordinateSystem.Name} to {targetCoordinateSystem.Name}"); + + writer.WritePropertyName("method"); + WriteMethod(writer, transformation.MethodName); + + writer.WritePropertyName("parameters"); + writer.WriteStartArray(); + if (transformation.UsesParameterFile) + { + WriteBoundFileParameter( + writer, + "Geoid (height correction) model file", + ArgumentGuard.ThrowIfNull(transformation.ParameterFileName, nameof(transformation.ParameterFileName))); + } + else if (transformation.Wgs84Parameters is not null) + { + WriteBoundTransformationParameters(writer, transformation.MethodName, transformation.Wgs84Parameters); + } + else + { + throw new NotSupportedException("BoundCRS transformations must define either numeric parameters or a parameter file."); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + private static void WriteBoundTransformationParameters(Utf8JsonWriter writer, string methodName, Wgs84ConversionInfo parameters) + { + if (IsGeocentricTranslationsMethod(methodName)) + { + WriteBoundLinearParameter(writer, "X-axis translation", parameters.Dx); + WriteBoundLinearParameter(writer, "Y-axis translation", parameters.Dy); + WriteBoundLinearParameter(writer, "Z-axis translation", parameters.Dz); + return; + } + + if (IsPositionVectorMethod(methodName) || IsCoordinateFrameRotationMethod(methodName)) + { + WriteBoundLinearParameter(writer, "X-axis translation", parameters.Dx); + WriteBoundLinearParameter(writer, "Y-axis translation", parameters.Dy); + WriteBoundLinearParameter(writer, "Z-axis translation", parameters.Dz); + WriteBoundAngularParameter(writer, "X-axis rotation", parameters.Ex); + WriteBoundAngularParameter(writer, "Y-axis rotation", parameters.Ey); + WriteBoundAngularParameter(writer, "Z-axis rotation", parameters.Ez); + WriteBoundScaleParameter(writer, "Scale difference", parameters.Ppm); + return; + } + + throw new NotSupportedException($"BoundCRS transformation method '{methodName}' is not supported."); + } + + private static void WriteBoundLinearParameter(Utf8JsonWriter writer, string name, double value) + { + writer.WriteStartObject(); + writer.WriteString("name", name); + writer.WriteNumber("value", value); + writer.WritePropertyName("unit"); + WriteLinearUnit(writer, LinearUnit.Metre); + writer.WriteEndObject(); + } + + private static void WriteBoundAngularParameter(Utf8JsonWriter writer, string name, double value) + { + writer.WriteStartObject(); + writer.WriteString("name", name); + writer.WriteNumber("value", value); + writer.WritePropertyName("unit"); + WriteAngularUnit(writer, ArcSecondUnit); + writer.WriteEndObject(); + } + + private static void WriteBoundScaleParameter(Utf8JsonWriter writer, string name, double value) + { + writer.WriteStartObject(); + writer.WriteString("name", name); + writer.WriteNumber("value", value); + writer.WritePropertyName("unit"); + WriteScaleUnit(writer, "parts per million", 1e-6d); + writer.WriteEndObject(); + } + + private static void WriteBoundFileParameter(Utf8JsonWriter writer, string name, string value) + { + writer.WriteStartObject(); + writer.WriteString("name", name); + writer.WriteString("value", value); + writer.WriteEndObject(); + } + + private static void WriteCompoundCoordinateSystem(Utf8JsonWriter writer, CompoundCoordinateSystem coordinateSystem) + { + writer.WriteStartObject(); + writer.WriteString("type", "CompoundCRS"); + writer.WriteString("name", coordinateSystem.Name); + + writer.WritePropertyName("components"); + writer.WriteStartArray(); + WriteCompoundComponents(writer, coordinateSystem); + writer.WriteEndArray(); + + WriteIdentifier(writer, coordinateSystem); + writer.WriteEndObject(); + } + + private static void WriteCoordinateSystemDefinition(Utf8JsonWriter writer, string subtype, CoordinateSystem coordinateSystem) + { + writer.WriteStartObject(); + writer.WriteString("subtype", subtype); + writer.WritePropertyName("axis"); + writer.WriteStartArray(); + for (int i = 0; i < coordinateSystem.Dimension; i++) + { + WriteAxis(writer, coordinateSystem, i, coordinateSystem.GetAxis(i), coordinateSystem.GetUnits(i)); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + private static void WriteAxis(Utf8JsonWriter writer, CoordinateSystem coordinateSystem, int axisIndex, AxisInfo axisInfo, IUnit unit) + { + writer.WriteStartObject(); + writer.WriteString("name", axisInfo.Name); + writer.WriteString("direction", GetAxisDirection(coordinateSystem, axisIndex, axisInfo.Orientation)); + writer.WritePropertyName("unit"); + WriteUnit(writer, unit); + writer.WriteEndObject(); + } + + private static void WriteHorizontalDatum(Utf8JsonWriter writer, HorizontalDatum horizontalDatum) + { + writer.WriteStartObject(); + writer.WriteString("type", "GeodeticReferenceFrame"); + writer.WriteString("name", horizontalDatum.Name); + writer.WritePropertyName("ellipsoid"); + WriteEllipsoid(writer, horizontalDatum.Ellipsoid); + WriteIdentifier(writer, horizontalDatum); + writer.WriteEndObject(); + } + + private static void WriteHorizontalDatumProperty(Utf8JsonWriter writer, HorizontalDatum horizontalDatum) + { + if (horizontalDatum.Ensemble is not null) + { + writer.WritePropertyName("datum_ensemble"); + WriteDatumEnsemble(writer, horizontalDatum.Ensemble); + return; + } + + writer.WritePropertyName("datum"); + WriteHorizontalDatum(writer, horizontalDatum); + } + + private static void WriteVerticalDatumProperty(Utf8JsonWriter writer, VerticalDatum verticalDatum) + { + if (verticalDatum.Ensemble is not null) + { + writer.WritePropertyName("datum_ensemble"); + WriteDatumEnsemble(writer, verticalDatum.Ensemble); + return; + } + + writer.WritePropertyName("datum"); + WriteVerticalDatum(writer, verticalDatum); + } + + private static void WriteDatumEnsemble(Utf8JsonWriter writer, DatumEnsemble datumEnsemble) + { + writer.WriteStartObject(); + writer.WriteString("type", "DatumEnsemble"); + writer.WriteString("name", datumEnsemble.Name); + + writer.WritePropertyName("members"); + writer.WriteStartArray(); + for (int i = 0; i < datumEnsemble.Members.Count; i++) + { + WriteDatumEnsembleMember(writer, datumEnsemble.Members[i]); + } + + writer.WriteEndArray(); + + if (datumEnsemble.Ellipsoid is not null) + { + writer.WritePropertyName("ellipsoid"); + WriteEllipsoid(writer, datumEnsemble.Ellipsoid); + } + + writer.WriteString("accuracy", datumEnsemble.Accuracy.ToString("G17", CultureInfo.InvariantCulture)); + WriteIdentifier(writer, datumEnsemble.Authority, datumEnsemble.AuthorityCode); + writer.WriteEndObject(); + } + + private static void WriteDatumEnsembleMember(Utf8JsonWriter writer, DatumEnsembleMember member) + { + writer.WriteStartObject(); + writer.WriteString("name", member.Name); + WriteIdentifier(writer, member.Authority, member.AuthorityCode); + writer.WriteEndObject(); + } + + private static void WriteEllipsoid(Utf8JsonWriter writer, Ellipsoid ellipsoid) + { + writer.WriteStartObject(); + writer.WriteString("type", "Ellipsoid"); + writer.WriteString("name", ellipsoid.Name); + writer.WriteNumber("semi_major_axis", ellipsoid.SemiMajorAxis); + if (ellipsoid.IsIvfDefinitive && !double.IsNaN(ellipsoid.InverseFlattening) && !double.IsInfinity(ellipsoid.InverseFlattening)) + { + writer.WriteNumber("inverse_flattening", ellipsoid.InverseFlattening); + } + else + { + writer.WriteNumber("semi_minor_axis", ellipsoid.SemiMinorAxis); + } + + writer.WritePropertyName("unit"); + WriteLinearUnit(writer, ellipsoid.AxisUnit); + WriteIdentifier(writer, ellipsoid); + writer.WriteEndObject(); + } + + private static void WritePrimeMeridian(Utf8JsonWriter writer, PrimeMeridian primeMeridian) + { + writer.WriteStartObject(); + writer.WriteString("name", primeMeridian.Name); + writer.WriteNumber("longitude", primeMeridian.Longitude); + writer.WritePropertyName("unit"); + WriteAngularUnit(writer, primeMeridian.AngularUnit); + WriteIdentifier(writer, primeMeridian); + writer.WriteEndObject(); + } + + private static void WriteVerticalDatum(Utf8JsonWriter writer, VerticalDatum verticalDatum) + { + writer.WriteStartObject(); + writer.WriteString("type", "VerticalReferenceFrame"); + writer.WriteString("name", verticalDatum.Name); + WriteIdentifier(writer, verticalDatum); + writer.WriteEndObject(); + } + + private static void WriteConversion(Utf8JsonWriter writer, IProjection projection, AngularUnit angularUnit, LinearUnit linearUnit) + { + string methodKey = ProjectionSerializationSupport.NormalizeMethodKey(projection.ClassName); + string methodName = ProjectionSerializationSupport.GetMethodName(projection.ClassName); + string conversionName = string.IsNullOrWhiteSpace(projection.Name) || projection.Name.Equals(projection.ClassName, StringComparison.OrdinalIgnoreCase) + ? methodName + : projection.Name; + + writer.WriteStartObject(); + writer.WriteString("type", "Conversion"); + writer.WriteString("name", conversionName); + + writer.WritePropertyName("method"); + WriteMethod(writer, methodName); + + writer.WritePropertyName("parameters"); + writer.WriteStartArray(); + for (int i = 0; i < projection.NumParameters; i++) + { + WriteProjectionParameter(writer, methodKey, projection.GetParameter(i), angularUnit, linearUnit); + } + + writer.WriteEndArray(); + WriteIdentifier(writer, projection); + writer.WriteEndObject(); + } + + private static void WriteDerivedAffineConversion(Utf8JsonWriter writer, Projection conversion, AngularUnit? angularUnit, LinearUnit? linearUnit) + { + writer.WriteStartObject(); + writer.WriteString("type", "Conversion"); + writer.WriteString( + "name", + string.IsNullOrWhiteSpace(conversion.Name) ? DerivedCoordinateSystemSupport.DefaultDerivingConversionName : conversion.Name); + + writer.WritePropertyName("method"); + WriteMethod(writer, conversion.ClassName); + + writer.WritePropertyName("parameters"); + writer.WriteStartArray(); + for (int i = 0; i < conversion.NumParameters; i++) + { + WriteDerivedAffineParameter(writer, conversion.GetParameter(i), angularUnit, linearUnit); + } + + writer.WriteEndArray(); + WriteIdentifier(writer, conversion); + writer.WriteEndObject(); + } + + private static void WriteMethod(Utf8JsonWriter writer, string methodName) + { + writer.WriteStartObject(); + writer.WriteString("name", methodName); + writer.WriteEndObject(); + } + + private static void WriteProjectionParameter(Utf8JsonWriter writer, string methodKey, ProjectionParameter parameter, AngularUnit angularUnit, LinearUnit linearUnit) + { + writer.WriteStartObject(); + writer.WriteString("name", ProjectionSerializationSupport.GetParameterName(methodKey, parameter.Name)); + writer.WriteNumber("value", parameter.Value); + + if (ProjectionSerializationSupport.ParameterUsesAngularUnit(parameter.Name)) + { + writer.WritePropertyName("unit"); + WriteAngularUnit(writer, angularUnit); + } + else if (ProjectionSerializationSupport.ParameterUsesLinearUnit(parameter.Name)) + { + writer.WritePropertyName("unit"); + WriteLinearUnit(writer, linearUnit); + } + else if (ProjectionSerializationSupport.ParameterUsesScaleUnit(parameter.Name)) + { + writer.WritePropertyName("unit"); + WriteScaleUnit(writer, "unity", 1d, "EPSG", 9201); + } + + writer.WriteEndObject(); + } + + private static void WriteDerivedAffineParameter(Utf8JsonWriter writer, ProjectionParameter parameter, AngularUnit? angularUnit, LinearUnit? linearUnit) + { + writer.WriteStartObject(); + writer.WriteString("name", parameter.Name); + writer.WriteNumber("value", parameter.Value); + + writer.WritePropertyName("unit"); + if (parameter.Name is "A0" or "B0") + { + if (angularUnit is not null) + { + WriteAngularUnit(writer, angularUnit); + } + else if (linearUnit is not null) + { + WriteLinearUnit(writer, linearUnit); + } + else + { + throw new NotSupportedException("Derived affine conversion parameters require either an angular or linear translation unit."); + } + } + else + { + WriteScaleUnit(writer, "unity", 1d, "EPSG", 9201); + } + + writer.WriteEndObject(); + } + + private static void WriteUnit(Utf8JsonWriter writer, IUnit unit) + { + switch (unit) + { + case AngularUnit angularUnit: + WriteAngularUnit(writer, angularUnit); + break; + case LinearUnit linearUnit: + WriteLinearUnit(writer, linearUnit); + break; + default: + throw new NotSupportedException($"PROJJSON writing is not supported for unit type '{unit.GetType().Name}'."); + } + } + + private static void WriteAngularUnit(Utf8JsonWriter writer, AngularUnit angularUnit) + { + writer.WriteStartObject(); + writer.WriteString("type", "AngularUnit"); + writer.WriteString("name", angularUnit.Name); + writer.WriteNumber("conversion_factor", angularUnit.RadiansPerUnit); + WriteIdentifier(writer, angularUnit); + writer.WriteEndObject(); + } + + private static void WriteLinearUnit(Utf8JsonWriter writer, LinearUnit linearUnit) + { + writer.WriteStartObject(); + writer.WriteString("type", "LinearUnit"); + writer.WriteString("name", linearUnit.Name); + writer.WriteNumber("conversion_factor", linearUnit.MetersPerUnit); + WriteIdentifier(writer, linearUnit); + writer.WriteEndObject(); + } + + private static void WriteScaleUnit(Utf8JsonWriter writer, string name, double conversionFactor, string? authority = null, long authorityCode = -1) + { + writer.WriteStartObject(); + writer.WriteString("type", "ScaleUnit"); + writer.WriteString("name", name); + writer.WriteNumber("conversion_factor", conversionFactor); + if (!string.IsNullOrWhiteSpace(authority) && authorityCode > 0) + { + writer.WritePropertyName("id"); + writer.WriteStartObject(); + writer.WriteString("authority", authority); + writer.WriteNumber("code", authorityCode); + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + } + + private static void WriteCompoundComponents(Utf8JsonWriter writer, CompoundCoordinateSystem coordinateSystem) + { + WriteCompoundComponent(writer, coordinateSystem.HeadCoordinateSystem); + WriteCompoundComponent(writer, coordinateSystem.TailCoordinateSystem); + } + + private static void WriteCompoundComponent(Utf8JsonWriter writer, CoordinateSystem coordinateSystem) + { + if (coordinateSystem is CompoundCoordinateSystem nested) + { + WriteCompoundComponents(writer, nested); + return; + } + + WriteCoordinateSystem(writer, coordinateSystem); + } + + private static bool IsGeocentricTranslationsMethod(string methodName) + { + return methodName.StartsWith("Geocentric translations", StringComparison.Ordinal); + } + + private static bool IsPositionVectorMethod(string methodName) + { + return methodName.StartsWith("Position Vector transformation", StringComparison.Ordinal); + } + + private static bool IsCoordinateFrameRotationMethod(string methodName) + { + return methodName.StartsWith("Coordinate Frame rotation", StringComparison.Ordinal); + } + + private static void WriteIdentifier(Utf8JsonWriter writer, IInfo info) + { + WriteIdentifier(writer, info.Authority, info.AuthorityCode); + } + + private static void WriteIdentifier(Utf8JsonWriter writer, string authority, long authorityCode) + { + if (string.IsNullOrWhiteSpace(authority) || authorityCode <= 0) + { + return; + } + + writer.WritePropertyName("id"); + writer.WriteStartObject(); + writer.WriteString("authority", authority); + writer.WriteNumber("code", authorityCode); + writer.WriteEndObject(); + } + + private static string GetAxisDirection(CoordinateSystem coordinateSystem, int axisIndex, AxisOrientationEnum orientation) + { + if (coordinateSystem is GeocentricCoordinateSystem) + { + return axisIndex switch + { + 0 => "geocentricX", + 1 => "geocentricY", + 2 => "geocentricZ", + _ => throw new NotSupportedException($"PROJJSON writing is not supported for geocentric axis index '{axisIndex}'."), + }; + } + + return orientation switch + { + AxisOrientationEnum.North => "north", + AxisOrientationEnum.South => "south", + AxisOrientationEnum.East => "east", + AxisOrientationEnum.West => "west", + AxisOrientationEnum.Up => "up", + AxisOrientationEnum.Down => "down", + _ => throw new NotSupportedException($"PROJJSON writing is not supported for axis orientation '{orientation}'."), + }; + } +} diff --git a/src/ProjNet/IO/CoordinateSystems/ProjectionParameterNameNormalizer.cs b/src/ProjNet/IO/CoordinateSystems/ProjectionParameterNameNormalizer.cs new file mode 100644 index 00000000..64d7a64f --- /dev/null +++ b/src/ProjNet/IO/CoordinateSystems/ProjectionParameterNameNormalizer.cs @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.CoordinateSystems; + +using System; + +/// +/// Normalizes WKT2 and PROJJSON projection parameter names to the internal parameter aliases. +/// +internal static class ProjectionParameterNameNormalizer +{ +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private const int StackallocThreshold = 128; +#endif + + /// + /// Normalizes a projection parameter name. + /// + /// The external parameter name to normalize. + /// The normalized parameter name. + internal static string Normalize(string parameterName) + { + string normalized = NormalizeLookupToken(parameterName); + + return normalized switch + { + "LONGITUDE_OF_NATURAL_ORIGIN" => "central_meridian", + "LONGITUDE_OF_FALSE_ORIGIN" => "central_meridian", + "LONGITUDE_OF_PROJECTION_CENTRE" => "central_meridian", + "LONGITUDE_OF_ORIGIN" => "central_meridian", + "LATITUDE_OF_NATURAL_ORIGIN" => "latitude_of_origin", + "LATITUDE_OF_FALSE_ORIGIN" => "latitude_of_origin", + "LATITUDE_OF_PROJECTION_CENTRE" => "latitude_of_origin", + "LATITUDE_OF_ORIGIN" => "latitude_of_origin", + "LATITUDE_OF_1ST_STANDARD_PARALLEL" => "standard_parallel_1", + "LATITUDE_OF_2ND_STANDARD_PARALLEL" => "standard_parallel_2", + "LATITUDE_OF_PSEUDO_STANDARD_PARALLEL" => "standard_parallel_1", + "EASTING_AT_FALSE_ORIGIN" => "false_easting", + "EASTING_AT_PROJECTION_CENTRE" => "false_easting", + "EASTING_AT_NATURAL_ORIGIN" => "false_easting", + "NORTHING_AT_FALSE_ORIGIN" => "false_northing", + "NORTHING_AT_PROJECTION_CENTRE" => "false_northing", + "NORTHING_AT_NATURAL_ORIGIN" => "false_northing", + "SCALE_FACTOR_AT_NATURAL_ORIGIN" => "scale_factor", + "SCALE_FACTOR_AT_PROJECTION_CENTRE" => "scale_factor", + "SCALE_FACTOR_ON_INITIAL_LINE" => "scale_factor", + "AZIMUTH_OF_INITIAL_LINE" => "azimuth", + "ANGLE_FROM_RECTIFIED_TO_SKEW_GRID" => "rectified_grid_angle", + _ => normalized, + }; + } + + /// + /// Normalizes a projection-related parameter name into the uppercase lookup token used by the readers. + /// + /// The external parameter name to normalize. + /// The normalized uppercase lookup token. + internal static string NormalizeLookupToken(string parameterName) + { + if (string.IsNullOrWhiteSpace(parameterName)) + { + return string.Empty; + } + + ReadOnlySpan trimmed = TrimWhitespace(parameterName.AsSpan()); +#if NET8_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span buffer = trimmed.Length <= StackallocThreshold + ? stackalloc char[StackallocThreshold] + : new char[trimmed.Length]; + int normalizedLength = NormalizeLookupTokenCore(trimmed, buffer); + return buffer.Slice(0, normalizedLength).ToString(); +#else + char[] buffer = new char[trimmed.Length]; + int normalizedLength = NormalizeLookupTokenCore(trimmed, buffer); + return new string(buffer, 0, normalizedLength); +#endif + } + + private static int NormalizeLookupTokenCore(ReadOnlySpan parameterName, Span destination) + { + int writeIndex = 0; + int underscoreRunLength = 0; + + for (int i = 0; i < parameterName.Length; i++) + { + char current = parameterName[i]; + if (current is '(' or ')') + { + continue; + } + + char normalizedCharacter = current is '-' or '/' or ' ' or '.' + ? '_' + : char.ToUpperInvariant(current); + + if (normalizedCharacter == '_') + { + underscoreRunLength++; + continue; + } + + FlushUnderscores(destination, ref writeIndex, ref underscoreRunLength); + destination[writeIndex++] = normalizedCharacter; + } + + FlushUnderscores(destination, ref writeIndex, ref underscoreRunLength); + return writeIndex; + } + + private static ReadOnlySpan TrimWhitespace(ReadOnlySpan value) + { + int start = 0; + while (start < value.Length && char.IsWhiteSpace(value[start])) + { + start++; + } + + int end = value.Length - 1; + while (end >= start && char.IsWhiteSpace(value[end])) + { + end--; + } + + return end < start ? [] : value.Slice(start, (end - start) + 1); + } + + private static void FlushUnderscores(Span destination, ref int writeIndex, ref int underscoreRunLength) + { + int underscoresToWrite = (underscoreRunLength + 1) / 2; + for (int i = 0; i < underscoresToWrite; i++) + { + destination[writeIndex++] = '_'; + } + + underscoreRunLength = 0; + } +} diff --git a/src/ProjNet/IO/CoordinateSystems/StreamTokenizer.cs b/src/ProjNet/IO/CoordinateSystems/StreamTokenizer.cs deleted file mode 100644 index 0ebf5df7..00000000 --- a/src/ProjNet/IO/CoordinateSystems/StreamTokenizer.cs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -using System; -using System.Globalization; -using System.IO; - -namespace ProjNet.IO.CoordinateSystems -{ - /// - ///The StreamTokenizer class takes an input stream and parses it into "tokens", allowing the tokens to be read one at a time. The parsing process is controlled by a table and a number of flags that can be set to various states. The stream tokenizer can recognize identifiers, numbers, quoted strings, and various comment style - /// - /// - ///This is a crude c# implementation of Java's StreamTokenizer class. - /// - internal class StreamTokenizer - { - private readonly NumberFormatInfo _nfi = CultureInfo.InvariantCulture.NumberFormat; - - private TokenType _currentTokenType; - private readonly TextReader _reader; - private string _currentToken; - - private int _lineNumber = 1; - private int _colNumber = 1; - private readonly bool _ignoreWhitespace; - - /// - /// Initializes a new instance of the StreamTokenizer class. - /// - /// A TextReader with some text to read. - /// Flag indicating whether whitespace should be ignored. - public StreamTokenizer(TextReader reader, bool ignoreWhitespace) - { - if (reader == null) - throw new ArgumentNullException("reader"); - - _reader = reader; - _ignoreWhitespace = ignoreWhitespace; - } - - /// - /// The current line number of the stream being read. - /// - public int LineNumber - { - get { return _lineNumber; } - } - - /// - /// The current column number of the stream being read. - /// - public int Column - { - get { return _colNumber; } - } - - public bool IgnoreWhitespace - { - get { return _ignoreWhitespace; } - } - - /// - /// If the current token is a number, this field contains the value of that number. - /// - /// - /// If the current token is a number, this field contains the value of that number. The current token is a number when the value of the ttype field is TT_NUMBER. - /// - /// Current token is not a number in a valid format. - public double GetNumericValue() - { - string number = GetStringValue(); - if (GetTokenType() == TokenType.Number) - return double.Parse(number, _nfi); - string s = string.Format(_nfi, "The token '{0}' is not a number at line {1} column {2}.", - number, LineNumber, Column); - throw new ArgumentException(s); - } - - /// - /// If the current token is a word token, this field contains a string giving the characters of the word token. - /// - public string GetStringValue() - { - return _currentToken; - } - - /// - /// Gets the token type of the current token. - /// - /// - public TokenType GetTokenType() - { - return _currentTokenType; - } - - /// - /// Returns the next token. - /// - /// Determines is whitespace is ignored. True if whitespace is to be ignored. - /// The TokenType of the next token. - public TokenType NextToken(bool ignoreWhitespace) - { - return ignoreWhitespace ? NextNonWhitespaceToken() : NextTokenAny(); - } - - /// - /// Returns the next token. - /// - /// The TokenType of the next token. - public TokenType NextToken() - { - return NextToken(IgnoreWhitespace); - } - - private TokenType NextTokenAny() - { - _currentToken = ""; - _currentTokenType = TokenType.Eof; - int finished = _reader.Read(); - - bool isNumber = false; - bool isWord = false; - - while (finished != -1) - { - char currentCharacter = (char) finished; - char nextCharacter = (char) _reader.Peek(); - _currentTokenType = GetType(currentCharacter); - var nextTokenType = GetType(nextCharacter); - - // handling of words with _ - if (isWord && currentCharacter == '_') - _currentTokenType = TokenType.Word; - // handing of words ending in numbers - if (isWord && _currentTokenType == TokenType.Number) - _currentTokenType = TokenType.Word; - - if (!isNumber) - { - if (_currentTokenType == TokenType.Word && nextCharacter == '_') - { - //enable words with _ inbetween - nextTokenType = TokenType.Word; - isWord = true; - } - if (_currentTokenType == TokenType.Word && nextTokenType == TokenType.Number) - { - //enable words ending with numbers - nextTokenType = TokenType.Word; - isWord = true; - } - } - - // handle negative numbers - if (currentCharacter == '-' && nextTokenType == TokenType.Number && isNumber == false) - { - _currentTokenType = TokenType.Number; - nextTokenType = TokenType.Number; - } - - // this handles numbers with a decimal point - if (isNumber && nextTokenType == TokenType.Number && currentCharacter == '.') - _currentTokenType = TokenType.Number; - if (_currentTokenType == TokenType.Number && nextCharacter == '.' && isNumber == false) - { - nextTokenType = TokenType.Number; - isNumber = true; - } - - // this handles numbers with a scientific notation - if (isNumber) - { - if (_currentTokenType == TokenType.Number && nextCharacter == 'E') - { - nextTokenType = TokenType.Number; - } - if (currentCharacter == 'E' && (nextCharacter == '-' || nextCharacter == '+')) - { - _currentTokenType = TokenType.Number; - nextTokenType = TokenType.Number; - } - if ((currentCharacter == 'E' || currentCharacter == '-' || currentCharacter == '+') && nextTokenType == TokenType.Number) - { - _currentTokenType = TokenType.Number; - } - } - - - _colNumber++; - if (_currentTokenType == TokenType.Eol) - { - _lineNumber++; - _colNumber = 1; - } - - _currentToken = _currentToken + currentCharacter; - if (_currentTokenType != nextTokenType) - finished = -1; - else if (_currentTokenType == TokenType.Symbol && currentCharacter != '-') - finished = -1; - else finished = _reader.Read(); - } - return _currentTokenType; - } - - /// - /// Determines a characters type (e.g. number, symbols, character). - /// - /// The character to determine. - /// The TokenType the character is. - private static TokenType GetType(char character) - { - if (char.IsDigit(character)) - return TokenType.Number; - if (char.IsLetter(character)) - return TokenType.Word; - if (character == '\n') - return TokenType.Eol; - if (char.IsWhiteSpace(character) || char.IsControl(character)) - return TokenType.Whitespace; - return TokenType.Symbol; - } - - /// - /// Returns next token that is not whitespace. - /// - /// - private TokenType NextNonWhitespaceToken() - { - - var tokenType = NextTokenAny(); - while (tokenType == TokenType.Whitespace || tokenType == TokenType.Eol) - tokenType = NextTokenAny(); - return tokenType; - } - } -} diff --git a/src/ProjNet/IO/CoordinateSystems/TokenType.cs b/src/ProjNet/IO/CoordinateSystems/TokenType.cs index 5ff64ad2..3aea1c8a 100644 --- a/src/ProjNet/IO/CoordinateSystems/TokenType.cs +++ b/src/ProjNet/IO/CoordinateSystems/TokenType.cs @@ -1,76 +1,43 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2002 Urban Science Applications, Inc. +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.IO.CoordinateSystems; -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ +/// +/// Represents the type of token created by the StreamTokenizer class. +/// +internal enum TokenType +{ + /// + /// Indicates that the token is a word. + /// + Word, -#region Using + /// + /// Indicates that the token is a number. + /// + Number, + /// + /// Indicates that the end of line has been read. The field can only have this value if the eolIsSignificant method has been called with the argument true. + /// + Eol, + /// + /// Indicates that the end of the input stream has been reached. + /// + Eof, -#endregion + /// + /// Indicates that the token is white space (space, tab, newline). + /// + Whitespace, -namespace ProjNet.IO.CoordinateSystems -{ - /// - /// Represents the type of token created by the StreamTokenizer class. - /// - internal enum TokenType - { - /// - /// Indicates that the token is a word. - /// - Word, - /// - /// Indicates that the token is a number. - /// - Number, - /// - /// Indicates that the end of line has been read. The field can only have this value if the eolIsSignificant method has been called with the argument true. - /// - Eol, - /// - /// Indicates that the end of the input stream has been reached. - /// - Eof, - /// - /// Indictaes that the token is white space (space, tab, newline). - /// - Whitespace, - /// - /// Characters that are not whitespace, numbers, etc... - /// - Symbol - } + /// + /// Characters that are not whitespace, numbers, etc... + /// + Symbol, } diff --git a/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs b/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs deleted file mode 100644 index c6fa98b0..00000000 --- a/src/ProjNet/IO/CoordinateSystems/WKTStreamTokenizer.cs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2005 - 2009 - Morten Nielsen (www.sharpgis.net) -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. - -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -// SOURCECODE IS MODIFIED FROM ANOTHER WORK AND IS ORIGINALLY BASED ON GeoTools.NET: -/* - * Copyright (C) 2002 Urban Science Applications, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -using System; -using System.Globalization; -using System.IO; - -namespace ProjNet.IO.CoordinateSystems -{ - /// - /// Reads a stream of Well Known Text (wkt) string and returns a stream of tokens. - /// - internal class WktStreamTokenizer : StreamTokenizer - { - private readonly NumberFormatInfo _nfi = CultureInfo.InvariantCulture.NumberFormat; - - /// - /// Initializes a new instance of the WktStreamTokenizer class. - /// - /// The WktStreamTokenizer class ais in reading WKT streams. - /// A TextReader that contains - public WktStreamTokenizer(TextReader reader) : base(reader, true) { } - - /// - /// Reads a token and checks it is what is expected. - /// - /// The expected token. - internal void ReadToken(string expectedToken) - { - NextToken(); - if (GetStringValue() != expectedToken) - { - string s = string.Format(_nfi, "Expecting ('{3}') but got a '{0}' at line {1} column {2}.", GetStringValue(), LineNumber, Column, expectedToken); - throw new ArgumentException(s); - } - } - - /// - /// Reads a string inside double quotes. - /// - /// - /// White space inside quotes is preserved. - /// - /// The string inside the double quotes. - public string ReadDoubleQuotedWord() - { - string word = ""; - - if (GetStringValue()!="\"") - ReadToken("\""); - NextToken(false); - while (GetStringValue() != "\"") - { - word = word + GetStringValue(); - NextToken(false); - } - return word; - } - - /// - /// Reads an opener - /// - /// The expected bracket type. - /// The bracket type encountered - public WktBracket ReadOpener(WktBracket expectedBracket = WktBracket.DontCare) - { - NextToken(); - string stringValue = GetStringValue(); - if (stringValue == "[") - { - if (expectedBracket == WktBracket.Square || expectedBracket == WktBracket.DontCare) - return WktBracket.Square; - } - else if (stringValue == "(") - { - if (expectedBracket == WktBracket.Round || expectedBracket == WktBracket.DontCare) - return WktBracket.Round; - } - - string expectedToken = expectedBracket == WktBracket.Square ? "[" : "("; - string s = string.Format(_nfi, "Expecting ('{3}') but got a '{0}' at line {1} column {2}.", stringValue, LineNumber, Column, expectedToken); - throw new ArgumentException(s); - } - - /// - /// Reads an closer - /// - /// The expected bracket type. - public void ReadCloser(WktBracket expectedBracket) - { - NextToken(); - CheckCloser(expectedBracket); - } - - /// - /// Checks if the current token is a closer of expected type. - /// - /// The expected bracket type. - public void CheckCloser(WktBracket expectedBracket) - { - string stringValue = GetStringValue(); - if (stringValue == "]") - { - if (expectedBracket == WktBracket.Square || expectedBracket == WktBracket.DontCare) - return; - } - else if (stringValue == ")") - { - if (expectedBracket == WktBracket.Round || expectedBracket == WktBracket.DontCare) - return; - } - - string expectedToken = expectedBracket == WktBracket.Square ? "]" : ")"; - string s = string.Format(_nfi, "Expecting ('{3}') but got a '{0}' at line {1} column {2}.", stringValue, LineNumber, Column, expectedToken); - throw new ArgumentException(s); - } - - /// - /// Reads the authority and authority code. - /// - /// String to place the authority in. - /// String to place the authority code in. - public void ReadAuthority(out string authority, out long authorityCode) - { - //AUTHORITY["EPGS","9102"]] - if (GetStringValue() != "AUTHORITY") - ReadToken("AUTHORITY"); - var bracket = ReadOpener(); - authority = ReadDoubleQuotedWord(); - ReadToken(","); - NextToken(); - if (GetTokenType() == TokenType.Number) - authorityCode = (long) GetNumericValue(); - else - long.TryParse(ReadDoubleQuotedWord(), NumberStyles.Any, _nfi, out authorityCode); - ReadCloser(bracket); - } - } -} diff --git a/src/ProjNet/IO/CoordinateSystems/WktBracket.cs b/src/ProjNet/IO/CoordinateSystems/WktBracket.cs index 6cef3ac4..36b70d17 100644 --- a/src/ProjNet/IO/CoordinateSystems/WktBracket.cs +++ b/src/ProjNet/IO/CoordinateSystems/WktBracket.cs @@ -1,39 +1,28 @@ -// Copyright 2021 - NetTopologySuite - Team -// -// This file is part of ProjNet. -// ProjNet is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// ProjNet is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany -// You should have received a copy of the GNU Lesser General Public License -// along with ProjNet; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +namespace ProjNet.IO.CoordinateSystems; -namespace ProjNet.IO.CoordinateSystems +/// +/// An enumeration of possible bracket types. +/// +internal enum WktBracket { /// - /// An enumeration of possible bracket types + /// Bracket type not specified. /// - internal enum WktBracket - { - /// - /// Bracket type not specified. - /// - DontCare, - /// - /// Opener "(", closer ")" - /// - Round, - /// - /// Opener "[", closer "]" - /// - Square, - //Brace - } + DontCare, + + /// + /// Opener "(", closer ")". + /// + Round, + + /// + /// Opener "[", closer "]". + /// + Square, + + // Brace } diff --git a/src/ProjNet/IO/CoordinateSystems/WktTokenizer.cs b/src/ProjNet/IO/CoordinateSystems/WktTokenizer.cs new file mode 100644 index 00000000..d47fb338 --- /dev/null +++ b/src/ProjNet/IO/CoordinateSystems/WktTokenizer.cs @@ -0,0 +1,687 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from GeoTools.NET. + +namespace ProjNet.IO.CoordinateSystems; + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Text; +using ProjNet.IO.Wkt; + +/// +/// Tokenizes a buffered Well Known Text (WKT) input stream. +/// +/// +/// This tokenizer operates on a fully buffered source string and exposes token slices +/// as without requiring per-token +/// builders in the scanning path. +/// +internal sealed class WktTokenizer +{ + private readonly string source; + private readonly bool ignoreWhitespaceByDefault; + + private int index; + private int lineNumber = 1; + private int column = 1; + + private int tokenStartIndex; + private int tokenLength; + private int tokenLine = 1; + private int tokenColumn = 1; + private TokenType tokenType = TokenType.Eof; + + /// + /// Initializes a new instance of the class. + /// + /// Fully buffered WKT source text. + /// + /// When , skips whitespace and end-of-line tokens. + /// + internal WktTokenizer(string source, bool ignoreWhitespaceByDefault = true) + { + this.source = ArgumentGuard.ThrowIfNull(source, nameof(source)); + this.ignoreWhitespaceByDefault = ignoreWhitespaceByDefault; + } + + /// + /// Initializes a new instance of the class. + /// + /// Reader providing WKT source text. + /// + /// When , skips whitespace and end-of-line tokens. + /// + internal WktTokenizer(TextReader reader, bool ignoreWhitespaceByDefault = true) + { + this.source = ArgumentGuard.ThrowIfNull(reader, nameof(reader)).ReadToEnd(); + this.ignoreWhitespaceByDefault = ignoreWhitespaceByDefault; + } + + /// + /// Gets the token type of the current token. + /// + internal TokenType TokenType => this.tokenType; + + /// + /// Gets the buffered WKT source string. + /// + internal string Source => this.source; + + /// + /// Gets the one-based line number where the current token starts. + /// + internal int LineNumber => this.tokenLine; + + /// + /// Gets the one-based column number where the current token starts. + /// + internal int Column => this.tokenColumn; + + /// + /// Gets a value indicating whether the tokenizer reached end of input. + /// + internal bool IsEndOfInput => this.tokenType == TokenType.Eof; + + /// + /// Gets the zero-based start index of the current token within the buffered source string. + /// + internal int TokenStartIndex => this.tokenStartIndex; + + /// + /// Gets the length of the current token. + /// + internal int TokenLength => this.tokenLength; + + /// + /// Gets the current token as a span over the buffered source text. + /// + /// Current token span. + internal ReadOnlySpan GetTokenSpan() + { + return this.source.AsSpan(this.tokenStartIndex, this.tokenLength); + } + + /// + /// Gets the current token as a string. + /// + /// The current token string value. + internal string GetStringValue() + { + return this.GetTokenString(); + } + + /// + /// Gets the token type of the current token. + /// + /// The current . + internal TokenType GetTokenType() + { + return this.tokenType; + } + + /// + /// Gets the current token as a string. + /// + /// The current token string value. + internal string GetTokenString() + { + return this.tokenLength == 0 + ? string.Empty + : this.source.Substring(this.tokenStartIndex, this.tokenLength); + } + + /// + /// Gets the current token parsed as a number. + /// + /// The current token parsed as . + /// + /// Thrown when the current token is not a valid numeric token. + /// + internal double GetNumericValue() + { + if (this.tokenType != TokenType.Number) + { + ThrowWktParseException($"The token '{this.GetTokenString()}' is not a number at line {this.LineNumber} column {this.Column}."); + } + + if (!this.TryGetNumericValue(out double value)) + { + ThrowWktParseException($"The token '{this.GetTokenString()}' is not a valid number at line {this.LineNumber} column {this.Column}."); + } + + return value; + } + + /// + /// Tries to parse the current token as a number. + /// + /// The parsed value, when successful. + /// + /// when the current token is numeric and parsing succeeded; + /// otherwise . + /// + internal bool TryGetNumericValue(out double value) + { + if (this.tokenType != TokenType.Number) + { + value = default; + return false; + } + + ReadOnlySpan tokenSpan = this.GetTokenSpan(); +#if NETSTANDARD2_0 + return double.TryParse( + tokenSpan.ToString(), + NumberStyles.Float | NumberStyles.AllowLeadingSign, + CultureInfo.InvariantCulture, + out value); +#else + return double.TryParse( + tokenSpan, + NumberStyles.Float | NumberStyles.AllowLeadingSign, + CultureInfo.InvariantCulture, + out value); +#endif + } + + /// + /// Tries to parse the current token as a 32-bit integer. + /// + /// The parsed integer value, when successful. + /// + /// when the current token is numeric and parsing as an integer succeeded; + /// otherwise . + /// + internal bool TryGetInt32Value(out int value) + { + if (this.tokenType != TokenType.Number) + { + value = default; + return false; + } + +#if NETSTANDARD2_0 + return int.TryParse( + this.GetTokenString(), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value); +#else + return int.TryParse( + this.GetTokenSpan(), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out value); +#endif + } + + /// + /// Determines whether the current token is the specified symbol. + /// + /// Expected symbol character. + /// when the current token is the requested symbol; otherwise . + internal bool IsCurrentSymbol(char symbol) + { + return this.IsCurrentSymbolCore(symbol); + } + + /// + /// Reads a token and verifies that it matches the expected token text. + /// + /// Expected token text. + /// Thrown when the token does not match. + internal void ReadToken(string expectedToken) + { + this.NextToken(); + if (!this.IsCurrentToken(expectedToken.AsSpan())) + { + ThrowWktParseException( + $"Expecting ('{expectedToken}') but got a '{this.GetTokenString()}' at line {this.LineNumber} column {this.Column}."); + } + } + + /// + /// Reads a string value enclosed in double quotes. + /// + /// The unquoted string value. + /// Thrown when the quoted value is not terminated. + internal string ReadDoubleQuotedWord() + { + if (!this.IsCurrentSymbol('"')) + { + this.ReadToken("\""); + } + + var builder = new StringBuilder(); + this.NextToken(false); + + while (true) + { + if (this.tokenType == TokenType.Eof) + { + ThrowWktParseException( + $"Unterminated quoted string at line {this.LineNumber} column {this.Column}."); + } + + if (this.IsCurrentSymbol('"')) + { + if (this.index < this.source.Length && this.source[this.index] == '"') + { + this.NextToken(false); + builder.Append('"'); + this.NextToken(false); + continue; + } + + return builder.ToString(); + } + + builder.Append(this.GetTokenString()); + this.NextToken(false); + } + } + + /// + /// Reads a double-quoted token and returns the raw content range without materializing the unescaped string. + /// + /// The start index and length of the content inside the surrounding double quotes. + /// Thrown when the quoted value is not terminated. + internal (int ContentStartIndex, int ContentLength) ReadDoubleQuotedContentRange() + { + if (!this.IsCurrentSymbol('"')) + { + this.ReadToken("\""); + } + + int contentStartIndex = this.index; + this.NextToken(false); + + while (true) + { + if (this.tokenType == TokenType.Eof) + { + ThrowWktParseException( + $"Unterminated quoted string at line {this.LineNumber} column {this.Column}."); + } + + if (this.IsCurrentSymbol('"')) + { + if (this.index < this.source.Length && this.source[this.index] == '"') + { + this.NextToken(false); + this.NextToken(false); + continue; + } + + return (contentStartIndex, this.tokenStartIndex - contentStartIndex); + } + + this.NextToken(false); + } + } + + /// + /// Reads an opening bracket token. + /// + /// Expected opening bracket type. + /// The encountered bracket type. + /// Thrown when the bracket does not match. + internal WktBracket ReadOpener(WktBracket expectedBracket = WktBracket.DontCare) + { + this.NextToken(); + if (this.IsCurrentSymbol('[')) + { + if (expectedBracket == WktBracket.Square || expectedBracket == WktBracket.DontCare) + { + return WktBracket.Square; + } + } + else if (this.IsCurrentSymbol('(')) + { + if (expectedBracket == WktBracket.Round || expectedBracket == WktBracket.DontCare) + { + return WktBracket.Round; + } + } + + string expectedToken = expectedBracket == WktBracket.Square ? "[" : "("; + return ThrowWktParseException( + $"Expecting ('{expectedToken}') but got a '{this.GetTokenString()}' at line {this.LineNumber} column {this.Column}."); + } + + /// + /// Reads and validates a closing bracket token. + /// + /// Expected closing bracket type. + internal void ReadCloser(WktBracket expectedBracket) + { + this.NextToken(); + this.CheckCloser(expectedBracket); + } + + /// + /// Validates that the current token is a matching closing bracket token. + /// + /// Expected closing bracket type. + /// Thrown when the bracket does not match. + internal void CheckCloser(WktBracket expectedBracket) + { + if (this.IsCurrentSymbol(']')) + { + if (expectedBracket == WktBracket.Square || expectedBracket == WktBracket.DontCare) + { + return; + } + } + else if (this.IsCurrentSymbol(')')) + { + if (expectedBracket == WktBracket.Round || expectedBracket == WktBracket.DontCare) + { + return; + } + } + + string expectedToken = expectedBracket == WktBracket.Square ? "]" : ")"; + ThrowWktParseException( + $"Expecting ('{expectedToken}') but got a '{this.GetTokenString()}' at line {this.LineNumber} column {this.Column}."); + } + + /// + /// Reads an AUTHORITY token block. + /// + /// Parsed authority name. + /// Parsed authority code. + internal void ReadAuthority(out string authority, out long authorityCode) + { + this.ReadAuthority(out authority, out authorityCode, out _); + } + + /// + /// Reads an AUTHORITY token block and reports whether the authority code token was numeric. + /// + /// Parsed authority name. + /// Parsed authority code. + /// + /// when the authority code token was numeric or parseable as integer; otherwise . + /// + internal void ReadAuthority(out string authority, out long authorityCode, out bool hasNumericAuthorityCode) + { + if (!this.IsCurrentToken("AUTHORITY".AsSpan())) + { + this.ReadToken("AUTHORITY"); + } + + WktBracket bracket = this.ReadOpener(); + authority = this.ReadDoubleQuotedWord(); + this.ReadToken(","); + this.NextToken(); + + if (this.tokenType == TokenType.Number) + { + authorityCode = (long)this.GetNumericValue(); + hasNumericAuthorityCode = true; + } + else + { + hasNumericAuthorityCode = long.TryParse( + this.ReadDoubleQuotedWord(), + NumberStyles.Any, + CultureInfo.InvariantCulture, + out authorityCode); + } + + this.ReadCloser(bracket); + } + + /// + /// Reads the next token using the default whitespace behavior. + /// + /// The type of the next token. + internal TokenType NextToken() + { + return this.NextToken(this.ignoreWhitespaceByDefault); + } + + /// + /// Reads the next token. + /// + /// + /// When , whitespace and end-of-line tokens are skipped. + /// + /// The type of the next token. + internal TokenType NextToken(bool ignoreWhitespace) + { + while (true) + { + this.tokenStartIndex = this.index; + this.tokenLine = this.lineNumber; + this.tokenColumn = this.column; + + if (this.index >= this.source.Length) + { + this.tokenLength = 0; + this.tokenType = TokenType.Eof; + return this.tokenType; + } + + char current = this.source[this.index]; + TokenType nextTokenType; + if (char.IsLetter(current)) + { + nextTokenType = TokenType.Word; + this.ConsumeWord(); + } + else if (this.TryConsumeNumber()) + { + nextTokenType = TokenType.Number; + } + else if (current == '\r' || current == '\n') + { + nextTokenType = TokenType.Eol; + this.ConsumeEol(); + } + else if (char.IsWhiteSpace(current) || char.IsControl(current)) + { + nextTokenType = TokenType.Whitespace; + this.ConsumeWhitespace(); + } + else + { + nextTokenType = TokenType.Symbol; + this.ConsumeSymbol(); + } + + this.tokenType = nextTokenType; + if (!ignoreWhitespace || (nextTokenType != TokenType.Whitespace && nextTokenType != TokenType.Eol)) + { + return this.tokenType; + } + } + } + + [DoesNotReturn] + private static void ThrowWktParseException(string message) + { + throw new WktParseException(message); + } + + [DoesNotReturn] + private static T ThrowWktParseException(string message) + { + throw new WktParseException(message); + } + + private void ConsumeWord() + { + this.AdvanceNonEolCharacter(); + while (this.index < this.source.Length) + { + char current = this.source[this.index]; + if (char.IsLetter(current) || char.IsDigit(current) || current == '_') + { + this.AdvanceNonEolCharacter(); + continue; + } + + break; + } + + this.tokenLength = this.index - this.tokenStartIndex; + } + + private bool TryConsumeNumber() + { + int scanIndex = this.index; + + if (this.source[scanIndex] == '-' || this.source[scanIndex] == '+') + { + if (!this.IsSignPrefixForNumber(scanIndex)) + { + return false; + } + + scanIndex++; + } + + bool hasDigits = false; + while (scanIndex < this.source.Length && char.IsDigit(this.source[scanIndex])) + { + scanIndex++; + hasDigits = true; + } + + if (scanIndex < this.source.Length && this.source[scanIndex] == '.') + { + scanIndex++; + while (scanIndex < this.source.Length && char.IsDigit(this.source[scanIndex])) + { + scanIndex++; + hasDigits = true; + } + } + + if (!hasDigits) + { + return false; + } + + if (scanIndex < this.source.Length && (this.source[scanIndex] == 'E' || this.source[scanIndex] == 'e')) + { + int exponentIndex = scanIndex + 1; + if (exponentIndex < this.source.Length && (this.source[exponentIndex] == '+' || this.source[exponentIndex] == '-')) + { + exponentIndex++; + } + + int exponentDigitsStart = exponentIndex; + while (exponentIndex < this.source.Length && char.IsDigit(this.source[exponentIndex])) + { + exponentIndex++; + } + + if (exponentIndex > exponentDigitsStart) + { + scanIndex = exponentIndex; + } + } + + while (this.index < scanIndex) + { + this.AdvanceNonEolCharacter(); + } + + this.tokenLength = this.index - this.tokenStartIndex; + return true; + } + + private bool IsSignPrefixForNumber(int signIndex) + { + int nextIndex = signIndex + 1; + if (nextIndex >= this.source.Length) + { + return false; + } + + char nextCharacter = this.source[nextIndex]; + if (char.IsDigit(nextCharacter)) + { + return true; + } + + if (nextCharacter != '.') + { + return false; + } + + int fractionalStartIndex = nextIndex + 1; + return fractionalStartIndex < this.source.Length && char.IsDigit(this.source[fractionalStartIndex]); + } + + private bool IsCurrentSymbolCore(char symbol) + { + return this.tokenType == TokenType.Symbol && + this.tokenLength == 1 && + this.source[this.tokenStartIndex] == symbol; + } + + private bool IsCurrentToken(ReadOnlySpan expectedToken) + { + return this.GetTokenSpan().SequenceEqual(expectedToken); + } + + private void ConsumeSymbol() + { + this.AdvanceNonEolCharacter(); + this.tokenLength = this.index - this.tokenStartIndex; + } + + private void ConsumeWhitespace() + { + while (this.index < this.source.Length) + { + char current = this.source[this.index]; + if (current == '\r' || current == '\n') + { + break; + } + + if (!(char.IsWhiteSpace(current) || char.IsControl(current))) + { + break; + } + + this.AdvanceNonEolCharacter(); + } + + this.tokenLength = this.index - this.tokenStartIndex; + } + + private void ConsumeEol() + { + if (this.source[this.index] == '\r') + { + this.index++; + if (this.index < this.source.Length && this.source[this.index] == '\n') + { + this.index++; + } + } + else + { + this.index++; + } + + this.lineNumber++; + this.column = 1; + this.tokenLength = this.index - this.tokenStartIndex; + } + + private void AdvanceNonEolCharacter() + { + this.index++; + this.column++; + } +} diff --git a/src/ProjNet/IO/Wkt/WktIdentifier.cs b/src/ProjNet/IO/Wkt/WktIdentifier.cs new file mode 100644 index 00000000..a91bb2ab --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktIdentifier.cs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System.Text; +using ProjNet; + +/// +/// Represents an unquoted identifier or literal value in WKT, such as an axis orientation +/// (e.g. NORTH) or a raw WKT fragment. +/// +public sealed class WktIdentifier : WktNode +{ + private readonly WktTextSlice text; + private string? name; + + /// + /// Initializes a new instance of the class. + /// + /// The identifier name or literal text. + public WktIdentifier(string name) + { + this.name = ArgumentGuard.ThrowIfNull(name, nameof(name)); + this.text = new WktTextSlice(this.name); + } + + /// + /// Initializes a new instance of the class from a source slice. + /// + /// The WKT source string. + /// The zero-based start index of the identifier text. + /// The length of the identifier text. + internal WktIdentifier(string source, int start, int length) + { + this.text = new WktTextSlice(source, start, length); + } + + /// + /// Gets the identifier name or literal text. + /// + public string Name => this.name ??= this.text.ToText(); + + /// + public override string ToString() => this.Name; + + /// + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) => this.ToString(); + + /// + internal override void AppendTo(StringBuilder builder) + { + this.text.AppendTo(builder); + } +} diff --git a/src/ProjNet/IO/Wkt/WktInteger.cs b/src/ProjNet/IO/Wkt/WktInteger.cs new file mode 100644 index 00000000..53a602c7 --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktInteger.cs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System.Globalization; +using System.Text; + +/// +/// Represents an integer value in WKT, e.g. 4326. +/// +public sealed class WktInteger : WktNode +{ + /// + /// Initializes a new instance of the class. + /// + /// The integer value. + public WktInteger(int value) => this.Value = value; + + /// + /// Gets the integer value. + /// + public int Value { get; } + + /// + public override string ToString() => this.Value.ToString(CultureInfo.InvariantCulture); + + /// + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) => this.ToString(); + + /// + internal override void AppendTo(StringBuilder builder) + { + builder.Append(this.Value.ToString(CultureInfo.InvariantCulture)); + } +} diff --git a/src/ProjNet/IO/Wkt/WktKeywordNode.cs b/src/ProjNet/IO/Wkt/WktKeywordNode.cs new file mode 100644 index 00000000..1f17f22a --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktKeywordNode.cs @@ -0,0 +1,640 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using ProjNet; +using ProjNet.IO.CoordinateSystems; + +/// +/// Represents a WKT keyword node with children, e.g. GEOGCS["WGS 84", ...]. +/// +public sealed class WktKeywordNode : WktNode +{ + private readonly WktTextSlice keywordText; + private readonly WktNode[] children; + private string? keyword; + + /// + /// Initializes a new instance of the class. + /// + /// The WKT keyword. + /// The child nodes. + public WktKeywordNode(string keyword, params WktNode[] children) + : this(new WktTextSlice(ArgumentGuard.ThrowIfNull(keyword, nameof(keyword))), CopyChildren(children), keyword) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The WKT keyword. + /// The child nodes. + public WktKeywordNode(string keyword, IReadOnlyList children) + : this(new WktTextSlice(ArgumentGuard.ThrowIfNull(keyword, nameof(keyword))), CopyChildren(children), keyword) + { + } + + /// + /// Initializes a new instance of the class from a source-backed keyword slice. + /// + /// The WKT source string. + /// The zero-based start index of the keyword text. + /// The keyword length. + /// The child node array to attach directly. + internal WktKeywordNode(string source, int keywordStart, int keywordLength, WktNode[] children) + : this(new WktTextSlice(source, keywordStart, keywordLength), TakeChildren(children), keyword: null) + { + } + + private WktKeywordNode(WktTextSlice keywordText, WktNode[] children, string? keyword) + { + this.keywordText = keywordText; + this.children = children; + this.keyword = keyword; + } + + /// + /// Gets the WKT keyword, e.g. GEOGCS, DATUM. + /// + public string Keyword => this.keyword ??= this.keywordText.ToText(); + + /// + /// Gets the child nodes of this keyword node. + /// + public IReadOnlyList Children => this.children; + + /// + public override string ToString() + { + var sb = new StringBuilder(); + this.AppendTo(sb); + return sb.ToString(); + } + + /// + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) + { + var sb = new StringBuilder(); + this.AppendFormattedTo(sb, indentLevel, indentSize); + return sb.ToString(); + } + + /// + /// Parses the tokenizer stream into a keyword-node tree. + /// + /// The tokenizer positioned at the start of a WKT expression. + /// The parsed root keyword node. + /// Thrown when the tokenizer input does not form a valid WKT tree. + internal static WktKeywordNode ParseTree(WktTokenizer tokenizer) + { + ArgumentGuard.ThrowIfNull(tokenizer, nameof(tokenizer)); + + tokenizer.NextToken(); + WktKeywordNode root = ParseKeywordNode(tokenizer, advancePastNode: true); + if (!tokenizer.IsEndOfInput) + { + throw new WktParseException( + $"Unexpected token '{tokenizer.GetTokenString()}' at line {tokenizer.LineNumber} column {tokenizer.Column} after the root WKT node."); + } + + return root; + } + + /// + /// Parses a single keyword-node subtree from the current tokenizer position. + /// + /// The tokenizer positioned on a keyword token. + /// The parsed keyword node. + /// Thrown when the tokenizer input does not form a valid WKT subtree. + internal static WktKeywordNode ParseSubtree(WktTokenizer tokenizer) + { + ArgumentGuard.ThrowIfNull(tokenizer, nameof(tokenizer)); + return ParseKeywordNode(tokenizer, advancePastNode: false); + } + + /// + /// Gets the string value of the indexed quoted-string child. + /// + /// Zero-based occurrence index among direct quoted-string children. + /// The quoted-string child value. + internal string GetString(int index) + { + return this.GetLeafChild(index, static child => child is WktQuotedString, static child => ((WktQuotedString)child).Value, nameof(index)); + } + + /// + /// Gets the numeric value of the indexed numeric child. + /// + /// Zero-based occurrence index among direct numeric children. + /// The numeric child value as a . + internal double GetNumber(int index) + { + return this.GetLeafChild(index, IsNumericNode, GetNumericValue, nameof(index)); + } + + /// + /// Gets the direct child node at the specified zero-based index. + /// + /// The direct child index. + /// The direct child node. + internal WktNode GetChild(int index) + { + if ((uint)index >= (uint)this.children.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), index, $"No direct child exists at index {index}."); + } + + return this.children[index]; + } + + /// + /// Gets the quoted-string child at the specified direct child index. + /// + /// The direct child index. + /// The quoted-string child value. + internal string GetStringChild(int index) + { + return this.GetDirectLeafChild(index, static child => child is WktQuotedString, static child => ((WktQuotedString)child).Value, "quoted string"); + } + + /// + /// Gets the identifier child at the specified direct child index. + /// + /// The direct child index. + /// The identifier child value. + internal string GetIdentifierChild(int index) + { + return this.GetDirectLeafChild(index, static child => child is WktIdentifier, static child => ((WktIdentifier)child).Name, "identifier"); + } + + /// + /// Gets the numeric child at the specified direct child index. + /// + /// The direct child index. + /// The numeric child value as a . + internal double GetNumberChild(int index) + { + return this.GetDirectLeafChild(index, IsNumericNode, GetNumericValue, "numeric value"); + } + + /// + /// Gets the direct leaf-text child at the specified index. + /// + /// The direct child index. + /// The direct leaf-text child value. + internal string GetLeafTextChild(int index) + { + return GetNodeText(this.GetChild(index)); + } + + /// + /// Finds the first direct keyword child matching the requested keyword. + /// + /// Keyword to match. + /// The first matching child, or . + internal WktKeywordNode? FindChild(string keyword) + { + for (int childIndex = 0; childIndex < this.children.Length; childIndex++) + { + if (this.children[childIndex] is WktKeywordNode keywordChild + && keywordChild.KeywordEquals(keyword)) + { + return keywordChild; + } + } + + return null; + } + + /// + /// Finds the first direct keyword child matching any of the requested keywords. + /// + /// Keywords to match. + /// The first matching child, or . + internal WktKeywordNode? FindChild(params string[] keywords) + { + ArgumentGuard.ThrowIfNull(keywords, nameof(keywords)); + + for (int childIndex = 0; childIndex < this.children.Length; childIndex++) + { + if (this.children[childIndex] is not WktKeywordNode keywordChild) + { + continue; + } + + for (int i = 0; i < keywords.Length; i++) + { + if (keywordChild.KeywordEquals(keywords[i])) + { + return keywordChild; + } + } + } + + return null; + } + + /// + /// Gets all direct numeric child values. + /// + /// The numeric child values. + internal IReadOnlyList GetAllNumbers() + { + var values = new List(); + for (int i = 0; i < this.children.Length; i++) + { + if (IsNumericNode(this.children[i])) + { + values.Add(GetNumericValue(this.children[i])); + } + } + + return values.Count == 0 ? Array.Empty() : values; + } + + /// + /// Gets the authority tuple from a nested ID[...] or AUTHORITY[...] child. + /// + /// The authority tuple when present; otherwise . + internal (string Authority, string Code)? GetAuthority() + { + WktKeywordNode? authorityNode = this.FindChild("ID", "AUTHORITY"); + if (authorityNode is null || authorityNode.children.Length < 2) + { + return null; + } + + return (GetNodeText(authorityNode.children[0]), GetNodeText(authorityNode.children[1])); + } + + /// + /// Returns the child nodes as a span for allocation-free internal iteration. + /// + /// The direct child nodes. + internal ReadOnlySpan GetChildrenSpan() + { + return this.children; + } + + /// + /// Determines whether this node's keyword matches the supplied text using ordinal ignore-case comparison. + /// + /// The keyword text to compare. + /// when the keywords match; otherwise . + internal bool KeywordEquals(string value) + { + return this.keywordText.EqualsOrdinalIgnoreCase(value); + } + + /// + /// Appends the compact WKT representation of this node to the provided builder. + /// + /// The target string builder. + internal override void AppendTo(StringBuilder builder) + { + ArgumentGuard.ThrowIfNull(builder, nameof(builder)); + this.keywordText.AppendTo(builder); + builder.Append('['); + for (int i = 0; i < this.children.Length; i++) + { + if (i > 0) + { + builder.Append(", "); + } + + this.children[i].AppendTo(builder); + } + + builder.Append(']'); + } + + /// + /// Appends the formatted WKT representation of this node to the provided builder. + /// + /// The target string builder. + /// The current indentation level. + /// The number of spaces per indentation level. + internal override void AppendFormattedTo(StringBuilder builder, int indentLevel, int indentSize) + { + ArgumentGuard.ThrowIfNull(builder, nameof(builder)); + AppendIndent(builder, indentLevel, indentSize); + this.keywordText.AppendTo(builder); + builder.Append('['); + + bool hasComplexChildren = HasKeywordChildren(this.children); + if (hasComplexChildren && this.children.Length > 0) + { + builder.AppendLine(); + for (int i = 0; i < this.children.Length; i++) + { + if (this.children[i] is WktKeywordNode keywordChild) + { + keywordChild.AppendFormattedTo(builder, indentLevel + 1, indentSize); + } + else + { + AppendIndent(builder, indentLevel + 1, indentSize); + this.children[i].AppendTo(builder); + } + + if (i < this.children.Length - 1) + { + builder.Append(','); + } + + builder.AppendLine(); + } + + AppendIndent(builder, indentLevel, indentSize); + } + else + { + for (int i = 0; i < this.children.Length; i++) + { + if (i > 0) + { + builder.Append(", "); + } + + this.children[i].AppendTo(builder); + } + } + + builder.Append(']'); + } + + private static WktKeywordNode ParseKeywordNode(WktTokenizer tokenizer, bool advancePastNode) + { + if (tokenizer.GetTokenType() != TokenType.Word) + { + throw new WktParseException( + $"Expected a WKT keyword at line {tokenizer.LineNumber} column {tokenizer.Column}, but found '{tokenizer.GetTokenString()}'."); + } + + int keywordStart = tokenizer.TokenStartIndex; + int keywordLength = tokenizer.TokenLength; + string source = tokenizer.Source; + tokenizer.NextToken(); + return ParseKeywordNodeAfterKeyword(tokenizer, source, keywordStart, keywordLength, advancePastNode); + } + + private static WktKeywordNode ParseKeywordNodeAfterKeyword(WktTokenizer tokenizer, string source, int keywordStart, int keywordLength, bool advancePastNode) + { + var keywordText = new WktTextSlice(source, keywordStart, keywordLength); + WktBracket bracket = GetCurrentOpener(tokenizer); + WktNode[] children = Array.Empty(); + int childCount = 0; + tokenizer.NextToken(); + + while (!IsCloser(tokenizer, bracket)) + { + if (tokenizer.IsEndOfInput) + { + throw new WktParseException( + $"Unexpected end of input while parsing '{keywordText.ToText()}' at line {tokenizer.LineNumber} column {tokenizer.Column}."); + } + + AddChild(ref children, ref childCount, ParseNodeAndAdvance(tokenizer)); + if (IsComma(tokenizer)) + { + tokenizer.NextToken(); + continue; + } + + tokenizer.CheckCloser(bracket); + } + + var node = new WktKeywordNode(source, keywordStart, keywordLength, TrimChildren(children, childCount)); + if (advancePastNode) + { + tokenizer.NextToken(); + } + + return node; + } + + private static WktNode ParseNodeAndAdvance(WktTokenizer tokenizer) + { + switch (tokenizer.GetTokenType()) + { + case TokenType.Symbol when tokenizer.IsCurrentSymbol('"'): + (int quotedContentStart, int quotedContentLength) = tokenizer.ReadDoubleQuotedContentRange(); + tokenizer.NextToken(); + return new WktQuotedString(tokenizer.Source, quotedContentStart, quotedContentLength); + + case TokenType.Number: + WktNode numericNode = CreateNumericNode(tokenizer); + tokenizer.NextToken(); + return numericNode; + + case TokenType.Word: + int wordStart = tokenizer.TokenStartIndex; + int wordLength = tokenizer.TokenLength; + string source = tokenizer.Source; + tokenizer.NextToken(); + return IsOpener(tokenizer) + ? ParseKeywordNodeAfterKeyword(tokenizer, source, wordStart, wordLength, advancePastNode: true) + : new WktIdentifier(source, wordStart, wordLength); + + default: + throw new WktParseException( + $"Unexpected token '{tokenizer.GetTokenString()}' at line {tokenizer.LineNumber} column {tokenizer.Column} while parsing WKT."); + } + } + + private static WktNode CreateNumericNode(WktTokenizer tokenizer) + { + if (tokenizer.TryGetInt32Value(out int integerValue)) + { + return new WktInteger(integerValue); + } + + return new WktNumber(tokenizer.GetNumericValue()); + } + + private static bool IsComma(WktTokenizer tokenizer) + { + return tokenizer.IsCurrentSymbol(','); + } + + private static bool IsOpener(WktTokenizer tokenizer) + { + return tokenizer.IsCurrentSymbol('[') || tokenizer.IsCurrentSymbol('('); + } + + private static bool IsCloser(WktTokenizer tokenizer, WktBracket bracket) + { + return bracket == WktBracket.Square + ? tokenizer.IsCurrentSymbol(']') + : tokenizer.IsCurrentSymbol(')'); + } + + private static WktBracket GetCurrentOpener(WktTokenizer tokenizer) + { + if (tokenizer.GetTokenType() != TokenType.Symbol) + { + throw new WktParseException( + $"Expected an opening bracket after a WKT keyword at line {tokenizer.LineNumber} column {tokenizer.Column}, but found '{tokenizer.GetTokenString()}'."); + } + + if (tokenizer.IsCurrentSymbol('[')) + { + return WktBracket.Square; + } + + if (tokenizer.IsCurrentSymbol('(')) + { + return WktBracket.Round; + } + + throw new WktParseException( + $"Expected an opening bracket after a WKT keyword at line {tokenizer.LineNumber} column {tokenizer.Column}, but found '{tokenizer.GetTokenString()}'."); + } + + private static string GetNodeText(WktNode node) + { + return node switch + { + WktQuotedString quotedString => quotedString.Value, + WktIdentifier identifier => identifier.Name, + WktInteger integer => integer.Value.ToString(CultureInfo.InvariantCulture), + WktNumber number => number.Value.ToString(CultureInfo.InvariantCulture), + _ => throw new ArgumentException($"Expected a leaf WKT value node but found '{node.GetType().Name}'.", nameof(node)), + }; + } + + private static bool IsNumericNode(WktNode node) + { + return node is WktNumber or WktInteger; + } + + private static double GetNumericValue(WktNode node) + { + return node switch + { + WktNumber number => number.Value, + WktInteger integer => integer.Value, + _ => throw new ArgumentException($"Expected a numeric WKT node but found '{node.GetType().Name}'.", nameof(node)), + }; + } + + private static bool HasKeywordChildren(WktNode[] children) + { + for (int i = 0; i < children.Length; i++) + { + if (children[i] is WktKeywordNode) + { + return true; + } + } + + return false; + } + + private static WktNode[] CopyChildren(IReadOnlyList? children) + { + children = ArgumentGuard.ThrowIfNull(children, nameof(children)); + + if (children.Count == 0) + { + return Array.Empty(); + } + + var copy = new WktNode[children.Count]; + for (int i = 0; i < children.Count; i++) + { + copy[i] = children[i]; + } + + return copy; + } + + private static WktNode[] TakeChildren(WktNode[]? children) + { + return ArgumentGuard.ThrowIfNull(children, nameof(children)); + } + + private static void AddChild(ref WktNode[] children, ref int count, WktNode child) + { + if (count == children.Length) + { + int newLength = children.Length == 0 ? 4 : children.Length * 2; + Array.Resize(ref children, newLength); + } + + children[count] = child; + count++; + } + + private static WktNode[] TrimChildren(WktNode[] children, int count) + { + if (count == 0) + { + return Array.Empty(); + } + + if (count == children.Length) + { + return children; + } + + var trimmedChildren = new WktNode[count]; + Array.Copy(children, trimmedChildren, count); + return trimmedChildren; + } + + private static void AppendIndent(StringBuilder builder, int indentLevel, int indentSize) + { + builder.Append(' ', indentLevel * indentSize); + } + + private T GetLeafChild( + int index, + Func predicate, + Func selector, + string paramName) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(paramName, index, "The occurrence index cannot be negative."); + } + + int currentIndex = 0; + for (int i = 0; i < this.children.Length; i++) + { + if (!predicate(this.children[i])) + { + continue; + } + + if (currentIndex == index) + { + return selector(this.children[i]); + } + + currentIndex++; + } + + throw new ArgumentOutOfRangeException(paramName, index, $"No child with occurrence index {index} matched the requested node type."); + } + + private T GetDirectLeafChild( + int index, + Func predicate, + Func selector, + string expectedNodeType) + { + WktNode child = this.GetChild(index); + if (!predicate(child)) + { + throw new ArgumentException($"Expected a {expectedNodeType} child at index {index} but found '{child.GetType().Name}'.", nameof(index)); + } + + return selector(child); + } +} diff --git a/src/ProjNet/IO/Wkt/WktNode.cs b/src/ProjNet/IO/Wkt/WktNode.cs new file mode 100644 index 00000000..035fedd7 --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktNode.cs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System.Text; + +/// +/// Abstract base class for all WKT (Well-Known Text) syntax tree nodes. +/// +public abstract class WktNode +{ + /// + /// Writes this node as a compact WKT string (single line, no extra whitespace). + /// + /// A compact WKT string representation of this node. + public abstract override string ToString(); + + /// + /// Writes this node as a formatted WKT string with indentation. + /// + /// The current indentation level. + /// The number of spaces per indentation level. + /// A formatted WKT string with indentation. + public abstract string ToFormattedString(int indentLevel = 0, int indentSize = 4); + + /// + /// Appends the compact WKT representation of this node to the provided string builder. + /// + /// The target string builder. + internal abstract void AppendTo(StringBuilder builder); + + /// + /// Appends the formatted WKT representation of this node to the provided string builder. + /// + /// The target string builder. + /// The current indentation level. + /// The number of spaces per indentation level. + internal virtual void AppendFormattedTo(StringBuilder builder, int indentLevel, int indentSize) + { + this.AppendTo(builder); + } +} diff --git a/src/ProjNet/IO/Wkt/WktNumber.cs b/src/ProjNet/IO/Wkt/WktNumber.cs new file mode 100644 index 00000000..c22c161e --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktNumber.cs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System.Globalization; +using System.Text; + +/// +/// Represents a numeric value in WKT, e.g. 6378137. +/// +public sealed class WktNumber : WktNode +{ + /// + /// Initializes a new instance of the class. + /// + /// The numeric value. + public WktNumber(double value) => this.Value = value; + + /// + /// Gets the numeric value. + /// + public double Value { get; } + + /// + public override string ToString() => this.Value.ToString(CultureInfo.InvariantCulture); + + /// + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) => this.ToString(); + + /// + internal override void AppendTo(StringBuilder builder) + { + builder.Append(this.Value.ToString(CultureInfo.InvariantCulture)); + } +} diff --git a/src/ProjNet/IO/Wkt/WktParseException.cs b/src/ProjNet/IO/Wkt/WktParseException.cs new file mode 100644 index 00000000..d19571df --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktParseException.cs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System; +using System.Runtime.Serialization; + +/// +/// The exception that is thrown when Well-Known Text (WKT) cannot be parsed structurally. +/// +[Serializable] +public sealed class WktParseException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public WktParseException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public WktParseException(string message) + : base(message) + { + } + + /// + /// 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 error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public WktParseException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. +#if NET8_0_OR_GREATER + [Obsolete("Formatter-based serialization is obsolete and should not be used.", DiagnosticId = "SYSLIB0051")] +#endif + private WktParseException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +} diff --git a/src/ProjNet/IO/Wkt/WktQuotedString.cs b/src/ProjNet/IO/Wkt/WktQuotedString.cs new file mode 100644 index 00000000..a848022e --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktQuotedString.cs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System; +using System.Text; +using ProjNet; + +/// +/// Represents a quoted string value in WKT, e.g. "WGS 84". +/// +public sealed class WktQuotedString : WktNode +{ + private readonly WktTextSlice rawContent; + private string? value; + + /// + /// Initializes a new instance of the class. + /// + /// The string value (without surrounding quotes). + public WktQuotedString(string value) + { + value = ArgumentGuard.ThrowIfNull(value, nameof(value)); + this.rawContent = CreateRawContent(value); + this.value = value; + } + + /// + /// Initializes a new instance of the class from raw quoted content. + /// + /// The WKT source string. + /// The zero-based start index of the content inside the quotes. + /// The length of the raw content inside the quotes. + internal WktQuotedString(string source, int contentStart, int contentLength) + { + this.rawContent = new WktTextSlice(source, contentStart, contentLength); + } + + /// + /// Gets the unquoted string value. + /// + public string Value => this.value ??= DecodeValue(this.rawContent); + + /// + public override string ToString() + { + var builder = new StringBuilder(this.rawContent.Length + 2); + this.AppendTo(builder); + return builder.ToString(); + } + + /// + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) => this.ToString(); + + /// + internal override void AppendTo(StringBuilder builder) + { + ArgumentGuard.ThrowIfNull(builder, nameof(builder)); + builder.Append('"'); + this.rawContent.AppendTo(builder); + builder.Append('"'); + } + + private static WktTextSlice CreateRawContent(string value) + { + int quoteCount = 0; + for (int i = 0; i < value.Length; i++) + { + if (value[i] == '"') + { + quoteCount++; + } + } + + if (quoteCount == 0) + { + return new WktTextSlice(value); + } + + var builder = new StringBuilder(value.Length + quoteCount); + for (int i = 0; i < value.Length; i++) + { + char current = value[i]; + if (current == '"') + { + builder.Append('"'); + } + + builder.Append(current); + } + + return new WktTextSlice(builder.ToString()); + } + + private static string DecodeValue(WktTextSlice rawContent) + { + if (!rawContent.Contains('"')) + { + return rawContent.ToText(); + } + + ReadOnlySpan rawSpan = rawContent.AsSpan(); + var builder = new StringBuilder(rawSpan.Length); + for (int i = 0; i < rawSpan.Length; i++) + { + char current = rawSpan[i]; + if (current == '"' && i + 1 < rawSpan.Length && rawSpan[i + 1] == '"') + { + builder.Append('"'); + i++; + continue; + } + + builder.Append(current); + } + + return builder.ToString(); + } +} diff --git a/src/ProjNet/IO/Wkt/WktTextSlice.cs b/src/ProjNet/IO/Wkt/WktTextSlice.cs new file mode 100644 index 00000000..a76ea44d --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktTextSlice.cs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System; +using System.Text; +using ProjNet; + +/// +/// Represents a slice of WKT text backed by a source string. +/// +internal readonly struct WktTextSlice +{ + private readonly string source; + private readonly int start; + + /// + /// Initializes a new instance of the struct from a complete string value. + /// + /// The full text value. + internal WktTextSlice(string value) + { + this.source = ArgumentGuard.ThrowIfNull(value, nameof(value)); + this.start = 0; + this.Length = value.Length; + } + + /// + /// Initializes a new instance of the struct from a source string slice. + /// + /// The source string that owns the slice. + /// The zero-based start index within the source string. + /// The length of the slice. + internal WktTextSlice(string source, int start, int length) + { + this.source = ArgumentGuard.ThrowIfNull(source, nameof(source)); + if (start < 0 || start > source.Length) + { + throw new ArgumentOutOfRangeException(nameof(start), start, "The slice start must be within the source string."); + } + + if (length < 0 || length > (source.Length - start)) + { + throw new ArgumentOutOfRangeException(nameof(length), length, "The slice length must fit within the source string."); + } + + this.start = start; + this.Length = length; + } + + /// + /// Gets the length of the slice. + /// + internal int Length { get; } + + /// + /// Returns the slice as a span over the backing string. + /// + /// The text slice as a span. + internal ReadOnlySpan AsSpan() + { + return this.source.AsSpan(this.start, this.Length); + } + + /// + /// Materializes the slice as a . + /// + /// The slice text. + internal string ToText() + { + return this.start == 0 && this.Length == this.source.Length + ? this.source + : this.source.Substring(this.start, this.Length); + } + + /// + /// Appends the slice to the provided string builder without materializing an intermediate string. + /// + /// The target string builder. + internal void AppendTo(StringBuilder builder) + { + ArgumentGuard.ThrowIfNull(builder, nameof(builder)); + if (this.Length == 0) + { + return; + } + + if (this.start == 0 && this.Length == this.source.Length) + { + builder.Append(this.source); + return; + } + + builder.Append(this.source, this.start, this.Length); + } + + /// + /// Determines whether the slice matches the supplied text using ordinal ignore-case comparison. + /// + /// The comparison text. + /// when both texts match; otherwise . + internal bool EqualsOrdinalIgnoreCase(string value) + { + value = ArgumentGuard.ThrowIfNull(value, nameof(value)); + return value.Length == this.Length && + string.Compare(this.source, this.start, value, 0, this.Length, StringComparison.OrdinalIgnoreCase) == 0; + } + + /// + /// Determines whether the slice contains the specified character. + /// + /// The character to locate. + /// when the character appears in the slice; otherwise . + internal bool Contains(char value) + { + return this.AsSpan().IndexOf(value) >= 0; + } +} diff --git a/src/ProjNet/IO/Wkt/WktVersion.cs b/src/ProjNet/IO/Wkt/WktVersion.cs new file mode 100644 index 00000000..8d0732d0 --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktVersion.cs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +/// +/// Identifies the WKT dialect to emit when serializing syntax tree nodes. +/// +public enum WktVersion +{ + /// + /// The legacy OGC simple-features / WKT1 form currently used by the existing writer implementation. + /// + Wkt1 = 0, + + /// + /// The ISO 19162:2019 WKT2 form. + /// + Wkt22019 = 1, +} diff --git a/src/ProjNet/IO/Wkt/WktVersionSupport.cs b/src/ProjNet/IO/Wkt/WktVersionSupport.cs new file mode 100644 index 00000000..9ae24dd9 --- /dev/null +++ b/src/ProjNet/IO/Wkt/WktVersionSupport.cs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.IO.Wkt; + +using System; +using ProjNet; + +/// +/// Provides shared validation and exception helpers for versioned WKT node serialization. +/// +internal static class WktVersionSupport +{ + /// + /// Validates that the supplied WKT version is one of the supported enum values. + /// + /// The WKT version to validate. + internal static void ThrowIfUnknown(WktVersion version) + { + if (version is not WktVersion.Wkt1 and not WktVersion.Wkt22019) + { + ArgumentGuard.ThrowArgumentOutOfRange(nameof(version), version, "Unsupported WKT version."); + } + } + + /// + /// Creates a standard not-supported exception for not-yet-implemented WKT versions. + /// + /// The object or type that does not yet support the requested version. + /// The requested WKT version. + /// A describing the unsupported serialization request. + internal static NotSupportedException CreateNotSupportedException(string subject, WktVersion version) + => new($"WKT version '{version}' is not implemented for {subject}."); + + /// + /// Creates a WKT2 ID node when authority metadata is available. + /// + /// The authority name. + /// The authority code. + /// A WKT2 ID node, or when no authority metadata is available. + internal static WktKeywordNode? CreateIdNode(string authority, long authorityCode) + { + if (string.IsNullOrWhiteSpace(authority) || authorityCode <= 0) + { + return null; + } + + WktNode authorityCodeNode = authorityCode is >= int.MinValue and <= int.MaxValue + ? new WktInteger((int)authorityCode) + : new WktNumber(authorityCode); + + return new WktKeywordNode( + "ID", + new WktQuotedString(authority), + authorityCodeNode); + } +} diff --git a/src/ProjNet/ProjNET.csproj b/src/ProjNet/ProjNET.csproj index 67ced427..72f5b478 100644 --- a/src/ProjNet/ProjNET.csproj +++ b/src/ProjNet/ProjNET.csproj @@ -3,12 +3,30 @@ ProjNet - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1;net8.0 + enable + true + true true true - 2.0.0 + 2.1.0 + 12.0 + + + netstandard2.0 + netstandard2.0;netstandard2.1;net8.0 + + + + + + + + Proj.NET Proj.NET performs point-to-point coordinate conversions between geodetic coordinate systems for use in .Net, Geographic Information Systems (GIS) or GPS applications. The spatial reference model used adheres to the Simple Features specification. @@ -23,11 +41,29 @@ Proj.NET performs point-to-point coordinate conversions between geodetic coordinate systems for use in fx. Geographic Information Systems (GIS) or GPS applications. The spatial reference model used adheres to the Simple Features specification. OGC;SFS;Projection + https://github.com/NetTopologySuite/ProjNet4GeoAPI + https://github.com/NetTopologySuite/ProjNet4GeoAPI.git + git + README.md - - + + + + + + + + + + + + + + + + diff --git a/src/ProjNet/PublicAPI.Shipped.txt b/src/ProjNet/PublicAPI.Shipped.txt new file mode 100644 index 00000000..ad184987 --- /dev/null +++ b/src/ProjNet/PublicAPI.Shipped.txt @@ -0,0 +1,1101 @@ +namespace ProjNet +{ + public class CoordinateSystemServices + { + public CoordinateSystemServices() { } + public CoordinateSystemServices(ProjNet.Data.ICoordinateSystemDefinitionProvider definitionProvider) { } + public CoordinateSystemServices(System.Collections.Generic.IEnumerable definitions) { } + public CoordinateSystemServices(ProjNet.CoordinateSystems.CoordinateSystemFactory coordinateSystemFactory, ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory coordinateTransformationFactory) { } + public CoordinateSystemServices(ProjNet.CoordinateSystems.CoordinateSystemFactory coordinateSystemFactory, ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory coordinateTransformationFactory, System.Collections.Generic.IEnumerable? enumeration) { } + public CoordinateSystemServices(ProjNet.CoordinateSystems.CoordinateSystemFactory coordinateSystemFactory, ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory coordinateTransformationFactory, System.Collections.Generic.IEnumerable? enumeration, ProjNet.Data.ICoordinateSystemDefinitionProvider? definitionProvider) { } + protected int Count { get; } + protected virtual int AddCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem coordinateSystem) { } + protected void AddCoordinateSystem(int srid, ProjNet.CoordinateSystems.CoordinateSystem coordinateSystem) { } + protected void Clear() { } + public ProjNet.CoordinateSystems.Transformations.ICoordinateTransformation? CreateTransformation(ProjNet.CoordinateSystems.CoordinateSystem? source, ProjNet.CoordinateSystems.CoordinateSystem? target) { } + public ProjNet.CoordinateSystems.Transformations.ICoordinateTransformation? CreateTransformation(int sourceSrid, int targetSrid) { } + public int[] GetAvailableSridValues() { } + public ProjNet.CoordinateSystems.CoordinateSystem? GetCoordinateSystem(int srid) { } + public ProjNet.CoordinateSystems.CoordinateSystem? GetCoordinateSystem(string authority, long code) { } + public System.Collections.Generic.IEnumerator GetEnumerator() { } + public int? GetSRID(string authority, long authorityCode) { } + public bool RemoveCoordinateSystem(int srid) { } + public ProjNet.CoordinateSystems.CoordinateSystem ResolveFromCatalog(ProjNet.CoordinateSystems.CoordinateSystem parsed) { } + public bool TryGetCoordinateSystem(int srid, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out ProjNet.CoordinateSystems.CoordinateSystem? coordinateSystem) { } + public bool TryGetCoordinateSystem(string authority, long code, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out ProjNet.CoordinateSystems.CoordinateSystem? coordinateSystem) { } + public bool TryResolveFromCatalog(ProjNet.CoordinateSystems.CoordinateSystem parsed, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out ProjNet.CoordinateSystems.CoordinateSystem? coordinateSystem) { } + } +} +namespace ProjNet.CoordinateSystems +{ + public class AngularUnit : ProjNet.CoordinateSystems.Info, ProjNet.CoordinateSystems.IInfo, ProjNet.CoordinateSystems.IUnit + { + public AngularUnit(double radiansPerUnit) { } + public double RadiansPerUnit { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.AngularUnit Degrees { get; } + public static ProjNet.CoordinateSystems.AngularUnit Gon { get; } + public static ProjNet.CoordinateSystems.AngularUnit Grad { get; } + public static ProjNet.CoordinateSystems.AngularUnit Radian { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.AngularUnit WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.AngularUnit WithName(string name) { } + } + public sealed class AxisInfo + { + public AxisInfo(ProjNet.CoordinateSystems.AxisInfo axisInfo) { } + public AxisInfo(string name, ProjNet.CoordinateSystems.AxisOrientationEnum orientation) { } + public string Name { get; } + public ProjNet.CoordinateSystems.AxisOrientationEnum Orientation { get; } + public string WKT { get; } + public string XML { get; } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + } + public enum AxisOrientationEnum : short + { + Other = 0, + North = 1, + South = 2, + East = 3, + West = 4, + Up = 5, + Down = 6, + } + public class BoundCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + protected BoundCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem sourceCoordinateSystem, ProjNet.CoordinateSystems.CoordinateSystem targetCoordinateSystem, ProjNet.CoordinateSystems.BoundTransformation transformation, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public ProjNet.CoordinateSystems.CoordinateSystem SourceCoordinateSystem { get; } + public ProjNet.CoordinateSystems.CoordinateSystem TargetCoordinateSystem { get; } + public ProjNet.CoordinateSystems.BoundTransformation Transformation { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.BoundCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.BoundCoordinateSystem WithName(string name) { } + } + public sealed class BoundTransformation : System.IEquatable + { + public BoundTransformation(string methodName, ProjNet.CoordinateSystems.Wgs84ConversionInfo wgs84Parameters) { } + public BoundTransformation(string methodName, string parameterFileName) { } + public string MethodName { get; } + public string? ParameterFileName { get; } + public bool UsesParameterFile { get; } + public bool UsesWgs84Parameters { get; } + public ProjNet.CoordinateSystems.Wgs84ConversionInfo? Wgs84Parameters { get; } + public bool Equals(ProjNet.CoordinateSystems.BoundTransformation? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + } + public class CompoundCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + public CompoundCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem headcs, ProjNet.CoordinateSystems.CoordinateSystem tailcs, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public ProjNet.CoordinateSystems.CoordinateSystem HeadCoordinateSystem { get; } + public ProjNet.CoordinateSystems.CoordinateSystem TailCoordinateSystem { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.CompoundCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.CompoundCoordinateSystem WithName(string name) { } + } + public sealed class ConcatenatedOperation : ProjNet.CoordinateSystems.Info + { + public ConcatenatedOperation(System.Collections.Generic.IReadOnlyList steps, ProjNet.CoordinateSystems.CoordinateSystem sourceCoordinateSystem, ProjNet.CoordinateSystems.CoordinateSystem targetCoordinateSystem, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public ProjNet.CoordinateSystems.CoordinateSystem SourceCoordinateSystem { get; } + public System.Collections.Generic.IReadOnlyList Steps { get; } + public ProjNet.CoordinateSystems.CoordinateSystem TargetCoordinateSystem { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.ConcatenatedOperation WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.ConcatenatedOperation WithName(string name) { } + } + public sealed class CoordinateOperation : ProjNet.CoordinateSystems.Info + { + public CoordinateOperation(string methodName, System.Collections.Generic.IReadOnlyList parameters, ProjNet.CoordinateSystems.CoordinateSystem sourceCoordinateSystem, ProjNet.CoordinateSystems.CoordinateSystem targetCoordinateSystem, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public string MethodName { get; } + public System.Collections.Generic.IReadOnlyList Parameters { get; } + public ProjNet.CoordinateSystems.CoordinateSystem SourceCoordinateSystem { get; } + public ProjNet.CoordinateSystems.CoordinateSystem TargetCoordinateSystem { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.CoordinateOperation WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.CoordinateOperation WithName(string name) { } + } + public abstract class CoordinateSystem : ProjNet.CoordinateSystems.Info + { + public double[] DefaultEnvelope { get; } + public int Dimension { get; } + public ProjNet.CoordinateSystems.AxisInfo GetAxis(int dimension) { } + public abstract ProjNet.CoordinateSystems.IUnit GetUnits(int dimension); + public string ToProjJson() { } + public virtual ProjNet.IO.Wkt.WktNode ToWktNode() { } + public virtual ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public virtual System.Xml.Linq.XElement ToXml() { } + } + public class CoordinateSystemFactory + { + public CoordinateSystemFactory() { } + public ProjNet.CoordinateSystems.BoundCoordinateSystem CreateBoundCoordinateSystem(string name, ProjNet.CoordinateSystems.CoordinateSystem sourceCoordinateSystem, ProjNet.CoordinateSystems.CoordinateSystem targetCoordinateSystem, ProjNet.CoordinateSystems.BoundTransformation transformation) { } + public ProjNet.CoordinateSystems.CompoundCoordinateSystem CreateCompoundCoordinateSystem(string name, ProjNet.CoordinateSystems.CoordinateSystem head, ProjNet.CoordinateSystems.CoordinateSystem tail) { } + public ProjNet.CoordinateSystems.Ellipsoid CreateEllipsoid(string name, double semiMajorAxis, double semiMinorAxis, ProjNet.CoordinateSystems.LinearUnit linearUnit) { } + public ProjNet.CoordinateSystems.FittedCoordinateSystem CreateFittedCoordinateSystem(string name, ProjNet.CoordinateSystems.CoordinateSystem baseCoordinateSystem, ProjNet.CoordinateSystems.Transformations.MathTransform toBase, System.Collections.Generic.List arAxes) { } + public ProjNet.CoordinateSystems.FittedCoordinateSystem CreateFittedCoordinateSystem(string name, ProjNet.CoordinateSystems.CoordinateSystem baseCoordinateSystem, string toBaseWkt, System.Collections.Generic.List arAxes) { } + public ProjNet.CoordinateSystems.Ellipsoid CreateFlattenedSphere(string name, double semiMajorAxis, double inverseFlattening, ProjNet.CoordinateSystems.LinearUnit linearUnit) { } + public ProjNet.CoordinateSystems.CoordinateSystem? CreateFromWkt(string wkt) { } + public ProjNet.CoordinateSystems.CoordinateSystem CreateFromXml(string xml) { } + public ProjNet.CoordinateSystems.GeocentricCoordinateSystem CreateGeocentricCoordinateSystem(string name, ProjNet.CoordinateSystems.HorizontalDatum datum, ProjNet.CoordinateSystems.LinearUnit linearUnit, ProjNet.CoordinateSystems.PrimeMeridian primeMeridian) { } + public ProjNet.CoordinateSystems.GeographicCoordinateSystem CreateGeographicCoordinateSystem(string name, ProjNet.CoordinateSystems.AngularUnit angularUnit, ProjNet.CoordinateSystems.HorizontalDatum datum, ProjNet.CoordinateSystems.PrimeMeridian primeMeridian, ProjNet.CoordinateSystems.AxisInfo axis0, ProjNet.CoordinateSystems.AxisInfo axis1) { } + public ProjNet.CoordinateSystems.HorizontalDatum CreateHorizontalDatum(string name, ProjNet.CoordinateSystems.DatumType datumType, ProjNet.CoordinateSystems.Ellipsoid ellipsoid, ProjNet.CoordinateSystems.Wgs84ConversionInfo? toWgs84) { } + public ProjNet.CoordinateSystems.PrimeMeridian CreatePrimeMeridian(string name, ProjNet.CoordinateSystems.AngularUnit angularUnit, double longitude) { } + public ProjNet.CoordinateSystems.ProjectedCoordinateSystem CreateProjectedCoordinateSystem(string name, ProjNet.CoordinateSystems.GeographicCoordinateSystem gcs, ProjNet.CoordinateSystems.IProjection projection, ProjNet.CoordinateSystems.LinearUnit linearUnit, ProjNet.CoordinateSystems.AxisInfo axis0, ProjNet.CoordinateSystems.AxisInfo axis1) { } + public ProjNet.CoordinateSystems.IProjection CreateProjection(string name, string wktProjectionClass, System.Collections.Generic.List parameters) { } + public ProjNet.CoordinateSystems.VerticalCoordinateSystem CreateVerticalCoordinateSystem(string name, ProjNet.CoordinateSystems.VerticalDatum datum, ProjNet.CoordinateSystems.LinearUnit verticalUnit, ProjNet.CoordinateSystems.AxisInfo axis) { } + public ProjNet.CoordinateSystems.VerticalDatum CreateVerticalDatum(string name, ProjNet.CoordinateSystems.DatumType datumType) { } + } + public static class CoordinateSystemUtilities + { + public static long CalcUtmZone(double lon) { } + public static double LatitudeToRadians(double y, bool edge) { } + public static double LongitudeToRadians(double x, bool edge) { } + } + public abstract class Datum : ProjNet.CoordinateSystems.Info + { + public ProjNet.CoordinateSystems.DatumType DatumType { get; } + public ProjNet.CoordinateSystems.DatumEnsemble? Ensemble { get; } + public override bool EqualParams(object obj) { } + public ProjNet.CoordinateSystems.Datum WithEnsemble(ProjNet.CoordinateSystems.DatumEnsemble? ensemble) { } + } + public sealed class DatumEnsemble : System.IEquatable + { + public DatumEnsemble(string name, System.Collections.Generic.IReadOnlyList members, double accuracy) { } + public DatumEnsemble(string name, System.Collections.Generic.IReadOnlyList members, double accuracy, ProjNet.CoordinateSystems.Ellipsoid? ellipsoid, string authority, long authorityCode) { } + public double Accuracy { get; } + public string Authority { get; } + public long AuthorityCode { get; } + public ProjNet.CoordinateSystems.Ellipsoid? Ellipsoid { get; } + public System.Collections.Generic.IReadOnlyList Members { get; } + public string Name { get; } + public bool Equals(ProjNet.CoordinateSystems.DatumEnsemble? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + } + public sealed class DatumEnsembleMember : System.IEquatable + { + public DatumEnsembleMember(string name) { } + public DatumEnsembleMember(string name, string authority, long authorityCode) { } + public string Authority { get; } + public long AuthorityCode { get; } + public string Name { get; } + public bool Equals(ProjNet.CoordinateSystems.DatumEnsembleMember? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + } + public enum DatumType + { + HD_Min = 1000, + HD_Other = 1000, + HD_Classic = 1001, + HD_Geocentric = 1002, + HD_Max = 1999, + VD_Min = 2000, + VD_Other = 2000, + VD_Orthometric = 2001, + VD_Ellipsoidal = 2002, + VD_AltitudeBarometric = 2003, + VD_Normal = 2004, + VD_GeoidModelDerived = 2005, + VD_Depth = 2006, + VD_Max = 2999, + LD_Min = 10000, + LD_Other = 10000, + LD_Engineering = 10001, + LD_Max = 32767, + TD_Min = 40000, + TD_Other = 40000, + TD_Max = 40999, + PD_Min = 50000, + PD_Other = 50000, + PD_Max = 50999, + } + public class Ellipsoid : ProjNet.CoordinateSystems.Info + { + public ProjNet.CoordinateSystems.LinearUnit AxisUnit { get; } + public double InverseFlattening { get; } + public bool IsIvfDefinitive { get; } + public double SemiMajorAxis { get; } + public double SemiMinorAxis { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.Ellipsoid Airy1830 { get; } + public static ProjNet.CoordinateSystems.Ellipsoid Bessel1841 { get; } + public static ProjNet.CoordinateSystems.Ellipsoid Clarke1866 { get; } + public static ProjNet.CoordinateSystems.Ellipsoid Clarke1880 { get; } + public static ProjNet.CoordinateSystems.Ellipsoid GRS80 { get; } + public static ProjNet.CoordinateSystems.Ellipsoid International1924 { get; } + public static ProjNet.CoordinateSystems.Ellipsoid Sphere { get; } + public static ProjNet.CoordinateSystems.Ellipsoid WGS72 { get; } + public static ProjNet.CoordinateSystems.Ellipsoid WGS84 { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.Ellipsoid WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.Ellipsoid WithName(string name) { } + } + public sealed class EngineeringCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + public EngineeringCoordinateSystem(ProjNet.CoordinateSystems.EngineeringDatum engineeringDatum, string coordinateSystemType, System.Collections.Generic.IReadOnlyList axisInfo, System.Collections.Generic.IReadOnlyList units, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public System.Collections.Generic.IReadOnlyList AxisUnits { get; } + public string CoordinateSystemType { get; } + public ProjNet.CoordinateSystems.EngineeringDatum EngineeringDatum { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.EngineeringCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.EngineeringCoordinateSystem WithName(string name) { } + } + public sealed class EngineeringDatum : ProjNet.CoordinateSystems.Datum + { + public EngineeringDatum(string name, string authority, long authorityCode, string alias, string remarks, string abbreviation) { } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.EngineeringDatum WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.EngineeringDatum WithEnsemble(ProjNet.CoordinateSystems.DatumEnsemble? ensemble) { } + public new ProjNet.CoordinateSystems.EngineeringDatum WithName(string name) { } + } + public class FittedCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + protected FittedCoordinateSystem(ProjNet.CoordinateSystems.CoordinateSystem baseSystem, ProjNet.CoordinateSystems.Transformations.MathTransform transform, string name, string authority, long code, string alias, string remarks, string abbreviation, System.Collections.Generic.IReadOnlyList? axisInfo = null) { } + public ProjNet.CoordinateSystems.CoordinateSystem BaseCoordinateSystem { get; } + public ProjNet.CoordinateSystems.Transformations.MathTransform ToBaseTransform { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public string ToBase() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.FittedCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.FittedCoordinateSystem WithName(string name) { } + } + public class GeocentricCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + public ProjNet.CoordinateSystems.HorizontalDatum HorizontalDatum { get; } + public ProjNet.CoordinateSystems.LinearUnit LinearUnit { get; } + public ProjNet.CoordinateSystems.PrimeMeridian PrimeMeridian { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.GeocentricCoordinateSystem WGS84 { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.GeocentricCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.GeocentricCoordinateSystem WithName(string name) { } + } + public class GeographicCoordinateSystem : ProjNet.CoordinateSystems.HorizontalCoordinateSystem + { + public ProjNet.CoordinateSystems.AngularUnit AngularUnit { get; } + public int NumConversionToWGS84 { get; } + public ProjNet.CoordinateSystems.PrimeMeridian PrimeMeridian { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.GeographicCoordinateSystem WGS84 { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public ProjNet.CoordinateSystems.Wgs84ConversionInfo GetWgs84ConversionInfo(int index) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.GeographicCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.GeographicCoordinateSystem WithName(string name) { } + } + public abstract class HorizontalCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + public ProjNet.CoordinateSystems.HorizontalDatum HorizontalDatum { get; } + } + public class HorizontalDatum : ProjNet.CoordinateSystems.Datum + { + public ProjNet.CoordinateSystems.Ellipsoid Ellipsoid { get; } + public override string WKT { get; } + public ProjNet.CoordinateSystems.Wgs84ConversionInfo? Wgs84Parameters { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.HorizontalDatum ED50 { get; } + public static ProjNet.CoordinateSystems.HorizontalDatum ETRF89 { get; } + public static ProjNet.CoordinateSystems.HorizontalDatum WGS72 { get; } + public static ProjNet.CoordinateSystems.HorizontalDatum WGS84 { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.HorizontalDatum WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.HorizontalDatum WithEnsemble(ProjNet.CoordinateSystems.DatumEnsemble? ensemble) { } + public new ProjNet.CoordinateSystems.HorizontalDatum WithName(string name) { } + public ProjNet.CoordinateSystems.HorizontalDatum WithWgs84Parameters(ProjNet.CoordinateSystems.Wgs84ConversionInfo? toWgs84) { } + } + public interface IInfo + { + string Abbreviation { get; } + string Alias { get; } + string Authority { get; } + long AuthorityCode { get; } + string Name { get; } + string Remarks { get; } + string WKT { get; } + string XML { get; } + bool EqualParams(object obj); + } + public interface IProjection : ProjNet.CoordinateSystems.IInfo + { + string ClassName { get; } + int NumParameters { get; } + ProjNet.CoordinateSystems.ProjectionParameter GetParameter(int index); + ProjNet.CoordinateSystems.ProjectionParameter? GetParameter(string name); + } + public interface IUnit : ProjNet.CoordinateSystems.IInfo { } + public abstract class Info : ProjNet.CoordinateSystems.IInfo + { + public string Abbreviation { get; } + public string Alias { get; } + public string Authority { get; } + public long AuthorityCode { get; } + public string Name { get; } + public string Remarks { get; } + public abstract string WKT { get; } + public abstract string XML { get; } + public abstract bool EqualParams(object obj); + public override string ToString() { } + public ProjNet.CoordinateSystems.Info WithAuthority(string authority, long code) { } + public ProjNet.CoordinateSystems.Info WithName(string name) { } + } + public class LinearUnit : ProjNet.CoordinateSystems.Info, ProjNet.CoordinateSystems.IInfo, ProjNet.CoordinateSystems.IUnit + { + public LinearUnit(double metersPerUnit, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public double MetersPerUnit { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.LinearUnit ClarkesFoot { get; } + public static ProjNet.CoordinateSystems.LinearUnit Foot { get; } + public static ProjNet.CoordinateSystems.LinearUnit Metre { get; } + public static ProjNet.CoordinateSystems.LinearUnit NauticalMile { get; } + public static ProjNet.CoordinateSystems.LinearUnit USSurveyFoot { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.LinearUnit WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.LinearUnit WithName(string name) { } + } + public sealed class Parameter + { + public Parameter(string name, double value) { } + public string Name { get; } + public double Value { get; } + } + public sealed class ParametricCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + public ParametricCoordinateSystem(ProjNet.CoordinateSystems.ParametricUnit parametricUnit, ProjNet.CoordinateSystems.ParametricDatum parametricDatum, ProjNet.CoordinateSystems.AxisInfo axisInfo, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public ProjNet.CoordinateSystems.ParametricDatum ParametricDatum { get; } + public ProjNet.CoordinateSystems.ParametricUnit ParametricUnit { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.ParametricCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.ParametricCoordinateSystem WithName(string name) { } + } + public sealed class ParametricDatum : ProjNet.CoordinateSystems.Datum + { + public ParametricDatum(string name, string authority, long authorityCode, string alias, string remarks, string abbreviation) { } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.ParametricDatum WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.ParametricDatum WithEnsemble(ProjNet.CoordinateSystems.DatumEnsemble? ensemble) { } + public new ProjNet.CoordinateSystems.ParametricDatum WithName(string name) { } + } + public sealed class ParametricUnit : ProjNet.CoordinateSystems.Info, ProjNet.CoordinateSystems.IInfo, ProjNet.CoordinateSystems.IUnit + { + public ParametricUnit(double conversionFactor, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public double ConversionFactor { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.ParametricUnit WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.ParametricUnit WithName(string name) { } + } + public class PrimeMeridian : ProjNet.CoordinateSystems.Info + { + public ProjNet.CoordinateSystems.AngularUnit AngularUnit { get; } + public double Longitude { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Athens { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Bern { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Bogota { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Brussels { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Ferro { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Greenwich { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Jakarta { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Lisbon { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Madrid { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Oslo { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Paris { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Rome { get; } + public static ProjNet.CoordinateSystems.PrimeMeridian Stockholm { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.PrimeMeridian WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.PrimeMeridian WithName(string name) { } + } + public class ProjectedCoordinateSystem : ProjNet.CoordinateSystems.HorizontalCoordinateSystem + { + public ProjNet.CoordinateSystems.GeographicCoordinateSystem GeographicCoordinateSystem { get; } + public ProjNet.CoordinateSystems.LinearUnit LinearUnit { get; } + public ProjNet.CoordinateSystems.IProjection Projection { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.ProjectedCoordinateSystem WebMercator { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.ProjectedCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.ProjectedCoordinateSystem WithName(string name) { } + public static ProjNet.CoordinateSystems.ProjectedCoordinateSystem WGS84_UTM(int zone, bool zoneIsNorth) { } + } + public class Projection : ProjNet.CoordinateSystems.Info, ProjNet.CoordinateSystems.IInfo, ProjNet.CoordinateSystems.IProjection + { + public string ClassName { get; } + public int NumParameters { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.CoordinateSystems.ProjectionParameter GetParameter(int index) { } + public ProjNet.CoordinateSystems.ProjectionParameter? GetParameter(string name) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.Projection WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.Projection WithName(string name) { } + } + public sealed class ProjectionParameter + { + public ProjectionParameter(string name, double value) { } + public string Name { get; } + public double Value { get; } + public string WKT { get; } + public string XML { get; } + public override string ToString() { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + } + public sealed class TemporalCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + public TemporalCoordinateSystem(ProjNet.CoordinateSystems.TimeUnit timeUnit, ProjNet.CoordinateSystems.TemporalDatum temporalDatum, ProjNet.CoordinateSystems.AxisInfo axisInfo, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public ProjNet.CoordinateSystems.TemporalDatum TemporalDatum { get; } + public ProjNet.CoordinateSystems.TimeUnit TimeUnit { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.TemporalCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.TemporalCoordinateSystem WithName(string name) { } + } + public sealed class TemporalDatum : ProjNet.CoordinateSystems.Datum + { + public TemporalDatum(string timeOrigin, string name, string authority, long authorityCode, string alias, string remarks, string abbreviation) { } + public string TimeOrigin { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.TemporalDatum WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.TemporalDatum WithEnsemble(ProjNet.CoordinateSystems.DatumEnsemble? ensemble) { } + public new ProjNet.CoordinateSystems.TemporalDatum WithName(string name) { } + } + public sealed class TimeUnit : ProjNet.CoordinateSystems.Info, ProjNet.CoordinateSystems.IInfo, ProjNet.CoordinateSystems.IUnit + { + public TimeUnit(double conversionFactor, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public double ConversionFactor { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.TimeUnit WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.TimeUnit WithName(string name) { } + } + public class Unit : ProjNet.CoordinateSystems.Info, ProjNet.CoordinateSystems.IInfo, ProjNet.CoordinateSystems.IUnit + { + public double ConversionFactor { get; } + public override string WKT { get; } + public override string XML { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public System.Xml.Linq.XElement ToXml() { } + } + public class VerticalCoordinateSystem : ProjNet.CoordinateSystems.CoordinateSystem + { + public VerticalCoordinateSystem(ProjNet.CoordinateSystems.LinearUnit linearUnit, ProjNet.CoordinateSystems.VerticalDatum verticalDatum, ProjNet.CoordinateSystems.AxisInfo axisInfo, string name, string authority, long authorityCode, string alias, string abbreviation, string remarks) { } + public ProjNet.CoordinateSystems.LinearUnit LinearUnit { get; } + public ProjNet.CoordinateSystems.VerticalDatum VerticalDatum { get; } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.VerticalCoordinateSystem ODN { get; } + public override bool EqualParams(object obj) { } + public override ProjNet.CoordinateSystems.IUnit GetUnits(int dimension) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public override System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.VerticalCoordinateSystem WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.VerticalCoordinateSystem WithName(string name) { } + } + public class VerticalDatum : ProjNet.CoordinateSystems.Datum + { + public VerticalDatum(ProjNet.CoordinateSystems.DatumType type, string name, string authority, long code, string alias, string remarks, string abbreviation) { } + public override string WKT { get; } + public override string XML { get; } + public static ProjNet.CoordinateSystems.VerticalDatum ODN { get; } + public override bool EqualParams(object obj) { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public new ProjNet.CoordinateSystems.VerticalDatum WithAuthority(string authority, long code) { } + public new ProjNet.CoordinateSystems.VerticalDatum WithEnsemble(ProjNet.CoordinateSystems.DatumEnsemble? ensemble) { } + public new ProjNet.CoordinateSystems.VerticalDatum WithName(string name) { } + } + public sealed class Wgs84ConversionInfo : System.IEquatable + { + public string AreaOfUse; + public double Dx; + public double Dy; + public double Dz; + public double Ex; + public double Ey; + public double Ez; + public double Ppm; + public Wgs84ConversionInfo() { } + public Wgs84ConversionInfo(double dx, double dy, double dz, double ex, double ey, double ez, double ppm) { } + public Wgs84ConversionInfo(double dx, double dy, double dz, double ex, double ey, double ez, double ppm, string areaOfUse) { } + public bool HasZeroValuesOnly { get; } + public string WKT { get; } + public string XML { get; } + public bool Equals(ProjNet.CoordinateSystems.Wgs84ConversionInfo? obj) { } + public override bool Equals(object? obj) { } + public double[] GetAffineTransform() { } + public override int GetHashCode() { } + public override string ToString() { } + public ProjNet.IO.Wkt.WktNode ToWktNode() { } + public ProjNet.IO.Wkt.WktNode ToWktNode(ProjNet.IO.Wkt.WktVersion version) { } + public System.Xml.Linq.XElement ToXml() { } + public void WriteAffineTransform(System.Span destination) { } + } +} +namespace ProjNet.CoordinateSystems.Projections +{ + public sealed class LambertAzimuthalEqualAreaProjection : ProjNet.CoordinateSystems.Projections.MapProjection + { + public LambertAzimuthalEqualAreaProjection(System.Collections.Generic.IEnumerable parameters) { } + public LambertAzimuthalEqualAreaProjection(System.Collections.Generic.IEnumerable parameters, ProjNet.CoordinateSystems.Projections.MapProjection? inverse) { } + public override ProjNet.CoordinateSystems.Transformations.MathTransform Inverse() { } + protected override void MetersToRadians(ref double x, ref double y) { } + protected override void RadiansToMeters(ref double lon, ref double lat) { } + } + public abstract class MapProjection : ProjNet.CoordinateSystems.Transformations.MathTransform, ProjNet.CoordinateSystems.IInfo, ProjNet.CoordinateSystems.IProjection + { + [System.Obsolete("Use DblLong instead.")] + protected const double DBLLONG = 4.61168601E+18D; + protected const double DblLong = 4.61168601E+18D; + [System.Obsolete("Use Eps10 instead.")] + protected const double EPS10 = 1E-10D; + [System.Obsolete("Use Eps7 instead.")] + protected const double EPS7 = 1E-07D; + [System.Obsolete("Use Epsln instead.")] + protected const double EPSLN = 1E-10D; + protected const double Eps10 = 1E-10D; + protected const double Eps7 = 1E-07D; + protected const double Epsln = 1E-10D; + [System.Obsolete("Use FortPi instead.")] + protected const double FORTPI = 0.7853981633974483D; + [System.Obsolete("Use FortPi instead.")] + protected const double FORT_PI = 0.7853981633974483D; + protected const double FortPi = 0.7853981633974483D; + [System.Obsolete("Use HalfPi instead.")] + protected const double HALFPI = 1.5707963267948966D; + [System.Obsolete("Use HalfPi instead.")] + protected const double HALF_PI = 1.5707963267948966D; + [System.Obsolete("Use HugeVal instead.")] + protected const double HUGEVAL = double.NaN; + [System.Obsolete("Use HugeVal instead.")] + protected const double HUGE_VAL = double.NaN; + protected const double HalfPi = 1.5707963267948966D; + protected const double HugeVal = double.NaN; + [System.Obsolete("Use MaxVal instead.")] + protected const double MAXVAL = 4D; + [System.Obsolete("Use MaxVal instead.")] + protected const double MAX_VAL = 4D; + protected const double MaxVal = 4D; + protected const double PI = 3.141592653589793D; + protected readonly ProjNet.CoordinateSystems.Projections.ProjectionParameterSet Parameters; + [System.Obsolete("Use TwoPi instead.")] + protected const double TWOPI = 6.283185307179586D; + [System.Obsolete("Use TwoPi instead.")] + protected const double TWO_PI = 6.283185307179586D; + protected const double TwoPi = 6.283185307179586D; + protected double centralMeridian; + protected readonly double e; + protected readonly double en0; + protected readonly double en1; + protected readonly double en2; + protected readonly double en3; + protected readonly double en4; + protected readonly double es; + protected readonly double falseEasting; + protected readonly double falseNorthing; + protected ProjNet.CoordinateSystems.Transformations.MathTransform? inverse; + protected readonly double latOrigin; + protected readonly double metersPerUnit; + protected const double prjMAXLONG = 2147483647D; + protected readonly double reciprocalMetersPerUnit; + protected readonly double scaleFactor; + protected readonly double semiMajor; + protected readonly double semiMinor; + protected MapProjection(System.Collections.Generic.IEnumerable parameters) { } + protected MapProjection(System.Collections.Generic.IEnumerable parameters, ProjNet.CoordinateSystems.Projections.MapProjection? inverse) { } + public string Abbreviation { get; } + public string Alias { get; } + public string Authority { get; } + public long AuthorityCode { get; } + protected double Central_parallel { get; } + public string ClassName { get; } + public override sealed int DimSource { get; } + public override sealed int DimTarget { get; } + protected virtual bool HasInverseSupport { get; } + protected double InverseSphericalRadius { get; } + protected bool IsInverse { get; } + public override bool IsInvertible { get; } + protected double Lon_origin { get; set; } + public string Name { get; } + public int NumParameters { get; } + protected double Phi0 { get; } + public string Remarks { get; } + protected double SphericalRadius { get; } + public override string WKT { get; } + public override string XML { get; } + [System.Obsolete("Use centralMeridian instead.")] + protected double central_meridian { get; set; } + [System.Obsolete("Use falseEasting instead.")] + protected double false_easting { get; } + [System.Obsolete("Use falseNorthing instead.")] + protected double false_northing { get; } + [System.Obsolete("Use latOrigin instead.")] + protected double lat_origin { get; } + [System.Obsolete("Use scaleFactor instead.")] + protected double scale_factor { get; } + protected virtual void DegreesToMeters(ref double lon, ref double lat) { } + protected virtual void DegreesToMeters(System.Span lons, System.Span lats, int strideX, int strideY) { } + protected virtual void DegreesToTarget(ref double lon, ref double lat) { } + protected virtual void DegreesToTarget(System.Span lons, System.Span lats, int strideX, int strideY) { } + public bool EqualParams(object obj) { } + protected ProjNet.CoordinateSystems.Transformations.MathTransform GetOrCreateInverse(System.Func createInverse) { } + public ProjNet.CoordinateSystems.ProjectionParameter GetParameter(int index) { } + public ProjNet.CoordinateSystems.ProjectionParameter? GetParameter(string name) { } + protected double Inv_mlfn(double arg) { } + public override void Invert() { } + protected void Invert(bool invertInverse) { } + protected virtual void MetersToDegrees(ref double x, ref double y) { } + protected virtual void MetersToDegrees(System.Span xs, System.Span ys, int strideX, int strideY) { } + protected abstract void MetersToRadians(ref double x, ref double y); + protected virtual void MetersToRadians(System.Span xs, System.Span ys, int strideX, int strideY) { } + protected void MetersToTarget(ref double x, ref double y) { } + protected void MetersToTarget(System.Span xs, System.Span ys, int strideX, int strideY) { } + protected double Mlfn(double phi, double sphi, double cphi) { } + protected abstract void RadiansToMeters(ref double lon, ref double lat); + protected virtual void RadiansToMeters(System.Span lons, System.Span lats, int strideX, int strideY) { } + protected virtual void SourceToDegrees(ref double x, ref double y) { } + protected virtual void SourceToDegrees(System.Span xs, System.Span ys, int strideX, int strideY) { } + protected void SourceToMeters(ref double x, ref double y) { } + protected void SourceToMeters(System.Span xs, System.Span ys, int strideX, int strideY) { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override System.Xml.Linq.XElement ToXml() { } + public override sealed void Transform(ref double x, ref double y, ref double z) { } + protected override sealed void TransformCore(System.Span xs, System.Span ys, System.Span zs, int strideX, int strideY, int strideZ) { } + protected static double Adjust_lon(double x) { } + protected static double Asinz(double con) { } + protected static double Authlat(double beta, double[] apa) { } + protected static double[] Authset(double es) { } + [System.Obsolete("Use ProjNet.CoordinateSystems.CoordinateSystemUtilities.CalcUtmZone instead.")] + public static long CalcUtmZone(double lon) { } + protected static System.Collections.Generic.List CloneParametersList(System.Collections.Generic.IEnumerable projectionParameters) { } + protected static double Hypot(double x, double y) { } + [System.Obsolete("Use ProjNet.CoordinateSystems.CoordinateSystemUtilities.LatitudeToRadians instead" + + ".")] + protected static double LatitudeToRadians(double y, bool edge) { } + [System.Obsolete("Use ProjNet.CoordinateSystems.CoordinateSystemUtilities.LongitudeToRadians instea" + + "d.")] + protected static double LongitudeToRadians(double x, bool edge) { } + protected static double Mlfn(double e0, double e1, double e2, double e3, double phi) { } + protected static double Msfnz(double eccent, double sinphi, double cosphi) { } + protected static double Phi1z(double eccent, double qs, out long flag) { } + protected static double Phi2z(double eccent, double ts, out long flag) { } + protected static double Qsfn(double sinphi, double eccent, double one_es) { } + protected static double Qsfnz(double sinphi, double eccent) { } + protected static double Sign(double x) { } + protected static void Sincos(double val, out double sin_val, out double cos_val) { } + protected static double Tsfnz(double eccent, double phi, double sinphi) { } + } + public sealed class ProjectionParameterSet : System.Collections.Generic.Dictionary, System.IEquatable + { + public ProjectionParameterSet(System.Collections.Generic.IEnumerable parameters) { } + public bool Equals(ProjNet.CoordinateSystems.Projections.ProjectionParameterSet? other) { } + public override bool Equals(object? obj) { } + public ProjNet.CoordinateSystems.ProjectionParameter? Find(string name) { } + public ProjNet.CoordinateSystems.ProjectionParameter GetAtIndex(int index) { } + public override int GetHashCode() { } + public double GetOptionalParameterValue(string name, double value, params string[] alternateNames) { } + public double GetParameterValue(string parameterName, params string[] alternateNames) { } + public System.Collections.Generic.IEnumerable ToProjectionParameter() { } + } + public class ProjectionsRegistry + { + public ProjectionsRegistry() { } + public static void Register(string name, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { } + public static void RegisterAlias(string aliasName, string existingName) { } + } +} +namespace ProjNet.CoordinateSystems.Transformations +{ + public sealed class AffineTransform : ProjNet.CoordinateSystems.Transformations.MathTransform + { + public AffineTransform(double[,] matrix) { } + public AffineTransform(double m00, double m01, double m02, double m10, double m11, double m12) { } + public override int DimSource { get; } + public override int DimTarget { get; } + public override string WKT { get; } + public override string XML { get; } + public double[,] GetMatrix() { } + public override ProjNet.CoordinateSystems.Transformations.MathTransform Inverse() { } + public override void Invert() { } + public override ProjNet.IO.Wkt.WktNode ToWktNode() { } + public override void Transform(ref double x, ref double y, ref double z) { } + } + public sealed class CoordinateTransformation : ProjNet.CoordinateSystems.Transformations.ICoordinateTransformation, ProjNet.CoordinateSystems.Transformations.ICoordinateTransformationCore + { + public string AreaOfUse { get; } + public string Authority { get; } + public long AuthorityCode { get; } + public ProjNet.CoordinateSystems.Transformations.MathTransform MathTransform { get; } + public string Name { get; } + public string Remarks { get; } + public ProjNet.CoordinateSystems.CoordinateSystem SourceCS { get; } + public ProjNet.CoordinateSystems.CoordinateSystem TargetCS { get; } + public ProjNet.CoordinateSystems.Transformations.TransformType TransformType { get; } + } + public class CoordinateTransformationFactory + { + public CoordinateTransformationFactory() { } + public ProjNet.CoordinateSystems.Transformations.ICoordinateTransformation CreateFromCoordinateSystems(ProjNet.CoordinateSystems.CoordinateSystem sourceCS, ProjNet.CoordinateSystems.CoordinateSystem targetCS) { } + public static void ConfigureGridResolution(ProjNet.Resources.IGridResourceFetchClient? fetchClient = null, System.Collections.Generic.IEnumerable? localDirectories = null, string? cacheDirectory = null, ProjNet.Resources.GridResourceResolutionMode mode = 0) { } + } + [System.Flags] + public enum DomainFlags + { + Inside = 1, + Outside = 2, + Discontinuous = 4, + } + public sealed class GeographicTransform : ProjNet.CoordinateSystems.Transformations.MathTransform + { + public override int DimSource { get; } + public override int DimTarget { get; } + public ProjNet.CoordinateSystems.GeographicCoordinateSystem SourceGCS { get; } + public ProjNet.CoordinateSystems.GeographicCoordinateSystem TargetGCS { get; } + public override string WKT { get; } + public override string XML { get; } + public override ProjNet.CoordinateSystems.Transformations.MathTransform Inverse() { } + public override void Invert() { } + public override sealed void Transform(ref double x, ref double y, ref double z) { } + } + public interface ICoordinateTransformation : ProjNet.CoordinateSystems.Transformations.ICoordinateTransformationCore + { + string AreaOfUse { get; } + string Authority { get; } + long AuthorityCode { get; } + ProjNet.CoordinateSystems.Transformations.MathTransform MathTransform { get; } + string Name { get; } + string Remarks { get; } + ProjNet.CoordinateSystems.Transformations.TransformType TransformType { get; } + } + public interface ICoordinateTransformationCore + { + ProjNet.CoordinateSystems.CoordinateSystem SourceCS { get; } + ProjNet.CoordinateSystems.CoordinateSystem TargetCS { get; } + } + public abstract class MathTransform + { + protected const double D2R = 0.017453292519943295D; + protected const double R2D = 57.29577951308232D; + protected MathTransform() { } + public abstract int DimSource { get; } + public abstract int DimTarget { get; } + public virtual bool IsInvertible { get; } + public virtual string WKT { get; } + public virtual string XML { get; } + public virtual double[,] Derivative(double[] point) { } + public virtual System.Collections.Generic.List GetCodomainConvexHull(System.Collections.Generic.List points) { } + public virtual System.Collections.Generic.List GetCodomainConvexHull(System.ReadOnlySpan points) { } + public virtual ProjNet.CoordinateSystems.Transformations.DomainFlags GetDomainFlags(System.Collections.Generic.List points) { } + public virtual ProjNet.CoordinateSystems.Transformations.DomainFlags GetDomainFlags(System.ReadOnlySpan points) { } + public virtual bool Identity() { } + public abstract ProjNet.CoordinateSystems.Transformations.MathTransform Inverse(); + public abstract void Invert(); + public virtual ProjNet.IO.Wkt.WktNode ToWktNode() { } + public virtual System.Xml.Linq.XElement ToXml() { } + public void Transform(System.Span xyzs) { } + public double[] Transform(double[] point) { } + public void Transform(System.ReadOnlySpan point, System.Span result) { } + [return: System.Runtime.CompilerServices.TupleElementNames(new string[] { + "X", + "Y"})] + public System.ValueTuple Transform(double x, double y) { } + public void Transform(ref double x, ref double y) { } + public void Transform(System.Span xys, System.Span zs = default, int strideZ = 0) { } + [return: System.Runtime.CompilerServices.TupleElementNames(new string[] { + "O1", + "O2", + "O3"})] + public System.ValueTuple Transform(double x, double y, double z) { } + public abstract void Transform(ref double x, ref double y, ref double z); + public void Transform(System.Span xs, System.Span ys, int strideX = 1, int strideY = 1) { } + public void Transform(System.Span xs, System.Span ys, System.Span zs, int strideX = 1, int strideY = 1, int strideZ = 1) { } + protected virtual void TransformCore(System.Span xs, System.Span ys, System.Span zs, int strideX, int strideY, int strideZ) { } + public System.Collections.Generic.IList TransformList(System.Collections.Generic.IList points) { } + protected static void AddInPlace(System.Span vals, int stride, double addend) { } + protected static void AddThenMultiplyInPlace(System.Span vals, int stride, double addend, double multiplier) { } + protected static double DegreesToRadians(double deg) { } + protected static void DegreesToRadians(System.Span degrees, int stride) { } + protected static void MultiplyInPlace(System.Span vals, int stride, double multiplier) { } + protected static void MultiplyThenAddInPlace(System.Span vals, int stride, double multiplier, double addend) { } + protected static double RadiansToDegrees(double rad) { } + protected static void RadiansToDegrees(System.Span radians, int stride) { } + } + public enum TransformType + { + Other = 0, + Conversion = 1, + Transformation = 2, + ConversionAndTransformation = 3, + } +} +namespace ProjNet.Data +{ + public readonly struct CoordinateSystemDefinition : System.IEquatable + { + public CoordinateSystemDefinition(int Srid, string Wkt) { } + public int Srid { get; init; } + public string Wkt { get; init; } + } + public readonly struct CoordinateSystemEntry : System.IEquatable + { + public CoordinateSystemEntry(int Srid, ProjNet.CoordinateSystems.CoordinateSystem CoordinateSystem) { } + public ProjNet.CoordinateSystems.CoordinateSystem CoordinateSystem { get; init; } + public int Srid { get; init; } + } + public interface ICoordinateSystemDefinitionProvider + { + System.Collections.Generic.IEnumerable GetDefinitions(); + } + public sealed class ManagedCoordinateSystemDefinitionProvider : ProjNet.Data.ICoordinateSystemDefinitionProvider + { + public ManagedCoordinateSystemDefinitionProvider() { } + public System.Collections.Generic.IEnumerable GetCoordinateSystems() { } + public System.Collections.Generic.IEnumerable GetDefinitions() { } + } +} +namespace ProjNet.Geometries +{ + public struct XY : System.IEquatable + { + public double X; + public double Y; + public XY(double x, double y) { } + public bool Equals(ProjNet.Geometries.XY other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static bool operator !=(ProjNet.Geometries.XY left, ProjNet.Geometries.XY right) { } + public static bool operator ==(ProjNet.Geometries.XY left, ProjNet.Geometries.XY right) { } + } + public struct XYZ : System.IEquatable + { + public double X; + public double Y; + public double Z; + public XYZ(double x, double y, double z) { } + public bool Equals(ProjNet.Geometries.XYZ other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public override string ToString() { } + public static bool operator !=(ProjNet.Geometries.XYZ left, ProjNet.Geometries.XYZ right) { } + public static bool operator ==(ProjNet.Geometries.XYZ left, ProjNet.Geometries.XYZ right) { } + } +} +namespace ProjNet.IO.CoordinateSystems +{ + public static class CoordinateSystemWktReader + { + public static ProjNet.CoordinateSystems.IInfo Parse(System.ReadOnlySpan wkt) { } + public static ProjNet.CoordinateSystems.IInfo Parse(string wkt) { } + } + public static class MathTransformWktReader + { + public static ProjNet.CoordinateSystems.Transformations.MathTransform Parse(string wkt) { } + } + public static class ProjJsonReader + { + public static ProjNet.CoordinateSystems.IInfo Parse(System.ReadOnlySpan json) { } + public static ProjNet.CoordinateSystems.IInfo Parse(string json) { } + } + public static class ProjJsonWriter + { + public static string ToJson(ProjNet.CoordinateSystems.CoordinateSystem coordinateSystem) { } + public static void WriteTo(System.Text.Json.Utf8JsonWriter writer, ProjNet.CoordinateSystems.CoordinateSystem coordinateSystem) { } + } +} +namespace ProjNet.IO.Wkt +{ + public sealed class WktIdentifier : ProjNet.IO.Wkt.WktNode + { + public WktIdentifier(string name) { } + public string Name { get; } + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) { } + public override string ToString() { } + } + public sealed class WktInteger : ProjNet.IO.Wkt.WktNode + { + public WktInteger(int value) { } + public int Value { get; } + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) { } + public override string ToString() { } + } + public sealed class WktKeywordNode : ProjNet.IO.Wkt.WktNode + { + public WktKeywordNode(string keyword, params ProjNet.IO.Wkt.WktNode[] children) { } + public WktKeywordNode(string keyword, System.Collections.Generic.IReadOnlyList children) { } + public System.Collections.Generic.IReadOnlyList Children { get; } + public string Keyword { get; } + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) { } + public override string ToString() { } + } + public abstract class WktNode + { + protected WktNode() { } + public abstract string ToFormattedString(int indentLevel = 0, int indentSize = 4); + public abstract override string ToString() { } + } + public sealed class WktNumber : ProjNet.IO.Wkt.WktNode + { + public WktNumber(double value) { } + public double Value { get; } + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) { } + public override string ToString() { } + } + [System.Serializable] + public sealed class WktParseException : System.Exception + { + public WktParseException() { } + public WktParseException(string message) { } + public WktParseException(string message, System.Exception innerException) { } + } + public sealed class WktQuotedString : ProjNet.IO.Wkt.WktNode + { + public WktQuotedString(string value) { } + public string Value { get; } + public override string ToFormattedString(int indentLevel = 0, int indentSize = 4) { } + public override string ToString() { } + } + public enum WktVersion + { + Wkt1 = 0, + Wkt22019 = 1, + } +} +namespace ProjNet.Resources +{ + public enum GridResourceResolutionMode + { + LocalOnly = 0, + LocalThenNetwork = 1, + } + public sealed class HttpGridResourceFetchClient : ProjNet.Resources.IGridResourceFetchClient + { + public HttpGridResourceFetchClient(System.Uri baseUri, System.Net.Http.HttpClient? httpClient = null) { } + public HttpGridResourceFetchClient(string baseUrl, System.Net.Http.HttpClient? httpClient = null) { } + public bool TryFetch(string gridName, string targetFilePath) { } + public System.Threading.Tasks.Task TryFetchAsync(string gridName, string targetFilePath, System.Threading.CancellationToken cancellationToken = default) { } + } + public interface IGridResourceFetchClient + { + bool TryFetch(string gridName, string targetFilePath); + System.Threading.Tasks.Task TryFetchAsync(string gridName, string targetFilePath, System.Threading.CancellationToken cancellationToken = default); + } + public sealed class NoOpGridResourceFetchClient : ProjNet.Resources.IGridResourceFetchClient + { + public NoOpGridResourceFetchClient() { } + public bool TryFetch(string gridName, string targetFilePath) { } + public System.Threading.Tasks.Task TryFetchAsync(string gridName, string targetFilePath, System.Threading.CancellationToken cancellationToken = default) { } + } +} diff --git a/src/ProjNet/Resources/GridResourceCacheManifest.cs b/src/ProjNet/Resources/GridResourceCacheManifest.cs new file mode 100644 index 00000000..4b433054 --- /dev/null +++ b/src/ProjNet/Resources/GridResourceCacheManifest.cs @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Resources; + +using System; +using System.Globalization; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +/// +/// Provides cache-manifest persistence and validation for downloaded grid resources. +/// +internal static class GridResourceCacheManifest +{ + private const string ManifestFileSuffix = ".projnet-fetch.json"; + + /// + /// Deletes the cached grid file and its manifest, ignoring files that do not exist. + /// + /// The cached grid file path. + internal static void DeleteArtifacts(string targetFilePath) + { + TryDelete(targetFilePath); + TryDelete(GetManifestPath(targetFilePath)); + } + + /// + /// Gets the cache-manifest path associated with a downloaded grid file. + /// + /// The cached grid file path. + /// The sidecar manifest path. + internal static string GetManifestPath(string targetFilePath) => $"{targetFilePath}{ManifestFileSuffix}"; + + /// + /// Determines whether the cached grid file is valid according to its manifest. + /// + /// + /// A missing manifest sidecar is treated as valid when the cached grid file itself exists. + /// Manifest validation is only enforced once the sidecar file has been written. + /// + /// The cached grid file path. + /// when the cache entry is usable; otherwise . + internal static bool IsValid(string targetFilePath) + { + if (!File.Exists(targetFilePath)) + { + return false; + } + + string manifestPath = GetManifestPath(targetFilePath); + if (!File.Exists(manifestPath)) + { + return true; + } + + try + { + using FileStream stream = File.OpenRead(manifestPath); + using var document = JsonDocument.Parse(stream); + JsonElement root = document.RootElement; + + if (!root.TryGetProperty("content_length", out JsonElement contentLengthElement) + || !contentLengthElement.TryGetInt64(out long expectedLength) + || expectedLength < 0) + { + return false; + } + + if (!root.TryGetProperty("sha256", out JsonElement sha256Element)) + { + return false; + } + + string? expectedHash = sha256Element.GetString(); + if (string.IsNullOrWhiteSpace(expectedHash)) + { + return false; + } + + long actualLength = new FileInfo(targetFilePath).Length; + if (actualLength != expectedLength) + { + return false; + } + + string actualHash = ComputeSha256(targetFilePath); + return string.Equals(actualHash, expectedHash, StringComparison.OrdinalIgnoreCase); + } + catch (IOException) + { + return false; + } + catch (JsonException) + { + return false; + } + catch (UnauthorizedAccessException) + { + return false; + } + } + + /// + /// Writes a cache manifest for the downloaded grid file. + /// + /// The cached grid file path. + /// The absolute source URI used for the download. + internal static void Write(string targetFilePath, string sourceUri) + { + ArgumentGuard.ThrowIfNull(targetFilePath, nameof(targetFilePath)); + ArgumentGuard.ThrowIfNull(sourceUri, nameof(sourceUri)); + + if (!File.Exists(targetFilePath)) + { + ArgumentGuard.ThrowArgument("The cached grid file must exist before writing its manifest.", nameof(targetFilePath)); + } + + string manifestPath = GetManifestPath(targetFilePath); + long contentLength = new FileInfo(targetFilePath).Length; + string sha256 = ComputeSha256(targetFilePath); + + using var stream = new FileStream(manifestPath, FileMode.Create, FileAccess.Write, FileShare.None); + using var writer = new Utf8JsonWriter(stream); + writer.WriteStartObject(); + writer.WriteString("source_uri", sourceUri); + writer.WriteNumber("content_length", contentLength); + writer.WriteString("sha256", sha256); + writer.WriteEndObject(); + writer.Flush(); + } + + private static string ComputeSha256(string targetFilePath) + { + using FileStream stream = File.OpenRead(targetFilePath); + using var hashAlgorithm = SHA256.Create(); + byte[] hash = hashAlgorithm.ComputeHash(stream); + + var builder = new StringBuilder(hash.Length * 2); + foreach (byte octet in hash) + { + builder.Append(octet.ToString("x2", CultureInfo.InvariantCulture)); + } + + return builder.ToString(); + } + + private static void TryDelete(string path) + { + if (!File.Exists(path)) + { + return; + } + + try + { + File.Delete(path); + } + catch (IOException) + { + } + catch (UnauthorizedAccessException) + { + } + } +} diff --git a/src/ProjNet/Resources/GridResourceResolutionMode.cs b/src/ProjNet/Resources/GridResourceResolutionMode.cs new file mode 100644 index 00000000..d782262e --- /dev/null +++ b/src/ProjNet/Resources/GridResourceResolutionMode.cs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Resources; + +/// +/// Specifies how grid resources are resolved by . +/// +public enum GridResourceResolutionMode +{ + /// + /// Resolves grids only from locally available files. + /// + LocalOnly = 0, + + /// + /// Resolves grids locally first, then falls back to network retrieval. + /// + LocalThenNetwork = 1, +} diff --git a/src/ProjNet/Resources/GridResourceResolver.cs b/src/ProjNet/Resources/GridResourceResolver.cs new file mode 100644 index 00000000..030531f1 --- /dev/null +++ b/src/ProjNet/Resources/GridResourceResolver.cs @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Resources; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +/// +/// Resolves named grid resources to local file paths, searching local directories first +/// and optionally falling back to network retrieval via an . +/// +internal sealed class GridResourceResolver +{ + private static readonly IGridResourceFetchClient DefaultFetchClient = new NoOpGridResourceFetchClient(); + + private readonly IGridResourceFetchClient fetchClient; + private readonly GridResourceResolverOptions options; + private readonly Dictionary resolvedPathByGridName = new(StringComparer.OrdinalIgnoreCase); + private readonly object sync = new(); + + /// + /// Initializes a new instance of the class. + /// + /// Resolution options including local search directories and cache settings. + /// Optional fetch client used for network retrieval; defaults to a no-op client when . + internal GridResourceResolver(GridResourceResolverOptions options, IGridResourceFetchClient? fetchClient = null) + { + this.options = ArgumentGuard.ThrowIfNull(options, nameof(options)); + this.fetchClient = fetchClient ?? DefaultFetchClient; + } + + /// + /// Attempts to resolve a named grid resource to an absolute local file path. + /// + /// + /// Resolution order: in-memory cache, local file system (rooted path or configured directories), + /// and network retrieval when is active. + /// Successful resolutions are cached for subsequent calls. + /// + /// The grid resource name or rooted file path to resolve. + /// The absolute local path when resolution succeeds; otherwise . + /// when the grid was located; otherwise . + internal bool TryResolve(string gridName, [NotNullWhen(true)] out string? resolvedPath) + { + if (string.IsNullOrWhiteSpace(gridName)) + { + ArgumentGuard.ThrowArgument("Grid name must not be empty.", nameof(gridName)); + } + + if (this.TryResolveFromCache(gridName, out resolvedPath)) + { + return true; + } + + if (this.TryResolveFromLocalSources(gridName, out resolvedPath)) + { + this.RememberResolvedPath(gridName, resolvedPath); + return true; + } + + if (this.options.Mode == GridResourceResolutionMode.LocalThenNetwork && this.TryResolveFromNetwork(gridName, out resolvedPath)) + { + this.RememberResolvedPath(gridName, resolvedPath); + return true; + } + + resolvedPath = null; + return false; + } + + /// + /// Asynchronously attempts to resolve a named grid resource to an absolute local file path. + /// + /// + /// Resolution order: in-memory cache, local file system (rooted path or configured directories), + /// and network retrieval via when + /// is active. + /// Successful resolutions are cached for subsequent calls. + /// + /// The grid resource name or rooted file path to resolve. + /// A token to monitor for cancellation requests. + /// The absolute local path when resolution succeeds; otherwise . + internal async Task TryResolveAsync(string gridName, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(gridName)) + { + ArgumentGuard.ThrowArgument("Grid name must not be empty.", nameof(gridName)); + } + + if (this.TryResolveFromCache(gridName, out string? resolvedPath)) + { + return resolvedPath; + } + + if (this.TryResolveFromLocalSources(gridName, out resolvedPath)) + { + this.RememberResolvedPath(gridName, resolvedPath); + return resolvedPath; + } + + if (this.options.Mode == GridResourceResolutionMode.LocalThenNetwork) + { + resolvedPath = await this.TryResolveFromNetworkAsync(gridName, cancellationToken).ConfigureAwait(false); + if (resolvedPath is not null) + { + this.RememberResolvedPath(gridName, resolvedPath); + return resolvedPath; + } + } + + return null; + } + + private static string GetGridFileName(string gridName) + { + string normalizedGridName = gridName.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + return Path.GetFileName(normalizedGridName); + } + + private void RememberResolvedPath(string gridName, string resolvedPath) + { + lock (this.sync) + { + this.resolvedPathByGridName[gridName] = resolvedPath; + } + } + + private bool TryResolveFromCache(string gridName, [NotNullWhen(true)] out string? resolvedPath) + { + lock (this.sync) + { + if (this.resolvedPathByGridName.TryGetValue(gridName, out resolvedPath)) + { + if (File.Exists(resolvedPath)) + { + return true; + } + + this.resolvedPathByGridName.Remove(gridName); + } + } + + resolvedPath = null; + return false; + } + + private bool TryResolveFromLocalSources(string gridName, [NotNullWhen(true)] out string? resolvedPath) + { + if (Path.IsPathRooted(gridName) && File.Exists(gridName)) + { + resolvedPath = Path.GetFullPath(gridName); + return true; + } + + string fileName = GetGridFileName(gridName); + if (string.IsNullOrWhiteSpace(fileName)) + { + resolvedPath = null; + return false; + } + + foreach (string localDirectory in this.options.LocalDirectories) + { + string candidatePath = Path.Combine(localDirectory, fileName); + if (!File.Exists(candidatePath)) + { + continue; + } + + resolvedPath = candidatePath; + return true; + } + + resolvedPath = null; + return false; + } + + private bool TryResolveFromNetwork(string gridName, [NotNullWhen(true)] out string? resolvedPath) + { + if (string.IsNullOrWhiteSpace(this.options.CacheDirectory)) + { + resolvedPath = null; + return false; + } + + Directory.CreateDirectory(this.options.CacheDirectory); + string fileName = GetGridFileName(gridName); + if (string.IsNullOrWhiteSpace(fileName)) + { + resolvedPath = null; + return false; + } + + string targetPath = Path.Combine(this.options.CacheDirectory, fileName); + if (File.Exists(targetPath)) + { + if (GridResourceCacheManifest.IsValid(targetPath)) + { + resolvedPath = targetPath; + return true; + } + + GridResourceCacheManifest.DeleteArtifacts(targetPath); + } + + if (!this.fetchClient.TryFetch(gridName, targetPath) || !GridResourceCacheManifest.IsValid(targetPath)) + { + GridResourceCacheManifest.DeleteArtifacts(targetPath); + resolvedPath = null; + return false; + } + + resolvedPath = targetPath; + return true; + } + + private async Task TryResolveFromNetworkAsync(string gridName, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(this.options.CacheDirectory)) + { + return null; + } + + Directory.CreateDirectory(this.options.CacheDirectory); + string fileName = GetGridFileName(gridName); + if (string.IsNullOrWhiteSpace(fileName)) + { + return null; + } + + string targetPath = Path.Combine(this.options.CacheDirectory, fileName); + if (File.Exists(targetPath)) + { + if (GridResourceCacheManifest.IsValid(targetPath)) + { + return targetPath; + } + + GridResourceCacheManifest.DeleteArtifacts(targetPath); + } + + if (!await this.fetchClient.TryFetchAsync(gridName, targetPath, cancellationToken).ConfigureAwait(false) || !GridResourceCacheManifest.IsValid(targetPath)) + { + GridResourceCacheManifest.DeleteArtifacts(targetPath); + return null; + } + + return targetPath; + } +} diff --git a/src/ProjNet/Resources/GridResourceResolverOptions.cs b/src/ProjNet/Resources/GridResourceResolverOptions.cs new file mode 100644 index 00000000..6d3ee6bb --- /dev/null +++ b/src/ProjNet/Resources/GridResourceResolverOptions.cs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Resources; + +using System.Collections.Generic; +using System.IO; +using System.Linq; + +/// +/// Holds the configuration options used by . +/// +internal sealed class GridResourceResolverOptions +{ + /// + /// Initializes a new instance of the class. + /// + /// Directories to search for grid files; blank or null entries are ignored. + /// Directory used to store network-fetched grid files; or whitespace disables network caching. + /// Resolution mode controlling whether network retrieval is attempted. + internal GridResourceResolverOptions(IEnumerable localDirectories, string? cacheDirectory, GridResourceResolutionMode mode) + { + localDirectories = ArgumentGuard.ThrowIfNull(localDirectories, nameof(localDirectories)); + this.LocalDirectories = [.. localDirectories + .Where(path => !string.IsNullOrWhiteSpace(path)) + .Select(Path.GetFullPath)]; + this.CacheDirectory = string.IsNullOrWhiteSpace(cacheDirectory) ? null : Path.GetFullPath(cacheDirectory); + this.Mode = mode; + } + + /// + /// Gets the absolute path of the directory used to cache network-fetched grid files, or when network caching is disabled. + /// + internal string? CacheDirectory { get; } + + /// + /// Gets the ordered list of absolute local directory paths searched during grid resolution. + /// + internal IReadOnlyList LocalDirectories { get; } + + /// + /// Gets the resolution mode that controls whether network retrieval is attempted after local search. + /// + internal GridResourceResolutionMode Mode { get; } +} diff --git a/src/ProjNet/Resources/HttpGridResourceFetchClient.cs b/src/ProjNet/Resources/HttpGridResourceFetchClient.cs new file mode 100644 index 00000000..64a3d840 --- /dev/null +++ b/src/ProjNet/Resources/HttpGridResourceFetchClient.cs @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Resources; + +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +/// +/// Downloads grid resources over HTTP into the local resolver cache. +/// +public sealed class HttpGridResourceFetchClient : IGridResourceFetchClient +{ + private const int CopyBufferSize = 81920; + private static readonly HttpClient SharedHttpClient = new(); + + private readonly Uri baseUri; + private readonly HttpClient httpClient; + + /// + /// Initializes a new instance of the class. + /// + /// The absolute base URL used to resolve grid file names. + /// Optional HTTP client to use for requests; when , a shared client is used. + public HttpGridResourceFetchClient(string baseUrl, HttpClient? httpClient = null) + : this(CreateBaseUri(baseUrl), httpClient) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The absolute base URI used to resolve grid file names. + /// Optional HTTP client to use for requests; when , a shared client is used. + public HttpGridResourceFetchClient(Uri baseUri, HttpClient? httpClient = null) + { + baseUri = ArgumentGuard.ThrowIfNull(baseUri, nameof(baseUri)); + if (!baseUri.IsAbsoluteUri) + { + ArgumentGuard.ThrowArgument("The grid fetch base URI must be absolute.", nameof(baseUri)); + } + + string absoluteUri = baseUri.AbsoluteUri; + this.baseUri = absoluteUri.Length > 0 && absoluteUri[absoluteUri.Length - 1] == '/' + ? baseUri + : new Uri($"{absoluteUri}/", UriKind.Absolute); + this.httpClient = httpClient ?? SharedHttpClient; + } + + /// + public bool TryFetch(string gridName, string targetFilePath) + { + #pragma warning disable CA1849 // IGridResourceFetchClient exposes a synchronous API, but HttpClient only offers async I/O. + return this.TryFetchCoreAsync(gridName, targetFilePath, CancellationToken.None).GetAwaiter().GetResult(); + #pragma warning restore CA1849 + } + + /// + public Task TryFetchAsync(string gridName, string targetFilePath, CancellationToken cancellationToken = default) + { + return this.TryFetchCoreAsync(gridName, targetFilePath, cancellationToken); + } + + private static Uri CreateBaseUri(string baseUrl) + { + baseUrl = ArgumentGuard.ThrowIfNull(baseUrl, nameof(baseUrl)); + if (string.IsNullOrWhiteSpace(baseUrl)) + { + ArgumentGuard.ThrowArgument("The grid fetch base URL must not be empty.", nameof(baseUrl)); + } + + if (!Uri.TryCreate(baseUrl, UriKind.Absolute, out Uri? baseUri)) + { + ArgumentGuard.ThrowArgument("The grid fetch base URL must be an absolute URI.", nameof(baseUrl)); + } + + return baseUri; + } + + private static void ReplaceFile(string sourcePath, string targetPath) + { + GridResourceCacheManifest.DeleteArtifacts(targetPath); + if (File.Exists(targetPath)) + { + File.Delete(targetPath); + } + + File.Move(sourcePath, targetPath); + } + + private static string ValidateTargetFilePath(string targetFilePath) + { + targetFilePath = ArgumentGuard.ThrowIfNull(targetFilePath, nameof(targetFilePath)); + if (string.IsNullOrWhiteSpace(targetFilePath)) + { + ArgumentGuard.ThrowArgument("The target file path must not be empty.", nameof(targetFilePath)); + } + + string? directoryPath = Path.GetDirectoryName(targetFilePath); + if (string.IsNullOrWhiteSpace(directoryPath)) + { + ArgumentGuard.ThrowArgument("The target file path must include a directory.", nameof(targetFilePath)); + } + + return directoryPath; + } + + private static string ValidateGridFileName(string gridName) + { + gridName = ArgumentGuard.ThrowIfNull(gridName, nameof(gridName)); + if (string.IsNullOrWhiteSpace(gridName)) + { + ArgumentGuard.ThrowArgument("The grid name must not be empty.", nameof(gridName)); + } + + string normalizedGridName = gridName.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + string fileName = Path.GetFileName(normalizedGridName); + if (string.IsNullOrWhiteSpace(fileName)) + { + ArgumentGuard.ThrowArgument("The grid name must resolve to a file name.", nameof(gridName)); + } + + return fileName; + } + + private async Task TryFetchCoreAsync(string gridName, string targetFilePath, CancellationToken cancellationToken) + { + string fileName = ValidateGridFileName(gridName); + string targetDirectory = ValidateTargetFilePath(targetFilePath); + Directory.CreateDirectory(targetDirectory); + + Uri requestUri = new(this.baseUri, Uri.EscapeDataString(fileName)); + string tempFilePath = $"{targetFilePath}.{Guid.NewGuid():N}.download"; + + try + { + using HttpResponseMessage response = await this.httpClient.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + + if (!response.IsSuccessStatusCode) + { + return false; + } + + long actualLength; + using Stream contentStream = +#if NET8_0_OR_GREATER + await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); +#else + await response.Content.ReadAsStreamAsync().ConfigureAwait(false); +#endif + using (var fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.None)) + { + await contentStream.CopyToAsync(fileStream, CopyBufferSize, cancellationToken).ConfigureAwait(false); + await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false); + actualLength = fileStream.Length; + } + + long? expectedLength = response.Content.Headers.ContentLength; + if (expectedLength.HasValue && actualLength != expectedLength.Value) + { + return false; + } + + ReplaceFile(tempFilePath, targetFilePath); + GridResourceCacheManifest.Write(targetFilePath, requestUri.AbsoluteUri); + return true; + } + catch (HttpRequestException) + { + GridResourceCacheManifest.DeleteArtifacts(targetFilePath); + return false; + } + catch (IOException) + { + GridResourceCacheManifest.DeleteArtifacts(targetFilePath); + return false; + } + catch (UnauthorizedAccessException) + { + GridResourceCacheManifest.DeleteArtifacts(targetFilePath); + return false; + } + finally + { + if (File.Exists(tempFilePath)) + { + File.Delete(tempFilePath); + } + } + } +} diff --git a/src/ProjNet/Resources/IGridResourceFetchClient.cs b/src/ProjNet/Resources/IGridResourceFetchClient.cs new file mode 100644 index 00000000..cfcde368 --- /dev/null +++ b/src/ProjNet/Resources/IGridResourceFetchClient.cs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Resources; + +using System.Threading; +using System.Threading.Tasks; + +/// +/// Defines a client that can fetch a named grid resource and write it to a local file. +/// +public interface IGridResourceFetchClient +{ + /// + /// Attempts to fetch the specified grid resource and save it to . + /// + /// The logical name or remote identifier of the grid resource. + /// The local file path where the fetched grid should be written. + /// when the resource was successfully fetched and written; otherwise . + bool TryFetch(string gridName, string targetFilePath); + + /// + /// Asynchronously attempts to fetch the specified grid resource and save it to . + /// + /// The logical name or remote identifier of the grid resource. + /// The local file path where the fetched grid should be written. + /// A token to monitor for cancellation requests. + /// A task that represents the asynchronous operation. The task result is when the resource was successfully fetched and written; otherwise . + Task TryFetchAsync(string gridName, string targetFilePath, CancellationToken cancellationToken = default); +} diff --git a/src/ProjNet/Resources/NoOpGridResourceFetchClient.cs b/src/ProjNet/Resources/NoOpGridResourceFetchClient.cs new file mode 100644 index 00000000..935b7efb --- /dev/null +++ b/src/ProjNet/Resources/NoOpGridResourceFetchClient.cs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Resources; + +using System.Threading; +using System.Threading.Tasks; + +/// +/// Represents a fetch client that never downloads grid resources. +/// +public sealed class NoOpGridResourceFetchClient : IGridResourceFetchClient +{ + /// + public bool TryFetch(string gridName, string targetFilePath) => false; + + /// + public Task TryFetchAsync(string gridName, string targetFilePath, CancellationToken cancellationToken = default) => Task.FromResult(false); +} diff --git a/src/ProjNet/StringCompatibility.cs b/src/ProjNet/StringCompatibility.cs new file mode 100644 index 00000000..2ef9ce91 --- /dev/null +++ b/src/ProjNet/StringCompatibility.cs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet; + +using System; +using System.Text; + +/// +/// Provides compatibility helpers for string operations across target frameworks. +/// +internal static class StringCompatibility +{ + /// + /// Replaces all ordinal matches of with . + /// + /// Input string to search. + /// Substring to replace. + /// Replacement substring. + /// The transformed string. + internal static string ReplaceOrdinal(string value, string oldValue, string newValue) + { +#if NETSTANDARD2_1_OR_GREATER + return value.Replace(oldValue, newValue, StringComparison.Ordinal); +#else + return ReplaceCore(value, oldValue, newValue, StringComparison.Ordinal); +#endif + } + + /// + /// Replaces all ordinal-ignore-case matches of with . + /// + /// Input string to search. + /// Substring to replace. + /// Replacement substring. + /// The transformed string. + internal static string ReplaceOrdinalIgnoreCase(string value, string oldValue, string newValue) + { +#if NETSTANDARD2_1_OR_GREATER + return value.Replace(oldValue, newValue, StringComparison.OrdinalIgnoreCase); +#else + return ReplaceCore(value, oldValue, newValue, StringComparison.OrdinalIgnoreCase); +#endif + } + +#if !NETSTANDARD2_1_OR_GREATER + private static string ReplaceCore(string value, string oldValue, string newValue, StringComparison comparison) + { + ArgumentGuard.ThrowIfNull(value, nameof(value)); + oldValue = ArgumentGuard.ThrowIfNullOrEmpty(oldValue, nameof(oldValue)); + ArgumentGuard.ThrowIfNull(newValue, nameof(newValue)); + + int matchIndex = value.IndexOf(oldValue, comparison); + if (matchIndex < 0) + { + return value; + } + + var builder = new StringBuilder(value.Length); + int startIndex = 0; + while (matchIndex >= 0) + { + builder.Append(value, startIndex, matchIndex - startIndex); + builder.Append(newValue); + startIndex = matchIndex + oldValue.Length; + matchIndex = value.IndexOf(oldValue, startIndex, comparison); + } + + builder.Append(value, startIndex, value.Length - startIndex); + return builder.ToString(); + } +#endif +} diff --git a/stryker-config.json b/stryker-config.json new file mode 100644 index 00000000..b4486fde --- /dev/null +++ b/stryker-config.json @@ -0,0 +1,27 @@ +{ + "stryker-config": { + "solution": "ProjNet4GeoAPI.sln", + "project": "src/ProjNet/ProjNET.csproj", + "test-projects": [ + "test/ProjNet.Tests/ProjNET.Tests.csproj" + ], + "language-version": "Csharp12", + "configuration": "Debug", + "target-framework": "net8.0", + "mutation-level": "Basic", + "test-runner": "mtp", + "coverage-analysis": "off", + "reporters": [ + "Progress", + "Html", + "Json" + ], + "thresholds": { + "high": 80, + "low": 70, + "break": 60 + }, + "concurrency": 1, + "verbosity": "info" + } +} diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 00000000..746207e3 --- /dev/null +++ b/stylecop.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "xmlHeader": false + } + } +} diff --git a/test/Directory.Build.props b/test/Directory.Build.props index aa0b5e27..e463602e 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -2,15 +2,9 @@ - + false - - - - - - diff --git a/test/ProjNet.Tests/CodeQuality/MathTransformConcurrencyTests.cs b/test/ProjNet.Tests/CodeQuality/MathTransformConcurrencyTests.cs new file mode 100644 index 00000000..50cf879a --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/MathTransformConcurrencyTests.cs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies concurrent read-only use of shared math-transform instances. +/// +public class MathTransformConcurrencyTests +{ + /// + /// Verifies that concurrent callers can share the same forward and inverse Helmert instances without nondeterministic results. + /// + /// A task that completes when all concurrent transform calls have been verified. + [Fact] + public async Task SharedHelmertTransformsProduceDeterministicConcurrentResults() + { + MathTransform forward = CreateTransform("+proj=helmert +convention=coordinate_frame +x=0.67678 +y=0.65495 +z=-0.52827 +rx=-0.022742 +ry=0.012667 +rz=0.022704 +s=-0.01070"); + MathTransform inverse = forward.Inverse(); + + double[] source = CreatePoint(3565285.0d, 855949.0d, 5201383.0d); + double[] expectedForward = forward.Transform(source); + double[] expectedInverse = inverse.Transform(expectedForward); + + double[][] forwardResults = await TransformConcurrentlyAsync(forward, source, taskCount: 8); + double[][] inverseResults = await TransformConcurrentlyAsync(inverse, expectedForward, taskCount: 8); + + AssertAllMatch(expectedForward, forwardResults, 1e-9d); + AssertAllMatch(expectedInverse, inverseResults, 1e-9d); + } + + private static void AssertAllMatch(double[] expected, double[][] actuals, double tolerance) + { + for (int i = 0; i < actuals.Length; i++) + { + Assert.Equal(expected.Length, actuals[i].Length); + for (int j = 0; j < expected.Length; j++) + { + Assert.InRange(Math.Abs(actuals[i][j] - expected[j]), 0d, tolerance); + } + } + } + + private static async Task TransformConcurrentlyAsync(MathTransform transform, double[] point, int taskCount) + { + using var gate = new ManualResetEventSlim(false); + Task[] tasks = Enumerable.Range(0, taskCount) + .Select(_ => Task.Run(() => + { + gate.Wait(); + return transform.Transform(point); + })) + .ToArray(); + + gate.Set(); + return await Task.WhenAll(tasks).ConfigureAwait(true); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; +} diff --git a/test/ProjNet.Tests/CodeQuality/MathTransformSpanOverloadTests.cs b/test/ProjNet.Tests/CodeQuality/MathTransformSpanOverloadTests.cs new file mode 100644 index 00000000..8f6f2b7a --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/MathTransformSpanOverloadTests.cs @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies span-based overload behavior on . +/// +public class MathTransformSpanOverloadTests +{ + /// + /// Verifies span point transform parity for 2D identity transforms. + /// + [Fact] + public void TransformReadOnlySpan2DMatchesArrayTransform() + { + MathTransform transform = new StubMathTransform(); + double[] input = [12.5, -7.25]; + + double[] expected = transform.Transform(input); + double[] actual = new double[expected.Length]; + transform.Transform(new ReadOnlySpan(input), actual.AsSpan()); + + Assert.Equal(expected, actual); + } + + /// + /// Verifies span point transform parity for 4D identity transforms. + /// + [Fact] + public void TransformReadOnlySpan4DMatchesArrayTransform() + { + MathTransform transform = new StubMathTransform(); + double[] input = [1.0, 2.0, 3.0, 4.0]; + + double[] expected = transform.Transform(input); + double[] actual = new double[expected.Length]; + transform.Transform(new ReadOnlySpan(input), actual.AsSpan()); + + Assert.Equal(expected, actual); + } + + /// + /// Verifies span point transform parity for dimensions above 4 ordinates. + /// + [Fact] + public void TransformReadOnlySpan5DMatchesArrayTransform() + { + MathTransform transform = new IdentityMathTransform(5); + double[] input = [11.0, -3.5, 4.25, 2026.0, 99.75]; + + double[] expected = transform.Transform(input); + double[] actual = new double[expected.Length]; + transform.Transform(new ReadOnlySpan(input), actual.AsSpan()); + + Assert.Equal(expected, actual); + } + + /// + /// Verifies that too-small destination spans are rejected. + /// + [Fact] + public void TransformReadOnlySpanWithSmallDestinationThrows() + { + MathTransform transform = new StubMathTransform(); + double[] input = [1.0, 2.0, 3.0, 4.0]; + double[] destination = new double[3]; + + ArgumentException exception = Assert.Throws(() => transform.Transform(new ReadOnlySpan(input), destination.AsSpan())); + Assert.Equal("result", exception.ParamName); + } + + /// + /// Verifies that span point transforms only overwrite the required destination prefix. + /// + [Fact] + public void TransformReadOnlySpanWithLargerDestinationPreservesRemainingValues() + { + MathTransform transform = new IdentityMathTransform(3); + double[] input = [4.0, 5.0, 6.0]; + double[] expected = transform.Transform(input); + + double[] actual = new double[6]; + for (int i = 0; i < actual.Length; i++) + { + actual[i] = -1.0; + } + + transform.Transform(new ReadOnlySpan(input), actual.AsSpan()); + + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], actual[i], 12); + } + + for (int i = expected.Length; i < actual.Length; i++) + { + Assert.Equal(-1.0, actual[i], 12); + } + } + + /// + /// Verifies span overload parity for convex hull and domain flag APIs. + /// + [Fact] + public void ConvexHullAndDomainFlagsSpanOverloadsMatchListOverloads() + { + var transform = new StubMathTransform(); + double[] ordinates = [10.0, 20.0, 30.0, 40.0]; + + List hullFromList = transform.GetCodomainConvexHull(new List(ordinates)); + List hullFromSpan = transform.GetCodomainConvexHull(ordinates.AsSpan()); + Assert.Equal(hullFromList, hullFromSpan); + + DomainFlags flagsFromList = transform.GetDomainFlags(new List(ordinates)); + DomainFlags flagsFromSpan = transform.GetDomainFlags(ordinates.AsSpan()); + Assert.Equal(flagsFromList, flagsFromSpan); + } + + /// + /// Verifies span overload parity for empty convex hull and domain flag inputs. + /// + [Fact] + public void ConvexHullAndDomainFlagsSpanOverloadsMatchListOverloadsForEmptyInput() + { + var transform = new StubMathTransform(); + double[] ordinates = []; + + List hullFromList = transform.GetCodomainConvexHull(new List(ordinates)); + List hullFromSpan = transform.GetCodomainConvexHull(ordinates.AsSpan()); + Assert.Equal(hullFromList, hullFromSpan); + + DomainFlags flagsFromList = transform.GetDomainFlags(new List(ordinates)); + DomainFlags flagsFromSpan = transform.GetDomainFlags(ordinates.AsSpan()); + Assert.Equal(flagsFromList, flagsFromSpan); + } + + private sealed class StubMathTransform : MathTransform + { + public override int DimSource => 2; + + public override int DimTarget => 2; + + public override string WKT => "PARAM_MT[\"Stub\"]"; + + public override string XML => ""; + + public override MathTransform Inverse() => this; + + public override void Invert() + { + } + + public override bool Identity() => true; + + public override void Transform(ref double x, ref double y, ref double z) + { + } + + public override List GetCodomainConvexHull(List points) + { + return new List(points); + } + + public override DomainFlags GetDomainFlags(List points) + { + return points.Count == 0 ? DomainFlags.Outside : DomainFlags.Inside; + } + } +} diff --git a/test/ProjNet.Tests/CodeQuality/MathTransformStackallocBoundaryTests.cs b/test/ProjNet.Tests/CodeQuality/MathTransformStackallocBoundaryTests.cs new file mode 100644 index 00000000..d061aa03 --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/MathTransformStackallocBoundaryTests.cs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies dimensional boundary behavior around stackalloc-backed transform paths. +/// +public class MathTransformStackallocBoundaryTests +{ + /// + /// Verifies 2D input uses the 3D transform path and returns 2 ordinates. + /// + [Fact] + public void TransformArray2DUsesThreeDimensionalPath() + { + var transform = new TrackingBoundaryMathTransform(); + + double[] result = transform.Transform([1d, 2d]); + + Assert.Equal([11d, 22d], result); + Assert.Equal(1, transform.Transform3DCalls); + Assert.Equal(0, transform.Transform4DCalls); + } + + /// + /// Verifies 3D input remains on the 3D transform path for a 2D target transform. + /// + [Fact] + public void TransformArray3DUsesThreeDimensionalPath() + { + var transform = new TrackingBoundaryMathTransform(); + + double[] result = transform.Transform([1d, 2d, 3d]); + + Assert.Equal(2, result.Length); + Assert.Equal(11d, result[0], 12); + Assert.Equal(22d, result[1], 12); + Assert.Equal(1, transform.Transform3DCalls); + Assert.Equal(0, transform.Transform4DCalls); + } + + /// + /// Verifies 4D input uses the 4D transform path at the stackalloc boundary. + /// + [Fact] + public void TransformArray4DUsesFourDimensionalPathAtBoundary() + { + var transform = new TrackingBoundaryMathTransform(); + + double[] result = transform.Transform([1d, 2d, 3d, 4d]); + + Assert.Equal([2d, 4d, 6d, 8d], result); + Assert.Equal(0, transform.Transform3DCalls); + Assert.Equal(1, transform.Transform4DCalls); + } + + /// + /// Verifies input above 4D uses the 4D transform path and preserves trailing ordinates. + /// + [Fact] + public void TransformArrayAbove4DUsesFourDimensionalPathAndPreservesTail() + { + var transform = new TrackingBoundaryMathTransform(); + + double[] result = transform.Transform([1d, 2d, 3d, 4d, 99d]); + + Assert.Equal(5, result.Length); + Assert.Equal(2d, result[0], 12); + Assert.Equal(4d, result[1], 12); + Assert.Equal(6d, result[2], 12); + Assert.Equal(8d, result[3], 12); + Assert.Equal(99d, result[4], 12); + Assert.Equal(0, transform.Transform3DCalls); + Assert.Equal(1, transform.Transform4DCalls); + } + + private sealed class TrackingBoundaryMathTransform : MathTransform + { + public int Transform3DCalls { get; private set; } + + public int Transform4DCalls { get; private set; } + + public override int DimSource => 2; + + public override int DimTarget => 2; + + public override string WKT => "PARAM_MT[\"TrackingBoundary\"]"; + + public override string XML => ""; + + public override MathTransform Inverse() => this; + + public override void Invert() + { + } + + public override bool Identity() => false; + + public override void Transform(ref double x, ref double y, ref double z) + { + this.Transform3DCalls++; + x += 10d; + y += 20d; + z += 30d; + } + + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + this.Transform4DCalls++; + x += 1d; + y += 2d; + z += 3d; + t += 4d; + } + } +} diff --git a/test/ProjNet.Tests/CodeQuality/ProjectionConstantsConsistencyTests.cs b/test/ProjNet.Tests/CodeQuality/ProjectionConstantsConsistencyTests.cs new file mode 100644 index 00000000..f77ee6d8 --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/ProjectionConstantsConsistencyTests.cs @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Reflection; +using ProjNet.CoordinateSystems.Projections; +using Xunit; + +/// +/// Verifies that shared projection constants keep their expected mathematical values. +/// +public class ProjectionConstantsConsistencyTests +{ + /// + /// Verifies that the shared map-projection angle constants match the corresponding fractions of pi. + /// + [Fact] + public void MapProjectionAngleConstants_MatchExpectedPiFractions() + { + double fortPi = GetMapProjectionConstant("FortPi"); + double halfPi = GetMapProjectionConstant("HalfPi"); + + Assert.Equal(Math.PI / 4d, fortPi, 15); + Assert.Equal(Math.PI / 2d, halfPi, 15); + } + + /// + /// Verifies that the shared projection helper constants keep their documented numeric values. + /// + [Fact] + public void ProjectionConstants_MatchExpectedNumericValues() + { + Type projectionConstantsType = GetProjectionConstantsType(); + + Assert.Equal(1d / 3d, GetProjectionConstant(projectionConstantsType, "OneThird"), 15); + Assert.Equal(2d / 3d, GetProjectionConstant(projectionConstantsType, "TwoThirds"), 15); + Assert.Equal(1.0000001d, GetProjectionConstant(projectionConstantsType, "OnePlusEps7"), 15); + Assert.Equal(1.000001d, GetProjectionConstant(projectionConstantsType, "OnePlusEps6"), 15); + Assert.Equal(1e-12d, GetProjectionConstant(projectionConstantsType, "Tolerance1E12"), 15); + } + + private static double GetMapProjectionConstant(string fieldName) + { + FieldInfo field = Assert.IsType( + typeof(MapProjection).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic), + exactMatch: false); + return Assert.IsType(field.GetRawConstantValue()); + } + + private static Type GetProjectionConstantsType() + { + Assembly assembly = Assert.IsType(Assembly.GetAssembly(typeof(MapProjection)), exactMatch: false); + return Assert.IsType( + assembly.GetType("ProjNet.CoordinateSystems.Projections.ProjectionConstants"), + exactMatch: false); + } + + private static double GetProjectionConstant(Type projectionConstantsType, string fieldName) + { + FieldInfo field = Assert.IsType( + projectionConstantsType.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public), + exactMatch: false); + return Assert.IsType(field.GetRawConstantValue()); + } +} diff --git a/test/ProjNet.Tests/CodeQuality/ProjectionKernelAlignmentTests.cs b/test/ProjNet.Tests/CodeQuality/ProjectionKernelAlignmentTests.cs new file mode 100644 index 00000000..d5e00118 --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/ProjectionKernelAlignmentTests.cs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests that the projection kernel correctly resolves well-known projection name aliases. +/// +public class ProjectionKernelAlignmentTests +{ + private static readonly double[] LambertAliasInput = [100000d, 100000d]; + private static readonly double[] MercatorAliasInput = [1000d, 2000d]; + private static readonly double[] TransverseMercatorAliasInput = [500000d, 4649776.22482d]; + private static readonly CoordinateSystemServices Services = new(CoordinateSystemServicesTests.LoadCsv()); + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Validates Mercator family aliases against the projection registry. + /// + /// Projection alias to resolve. + [Theory] + [InlineData("Mercator (variant A)")] + [InlineData("Mercator (variant B)")] + [InlineData("Web_Mercator")] + public void SupportsMercatorVariantAliases(string projectionName) + { + ProjectedCoordinateSystem source = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + $"PROJCS[\"Alias Mercator\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1],AUTHORITY[\"EPSG\",\"3857\"]]"); + + GeographicCoordinateSystem target = GeographicCoordinateSystem.WGS84; + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] result = transform.MathTransform.Transform(MercatorAliasInput); + + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Validates Transverse Mercator family aliases against the projection registry. + /// + /// Projection alias to resolve. + [Theory] + [InlineData("Transverse_Mercator_South_Oriented")] + [InlineData("Gauss_Kruger")] + [InlineData("UTM")] + [InlineData("ETMERC")] + [InlineData("Extended_Transverse_Mercator")] + [InlineData("Approx_TMerc")] + public void SupportsTransverseMercatorAliases(string projectionName) + { + ProjectedCoordinateSystem source = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + $"PROJCS[\"Alias TM\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1],AUTHORITY[\"EPSG\",\"32632\"]]"); + + GeographicCoordinateSystem target = GeographicCoordinateSystem.WGS84; + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] result = transform.MathTransform.Transform(TransverseMercatorAliasInput); + + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Validates Lambert Conformal Conic aliases against the projection registry. + /// + /// Projection alias to resolve. + [Theory] + [InlineData("Lambert_Conformal_Conic_1SP")] + [InlineData("Lambert_Conformal_Conic_2SP_Belgium")] + public void SupportsLambertConformalAliases(string projectionName) + { + ProjectedCoordinateSystem source = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + $"PROJCS[\"Alias LCC\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",40],PARAMETER[\"central_meridian\",-100],PARAMETER[\"standard_parallel_1\",33],PARAMETER[\"standard_parallel_2\",45],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + + GeographicCoordinateSystem target = GeographicCoordinateSystem.WGS84; + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] result = transform.MathTransform.Transform(LambertAliasInput); + + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Quantifies the remaining Snyder-kernel approximation error behind the explicit approximate transverse Mercator alias. + /// + [Theory] + [InlineData(6d, 45d, 0.01d)] + [InlineData(10d, 45d, 0.03d)] + public void ApproximateTransverseMercatorAliasMatchesExtendedReferenceWithinExpectedBounds( + double deltaLongitudeDegrees, + double latitudeDegrees, + double maxErrorMeters) + { + const string tmercWkt = + "PROJCS[\"TM CM0\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Approx_TMerc\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + CoordinateSystem source = Assert.IsType(Services.GetCoordinateSystem(4326), exactMatch: false); + ProjectedCoordinateSystem target = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + tmercWkt); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + + double[] projected = transform.MathTransform.Transform([deltaLongitudeDegrees, latitudeDegrees]); + (double expectedX, double expectedY) = deltaLongitudeDegrees switch + { + 6d => (472891.7912691528d, 5000491.005461439d), + 10d => (788141.0602297583d, 5031833.62225004d), + _ => throw new ArgumentOutOfRangeException(nameof(deltaLongitudeDegrees)), + }; + + double error = Math.Max(Math.Abs(projected[0] - expectedX), Math.Abs(projected[1] - expectedY)); + Assert.InRange(error, 0d, maxErrorMeters); + } +} diff --git a/test/ProjNet.Tests/CodeQuality/PublicApiBaselineTests.cs b/test/ProjNet.Tests/CodeQuality/PublicApiBaselineTests.cs new file mode 100644 index 00000000..d541c244 --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/PublicApiBaselineTests.cs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.IO; +using ProjNet; +using PublicApiGenerator; +using Xunit; + +/// +/// Tests that verify the public API surface of the ProjNet assembly matches the committed baseline. +/// +public class PublicApiBaselineTests +{ + private const string BaselineFileName = "PublicAPI.Shipped.txt"; + private const string UpdateBaselineEnvironmentVariable = "PROJNET_UPDATE_PUBLIC_API_BASELINE"; + private static readonly string[] ExcludedPublicApiAttributes = + [ + "System.Runtime.Versioning.TargetFrameworkAttribute", + "System.Reflection.AssemblyMetadataAttribute", + ]; + + /// + /// Verifies that the current public API of the ProjNet assembly matches the committed baseline file. + /// + [Fact] + public void PublicApiMatchesBaseline() + { + string baselinePath = GetBaselinePath(); + string currentPublicApi = GenerateNormalizedPublicApi(); + + if (Environment.GetEnvironmentVariable(UpdateBaselineEnvironmentVariable) == "1") + { + File.WriteAllText(baselinePath, currentPublicApi + Environment.NewLine); + return; + } + + if (!File.Exists(baselinePath)) + { + throw new InvalidOperationException($"Public API baseline file was not found at '{baselinePath}'. Set {UpdateBaselineEnvironmentVariable}=1 and run this test to generate it."); + } + + string baseline = NormalizeLineEndings(File.ReadAllText(baselinePath)); + Assert.Equal(baseline, currentPublicApi); + } + + /// + /// Verifies that multidimensional array members are normalized to their correct public API signatures. + /// + [Fact] + public void GeneratedPublicApiPreservesMultiDimensionalArrayRanks() + { + string currentPublicApi = GenerateNormalizedPublicApi(); + + Assert.Contains("public AffineTransform(double[,] matrix) { }", currentPublicApi, StringComparison.Ordinal); + Assert.Contains("public double[,] GetMatrix() { }", currentPublicApi, StringComparison.Ordinal); + Assert.Contains("public virtual double[,] Derivative(double[] point) { }", currentPublicApi, StringComparison.Ordinal); + } + + private static string GetBaselinePath() + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory is not null) + { + string solutionPath = Path.Combine(directory.FullName, "ProjNet4GeoAPI.sln"); + if (File.Exists(solutionPath)) + { + return Path.Combine(directory.FullName, "src", "ProjNet", BaselineFileName); + } + + directory = directory.Parent; + } + + throw new InvalidOperationException("Unable to locate repository root from test output directory."); + } + + private static string NormalizeLineEndings(string text) + { + return text.Replace("\r\n", "\n", StringComparison.Ordinal).Replace("\r", "\n", StringComparison.Ordinal).TrimEnd(); + } + + private static string NormalizeGeneratedPublicApi(string text) + { + return NormalizeLineEndings(text) + .Replace("public AffineTransform(double[] matrix) { }", "public AffineTransform(double[,] matrix) { }", StringComparison.Ordinal) + .Replace("public double[] GetMatrix() { }", "public double[,] GetMatrix() { }", StringComparison.Ordinal) + .Replace("public virtual double[] Derivative(double[] point) { }", "public virtual double[,] Derivative(double[] point) { }", StringComparison.Ordinal); + } + + private static string GenerateNormalizedPublicApi() + { + return NormalizeGeneratedPublicApi(typeof(CoordinateSystemServices).Assembly.GeneratePublicApi(new ApiGeneratorOptions + { + IncludeAssemblyAttributes = false, + ExcludeAttributes = ExcludedPublicApiAttributes, + })); + } +} diff --git a/test/ProjNet.Tests/CodeQuality/SealedConsistencyTests.cs b/test/ProjNet.Tests/CodeQuality/SealedConsistencyTests.cs new file mode 100644 index 00000000..51d50e2c --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/SealedConsistencyTests.cs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using Xunit; + +/// +/// Guards the intended sealed-versus-extendable split for concrete classes in the ProjNET assembly. +/// +public class SealedConsistencyTests +{ + private static readonly HashSet ExtendableConcreteTypes = + [ + "ProjNet.CoordinateSystemServices", + "ProjNet.CoordinateSystems.AngularUnit", + "ProjNet.CoordinateSystems.BoundCoordinateSystem", + "ProjNet.CoordinateSystems.CompoundCoordinateSystem", + "ProjNet.CoordinateSystems.CoordinateSystemFactory", + "ProjNet.CoordinateSystems.Ellipsoid", + "ProjNet.CoordinateSystems.FittedCoordinateSystem", + "ProjNet.CoordinateSystems.GeocentricCoordinateSystem", + "ProjNet.CoordinateSystems.GeographicCoordinateSystem", + "ProjNet.CoordinateSystems.HorizontalDatum", + "ProjNet.CoordinateSystems.LinearUnit", + "ProjNet.CoordinateSystems.PrimeMeridian", + "ProjNet.CoordinateSystems.ProjectedCoordinateSystem", + "ProjNet.CoordinateSystems.Projection", + "ProjNet.CoordinateSystems.Projections.AlbersProjection", + "ProjNet.CoordinateSystems.Projections.BaconProjection", + "ProjNet.CoordinateSystems.Projections.Eckert3Projection", + "ProjNet.CoordinateSystems.Projections.GeneralSinusoidalProjection", + "ProjNet.CoordinateSystems.Projections.HotineObliqueMercatorProjection", + "ProjNet.CoordinateSystems.Projections.KrovakProjection", + "ProjNet.CoordinateSystems.Projections.Mercator", + "ProjNet.CoordinateSystems.Projections.MollweideProjection", + "ProjNet.CoordinateSystems.Projections.PolarStereographicProjection", + "ProjNet.CoordinateSystems.Projections.ProjectionsRegistry", + "ProjNet.CoordinateSystems.Projections.PutninsP3Projection", + "ProjNet.CoordinateSystems.Projections.PutninsP4PProjection", + "ProjNet.CoordinateSystems.Projections.PutninsP5Projection", + "ProjNet.CoordinateSystems.Projections.PutninsP6Projection", + "ProjNet.CoordinateSystems.Projections.UrmaevFlatPolarSinusoidalProjection", + "ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory", + "ProjNet.CoordinateSystems.Unit", + "ProjNet.CoordinateSystems.VerticalCoordinateSystem", + "ProjNet.CoordinateSystems.VerticalDatum", + ]; + + /// + /// Verifies that every concrete class in the production assembly is either sealed or explicitly allowlisted as intentionally extensible. + /// + [Fact] + public void ConcreteClassesAreEitherSealedOrExplicitlyAllowlisted() + { + Assembly assembly = typeof(CoordinateSystemServices).Assembly; + var concreteNonSealedClasses = assembly + .GetTypes() + .Where(type => type.IsClass) + .Where(type => !type.IsAbstract && !type.IsSealed) + .Where(type => !type.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false)) + .Select(type => type.FullName) + .OfType() + .OrderBy(name => name, StringComparer.Ordinal) + .ToList(); + + var unexpectedConcreteNonSealedClasses = concreteNonSealedClasses + .Where(name => !ExtendableConcreteTypes.Contains(name)) + .ToList(); + + var staleAllowlistEntries = ExtendableConcreteTypes + .Where(name => !concreteNonSealedClasses.Contains(name, StringComparer.Ordinal)) + .OrderBy(name => name, StringComparer.Ordinal) + .ToList(); + + Assert.True( + unexpectedConcreteNonSealedClasses.Count == 0, + "Unexpected non-sealed concrete classes: " + string.Join(", ", unexpectedConcreteNonSealedClasses)); + Assert.True( + staleAllowlistEntries.Count == 0, + "Allowlist contains classes that are no longer concrete and non-sealed: " + string.Join(", ", staleAllowlistEntries)); + } +} diff --git a/test/ProjNet.Tests/CodeQuality/SpanParseUtilityTests.cs b/test/ProjNet.Tests/CodeQuality/SpanParseUtilityTests.cs new file mode 100644 index 00000000..ad4690d1 --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/SpanParseUtilityTests.cs @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies the shared finite-double parsing helpers used by runtime transformation argument parsing. +/// +public class SpanParseUtilityTests +{ + /// + /// Verifies the shared finite-double parser accepts representative invariant-culture values. + /// + /// Token to parse. + /// Expected parsed value. + [Theory] + [InlineData("1.5", 1.5d)] + [InlineData(" 1234.5 ", 1234.5d)] + [InlineData("1,234.5", 1234.5d)] + public void TryParseFiniteDouble_ValidFiniteValues_ReturnsTrue(string token, double expected) + { + bool parsed = SpanParseUtility.TryParseFiniteDouble(token, out double value); + + Assert.True(parsed); + Assert.Equal(expected, value, 12); + } + + /// + /// Verifies the shared finite-double parser rejects invalid and non-finite values. + /// + /// Token to parse. + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("NaN")] + [InlineData("Infinity")] + [InlineData("-Infinity")] + [InlineData("abc")] + public void TryParseFiniteDouble_InvalidOrNonFiniteValues_ReturnsFalse(string token) + { + bool parsed = SpanParseUtility.TryParseFiniteDouble(token, out double value); + + Assert.False(parsed); + Assert.True(double.IsNaN(value) || double.IsInfinity(value) || value == 0d); + } + + /// + /// Verifies CSV parsing still rejects non-finite segments once the shared finite-double logic is centralized. + /// + [Fact] + public void TryParseCsvValues_NonFiniteSegment_ReturnsInvalidValue() + { + Span destination = stackalloc double[2]; + + CsvParseStatus status = SpanParseUtility.TryParseCsvValues("1,NaN", destination, out int parsedCount); + + Assert.Equal(CsvParseStatus.InvalidValue, status); + Assert.Equal(1, parsedCount); + Assert.Equal(1d, destination[0], 12); + } + + /// + /// Verifies the presence-check overload only succeeds when the optional key exists and parses as a finite value. + /// + [Fact] + public void TryGetOptionalDouble_PresentFiniteValue_ReturnsTrueAndParsedValue() + { + var args = new Dictionary + { + ["dx"] = "1.25", + }; + + bool parsed = SpanParseUtility.TryGetOptionalDouble(args, "dx", out double value); + + Assert.True(parsed); + Assert.Equal(1.25d, value, 12); + } + + /// + /// Verifies the presence-check overload treats a missing key as absent instead of injecting a default value. + /// + [Fact] + public void TryGetOptionalDouble_MissingKey_ReturnsFalse() + { + bool parsed = SpanParseUtility.TryGetOptionalDouble(new Dictionary(), "dx", out double value); + + Assert.False(parsed); + Assert.Equal(0d, value); + } + + /// + /// Verifies the default-value overload returns the supplied fallback when the key is absent. + /// + [Fact] + public void TryGetOptionalDouble_WithDefault_MissingKey_ReturnsDefaultValue() + { + bool parsed = SpanParseUtility.TryGetOptionalDouble( + new Dictionary(), + "scale", + 2.5d, + out double value, + out string? skipReason); + + Assert.True(parsed); + Assert.Null(skipReason); + Assert.Equal(2.5d, value, 12); + } + + /// + /// Verifies the diagnostic overload reports invalid optional values without overwriting the zero default. + /// + [Fact] + public void TryGetOptionalDouble_WithImplicitZeroDefault_InvalidToken_ReturnsReason() + { + var args = new Dictionary + { + ["dx"] = "oops", + }; + + bool parsed = SpanParseUtility.TryGetOptionalDouble(args, "dx", out double value, out string? skipReason); + + Assert.False(parsed); + Assert.Equal(0d, value); + Assert.Equal("Invalid value for +dx.", skipReason); + } + + /// + /// Verifies the default-value overload reports invalid tokens and leaves the parser's failed-value output in place. + /// + [Fact] + public void TryGetOptionalDouble_WithDefault_InvalidToken_ReturnsReasonAndFailedValue() + { + var args = new Dictionary + { + ["scale"] = "not-a-number", + }; + + bool parsed = SpanParseUtility.TryGetOptionalDouble(args, "scale", 3d, out double value, out string? skipReason); + + Assert.False(parsed); + Assert.Equal(0d, value); + Assert.Equal("Invalid value for +scale.", skipReason); + } +} diff --git a/test/ProjNet.Tests/CodeQuality/StaticAccessorConsistencyTests.cs b/test/ProjNet.Tests/CodeQuality/StaticAccessorConsistencyTests.cs new file mode 100644 index 00000000..b660f0df --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/StaticAccessorConsistencyTests.cs @@ -0,0 +1,739 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Verifies that the built-in static coordinate-system accessors keep their expected metadata. +/// +public class StaticAccessorConsistencyTests +{ + private static readonly AngularUnitExpectation[] AngularUnitExpectations = + [ + new(nameof(AngularUnit.Degrees), new InfoExpectation("degree", "EPSG", 9102, "deg", string.Empty, "=pi/180 radians"), 0.017453292519943295769236907684886d), + new(nameof(AngularUnit.Radian), new InfoExpectation("radian", "EPSG", 9101, "rad", string.Empty, "SI standard unit"), 1d), + new(nameof(AngularUnit.Grad), new InfoExpectation("grad", "EPSG", 9105, "gr", string.Empty, "=pi/200 radians"), 0.015707963267948966192313216916398d), + new(nameof(AngularUnit.Gon), new InfoExpectation("gon", "EPSG", 9106, "g", string.Empty, "=pi/200 radians"), 0.015707963267948966192313216916398d), + ]; + + private static readonly LinearUnitExpectation[] LinearUnitExpectations = + [ + new(nameof(LinearUnit.Metre), new InfoExpectation("metre", "EPSG", 9001, "m", string.Empty, "Also known as International metre. SI standard unit"), 1d), + new(nameof(LinearUnit.Foot), new InfoExpectation("foot", "EPSG", 9002, "ft", string.Empty, string.Empty), 0.3048d), + new(nameof(LinearUnit.USSurveyFoot), new InfoExpectation("US survey foot", "EPSG", 9003, "American foot", "ftUS", "Used in USA"), 0.304800609601219d), + new(nameof(LinearUnit.NauticalMile), new InfoExpectation("nautical mile", "EPSG", 9030, "NM", string.Empty, string.Empty), 1852d), + new(nameof(LinearUnit.ClarkesFoot), new InfoExpectation("Clarke's foot", "EPSG", 9005, "Clarke's foot", string.Empty, "Assumes Clarke's 1865 ratio"), 0.3047972654d), + ]; + + private static readonly PrimeMeridianExpectation[] PrimeMeridianExpectations = + [ + new(nameof(PrimeMeridian.Greenwich), new InfoExpectation("Greenwich", "EPSG", 8901, string.Empty, string.Empty, string.Empty), 0d), + new(nameof(PrimeMeridian.Lisbon), new InfoExpectation("Lisbon", "EPSG", 8902, string.Empty, string.Empty, string.Empty), -9.0754862d), + new(nameof(PrimeMeridian.Paris), new InfoExpectation("Paris", "EPSG", 8903, string.Empty, string.Empty, "Value adopted by IGN (Paris) in 1936"), 2.5969213d), + new(nameof(PrimeMeridian.Bogota), new InfoExpectation("Bogota", "EPSG", 8904, string.Empty, string.Empty, string.Empty), -74.04513d), + new(nameof(PrimeMeridian.Madrid), new InfoExpectation("Madrid", "EPSG", 8905, string.Empty, string.Empty, string.Empty), -3.411658d), + new(nameof(PrimeMeridian.Rome), new InfoExpectation("Rome", "EPSG", 8906, string.Empty, string.Empty, string.Empty), 12.27084d), + new(nameof(PrimeMeridian.Bern), new InfoExpectation("Bern", "EPSG", 8907, string.Empty, string.Empty, "1895 value"), 7.26225d), + new(nameof(PrimeMeridian.Jakarta), new InfoExpectation("Jakarta", "EPSG", 8908, string.Empty, string.Empty, string.Empty), 106.482779d), + new(nameof(PrimeMeridian.Ferro), new InfoExpectation("Ferro", "EPSG", 8909, string.Empty, string.Empty, "Used in Austria and former Czechoslovakia"), -17.66666666666667d), + new(nameof(PrimeMeridian.Brussels), new InfoExpectation("Brussels", "EPSG", 8910, string.Empty, string.Empty, string.Empty), 4.220471d), + new(nameof(PrimeMeridian.Stockholm), new InfoExpectation("Stockholm", "EPSG", 8911, string.Empty, string.Empty, string.Empty), 18.03298d), + new(nameof(PrimeMeridian.Athens), new InfoExpectation("Athens", "EPSG", 8912, string.Empty, string.Empty, "Used in Greece for older mapping based on Hatt projection"), 23.4258815d), + new(nameof(PrimeMeridian.Oslo), new InfoExpectation("Oslo", "EPSG", 8913, string.Empty, string.Empty, "Formerly known as Kristiania or Christiania"), 10.43225d), + ]; + + private static readonly EllipsoidExpectation[] EllipsoidExpectations = + [ + new(nameof(Ellipsoid.Airy1830), new InfoExpectation("Airy 1830", "EPSG", 7001, string.Empty, string.Empty, string.Empty), 6377563.396d, 299.3249646d, true, LinearUnit.Metre), + new(nameof(Ellipsoid.Bessel1841), new InfoExpectation("Bessel 1841", "EPSG", 7004, string.Empty, string.Empty, string.Empty), 6377397.155d, 299.1528128d, true, LinearUnit.Metre), + new(nameof(Ellipsoid.WGS84), new InfoExpectation("WGS 84", "EPSG", 7030, "WGS84", string.Empty, "Inverse flattening derived from four defining parameters"), 6378137d, 298.257223563d, true, LinearUnit.Metre), + new(nameof(Ellipsoid.WGS72), new InfoExpectation("WGS 72", "EPSG", 7043, "WGS 72", string.Empty, string.Empty), 6378135d, 298.26d, true, LinearUnit.Metre), + new(nameof(Ellipsoid.GRS80), new InfoExpectation("GRS 1980", "EPSG", 7019, "International 1979", string.Empty, "Adopted by IUGG 1979 Canberra"), 6378137d, 298.257222101d, true, LinearUnit.Metre), + new(nameof(Ellipsoid.International1924), new InfoExpectation("International 1924", "EPSG", 7022, "Hayford 1909", string.Empty, "Described as a=6378388 m"), 6378388d, 297d, true, LinearUnit.Metre), + new(nameof(Ellipsoid.Clarke1880), new InfoExpectation("Clarke 1880", "EPSG", 7034, "Clarke 1880", string.Empty, "Clarke gave a and b"), 20926202d, 297d, true, LinearUnit.ClarkesFoot), + new(nameof(Ellipsoid.Clarke1866), new InfoExpectation("Clarke 1866", "EPSG", 7008, "Clarke 1866", string.Empty, "Original definition a=20926062"), 6378206.4d, double.PositiveInfinity, false, LinearUnit.Metre), + new(nameof(Ellipsoid.Sphere), new InfoExpectation("GRS 1980 Authalic Sphere", "EPSG", 7048, "Sphere", string.Empty, "Authalic sphere derived from GRS 1980 ellipsoid"), 6370997d, double.PositiveInfinity, false, LinearUnit.Metre), + ]; + + private static readonly HorizontalDatumExpectation[] HorizontalDatumExpectations = + [ + new(nameof(HorizontalDatum.WGS84), new InfoExpectation("World Geodetic System 1984", "EPSG", 6326, string.Empty, string.Empty, "Since 1997, WGS 84 has been maintained within 10cm"), DatumType.HD_Geocentric, Ellipsoid.WGS84, null), + new(nameof(HorizontalDatum.WGS72), new InfoExpectation("World Geodetic System 1972", "EPSG", 6322, string.Empty, string.Empty, "Used by GPS before 1987"), DatumType.HD_Geocentric, Ellipsoid.WGS72, new Wgs84ConversionInfo(0d, 0d, 4.5d, 0d, 0d, 0.554d, 0.219d)), + new(nameof(HorizontalDatum.ETRF89), new InfoExpectation("European Terrestrial Reference System 1989", "EPSG", 6258, "ETRF89", string.Empty, "The distinction in usage between ETRF89 and ETRS89 is confused"), DatumType.HD_Geocentric, Ellipsoid.GRS80, new Wgs84ConversionInfo()), + new(nameof(HorizontalDatum.ED50), new InfoExpectation("European Datum 1950", "EPSG", 6230, "ED50", string.Empty, string.Empty), DatumType.HD_Geocentric, Ellipsoid.International1924, new Wgs84ConversionInfo(-87d, -98d, -121d, 0d, 0d, 0d, 0d)), + ]; + + private static readonly VerticalDatumExpectation[] VerticalDatumExpectations = + [ + new(nameof(VerticalDatum.ODN), new InfoExpectation("Ordnance Datum Newlyn", "EPSG", 5101, string.Empty, string.Empty, string.Empty), DatumType.VD_GeoidModelDerived), + ]; + + /// + /// Returns the angular-unit expectations for theory-based tests. + /// + /// The angular-unit expectation rows. + public static IEnumerable GetAngularUnitExpectations() + { + return AngularUnitExpectations.Select(static expectation => new object[] + { + expectation.PropertyName, + expectation.Info.Name, + expectation.Info.Authority, + expectation.Info.AuthorityCode, + expectation.Info.Alias, + expectation.Info.Abbreviation, + expectation.Info.RemarksFragment, + expectation.RadiansPerUnit, + }); + } + + /// + /// Returns the linear-unit expectations for theory-based tests. + /// + /// The linear-unit expectation rows. + public static IEnumerable GetLinearUnitExpectations() + { + return LinearUnitExpectations.Select(static expectation => new object[] + { + expectation.PropertyName, + expectation.Info.Name, + expectation.Info.Authority, + expectation.Info.AuthorityCode, + expectation.Info.Alias, + expectation.Info.Abbreviation, + expectation.Info.RemarksFragment, + expectation.MetersPerUnit, + }); + } + + /// + /// Returns the prime-meridian expectations for theory-based tests. + /// + /// The prime-meridian expectation rows. + public static IEnumerable GetPrimeMeridianExpectations() + { + return PrimeMeridianExpectations.Select(static expectation => new object[] + { + expectation.PropertyName, + expectation.Info.Name, + expectation.Info.Authority, + expectation.Info.AuthorityCode, + expectation.Info.Alias, + expectation.Info.Abbreviation, + expectation.Info.RemarksFragment, + expectation.Longitude, + }); + } + + /// + /// Returns the ellipsoid expectations for theory-based tests. + /// + /// The ellipsoid expectation rows. + public static IEnumerable GetEllipsoidExpectations() + { + return EllipsoidExpectations.Select(static expectation => new object[] + { + expectation.PropertyName, + expectation.Info.Name, + expectation.Info.Authority, + expectation.Info.AuthorityCode, + expectation.Info.Alias, + expectation.Info.Abbreviation, + expectation.Info.RemarksFragment, + expectation.SemiMajorAxis, + expectation.InverseFlattening, + expectation.IsIvfDefinitive, + GetLinearUnitAccessorName(expectation.AxisUnit), + }); + } + + /// + /// Returns the horizontal-datum expectations for theory-based tests. + /// + /// The horizontal-datum expectation rows. + public static IEnumerable GetHorizontalDatumExpectations() + { + return HorizontalDatumExpectations.Select(static expectation => new object[] + { + expectation.PropertyName, + expectation.Info.Name, + expectation.Info.Authority, + expectation.Info.AuthorityCode, + expectation.Info.Alias, + expectation.Info.Abbreviation, + expectation.Info.RemarksFragment, + expectation.DatumType, + GetEllipsoidAccessorName(expectation.Ellipsoid), + expectation.ExpectedWgs84Parameters is not null, + expectation.ExpectedWgs84Parameters?.Dx ?? 0d, + expectation.ExpectedWgs84Parameters?.Dy ?? 0d, + expectation.ExpectedWgs84Parameters?.Dz ?? 0d, + expectation.ExpectedWgs84Parameters?.Ex ?? 0d, + expectation.ExpectedWgs84Parameters?.Ey ?? 0d, + expectation.ExpectedWgs84Parameters?.Ez ?? 0d, + expectation.ExpectedWgs84Parameters?.Ppm ?? 0d, + }); + } + + /// + /// Returns the vertical-datum expectations for theory-based tests. + /// + /// The vertical-datum expectation rows. + public static IEnumerable GetVerticalDatumExpectations() + { + return VerticalDatumExpectations.Select(static expectation => new object[] + { + expectation.PropertyName, + expectation.Info.Name, + expectation.Info.Authority, + expectation.Info.AuthorityCode, + expectation.Info.Alias, + expectation.Info.Abbreviation, + expectation.Info.RemarksFragment, + expectation.DatumType, + }); + } + + /// + /// Verifies that all selected public static property accessors are tracked by this test suite. + /// + [Fact] + public void PublicStaticAccessorProperties_AreCoveredByExpectationTables() + { + Dictionary coveredPropertyNames = new() + { + [typeof(AngularUnit)] = AngularUnitExpectations.Select(static expectation => expectation.PropertyName).ToArray(), + [typeof(LinearUnit)] = LinearUnitExpectations.Select(static expectation => expectation.PropertyName).ToArray(), + [typeof(PrimeMeridian)] = PrimeMeridianExpectations.Select(static expectation => expectation.PropertyName).ToArray(), + [typeof(Ellipsoid)] = EllipsoidExpectations.Select(static expectation => expectation.PropertyName).ToArray(), + [typeof(HorizontalDatum)] = HorizontalDatumExpectations.Select(static expectation => expectation.PropertyName).ToArray(), + [typeof(VerticalDatum)] = VerticalDatumExpectations.Select(static expectation => expectation.PropertyName).ToArray(), + [typeof(GeographicCoordinateSystem)] = [nameof(GeographicCoordinateSystem.WGS84)], + [typeof(GeocentricCoordinateSystem)] = [nameof(GeocentricCoordinateSystem.WGS84)], + [typeof(ProjectedCoordinateSystem)] = [nameof(ProjectedCoordinateSystem.WebMercator)], + [typeof(VerticalCoordinateSystem)] = [nameof(VerticalCoordinateSystem.ODN)], + }; + + foreach ((Type type, string[] expectedPropertyNames) in coveredPropertyNames) + { + string[] actualPropertyNames = type.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) + .Where(static property => property.PropertyType == property.DeclaringType) + .Select(static property => property.Name) + .OrderBy(static propertyName => propertyName, StringComparer.Ordinal) + .ToArray(); + string[] expected = expectedPropertyNames + .OrderBy(static propertyName => propertyName, StringComparer.Ordinal) + .ToArray(); + + Assert.Equal(expected, actualPropertyNames); + } + } + + /// + /// Verifies that built-in angular-unit accessors expose the expected metadata. + /// + /// The static property name. + /// The expected display name. + /// The expected authority. + /// The expected authority code. + /// The expected alias. + /// The expected abbreviation. + /// The expected remarks content or the empty string. + /// The expected radians-per-unit factor. + [Theory] + [MemberData(nameof(GetAngularUnitExpectations))] + public void AngularUnitStatics_HaveExpectedMetadata( + string propertyName, + string expectedName, + string expectedAuthority, + long expectedAuthorityCode, + string expectedAlias, + string expectedAbbreviation, + string expectedRemarksFragment, + double expectedRadiansPerUnit) + { + AngularUnit unit = GetStaticPropertyValue(typeof(AngularUnit), propertyName); + + AssertInfoMetadata(unit, new InfoExpectation(expectedName, expectedAuthority, expectedAuthorityCode, expectedAlias, expectedAbbreviation, expectedRemarksFragment)); + Assert.Equal(expectedRadiansPerUnit, unit.RadiansPerUnit, 15); + } + + /// + /// Verifies that built-in linear-unit accessors expose the expected metadata. + /// + /// The static property name. + /// The expected display name. + /// The expected authority. + /// The expected authority code. + /// The expected alias. + /// The expected abbreviation. + /// The expected remarks content or the empty string. + /// The expected meters-per-unit factor. + [Theory] + [MemberData(nameof(GetLinearUnitExpectations))] + public void LinearUnitStatics_HaveExpectedMetadata( + string propertyName, + string expectedName, + string expectedAuthority, + long expectedAuthorityCode, + string expectedAlias, + string expectedAbbreviation, + string expectedRemarksFragment, + double expectedMetersPerUnit) + { + LinearUnit unit = GetStaticPropertyValue(typeof(LinearUnit), propertyName); + + AssertInfoMetadata(unit, new InfoExpectation(expectedName, expectedAuthority, expectedAuthorityCode, expectedAlias, expectedAbbreviation, expectedRemarksFragment)); + Assert.Equal(expectedMetersPerUnit, unit.MetersPerUnit, 15); + } + + /// + /// Verifies that built-in prime-meridian accessors expose the expected metadata. + /// + /// The static property name. + /// The expected display name. + /// The expected authority. + /// The expected authority code. + /// The expected alias. + /// The expected abbreviation. + /// The expected remarks content or the empty string. + /// The expected longitude in degrees. + [Theory] + [MemberData(nameof(GetPrimeMeridianExpectations))] + public void PrimeMeridianStatics_HaveExpectedMetadata( + string propertyName, + string expectedName, + string expectedAuthority, + long expectedAuthorityCode, + string expectedAlias, + string expectedAbbreviation, + string expectedRemarksFragment, + double expectedLongitude) + { + PrimeMeridian meridian = GetStaticPropertyValue(typeof(PrimeMeridian), propertyName); + + AssertInfoMetadata(meridian, new InfoExpectation(expectedName, expectedAuthority, expectedAuthorityCode, expectedAlias, expectedAbbreviation, expectedRemarksFragment)); + Assert.Equal(expectedLongitude, meridian.Longitude, 12); + Assert.True(meridian.AngularUnit.EqualParams(AngularUnit.Degrees)); + } + + /// + /// Verifies that built-in ellipsoid accessors expose the expected metadata. + /// + /// The static property name. + /// The expected display name. + /// The expected authority. + /// The expected authority code. + /// The expected alias. + /// The expected abbreviation. + /// The expected remarks content or the empty string. + /// The expected semi-major axis. + /// The expected inverse flattening. + /// The expected IVF-definitive flag. + /// The expected axis-unit accessor name. + [Theory] + [MemberData(nameof(GetEllipsoidExpectations))] + public void EllipsoidStatics_HaveExpectedMetadata( + string propertyName, + string expectedName, + string expectedAuthority, + long expectedAuthorityCode, + string expectedAlias, + string expectedAbbreviation, + string expectedRemarksFragment, + double expectedSemiMajorAxis, + double expectedInverseFlattening, + bool expectedIsIvfDefinitive, + string expectedAxisUnitName) + { + Ellipsoid ellipsoid = GetStaticPropertyValue(typeof(Ellipsoid), propertyName); + LinearUnit expectedAxisUnit = GetStaticPropertyValue(typeof(LinearUnit), expectedAxisUnitName); + + AssertInfoMetadata(ellipsoid, new InfoExpectation(expectedName, expectedAuthority, expectedAuthorityCode, expectedAlias, expectedAbbreviation, expectedRemarksFragment)); + Assert.Equal(expectedSemiMajorAxis, ellipsoid.SemiMajorAxis, 12); + Assert.Equal(expectedInverseFlattening, ellipsoid.InverseFlattening, 12); + Assert.Equal(expectedIsIvfDefinitive, ellipsoid.IsIvfDefinitive); + Assert.True(ellipsoid.AxisUnit.EqualParams(expectedAxisUnit)); + } + + /// + /// Verifies that built-in horizontal-datum accessors expose the expected metadata. + /// + /// The static property name. + /// The expected display name. + /// The expected authority. + /// The expected authority code. + /// The expected alias. + /// The expected abbreviation. + /// The expected remarks content or the empty string. + /// The expected datum type. + /// The expected ellipsoid accessor name. + /// when Bursa-Wolf parameters are expected. + /// The expected X translation. + /// The expected Y translation. + /// The expected Z translation. + /// The expected X rotation. + /// The expected Y rotation. + /// The expected Z rotation. + /// The expected ppm scale term. + [Theory] + [MemberData(nameof(GetHorizontalDatumExpectations))] + public void HorizontalDatumStatics_HaveExpectedMetadata( + string propertyName, + string expectedName, + string expectedAuthority, + long expectedAuthorityCode, + string expectedAlias, + string expectedAbbreviation, + string expectedRemarksFragment, + DatumType expectedDatumType, + string expectedEllipsoidName, + bool hasExpectedWgs84Parameters, + double expectedDx, + double expectedDy, + double expectedDz, + double expectedEx, + double expectedEy, + double expectedEz, + double expectedPpm) + { + HorizontalDatum datum = GetStaticPropertyValue(typeof(HorizontalDatum), propertyName); + Ellipsoid expectedEllipsoid = GetStaticPropertyValue(typeof(Ellipsoid), expectedEllipsoidName); + Wgs84ConversionInfo? expectedParameters = hasExpectedWgs84Parameters + ? new Wgs84ConversionInfo(expectedDx, expectedDy, expectedDz, expectedEx, expectedEy, expectedEz, expectedPpm) + : null; + + AssertInfoMetadata(datum, new InfoExpectation(expectedName, expectedAuthority, expectedAuthorityCode, expectedAlias, expectedAbbreviation, expectedRemarksFragment)); + Assert.Equal(expectedDatumType, datum.DatumType); + Assert.True(datum.Ellipsoid.EqualParams(expectedEllipsoid)); + AssertWgs84Parameters(datum.Wgs84Parameters, expectedParameters); + } + + /// + /// Verifies that the predefined vertical datum keeps its expected metadata. + /// + /// The static property name. + /// The expected display name. + /// The expected authority. + /// The expected authority code. + /// The expected alias. + /// The expected abbreviation. + /// The expected remarks content or the empty string. + /// The expected datum type. + [Theory] + [MemberData(nameof(GetVerticalDatumExpectations))] + public void VerticalDatumStatics_HaveExpectedMetadata( + string propertyName, + string expectedName, + string expectedAuthority, + long expectedAuthorityCode, + string expectedAlias, + string expectedAbbreviation, + string expectedRemarksFragment, + DatumType expectedDatumType) + { + VerticalDatum datum = GetStaticPropertyValue(typeof(VerticalDatum), propertyName); + + AssertInfoMetadata(datum, new InfoExpectation(expectedName, expectedAuthority, expectedAuthorityCode, expectedAlias, expectedAbbreviation, expectedRemarksFragment)); + Assert.Equal(expectedDatumType, datum.DatumType); + } + + /// + /// Verifies that the predefined geographic WGS84 accessor keeps its expected metadata and axis order. + /// + [Fact] + public void GeographicCoordinateSystemWgs84_HasExpectedMetadata() + { + GeographicCoordinateSystem coordinateSystem = GeographicCoordinateSystem.WGS84; + + AssertInfoMetadata(coordinateSystem, new InfoExpectation("WGS 84", "EPSG", 4326, string.Empty, string.Empty, string.Empty)); + Assert.True(coordinateSystem.AngularUnit.EqualParams(AngularUnit.Degrees)); + Assert.True(coordinateSystem.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(coordinateSystem.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)); + AssertAxis(coordinateSystem.GetAxis(0), "Lon", AxisOrientationEnum.East); + AssertAxis(coordinateSystem.GetAxis(1), "Lat", AxisOrientationEnum.North); + } + + /// + /// Verifies that the predefined geocentric WGS84 accessor keeps its expected metadata and axis order. + /// + [Fact] + public void GeocentricCoordinateSystemWgs84_HasExpectedMetadata() + { + GeocentricCoordinateSystem coordinateSystem = GeocentricCoordinateSystem.WGS84; + + AssertInfoMetadata(coordinateSystem, new InfoExpectation("WGS 84", "EPSG", 4978, string.Empty, string.Empty, string.Empty)); + Assert.True(coordinateSystem.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(coordinateSystem.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.True(coordinateSystem.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)); + AssertAxis(coordinateSystem.GetAxis(0), "Geocentric X (X)", AxisOrientationEnum.Other); + AssertAxis(coordinateSystem.GetAxis(1), "Geocentric Y (Y)", AxisOrientationEnum.East); + AssertAxis(coordinateSystem.GetAxis(2), "Geocentric Z (Z)", AxisOrientationEnum.North); + } + + /// + /// Verifies that the predefined Web Mercator accessor keeps its expected metadata and normalization. + /// + [Fact] + public void ProjectedCoordinateSystemWebMercator_HasExpectedMetadata() + { + ProjectedCoordinateSystem coordinateSystem = ProjectedCoordinateSystem.WebMercator; + Projection projection = Assert.IsType(coordinateSystem.Projection); + + AssertInfoMetadata( + coordinateSystem, + new InfoExpectation( + "WGS 84 / Pseudo-Mercator", + "EPSG", + 3857, + "WGS 84 / Popular Visualisation Pseudo-Mercator", + "WebMercator", + "spherical development of ellipsoidal coordinates")); + Assert.True(coordinateSystem.GeographicCoordinateSystem.EqualParams(GeographicCoordinateSystem.WGS84)); + Assert.True(coordinateSystem.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(coordinateSystem.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.Equal("Popular Visualisation Pseudo-Mercator", projection.ClassName); + AssertAxis(coordinateSystem.GetAxis(0), "East", AxisOrientationEnum.East); + AssertAxis(coordinateSystem.GetAxis(1), "North", AxisOrientationEnum.North); + } + + /// + /// Verifies that the predefined WGS84 UTM helper keeps its expected metadata for both hemispheres. + /// + /// The UTM zone. + /// for the northern hemisphere; otherwise . + /// The expected authority code. + /// The expected coordinate-system name. + [Theory] + [InlineData(32, true, 32632L, "WGS 84 / UTM zone 32N")] + [InlineData(32, false, 32732L, "WGS 84 / UTM zone 32S")] + public void ProjectedCoordinateSystemWgs84Utm_HasExpectedMetadata(int zone, bool zoneIsNorth, long expectedAuthorityCode, string expectedName) + { + var coordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(zone, zoneIsNorth); + Projection projection = Assert.IsType(coordinateSystem.Projection); + + AssertInfoMetadata( + coordinateSystem, + new InfoExpectation( + expectedName, + "EPSG", + expectedAuthorityCode, + string.Empty, + string.Empty, + "Large and medium scale topographic mapping and engineering survey")); + Assert.True(coordinateSystem.GeographicCoordinateSystem.EqualParams(GeographicCoordinateSystem.WGS84)); + Assert.True(coordinateSystem.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(coordinateSystem.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.Equal("Transverse_Mercator", projection.ClassName); + AssertAxis(coordinateSystem.GetAxis(0), "East", AxisOrientationEnum.East); + AssertAxis(coordinateSystem.GetAxis(1), "North", AxisOrientationEnum.North); + } + + /// + /// Verifies that the predefined ODN vertical coordinate system keeps its expected metadata. + /// + [Fact] + public void VerticalCoordinateSystemOdn_HasExpectedMetadata() + { + VerticalCoordinateSystem coordinateSystem = VerticalCoordinateSystem.ODN; + + AssertInfoMetadata(coordinateSystem, new InfoExpectation("Newlyn", "EPSG", 5701, string.Empty, "ODN", string.Empty)); + Assert.True(coordinateSystem.VerticalDatum.EqualParams(VerticalDatum.ODN)); + Assert.True(coordinateSystem.LinearUnit.EqualParams(LinearUnit.Metre)); + AssertAxis(coordinateSystem.GetAxis(0), "Up", AxisOrientationEnum.Up); + } + + private static T GetStaticPropertyValue(Type declaringType, string propertyName) + { + PropertyInfo property = Assert.IsType( + declaringType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static), + exactMatch: false); + return Assert.IsType(property.GetValue(null)); + } + + private static void AssertInfoMetadata(Info info, InfoExpectation expectation) + { + Assert.Equal(expectation.Name, info.Name); + Assert.Equal(expectation.Authority, info.Authority); + Assert.Equal(expectation.AuthorityCode, info.AuthorityCode); + Assert.Equal(expectation.Alias, info.Alias); + Assert.Equal(expectation.Abbreviation, info.Abbreviation); + AssertRemarks(info.Remarks, expectation.RemarksFragment); + } + + private static void AssertRemarks(string actual, string expectedFragment) + { + if (expectedFragment.Length == 0) + { + Assert.Equal(string.Empty, actual); + return; + } + + Assert.Contains(expectedFragment, actual, StringComparison.Ordinal); + } + + private static void AssertAxis(AxisInfo axis, string expectedName, AxisOrientationEnum expectedOrientation) + { + Assert.Equal(expectedName, axis.Name); + Assert.Equal(expectedOrientation, axis.Orientation); + } + + private static void AssertWgs84Parameters(Wgs84ConversionInfo? actual, Wgs84ConversionInfo? expected) + { + if (expected is null) + { + Assert.Null(actual); + return; + } + + Wgs84ConversionInfo parameters = Assert.IsType(actual); + Assert.Equal(expected.Dx, parameters.Dx, 12); + Assert.Equal(expected.Dy, parameters.Dy, 12); + Assert.Equal(expected.Dz, parameters.Dz, 12); + Assert.Equal(expected.Ex, parameters.Ex, 12); + Assert.Equal(expected.Ey, parameters.Ey, 12); + Assert.Equal(expected.Ez, parameters.Ez, 12); + Assert.Equal(expected.Ppm, parameters.Ppm, 12); + } + + private static string GetEllipsoidAccessorName(Ellipsoid ellipsoid) + { + if (ellipsoid.EqualParams(Ellipsoid.WGS84)) + { + return nameof(Ellipsoid.WGS84); + } + + if (ellipsoid.EqualParams(Ellipsoid.WGS72)) + { + return nameof(Ellipsoid.WGS72); + } + + if (ellipsoid.EqualParams(Ellipsoid.GRS80)) + { + return nameof(Ellipsoid.GRS80); + } + + if (ellipsoid.EqualParams(Ellipsoid.Airy1830)) + { + return nameof(Ellipsoid.Airy1830); + } + + if (ellipsoid.EqualParams(Ellipsoid.Bessel1841)) + { + return nameof(Ellipsoid.Bessel1841); + } + + if (ellipsoid.EqualParams(Ellipsoid.International1924)) + { + return nameof(Ellipsoid.International1924); + } + + throw new InvalidOperationException($"No ellipsoid accessor mapping was configured for '{ellipsoid.Name}'."); + } + + private static string GetLinearUnitAccessorName(LinearUnit unit) + { + if (unit.EqualParams(LinearUnit.Metre)) + { + return nameof(LinearUnit.Metre); + } + + if (unit.EqualParams(LinearUnit.ClarkesFoot)) + { + return nameof(LinearUnit.ClarkesFoot); + } + + throw new InvalidOperationException($"No linear-unit accessor mapping was configured for '{unit.Name}'."); + } + + /// + /// Expected metadata for an -derived static accessor. + /// + /// The expected display name. + /// The expected authority. + /// The expected authority code. + /// The expected alias. + /// The expected abbreviation. + /// The expected remarks content or the empty string. + private sealed record InfoExpectation( + string Name, + string Authority, + long AuthorityCode, + string Alias, + string Abbreviation, + string RemarksFragment); + + /// + /// Expected metadata for an angular-unit static accessor. + /// + /// The static property name. + /// The shared info metadata expectation. + /// The expected radians-per-unit factor. + private sealed record AngularUnitExpectation(string PropertyName, InfoExpectation Info, double RadiansPerUnit); + + /// + /// Expected metadata for a linear-unit static accessor. + /// + /// The static property name. + /// The shared info metadata expectation. + /// The expected meters-per-unit factor. + private sealed record LinearUnitExpectation(string PropertyName, InfoExpectation Info, double MetersPerUnit); + + /// + /// Expected metadata for a prime-meridian static accessor. + /// + /// The static property name. + /// The shared info metadata expectation. + /// The expected longitude. + private sealed record PrimeMeridianExpectation(string PropertyName, InfoExpectation Info, double Longitude); + + /// + /// Expected metadata for an ellipsoid static accessor. + /// + /// The static property name. + /// The shared info metadata expectation. + /// The expected semi-major axis. + /// The expected inverse flattening. + /// The expected IVF-definitive flag. + /// The expected axis unit. + private sealed record EllipsoidExpectation( + string PropertyName, + InfoExpectation Info, + double SemiMajorAxis, + double InverseFlattening, + bool IsIvfDefinitive, + LinearUnit AxisUnit); + + /// + /// Expected metadata for a horizontal-datum static accessor. + /// + /// The static property name. + /// The shared info metadata expectation. + /// The expected datum type. + /// The expected ellipsoid. + /// The expected Bursa-Wolf parameters, or . + private sealed record HorizontalDatumExpectation( + string PropertyName, + InfoExpectation Info, + DatumType DatumType, + Ellipsoid Ellipsoid, + Wgs84ConversionInfo? ExpectedWgs84Parameters); + + /// + /// Expected metadata for a vertical-datum static accessor. + /// + /// The static property name. + /// The shared info metadata expectation. + /// The expected datum type. + private sealed record VerticalDatumExpectation( + string PropertyName, + InfoExpectation Info, + DatumType DatumType); +} diff --git a/test/ProjNet.Tests/CodeQuality/StaticInstanceImmutabilityTests.cs b/test/ProjNet.Tests/CodeQuality/StaticInstanceImmutabilityTests.cs new file mode 100644 index 00000000..72bb8b23 --- /dev/null +++ b/test/ProjNet.Tests/CodeQuality/StaticInstanceImmutabilityTests.cs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Verifies that cloning well-known static coordinate-system model accessors does not mutate subsequent accessor results. +/// +public class StaticInstanceImmutabilityTests +{ + /// + /// Verifies that representative static accessors stay unchanged after clone-style metadata updates. + /// + [Fact] + public void StaticAccessors_RemainUnchangedAfterMetadataCloneOperations() + { + AssertCloneIsolation( + () => Ellipsoid.WGS84, + ellipsoid => ellipsoid.WithAuthority("TEST", 17030).WithName("Mutated ellipsoid"), + "WGS 84", + "EPSG", + 7030); + AssertCloneIsolation( + () => LinearUnit.Metre, + unit => unit.WithAuthority("TEST", 19001).WithName("Mutated metre"), + "metre", + "EPSG", + 9001); + AssertCloneIsolation( + () => HorizontalDatum.WGS84, + datum => datum.WithAuthority("TEST", 16326).WithName("Mutated datum"), + "World Geodetic System 1984", + "EPSG", + 6326); + AssertCloneIsolation( + () => GeographicCoordinateSystem.WGS84, + coordinateSystem => coordinateSystem.WithAuthority("TEST", 14326).WithName("Mutated geographic CRS"), + "WGS 84", + "EPSG", + 4326); + AssertCloneIsolation( + () => VerticalCoordinateSystem.ODN, + coordinateSystem => coordinateSystem.WithAuthority("TEST", 15701).WithName("Mutated vertical CRS"), + "Newlyn", + "EPSG", + 5701); + } + + private static void AssertCloneIsolation( + Func accessor, + Func cloneFactory, + string expectedName, + string expectedAuthority, + long expectedAuthorityCode) + where TInfo : Info + { + TInfo original = accessor(); + TInfo clone = cloneFactory(original); + TInfo fresh = accessor(); + + Assert.NotSame(original, clone); + Assert.Equal(expectedName, original.Name); + Assert.Equal(expectedAuthority, original.Authority); + Assert.Equal(expectedAuthorityCode, original.AuthorityCode); + Assert.Equal(expectedName, fresh.Name); + Assert.Equal(expectedAuthority, fresh.Authority); + Assert.Equal(expectedAuthorityCode, fresh.AuthorityCode); + Assert.NotEqual(expectedName, clone.Name); + Assert.NotEqual(expectedAuthority, clone.Authority); + Assert.NotEqual(expectedAuthorityCode, clone.AuthorityCode); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystemServicesTest.cs b/test/ProjNet.Tests/CoordinateSystemServicesTest.cs deleted file mode 100644 index 0110c533..00000000 --- a/test/ProjNet.Tests/CoordinateSystemServicesTest.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading; -using System.Xml.Linq; -using NUnit.Framework; -using ProjNet; -using ProjNet.CoordinateSystems; -using ProjNet.CoordinateSystems.Transformations; - -namespace ProjNET.Tests -{ - public class CoordinateSystemServicesTest - { - [Test] - public void TestConstructor() - { - var css = new CoordinateSystemServices(new CoordinateSystemFactory(), - new CoordinateTransformationFactory()); - - Assert.IsNotNull(css.GetCoordinateSystem(4326)); - Assert.IsNotNull(css.GetCoordinateSystem(3857)); - } - - [TestCase(@"D:\temp\ConsoleApplication9\SpatialRefSys.xml")] - public void TestConstructorLoadXml(string xmlPath) - { - if (!File.Exists(xmlPath)) - throw new IgnoreException("Specified file not found"); - - var css = new CoordinateSystemServices(new CoordinateSystemFactory(), - new CoordinateTransformationFactory(), LoadXml(xmlPath)); - - Assert.IsNotNull(css.GetCoordinateSystem(4326)); - Assert.IsNotNull(css.GetCoordinateSystem("EPSG", 4326)); - Assert.IsTrue(ReferenceEquals(css.GetCoordinateSystem("EPSG", 4326), css.GetCoordinateSystem(4326))); - - } - - [TestCase(@"")] - public void TestConstructorLoadCsv(string csvPath) - { - if (!string.IsNullOrWhiteSpace(csvPath)) - if (!File.Exists(csvPath)) - throw new IgnoreException("Specified file not found"); - - var css = new CoordinateSystemServices(new CoordinateSystemFactory(), - new CoordinateTransformationFactory(), LoadCsv(csvPath)); - - Assert.IsNotNull(css.GetCoordinateSystem(4326)); - Assert.IsNotNull(css.GetCoordinateSystem("EPSG", 4326)); - Assert.IsTrue(ReferenceEquals(css.GetCoordinateSystem("EPSG", 4326), css.GetCoordinateSystem(4326))); - Thread.Sleep(1000); - - } - - - internal static IEnumerable> LoadCsv(string csvPath = null) - { - - Console.WriteLine("Reading '{0}'.", csvPath ?? "SRID.csv from resources stream"); - var sw = new Stopwatch(); - sw.Start(); - - foreach (var sridWkt in SRIDReader.GetSrids(csvPath)) - yield return new KeyValuePair(sridWkt.WktId, sridWkt.Wkt); - - sw.Stop(); - Console.WriteLine("Read '{1}' in {0:N0}ms", sw.ElapsedMilliseconds, csvPath ?? "SRID.csv from resources stream"); - } - - private static IEnumerable> LoadXml(string xmlPath) - { - var stream = System.IO.File.OpenRead(xmlPath); - - Console.WriteLine("Reading '{0}'.", xmlPath); - var sw = new Stopwatch(); - sw.Start(); - - var document = XDocument.Load(stream); - - var rs = from tmp in document.Elements("SpatialReference").Elements("ReferenceSystem") select tmp; - - foreach (var node in rs) - { - var sridElement = node.Element("SRID"); - if (sridElement != null) - { - int srid = int.Parse(sridElement.Value); - yield return new KeyValuePair(srid, node.LastNode.ToString()); - } - } - - sw.Stop(); - Console.WriteLine("Read '{1}' in {0:N0}ms", sw.ElapsedMilliseconds, xmlPath); - } - - } -} diff --git a/test/ProjNet.Tests/CoordinateSystems/AuxiliaryLatitudeSeriesTests.cs b/test/ProjNet.Tests/CoordinateSystems/AuxiliaryLatitudeSeriesTests.cs new file mode 100644 index 00000000..3e6ac134 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/AuxiliaryLatitudeSeriesTests.cs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using Xunit; + +/// +/// Verifies the auxiliary-latitude series coefficients used by the exact ETMERC helpers. +/// +public class AuxiliaryLatitudeSeriesTests +{ + /// + /// Verifies the geographic-to-conformal coefficient set against WGS84 reference values. + /// + [Fact] + public void BuildGeographicToConformalCoefficients_WithWgs84ThirdFlattening_ReturnsReferenceValues() + { + double thirdFlattening = CreateWgs84ThirdFlattening(); + double[] actual = AuxiliaryLatitudeSeries.BuildGeographicToConformalCoefficients(thirdFlattening); + double[] expected = + [ + -3.356554619797427665e-03, + 4.694573027162596873e-06, + -8.194497547212908632e-09, + 1.557996682859191760e-11, + -3.103292241538314634e-14, + 6.389147500821738608e-17, + ]; + + AssertCoefficientsEqual(expected, actual); + } + + /// + /// Verifies the conformal-to-geographic coefficient set against WGS84 reference values. + /// + [Fact] + public void BuildConformalToGeographicCoefficients_WithWgs84ThirdFlattening_ReturnsReferenceValues() + { + double thirdFlattening = CreateWgs84ThirdFlattening(); + double[] actual = AuxiliaryLatitudeSeries.BuildConformalToGeographicCoefficients(thirdFlattening); + double[] expected = + [ + 3.356551469132832127e-03, + 6.571873198628443494e-06, + 1.764640411308580812e-08, + 5.387753784255921739e-11, + 1.764007472632213096e-13, + 6.056073876794177066e-16, + ]; + + AssertCoefficientsEqual(expected, actual); + } + + /// + /// Verifies the conformal/rectifying coefficient sets and normalized radius against WGS84 reference values. + /// + [Fact] + public void RectifyingCoefficientFamilies_WithWgs84ThirdFlattening_ReturnReferenceValues() + { + double thirdFlattening = CreateWgs84ThirdFlattening(); + double[] conformalToRectifying = AuxiliaryLatitudeSeries.BuildConformalToRectifyingCoefficients(thirdFlattening); + double[] rectifyingToConformal = AuxiliaryLatitudeSeries.BuildRectifyingToConformalCoefficients(thirdFlattening); + + double[] expectedConformalToRectifying = + [ + 8.377318206244698320e-04, + 7.608527773572307478e-07, + 1.197645503329452535e-09, + 2.429170607201358663e-12, + 5.711757677865803845e-15, + 1.491117731258389510e-17, + ]; + + double[] expectedRectifyingToConformal = + [ + -8.377321640579486446e-04, + -5.905870152220203267e-08, + -1.673482665283996826e-10, + -2.164798040062705858e-13, + -3.787978046168604770e-16, + -7.248748890694154495e-19, + ]; + + AssertCoefficientsEqual(expectedConformalToRectifying, conformalToRectifying); + AssertCoefficientsEqual(expectedRectifyingToConformal, rectifyingToConformal); + Assert.Equal(9.983242984312526991e-01, AuxiliaryLatitudeSeries.RectifyingRadius(thirdFlattening), 15); + } + + /// + /// Verifies that the geographic/conformal coefficient pair round-trips a representative latitude. + /// + [Fact] + public void Convert_WithInverseCoefficientPair_RoundTripsRepresentativeLatitude() + { + double thirdFlattening = CreateWgs84ThirdFlattening(); + double geographicLatitude = DegreesToRadians(40d); + double[] geographicToConformal = AuxiliaryLatitudeSeries.BuildGeographicToConformalCoefficients(thirdFlattening); + double[] conformalToGeographic = AuxiliaryLatitudeSeries.BuildConformalToGeographicCoefficients(thirdFlattening); + + double conformalLatitude = AuxiliaryLatitudeSeries.Convert(geographicLatitude, geographicToConformal); + double roundTrippedLatitude = AuxiliaryLatitudeSeries.Convert(conformalLatitude, conformalToGeographic); + + Assert.Equal(6.948277525098944807e-01, conformalLatitude, 15); + Assert.Equal(geographicLatitude, roundTrippedLatitude, 15); + } + + private static double DegreesToRadians(double degrees) + => degrees * (Math.PI / 180d); + + private static void AssertCoefficientsEqual(double[] expected, double[] actual) + { + Assert.Equal(expected.Length, actual.Length); + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], actual[i], 15); + } + } + + private static double CreateWgs84ThirdFlattening() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + double flattening = 1d / ellipsoid.InverseFlattening; + return flattening / (2d - flattening); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/AxisInfoTests.cs b/test/ProjNet.Tests/CoordinateSystems/AxisInfoTests.cs new file mode 100644 index 00000000..62c347e8 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/AxisInfoTests.cs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class AxisInfoTests +{ + /// + /// Verifies that the constructor assigns name and orientation. + /// + [Fact] + public void Constructor_SetsNameAndOrientation() + { + var axis = new AxisInfo("Longitude", AxisOrientationEnum.East); + + Assert.Equal("Longitude", axis.Name); + Assert.Equal(AxisOrientationEnum.East, axis.Orientation); + } + + /// + /// Verifies that the copy constructor duplicates the source values into a distinct instance. + /// + [Fact] + public void CopyConstructor_CopiesValuesIntoNewInstance() + { + var source = new AxisInfo("Longitude", AxisOrientationEnum.East); + + var copy = new AxisInfo(source); + + Assert.NotSame(source, copy); + Assert.Equal(source.Name, copy.Name); + Assert.Equal(source.Orientation, copy.Orientation); + } + + /// + /// Verifies that WKT uses the expected keyword, axis name, and upper-cased orientation. + /// + [Fact] + public void WKT_FormatsExpectedValue() + { + var axis = new AxisInfo("Latitude", AxisOrientationEnum.North); + + Assert.Equal("AXIS[\"Latitude\", NORTH]", axis.WKT); + } + + /// + /// Verifies that XML uses the expected element name and attributes. + /// + [Fact] + public void XML_FormatsExpectedValue() + { + var axis = new AxisInfo("Longitude", AxisOrientationEnum.East); + + Assert.Equal("", axis.XML); + } + + /// + /// Verifies that returns the expected XML element. + /// + [Fact] + public void ToXml_ReturnsExpectedElement() + { + var axis = new AxisInfo("Height", AxisOrientationEnum.Up); + + XElement xml = axis.ToXml(); + + Assert.Equal("CS_AxisInfo", xml.Name.LocalName); + Assert.Equal("Height", (string?)xml.Attribute("Name")); + Assert.Equal("UP", (string?)xml.Attribute("Orientation")); + } + + /// + /// Verifies that returns the expected node structure. + /// + [Fact] + public void ToWktNode_ReturnsExpectedKeywordNode() + { + var axis = new AxisInfo("Longitude", AxisOrientationEnum.East); + + WktKeywordNode node = Assert.IsType(axis.ToWktNode()); + + Assert.Equal("AXIS", node.Keyword); + Assert.Equal(2, node.Children.Count); + + WktQuotedString nameNode = Assert.IsType(node.Children[0]); + Assert.Equal("Longitude", nameNode.Value); + + WktIdentifier orientationNode = Assert.IsType(node.Children[1]); + Assert.Equal("EAST", orientationNode.Name); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/BoundCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/BoundCoordinateSystemTests.cs new file mode 100644 index 00000000..2499bec4 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/BoundCoordinateSystemTests.cs @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for and . +/// +public class BoundCoordinateSystemTests +{ + /// + /// Verifies that the constructor stores the supplied values and copies the source axes. + /// + [Fact] + public void Constructor_SetsPropertiesAndCopiesSourceAxes() + { + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem( + "Source", + CreateCustomAxisInfo(), + AngularUnit.Grad, + PrimeMeridian.Paris); + GeographicCoordinateSystem targetCoordinateSystem = GeographicCoordinateSystem.WGS84; + BoundTransformation transformation = new("Geocentric translations", new Wgs84ConversionInfo(1, 2, 3, 0, 0, 0, 0)); + var system = new BoundCoordinateSystem( + sourceCoordinateSystem, + targetCoordinateSystem, + transformation, + "Bound source", + "TEST", + 42, + "alias", + "abbr", + "remarks"); + + Assert.Equal("Bound source", system.Name); + Assert.Equal("TEST", system.Authority); + Assert.Equal(42, system.AuthorityCode); + Assert.Equal("alias", system.Alias); + Assert.Equal("abbr", system.Abbreviation); + Assert.Equal("remarks", system.Remarks); + Assert.Same(sourceCoordinateSystem, system.SourceCoordinateSystem); + Assert.Same(targetCoordinateSystem, system.TargetCoordinateSystem); + Assert.Same(transformation, system.Transformation); + Assert.Equal(sourceCoordinateSystem.Dimension, system.Dimension); + Assert.NotSame(sourceCoordinateSystem.GetAxis(0), system.GetAxis(0)); + Assert.Equal(sourceCoordinateSystem.GetAxis(0).Name, system.GetAxis(0).Name); + Assert.Equal(sourceCoordinateSystem.GetAxis(0).Orientation, system.GetAxis(0).Orientation); + Assert.NotSame(sourceCoordinateSystem.GetAxis(1), system.GetAxis(1)); + Assert.Equal(sourceCoordinateSystem.GetAxis(1).Name, system.GetAxis(1).Name); + Assert.Equal(sourceCoordinateSystem.GetAxis(1).Orientation, system.GetAxis(1).Orientation); + } + + /// + /// Verifies that the constructor rejects a null source coordinate system. + /// + [Fact] + public void Constructor_NullSourceCoordinateSystem_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new BoundCoordinateSystem( + null!, + GeographicCoordinateSystem.WGS84, + CreateWgs84Transformation(), + "Bound source", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("sourceCoordinateSystem", exception.ParamName); + } + + /// + /// Verifies that the constructor rejects a null target coordinate system. + /// + [Fact] + public void Constructor_NullTargetCoordinateSystem_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new BoundCoordinateSystem( + CreateCoordinateSystem("Source"), + null!, + CreateWgs84Transformation(), + "Bound source", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("targetCoordinateSystem", exception.ParamName); + } + + /// + /// Verifies that the constructor rejects a null bound transformation. + /// + [Fact] + public void Constructor_NullTransformation_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new BoundCoordinateSystem( + CreateCoordinateSystem("Source"), + GeographicCoordinateSystem.WGS84, + null!, + "Bound source", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("transformation", exception.ParamName); + } + + /// + /// Verifies that WKT falls back to the source coordinate system representation. + /// + [Fact] + public void WKT_DelegatesToSourceCoordinateSystem() + { + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem("Source", angularUnit: AngularUnit.Grad, primeMeridian: PrimeMeridian.Paris); + BoundCoordinateSystem system = CreateSystem(sourceCoordinateSystem: sourceCoordinateSystem); + + Assert.Equal(sourceCoordinateSystem.WKT, system.WKT); + } + + /// + /// Verifies that XML falls back to the source coordinate system representation. + /// + [Fact] + public void XML_DelegatesToSourceCoordinateSystem() + { + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem("Source", angularUnit: AngularUnit.Grad, primeMeridian: PrimeMeridian.Paris); + BoundCoordinateSystem system = CreateSystem(sourceCoordinateSystem: sourceCoordinateSystem); + + Assert.Equal(sourceCoordinateSystem.XML, system.XML); + } + + /// + /// Verifies that delegates to the source coordinate system. + /// + [Fact] + public void ToXml_DelegatesToSourceCoordinateSystem() + { + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem("Source"); + BoundCoordinateSystem system = CreateSystem(sourceCoordinateSystem: sourceCoordinateSystem); + + Assert.True(XNode.DeepEquals(sourceCoordinateSystem.ToXml(), system.ToXml())); + } + + /// + /// Verifies that exposes the source coordinate system structure. + /// + [Fact] + public void ToWktNode_ReturnsSourceCoordinateSystemStructure() + { + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem("Source"); + BoundCoordinateSystem system = CreateSystem(sourceCoordinateSystem: sourceCoordinateSystem); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal("GEOGCS", node.Keyword); + Assert.Equal(sourceCoordinateSystem.WKT, node.ToString()); + } + + /// + /// Verifies that WKT2 output emits a BOUNDCRS node and roundtrips through the native reader. + /// + [Fact] + public void ToWktNode_WithWkt22019_RoundTripsAsBoundCrs() + { + CoordinateSystemFactory factory = new(); + BoundCoordinateSystem system = CreateSystem(); + string wkt = system.ToWktNode(WktVersion.Wkt22019).ToString(); + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("BOUNDCRS[", wkt, StringComparison.Ordinal); + Assert.True(system.EqualParams(parsed)); + } + + /// + /// Verifies that delegates to the source coordinate system. + /// + /// The dimension index. + [Theory] + [InlineData(0)] + [InlineData(1)] + public void GetUnits_DelegatesToSourceCoordinateSystem(int dimension) + { + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem("Source", angularUnit: AngularUnit.Grad); + BoundCoordinateSystem system = CreateSystem(sourceCoordinateSystem: sourceCoordinateSystem); + + IUnit unit = system.GetUnits(dimension); + + Assert.True(unit.EqualParams(AngularUnit.Grad)); + } + + /// + /// Verifies that equivalent bound coordinate systems compare equal. + /// + [Fact] + public void EqualParams_SameValues_ReturnsTrue() + { + BoundCoordinateSystem first = CreateSystem( + sourceCoordinateSystem: CreateCoordinateSystem("Source one"), + targetCoordinateSystem: CreateCoordinateSystem("Target one"), + transformation: CreateWgs84Transformation(), + name: "First"); + BoundCoordinateSystem second = CreateSystem( + sourceCoordinateSystem: CreateCoordinateSystem("Source two"), + targetCoordinateSystem: CreateCoordinateSystem("Target two"), + transformation: CreateWgs84Transformation(), + name: "Second"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that a different target coordinate system causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentTargetCoordinateSystem_ReturnsFalse() + { + BoundCoordinateSystem first = CreateSystem(targetCoordinateSystem: CreateCoordinateSystem("Target one", primeMeridian: PrimeMeridian.Greenwich)); + BoundCoordinateSystem second = CreateSystem(targetCoordinateSystem: CreateCoordinateSystem("Target two", primeMeridian: PrimeMeridian.Paris)); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different transformation causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentTransformation_ReturnsFalse() + { + BoundCoordinateSystem first = CreateSystem(transformation: new BoundTransformation("Geocentric translations", new Wgs84ConversionInfo(1, 2, 3, 0, 0, 0, 0))); + BoundCoordinateSystem second = CreateSystem(transformation: new BoundTransformation("Geocentric translations", new Wgs84ConversionInfo(4, 5, 6, 0, 0, 0, 0))); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different object type causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(CreateSystem().EqualParams("not a coordinate system")); + } + + /// + /// Verifies that the factory creates a bound coordinate system with the supplied values. + /// + [Fact] + public void Factory_CreateBoundCoordinateSystem_ReturnsExpectedSystem() + { + var factory = new CoordinateSystemFactory(); + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem("Source"); + GeographicCoordinateSystem targetCoordinateSystem = GeographicCoordinateSystem.WGS84; + BoundTransformation transformation = CreateWgs84Transformation(); + + BoundCoordinateSystem system = factory.CreateBoundCoordinateSystem("Factory bound", sourceCoordinateSystem, targetCoordinateSystem, transformation); + + Assert.Equal("Factory bound", system.Name); + Assert.Same(sourceCoordinateSystem, system.SourceCoordinateSystem); + Assert.Same(targetCoordinateSystem, system.TargetCoordinateSystem); + Assert.Same(transformation, system.Transformation); + } + + /// + /// Verifies the runtime factory normalizes bound sources through the legacy path while keeping the public source and target coordinate systems intact. + /// + [Fact] + public void CoordinateTransformationFactory_CreateFromCoordinateSystems_WithBoundSource_MatchesLegacyRuntime() + { + HorizontalDatum sourceDatum = CreateHorizontalDatum(); + GeographicCoordinateSystem sourceCoordinateSystem = CreateCoordinateSystem("Custom geographic", horizontalDatum: sourceDatum); + GeographicCoordinateSystem legacySourceCoordinateSystem = CreateCoordinateSystem("Custom geographic", horizontalDatum: CreateHorizontalDatum(CreateWgs84Parameters())); + GeographicCoordinateSystem targetCoordinateSystem = GeographicCoordinateSystem.WGS84; + BoundCoordinateSystem boundSource = CreateSystem( + sourceCoordinateSystem: sourceCoordinateSystem, + targetCoordinateSystem: targetCoordinateSystem, + transformation: CreateWgs84Transformation()); + + var factory = new CoordinateTransformationFactory(); + ICoordinateTransformation boundTransformation = factory.CreateFromCoordinateSystems(boundSource, targetCoordinateSystem); + ICoordinateTransformation legacyTransformation = factory.CreateFromCoordinateSystems(legacySourceCoordinateSystem, targetCoordinateSystem); + + double[] boundOutput = boundTransformation.MathTransform.Transform([10d, 50d]); + double[] legacyOutput = legacyTransformation.MathTransform.Transform([10d, 50d]); + + Assert.Same(boundSource, boundTransformation.SourceCS); + Assert.Same(targetCoordinateSystem, boundTransformation.TargetCS); + Assert.Equal(legacyOutput[0], boundOutput[0], 9); + Assert.Equal(legacyOutput[1], boundOutput[1], 9); + } + + /// + /// Verifies that the WGS84-parameter constructor stores the supplied values. + /// + [Fact] + public void BoundTransformation_Wgs84Constructor_SetsProperties() + { + Wgs84ConversionInfo parameters = new(1, 2, 3, 4, 5, 6, 7); + BoundTransformation transformation = new("Position Vector transformation", parameters); + + Assert.Equal("Position Vector transformation", transformation.MethodName); + Assert.Same(parameters, transformation.Wgs84Parameters); + Assert.Null(transformation.ParameterFileName); + Assert.True(transformation.UsesWgs84Parameters); + Assert.False(transformation.UsesParameterFile); + } + + /// + /// Verifies that the parameter-file constructor stores the supplied values. + /// + [Fact] + public void BoundTransformation_ParameterFileConstructor_SetsProperties() + { + BoundTransformation transformation = new("Geographic3D to GravityRelatedHeight (EGM)", "us_nga_egm96_15.tif"); + + Assert.Equal("Geographic3D to GravityRelatedHeight (EGM)", transformation.MethodName); + Assert.Null(transformation.Wgs84Parameters); + Assert.Equal("us_nga_egm96_15.tif", transformation.ParameterFileName); + Assert.False(transformation.UsesWgs84Parameters); + Assert.True(transformation.UsesParameterFile); + } + + /// + /// Verifies that a missing method name is rejected. + /// + /// The invalid method name. + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void BoundTransformation_InvalidMethodName_ThrowsArgumentException(string? methodName) + { + ArgumentException exception = Assert.Throws(() => new BoundTransformation(methodName!, CreateWgs84Parameters())); + + Assert.Equal("methodName", exception.ParamName); + } + + /// + /// Verifies that the WGS84-parameter constructor rejects a null parameter object. + /// + [Fact] + public void BoundTransformation_NullWgs84Parameters_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new BoundTransformation("Geocentric translations", (Wgs84ConversionInfo)null!)); + + Assert.Equal("wgs84Parameters", exception.ParamName); + } + + /// + /// Verifies that an invalid parameter file name is rejected. + /// + /// The invalid parameter file name. + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(null)] + public void BoundTransformation_InvalidParameterFileName_ThrowsArgumentException(string? parameterFileName) + { + ArgumentException exception = Assert.Throws(() => new BoundTransformation("Grid interpolation", parameterFileName!)); + + Assert.Equal("parameterFileName", exception.ParamName); + } + + /// + /// Verifies that equivalent WGS84-parameter transformations compare equal. + /// + [Fact] + public void BoundTransformation_EqualsEquivalentWgs84Transformations_ReturnsTrue() + { + BoundTransformation first = new("Geocentric translations", CreateWgs84Parameters()); + BoundTransformation second = new("Geocentric translations", CreateWgs84Parameters()); + + Assert.True(first.Equals(second)); + Assert.Equal(first.GetHashCode(), second.GetHashCode()); + } + + /// + /// Verifies that different transformation representations do not compare equal. + /// + [Fact] + public void BoundTransformation_EqualsDifferentRepresentation_ReturnsFalse() + { + BoundTransformation first = new("Geocentric translations", CreateWgs84Parameters()); + BoundTransformation second = new("Geocentric translations", "us_nga_egm96_15.tif"); + + Assert.False(first.Equals(second)); + Assert.False(first.Equals((object?)second)); + } + + private static BoundCoordinateSystem CreateSystem( + CoordinateSystem? sourceCoordinateSystem = null, + CoordinateSystem? targetCoordinateSystem = null, + BoundTransformation? transformation = null, + string name = "Bound source", + string authority = "", + long authorityCode = -1) + { + return new BoundCoordinateSystem( + sourceCoordinateSystem ?? CreateCoordinateSystem("Source"), + targetCoordinateSystem ?? GeographicCoordinateSystem.WGS84, + transformation ?? CreateWgs84Transformation(), + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static GeographicCoordinateSystem CreateCoordinateSystem( + string name, + List? axisInfo = null, + AngularUnit? angularUnit = null, + PrimeMeridian? primeMeridian = null, + HorizontalDatum? horizontalDatum = null) + { + return new GeographicCoordinateSystem( + angularUnit ?? AngularUnit.Degrees, + horizontalDatum ?? HorizontalDatum.WGS84, + primeMeridian ?? PrimeMeridian.Greenwich, + axisInfo ?? [new AxisInfo("Lon", AxisOrientationEnum.East), new AxisInfo("Lat", AxisOrientationEnum.North)], + name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } + + private static List CreateCustomAxisInfo() + { + return [new AxisInfo("Northing", AxisOrientationEnum.North), new AxisInfo("Easting", AxisOrientationEnum.East)]; + } + + private static BoundTransformation CreateWgs84Transformation() => new("Position Vector transformation (geog2D domain)", CreateWgs84Parameters()); + + private static HorizontalDatum CreateHorizontalDatum(Wgs84ConversionInfo? wgs84Parameters = null) + => new(Ellipsoid.GRS80, wgs84Parameters, DatumType.HD_Geocentric, "Custom datum", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + private static Wgs84ConversionInfo CreateWgs84Parameters() => new(1, 2, 3, 4, 5, 6, 7); +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CompoundCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/CompoundCoordinateSystemTests.cs new file mode 100644 index 00000000..4750fcf1 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CompoundCoordinateSystemTests.cs @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class CompoundCoordinateSystemTests +{ + /// + /// Verifies that the constructor stores metadata, component systems, and merged axes. + /// + [Fact] + public void Constructor_SetsPropertiesAndAxes() + { + GeographicCoordinateSystem head = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem tail = VerticalCoordinateSystem.ODN; + var system = new CompoundCoordinateSystem(head, tail, "Custom compound", "EPSG", 9900, "alias", "abbr", "remarks"); + + Assert.Equal("Custom compound", system.Name); + Assert.Equal("EPSG", system.Authority); + Assert.Equal(9900, system.AuthorityCode); + Assert.Equal("alias", system.Alias); + Assert.Equal("abbr", system.Abbreviation); + Assert.Equal("remarks", system.Remarks); + Assert.Same(head, system.HeadCoordinateSystem); + Assert.Same(tail, system.TailCoordinateSystem); + Assert.Equal(head.Dimension + tail.Dimension, system.Dimension); + Assert.Same(head.GetAxis(0), system.GetAxis(0)); + Assert.Same(head.GetAxis(1), system.GetAxis(1)); + Assert.Same(tail.GetAxis(0), system.GetAxis(2)); + } + + /// + /// Verifies that WKT omits the authority clause when no authority metadata is available. + /// + [Fact] + public void WKT_WithoutAuthority_FormatsExpectedValue() + { + GeographicCoordinateSystem head = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem tail = VerticalCoordinateSystem.ODN; + var system = new CompoundCoordinateSystem(head, tail, "Custom compound", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.Equal($"COMPD_CS[\"Custom compound\", {head.WKT}, {tail.WKT}]", system.WKT); + } + + /// + /// Verifies that WKT omits the authority clause when the authority code is not positive. + /// + [Fact] + public void WKT_WithAuthorityNameButNonPositiveCode_OmitsAuthorityClause() + { + GeographicCoordinateSystem head = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem tail = VerticalCoordinateSystem.ODN; + CompoundCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 0); + + Assert.Equal($"COMPD_CS[\"Custom compound\", {head.WKT}, {tail.WKT}]", system.WKT); + } + + /// + /// Verifies that WKT includes the authority clause when authority metadata is present. + /// + [Fact] + public void WKT_WithAuthority_FormatsExpectedValue() + { + GeographicCoordinateSystem head = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem tail = VerticalCoordinateSystem.ODN; + var system = new CompoundCoordinateSystem(head, tail, "Custom compound", "EPSG", 9900, string.Empty, string.Empty, string.Empty); + + Assert.Equal($"COMPD_CS[\"Custom compound\", {head.WKT}, {tail.WKT}, AUTHORITY[\"EPSG\", \"9900\"]]", system.WKT); + } + + /// + /// Verifies that XML contains the expected outer and inner elements. + /// + [Fact] + public void XML_ContainsExpectedStructure() + { + CompoundCoordinateSystem system = CreateSystem(); + var xml = XElement.Parse(system.XML); + XElement inner = Assert.IsType(xml.Element("CS_CompoundCoordinateSystem")); + + Assert.Equal("CS_CoordinateSystem", xml.Name.LocalName); + Assert.Equal("3", (string?)xml.Attribute("Dimension")); + Assert.NotNull(inner.Element("CS_Info")); + Assert.Equal(3, new System.Collections.Generic.List(inner.Elements("CS_AxisInfo")).Count); + Assert.Equal(2, new System.Collections.Generic.List(inner.Elements("CS_CoordinateSystem")).Count); + } + + /// + /// Verifies that matches the XML property. + /// + [Fact] + public void ToXml_MatchesXmlProperty() + { + CompoundCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 9900); + XElement xml = system.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(system.XML), xml)); + } + + /// + /// Verifies that GetUnits returns the head coordinate system unit for head dimensions. + /// + [Theory] + [InlineData(0)] + [InlineData(1)] + public void GetUnits_HeadDimension_ReturnsHeadUnit(int dimension) + { + CompoundCoordinateSystem system = CreateSystem(); + + IUnit unit = system.GetUnits(dimension); + + Assert.True(unit.EqualParams(AngularUnit.Degrees)); + } + + /// + /// Verifies that GetUnits returns the tail coordinate system unit for tail dimensions. + /// + [Fact] + public void GetUnits_TailDimension_ReturnsTailUnit() + { + CompoundCoordinateSystem system = CreateSystem(); + + IUnit unit = system.GetUnits(2); + + Assert.True(unit.EqualParams(LinearUnit.Metre)); + } + + /// + /// Verifies that GetUnits rejects invalid dimension indices. + /// + /// The invalid dimension index. + [Theory] + [InlineData(-1)] + [InlineData(3)] + public void GetUnits_InvalidDimension_ThrowsArgumentException(int dimension) + { + CompoundCoordinateSystem system = CreateSystem(); + + ArgumentException exception = Assert.Throws(() => system.GetUnits(dimension)); + + Assert.Equal("dimension", exception.ParamName); + } + + /// + /// Verifies that matches WKT when no authority metadata is present. + /// + [Fact] + public void ToWktNode_WithoutAuthority_MatchesWkt() + { + CompoundCoordinateSystem system = CreateSystem(); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal("COMPD_CS", node.Keyword); + Assert.Equal(3, node.Children.Count); + Assert.Equal("Custom compound", Assert.IsType(node.Children[0]).Value); + Assert.Equal("GEOGCS", Assert.IsType(node.Children[1]).Keyword); + Assert.Equal("VERT_CS", Assert.IsType(node.Children[2]).Keyword); + } + + /// + /// Verifies that omits authority when the code is not positive. + /// + [Fact] + public void ToWktNode_WithAuthorityNameButNonPositiveCode_OmitsAuthorityNode() + { + CompoundCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 0); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal(3, node.Children.Count); + Assert.Equal("Custom compound", Assert.IsType(node.Children[0]).Value); + Assert.Equal("GEOGCS", Assert.IsType(node.Children[1]).Keyword); + Assert.Equal("VERT_CS", Assert.IsType(node.Children[2]).Keyword); + } + + /// + /// Verifies that includes an authority node when metadata is present. + /// + [Fact] + public void ToWktNode_WithAuthority_IncludesAuthorityNode() + { + CompoundCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 9900); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + WktKeywordNode authorityNode; + + Assert.Equal(4, node.Children.Count); + Assert.Equal("Custom compound", Assert.IsType(node.Children[0]).Value); + Assert.Equal("GEOGCS", Assert.IsType(node.Children[1]).Keyword); + Assert.Equal("VERT_CS", Assert.IsType(node.Children[2]).Keyword); + authorityNode = Assert.IsType(node.Children[3]); + Assert.Equal("AUTHORITY", authorityNode.Keyword); + Assert.Equal("EPSG", Assert.IsType(authorityNode.Children[0]).Value); + Assert.Equal("9900", Assert.IsType(authorityNode.Children[1]).Value); + } + + /// + /// Verifies that equal compound systems compare equal. + /// + [Fact] + public void EqualParams_SameValues_ReturnsTrue() + { + CompoundCoordinateSystem first = CreateSystem(name: "A"); + CompoundCoordinateSystem second = CreateSystem(name: "B"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that differing head coordinate systems compare unequal. + /// + [Fact] + public void EqualParams_DifferentHead_ReturnsFalse() + { + CompoundCoordinateSystem first = CreateSystem(); + CompoundCoordinateSystem second = CreateSystem(head: GeocentricCoordinateSystem.WGS84); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that differing tail coordinate systems compare unequal. + /// + [Fact] + public void EqualParams_DifferentTail_ReturnsFalse() + { + CompoundCoordinateSystem first = CreateSystem(); + CompoundCoordinateSystem second = CreateSystem(tail: CreateFootVerticalCoordinateSystem()); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different object types compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(CreateSystem().EqualParams("not a coordinate system")); + } + + private static CompoundCoordinateSystem CreateSystem( + string name = "Custom compound", + CoordinateSystem? head = null, + CoordinateSystem? tail = null, + string authority = "", + long authorityCode = -1) + { + return new CompoundCoordinateSystem( + head ?? GeographicCoordinateSystem.WGS84, + tail ?? VerticalCoordinateSystem.ODN, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static VerticalCoordinateSystem CreateFootVerticalCoordinateSystem() + { + return new VerticalCoordinateSystem( + LinearUnit.Foot, + VerticalDatum.ODN, + new AxisInfo("Up", AxisOrientationEnum.Up), + "Foot height", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CoordinateOperationTests.cs b/test/ProjNet.Tests/CoordinateSystems/CoordinateOperationTests.cs new file mode 100644 index 00000000..abfd752b --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CoordinateOperationTests.cs @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for and . +/// +public class CoordinateOperationTests +{ + /// + /// Verifies that the coordinate operation constructor stores its properties. + /// + [Fact] + public void CoordinateOperation_Constructor_SetsProperties() + { + CoordinateOperation operation = CreateCoordinateOperation("Axis swap"); + + Assert.Equal("Axis swap", operation.Name); + Assert.Equal("Affine parametric transformation", operation.MethodName); + Assert.Single(operation.Parameters); + Assert.Equal("Parameter A", operation.Parameters[0].Name); + } + + /// + /// Verifies that returns a coordinate-operation clone with updated authority metadata. + /// + [Fact] + public void CoordinateOperation_WithAuthority_ReturnsUpdatedClone() + { + CoordinateOperation original = CreateCoordinateOperation("Axis swap"); + CoordinateOperation clone = original.WithAuthority("EPSG", 9603); + + Assert.Equal("EPSG", clone.Authority); + Assert.Equal(9603, clone.AuthorityCode); + Assert.Equal("TEST", original.Authority); + Assert.Equal(1, original.AuthorityCode); + Assert.NotSame(original, clone); + Assert.NotSame(original.SourceCoordinateSystem, clone.SourceCoordinateSystem); + Assert.True(original.SourceCoordinateSystem.EqualParams(clone.SourceCoordinateSystem)); + } + + /// + /// Verifies that coordinate operation WKT output contains the expected keywords. + /// + [Fact] + public void CoordinateOperation_Wkt_ContainsSourceTargetAndMethod() + { + string wkt = CreateCoordinateOperation("Axis swap").ToWktNode(WktVersion.Wkt22019).ToString(); + + Assert.StartsWith("COORDINATEOPERATION[", wkt, System.StringComparison.Ordinal); + Assert.Contains("SOURCECRS[", wkt, System.StringComparison.Ordinal); + Assert.Contains("TARGETCRS[", wkt, System.StringComparison.Ordinal); + Assert.Contains("METHOD[\"Affine parametric transformation\"]", wkt, System.StringComparison.Ordinal); + } + + /// + /// Verifies that the concatenated operation constructor stores its steps. + /// + [Fact] + public void ConcatenatedOperation_Constructor_SetsSteps() + { + ConcatenatedOperation operation = CreateConcatenatedOperation(); + + Assert.Equal("Chained operation", operation.Name); + Assert.Equal(2, operation.Steps.Count); + Assert.Equal("Step 1", operation.Steps[0].Name); + Assert.Equal("Step 2", operation.Steps[1].Name); + } + + /// + /// Verifies that returns a concatenated-operation clone with updated authority metadata. + /// + [Fact] + public void ConcatenatedOperation_WithAuthority_ReturnsUpdatedClone() + { + ConcatenatedOperation original = CreateConcatenatedOperation(); + ConcatenatedOperation clone = original.WithAuthority("EPSG", 9604); + + Assert.Equal("EPSG", clone.Authority); + Assert.Equal(9604, clone.AuthorityCode); + Assert.Equal("TEST", original.Authority); + Assert.Equal(2, original.AuthorityCode); + Assert.NotSame(original, clone); + Assert.Equal(original.Steps.Count, clone.Steps.Count); + Assert.NotSame(original.Steps[0], clone.Steps[0]); + Assert.True(original.Steps[0].EqualParams(clone.Steps[0])); + } + + /// + /// Verifies that concatenated operation WKT output contains STEP blocks. + /// + [Fact] + public void ConcatenatedOperation_Wkt_ContainsStepBlocks() + { + string wkt = CreateConcatenatedOperation().ToWktNode(WktVersion.Wkt22019).ToString(); + + Assert.StartsWith("CONCATENATEDOPERATION[", wkt, System.StringComparison.Ordinal); + Assert.Contains("STEP[COORDINATEOPERATION[", wkt, System.StringComparison.Ordinal); + } + + /// + /// Verifies that concatenated operation WKT2 serialization round-trips through the reader without losing step metadata. + /// + [Fact] + public void ConcatenatedOperation_Wkt2_RoundTripsThroughReader() + { + ConcatenatedOperation original = CreateConcatenatedOperation(); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + ConcatenatedOperation parsed = Assert.IsType(CoordinateSystemWktReader.Parse(wkt)); + + Assert.True(original.EqualParams(parsed)); + Assert.Equal(original.Name, parsed.Name); + Assert.Equal(original.Steps.Count, parsed.Steps.Count); + Assert.Equal(original.Steps[0].MethodName, parsed.Steps[0].MethodName); + Assert.Equal(original.Steps[1].Parameters[0].Value, parsed.Steps[1].Parameters[0].Value); + } + + private static CoordinateOperation CreateCoordinateOperation(string name) + { + return new CoordinateOperation( + "Affine parametric transformation", + new List { new("Parameter A", 1d) }, + GeographicCoordinateSystem.WGS84, + GeographicCoordinateSystem.WGS84, + name, + "TEST", + 1, + string.Empty, + string.Empty, + string.Empty); + } + + private static ConcatenatedOperation CreateConcatenatedOperation() + { + return new ConcatenatedOperation( + new List + { + CreateCoordinateOperation("Step 1"), + CreateCoordinateOperation("Step 2"), + }, + GeographicCoordinateSystem.WGS84, + GeographicCoordinateSystem.WGS84, + "Chained operation", + "TEST", + 2, + string.Empty, + string.Empty, + string.Empty); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemCoverageTests.cs b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemCoverageTests.cs new file mode 100644 index 00000000..41ef1e4a --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemCoverageTests.cs @@ -0,0 +1,1242 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using Xunit; + +/// +/// Coverage tests for coordinate system classes with low coverage: +/// , , +/// , , +/// , , and . +/// +public class CoordinateSystemCoverageTests +{ + private static readonly CoordinateSystemFactory Factory = new(); + + // ======================================================================== + // GeocentricCoordinateSystem + // ======================================================================== + + /// + /// Verifies that the factory creates a geocentric system with 3 dimensions. + /// + [Fact] + public void GeocentricCS_Factory_DimensionIsThree() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.Equal(3, gcs.Dimension); + } + + /// + /// Verifies that the factory assigns the specified name. + /// + [Fact] + public void GeocentricCS_Factory_NameIsAssigned() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "TestGeocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.Equal("TestGeocentric", gcs.Name); + } + + /// + /// Verifies that properties are accessible after factory creation. + /// + [Fact] + public void GeocentricCS_Factory_PropertiesAccessible() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.True(gcs.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(gcs.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.True(gcs.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)); + } + + /// + /// Verifies that WKT output starts with GEOCCS and contains the name. + /// + [Fact] + public void GeocentricCS_WKT_ContainsGeoccsAndName() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.StartsWith("GEOCCS[\"WGS84 Geocentric\"", gcs.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT output contains datum, unit, and prime meridian. + /// + [Fact] + public void GeocentricCS_WKT_ContainsSubComponents() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + string wkt = gcs.WKT; + + Assert.Contains("DATUM[", wkt, StringComparison.Ordinal); + Assert.Contains("PRIMEM[", wkt, StringComparison.Ordinal); + Assert.Contains("UNIT[", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that XML output contains the expected element tags. + /// + [Fact] + public void GeocentricCS_XML_ContainsExpectedElements() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + string xml = gcs.XML; + + Assert.Contains("CS_GeocentricCoordinateSystem", xml, StringComparison.Ordinal); + Assert.Contains("CS_HorizontalDatum", xml, StringComparison.Ordinal); + Assert.Contains("CS_LinearUnit", xml, StringComparison.Ordinal); + Assert.Contains("CS_PrimeMeridian", xml, StringComparison.Ordinal); + } + + /// + /// Verifies that XML output contains the dimension attribute set to 3. + /// + [Fact] + public void GeocentricCS_XML_ContainsDimension() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.Contains("Dimension=\"3\"", gcs.XML, StringComparison.Ordinal); + } + + /// + /// Verifies that GetUnits returns the linear unit for all three dimensions. + /// + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public void GeocentricCS_GetUnits_ReturnsLinearUnit(int dimension) + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + IUnit unit = gcs.GetUnits(dimension); + + Assert.IsType(unit); + Assert.True(gcs.LinearUnit.EqualParams(unit)); + } + + /// + /// Verifies that EqualParams returns true for an equivalent system. + /// + [Fact] + public void GeocentricCS_EqualParams_EquivalentSystems_ReturnsTrue() + { + GeocentricCoordinateSystem a = Factory.CreateGeocentricCoordinateSystem( + "GCS1", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + GeocentricCoordinateSystem b = Factory.CreateGeocentricCoordinateSystem( + "GCS2", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for a different linear unit. + /// + [Fact] + public void GeocentricCS_EqualParams_DifferentUnit_ReturnsFalse() + { + GeocentricCoordinateSystem a = Factory.CreateGeocentricCoordinateSystem( + "GCS1", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + GeocentricCoordinateSystem b = Factory.CreateGeocentricCoordinateSystem( + "GCS2", HorizontalDatum.WGS84, LinearUnit.Foot, PrimeMeridian.Greenwich); + + Assert.False(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void GeocentricCS_EqualParams_DifferentType_ReturnsFalse() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.False(gcs.EqualParams("not a CS")); + } + + /// + /// Verifies that the static WGS84 property returns a valid geocentric system. + /// + [Fact] + public void GeocentricCS_WGS84_StaticProperty_IsValid() + { + GeocentricCoordinateSystem wgs84 = GeocentricCoordinateSystem.WGS84; + + Assert.NotNull(wgs84); + Assert.Equal(3, wgs84.Dimension); + Assert.True(wgs84.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + } + + /// + /// Verifies WKT round-trip for a geocentric coordinate system. + /// + [Fact] + public void GeocentricCS_WKT_RoundTrip() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + string wkt = gcs.WKT; + + GeocentricCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(Factory, wkt); + Assert.True(gcs.EqualParams(parsed)); + } + + /// + /// Verifies that ToString returns WKT. + /// + [Fact] + public void GeocentricCS_ToString_ReturnsWKT() + { + GeocentricCoordinateSystem gcs = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + + Assert.Equal(gcs.WKT, gcs.ToString()); + } + + // ======================================================================== + // CompoundCoordinateSystem + // ======================================================================== + + /// + /// Verifies that a compound system has the combined dimension of head and tail. + /// + [Fact] + public void CompoundCS_Dimension_IsSumOfComponents() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + + Assert.Equal(geoCs.Dimension + vertCs.Dimension, compound.Dimension); + } + + /// + /// Verifies that HeadCoordinateSystem returns the first component. + /// + [Fact] + public void CompoundCS_HeadCoordinateSystem_ReturnsFirst() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + + Assert.Same(geoCs, compound.HeadCoordinateSystem); + } + + /// + /// Verifies that TailCoordinateSystem returns the second component. + /// + [Fact] + public void CompoundCS_TailCoordinateSystem_ReturnsSecond() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + + Assert.Same(vertCs, compound.TailCoordinateSystem); + } + + /// + /// Verifies that WKT output starts with COMPD_CS and contains both sub-systems. + /// + [Fact] + public void CompoundCS_WKT_ContainsCompdCsAndSubSystems() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + string wkt = compound.WKT; + + Assert.StartsWith("COMPD_CS[\"WGS84 + ODN\"", wkt, StringComparison.Ordinal); + Assert.Contains("GEOGCS[", wkt, StringComparison.Ordinal); + Assert.Contains("VERT_CS[", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that XML output contains expected compound system elements. + /// + [Fact] + public void CompoundCS_XML_ContainsExpectedElements() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + string xml = compound.XML; + + Assert.Contains("CS_CompoundCoordinateSystem", xml, StringComparison.Ordinal); + Assert.Contains("CS_CoordinateSystem", xml, StringComparison.Ordinal); + } + + /// + /// Verifies that GetUnits delegates to head CS for head dimensions. + /// + [Fact] + public void CompoundCS_GetUnits_HeadDimension_ReturnsHeadUnit() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + + IUnit unit = compound.GetUnits(0); + Assert.IsType(unit); + } + + /// + /// Verifies that GetUnits delegates to tail CS for tail dimensions. + /// + [Fact] + public void CompoundCS_GetUnits_TailDimension_ReturnsTailUnit() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + + IUnit unit = compound.GetUnits(geoCs.Dimension); + Assert.IsType(unit); + } + + /// + /// Verifies that EqualParams returns true for equivalent compound systems. + /// + [Fact] + public void CompoundCS_EqualParams_EquivalentSystems_ReturnsTrue() + { + CompoundCoordinateSystem a = Factory.CreateCompoundCoordinateSystem( + "CS1", GeographicCoordinateSystem.WGS84, VerticalCoordinateSystem.ODN); + CompoundCoordinateSystem b = Factory.CreateCompoundCoordinateSystem( + "CS2", GeographicCoordinateSystem.WGS84, VerticalCoordinateSystem.ODN); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for different tail systems. + /// + [Fact] + public void CompoundCS_EqualParams_DifferentTail_ReturnsFalse() + { + VerticalCoordinateSystem tailA = VerticalCoordinateSystem.ODN; + var tailB = new VerticalCoordinateSystem( + LinearUnit.Foot, + VerticalDatum.ODN, + new AxisInfo("Up", AxisOrientationEnum.Up), + "Different", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + CompoundCoordinateSystem a = Factory.CreateCompoundCoordinateSystem( + "CS1", GeographicCoordinateSystem.WGS84, tailA); + CompoundCoordinateSystem b = Factory.CreateCompoundCoordinateSystem( + "CS2", GeographicCoordinateSystem.WGS84, tailB); + + Assert.False(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void CompoundCS_EqualParams_DifferentType_ReturnsFalse() + { + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84+ODN", GeographicCoordinateSystem.WGS84, VerticalCoordinateSystem.ODN); + + Assert.False(compound.EqualParams("not a CS")); + } + + /// + /// Verifies WKT round-trip for a compound coordinate system. + /// + [Fact] + public void CompoundCS_WKT_RoundTrip() + { + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", GeographicCoordinateSystem.WGS84, VerticalCoordinateSystem.ODN); + string wkt = compound.WKT; + + CompoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(Factory, wkt); + Assert.True(compound.EqualParams(parsed)); + } + + /// + /// Verifies that GetAxis returns axes from both head and tail systems. + /// + [Fact] + public void CompoundCS_GetAxis_ReturnsAxesFromBothSystems() + { + GeographicCoordinateSystem geoCs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vertCs = VerticalCoordinateSystem.ODN; + + CompoundCoordinateSystem compound = Factory.CreateCompoundCoordinateSystem( + "WGS84 + ODN", geoCs, vertCs); + + for (int i = 0; i < compound.Dimension; i++) + { + AxisInfo axis = compound.GetAxis(i); + Assert.NotNull(axis.Name); + } + } + + // ======================================================================== + // FittedCoordinateSystem + // ======================================================================== + + /// + /// Verifies that a fitted CS can be created via factory with a WKT transform string. + /// + [Fact] + public void FittedCS_Factory_CreatesValidSystem() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + + Assert.NotNull(fitted); + Assert.Equal("Fitted WGS84", fitted.Name); + } + + /// + /// Verifies that BaseCoordinateSystem returns the underlying system. + /// + [Fact] + public void FittedCS_BaseCoordinateSystem_ReturnsBase() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + + Assert.True(baseCs.EqualParams(fitted.BaseCoordinateSystem)); + } + + /// + /// Verifies that ToBaseTransform is accessible and not null. + /// + [Fact] + public void FittedCS_ToBaseTransform_IsNotNull() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + + Assert.NotNull(fitted.ToBaseTransform); + } + + /// + /// Verifies that ToBase returns a non-empty WKT string. + /// + [Fact] + public void FittedCS_ToBase_ReturnsTransformWkt() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + + string toBase = fitted.ToBase(); + + Assert.NotNull(toBase); + Assert.NotEmpty(toBase); + } + + /// + /// Verifies that WKT starts with FITTED_CS and contains base CS. + /// + [Fact] + public void FittedCS_WKT_ContainsFittedCsAndBase() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + string wkt = fitted.WKT; + + Assert.StartsWith("FITTED_CS[\"Fitted WGS84\"", wkt, StringComparison.Ordinal); + Assert.Contains("GEOGCS[", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that XML throws NotSupportedException. + /// + [Fact] + public void FittedCS_XML_ThrowsNotSupportedException() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + + Assert.Throws(() => fitted.XML); + } + + /// + /// Verifies that GetUnits delegates to the base coordinate system. + /// + [Theory] + [InlineData(0)] + [InlineData(1)] + public void FittedCS_GetUnits_DelegatesToBase(int dimension) + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + + IUnit unit = fitted.GetUnits(dimension); + + Assert.IsType(unit); + } + + /// + /// Verifies that EqualParams returns true for equivalent fitted systems. + /// + [Fact] + public void FittedCS_EqualParams_EquivalentSystems_ReturnsTrue() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + string toBaseWkt = "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]"; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem a = Factory.CreateFittedCoordinateSystem("F1", baseCs, toBaseWkt, axes); + FittedCoordinateSystem b = Factory.CreateFittedCoordinateSystem("F2", baseCs, toBaseWkt, axes); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void FittedCS_EqualParams_DifferentType_ReturnsFalse() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + FittedCoordinateSystem fitted = Factory.CreateFittedCoordinateSystem( + "Fitted WGS84", + baseCs, + "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]", + axes); + + Assert.False(fitted.EqualParams("not a CS")); + } + + /// + /// Verifies that a fitted CS created via MathTransform overload works correctly. + /// + [Fact] + public void FittedCS_Factory_WithMathTransform_CreatesValidSystem() + { + GeographicCoordinateSystem baseCs = GeographicCoordinateSystem.WGS84; + var axes = new List + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + string toBaseWkt = "PARAM_MT[\"Affine\", PARAMETER[\"num_row\", 3], PARAMETER[\"num_col\", 3], PARAMETER[\"elt_0_0\", 1], PARAMETER[\"elt_0_1\", 0], PARAMETER[\"elt_0_2\", 0], PARAMETER[\"elt_1_0\", 0], PARAMETER[\"elt_1_1\", 1], PARAMETER[\"elt_1_2\", 0], PARAMETER[\"elt_2_0\", 0], PARAMETER[\"elt_2_1\", 0], PARAMETER[\"elt_2_2\", 1]]"; + FittedCoordinateSystem fittedViaWkt = Factory.CreateFittedCoordinateSystem("F1", baseCs, toBaseWkt, axes); + MathTransform transform = fittedViaWkt.ToBaseTransform; + + FittedCoordinateSystem fittedViaMt = Factory.CreateFittedCoordinateSystem("F2", baseCs, transform, axes); + + Assert.NotNull(fittedViaMt); + Assert.True(fittedViaMt.BaseCoordinateSystem.EqualParams(baseCs)); + } + + // ======================================================================== + // PrimeMeridian + // ======================================================================== + + /// + /// Verifies longitude values for all predefined prime meridians. + /// + [Theory] + [InlineData("Greenwich", 0.0)] + [InlineData("Lisbon", -9.0754862)] + [InlineData("Paris", 2.5969213)] + [InlineData("Bogota", -74.04513)] + [InlineData("Madrid", -3.411658)] + [InlineData("Rome", 12.27084)] + [InlineData("Bern", 7.26225)] + [InlineData("Jakarta", 106.482779)] + [InlineData("Ferro", -17.66666666666667)] + [InlineData("Brussels", 4.220471)] + [InlineData("Stockholm", 18.03298)] + [InlineData("Athens", 23.4258815)] + [InlineData("Oslo", 10.43225)] + public void PrimeMeridian_StaticInstances_HaveCorrectLongitude(string name, double expectedLongitude) + { + PrimeMeridian pm = name switch + { + "Greenwich" => PrimeMeridian.Greenwich, + "Lisbon" => PrimeMeridian.Lisbon, + "Paris" => PrimeMeridian.Paris, + "Bogota" => PrimeMeridian.Bogota, + "Madrid" => PrimeMeridian.Madrid, + "Rome" => PrimeMeridian.Rome, + "Bern" => PrimeMeridian.Bern, + "Jakarta" => PrimeMeridian.Jakarta, + "Ferro" => PrimeMeridian.Ferro, + "Brussels" => PrimeMeridian.Brussels, + "Stockholm" => PrimeMeridian.Stockholm, + "Athens" => PrimeMeridian.Athens, + "Oslo" => PrimeMeridian.Oslo, + _ => throw new ArgumentException($"Unknown meridian: {name}"), + }; + + Assert.Equal(expectedLongitude, pm.Longitude, 10); + } + + /// + /// Verifies that all predefined meridians have EPSG authority. + /// + [Theory] + [InlineData("Greenwich", 8901)] + [InlineData("Lisbon", 8902)] + [InlineData("Paris", 8903)] + [InlineData("Bogota", 8904)] + [InlineData("Madrid", 8905)] + [InlineData("Rome", 8906)] + [InlineData("Bern", 8907)] + [InlineData("Jakarta", 8908)] + [InlineData("Ferro", 8909)] + [InlineData("Brussels", 8910)] + [InlineData("Stockholm", 8911)] + [InlineData("Athens", 8912)] + [InlineData("Oslo", 8913)] + public void PrimeMeridian_StaticInstances_HaveCorrectEpsgCode(string name, long expectedCode) + { + PrimeMeridian pm = name switch + { + "Greenwich" => PrimeMeridian.Greenwich, + "Lisbon" => PrimeMeridian.Lisbon, + "Paris" => PrimeMeridian.Paris, + "Bogota" => PrimeMeridian.Bogota, + "Madrid" => PrimeMeridian.Madrid, + "Rome" => PrimeMeridian.Rome, + "Bern" => PrimeMeridian.Bern, + "Jakarta" => PrimeMeridian.Jakarta, + "Ferro" => PrimeMeridian.Ferro, + "Brussels" => PrimeMeridian.Brussels, + "Stockholm" => PrimeMeridian.Stockholm, + "Athens" => PrimeMeridian.Athens, + "Oslo" => PrimeMeridian.Oslo, + _ => throw new ArgumentException($"Unknown meridian: {name}"), + }; + + Assert.Equal("EPSG", pm.Authority); + Assert.Equal(expectedCode, pm.AuthorityCode); + } + + /// + /// Verifies that WKT starts with PRIMEM and contains the meridian name and longitude. + /// + [Fact] + public void PrimeMeridian_WKT_ContainsPrimemAndName() + { + string wkt = PrimeMeridian.Greenwich.WKT; + + Assert.StartsWith("PRIMEM[\"Greenwich\"", wkt, StringComparison.Ordinal); + Assert.Contains("AUTHORITY[\"EPSG\", \"8901\"]", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that XML output contains the expected elements and longitude attribute. + /// + [Fact] + public void PrimeMeridian_XML_ContainsExpectedElements() + { + string xml = PrimeMeridian.Greenwich.XML; + + Assert.Contains("CS_PrimeMeridian", xml, StringComparison.Ordinal); + Assert.Contains("Longitude=\"0\"", xml, StringComparison.Ordinal); + } + + /// + /// Verifies that a factory-created prime meridian has correct values. + /// + [Fact] + public void PrimeMeridian_Factory_CreatesWithCorrectValues() + { + PrimeMeridian pm = Factory.CreatePrimeMeridian( + "CustomMeridian", AngularUnit.Degrees, 45.0); + + Assert.Equal("CustomMeridian", pm.Name); + Assert.Equal(45.0, pm.Longitude); + Assert.True(pm.AngularUnit.EqualParams(AngularUnit.Degrees)); + } + + /// + /// Verifies that EqualParams returns true for meridians with same longitude and unit. + /// + [Fact] + public void PrimeMeridian_EqualParams_SameValues_ReturnsTrue() + { + PrimeMeridian a = Factory.CreatePrimeMeridian("PM1", AngularUnit.Degrees, 10.0); + PrimeMeridian b = Factory.CreatePrimeMeridian("PM2", AngularUnit.Degrees, 10.0); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for different longitudes. + /// + [Fact] + public void PrimeMeridian_EqualParams_DifferentLongitude_ReturnsFalse() + { + PrimeMeridian a = Factory.CreatePrimeMeridian("PM1", AngularUnit.Degrees, 10.0); + PrimeMeridian b = Factory.CreatePrimeMeridian("PM2", AngularUnit.Degrees, 20.0); + + Assert.False(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for different angular units. + /// + [Fact] + public void PrimeMeridian_EqualParams_DifferentUnit_ReturnsFalse() + { + PrimeMeridian a = Factory.CreatePrimeMeridian("PM1", AngularUnit.Degrees, 10.0); + PrimeMeridian b = Factory.CreatePrimeMeridian("PM2", AngularUnit.Radian, 10.0); + + Assert.False(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void PrimeMeridian_EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(PrimeMeridian.Greenwich.EqualParams("not a PM")); + } + + /// + /// Verifies WKT round-trip for Paris prime meridian. + /// + [Fact] + public void PrimeMeridian_Paris_WKT_ContainsLongitude() + { + string wkt = PrimeMeridian.Paris.WKT; + + Assert.Contains("Paris", wkt, StringComparison.Ordinal); + Assert.Contains("2.5969213", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies XML output for non-Greenwich meridians. + /// + [Fact] + public void PrimeMeridian_NonGreenwich_XML_ContainsLongitude() + { + string xml = PrimeMeridian.Rome.XML; + + Assert.Contains("CS_PrimeMeridian", xml, StringComparison.Ordinal); + Assert.Contains("12.27084", xml, StringComparison.Ordinal); + } + + // ======================================================================== + // Info base class (tested via concrete types) + // ======================================================================== + + /// + /// Verifies that all Info properties are accessible on a concrete type. + /// + [Fact] + public void Info_Properties_AccessibleOnConcreteType() + { + PrimeMeridian pm = PrimeMeridian.Greenwich; + + Assert.Equal("Greenwich", pm.Name); + Assert.Equal("EPSG", pm.Authority); + Assert.Equal(8901, pm.AuthorityCode); + } + + /// + /// Verifies that ToString returns WKT. + /// + [Fact] + public void Info_ToString_ReturnsWKT() + { + PrimeMeridian pm = PrimeMeridian.Greenwich; + + Assert.Equal(pm.WKT, pm.ToString()); + } + + /// + /// Verifies that preserves the concrete runtime type. + /// + [Fact] + public void Info_WithName_OnPrimeMeridian_ReturnsUpdatedClone() + { + PrimeMeridian renamed = PrimeMeridian.Greenwich.WithName("Custom Greenwich"); + + Assert.Equal("Custom Greenwich", renamed.Name); + Assert.Equal("EPSG", renamed.Authority); + Assert.Equal(8901, renamed.AuthorityCode); + Assert.True(PrimeMeridian.Greenwich.EqualParams(renamed)); + } + + /// + /// Verifies that rebuilds composed coordinate systems correctly. + /// + [Fact] + public void Info_WithAuthority_OnGeocentricCoordinateSystem_ReturnsUpdatedClone() + { + GeocentricCoordinateSystem source = Factory.CreateGeocentricCoordinateSystem( + "WGS84 Geocentric", + HorizontalDatum.WGS84, + LinearUnit.Metre, + PrimeMeridian.Greenwich); + + GeocentricCoordinateSystem updated = source.WithAuthority("TEST", 1001); + + Assert.Equal("TEST", updated.Authority); + Assert.Equal(1001, updated.AuthorityCode); + Assert.Equal(source.Name, updated.Name); + Assert.True(updated.HorizontalDatum.EqualParams(source.HorizontalDatum)); + Assert.True(updated.LinearUnit.EqualParams(source.LinearUnit)); + Assert.True(updated.PrimeMeridian.EqualParams(source.PrimeMeridian)); + } + + /// + /// Verifies that EqualParams ignores Name differences (name is excluded from comparison). + /// + [Fact] + public void Info_EqualParams_IgnoresName() + { + PrimeMeridian a = Factory.CreatePrimeMeridian("Name1", AngularUnit.Degrees, 0.0); + PrimeMeridian b = Factory.CreatePrimeMeridian("Name2", AngularUnit.Degrees, 0.0); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams ignores Authority and AuthorityCode differences. + /// + [Fact] + public void Info_EqualParams_IgnoresAuthority() + { + var a = new LinearUnit(1.0, "metre", "EPSG", 9001, string.Empty, string.Empty, string.Empty); + var b = new LinearUnit(1.0, "meter", "OTHER", 1, string.Empty, string.Empty, string.Empty); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams ignores Remarks differences. + /// + [Fact] + public void Info_EqualParams_IgnoresRemarks() + { + var a = new LinearUnit(1.0, "metre", "EPSG", 9001, string.Empty, string.Empty, "Remark A"); + var b = new LinearUnit(1.0, "meter", "EPSG", 9001, string.Empty, string.Empty, "Remark B"); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams ignores Alias and Abbreviation differences. + /// + [Fact] + public void Info_EqualParams_IgnoresAliasAndAbbreviation() + { + var a = new LinearUnit(1.0, "metre", "EPSG", 9001, "m", "alias1", string.Empty); + var b = new LinearUnit(1.0, "meter", "EPSG", 9001, "mt", "alias2", string.Empty); + + Assert.True(a.EqualParams(b)); + } + + // ======================================================================== + // ParameterInfo (tested via Projection) + // ======================================================================== + + /// + /// Verifies that NumParameters returns the parameter count of a projection. + /// + [Fact] + public void ParameterInfo_NumParameters_ReturnsCorrectCount() + { + var parameters = new List + { + new ProjectionParameter("latitude_of_origin", 0), + new ProjectionParameter("central_meridian", 0), + new ProjectionParameter("scale_factor", 1.0), + }; + + IProjection projection = Factory.CreateProjection("test", "transverse_mercator", parameters); + + Assert.Equal(3, projection.NumParameters); + } + + /// + /// Verifies that GetParameter returns the correct parameter by index. + /// + [Fact] + public void ParameterInfo_GetParameter_ByIndex_ReturnsCorrectValue() + { + var parameters = new List + { + new ProjectionParameter("scale_factor", 0.9996), + new ProjectionParameter("central_meridian", -93.0), + }; + + IProjection projection = Factory.CreateProjection("test", "transverse_mercator", parameters); + + ProjectionParameter param = projection.GetParameter(0); + Assert.Equal("scale_factor", param.Name); + Assert.Equal(0.9996, param.Value); + } + + /// + /// Verifies that GetParameter by name returns the correct parameter. + /// + [Fact] + public void ParameterInfo_GetParameter_ByName_ReturnsCorrectValue() + { + var parameters = new List + { + new ProjectionParameter("scale_factor", 0.9996), + new ProjectionParameter("central_meridian", -93.0), + }; + + IProjection projection = Factory.CreateProjection("test", "transverse_mercator", parameters); + + ProjectionParameter param = projection.GetParameter("central_meridian")!; + Assert.Equal(-93.0, param.Value); + } + + // ======================================================================== + // CoordinateSystemServices — edge cases + // ======================================================================== + + /// + /// Verifies that the default constructor initializes with known coordinate systems. + /// + [Fact] + public void Services_DefaultConstructor_ContainsWGS84() + { + var services = new CoordinateSystemServices(); + + CoordinateSystem? cs = services.GetCoordinateSystem(4326); + + Assert.NotNull(cs); + } + + /// + /// Verifies that GetCoordinateSystem returns null for an unknown SRID. + /// + [Fact] + public void Services_GetCoordinateSystem_UnknownSrid_ReturnsNull() + { + var services = new CoordinateSystemServices(); + + CoordinateSystem? cs = services.GetCoordinateSystem(999999); + + Assert.Null(cs); + } + + /// + /// Verifies that TryGetCoordinateSystem returns false for an unknown SRID. + /// + [Fact] + public void Services_TryGetCoordinateSystem_UnknownSrid_ReturnsFalse() + { + var services = new CoordinateSystemServices(); + + bool found = services.TryGetCoordinateSystem(999999, out CoordinateSystem? cs); + + Assert.False(found); + Assert.Null(cs); + } + + /// + /// Verifies that TryGetCoordinateSystem returns true for a known SRID. + /// + [Fact] + public void Services_TryGetCoordinateSystem_KnownSrid_ReturnsTrue() + { + var services = new CoordinateSystemServices(); + + bool found = services.TryGetCoordinateSystem(4326, out CoordinateSystem? cs); + + Assert.True(found); + Assert.NotNull(cs); + } + + /// + /// Verifies that GetCoordinateSystem by authority/code returns the correct system. + /// + [Fact] + public void Services_GetCoordinateSystem_ByAuthorityCode_ReturnsSystem() + { + var services = new CoordinateSystemServices(); + + CoordinateSystem? cs = services.GetCoordinateSystem("EPSG", 4326); + + Assert.NotNull(cs); + } + + /// + /// Verifies that TryGetCoordinateSystem by authority/code returns false for unknown. + /// + [Fact] + public void Services_TryGetCoordinateSystem_ByAuthorityCode_UnknownCode_ReturnsFalse() + { + var services = new CoordinateSystemServices(); + + bool found = services.TryGetCoordinateSystem("EPSG", 999999, out CoordinateSystem? cs); + + Assert.False(found); + Assert.Null(cs); + } + + /// + /// Verifies that GetAvailableSridValues returns a sorted non-empty array. + /// + [Fact] + public void Services_GetAvailableSridValues_ReturnsNonEmptySortedArray() + { + var services = new CoordinateSystemServices(); + + int[] srids = services.GetAvailableSridValues(); + + Assert.NotEmpty(srids); + + // Verify sorted + for (int i = 1; i < srids.Length; i++) + { + Assert.True(srids[i] >= srids[i - 1], "SRID values should be sorted"); + } + } + + /// + /// Verifies that GetSRID returns the correct SRID for a known authority/code. + /// + [Fact] + public void Services_GetSRID_KnownSystem_ReturnsSrid() + { + var services = new CoordinateSystemServices(); + + int? srid = services.GetSRID("EPSG", 4326); + + Assert.NotNull(srid); + Assert.Equal(4326, srid.Value); + } + + /// + /// Verifies that GetSRID returns null for an unknown authority/code. + /// + [Fact] + public void Services_GetSRID_UnknownSystem_ReturnsNull() + { + var services = new CoordinateSystemServices(); + + int? srid = services.GetSRID("UNKNOWN", 99999); + + Assert.Null(srid); + } + + /// + /// Verifies that RemoveCoordinateSystem throws NotSupportedException. + /// + [Fact] + public void Services_RemoveCoordinateSystem_ThrowsNotSupportedException() + { + var services = new CoordinateSystemServices(); + + Assert.Throws(() => services.RemoveCoordinateSystem(4326)); + } + + /// + /// Verifies that CreateTransformation returns a transformation between two known systems. + /// + [Fact] + public void Services_CreateTransformation_BetweenKnownSystems_ReturnsTransformation() + { + var services = new CoordinateSystemServices(); + + ICoordinateTransformation? transformation = services.CreateTransformation(4326, 3857); + + Assert.NotNull(transformation); + } + + /// + /// Verifies that CreateTransformation returns null for an unknown source SRID. + /// + [Fact] + public void Services_CreateTransformation_UnknownSrid_ReturnsNull() + { + var services = new CoordinateSystemServices(); + + ICoordinateTransformation? transformation = services.CreateTransformation(999999, 4326); + + Assert.Null(transformation); + } + + /// + /// Verifies that CreateTransformation with null source returns null. + /// + [Fact] + public void Services_CreateTransformation_NullSources_ReturnsNull() + { + var services = new CoordinateSystemServices(); + + ICoordinateTransformation? transformation = services.CreateTransformation(null, null); + + Assert.Null(transformation); + } + + /// + /// Verifies that GetAvailableSridValues yields entries. + /// + [Fact] + public void Services_GetAvailableSridValues_YieldsEntries() + { + var services = new CoordinateSystemServices(); + + int[] srids = services.GetAvailableSridValues(); + + Assert.NotEmpty(srids); + Assert.Contains(4326, srids); + } + + /// + /// Verifies that constructing with explicit factories works. + /// + [Fact] + public void Services_ConstructorWithFactories_IsUsable() + { + CoordinateSystemFactory csFactory = CoordinateSystemTestHelpers.CreateCoordinateSystemFactory(); + CoordinateTransformationFactory ctFactory = CoordinateSystemTestHelpers.CreateCoordinateTransformationFactory(); + + var services = new CoordinateSystemServices(csFactory, ctFactory); + + Assert.NotNull(services); + Assert.NotEmpty(services.GetAvailableSridValues()); + } + + /// + /// Verifies that constructing with definitions initializes systems. + /// + [Fact] + public void Services_ConstructorWithDefinitions_InitializesSystems() + { + string wkt = GeographicCoordinateSystem.WGS84.WKT; + CoordinateSystemDefinition[] definitions = new[] + { + new CoordinateSystemDefinition(4326, wkt), + }; + + var services = new CoordinateSystemServices(definitions); + + CoordinateSystem? cs = services.GetCoordinateSystem(4326); + Assert.NotNull(cs); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemFactoryGuardTests.cs b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemFactoryGuardTests.cs new file mode 100644 index 00000000..408023e2 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemFactoryGuardTests.cs @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies coordinate-system factory guards report the caller-facing parameter names. +/// +public class CoordinateSystemFactoryGuardTests +{ + private static readonly CoordinateSystemFactory Factory = new(); + + /// + /// Gets the factory method keys that reject blank names. + /// + public static TheoryData BlankNameFactories => + new() + { + nameof(CoordinateSystemFactory.CreateCompoundCoordinateSystem), + "CreateFittedCoordinateSystemFromWkt", + "CreateFittedCoordinateSystemFromTransform", + nameof(CoordinateSystemFactory.CreateFlattenedSphere), + nameof(CoordinateSystemFactory.CreateProjection), + nameof(CoordinateSystemFactory.CreateHorizontalDatum), + nameof(CoordinateSystemFactory.CreatePrimeMeridian), + nameof(CoordinateSystemFactory.CreateGeographicCoordinateSystem), + nameof(CoordinateSystemFactory.CreateVerticalDatum), + nameof(CoordinateSystemFactory.CreateVerticalCoordinateSystem), + nameof(CoordinateSystemFactory.CreateGeocentricCoordinateSystem), + }; + + /// + /// Verifies that all blank-name factory guards report name. + /// + /// The factory method key. + [Theory] + [MemberData(nameof(BlankNameFactories))] + public void FactoryMethodsRejectBlankNameWithNameParamName(string factoryMethod) + { + ArgumentException exception = Assert.Throws(() => InvokeBlankName(factoryMethod)); + + Assert.Equal("name", exception.ParamName); + } + + /// + /// Verifies that projection creation reports the parameter collection when it is empty. + /// + [Fact] + public void CreateProjectionRejectsEmptyParametersWithParametersParamName() + { + ArgumentException exception = Assert.Throws( + () => Factory.CreateProjection( + "Mercator", + "Mercator_1SP", + [])); + + Assert.Equal("parameters", exception.ParamName); + } + + private static void InvokeBlankName(string factoryMethod) + { + switch (factoryMethod) + { + case nameof(CoordinateSystemFactory.CreateCompoundCoordinateSystem): + Factory.CreateCompoundCoordinateSystem(" ", GeographicCoordinateSystem.WGS84, VerticalCoordinateSystem.ODN); + return; + case "CreateFittedCoordinateSystemFromWkt": + Factory.CreateFittedCoordinateSystem( + " ", + GeographicCoordinateSystem.WGS84, + CreateIdentityAffineTransform().WKT, + CreateAxisInfoPair()); + return; + case "CreateFittedCoordinateSystemFromTransform": + Factory.CreateFittedCoordinateSystem( + " ", + GeographicCoordinateSystem.WGS84, + CreateIdentityAffineTransform(), + CreateAxisInfoPair()); + return; + case nameof(CoordinateSystemFactory.CreateFlattenedSphere): + Factory.CreateFlattenedSphere(" ", 6378137d, 298.257223563d, LinearUnit.Metre); + return; + case nameof(CoordinateSystemFactory.CreateProjection): + Factory.CreateProjection(" ", "Mercator_1SP", CreateProjectionParameters()); + return; + case nameof(CoordinateSystemFactory.CreateHorizontalDatum): + Factory.CreateHorizontalDatum(" ", DatumType.HD_Geocentric, Ellipsoid.WGS84, null); + return; + case nameof(CoordinateSystemFactory.CreatePrimeMeridian): + Factory.CreatePrimeMeridian(" ", AngularUnit.Degrees, 0d); + return; + case nameof(CoordinateSystemFactory.CreateGeographicCoordinateSystem): + Factory.CreateGeographicCoordinateSystem( + " ", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + return; + case nameof(CoordinateSystemFactory.CreateVerticalDatum): + Factory.CreateVerticalDatum(" ", DatumType.VD_Orthometric); + return; + case nameof(CoordinateSystemFactory.CreateVerticalCoordinateSystem): + Factory.CreateVerticalCoordinateSystem(" ", VerticalDatum.ODN, LinearUnit.Metre, new AxisInfo("Up", AxisOrientationEnum.Up)); + return; + case nameof(CoordinateSystemFactory.CreateGeocentricCoordinateSystem): + Factory.CreateGeocentricCoordinateSystem(" ", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + return; + default: + throw new InvalidOperationException($"Unknown factory method key '{factoryMethod}'."); + } + } + + private static AffineTransform CreateIdentityAffineTransform() + { + double[,] matrix = (double[,])Array.CreateInstance(typeof(double), 3, 3); + matrix[0, 0] = 1d; + matrix[1, 1] = 1d; + matrix[2, 2] = 1d; + return new AffineTransform(matrix); + } + + private static List CreateAxisInfoPair() + { + return + [ + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North), + ]; + } + + private static List CreateProjectionParameters() + { + return + [ + new ProjectionParameter("latitude_of_origin", 0d), + new ProjectionParameter("central_meridian", 0d), + ]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemKeyTests.cs b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemKeyTests.cs new file mode 100644 index 00000000..2ac67d00 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemKeyTests.cs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests that exercise the internal CoordinateSystemKey equality and hashing +/// behavior indirectly through lookups. +/// +/// +/// CoordinateSystemKey is a private nested class inside +/// and cannot be tested directly. These tests verify the lookup behavior that depends on key equality. +/// +public class CoordinateSystemKeyTests +{ + /// + /// Verifies that looking up a coordinate system by SRID returns a non-null result for a well-known code. + /// + [Fact] + public void GetCoordinateSystem_BySrid_ReturnsNonNull() + { + CoordinateSystemServices css = CreateServices(); + + CoordinateSystem? cs = css.GetCoordinateSystem(4326); + + Assert.NotNull(cs); + } + + /// + /// Verifies that looking up by authority and code returns the same object as by SRID. + /// + [Fact] + public void GetCoordinateSystem_ByAuthorityAndCode_ReturnsSameAsById() + { + CoordinateSystemServices css = CreateServices(); + + CoordinateSystem? bySrid = css.GetCoordinateSystem(4326); + CoordinateSystem? byAuth = css.GetCoordinateSystem("EPSG", 4326); + + Assert.NotNull(bySrid); + Assert.Same(bySrid, byAuth); + } + + /// + /// Verifies that GetSRID returns the expected value for a registered coordinate system. + /// + [Fact] + public void GetSrid_ForRegisteredSystem_ReturnsExpectedValue() + { + CoordinateSystemServices css = CreateServices(); + + int? srid = css.GetSRID("EPSG", 4326); + + Assert.Equal(4326, srid); + } + + /// + /// Verifies that GetCoordinateSystem returns null for an unknown SRID. + /// + [Fact] + public void GetCoordinateSystem_UnknownSrid_ReturnsNull() + { + CoordinateSystemServices css = CreateServices(); + + CoordinateSystem? cs = css.GetCoordinateSystem(999999); + + Assert.Null(cs); + } + + /// + /// Verifies that multiple well-known codes resolve to distinct coordinate system instances. + /// + [Fact] + public void GetCoordinateSystem_DifferentSrids_ReturnDifferentInstances() + { + CoordinateSystemServices css = CreateServices(); + + CoordinateSystem? cs4326 = css.GetCoordinateSystem(4326); + CoordinateSystem? cs3857 = css.GetCoordinateSystem(3857); + + Assert.NotNull(cs4326); + Assert.NotNull(cs3857); + Assert.NotSame(cs4326, cs3857); + } + + /// + /// Verifies that repeated lookups for the same SRID return the same cached instance. + /// + [Fact] + public void GetCoordinateSystem_RepeatedLookup_ReturnsCachedInstance() + { + CoordinateSystemServices css = CreateServices(); + + CoordinateSystem? first = css.GetCoordinateSystem(4326); + CoordinateSystem? second = css.GetCoordinateSystem(4326); + + Assert.Same(first, second); + } + + /// + /// Verifies that GetSRID returns null for an unregistered authority and code. + /// + [Fact] + public void GetSrid_UnregisteredAuthorityCode_ReturnsNull() + { + CoordinateSystemServices css = CreateServices(); + + int? srid = css.GetSRID("UNKNOWN", 99999); + + Assert.Null(srid); + } + + /// + /// Verifies that registering and looking up a coordinate system with an authority code larger than does not overflow the lookup hash. + /// + [Fact] + public void GetSrid_LargeAuthorityCode_DoesNotOverflowHashing() + { + const long LargeAuthorityCode = (long)int.MaxValue + 12345L; + TestCoordinateSystemServices css = CreateMutableServices(); + GeographicCoordinateSystem coordinateSystem = GeographicCoordinateSystem.WGS84 + .WithAuthority("TEST", LargeAuthorityCode) + .WithName("Large code WGS84"); + + css.Register(4326, coordinateSystem); + + Assert.Equal(4326, css.GetSRID("TEST", LargeAuthorityCode)); + Assert.Same(coordinateSystem, css.GetCoordinateSystem("TEST", LargeAuthorityCode)); + } + + private static CoordinateSystemServices CreateServices() + { + return CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + } + + private static TestCoordinateSystemServices CreateMutableServices() + { + return new TestCoordinateSystemServices(); + } + + private sealed class TestCoordinateSystemServices : CoordinateSystemServices + { + public TestCoordinateSystemServices() + : base( + CoordinateSystemTestHelpers.CreateCoordinateSystemFactory(), + CoordinateSystemTestHelpers.CreateCoordinateTransformationFactory(), + []) + { + } + + public void Register(int srid, CoordinateSystem coordinateSystem) + { + this.AddCoordinateSystem(srid, coordinateSystem); + } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemServicesClearTests.cs b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemServicesClearTests.cs new file mode 100644 index 00000000..5d25269c --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemServicesClearTests.cs @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using Xunit; + +/// +/// Tests for consistency. +/// +public class CoordinateSystemServicesClearTests +{ + /// + /// Verifies that clearing the registry removes both SRID and authority-code lookups. + /// + [Fact] + public void Clear_RemovesCoordinateSystemAndReverseLookup() + { + var services = new TestCoordinateSystemServices(); + + services.Register(4326, GeographicCoordinateSystem.WGS84); + + Assert.Equal(4326, services.GetSRID("EPSG", 4326)); + Assert.Equal(1, services.RegisteredCount); + + services.ClearRegistry(); + + Assert.Null(services.GetCoordinateSystem(4326)); + Assert.Null(services.GetSRID("EPSG", 4326)); + Assert.Equal(0, services.RegisteredCount); + } + + /// + /// Verifies that clearing the registry allows the same SRID and authority code to be registered again without stale lookup state. + /// + [Fact] + public void Clear_AllowsReRegisteringCoordinateSystemWithSameAuthorityCode() + { + var services = new TestCoordinateSystemServices(); + GeographicCoordinateSystem replacement = GeographicCoordinateSystem.WGS84 + .WithName("Replacement WGS84") + .WithAuthority("EPSG", 4326); + + services.Register(4326, GeographicCoordinateSystem.WGS84); + services.ClearRegistry(); + services.Register(4326, replacement); + + Assert.Same(replacement, services.GetCoordinateSystem(4326)); + Assert.Same(replacement, services.GetCoordinateSystem("EPSG", 4326)); + Assert.Equal(4326, services.GetSRID("EPSG", 4326)); + Assert.Equal(1, services.RegisteredCount); + } + + /// + /// Verifies that clearing the registry also removes cached SRID-pair transformations. + /// + [Fact] + public void Clear_RemovesCachedTransformationInstances() + { + var services = new TestCoordinateSystemServices(); + + services.Register(4326, GeographicCoordinateSystem.WGS84); + services.Register(3857, ProjectedCoordinateSystem.WebMercator); + + ICoordinateTransformation first = Assert.IsAssignableFrom(services.CreateTransformation(4326, 3857)); + Assert.Same(first, Assert.IsAssignableFrom(services.CreateTransformation(4326, 3857))); + + services.ClearRegistry(); + services.Register(4326, GeographicCoordinateSystem.WGS84); + services.Register(3857, ProjectedCoordinateSystem.WebMercator); + + ICoordinateTransformation second = Assert.IsAssignableFrom(services.CreateTransformation(4326, 3857)); + + Assert.NotSame(first, second); + Assert.Same(second, Assert.IsAssignableFrom(services.CreateTransformation(4326, 3857))); + } + + /// + /// Verifies that replacing a registered coordinate system invalidates affected cached transformations. + /// + [Fact] + public void Register_ReplacementCoordinateSystem_InvalidatesAffectedTransformationCache() + { + var services = new TestCoordinateSystemServices(); + ProjectedCoordinateSystem replacement = ProjectedCoordinateSystem.WebMercator + .WithName("Replacement Web Mercator") + .WithAuthority("TEST", 93857); + + services.Register(4326, GeographicCoordinateSystem.WGS84); + services.Register(3857, ProjectedCoordinateSystem.WebMercator); + + ICoordinateTransformation first = Assert.IsAssignableFrom(services.CreateTransformation(4326, 3857)); + + services.Register(3857, replacement); + Assert.Same(replacement, services.GetCoordinateSystem(3857)); + + ICoordinateTransformation second = Assert.IsAssignableFrom(services.CreateTransformation(4326, 3857)); + + Assert.NotSame(first, second); + Assert.Same(second, Assert.IsAssignableFrom(services.CreateTransformation(4326, 3857))); + } + + /// + /// Verifies that clearing the registry uses the same lock as registration updates. + /// + /// A task that completes after the lock-observation assertion finishes. + [Fact] + public async Task Clear_WaitsForRegistryLock() + { + var services = new TestCoordinateSystemServices(); + using var clearStarted = new ManualResetEventSlim(); + using var clearCompleted = new ManualResetEventSlim(); + object syncRoot = services.GetSridDictionarySyncRoot(); + + Monitor.Enter(syncRoot); + try + { + var clearTask = Task.Run( + () => + { + clearStarted.Set(); + services.ClearRegistry(); + clearCompleted.Set(); + }, + TestContext.Current.CancellationToken); + + Assert.True(clearStarted.Wait(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken)); + Assert.False(clearCompleted.Wait(TimeSpan.FromMilliseconds(200), TestContext.Current.CancellationToken)); + + Monitor.Exit(syncRoot); + await clearTask.ConfigureAwait(true); + } + finally + { + if (Monitor.IsEntered(syncRoot)) + { + Monitor.Exit(syncRoot); + } + } + + Assert.True(clearCompleted.IsSet); + } + + private sealed class TestCoordinateSystemServices : CoordinateSystemServices + { + public TestCoordinateSystemServices() + : base( + CoordinateSystemTestHelpers.CreateCoordinateSystemFactory(), + CoordinateSystemTestHelpers.CreateCoordinateTransformationFactory(), + new List()) + { + } + + public int RegisteredCount => this.Count; + + public void Register(int srid, CoordinateSystem coordinateSystem) + { + this.AddCoordinateSystem(srid, coordinateSystem); + } + + public void ClearRegistry() + { + this.Clear(); + } + + /// + /// Gets the SRID dictionary sync root used by the service implementation. + /// + /// The sync root object for the SRID dictionary. + public object GetSridDictionarySyncRoot() + { + FieldInfo field = typeof(CoordinateSystemServices).GetField("csBySrid", BindingFlags.Instance | BindingFlags.NonPublic)!; + var dictionary = (IDictionary)field.GetValue(this)!; + return dictionary.SyncRoot; + } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemTests.cs new file mode 100644 index 00000000..ea4a8cc9 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemTests.cs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class CoordinateSystemTests +{ + /// + /// Verifies that reflects the configured axis count. + /// + [Fact] + public void Dimension_ReturnsAxisCount() + { + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(); + + Assert.Equal(2, coordinateSystem.Dimension); + } + + /// + /// Verifies that the constructor rejects axis definitions. + /// + [Fact] + public void Constructor_NullAxisInfo_ThrowsArgumentNullException() + { + Assert.Throws(() => new TestCoordinateSystem(null!, null)); + } + + /// + /// Verifies that exposes the configured envelope. + /// + [Fact] + public void DefaultEnvelope_ReturnsConfiguredEnvelope() + { + double[] expectedEnvelope = [-180d, -90d, 180d, 90d]; + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(defaultEnvelope: expectedEnvelope); + + Assert.Equal(expectedEnvelope, coordinateSystem.DefaultEnvelope); + } + + /// + /// Verifies that returns a defensive copy. + /// + [Fact] + public void DefaultEnvelope_GetterReturnsDefensiveCopy() + { + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(defaultEnvelope: [-180d, -90d, 180d, 90d]); + + double[] firstRead = coordinateSystem.DefaultEnvelope; + double[] secondRead = coordinateSystem.DefaultEnvelope; + firstRead[0] = 0d; + + Assert.NotSame(firstRead, secondRead); + Assert.Equal(-180d, secondRead[0]); + } + + /// + /// Verifies that the constructor clones the assigned default envelope array. + /// + [Fact] + public void Constructor_ClonesAssignedDefaultEnvelope() + { + double[] sourceEnvelope = [-180d, -90d, 180d, 90d]; + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(defaultEnvelope: sourceEnvelope); + sourceEnvelope[0] = 0d; + + Assert.Equal(-180d, coordinateSystem.DefaultEnvelope[0]); + } + + /// + /// Verifies that uses the WKT string by default. + /// + [Fact] + public void ToWktNode_DefaultImplementation_ReturnsIdentifier() + { + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(); + + WktIdentifier node = Assert.IsType(coordinateSystem.ToWktNode()); + + Assert.Equal("CS_WKT", node.Name); + } + + /// + /// Verifies that throws by default when XML serialization is unsupported. + /// + [Fact] + public void ToXml_DefaultImplementation_ThrowsNotSupportedException() + { + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(); + + Assert.Throws(() => coordinateSystem.ToXml()); + } + + /// + /// Verifies that returns the configured axis for a valid dimension. + /// + [Fact] + public void GetAxis_WithValidDimension_ReturnsAxis() + { + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(); + + AxisInfo axis = coordinateSystem.GetAxis(1); + + Assert.Equal("Latitude", axis.Name); + Assert.Equal(AxisOrientationEnum.North, axis.Orientation); + } + + /// + /// Verifies that rejects negative dimensions. + /// + [Fact] + public void GetAxis_WithNegativeDimension_ThrowsArgumentOutOfRangeException() + { + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(); + + ArgumentOutOfRangeException exception = Assert.Throws(() => coordinateSystem.GetAxis(-1)); + Assert.Equal("dimension", exception.ParamName); + } + + /// + /// Verifies that rejects dimensions beyond the configured axis count. + /// + [Fact] + public void GetAxis_WithOutOfRangeDimension_ThrowsArgumentOutOfRangeException() + { + TestCoordinateSystem coordinateSystem = CreateCoordinateSystem(); + + ArgumentOutOfRangeException exception = Assert.Throws(() => coordinateSystem.GetAxis(2)); + Assert.Equal("dimension", exception.ParamName); + } + + private static TestCoordinateSystem CreateCoordinateSystem(List? axisInfo = null, double[]? defaultEnvelope = null) + { + return new TestCoordinateSystem( + axisInfo ?? + [ + new AxisInfo("Longitude", AxisOrientationEnum.East), + new AxisInfo("Latitude", AxisOrientationEnum.North), + ], + defaultEnvelope); + } + + private sealed class TestCoordinateSystem : CoordinateSystem + { + internal TestCoordinateSystem(List axisInfo, double[]? defaultEnvelope) + : base("Test CS", "AUTH", 1, "alias", "abbr", "remarks", axisInfo, defaultEnvelope) + { + } + + public override string WKT => "CS_WKT"; + + public override string XML => ""; + + public override IUnit GetUnits(int dimension) => LinearUnit.Metre; + + public override bool EqualParams(object obj) => obj is TestCoordinateSystem; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemUtilitiesTests.cs b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemUtilitiesTests.cs new file mode 100644 index 00000000..51873298 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/CoordinateSystemUtilitiesTests.cs @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for . +/// +public class CoordinateSystemUtilitiesTests +{ + /// + /// Verifies that returns the expected UTM zone. + /// + /// The longitude in decimal degrees. + /// The expected UTM zone. + [Theory] + [InlineData(-180d, 1L)] + [InlineData(-174d, 2L)] + [InlineData(0d, 31L)] + [InlineData(6d, 32L)] + [InlineData(179d, 60L)] + [InlineData(180d, 60L)] + public void CalcUtmZone_ReturnsExpectedZone(double longitude, long expectedZone) + { + long zone = CoordinateSystemUtilities.CalcUtmZone(longitude); + + Assert.Equal(expectedZone, zone); + } + + /// + /// Verifies that converts valid values. + /// + /// The longitude in decimal degrees. + /// Whether the closed interval endpoints are accepted. + /// The expected value in radians. + [Theory] + [InlineData(0d, false, 0d)] + [InlineData(180d, true, Math.PI)] + [InlineData(-90d, false, -Math.PI / 2d)] + public void LongitudeToRadians_WithValidValue_ReturnsRadians(double longitude, bool edge, double expectedRadians) + { + double radians = CoordinateSystemUtilities.LongitudeToRadians(longitude, edge); + + Assert.Equal(expectedRadians, radians, 12); + } + + /// + /// Verifies that rejects out-of-range values. + /// + /// The longitude in decimal degrees. + /// Whether the closed interval endpoints are accepted. + [Theory] + [InlineData(-180d, false)] + [InlineData(180d, false)] + [InlineData(181d, true)] + public void LongitudeToRadians_WithOutOfRangeValue_ThrowsArgumentOutOfRangeException(double longitude, bool edge) + { + Assert.Throws(() => CoordinateSystemUtilities.LongitudeToRadians(longitude, edge)); + } + + /// + /// Verifies that converts valid values. + /// + /// The latitude in decimal degrees. + /// Whether the closed interval endpoints are accepted. + /// The expected value in radians. + [Theory] + [InlineData(0d, false, 0d)] + [InlineData(90d, true, Math.PI / 2d)] + [InlineData(-45d, false, -Math.PI / 4d)] + public void LatitudeToRadians_WithValidValue_ReturnsRadians(double latitude, bool edge, double expectedRadians) + { + double radians = CoordinateSystemUtilities.LatitudeToRadians(latitude, edge); + + Assert.Equal(expectedRadians, radians, 12); + } + + /// + /// Verifies that rejects out-of-range values. + /// + /// The latitude in decimal degrees. + /// Whether the closed interval endpoints are accepted. + [Theory] + [InlineData(-90d, false)] + [InlineData(90d, false)] + [InlineData(91d, true)] + public void LatitudeToRadians_WithOutOfRangeValue_ThrowsArgumentOutOfRangeException(double latitude, bool edge) + { + Assert.Throws(() => CoordinateSystemUtilities.LatitudeToRadians(latitude, edge)); + } + + /// + /// Verifies that the obsolete helper forwards to . + /// + [Fact] + public void ObsoleteCalcUtmZone_MatchesCoordinateSystemUtilities() + { + Assert.Equal( + CoordinateSystemUtilities.CalcUtmZone(15d), + CompatibilityProjection.ForwardCalcUtmZone(15d)); + } + + /// + /// Verifies that the obsolete angle helpers forward to . + /// + [Fact] + public void ObsoleteAngleHelpers_MatchCoordinateSystemUtilities() + { + _ = new CompatibilityProjection(); + + Assert.Equal( + CoordinateSystemUtilities.LongitudeToRadians(45d, edge: false), + CompatibilityProjection.ForwardLongitudeToRadians(45d, edge: false), + 12); + Assert.Equal( + CoordinateSystemUtilities.LatitudeToRadians(-30d, edge: true), + CompatibilityProjection.ForwardLatitudeToRadians(-30d, edge: true), + 12); + } + + /// + /// Verifies that the obsolete helper still normalizes longitudes to [-π, π]. + /// + [Theory] + [InlineData(0d, 0d)] + [InlineData(Math.PI, Math.PI)] + [InlineData(-Math.PI, -Math.PI)] + [InlineData(1.5d * Math.PI, -0.5d * Math.PI)] + [InlineData(-1.5d * Math.PI, 0.5d * Math.PI)] + [InlineData(2d * Math.PI, 0d)] + [InlineData(-2d * Math.PI, 0d)] + [InlineData(5d * Math.PI, Math.PI)] + [InlineData(-5d * Math.PI, -Math.PI)] + [InlineData((20d * Math.PI) + 0.25d, 0.25d)] + [InlineData((-20d * Math.PI) - 0.25d, -0.25d)] + public void ObsoleteAdjustLon_NormalizesToCanonicalInterval(double longitude, double expected) + { + Assert.Equal(expected, CompatibilityProjection.ForwardAdjustLon(longitude), 12); + } + + private sealed class CompatibilityProjection : MapProjection + { + internal CompatibilityProjection() + : base( + [ + new ProjectionParameter("semi_major", 6378137d), + new ProjectionParameter("semi_minor", 6356752.314245179d), + new ProjectionParameter("unit", 1d), + new ProjectionParameter("central_meridian", 0d), + ]) + { + } + + public override MathTransform Inverse() => this; + + internal static long ForwardCalcUtmZone(double longitude) + { +#pragma warning disable CS0618 + return CalcUtmZone(longitude); +#pragma warning restore CS0618 + } + + internal static double ForwardLongitudeToRadians(double longitude, bool edge) + { +#pragma warning disable CS0618 + return LongitudeToRadians(longitude, edge); +#pragma warning restore CS0618 + } + + internal static double ForwardLatitudeToRadians(double latitude, bool edge) + { +#pragma warning disable CS0618 + return LatitudeToRadians(latitude, edge); +#pragma warning restore CS0618 + } + + internal static double ForwardAdjustLon(double longitude) + { +#pragma warning disable CS0618 + return Adjust_lon(longitude); +#pragma warning restore CS0618 + } + + protected override void MetersToRadians(ref double x, ref double y) + { + } + + protected override void RadiansToMeters(ref double lon, ref double lat) + { + } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/DatumEnsembleTests.cs b/test/ProjNet.Tests/CoordinateSystems/DatumEnsembleTests.cs new file mode 100644 index 00000000..8e2d2c43 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/DatumEnsembleTests.cs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Tests for and . +/// +public class DatumEnsembleTests +{ + /// + /// Verifies the constructor stores the supplied members, accuracy, ellipsoid, and identifier. + /// + [Fact] + public void Constructor_SetsMembersAccuracyEllipsoidAndIdentifier() + { + DatumEnsembleMember[] members = + [ + new DatumEnsembleMember("World Geodetic System 1984 (Transit)", "EPSG", 1166), + new DatumEnsembleMember("World Geodetic System 1984 (G730)", "EPSG", 1152), + ]; + + var ensemble = new DatumEnsemble( + "World Geodetic System 1984 ensemble", + members, + 2d, + Ellipsoid.WGS84, + "EPSG", + 6326); + + Assert.Equal("World Geodetic System 1984 ensemble", ensemble.Name); + Assert.Equal(2d, ensemble.Accuracy); + Assert.True(Assert.IsType(ensemble.Ellipsoid).EqualParams(Ellipsoid.WGS84)); + Assert.Equal("EPSG", ensemble.Authority); + Assert.Equal(6326, ensemble.AuthorityCode); + Assert.Equal(2, ensemble.Members.Count); + Assert.Equal("World Geodetic System 1984 (Transit)", ensemble.Members[0].Name); + } + + /// + /// Verifies empty member lists are rejected. + /// + [Fact] + public void Constructor_WithEmptyMembers_ThrowsArgumentException() + { + Assert.Throws(() => new DatumEnsemble("Invalid", [], 1d)); + } + + /// + /// Verifies equivalent ensembles compare equal. + /// + [Fact] + public void Equals_WithEquivalentValues_ReturnsTrue() + { + var left = new DatumEnsemble( + "European Terrestrial Reference System 1989 ensemble", + [new DatumEnsembleMember("ETRF89", "EPSG", 1178)], + 0.1d, + Ellipsoid.GRS80, + "EPSG", + 6258); + var right = new DatumEnsemble( + "European Terrestrial Reference System 1989 ensemble", + [new DatumEnsembleMember("ETRF89", "EPSG", 1178)], + 0.1d, + Ellipsoid.GRS80, + "EPSG", + 6258); + + Assert.True(left.Equals(right)); + Assert.Equal(left.GetHashCode(), right.GetHashCode()); + } + + /// + /// Verifies accuracy changes produce different ensembles. + /// + [Fact] + public void Equals_WithDifferentAccuracy_ReturnsFalse() + { + var left = new DatumEnsemble("Vertical ensemble", [new DatumEnsembleMember("A")], 0.02d); + var right = new DatumEnsemble("Vertical ensemble", [new DatumEnsembleMember("A")], 0.05d); + + Assert.False(left.Equals(right)); + } + + /// + /// Verifies member equality uses name and identifier. + /// + [Fact] + public void MemberEquals_WithDifferentIdentifier_ReturnsFalse() + { + var left = new DatumEnsembleMember("World Geodetic System 1984 (Transit)", "EPSG", 1166); + var right = new DatumEnsembleMember("World Geodetic System 1984 (Transit)", "EPSG", 1152); + + Assert.False(left.Equals(right)); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/DatumTests.cs b/test/ProjNet.Tests/CoordinateSystems/DatumTests.cs new file mode 100644 index 00000000..bcc54ae4 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/DatumTests.cs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Tests for . +/// +public class DatumTests +{ + /// + /// Verifies that the constructor stores the supplied datum type. + /// + [Fact] + public void Constructor_SetsDatumType() + { + var datum = new TestDatum(DatumType.VD_Orthometric, "Test datum"); + + Assert.Equal(DatumType.VD_Orthometric, datum.DatumType); + } + + /// + /// Verifies that equality is based on datum type and ignores metadata. + /// + [Fact] + public void EqualParams_SameDatumTypeDifferentMetadata_ReturnsTrue() + { + var first = new TestDatum(DatumType.VD_Orthometric, "First datum", "EPSG", 1, "a1", "r1", "abbr1"); + var second = new TestDatum(DatumType.VD_Orthometric, "Second datum", "OTHER", 2, "a2", "r2", "abbr2"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that different datum types compare unequal. + /// + [Fact] + public void EqualParams_DifferentDatumType_ReturnsFalse() + { + var first = new TestDatum(DatumType.VD_Orthometric, "First datum"); + var second = new TestDatum(DatumType.VD_Depth, "Second datum"); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that non-datum objects compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + var datum = new TestDatum(DatumType.VD_Orthometric, "Test datum"); + + Assert.False(datum.EqualParams("not a datum")); + } + + /// + /// Verifies ensemble metadata can be retained without affecting datum parameter equality. + /// + [Fact] + public void EqualParams_WithDifferentEnsembleMetadata_IgnoresEnsemble() + { + var first = new TestDatum( + DatumType.VD_Orthometric, + "First datum", + ensemble: new DatumEnsemble("Vertical ensemble", [new DatumEnsembleMember("Member A")], 0.1d)); + var second = new TestDatum( + DatumType.VD_Orthometric, + "Second datum", + ensemble: new DatumEnsemble("Other ensemble", [new DatumEnsembleMember("Member B")], 0.2d)); + + Assert.True(first.EqualParams(second)); + Assert.NotNull(first.Ensemble); + Assert.NotNull(second.Ensemble); + } + + private sealed class TestDatum : Datum + { + public TestDatum( + DatumType type, + string name, + string authority = "AUTH", + long code = 1, + string alias = "", + string remarks = "", + string abbreviation = "", + DatumEnsemble? ensemble = null) + : base(type, name, authority, code, alias, remarks, abbreviation, ensemble) + { + } + + public override string WKT => "TEST_DATUM"; + + public override string XML => ""; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/EllipsoidTests.cs b/test/ProjNet.Tests/CoordinateSystems/EllipsoidTests.cs new file mode 100644 index 00000000..a960c4e2 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/EllipsoidTests.cs @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class EllipsoidTests +{ + /// + /// Verifies that the built-in ellipsoid factories expose the expected metadata. + /// + /// Well-known ellipsoid key. + /// Expected ellipsoid name. + /// Expected authority code. + /// Expected IVF definitiveness. + /// Whether the axis unit should be Clarke's foot. + [Theory] + [InlineData("WGS84", "WGS 84", 7030L, true, false)] + [InlineData("WGS72", "WGS 72", 7043L, true, false)] + [InlineData("GRS80", "GRS 1980", 7019L, true, false)] + [InlineData("International1924", "International 1924", 7022L, true, false)] + [InlineData("Clarke1880", "Clarke 1880", 7034L, true, true)] + [InlineData("Clarke1866", "Clarke 1866", 7008L, false, false)] + [InlineData("Sphere", "GRS 1980 Authalic Sphere", 7048L, false, false)] + public void KnownEllipsoids_ExposeExpectedMetadata( + string key, + string expectedName, + long expectedAuthorityCode, + bool expectedIvfDefinitive, + bool usesClarkesFoot) + { + Ellipsoid ellipsoid = GetKnownEllipsoid(key); + LinearUnit expectedUnit = usesClarkesFoot ? LinearUnit.ClarkesFoot : LinearUnit.Metre; + + Assert.Equal(expectedName, ellipsoid.Name); + Assert.Equal("EPSG", ellipsoid.Authority); + Assert.Equal(expectedAuthorityCode, ellipsoid.AuthorityCode); + Assert.Equal(expectedIvfDefinitive, ellipsoid.IsIvfDefinitive); + Assert.True(ellipsoid.AxisUnit.EqualParams(expectedUnit)); + } + + /// + /// Verifies that IVF-definitive ellipsoids with zero inverse flattening use the semi-major axis as semi-minor axis. + /// + [Fact] + public void Constructor_WithZeroInverseFlatteningAndIvfDefinitive_UsesSemiMajorAxisAsSemiMinor() + { + var ellipsoid = new Ellipsoid(10d, 7d, 0d, true, LinearUnit.Metre, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.Equal(10d, ellipsoid.SemiMinorAxis); + } + + /// + /// Verifies that IVF-definitive ellipsoids compute the semi-minor axis from inverse flattening. + /// + [Fact] + public void Constructor_WithFiniteInverseFlatteningAndIvfDefinitive_ComputesSemiMinorAxis() + { + var ellipsoid = new Ellipsoid(10d, 7d, 2d, true, LinearUnit.Metre, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.Equal(5d, ellipsoid.SemiMinorAxis); + } + + /// + /// Verifies that non-IVF-definitive ellipsoids preserve the supplied semi-minor axis. + /// + [Fact] + public void Constructor_WithoutIvfDefinitive_PreservesSemiMinorAxis() + { + var ellipsoid = new Ellipsoid(10d, 7d, double.PositiveInfinity, false, LinearUnit.Metre, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.Equal(7d, ellipsoid.SemiMinorAxis); + } + + /// + /// Verifies that WKT omits the authority clause when authority information is unavailable. + /// + [Fact] + public void WKT_WithoutAuthority_OmitsAuthorityClause() + { + var ellipsoid = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.Equal("SPHEROID[\"Custom\", 10, 2]", ellipsoid.WKT); + } + + /// + /// Verifies that WKT includes the authority clause when authority information is available. + /// + [Fact] + public void WKT_WithAuthority_IncludesAuthorityClause() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + + Assert.Equal("SPHEROID[\"WGS 84\", 6378137, 298.257223563, AUTHORITY[\"EPSG\", \"7030\"]]", ellipsoid.WKT); + } + + /// + /// Verifies that XML contains the expected attributes and child elements. + /// + [Fact] + public void XML_ContainsExpectedStructure() + { + var ellipsoid = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.ClarkesFoot, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var xml = XElement.Parse(ellipsoid.XML); + + Assert.Equal("CS_Ellipsoid", xml.Name.LocalName); + Assert.Equal("10", (string?)xml.Attribute("SemiMajorAxis")); + Assert.Equal("7", (string?)xml.Attribute("SemiMinorAxis")); + Assert.Equal("2", (string?)xml.Attribute("InverseFlattening")); + Assert.Equal("0", (string?)xml.Attribute("IvfDefinitive")); + Assert.NotNull(xml.Element("CS_Info")); + Assert.NotNull(xml.Element("CS_LinearUnit")); + } + + /// + /// Verifies that IVF-definitive XML uses a flag value of 1. + /// + [Fact] + public void XML_WithIvfDefinitive_ContainsOneFlag() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + var xml = XElement.Parse(ellipsoid.XML); + + Assert.Equal("1", (string?)xml.Attribute("IvfDefinitive")); + Assert.NotNull(xml.Element("CS_Info")); + Assert.NotNull(xml.Element("CS_LinearUnit")); + } + + /// + /// Verifies that matches the XML property for IVF-definitive ellipsoids with authority information. + /// + [Fact] + public void ToXml_WithAuthorityAndIvfDefinitive_MatchesXmlProperty() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + XElement element = ellipsoid.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(ellipsoid.XML), element)); + } + + /// + /// Verifies that matches the XML property for non-IVF-definitive ellipsoids without authority information. + /// + [Fact] + public void ToXml_WithoutAuthority_MatchesXmlProperty() + { + var ellipsoid = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.ClarkesFoot, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + XElement element = ellipsoid.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(ellipsoid.XML), element)); + } + + /// + /// Verifies that the WKT node includes authority information when available. + /// + [Fact] + public void ToWktNode_WithAuthority_IncludesAuthorityNode() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + WktKeywordNode node = Assert.IsType(ellipsoid.ToWktNode()); + + Assert.Equal("SPHEROID", node.Keyword); + Assert.Equal(4, node.Children.Count); + Assert.IsType(node.Children[3]); + } + + /// + /// Verifies that the WKT node omits authority information when it is unavailable. + /// + [Fact] + public void ToWktNode_WithoutAuthority_OmitsAuthorityNode() + { + var ellipsoid = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + WktKeywordNode node = Assert.IsType(ellipsoid.ToWktNode()); + + Assert.Equal(3, node.Children.Count); + } + + /// + /// Verifies that equality ignores metadata when the geometric parameters match. + /// + [Fact] + public void EqualParams_SameParametersDifferentMetadata_ReturnsTrue() + { + var first = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "First", "EPSG", 1, "a1", "abbr1", "r1"); + var second = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "Second", "OTHER", 2, "a2", "abbr2", "r2"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that a different inverse flattening value breaks equality. + /// + [Fact] + public void EqualParams_DifferentInverseFlattening_ReturnsFalse() + { + var first = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var second = new Ellipsoid(10d, 7d, 3d, false, LinearUnit.Metre, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different IVF definitiveness flag breaks equality. + /// + [Fact] + public void EqualParams_DifferentIvfDefinitive_ReturnsFalse() + { + var first = new Ellipsoid(10d, 10d, 0d, true, LinearUnit.Metre, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var second = new Ellipsoid(10d, 10d, 0d, false, LinearUnit.Metre, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different semi-major axis breaks equality. + /// + [Fact] + public void EqualParams_DifferentSemiMajorAxis_ReturnsFalse() + { + var first = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var second = new Ellipsoid(11d, 7d, 2d, false, LinearUnit.Metre, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different semi-minor axis breaks equality. + /// + [Fact] + public void EqualParams_DifferentSemiMinorAxis_ReturnsFalse() + { + var first = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var second = new Ellipsoid(10d, 8d, 2d, false, LinearUnit.Metre, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different axis unit breaks equality. + /// + [Fact] + public void EqualParams_DifferentAxisUnit_ReturnsFalse() + { + var first = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Metre, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var second = new Ellipsoid(10d, 7d, 2d, false, LinearUnit.Foot, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that non-ellipsoid objects compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + + Assert.False(ellipsoid.EqualParams("not an ellipsoid")); + } + + private static Ellipsoid GetKnownEllipsoid(string key) + { + return key switch + { + "WGS84" => Ellipsoid.WGS84, + "WGS72" => Ellipsoid.WGS72, + "GRS80" => Ellipsoid.GRS80, + "International1924" => Ellipsoid.International1924, + "Clarke1880" => Ellipsoid.Clarke1880, + "Clarke1866" => Ellipsoid.Clarke1866, + "Sphere" => Ellipsoid.Sphere, + _ => throw new ArgumentOutOfRangeException(nameof(key)), + }; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/EngineeringCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/EngineeringCoordinateSystemTests.cs new file mode 100644 index 00000000..7d7c64e2 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/EngineeringCoordinateSystemTests.cs @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for and . +/// +public class EngineeringCoordinateSystemTests +{ + /// + /// Verifies that the constructor stores the engineering-specific properties. + /// + [Fact] + public void Constructor_SetsEngineeringSpecificProperties() + { + EngineeringDatum datum = new("Local plant", "EPSG", 1098, string.Empty, string.Empty, string.Empty); + var coordinateSystem = new EngineeringCoordinateSystem( + datum, + "Cartesian", + [new AxisInfo("x", AxisOrientationEnum.East), new AxisInfo("y", AxisOrientationEnum.North)], + [LinearUnit.Metre, LinearUnit.Metre], + "Plant grid", + "EPSG", + 5800, + string.Empty, + string.Empty, + string.Empty); + + Assert.Same(datum, coordinateSystem.EngineeringDatum); + Assert.Equal("Cartesian", coordinateSystem.CoordinateSystemType); + Assert.Equal(2, coordinateSystem.Dimension); + Assert.Equal(2, coordinateSystem.AxisUnits.Count); + } + + /// + /// Verifies that per-axis units are exposed through . + /// + [Fact] + public void GetUnits_ReturnsPerAxisUnits() + { + EngineeringCoordinateSystem coordinateSystem = CreateEngineeringCoordinateSystem([LinearUnit.Metre, new ParametricUnit(1d, "unity", string.Empty, -1, string.Empty, string.Empty, string.Empty)]); + + Assert.True(coordinateSystem.GetUnits(0).EqualParams(LinearUnit.Metre)); + Assert.True(coordinateSystem.GetUnits(1).EqualParams(new ParametricUnit(1d, "unity", string.Empty, -1, string.Empty, string.Empty, string.Empty))); + } + + /// + /// Verifies that WKT2 output uses ENGCRS and retains mixed axis units. + /// + [Fact] + public void ToWktNode_WithMixedUnits_UsesAxisLevelUnits() + { + EngineeringCoordinateSystem coordinateSystem = CreateEngineeringCoordinateSystem([LinearUnit.Metre, new ParametricUnit(1d, "unity", string.Empty, -1, string.Empty, string.Empty, string.Empty)]); + string wkt = coordinateSystem.ToWktNode(WktVersion.Wkt22019).ToString(); + + Assert.StartsWith("ENGCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("EDATUM[\"Local plant\"", wkt, StringComparison.Ordinal); + Assert.Contains("LENGTHUNIT[\"metre\"", wkt, StringComparison.Ordinal); + Assert.Contains("PARAMETRICUNIT[\"unity\"", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT2 roundtrips preserve mixed per-axis engineering units. + /// + [Fact] + public void ToWktNode_WithMixedUnits_RoundTripsEngineeringAxisUnits() + { + EngineeringCoordinateSystem original = CreateEngineeringCoordinateSystem([LinearUnit.Metre, new ParametricUnit(1d, "unity", string.Empty, -1, string.Empty, string.Empty, string.Empty)]); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + + EngineeringCoordinateSystem roundTripped = CoordinateSystemTestHelpers.RequireCoordinateSystem(wkt); + + Assert.True(original.EqualParams(roundTripped)); + Assert.IsType(roundTripped.AxisUnits[0]); + Assert.IsType(roundTripped.AxisUnits[1]); + Assert.Equal("unity", roundTripped.AxisUnits[1].Name); + } + + /// + /// Verifies that equivalent engineering coordinate systems compare equal. + /// + [Fact] + public void EqualParams_IgnoresMetadataButComparesUnits() + { + EngineeringCoordinateSystem first = CreateEngineeringCoordinateSystem([LinearUnit.Metre, LinearUnit.Metre], name: "First"); + EngineeringCoordinateSystem second = CreateEngineeringCoordinateSystem([LinearUnit.Metre, LinearUnit.Metre], name: "Second"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that the constructor reports the unit collection when the axis and unit counts differ. + /// + [Fact] + public void Constructor_MismatchedAxisAndUnitCounts_ThrowsArgumentExceptionWithUnitsParamName() + { + ArgumentException exception = Assert.Throws(() => new EngineeringCoordinateSystem( + new EngineeringDatum("Local plant", "EPSG", 1098, string.Empty, string.Empty, string.Empty), + "Cartesian", + [new AxisInfo("x", AxisOrientationEnum.East), new AxisInfo("y", AxisOrientationEnum.North)], + [LinearUnit.Metre], + "Plant grid", + "EPSG", + 5800, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("units", exception.ParamName); + } + + private static EngineeringCoordinateSystem CreateEngineeringCoordinateSystem(IUnit[] units, string name = "Plant grid") + { + return new EngineeringCoordinateSystem( + new EngineeringDatum("Local plant", "EPSG", 1098, string.Empty, string.Empty, string.Empty), + "Cartesian", + [new AxisInfo("x", AxisOrientationEnum.East), new AxisInfo("y", AxisOrientationEnum.North)], + units, + name, + "EPSG", + 5800, + string.Empty, + string.Empty, + string.Empty); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/FittedCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/FittedCoordinateSystemTests.cs new file mode 100644 index 00000000..213012d4 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/FittedCoordinateSystemTests.cs @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class FittedCoordinateSystemTests +{ + /// + /// Verifies that the constructor stores the supplied values and copies the base axes. + /// + [Fact] + public void Constructor_SetsPropertiesAndCopiesBaseAxes() + { + GeographicCoordinateSystem baseCoordinateSystem = CreateBaseCoordinateSystem(CreateCustomAxisInfo(), AngularUnit.Grad); + AffineTransform transform = CreateTransform(); + var system = new FittedCoordinateSystem( + baseCoordinateSystem, + transform, + "Custom fitted", + "TEST", + 42, + "alias", + "remarks", + "abbr"); + + Assert.Equal("Custom fitted", system.Name); + Assert.Equal("TEST", system.Authority); + Assert.Equal(42, system.AuthorityCode); + Assert.Equal("alias", system.Alias); + Assert.Equal("remarks", system.Remarks); + Assert.Equal("abbr", system.Abbreviation); + Assert.Same(baseCoordinateSystem, system.BaseCoordinateSystem); + Assert.Same(transform, system.ToBaseTransform); + Assert.Equal(baseCoordinateSystem.Dimension, system.Dimension); + Assert.Same(baseCoordinateSystem.GetAxis(0), system.GetAxis(0)); + Assert.Same(baseCoordinateSystem.GetAxis(1), system.GetAxis(1)); + } + + /// + /// Verifies that the constructor rejects a null base coordinate system. + /// + [Fact] + public void Constructor_NullBaseCoordinateSystem_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new FittedCoordinateSystem( + null!, + CreateTransform(), + "Custom fitted", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("baseSystem", exception.ParamName); + } + + /// + /// Verifies that the constructor rejects a null transform. + /// + [Fact] + public void Constructor_NullTransform_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new FittedCoordinateSystem( + GeographicCoordinateSystem.WGS84, + null!, + "Custom fitted", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("transform", exception.ParamName); + } + + /// + /// Verifies the factory preserves explicit fitted-axis metadata when it is supplied. + /// + [Fact] + public void Factory_WithCustomAxes_PreservesSuppliedAxisInfo() + { + CoordinateSystemFactory factory = new(); + List fittedAxes = + [ + new AxisInfo("Local latitude", AxisOrientationEnum.North), + new AxisInfo("Local longitude", AxisOrientationEnum.East), + ]; + + FittedCoordinateSystem system = factory.CreateFittedCoordinateSystem( + "Custom fitted", + GeographicCoordinateSystem.WGS84, + CreateTransform(), + fittedAxes); + + Assert.Equal("Local latitude", system.GetAxis(0).Name); + Assert.Equal(AxisOrientationEnum.North, system.GetAxis(0).Orientation); + Assert.Equal("Local longitude", system.GetAxis(1).Name); + Assert.Equal(AxisOrientationEnum.East, system.GetAxis(1).Orientation); + } + + /// + /// Verifies that explicit fitted axes report axisInfo when the axis count does not match the base system. + /// + [Fact] + public void Constructor_MismatchedExplicitAxisCount_ThrowsArgumentExceptionWithAxisInfoParamName() + { + ArgumentException exception = Assert.Throws(() => new FittedCoordinateSystem( + GeographicCoordinateSystem.WGS84, + CreateTransform(), + "Custom fitted", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty, + [new AxisInfo("Latitude", AxisOrientationEnum.North)])); + + Assert.Equal("axisInfo", exception.ParamName); + } + + /// + /// Verifies that WKT contains the fitted keyword, transform, and base coordinate system. + /// + [Fact] + public void WKT_ContainsTransformAndBaseCoordinateSystem() + { + GeographicCoordinateSystem baseCoordinateSystem = CreateBaseCoordinateSystem(); + AffineTransform transform = CreateTransform(); + FittedCoordinateSystem system = CreateSystem(baseCoordinateSystem, transform, authority: "EPSG", authorityCode: 910001); + + Assert.Equal($"FITTED_CS[\"Custom fitted\", {transform.WKT}, {baseCoordinateSystem.WKT}]", system.WKT); + } + + /// + /// Verifies that XML is not supported. + /// + [Fact] + public void XML_ThrowsNotSupportedException() + { + FittedCoordinateSystem system = CreateSystem(); + + Assert.Throws(() => system.XML); + } + + /// + /// Verifies that is not supported. + /// + [Fact] + public void ToXml_ThrowsNotSupportedException() + { + FittedCoordinateSystem system = CreateSystem(); + + Assert.Throws(() => system.ToXml()); + } + + /// + /// Verifies that exposes the fitted coordinate system structure. + /// + [Fact] + public void ToWktNode_ReturnsExpectedStructure() + { + GeographicCoordinateSystem baseCoordinateSystem = CreateBaseCoordinateSystem(); + AffineTransform transform = CreateTransform(); + FittedCoordinateSystem system = CreateSystem(baseCoordinateSystem, transform); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal("FITTED_CS", node.Keyword); + Assert.Equal("Custom fitted", Assert.IsType(node.Children[0]).Value); + Assert.Equal(transform.WKT, Assert.IsType(node.Children[1]).Name); + Assert.Equal("GEOGCS", Assert.IsType(node.Children[2]).Keyword); + Assert.Equal(system.WKT, node.ToString()); + } + + /// + /// Verifies that returns the transform WKT. + /// + [Fact] + public void ToBase_ReturnsTransformWkt() + { + AffineTransform transform = CreateTransform(); + FittedCoordinateSystem system = CreateSystem(transform: transform); + + Assert.Equal(transform.WKT, system.ToBase()); + } + + /// + /// Verifies that delegates to the base coordinate system. + /// + /// The dimension index. + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1)] + [InlineData(99)] + public void GetUnits_DelegatesToBaseCoordinateSystem(int dimension) + { + GeographicCoordinateSystem baseCoordinateSystem = CreateBaseCoordinateSystem(angularUnit: AngularUnit.Grad); + FittedCoordinateSystem system = CreateSystem(baseCoordinateSystem: baseCoordinateSystem); + IUnit unit = system.GetUnits(dimension); + + Assert.True(unit.EqualParams(AngularUnit.Grad)); + } + + /// + /// Verifies that equivalent fitted coordinate systems compare equal. + /// + [Fact] + public void EqualParams_SameValues_ReturnsTrue() + { + GeographicCoordinateSystem baseCoordinateSystem = CreateBaseCoordinateSystem(); + AffineTransform transform = CreateTransform(); + FittedCoordinateSystem first = CreateSystem(baseCoordinateSystem, transform, name: "First"); + FittedCoordinateSystem second = CreateSystem(baseCoordinateSystem, transform, name: "Second"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that a different base coordinate system causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentBaseCoordinateSystem_ReturnsFalse() + { + FittedCoordinateSystem first = CreateSystem(baseCoordinateSystem: CreateBaseCoordinateSystem()); + FittedCoordinateSystem second = CreateSystem(baseCoordinateSystem: CreateBaseCoordinateSystem(primeMeridian: PrimeMeridian.Paris)); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different transform causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentTransform_ReturnsFalse() + { + GeographicCoordinateSystem baseCoordinateSystem = CreateBaseCoordinateSystem(); + FittedCoordinateSystem first = CreateSystem(baseCoordinateSystem, CreateTransform(translationX: 10, translationY: 20)); + FittedCoordinateSystem second = CreateSystem(baseCoordinateSystem, CreateTransform(translationX: 30, translationY: 40)); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different fitted-axis orientation causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentAxisOrientation_ReturnsFalse() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem baseCoordinateSystem = CreateBaseCoordinateSystem(); + FittedCoordinateSystem first = factory.CreateFittedCoordinateSystem( + "First", + baseCoordinateSystem, + CreateTransform(), + [new AxisInfo("Latitude", AxisOrientationEnum.North), new AxisInfo("Longitude", AxisOrientationEnum.East)]); + FittedCoordinateSystem second = factory.CreateFittedCoordinateSystem( + "Second", + baseCoordinateSystem, + CreateTransform(), + [new AxisInfo("Latitude", AxisOrientationEnum.East), new AxisInfo("Longitude", AxisOrientationEnum.North)]); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different object type causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(CreateSystem().EqualParams("not a coordinate system")); + } + + private static FittedCoordinateSystem CreateSystem( + CoordinateSystem? baseCoordinateSystem = null, + MathTransform? transform = null, + string name = "Custom fitted", + string authority = "", + long authorityCode = -1) + { + return new FittedCoordinateSystem( + baseCoordinateSystem ?? CreateBaseCoordinateSystem(), + transform ?? CreateTransform(), + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static GeographicCoordinateSystem CreateBaseCoordinateSystem( + List? axisInfo = null, + AngularUnit? angularUnit = null, + HorizontalDatum? horizontalDatum = null, + PrimeMeridian? primeMeridian = null) + { + return new GeographicCoordinateSystem( + angularUnit ?? AngularUnit.Degrees, + horizontalDatum ?? HorizontalDatum.WGS84, + primeMeridian ?? PrimeMeridian.Greenwich, + axisInfo ?? CreateDefaultAxisInfo(), + "Base geographic", + "EPSG", + 4326, + string.Empty, + string.Empty, + string.Empty); + } + + private static AffineTransform CreateTransform(double translationX = 10, double translationY = 20) + { + return new AffineTransform( + 1, + 0, + translationX, + 0, + 1, + translationY); + } + + private static List CreateDefaultAxisInfo() + { + return + [ + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North), + ]; + } + + private static List CreateCustomAxisInfo() + { + return + [ + new AxisInfo("Longitude", AxisOrientationEnum.East), + new AxisInfo("Latitude", AxisOrientationEnum.North), + ]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/GeocentricCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/GeocentricCoordinateSystemTests.cs new file mode 100644 index 00000000..3f3e68dd --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/GeocentricCoordinateSystemTests.cs @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class GeocentricCoordinateSystemTests +{ + /// + /// Verifies that the predefined WGS84 system exposes the expected metadata and defaults. + /// + [Fact] + public void WGS84_HasExpectedMetadata() + { + GeocentricCoordinateSystem system = GeocentricCoordinateSystem.WGS84; + + Assert.Equal("WGS 84", system.Name); + Assert.Equal(3, system.Dimension); + Assert.True(system.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(system.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.True(system.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)); + Assert.Equal("Geocentric X (X)", system.GetAxis(0).Name); + Assert.Equal("Geocentric Y (Y)", system.GetAxis(1).Name); + Assert.Equal("Geocentric Z (Z)", system.GetAxis(2).Name); + } + + /// + /// Verifies that the predefined WGS84 system now reuses the same immutable catalog-backed instance. + /// + [Fact] + public void WGS84_ReturnsSameInstance() + { + GeocentricCoordinateSystem first = GeocentricCoordinateSystem.WGS84; + GeocentricCoordinateSystem second = GeocentricCoordinateSystem.WGS84; + + Assert.Same(first, second); + } + + /// + /// Verifies that the internal constructor stores the supplied values. + /// + [Fact] + public void Constructor_SetsProperties() + { + List axisInfo = CreateDefaultAxisInfo(); + var system = new GeocentricCoordinateSystem( + HorizontalDatum.ED50, + LinearUnit.Foot, + PrimeMeridian.Paris, + axisInfo, + "Custom geocentric", + "TEST", + 42, + "alias", + "remarks", + "abbr"); + + Assert.Equal("Custom geocentric", system.Name); + Assert.Equal("TEST", system.Authority); + Assert.Equal(42, system.AuthorityCode); + Assert.Equal("alias", system.Alias); + Assert.Equal("remarks", system.Remarks); + Assert.Equal("abbr", system.Abbreviation); + Assert.Same(axisInfo, system.AxisInfo); + Assert.True(system.HorizontalDatum.EqualParams(HorizontalDatum.ED50)); + Assert.True(system.LinearUnit.EqualParams(LinearUnit.Foot)); + Assert.True(system.PrimeMeridian.EqualParams(PrimeMeridian.Paris)); + } + + /// + /// Verifies that the constructor rejects a null datum. + /// + [Fact] + public void Constructor_NullDatum_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new GeocentricCoordinateSystem( + null!, + LinearUnit.Metre, + PrimeMeridian.Greenwich, + CreateDefaultAxisInfo(), + "Custom geocentric", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("datum", exception.ParamName); + } + + /// + /// Verifies that the constructor rejects a null linear unit. + /// + [Fact] + public void Constructor_NullLinearUnit_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new GeocentricCoordinateSystem( + HorizontalDatum.WGS84, + null!, + PrimeMeridian.Greenwich, + CreateDefaultAxisInfo(), + "Custom geocentric", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("linearUnit", exception.ParamName); + } + + /// + /// Verifies that the constructor rejects a null prime meridian. + /// + [Fact] + public void Constructor_NullPrimeMeridian_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new GeocentricCoordinateSystem( + HorizontalDatum.WGS84, + LinearUnit.Metre, + null!, + CreateDefaultAxisInfo(), + "Custom geocentric", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("primeMeridian", exception.ParamName); + } + + /// + /// Verifies that the constructor rejects a null axis list. + /// + [Fact] + public void Constructor_NullAxisInfo_ThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws(() => new GeocentricCoordinateSystem( + HorizontalDatum.WGS84, + LinearUnit.Metre, + PrimeMeridian.Greenwich, + null!, + "Custom geocentric", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("axisInfo", exception.ParamName); + } + + /// + /// Verifies that the constructor rejects axis lists that do not contain exactly three axes. + /// + [Fact] + public void Constructor_AxisInfoWithoutThreeAxes_ThrowsArgumentException() + { + ArgumentException exception = Assert.Throws(() => new GeocentricCoordinateSystem( + HorizontalDatum.WGS84, + LinearUnit.Metre, + PrimeMeridian.Greenwich, + new List { new("X", AxisOrientationEnum.Other), new("Y", AxisOrientationEnum.East) }, + "Custom geocentric", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + + Assert.Equal("axisInfo", exception.ParamName); + } + + /// + /// Verifies that WKT omits axis clauses when the default geocentric axes are used. + /// + [Fact] + public void WKT_WithDefaultAxes_OmitsAxisClauses() + { + GeocentricCoordinateSystem system = CreateSystem("Custom geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + + Assert.DoesNotContain("AXIS[", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT includes custom axis clauses when the axis definitions differ from the defaults. + /// + [Fact] + public void WKT_WithCustomAxes_IncludesAxisClauses() + { + GeocentricCoordinateSystem system = CreateSystem("Custom geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateCustomAxisInfo()); + + Assert.Contains("AXIS[\"Longitude\", EAST]", system.WKT, StringComparison.Ordinal); + Assert.Contains("AXIS[\"Latitude\", NORTH]", system.WKT, StringComparison.Ordinal); + Assert.Contains("AXIS[\"Height\", UP]", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT includes authority metadata when it is available. + /// + [Fact] + public void WKT_WithAuthority_IncludesAuthorityClause() + { + GeocentricCoordinateSystem system = CreateSystem( + "Custom geocentric", + HorizontalDatum.WGS84, + LinearUnit.Metre, + PrimeMeridian.Greenwich, + CreateDefaultAxisInfo(), + "EPSG", + 4984); + + Assert.Contains("AUTHORITY[\"EPSG\", \"4984\"]", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT omits authority metadata when it is unavailable. + /// + [Fact] + public void WKT_WithoutAuthority_OmitsAuthorityClause() + { + GeocentricCoordinateSystem system = GeocentricCoordinateSystem.WGS84; + + Assert.DoesNotContain("AUTHORITY[\"\",", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that XML includes all expected nested elements for a default system. + /// + [Fact] + public void XML_WithDefaultAxes_ContainsExpectedStructure() + { + GeocentricCoordinateSystem system = GeocentricCoordinateSystem.WGS84; + var xml = XElement.Parse(system.XML); + XElement inner = Assert.IsType(xml.Element("CS_GeocentricCoordinateSystem")); + + Assert.Equal("CS_CoordinateSystem", xml.Name.LocalName); + Assert.Equal("3", (string?)xml.Attribute("Dimension")); + Assert.NotNull(inner.Element("CS_Info")); + Assert.Equal(3, inner.Elements("CS_AxisInfo").Count()); + Assert.NotNull(inner.Element("CS_HorizontalDatum")); + Assert.NotNull(inner.Element("CS_LinearUnit")); + Assert.NotNull(inner.Element("CS_PrimeMeridian")); + } + + /// + /// Verifies that matches the XML property. + /// + [Fact] + public void ToXml_MatchesXmlProperty() + { + GeocentricCoordinateSystem system = CreateSystem("Custom geocentric", HorizontalDatum.ED50, LinearUnit.Foot, PrimeMeridian.Paris, CreateCustomAxisInfo()); + XElement xml = system.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(system.XML), xml)); + } + + /// + /// Verifies that GetUnits returns the linear unit for all valid dimensions. + /// + /// The queried dimension index. + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public void GetUnits_ValidDimension_ReturnsLinearUnit(int dimension) + { + GeocentricCoordinateSystem system = GeocentricCoordinateSystem.WGS84; + + IUnit unit = system.GetUnits(dimension); + + Assert.True(unit.EqualParams(LinearUnit.Metre)); + } + + /// + /// Verifies that GetUnits ignores out-of-range dimensions and still returns the shared linear unit. + /// + /// The queried out-of-range dimension index. + [Theory] + [InlineData(-1)] + [InlineData(3)] + public void GetUnits_OutOfRangeDimension_ReturnsLinearUnit(int dimension) + { + GeocentricCoordinateSystem system = GeocentricCoordinateSystem.WGS84; + + IUnit unit = system.GetUnits(dimension); + + Assert.True(unit.EqualParams(LinearUnit.Metre)); + } + + /// + /// Verifies that ToWktNode omits axis nodes for the default geocentric axis set. + /// + [Fact] + public void ToWktNode_WithDefaultAxes_OmitsAxisNodes() + { + GeocentricCoordinateSystem system = CreateSystem("Custom geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal("GEOCCS", node.Keyword); + Assert.Equal(4, node.Children.Count); + } + + /// + /// Verifies that ToWktNode includes custom axis nodes when non-default axes are used. + /// + [Fact] + public void ToWktNode_WithCustomAxes_IncludesAxisNodes() + { + GeocentricCoordinateSystem system = CreateSystem("Custom geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateCustomAxisInfo()); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal(7, node.Children.Count); + Assert.Equal("AXIS", Assert.IsType(node.Children[4]).Keyword); + Assert.Equal("AXIS", Assert.IsType(node.Children[5]).Keyword); + Assert.Equal("AXIS", Assert.IsType(node.Children[6]).Keyword); + } + + /// + /// Verifies that ToWktNode includes an authority node when authority metadata is present. + /// + [Fact] + public void ToWktNode_WithAuthority_IncludesAuthorityNode() + { + GeocentricCoordinateSystem system = CreateSystem( + "Custom geocentric", + HorizontalDatum.WGS84, + LinearUnit.Metre, + PrimeMeridian.Greenwich, + CreateDefaultAxisInfo(), + "EPSG", + 4984); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal("AUTHORITY", Assert.IsType(node.Children[4]).Keyword); + } + + /// + /// Verifies that equal systems compare equal when datum, unit, and prime meridian all match. + /// + [Fact] + public void EqualParams_SameValues_ReturnsTrue() + { + GeocentricCoordinateSystem first = CreateSystem("A", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + GeocentricCoordinateSystem second = CreateSystem("B", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateCustomAxisInfo()); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that differing horizontal datums compare unequal. + /// + [Fact] + public void EqualParams_DifferentDatum_ReturnsFalse() + { + GeocentricCoordinateSystem first = CreateSystem("A", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + GeocentricCoordinateSystem second = CreateSystem("B", HorizontalDatum.ED50, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that differing linear units compare unequal. + /// + [Fact] + public void EqualParams_DifferentLinearUnit_ReturnsFalse() + { + GeocentricCoordinateSystem first = CreateSystem("A", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + GeocentricCoordinateSystem second = CreateSystem("B", HorizontalDatum.WGS84, LinearUnit.Foot, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that differing prime meridians compare unequal. + /// + [Fact] + public void EqualParams_DifferentPrimeMeridian_ReturnsFalse() + { + GeocentricCoordinateSystem first = CreateSystem("A", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich, CreateDefaultAxisInfo()); + GeocentricCoordinateSystem second = CreateSystem("B", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Paris, CreateDefaultAxisInfo()); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different object types compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(GeocentricCoordinateSystem.WGS84.EqualParams("not a coordinate system")); + } + + private static GeocentricCoordinateSystem CreateSystem( + string name, + HorizontalDatum datum, + LinearUnit linearUnit, + PrimeMeridian primeMeridian, + List axisInfo, + string authority = "", + long authorityCode = -1) + { + return new GeocentricCoordinateSystem( + datum, + linearUnit, + primeMeridian, + axisInfo, + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static List CreateDefaultAxisInfo() + { + return + [ + new AxisInfo("X", AxisOrientationEnum.Other), + new AxisInfo("Y", AxisOrientationEnum.East), + new AxisInfo("Z", AxisOrientationEnum.North), + ]; + } + + private static List CreateCustomAxisInfo() + { + return + [ + new AxisInfo("Longitude", AxisOrientationEnum.East), + new AxisInfo("Latitude", AxisOrientationEnum.North), + new AxisInfo("Height", AxisOrientationEnum.Up), + ]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/HorizontalCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/HorizontalCoordinateSystemTests.cs new file mode 100644 index 00000000..a07a2a53 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/HorizontalCoordinateSystemTests.cs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Tests for . +/// +public class HorizontalCoordinateSystemTests +{ + /// + /// Verifies that the constructor stores the horizontal datum and axes for a valid two-axis definition. + /// + [Fact] + public void Constructor_WithTwoAxes_SetsDatumAndAxisInfo() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + List axisInfo = + [ + new AxisInfo("Longitude", AxisOrientationEnum.East), + new AxisInfo("Latitude", AxisOrientationEnum.North), + ]; + + TestHorizontalCoordinateSystem coordinateSystem = new(datum, axisInfo); + + Assert.Same(datum, coordinateSystem.HorizontalDatum); + Assert.Equal(2, coordinateSystem.Dimension); + Assert.Same(axisInfo[0], coordinateSystem.GetAxis(0)); + Assert.Same(axisInfo[1], coordinateSystem.GetAxis(1)); + } + + /// + /// Verifies that the constructor rejects a missing datum. + /// + [Fact] + public void Constructor_WithNullDatum_ThrowsArgumentNullException() + { + List axisInfo = + [ + new AxisInfo("Longitude", AxisOrientationEnum.East), + new AxisInfo("Latitude", AxisOrientationEnum.North), + ]; + + Assert.Throws(() => new TestHorizontalCoordinateSystem(null!, axisInfo)); + } + + /// + /// Verifies that the constructor rejects a missing axis list. + /// + [Fact] + public void Constructor_WithNullAxisInfo_ThrowsArgumentNullException() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + + Assert.Throws(() => new TestHorizontalCoordinateSystem(datum, null!)); + } + + /// + /// Verifies that the constructor rejects axis lists that do not contain exactly two axes. + /// + [Fact] + public void Constructor_WithNonTwoAxisList_ThrowsArgumentException() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + List axisInfo = + [ + new AxisInfo("Longitude", AxisOrientationEnum.East), + ]; + + ArgumentException exception = Assert.Throws(() => new TestHorizontalCoordinateSystem(datum, axisInfo)); + Assert.Equal("axisInfo", exception.ParamName); + } + + private sealed class TestHorizontalCoordinateSystem : HorizontalCoordinateSystem + { + internal TestHorizontalCoordinateSystem(HorizontalDatum datum, List axisInfo) + : base(datum, axisInfo, "Test HCS", "AUTH", 1, "alias", "remarks", "abbr") + { + } + + public override string WKT => "HCS_WKT"; + + public override string XML => ""; + + public override IUnit GetUnits(int dimension) => AngularUnit.Degrees; + + public override bool EqualParams(object obj) => obj is TestHorizontalCoordinateSystem; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/HorizontalDatumTests.cs b/test/ProjNet.Tests/CoordinateSystems/HorizontalDatumTests.cs new file mode 100644 index 00000000..d8edcbbf --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/HorizontalDatumTests.cs @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class HorizontalDatumTests +{ + /// + /// Verifies that the predefined WGS84 datum exposes the expected metadata. + /// + [Fact] + public void WGS84_HasExpectedMetadata() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + + Assert.Equal("World Geodetic System 1984", datum.Name); + Assert.Equal("EPSG", datum.Authority); + Assert.Equal(6326, datum.AuthorityCode); + Assert.Equal(DatumType.HD_Geocentric, datum.DatumType); + Assert.True(datum.Ellipsoid.EqualParams(Ellipsoid.WGS84)); + Assert.Null(datum.Wgs84Parameters); + } + + /// + /// Verifies that the predefined WGS72 datum exposes the expected metadata and Bursa-Wolf parameters. + /// + [Fact] + public void WGS72_HasExpectedMetadata() + { + HorizontalDatum datum = HorizontalDatum.WGS72; + Wgs84ConversionInfo parameters = Assert.IsType(datum.Wgs84Parameters); + + Assert.Equal("World Geodetic System 1972", datum.Name); + Assert.Equal("EPSG", datum.Authority); + Assert.Equal(6322, datum.AuthorityCode); + Assert.Equal(DatumType.HD_Geocentric, datum.DatumType); + Assert.True(datum.Ellipsoid.EqualParams(Ellipsoid.WGS72)); + Assert.Equal(0d, parameters.Dx); + Assert.Equal(0d, parameters.Dy); + Assert.Equal(4.5d, parameters.Dz); + Assert.Equal(0d, parameters.Ex); + Assert.Equal(0d, parameters.Ey); + Assert.Equal(0.554d, parameters.Ez); + Assert.Equal(0.219d, parameters.Ppm); + } + + /// + /// Verifies that the predefined ETRF89 datum exposes the expected metadata and zero WGS84 parameters. + /// + [Fact] + public void ETRF89_HasExpectedMetadata() + { + HorizontalDatum datum = HorizontalDatum.ETRF89; + Wgs84ConversionInfo parameters = Assert.IsType(datum.Wgs84Parameters); + + Assert.Equal("European Terrestrial Reference System 1989", datum.Name); + Assert.Equal("EPSG", datum.Authority); + Assert.Equal(6258, datum.AuthorityCode); + Assert.Equal("ETRF89", datum.Alias); + Assert.Equal(DatumType.HD_Geocentric, datum.DatumType); + Assert.True(datum.Ellipsoid.EqualParams(Ellipsoid.GRS80)); + Assert.True(parameters.HasZeroValuesOnly); + } + + /// + /// Verifies that the predefined ED50 datum exposes the expected metadata and WGS84 conversion parameters. + /// + [Fact] + public void ED50_HasExpectedMetadata() + { + HorizontalDatum datum = HorizontalDatum.ED50; + Wgs84ConversionInfo parameters = Assert.IsType(datum.Wgs84Parameters); + + Assert.Equal("European Datum 1950", datum.Name); + Assert.Equal("EPSG", datum.Authority); + Assert.Equal(6230, datum.AuthorityCode); + Assert.Equal("ED50", datum.Alias); + Assert.Equal(DatumType.HD_Geocentric, datum.DatumType); + Assert.True(datum.Ellipsoid.EqualParams(Ellipsoid.International1924)); + Assert.Equal(-87d, parameters.Dx); + Assert.Equal(-98d, parameters.Dy); + Assert.Equal(-121d, parameters.Dz); + } + + /// + /// Verifies that datums created through the public factory store the supplied values. + /// + [Fact] + public void FactoryCreateHorizontalDatum_SetsProperties() + { + Wgs84ConversionInfo parameters = new(1d, 2d, 3d, 4d, 5d, 6d, 7d); + HorizontalDatum datum = CreateDatum("Custom datum", DatumType.HD_Classic, Ellipsoid.Clarke1866, parameters); + + Assert.Equal("Custom datum", datum.Name); + Assert.Equal(string.Empty, datum.Authority); + Assert.Equal(-1, datum.AuthorityCode); + Assert.Equal(DatumType.HD_Classic, datum.DatumType); + Assert.True(datum.Ellipsoid.EqualParams(Ellipsoid.Clarke1866)); + Assert.Same(parameters, datum.Wgs84Parameters); + } + + /// + /// Verifies that clones the datum with replacement Bursa-Wolf parameters. + /// + [Fact] + public void WithWgs84Parameters_ReturnsCloneWithUpdatedParameters() + { + HorizontalDatum original = HorizontalDatum.WGS84; + Wgs84ConversionInfo replacement = new(1d, 2d, 3d, 4d, 5d, 6d, 7d); + HorizontalDatum clone = original.WithWgs84Parameters(replacement); + Wgs84ConversionInfo cloneParameters = Assert.IsType(clone.Wgs84Parameters); + + Assert.NotSame(original, clone); + Assert.Null(original.Wgs84Parameters); + Assert.NotSame(replacement, cloneParameters); + Assert.Equal(replacement, cloneParameters); + Assert.NotSame(original.Ellipsoid, clone.Ellipsoid); + Assert.True(original.Ellipsoid.EqualParams(clone.Ellipsoid)); + } + + /// + /// Verifies that can clear existing Bursa-Wolf parameters. + /// + [Fact] + public void WithWgs84Parameters_WithNull_ClearsParameters() + { + HorizontalDatum original = HorizontalDatum.ED50; + HorizontalDatum clone = original.WithWgs84Parameters(null); + + Assert.NotSame(original, clone); + Assert.NotNull(original.Wgs84Parameters); + Assert.Null(clone.Wgs84Parameters); + Assert.True(original.Ellipsoid.EqualParams(clone.Ellipsoid)); + } + + /// + /// Verifies that clones horizontal datums with replacement ensemble metadata. + /// + [Fact] + public void WithEnsemble_ReturnsCloneWithUpdatedEnsemble() + { + HorizontalDatum original = HorizontalDatum.WGS84; + DatumEnsemble ensemble = new( + "World Geodetic System 1984 ensemble", + [ + new DatumEnsembleMember("World Geodetic System 1984 (Transit)", "EPSG", 1166), + new DatumEnsembleMember("World Geodetic System 1984 (G730)", "EPSG", 1152), + ], + 2d, + original.Ellipsoid, + "EPSG", + 6326); + HorizontalDatum clone = Assert.IsType(original.WithEnsemble(ensemble)); + DatumEnsemble cloneEnsemble = Assert.IsType(clone.Ensemble); + + Assert.NotSame(original, clone); + Assert.Null(original.Ensemble); + Assert.NotSame(ensemble, cloneEnsemble); + Assert.Equal(ensemble, cloneEnsemble); + Assert.NotSame(original.Ellipsoid, clone.Ellipsoid); + Assert.Same(clone.Ellipsoid, cloneEnsemble.Ellipsoid); + } + + /// + /// Verifies that WKT omits both optional clauses when neither WGS84 parameters nor authority metadata are present. + /// + [Fact] + public void WKT_WithoutAuthorityAndWithoutWgs84_OmitsOptionalClauses() + { + HorizontalDatum datum = CreateDatum("Custom datum", DatumType.HD_Classic, Ellipsoid.GRS80, null); + + Assert.Equal($"DATUM[\"Custom datum\", {Ellipsoid.GRS80.WKT}]", datum.WKT); + } + + /// + /// Verifies that WKT includes Bursa-Wolf parameters but omits authority when only WGS84 parameters are present. + /// + [Fact] + public void WKT_WithoutAuthorityAndWithWgs84_IncludesTowgs84Only() + { + HorizontalDatum datum = CreateDatum("Custom datum", DatumType.HD_Classic, Ellipsoid.GRS80, new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d)); + + Assert.Equal($"DATUM[\"Custom datum\", {Ellipsoid.GRS80.WKT}, TOWGS84[1, 2, 3, 4, 5, 6, 7]]", datum.WKT); + } + + /// + /// Verifies that WKT includes authority when it is available but omits WGS84 parameters when absent. + /// + [Fact] + public void WKT_WithAuthorityAndWithoutWgs84_IncludesAuthorityOnly() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + + Assert.DoesNotContain("TOWGS84", datum.WKT, System.StringComparison.Ordinal); + Assert.Contains("AUTHORITY[\"EPSG\", \"6326\"]", datum.WKT, System.StringComparison.Ordinal); + } + + /// + /// Verifies that WKT includes both Bursa-Wolf parameters and authority metadata when both are available. + /// + [Fact] + public void WKT_WithAuthorityAndWithWgs84_IncludesAllOptionalClauses() + { + HorizontalDatum datum = HorizontalDatum.ED50; + + Assert.Contains("TOWGS84[-87, -98, -121, 0, 0, 0, 0]", datum.WKT, System.StringComparison.Ordinal); + Assert.Contains("AUTHORITY[\"EPSG\", \"6230\"]", datum.WKT, System.StringComparison.Ordinal); + } + + /// + /// Verifies that XML omits WGS84 conversion information when the datum has no Bursa-Wolf parameters. + /// + [Fact] + public void XML_WithoutWgs84_OmitsConversionElement() + { + HorizontalDatum datum = CreateDatum("Custom datum", DatumType.HD_Classic, Ellipsoid.GRS80, null); + var xml = XElement.Parse(datum.XML); + + Assert.Equal("CS_HorizontalDatum", xml.Name.LocalName); + Assert.Equal("1001", (string?)xml.Attribute("DatumType")); + Assert.NotNull(xml.Element("CS_Info")); + Assert.NotNull(xml.Element("CS_Ellipsoid")); + Assert.Null(xml.Element("CS_WGS84ConversionInfo")); + } + + /// + /// Verifies that XML includes WGS84 conversion information when Bursa-Wolf parameters are present. + /// + [Fact] + public void XML_WithWgs84_IncludesConversionElement() + { + HorizontalDatum datum = HorizontalDatum.ED50; + var xml = XElement.Parse(datum.XML); + + Assert.Equal("CS_HorizontalDatum", xml.Name.LocalName); + Assert.Equal("1002", (string?)xml.Attribute("DatumType")); + Assert.NotNull(xml.Element("CS_WGS84ConversionInfo")); + } + + /// + /// Verifies that matches the XML property when no WGS84 parameters are present. + /// + [Fact] + public void ToXml_WithoutWgs84_MatchesXmlProperty() + { + HorizontalDatum datum = CreateDatum("Custom datum", DatumType.HD_Classic, Ellipsoid.GRS80, null); + XElement xml = datum.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(datum.XML), xml)); + } + + /// + /// Verifies that matches the XML property when WGS84 parameters are present. + /// + [Fact] + public void ToXml_WithWgs84_MatchesXmlProperty() + { + HorizontalDatum datum = HorizontalDatum.ED50; + XElement xml = datum.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(datum.XML), xml)); + } + + /// + /// Verifies that the WKT node contains only the name and ellipsoid when no optional clauses are present. + /// + [Fact] + public void ToWktNode_WithoutAuthorityAndWithoutWgs84_ReturnsNameAndEllipsoidOnly() + { + HorizontalDatum datum = CreateDatum("Custom datum", DatumType.HD_Classic, Ellipsoid.GRS80, null); + WktKeywordNode node = Assert.IsType(datum.ToWktNode()); + + Assert.Equal("DATUM", node.Keyword); + Assert.Equal(2, node.Children.Count); + Assert.IsType(node.Children[0]); + Assert.Equal("SPHEROID", Assert.IsType(node.Children[1]).Keyword); + } + + /// + /// Verifies that the WKT node includes Bursa-Wolf parameters but no authority when only WGS84 parameters are present. + /// + [Fact] + public void ToWktNode_WithoutAuthorityAndWithWgs84_OmitsAuthorityNode() + { + HorizontalDatum datum = CreateDatum("Custom datum", DatumType.HD_Classic, Ellipsoid.GRS80, new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d)); + WktKeywordNode node = Assert.IsType(datum.ToWktNode()); + + Assert.Equal(3, node.Children.Count); + Assert.Equal("TOWGS84", Assert.IsType(node.Children[2]).Keyword); + } + + /// + /// Verifies that the WKT node includes authority when the datum has authority metadata but no WGS84 parameters. + /// + [Fact] + public void ToWktNode_WithAuthorityAndWithoutWgs84_IncludesAuthorityNode() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + WktKeywordNode node = Assert.IsType(datum.ToWktNode()); + + Assert.Equal(3, node.Children.Count); + Assert.Equal("AUTHORITY", Assert.IsType(node.Children[2]).Keyword); + } + + /// + /// Verifies that the WKT node includes both Bursa-Wolf parameters and authority metadata when both are present. + /// + [Fact] + public void ToWktNode_WithAuthorityAndWithWgs84_IncludesTowgs84AndAuthorityNodes() + { + HorizontalDatum datum = HorizontalDatum.ED50; + WktKeywordNode node = Assert.IsType(datum.ToWktNode()); + + Assert.Equal(4, node.Children.Count); + Assert.Equal("TOWGS84", Assert.IsType(node.Children[2]).Keyword); + Assert.Equal("AUTHORITY", Assert.IsType(node.Children[3]).Keyword); + } + + /// + /// Verifies that equal custom datums compare equal when ellipsoid, datum type, and WGS84 parameters all match. + /// + [Fact] + public void EqualParams_SameValuesWithoutWgs84_ReturnsTrue() + { + HorizontalDatum first = CreateDatum("A", DatumType.HD_Classic, Ellipsoid.GRS80, null); + HorizontalDatum second = CreateDatum("B", DatumType.HD_Classic, Ellipsoid.GRS80, null); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that equal datums with Bursa-Wolf parameters compare equal. + /// + [Fact] + public void EqualParams_SameValuesWithWgs84_ReturnsTrue() + { + HorizontalDatum first = CreateDatum("A", DatumType.HD_Classic, Ellipsoid.GRS80, new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d)); + HorizontalDatum second = CreateDatum("B", DatumType.HD_Classic, Ellipsoid.GRS80, new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d)); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that a datum with Bursa-Wolf parameters does not compare equal to one without them. + /// + [Fact] + public void EqualParams_OneHasWgs84Parameters_ReturnsFalse() + { + HorizontalDatum first = CreateDatum("A", DatumType.HD_Classic, Ellipsoid.GRS80, null); + HorizontalDatum second = CreateDatum("B", DatumType.HD_Classic, Ellipsoid.GRS80, new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d)); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different Bursa-Wolf parameters compare unequal. + /// + [Fact] + public void EqualParams_DifferentWgs84Parameters_ReturnsFalse() + { + HorizontalDatum first = CreateDatum("A", DatumType.HD_Classic, Ellipsoid.GRS80, new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d)); + HorizontalDatum second = CreateDatum("B", DatumType.HD_Classic, Ellipsoid.GRS80, new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 8d)); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different ellipsoids compare unequal. + /// + [Fact] + public void EqualParams_DifferentEllipsoid_ReturnsFalse() + { + HorizontalDatum first = CreateDatum("A", DatumType.HD_Classic, Ellipsoid.GRS80, null); + HorizontalDatum second = CreateDatum("B", DatumType.HD_Classic, Ellipsoid.Clarke1866, null); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that datums compare equal when both ellipsoids are absent and the remaining parameters match. + /// + [Fact] + public void EqualParams_BothEllipsoidsNull_ReturnsTrue() + { + HorizontalDatum first = new(null!, null, DatumType.HD_Classic, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + HorizontalDatum second = new(null!, null, DatumType.HD_Classic, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that datums compare unequal when only the left ellipsoid is absent. + /// + [Fact] + public void EqualParams_LeftEllipsoidNull_ReturnsFalse() + { + HorizontalDatum first = new(null!, null, DatumType.HD_Classic, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + HorizontalDatum second = CreateDatum("B", DatumType.HD_Classic, Ellipsoid.GRS80, null); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that datums compare unequal when only the right ellipsoid is absent. + /// + [Fact] + public void EqualParams_RightEllipsoidNull_ReturnsFalse() + { + HorizontalDatum first = CreateDatum("A", DatumType.HD_Classic, Ellipsoid.GRS80, null); + HorizontalDatum second = new(null!, null, DatumType.HD_Classic, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different datum types compare unequal. + /// + [Fact] + public void EqualParams_DifferentDatumType_ReturnsFalse() + { + HorizontalDatum first = CreateDatum("A", DatumType.HD_Classic, Ellipsoid.GRS80, null); + HorizontalDatum second = CreateDatum("B", DatumType.HD_Geocentric, Ellipsoid.GRS80, null); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different object types compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(HorizontalDatum.WGS84.EqualParams("not a datum")); + } + + private static HorizontalDatum CreateDatum( + string name, + DatumType datumType, + Ellipsoid ellipsoid, + Wgs84ConversionInfo? toWgs84) + { + return new CoordinateSystemFactory().CreateHorizontalDatum(name, datumType, ellipsoid, toWgs84); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/InfoTests.cs b/test/ProjNet.Tests/CoordinateSystems/InfoTests.cs new file mode 100644 index 00000000..3ac32167 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/InfoTests.cs @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for . +/// +public class InfoTests +{ + /// + /// Verifies that the constructor stores the supplied metadata and returns WKT. + /// + [Fact] + public void Constructor_SetsMetadataAndToStringReturnsWkt() + { + var info = new TestInfo( + "WGS 84", + "EPSG", + 4326, + "alias", + "abbr", + "remarks", + "GEOGCS[\"WGS 84\"]", + ""); + + Assert.Equal("WGS 84", info.Name); + Assert.Equal("EPSG", info.Authority); + Assert.Equal(4326, info.AuthorityCode); + Assert.Equal("alias", info.Alias); + Assert.Equal("abbr", info.Abbreviation); + Assert.Equal("remarks", info.Remarks); + Assert.Equal("GEOGCS[\"WGS 84\"]", info.ToString()); + } + + /// + /// Verifies that returns a new instance with updated authority metadata. + /// + [Fact] + public void WithAuthority_ReturnsCloneWithUpdatedAuthorityMetadata() + { + LinearUnit original = LinearUnit.Metre; + LinearUnit clone = original.WithAuthority("TEST", 1234); + + Assert.Equal("TEST", clone.Authority); + Assert.Equal(1234, clone.AuthorityCode); + Assert.Equal(original.Name, clone.Name); + Assert.Equal("EPSG", original.Authority); + Assert.Equal(9001, original.AuthorityCode); + Assert.NotSame(original, clone); + } + + /// + /// Verifies that returns a new instance with updated naming metadata. + /// + [Fact] + public void WithName_ReturnsCloneWithUpdatedName() + { + LinearUnit original = LinearUnit.Metre; + LinearUnit clone = original.WithName("Meter"); + + Assert.Equal("Meter", clone.Name); + Assert.Equal("metre", original.Name); + Assert.Equal(original.Authority, clone.Authority); + Assert.Equal(original.AuthorityCode, clone.AuthorityCode); + Assert.NotSame(original, clone); + } + + /// + /// Verifies that the typed WithAuthority overloads on unit-like types and ellipsoid/prime-meridian types return concrete clones without casts. + /// + [Fact] + public void TypedWithAuthority_ReturnsConcreteClonesWithoutCasts() + { + AngularUnit angularClone = AngularUnit.Degrees.WithAuthority("TEST", 1001); + LinearUnit linearClone = LinearUnit.Metre.WithAuthority("TEST", 1002); + ParametricUnit parametricClone = new ParametricUnit(0.1d, "pressure", "EPSG", 1024, string.Empty, string.Empty, string.Empty).WithAuthority("TEST", 1003); + TimeUnit timeClone = new TimeUnit(1d, "second", "EPSG", 1040, string.Empty, string.Empty, string.Empty).WithAuthority("TEST", 1004); + Ellipsoid ellipsoidClone = Ellipsoid.WGS84.WithAuthority("TEST", 1005); + PrimeMeridian primeMeridianClone = PrimeMeridian.Greenwich.WithAuthority("TEST", 1006); + + Assert.Equal("TEST", angularClone.Authority); + Assert.Equal(1002, linearClone.AuthorityCode); + Assert.Equal("TEST", parametricClone.Authority); + Assert.Equal(1004, timeClone.AuthorityCode); + Assert.Equal("TEST", ellipsoidClone.Authority); + Assert.Equal(1006, primeMeridianClone.AuthorityCode); + } + + /// + /// Verifies that the typed WithName overloads on unit-like types and ellipsoid/prime-meridian types return concrete clones without casts. + /// + [Fact] + public void TypedWithName_ReturnsConcreteClonesWithoutCasts() + { + AngularUnit angularClone = AngularUnit.Degrees.WithName("Degree"); + LinearUnit linearClone = LinearUnit.Metre.WithName("Meter"); + ParametricUnit parametricClone = new ParametricUnit(0.1d, "pressure", "EPSG", 1024, string.Empty, string.Empty, string.Empty).WithName("Pressure unit"); + TimeUnit timeClone = new TimeUnit(1d, "second", "EPSG", 1040, string.Empty, string.Empty, string.Empty).WithName("Second unit"); + Ellipsoid ellipsoidClone = Ellipsoid.WGS84.WithName("Custom WGS 84"); + PrimeMeridian primeMeridianClone = PrimeMeridian.Greenwich.WithName("Custom Greenwich"); + + Assert.Equal("Degree", angularClone.Name); + Assert.Equal("Meter", linearClone.Name); + Assert.Equal("Pressure unit", parametricClone.Name); + Assert.Equal("Second unit", timeClone.Name); + Assert.Equal("Custom WGS 84", ellipsoidClone.Name); + Assert.Equal("Custom Greenwich", primeMeridianClone.Name); + } + + /// + /// Verifies that the typed datum WithAuthority overloads return concrete clones without casts. + /// + [Fact] + public void TypedDatumWithAuthority_ReturnsConcreteClonesWithoutCasts() + { + HorizontalDatum horizontalClone = HorizontalDatum.WGS84.WithAuthority("TEST", 2001); + VerticalDatum verticalClone = VerticalDatum.ODN.WithAuthority("TEST", 2002); + EngineeringDatum engineeringClone = new EngineeringDatum("Engineering datum", "EPSG", 9300, string.Empty, string.Empty, string.Empty).WithAuthority("TEST", 2003); + ParametricDatum parametricClone = new ParametricDatum("Parametric datum", "EPSG", 9301, string.Empty, string.Empty, string.Empty).WithAuthority("TEST", 2004); + TemporalDatum temporalClone = new TemporalDatum("2024-01-01T00:00:00Z", "Temporal datum", "EPSG", 9302, string.Empty, string.Empty, string.Empty).WithAuthority("TEST", 2005); + + Assert.Equal("TEST", horizontalClone.Authority); + Assert.Equal(2002, verticalClone.AuthorityCode); + Assert.Equal("TEST", engineeringClone.Authority); + Assert.Equal(2004, parametricClone.AuthorityCode); + Assert.Equal("TEST", temporalClone.Authority); + } + + /// + /// Verifies that the typed datum WithName overloads return concrete clones without casts. + /// + [Fact] + public void TypedDatumWithName_ReturnsConcreteClonesWithoutCasts() + { + HorizontalDatum horizontalClone = HorizontalDatum.WGS84.WithName("Horizontal datum"); + VerticalDatum verticalClone = VerticalDatum.ODN.WithName("Vertical datum"); + EngineeringDatum engineeringClone = new EngineeringDatum("Engineering datum", "EPSG", 9300, string.Empty, string.Empty, string.Empty).WithName("Engineering datum clone"); + ParametricDatum parametricClone = new ParametricDatum("Parametric datum", "EPSG", 9301, string.Empty, string.Empty, string.Empty).WithName("Parametric datum clone"); + TemporalDatum temporalClone = new TemporalDatum("2024-01-01T00:00:00Z", "Temporal datum", "EPSG", 9302, string.Empty, string.Empty, string.Empty).WithName("Temporal datum clone"); + + Assert.Equal("Horizontal datum", horizontalClone.Name); + Assert.Equal("Vertical datum", verticalClone.Name); + Assert.Equal("Engineering datum clone", engineeringClone.Name); + Assert.Equal("Parametric datum clone", parametricClone.Name); + Assert.Equal("Temporal datum clone", temporalClone.Name); + } + + /// + /// Verifies that the typed datum WithEnsemble overloads return concrete clones without casts when ensembles are supported. + /// + [Fact] + public void TypedDatumWithEnsemble_ReturnsConcreteClonesForSupportedDatums() + { + DatumEnsemble horizontalEnsemble = CreateTestEnsemble("Horizontal ensemble", HorizontalDatum.WGS84.Ellipsoid); + DatumEnsemble verticalEnsemble = CreateTestEnsemble("Vertical ensemble"); + + HorizontalDatum horizontalClone = HorizontalDatum.WGS84.WithEnsemble(horizontalEnsemble); + VerticalDatum verticalClone = VerticalDatum.ODN.WithEnsemble(verticalEnsemble); + + Assert.Equal("Horizontal ensemble", Assert.IsType(horizontalClone.Ensemble).Name); + Assert.Equal("Vertical ensemble", Assert.IsType(verticalClone.Ensemble).Name); + } + + /// + /// Verifies that the typed datum WithEnsemble overloads keep unsupported datum types typed and reject non-null ensemble metadata. + /// + [Fact] + public void TypedDatumWithEnsemble_OnUnsupportedDatumsRejectsNonNullMetadata() + { + DatumEnsemble ensemble = CreateTestEnsemble("Unsupported ensemble"); + + var engineeringDatum = new EngineeringDatum("Engineering datum", "EPSG", 9300, string.Empty, string.Empty, string.Empty); + var parametricDatum = new ParametricDatum("Parametric datum", "EPSG", 9301, string.Empty, string.Empty, string.Empty); + var temporalDatum = new TemporalDatum("2024-01-01T00:00:00Z", "Temporal datum", "EPSG", 9302, string.Empty, string.Empty, string.Empty); + + EngineeringDatum engineeringClone = engineeringDatum.WithEnsemble(null); + ParametricDatum parametricClone = parametricDatum.WithEnsemble(null); + TemporalDatum temporalClone = temporalDatum.WithEnsemble(null); + + Assert.NotSame(engineeringDatum, engineeringClone); + Assert.NotSame(parametricDatum, parametricClone); + Assert.NotSame(temporalDatum, temporalClone); + + Assert.Throws(() => engineeringDatum.WithEnsemble(ensemble)); + Assert.Throws(() => parametricDatum.WithEnsemble(ensemble)); + Assert.Throws(() => temporalDatum.WithEnsemble(ensemble)); + } + + /// + /// Verifies that the typed coordinate-system WithAuthority overloads return concrete clones without casts. + /// + [Fact] + public void TypedCoordinateSystemWithAuthority_ReturnsConcreteClonesWithoutCasts() + { + GeographicCoordinateSystem geographicClone = GeographicCoordinateSystem.WGS84.WithAuthority("TEST", 3001); + ProjectedCoordinateSystem projectedClone = ProjectedCoordinateSystem.WebMercator.WithAuthority("TEST", 3002); + GeocentricCoordinateSystem geocentricClone = GeocentricCoordinateSystem.WGS84.WithAuthority("TEST", 3003); + VerticalCoordinateSystem verticalClone = VerticalCoordinateSystem.ODN.WithAuthority("TEST", 3004); + CompoundCoordinateSystem compoundClone = CreateTestCompoundCoordinateSystem().WithAuthority("TEST", 3005); + BoundCoordinateSystem boundClone = CreateTestBoundCoordinateSystem().WithAuthority("TEST", 3006); + FittedCoordinateSystem fittedClone = CreateTestFittedCoordinateSystem().WithAuthority("TEST", 3007); + EngineeringCoordinateSystem engineeringClone = CreateTestEngineeringCoordinateSystem().WithAuthority("TEST", 3008); + ParametricCoordinateSystem parametricClone = CreateTestParametricCoordinateSystem().WithAuthority("TEST", 3009); + TemporalCoordinateSystem temporalClone = CreateTestTemporalCoordinateSystem().WithAuthority("TEST", 3010); + + Assert.Equal("TEST", geographicClone.Authority); + Assert.Equal(3002, projectedClone.AuthorityCode); + Assert.Equal("TEST", geocentricClone.Authority); + Assert.Equal(3004, verticalClone.AuthorityCode); + Assert.Equal("TEST", compoundClone.Authority); + Assert.Equal(3006, boundClone.AuthorityCode); + Assert.Equal("TEST", fittedClone.Authority); + Assert.Equal(3008, engineeringClone.AuthorityCode); + Assert.Equal("TEST", parametricClone.Authority); + Assert.Equal(3010, temporalClone.AuthorityCode); + } + + /// + /// Verifies that the typed coordinate-system WithName overloads return concrete clones without casts. + /// + [Fact] + public void TypedCoordinateSystemWithName_ReturnsConcreteClonesWithoutCasts() + { + GeographicCoordinateSystem geographicClone = GeographicCoordinateSystem.WGS84.WithName("Geographic clone"); + ProjectedCoordinateSystem projectedClone = ProjectedCoordinateSystem.WebMercator.WithName("Projected clone"); + GeocentricCoordinateSystem geocentricClone = GeocentricCoordinateSystem.WGS84.WithName("Geocentric clone"); + VerticalCoordinateSystem verticalClone = VerticalCoordinateSystem.ODN.WithName("Vertical clone"); + CompoundCoordinateSystem compoundClone = CreateTestCompoundCoordinateSystem().WithName("Compound clone"); + BoundCoordinateSystem boundClone = CreateTestBoundCoordinateSystem().WithName("Bound clone"); + FittedCoordinateSystem fittedClone = CreateTestFittedCoordinateSystem().WithName("Fitted clone"); + EngineeringCoordinateSystem engineeringClone = CreateTestEngineeringCoordinateSystem().WithName("Engineering clone"); + ParametricCoordinateSystem parametricClone = CreateTestParametricCoordinateSystem().WithName("Parametric clone"); + TemporalCoordinateSystem temporalClone = CreateTestTemporalCoordinateSystem().WithName("Temporal clone"); + + Assert.Equal("Geographic clone", geographicClone.Name); + Assert.Equal("Projected clone", projectedClone.Name); + Assert.Equal("Geocentric clone", geocentricClone.Name); + Assert.Equal("Vertical clone", verticalClone.Name); + Assert.Equal("Compound clone", compoundClone.Name); + Assert.Equal("Bound clone", boundClone.Name); + Assert.Equal("Fitted clone", fittedClone.Name); + Assert.Equal("Engineering clone", engineeringClone.Name); + Assert.Equal("Parametric clone", parametricClone.Name); + Assert.Equal("Temporal clone", temporalClone.Name); + } + + /// + /// Verifies that the typed operation-model WithAuthority overloads return concrete clones without casts. + /// + [Fact] + public void TypedOperationWithAuthority_ReturnsConcreteClonesWithoutCasts() + { + Projection projectionClone = CreateTestProjection().WithAuthority("TEST", 4001); + CoordinateOperation coordinateOperationClone = CreateTestCoordinateOperation().WithAuthority("TEST", 4002); + ConcatenatedOperation concatenatedOperationClone = CreateTestConcatenatedOperation().WithAuthority("TEST", 4003); + + Assert.Equal("TEST", projectionClone.Authority); + Assert.Equal(4002, coordinateOperationClone.AuthorityCode); + Assert.Equal("TEST", concatenatedOperationClone.Authority); + } + + /// + /// Verifies that the typed operation-model WithName overloads return concrete clones without casts. + /// + [Fact] + public void TypedOperationWithName_ReturnsConcreteClonesWithoutCasts() + { + Projection projectionClone = CreateTestProjection().WithName("Projection clone"); + CoordinateOperation coordinateOperationClone = CreateTestCoordinateOperation().WithName("Coordinate operation clone"); + ConcatenatedOperation concatenatedOperationClone = CreateTestConcatenatedOperation().WithName("Concatenated operation clone"); + + Assert.Equal("Projection clone", projectionClone.Name); + Assert.Equal("Coordinate operation clone", coordinateOperationClone.Name); + Assert.Equal("Concatenated operation clone", concatenatedOperationClone.Name); + } + + /// + /// Verifies that base-typed callers dispatch to the concrete clone path. + /// + [Fact] + public void InfoWithAuthority_OnBaseTypedReference_UsesVirtualDispatch() + { + Info info = CreateTestBoundCoordinateSystem(); + + Info clone = info.WithAuthority("TEST", 5001); + + BoundCoordinateSystem typedClone = Assert.IsType(clone); + Assert.Equal("TEST", typedClone.Authority); + Assert.Equal(5001, typedClone.AuthorityCode); + } + + /// + /// Verifies that base-typed callers dispatch to the concrete clone path. + /// + [Fact] + public void InfoWithName_OnBaseTypedReference_UsesVirtualDispatch() + { + Info info = CreateTestConcatenatedOperation(); + + Info clone = info.WithName("Fallback clone"); + + Assert.Equal("Fallback clone", Assert.IsType(clone).Name); + } + + /// + /// Verifies that generic instances remain supported through the base-typed clone path. + /// + [Fact] + public void InfoWithAuthority_OnGenericUnit_UsesCloneCoreDispatch() + { + Info info = new Unit("unity", 1d); + + Info clone = info.WithAuthority("TEST", 6001); + + Unit typedClone = Assert.IsType(clone); + Assert.Equal("TEST", typedClone.Authority); + Assert.Equal(6001, typedClone.AuthorityCode); + } + + /// + /// Verifies that generic instances remain supported through the base-typed name clone path. + /// + [Fact] + public void InfoWithName_OnGenericUnit_UsesCloneCoreDispatch() + { + Info info = new Unit("unity", 1d); + + Info clone = info.WithName("custom unity"); + + Assert.Equal("custom unity", Assert.IsType(clone).Name); + } + + /// + /// Verifies that the XElement-based info XML string includes the supported metadata attributes in the expected order. + /// + [Fact] + public void InfoXmlElementString_WithMetadata_IncludesExpectedAttributes() + { + var info = new TestInfo( + "WGS 84", + "EPSG", + 4326, + "alias", + "abbr", + "remarks", + "WKT", + "XML"); + + Assert.Equal( + "", + info.InfoXmlElement.ToString(SaveOptions.DisableFormatting)); + } + + /// + /// Verifies that the XElement-based info XML string omits optional attributes when the values are blank or not positive. + /// + [Fact] + public void InfoXmlElementString_WithBlankMetadata_OmitsOptionalAttributes() + { + var info = new TestInfo( + " ", + "\t", + 0, + "alias", + string.Empty, + "remarks", + "WKT", + "XML"); + + Assert.Equal("", info.InfoXmlElement.ToString(SaveOptions.DisableFormatting)); + } + + /// + /// Verifies that includes the expected attributes and values. + /// + [Fact] + public void InfoXmlElement_WithMetadata_IncludesExpectedAttributes() + { + var info = new TestInfo( + "WGS 84", + "EPSG", + 4326, + "alias", + "abbr", + "remarks", + "WKT", + "XML"); + + XElement xml = info.InfoXmlElement; + + Assert.Equal("CS_Info", xml.Name.LocalName); + Assert.Equal("4326", (string?)xml.Attribute("AuthorityCode")); + Assert.Equal("abbr", (string?)xml.Attribute("Abbreviation")); + Assert.Equal("EPSG", (string?)xml.Attribute("Authority")); + Assert.Equal("WGS 84", (string?)xml.Attribute("Name")); + } + + /// + /// Verifies that omits optional attributes when the values are blank or not positive. + /// + [Fact] + public void InfoXmlElement_WithBlankMetadata_OmitsOptionalAttributes() + { + var info = new TestInfo( + " ", + "\t", + -1, + "alias", + string.Empty, + "remarks", + "WKT", + "XML"); + + XElement xml = info.InfoXmlElement; + + Assert.Empty(xml.Attributes()); + } + + private static DatumEnsemble CreateTestEnsemble(string name, Ellipsoid? ellipsoid = null) + { + return new DatumEnsemble( + name, + [ + new DatumEnsembleMember("Member A"), + new DatumEnsembleMember("Member B"), + ], + 0.25d, + ellipsoid, + "TEST", + 1); + } + + private static CompoundCoordinateSystem CreateTestCompoundCoordinateSystem() + { + return new CompoundCoordinateSystem( + GeographicCoordinateSystem.WGS84, + VerticalCoordinateSystem.ODN, + "Custom compound", + "EPSG", + 9900, + string.Empty, + string.Empty, + string.Empty); + } + + private static BoundCoordinateSystem CreateTestBoundCoordinateSystem() + { + return new BoundCoordinateSystem( + CreateTestGeographicCoordinateSystem("Source"), + GeographicCoordinateSystem.WGS84, + new BoundTransformation("Geocentric translations", new Wgs84ConversionInfo(1, 2, 3, 0, 0, 0, 0)), + "Bound source", + "EPSG", + 9901, + string.Empty, + string.Empty, + string.Empty); + } + + private static FittedCoordinateSystem CreateTestFittedCoordinateSystem() + { + return new FittedCoordinateSystem( + CreateTestGeographicCoordinateSystem("Base geographic"), + new AffineTransform(1, 0, 10, 0, 1, 20), + "Custom fitted", + "EPSG", + 9902, + string.Empty, + string.Empty, + string.Empty); + } + + private static EngineeringCoordinateSystem CreateTestEngineeringCoordinateSystem() + { + return new EngineeringCoordinateSystem( + new EngineeringDatum("Local plant", "EPSG", 1098, string.Empty, string.Empty, string.Empty), + "Cartesian", + [new AxisInfo("x", AxisOrientationEnum.East), new AxisInfo("y", AxisOrientationEnum.North)], + [LinearUnit.Metre, LinearUnit.Metre], + "Plant grid", + "EPSG", + 5800, + string.Empty, + string.Empty, + string.Empty); + } + + private static ParametricCoordinateSystem CreateTestParametricCoordinateSystem() + { + return new ParametricCoordinateSystem( + new ParametricUnit(0.1d, "pressure", "EPSG", 0, string.Empty, string.Empty, string.Empty), + new ParametricDatum("Reservoir datum", "EPSG", 0, string.Empty, string.Empty, string.Empty), + new AxisInfo("pressure", AxisOrientationEnum.Up), + "Reservoir pressure", + "EPSG", + 0, + string.Empty, + string.Empty, + string.Empty); + } + + private static TemporalCoordinateSystem CreateTestTemporalCoordinateSystem() + { + return new TemporalCoordinateSystem( + new TimeUnit(1d, "second", "EPSG", 1040, string.Empty, string.Empty, string.Empty), + new TemporalDatum("1950-01-01T00:00:00Z", "Unix epoch", "EPSG", 1040, string.Empty, string.Empty, string.Empty), + new AxisInfo("time", AxisOrientationEnum.Other), + "Temporal axis", + "EPSG", + 1041, + string.Empty, + string.Empty, + string.Empty); + } + + private static Projection CreateTestProjection() + { + return new Projection( + "Transverse_Mercator", + [new ProjectionParameter("latitude_of_origin", 0d)], + "Transverse Mercator", + "EPSG", + 9807, + string.Empty, + string.Empty, + string.Empty); + } + + private static CoordinateOperation CreateTestCoordinateOperation(string name = "Test operation") + { + return new CoordinateOperation( + "Axis order reversal", + [new Parameter("Order", 1d)], + CreateTestGeographicCoordinateSystem("Operation source"), + CreateTestGeographicCoordinateSystem("Operation target"), + name, + "EPSG", + 9603, + string.Empty, + string.Empty, + string.Empty); + } + + private static ConcatenatedOperation CreateTestConcatenatedOperation() + { + return new ConcatenatedOperation( + [CreateTestCoordinateOperation("Step 1"), CreateTestCoordinateOperation("Step 2")], + CreateTestGeographicCoordinateSystem("Concatenated source"), + CreateTestGeographicCoordinateSystem("Concatenated target"), + "Test concatenated operation", + "EPSG", + 9610, + string.Empty, + string.Empty, + string.Empty); + } + + private static GeographicCoordinateSystem CreateTestGeographicCoordinateSystem(string name) + { + return new GeographicCoordinateSystem( + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + [new AxisInfo("Lon", AxisOrientationEnum.East), new AxisInfo("Lat", AxisOrientationEnum.North)], + name, + "EPSG", + 4326, + string.Empty, + string.Empty, + string.Empty); + } + + private sealed class TestInfo : Info + { + private readonly string wkt; + private readonly string xml; + + internal TestInfo( + string name, + string authority, + long code, + string alias, + string abbreviation, + string remarks, + string wkt, + string xml) + : base(name, authority, code, alias, abbreviation, remarks) + { + this.wkt = wkt; + this.xml = xml; + } + + public override string WKT => this.wkt; + + public override string XML => this.xml; + + public override bool EqualParams(object obj) => ReferenceEquals(this, obj); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/ParameterInfoTests.cs b/test/ProjNet.Tests/CoordinateSystems/ParameterInfoTests.cs new file mode 100644 index 00000000..f08ed646 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/ParameterInfoTests.cs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Tests for . +/// +public class ParameterInfoTests +{ + /// + /// Verifies that returns zero when the parameter list is unset. + /// + [Fact] + public void NumParameters_WithNullParameters_ReturnsZero() + { + var parameterInfo = new ParameterInfo(); + + Assert.Equal(0, parameterInfo.NumParameters); + } + + /// + /// Verifies that returns the current parameter count. + /// + [Fact] + public void NumParameters_WithParameters_ReturnsCount() + { + var parameterInfo = new ParameterInfo + { + Parameters = + [ + new Parameter("scale_factor", 0.9996), + new Parameter("central_meridian", 9.0), + ], + }; + + Assert.Equal(2, parameterInfo.NumParameters); + } + + /// + /// Verifies that returns an empty array. + /// + [Fact] + public void DefaultParameters_ReturnsEmptyArray() + { + var parameterInfo = new ParameterInfo(); + + Parameter[] parameters = parameterInfo.DefaultParameters(); + + Assert.Empty(parameters); + } + + /// + /// Verifies that returns when no parameters are available. + /// + [Fact] + public void GetParameterByName_WithNullParameterList_ReturnsNull() + { + var parameterInfo = new ParameterInfo(); + + Parameter? parameter = parameterInfo.GetParameterByName("scale_factor"); + + Assert.Null(parameter); + } + + /// + /// Verifies that returns the matching parameter instance. + /// + [Fact] + public void GetParameterByName_WithMatchingName_ReturnsParameter() + { + var expected = new Parameter("central_meridian", 15.0); + var parameterInfo = new ParameterInfo + { + Parameters = + [ + new Parameter("scale_factor", 1.0), + expected, + ], + }; + + Parameter? parameter = parameterInfo.GetParameterByName("central_meridian"); + + Assert.Same(expected, parameter); + } + + /// + /// Verifies that skips null entries and returns when the name is missing. + /// + [Fact] + public void GetParameterByName_WithNullEntryAndMissingName_ReturnsNull() + { + var parameterInfo = new ParameterInfo + { + Parameters = + [ + null!, + new Parameter("scale_factor", 1.0), + ], + }; + + Parameter? parameter = parameterInfo.GetParameterByName("latitude_of_origin"); + + Assert.Null(parameter); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/ParameterTests.cs b/test/ProjNet.Tests/CoordinateSystems/ParameterTests.cs new file mode 100644 index 00000000..3cf51f7d --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/ParameterTests.cs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for and . +/// +public class ParameterTests +{ + /// + /// Verifies that the constructor assigns the name and value. + /// + [Fact] + public void Parameter_Constructor_SetsNameAndValue() + { + var parameter = new Parameter("scale_factor", 1.0); + + Assert.Equal("scale_factor", parameter.Name); + Assert.Equal(1.0, parameter.Value); + } + + /// + /// Verifies that the constructor assigns the name and value. + /// + [Fact] + public void ProjectionParameter_Constructor_SetsNameAndValue() + { + var parameter = new ProjectionParameter("central_meridian", 15.0); + + Assert.Equal("central_meridian", parameter.Name); + Assert.Equal(15.0, parameter.Value); + } + + /// + /// Verifies that WKT uses invariant formatting and includes name and value. + /// + [Fact] + public void ProjectionParameter_WKT_FormatsExpectedValue() + { + var parameter = new ProjectionParameter("scale_factor", 0.9996); + + Assert.Equal("PARAMETER[\"scale_factor\", 0.9996]", parameter.WKT); + } + + /// + /// Verifies that XML uses the expected element name and attributes. + /// + [Fact] + public void ProjectionParameter_XML_FormatsExpectedValue() + { + var parameter = new ProjectionParameter("central_meridian", 15.5); + + Assert.Equal("", parameter.XML); + } + + /// + /// Verifies that returns the expected element. + /// + [Fact] + public void ProjectionParameter_ToXml_ReturnsExpectedElement() + { + var parameter = new ProjectionParameter("false_easting", 500000.0); + + XElement xml = parameter.ToXml(); + + Assert.Equal("CS_ProjectionParameter", xml.Name.LocalName); + Assert.Equal("false_easting", (string?)xml.Attribute("Name")); + Assert.Equal("500000", (string?)xml.Attribute("Value")); + } + + /// + /// Verifies that returns the expected node structure. + /// + [Fact] + public void ProjectionParameter_ToWktNode_ReturnsKeywordNode() + { + var parameter = new ProjectionParameter("central_meridian", 15.0); + + WktKeywordNode node = Assert.IsType(parameter.ToWktNode()); + + Assert.Equal("PARAMETER", node.Keyword); + Assert.Equal(2, node.Children.Count); + + WktQuotedString nameNode = Assert.IsType(node.Children[0]); + Assert.Equal("central_meridian", nameNode.Value); + + WktNumber valueNode = Assert.IsType(node.Children[1]); + Assert.Equal(15.0, valueNode.Value, 12); + } + + /// + /// Verifies that returns a readable diagnostic string. + /// + [Fact] + public void ProjectionParameter_ToString_ReturnsReadableValue() + { + var parameter = new ProjectionParameter("scale_factor", 0.9996); + + string text = parameter.ToString(); + + Assert.StartsWith("ProjectionParameter 'scale_factor': ", text, StringComparison.Ordinal); + Assert.Contains("9996", text, StringComparison.Ordinal); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/ParametricCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/ParametricCoordinateSystemTests.cs new file mode 100644 index 00000000..2e0f0b2e --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/ParametricCoordinateSystemTests.cs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for , , and . +/// +public class ParametricCoordinateSystemTests +{ + /// + /// Verifies that parametric units retain their conversion factor and keyword. + /// + [Fact] + public void ParametricUnit_ToWktNode_UsesParametricUnitKeyword() + { + var unit = new ParametricUnit(0.1d, "pressure", "EPSG", 0, string.Empty, string.Empty, string.Empty); + + Assert.Equal(0.1d, unit.ConversionFactor); + Assert.StartsWith("PARAMETRICUNIT[\"pressure\"", unit.ToWktNode(WktVersion.Wkt22019).ToString(), StringComparison.Ordinal); + } + + /// + /// Verifies that parametric datum output uses PDATUM. + /// + [Fact] + public void ParametricDatum_ToWktNode_UsesPdatumKeyword() + { + var datum = new ParametricDatum("Reservoir datum", "EPSG", 0, string.Empty, string.Empty, string.Empty); + + Assert.StartsWith("PDATUM[\"Reservoir datum\"", datum.ToWktNode(WktVersion.Wkt22019).ToString(), StringComparison.Ordinal); + } + + /// + /// Verifies that parametric coordinate systems expose their single unit. + /// + [Fact] + public void GetUnits_ReturnsParametricUnit() + { + ParametricCoordinateSystem coordinateSystem = CreateParametricCoordinateSystem(); + + Assert.True(coordinateSystem.GetUnits(0).EqualParams(coordinateSystem.ParametricUnit)); + Assert.ThrowsAny(() => coordinateSystem.GetUnits(1)); + } + + /// + /// Verifies that WKT2 output uses PARAMETRICCRS. + /// + [Fact] + public void ToWktNode_UsesParametricCrsKeyword() + { + string wkt = CreateParametricCoordinateSystem().ToWktNode(WktVersion.Wkt22019).ToString(); + + Assert.StartsWith("PARAMETRICCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("PARAMETRICUNIT[\"pressure\"", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT2 roundtrips preserve the parametric unit metadata. + /// + [Fact] + public void ToWktNode_RoundTripsParametricCoordinateSystemWithParametricUnit() + { + ParametricCoordinateSystem original = CreateParametricCoordinateSystem(); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + + ParametricCoordinateSystem roundTripped = CoordinateSystemTestHelpers.RequireCoordinateSystem(wkt); + + Assert.True(original.EqualParams(roundTripped)); + ParametricUnit unit = Assert.IsType(roundTripped.ParametricUnit); + Assert.Equal(original.ParametricUnit.ConversionFactor, unit.ConversionFactor); + Assert.Equal(original.ParametricUnit.Name, unit.Name); + } + + private static ParametricCoordinateSystem CreateParametricCoordinateSystem() + { + return new ParametricCoordinateSystem( + new ParametricUnit(0.1d, "pressure", "EPSG", 0, string.Empty, string.Empty, string.Empty), + new ParametricDatum("Reservoir datum", "EPSG", 0, string.Empty, string.Empty, string.Empty), + new AxisInfo("pressure", AxisOrientationEnum.Up), + "Reservoir pressure", + "EPSG", + 0, + string.Empty, + string.Empty, + string.Empty); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/PrimeMeridianTests.cs b/test/ProjNet.Tests/CoordinateSystems/PrimeMeridianTests.cs new file mode 100644 index 00000000..a7172e45 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/PrimeMeridianTests.cs @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class PrimeMeridianTests +{ + /// + /// Verifies that the built-in prime meridians expose the expected metadata. + /// + /// Well-known prime meridian key. + /// Expected meridian name. + /// Expected authority code. + /// Expected longitude in degrees. + [Theory] + [InlineData("Greenwich", "Greenwich", 8901L, 0.0)] + [InlineData("Lisbon", "Lisbon", 8902L, -9.0754862)] + [InlineData("Paris", "Paris", 8903L, 2.5969213)] + [InlineData("Bogota", "Bogota", 8904L, -74.04513)] + [InlineData("Madrid", "Madrid", 8905L, -3.411658)] + [InlineData("Rome", "Rome", 8906L, 12.27084)] + [InlineData("Bern", "Bern", 8907L, 7.26225)] + [InlineData("Jakarta", "Jakarta", 8908L, 106.482779)] + [InlineData("Ferro", "Ferro", 8909L, -17.66666666666667)] + [InlineData("Brussels", "Brussels", 8910L, 4.220471)] + [InlineData("Stockholm", "Stockholm", 8911L, 18.03298)] + [InlineData("Athens", "Athens", 8912L, 23.4258815)] + [InlineData("Oslo", "Oslo", 8913L, 10.43225)] + public void KnownPrimeMeridians_ExposeExpectedMetadata( + string key, + string expectedName, + long expectedAuthorityCode, + double expectedLongitude) + { + PrimeMeridian meridian = GetKnownPrimeMeridian(key); + + Assert.Equal(expectedName, meridian.Name); + Assert.Equal("EPSG", meridian.Authority); + Assert.Equal(expectedAuthorityCode, meridian.AuthorityCode); + Assert.Equal(expectedLongitude, meridian.Longitude, 12); + Assert.True(meridian.AngularUnit.EqualParams(AngularUnit.Degrees)); + } + + /// + /// Verifies that the constructor assigns all properties. + /// + [Fact] + public void Constructor_SetsProperties() + { + var meridian = new PrimeMeridian(1.25, AngularUnit.Grad, "Custom", "AUTH", 42, "alias", "abbr", "remarks"); + + Assert.Equal(1.25, meridian.Longitude, 12); + Assert.True(meridian.AngularUnit.EqualParams(AngularUnit.Grad)); + Assert.Equal("Custom", meridian.Name); + Assert.Equal("AUTH", meridian.Authority); + Assert.Equal(42, meridian.AuthorityCode); + Assert.Equal("alias", meridian.Alias); + Assert.Equal("abbr", meridian.Abbreviation); + Assert.Equal("remarks", meridian.Remarks); + } + + /// + /// Verifies that WKT includes the authority clause when authority information is available. + /// + [Fact] + public void WKT_WithAuthority_FormatsExpectedValue() + { + PrimeMeridian meridian = PrimeMeridian.Greenwich; + + Assert.Equal("PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]]", meridian.WKT); + } + + /// + /// Verifies that WKT omits the authority clause when authority information is unavailable. + /// + [Fact] + public void WKT_WithoutAuthority_OmitsAuthorityClause() + { + var meridian = new PrimeMeridian(1.25, AngularUnit.Grad, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.Equal("PRIMEM[\"Custom\", 1.25]", meridian.WKT); + } + + /// + /// Verifies that XML contains the expected element name, longitude attribute, and child elements. + /// + [Fact] + public void XML_ContainsExpectedStructure() + { + var meridian = new PrimeMeridian(1.25, AngularUnit.Grad, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var xml = XElement.Parse(meridian.XML); + + Assert.Equal("CS_PrimeMeridian", xml.Name.LocalName); + Assert.Equal("1.25", (string?)xml.Attribute("Longitude")); + Assert.NotNull(xml.Element("CS_Info")); + Assert.NotNull(xml.Element("CS_AngularUnit")); + } + + /// + /// Verifies that matches the XML property. + /// + [Fact] + public void ToXml_MatchesXmlProperty() + { + PrimeMeridian meridian = PrimeMeridian.Greenwich; + XElement element = meridian.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(meridian.XML), element)); + } + + /// + /// Verifies that the WKT node includes authority information when available. + /// + [Fact] + public void ToWktNode_WithAuthority_IncludesAuthorityNode() + { + PrimeMeridian meridian = PrimeMeridian.Greenwich; + WktKeywordNode node = Assert.IsType(meridian.ToWktNode()); + + Assert.Equal("PRIMEM", node.Keyword); + Assert.Equal(3, node.Children.Count); + Assert.IsType(node.Children[2]); + } + + /// + /// Verifies that the WKT node omits authority information when it is unavailable. + /// + [Fact] + public void ToWktNode_WithoutAuthority_OmitsAuthorityNode() + { + var meridian = new PrimeMeridian(1.25, AngularUnit.Grad, "Custom", string.Empty, -1, string.Empty, string.Empty, string.Empty); + WktKeywordNode node = Assert.IsType(meridian.ToWktNode()); + + Assert.Equal(2, node.Children.Count); + } + + /// + /// Verifies that equality ignores metadata when longitude and angular unit match. + /// + [Fact] + public void EqualParams_SameParametersDifferentMetadata_ReturnsTrue() + { + var first = new PrimeMeridian(1.25, AngularUnit.Grad, "First", "EPSG", 1, "a1", "abbr1", "r1"); + var second = new PrimeMeridian(1.25, AngularUnit.Grad, "Second", "OTHER", 2, "a2", "abbr2", "r2"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that a different longitude breaks equality. + /// + [Fact] + public void EqualParams_DifferentLongitude_ReturnsFalse() + { + var first = new PrimeMeridian(1.25, AngularUnit.Grad, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var second = new PrimeMeridian(2.5, AngularUnit.Grad, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different angular unit breaks equality. + /// + [Fact] + public void EqualParams_DifferentAngularUnit_ReturnsFalse() + { + var first = new PrimeMeridian(1.25, AngularUnit.Grad, "A", string.Empty, -1, string.Empty, string.Empty, string.Empty); + var second = new PrimeMeridian(1.25, AngularUnit.Degrees, "B", string.Empty, -1, string.Empty, string.Empty, string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different object types compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(PrimeMeridian.Greenwich.EqualParams("not a meridian")); + } + + private static PrimeMeridian GetKnownPrimeMeridian(string key) + { + return key switch + { + "Greenwich" => PrimeMeridian.Greenwich, + "Lisbon" => PrimeMeridian.Lisbon, + "Paris" => PrimeMeridian.Paris, + "Bogota" => PrimeMeridian.Bogota, + "Madrid" => PrimeMeridian.Madrid, + "Rome" => PrimeMeridian.Rome, + "Bern" => PrimeMeridian.Bern, + "Jakarta" => PrimeMeridian.Jakarta, + "Ferro" => PrimeMeridian.Ferro, + "Brussels" => PrimeMeridian.Brussels, + "Stockholm" => PrimeMeridian.Stockholm, + "Athens" => PrimeMeridian.Athens, + "Oslo" => PrimeMeridian.Oslo, + _ => throw new ArgumentOutOfRangeException(nameof(key)), + }; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/ProjectedCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/ProjectedCoordinateSystemTests.cs new file mode 100644 index 00000000..d210ac19 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/ProjectedCoordinateSystemTests.cs @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class ProjectedCoordinateSystemTests +{ + private static readonly CoordinateSystemFactory Factory = new(); + + /// + /// Verifies that the internal constructor stores the supplied values. + /// + [Fact] + public void Constructor_SetsProperties() + { + GeographicCoordinateSystem geographicCoordinateSystem = GeographicCoordinateSystem.WGS84; + Projection projection = CreateProjection(); + List axisInfo = CreateDefaultAxisInfo(); + var system = new ProjectedCoordinateSystem( + HorizontalDatum.WGS84, + geographicCoordinateSystem, + LinearUnit.Foot, + projection, + axisInfo, + "Custom projected", + "EPSG", + 9999, + "alias", + "remarks", + "abbr"); + + Assert.Equal("Custom projected", system.Name); + Assert.Equal("EPSG", system.Authority); + Assert.Equal(9999, system.AuthorityCode); + Assert.Equal("alias", system.Alias); + Assert.Equal("remarks", system.Remarks); + Assert.Equal("abbr", system.Abbreviation); + Assert.True(system.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.Same(geographicCoordinateSystem, system.GeographicCoordinateSystem); + Assert.True(system.LinearUnit.EqualParams(LinearUnit.Foot)); + Assert.Same(projection, system.Projection); + Assert.Same(axisInfo, system.AxisInfo); + } + + /// + /// Verifies that the factory creates a projected coordinate system with the expected defaults. + /// + [Fact] + public void Factory_CreatesProjectedCoordinateSystem() + { + GeographicCoordinateSystem geographicCoordinateSystem = GeographicCoordinateSystem.WGS84; + Projection projection = CreateProjection(); + AxisInfo axis0 = new("East", AxisOrientationEnum.East); + AxisInfo axis1 = new("North", AxisOrientationEnum.North); + ProjectedCoordinateSystem system = Factory.CreateProjectedCoordinateSystem( + "Factory projected", + geographicCoordinateSystem, + projection, + LinearUnit.Metre, + axis0, + axis1); + + Assert.Equal("Factory projected", system.Name); + Assert.Equal(2, system.Dimension); + Assert.True(system.HorizontalDatum.EqualParams(geographicCoordinateSystem.HorizontalDatum)); + Assert.Same(geographicCoordinateSystem, system.GeographicCoordinateSystem); + Assert.True(system.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.Same(projection, system.Projection); + Assert.Same(axis0, system.GetAxis(0)); + Assert.Same(axis1, system.GetAxis(1)); + } + + /// + /// Verifies that the predefined Web Mercator coordinate system exposes the expected metadata. + /// + [Fact] + public void WebMercator_HasExpectedMetadata() + { + ProjectedCoordinateSystem system = ProjectedCoordinateSystem.WebMercator; + Projection projection = Assert.IsType(system.Projection); + + Assert.Equal("WGS 84 / Pseudo-Mercator", system.Name); + Assert.Equal("EPSG", system.Authority); + Assert.Equal(3857, system.AuthorityCode); + Assert.Equal("WGS 84 / Popular Visualisation Pseudo-Mercator", system.Alias); + Assert.Equal("WebMercator", system.Abbreviation); + Assert.True(system.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(system.GeographicCoordinateSystem.EqualParams(GeographicCoordinateSystem.WGS84)); + Assert.True(system.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.Equal("Popular Visualisation Pseudo-Mercator", projection.ClassName); + Assert.Equal(4, projection.NumParameters); + Assert.Equal(0.0, projection.GetParameter("false_northing")!.Value); + Assert.Contains("spherical development", system.Remarks, StringComparison.Ordinal); + } + + /// + /// Verifies that WGS84 UTM uses the expected north and south metadata and parameter values. + /// + /// The UTM zone. + /// Whether the zone is in the northern hemisphere. + /// The expected authority code. + /// The expected false northing parameter. + /// The expected coordinate system name. + [Theory] + [InlineData(32, true, 32632L, 0.0, "WGS 84 / UTM zone 32N")] + [InlineData(32, false, 32732L, 10000000.0, "WGS 84 / UTM zone 32S")] + public void WGS84_UTM_HasExpectedMetadata(int zone, bool zoneIsNorth, long expectedAuthorityCode, double expectedFalseNorthing, string expectedName) + { + var system = ProjectedCoordinateSystem.WGS84_UTM(zone, zoneIsNorth); + Projection projection = Assert.IsType(system.Projection); + + Assert.Equal(expectedName, system.Name); + Assert.Equal("EPSG", system.Authority); + Assert.Equal(expectedAuthorityCode, system.AuthorityCode); + Assert.True(system.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(system.GeographicCoordinateSystem.EqualParams(GeographicCoordinateSystem.WGS84)); + Assert.True(system.LinearUnit.EqualParams(LinearUnit.Metre)); + Assert.Equal("Transverse_Mercator", projection.ClassName); + Assert.Equal((zone * 6) - 183, projection.GetParameter("central_meridian")!.Value); + Assert.Equal(0.9996, projection.GetParameter("scale_factor")!.Value, 12); + Assert.Equal(500000.0, projection.GetParameter("false_easting")!.Value); + Assert.Equal(expectedFalseNorthing, projection.GetParameter("false_northing")!.Value); + } + + /// + /// Verifies that the projected coordinate system exposes the members supplied at construction time. + /// + [Fact] + public void ConstructorConfiguredValues_AreExposed() + { + GeographicCoordinateSystem geographicCoordinateSystem = CreateParisGeographicCoordinateSystem(); + Projection projection = CreateProjection("Lambert_Conformal_Conic_2SP"); + ProjectedCoordinateSystem system = CreateSystem( + horizontalDatum: HorizontalDatum.ED50, + geographicCoordinateSystem: geographicCoordinateSystem, + linearUnit: LinearUnit.Foot, + projection: projection); + + Assert.True(system.HorizontalDatum.EqualParams(HorizontalDatum.ED50)); + Assert.Same(geographicCoordinateSystem, system.GeographicCoordinateSystem); + Assert.True(system.LinearUnit.EqualParams(LinearUnit.Foot)); + Assert.Same(projection, system.Projection); + } + + /// + /// Verifies that preserves the projected CRS shape while replacing authority metadata. + /// + [Fact] + public void WithAuthority_ReturnsProjectedCloneWithUpdatedAuthorityMetadata() + { + ProjectedCoordinateSystem original = CreateSystem(authority: "TEST", authorityCode: 7); + ProjectedCoordinateSystem clone = original.WithAuthority("EPSG", 32632); + + Assert.Equal("EPSG", clone.Authority); + Assert.Equal(32632, clone.AuthorityCode); + Assert.Equal("TEST", original.Authority); + Assert.Equal(7, original.AuthorityCode); + Assert.NotSame(original, clone); + Assert.NotSame(original.GeographicCoordinateSystem, clone.GeographicCoordinateSystem); + Assert.True(original.GeographicCoordinateSystem.EqualParams(clone.GeographicCoordinateSystem)); + Assert.NotSame(original.HorizontalDatum, clone.HorizontalDatum); + Assert.True(original.HorizontalDatum.EqualParams(clone.HorizontalDatum)); + } + + /// + /// Verifies that preserves the projected CRS structure while replacing its name. + /// + [Fact] + public void WithName_ReturnsProjectedCloneWithUpdatedName() + { + ProjectedCoordinateSystem original = CreateSystem(name: "Custom projected"); + ProjectedCoordinateSystem clone = original.WithName("Renamed projected"); + + Assert.Equal("Renamed projected", clone.Name); + Assert.Equal("Custom projected", original.Name); + Assert.Equal(original.Authority, clone.Authority); + Assert.Equal(original.AuthorityCode, clone.AuthorityCode); + Assert.NotSame(original, clone); + Assert.NotSame(original.GeographicCoordinateSystem, clone.GeographicCoordinateSystem); + Assert.True(original.GeographicCoordinateSystem.EqualParams(clone.GeographicCoordinateSystem)); + } + + /// + /// Verifies that WKT omits axis clauses when the default projected axes are used. + /// + [Fact] + public void WKT_WithDefaultAxes_OmitsAxisClauses() + { + ProjectedCoordinateSystem system = CreateSystem(axisInfo: CreateDefaultAxisInfo()); + + Assert.DoesNotContain("AXIS[", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT includes custom axis clauses when the axis definitions differ from the defaults. + /// + [Fact] + public void WKT_WithCustomAxes_IncludesAxisClauses() + { + ProjectedCoordinateSystem system = CreateSystem(axisInfo: CreateCustomAxisInfo()); + + Assert.Contains("AXIS[\"East\", EAST]", system.WKT, StringComparison.Ordinal); + Assert.Contains("AXIS[\"North\", NORTH]", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT includes authority metadata when it is available. + /// + [Fact] + public void WKT_WithAuthority_IncludesAuthorityClause() + { + ProjectedCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 32632); + + Assert.Contains("AUTHORITY[\"EPSG\", \"32632\"]", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT omits authority metadata when the authority code is not positive. + /// + [Fact] + public void WKT_WithAuthorityNameButNonPositiveCode_OmitsAuthorityClause() + { + ProjectedCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 0); + + Assert.DoesNotContain("AUTHORITY[\"EPSG\", \"0\"]", system.WKT, StringComparison.Ordinal); + } + + /// + /// Verifies that XML contains the expected structure when the projection is a instance. + /// + [Fact] + public void XML_WithProjectionInstance_ContainsExpectedStructure() + { + ProjectedCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 32632); + var xml = XElement.Parse(system.XML); + XElement inner = Assert.IsType(xml.Element("CS_ProjectedCoordinateSystem")); + + Assert.Equal("CS_CoordinateSystem", xml.Name.LocalName); + Assert.Equal("2", (string?)xml.Attribute("Dimension")); + Assert.NotNull(inner.Element("CS_Info")); + Assert.Equal(2, inner.Elements("CS_AxisInfo").Count()); + Assert.NotNull(inner.Element("CS_CoordinateSystem")); + Assert.NotNull(inner.Element("CS_LinearUnit")); + Assert.NotNull(inner.Element("CS_Projection")); + } + + /// + /// Verifies that XML still emits the projection element when the projection does not use the concrete type. + /// + [Fact] + public void XML_WithNonProjectionInstance_UsesProjectionXml() + { + ProjectedCoordinateSystem system = CreateSystem(projection: new FakeProjection()); + var xml = XElement.Parse(system.XML); + XElement inner = Assert.IsType(xml.Element("CS_ProjectedCoordinateSystem")); + XElement projection = Assert.IsType(inner.Element("CS_Projection")); + + Assert.Equal("Fake_Projection", projection.Attribute("Classname")?.Value); + } + + /// + /// Verifies that matches the XML property when the projection is a instance. + /// + [Fact] + public void ToXml_WithProjectionInstance_MatchesXmlProperty() + { + ProjectedCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 32632); + XElement xml = system.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(system.XML), xml)); + } + + /// + /// Verifies that matches WKT when default axes are used. + /// + [Fact] + public void ToWktNode_WithDefaultAxes_MatchesWkt() + { + ProjectedCoordinateSystem system = CreateSystem(axisInfo: CreateDefaultAxisInfo()); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + + Assert.Equal("PROJCS", node.Keyword); + Assert.Equal(system.WKT, node.ToString()); + } + + /// + /// Verifies that uses an identifier node when the projection is not a instance. + /// + [Fact] + public void ToWktNode_WithNonProjectionInstance_UsesIdentifierNode() + { + FakeProjection projection = new(); + ProjectedCoordinateSystem system = CreateSystem(projection: projection, axisInfo: CreateDefaultAxisInfo()); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + WktIdentifier projectionNode = Assert.IsType(node.Children[2]); + + Assert.Equal(projection.WKT, projectionNode.Name); + Assert.Equal(projection.NumParameters + 4, node.Children.Count); + } + + /// + /// Verifies that includes an authority node when metadata is present. + /// + [Fact] + public void ToWktNode_WithAuthority_IncludesAuthorityNode() + { + ProjectedCoordinateSystem system = CreateSystem(authority: "EPSG", authorityCode: 32632, axisInfo: CreateDefaultAxisInfo()); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + WktKeywordNode authorityNode = Assert.IsType(node.Children[^1]); + + Assert.Equal("AUTHORITY", authorityNode.Keyword); + Assert.Equal("EPSG", Assert.IsType(authorityNode.Children[0]).Value); + Assert.Equal("32632", Assert.IsType(authorityNode.Children[1]).Value); + } + + /// + /// Verifies that includes custom axis nodes when the axes differ from the defaults. + /// + [Fact] + public void ToWktNode_WithCustomAxes_IncludesAxisNodes() + { + ProjectedCoordinateSystem system = CreateSystem(axisInfo: CreateCustomAxisInfo()); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + WktKeywordNode firstAxis = Assert.IsType(node.Children[^2]); + WktKeywordNode secondAxis = Assert.IsType(node.Children[^1]); + + Assert.Equal("AXIS", firstAxis.Keyword); + Assert.Equal("East", Assert.IsType(firstAxis.Children[0]).Value); + Assert.Equal("EAST", Assert.IsType(firstAxis.Children[1]).Name); + Assert.Equal("AXIS", secondAxis.Keyword); + Assert.Equal("North", Assert.IsType(secondAxis.Children[0]).Value); + Assert.Equal("NORTH", Assert.IsType(secondAxis.Children[1]).Name); + } + + /// + /// Verifies that includes axis nodes when only the first axis name differs from the defaults. + /// + [Fact] + public void ToWktNode_WithFirstAxisNameChanged_IncludesAxisNodes() + { + ProjectedCoordinateSystem system = CreateSystem(axisInfo: + [ + new AxisInfo("Easting", AxisOrientationEnum.East), + new AxisInfo("Y", AxisOrientationEnum.North), + ]); + WktKeywordNode node = Assert.IsType(system.ToWktNode()); + WktKeywordNode firstAxis = Assert.IsType(node.Children[^2]); + WktKeywordNode secondAxis = Assert.IsType(node.Children[^1]); + + Assert.Equal("Easting", Assert.IsType(firstAxis.Children[0]).Value); + Assert.Equal("Y", Assert.IsType(secondAxis.Children[0]).Value); + } + + /// + /// Verifies that GetUnits returns the linear unit regardless of the requested dimension. + /// + /// The requested dimension index. + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1)] + [InlineData(99)] + public void GetUnits_ReturnsLinearUnitForAnyDimension(int dimension) + { + ProjectedCoordinateSystem system = CreateSystem(linearUnit: LinearUnit.Foot); + IUnit unit = system.GetUnits(dimension); + + Assert.True(unit.EqualParams(LinearUnit.Foot)); + } + + /// + /// Verifies that equal projected coordinate systems compare equal. + /// + [Fact] + public void EqualParams_SameValues_ReturnsTrue() + { + ProjectedCoordinateSystem first = CreateSystem(name: "First"); + ProjectedCoordinateSystem second = CreateSystem(name: "Second"); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that projected coordinate systems reject invalid axis counts. + /// + [Fact] + public void Constructor_InvalidAxisCount_ThrowsArgumentException() + { + Assert.Throws(() => CreateSystem(axisInfo: [new AxisInfo("Only", AxisOrientationEnum.East)])); + } + + /// + /// Verifies that a different axis orientation causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentAxisOrientation_ReturnsFalse() + { + ProjectedCoordinateSystem first = CreateSystem(); + ProjectedCoordinateSystem second = CreateSystem(axisInfo: + [ + new AxisInfo("X", AxisOrientationEnum.East), + new AxisInfo("Y", AxisOrientationEnum.South), + ]); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different geographic coordinate system causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentGeographicCoordinateSystem_ReturnsFalse() + { + ProjectedCoordinateSystem first = CreateSystem(); + ProjectedCoordinateSystem second = CreateSystem(geographicCoordinateSystem: CreateParisGeographicCoordinateSystem()); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different horizontal datum causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentHorizontalDatum_ReturnsFalse() + { + ProjectedCoordinateSystem first = CreateSystem(); + ProjectedCoordinateSystem second = CreateSystem(horizontalDatum: HorizontalDatum.ED50); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different linear unit causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentLinearUnit_ReturnsFalse() + { + ProjectedCoordinateSystem first = CreateSystem(); + ProjectedCoordinateSystem second = CreateSystem(linearUnit: LinearUnit.Foot); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that a different projection causes equality to fail. + /// + [Fact] + public void EqualParams_DifferentProjection_ReturnsFalse() + { + ProjectedCoordinateSystem first = CreateSystem(); + ProjectedCoordinateSystem second = CreateSystem(projection: CreateProjection(falseNorthing: 1.0)); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that different object types compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(CreateSystem().EqualParams("not a projected coordinate system")); + } + + private static Projection CreateProjection(string className = "Transverse_Mercator", double falseNorthing = 0.0) + { + return new Projection( + className, + [ + new ProjectionParameter("latitude_of_origin", 0.0), + new ProjectionParameter("central_meridian", 9.0), + new ProjectionParameter("false_northing", falseNorthing), + ], + "Projection", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } + + private static ProjectedCoordinateSystem CreateSystem( + string name = "Custom projected", + HorizontalDatum? horizontalDatum = null, + GeographicCoordinateSystem? geographicCoordinateSystem = null, + LinearUnit? linearUnit = null, + IProjection? projection = null, + List? axisInfo = null, + string authority = "", + long authorityCode = -1) + { + GeographicCoordinateSystem effectiveGeographicCoordinateSystem = geographicCoordinateSystem ?? GeographicCoordinateSystem.WGS84; + + return new ProjectedCoordinateSystem( + horizontalDatum ?? effectiveGeographicCoordinateSystem.HorizontalDatum, + effectiveGeographicCoordinateSystem, + linearUnit ?? LinearUnit.Metre, + projection ?? CreateProjection(), + axisInfo ?? CreateDefaultAxisInfo(), + name, + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static GeographicCoordinateSystem CreateParisGeographicCoordinateSystem() + { + return Factory.CreateGeographicCoordinateSystem( + "Paris geographic", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Paris, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + private static List CreateDefaultAxisInfo() + { + return + [ + new AxisInfo("X", AxisOrientationEnum.East), + new AxisInfo("Y", AxisOrientationEnum.North), + ]; + } + + private static List CreateCustomAxisInfo() + { + return + [ + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North), + ]; + } + + private sealed class FakeProjection : IProjection + { + private readonly List parameters = + [ + new ProjectionParameter("latitude_of_origin", 0.0), + new ProjectionParameter("central_meridian", 12.0), + ]; + + public string Name => "Fake projection"; + + public string Authority => string.Empty; + + public long AuthorityCode => -1; + + public string Alias => string.Empty; + + public string Abbreviation => string.Empty; + + public string Remarks => string.Empty; + + public string WKT => "PROJECTION[\"Fake_Projection\"]"; + + public string XML => ""; + + public int NumParameters => this.parameters.Count; + + public string ClassName => "Fake_Projection"; + + public ProjectionParameter GetParameter(int index) => this.parameters[index]; + + public ProjectionParameter? GetParameter(string name) + { + foreach (ProjectionParameter parameter in this.parameters) + { + if (parameter.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + return parameter; + } + } + + return null; + } + + public bool EqualParams(object obj) + { + if (obj is not FakeProjection other || other.parameters.Count != this.parameters.Count) + { + return false; + } + + for (int i = 0; i < this.parameters.Count; i++) + { + if (!string.Equals(this.parameters[i].Name, other.parameters[i].Name, StringComparison.OrdinalIgnoreCase) || + this.parameters[i].Value != other.parameters[i].Value) + { + return false; + } + } + + return true; + } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/ProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/ProjectionTests.cs new file mode 100644 index 00000000..4eacb604 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/ProjectionTests.cs @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class ProjectionTests +{ + /// + /// Verifies that the constructor stores metadata, class name, and parameters. + /// + [Fact] + public void Constructor_SetsPropertiesAndParameters() + { + List parameters = CreateParameters(); + var projection = new Projection( + "Transverse_Mercator", + parameters, + "UTM zone 32N", + "EPSG", + 9807, + "alias", + "remarks", + "abbr"); + + Assert.Equal("Transverse_Mercator", projection.ClassName); + Assert.Equal("UTM zone 32N", projection.Name); + Assert.Equal("EPSG", projection.Authority); + Assert.Equal(9807, projection.AuthorityCode); + Assert.Equal("alias", projection.Alias); + Assert.Equal("remarks", projection.Remarks); + Assert.Equal("abbr", projection.Abbreviation); + Assert.Equal(3, projection.NumParameters); + Assert.Same(parameters[0], projection.GetParameter(0)); + } + + /// + /// Verifies that the internal parameter collection is exposed as a get-only property. + /// + [Fact] + public void Parameters_Property_IsGetOnly() + { + PropertyInfo parametersProperty = typeof(Projection).GetProperty( + "Parameters", + BindingFlags.Instance | BindingFlags.NonPublic)!; + + Assert.True(parametersProperty.CanRead); + Assert.False(parametersProperty.CanWrite); + } + + /// + /// Verifies that WKT omits the authority clause when authority metadata is absent. + /// + [Fact] + public void WKT_WithoutAuthority_FormatsExpectedValue() + { + Projection projection = CreateProjection(); + + Assert.Equal("PROJECTION[\"Transverse_Mercator\"]", projection.WKT); + } + + /// + /// Verifies that WKT omits the authority clause when the authority code is not positive. + /// + [Fact] + public void WKT_WithAuthorityNameButNonPositiveCode_OmitsAuthorityClause() + { + Projection projection = CreateProjection(authority: "EPSG", authorityCode: 0); + + Assert.Equal("PROJECTION[\"Transverse_Mercator\"]", projection.WKT); + } + + /// + /// Verifies that WKT includes the authority clause when metadata is present. + /// + [Fact] + public void WKT_WithAuthority_FormatsExpectedValue() + { + Projection projection = CreateProjection(authority: "EPSG", authorityCode: 9807); + + Assert.Equal("PROJECTION[\"Transverse_Mercator\", AUTHORITY[\"EPSG\", \"9807\"]]", projection.WKT); + } + + /// + /// Verifies that XML contains the expected metadata and parameter elements. + /// + [Fact] + public void XML_ContainsInfoAndParameterElements() + { + Projection projection = CreateProjection(authority: "EPSG", authorityCode: 9807); + var xml = XElement.Parse(projection.XML); + + Assert.Equal("CS_Projection", xml.Name.LocalName); + Assert.Equal("Transverse_Mercator", (string?)xml.Attribute("Classname")); + + XElement info = Assert.IsType(xml.Element("CS_Info")); + Assert.Equal("9807", (string?)info.Attribute("AuthorityCode")); + Assert.Equal("EPSG", (string?)info.Attribute("Authority")); + Assert.Equal("UTM zone 32N", (string?)info.Attribute("Name")); + Assert.Equal("abbr", (string?)info.Attribute("Abbreviation")); + Assert.Equal(3, new List(xml.Elements("CS_ProjectionParameter")).Count); + } + + /// + /// Verifies that matches the XML property. + /// + [Fact] + public void ToXml_MatchesXmlProperty() + { + Projection projection = CreateProjection(authority: "EPSG", authorityCode: 9807); + XElement xml = projection.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(projection.XML), xml)); + } + + /// + /// Verifies that returns a projection node without authority when metadata is absent. + /// + [Fact] + public void ToWktNode_WithoutAuthority_ReturnsKeywordNode() + { + Projection projection = CreateProjection(); + WktKeywordNode node = Assert.IsType(projection.ToWktNode()); + + Assert.Equal("PROJECTION", node.Keyword); + Assert.Single(node.Children); + Assert.Equal("Transverse_Mercator", Assert.IsType(node.Children[0]).Value); + } + + /// + /// Verifies that omits authority when the code is not positive. + /// + [Fact] + public void ToWktNode_WithAuthorityNameButNonPositiveCode_OmitsAuthorityNode() + { + Projection projection = CreateProjection(authority: "EPSG", authorityCode: 0); + WktKeywordNode node = Assert.IsType(projection.ToWktNode()); + + Assert.Single(node.Children); + Assert.Equal("Transverse_Mercator", Assert.IsType(node.Children[0]).Value); + } + + /// + /// Verifies that includes authority metadata when present. + /// + [Fact] + public void ToWktNode_WithAuthority_IncludesAuthorityNode() + { + Projection projection = CreateProjection(authority: "EPSG", authorityCode: 9807); + WktKeywordNode node = Assert.IsType(projection.ToWktNode()); + WktKeywordNode authorityNode; + + Assert.Equal(2, node.Children.Count); + Assert.Equal("Transverse_Mercator", Assert.IsType(node.Children[0]).Value); + authorityNode = Assert.IsType(node.Children[1]); + Assert.Equal("AUTHORITY", authorityNode.Keyword); + Assert.Equal("EPSG", Assert.IsType(authorityNode.Children[0]).Value); + Assert.Equal("9807", Assert.IsType(authorityNode.Children[1]).Value); + } + + /// + /// Verifies that indexed lookup returns the requested parameter. + /// + [Fact] + public void GetParameter_ByIndex_ReturnsParameter() + { + Projection projection = CreateProjection(); + ProjectionParameter parameter = projection.GetParameter(1); + + Assert.Equal("central_meridian", parameter.Name); + Assert.Equal(9.0, parameter.Value); + } + + /// + /// Verifies that named lookup is case insensitive. + /// + [Fact] + public void GetParameter_ByName_IsCaseInsensitive() + { + Projection projection = CreateProjection(); + ProjectionParameter? parameter = projection.GetParameter("CENTRAL_MERIDIAN"); + + Assert.NotNull(parameter); + Assert.Equal(9.0, parameter.Value); + } + + /// + /// Verifies that missing named parameters return null. + /// + [Fact] + public void GetParameter_ByName_WhenMissing_ReturnsNull() + { + Projection projection = CreateProjection(); + + Assert.Null(projection.GetParameter("false_northing")); + } + + /// + /// Verifies that projections compare equal when parameter names and values match in a different order. + /// + [Fact] + public void EqualParams_SameParametersInDifferentOrder_ReturnsTrue() + { + Projection first = CreateProjection(name: "First", authority: "EPSG", authorityCode: 9807); + Projection second = CreateProjection( + className: "Transverse_Mercator", + name: "Second", + authority: "IGNF", + authorityCode: 1, + parameters: + [ + new ProjectionParameter("scale_factor", 0.9996), + new ProjectionParameter("central_meridian", 9.0), + new ProjectionParameter("latitude_of_origin", 0.0), + ]); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that projections with different parameter counts compare unequal. + /// + [Fact] + public void EqualParams_DifferentParameterCount_ReturnsFalse() + { + Projection first = CreateProjection(); + Projection second = CreateProjection(parameters: + [ + new ProjectionParameter("latitude_of_origin", 0.0), + new ProjectionParameter("central_meridian", 9.0), + ]); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that projections with different parameter names compare unequal. + /// + [Fact] + public void EqualParams_DifferentParameterName_ReturnsFalse() + { + Projection first = CreateProjection(); + Projection second = CreateProjection(parameters: + [ + new ProjectionParameter("latitude_of_origin", 0.0), + new ProjectionParameter("longitude_of_center", 9.0), + new ProjectionParameter("scale_factor", 0.9996), + ]); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that projections with different parameter values compare unequal. + /// + [Fact] + public void EqualParams_DifferentParameterValue_ReturnsFalse() + { + Projection first = CreateProjection(); + Projection second = CreateProjection(parameters: + [ + new ProjectionParameter("latitude_of_origin", 0.0), + new ProjectionParameter("central_meridian", 10.0), + new ProjectionParameter("scale_factor", 0.9996), + ]); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that projections compare unequal to different object types. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(CreateProjection().EqualParams("not a projection")); + } + + private static Projection CreateProjection( + string className = "Transverse_Mercator", + string name = "UTM zone 32N", + string authority = "", + long authorityCode = -1, + List? parameters = null) + { + return new Projection( + className, + parameters ?? CreateParameters(), + name, + authority, + authorityCode, + "alias", + "remarks", + "abbr"); + } + + private static List CreateParameters() + { + return + [ + new ProjectionParameter("latitude_of_origin", 0.0), + new ProjectionParameter("central_meridian", 9.0), + new ProjectionParameter("scale_factor", 0.9996), + ]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/AdamsGuyouPeirceProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/AdamsGuyouPeirceProjectionTests.cs new file mode 100644 index 00000000..6626d24a --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/AdamsGuyouPeirceProjectionTests.cs @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Adams, Guyou, and Peirce projection families. +/// +public class AdamsGuyouPeirceProjectionTests +{ + private const string Sphere6370997 = "SPHEROID[\"Sphere\",6370997,0]"; + private const string Wgs84 = "SPHEROID[\"WGS 84\",6378137,298.257223563]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for Adams, Guyou, and Peirce projection families. + /// + /// Projection alias. + [Theory] + [InlineData("guyou")] + [InlineData("Guyou")] + [InlineData("peirce_q")] + [InlineData("Peirce_Quincuncial")] + [InlineData("adams_hemi")] + [InlineData("Adams_Hemisphere_In_A_Square")] + [InlineData("adams_ws1")] + [InlineData("Adams_World_In_A_Square_I")] + [InlineData("adams_ws2")] + [InlineData("Adams_World_In_A_Square_II")] + public void SupportsAdamsGuyouPeirceAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, Sphere6370997, null)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ fixture forward vectors for Adams/Guyou/Peirce family projections. + /// + [Theory] + [InlineData("adams_hemi", Sphere6370997, -89.9433443609d, -87.0825895518d, -2032451.307d, -14670658.595d, null, 2e-3d)] + [InlineData("adams_ws1", Sphere6370997, -159.5146913398d, -89.9552061084d, -350717.162d, -11748881.092d, null, 2e-3d)] + [InlineData("adams_ws2", Sphere6370997, -169.9316998581d, -89.6983443874d, -2757243.603d, -13694037.516d, null, 2e-3d)] + [InlineData("guyou", Sphere6370997, -89.3858632536d, -85.7390309668d, -671252.534d, -11805089.168d, null, 2e-3d)] + [InlineData("guyou", "SPHEROID[\"Sphere\",1,0]", 0d, 90d, 0d, 1.85407d, null, 1e-5d)] + [InlineData("peirce_q", Sphere6370997, -159.2003712209d, -89.5537263306d, -16684778.66d, 16659858.26d, ",PARAMETER[\"shape\",0]", 0.2d)] + [InlineData("peirce_q", Sphere6370997, -159.2003712209d, -89.5537263306d, 11829925.59d, 46389.53d, ",PARAMETER[\"shape\",4]", 0.2d)] + [InlineData("peirce_q", Sphere6370997, -159.2003712209d, -89.5537263306d, 17621.38d, 46389.53d, ",PARAMETER[\"shape\",4],PARAMETER[\"scrollx\",0.75]", 0.2d)] + [InlineData("peirce_q", Sphere6370997, -159.2003712209d, -89.5537263306d, -17621.38d, 11765914.68d, ",PARAMETER[\"shape\",5]", 0.2d)] + [InlineData("peirce_q", Sphere6370997, -159.2003712209d, -89.5537263306d, -17621.38d, -46389.53d, ",PARAMETER[\"shape\",5],PARAMETER[\"scrolly\",-0.25]", 0.2d)] + [InlineData("peirce_q", Sphere6370997, -179.2332724818d, 70.8217746040d, -28794.10d, 2152288.77d, ",PARAMETER[\"shape\",2]", 0.2d)] + [InlineData("peirce_q", Sphere6370997, -159.2003712209d, -89.5537263306d, -17621.38d, 46389.53d, ",PARAMETER[\"shape\",3]", 0.2d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + string spheroidClause, + double longitude, + double latitude, + double expectedX, + double expectedY, + string? extraParameters, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ fixture inverse vectors for inverse-capable Adams, Guyou, and Peirce projection families. + /// + [Theory] + [InlineData("adams_ws2", Wgs84, 0d, 0d, 0d, 0d, null, 2e-9d)] + [InlineData("adams_ws2", Wgs84, 2021909.611d, 4162291.966d, 40d, 60d, null, 5e-9d)] + [InlineData("peirce_q", Sphere6370997, 0d, 0d, 0d, 90d, ",PARAMETER[\"shape\",0]", 2e-9d)] + [InlineData("peirce_q", Sphere6370997, 8361921.234827488d, -8361921.234827488d, 0d, 0d, ",PARAMETER[\"shape\",0]", 0.1d)] + [InlineData("peirce_q", Sphere6370997, 11825542.552198235d, 0d, 90d, 0d, ",PARAMETER[\"shape\",1]", 0.1d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + string spheroidClause, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + string? extraParameters, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for inverse-capable Adams, Guyou, and Peirce projection families. + /// + [Theory] + [InlineData("adams_ws2", Wgs84, 40d, 60d, null, 2e-7d)] + [InlineData("adams_ws2", Wgs84, -179.999d, 0d, null, 2e-6d)] + [InlineData("peirce_q", Sphere6370997, 45d, 45d, ",PARAMETER[\"shape\",0]", 1e-6d)] + [InlineData("peirce_q", Sphere6370997, 90d, 0d, ",PARAMETER[\"shape\",1]", 1e-6d)] + public void SupportsAdamsGuyouPeirceRoundtrip(string projectionName, string spheroidClause, double longitude, double latitude, string? extraParameters, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies Adams/Guyou forward-only and Peirce shape-dependent inverse behavior. + /// + /// Projection name. + /// Optional projection parameters. + [Theory] + [InlineData("guyou", null)] + [InlineData("adams_hemi", null)] + [InlineData("adams_ws1", null)] + [InlineData("peirce_q", ",PARAMETER[\"shape\",2]")] + [InlineData("peirce_q", ",PARAMETER[\"shape\",3]")] + [InlineData("peirce_q", ",PARAMETER[\"shape\",4]")] + [InlineData("peirce_q", ",PARAMETER[\"shape\",5]")] + public void ForwardOnlyVariantsDoNotSupportInverse(string projectionName, string? extraParameters) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, Sphere6370997, extraParameters)); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies invalid Peirce shape and scroll ranges are rejected. + /// + [Theory] + [InlineData(",PARAMETER[\"shape\",9]")] + [InlineData(",PARAMETER[\"shape\",4],PARAMETER[\"scrollx\",1.5]")] + [InlineData(",PARAMETER[\"shape\",5],PARAMETER[\"scrolly\",-1.5]")] + public void RejectsInvalidPeirceParameters(string? extraParameters) + { + ArgumentException exception = Assert.Throws(() => + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("peirce_q", Sphere6370997, extraParameters)); + CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + }); + + Assert.Equal("parameters", exception.ParamName); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, string? extraParameters) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D6-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/AiryChamberlinBipolarProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/AiryChamberlinBipolarProjectionTests.cs new file mode 100644 index 00000000..c3993ea0 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/AiryChamberlinBipolarProjectionTests.cs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates current projection group projections (airy, chamb, bipc). +/// +public class AiryChamberlinBipolarProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for covered projections. + /// + /// Projection alias. + [Theory] + [InlineData("airy")] + [InlineData("bipc")] + [InlineData("Bipolar_Conic")] + [InlineData("chamb")] + [InlineData("Chamberlin_Trimetric")] + public void SupportsAliasesFromWkt(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, GetDefaultProfile(projectionName))); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for covered projections. + /// + /// Projection code. + /// Sphere radius meters for the fixture profile. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + /// Optional WKT parameter segment. + [Theory] + [InlineData("airy", 6400000d, 2d, 1d, 189109.886908621d, 94583.752387504d, null)] + [InlineData("chamb", 6400000d, 2d, 1d, -27864.779586801d, -223364.324593274d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("bipc", 6400000d, 2d, 1d, 2460565.740974965d, -14598319.989330800d, null)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double sphereRadius, + double longitude, + double latitude, + double expectedX, + double expectedY, + string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, new ProjectionProfile(sphereRadius, extraParameters))); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable covered projections. + /// + /// Projection code. + /// Sphere radius meters for the fixture profile. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + [Theory] + [InlineData("bipc", 6400000d, 200d, 100d, -73.038693105d, 17.248116270d)] + [InlineData("bipc", 6400000d, -200d, -100d, -73.034503807d, 17.246835092d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double sphereRadius, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, new ProjectionProfile(sphereRadius, null))); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies Airy and Chamberlin remain forward-only in this test set. + /// + /// Projection code. + [Theory] + [InlineData("airy")] + [InlineData("chamb")] + public void ForwardOnlyProjectionsDoNotSupportInverse(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, GetDefaultProfile(projectionName))); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies bipolar conic roundtrip stability. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(2d, 1d)] + [InlineData(-2d, -1d)] + public void SupportsBipcRoundtrip(double longitude, double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("bipc", new ProjectionProfile(6400000d, null))); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-9); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-9); + } + + /// + /// Verifies Airy forward behavior for polar aspects and no_cut builtins cases. + /// + [Fact] + public void AiryPolarAndNoCutCasesMatchBuiltins() + { + ProjectedCoordinateSystem northPole = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt( + "airy", + new ProjectionProfile( + 1d, + ",PARAMETER[\"latitude_of_origin\",90]"))); + ICoordinateTransformation northForward = CoordinateTransformationFactory.CreateFromCoordinateSystems(northPole.GeographicCoordinateSystem, northPole); + + double[] northZero = northForward.MathTransform.Transform(CreatePoint(0d, 0d)); + double[] northAtPole = northForward.MathTransform.Transform(CreatePoint(0d, 90d)); + + Assert.InRange(Math.Abs(northZero[0] - 0d), 0d, 1e-6); + Assert.InRange(Math.Abs(northZero[1] - (-1.3863d)), 0d, 1e-4); + Assert.InRange(Math.Abs(northAtPole[0] - 0d), 0d, 1e-6); + Assert.InRange(Math.Abs(northAtPole[1] - 0d), 0d, 1e-6); + Assert.Throws(() => northForward.MathTransform.Transform(CreatePoint(0d, -90d))); + + ProjectedCoordinateSystem noCut = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt( + "airy", + new ProjectionProfile( + 1d, + ",PARAMETER[\"latitude_of_origin\",-90],PARAMETER[\"no_cut\",1]"))); + ICoordinateTransformation noCutForward = CoordinateTransformationFactory.CreateFromCoordinateSystems(noCut.GeographicCoordinateSystem, noCut); + double[] noCutProjected = noCutForward.MathTransform.Transform(CreatePoint(0d, 10d)); + + Assert.InRange(Math.Abs(noCutProjected[0] - 0d), 0d, 1e-6); + Assert.InRange(Math.Abs(noCutProjected[1] - 1.5677d), 0d, 1e-4); + } + + private static string BuildProjectedWkt(string projectionName, ProjectionProfile profile) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectionProfile effectiveProfile = profile ?? new ProjectionProfile(6400000d, null); + bool hasLatitudeOfOrigin = effectiveProfile.ExtraParameters?.IndexOf("latitude_of_origin", StringComparison.OrdinalIgnoreCase) >= 0; + string latitudeOfOriginParameter = hasLatitudeOfOrigin ? string.Empty : ",PARAMETER[\"latitude_of_origin\",0]"; + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",{effectiveProfile.SphereRadius:R},0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"]{latitudeOfOriginParameter},PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{effectiveProfile.ExtraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static ProjectionProfile GetDefaultProfile(string projectionName) + { + return projectionName.Equals("chamb", StringComparison.OrdinalIgnoreCase) || + projectionName.Equals("chamberlin_trimetric", StringComparison.OrdinalIgnoreCase) + ? new ProjectionProfile(6400000d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]") + : new ProjectionProfile(6400000d, null); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; + + private sealed class ProjectionProfile + { + public ProjectionProfile(double sphereRadius, string? extraParameters) + { + this.SphereRadius = sphereRadius; + this.ExtraParameters = extraParameters; + } + + public double SphereRadius { get; } + + public string? ExtraParameters { get; } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/AitoffWinkelProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/AitoffWinkelProjectionTests.cs new file mode 100644 index 00000000..f4bb807b --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/AitoffWinkelProjectionTests.cs @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Aitoff/Winkel projection support and related aliases. +/// +public class AitoffWinkelProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that Aitoff and Winkel aliases resolve from WKT. + /// + /// Projection alias to validate. + [Theory] + [InlineData("aitoff")] + [InlineData("wink1")] + [InlineData("wintri")] + [InlineData("winkel_i")] + [InlineData("winkel_tripel")] + public void SupportsAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, null)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies roundtrip stability for Aitoff and Winkel variants. + /// + /// Projection alias to validate. + /// Optional lat_1 value in degrees for projections that support it. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Maximum absolute roundtrip delta (degrees). + [Theory] + [InlineData("aitoff", null, 2d, 1d, 1e-8)] + [InlineData("wink1", null, -2d, -1d, 1e-8)] + [InlineData("wintri", 0d, -2d, 1d, 1e-8)] + public void SupportsRoundtrip(string projectionName, double? latitude1, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, latitude1)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that Winkel II forward projection matches PROJ vectors for edge inputs. + /// + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData(-179.999d, 89.999d, -10052657.852d, 10053040.641d)] + [InlineData(179.999d, 89.999d, 10052657.852d, 10053040.641d)] + [InlineData(-179.999d, -89.999d, -10052657.852d, -10053040.641d)] + [InlineData(179.999d, -89.999d, 10052657.852d, -10053040.641d)] + public void WinkelIiMatchesProjBuiltinsForwardEdgeVectors(double longitude, double latitude, double expectedX, double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("wink2", 0.5d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-3); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-3); + } + + /// + /// Verifies forward values against PROJ builtins vectors. + /// + /// Projection alias to validate. + /// Optional lat_1 value in degrees for projections that support it. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData("aitoff", null, 2d, 1d, 223379.458811696d, 111706.742883853d)] + [InlineData("wink1", null, 2d, 1d, 223385.131640953d, 111701.072127637d)] + [InlineData("wink2", 0.5d, 2d, 1d, 223387.396433786d, 124752.032797445d)] + [InlineData("wintri", 0d, 2d, 1d, 223390.801533485d, 111703.907505745d)] + [InlineData("winkel_tripel", 0d, -2d, -1d, -223390.801533485d, -111703.907505745d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double? latitude1, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, latitude1)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies inverse values against PROJ builtins vectors. + /// + /// Projection alias to validate. + /// Optional lat_1 value in degrees for projections that support it. + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData("aitoff", null, 200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData("wink1", null, -200d, -100d, -0.001790493d, -0.000895247d)] + [InlineData("wintri", 0d, -200d, 100d, -0.001790493d, 0.000895247d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double? latitude1, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, latitude1)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + ArgumentNullException.ThrowIfNull(projectionName); + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies Winkel II roundtrip stability now that inverse support is implemented. + /// + [Theory] + [InlineData(0.5d, 2d, 1d, 2e-8d)] + [InlineData(0.5d, -2d, -1d, 2e-8d)] + [InlineData(50.467d, 10d, 20d, 2e-8d)] + public void SupportsWinkelIiRoundtrip(double latitude1, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("wink2", latitude1)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that Winkel II aliases resolve when required lat_1 is provided. + /// + /// Projection alias to validate. + [Theory] + [InlineData("wink2")] + [InlineData("winkel_ii")] + public void SupportsWinkelIiAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, 0.5d)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + private static string BuildProjectedWkt(string projectionName, double? latitude1) + { + string lat1Parameter = latitude1.HasValue + ? $",PARAMETER[\"standard_parallel_1\",{latitude1.Value.ToString(CultureInfo.InvariantCulture)}]" + : string.Empty; + + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"Sphere\",DATUM[\"Sphere_Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{lat1Parameter},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/AzimuthalEquidistantProjectionRegressionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/AzimuthalEquidistantProjectionRegressionTests.cs new file mode 100644 index 00000000..54f61b66 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/AzimuthalEquidistantProjectionRegressionTests.cs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for Azimuthal Equidistant projection parity with PROJ reference vectors. +/// +public class AzimuthalEquidistantProjectionRegressionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies north-pole spherical forward projection against PROJ. + /// + [Fact] + public void AzimuthalEquidistantPolarSphericalMatchesProjReference() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt( + projectionName: "aeqd", + spheroidClause: "SPHEROID[\"Sphere\",6371000,0]", + latitudeOfOrigin: 90d, + centralMeridian: 0d)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + double[] projectedPoint = forward.MathTransform.Transform([30d, 60d]); + + Assert.InRange(Math.Abs(projectedPoint[0] - 1667923.8996683809d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - (-2888928.9373840508d)), 0d, 1e-3d); + } + + /// + /// Verifies oblique ellipsoidal forward projection against PROJ. + /// + [Fact] + public void AzimuthalEquidistantEllipsoidalObliqueMatchesProjReference() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt( + projectionName: "aeqd", + spheroidClause: "SPHEROID[\"WGS 84\",6378137,298.257223563]", + latitudeOfOrigin: 48d, + centralMeridian: 10d)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + double[] projectedPoint = forward.MathTransform.Transform([16.7139129117067757d, 52.2393942647647999d]); + + Assert.InRange(Math.Abs(projectedPoint[0] - 458432.3283705170d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 491980.7580910911d), 0d, 1e-3d); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, double latitudeOfOrigin, double centralMeridian) + { + return FormattableString.Invariant( + $"PROJCS[\"Regression-{projectionName}\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{latitudeOfOrigin}],PARAMETER[\"central_meridian\",{centralMeridian}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/CentralCylindricalAndUrmaevProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/CentralCylindricalAndUrmaevProjectionTests.cs new file mode 100644 index 00000000..4ae716e5 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/CentralCylindricalAndUrmaevProjectionTests.cs @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates current projection group projections (cc, gn_sinu, eck6, mbtfps, urm5, urmfps, wag1). +/// +public class CentralCylindricalAndUrmaevProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for covered projections. + /// + /// Projection alias. + /// Optional WKT parameter segment. + [Theory] + [InlineData("cc", null)] + [InlineData("Central_Cylindrical", null)] + [InlineData("gn_sinu", ",PARAMETER[\"m\",1],PARAMETER[\"n\",2]")] + [InlineData("General_Sinusoidal", ",PARAMETER[\"m\",1],PARAMETER[\"n\",2]")] + [InlineData("eck6", null)] + [InlineData("Eckert_VI", null)] + [InlineData("mbtfps", null)] + [InlineData("McBryde_Thomas_Flat_Polar_Sinusoidal", null)] + [InlineData("urmfps", ",PARAMETER[\"n\",0.5]")] + [InlineData("Urmaev_Flat_Polar_Sinusoidal", ",PARAMETER[\"n\",0.5]")] + [InlineData("urm5", ",PARAMETER[\"n\",0.5]")] + [InlineData("Urmaev_V", ",PARAMETER[\"n\",0.5]")] + [InlineData("wag1", null)] + [InlineData("Wagner_I", null)] + public void SupportsAliasesFromWkt(string projectionName, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for covered projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + /// Optional WKT parameter segment. + [Theory] + [InlineData("cc", 2d, 1d, 223402.144255274d, 111712.415540593d, null)] + [InlineData("gn_sinu", 2d, 1d, 223385.132504696d, 111698.236447187d, ",PARAMETER[\"m\",1],PARAMETER[\"n\",2]")] + [InlineData("eck6", 2d, 1d, 197021.605628992d, 126640.420733174d, null)] + [InlineData("mbtfps", 2d, 1d, 204740.117478572d, 121864.729719340d, null)] + [InlineData("urm5", 2d, 1d, 223393.638433964d, 111696.818785117d, ",PARAMETER[\"n\",0.5]")] + [InlineData("urmfps", 2d, 1d, 196001.708134192d, 127306.843329993d, ",PARAMETER[\"n\",0.5]")] + [InlineData("wag1", 2d, 1d, 195986.781561158d, 127310.075060660d, null)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY, + string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable covered projections. + /// + /// Projection code. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + /// Optional WKT parameter segment. + [Theory] + [InlineData("cc", 200d, 100d, 0.001790493d, 0.000895247d, null)] + [InlineData("gn_sinu", 200d, 100d, 0.001790493d, 0.000895247d, ",PARAMETER[\"m\",1],PARAMETER[\"n\",2]")] + [InlineData("eck6", 200d, 100d, 0.002029979d, 0.000789630d, null)] + [InlineData("mbtfps", 200d, 100d, 0.001953415d, 0.000820580d, null)] + [InlineData("urmfps", 200d, 100d, 0.002040721d, 0.000785474d, ",PARAMETER[\"n\",0.5]")] + [InlineData("wag1", 200d, 100d, 0.002040721d, 0.000785474d, null)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies Urmaev V remains forward-only in this test set. + /// + [Fact] + public void Urm5DoesNotSupportInverse() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("urm5", false, ",PARAMETER[\"n\",0.5]")); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies Urmaev V rejects invalid n/alpha combinations. + /// + [Fact] + public void Urm5RejectsInvalidNAlphaCombination() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("urm5", false, ",PARAMETER[\"n\",1],PARAMETER[\"alpha\",90]")); + Assert.Throws(() => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected)); + } + + /// + /// Verifies roundtrip stability for inverse-capable covered projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Optional WKT parameter segment. + [Theory] + [InlineData("cc", 2d, 1d, null)] + [InlineData("gn_sinu", -2d, -1d, ",PARAMETER[\"m\",1],PARAMETER[\"n\",2]")] + [InlineData("eck6", 2d, -1d, null)] + [InlineData("mbtfps", -2d, 1d, null)] + [InlineData("urmfps", 2d, 1d, ",PARAMETER[\"n\",0.5]")] + [InlineData("wag1", -2d, -1d, null)] + public void SupportsRoundtrip(string projectionName, double longitude, double latitude, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-9); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-9); + } + + private static string BuildProjectedWkt(string projectionName, bool useWgs84, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + string spheroidClause = useWgs84 + ? "SPHEROID[\"WGS 84\",6378137,298.257223563]" + : "SPHEROID[\"Sphere\",6400000,0]"; + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/ConicAndEqualAreaMiscProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/ConicAndEqualAreaMiscProjectionTests.cs new file mode 100644 index 00000000..7deb9ea0 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/ConicAndEqualAreaMiscProjectionTests.cs @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates conic and equal-area miscellaneous projections. +/// +public class ConicAndEqualAreaMiscProjectionTests +{ + private const string Sphere6390000 = "SPHEROID[\"Sphere\",6390000,0]"; + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for conic and equal-area miscellaneous projections. + /// + /// Projection alias. + [Theory] + [InlineData("kav5")] + [InlineData("Kavrayskiy_V")] + [InlineData("qua_aut")] + [InlineData("Quartic_Authalic")] + [InlineData("fouc")] + [InlineData("Foucaut")] + [InlineData("mbt_s")] + [InlineData("McBryde_Thomas_Flat_Polar_Sine")] + [InlineData("ccon")] + [InlineData("Central_Conic")] + [InlineData("lcca")] + [InlineData("Lambert_Conformal_Conic_Alternative")] + [InlineData("ocea")] + [InlineData("Oblique_Cylindrical_Equal_Area")] + [InlineData("oea")] + [InlineData("Oblated_Equal_Area")] + [InlineData("rpoly")] + [InlineData("Rectangular_Polyconic")] + [InlineData("tpeqd")] + [InlineData("Two_Point_Equidistant")] + public void SupportsConicAndEqualAreaMiscAliasesFromWkt(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildAliasWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for conic and equal-area miscellaneous projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + /// Absolute tolerance. + [Theory] + [InlineData("kav5", 2d, 1d, 200360.905308829d, 123685.082476998d, 1e-6d)] + [InlineData("qua_aut", 2d, 1d, 222613.549033097d, 111318.077887984d, 1e-6d)] + [InlineData("fouc", 2d, 1d, 222588.120675892d, 111322.316700694d, 1e-6d)] + [InlineData("mbt_s", 2d, 1d, 204131.517850273d, 121400.330225508d, 1e-6d)] + [InlineData("lcca", 2d, 1d, 222605.285770237d, 67.806007272d, 1e-6d)] + [InlineData("ocea", 2d, 1d, 19994423.837934088d, 223322.760576728d, 1e-3d)] + [InlineData("oea", 2d, 1d, 228926.872097864d, 99870.488430076d, 1e-6d)] + [InlineData("rpoly", 2d, 1d, 223368.098302014d, 111769.110486991d, 1e-6d)] + [InlineData("tpeqd", 2d, 1d, -27750.758831679d, -222599.403691777d, 1e-6d)] + [InlineData("ccon", 24d, 55d, 650031.5410941322d, -4106.161777064670d, 1e-6d)] + public void MatchesProjBuiltinsForwardVectors(string projectionName, double longitude, double latitude, double expectedX, double expectedY, double tolerance) + { + ArgumentNullException.ThrowIfNull(projectionName); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildCanonicalWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable conic and equal-area miscellaneous projections. + /// + /// Projection code. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + /// Absolute tolerance. + [Theory] + [InlineData("kav5", 200d, 100d, 0.001996259d, 0.000808483d, 2e-9d)] + [InlineData("qua_aut", 200d, 100d, 0.001796631d, 0.000898315d, 2e-9d)] + [InlineData("fouc", 200d, 100d, 0.001796631d, 0.000898315d, 2e-9d)] + [InlineData("mbt_s", 200d, 100d, 0.001959383d, 0.000823699d, 2e-9d)] + [InlineData("lcca", 200d, 100d, 0.001796903d, 1.000904366d, 2e-9d)] + [InlineData("ocea", 200d, 100d, 179.999104753d, 0.001790493d, 2e-9d)] + [InlineData("oea", 200d, 100d, 0.001741186d, 0.000987727d, 2e-9d)] + [InlineData("tpeqd", 200d, 100d, -0.000898556d, 1.251796630d, 2e-9d)] + [InlineData("ccon", 330000d, -350000d, 19d, 52d, 2e-11d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ArgumentNullException.ThrowIfNull(projectionName); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildCanonicalWkt(projectionName)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for inverse-capable conic and equal-area miscellaneous projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData("kav5", 2d, 1d)] + [InlineData("qua_aut", -2d, -1d)] + [InlineData("fouc", 2d, -1d)] + [InlineData("mbt_s", -2d, 1d)] + [InlineData("lcca", 2d, 1d)] + [InlineData("ocea", 2d, 1d)] + [InlineData("oea", 2d, 1d)] + [InlineData("tpeqd", 2d, 1d)] + [InlineData("ccon", 24d, 55d)] + public void SupportsConicAndEqualAreaMiscRoundtrip(string projectionName, double longitude, double latitude) + { + ArgumentNullException.ThrowIfNull(projectionName); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildCanonicalWkt(projectionName)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-7d); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-7d); + } + + /// + /// Verifies rpoly remains forward-only. + /// + [Fact] + public void RectangularPolyconicDoesNotSupportInverse() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildRpolyWkt("rpoly")); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies ocea alpha/lonc mode matches builtins vectors. + /// + [Fact] + public void ObliqueCylindricalEqualAreaSupportsAlphaLoncMode() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildOceaAlphaWkt("ocea")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - 19994423.837934091687d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 223322.760576728586d), 0d, 1e-6d); + } + + /// + /// Verifies ocea two-point mode defaults omitted longitudes to zero like PROJ. + /// + [Fact] + public void ObliqueCylindricalEqualAreaSupportsImplicitZeroLongitudesInTwoPointMode() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildOceaTwoPointWithoutLongitudesWkt("ocea")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - 19994423.837934088d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 223322.760576728d), 0d, 1e-6d); + } + + /// + /// Verifies ocea alpha mode defaults an omitted lonc parameter to zero like PROJ. + /// + [Fact] + public void ObliqueCylindricalEqualAreaSupportsImplicitZeroLongitudeOfCenter() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildOceaAlphaWithoutLoncWkt("ocea")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - 19994423.837934091687d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 223322.760576728586d), 0d, 1e-6d); + } + + /// + /// Verifies tpeqd defaults omitted control-point longitudes to zero like PROJ. + /// + [Fact] + public void TwoPointEquidistantSupportsImplicitZeroLongitudes() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildTpeqdWithoutLongitudesWkt("tpeqd")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - -27845.882978485d), 0d, 1e-6d); + Assert.InRange(Math.Abs(projectedPoint[1] - -223362.430695260d), 0d, 1e-6d); + } + + /// + /// Verifies tpeqd rejects degenerate pole control points. + /// + [Fact] + public void TwoPointEquidistantRejectsDegeneratePolarControlPoints() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildTpeqdDegenerateWkt()); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected)); + } + + private static string BuildAliasWkt(string projectionName) + { + if (projectionName.Equals("ccon", StringComparison.OrdinalIgnoreCase) + || projectionName.Equals("central_conic", StringComparison.OrdinalIgnoreCase)) + { + return BuildCconWkt(projectionName); + } + + if (projectionName.Equals("lcca", StringComparison.OrdinalIgnoreCase) + || projectionName.Equals("lambert_conformal_conic_alternative", StringComparison.OrdinalIgnoreCase)) + { + return BuildLccaWkt(projectionName); + } + + if (projectionName.Equals("ocea", StringComparison.OrdinalIgnoreCase) + || projectionName.Equals("oblique_cylindrical_equal_area", StringComparison.OrdinalIgnoreCase)) + { + return BuildOceaTwoPointWkt(projectionName); + } + + if (projectionName.Equals("oea", StringComparison.OrdinalIgnoreCase) + || projectionName.Equals("oblated_equal_area", StringComparison.OrdinalIgnoreCase)) + { + return BuildOeaWkt(projectionName); + } + + if (projectionName.Equals("rpoly", StringComparison.OrdinalIgnoreCase) + || projectionName.Equals("rectangular_polyconic", StringComparison.OrdinalIgnoreCase)) + { + return BuildRpolyWkt(projectionName); + } + + return projectionName.Equals("tpeqd", StringComparison.OrdinalIgnoreCase) + || projectionName.Equals("two_point_equidistant", StringComparison.OrdinalIgnoreCase) + ? BuildTpeqdWkt(projectionName) + : BuildProjectedWkt(projectionName, Grs80, null); + } + + private static string BuildCanonicalWkt(string projectionName) + { + return projectionName.ToUpperInvariant() switch + { + "CCON" => BuildCconWkt("ccon"), + "LCCA" => BuildLccaWkt("lcca"), + "OCEA" => BuildOceaTwoPointWkt("ocea"), + "OEA" => BuildOeaWkt("oea"), + "RPOLY" => BuildRpolyWkt("rpoly"), + "TPEQD" => BuildTpeqdWkt("tpeqd"), + _ => BuildProjectedWkt(projectionName, Grs80, null), + }; + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, string? extraParameters) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static string BuildCconWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6390000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",52],PARAMETER[\"central_meridian\",19],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",330000],PARAMETER[\"false_northing\",-350000],PARAMETER[\"lat_1\",52],UNIT[\"metre\",1]]"); + } + + private static string BuildLccaWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Grs80}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",1],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2],UNIT[\"metre\",1]]"); + } + + private static string BuildOceaTwoPointWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6400000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2],PARAMETER[\"lon_1\",0],PARAMETER[\"lon_2\",0],UNIT[\"metre\",1]]"); + } + + private static string BuildOceaAlphaWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}-alpha\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6400000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",45],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"alpha\",0],PARAMETER[\"lonc\",0],UNIT[\"metre\",1]]"); + } + + private static string BuildOceaTwoPointWithoutLongitudesWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}-implicit-lon\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6400000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2],UNIT[\"metre\",1]]"); + } + + private static string BuildOceaAlphaWithoutLoncWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}-implicit-lonc\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6400000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",45],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"alpha\",0],UNIT[\"metre\",1]]"); + } + + private static string BuildOeaWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6400000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"n\",1],PARAMETER[\"m\",2],PARAMETER[\"theta\",3],UNIT[\"metre\",1]]"); + } + + private static string BuildRpolyWkt(string projectionName) + { + return BuildProjectedWkt(projectionName, Sphere6400000, null); + } + + private static string BuildTpeqdWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Grs80}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2],PARAMETER[\"lon_1\",0],PARAMETER[\"lon_2\",0],UNIT[\"metre\",1]]"); + } + + private static string BuildTpeqdWithoutLongitudesWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-C-{projectionName}-implicit-lon\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6400000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2],UNIT[\"metre\",1]]"); + } + + private static string BuildTpeqdDegenerateWkt() + { + return "PROJCS[\"Specialty-C-tpeqd-degenerate\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"tpeqd\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"lat_1\",90],PARAMETER[\"lat_2\",90],PARAMETER[\"lon_1\",0],PARAMETER[\"lon_2\",1],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/ConicProjectionSupportTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/ConicProjectionSupportTests.cs new file mode 100644 index 00000000..bf9cfedd --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/ConicProjectionSupportTests.cs @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests support for conic map projections, verifying alias resolution from WKT and forward/inverse coordinate roundtrip accuracy. +/// +public class ConicProjectionSupportTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Validates that Equidistant Conic aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("eqdc")] + [InlineData("equidistant_conic")] + [InlineData("equidistant_conic_(spherical)")] + public void SupportsEqdcProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Validates that the Equidistant Conic projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(9.6d, 43.2d, 1e-8d)] + public void SupportsEqdcProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("eqdc")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Validates that Bonne aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("bonne")] + public void SupportsBonneProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Validates that the Bonne projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(12.8d, 31.4d, 1e-8d)] + public void SupportsBonneProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("bonne")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Validates that Perspective Conic aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("pconic")] + [InlineData("perspective_conic")] + public void SupportsPconicProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Validates that the Perspective Conic projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(-7.25d, 44.1d, 1e-8d)] + public void SupportsPconicProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("pconic")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName) + { + return + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"standard_parallel_1\",20],PARAMETER[\"standard_parallel_2\",50],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) + { + return [x, y]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/CylindricalProjectionSupportTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/CylindricalProjectionSupportTests.cs new file mode 100644 index 00000000..f61f3fc0 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/CylindricalProjectionSupportTests.cs @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests support for cylindrical map projections, verifying alias resolution from WKT and forward/inverse coordinate roundtrip accuracy. +/// +public class CylindricalProjectionSupportTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that Miller projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("mill")] + [InlineData("miller")] + [InlineData("miller_cylindrical")] + public void SupportsMillerProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Miller cylindrical projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(17.45d, -23.1d, 1e-6d)] + public void SupportsMillerProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("mill")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that EQC projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("eqc")] + [InlineData("equidistant_cylindrical")] + [InlineData("plate_carree")] + [InlineData("equirectangular")] + public void SupportsEqcProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Equidistant Cylindrical projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(-11.25d, 31.8d, 1e-8d)] + public void SupportsEqcProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("eqc")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that EQC honors true-scale latitude aliases instead of silently falling back to the equatorial default. + /// + [Theory] + [InlineData("lat_ts")] + [InlineData("latitude_true_scale")] + [InlineData("latitude_of_true_scale")] + public void SupportsEqcTrueScaleLatitudeAliases(string parameterName) + { + ProjectedCoordinateSystem projectedWithAlias = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildEqcAliasWkt(parameterName)); + ProjectedCoordinateSystem projectedWithCanonicalParameter = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildEqcAliasWkt("standard_parallel_1")); + ICoordinateTransformation aliasForward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projectedWithAlias.GeographicCoordinateSystem, projectedWithAlias); + ICoordinateTransformation canonicalForward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projectedWithCanonicalParameter.GeographicCoordinateSystem, projectedWithCanonicalParameter); + + double[] aliasPoint = aliasForward.MathTransform.Transform(CreatePoint(2d, 0d)); + double[] canonicalPoint = canonicalForward.MathTransform.Transform(CreatePoint(2d, 0d)); + + Assert.InRange(System.Math.Abs(aliasPoint[0] - canonicalPoint[0]), 0d, 1e-9d); + Assert.InRange(System.Math.Abs(aliasPoint[1] - canonicalPoint[1]), 0d, 1e-12d); + } + + /// + /// Verifies PROJ builtins forward vectors for the ellipsoidal EQC path. + /// + /// The latitude of true scale in degrees. + /// The latitude of natural origin in degrees. + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Expected easting in metres. + /// Expected northing in metres. + /// Allowed absolute tolerance in metres. + [Theory] + [InlineData(0d, 0d, 10d, 55d, 1113194.91d, 6097230.31d, 0.05d)] + [InlineData(45d, 0d, 2d, 49d, 157693.670d, 5429627.632d, 0.05d)] + [InlineData(30d, 45d, 0d, 60d, 0d, 1669128.442d, 0.05d)] + public void MatchesEqcEllipsoidalForwardVectors( + double standardParallel, + double latitudeOfOrigin, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildEqcProjectedWkt(true, "lat_ts", standardParallel, latitudeOfOrigin)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(System.Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(System.Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for the ellipsoidal EQC path. + /// + /// The latitude of true scale in degrees. + /// The latitude of natural origin in degrees. + /// Input easting in metres. + /// Input northing in metres. + /// Expected longitude in degrees. + /// Expected latitude in degrees. + /// Allowed absolute tolerance in degrees. + [Theory] + [InlineData(0d, 0d, 1113194.91d, 6097230.31d, 10d, 55d, 1e-7d)] + [InlineData(45d, 0d, 157693.670d, 5429627.632d, 2d, 49d, 1e-7d)] + [InlineData(30d, 45d, 0d, 1669128.442d, 0d, 60d, 1e-7d)] + public void MatchesEqcEllipsoidalInverseVectors( + double standardParallel, + double latitudeOfOrigin, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildEqcProjectedWkt(true, "lat_ts", standardParallel, latitudeOfOrigin)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(System.Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies that CEA projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("cea")] + [InlineData("cylindrical_equal_area")] + [InlineData("lambert_cylindrical_equal_area")] + [InlineData("equal_area_cylindrical")] + public void SupportsCeaProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Cylindrical Equal Area projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(42.6d, 14.2d, 1e-8d)] + public void SupportsCeaProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("cea")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that loxim projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("loxim")] + [InlineData("loximuthal")] + public void SupportsLoximProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Loximuthal projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(15.75d, -9.4d, 1e-8d)] + public void SupportsLoximProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("loxim")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that patterson projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("patterson")] + public void SupportsPattersonProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Patterson projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(-98.2d, 37.9d, 1e-8d)] + public void SupportsPattersonProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("patterson")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName) + { + return + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static string BuildEqcAliasWkt(string parameterName) + { + return BuildEqcProjectedWkt(false, parameterName, 45d, 0d); + } + + private static string BuildEqcProjectedWkt(bool useWgs84, string standardParallelParameterName, double standardParallelDegrees, double latitudeOfOriginDegrees) + { + string spheroidClause = useWgs84 + ? "SPHEROID[\"WGS 84\",6378137,298.257223563]" + : "SPHEROID[\"Sphere\",6400000,0]"; + return System.FormattableString.Invariant( + $"PROJCS[\"Projection-eqc\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"eqc\"],PARAMETER[\"latitude_of_origin\",{latitudeOfOriginDegrees}],PARAMETER[\"central_meridian\",0],PARAMETER[\"{standardParallelParameterName}\",{standardParallelDegrees}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) + { + return [x, y]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/EckertProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/EckertProjectionTests.cs new file mode 100644 index 00000000..058e647f --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/EckertProjectionTests.cs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Eckert I-V projection support. +/// +public class EckertProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for eckert projections. + /// + /// Projection alias. + [Theory] + [InlineData("eck1")] + [InlineData("Eckert_I")] + [InlineData("eck2")] + [InlineData("Eckert_II")] + [InlineData("eck3")] + [InlineData("Eckert_III")] + [InlineData("eck4")] + [InlineData("Eckert_IV")] + [InlineData("eck5")] + [InlineData("Eckert_V")] + public void SupportsAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for Eckert I-V. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + [Theory] + [InlineData("eck1", 2d, 1d, 204680.888202951d, 102912.178426065d)] + [InlineData("eck1", -2d, -1d, -204680.888202951d, -102912.178426065d)] + [InlineData("eck2", 2d, 1d, 204472.870907960d, 121633.734975242d)] + [InlineData("eck2", -2d, -1d, -204472.870907960d, -121633.734975242d)] + [InlineData("eck3", 2d, 1d, 188652.015721538d, 94328.919337031d)] + [InlineData("eck3", -2d, -1d, -188652.015721538d, -94328.919337031d)] + [InlineData("eck4", 2d, 1d, 188646.389356416d, 132268.540174065d)] + [InlineData("eck4", -2d, -1d, -188646.389356416d, -132268.540174065d)] + [InlineData("eck5", 2d, 1d, 197031.392134061d, 98523.198847227d)] + [InlineData("eck5", -2d, -1d, -197031.392134061d, -98523.198847227d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-6); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-6); + } + + /// + /// Verifies PROJ builtins inverse vectors for Eckert I-V. + /// + /// Projection code. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + [Theory] + [InlineData("eck1", 200d, 100d, 0.001943415d, 0.000971702d)] + [InlineData("eck1", -200d, -100d, -0.001943415d, -0.000971702d)] + [InlineData("eck2", 200d, 100d, 0.001943415d, 0.000824804d)] + [InlineData("eck2", -200d, -100d, -0.001943415d, -0.000824804d)] + [InlineData("eck3", 200d, 100d, 0.002120241d, 0.001060120d)] + [InlineData("eck3", -200d, -100d, -0.002120241d, -0.001060120d)] + [InlineData("eck4", 200d, 100d, 0.002120241d, 0.000756015d)] + [InlineData("eck4", -200d, -100d, -0.002120241d, -0.000756015d)] + [InlineData("eck5", 200d, 100d, 0.002029979d, 0.001014989d)] + [InlineData("eck5", -200d, -100d, -0.002029979d, -0.001014989d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies eck4 edge-of-domain vectors from builtins. + /// + /// Projected x meters. + /// Projected y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + [Theory] + [InlineData(-8489602.74033281d, 8489602.74033281d, -180d, 90d)] + [InlineData(8489602.74033281d, 8489602.74033281d, 180d, 90d)] + [InlineData(-16979205.4807d, 0d, -180d, 0d)] + [InlineData(16979205.4807d, 0d, 180d, 0d)] + [InlineData(-8489602.74033281d, -8489602.74033281d, -180d, -90d)] + [InlineData(8489602.74033281d, -8489602.74033281d, 180d, -90d)] + public void MatchesProjBuiltinsEck4EdgeCases(double x, double y, double expectedLon, double expectedLat) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("eck4")); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLon), 0d, 1e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLat), 0d, 1e-9); + } + + /// + /// Verifies eck4 out-of-domain inverse inputs are rejected. + /// + /// Projected x meters. + /// Projected y meters. + [Theory] + [InlineData(-8489602.75d, 8489602.74033281d)] + [InlineData(8489602.75d, 8489602.74033281d)] + [InlineData(0d, 8489602.75d)] + [InlineData(-16979205.49d, 0d)] + [InlineData(16979205.49d, 0d)] + [InlineData(-8489602.75d, -8489602.74033281d)] + [InlineData(8489602.75d, -8489602.74033281d)] + [InlineData(0d, -8489602.75d)] + public void RejectsEck4OutsideProjectionDomain(double x, double y) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("eck4")); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + Assert.Throws(() => inverse.MathTransform.Transform(CreatePoint(x, y))); + } + + private static string BuildProjectedWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/EllipsoidalProjectionRegressionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/EllipsoidalProjectionRegressionTests.cs new file mode 100644 index 00000000..f5ef8f38 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/EllipsoidalProjectionRegressionTests.cs @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Contains regression tests for ellipsoidal projection paths that previously used spherical formulas. +/// +public class EllipsoidalProjectionRegressionTests +{ + private const string Wgs84 = "SPHEROID[\"WGS 84\",6378137,298.257223563]"; + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies Equal Earth ellipsoidal vectors from PROJ builtins. + /// + [Fact] + public void EqualEarthEllipsoidalMatchesProjBuiltinsVectors() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("eqearth", Wgs84, null)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(-70d, -31.2d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - (-6241081.64d)), 0d, 1e-2d); + Assert.InRange(Math.Abs(projectedPoint[1] - (-3907019.16d)), 0d, 1e-2d); + + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected, + projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(-6241081.64d, -3907019.16d)); + + Assert.InRange(Math.Abs(geographicPoint[0] - (-70d)), 0d, 5e-8d); + Assert.InRange(Math.Abs(geographicPoint[1] - (-31.2d)), 0d, 5e-8d); + } + + /// + /// Verifies Equidistant Conic ellipsoidal vectors from PROJ builtins. + /// + [Fact] + public void EquidistantConicEllipsoidalMatchesProjBuiltinsVectors() + { + const string parameters = ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]"; + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("eqdc", Grs80, parameters)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - 222588.440269286d), 0d, 1e-4d); + Assert.InRange(Math.Abs(projectedPoint[1] - 110659.134907347d), 0d, 1e-4d); + + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected, + projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(200d, 100d)); + + Assert.InRange(Math.Abs(geographicPoint[0] - 0.001796359d), 0d, 1e-9d); + Assert.InRange(Math.Abs(geographicPoint[1] - 0.000904369d), 0d, 1e-9d); + } + + /// + /// Verifies Cylindrical Equal Area ellipsoidal vectors from PROJ builtins. + /// + [Fact] + public void CylindricalEqualAreaEllipsoidalMatchesProjBuiltinsVectors() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("cea", Grs80, null)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - 222638.981586547d), 0d, 1e-4d); + Assert.InRange(Math.Abs(projectedPoint[1] - 110568.812396267d), 0d, 1e-4d); + + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected, + projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(16697923.6190d, 4865983.5552d)); + + Assert.InRange(Math.Abs(geographicPoint[0] - 150d), 0d, 1e-8d); + Assert.InRange(Math.Abs(geographicPoint[1] - 50d), 0d, 1e-8d); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, string? extraParameters) + { + return FormattableString.Invariant( + $"PROJCS[\"Regression-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) + { + return [x, y]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/EqualAreaProjectionSupportTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/EqualAreaProjectionSupportTests.cs new file mode 100644 index 00000000..3dd91b08 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/EqualAreaProjectionSupportTests.cs @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests support for equal-area map projections, verifying alias resolution from WKT and forward/inverse coordinate roundtrip accuracy. +/// +public class EqualAreaProjectionSupportTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that hammer projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("hammer")] + public void SupportsHammerProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Hammer projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(12.3d, -28.75d, 1e-7d)] + public void SupportsHammerProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("hammer")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that sinusoidal projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("sinu")] + [InlineData("sinusoidal")] + public void SupportsSinusoidalProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Sinusoidal projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(-5.9d, 47.2d, 1e-7d)] + public void SupportsSinusoidalProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("sinu")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that Goode projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("goode")] + [InlineData("goode_homolosine")] + public void SupportsGoodeProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Goode Homolosine projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(17.6d, 34.15d, 2e-5d)] + public void SupportsGoodeProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("goode")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that IGH projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("igh")] + [InlineData("interrupted_goode_homolosine")] + public void SupportsIghProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the Interrupted Goode Homolosine projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(20d, -22d, 2e-5d)] + public void SupportsIghProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("igh")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies that healpix projection aliases can be parsed from WKT. + /// + /// The projection alias under test. + [Theory] + [InlineData("healpix")] + public void SupportsHealpixProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the HEALPix projection supports a forward/inverse coordinate roundtrip within the expected tolerance. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Allowed roundtrip tolerance. + [Theory] + [InlineData(45d, 35d, 1e-6d)] + public void SupportsHealpixProjectionRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("healpix")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName) + { + return + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) + { + return [x, y]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/ExtendedTransverseMercatorProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/ExtendedTransverseMercatorProjectionTests.cs new file mode 100644 index 00000000..c529f447 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/ExtendedTransverseMercatorProjectionTests.cs @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates exact ETMERC support against PROJ reference vectors. +/// +public class ExtendedTransverseMercatorProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that ETMERC aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("etmerc")] + [InlineData("ETMERC")] + [InlineData("Extended_Transverse_Mercator")] + public void SupportsEtmercAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies that the PROJ-style transverse Mercator aliases resolve to the exact kernel for ellipsoidal inputs. + /// + /// Projection alias to validate. + [Theory] + [InlineData("tmerc")] + [InlineData("Gauss_Kruger")] + public void ProjStyleTransverseMercatorAliasesUseExactKernel(string projectionName) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(44.69d, 35.37d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - 4168136.489446198d), 0d, 1e-6d); + Assert.InRange(Math.Abs(projectedPoint[1] - 4985511.302287407d), 0d, 1e-6d); + } + + /// + /// Verifies forward ETMERC vectors from PROJ builtins, including the wide-offset hotspot. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Expected easting in metres. + /// Expected northing in metres. + [Theory] + [InlineData(2d, 1d, 222650.796797586d, 110642.229411933d)] + [InlineData(44.69d, 35.37d, 4168136.489446198d, 4985511.302287407d)] + public void MatchesProjBuiltinsForwardVectors( + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("etmerc")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-6d); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-6d); + } + + /// + /// Verifies inverse ETMERC vectors from PROJ builtins, including the wide-offset hotspot. + /// + /// Input easting in metres. + /// Input northing in metres. + /// Expected longitude in degrees. + /// Expected latitude in degrees. + [Theory] + [InlineData(200d, 100d, 0.00179663056816d, 0.00090436947663d)] + [InlineData(4168136.489446198d, 4985511.302287407d, 44.69d, 35.37d)] + public void MatchesProjBuiltinsInverseVectors( + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("etmerc")); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 1e-10d); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 1e-10d); + } + + /// + /// Verifies forward/inverse roundtrip stability for the exact ETMERC kernel. + /// + [Fact] + public void SupportsWideOffsetRoundtrip() + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("etmerc")); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(44.69d, 35.37d)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - 44.69d), 0d, 1e-10d); + Assert.InRange(Math.Abs(roundtrip[1] - 35.37d), 0d, 1e-10d); + } + + private static string BuildProjectedWkt(string projectionName) + { + return FormattableString.Invariant( + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"GRS 80\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/GaussSchreiberTransverseMercatorProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/GaussSchreiberTransverseMercatorProjectionTests.cs new file mode 100644 index 00000000..cc5ad691 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/GaussSchreiberTransverseMercatorProjectionTests.cs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Gauss-Schreiber Transverse Mercator (gstmerc) projection support. +/// +public class GaussSchreiberTransverseMercatorProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that gstmerc aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("gstmerc")] + [InlineData("Gauss_Schreiber_Transverse_Mercator")] + [InlineData("Gauss_Laborde_Reunion")] + public void SupportsGstmercAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward values against PROJ builtins vectors for gstmerc. + /// + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData(2d, 1d, 223413.466406322d, 111769.145040586d)] + [InlineData(2d, -1d, 223413.466406322d, -111769.145040587d)] + [InlineData(-2d, 1d, -223413.466406323d, 111769.145040586d)] + [InlineData(-2d, -1d, -223413.466406323d, -111769.145040587d)] + public void MatchesProjBuiltinsForwardVectors( + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("gstmerc")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies inverse values against PROJ builtins vectors for gstmerc. + /// + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData(200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData(200d, -100d, 0.001790493d, -0.000895247d)] + [InlineData(-200d, 100d, -0.001790493d, 0.000895247d)] + [InlineData(-200d, -100d, -0.001790493d, -0.000895247d)] + public void MatchesProjBuiltinsInverseVectors( + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("gstmerc")); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies forward/inverse roundtrip stability for gstmerc. + /// + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Maximum absolute roundtrip delta (degrees). + [Theory] + [InlineData(2d, 1d, 1e-9)] + [InlineData(-2d, -1d, 1e-9)] + public void SupportsGstmercRoundtrip(double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("gstmerc")); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/GeostationaryProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/GeostationaryProjectionTests.cs new file mode 100644 index 00000000..fcbc3538 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/GeostationaryProjectionTests.cs @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates geostationary satellite (geos) projection support. +/// +public class GeostationaryProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that geos aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("geos")] + [InlineData("Geostationary_Satellite")] + public void SupportsGeosAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, 6378137d, 298.257222101d, 35785831d)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward/inverse roundtrip stability for geos. + /// + /// Projection alias to validate. + /// Whether to use a spherical ellipsoid definition. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Maximum absolute roundtrip delta (degrees). + [Theory] + [InlineData("geos", false, 2d, 1d, 1e-9)] + [InlineData("geos", false, -2d, -1d, 1e-9)] + [InlineData("Geostationary_Satellite", true, -2d, 1d, 1e-9)] + public void SupportsGeosRoundtrip(string projectionName, bool useSphere, double longitude, double latitude, double tolerance) + { + double semiMajor = useSphere ? 6400000d : 6378137d; + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(projectionName, useSphere, semiMajor, 298.257222101d, 35785831d)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies forward values against PROJ builtins vectors for ellipsoidal and spherical geos. + /// + /// Projection alias to validate. + /// Whether to use a spherical ellipsoid definition. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData("geos", false, 2d, 1d, 222527.070365800d, 110551.303413329d)] + [InlineData("geos", false, -2d, -1d, -222527.070365800d, -110551.303413329d)] + [InlineData("geos", true, 2d, 1d, 223289.457635795d, 111677.657456537d)] + [InlineData("Geostationary_Satellite", true, -2d, -1d, -223289.457635795d, -111677.657456537d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + bool useSphere, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + double semiMajor = useSphere ? 6400000d : 6378137d; + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(projectionName, useSphere, semiMajor, 298.257222101d, 35785831d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies inverse values against PROJ builtins vectors for ellipsoidal and spherical geos. + /// + /// Projection alias to validate. + /// Whether to use a spherical ellipsoid definition. + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData("geos", false, 200d, 100d, 0.001796631d, 0.000904369d)] + [InlineData("geos", false, -200d, -100d, -0.001796631d, -0.000904369d)] + [InlineData("geos", true, 200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData("Geostationary_Satellite", true, -200d, -100d, -0.001790493d, -0.000895247d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + bool useSphere, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + double semiMajor = useSphere ? 6400000d : 6378137d; + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(projectionName, useSphere, semiMajor, 298.257222101d, 35785831d)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies invalid satellite height values are rejected. + /// + /// Height parameter to validate. + [Theory] + [InlineData(0d)] + [InlineData(1e11d)] + public void RejectsInvalidHeightValues(double satelliteHeight) + { + Assert.Throws(() => + { + string wkt = BuildProjectedWkt("geos", true, 1d, 0d, satelliteHeight); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + forward.MathTransform.Transform(CreatePoint(2d, 1d)); + }); + } + + /// + /// Verifies optional sweep parameter is accepted and changes axis handling. + /// + [Fact] + public void SupportsSweepXParameter() + { + string wkt = BuildProjectedWkt("geos", true, 6400000d, 0d, 35785831d, sweepX: true); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + Assert.True(Math.Abs(projectedPoint[0]) > 0d); + Assert.True(Math.Abs(projectedPoint[1]) > 0d); + } + + private static string BuildProjectedWkt(string projectionName, bool useSphere, double semiMajor, double inverseFlattening, double satelliteHeight, bool sweepX = false) + { + string spheroid = useSphere + ? FormattableString.Invariant($"SPHEROID[\"Sphere\",{semiMajor},0]") + : FormattableString.Invariant($"SPHEROID[\"GRS 80\",{semiMajor},{inverseFlattening}]"); + + string hText = satelliteHeight.ToString(CultureInfo.InvariantCulture); + string sweepParameter = sweepX + ? ",PARAMETER[\"sweep_x\",1]" + : string.Empty; + + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroid}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"h\",{hText}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{sweepParameter},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/GlobularAndMiscProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/GlobularAndMiscProjectionTests.cs new file mode 100644 index 00000000..96881b8c --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/GlobularAndMiscProjectionTests.cs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates globular and miscellaneous specialty projections. +/// +public class GlobularAndMiscProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for globular and miscellaneous projections. + /// + /// Projection alias. + /// Optional WKT parameter segment. + [Theory] + [InlineData("august", null)] + [InlineData("August_Epicycloidal", null)] + [InlineData("bacon", null)] + [InlineData("Bacon_Globular", null)] + [InlineData("apian", null)] + [InlineData("Apian_Globular_I", null)] + [InlineData("ortel", null)] + [InlineData("Ortelius_Oval", null)] + [InlineData("comill", null)] + [InlineData("Compact_Miller", null)] + [InlineData("denoy", null)] + [InlineData("Denoyer_Semi_Elliptical", null)] + [InlineData("fouc_s", null)] + [InlineData("Foucaut_Sinusoidal", null)] + [InlineData("gins8", null)] + [InlineData("Ginsburg_VIII", null)] + [InlineData("lagrng", ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"W\",2]")] + [InlineData("Lagrange", ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"W\",2]")] + [InlineData("larr", null)] + [InlineData("Larrivee", null)] + [InlineData("lask", null)] + [InlineData("Laskowski", null)] + [InlineData("tcc", null)] + [InlineData("Transverse_Central_Cylindrical", null)] + public void SupportsGlobularAndMiscAliasesFromWkt(string projectionName, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for globular and miscellaneous projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + /// Optional WKT parameter segment. + [Theory] + [InlineData("august", 2d, 1d, 223404.978180972d, 111722.340289763d, null)] + [InlineData("bacon", 2d, 1d, 223334.132555965d, 175450.725922666d, null)] + [InlineData("apian", 2d, 1d, 223374.577355253d, 111701.072127637d, null)] + [InlineData("ortel", 2d, 1d, 223374.577355253d, 111701.072127637d, null)] + [InlineData("comill", 2d, 1d, 223402.144255274d, 110611.859089459d, null)] + [InlineData("denoy", 2d, 1d, 223377.422876954d, 111701.072127637d, null)] + [InlineData("fouc_s", 2d, 1d, 223402.144255274d, 111695.401198614d, null)] + [InlineData("gins8", 2d, 1d, 194350.250939590d, 111703.907635335d, null)] + [InlineData("lagrng", 2d, 1d, 111703.375917226d, 27929.831908033d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"W\",2]")] + [InlineData("larr", 2d, 1d, 223393.637624201d, 111707.215961256d, null)] + [InlineData("lask", 2d, 1d, 217928.275907355d, 112144.329220142d, null)] + [InlineData("tcc", 2d, 1d, 223458.844192458d, 111769.145040586d, null)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY, + string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable globular and miscellaneous projections. + /// + /// Projection code. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + /// Optional WKT parameter segment. + [Theory] + [InlineData("comill", 200d, 100d, 0.001790493d, 0.000904107d, null)] + [InlineData("fouc_s", 200d, 100d, 0.001790493d, 0.000895247d, null)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies forward-only globular and miscellaneous projections reject inverse. + /// + /// Projection code. + /// Optional WKT parameter segment. + [Theory] + [InlineData("august", null)] + [InlineData("bacon", null)] + [InlineData("apian", null)] + [InlineData("ortel", null)] + [InlineData("denoy", null)] + [InlineData("gins8", null)] + [InlineData("larr", null)] + [InlineData("lask", null)] + [InlineData("tcc", null)] + public void ForwardOnlyProjectionsDoNotSupportInverse(string projectionName, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies roundtrip stability for inverse-capable globular and miscellaneous projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Optional WKT parameter segment. + [Theory] + [InlineData("comill", 2d, 1d, null)] + [InlineData("fouc_s", -2d, -1d, null)] + [InlineData("lagrng", 2d, -1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"W\",2]")] + public void SupportsGlobularAndMiscRoundtrip(string projectionName, double longitude, double latitude, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false, extraParameters)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-9); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-9); + } + + /// + /// Verifies Lagrange rejects invalid W. + /// + [Fact] + public void LagrangeRejectsInvalidW() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("lagrng", false, ",PARAMETER[\"W\",-1],PARAMETER[\"lat_1\",0.5]")); + Assert.Throws(() => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected)); + } + + /// + /// Verifies Lagrange rejects invalid lat_1. + /// + [Fact] + public void LagrangeRejectsInvalidLat1() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("lagrng", false, ",PARAMETER[\"lat_1\",90.00001]")); + Assert.Throws(() => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected)); + } + + private static string BuildProjectedWkt(string projectionName, bool useWgs84, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + + string spheroidClause = useWgs84 + ? "SPHEROID[\"WGS 84\",6378137,298.257223563]" + : "SPHEROID[\"Sphere\",6400000,0]"; + return FormattableString.Invariant($"PROJCS[\"Specialty-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/GnomonicProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/GnomonicProjectionTests.cs new file mode 100644 index 00000000..bbf37786 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/GnomonicProjectionTests.cs @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates gnomonic projection behavior against PROJ reference vectors. +/// +public class GnomonicProjectionTests +{ + private const double ForwardTolerance = 5e-5d; + private const double InverseTolerance = 2e-7d; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies ellipsoidal forward vectors for the gnomonic projection. + /// + /// Projection center latitude. + /// Input longitude. + /// Input latitude. + /// Expected x coordinate. + /// Expected y coordinate. + [Theory] + [InlineData(0d, 10d, 80d, 0.176333043342897d, 5.723194021247466d)] + [InlineData(0d, 20d, 70d, 0.364056496605216d, 2.903717319916686d)] + [InlineData(0d, 80d, 80d, 5.713366365209261d, 32.729848361175208d)] + [InlineData(0d, 0d, 89.99d, 0d, 5700.9221603850146d)] + [InlineData(90d, 45d, 45d, 0.707863156628200d, -0.707863156628200d)] + [InlineData(-90d, 45d, -45d, 0.707863156628200d, 0.707863156628200d)] + [InlineData(45d, 0d, 0d, 0d, -0.989689577444773d)] + [InlineData(45d, 0d, 90d, 0d, 1.002503117123815d)] + [InlineData(45d, 0d, -45d, 0d, -154.86226463965525d)] + public void EllipsoidalForwardMatchesProjReference( + double latitudeOfOrigin, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(latitudeOfOrigin)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, ForwardTolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, ForwardTolerance); + } + + /// + /// Verifies ellipsoidal inverse vectors for the gnomonic projection. + /// + /// Projection center latitude. + /// Input x coordinate. + /// Input y coordinate. + /// Expected longitude. + /// Expected latitude. + [Theory] + [InlineData(0d, 0.176333043342897d, 5.723194021247466d, 10d, 80d)] + [InlineData(0d, 0.364056496605216d, 2.903717319916686d, 20d, 70d)] + [InlineData(0d, 5.713366365209261d, 32.729848361175208d, 80d, 80d)] + [InlineData(0d, 0d, 5700.9221603850146d, 0d, 89.99d)] + [InlineData(90d, 0.707863156628200d, -0.707863156628200d, 45d, 45d)] + [InlineData(90d, 0d, -127.48350842637615d, 0d, 0d)] + [InlineData(45d, 0d, -0.989689577444773d, 0d, 0d)] + [InlineData(45d, 0d, 1.002503117123815d, 0d, 90d)] + [InlineData(45d, 0d, -154.86226463965525d, 0d, -45d)] + public void EllipsoidalInverseMatchesProjReference( + double latitudeOfOrigin, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(latitudeOfOrigin)); + + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected, + projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, InverseTolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, InverseTolerance); + } + + /// + /// Verifies points beyond the supported ellipsoidal domain fail in the forward direction. + /// + [Theory] + [InlineData(0d, 180d, 89.99d)] + [InlineData(90d, 0d, -0.5d)] + [InlineData(90d, 90d, -0.5d)] + [InlineData(-90d, 0d, 0.5d)] + [InlineData(-90d, 90d, 0.5d)] + [InlineData(45d, 0d, -45.5d)] + public void EllipsoidalForwardOutsideDomainReturnsNaN(double latitudeOfOrigin, double longitude, double latitude) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(latitudeOfOrigin)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.True(double.IsNaN(projectedPoint[0])); + Assert.True(double.IsNaN(projectedPoint[1])); + } + + private static string BuildProjectedWkt(double latitudeOfOrigin) + { + return FormattableString.Invariant( + $"PROJCS[\"Regression-gnom-{latitudeOfOrigin}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"GIE Ellipsoid\",1,200]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"gnom\"],PARAMETER[\"latitude_of_origin\",{latitudeOfOrigin}],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/HotineObliqueMercatorProjectionRegressionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/HotineObliqueMercatorProjectionRegressionTests.cs new file mode 100644 index 00000000..aeef5ef7 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/HotineObliqueMercatorProjectionRegressionTests.cs @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for Hotine Oblique Mercator parity with PROJ reference vectors. +/// +public class HotineObliqueMercatorProjectionRegressionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies exact pole forward projection against PROJ, where polar special-case v values are required. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Expected projected X from PROJ. + /// Expected projected Y from PROJ. + [Theory] + [InlineData(0d, 90d, 264739.4033272466d, 5179881.4284288045d)] + [InlineData(0d, -90d, -6812922.0677616373d, -14440412.1883131303d)] + public void HotineObliqueMercatorForwardAtExactPolesMatchesProjReference( + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt()); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + + double[] projectedPoint = forward.MathTransform.Transform([longitude, latitude]); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-3d); + } + + /// + /// Verifies that omerc supports PROJ's two-point mode even when lon_1 and lon_2 are omitted. + /// + [Fact] + public void ObliqueMercatorTwoPointModeWithImplicitZeroLongitudesMatchesProjReference() + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildObliqueMercatorTwoPointWkt(noRotation: false)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + + double[] projectedPoint = forward.MathTransform.Transform([2d, 1d]); + + Assert.InRange(Math.Abs(projectedPoint[0] - 222650.796885261d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 110642.229314984d), 0d, 1e-3d); + } + + /// + /// Verifies that omerc treats an omitted rectified grid angle as gamma = alpha. + /// + [Fact] + public void ObliqueMercatorAlphaModeWithoutRectifiedGridAngleMatchesProjReference() + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildObliqueMercatorAlphaWithoutGammaWkt()); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + + double[] projectedPoint = forward.MathTransform.Transform([2d, 1d]); + + Assert.InRange(Math.Abs(projectedPoint[0] - -3569.825230822232d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - -5093592.310871849768d), 0d, 1e-3d); + } + + /// + /// Verifies that omerc +no_rot bypasses the rectified-grid rotation like PROJ. + /// + [Fact] + public void ObliqueMercatorNoRotModeMatchesProjReference() + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildObliqueMercatorTwoPointWkt(noRotation: true)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + + double[] projectedPoint = forward.MathTransform.Transform([2d, 1d]); + + Assert.InRange(Math.Abs(projectedPoint[0] - 110642.229314984d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 222650.796885261d), 0d, 1e-3d); + } + + private static string BuildProjectedWkt() + { + return + "PROJCS[\"Regression-omerc\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"hotine_oblique_mercator\"],PARAMETER[\"latitude_of_center\",45],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"azimuth\",35.264383770917604],PARAMETER[\"rectified_grid_angle\",35.264383770917604],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static string BuildObliqueMercatorAlphaWithoutGammaWkt() + { + return + "PROJCS[\"Regression-omerc-alpha-default-gamma\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"hotine_oblique_mercator\"],PARAMETER[\"latitude_of_center\",45],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"azimuth\",35.264383770917604],UNIT[\"metre\",1]]"; + } + + private static string BuildObliqueMercatorTwoPointWkt(bool noRotation) + { + string noRotationParameter = noRotation ? ",PARAMETER[\"no_rot\",1]" : string.Empty; + return + $"PROJCS[\"Regression-omerc-two-point{(noRotation ? "-no-rot" : string.Empty)}\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",SPHEROID[\"GRS 80\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"hotine_oblique_mercator\"],PARAMETER[\"latitude_of_center\",0],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]{noRotationParameter},UNIT[\"metre\",1]]"; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/IcosahedralProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/IcosahedralProjectionTests.cs new file mode 100644 index 00000000..c2498430 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/IcosahedralProjectionTests.cs @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates icosahedral projection variants (airocean, isea). +/// +public class IcosahedralProjectionTests +{ + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + private const string Sphere637100718091875 = "SPHEROID[\"Sphere\",6371007.18091875,0]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for icosahedral projection variants. + /// + /// Projection alias. + /// for ISEA aliases, for Airocean aliases. + [Theory] + [InlineData("airocean", false)] + [InlineData("Airocean", false)] + [InlineData("isea", true)] + [InlineData("Icosahedral_Snyder_Equal_Area", true)] + public void SupportsIcosahedralAliasesFromWkt(string projectionName, bool isIsea) + { + string aliasWkt = isIsea + ? BuildIseaWkt(projectionName, Sphere6400000, 0d, 0d, 3d, 4d, 0d) + : BuildAiroceanWkt(projectionName, 0d); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + aliasWkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for airocean vertical and horizontal orientations. + /// + [Theory] + [InlineData(0d, 23d, 28d, 13572113.73386754d, 23493648.55327798d, 1e-3d)] + [InlineData(0d, 71d, 46d, 9714915.991790695d, 23488176.361173604d, 1e-3d)] + [InlineData(1d, 23d, 28d, 13391387.087562159d, 13572113.73386754d, 1e-3d)] + [InlineData(1d, 71d, 46d, 13396859.279666536d, 9714915.991790695d, 1e-3d)] + public void MatchesAiroceanForwardVectors( + double orientCode, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildAiroceanWkt("airocean", orientCode)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for airocean vertical and horizontal orientations. + /// + [Theory] + [InlineData(0d, 13600000d, 23500000d, 22.77346472511832d, 27.745464601997153d, 2e-9d)] + [InlineData(0d, 9700000d, 23500000d, 71.26673004703193d, 45.89205035111361d, 2e-9d)] + [InlineData(1d, 13400000d, 13600000d, 22.653513921934305d, 27.877587719075937d, 2e-9d)] + [InlineData(1d, 13400000d, 9700000d, 71.23213038171733d, 46.05944622180928d, 2e-9d)] + public void MatchesAiroceanInverseVectors( + double orientCode, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildAiroceanWkt("airocean", orientCode)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies airocean roundtrip stability across both orientations. + /// + [Theory] + [InlineData(0d, 23d, 28d)] + [InlineData(0d, -11d, -34d)] + [InlineData(1d, 23d, 28d)] + [InlineData(1d, -109d, -46d)] + public void SupportsAiroceanRoundtrip(double orientCode, double longitude, double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildAiroceanWkt("airocean", orientCode)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-7d); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-7d); + } + + /// + /// Verifies airocean rejects projected points outside the valid domain. + /// + [Fact] + public void AiroceanRejectsOutsideDomainInverseInput() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildAiroceanWkt("airocean", 0d)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + Assert.Throws(() => inverse.MathTransform.Transform(CreatePoint(0d, 0d))); + } + + /// + /// Verifies PROJ builtins forward vectors for isea default and pole orientation. + /// + [Theory] + [InlineData(Sphere6400000, 0d, 2d, 1d, -1097074.9481534758d, 3442909.3097474533d, 1e-3d)] + [InlineData(Sphere6400000, 0d, -2d, -1d, -1575486.3537720195d, 3234352.6953102099d, 1e-3d)] + [InlineData(Sphere637100718091875, 0d, 0d, 0d, -1331454.0746232667d, 3323137.7716348548d, 1e-3d)] + [InlineData(Sphere637100718091875, 0d, 90d, 0d, 8564460.6391008701d, 593869.2974855418d, 1e-3d)] + [InlineData(Sphere637100718091875, 1d, 0d, 0d, 0d, -195097.13364071414d, 1e-3d)] + [InlineData(Sphere637100718091875, 1d, 90d, 0d, 9593072.4354674518d, 0d, 1e-3d)] + public void MatchesIseaForwardVectors( + string spheroidClause, + double orientCode, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildIseaWkt("isea", spheroidClause, orientCode, 0d, 3d, 4d, 0d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins-derived inverse vectors for isea default and pole orientation. + /// + [Theory] + [InlineData(Sphere6400000, 0d, -1097074.9481534758d, 3442909.3097474533d, 2d, 1d, 2e-9d)] + [InlineData(Sphere6400000, 0d, -1575486.3537720195d, 3234352.6953102099d, -2d, -1d, 2e-9d)] + [InlineData(Sphere637100718091875, 0d, -1331454.0746232667d, 3323137.7716348548d, 0d, 0d, 2e-9d)] + [InlineData(Sphere637100718091875, 0d, 8564460.6391008701d, 593869.2974855418d, 90d, 0d, 2e-9d)] + [InlineData(Sphere637100718091875, 1d, 0d, -195097.13364071414d, 0d, 0d, 2e-9d)] + [InlineData(Sphere637100718091875, 1d, 9593072.4354674518d, 0d, 90d, 0d, 2e-9d)] + public void MatchesIseaInverseVectors( + string spheroidClause, + double orientCode, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildIseaWkt("isea", spheroidClause, orientCode, 0d, 3d, 4d, 0d)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies isea roundtrip stability for the supported inverse parameter sets. + /// + [Theory] + [InlineData(Sphere6400000, 0d, 2d, 1d, 2e-7d)] + [InlineData(Sphere6400000, 0d, -2d, -1d, 2e-7d)] + [InlineData(Sphere637100718091875, 0d, -75d, 45d, 2e-7d)] + [InlineData(Sphere637100718091875, 1d, -75d, 45d, 2e-7d)] + public void SupportsIseaRoundtrip( + string spheroidClause, + double orientCode, + double longitude, + double latitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildIseaWkt("isea", spheroidClause, orientCode, 0d, 3d, 4d, 0d)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies unsupported ISEA modes are rejected by projection construction. + /// + [Theory] + [InlineData(1d, 3d, 4d)] + [InlineData(2d, 3d, 4d)] + [InlineData(3d, 3d, 31d)] + public void IseaRejectsUnsupportedModes(double modeCode, double aperture, double resolution) + { + Assert.Throws(() => + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildIseaWkt("isea", Sphere6400000, 0d, modeCode, aperture, resolution, 0d)); + CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + }); + } + + /// + /// Verifies invalid Airocean orientation values report the projection parameter container as the failing argument. + /// + [Fact] + public void AiroceanRejectsInvalidOrientationWithParametersParamName() + { + ArgumentException exception = Assert.Throws(() => + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildAiroceanWkt("airocean", 9d)); + CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + }); + + Assert.Equal("parameters", exception.ParamName); + } + + /// + /// Verifies ISEA inverse is explicitly limited to the supported planar parameter set. + /// + [Fact] + public void IseaRejectsInverseOutsideSupportedPlanarSubset() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildIseaWkt("isea", Sphere6400000, 0d, 0d, 3d, 5d, 0d)); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + private static string BuildAiroceanWkt(string projectionName, double orientCode) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D8-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Grs80}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"orient\",{orientCode.ToString("R", CultureInfo.InvariantCulture)}],UNIT[\"metre\",1]]"); + } + + private static string BuildIseaWkt( + string projectionName, + string spheroidClause, + double orientCode, + double modeCode, + double aperture, + double resolution, + double azimuth) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D8-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"orient\",{orientCode.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"mode\",{modeCode.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"aperture\",{aperture.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"resolution\",{resolution.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"azi\",{azimuth.ToString("R", CultureInfo.InvariantCulture)}],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/InterruptedAndSpecialMercatorProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/InterruptedAndSpecialMercatorProjectionTests.cs new file mode 100644 index 00000000..25fc944a --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/InterruptedAndSpecialMercatorProjectionTests.cs @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates interrupted and specialized Mercator-family projections. +/// +public class InterruptedAndSpecialMercatorProjectionTests +{ + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + private const string Sphere6370997 = "SPHEROID[\"Sphere\",6370997,0]"; + private const string Sphere1 = "SPHEROID[\"Sphere\",1,0]"; + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for interrupted and specialized Mercator-family projections. + /// + /// Projection alias. + /// Spheroid clause. + /// Optional additional projection parameters. + [Theory] + [InlineData("tobmerc", Sphere6370997, null)] + [InlineData("Tobler_Mercator", Sphere6370997, null)] + [InlineData("calcofi", Grs80, null)] + [InlineData("Cal_Coop_Ocean_Fish_Invest_Lines_Stations", Grs80, null)] + [InlineData("mbtfpp", Sphere6400000, null)] + [InlineData("McBryde_Thomas_Flat_Polar_Parabolic", Sphere6400000, null)] + [InlineData("mbtfpq", Sphere6400000, null)] + [InlineData("McBryde_Thomas_Flat_Polar_Quartic", Sphere6400000, null)] + [InlineData("imoll", Sphere6400000, null)] + [InlineData("Interrupted_Mollweide", Sphere6400000, null)] + [InlineData("imoll_o", Sphere6400000, null)] + [InlineData("Interrupted_Mollweide_Oceanic_View", Sphere6400000, null)] + [InlineData("igh_o", Sphere6400000, null)] + [InlineData("Interrupted_Goode_Homolosine_Oceanic_View", Sphere6400000, null)] + public void SupportsInterruptedAndSpecialMercatorAliasesFromWkt(string projectionName, string spheroidClause, string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + ArgumentNullException.ThrowIfNull(spheroidClause); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies aliases resolve from WKT for col_urban with required non-default parameters. + /// + /// Projection alias. + [Theory] + [InlineData("col_urban")] + [InlineData("Colombia_Urban")] + public void SupportsColombiaUrbanAliasesFromWkt(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildColUrbanWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(-74.25d, 4.8d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for interrupted and specialized Mercator-family projections. + /// + /// Projection code. + /// Spheroid clause. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + /// Optional additional projection parameters. + /// Absolute tolerance. + [Theory] + [InlineData("tobmerc", Sphere6370997, 2d, 1d, 222322.011656333081d, 111200.520030584055d, null, 1e-6)] + [InlineData("calcofi", Grs80, 2d, 1d, 508.444872150d, -1171.764860418d, null, 1e-6)] + [InlineData("mbtfpp", Sphere6400000, 2d, 1d, 206804.786929820d, 120649.762565793d, null, 1e-6)] + [InlineData("mbtfpq", Sphere6400000, 2d, 1d, 209391.854738393d, 119161.040199055d, null, 1e-6)] + [InlineData("imoll", Sphere6400000, 2d, 1d, -912080.283811148372d, 124066.283433859542d, null, 1e-6)] + [InlineData("imoll_o", Sphere6400000, 2d, 1d, -1357849.196080365917d, 124066.283433859542d, null, 1e-6)] + [InlineData("igh_o", Sphere6400000, 2d, 1d, 223197.992883418d, 111701.072127637d, null, 1e-6)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + string spheroidClause, + double longitude, + double latitude, + double expectedX, + double expectedY, + string? extraParameters, + double tolerance) + { + ArgumentNullException.ThrowIfNull(projectionName); + ArgumentNullException.ThrowIfNull(spheroidClause); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins forward vector for calcofi when non-zero lon0/x0/y0 are provided and internally ignored. + /// + [Fact] + public void MatchesCalcofiForwardVectorWithIgnoredLon0AndOffsets() + { + const double expectedX = 301.769827d; + const double expectedY = -1567.849822d; + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildCalcofiCustomWkt()); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(10d, 50d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-6); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-6); + } + + /// + /// Verifies PROJ builtins forward vector for col_urban. + /// + [Fact] + public void MatchesColombiaUrbanForwardVector() + { + const double expectedX = 80859.033d; + const double expectedY = 122543.174d; + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildColUrbanWkt("col_urban")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(-74.25d, 4.8d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-3); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-3); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable interrupted and specialized Mercator-family projections. + /// + /// Projection code. + /// Spheroid clause. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + /// Optional additional projection parameters. + /// Absolute tolerance. + [Theory] + [InlineData("tobmerc", Sphere6370997, 200d, 100d, 0.001798644059d, 0.000899322029d, null, 2e-9)] + [InlineData("calcofi", Grs80, 200d, 100d, -110.363307925d, 12.032056976d, null, 2e-9)] + [InlineData("mbtfpp", Sphere6400000, 200d, 100d, 0.001933954d, 0.000828837d, null, 2e-9)] + [InlineData("mbtfpq", Sphere6400000, 200d, 100d, 0.001910106d, 0.000839185d, null, 2e-9)] + [InlineData("imoll", Sphere6400000, 200d, 100d, 11.074062190626d, 0.000806005080d, null, 2e-9)] + [InlineData("imoll_o", Sphere6400000, 200d, 100d, 15.502891574921d, 0.000806005080d, null, 2e-9)] + [InlineData("igh_o", Sphere6400000, 200d, 100d, 0.001790494d, 0.000895247d, null, 2e-9)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + string spheroidClause, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + string? extraParameters, + double tolerance) + { + ArgumentNullException.ThrowIfNull(projectionName); + ArgumentNullException.ThrowIfNull(spheroidClause); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for inverse-capable interrupted and specialized Mercator-family projections. + /// + /// Projection code. + /// Spheroid clause. + /// Input longitude degrees. + /// Input latitude degrees. + /// Optional additional projection parameters. + [Theory] + [InlineData("tobmerc", Sphere6370997, 2d, 75d, null)] + [InlineData("calcofi", Grs80, 2d, 1d, null)] + [InlineData("mbtfpp", Sphere6400000, 2d, 1d, null)] + [InlineData("mbtfpq", Sphere6400000, 2d, 1d, null)] + [InlineData("imoll", Sphere6400000, -39.99d, 0.1d, null)] + [InlineData("imoll_o", Sphere6400000, -89.99d, 0.1d, null)] + [InlineData("igh_o", Sphere6400000, 170d, 70d, null)] + public void SupportsInterruptedAndSpecialMercatorRoundtrip( + string projectionName, + string spheroidClause, + double longitude, + double latitude, + string? extraParameters) + { + ArgumentNullException.ThrowIfNull(projectionName); + ArgumentNullException.ThrowIfNull(spheroidClause); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-8); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-8); + } + + /// + /// Verifies roundtrip stability for col_urban. + /// + [Fact] + public void SupportsColombiaUrbanRoundtrip() + { + const double longitude = -74.25d; + const double latitude = 4.8d; + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildColUrbanWkt("col_urban")); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-8); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-8); + } + + /// + /// Verifies Tobler-Mercator rejects pole input. + /// + /// Input latitude at the pole. + [Theory] + [InlineData(90d)] + [InlineData(-90d)] + public void ToblerMercatorRejectsPoles(double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("tobmerc", Sphere6370997, null)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + Assert.Throws(() => forward.MathTransform.Transform(CreatePoint(0d, latitude))); + } + + /// + /// Verifies Tobler-Mercator with unit sphere remains numerically stable near zero. + /// + [Fact] + public void ToblerMercatorUnitSphereInverseNearZeroIsStable() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("tobmerc", Sphere1, null)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(0d, 1e-15d)); + + Assert.InRange(Math.Abs(geographicPoint[0]), 0d, 1e-13); + Assert.InRange(Math.Abs(geographicPoint[1] - 1e-15d), 0d, 1e-13); + } + + /// + /// Verifies Tobler-Mercator forward projection uses the longitude relative to the central meridian. + /// + [Fact] + public void ToblerMercatorForwardRespectsCentralMeridianOffset() + { + ProjectedCoordinateSystem centeredAtGreenwich = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("tobmerc", Sphere6370997, null)); + ProjectedCoordinateSystem centeredAtTenDegrees = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("tobmerc", Sphere6370997, null, 10d)); + ICoordinateTransformation forwardGreenwich = CoordinateTransformationFactory.CreateFromCoordinateSystems( + centeredAtGreenwich.GeographicCoordinateSystem, + centeredAtGreenwich); + ICoordinateTransformation forwardTenDegrees = CoordinateTransformationFactory.CreateFromCoordinateSystems( + centeredAtTenDegrees.GeographicCoordinateSystem, + centeredAtTenDegrees); + + double[] greenwichPoint = forwardGreenwich.MathTransform.Transform(CreatePoint(2d, 30d)); + double[] shiftedPoint = forwardTenDegrees.MathTransform.Transform(CreatePoint(12d, 30d)); + + Assert.InRange(Math.Abs(shiftedPoint[0] - greenwichPoint[0]), 0d, 1e-9); + Assert.InRange(Math.Abs(shiftedPoint[1] - greenwichPoint[1]), 0d, 1e-9); + } + + /// + /// Verifies Tobler-Mercator inverse projection re-applies the central meridian after recovering longitude. + /// + [Fact] + public void ToblerMercatorInverseReappliesCentralMeridianOffset() + { + ProjectedCoordinateSystem centeredAtGreenwich = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("tobmerc", Sphere6370997, null)); + ProjectedCoordinateSystem centeredAtTenDegrees = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("tobmerc", Sphere6370997, null, 10d)); + ICoordinateTransformation forwardGreenwich = CoordinateTransformationFactory.CreateFromCoordinateSystems( + centeredAtGreenwich.GeographicCoordinateSystem, + centeredAtGreenwich); + ICoordinateTransformation inverseTenDegrees = CoordinateTransformationFactory.CreateFromCoordinateSystems( + centeredAtTenDegrees, + centeredAtTenDegrees.GeographicCoordinateSystem); + + double[] projectedPoint = forwardGreenwich.MathTransform.Transform(CreatePoint(2d, 30d)); + double[] shiftedGeographicPoint = inverseTenDegrees.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(shiftedGeographicPoint[0] - 12d), 0d, 1e-9); + Assert.InRange(Math.Abs(shiftedGeographicPoint[1] - 30d), 0d, 1e-9); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, string? extraParameters) + { + return BuildProjectedWkt(projectionName, spheroidClause, extraParameters, 0d); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, string? extraParameters, double centralMeridian) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-B-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",{centralMeridian.ToString(CultureInfo.InvariantCulture)}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static string BuildColUrbanWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-B-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Grs80}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",4.68048611111111],PARAMETER[\"central_meridian\",-74.1465916666667],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",92334.879],PARAMETER[\"false_northing\",109320.965],PARAMETER[\"h_0\",2550],UNIT[\"metre\",1]]"); + } + + private static string BuildCalcofiCustomWkt() + { + return "PROJCS[\"Specialty-B-calcofi-custom\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",400,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"calcofi\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",50],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",10000],PARAMETER[\"false_northing\",500000],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/LambertEqualAreaConicProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/LambertEqualAreaConicProjectionTests.cs new file mode 100644 index 00000000..76b1c63b --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/LambertEqualAreaConicProjectionTests.cs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies the dedicated Lambert equal-area conic normalization layer over the Albers projection. +/// +public class LambertEqualAreaConicProjectionTests +{ + /// + /// Verifies that the dedicated Lambert projection matches the equivalent normalized Albers projection and round-trips correctly. + /// + /// Whether to exercise the southern-hemisphere variant. + /// Sample longitude in degrees. + /// Sample latitude in degrees. + [Theory] + [InlineData(false, -75d, 35d)] + [InlineData(true, 25d, -35d)] + public void LambertEqualAreaConicProjection_MatchesNormalizedAlbersAndRoundTrips(bool south, double longitude, double latitude) + { + LambertEqualAreaConicProjection lambert = CreateLambertProjection(south); + AlbersProjection albers = CreateNormalizedAlbersProjection(south); + + double[] expected = albers.Transform([longitude, latitude]); + double[] actual = lambert.Transform([longitude, latitude]); + LambertEqualAreaConicProjection inverse = Assert.IsType(lambert.Inverse()); + double[] roundTripped = inverse.Transform(actual); + + Assert.Equal(expected[0], actual[0], 10); + Assert.Equal(expected[1], actual[1], 10); + Assert.Equal(longitude, roundTripped[0], 8); + Assert.Equal(latitude, roundTripped[1], 8); + } + + private static LambertEqualAreaConicProjection CreateLambertProjection(bool south) + { + return new LambertEqualAreaConicProjection(CreateLambertParameters(south)); + } + + private static AlbersProjection CreateNormalizedAlbersProjection(bool south) + { + return new AlbersProjection(CreateNormalizedAlbersParameters(south)); + } + + private static List CreateLambertParameters(bool south) + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + double latitudeOfCenter = south ? -30d : 30d; + double latitudeOfStandardParallel = south ? -40d : 40d; + double centralMeridian = south ? 20d : -96d; + double falseEasting = south ? 500000d : 0d; + double falseNorthing = south ? 1000000d : 0d; + + List parameters = + [ + new ProjectionParameter("semi_major", ellipsoid.SemiMajorAxis), + new ProjectionParameter("semi_minor", ellipsoid.SemiMinorAxis), + new ProjectionParameter("central_meridian", centralMeridian), + new ProjectionParameter("latitude_of_center", latitudeOfCenter), + new ProjectionParameter("lat_1", latitudeOfStandardParallel), + new ProjectionParameter("false_easting", falseEasting), + new ProjectionParameter("false_northing", falseNorthing), + new ProjectionParameter("unit", 1d), + ]; + + if (south) + { + parameters.Add(new ProjectionParameter("south", 1d)); + } + + return parameters; + } + + private static IEnumerable CreateNormalizedAlbersParameters(bool south) + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + double latitudeOfCenter = south ? -30d : 30d; + double latitudeOfStandardParallel = south ? -40d : 40d; + double centralMeridian = south ? 20d : -96d; + double falseEasting = south ? 500000d : 0d; + double falseNorthing = south ? 1000000d : 0d; + + return + [ + new ProjectionParameter("semi_major", ellipsoid.SemiMajorAxis), + new ProjectionParameter("semi_minor", ellipsoid.SemiMinorAxis), + new ProjectionParameter("central_meridian", centralMeridian), + new ProjectionParameter("latitude_of_center", latitudeOfCenter), + new ProjectionParameter("standard_parallel_1", south ? -90d : 90d), + new ProjectionParameter("standard_parallel_2", latitudeOfStandardParallel), + new ProjectionParameter("false_easting", falseEasting), + new ProjectionParameter("false_northing", falseNorthing), + new ProjectionParameter("unit", 1d), + ]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/LatLongProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/LatLongProjectionTests.cs new file mode 100644 index 00000000..b9e8663e --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/LatLongProjectionTests.cs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates latlong/longlat projection aliases. +/// +public class LatLongProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that latlong aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("latlong")] + [InlineData("longlat")] + public void SupportsLatLongAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(12.5d, 45.75d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward/inverse roundtrip stability for latlong aliases. + /// + /// Projection alias to validate. + /// Input longitude. + /// Input latitude. + /// Maximum absolute roundtrip delta. + [Theory] + [InlineData("latlong", 12.5d, 45.75d, 1e-10)] + [InlineData("longlat", -73.5d, 22.125d, 1e-10)] + public void SupportsLatLongRoundtrip(string projectionName, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName) + { + return + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/LeacUpsWebMercatorProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/LeacUpsWebMercatorProjectionTests.cs new file mode 100644 index 00000000..4ec3dd07 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/LeacUpsWebMercatorProjectionTests.cs @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates LEAC, UPS, and Web Mercator projection variants. +/// +public class LeacUpsWebMercatorProjectionTests +{ + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for LEAC, UPS, and Web Mercator projection variants. + /// + /// Projection alias. + [Theory] + [InlineData("leac")] + [InlineData("ups")] + [InlineData("webmerc")] + public void SupportsLeacUpsWebMercatorAliasesFromWkt(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + string wkt = projectionName.Equals("leac", StringComparison.OrdinalIgnoreCase) + ? BuildLeacWkt(projectionName, Grs80, 0d, false) + : BuildUpsWkt(projectionName, Grs80, false); + if (projectionName.Equals("webmerc", StringComparison.OrdinalIgnoreCase)) + { + wkt = BuildWebMercWkt(projectionName, Grs80); + } + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for leac. + /// + [Theory] + [InlineData(Grs80, 2d, 1d, 220685.140542979d, 112983.500889396d, 1e-3d)] + [InlineData(Grs80, 2d, -1d, 224553.312279826d, -108128.636744873d, 1e-3d)] + [InlineData(Grs80, -2d, 1d, -220685.140542979d, 112983.500889396d, 1e-3d)] + [InlineData(Grs80, -2d, -1d, -224553.312279826d, -108128.636744873d, 1e-3d)] + [InlineData(Sphere6400000, 2d, 1d, 221432.868592852d, 114119.454526532d, 1e-3d)] + [InlineData(Sphere6400000, 2d, -1d, 225331.724127111d, -109245.829435056d, 1e-3d)] + [InlineData(Sphere6400000, -2d, 1d, -221432.868592852d, 114119.454526532d, 1e-3d)] + [InlineData(Sphere6400000, -2d, -1d, -225331.724127111d, -109245.829435056d, 1e-3d)] + public void MatchesLeacForwardVectors( + string spheroidClause, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildLeacWkt("leac", spheroidClause, 0d, false)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for leac. + /// + [Theory] + [InlineData(Grs80, 200d, 100d, 0.001796645d, 0.000904352d, 2e-9d)] + [InlineData(Grs80, 200d, -100d, 0.001796616d, -0.000904387d, 2e-9d)] + [InlineData(Grs80, -200d, 100d, -0.001796645d, 0.000904352d, 2e-9d)] + [InlineData(Grs80, -200d, -100d, -0.001796616d, -0.000904387d, 2e-9d)] + [InlineData(Sphere6400000, 200d, 100d, 0.001790507d, 0.000895229d, 2e-9d)] + [InlineData(Sphere6400000, 200d, -100d, 0.001790479d, -0.000895264d, 2e-9d)] + [InlineData(Sphere6400000, -200d, 100d, -0.001790507d, 0.000895229d, 2e-9d)] + [InlineData(Sphere6400000, -200d, -100d, -0.001790479d, -0.000895264d, 2e-9d)] + public void MatchesLeacInverseVectors( + string spheroidClause, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildLeacWkt("leac", spheroidClause, 0d, false)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins forward vectors for ups. + /// + [Theory] + [InlineData(2d, 1d, 2433455.563438467d, -10412543.301512826d, 1e-3d)] + [InlineData(2d, -1d, 2448749.118568199d, -10850493.419804076d, 1e-3d)] + [InlineData(-2d, 1d, 1566544.436561533d, -10412543.301512826d, 1e-3d)] + [InlineData(-2d, -1d, 1551250.881431801d, -10850493.419804076d, 1e-3d)] + public void MatchesUpsForwardVectors(double longitude, double latitude, double expectedX, double expectedY, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildUpsWkt("ups", Grs80, false)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for ups. + /// + [Theory] + [InlineData(200d, 100d, -44.998567498d, 64.918236287d, 2e-9d)] + [InlineData(200d, -100d, -44.995702709d, 64.917020251d, 2e-9d)] + [InlineData(-200d, 100d, -45.004297076d, 64.915804281d, 2e-9d)] + [InlineData(-200d, -100d, -45.001432287d, 64.914588378d, 2e-9d)] + public void MatchesUpsInverseVectors(double x, double y, double expectedLongitude, double expectedLatitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildUpsWkt("ups", Grs80, false)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies UPS rejects spherical ellipsoids (matching PROJ semantics). + /// + [Fact] + public void UpsRejectsSphericalEllipsoid() + { + Assert.Throws(() => + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildUpsWkt("ups", Sphere6400000, false)); + CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + }); + } + + /// + /// Verifies that batched Web Mercator forward transformation matches point-wise transformation results. + /// + [Fact] + public void WebMercatorBatchTransformMatchesPointwiseTransform() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildWebMercWkt("webmerc", Grs80)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] batchedLongitudes = [-170d, -120.5d, -45d, 0d, 37.5d, 89.9d, 120.25d, 170d]; + double[] batchedLatitudes = [-80d, -65d, -30.5d, -1d, 1d, 30.5d, 65d, 80d]; + double[] expectedLongitudes = (double[])batchedLongitudes.Clone(); + double[] expectedLatitudes = (double[])batchedLatitudes.Clone(); + + for (int i = 0; i < expectedLongitudes.Length; i++) + { + forward.MathTransform.Transform(ref expectedLongitudes[i], ref expectedLatitudes[i]); + } + + forward.MathTransform.Transform(batchedLongitudes, batchedLatitudes); + + for (int i = 0; i < batchedLongitudes.Length; i++) + { + Assert.InRange(Math.Abs(batchedLongitudes[i] - expectedLongitudes[i]), 0d, 1e-9d); + Assert.InRange(Math.Abs(batchedLatitudes[i] - expectedLatitudes[i]), 0d, 1e-9d); + } + } + + /// + /// Verifies that batched Web Mercator forward transformation propagates NaN inputs consistently with point-wise transformation. + /// + [Fact] + public void WebMercatorBatchTransformPropagatesNaNConsistently() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildWebMercWkt("webmerc", Grs80)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] batchedLongitudes = [0d, double.NaN, 10d, 20d, double.NaN, -40d, 50d, 60d]; + double[] batchedLatitudes = [0d, 5d, double.NaN, 15d, 20d, double.NaN, 30d, 40d]; + double[] expectedLongitudes = (double[])batchedLongitudes.Clone(); + double[] expectedLatitudes = (double[])batchedLatitudes.Clone(); + + for (int i = 0; i < expectedLongitudes.Length; i++) + { + forward.MathTransform.Transform(ref expectedLongitudes[i], ref expectedLatitudes[i]); + } + + forward.MathTransform.Transform(batchedLongitudes, batchedLatitudes); + + for (int i = 0; i < batchedLongitudes.Length; i++) + { + Assert.Equal(double.IsNaN(expectedLongitudes[i]), double.IsNaN(batchedLongitudes[i])); + Assert.Equal(double.IsNaN(expectedLatitudes[i]), double.IsNaN(batchedLatitudes[i])); + if (!double.IsNaN(expectedLongitudes[i])) + { + Assert.InRange(Math.Abs(batchedLongitudes[i] - expectedLongitudes[i]), 0d, 1e-9d); + } + + if (!double.IsNaN(expectedLatitudes[i])) + { + Assert.InRange(Math.Abs(batchedLatitudes[i] - expectedLatitudes[i]), 0d, 1e-9d); + } + } + } + + /// + /// Verifies that batched Web Mercator forward transformation rejects pole latitude inputs. + /// + [Fact] + public void WebMercatorBatchTransformRejectsPoleLatitude() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildWebMercWkt("webmerc", Grs80)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] batchedLongitudes = [0d, 10d, 20d, 30d, 40d, 50d, 60d, 70d]; + double[] batchedLatitudes = [0d, 10d, 20d, 30d, 40d, 50d, 60d, 90d]; + + Assert.Throws(() => forward.MathTransform.Transform(batchedLongitudes, batchedLatitudes)); + } + + private static string BuildLeacWkt(string projectionName, string spheroidClause, double standardParallel1, bool south) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D9-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"standard_parallel_1\",{standardParallel1.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"south\",{(south ? "1" : "0")}],UNIT[\"metre\",1]]"); + } + + private static string BuildUpsWkt(string projectionName, string spheroidClause, bool south) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D9-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{(south ? "-90" : "90")}],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"south\",{(south ? "1" : "0")}],UNIT[\"metre\",1]]"); + } + + private static string BuildWebMercWkt(string projectionName, string spheroidClause) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D9-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/MiscPseudoCylindricalProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/MiscPseudoCylindricalProjectionTests.cs new file mode 100644 index 00000000..e1350cd2 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/MiscPseudoCylindricalProjectionTests.cs @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates current projection group pseudo-cylindrical projections. +/// +public class MiscPseudoCylindricalProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for covered projections. + /// + /// Projection alias. + [Theory] + [InlineData("gall")] + [InlineData("Gall_Stereographic")] + [InlineData("crast")] + [InlineData("Craster_Parabolic")] + [InlineData("fahey")] + [InlineData("collg")] + [InlineData("Collignon")] + [InlineData("boggs")] + [InlineData("Boggs_Eumorphic")] + [InlineData("hatano")] + [InlineData("Hatano_Asymmetrical_Equal_Area")] + [InlineData("nell")] + [InlineData("nell_h")] + [InlineData("Nell_Hammer")] + [InlineData("nicol")] + [InlineData("Nicolosi_Globular")] + [InlineData("times")] + [InlineData("Times_Projection")] + public void SupportsAliasesFromWkt(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for covered projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + [Theory] + [InlineData("gall", 2d, 1d, 157969.171134520d, 95345.249178386d)] + [InlineData("crast", 2d, 1d, 218280.142056781d, 114306.045604280d)] + [InlineData("fahey", 2d, 1d, 182993.344649124d, 101603.193569884d)] + [InlineData("collg", 2d, 1d, 249872.921577930d, 99423.174788460d)] + [InlineData("boggs", 2d, 1d, 211949.700808182d, 117720.998305411d)] + [InlineData("hatano", 2d, 1d, 189878.878946528d, 131409.802440626d)] + [InlineData("nell", 2d, 1d, 223385.132504696d, 111698.236447187d)] + [InlineData("nell_h", 2d, 1d, 223385.131640953d, 111698.236533562d)] + [InlineData("nicol", 2d, 1d, 223374.561814140d, 111732.553988545d)] + [InlineData("times", 25d, -10d, 2065971.530107881d, -951526.064849459d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ArgumentNullException.ThrowIfNull(projectionName); + + bool useSphereEllps = projectionName.Equals("times", StringComparison.OrdinalIgnoreCase); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, useSphereEllps)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable covered projections. + /// + /// Projection code. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + [Theory] + [InlineData("gall", 200d, 100d, 0.002532140d, 0.001048847d)] + [InlineData("crast", 200d, 100d, 0.001832259d, 0.000874839d)] + [InlineData("fahey", 200d, 100d, 0.002185789d, 0.000984246d)] + [InlineData("collg", 200d, 100d, 0.001586797d, 0.001010173d)] + [InlineData("hatano", 200d, 100d, 0.002106462d, 0.000760957d)] + [InlineData("nell", 200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData("nell_h", 200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData("times", 2065971.530107881d, -951526.064849459d, 25d, -10d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ArgumentNullException.ThrowIfNull(projectionName); + + bool useSphereEllps = projectionName.Equals("times", StringComparison.OrdinalIgnoreCase); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, useSphereEllps)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies Boggs and Nicolosi remain forward-only in this test set. + /// + /// Projection code. + [Theory] + [InlineData("boggs")] + [InlineData("nicol")] + public void ForwardOnlyProjectionsDoNotSupportInverse(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false)); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies roundtrip stability for inverse-capable covered projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData("gall", 2d, 1d)] + [InlineData("crast", -2d, -1d)] + [InlineData("fahey", 2d, -1d)] + [InlineData("collg", -2d, 1d)] + [InlineData("hatano", 2d, 1d)] + [InlineData("nell", -2d, -1d)] + [InlineData("nell_h", 2d, -1d)] + [InlineData("times", -35d, 20d)] + public void SupportsRoundtrip(string projectionName, double longitude, double latitude) + { + ArgumentNullException.ThrowIfNull(projectionName); + + bool useSphereEllps = projectionName.Equals("times", StringComparison.OrdinalIgnoreCase); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, useSphereEllps)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-9); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-9); + } + + private static string BuildProjectedWkt(string projectionName, bool useSphereEllps) + { + ArgumentNullException.ThrowIfNull(projectionName); + + string spheroidClause = useSphereEllps + ? "SPHEROID[\"Sphere\",6370997,0]" + : "SPHEROID[\"Sphere\",6400000,0]"; + + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/ModifiedStereographicProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/ModifiedStereographicProjectionTests.cs new file mode 100644 index 00000000..a65b6f76 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/ModifiedStereographicProjectionTests.cs @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates modified stereographic projection variants. +/// +public class ModifiedStereographicProjectionTests +{ + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + private const string Sphere6370997 = "SPHEROID[\"Sphere\",6370997,0]"; + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + private const string Clarke66 = "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for modified stereographic projection variants. + /// + /// Projection alias. + /// Spheroid clause. + [Theory] + [InlineData("qsc", Grs80)] + [InlineData("Quadrilateralized_Spherical_Cube", Grs80)] + [InlineData("rouss", Grs80)] + [InlineData("Roussilhe_Stereographic", Grs80)] + [InlineData("mil_os", Sphere6400000)] + [InlineData("Miller_Oblated_Stereographic", Sphere6400000)] + [InlineData("lee_os", Sphere6400000)] + [InlineData("Lee_Oblated_Stereographic", Sphere6400000)] + [InlineData("gs48", Sphere6370997)] + [InlineData("Modified_Stereographic_48_US", Sphere6370997)] + [InlineData("alsk", Clarke66)] + [InlineData("Modified_Stereographic_Alaska", Clarke66)] + [InlineData("gs50", Clarke66)] + [InlineData("Modified_Stereographic_50_US", Clarke66)] + public void SupportsModifiedStereographicAliasesFromWkt(string projectionName, string spheroidClause) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for modified stereographic projection variants. + /// + /// Projection code. + /// Spheroid clause. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + /// Absolute tolerance. + [Theory] + [InlineData("qsc", Grs80, 2d, 1d, 304638.450843852d, 164123.870923794d, 1e-6d)] + [InlineData("rouss", Grs80, 2d, 1d, 222644.894131617d, 110611.091868370d, 1e-6d)] + [InlineData("mil_os", Sphere6400000, 2d, 1d, -1908527.949594205d, -1726237.473061448d, 1e-6d)] + [InlineData("lee_os", Sphere6400000, 2d, 1d, -25564478.952605054d, 154490848.828625500d, 1e-6d)] + [InlineData("gs48", Sphere6370997, -119d, 40d, -1923908.446529346d, 355874.658944479d, 1e-6d)] + [InlineData("alsk", Clarke66, -160d, 55d, -513253.146950842d, -968928.031867943d, 1e-6d)] + [InlineData("gs50", Clarke66, -130d, 45d, -771831.518853336d, 48465.166491305d, 1e-6d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + string spheroidClause, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for modified stereographic projection variants. + /// + /// Projection code. + /// Spheroid clause. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + /// Absolute tolerance. + [Theory] + [InlineData("qsc", Grs80, 200d, 100d, 0.001321341d, 0.000610653d, 2e-9d)] + [InlineData("rouss", Grs80, 200d, 100d, 0.001796631d, 0.000904369d, 2e-9d)] + [InlineData("mil_os", Sphere6400000, 200d, 100d, 20.002036394d, 18.000968347d, 2e-9d)] + [InlineData("lee_os", Sphere6400000, 200d, 100d, -164.997479458d, -9.998758861d, 2e-9d)] + [InlineData("gs48", Sphere6370997, -1923000d, 355000d, -118.987112613d, 39.994449789d, 2e-9d)] + [InlineData("alsk", Clarke66, -500000d, -950000d, -159.830804303d, 55.183195262d, 2e-9d)] + [InlineData("gs50", Clarke66, -800000d, 500000d, -131.171390467d, 49.084969746d, 2e-9d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + string spheroidClause, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for inverse-capable modified stereographic projection variants. + /// + /// Projection code. + /// Spheroid clause. + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData("qsc", Grs80, 2d, 1d)] + [InlineData("rouss", Grs80, 2d, 1d)] + [InlineData("mil_os", Sphere6400000, 2d, 1d)] + [InlineData("lee_os", Sphere6400000, -164.997479458d, -9.998758861d)] + [InlineData("gs48", Sphere6370997, -95d, 35d)] + [InlineData("alsk", Clarke66, -145d, 60d)] + [InlineData("gs50", Clarke66, -80d, 36d)] + public void SupportsModifiedStereographicRoundtrip(string projectionName, string spheroidClause, double longitude, double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-7d); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-7d); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D2-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/NearSidedPerspectiveLabordeProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/NearSidedPerspectiveLabordeProjectionTests.cs new file mode 100644 index 00000000..607e87c3 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/NearSidedPerspectiveLabordeProjectionTests.cs @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates near-sided perspective (nsper/tpers) and Laborde (labrd) projection support. +/// +public class NearSidedPerspectiveLabordeProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that nsper aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("nsper")] + [InlineData("Near_Sided_Perspective")] + [InlineData("tpers")] + [InlineData("Tilted_Perspective")] + public void SupportsPerspectiveAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildNsperProjectedWkt(projectionName, 6400000d, 1000000d, 0d, 0d, null, null)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward values against PROJ builtins vectors for nsper. + /// + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData(2d, 1d, 222239.816114100d, 111153.763991925d)] + [InlineData(2d, -1d, 222239.816114100d, -111153.763991925d)] + [InlineData(-2d, 1d, -222239.816114100d, 111153.763991925d)] + [InlineData(-2d, -1d, -222239.816114100d, -111153.763991925d)] + public void MatchesProjBuiltinsNsperForwardVectors( + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildNsperProjectedWkt("nsper", 6400000d, 1000000d, 0d, 0d, null, null)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies inverse values against PROJ builtins vectors for nsper. + /// + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData(200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData(200d, -100d, 0.001790493d, -0.000895247d)] + [InlineData(-200d, 100d, -0.001790493d, 0.000895247d)] + [InlineData(-200d, -100d, -0.001790493d, -0.000895247d)] + public void MatchesProjBuiltinsNsperInverseVectors( + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildNsperProjectedWkt("nsper", 6400000d, 1000000d, 0d, 0d, null, null)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies polar and oblique nsper setups from builtins. + /// + /// lat_0 parameter (degrees). + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData(90d, 45d, 45d, 0.4555d, -0.4555d)] + [InlineData(-90d, -45d, -45d, -0.4555d, 0.4555d)] + [InlineData(45d, 45d, 45d, 0.4767d, 0.1396d)] + public void MatchesProjBuiltinsNsperPolarAndObliqueCases( + double latitudeOfOrigin, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildNsperProjectedWkt("nsper", 1d, 3d, latitudeOfOrigin, 0d, null, null)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 5e-5); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 5e-5); + } + + /// + /// Verifies tpers vectors from builtins (+azi and +tilt variants). + /// + /// Tilt angle in degrees. + /// Azimuth angle in degrees. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData(null, 20d, 2d, 1d, 170820.288955531d, 180460.865555805d)] + [InlineData(20d, null, 2d, 1d, 213598.340357101d, 113687.930830744d)] + public void MatchesProjBuiltinsTpersForwardVectors( + double? tilt, + double? azimuth, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildNsperProjectedWkt("tpers", 6400000d, 1000000d, 0d, 0d, tilt, azimuth)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies Laborde aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("labrd")] + [InlineData("Laborde")] + public void SupportsLabordeAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildLabrdProjectedWkt(projectionName, 2d, 0.5d, 0d)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward values against PROJ builtins vectors for labrd. + /// + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData(2d, 1d, 166973.166090228d, -110536.912730266d)] + [InlineData(2d, -1d, 166973.168287157d, -331761.993650884d)] + [InlineData(-2d, 1d, -278345.500519976d, -110469.032642032d)] + [InlineData(-2d, -1d, -278345.504185270d, -331829.870790275d)] + public void MatchesProjBuiltinsLabrdForwardVectors( + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildLabrdProjectedWkt("labrd", 2d, 0.5d, 0d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 5e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 5e-7); + } + + /// + /// Verifies inverse values against PROJ builtins vectors for labrd. + /// + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData(200d, 100d, 0.501797719d, 2.000904357d)] + [InlineData(200d, -100d, 0.501797717d, 1.999095641d)] + [InlineData(-200d, 100d, 0.498202281d, 2.000904357d)] + [InlineData(-200d, -100d, 0.498202283d, 1.999095641d)] + public void MatchesProjBuiltinsLabrdInverseVectors( + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildLabrdProjectedWkt("labrd", 2d, 0.5d, 0d)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies invalid perspective height values are rejected. + /// + /// Height parameter to validate. + [Theory] + [InlineData(0d)] + [InlineData(1e11d)] + public void RejectsInvalidNsperHeight(double h) + { + Assert.Throws(() => + { + string wkt = BuildNsperProjectedWkt("nsper", 1d, h, 0d, 0d, null, null); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + forward.MathTransform.Transform(CreatePoint(2d, 1d)); + }); + } + + /// + /// Verifies labrd rejects invalid lat_0 value. + /// + [Fact] + public void RejectsInvalidLabrdLatitudeOfOrigin() + { + Assert.Throws(() => + { + string wkt = BuildLabrdProjectedWkt("labrd", 0d, 0.5d, 0d); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + forward.MathTransform.Transform(CreatePoint(2d, 1d)); + }); + } + + private static string BuildNsperProjectedWkt( + string projectionName, + double semiMajor, + double height, + double latitudeOfOrigin, + double centralMeridian, + double? tilt, + double? azimuth) + { + string tiltParameter = tilt.HasValue + ? FormattableString.Invariant($",PARAMETER[\"tilt\",{tilt.Value}]") + : string.Empty; + string azimuthParameter = azimuth.HasValue + ? FormattableString.Invariant($",PARAMETER[\"azi\",{azimuth.Value}]") + : string.Empty; + + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",{semiMajor.ToString("R", CultureInfo.InvariantCulture)},0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{latitudeOfOrigin.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"central_meridian\",{centralMeridian.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"h\",{height.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{tiltParameter}{azimuthParameter},UNIT[\"metre\",1]]"); + } + + private static string BuildLabrdProjectedWkt(string projectionName, double latitudeOfOrigin, double centralMeridian, double azimuth) + { + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"GRS 80\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{latitudeOfOrigin.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"central_meridian\",{centralMeridian.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"azi\",{azimuth.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/NewZealandMapGridProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/NewZealandMapGridProjectionTests.cs new file mode 100644 index 00000000..dab9fd1d --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/NewZealandMapGridProjectionTests.cs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates New Zealand Map Grid (nzmg) projection support. +/// +public class NewZealandMapGridProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that nzmg aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("nzmg")] + [InlineData("New_Zealand_Map_Grid")] + public void SupportsNzmgAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(173.5d, -41.5d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward/inverse roundtrip stability for nzmg. + /// + /// Projection alias to validate. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Maximum absolute roundtrip delta (degrees). + [Theory] + [InlineData("nzmg", 173.2d, -41.1d, 5e-7)] + [InlineData("nzmg", 174.0d, -40.5d, 5e-7)] + [InlineData("New_Zealand_Map_Grid", 172.8d, -42.0d, 5e-7)] + public void SupportsNzmgRoundtrip(string projectionName, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies forward values against PROJ builtins vectors. + /// + /// Projection alias to validate. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData("nzmg", 2d, 1d, 3352675144.747425100d, -7043205391.100243600d)] + [InlineData("nzmg", 2d, -1d, 3691989502.779306400d, -6729069415.332104700d)] + [InlineData("New_Zealand_Map_Grid", -2d, 1d, 4099000768.453238500d, -7863208779.667248700d)] + [InlineData("New_Zealand_Map_Grid", -2d, -1d, 4466166927.369976000d, -7502531736.628604900d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 5e-4); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 5e-4); + } + + /// + /// Verifies inverse values against PROJ builtins vectors. + /// + /// Projection alias to validate. + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData("nzmg", 200000d, 100000d, 175.482086827d, -69.422692183d)] + [InlineData("nzmg", 200000d, -100000d, 175.756819473d, -69.533571088d)] + [InlineData("New_Zealand_Map_Grid", -200000d, 100000d, 134.605119233d, -61.459995711d)] + [InlineData("New_Zealand_Map_Grid", -200000d, -100000d, 134.333684316d, -61.621553676d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + private static string BuildProjectedWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"GRS 80\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",-41],PARAMETER[\"central_meridian\",173],PARAMETER[\"false_easting\",2510000],PARAMETER[\"false_northing\",6023150],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionCoverageTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionCoverageTests.cs new file mode 100644 index 00000000..a41ade49 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionCoverageTests.cs @@ -0,0 +1,1100 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Improves coverage for projections with low line coverage by exercising forward, +/// inverse, and round-trip transforms across a variety of geographic positions. +/// +public class ProjectionCoverageTests +{ + private const string Wgs84 = "SPHEROID[\"WGS 84\",6378137,298.257223563]"; + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + private const string Sphere6370997 = "SPHEROID[\"Sphere\",6370997,0]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + // ------------------------------------------------------------------ + // QuadrilateralizedSphericalCube (qsc) – 46.2 % coverage + // ------------------------------------------------------------------ + + /// + /// Verifies QSC forward/inverse round-trip for various face positions. + /// + /// Input longitude degrees. + /// Input latitude degrees. + /// Latitude of origin to select the cube face. + [Theory] + [InlineData(0d, 0d, 0d)] + [InlineData(10d, 20d, 0d)] + [InlineData(-30d, 45d, 0d)] + [InlineData(90d, 10d, 0d)] + [InlineData(-90d, -10d, 0d)] + [InlineData(179d, 5d, 0d)] + [InlineData(-179d, -5d, 0d)] + [InlineData(0d, 89d, 90d)] + [InlineData(90d, 89d, 90d)] + [InlineData(-120d, 85d, 90d)] + [InlineData(0d, -89d, -90d)] + [InlineData(45d, -85d, -90d)] + [InlineData(-170d, -80d, -90d)] + [InlineData(180d, 0d, 0d)] + [InlineData(0d, 45d, 0d)] + public void QscRoundTrip(double longitude, double latitude, double latitudeOfOrigin) + { + string wkt = BuildProjectedWkt("qsc", Wgs84, latitudeOfOrigin, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies QSC forward transform produces finite, non-zero results for non-origin points. + /// + [Fact] + public void QscForwardProducesFiniteResults() + { + string wkt = BuildProjectedWkt("qsc", Wgs84, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(30d, 45d)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + Assert.NotEqual(0d, result[0]); + Assert.NotEqual(0d, result[1]); + } + + /// + /// Verifies QSC forward transform with non-zero central meridian produces finite results. + /// + /// Central meridian degrees. + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(90d, 92d, 20d)] + [InlineData(-90d, -88d, -20d)] + [InlineData(45d, 50d, 30d)] + public void QscForwardWithCentralMeridian(double centralMeridian, double longitude, double latitude) + { + string wkt = BuildProjectedWkt("qsc", Wgs84, 0d, centralMeridian, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + // ------------------------------------------------------------------ + // HealpixProjection (healpix) – 51.2 % coverage + // ------------------------------------------------------------------ + + /// + /// Verifies HEALPix spherical forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-30d, 20d)] + [InlineData(90d, 45d)] + [InlineData(-90d, -45d)] + [InlineData(179d, 10d)] + [InlineData(-179d, -10d)] + [InlineData(0d, 89d)] + [InlineData(0d, -89d)] + [InlineData(45d, 60d)] + [InlineData(-120d, -70d)] + public void HealpixSphericalRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("healpix", Sphere6400000, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies HEALPix ellipsoidal (WGS 84) forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-30d, 20d)] + [InlineData(90d, 45d)] + [InlineData(-90d, -45d)] + [InlineData(179d, 10d)] + [InlineData(-179d, -10d)] + [InlineData(0d, 89d)] + [InlineData(0d, -89d)] + public void HealpixEllipsoidalRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("healpix", Wgs84, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies ellipsoidal HEALPix forward output matches the corresponding authalic-sphere projection. + /// + [Fact] + public void HealpixEllipsoidalForwardMatchesAuthalicSphereScale() + { + const double semiMajor = 6378137d; + const double inverseFlattening = 298.257223563d; + const double longitude = 45d; + const double latitude = 35d; + double flattening = 1d / inverseFlattening; + double eccentricitySquared = (2d * flattening) - (flattening * flattening); + double eccentricity = Math.Sqrt(eccentricitySquared); + double oneEs = 1d - eccentricitySquared; + double qp = QsfnForTests(1d, eccentricity, oneEs); + double authalicRadius = semiMajor * Math.Sqrt(0.5d * qp); + double phi = latitude * (Math.PI / 180d); + double q = QsfnForTests(Math.Sin(phi), eccentricity, oneEs); + double authalicLatitude = Math.Asin(Math.Max(-1d, Math.Min(1d, q / qp))) * (180d / Math.PI); + string sphericalClause = FormattableString.Invariant($"SPHEROID[\"Authalic Sphere\",{authalicRadius},0]"); + string ellipsoidalWkt = BuildProjectedWkt("healpix", Wgs84, 0d, 0d, null); + string sphericalWkt = BuildProjectedWkt("healpix", sphericalClause, 0d, 0d, null); + ProjectedCoordinateSystem ellipsoidalProjected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, ellipsoidalWkt); + ProjectedCoordinateSystem sphericalProjected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, sphericalWkt); + ICoordinateTransformation ellipsoidalForward = CoordinateTransformationFactory.CreateFromCoordinateSystems(ellipsoidalProjected.GeographicCoordinateSystem, ellipsoidalProjected); + ICoordinateTransformation sphericalForward = CoordinateTransformationFactory.CreateFromCoordinateSystems(sphericalProjected.GeographicCoordinateSystem, sphericalProjected); + + double[] actual = ellipsoidalForward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] expected = sphericalForward.MathTransform.Transform(CreatePoint(longitude, authalicLatitude)); + + Assert.InRange(Math.Abs(actual[0] - expected[0]), 0d, 1e-6); + Assert.InRange(Math.Abs(actual[1] - expected[1]), 0d, 1e-6); + } + + /// + /// Verifies HEALPix forward produces finite results. + /// + /// Spheroid clause. + [Theory] + [InlineData(Sphere6400000)] + [InlineData(Wgs84)] + public void HealpixForwardProducesFiniteResults(string spheroidClause) + { + ArgumentNullException.ThrowIfNull(spheroidClause); + + string wkt = BuildProjectedWkt("healpix", spheroidClause, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(45d, 30d)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + // ------------------------------------------------------------------ + // GoodeProjection (goode) – 54.8 % coverage + // ------------------------------------------------------------------ + + /// + /// Verifies Goode Homolosine forward/inverse round-trip across interrupt zones. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-100d, 30d)] + [InlineData(-40d, 20d)] + [InlineData(30d, 10d)] + [InlineData(80d, -10d)] + [InlineData(-60d, -30d)] + [InlineData(150d, -20d)] + [InlineData(-170d, 40d)] + [InlineData(10d, 50d)] + [InlineData(-10d, -50d)] + [InlineData(0d, 40.69d)] + [InlineData(0d, -40.69d)] + [InlineData(0d, 89d)] + [InlineData(0d, -89d)] + [InlineData(179d, 0d)] + [InlineData(-179d, 0d)] + public void GoodeSphericalRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("goode", Sphere6400000, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies Goode Homolosine ellipsoidal (WGS 84) forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-100d, 30d)] + [InlineData(80d, -10d)] + [InlineData(150d, -60d)] + [InlineData(0d, 89d)] + [InlineData(0d, -89d)] + public void GoodeEllipsoidalRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("goode", Wgs84, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies Goode Homolosine forward vectors are finite and non-zero for non-origin input. + /// + [Fact] + public void GoodeForwardProducesFiniteResults() + { + string wkt = BuildProjectedWkt("goode", Sphere6400000, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(-100d, 50d)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + Assert.NotEqual(0d, result[0]); + Assert.NotEqual(0d, result[1]); + } + + // ------------------------------------------------------------------ + // Winkel2Projection (wink2) – 57.5 % coverage – forward only + // ------------------------------------------------------------------ + + /// + /// Verifies Winkel II forward transform produces finite results. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-90d, 45d)] + [InlineData(90d, -45d)] + [InlineData(179d, 89d)] + [InlineData(-179d, -89d)] + [InlineData(0d, 89d)] + [InlineData(0d, -89d)] + [InlineData(45d, 0d)] + [InlineData(-120d, 60d)] + public void Winkel2ForwardProducesFiniteResults(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("wink2", Sphere6400000, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + /// + /// Verifies Winkel II forward produces non-zero output for non-zero input. + /// + [Fact] + public void Winkel2ForwardNonZeroInput() + { + string wkt = BuildProjectedWkt("wink2", Sphere6400000, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(30d, 50d)); + + Assert.NotEqual(0d, result[0]); + Assert.NotEqual(0d, result[1]); + } + + /// + /// Verifies Winkel II inverse projection is available. + /// + [Fact] + public void Winkel2SupportsInverse() + { + string wkt = BuildProjectedWkt("wink2", Sphere6400000, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] result = inverse.MathTransform.Transform(CreatePoint(200d, 100d)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + /// + /// Verifies Winkel II with custom standard parallel. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(10d, 20d)] + [InlineData(-60d, 70d)] + [InlineData(150d, -30d)] + public void Winkel2WithStandardParallel(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("wink2", Sphere6400000, 30d, 0d, ",PARAMETER[\"standard_parallel_1\",50.467]"); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + // ------------------------------------------------------------------ + // LambertAzimuthalEqualAreaProjection (laea) – 59.7 % coverage + // ------------------------------------------------------------------ + + /// + /// Verifies LAEA equatorial mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-30d, 20d)] + [InlineData(90d, 45d)] + [InlineData(-90d, -45d)] + [InlineData(10d, 89d)] + [InlineData(-10d, -89d)] + [InlineData(179d, 0d)] + [InlineData(-179d, 0d)] + public void LaeaEquatorialRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("laea", Wgs84, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies LAEA north-polar mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 89d)] + [InlineData(90d, 60d)] + [InlineData(-90d, 70d)] + [InlineData(180d, 80d)] + [InlineData(-45d, 45d)] + [InlineData(0d, 10d)] + public void LaeaNorthPoleRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("laea", Wgs84, 90d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies LAEA south-polar mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, -89d)] + [InlineData(90d, -60d)] + [InlineData(-90d, -70d)] + [InlineData(180d, -80d)] + [InlineData(-45d, -45d)] + [InlineData(0d, -10d)] + public void LaeaSouthPoleRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("laea", Wgs84, -90d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies LAEA oblique mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(10d, 52d)] + [InlineData(-5d, 40d)] + [InlineData(30d, 60d)] + [InlineData(0d, 89d)] + [InlineData(-20d, 30d)] + [InlineData(50d, 70d)] + public void LaeaObliqueRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("laea", Wgs84, 52d, 10d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies LAEA spherical variant forward transform produces finite results. + /// + /// Input longitude degrees. + /// Input latitude degrees. + /// Latitude of origin. + [Theory] + [InlineData(0d, 0d, 0d)] + [InlineData(10d, 5d, 0d)] + [InlineData(-10d, -5d, 0d)] + [InlineData(0d, 85d, 90d)] + [InlineData(0d, -85d, -90d)] + [InlineData(5d, 47d, 45d)] + public void LaeaSphericalForward(double longitude, double latitude, double latitudeOfOrigin) + { + string wkt = BuildProjectedWkt("laea", Sphere6400000, latitudeOfOrigin, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + /// + /// Verifies LAEA spherical equatorial forward northing against the analytical formula. + /// + [Fact] + public void LaeaSphericalEquatorialForwardMatchesAnalyticalNorthing() + { + const double radius = 6400000d; + const double latitude = 5d; + string wkt = BuildProjectedWkt("laea", Sphere6400000, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(0d, latitude)); + double phi = latitude * (Math.PI / 180d); + double expectedNorthing = radius * Math.Sqrt(2d / (1d + Math.Cos(phi))) * Math.Sin(phi); + + Assert.InRange(Math.Abs(projectedPoint[0]), 0d, 1e-9); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedNorthing), 0d, 1e-6); + } + + /// + /// Verifies Cassini-Soldner easting against the analytical 4th-order polynomial expansion. + /// + [Fact] + public void CassiniSoldnerForwardMatchesAnalyticalEasting() + { + const double latitude = 52.518611111111d; + const double longitude = 20d; + const double semiMajor = 6378137d; + const double inverseFlattening = 298.257223563d; + const double expectedEasting = 1340021.76450623d; + string wkt = BuildProjectedWkt("cass", "SPHEROID[\"WGS 84\",6378137,298.257223563]", 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double flattening = 1d / inverseFlattening; + double eccentricitySquared = (2d * flattening) - (flattening * flattening); + double cFactor = eccentricitySquared / (1d - eccentricitySquared); + double phi = latitude * (Math.PI / 180d); + double lambda = longitude * (Math.PI / 180d); + double sinPhi = Math.Sin(phi); + double cosPhi = Math.Cos(phi); + double n = 1d / Math.Sqrt(1d - (eccentricitySquared * sinPhi * sinPhi)); + double tanPhi = Math.Tan(phi); + double t = tanPhi * tanPhi; + double a1 = lambda * cosPhi; + double a2 = a1 * a1; + double c = cFactor * cosPhi * cosPhi; + double analyticalEasting = semiMajor * n * a1 * (1d - (a2 * t * ((1d / 6d) + (((8d - t + (8d * c)) * a2) / 120d)))); + + Assert.InRange(Math.Abs(analyticalEasting - expectedEasting), 0d, 1e-6); + Assert.InRange(Math.Abs(projectedPoint[0] - analyticalEasting), 0d, 1e-6); + } + + /// + /// Verifies Transverse Mercator inverse latitude uses Snyder coefficient 1575 in the t^4 term. + /// + [Fact] + public void TransverseMercatorInverseLatitudeUsesSnyderCoefficient1575() + { + const double semiMajor = 6377563.396d; + const double inverseFlattening = 299.32496d; + const double latitudeOfOrigin = 49d; + const double centralMeridian = -2d; + const double scaleFactor = 0.9996012717d; + const double easting = -4370667.706314864d; + const double northing = 1991695.1549298093d; + string wkt = FormattableString.Invariant( + $"PROJCS[\"Coverage-transverse_mercator\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Airy 1830\",{semiMajor},{inverseFlattening}]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"transverse_mercator\"],PARAMETER[\"latitude_of_origin\",{latitudeOfOrigin}],PARAMETER[\"central_meridian\",{centralMeridian}],PARAMETER[\"scale_factor\",{scaleFactor}],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(easting, northing)); + double expectedLatitude = ComputeTransverseMercatorInverseLatitude( + easting, + northing, + semiMajor, + inverseFlattening, + scaleFactor, + latitudeOfOrigin, + 1575d); + double legacyLatitude = ComputeTransverseMercatorInverseLatitude( + easting, + northing, + semiMajor, + inverseFlattening, + scaleFactor, + latitudeOfOrigin, + 1574d); + + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 1e-8); + Assert.True(Math.Abs(geographicPoint[1] - legacyLatitude) > 1e-3); + } + + /// + /// Verifies Albers inverse fails near the cone apex because Math.Atan cannot resolve the quadrant. + /// + [Fact] + public void AlbersInverseNearApexHasQuadrantError() + { + const double longitude = 120d; + const double latitude = 30d; + const double latitudeOfOrigin = 0d; + const double standardParallel1 = 90d; + const double standardParallel2 = 60d; + string wkt = BuildProjectedWkt( + "albers", + Sphere6400000, + latitudeOfOrigin, + 0d, + FormattableString.Invariant($",PARAMETER[\"standard_parallel_1\",{standardParallel1}],PARAMETER[\"standard_parallel_2\",{standardParallel2}]")); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies Albers handles tangent cones where both standard parallels are equal. + /// + /// Spheroid clause. + [Theory] + [InlineData(Sphere6400000)] + [InlineData(Wgs84)] + public void AlbersTangentParallelsRoundTrip(string spheroidClause) + { + const double longitude = -75d; + const double latitude = 35d; + string wkt = BuildProjectedWkt( + "albers", + spheroidClause, + 23d, + -96d, + ",PARAMETER[\"standard_parallel_1\",29.5],PARAMETER[\"standard_parallel_2\",29.5]"); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.False(double.IsNaN(projectedPoint[0])); + Assert.False(double.IsNaN(projectedPoint[1])); + Assert.False(double.IsInfinity(projectedPoint[0])); + Assert.False(double.IsInfinity(projectedPoint[1])); + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies Polar Stereographic UPS north applies scale_factor consistently. + /// + [Fact] + public void PolarStereographicUpsNorthMatchesReferenceCoordinate() + { + const double longitude = 15d; + const double latitude = 73d; + const double expectedEasting = 2491967.01029204d; + const double expectedNorthing = 163954.12194234435d; + string wkt = "PROJCS[\"Coverage-UPS-North\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Polar_Stereographic\"],PARAMETER[\"latitude_of_origin\",90],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",0.994],PARAMETER[\"false_easting\",2000000],PARAMETER[\"false_northing\",2000000],UNIT[\"metre\",1]]"; + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedEasting), 0d, 1d); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedNorthing), 0d, 1d); + } + + // ------------------------------------------------------------------ + // MercatorAuxiliarySphere (mercator_auxiliary_sphere) – 59.3 % coverage + // ------------------------------------------------------------------ + + /// + /// Verifies Mercator Auxiliary Sphere forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-30d, 20d)] + [InlineData(90d, 45d)] + [InlineData(-90d, -45d)] + [InlineData(179d, 10d)] + [InlineData(-179d, -10d)] + [InlineData(0d, 85d)] + [InlineData(0d, -85d)] + public void MercatorAuxiliarySphereRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("mercator_auxiliary_sphere", Wgs84, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies Mercator Auxiliary Sphere with a sphere datum. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(45d, 30d)] + [InlineData(-120d, -60d)] + [InlineData(179d, 80d)] + public void MercatorAuxiliarySphereSphericalRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("mercator_auxiliary_sphere", Sphere6370997, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies Mercator Auxiliary Sphere with non-zero central meridian. + /// + /// Central meridian degrees. + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(10d, 15d, 20d)] + [InlineData(-100d, -95d, 30d)] + [InlineData(170d, 175d, -40d)] + public void MercatorAuxiliarySphereCentralMeridianRoundTrip(double centralMeridian, double longitude, double latitude) + { + string wkt = BuildProjectedWkt("mercator_auxiliary_sphere", Wgs84, 0d, centralMeridian, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + // ------------------------------------------------------------------ + // OrthographicProjection (ortho) – 60.8 % coverage + // ------------------------------------------------------------------ + + /// + /// Verifies orthographic equatorial mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d)] + [InlineData(2d, 1d)] + [InlineData(-30d, 20d)] + [InlineData(60d, 45d)] + [InlineData(-60d, -45d)] + [InlineData(0d, 89d)] + [InlineData(0d, -89d)] + [InlineData(89d, 0d)] + [InlineData(-89d, 0d)] + public void OrthoEquatorialRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("ortho", Wgs84, 0d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies orthographic north-polar mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 89d)] + [InlineData(90d, 60d)] + [InlineData(-90d, 70d)] + [InlineData(180d, 80d)] + [InlineData(-45d, 45d)] + [InlineData(0d, 10d)] + public void OrthoNorthPoleRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("ortho", Wgs84, 90d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies orthographic south-polar mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, -89d)] + [InlineData(90d, -60d)] + [InlineData(-90d, -70d)] + [InlineData(180d, -80d)] + [InlineData(-45d, -45d)] + [InlineData(0d, -10d)] + public void OrthoSouthPoleRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("ortho", Wgs84, -90d, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies orthographic oblique mode forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(10d, 52d)] + [InlineData(-5d, 40d)] + [InlineData(30d, 60d)] + [InlineData(0d, 89d)] + [InlineData(-20d, 30d)] + [InlineData(50d, 70d)] + public void OrthoObliqueRoundTrip(double longitude, double latitude) + { + string wkt = BuildProjectedWkt("ortho", Wgs84, 45d, 10d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies orthographic spherical variant forward/inverse round-trip. + /// + /// Input longitude degrees. + /// Input latitude degrees. + /// Latitude of origin. + [Theory] + [InlineData(0d, 0d, 0d)] + [InlineData(30d, 45d, 0d)] + [InlineData(-60d, -30d, 0d)] + [InlineData(0d, 89d, 90d)] + [InlineData(0d, -89d, -90d)] + [InlineData(10d, 50d, 45d)] + public void OrthoSphericalRoundTrip(double longitude, double latitude, double latitudeOfOrigin) + { + string wkt = BuildProjectedWkt("ortho", Sphere6400000, latitudeOfOrigin, 0d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-6); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-6); + } + + /// + /// Verifies orthographic forward at center maps to origin. + /// + [Fact] + public void OrthoCenterMapsToOrigin() + { + string wkt = BuildProjectedWkt("ortho", Wgs84, 45d, 10d, null); + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] result = forward.MathTransform.Transform(CreatePoint(10d, 45d)); + + Assert.InRange(Math.Abs(result[0]), 0d, 1e-3); + Assert.InRange(Math.Abs(result[1]), 0d, 1e-3); + } + + /// + /// Verifies Orthographic honors the optional alpha rotation parameter. + /// + [Fact] + public void OrthoAlphaRotationChangesProjectedCoordinate() + { + const double longitude = -122d; + const double latitude = 38d; + string wktNoAlpha = BuildProjectedWkt("ortho", Wgs84, 37.628969166666664d, -122.39394166666668d, null); + string wktWithAlpha = BuildProjectedWkt( + "ortho", + Wgs84, + 37.628969166666664d, + -122.39394166666668d, + ",PARAMETER[\"alpha\",27.7927777777777]"); + ProjectedCoordinateSystem projectedNoAlpha = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wktNoAlpha); + ProjectedCoordinateSystem projectedWithAlpha = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wktWithAlpha); + ICoordinateTransformation forwardNoAlpha = CoordinateTransformationFactory.CreateFromCoordinateSystems(projectedNoAlpha.GeographicCoordinateSystem, projectedNoAlpha); + ICoordinateTransformation forwardWithAlpha = CoordinateTransformationFactory.CreateFromCoordinateSystems(projectedWithAlpha.GeographicCoordinateSystem, projectedWithAlpha); + + double[] noAlpha = forwardNoAlpha.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] withAlpha = forwardWithAlpha.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.True(Math.Abs(noAlpha[0] - withAlpha[0]) > 1d || Math.Abs(noAlpha[1] - withAlpha[1]) > 1d); + } + + // ------------------------------------------------------------------ + // WKT builder and helpers + // ------------------------------------------------------------------ + private static double ComputeTransverseMercatorInverseLatitude( + double xMeters, + double yMeters, + double semiMajor, + double inverseFlattening, + double scaleFactor, + double latitudeOfOriginDegrees, + double coefficient) + { + const double epsilon = 1e-6; + const double c00 = 1d; + const double c02 = 0.25d; + const double c04 = 0.046875d; + const double c06 = 0.01953125d; + const double c08 = 0.01068115234375d; + const double c22 = 0.75d; + const double c44 = 0.46875d; + const double c46 = 0.01302083333333333333d; + const double c48 = 0.00712076822916666666d; + const double c66 = 0.36458333333333333333d; + const double c68 = 0.00569661458333333333d; + const double c88 = 0.3076171875d; + double flattening = 1d / inverseFlattening; + double eccentricitySquared = (2d * flattening) - (flattening * flattening); + double esp = eccentricitySquared / (1d - eccentricitySquared); + double en0 = c00 - (eccentricitySquared * (c02 + (eccentricitySquared * + (c04 + (eccentricitySquared * (c06 + (eccentricitySquared * c08))))))); + double en1 = eccentricitySquared * (c22 - (eccentricitySquared * + (c04 + (eccentricitySquared * (c06 + (eccentricitySquared * c08)))))); + double tSeries = eccentricitySquared * eccentricitySquared; + double en2 = tSeries * (c44 - (eccentricitySquared * (c46 + (eccentricitySquared * c48)))); + double en3 = (tSeries *= eccentricitySquared) * (c66 - (eccentricitySquared * c68)); + double en4 = tSeries * eccentricitySquared * c88; + double latitudeOfOrigin = latitudeOfOriginDegrees * (Math.PI / 180d); + double ml0 = Mlfn(en0, en1, en2, en3, en4, latitudeOfOrigin, Math.Sin(latitudeOfOrigin), Math.Cos(latitudeOfOrigin)); + double x = xMeters / semiMajor; + double y = yMeters / semiMajor; + double phi = InvMlfn(ml0 + (y / scaleFactor), eccentricitySquared, en0, en1, en2, en3, en4); + + if (Math.Abs(phi) >= Math.PI / 2d) + { + return y < 0d ? -90d : 90d; + } + + double sinphi = Math.Sin(phi); + double cosphi = Math.Cos(phi); + double t = Math.Abs(cosphi) > epsilon ? sinphi / cosphi : 0d; + double n = esp * cosphi * cosphi; + double con = 1d - (eccentricitySquared * sinphi * sinphi); + double d = x * Math.Sqrt(con) / scaleFactor; + con *= t; + t *= t; + double ds = d * d; + double innerMost = 1385d + (t * (3633d + (t * (4095d + (coefficient * t))))); + double sixthTerm = 61d + (t * (90d - (252d * n) + (45d * t))) + (46d * n) - ((ds / 56d) * innerMost); + double fourthTerm = 5d + (t * (3d - (9d * n))) + (n * (1d - (4d * n))) - ((ds / 30d) * sixthTerm); + double latitudeRadians = phi - ((con * ds / (1d - eccentricitySquared)) * 0.5d * (1d - ((ds / 12d) * fourthTerm))); + + return latitudeRadians * (180d / Math.PI); + } + + private static double Mlfn(double en0, double en1, double en2, double en3, double en4, double phi, double sinPhi, double cosPhi) + { + cosPhi *= sinPhi; + sinPhi *= sinPhi; + return (en0 * phi) - (cosPhi * (en1 + (sinPhi * (en2 + (sinPhi * (en3 + (sinPhi * en4))))))); + } + + private static double InvMlfn(double arg, double eccentricitySquared, double en0, double en1, double en2, double en3, double en4) + { + double phi = arg; + double k = 1d / (1d - eccentricitySquared); + for (int i = 0; i < 20; i++) + { + double sinPhi = Math.Sin(phi); + double t = 1d - (eccentricitySquared * sinPhi * sinPhi); + t = (Mlfn(en0, en1, en2, en3, en4, phi, sinPhi, Math.Cos(phi)) - arg) * (t * Math.Sqrt(t)) * k; + phi -= t; + if (Math.Abs(t) < 1e-11d) + { + return phi; + } + } + + throw new InvalidOperationException("Transverse Mercator inverse meridional iteration did not converge."); + } + + private static double QsfnForTests(double sinphi, double eccent, double oneEs) + { + const double eps7 = 1e-7; + if (eccent < eps7) + { + return sinphi + sinphi; + } + + double con = eccent * sinphi; + double div1 = 1d - (con * con); + double div2 = 1d + con; + if (div1 == 0d || div2 == 0d) + { + throw new InvalidOperationException("Singular authalic q computation for HEALPix test."); + } + + return oneEs * ((sinphi / div1) - ((0.5d / eccent) * Math.Log((1d - con) / div2))); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, double latitudeOfOrigin, double centralMeridian, string? extraParameters) + { + return FormattableString.Invariant( + $"PROJCS[\"Coverage-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{latitudeOfOrigin}],PARAMETER[\"central_meridian\",{centralMeridian}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionParameterSetTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionParameterSetTests.cs new file mode 100644 index 00000000..aeee32de --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionParameterSetTests.cs @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using Xunit; + +/// +/// Tests for . +/// +public class ProjectionParameterSetTests +{ + /// + /// Verifies that the constructor rejects a null parameter sequence. + /// + [Fact] + public void Constructor_NullParameters_ThrowsArgumentNullException() + { + Assert.Throws(() => new ProjectionParameterSet(null!)); + } + + /// + /// Verifies that the original parameter names and insertion order are preserved when enumerating. + /// + [Fact] + public void ToProjectionParameter_PreservesOriginalNamesAndOrder() + { + ProjectionParameterSet parameterSet = CreateParameterSet( + new ProjectionParameter("Central_Meridian", 15.0), + new ProjectionParameter("Scale_Factor", 0.9996)); + + ProjectionParameter[] parameters = parameterSet.ToProjectionParameter().ToArray(); + + Assert.Collection( + parameters, + parameter => + { + Assert.Equal("Central_Meridian", parameter.Name); + Assert.Equal(15.0, parameter.Value); + }, + parameter => + { + Assert.Equal("Scale_Factor", parameter.Name); + Assert.Equal(0.9996, parameter.Value); + }); + } + + /// + /// Verifies that mandatory lookup is case-insensitive for the primary name. + /// + [Fact] + public void GetParameterValue_PrimaryName_IsCaseInsensitive() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("Central_Meridian", 15.0)); + + double value = parameterSet.GetParameterValue("central_meridian"); + + Assert.Equal(15.0, value); + } + + /// + /// Verifies that mandatory lookup falls back to alternate names. + /// + [Fact] + public void GetParameterValue_AlternateName_ReturnsValue() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("Longitude_Of_Center", 10.0)); + + double value = parameterSet.GetParameterValue("central_meridian", "longitude_of_center", "lon_0"); + + Assert.Equal(10.0, value); + } + + /// + /// Verifies that missing mandatory parameters raise a helpful exception message. + /// + [Fact] + public void GetParameterValue_MissingValue_ThrowsArgumentException() + { + ProjectionParameter[] parameters = []; + var parameterSet = new ProjectionParameterSet(parameters); + + ArgumentException exception = Assert.Throws( + () => parameterSet.GetParameterValue("central_meridian", "longitude_of_center", "lon_0")); + + Assert.Equal("parameterName", exception.ParamName); + Assert.Contains("Missing projection parameter 'central_meridian'", exception.Message, StringComparison.Ordinal); + Assert.Contains("'longitude_of_center'", exception.Message, StringComparison.Ordinal); + Assert.Contains("'lon_0'", exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies that optional lookup returns the primary value when present. + /// + [Fact] + public void GetOptionalParameterValue_PrimaryName_ReturnsStoredValue() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("Scale_Factor", 0.9996)); + + double value = parameterSet.GetOptionalParameterValue("scale_factor", 1.0); + + Assert.Equal(0.9996, value); + } + + /// + /// Verifies that optional lookup falls back to alternate names. + /// + [Fact] + public void GetOptionalParameterValue_AlternateName_ReturnsStoredValue() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("Latitude_Of_Center", 45.0)); + + double value = parameterSet.GetOptionalParameterValue("latitude_of_origin", 0.0, "latitude_of_center"); + + Assert.Equal(45.0, value); + } + + /// + /// Verifies that optional lookup returns the supplied default value when absent. + /// + [Fact] + public void GetOptionalParameterValue_MissingValue_ReturnsDefault() + { + ProjectionParameter[] parameters = []; + var parameterSet = new ProjectionParameterSet(parameters); + + double value = parameterSet.GetOptionalParameterValue("scale_factor", 1.0, "k_0"); + + Assert.Equal(1.0, value); + } + + /// + /// Verifies that returns a parameter with its original casing. + /// + [Fact] + public void Find_ExistingParameter_ReturnsParameterWithOriginalName() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("Scale_Factor", 0.9996)); + + ProjectionParameter? parameter = parameterSet.Find("scale_factor"); + + ProjectionParameter found = Assert.IsType(parameter); + Assert.Equal("Scale_Factor", found.Name); + Assert.Equal(0.9996, found.Value); + } + + /// + /// Verifies that returns null when the parameter is absent. + /// + [Fact] + public void Find_MissingParameter_ReturnsNull() + { + ProjectionParameter[] parameters = []; + var parameterSet = new ProjectionParameterSet(parameters); + + Assert.Null(parameterSet.Find("scale_factor")); + } + + /// + /// Verifies that indexed access returns parameters in insertion order. + /// + [Fact] + public void GetAtIndex_ReturnsParameterInInsertionOrder() + { + ProjectionParameterSet parameterSet = CreateParameterSet( + new ProjectionParameter("central_meridian", 15.0), + new ProjectionParameter("scale_factor", 0.9996)); + + ProjectionParameter parameter = parameterSet.GetAtIndex(1); + + Assert.Equal("scale_factor", parameter.Name); + Assert.Equal(0.9996, parameter.Value); + } + + /// + /// Verifies that indexed access rejects indices below the valid range. + /// + [Fact] + public void GetAtIndex_NegativeIndex_ThrowsArgumentOutOfRangeException() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("central_meridian", 15.0)); + + Assert.Throws(() => parameterSet.GetAtIndex(-1)); + } + + /// + /// Verifies that indexed access rejects indices above the valid range. + /// + [Fact] + public void GetAtIndex_IndexPastEnd_ThrowsArgumentOutOfRangeException() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("central_meridian", 15.0)); + + Assert.Throws(() => parameterSet.GetAtIndex(1)); + } + + /// + /// Verifies that equality returns true for equivalent sets. + /// + [Fact] + public void Equals_EquivalentSets_ReturnsTrue() + { + ProjectionParameterSet first = CreateParameterSet( + new ProjectionParameter("central_meridian", 15.0), + new ProjectionParameter("scale_factor", 0.9996)); + ProjectionParameterSet second = CreateParameterSet( + new ProjectionParameter("Central_Meridian", 15.0), + new ProjectionParameter("Scale_Factor", 0.9996)); + + Assert.True(first.Equals(second)); + Assert.True(first.Equals((object)second)); + } + + /// + /// Verifies that equality returns false when the other set is null. + /// + [Fact] + public void Equals_NullSet_ReturnsFalse() + { + ProjectionParameterSet first = CreateParameterSet(new ProjectionParameter("central_meridian", 15.0)); + + Assert.False(first.Equals((ProjectionParameterSet?)null)); + } + + /// + /// Verifies that equality returns false when the parameter counts differ. + /// + [Fact] + public void Equals_DifferentCounts_ReturnsFalse() + { + ProjectionParameterSet first = CreateParameterSet(new ProjectionParameter("central_meridian", 15.0)); + ProjectionParameterSet second = CreateParameterSet( + new ProjectionParameter("central_meridian", 15.0), + new ProjectionParameter("scale_factor", 0.9996)); + + Assert.False(first.Equals(second)); + } + + /// + /// Verifies that equality returns false when the other set is missing a key. + /// + [Fact] + public void Equals_MissingKey_ReturnsFalse() + { + ProjectionParameterSet first = CreateParameterSet(new ProjectionParameter("central_meridian", 15.0)); + ProjectionParameterSet second = CreateParameterSet(new ProjectionParameter("false_easting", 15.0)); + + Assert.False(first.Equals(second)); + } + + /// + /// Verifies that equality returns false when values differ. + /// + [Fact] + public void Equals_DifferentValue_ReturnsFalse() + { + ProjectionParameterSet first = CreateParameterSet(new ProjectionParameter("central_meridian", 15.0)); + ProjectionParameterSet second = CreateParameterSet(new ProjectionParameter("central_meridian", 10.0)); + + Assert.False(first.Equals(second)); + } + + /// + /// Verifies that the object overload returns false for a different type. + /// + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + ProjectionParameterSet parameterSet = CreateParameterSet(new ProjectionParameter("central_meridian", 15.0)); + + Assert.False(parameterSet.Equals("not a parameter set")); + } + + /// + /// Verifies that equivalent sets produce the same hash code. + /// + [Fact] + public void GetHashCode_EquivalentSets_ReturnSameValue() + { + ProjectionParameterSet first = CreateParameterSet( + new ProjectionParameter("central_meridian", 15.0), + new ProjectionParameter("scale_factor", 0.9996)); + ProjectionParameterSet second = CreateParameterSet( + new ProjectionParameter("Central_Meridian", 15.0), + new ProjectionParameter("Scale_Factor", 0.9996)); + + Assert.Equal(first.GetHashCode(), second.GetHashCode()); + } + + private static ProjectionParameterSet CreateParameterSet(params ProjectionParameter[] parameters) + { + return new ProjectionParameterSet(parameters); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionSupportTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionSupportTests.cs new file mode 100644 index 00000000..cb803148 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/ProjectionSupportTests.cs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates projection aliases and roundtrip behavior. +/// +public class ProjectionSupportTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that projection aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("eqearth")] + [InlineData("natearth")] + [InlineData("natearth2")] + [InlineData("robin")] + [InlineData("moll")] + [InlineData("aeqd")] + [InlineData("gnom")] + public void SupportsProjectionAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(1000d, 2000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward/inverse roundtrip stability for projection aliases. + /// + /// Projection alias to validate. + /// Input longitude. + /// Input latitude. + /// Maximum absolute roundtrip delta. + [Theory] + [InlineData("eqearth", 12.5d, 25.25d, 1e-6)] + [InlineData("natearth", -32.4d, 18.6d, 1e-6)] + [InlineData("natearth2", 77.2d, -22.8d, 1e-6)] + [InlineData("robin", 101.5d, 40.1d, 1e-5)] + [InlineData("moll", -73.8d, 5.5d, 1e-6)] + [InlineData("aeqd", 12.5d, 25.25d, 1e-6)] + [InlineData("gnom", 8.2d, 15.4d, 1e-6)] + public void SupportsProjectionRoundtrip(string projectionName, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(System.Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(System.Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName) + { + return + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) + { + return [x, y]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/PutninsAndVanDerGrintenProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/PutninsAndVanDerGrintenProjectionTests.cs new file mode 100644 index 00000000..460134a6 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/PutninsAndVanDerGrintenProjectionTests.cs @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Putnins and Van der Grinten projection families. +/// +public class PutninsAndVanDerGrintenProjectionTests +{ + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for Putnins and Van der Grinten projection families. + /// + /// Projection alias. + [Theory] + [InlineData("putp1")] + [InlineData("Putnins_P1")] + [InlineData("putp3p")] + [InlineData("Putnins_P3P")] + [InlineData("putp5p")] + [InlineData("Putnins_P5P")] + [InlineData("putp6p")] + [InlineData("Putnins_P6P")] + [InlineData("kav7")] + [InlineData("Kavrayskiy_VII")] + [InlineData("wag4")] + [InlineData("Wagner_IV")] + [InlineData("wag5")] + [InlineData("Wagner_V")] + [InlineData("wag6")] + [InlineData("Wagner_VI")] + [InlineData("weren")] + [InlineData("Werenskiold_I")] + [InlineData("vandg2")] + [InlineData("Van_Der_Grinten_II")] + [InlineData("vandg3")] + [InlineData("Van_Der_Grinten_III")] + [InlineData("vandg4")] + [InlineData("Van_Der_Grinten_IV")] + public void SupportsPutninsAndVanDerGrintenAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for Putnins and Van der Grinten projection families. + /// + [Theory] + [InlineData("putp1", 2d, 1d, 211642.762754160d, 105831.180787330d, 1e-6d)] + [InlineData("putp3p", 2d, 1d, 178238.118539985d, 89124.560786088d, 1e-6d)] + [InlineData("putp5p", 2d, 1d, 226388.175248756d, 113204.568558475d, 1e-6d)] + [InlineData("putp6p", 2d, 1d, 198034.195132195d, 125989.475461323d, 1e-6d)] + [InlineData("kav7", 2d, 1d, 193462.974943729d, 111701.072127637d, 1e-6d)] + [InlineData("wag4", 2d, 1d, 192801.218662384d, 129416.216394803d, 1e-6d)] + [InlineData("wag5", 2d, 1d, 203227.051925325d, 138651.631442713d, 1e-6d)] + [InlineData("wag6", 2d, 1d, 223391.801323985d, 111701.072127637d, 1e-6d)] + [InlineData("weren", 2d, 1d, 223378.515757634d, 146214.093042288d, 1e-6d)] + [InlineData("vandg2", 2d, 1d, 223395.247850437d, 111718.491037226d, 1e-4d)] + [InlineData("vandg3", 2d, 1d, 223395.249552831d, 111704.519904421d, 1e-6d)] + [InlineData("vandg4", 2d, 1d, 223374.577294355d, 111701.195484154d, 1e-6d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable Putnins and Van der Grinten projection families. + /// + [Theory] + [InlineData("putp1", 200d, 100d, 0.001889802d, 0.000944901d, 2e-9d)] + [InlineData("putp3p", 200d, 100d, 0.002244050d, 0.001122025d, 2e-9d)] + [InlineData("putp5p", 200d, 100d, 0.001766713d, 0.000883357d, 2e-9d)] + [InlineData("putp6p", 200d, 100d, 0.002019551d, 0.000793716d, 2e-9d)] + [InlineData("kav7", 200d, 100d, 0.002067483d, 0.000895247d, 2e-9d)] + [InlineData("wag4", 200d, 100d, 0.002074503d, 0.000772683d, 2e-9d)] + [InlineData("wag5", 200d, 100d, 0.001968072d, 0.000721216d, 2e-9d)] + [InlineData("wag6", 200d, 100d, 0.001790493d, 0.000895247d, 2e-9d)] + [InlineData("weren", 200d, 100d, 0.001790493d, 0.000683918d, 2e-9d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for inverse-capable Putnins and Van der Grinten projection families. + /// + [Theory] + [InlineData("putp1", 2d, 1d)] + [InlineData("putp3p", 2d, 1d)] + [InlineData("putp5p", 2d, 1d)] + [InlineData("putp6p", 2d, 1d)] + [InlineData("kav7", 2d, 1d)] + [InlineData("wag4", 2d, 1d)] + [InlineData("wag5", 2d, 1d)] + [InlineData("wag6", 2d, 1d)] + [InlineData("weren", 2d, 1d)] + public void SupportsPutninsAndVanDerGrintenRoundtrip(string projectionName, double longitude, double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-7d); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-7d); + } + + /// + /// Verifies van der Grinten II/III/IV remain forward-only. + /// + /// Projection code. + [Theory] + [InlineData("vandg2")] + [InlineData("vandg3")] + [InlineData("vandg4")] + public void VanDerGrintenVariantsDoNotSupportInverse(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + private static string BuildProjectedWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D5-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Sphere6400000}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/PutninsProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/PutninsProjectionTests.cs new file mode 100644 index 00000000..1cc7d945 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/PutninsProjectionTests.cs @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Putnins projection support for current projection group (putp2, putp3, putp4p, putp5, putp6). +/// +public class PutninsProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for Putnins projections. + /// + /// Projection alias. + [Theory] + [InlineData("putp2")] + [InlineData("Putnins_P2")] + [InlineData("putp3")] + [InlineData("Putnins_P3")] + [InlineData("putp4p")] + [InlineData("Putnins_P4P")] + [InlineData("putp5")] + [InlineData("Putnins_P5")] + [InlineData("putp6")] + [InlineData("Putnins_P6")] + public void SupportsAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for Putnins projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + [Theory] + [InlineData("putp2", 2d, 1d, 211638.039634339d, 117895.033043380d)] + [InlineData("putp2", -2d, -1d, -211638.039634339d, -117895.033043380d)] + [InlineData("putp3", 2d, 1d, 178227.115507794d, 89124.560786088d)] + [InlineData("putp3", -2d, -1d, -178227.115507794d, -89124.560786088d)] + [InlineData("putp4p", 2d, 1d, 195241.477349386d, 127796.782307926d)] + [InlineData("putp4p", -2d, -1d, -195241.477349386d, -127796.782307926d)] + [InlineData("putp5", 2d, 1d, 226367.213380562d, 113204.568558475d)] + [InlineData("putp5", -2d, -1d, -226367.213380562d, -113204.568558475d)] + [InlineData("putp6", 2d, 1d, 226369.395133403d, 110218.523796521d)] + [InlineData("putp6", -2d, -1d, -226369.395133403d, -110218.523796521d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies PROJ builtins inverse vectors for Putnins projections. + /// + /// Projection code. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + [Theory] + [InlineData("putp2", 200d, 100d, 0.001889802d, 0.000848202d)] + [InlineData("putp2", -200d, -100d, -0.001889802d, -0.000848202d)] + [InlineData("putp3", 200d, 100d, 0.002244050d, 0.001122025d)] + [InlineData("putp3", -200d, -100d, -0.002244050d, -0.001122025d)] + [InlineData("putp4p", 200d, 100d, 0.002048528d, 0.000782480d)] + [InlineData("putp4p", -200d, -100d, -0.002048528d, -0.000782480d)] + [InlineData("putp5", 200d, 100d, 0.001766713d, 0.000883357d)] + [InlineData("putp5", -200d, -100d, -0.001766713d, -0.000883357d)] + [InlineData("putp6", 200d, 100d, 0.001766713d, 0.000907296d)] + [InlineData("putp6", -200d, -100d, -0.001766713d, -0.000907296d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies roundtrip stability for Putnins projections. + /// + /// Projection code. + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData("putp2", 2d, 1d)] + [InlineData("putp3", -2d, -1d)] + [InlineData("putp4p", 2d, -1d)] + [InlineData("putp5", -2d, 1d)] + [InlineData("putp6", 2d, 1d)] + public void SupportsRoundtrip(string projectionName, double longitude, double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-9); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-9); + } + + private static string BuildProjectedWkt(string projectionName) + { + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/RobinsonProjectionRegressionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/RobinsonProjectionRegressionTests.cs new file mode 100644 index 00000000..f98b683a --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/RobinsonProjectionRegressionTests.cs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for Robinson projection parity with PROJ reference vectors. +/// +public class RobinsonProjectionRegressionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies Robinson forward projection against PROJ-generated vectors at latitude band midpoints. + /// + [Theory] + [InlineData(10d, 2.5d, 944391.935119083d, 267379.790905731d)] + [InlineData(10d, 12.5d, 938180.308475870d, 1336899.020249400d)] + [InlineData(10d, 42.5d, 859104.434900386d, 4541404.267002712d)] + [InlineData(10d, 87.5d, 520350.141311250d, 8537587.027768036d)] + public void RobinsonForwardMatchesProjCubicReference(double longitude, double latitude, double expectedX, double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("robin")); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems( + projected.GeographicCoordinateSystem, + projected); + double[] projectedPoint = forward.MathTransform.Transform([longitude, latitude]); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-3d); + } + + private static string BuildProjectedWkt(string projectionName) + { + return + $"PROJCS[\"Regression-{projectionName}\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/S2ProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/S2ProjectionTests.cs new file mode 100644 index 00000000..ab019a53 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/S2ProjectionTests.cs @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates S2 projection variants. +/// +public class S2ProjectionTests +{ + private const string Wgs84 = "SPHEROID[\"WGS 84\",6378137,298.257223563]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for S2 projection variants. + /// + /// Projection alias. + [Theory] + [InlineData("s2")] + [InlineData("S2")] + [InlineData("s2_projection")] + [InlineData("S2_Projection")] + public void SupportsS2AliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, 0d, 0d, 1d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(0d, 0d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for S2 projection variants. + /// + /// Projection latitude of origin in degrees. + /// Projection central meridian in degrees. + /// uv_to_st mode code (0=linear, 1=quadratic, 2=tangent, 3=none, null=default quadratic). + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x (S2 unit coordinate). + /// Expected y (S2 unit coordinate). + [Theory] + [InlineData(0d, 0d, 0d, 0d, 0d, 0.5d, 0.5d)] + [InlineData(0d, 0d, 0d, 0d, 45.19242321598196d, 0.5d, 1d)] + [InlineData(0d, 0d, 0d, -45d, 0d, 0d, 0.5d)] + [InlineData(0d, 0d, 0d, 20d, 20.124006563576454d, 0.6819851171331012d, 0.6936645165744716d)] + [InlineData(0d, 90d, 1d, 90d, 0d, 0.5d, 0.5d)] + [InlineData(0d, 90d, 1d, 70d, 20.124006563576454d, 0.27682804555233764d, 0.7351848576118168d)] + [InlineData(0d, 90d, 1d, 110d, 20.124006563576454d, 0.7231719544476624d, 0.7351848576118168d)] + [InlineData(90d, 0d, 2d, 0d, 90d, 0.5d, 0.5d)] + [InlineData(90d, 0d, 2d, 20d, 70.12337013762532d, 0.29020309743436806d, 0.4211558922141421d)] + [InlineData(90d, 0d, 2d, -20d, 70.12337013762532d, 0.29020309743436806d, 0.5788441077858579d)] + [InlineData(0d, 180d, 3d, 180d, 0d, 0d, 0d)] + [InlineData(0d, 180d, 3d, 160d, 20.124006563576454d, -0.3873290331489431d, -0.3639702342662023d)] + [InlineData(0d, 180d, 3d, -160d, 20.124006563576454d, -0.3873290331489431d, 0.3639702342662023d)] + [InlineData(0d, -90d, null, -90d, 0d, 0.5d, 0.5d)] + [InlineData(0d, -90d, null, -70d, 20.124006563576454d, 0.26481514238818316d, 0.7231719544476624d)] + [InlineData(0d, -90d, null, -110d, 20.124006563576454d, 0.26481514238818316d, 0.27682804555233764d)] + [InlineData(-90d, 0d, 0d, 0d, -90d, 0.5d, 0.5d)] + [InlineData(-90d, 0d, 0d, 20d, -70.12337013762533d, 0.5622425758450019d, 0.6710100716628344d)] + [InlineData(-90d, 0d, 0d, -20d, -70.12337013762533d, 0.4377574241549981d, 0.6710100716628344d)] + public void MatchesProjBuiltinsForwardVectors( + double lat0, + double lon0, + double? uvToSt, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("s2", lat0, lon0, uvToSt)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 2e-12d); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 2e-12d); + } + + /// + /// Verifies PROJ builtins inverse vectors for S2 projection variants. + /// + /// Projection latitude of origin in degrees. + /// Projection central meridian in degrees. + /// uv_to_st mode code (0=linear, 1=quadratic, 2=tangent, 3=none, null=default quadratic). + /// Input x (S2 unit coordinate). + /// Input y (S2 unit coordinate). + /// Expected longitude degrees. + /// Expected latitude degrees. + [Theory] + [InlineData(0d, 0d, 0d, 0.5d, 0.5d, 0d, 0d)] + [InlineData(0d, 0d, 0d, 0.5d, 1d, 0d, 45.19242321598196d)] + [InlineData(0d, 0d, 0d, 0d, 0.5d, -45d, 0d)] + [InlineData(0d, 0d, 0d, 0.6819851171331012d, 0.6936645165744716d, 20d, 20.124006563576454d)] + [InlineData(0d, 90d, 1d, 0.5d, 0.5d, 90d, 0d)] + [InlineData(0d, 90d, 1d, 0.27682804555233764d, 0.7351848576118168d, 70d, 20.124006563576454d)] + [InlineData(90d, 0d, 2d, 0.5d, 0.5d, 0d, 90d)] + [InlineData(90d, 0d, 2d, 0.29020309743436806d, 0.4211558922141421d, 20d, 70.12337013762532d)] + [InlineData(0d, 180d, 3d, 0d, 0d, 180d, 0d)] + [InlineData(0d, 180d, 3d, -0.3873290331489431d, -0.3639702342662023d, 160d, 20.124006563576454d)] + [InlineData(0d, -90d, null, 0.5d, 0.5d, -90d, 0d)] + [InlineData(0d, -90d, null, 0.26481514238818316d, 0.7231719544476624d, -70d, 20.124006563576454d)] + [InlineData(-90d, 0d, 0d, 0.5d, 0.5d, 0d, -90d)] + [InlineData(-90d, 0d, 0d, 0.5622425758450019d, 0.6710100716628344d, 20d, -70.12337013762533d)] + public void MatchesProjBuiltinsInverseVectors( + double lat0, + double lon0, + double? uvToSt, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("s2", lat0, lon0, uvToSt)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9d); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9d); + } + + /// + /// Verifies roundtrip stability for representative S2 faces and UV/ST modes. + /// + /// Projection latitude of origin in degrees. + /// Projection central meridian in degrees. + /// uv_to_st mode code (0=linear, 1=quadratic, 2=tangent, 3=none, null=default quadratic). + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData(0d, 0d, 0d, 20d, 20.124006563576454d)] + [InlineData(0d, 90d, 1d, 70d, 20.124006563576454d)] + [InlineData(90d, 0d, 2d, 20d, 70.12337013762532d)] + [InlineData(0d, 180d, 3d, 160d, 20.124006563576454d)] + [InlineData(0d, -90d, null, -70d, 20.124006563576454d)] + [InlineData(-90d, 0d, 0d, 20d, -70.12337013762533d)] + public void SupportsS2Roundtrip(double lat0, double lon0, double? uvToSt, double longitude, double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("s2", lat0, lon0, uvToSt)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-9d); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-9d); + } + + /// + /// Verifies invalid uv_to_st values are rejected. + /// + [Fact] + public void RejectsInvalidUvToStMode() + { + ArgumentException exception = Assert.Throws(() => + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("s2", 0d, 0d, 9d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + forward.MathTransform.Transform(CreatePoint(0d, 0d)); + }); + + Assert.Equal("parameters", exception.ParamName); + } + + private static string BuildProjectedWkt(string projectionName, double lat0, double lon0, double? uvToSt) + { + string uvParameter = uvToSt.HasValue + ? FormattableString.Invariant($",PARAMETER[\"uv_to_st\",{uvToSt.Value.ToString("R", CultureInfo.InvariantCulture)}]") + : string.Empty; + + return FormattableString.Invariant($"PROJCS[\"Specialty-D3-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Wgs84}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{lat0.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"central_meridian\",{lon0.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{uvParameter},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/SchProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/SchProjectionTests.cs new file mode 100644 index 00000000..ad852247 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/SchProjectionTests.cs @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates SCH projection runtime parity. +/// +public class SchProjectionTests +{ + private const string SchForwardOperation = "+proj=sch +datum=WGS84 +plat_0=30.0 +plon_0=45.0 +phdg_0=-12.0 +nodefs"; + private const string SchInverseOperation = "+proj=sch +datum=WGS84 +plat_0=30.0 +plon_0=45.0 +phdg_0=-12.0 +nodefs +inv"; + + /// + /// Verifies aliases resolve through runtime pipeline factory for SCH. + /// + /// PROJ operation string. + [Theory] + [InlineData("+proj=sch +datum=WGS84 +plat_0=30 +plon_0=45 +phdg_0=-12")] + [InlineData("+proj=spherical_cross_track_height +datum=WGS84 +plat_0=30 +plon_0=45 +phdg_0=-12")] + public void SupportsSchAliasesInRuntime(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + Assert.IsType(transform, exactMatch: false); + } + + /// + /// Verifies SCH forward vectors from PROJ test_cs2cs_various.yaml. + /// + /// Input longitude in degrees. + /// Input latitude in degrees. + /// Input height in meters. + /// Expected SCH X. + /// Expected SCH Y. + /// Expected SCH Z. + [Theory] + [InlineData(0d, 0d, 0d, -1977112.0305592d, 5551475.1418378d, 6595.7256583d)] + [InlineData(0d, 90d, 0d, 6618337.9734775d, -1152927.4060894d, 10055.1157181d)] + [InlineData(45d, 45d, 0d, 1630035.5650122d, -342353.6396475d, 128.3445654d)] + [InlineData(45.1d, 44.9d, 0d, 1617547.4295637d, -347855.9734973d, 125.4645102d)] + [InlineData(44.9d, 45.1d, 0d, 1642526.7453121d, -336878.8571851d, 131.3265616d)] + [InlineData(30d, 45d, 0d, 1974596.2356203d, 787409.8217445d, 773.0028577d)] + public void MatchesSchForwardVectors( + double inputLongitude, + double inputLatitude, + double inputHeight, + double expectedX, + double expectedY, + double expectedZ) + { + MathTransform transform = CreateTransform(SchForwardOperation); + double[] projected = transform.Transform(CreatePoint(inputLongitude, inputLatitude, inputHeight)); + + Assert.InRange(Math.Abs(projected[0] - expectedX), 0d, 1e-6); + Assert.InRange(Math.Abs(projected[1] - expectedY), 0d, 1e-6); + Assert.InRange(Math.Abs(projected[2] - expectedZ), 0d, 1e-6); + } + + /// + /// Verifies SCH inverse vectors from PROJ test_cs2cs_various.yaml. + /// + /// Input SCH X. + /// Input SCH Y. + /// Input SCH Z. + /// Expected longitude in degrees. + /// Expected latitude in degrees. + /// Expected ellipsoidal height in meters. + [Theory] + [InlineData(0d, 0d, 2d, 45d, 30d, 2d)] + [InlineData(0d, 1000d, 0d, 44.989863d, 29.998124d, -0.000362d)] + [InlineData(1000d, 0d, 0d, 44.997845d, 30.008824d, 0d)] + [InlineData(1000d, 1000d, 0d, 44.987707d, 30.006948d, -0.000523d)] + public void MatchesSchInverseVectors( + double inputX, + double inputY, + double inputZ, + double expectedLongitude, + double expectedLatitude, + double expectedHeight) + { + MathTransform transform = CreateTransform(SchInverseOperation); + double[] geographic = transform.Transform(CreatePoint(inputX, inputY, inputZ)); + + Assert.InRange(Math.Abs(geographic[0] - expectedLongitude), 0d, 1e-6); + Assert.InRange(Math.Abs(geographic[1] - expectedLatitude), 0d, 1e-6); + Assert.InRange(Math.Abs(geographic[2] - expectedHeight), 0d, 2e-3); + } + + /// + /// Verifies runtime validation errors for missing mandatory SCH parameters. + /// + /// PROJ operation string. + /// Token expected in validation message. + [Theory] + [InlineData("+proj=sch +plon_0=45 +phdg_0=-12", "plat_0")] + [InlineData("+proj=sch +plat_0=30 +phdg_0=-12", "plon_0")] + [InlineData("+proj=sch +plat_0=30 +plon_0=45", "phdg_0")] + public void SchCreationFailsWhenMandatoryParametersAreMissing(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains(expectedToken, skipReason, StringComparison.Ordinal); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/SimpleConicAndImwProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/SimpleConicAndImwProjectionTests.cs new file mode 100644 index 00000000..6a7c0ab9 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/SimpleConicAndImwProjectionTests.cs @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates simple conic and IMW-style projections. +/// +public class SimpleConicAndImwProjectionTests +{ + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for simple conic and IMW-style projections. + /// + /// Projection alias. + /// Spheroid clause. + /// Optional additional projection parameters. + [Theory] + [InlineData("euler", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("murd1", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("murd2", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("murd3", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("tissot", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("vitk1", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("imw_p", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("International_Map_of_the_World_Polyconic", Grs80, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("mbt_fps", Sphere6400000, null)] + [InlineData("McBryde_Thomas_Flat_Pole_Sine", Sphere6400000, null)] + [InlineData("bertin1953", Sphere6400000, null)] + [InlineData("Bertin_1953", Sphere6400000, null)] + public void SupportsSimpleConicAndImwAliasesFromWkt(string projectionName, string spheroidClause, string? extraParameters) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for simple conic and IMW-style projections. + /// + /// Projection code. + /// Spheroid clause. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + /// Optional additional projection parameters. + /// Absolute tolerance. + [Theory] + [InlineData("euler", Grs80, 2d, 1d, 222597.634659108d, 111404.240549919d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 1e-6d)] + [InlineData("murd1", Grs80, 2d, 1d, 222600.813473554d, 111404.244180546d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 1e-6d)] + [InlineData("murd2", Grs80, 2d, 1d, 222588.099751230d, 111426.140027412d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 1e-6d)] + [InlineData("murd3", Grs80, 2d, 1d, 222600.814077577d, 111404.246601372d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 1e-6d)] + [InlineData("tissot", Grs80, 2d, 1d, 222641.078699631d, 54347.828487281d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 1e-6d)] + [InlineData("vitk1", Grs80, 2d, 1d, 222607.171211458d, 111404.251442435d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 1e-6d)] + [InlineData("imw_p", Grs80, 2d, 1d, 222588.441139376d, 55321.128653810d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 1e-6d)] + [InlineData("mbt_fps", Sphere6400000, 2d, 1d, 198798.176129850d, 125512.017254531d, null, 1e-6d)] + [InlineData("bertin1953", Sphere6400000, 16.5d, 42d, 0d, 0d, null, 1e-6d)] + [InlineData("bertin1953", Sphere6400000, 0d, 0d, -1665321.948851200d, -4385446.772108800d, null, 1e-5d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + string spheroidClause, + double longitude, + double latitude, + double expectedX, + double expectedY, + string? extraParameters, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for inverse-capable simple conic and IMW-style projections. + /// + /// Projection code. + /// Spheroid clause. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + /// Optional additional projection parameters. + /// Absolute tolerance. + [Theory] + [InlineData("euler", Grs80, 200d, 100d, 0.001796281d, 0.000898315d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 2e-9d)] + [InlineData("murd1", Grs80, 200d, 100d, 0.001796255d, 0.000898315d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 2e-9d)] + [InlineData("murd2", Grs80, 200d, 100d, 0.001796357d, 0.000897887d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 2e-9d)] + [InlineData("murd3", Grs80, 200d, 100d, 0.001796255d, 0.000898315d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 2e-9d)] + [InlineData("tissot", Grs80, 200d, 100d, 0.001796281d, 0.513444955d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 2e-9d)] + [InlineData("vitk1", Grs80, 200d, 100d, 0.001796204d, 0.000898315d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 2e-9d)] + [InlineData("imw_p", Grs80, 200d, 100d, 0.001796699d, 0.500904924d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]", 2e-9d)] + [InlineData("mbt_fps", Sphere6400000, 200d, 100d, 0.002011971d, 0.000796712d, null, 2e-9d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + string spheroidClause, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + string? extraParameters, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for inverse-capable simple conic and IMW-style projections. + /// + /// Projection code. + /// Spheroid clause. + /// Input longitude degrees. + /// Input latitude degrees. + /// Optional additional projection parameters. + [Theory] + [InlineData("euler", Grs80, 2d, 1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("murd1", Grs80, -2d, -1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("murd2", Grs80, 2d, -1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("murd3", Grs80, -2d, 1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("tissot", Grs80, 2d, 1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("vitk1", Grs80, 2d, 1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("imw_p", Grs80, 2d, 1d, ",PARAMETER[\"lat_1\",0.5],PARAMETER[\"lat_2\",2]")] + [InlineData("mbt_fps", Sphere6400000, 2d, 1d, null)] + public void SupportsSimpleConicAndImwRoundtrip(string projectionName, string spheroidClause, double longitude, double latitude, string? extraParameters) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-7d); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-7d); + } + + /// + /// Verifies Bertin 1953 remains forward-only. + /// + [Fact] + public void Bertin1953DoesNotSupportInverse() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("bertin1953", Sphere6400000, null)); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies IMW Polyconic supports the special lat_1=0, lat_2=10 branch. + /// + [Fact] + public void ImwPolyconicSupportsLat1ZeroBranch() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt("imw_p", Grs80, ",PARAMETER[\"lat_1\",0],PARAMETER[\"lat_2\",10]")); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(0.000898315284d, 0d)); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(100d, 0d)); + + Assert.InRange(Math.Abs(projectedPoint[0] - 100d), 0d, 1e-6d); + Assert.InRange(Math.Abs(projectedPoint[1]), 0d, 1e-6d); + Assert.InRange(Math.Abs(geographicPoint[0] - 0.000898315284d), 0d, 1e-12d); + Assert.InRange(Math.Abs(geographicPoint[1]), 0d, 1e-12d); + } + + /// + /// Verifies conic variants reject degenerate standard parallels. + /// + /// Projection code. + [Theory] + [InlineData("euler")] + [InlineData("murd1")] + [InlineData("murd2")] + [InlineData("murd3")] + [InlineData("tissot")] + [InlineData("vitk1")] + public void SimpleConicVariantsRejectDegenerateStandardParallels(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(projectionName, Sphere6400000, ",PARAMETER[\"lat_1\",1],PARAMETER[\"lat_2\",1]")); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected)); + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, string? extraParameters) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D1-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/SnyderAppendixAProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/SnyderAppendixAProjectionTests.cs new file mode 100644 index 00000000..00860c38 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/SnyderAppendixAProjectionTests.cs @@ -0,0 +1,959 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Text; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies Snyder Appendix A worked examples against the implemented projection kernels. +/// +public sealed class SnyderAppendixAProjectionTests +{ + private const string Sphere1 = "SPHEROID[\"Sphere\",1,0]"; + private const string Sphere3 = "SPHEROID[\"Sphere\",3,0]"; + private const string Clarke66 = "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982]"; + private const string International = "SPHEROID[\"International\",6378388,297]"; + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Gets the Snyder Appendix A forward-reference cases. + /// + /// The worked forward vectors. + public static IEnumerable> GetForwardCases() + { + const double sphereTolerance = 5e-7d; + const double fineSphereTolerance = 3e-8d; + const double metreTolerance = 0.15d; + + yield return CreateForwardCase( + "Mercator sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 266-268", + BuildProjectedWkt( + "merc", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -180d), + ("scale_factor", 1d)), + -75d, + 35d, + 1.8325957d, + 0.6528366d, + sphereTolerance); + yield return CreateForwardCase( + "Mercator ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 266-268", + BuildProjectedWkt( + "merc", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -180d), + ("scale_factor", 1d)), + -75d, + 35d, + 11688673.70d, + 4139145.60d, + metreTolerance); + + yield return CreateForwardCase( + "Transverse Mercator sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 268-271", + BuildProjectedWkt( + "tmerc", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("scale_factor", 1d)), + -73.5d, + 40.5d, + 0.0199077d, + 0.7070276d, + sphereTolerance); + yield return CreateForwardCase( + "Transverse Mercator ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 268-271", + BuildProjectedWkt( + "tmerc", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("scale_factor", 0.9996d)), + -73.5d, + 40.5d, + 127106.50d, + 4484124.40d, + metreTolerance); + + yield return CreateForwardCase( + "Cylindrical Equal Area sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 272-273", + BuildProjectedWkt( + "cea", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 30d)), + 80d, + 35d, + 2.3428242d, + 0.6623090d, + sphereTolerance); + + // The extracted Appendix A normal-aspect ellipsoidal CEA numbers are internally + // consistent for phi = 5 degrees, which matches the q/y values and inverse block. + yield return CreateForwardCase( + "Cylindrical Equal Area ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 281-287", + BuildProjectedWkt( + "cea", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 5d)), + -78d, + 5d, + -332699.80d, + 554248.50d, + metreTolerance); + + yield return CreateForwardCase( + "Albers sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 291-294", + BuildProjectedWkt( + "aea", + Sphere1, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + -75d, + 35d, + 0.2952720d, + 0.2416774d, + sphereTolerance); + yield return CreateForwardCase( + "Albers ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 291-294", + BuildProjectedWkt( + "aea", + Clarke66, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + -75d, + 35d, + 1885472.70d, + 1535925.00d, + metreTolerance); + + yield return CreateForwardCase( + "Lambert Conformal Conic sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 295-298", + BuildProjectedWkt( + "lcc", + Sphere1, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 33d), + ("standard_parallel_2", 45d)), + -75d, + 35d, + 0.2966785d, + 0.2462112d, + sphereTolerance); + yield return CreateForwardCase( + "Lambert Conformal Conic ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 296-298", + BuildProjectedWkt( + "lcc", + Clarke66, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 33d), + ("standard_parallel_2", 45d)), + -75d, + 35d, + 1894410.90d, + 1564649.50d, + metreTolerance); + + yield return CreateForwardCase( + "Equidistant Conic sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 298-301", + BuildProjectedWkt( + "eqdc", + Sphere1, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + -75d, + 35d, + 0.2952057d, + 0.2424021d, + sphereTolerance); + yield return CreateForwardCase( + "Equidistant Conic ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 299-301", + BuildProjectedWkt( + "eqdc", + Clarke66, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + -75d, + 35d, + 1885051.90d, + 1540507.60d, + metreTolerance); + + yield return CreateForwardCase( + "Lambert Azimuthal Equal Area sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 332-337", + BuildProjectedWkt( + "laea", + Sphere3, + ("latitude_of_origin", 40d), + ("central_meridian", -100d)), + 100d, + -20d, + -4.2339303d, + 4.0257775d, + sphereTolerance); + yield return CreateForwardCase( + "Lambert Azimuthal Equal Area ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 333-336", + BuildProjectedWkt( + "laea", + Clarke66, + ("latitude_of_origin", 40d), + ("central_meridian", -100d)), + -110d, + 30d, + -965932.10d, + -1056814.90d, + metreTolerance); + yield return CreateForwardCase( + "Lambert Azimuthal Equal Area ellipsoidal polar forward", + "Snyder (1987), PP 1395, Appendix A, pp. 334-337", + BuildProjectedWkt( + "laea", + International, + ("latitude_of_origin", 90d), + ("central_meridian", -100d)), + 5d, + 80d, + 1077459.70d, + 288704.50d, + metreTolerance); + + yield return CreateForwardCase( + "Van der Grinten sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 363-365", + BuildProjectedWkt( + "vandg", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -85d)), + -160d, + -50d, + -1.1954154d, + -0.9960733d, + sphereTolerance); + + yield return CreateForwardCase( + "Sinusoidal sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 365-366", + BuildProjectedWkt( + "sinu", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + -75d, + -50d, + 0.1682814d, + -0.8726646d, + sphereTolerance); + yield return CreateForwardCase( + "Sinusoidal ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 366-366", + BuildProjectedWkt( + "sinu", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + -75d, + -50d, + 1075471.50d, + -5540628.00d, + metreTolerance); + + yield return CreateForwardCase( + "Mollweide sphere forward", + "Snyder (1987), PP 1395, Appendix A, p. 367", + BuildProjectedWkt( + "moll", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + -75d, + -50d, + 0.1788845d, + -0.9208758d, + sphereTolerance); + + yield return CreateForwardCase( + "Eckert IV sphere forward", + "Snyder (1987), PP 1395, Appendix A, p. 368", + BuildProjectedWkt( + "eck4", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + -75d, + -50d, + 0.1875270d, + -0.9519210d, + sphereTolerance); + + yield return CreateForwardCase( + "Eckert VI sphere forward", + "Snyder (1987), PP 1395, Appendix A, p. 369", + BuildProjectedWkt( + "eck6", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + -75d, + -50d, + 0.1693623d, + -0.9570223d, + sphereTolerance); + + yield return CreateForwardCase( + "Polyconic sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 303-306", + BuildProjectedWkt( + "poly", + Sphere1, + ("latitude_of_origin", 30d), + ("central_meridian", -96d)), + -75d, + 40d, + 0.2781798d, + 0.2074541d, + sphereTolerance); + yield return CreateForwardCase( + "Polyconic ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 304-306", + BuildProjectedWkt( + "poly", + Clarke66, + ("latitude_of_origin", 30d), + ("central_meridian", -96d)), + -75d, + 40d, + 1776774.50d, + 1319657.80d, + metreTolerance); + + yield return CreateForwardCase( + "Bonne sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 309-311", + BuildProjectedWkt( + "bonne", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 40d)), + -85d, + 30d, + -0.1508418d, + -0.1661807d, + sphereTolerance); + yield return CreateForwardCase( + "Bonne ellipsoidal forward", + "Snyder (1987), PP 1395, Appendix A, pp. 309-311", + BuildProjectedWkt( + "bonne", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 40d)), + -85d, + 30d, + -962915.10d, + -1056065.00d, + metreTolerance); + + yield return CreateForwardCase( + "Modified stereographic Alaska sphere forward", + "Snyder (1987), PP 1395, Appendix A, pp. 344-347", + BuildProjectedWkt( + "alsk", + Sphere1, + ("latitude_of_origin", 64d), + ("central_meridian", -152d), + ("scale_factor", 1d / 6370997d)), + -150d, + 60d, + 0.01739129d, + -0.06937775d, + fineSphereTolerance); + } + + /// + /// Gets the Snyder Appendix A inverse-reference cases. + /// + /// The worked inverse vectors. + public static IEnumerable> GetInverseCases() + { + const double sphereTolerance = 2e-6d; + const double relaxedSphereTolerance = 3e-6d; + const double alaskaInverseTolerance = 3e-7d; + const double degreeTolerance = 1e-5d; + + yield return CreateInverseCase( + "Mercator sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 266-268", + BuildProjectedWkt( + "merc", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -180d), + ("scale_factor", 1d)), + 1.8325957d, + 0.6528366d, + -75d, + 35d, + sphereTolerance); + yield return CreateInverseCase( + "Mercator ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 266-268", + BuildProjectedWkt( + "merc", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -180d), + ("scale_factor", 1d)), + 11688673.70d, + 4139145.60d, + -75d, + 35d, + degreeTolerance); + + yield return CreateInverseCase( + "Transverse Mercator sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 268-271", + BuildProjectedWkt( + "tmerc", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("scale_factor", 1d)), + 0.0199077d, + 0.7070276d, + -73.5d, + 40.5d, + relaxedSphereTolerance); + yield return CreateInverseCase( + "Transverse Mercator ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 268-271", + BuildProjectedWkt( + "tmerc", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("scale_factor", 0.9996d)), + 127106.50d, + 4484124.40d, + -73.5d, + 40.5d, + degreeTolerance); + + yield return CreateInverseCase( + "Cylindrical Equal Area sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 272-273", + BuildProjectedWkt( + "cea", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 30d)), + 2.3428242d, + 0.6623090d, + 80d, + 35d, + sphereTolerance); + yield return CreateInverseCase( + "Cylindrical Equal Area ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 281-287", + BuildProjectedWkt( + "cea", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 5d)), + -332699.80d, + 554248.50d, + -78d, + 5d, + degreeTolerance); + + yield return CreateInverseCase( + "Albers sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 291-294", + BuildProjectedWkt( + "aea", + Sphere1, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + 0.2952720d, + 0.2416774d, + -75d, + 35d, + relaxedSphereTolerance); + yield return CreateInverseCase( + "Albers ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 291-294", + BuildProjectedWkt( + "aea", + Clarke66, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + 1885472.70d, + 1535925.00d, + -75d, + 35d, + degreeTolerance); + + yield return CreateInverseCase( + "Lambert Conformal Conic sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 295-298", + BuildProjectedWkt( + "lcc", + Sphere1, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 33d), + ("standard_parallel_2", 45d)), + 0.2966785d, + 0.2462112d, + -75d, + 35d, + relaxedSphereTolerance); + yield return CreateInverseCase( + "Lambert Conformal Conic ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 296-298", + BuildProjectedWkt( + "lcc", + Clarke66, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 33d), + ("standard_parallel_2", 45d)), + 1894410.90d, + 1564649.50d, + -75d, + 35d, + degreeTolerance); + + yield return CreateInverseCase( + "Equidistant Conic sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 298-301", + BuildProjectedWkt( + "eqdc", + Sphere1, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + 0.2952057d, + 0.2424021d, + -75d, + 35d, + relaxedSphereTolerance); + yield return CreateInverseCase( + "Equidistant Conic ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 299-301", + BuildProjectedWkt( + "eqdc", + Clarke66, + ("latitude_of_origin", 23d), + ("central_meridian", -96d), + ("standard_parallel_1", 29.5d), + ("standard_parallel_2", 45.5d)), + 1885051.90d, + 1540507.60d, + -75d, + 35d, + degreeTolerance); + + yield return CreateInverseCase( + "Lambert Azimuthal Equal Area sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 332-337", + BuildProjectedWkt( + "laea", + Sphere3, + ("latitude_of_origin", 40d), + ("central_meridian", -100d)), + -4.2339303d, + 4.0257775d, + 100d, + -20d, + sphereTolerance); + yield return CreateInverseCase( + "Lambert Azimuthal Equal Area ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 333-336", + BuildProjectedWkt( + "laea", + Clarke66, + ("latitude_of_origin", 40d), + ("central_meridian", -100d)), + -965932.10d, + -1056814.90d, + -110d, + 30d, + degreeTolerance); + yield return CreateInverseCase( + "Lambert Azimuthal Equal Area ellipsoidal polar inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 334-337", + BuildProjectedWkt( + "laea", + International, + ("latitude_of_origin", 90d), + ("central_meridian", -100d)), + 1077459.70d, + 288704.50d, + 5d, + 80d, + degreeTolerance); + + yield return CreateInverseCase( + "Van der Grinten sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 363-365", + BuildProjectedWkt( + "vandg", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -85d)), + -1.1954154d, + -0.9960733d, + -160d, + -50d, + sphereTolerance); + + yield return CreateInverseCase( + "Sinusoidal sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 365-366", + BuildProjectedWkt( + "sinu", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + 0.1682814d, + -0.8726646d, + -75d, + -50d, + sphereTolerance); + yield return CreateInverseCase( + "Sinusoidal ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, p. 366", + BuildProjectedWkt( + "sinu", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + 1075471.50d, + -5540628.00d, + -75d, + -50d, + degreeTolerance); + + yield return CreateInverseCase( + "Mollweide sphere inverse", + "Snyder (1987), PP 1395, Appendix A, p. 367", + BuildProjectedWkt( + "moll", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + 0.1788845d, + -0.9208758d, + -75d, + -50d, + sphereTolerance); + + yield return CreateInverseCase( + "Eckert IV sphere inverse", + "Snyder (1987), PP 1395, Appendix A, p. 368", + BuildProjectedWkt( + "eck4", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + 0.1875270d, + -0.9519210d, + -75d, + -50d, + sphereTolerance); + + yield return CreateInverseCase( + "Eckert VI sphere inverse", + "Snyder (1987), PP 1395, Appendix A, p. 369", + BuildProjectedWkt( + "eck6", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -90d)), + 0.1693623d, + -0.9570223d, + -75d, + -50d, + relaxedSphereTolerance); + + yield return CreateInverseCase( + "Polyconic sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 303-306", + BuildProjectedWkt( + "poly", + Sphere1, + ("latitude_of_origin", 30d), + ("central_meridian", -96d)), + 0.2781798d, + 0.2074541d, + -75d, + 40d, + sphereTolerance); + yield return CreateInverseCase( + "Polyconic ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 304-306", + BuildProjectedWkt( + "poly", + Clarke66, + ("latitude_of_origin", 30d), + ("central_meridian", -96d)), + 1776774.50d, + 1319657.80d, + -75d, + 40d, + degreeTolerance); + + yield return CreateInverseCase( + "Bonne sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 309-311", + BuildProjectedWkt( + "bonne", + Sphere1, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 40d)), + -0.1508418d, + -0.1661807d, + -85d, + 30d, + sphereTolerance); + yield return CreateInverseCase( + "Bonne ellipsoidal inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 309-311", + BuildProjectedWkt( + "bonne", + Clarke66, + ("latitude_of_origin", 0d), + ("central_meridian", -75d), + ("standard_parallel_1", 40d)), + -962915.10d, + -1056065.00d, + -85d, + 30d, + degreeTolerance); + + yield return CreateInverseCase( + "Modified stereographic Alaska sphere inverse", + "Snyder (1987), PP 1395, Appendix A, pp. 344-347", + BuildProjectedWkt( + "alsk", + Sphere1, + ("latitude_of_origin", 64d), + ("central_meridian", -152d), + ("scale_factor", 1d / 6370997d)), + 0.01739129d, + -0.06937775d, + -150d, + 60d, + alaskaInverseTolerance); + } + + /// + /// Verifies Snyder Appendix A forward vectors. + /// + /// Human-readable case label. + /// Appendix citation. + /// Projection WKT. + /// Source longitude degrees. + /// Source latitude degrees. + /// Expected projected x. + /// Expected projected y. + /// Absolute tolerance. + [Theory] + [MemberData(nameof(GetForwardCases))] + public void MatchesSnyderAppendixAForwardVectors( + string caseLabel, + string citation, + string wkt, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + AssertCoordinateWithinTolerance(caseLabel, citation, "X", projectedPoint[0], expectedX, tolerance); + AssertCoordinateWithinTolerance(caseLabel, citation, "Y", projectedPoint[1], expectedY, tolerance); + } + + /// + /// Verifies Snyder Appendix A inverse vectors. + /// + /// Human-readable case label. + /// Appendix citation. + /// Projection WKT. + /// Source projected x. + /// Source projected y. + /// Expected longitude degrees. + /// Expected latitude degrees. + /// Absolute tolerance. + [Theory] + [MemberData(nameof(GetInverseCases))] + public void MatchesSnyderAppendixAInverseVectors( + string caseLabel, + string citation, + string wkt, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + AssertCoordinateWithinTolerance(caseLabel, citation, "longitude", geographicPoint[0], expectedLongitude, tolerance); + AssertCoordinateWithinTolerance(caseLabel, citation, "latitude", geographicPoint[1], expectedLatitude, tolerance); + } + + private static TheoryDataRow CreateForwardCase( + string caseLabel, + string citation, + string wkt, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + return new TheoryDataRow( + caseLabel, + citation, + wkt, + longitude, + latitude, + expectedX, + expectedY, + tolerance); + } + + private static TheoryDataRow CreateInverseCase( + string caseLabel, + string citation, + string wkt, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + return new TheoryDataRow( + caseLabel, + citation, + wkt, + x, + y, + expectedLongitude, + expectedLatitude, + tolerance); + } + + private static void AssertCoordinateWithinTolerance( + string caseLabel, + string citation, + string axis, + double actual, + double expected, + double tolerance) + { + double delta = Math.Abs(actual - expected); + Assert.True( + delta <= tolerance, + FormattableString.Invariant($"{caseLabel}: expected {axis} {expected:R}, actual {actual:R}, delta {delta:R}, tolerance {tolerance:R}. {citation}.")); + } + + private static string BuildProjectedWkt( + string projectionName, + string spheroidClause, + params (string Name, double Value)[] parameters) + { + StringBuilder builder = new(); + builder.Append(FormattableString.Invariant( + $"PROJCS[\"Snyder-AppendixA-{projectionName}\",GEOGCS[\"Snyder\",DATUM[\"Snyder_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"]")); + foreach ((string name, double value) in parameters) + { + builder.Append(FormattableString.Invariant($",PARAMETER[\"{name}\",{value:R}]")); + } + + if (!HasParameter(parameters, "false_easting")) + { + builder.Append(",PARAMETER[\"false_easting\",0]"); + } + + if (!HasParameter(parameters, "false_northing")) + { + builder.Append(",PARAMETER[\"false_northing\",0]"); + } + + builder.Append(",UNIT[\"metre\",1]]"); + return builder.ToString(); + } + + private static bool HasParameter((string Name, double Value)[] parameters, string name) + { + foreach ((string parameterName, _) in parameters) + { + if (string.Equals(parameterName, name, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/SpaceObliqueMercatorProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/SpaceObliqueMercatorProjectionTests.cs new file mode 100644 index 00000000..7dcd640c --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/SpaceObliqueMercatorProjectionTests.cs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates space oblique mercator projection variants. +/// +public class SpaceObliqueMercatorProjectionTests +{ + private const string Grs80 = "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + private const string Sphere6400000 = "SPHEROID[\"Sphere\",6400000,0]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for space oblique mercator projection variants. + /// + /// Projection alias. + [Theory] + [InlineData("som")] + [InlineData("Space_Oblique_Mercator")] + [InlineData("misrsom")] + [InlineData("lsat")] + public void SupportsSpaceObliqueMercatorAliasesFromWkt(string projectionName) + { + ArgumentNullException.ThrowIfNull(projectionName); + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildAliasWkt(projectionName)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for space oblique mercator projection variants. + /// + [Theory] + [InlineData("som", Grs80, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]", 2d, 1d, 18556630.368369825d, 9533394.675311271d, 1e-3d)] + [InlineData("som", Sphere6400000, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]", 2d, 1d, 18641249.279170386d, 9563342.532334166d, 1e-3d)] + [InlineData("misrsom", Grs80, ",PARAMETER[\"path\",1]", 2d, 1d, 18556630.368369825d, 9533394.675311271d, 1e-3d)] + [InlineData("misrsom", Sphere6400000, ",PARAMETER[\"path\",1]", 2d, 1d, 18641249.279170386d, 9563342.532334166d, 1e-3d)] + [InlineData("lsat", Grs80, ",PARAMETER[\"lsat\",1],PARAMETER[\"path\",2]", 2d, 1d, 18241950.014558550d, 9998256.839822935d, 1e-3d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + string spheroidClause, + string extraParameters, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies PROJ builtins inverse vectors for space oblique mercator projection variants. + /// + [Theory] + [InlineData("som", Grs80, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]", 200d, 100d, 127.759503988d, 0.001735150d, 2e-9d)] + [InlineData("som", Sphere6400000, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]", 200d, 100d, 127.759505148d, 0.001716231d, 2e-9d)] + [InlineData("misrsom", Grs80, ",PARAMETER[\"path\",1]", 200d, 100d, 127.759503988d, 0.001735150d, 2e-9d)] + [InlineData("misrsom", Sphere6400000, ",PARAMETER[\"path\",1]", 200d, 100d, 127.759505148d, 0.001716231d, 2e-9d)] + [InlineData("lsat", Grs80, ",PARAMETER[\"lsat\",1],PARAMETER[\"path\",2]", 200d, 100d, 126.000423835d, 0.001723782d, 2e-9d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + string spheroidClause, + string extraParameters, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for inverse-capable space oblique mercator projection variants. + /// + [Theory] + [InlineData("som", Grs80, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]", 2d, 1d)] + [InlineData("som", Sphere6400000, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]", -2d, -1d)] + [InlineData("misrsom", Grs80, ",PARAMETER[\"path\",1]", 2d, 1d)] + [InlineData("misrsom", Sphere6400000, ",PARAMETER[\"path\",1]", -2d, -1d)] + [InlineData("lsat", Grs80, ",PARAMETER[\"lsat\",1],PARAMETER[\"path\",2]", 2d, 1d)] + public void SupportsSpaceObliqueMercatorRoundtrip( + string projectionName, + string spheroidClause, + string extraParameters, + double longitude, + double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 2e-7d); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 2e-7d); + } + + /// + /// Verifies invalid SOM setup parameters are rejected. + /// + [Theory] + [InlineData("misrsom", Grs80, ",PARAMETER[\"path\",234]")] + [InlineData("lsat", Grs80, ",PARAMETER[\"lsat\",0],PARAMETER[\"path\",1]")] + [InlineData("lsat", Grs80, ",PARAMETER[\"lsat\",1],PARAMETER[\"path\",252]")] + [InlineData("som", Grs80, ",PARAMETER[\"inc_angle\",190],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]")] + [InlineData("som", Grs80, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"asc_lon\",127.7605356226]")] + public void RejectsInvalidSpaceObliqueMercatorParameterSets(string projectionName, string spheroidClause, string extraParameters) + { + ArgumentException exception = Assert.Throws(() => + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, spheroidClause, extraParameters)); + CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + }); + + Assert.Equal("parameters", exception.ParamName); + } + + private static string BuildAliasWkt(string projectionName) + { + return projectionName.ToUpperInvariant() switch + { + "MISRSOM" => BuildProjectedWkt(projectionName, Grs80, ",PARAMETER[\"path\",1]"), + "LSAT" => BuildProjectedWkt(projectionName, Grs80, ",PARAMETER[\"lsat\",1],PARAMETER[\"path\",2]"), + _ => BuildProjectedWkt(projectionName, Grs80, ",PARAMETER[\"inc_angle\",98.30382],PARAMETER[\"ps_rev\",0.06866666666666667],PARAMETER[\"asc_lon\",127.7605356226]"), + }; + } + + private static string BuildProjectedWkt(string projectionName, string spheroidClause, string extraParameters) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D4-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroidClause}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{extraParameters ?? string.Empty},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/SpilhausProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/SpilhausProjectionTests.cs new file mode 100644 index 00000000..20e4c9b2 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/SpilhausProjectionTests.cs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Spilhaus projection variants. +/// +public class SpilhausProjectionTests +{ + private const string Wgs84 = "SPHEROID[\"WGS 84\",6378137,298.257223563]"; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT. + /// + /// Projection alias. + [Theory] + [InlineData("spilhaus")] + [InlineData("Spilhaus")] + public void SupportsSpilhausAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, -49.56371678d, 66.94970198d, 40.17823482d, 45d, 1d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(130.4d, -16.2d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ/GIE forward vectors for default and parameterized Spilhaus. + /// + [Theory] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 45d, 1d, 130.4d, -16.2d, 3733410.0118d, -9320.8573d, 5000d)] + [InlineData(-49.56371678d, 10.1d, 40.17823482d, 45d, 1d, 130.4d, -16.2d, 4343770.7991d, -3701935.6242d, 5000d)] + [InlineData(30.1d, 66.94970198d, 40.17823482d, 45d, 1d, 130.4d, -16.2d, 3637341.2895d, -2571368.8666d, 5000d)] + [InlineData(-49.56371678d, 66.94970198d, 9.1d, 45d, 1d, 130.4d, -16.2d, 3061806.4542d, -1678791.7428d, 5000d)] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 40.1d, 1d, 130.4d, -16.2d, 3720561.6630d, 309609.60362d, 5000d)] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 45d, 0.9d, 130.4d, -16.2d, 3360069.0106d, -8388.7716d, 5000d)] + public void MatchesProjBuiltinsForwardVectors( + double lat0, + double lon0, + double azi, + double rot, + double k0, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("spilhaus", lat0, lon0, azi, rot, k0)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies inverse vectors for representative Spilhaus cases. + /// + [Theory] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 45d, 1d, 3733410.0118d, -9320.8573d, 130.4d, -16.2d, 0.01d)] + [InlineData(-49.56371678d, 10.1d, 40.17823482d, 45d, 1d, 4343770.7991d, -3701935.6242d, 130.4d, -16.2d, 0.01d)] + [InlineData(30.1d, 66.94970198d, 40.17823482d, 45d, 1d, 3637341.2895d, -2571368.8666d, 130.4d, -16.2d, 0.01d)] + public void MatchesProjBuiltinsInverseVectors( + double lat0, + double lon0, + double azi, + double rot, + double k0, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("spilhaus", lat0, lon0, azi, rot, k0)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies roundtrip stability for representative global points. + /// + [Theory] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 45d, 1d, -20.1d, 74.1d, 0.05d)] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 45d, 1d, -170d, -80d, 0.05d)] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 45d, 1d, 173d, 70d, 0.05d)] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 40.1d, 1d, 130.4d, -16.2d, 0.05d)] + [InlineData(-49.56371678d, 66.94970198d, 40.17823482d, 45d, 0.9d, 130.4d, -16.2d, 0.05d)] + public void SupportsSpilhausRoundtrip(double lat0, double lon0, double azi, double rot, double k0, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("spilhaus", lat0, lon0, azi, rot, k0)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName, double lat0, double lon0, double azi, double rot, double k0) + { + return FormattableString.Invariant($"PROJCS[\"Specialty-D7-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{Wgs84}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{lat0.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"central_meridian\",{lon0.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"scale_factor\",{k0.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],PARAMETER[\"azi\",{azi.ToString("R", CultureInfo.InvariantCulture)}],PARAMETER[\"rot\",{rot.ToString("R", CultureInfo.InvariantCulture)}],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/StereographicProjectionRegressionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/StereographicProjectionRegressionTests.cs new file mode 100644 index 00000000..f41c70fa --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/StereographicProjectionRegressionTests.cs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for PROJ-style stereographic parity across the supported modes. +/// +public class StereographicProjectionRegressionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies equatorial ellipsoidal stereographic forward vectors against PROJ builtins. + /// + [Theory] + [InlineData(2d, 1d, 222644.854550117d, 110610.883474174d)] + [InlineData(2d, -1d, 222644.854550117d, -110610.883474174d)] + [InlineData(-2d, 1d, -222644.854550117d, 110610.883474174d)] + [InlineData(-2d, -1d, -222644.854550117d, -110610.883474174d)] + public void EquatorialEllipsoidalForwardMatchesProjReference(double longitude, double latitude, double expectedX, double expectedY) + { + double[] projectedPoint = CreateForwardTransform(BuildEllipsoidalEquatorialWkt()).MathTransform.Transform([longitude, latitude]); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-3d); + } + + /// + /// Verifies equatorial ellipsoidal stereographic inverse vectors against PROJ builtins. + /// + [Theory] + [InlineData(200d, 100d, 0.001796631d, 0.000904369d)] + [InlineData(200d, -100d, 0.001796631d, -0.000904369d)] + [InlineData(-200d, 100d, -0.001796631d, 0.000904369d)] + [InlineData(-200d, -100d, -0.001796631d, -0.000904369d)] + public void EquatorialEllipsoidalInverseMatchesProjReference(double x, double y, double expectedLongitude, double expectedLatitude) + { + double[] geographicPoint = CreateInverseTransform(BuildEllipsoidalEquatorialWkt()).MathTransform.Transform([x, y]); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 1e-9d); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 1e-9d); + } + + /// + /// Verifies equatorial spherical stereographic forward vectors against PROJ builtins. + /// + [Theory] + [InlineData(2d, 1d, 223407.810259507d, 111737.938996443d)] + [InlineData(2d, -1d, 223407.810259507d, -111737.938996443d)] + [InlineData(-2d, 1d, -223407.810259507d, 111737.938996443d)] + [InlineData(-2d, -1d, -223407.810259507d, -111737.938996443d)] + public void EquatorialSphericalForwardMatchesProjReference(double longitude, double latitude, double expectedX, double expectedY) + { + double[] projectedPoint = CreateForwardTransform(BuildSphericalEquatorialWkt()).MathTransform.Transform([longitude, latitude]); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-3d); + } + + /// + /// Verifies equatorial spherical stereographic inverse vectors against PROJ builtins. + /// + [Theory] + [InlineData(200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData(200d, -100d, 0.001790493d, -0.000895247d)] + [InlineData(-200d, 100d, -0.001790493d, 0.000895247d)] + [InlineData(-200d, -100d, -0.001790493d, -0.000895247d)] + public void EquatorialSphericalInverseMatchesProjReference(double x, double y, double expectedLongitude, double expectedLatitude) + { + double[] geographicPoint = CreateInverseTransform(BuildSphericalEquatorialWkt()).MathTransform.Transform([x, y]); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 1e-9d); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 1e-9d); + } + + /// + /// Verifies polar ellipsoidal true-scale vectors against PROJ builtins. + /// + [Fact] + public void PolarEllipsoidalVariantBMatchesProjReference() + { + double[] projectedPoint = CreateForwardTransform(BuildPolarEllipsoidalVariantBWkt()).MathTransform.Transform([20d, -70d]); + + Assert.InRange(Math.Abs(projectedPoint[0] - 748315.3282d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 2055979.4669d), 0d, 1e-3d); + } + + /// + /// Verifies polar spherical true-scale vectors against PROJ builtins. + /// + [Fact] + public void PolarSphericalVariantBMatchesProjReference() + { + double[] projectedPoint = CreateForwardTransform(BuildPolarSphericalVariantBWkt()).MathTransform.Transform([20d, -70d]); + + Assert.InRange(Math.Abs(projectedPoint[0] - 746100.2968d), 0d, 1e-3d); + Assert.InRange(Math.Abs(projectedPoint[1] - 2049893.7182d), 0d, 1e-3d); + } + + private static ICoordinateTransformation CreateForwardTransform(string projectedWkt) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + projectedWkt); + return CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + } + + private static ICoordinateTransformation CreateInverseTransform(string projectedWkt) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + projectedWkt); + return CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + } + + private static string BuildEllipsoidalEquatorialWkt() + { + return "PROJCS[\"Regression-stere-ellipsoidal-equatorial\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",SPHEROID[\"GRS 80\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"stere\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static string BuildSphericalEquatorialWkt() + { + return "PROJCS[\"Regression-stere-spherical-equatorial\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"stere\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static string BuildPolarEllipsoidalVariantBWkt() + { + return "PROJCS[\"Regression-stere-ellipsoidal-polar-b\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",SPHEROID[\"GRS 80\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"stere\"],PARAMETER[\"latitude_of_origin\",-90],PARAMETER[\"central_meridian\",0],PARAMETER[\"lat_ts\",-70],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static string BuildPolarSphericalVariantBWkt() + { + return "PROJCS[\"Regression-stere-spherical-polar-b\",GEOGCS[\"Regression-Geog\",DATUM[\"Regression-Datum\",SPHEROID[\"Sphere\",6378137,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"stere\"],PARAMETER[\"latitude_of_origin\",-90],PARAMETER[\"central_meridian\",0],PARAMETER[\"lat_ts\",-70],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/SwissObliqueMercatorProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/SwissObliqueMercatorProjectionTests.cs new file mode 100644 index 00000000..5dd7fac1 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/SwissObliqueMercatorProjectionTests.cs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Swiss Oblique Mercator (somerc) projection support. +/// +public class SwissObliqueMercatorProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that somerc aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("somerc")] + [InlineData("Swiss_Oblique_Mercator")] + public void SupportsSomercAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward/inverse roundtrip stability for somerc aliases. + /// + /// Projection alias to validate. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Maximum absolute roundtrip delta (degrees). + [Theory] + [InlineData("somerc", 2d, 1d, 1e-9)] + [InlineData("somerc", -2d, -1d, 1e-9)] + [InlineData("Swiss_Oblique_Mercator", 0.25d, -0.5d, 1e-9)] + public void SupportsSomercRoundtrip(string projectionName, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, false)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies forward values against PROJ builtins vectors for ellipsoidal and spherical somerc. + /// + /// Projection alias to validate. + /// Whether to use a spherical ellipsoid definition. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData("somerc", false, 2d, 1d, 222638.981586547d, 110579.965218249d)] + [InlineData("somerc", false, 2d, -1d, 222638.981586547d, -110579.965218251d)] + [InlineData("somerc", true, 2d, 1d, 223402.144255274d, 111706.743574944d)] + [InlineData("Swiss_Oblique_Mercator", true, -2d, -1d, -223402.144255274d, -111706.743574945d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + bool useSphere, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, useSphere)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies inverse values against PROJ builtins vectors for ellipsoidal and spherical somerc. + /// + /// Projection alias to validate. + /// Whether to use a spherical ellipsoid definition. + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData("somerc", false, 200d, 100d, 0.001796631d, 0.000904369d)] + [InlineData("somerc", false, -200d, -100d, -0.001796631d, -0.000904369d)] + [InlineData("somerc", true, 200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData("Swiss_Oblique_Mercator", true, -200d, -100d, -0.001790493d, -0.000895247d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + bool useSphere, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, useSphere)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 1e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 1e-9); + } + + private static string BuildProjectedWkt(string projectionName, bool useSphere) + { + string spheroid = useSphere + ? "SPHEROID[\"Sphere\",6400000,0]" + : "SPHEROID[\"GRS 80\",6378137,298.257222101]"; + + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",{spheroid}],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/TransverseCylindricalEqualAreaProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/TransverseCylindricalEqualAreaProjectionTests.cs new file mode 100644 index 00000000..20f18c16 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/TransverseCylindricalEqualAreaProjectionTests.cs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates TCEA projection aliases and roundtrip behavior. +/// +public class TransverseCylindricalEqualAreaProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that TCEA aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("tcea")] + [InlineData("Transverse_Cylindrical_Equal_Area")] + public void SupportsTceaAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, 0d, 0d, 1d)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + double[] result = transform.MathTransform.Transform(CreatePoint(120000d, 210000d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward/inverse roundtrip stability for TCEA aliases. + /// + /// Projection alias to validate. + /// Input longitude. + /// Input latitude. + /// Projection latitude of origin. + /// Projection central meridian. + /// Projection scale factor. + /// Maximum absolute roundtrip delta. + [Theory] + [InlineData("tcea", 8.2d, 47.3d, 0d, 0d, 1d, 1e-9)] + [InlineData("tcea", -73.5d, 22.1d, 10d, -30d, 0.9999d, 1e-8)] + [InlineData("Transverse_Cylindrical_Equal_Area", 45.5d, -12.75d, -5d, 20d, 1.0002d, 1e-8)] + public void SupportsTceaRoundtrip( + string projectionName, + double longitude, + double latitude, + double latitudeOfOrigin, + double centralMeridian, + double scaleFactor, + double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + BuildProjectedWkt(projectionName, latitudeOfOrigin, centralMeridian, scaleFactor)); + + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, GeographicCoordinateSystem.WGS84); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + private static string BuildProjectedWkt(string projectionName, double latitudeOfOrigin, double centralMeridian, double scaleFactor) + { + string latitudeText = latitudeOfOrigin.ToString(CultureInfo.InvariantCulture); + string meridianText = centralMeridian.ToString(CultureInfo.InvariantCulture); + string scaleText = scaleFactor.ToString(CultureInfo.InvariantCulture); + + return + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",{latitudeText}],PARAMETER[\"central_meridian\",{meridianText}],PARAMETER[\"scale_factor\",{scaleText}],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/VanDerGrintenProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/VanDerGrintenProjectionTests.cs new file mode 100644 index 00000000..afce1553 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/VanDerGrintenProjectionTests.cs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates van der Grinten I (vandg) projection support. +/// +public class VanDerGrintenProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that vandg aliases resolve from WKT and produce usable transforms. + /// + /// Projection alias to validate. + [Theory] + [InlineData("vandg")] + [InlineData("VanDerGrinten")] + [InlineData("van_der_grinten")] + public void SupportsVandgAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, 6400000d)); + ICoordinateTransformation transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] result = transform.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(transform); + Assert.NotNull(result); + Assert.True(result.Length >= 2); + } + + /// + /// Verifies forward/inverse roundtrip stability for vandg aliases. + /// + /// Projection alias to validate. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Maximum absolute roundtrip delta (degrees). + [Theory] + [InlineData("vandg", 2d, 1d, 2e-8)] + [InlineData("vandg", -2d, -1d, 2e-8)] + [InlineData("VanDerGrinten", 30d, -20d, 2e-8)] + public void SupportsVandgRoundtrip(string projectionName, double longitude, double latitude, double tolerance) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, 6400000d)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, tolerance); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, tolerance); + } + + /// + /// Verifies forward values against PROJ builtins vectors. + /// + /// Projection alias to validate. + /// Input longitude (degrees). + /// Input latitude (degrees). + /// Expected x result (meters). + /// Expected y result (meters). + [Theory] + [InlineData("vandg", 2d, 1d, 223395.249543407d, 111704.596633675d)] + [InlineData("vandg", 2d, -1d, 223395.249543407d, -111704.596633675d)] + [InlineData("vandergrinten", -2d, 1d, -223395.249543407d, 111704.596633675d)] + [InlineData("van_der_grinten_i", -2d, -1d, -223395.249543407d, -111704.596633675d)] + [InlineData("vandg", 179.9d, 50d, 18549161.7268d, 7731305.7162d)] + [InlineData("vandg", 180.1d, 50d, -18549161.7268d, 7731305.7162d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, 6400000d)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 3.5e-4); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 3.5e-4); + } + + /// + /// Verifies inverse values against PROJ builtins vectors. + /// + /// Projection alias to validate. + /// Input x (meters). + /// Input y (meters). + /// Expected longitude (degrees). + /// Expected latitude (degrees). + [Theory] + [InlineData("vandg", 200d, 100d, 0.001790494d, 0.000895247d)] + [InlineData("vandergrinten", 200d, -100d, 0.001790494d, -0.000895247d)] + [InlineData("van_der_grinten", -200d, 100d, -0.001790494d, 0.000895247d)] + [InlineData("van_der_grinten_i", -200d, -100d, -0.001790494d, -0.000895247d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, 6400000d)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 1e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 1e-9); + } + + private static string BuildProjectedWkt(string projectionName, double radius) + { + string radiusText = radius.ToString(CultureInfo.InvariantCulture); + return + $"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"Sphere\",DATUM[\"Sphere_Datum\",SPHEROID[\"Sphere\",{radiusText},0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]"; + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Projections/WagnerProjectionTests.cs b/test/ProjNet.Tests/CoordinateSystems/Projections/WagnerProjectionTests.cs new file mode 100644 index 00000000..89c326c5 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Projections/WagnerProjectionTests.cs @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Globalization; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates Wagner projection support for current projection group (wag2, wag3, wag7). +/// +public class WagnerProjectionTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies aliases resolve from WKT for Wagner projections. + /// + /// Projection alias. + [Theory] + [InlineData("wag2")] + [InlineData("Wagner_II")] + [InlineData("wag3")] + [InlineData("Wagner_III")] + [InlineData("wag7")] + [InlineData("Wagner_VII")] + public void SupportsAliasesFromWkt(string projectionName) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, null)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(2d, 1d)); + + Assert.NotNull(projected); + Assert.NotNull(forward); + Assert.NotNull(projectedPoint); + Assert.True(projectedPoint.Length >= 2); + } + + /// + /// Verifies PROJ builtins forward vectors for Wagner projections. + /// + /// Projection code. + /// Optional lat_ts in degrees. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected x meters. + /// Expected y meters. + [Theory] + [InlineData("wag2", null, 2d, 1d, 206589.888099962d, 120778.040357547d)] + [InlineData("wag2", null, -2d, -1d, -206589.888099962d, -120778.040357547d)] + [InlineData("wag3", null, 2d, 1d, 223387.021718166d, 111701.072127637d)] + [InlineData("wag3", null, -2d, -1d, -223387.021718166d, -111701.072127637d)] + [InlineData("wag7", null, 2d, 1d, 198601.876957312d, 125637.045714171d)] + [InlineData("wag7", null, -2d, -1d, -198601.876957312d, -125637.045714171d)] + public void MatchesProjBuiltinsForwardVectors( + string projectionName, + double? latTs, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, latTs)); + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected.GeographicCoordinateSystem, projected); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + + Assert.InRange(Math.Abs(projectedPoint[0] - expectedX), 0d, 1e-7); + Assert.InRange(Math.Abs(projectedPoint[1] - expectedY), 0d, 1e-7); + } + + /// + /// Verifies PROJ builtins inverse vectors for Wagner II and III. + /// + /// Projection code. + /// Optional lat_ts in degrees. + /// Input x meters. + /// Input y meters. + /// Expected longitude degrees. + /// Expected latitude degrees. + [Theory] + [InlineData("wag2", null, 200d, 100d, 0.001936024d, 0.000827958d)] + [InlineData("wag2", null, -200d, -100d, -0.001936024d, -0.000827958d)] + [InlineData("wag3", null, 200d, 100d, 0.001790493d, 0.000895247d)] + [InlineData("wag3", null, -200d, -100d, -0.001790493d, -0.000895247d)] + public void MatchesProjBuiltinsInverseVectors( + string projectionName, + double? latTs, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, latTs)); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem); + double[] geographicPoint = inverse.MathTransform.Transform(CreatePoint(x, y)); + + Assert.InRange(Math.Abs(geographicPoint[0] - expectedLongitude), 0d, 2e-9); + Assert.InRange(Math.Abs(geographicPoint[1] - expectedLatitude), 0d, 2e-9); + } + + /// + /// Verifies Wagner VII inverse is intentionally unavailable. + /// + [Fact] + public void WagnerViiInverseIsNotSupported() + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("wag7", null)); + Assert.Throws( + () => CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, projected.GeographicCoordinateSystem)); + } + + /// + /// Verifies roundtrip stability for Wagner II and III. + /// + /// Projection code. + /// Optional lat_ts in degrees. + /// Input longitude degrees. + /// Input latitude degrees. + [Theory] + [InlineData("wag2", null, 2d, 1d)] + [InlineData("wag2", null, -2d, -1d)] + [InlineData("wag3", null, 2d, 1d)] + [InlineData("wag3", null, -2d, -1d)] + [InlineData("wag3", 10d, 2d, 1d)] + [InlineData("wag3", 10d, -2d, -1d)] + public void SupportsRoundtrip(string projectionName, double? latTs, double longitude, double latitude) + { + ProjectedCoordinateSystem projected = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt(projectionName, latTs)); + GeographicCoordinateSystem geographic = projected.GeographicCoordinateSystem; + ICoordinateTransformation forward = CoordinateTransformationFactory.CreateFromCoordinateSystems(geographic, projected); + ICoordinateTransformation inverse = CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic); + double[] projectedPoint = forward.MathTransform.Transform(CreatePoint(longitude, latitude)); + double[] roundtrip = inverse.MathTransform.Transform(projectedPoint); + + Assert.InRange(Math.Abs(roundtrip[0] - longitude), 0d, 1e-9); + Assert.InRange(Math.Abs(roundtrip[1] - latitude), 0d, 1e-9); + } + + /// + /// Verifies that lat_ts changes Wagner III forward output while remaining numerically stable. + /// + [Fact] + public void WagnerIiiLatTsChangesForwardResult() + { + ProjectedCoordinateSystem projectedDefault = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("wag3", null)); + ProjectedCoordinateSystem projectedLatTs10 = ProjNet.Tests.CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, BuildProjectedWkt("wag3", 10d)); + ICoordinateTransformation forwardDefault = CoordinateTransformationFactory.CreateFromCoordinateSystems(projectedDefault.GeographicCoordinateSystem, projectedDefault); + ICoordinateTransformation forwardLatTs10 = CoordinateTransformationFactory.CreateFromCoordinateSystems(projectedLatTs10.GeographicCoordinateSystem, projectedLatTs10); + + double[] point = CreatePoint(2d, 1d); + double[] projectedDefaultPoint = forwardDefault.MathTransform.Transform(point); + double[] projectedLatTs10Point = forwardLatTs10.MathTransform.Transform(point); + + Assert.InRange(Math.Abs(projectedDefaultPoint[1] - projectedLatTs10Point[1]), 0d, 1e-9); + Assert.True(Math.Abs(projectedDefaultPoint[0] - projectedLatTs10Point[0]) > 1e-6); + } + + private static string BuildProjectedWkt(string projectionName, double? latTs) + { + string latTsParameter = latTs.HasValue + ? FormattableString.Invariant($",PARAMETER[\"lat_ts\",{latTs.Value}]") + : string.Empty; + + return FormattableString.Invariant($"PROJCS[\"Projection-{projectionName}\",GEOGCS[\"GIE\",DATUM[\"GIE_Datum\",SPHEROID[\"Sphere\",6400000,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"{projectionName}\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]{latTsParameter},UNIT[\"metre\",1]]"); + } + + private static double[] CreatePoint(double x, double y) => [x, y]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/ProjectionsRegistryTests.cs b/test/ProjNet.Tests/CoordinateSystems/ProjectionsRegistryTests.cs new file mode 100644 index 00000000..0c022e75 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/ProjectionsRegistryTests.cs @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using Xunit; + +/// +/// Verifies projection-registry behaviors that are important for immutable projection identity handling. +/// +public class ProjectionsRegistryTests +{ + /// + /// Verifies alias-based registry lookups keep the requested projection name while retaining the canonical implementation name as alias metadata. + /// + [Fact] + public void CreateProjection_WithAliasLookup_PreservesRequestedName() + { + MapProjection projection = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("mercator", CreateMercatorParameters())); + + Assert.Equal("mercator", projection.Name); + Assert.Equal("mercator", projection.ClassName); + Assert.Equal("Mercator_2SP", projection.Alias); + Assert.Equal("EPSG", projection.Authority); + Assert.Equal(9805, projection.AuthorityCode); + } + + /// + /// Verifies canonical registry lookups do not synthesize an alias when the constructor already assigns the requested name. + /// + [Fact] + public void CreateProjection_WithCanonicalLookup_DoesNotAssignAlias() + { + MapProjection projection = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("Mercator_1SP", CreateMercatorParameters(scaleFactor: 1d))); + + Assert.Equal("Mercator_1SP", projection.Name); + Assert.Equal("Mercator_1SP", projection.ClassName); + Assert.Equal(string.Empty, projection.Alias); + Assert.Equal("EPSG", projection.Authority); + } + + /// + /// Verifies custom registrations that still use the public + /// surface retain the requested alias metadata. + /// + [Fact] + public void Register_WithCustomAlias_PreservesRequestedName() + { + const string alias = "copilot_mercator_test_alias"; + ProjectionsRegistry.Register(alias, typeof(Mercator)); + + MapProjection projection = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection(alias, CreateMercatorParameters())); + + Assert.Equal(alias, projection.Name); + Assert.Equal(alias, projection.ClassName); + Assert.Equal("Mercator_2SP", projection.Alias); + Assert.Equal("EPSG", projection.Authority); + Assert.Equal(9805, projection.AuthorityCode); + } + + /// + /// Verifies the general PROJ stere alias and WKT/EPSG polar_stereographic + /// remain routed to their distinct runtime implementations. + /// + [Fact] + public void CreateProjection_WithStereAliases_UsesExpectedRuntimeImplementation() + { + MapProjection generalStereographic = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("stere", CreateStereographicParameters(latitudeOfOrigin: 45d))); + MapProjection polarStereographic = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("polar_stereographic", CreateStereographicParameters(latitudeOfOrigin: -71d))); + + Assert.IsType(generalStereographic); + Assert.IsType(polarStereographic); + } + + private static List CreateMercatorParameters(double? scaleFactor = null) + { + var parameters = new List + { + new("semi_major", 6378137d), + new("semi_minor", 6356752.314245179d), + new("central_meridian", 0d), + new("latitude_of_origin", 0d), + new("unit", 1d), + }; + + if (scaleFactor is not null) + { + parameters.Add(new ProjectionParameter("scale_factor", scaleFactor.Value)); + } + + return parameters; + } + + private static List CreateStereographicParameters(double latitudeOfOrigin) + { + return + [ + new ProjectionParameter("semi_major", 6378137d), + new ProjectionParameter("semi_minor", 6356752.314245179d), + new ProjectionParameter("central_meridian", 0d), + new ProjectionParameter("latitude_of_origin", latitudeOfOrigin), + new ProjectionParameter("scale_factor", 1d), + new ProjectionParameter("unit", 1d), + ]; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/TemporalCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/TemporalCoordinateSystemTests.cs new file mode 100644 index 00000000..109946c5 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/TemporalCoordinateSystemTests.cs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for , , and . +/// +public class TemporalCoordinateSystemTests +{ + /// + /// Verifies that temporal datum output retains the time origin. + /// + [Fact] + public void TemporalDatum_ToWktNode_RetainsTimeOrigin() + { + var datum = new TemporalDatum("1950-01-01T00:00:00Z", "Unix epoch", "EPSG", 1040, string.Empty, string.Empty, string.Empty); + string wkt = datum.ToWktNode(WktVersion.Wkt22019).ToString(); + + Assert.Contains("TDATUM[\"Unix epoch\"", wkt, StringComparison.Ordinal); + Assert.Contains("TIMEORIGIN[\"1950-01-01T00:00:00Z\"]", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that time units retain their conversion factor and keyword. + /// + [Fact] + public void TimeUnit_ToWktNode_UsesTimeUnitKeyword() + { + var unit = new TimeUnit(1d, "second", "EPSG", 1040, string.Empty, string.Empty, string.Empty); + + Assert.Equal(1d, unit.ConversionFactor); + Assert.StartsWith("TIMEUNIT[\"second\"", unit.ToWktNode(WktVersion.Wkt22019).ToString(), StringComparison.Ordinal); + } + + /// + /// Verifies that temporal coordinate systems expose their single time unit. + /// + [Fact] + public void GetUnits_ReturnsTimeUnit() + { + TemporalCoordinateSystem coordinateSystem = CreateTemporalCoordinateSystem(); + + Assert.True(coordinateSystem.GetUnits(0).EqualParams(coordinateSystem.TimeUnit)); + Assert.ThrowsAny(() => coordinateSystem.GetUnits(1)); + } + + /// + /// Verifies that WKT2 output uses TIMECRS. + /// + [Fact] + public void ToWktNode_UsesTimeCrsKeyword() + { + string wkt = CreateTemporalCoordinateSystem().ToWktNode(WktVersion.Wkt22019).ToString(); + + Assert.StartsWith("TIMECRS[", wkt, StringComparison.Ordinal); + Assert.Contains("TIMEUNIT[\"second\"", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT2 roundtrips preserve the temporal unit metadata. + /// + [Fact] + public void ToWktNode_RoundTripsTemporalCoordinateSystemWithTimeUnit() + { + TemporalCoordinateSystem original = CreateTemporalCoordinateSystem(); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + + TemporalCoordinateSystem roundTripped = CoordinateSystemTestHelpers.RequireCoordinateSystem(wkt); + + Assert.True(original.EqualParams(roundTripped)); + TimeUnit unit = Assert.IsType(roundTripped.TimeUnit); + Assert.Equal(original.TimeUnit.ConversionFactor, unit.ConversionFactor); + Assert.Equal(original.TimeUnit.Name, unit.Name); + } + + private static TemporalCoordinateSystem CreateTemporalCoordinateSystem() + { + return new TemporalCoordinateSystem( + new TimeUnit(1d, "second", "EPSG", 1040, string.Empty, string.Empty, string.Empty), + new TemporalDatum("1950-01-01T00:00:00Z", "Unix epoch", "EPSG", 1040, string.Empty, string.Empty, string.Empty), + new AxisInfo("time", AxisOrientationEnum.Other), + "Temporal axis", + "EPSG", + 1041, + string.Empty, + string.Empty, + string.Empty); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/AffineRuntimeMathTransformTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/AffineRuntimeMathTransformTests.cs new file mode 100644 index 00000000..d322036b --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/AffineRuntimeMathTransformTests.cs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies the runtime affine transform implementation created from PROJ pipeline arguments. +/// +public class AffineRuntimeMathTransformTests +{ + /// + /// Verifies that affine runtime parameters transform spatial and temporal ordinates as expected. + /// + [Fact] + public void TryCreate_WithAffineParameters_TransformsFourDimensionalPoint() + { + AffineRuntimeMathTransform transform = CreateTransform(); + + double[] transformed = transform.Transform([1d, 2d, 3d, 4d]); + + Assert.Equal(9d, transformed[0], 12); + Assert.Equal(2d, transformed[1], 12); + Assert.Equal(23d, transformed[2], 12); + Assert.Equal(21d, transformed[3], 12); + } + + /// + /// Verifies that the computed inverse restores the original 4D coordinate and is cached. + /// + [Fact] + public void Inverse_RestoresOriginalFourDimensionalPoint() + { + AffineRuntimeMathTransform transform = CreateTransform(); + MathTransform inverse = transform.Inverse(); + + Assert.Same(inverse, transform.Inverse()); + + double[] transformed = transform.Transform([1d, 2d, 3d, 4d]); + double[] restored = inverse.Transform(transformed); + + Assert.Equal(1d, restored[0], 12); + Assert.Equal(2d, restored[1], 12); + Assert.Equal(3d, restored[2], 12); + Assert.Equal(4d, restored[3], 12); + } + + /// + /// Verifies that default affine parameters collapse to an identity transform. + /// + [Fact] + public void TryCreate_WithDefaultParameters_ReturnsIdentityTransform() + { + bool created = AffineRuntimeMathTransform.TryCreate([], out MathTransform? transform, out string? skipReason); + + Assert.True(created); + Assert.Null(skipReason); + Assert.IsType(transform); + Assert.True(transform.Identity()); + } + + /// + /// Verifies that taking the inverse twice recreates the original mapping. + /// + [Fact] + public void InverseOfInverse_PreservesForwardMapping() + { + AffineRuntimeMathTransform transform = CreateTransform(); + MathTransform doubleInverse = transform.Inverse().Inverse(); + + double[] expected = transform.Transform([2d, -1d, 0.5d, 8d]); + double[] actual = doubleInverse.Transform([2d, -1d, 0.5d, 8d]); + + Assert.Equal(expected[0], actual[0], 12); + Assert.Equal(expected[1], actual[1], 12); + Assert.Equal(expected[2], actual[2], 12); + Assert.Equal(expected[3], actual[3], 12); + } + + private static AffineRuntimeMathTransform CreateTransform() + { + bool created = AffineRuntimeMathTransform.TryCreate(CreateArguments(), out MathTransform? transform, out string? skipReason); + + Assert.True(created); + Assert.Null(skipReason); + return Assert.IsType(transform); + } + + private static Dictionary CreateArguments() + { + return new Dictionary + { + ["xoff"] = "5", + ["yoff"] = "-7", + ["zoff"] = "11", + ["toff"] = "13", + ["s11"] = "2", + ["s12"] = "1", + ["s22"] = "3", + ["s23"] = "1", + ["s33"] = "4", + ["tscale"] = "2", + }; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisOrderHelperTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisOrderHelperTests.cs new file mode 100644 index 00000000..e324ba5b --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisOrderHelperTests.cs @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for . +/// +public class AxisOrderHelperTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + + /// + /// Verifies that returns + /// and outputs when the source coordinate system is . + /// + [Fact] + public void TryCreateAxisSwapTransformWithNullSourceReturnsFalse() + { + CoordinateSystem source = null!; + CoordinateSystem target = GeographicCoordinateSystem.WGS84; + + bool ok = AxisOrderHelper.TryCreateAxisSwapTransform(source, target, out MathTransform? transform); + + Assert.False(ok); + Assert.Null(transform); + } + + /// + /// Verifies that returns + /// and outputs when both coordinate systems are one-dimensional (vertical). + /// + [Fact] + public void TryCreateAxisSwapTransformWithOneDimensionalSystemsReturnsFalse() + { + VerticalCoordinateSystem source = CreateVerticalCoordinateSystem("Vertical source", AxisOrientationEnum.Up); + VerticalCoordinateSystem target = CreateVerticalCoordinateSystem("Vertical target", AxisOrientationEnum.Down); + + bool ok = AxisOrderHelper.TryCreateAxisSwapTransform(source, target, out MathTransform? transform); + + Assert.False(ok); + Assert.Null(transform); + } + + /// + /// Verifies that returns + /// and outputs when the target coordinate system uses an unsupported axis orientation + /// (geocentric). + /// + [Fact] + public void TryCreateAxisSwapTransformWithUnsupportedTargetOrientationReturnsFalse() + { + GeographicCoordinateSystem source = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Source EN", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + GeocentricCoordinateSystem target = CoordinateSystemFactory.CreateGeocentricCoordinateSystem( + "Target geocentric", + HorizontalDatum.WGS84, + LinearUnit.Metre, + PrimeMeridian.Greenwich); + + bool ok = AxisOrderHelper.TryCreateAxisSwapTransform(source, target, out MathTransform? transform); + + Assert.False(ok); + Assert.Null(transform); + } + + /// + /// Verifies that returns + /// and outputs when the source coordinate system has a duplicated axis role that + /// prevents an unambiguous mapping. + /// + [Fact] + public void TryCreateAxisSwapTransformWithMissingSourceRoleReturnsFalse() + { + GeographicCoordinateSystem sourceHorizontal = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Source duplicate horizontal", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.East)); + + GeographicCoordinateSystem targetHorizontal = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Target normal horizontal", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + VerticalCoordinateSystem vertical = CreateVerticalCoordinateSystem("Vertical", AxisOrientationEnum.Up); + CompoundCoordinateSystem source = CoordinateSystemFactory.CreateCompoundCoordinateSystem("Source", sourceHorizontal, vertical); + CompoundCoordinateSystem target = CoordinateSystemFactory.CreateCompoundCoordinateSystem("Target", targetHorizontal, vertical); + + bool ok = AxisOrderHelper.TryCreateAxisSwapTransform(source, target, out MathTransform? transform); + + Assert.False(ok); + Assert.Null(transform); + } + + /// + /// Verifies that returns + /// and produces a transform that negates the vertical coordinate when the source vertical axis is + /// and the target is . + /// + [Fact] + public void TryCreateAxisSwapTransformWithUpToDownTargetCreatesVerticalSignFlip() + { + GeographicCoordinateSystem sourceHorizontal = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Source EN", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + GeographicCoordinateSystem targetHorizontal = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Target EN", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + CompoundCoordinateSystem source = CoordinateSystemFactory.CreateCompoundCoordinateSystem( + "Source ENU", + sourceHorizontal, + CreateVerticalCoordinateSystem("Source up", AxisOrientationEnum.Up)); + + CompoundCoordinateSystem target = CoordinateSystemFactory.CreateCompoundCoordinateSystem( + "Target END", + targetHorizontal, + CreateVerticalCoordinateSystem("Target down", AxisOrientationEnum.Down)); + + bool ok = AxisOrderHelper.TryCreateAxisSwapTransform(source, target, out MathTransform? transform); + + Assert.True(ok); + double[] transformed = Assert.IsType(transform, exactMatch: false).Transform([10d, 20d, 30d]); + + Assert.Equal(10d, transformed[0], 12); + Assert.Equal(20d, transformed[1], 12); + Assert.Equal(-30d, transformed[2], 12); + } + + private static VerticalCoordinateSystem CreateVerticalCoordinateSystem(string name, AxisOrientationEnum orientation) + { + VerticalDatum datum = CoordinateSystemFactory.CreateVerticalDatum($"{name} datum", DatumType.VD_Other); + return CoordinateSystemFactory.CreateVerticalCoordinateSystem(name, datum, LinearUnit.Metre, new AxisInfo("V", orientation)); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisSwapMathTransformTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisSwapMathTransformTests.cs new file mode 100644 index 00000000..fc1b6c04 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisSwapMathTransformTests.cs @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for . +/// +public class AxisSwapMathTransformTests +{ + /// + /// Verifies that the constructor throws when the + /// dimension argument is not 2, 3, or 4. + /// + [Theory] + [InlineData(1)] + [InlineData(5)] + public void ConstructorWithInvalidDimensionThrowsArgumentOutOfRange(int dimension) + { + ArgumentOutOfRangeException exception = Assert.Throws(() => new AxisSwapMathTransform(dimension, 0, 1, 1, 1, 2, 1, 3, 1)); + + Assert.Equal("dimension", exception.ParamName); + } + + /// + /// Verifies that the constructor throws when an + /// x-axis source index is outside the valid range for the given dimension. + /// + [Theory] + [InlineData(-1)] + [InlineData(4)] + public void ConstructorWithInvalidSourceIndexThrowsArgumentOutOfRange(int sourceIndex) + { + ArgumentOutOfRangeException exception = Assert.Throws(() => new AxisSwapMathTransform(4, sourceIndex, 1, 1, 1, 2, 1, 3, 1)); + + Assert.Equal("xSourceIndex", exception.ParamName); + } + + /// + /// Verifies that the constructor throws when a sign + /// argument is not exactly 1 or -1. + /// + [Theory] + [InlineData(0)] + [InlineData(2)] + [InlineData(-2)] + public void ConstructorWithInvalidSignThrowsArgumentOutOfRange(int sign) + { + ArgumentOutOfRangeException exception = Assert.Throws(() => new AxisSwapMathTransform(4, 0, sign, 1, 1, 2, 1, 3, 1)); + + Assert.Equal("xSign", exception.ParamName); + } + + /// + /// Verifies that returns when the + /// transform maps each 4D axis to itself with a positive sign. + /// + [Fact] + public void IdentityReturnsTrueForCanonical4DMapping() + { + var transform = new AxisSwapMathTransform(4, 0, 1, 1, 1, 2, 1, 3, 1); + + Assert.True(transform.Identity()); + } + + /// + /// Verifies that returns when + /// at least one axis in the 4D mapping has a negative sign. + /// + [Fact] + public void IdentityReturnsFalseWhen4DMappingChangesSign() + { + var transform = new AxisSwapMathTransform(4, 0, 1, 1, 1, 2, 1, 3, -1); + + Assert.False(transform.Identity()); + } + + /// + /// Verifies that applying and then transforming a 4D point + /// recovers the original coordinates. + /// + [Fact] + public void InverseRoundTrips4DPoint() + { + var transform = new AxisSwapMathTransform(4, 3, 1, 2, 1, 1, -1, 0, 1); + double[] input = [2d, 49d, 10d, 100d]; + + double[] transformed = transform.Transform(input); + double[] roundtrip = transform.Inverse().Transform(transformed); + + Assert.Equal(100d, transformed[0], 12); + Assert.Equal(10d, transformed[1], 12); + Assert.Equal(-49d, transformed[2], 12); + Assert.Equal(2d, transformed[3], 12); + + Assert.Equal(input[0], roundtrip[0], 12); + Assert.Equal(input[1], roundtrip[1], 12); + Assert.Equal(input[2], roundtrip[2], 12); + Assert.Equal(input[3], roundtrip[3], 12); + } + + /// + /// Verifies that mutates the transform in place so that + /// a subsequent transform call applies the inverse mapping, recovering the original coordinates. + /// + [Fact] + public void InvertMutatesIntoInverseMapping() + { + var transform = new AxisSwapMathTransform(4, 3, 1, 2, 1, 1, -1, 0, 1); + double[] input = [2d, 49d, 10d, 100d]; + double[] transformed = transform.Transform(input); + + transform.Invert(); + double[] roundtrip = transform.Transform(transformed); + + Assert.Equal(input[0], roundtrip[0], 12); + Assert.Equal(input[1], roundtrip[1], 12); + Assert.Equal(input[2], roundtrip[2], 12); + Assert.Equal(input[3], roundtrip[3], 12); + } + + /// + /// Verifies that a 2D transform that remaps X and Y leaves the Z coordinate unchanged. + /// + [Fact] + public void TwoDimensionalTransformLeavesZUntouched() + { + var transform = new AxisSwapMathTransform(2, 1, 1, 0, -1, 2, 1, 3, 1); + double x = 3d; + double y = 4d; + double z = 5d; + + transform.Transform(ref x, ref y, ref z); + + Assert.Equal(4d, x, 12); + Assert.Equal(-3d, y, 12); + Assert.Equal(5d, z, 12); + } + + /// + /// Verifies that a 3D transform that remaps X, Y, and Z leaves the time coordinate unchanged. + /// + [Fact] + public void ThreeDimensionalTransformLeavesTimeUntouched() + { + var transform = new AxisSwapMathTransform(3, 1, 1, 0, 1, 2, -1, 3, 1); + double x = 3d; + double y = 4d; + double z = 5d; + double t = 6d; + + transform.Transform(ref x, ref y, ref z, ref t); + + Assert.Equal(4d, x, 12); + Assert.Equal(3d, y, 12); + Assert.Equal(-5d, z, 12); + Assert.Equal(6d, t, 12); + } + + /// + /// Verifies that the inherited and + /// properties each throw . + /// + [Fact] + public void WktAndXmlPropertiesThrowNotSupportedException() + { + var transform = new AxisSwapMathTransform(2, 0, 1, 1, 1, 2, 1, 3, 1); + + Assert.Throws(() => _ = transform.WKT); + Assert.Throws(() => _ = transform.XML); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisSwapTransformationTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisSwapTransformationTests.cs new file mode 100644 index 00000000..200cd0d3 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/AxisSwapTransformationTests.cs @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for axis-swap and unit-conversion behavior in coordinate transformations built by +/// . +/// +public class AxisSwapTransformationTests +{ + private static readonly double[] GeographicAxisInput = [12d, 55d]; + private static readonly double[] ProjectedAxisInput = [500000d, 6100000d]; + private static readonly double[] UnitConversionInput = [100d, 200d]; + private static readonly double[] RadianConversionInput = [180d, 90d]; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that a transformation from a Lon/Lat (East/North) to a Lat/Lon (North/East) geographic + /// coordinate system swaps the two coordinate values. + /// + [Fact] + public void GeographicAxisSwapLonLatToLatLonSwapsCoordinates() + { + GeographicCoordinateSystem source = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Source EN", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + GeographicCoordinateSystem target = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Target NE", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lat", AxisOrientationEnum.North), + new AxisInfo("Lon", AxisOrientationEnum.East)); + + MathTransform transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target).MathTransform; + double[] transformed = transform.Transform(GeographicAxisInput); + + Assert.Equal(55d, transformed[0], 12); + Assert.Equal(12d, transformed[1], 12); + } + + /// + /// Verifies that a transformation from an East/North to a West/South geographic coordinate system + /// negates both coordinate values. + /// + [Fact] + public void GeographicAxisSwapEastNorthToWestSouthNegatesAxes() + { + GeographicCoordinateSystem source = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Source EN", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + GeographicCoordinateSystem target = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Target WS", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.West), + new AxisInfo("Lat", AxisOrientationEnum.South)); + + MathTransform transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target).MathTransform; + double[] transformed = transform.Transform(GeographicAxisInput); + + Assert.Equal(-12d, transformed[0], 12); + Assert.Equal(-55d, transformed[1], 12); + } + + /// + /// Verifies that a transformation from an East/North to a North/East projected coordinate system + /// swaps the easting and northing values. + /// + [Fact] + public void ProjectedAxisSwapEastNorthToNorthEastSwapsProjectedAxes() + { + var projectionParameters = new List + { + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + }; + + IProjection projection = CoordinateSystemFactory.CreateProjection("Mercator", "mercator", projectionParameters); + GeographicCoordinateSystem geographic = GeographicCoordinateSystem.WGS84; + ProjectedCoordinateSystem source = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "Source EN", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + ProjectedCoordinateSystem target = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "Target NE", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("North", AxisOrientationEnum.North), + new AxisInfo("East", AxisOrientationEnum.East)); + + MathTransform transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target).MathTransform; + double[] transformed = transform.Transform(ProjectedAxisInput); + + Assert.Equal(6100000d, transformed[0], 8); + Assert.Equal(500000d, transformed[1], 8); + } + + /// + /// Verifies that a transformation between two geographic coordinate systems that share the same axes + /// but differ only in angular unit converts degree values to the equivalent radian values. + /// + [Fact] + public void GeographicUnitConversionDegreesToRadiansConvertsCoordinates() + { + GeographicCoordinateSystem source = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Source Degrees", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + GeographicCoordinateSystem target = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Target Radians", + AngularUnit.Radian, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + MathTransform transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target).MathTransform; + double[] transformed = transform.Transform(RadianConversionInput); + + Assert.Equal(System.Math.PI, transformed[0], 12); + Assert.Equal(System.Math.PI / 2d, transformed[1], 12); + } + + /// + /// Verifies that a transformation between two projected coordinate systems that share the same axes + /// but differ only in linear unit converts metre values to the equivalent foot values. + /// + [Fact] + public void ProjectedUnitConversionMetreToFootConvertsProjectedCoordinates() + { + IProjection projection = CreateMercatorProjection(); + GeographicCoordinateSystem geographic = GeographicCoordinateSystem.WGS84; + + ProjectedCoordinateSystem source = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "Source Metre", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + ProjectedCoordinateSystem target = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "Target Foot", + geographic, + projection, + LinearUnit.Foot, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + MathTransform transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target).MathTransform; + double[] transformed = transform.Transform(UnitConversionInput); + + Assert.Equal(328.0839895013123d, transformed[0], 9); + Assert.Equal(656.1679790026246d, transformed[1], 9); + } + + /// + /// Verifies that a transformation from a metre East/North to a foot North/East projected coordinate + /// system simultaneously converts units from metres to feet and swaps the axis order. + /// + [Fact] + public void ProjectedUnitAndAxisConversionMetreEastNorthToFootNorthEastConvertsAndSwaps() + { + IProjection projection = CreateMercatorProjection(); + GeographicCoordinateSystem geographic = GeographicCoordinateSystem.WGS84; + + ProjectedCoordinateSystem source = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "Source Metre EN", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + ProjectedCoordinateSystem target = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "Target Foot NE", + geographic, + projection, + LinearUnit.Foot, + new AxisInfo("North", AxisOrientationEnum.North), + new AxisInfo("East", AxisOrientationEnum.East)); + + MathTransform transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target).MathTransform; + double[] transformed = transform.Transform(UnitConversionInput); + + Assert.Equal(656.1679790026246d, transformed[0], 9); + Assert.Equal(328.0839895013123d, transformed[1], 9); + } + + private static IProjection CreateMercatorProjection() + { + var projectionParameters = new List + { + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + }; + + return CoordinateSystemFactory.CreateProjection("Mercator", "mercator", projectionParameters); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/CompositeMathTransformTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/CompositeMathTransformTests.cs new file mode 100644 index 00000000..b18527dd --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/CompositeMathTransformTests.cs @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for . +/// +public class CompositeMathTransformTests +{ + /// + /// Verifies that DimSource is taken from the first transform in the chain. + /// + [Fact] + public void DimSource_ReturnsFirstTransformDimSource() + { + var composite = new CompositeMathTransform([new IdentityMathTransform(3)]); + + Assert.Equal(3, composite.DimSource); + } + + /// + /// Verifies that DimTarget is taken from the last transform in the chain. + /// + [Fact] + public void DimTarget_ReturnsLastTransformDimTarget() + { + var composite = new CompositeMathTransform( + [ + new IdentityMathTransform(2), + new IdentityMathTransform(4), + ]); + + Assert.Equal(4, composite.DimTarget); + } + + /// + /// Verifies that Identity returns true when all transforms are identity. + /// + [Fact] + public void Identity_AllIdentity_ReturnsTrue() + { + var composite = new CompositeMathTransform( + [ + new IdentityMathTransform(2), + new IdentityMathTransform(2), + ]); + + Assert.True(composite.Identity()); + } + + /// + /// Verifies that Identity returns false when any transform is not identity. + /// + [Fact] + public void Identity_ContainsNonIdentity_ReturnsFalse() + { + var composite = new CompositeMathTransform( + [ + new IdentityMathTransform(2), + new OffsetMathTransform(5.0), + ]); + + Assert.False(composite.Identity()); + } + + /// + /// Verifies that Transform chains multiple transforms sequentially. + /// + [Fact] + public void Transform_ChainsTransformsSequentially() + { + var composite = new CompositeMathTransform( + [ + new OffsetMathTransform(10.0), + new OffsetMathTransform(5.0), + ]); + + double x = 1.0, y = 2.0, z = 0.0; + composite.Transform(ref x, ref y, ref z); + + Assert.Equal(16.0, x, 12); + Assert.Equal(17.0, y, 12); + } + + /// + /// Verifies that a single identity transform leaves values unchanged. + /// + [Fact] + public void Transform_SingleIdentity_LeavesValuesUnchanged() + { + var composite = new CompositeMathTransform([new IdentityMathTransform(2)]); + + double x = 42.0, y = 99.0, z = 0.0; + composite.Transform(ref x, ref y, ref z); + + Assert.Equal(42.0, x, 12); + Assert.Equal(99.0, y, 12); + } + + /// + /// Verifies that Inverse returns a transform that reverses the chain order and inverts each transform. + /// + [Fact] + public void Inverse_ReversesAndInvertsChain() + { + var composite = new CompositeMathTransform( + [ + new OffsetMathTransform(10.0), + new OffsetMathTransform(5.0), + ]); + + MathTransform inverse = composite.Inverse(); + + double x = 16.0, y = 17.0, z = 0.0; + inverse.Transform(ref x, ref y, ref z); + + Assert.Equal(1.0, x, 12); + Assert.Equal(2.0, y, 12); + } + + /// + /// Verifies that Inverse returns the same instance on repeated calls (caching). + /// + [Fact] + public void Inverse_ReturnsSameInstanceOnRepeatedCalls() + { + var composite = new CompositeMathTransform([new IdentityMathTransform(2)]); + + MathTransform inverse1 = composite.Inverse(); + MathTransform inverse2 = composite.Inverse(); + + Assert.Same(inverse1, inverse2); + } + + /// + /// Verifies that Invert modifies the composite in place. + /// + [Fact] + public void Invert_ModifiesTransformInPlace() + { + var composite = new CompositeMathTransform( + [ + new OffsetMathTransform(10.0), + new OffsetMathTransform(5.0), + ]); + + // Forward: x=0 -> x=15 + double x = 0.0, y = 0.0, z = 0.0; + composite.Transform(ref x, ref y, ref z); + Assert.Equal(15.0, x, 12); + + // Invert in place: now should subtract + composite.Invert(); + x = 15.0; + y = 15.0; + z = 0.0; + composite.Transform(ref x, ref y, ref z); + + Assert.Equal(0.0, x, 12); + Assert.Equal(0.0, y, 12); + } + + /// + /// Verifies that in-place inversion can reverse immutable child transforms by using . + /// + [Fact] + public void Invert_UsesInverseForImmutableChildren() + { + var composite = new CompositeMathTransform( + [ + new ImmutableOffsetMathTransform(10.0), + new ImmutableOffsetMathTransform(5.0), + ]); + + composite.Invert(); + + double x = 15.0, y = 15.0, z = 0.0; + composite.Transform(ref x, ref y, ref z); + + Assert.Equal(0.0, x, 12); + Assert.Equal(0.0, y, 12); + } + + /// + /// Verifies that WKT throws . + /// + [Fact] + public void WKT_ThrowsNotSupportedException() + { + var composite = new CompositeMathTransform([new IdentityMathTransform(2)]); + + Assert.Throws(() => composite.WKT); + } + + /// + /// Verifies that XML throws . + /// + [Fact] + public void XML_ThrowsNotSupportedException() + { + var composite = new CompositeMathTransform([new IdentityMathTransform(2)]); + + Assert.Throws(() => composite.XML); + } + + /// + /// A simple test double that offsets X and Y by a fixed amount. + /// + private sealed class OffsetMathTransform : MathTransform + { + private double offset; + + public OffsetMathTransform(double offset) + { + this.offset = offset; + } + + /// + public override int DimSource => 2; + + /// + public override int DimTarget => 2; + + /// + public override string WKT => throw new NotImplementedException(); + + /// + public override string XML => throw new NotImplementedException(); + + /// + public override bool Identity() => this.offset == 0; + + /// + public override MathTransform Inverse() => new OffsetMathTransform(-this.offset); + + /// + public override void Invert() => this.offset = -this.offset; + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + x += this.offset; + y += this.offset; + } + } + + /// + /// A simple immutable test double that offsets X and Y by a fixed amount. + /// + private sealed class ImmutableOffsetMathTransform : MathTransform + { + private readonly double offset; + + public ImmutableOffsetMathTransform(double offset) + { + this.offset = offset; + } + + /// + public override int DimSource => 2; + + /// + public override int DimTarget => 2; + + /// + public override string WKT => throw new NotImplementedException(); + + /// + public override string XML => throw new NotImplementedException(); + + /// + public override bool Identity() => this.offset == 0d; + + /// + public override MathTransform Inverse() => new ImmutableOffsetMathTransform(-this.offset); + + /// + public override void Invert() => throw new NotSupportedException(); + + /// + public override void Transform(ref double x, ref double y, ref double z) + { + x += this.offset; + y += this.offset; + } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateOperationResolverTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateOperationResolverTests.cs new file mode 100644 index 00000000..c39dca27 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateOperationResolverTests.cs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests the lightweight candidate scoring in . +/// +public class CoordinateOperationResolverTests +{ + /// + /// Verifies that a direct non-identity candidate with authority metadata wins over the synthetic identity candidate + /// when source and target are parameter-equivalent but carry distinct authority identities. + /// + [Fact] + public void ResolveWithDistinctAuthorityDirectCandidatePrefersDirectTransformation() + { + GeographicCoordinateSystem source = GeographicCoordinateSystem.WGS84.WithAuthority("EPSG", 4326); + GeographicCoordinateSystem target = GeographicCoordinateSystem.WGS84.WithAuthority("IGNF", 94326); + CoordinateTransformation directCandidate = CreateDirectCandidate(source, target, "EPSG", 1234); + + ICoordinateTransformation? resolved = CoordinateOperationResolver.Resolve(source, target, (_, _) => directCandidate); + + Assert.Same(directCandidate, resolved); + } + + /// + /// Verifies that the resolver keeps the synthetic identity candidate when the direct non-identity candidate lacks + /// authority metadata and therefore does not receive the higher distinct-authority score. + /// + [Fact] + public void ResolveWithoutDirectAuthorityMetadataPrefersIdentityTransformation() + { + GeographicCoordinateSystem source = GeographicCoordinateSystem.WGS84.WithAuthority("EPSG", 4326); + GeographicCoordinateSystem target = GeographicCoordinateSystem.WGS84.WithAuthority("IGNF", 94326); + CoordinateTransformation directCandidate = CreateDirectCandidate(source, target, string.Empty, -1); + + ICoordinateTransformation resolved = Assert.IsAssignableFrom( + CoordinateOperationResolver.Resolve(source, target, (_, _) => directCandidate)); + + Assert.NotSame(directCandidate, resolved); + Assert.IsType(resolved.MathTransform); + Assert.Equal(string.Empty, resolved.Authority); + Assert.Equal(-1, resolved.AuthorityCode); + } + + private static CoordinateTransformation CreateDirectCandidate( + GeographicCoordinateSystem source, + GeographicCoordinateSystem target, + string authority, + long authorityCode) + { + return new CoordinateTransformation( + source, + target, + TransformType.Transformation, + new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris), + "Prime meridian shift", + authority, + authorityCode, + string.Empty, + string.Empty); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateSystemServicesTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateSystemServicesTests.cs new file mode 100644 index 00000000..94f20d14 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateSystemServicesTests.cs @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using Xunit; + +/// +/// Tests for . +/// +public class CoordinateSystemServicesTests +{ + /// + /// Verifies that the default constructor initializes the service with EPSG 4326 and EPSG 3857 coordinate systems. + /// + [Fact] + public void TestConstructor() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + + Assert.NotNull(css.GetCoordinateSystem(4326)); + Assert.NotNull(css.GetCoordinateSystem(3857)); + } + + /// + /// Verifies that the SRID-based transformation overload reuses the cached transformation instance for repeated requests. + /// + [Fact] + public void CreateTransformationBySrid_ReusesCachedTransformationInstance() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + + ICoordinateTransformation first = Assert.IsAssignableFrom(css.CreateTransformation(4326, 3857)); + ICoordinateTransformation second = Assert.IsAssignableFrom(css.CreateTransformation(4326, 3857)); + + Assert.Same(first, second); + } + + /// + /// Verifies that concurrent SRID-based requests converge on the same cached transformation instance. + /// + /// A task that completes after the concurrent cache assertions finish. + [Fact] + public async Task CreateTransformationBySrid_ConcurrentCallsReturnSameCachedTransformation() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + + Task[] tasks = Enumerable.Range(0, 8) + .Select(_ => Task.Run(() => css.CreateTransformation(4326, 3857), TestContext.Current.CancellationToken)) + .ToArray(); + ICoordinateTransformation?[] transformations = await Task.WhenAll(tasks).ConfigureAwait(true); + ICoordinateTransformation first = Assert.IsAssignableFrom(transformations[0]); + + for (int i = 1; i < transformations.Length; i++) + { + Assert.Same(first, Assert.IsAssignableFrom(transformations[i])); + } + } + + /// + /// Verifies that TryGetCoordinateSystem by SRID returns and a non-null system for a known SRID, and with for an unknown SRID. + /// + [Fact] + public void TestTryGetCoordinateSystemBySrid() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + + bool found = css.TryGetCoordinateSystem(4326, out CoordinateSystem? coordinateSystem); + bool missing = css.TryGetCoordinateSystem(999999, out CoordinateSystem? missingCoordinateSystem); + + Assert.True(found); + Assert.NotNull(coordinateSystem); + Assert.False(missing); + Assert.Null(missingCoordinateSystem); + } + + /// + /// Verifies that TryGetCoordinateSystem by authority and code returns for a known entry and for an unknown code. + /// + [Fact] + public void TestTryGetCoordinateSystemByAuthorityCode() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + + bool found = css.TryGetCoordinateSystem("EPSG", 3857, out CoordinateSystem? coordinateSystem); + bool missing = css.TryGetCoordinateSystem("EPSG", -1, out CoordinateSystem? missingCoordinateSystem); + + Assert.True(found); + Assert.NotNull(coordinateSystem); + Assert.False(missing); + Assert.Null(missingCoordinateSystem); + } + + /// + /// Ensures authority/code lookup returns null when the coordinate system is not registered. + /// + [Fact] + public void GetCoordinateSystemByAuthorityCodeReturnsNullWhenMissing() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + + CoordinateSystem? missing = css.GetCoordinateSystem("EPSG", -1); + + Assert.Null(missing); + } + + /// + /// Verifies that catalog resolution returns the canonical registered instance for a parsed coordinate system with matching authority metadata. + /// + [Fact] + public void ResolveFromCatalogReturnsCanonicalCatalogInstance() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + CoordinateSystem catalog = Assert.IsAssignableFrom(css.GetCoordinateSystem(4326)); + CoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(catalog.WKT); + + CoordinateSystem resolved = css.ResolveFromCatalog(parsed); + + Assert.NotSame(catalog, parsed); + Assert.Same(catalog, resolved); + } + + /// + /// Verifies that returns the canonical registered instance for a parsed coordinate system with matching authority metadata. + /// + [Fact] + public void TryResolveFromCatalogReturnsCanonicalCatalogInstance() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + CoordinateSystem catalog = Assert.IsAssignableFrom(css.GetCoordinateSystem(3857)); + CoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(catalog.WKT); + + bool resolved = css.TryResolveFromCatalog(parsed, out CoordinateSystem? resolvedCoordinateSystem); + + Assert.True(resolved); + Assert.NotSame(catalog, parsed); + Assert.Same(catalog, resolvedCoordinateSystem); + } + + /// + /// Verifies that catalog resolution preserves the parsed instance when the parsed coordinate system has no top-level authority metadata. + /// + [Fact] + public void ResolveFromCatalogReturnsInputWhenAuthorityMetadataIsMissing() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + CoordinateSystem parsed = GeographicCoordinateSystem.WGS84.WithAuthority(string.Empty, -1); + + CoordinateSystem resolved = css.ResolveFromCatalog(parsed); + + Assert.Same(parsed, resolved); + } + + /// + /// Verifies that catalog resolution preserves the parsed instance when the parsed coordinate system points to an unregistered authority code. + /// + [Fact] + public void ResolveFromCatalogReturnsInputWhenAuthorityCodeIsUnknown() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + CoordinateSystem parsed = GeographicCoordinateSystem.WGS84.WithAuthority("EPSG", 999999); + + CoordinateSystem resolved = css.ResolveFromCatalog(parsed); + + Assert.Same(parsed, resolved); + } + + /// + /// Verifies that returns when the parsed coordinate system has no top-level authority metadata. + /// + [Fact] + public void TryResolveFromCatalogReturnsFalseWhenAuthorityMetadataIsMissing() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + CoordinateSystem parsed = GeographicCoordinateSystem.WGS84.WithAuthority(string.Empty, -1); + + bool resolved = css.TryResolveFromCatalog(parsed, out CoordinateSystem? resolvedCoordinateSystem); + + Assert.False(resolved); + Assert.Null(resolvedCoordinateSystem); + } + + /// + /// Verifies that returns when the parsed coordinate system points to an unregistered authority code. + /// + [Fact] + public void TryResolveFromCatalogReturnsFalseWhenAuthorityCodeIsUnknown() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + CoordinateSystem parsed = GeographicCoordinateSystem.WGS84.WithAuthority("EPSG", 999999); + + bool resolved = css.TryResolveFromCatalog(parsed, out CoordinateSystem? resolvedCoordinateSystem); + + Assert.False(resolved); + Assert.Null(resolvedCoordinateSystem); + } + + /// + /// Verifies that GetAvailableSridValues returns a non-empty array that includes well-known SRIDs such as 4326 and 3857. + /// + [Fact] + public void TestGetAvailableSridValues() + { + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + + int[] srids = css.GetAvailableSridValues(); + Assert.NotNull(srids); + Assert.True(Array.IndexOf(srids, 4326) >= 0); + Assert.True(Array.IndexOf(srids, 3857) >= 0); + } + + /// + /// Verifies that the managed provider exposes more than 7000 definitions and includes EPSG 4326 and EPSG 3857. + /// + [Fact] + public void TestManagedProviderIncludesFullGeneratedCatalog() + { + var provider = new ManagedCoordinateSystemDefinitionProvider(); + var definitions = provider.GetDefinitions().ToList(); + + Assert.True(definitions.Count > 7000); + Assert.Contains(definitions, item => item.Srid == 4326); + Assert.Contains(definitions, item => item.Srid == 3857); + } + + /// + /// Verifies that an is consumed directly without invoking WKT parsing. + /// + [Fact] + public void TestManagedObjectProviderBypassesWktParsing() + { + var provider = new TestManagedProvider(); + var css = new CoordinateSystemServices(provider); + + Assert.NotNull(css.GetCoordinateSystem(4326)); + Assert.NotNull(css.GetCoordinateSystem(3857)); + } + + /// + /// Verifies that an exception thrown by a provider during initialization is wrapped in an . + /// + [Fact] + public void TestInitializationFailurePropagatesAsInvalidOperationException() + { + var css = new CoordinateSystemServices(new ThrowingDefinitionProvider()); + + InvalidOperationException exception = Assert.Throws(() => css.GetCoordinateSystem(4326)); + Assert.NotNull(exception.InnerException); + Assert.Equal("Coordinate system initialization failed.", exception.Message); + } + + /// + /// Verifies malformed WKT definitions are skipped during initialization while valid definitions still load. + /// + [Fact] + public void InitializationSkipsMalformedDefinitions() + { + CoordinateSystemDefinition[] definitions = + [ + new CoordinateSystemDefinition(999001, "GEODCRS[\"Broken\""), + new CoordinateSystemDefinition(4326, GeographicCoordinateSystem.WGS84.WKT), + ]; + var css = new CoordinateSystemServices(definitions); + + Assert.Null(css.GetCoordinateSystem(999001)); + Assert.NotNull(css.GetCoordinateSystem(4326)); + } + + /// + /// Verifies unsupported WKT definitions are skipped during initialization while valid definitions still load. + /// + [Fact] + public void InitializationSkipsUnsupportedDefinitions() + { + CoordinateSystemDefinition[] definitions = + [ + new CoordinateSystemDefinition(999002, "BoundCrs[]"), + new CoordinateSystemDefinition(4326, GeographicCoordinateSystem.WGS84.WKT), + ]; + var css = new CoordinateSystemServices(definitions); + + Assert.Null(css.GetCoordinateSystem(999002)); + Assert.NotNull(css.GetCoordinateSystem(4326)); + } + + /// + /// Validates CSV-backed constructor loading for coordinate system definitions. + /// + /// Path to the CSV definition file, or empty for embedded defaults. + [Theory] + [InlineData(@"")] + public void TestConstructorLoadCsv(string csvPath) + { + if (!string.IsNullOrWhiteSpace(csvPath)) + { + Assert.True(File.Exists(csvPath), FormattableString.Invariant($"Specified file not found: {csvPath}")); + } + + CoordinateSystemServices css = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(LoadCsv(csvPath)); + + Assert.NotNull(css.GetCoordinateSystem(4326)); + Assert.NotNull(css.GetCoordinateSystem("EPSG", 4326)); + Assert.True(ReferenceEquals(css.GetCoordinateSystem("EPSG", 4326), css.GetCoordinateSystem(4326))); + } + + /// + /// Loads SRID/WKT definitions from CSV input or embedded defaults. + /// + /// Optional path to an external CSV file. + /// Sequence of SRID/WKT pairs. + internal static IEnumerable LoadCsv(string? csvPath = null) + { + Debug.WriteLine(FormattableString.Invariant($"Reading '{csvPath ?? "SRID.csv from resources stream"}'.")); + var sw = new Stopwatch(); + sw.Start(); + + foreach (SRIDReader.WktString sridWkt in SRIDReader.GetSrids(csvPath)) + { + yield return new CoordinateSystemDefinition(sridWkt.WktId, sridWkt.Wkt); + } + + sw.Stop(); + Debug.WriteLine(FormattableString.Invariant($"Read '{csvPath ?? "SRID.csv from resources stream"}' in {sw.ElapsedMilliseconds:N0}ms")); + } + + private sealed class TestManagedProvider : ICoordinateSystemDefinitionProvider, IManagedCoordinateSystemProvider + { + public IEnumerable GetCoordinateSystems() + { + yield return new CoordinateSystemEntry(4326, GeographicCoordinateSystem.WGS84); + yield return new CoordinateSystemEntry(3857, ProjectedCoordinateSystem.WebMercator); + } + + public IEnumerable GetDefinitions() + { + yield return new CoordinateSystemDefinition(4326, "INVALID_WKT_SHOULD_NOT_BE_USED"); + } + } + + private sealed class ThrowingDefinitionProvider : ICoordinateSystemDefinitionProvider + { + public IEnumerable GetDefinitions() + { + throw new InvalidOperationException("Synthetic provider failure."); + } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateTransformTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateTransformTests.cs new file mode 100644 index 00000000..4c63fcc3 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateTransformTests.cs @@ -0,0 +1,1467 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Geometries; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for coordinate system transformations across various projection types and datum shifts. +/// +public class CoordinateTransformTests : CoordinateTransformTestsBase +{ + private static readonly double[] AffineTargetPoint = [3456926.640, 5481071.278]; + private static readonly double[] AffineTestPoint = [2040.0, 1590.0]; + private static readonly double[] CassiniSoldnerExpected = [25244.540, 21300.969]; + private static readonly double[] CassiniSoldnerInput = [13.408055555556, 52.518611111111]; + private static readonly double[] TransformListSamplePoint1 = [290586.087, 6714000]; + private static readonly double[] TransformListSamplePoint2 = [290586.392, 6713996.224]; + private static readonly double[] TransformListSamplePoint3 = [290590.133, 6713973.772]; + private static readonly double[] OrthographicHorizonTestPoint = [180.0, 0.0]; + + /// + /// Initializes a new instance of the class. + /// + public CoordinateTransformTests() + { + this.Verbose = true; + } + + /// + /// Verifies that transforming an array of coordinates produces the same results as transforming each coordinate individually. + /// + [Fact] + public void TestTransformListOfCoordinates() + { + CoordinateSystem utm35ETRS = this.RequireCoordinateSystem( + "PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); + + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + ICoordinateTransformation trans = this.CreateTransformation(utm35ETRS, utm33); + + XY[] points = + [ + new XY(290586.087, 6714000), new XY(290586.392, 6713996.224), + new XY(290590.133, 6713973.772), new XY(290594.111, 6713957.416), + new XY(290596.615, 6713943.567), new XY(290596.701, 6713939.485), + ]; + + var tpoints = (XY[])points.Clone(); + trans.MathTransform.Transform(tpoints); + for (int i = 0; i < points.Length; i++) + { + double expectedX = points[i].X; + double expectedY = points[i].Y; + trans.MathTransform.Transform(ref expectedX, ref expectedY); + + double actualX = tpoints[i].X; + double actualY = tpoints[i].Y; + + Assert.Equal(expectedX, actualX, 8); + Assert.Equal(expectedY, actualY, 8); + } + } + + /// + /// Verifies that TransformList for double-array inputs produces the same results as individual point transforms. + /// + [Fact] + public void TestTransformListOfDoubleArray() + { + CoordinateSystem utm35ETRS = this.RequireCoordinateSystem( + "PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); + + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + ICoordinateTransformation trans = this.CreateTransformation(utm35ETRS, utm33); + + double[][] points = + [ + [290586.087, 6714000], [90586.392, 6713996.224], + [290590.133, 6713973.772], [290594.111, 6713957.416], + [290596.615, 6713943.567], [290596.701, 6713939.485], + ]; + + double[][] tpoints = [.. trans.MathTransform.TransformList(points)]; + for (int i = 0; i < points.Length; i++) + { + double expectedX = points[i][0]; + double expectedY = points[i][1]; + trans.MathTransform.Transform(ref expectedX, ref expectedY); + + double actualX = tpoints[i][0]; + double actualY = tpoints[i][1]; + + Assert.Equal(expectedX, actualX, 8); + Assert.Equal(expectedY, actualY, 8); + } + } + + /// + /// Verifies that a Lambert Azimuthal Equal Area WKT containing a negative central meridian is parsed without error. + /// + [Fact] + public void TestCentralMeridianParse() + { + const string strSouthPole = "PROJCS[\"South_Pole_Lambert_Azimuthal_Equal_Area\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Lambert_Azimuthal_Equal_Area\"],PARAMETER[\"False_Easting\",0],PARAMETER[\"False_Northing\",0],PARAMETER[\"Central_Meridian\",-127],PARAMETER[\"Latitude_Of_Origin\",-90],UNIT[\"Meter\",1]]"; + + CoordinateSystem pSouthPole = this.RequireCoordinateSystem(strSouthPole); + Assert.NotNull(pSouthPole); + } + + /// + /// Verifies forward and inverse Albers Conical Equal Area projection using the Clarke 1866 ellipsoid with metre output units. + /// + [Fact] + public void TestAlbersProjection() + { + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1866", 6378206.4, 294.9786982138982, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum("Clarke 1866", DatumType.HD_Geocentric, ellipsoid, null); + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Clarke 1866", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + var parameters = new List(5) + { + new("central_meridian", -96), + new("latitude_of_center", 23), + new("standard_parallel_1", 29.5), + new("standard_parallel_2", 45.5), + new("false_easting", 0), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Albers Conical Equal Area", "albers", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Albers Conical Equal Area", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans1 = this.CreateTransformation(gcs, coordsys); + ICoordinateTransformation trans2 = this.CreateTransformation(coordsys, gcs); + + double[] pGeo = [-75, 35]; + double[] pUtm = trans1.MathTransform.Transform(pGeo); + double[] pGeo2 = trans2.MathTransform.Transform(pUtm); + + double[] expected = [1885472.7, 1535925]; + this.AssertCoordinateWithinTolerance("Albers", expected, pUtm, 0.05); + this.AssertCoordinateWithinTolerance("Albers", pGeo, pGeo2, 0.0000001, reverse: true); + } + + /// + /// Verifies forward and inverse Albers Conical Equal Area projection using the Clarke 1866 ellipsoid with feet output units. + /// + [Fact] + public void TestAlbersProjectionFeet() + { + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1866", 6378206.4, 294.9786982138982, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum("Clarke 1866", DatumType.HD_Geocentric, ellipsoid, null); + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Clarke 1866", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + var parameters = new List(5) + { + new("central_meridian", -96), + new("latitude_of_center", 23), + new("standard_parallel_1", 29.5), + new("standard_parallel_2", 45.5), + new("false_easting", 0), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Albers Conical Equal Area", "albers", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Albers Conical Equal Area", gcs, projection, LinearUnit.Foot, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcs, coordsys); + + double[] pGeo = [-75, 35]; + double[] pUtm = trans.MathTransform.Transform(pGeo); + double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); + + double[] expected = [1885472.7 / LinearUnit.Foot.MetersPerUnit, 1535925 / LinearUnit.Foot.MetersPerUnit]; + this.AssertCoordinateWithinTolerance("Albers", expected, pUtm, 0.1); + this.AssertCoordinateWithinTolerance("Albers", pGeo, pGeo2, 0.0000001, reverse: true); + } + + /// + /// Verifies forward and inverse Mercator 1SP projection using the Bessel 1840 ellipsoid with metre output units. + /// + [Fact] + public void TestMercator1SPProjection() + { + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum("Bessel 1840", DatumType.HD_Geocentric, ellipsoid, null); + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Bessel 1840", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + var parameters = new List(5) + { + new("latitude_of_origin", 0), + new("central_meridian", 110), + new("scale_factor", 0.997), + new("false_easting", 3900000), + new("false_northing", 900000), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Mercator_1SP", "Mercator_1SP", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Makassar / NEIEZ", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcs, coordsys); + + double[] pGeo = [120, -3]; + double[] pUtm = trans.MathTransform.Transform(pGeo); + double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); + + double[] expected = [5009726.58, 569150.82]; + this.AssertCoordinateWithinTolerance("Mercator_1SP", expected, pUtm, 0.02); + this.AssertCoordinateWithinTolerance("Mercator_1SP", pGeo, pGeo2, 0.0000001, reverse: true); + } + + /// + /// Verifies forward and inverse Mercator 1SP projection using the Bessel 1840 ellipsoid with feet output units. + /// + [Fact] + public void TestMercator1SPProjectionFeet() + { + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum("Bessel 1840", DatumType.HD_Geocentric, ellipsoid, null); + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Bessel 1840", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + var parameters = new List(5) + { + new("latitude_of_origin", 0), + new("central_meridian", 110), + new("scale_factor", 0.997), + new("false_easting", 3900000 / LinearUnit.Foot.MetersPerUnit), + new("false_northing", 900000 / LinearUnit.Foot.MetersPerUnit), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Mercator_1SP", "Mercator_1SP", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Makassar / NEIEZ", gcs, projection, LinearUnit.Foot, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcs, coordsys); + + double[] pGeo = [120d, -3d]; + double[] pUtm = trans.MathTransform.Transform(pGeo); + double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); + + double[] expected = [5009726.58 / LinearUnit.Foot.MetersPerUnit, 569150.82 / LinearUnit.Foot.MetersPerUnit]; + this.AssertCoordinateWithinTolerance("Mercator_1SP", expected, pUtm, 0.02); + this.AssertCoordinateWithinTolerance("Mercator_1SP", pGeo, pGeo2, 0.0000001, reverse: true); + } + + /// + /// Verifies forward and inverse Mercator 2SP (Caspian Sea) projection using the Krassowski 1940 ellipsoid. + /// + [Fact] + public void TestMercator2SPProjection() + { + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Krassowski 1940", 6378245.0, 298.3, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum("Krassowski 1940", DatumType.HD_Geocentric, ellipsoid, null); + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Krassowski 1940", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + var parameters = new List(5) + { + new("latitude_of_origin", 42), + new("central_meridian", 51), + new("false_easting", 0), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Mercator_2SP", "Mercator_2SP", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Pulkovo 1942 / Mercator Caspian Sea", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcs, coordsys); + + double[] pGeo = [53d, 53d]; + double[] pUtm = trans.MathTransform.Transform(pGeo); + double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); + + double[] expected = [165704.29, 5171848.07]; + this.AssertCoordinateWithinTolerance("Mercator_2SP", expected, pUtm, 0.02); + this.AssertCoordinateWithinTolerance("Mercator_2SP", pGeo, pGeo2, 0.0000001, reverse: true); + } + + /// + /// Verifies forward and inverse Transverse Mercator projection for the OSGB 1936 British National Grid. + /// + [Fact] + public void TestTransverseMercatorProjection() + { + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Airy 1830", 6377563.396, 299.32496, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum("Airy 1830", DatumType.HD_Geocentric, ellipsoid, null); + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Airy 1830", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + var parameters = new List(5) + { + new("latitude_of_origin", 49), + new("central_meridian", -2), + new("scale_factor", 0.9996012717), // 0.9996 + new("false_easting", 400000), + new("false_northing", -100000), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Transverse Mercator", "Transverse_Mercator", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("OSGB 1936 / British National Grid", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcs, coordsys); + + double[] pGeo = [0.5, 50.5]; + double[] pUtm = trans.MathTransform.Transform(pGeo); + double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); + + // "POINT(577393.372775651 69673.621953601)" + double[] expected = [577274.98, 69740.49]; + this.AssertCoordinateWithinTolerance("TransverseMercator", expected, pUtm, 0.01); + this.AssertCoordinateWithinTolerance("TransverseMercator", pGeo, pGeo2, 1E-6, reverse: true); + } + + /// + /// Verifies forward and inverse Lambert Conic Conformal 2SP projection for the NAD27 / Texas South Central system. + /// + [Fact] + public void TestLambertConicConformal2SPProjection() + { + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1866", 20925832.16, 294.97470, LinearUnit.USSurveyFoot); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum("Clarke 1866", DatumType.HD_Geocentric, ellipsoid, null); + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Clarke 1866", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + var parameters = new List(5) + { + new("latitude_of_origin", 27.833333333), + new("central_meridian", -99), + new("standard_parallel_1", 28.3833333333), + new("standard_parallel_2", 30.2833333333), + new("false_easting", 2000000 / LinearUnit.USSurveyFoot.MetersPerUnit), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Lambert Conic Conformal (2SP)", "lambert_conformal_conic_2sp", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("NAD27 / Texas South Central", gcs, projection, LinearUnit.USSurveyFoot, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcs, coordsys); + + double[] pGeo = [-96, 28.5]; + double[] pUtm = trans.MathTransform.Transform(pGeo); + double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); + + double[] expected = [2963503.91 / LinearUnit.USSurveyFoot.MetersPerUnit, 254759.80 / LinearUnit.USSurveyFoot.MetersPerUnit]; + this.AssertCoordinateWithinTolerance("LambertConicConformal2SP", expected, pUtm, 0.05); + this.AssertCoordinateWithinTolerance("LambertConicConformal2SP", pGeo, pGeo2, 0.0000001, reverse: true); + } + + private ICoordinateTransformation CreateGeo2Laea(double centralMeridian, double latitudeOfOrigin) + { + GeographicCoordinateSystem wgs84 = GeographicCoordinateSystem.WGS84; + string laeaWkt = + "PROJCS[\"Lambert_Azimuthal_Equal_Area_Custom\"," + + "GEOGCS[\"GCS_WGS_1984\"," + + "DATUM[\"D_WGS_1984\"," + + "SPHEROID[\"WGS_1984\",6378137.0,298.257223563]]," + + "PRIMEM[\"Greenwich\",0.0]," + + "UNIT[\"Degree\",0.0174532925199433]]," + + "PROJECTION[\"Lambert_Azimuthal_Equal_Area\"]," + + "PARAMETER[\"False_Easting\",0.0]," + + "PARAMETER[\"False_Northing\",0.0]," + + $"PARAMETER[\"Central_Meridian\",{centralMeridian}]," + + $"PARAMETER[\"Latitude_Of_Origin\",{latitudeOfOrigin}]," + + "UNIT[\"Meter\",1.0]]"; + + CoordinateSystem coordsys = this.RequireCoordinateSystem(laeaWkt); + + return this.CreateTransformation(wgs84, coordsys); + } + + /// + /// Verifies that forward and inverse Lambert Azimuthal Equal Area transforms round-trip to the projection origin across 1000 random projection centers. + /// + [Fact] + public void TestLambertAzimuthalEqualAreaProjectionRoundTripOnOrigin() + { + for (int i = 0; i < 1000; i++) + { + double centralMeridian = this.Random.Next(-180, +180); + double latitudeOfOrigin = this.Random.Next(-90, +90); + + ICoordinateTransformation trans = this.CreateGeo2Laea(centralMeridian, latitudeOfOrigin); + + MathTransform forward = trans.MathTransform; + MathTransform reverse = forward.Inverse(); + + double[] pGeo = [centralMeridian, latitudeOfOrigin]; + + double[] pLaea = forward.Transform(pGeo); + + double[] pGeo2 = reverse.Transform(pLaea); + + double[] expectedPLaea = [0, 0]; + + this.AssertCoordinateWithinTolerance("Lambert_Azimuthal_Equal_Area", expectedPLaea, pLaea, 0.05); + this.AssertCoordinateWithinTolerance("Lambert_Azimuthal_Equal_Area", pGeo, pGeo2, 0.0000001, reverse: true); + } + } + + /// + /// Verifies that forward and inverse Lambert Azimuthal Equal Area transforms round-trip correctly for 1000 random off-origin points. + /// + [Fact] + public void TestLambertAzimuthalEqualAreaProjectionRoundTripOnArbitraryPoint() + { + int GetRandomSign() + { + return this.Random.Next() % 2 == 0 ? -1 : +1; + } + + for (int i = 0; i < 1000; i++) + { + double centralMeridian = this.Random.Next(-150, +150); + double latitudeOfOrigin = this.Random.Next(-70, +70); + + ICoordinateTransformation trans = this.CreateGeo2Laea(centralMeridian, latitudeOfOrigin); + + MathTransform forward = trans.MathTransform; + MathTransform reverse = forward.Inverse(); + + double lat = latitudeOfOrigin + ((0.01 + this.Random.NextDouble()) * GetRandomSign()); + double lon = centralMeridian + ((0.01 + this.Random.NextDouble()) * GetRandomSign()); + + double[] pGeo = [lon, lat]; + + double[] pLaea = forward.Transform(pGeo); + + double[] pGeo2 = reverse.Transform(pLaea); + + Assert.NotEqual(0d, pLaea[0]); + Assert.NotEqual(0d, pLaea[1]); + this.AssertCoordinateWithinTolerance("Lambert_Azimuthal_Equal_Area", pGeo, pGeo2, 0.0000001, reverse: true); + } + } + + /// + /// Verifies the geographic-to-geocentric coordinate transformation and its inverse using the ETRF89 datum. + /// + [Fact] + public void TestGeocentric() + { + GeographicCoordinateSystem gcs = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "ETRF89 Geographic", + AngularUnit.Degrees, + HorizontalDatum.ETRF89, + PrimeMeridian.Greenwich, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + GeocentricCoordinateSystem gcenCs = this.CoordinateSystemFactory.CreateGeocentricCoordinateSystem("ETRF89 Geocentric", HorizontalDatum.ETRF89, LinearUnit.Metre, PrimeMeridian.Greenwich); + ICoordinateTransformation ct = this.CreateTransformation(gcs, gcenCs); + double[] pExpected = [2 + (7.0 / 60) + (46.38 / 3600), 53 + (48.0 / 60) + (33.82 / 3600)]; // Point.FromDMS(2, 7, 46.38, 53, 48, 33.82); + double[] pExpected3D = [pExpected[0], pExpected[1], 73.0]; + double[] p0 = [3771793.97, 140253.34, 5124304.35]; + double[] p1 = ct.MathTransform.Transform(pExpected3D); + double[] p2 = ct.MathTransform.Inverse().Transform(p1); + this.AssertCoordinateWithinTolerance("Geocentric", p0, p1, 0.01); + this.AssertCoordinateWithinTolerance("Geocentric", pExpected, p2, TestTolerances.CoordinateRoundTrip, reverse: true); + } + + /// + /// Verifies datum shift transformations between WGS72, WGS84, and ED50 in both geocentric and projected (UTM) coordinate spaces. + /// + [Fact] + public void TestDatumTransform() + { + // Define datums, set parameters + HorizontalDatum wgs72 = HorizontalDatum.WGS72; + HorizontalDatum ed50 = HorizontalDatum.ED50.WithWgs84Parameters( + new Wgs84ConversionInfo( + -81.0703, + -89.3603, + -115.7526, + -0.48488, + -0.02436, + -0.41321, + -0.540645)); + + // Define geographic coordinate systems + _ = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS72 Geographic", + AngularUnit.Degrees, + wgs72, + PrimeMeridian.Greenwich, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + GeographicCoordinateSystem gcsWGS84 = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS84 Geographic", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + GeographicCoordinateSystem gcsED50 = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "ED50 Geographic", + AngularUnit.Degrees, + ed50, + PrimeMeridian.Greenwich, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + // Define geocentric coordinate systems + GeocentricCoordinateSystem gcenCsWGS72 = this.CoordinateSystemFactory.CreateGeocentricCoordinateSystem("WGS72 Geocentric", wgs72, LinearUnit.Metre, PrimeMeridian.Greenwich); + GeocentricCoordinateSystem gcenCsWGS84 = this.CoordinateSystemFactory.CreateGeocentricCoordinateSystem("WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); + _ = this.CoordinateSystemFactory.CreateGeocentricCoordinateSystem("ED50 Geocentric", ed50, LinearUnit.Metre, PrimeMeridian.Greenwich); + + // Define projections + var parameters = new List(5) + { + new("latitude_of_origin", 0), + new("central_meridian", 9), + new("scale_factor", 0.9996), + new("false_easting", 500000), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Transverse Mercator", "Transverse_Mercator", parameters); + ProjectedCoordinateSystem utmED50 = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("ED50 UTM Zone 32N", gcsED50, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + ProjectedCoordinateSystem utmWGS84 = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("WGS84 UTM Zone 32N", gcsWGS84, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + // Test datum-shift from WGS72 to WGS84 + double[] pGeoCenWGS72 = [3657660.66, 255768.55, 5201382.11]; + ICoordinateTransformation geocen_ed50_2_Wgs84 = this.CreateTransformation(gcenCsWGS72, gcenCsWGS84); + double[] pGeoCenWGS84 = geocen_ed50_2_Wgs84.MathTransform.Transform(pGeoCenWGS72); + + double[] pExpected = [3657660.78, 255778.43, 5201387.75]; + this.AssertCoordinateWithinTolerance("Datum WGS72->WGS84", pExpected, pGeoCenWGS84, 0.01); + + // and inverse + double[] pGeoCenWGS72calc = geocen_ed50_2_Wgs84.MathTransform.Inverse().Transform(pGeoCenWGS84); + this.AssertCoordinateWithinTolerance("Datum WGS84->WGS72", pGeoCenWGS72, pGeoCenWGS72calc, 0.001); + + ICoordinateTransformation utm_ed50_2_Wgs84 = this.CreateTransformation(utmED50, utmWGS84); + double[] pUTMED50 = [600000, 6100000]; + double[] pUTMWGS84 = utm_ed50_2_Wgs84.MathTransform.Transform(pUTMED50); + pExpected = [599928.6, 6099790.2]; + this.AssertCoordinateWithinTolerance("Datum ED50->WGS84", pExpected, pUTMWGS84, 0.1); + + // and inverse + double[] pUTMED50calc = utm_ed50_2_Wgs84.MathTransform.Inverse().Transform(pUTMWGS84); + this.AssertCoordinateWithinTolerance("Datum WGS84->ED50", pUTMED50, pUTMED50calc, 0.01); + + // Perform reverse + ICoordinateTransformation utm_Wgs84_2_Ed50 = this.CreateTransformation(utmWGS84, utmED50); + pUTMED50 = utm_Wgs84_2_Ed50.MathTransform.Transform(pUTMWGS84); + pExpected = [600000, 6100000]; + this.AssertCoordinateWithinTolerance("Datum", pExpected, pUTMED50, 0.1); + + // and inverse + double[] pUTMWGS84calc = utm_Wgs84_2_Ed50.MathTransform.Inverse().Transform(pUTMED50); + this.AssertCoordinateWithinTolerance("Datum", pUTMWGS84, pUTMWGS84calc, 0.1); + + // Assert.True(Math.Abs((pUTMWGS84 as Point3D).Z - 36.35) < 0.5); + // Point pExpected = Point.FromDMS(2, 7, 46.38, 53, 48, 33.82); + } + + /// + /// Verifies forward and inverse Krovak projection referenced to the Greenwich meridian (EPSG 5514 / 102067). + /// + [Fact] + public void TestKrovakGreenwichProjection() + { + // test case for epsg 5514 (102067) + GeographicCoordinateSystem gcsWGS84 = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS84 Geographic", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum( + "Bessel 1840", + DatumType.HD_Geocentric, + ellipsoid, + new Wgs84ConversionInfo(570.8, 85.7, 462.8, 4.998, 1.587, 5.261, 3.56)); + + GeographicCoordinateSystem gcsKrovak = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Bessel 1840", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + var parameters = new List(5) + { + new("latitude_of_center", 49.5), + new("longitude_of_center", 24.83333333333333), + new("azimuth", 30.28813972222222), + new("pseudo_standard_parallel_1", 78.5), + new("scale_factor", 0.9999), + new("false_easting", 0), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Krovak", "Krovak", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Krovak", gcsKrovak, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcsWGS84, coordsys); + ICoordinateTransformation trans2 = this.CreateTransformation(gcsWGS84, coordsys); + + // test case 1 + double[] pGeo = [12d, 48d]; + double[] expected = [-953116.2548718402, -1245513.5788112187]; + + double[] pUtm = trans.MathTransform.Transform(pGeo); + + // can't inverse trans - Inverse() of ConcateratedTransform makes shallow copy and call Invert on each ICoordinateTransformation.MathTransform - this changes original transformation! + double[] pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); + + this.AssertCoordinateWithinTolerance("Krovak", expected, pUtm, 0.2); + this.AssertCoordinateWithinTolerance("Krovak", pGeo, pGeo2, 0.001, reverse: true); + + // test case 2 + pGeo = [18, 49]; + expected = [-499143.4909304862, -1192340.009253714]; + + pUtm = trans.MathTransform.Transform(pGeo); + pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); + + this.AssertCoordinateWithinTolerance("Krovak", expected, pUtm, 0.2); + this.AssertCoordinateWithinTolerance("Krovak", pGeo, pGeo2, 0.001); + } + + /// + /// Verifies forward and inverse Krovak projection referenced to the Ferro prime meridian (EPSG 2065). + /// + [Fact] + public void TestKrovakFerroProjection() + { + // test case for epsg 2065 (prime meridian at Ferro) + GeographicCoordinateSystem gcsWGS84 = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS84 Geographic", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + Ellipsoid ellipsoid = this.CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); + + HorizontalDatum datum = this.CoordinateSystemFactory.CreateHorizontalDatum( + "Bessel 1840", + DatumType.HD_Geocentric, + ellipsoid, + new Wgs84ConversionInfo(570.8, 85.7, 462.8, 4.998, 1.587, 5.261, 3.56)); + + GeographicCoordinateSystem gcsKrovak = this.CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "Bessel 1840", + AngularUnit.Degrees, + datum, + PrimeMeridian.Ferro, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + var parameters = new List(5) + { + new("latitude_of_center", 49.5), + new("longitude_of_center", 42.5), + new("azimuth", 30.28813972222222), + new("pseudo_standard_parallel_1", 78.5), + new("scale_factor", 0.9999), + new("false_easting", 0), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Krovak", "Krovak", parameters); + + ProjectedCoordinateSystem coordsys = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Krovak", gcsKrovak, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); + + ICoordinateTransformation trans = this.CreateTransformation(gcsWGS84, coordsys); + ICoordinateTransformation trans2 = this.CreateTransformation(gcsWGS84, coordsys); + + // test case 1 + double[] pGeo = [12d, 48d]; + double[] expected = [-953116.2548718402, -1245513.5788112187]; + + double[] pUtm = trans.MathTransform.Transform(pGeo); + + // can't inverse trans - Inverse() of ConcateratedTransform makes shallow copy and call Invert on each ICoordinateTransformation.MathTransform - this changes original transformation! + double[] pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); + + this.AssertCoordinateWithinTolerance("Krovak", expected, pUtm, 0.2); + this.AssertCoordinateWithinTolerance("Krovak", pGeo, pGeo2, 0.001, reverse: true); + + // test case 2 + pGeo = [18, 49]; + expected = [-499143.4909304862, -1192340.009253714]; + + pUtm = trans.MathTransform.Transform(pGeo); + pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); + + this.AssertCoordinateWithinTolerance("Krovak", expected, pUtm, 0.2); + this.AssertCoordinateWithinTolerance("Krovak", pGeo, pGeo2, 0.001); + } + + /// + /// Verifies forward and inverse Oblique Stereographic projection for EPSG 2171 (Pulkovo 1942(58) / Poland zone I). + /// + [Fact] + public void TestObliqueStereographicProjection() + { + // test data from http://www.spatialreference.org/ref/epsg/2171/ + double[] coord2171 = [4615496.325851, 5605702.221723]; + double[] coord4326 = [20.78002815042, 50.25299100927]; + + string wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]"; + string wkt2171 = "PROJCS[\"Pulkovo 1942(58) / Poland zone I\",GEOGCS[\"Pulkovo 1942(58)\",DATUM[\"Pulkovo_1942_58\",SPHEROID[\"Krassowsky 1940\",6378245,298.3,AUTHORITY[\"EPSG\",\"7024\"]],TOWGS84[33.4,-146.6,-76.3,-0.359,-0.053,0.844,-0.84],AUTHORITY[\"EPSG\",\"6179\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4179\"]],PROJECTION[\"Oblique_Stereographic\"],PARAMETER[\"latitude_of_origin\",50.625],PARAMETER[\"central_meridian\",21.08333333333333],PARAMETER[\"scale_factor\",0.9998],PARAMETER[\"false_easting\",4637000],PARAMETER[\"false_northing\",5647000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"2171\"]]"; + + CoordinateSystem cs1 = this.RequireCoordinateSystem(wkt4326); + CoordinateSystem cs2 = this.RequireCoordinateSystem(wkt2171); + + ICoordinateTransformation ict = this.CreateTransformation(cs2, cs1); + + double[] transformedCoord4326 = ict.MathTransform.Transform(coord2171); + + Assert.Equal(coord4326[0], transformedCoord4326[0], 0.01); + Assert.Equal(coord4326[1], transformedCoord4326[1], 0.01); + + ICoordinateTransformation ict2 = this.CreateTransformation(cs1, cs2); + double[] transformedCoord2171 = ict2.MathTransform.Transform(coord4326); + + Assert.Equal(coord2171[0], transformedCoord2171[0], 1); + Assert.Equal(coord2171[1], transformedCoord2171[1], 1); + } + + /// + /// Verifies forward and inverse Universal Polar Stereographic (UPS North) projection for EPSG 32661. + /// + [Fact] + public void TestUniversalPolarStereographicProjection() + { + // test data from http://epsg.io/transform + double[] coord4326 = [15.00, 73.00]; + double[] coord32661 = [2491967.01029204, 163954.12194234435]; + + string wkt4326 = string.Empty + + "GEOGCS[\"WGS 84\"," + + "DATUM[\"WGS_1984\"," + + "SPHEROID[\"WGS 84\",6378137,298.257223563," + + "AUTHORITY[\"EPSG\",\"7030\"]]," + + "AUTHORITY[\"EPSG\",\"6326\"]]," + + "PRIMEM[\"Greenwich\",0," + + "AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.01745329251994328," + + "AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4326\"]]"; + + string wkt32661 = string.Empty + + "PROJCS[\"WGS 84 / UPS North (N,E)\"," + + "GEOGCS[\"WGS 84\"," + + "DATUM[\"WGS_1984\"," + + "SPHEROID[\"WGS 84\",6378137,298.257223563," + + "AUTHORITY[\"EPSG\",\"7030\"]]," + + "AUTHORITY[\"EPSG\",\"6326\"]]," + + "PRIMEM[\"Greenwich\",0," + + "AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433," + + "AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4326\"]]," + + "PROJECTION[\"Polar_Stereographic\"]," + + "PARAMETER[\"latitude_of_origin\",90]," + + "PARAMETER[\"central_meridian\",0]," + + "PARAMETER[\"scale_factor\",0.994]," + + "PARAMETER[\"false_easting\",2000000]," + + "PARAMETER[\"false_northing\",2000000]," + + "UNIT[\"metre\",1," + + "AUTHORITY[\"EPSG\",\"9001\"]]," + + "AUTHORITY[\"EPSG\",\"32661\"]]"; + + CoordinateSystem cs1 = this.RequireCoordinateSystem(wkt4326); + CoordinateSystem cs2 = this.RequireCoordinateSystem(wkt32661); + + ICoordinateTransformation ict = this.CreateTransformation(cs2, cs1); + ICoordinateTransformation ict2 = this.CreateTransformation(cs1, cs2); + double[] transformedCoord4326 = ict.MathTransform.Transform(coord32661); + double[] transformedCoord32661 = ict2.MathTransform.Transform(coord4326); + + Assert.Equal(coord4326[0], transformedCoord4326[0], 0.01); + Assert.Equal(coord4326[1], transformedCoord4326[1], 0.01); + Assert.Equal(coord32661[0], transformedCoord32661[0], 1); + Assert.Equal(coord32661[1], transformedCoord32661[1], 1); + } + + /// + /// Verifies forward and inverse Australian Antarctic Polar Stereographic projection for EPSG 3032. + /// + [Fact] + public void TestAustralianAntarcticPolarStereographicProjection() + { + // test data from http://epsg.io/transform + double[] coord4326 = [15.00, -73.00]; + double[] coord3032 = [4476201.247377692, 7066975.373300694]; + + string wkt4326 = string.Empty + + "GEOGCS[\"WGS 84\"," + + "DATUM[\"WGS_1984\"," + + "SPHEROID[\"WGS 84\",6378137,298.257223563," + + "AUTHORITY[\"EPSG\",\"7030\"]]," + + "AUTHORITY[\"EPSG\",\"6326\"]]," + + "PRIMEM[\"Greenwich\",0," + + "AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.01745329251994328," + + "AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4326\"]]"; + + string wkt3032 = string.Empty + + "PROJCS[\"WGS 84 / Australian Antarctic Polar Stereographic\"," + + "GEOGCS[\"WGS 84\"," + + "DATUM[\"WGS_1984\"," + + "SPHEROID[\"WGS 84\",6378137,298.257223563," + + "AUTHORITY[\"EPSG\",\"7030\"]]," + + "AUTHORITY[\"EPSG\",\"6326\"]]," + + "PRIMEM[\"Greenwich\",0," + + "AUTHORITY[\"EPSG\",\"8901\"]]," + + "UNIT[\"degree\",0.0174532925199433," + + "AUTHORITY[\"EPSG\",\"9122\"]]," + + "AUTHORITY[\"EPSG\",\"4326\"]]," + + "PROJECTION[\"Polar_Stereographic\"]," + + "PARAMETER[\"latitude_of_origin\",-71]," + + "PARAMETER[\"central_meridian\",70]," + + "PARAMETER[\"false_easting\",6000000]," + + "PARAMETER[\"false_northing\",6000000]," + + "UNIT[\"metre\",1," + + "AUTHORITY[\"EPSG\",\"9001\"]]," + + "AUTHORITY[\"EPSG\",\"3032\"]]"; + + CoordinateSystem cs1 = this.RequireCoordinateSystem(wkt4326); + CoordinateSystem cs2 = this.RequireCoordinateSystem(wkt3032); + + ICoordinateTransformation ict = this.CreateTransformation(cs2, cs1); + ICoordinateTransformation ict2 = this.CreateTransformation(cs1, cs2); + double[] transformedCoord4326 = ict.MathTransform.Transform(coord3032); + double[] transformedCoord3032 = ict2.MathTransform.Transform(coord4326); + + Assert.Equal(coord4326[0], transformedCoord4326[0], 0.01); + Assert.Equal(coord4326[1], transformedCoord4326[1], 0.01); + Assert.Equal(coord3032[0], transformedCoord3032[0], 1); + Assert.Equal(coord3032[1], transformedCoord3032[1], 1); + } + + /// + /// Verifies that a feet-based projected system (EPSG 2868, Arizona Central State Plane) transforms correctly from WGS84 geographic coordinates. + /// + [Fact] + public void TestUnitTransforms() + { + CoordinateSystem nadUTM = Assert.IsType(SRIDReader.GetCSbyID(2868), exactMatch: false); // UTM Arizona Central State Plane using Feet as units + CoordinateSystem wgs84GCS = Assert.IsType(SRIDReader.GetCSbyID(4326), exactMatch: false); // GCS WGS84 + ICoordinateTransformation trans = this.CreateTransformation(wgs84GCS, nadUTM); + + double[] p0 = [-111.89, 34.165]; + + double[] expected = [708066.19057935325, 1151426.4460563776]; + + double[] p1 = trans.MathTransform.Transform(p0); + double[] p2 = trans.MathTransform.Inverse().Transform(p1); + + this.AssertCoordinateWithinTolerance("Unit", expected, p1, 0.013); + + // WARNING: This accuracy is too poor! + this.AssertCoordinateWithinTolerance("Unit", p0, p2, 0.0001, reverse: true); + } + + /// + /// Verifies the Polyconic projection (SAD69 / Brazil Polyconic, EPSG 29101) forward and inverse transforms. + /// + [Fact(DisplayName = "Accuracy very poor!")] + public void TestPolyconicTransforms() + { + CoordinateSystem wgs84GCS = Assert.IsType(SRIDReader.GetCSbyID(4326), exactMatch: false); // GCS WGS84 + string wkt = + + // "PROJCS[\"SAD69 / Brazil Polyconic (deprecated)\",GEOGCS[\"SAD69\",DATUM[\"South_American_Datum_1969\",SPHEROID[\"GRS 1967\",6378160,298.247167427,AUTHORITY[\"EPSG\",\"7036\"]],TOWGS84[-57,1,-41,0,0,0,0],AUTHORITY[\"EPSG\",\"6291\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4291\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],PROJECTION[\"Polyconic\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-54],PARAMETER[\"false_easting\",5000000],PARAMETER[\"false_northing\",10000000],AUTHORITY[\"EPSG\",\"29100\"],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]"; + // "PROJCS[\"SAD69 / Brazil Polyconic\",GEOGCS[\"SAD69\",DATUM[\"South_American_Datum_1969\",SPHEROID[\"GRS 1967 Modified\",6378160,298.25,AUTHORITY[\"EPSG\",\"7050\"]],TOWGS84[-57,1,-41,0,0,0,0],AUTHORITY[\"EPSG\",\"6618\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4618\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],PROJECTION[\"Polyconic\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-54],PARAMETER[\"false_easting\",5000000],PARAMETER[\"false_northing\",10000000],AUTHORITY[\"EPSG\",\"29101\"],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]"; + "PROJCS[\"SAD69 / Brazil Polyconic\",GEOGCS[\"SAD69\",DATUM[\"South_American_Datum_1969\",SPHEROID[\"GRS 1967 (SAD69)\", 6378160, 298.25, AUTHORITY[\"EPSG\", \"7050\"]],AUTHORITY[\"EPSG\", \"6618\"]], PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]],UNIT[\"degree\", 0.01745329251994328, AUTHORITY[\"EPSG\", \"9122\"]], AUTHORITY[\"EPSG\", \"4618\"]], PROJECTION[\"Polyconic\"],PARAMETER[\"latitude_of_origin\", 0], PARAMETER[\"central_meridian\", -54],PARAMETER[\"false_easting\", 5000000], PARAMETER[\"false_northing\", 10000000],UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]], AXIS[\"X\", EAST], AXIS[\"Y\", NORTH],AUTHORITY[\"EPSG\", \"29101\"]]"; + CoordinateSystem sad69 = this.RequireCoordinateSystem(wkt); + + ICoordinateTransformation trans = this.CreateTransformation(wgs84GCS, sad69); + double[] p0 = [-50.085, -14.32]; + double[] expected = [5422386.5795, 8412674.8723]; + + // "POINT(5422386.57956145 8412722.92229278)" + double[] p1 = trans.MathTransform.Transform(p0); + trans.MathTransform.Invert(); + double[] p2 = trans.MathTransform.Transform(p1); + + this.AssertCoordinateWithinTolerance("Polyconic", expected, p1, 50); + this.AssertCoordinateWithinTolerance("Polyconic", p0, p2, 0.0001, reverse: true); + } + + /// + /// Verifies forward and inverse Cassini-Soldner projection for DHDN / Soldner Berlin (EPSG 3068). + /// + [Fact] + public void TestCassiniSoldner() + { + GeographicCoordinateSystem csSource = GeographicCoordinateSystem.WGS84; + CoordinateSystem csTarget = this.RequireCoordinateSystem("PROJCS[\"DHDN / Soldner Berlin\",GEOGCS[\"DHDN\",DATUM[\"Deutsches_Hauptdreiecksnetz\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],TOWGS84[598.1,73.7,418.2,0.202,0.045,-2.455,6.7],AUTHORITY[\"EPSG\",\"6314\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4314\"]],PROJECTION[\"Cassini_Soldner\"],PARAMETER[\"latitude_of_origin\",52.41864827777778],PARAMETER[\"central_meridian\",13.62720366666667],PARAMETER[\"false_easting\",40000],PARAMETER[\"false_northing\",10000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"x\",NORTH],AXIS[\"y\",EAST],AUTHORITY[\"EPSG\",\"3068\"]]"); + + this.AssertTransformation( + "CassiniSoldner", + csSource, + csTarget, + CassiniSoldnerInput, + CassiniSoldnerExpected, + 0.3, + TestTolerances.CoordinateRoundTrip); + } + + /// + /// Verifies forward and inverse Hotine Oblique Mercator projection for NAD83(NSRS2007) / Alaska zone 1 (EPSG 3468). + /// + [Fact] + public void TestHotineObliqueMercator() + { + GeographicCoordinateSystem csSource = GeographicCoordinateSystem.WGS84; + CoordinateSystem csTarget = this.RequireCoordinateSystem("PROJCS[\"NAD83(NSRS2007) / Alaska zone 1\",GEOGCS[\"NAD83(NSRS2007)\",DATUM[\"NAD83_National_Spatial_Reference_System_2007\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6759\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4759\"]],PROJECTION[\"Hotine_Oblique_Mercator\"],PARAMETER[\"latitude_of_center\",57],PARAMETER[\"longitude_of_center\",-133.6666666666667],PARAMETER[\"azimuth\",323.1301023611111],PARAMETER[\"rectified_grid_angle\",323.1301023611111],PARAMETER[\"scale_factor\",0.9999],PARAMETER[\"false_easting\",5000000],PARAMETER[\"false_northing\",-5000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],AUTHORITY[\"EPSG\",\"3468\"]]"); + + // 61.216667 deg, -149.883333 deg + // "POINT(4136805.82642057 -4424019.78560519)" + this.AssertTransformation( + "HotineObliqueMercator", + csSource, + csTarget, + [-149.883333, 61.216667], + [4136805.826, -4424019.786], + 0.01, + TestTolerances.CoordinateRoundTrip); + } + + /// + /// Verifies that a concatenated transform correctly modifies an array in-place. + /// + [Fact] + public void TestTransformListOnConcatenatedDoTransform() + { + CoordinateSystem utm35ETRS = + this.RequireCoordinateSystem("PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); + + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + ICoordinateTransformation trans = this.CreateTransformation(utm35ETRS, utm33); + + var coords = new XY[] + { + new(290586.087, 6714000), + new(290586.392, 6713996.224), + new(290590.133, 6713973.772), + }; + + trans.MathTransform.Transform(coords); + Assert.NotEqual(290586.087, coords[0].X); + Assert.NotEqual(6714000, coords[0].Y); + } + + /// + /// Verifies that a concatenated transform correctly converts a list of double-array coordinates. + /// + [Fact] + public void TestTransformListOnConcatenatedDoTransformDoubleArr() + { + CoordinateSystem utm35ETRS = + this.RequireCoordinateSystem("PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); + + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + ICoordinateTransformation trans = this.CreateTransformation(utm35ETRS, utm33); + + var coords = new List + { + TransformListSamplePoint1, + TransformListSamplePoint2, + TransformListSamplePoint3, + }; + + IList transformedCoords = trans.MathTransform.TransformList(coords); + Assert.NotEqual(290586.087, transformedCoords[0][0]); + Assert.NotEqual(6714000, transformedCoords[0][1]); + } + + /// + /// Test transformation for affine transformation. + /// + [Fact] + public void AffineTransformationTest() + { + // Local coordinate system MNAU (Kraftwerk Maeuserich) (based on Gauss-Krueger using affine transformation) + // affine transform + // 1) Offset: X=-3454886,640m Y=-5479481,278m; + // 2)Rotation: 332,0657, Rotation point X=3456926,640m Y=5481071,278m; + // 3) Scale: 1.0 + double[,] matrix = new double[,] + { + { 0.883485346527455, -0.468458794848877, 3455869.17937689 }, + { 0.468458794848877, 0.883485346527455, 5478710.88035753 }, + { 0.0, 0.0, 1 }, + }; + var mt = new AffineTransform(matrix); + + Assert.NotNull(mt); + + Assert.Equal(2, mt.DimSource); + Assert.Equal(2, mt.DimTarget); + + // Transformation example (MNAU -> GK) + // Start point (MNAU) X=2040,000m Y=1590,000m] + // Target point (GK): X=3456926,640m Y=5481071,278m; + double[] outPt = mt.Transform(AffineTestPoint); + + Assert.Equal(2, outPt.Length); + Assert.Equal(3456926.640, outPt[0], 0.00000001); + Assert.Equal(5481071.278, outPt[1], 0.00000001); + } + + /// + /// Test inverse transformation for affine transformation. + /// + [Fact] + public void InverseAffineTransformationTest() + { + // Local coordinate system MNAU (Kraftwerk Maeuserich) (based on Gauss-Krueger using affine transformation) + // affine transform + // 1) Offset: X=-3454886,640m Y=-5479481,278m; + // 2)Rotation: 332,0657, Rotation point X=3456926,640m Y=5481071,278m; + // 3) Scale: 1.0 + double[,] matrix = new double[,] + { + { 0.883485346527455, -0.468458794848877, 3455869.17937689 }, + { 0.468458794848877, 0.883485346527455, 5478710.88035753 }, + { 0.0, 0.0, 1 }, + }; + var mt = new AffineTransform(matrix); + + Assert.NotNull(mt); + + Assert.Equal(2, mt.DimSource); + Assert.Equal(2, mt.DimTarget); + + // Transformation example (MNAU -> GK) + // Start point (MNAU) X=2040,000m Y=1590,000m] + // Target point (GK): X=3456926,640m Y=5481071,278m; + + // check source transform + double[] outPt = mt.Transform(AffineTestPoint); + + Assert.Equal(2, outPt.Length); + Assert.Equal(3456926.640, outPt[0], 0.00000001); + Assert.Equal(5481071.278, outPt[1], 0.00000001); + + MathTransform invMt = mt.Inverse(); + + double[] inPt = invMt.Transform(AffineTargetPoint); + + Assert.Equal(2, inPt.Length); + Assert.Equal(2040.0, inPt[0], 0.00000001); + Assert.Equal(1590.0, inPt[1], 0.00000001); + + // check source transform - once more + double[] outPt2 = mt.Transform(AffineTestPoint); + + Assert.Equal(2, outPt2.Length); + Assert.Equal(3456926.640, outPt2[0], 0.00000001); + Assert.Equal(5481071.278, outPt2[1], 0.00000001); + } + + /// + /// Coordinate transformation test for fitted coordinate system - test CS - local coordinate system MNAU. + /// + [Fact] + public void TestTransformOnFittedCoordinateSystem() + { + // Local coordinate system MNAU (Kraftwerk Maeuserich) (based on Gauss-Krueger using affine transformation) + // affine transform + // 1) Offset: X=-3454886,640m Y=-5479481,278m; + // 2)Rotation: 332,0657, Rotation point X=3456926,640m Y=5481071,278m; + // 3) Scale: 1.0 + string ft_wkt = "FITTED_CS[\"Local coordinate system MNAU (based on Gauss-Krueger)\"," + + "PARAM_MT[\"Affine\"," + + "PARAMETER[\"num_row\",3],PARAMETER[\"num_col\",3],PARAMETER[\"elt_0_0\", 0.883485346527455],PARAMETER[\"elt_0_1\", -0.468458794848877],PARAMETER[\"elt_0_2\", 3455869.17937689],PARAMETER[\"elt_1_0\", 0.468458794848877],PARAMETER[\"elt_1_1\", 0.883485346527455],PARAMETER[\"elt_1_2\", 5478710.88035753],PARAMETER[\"elt_2_2\", 1]]," + + "PROJCS[\"DHDN / Gauss-Kruger zone 3\"," + + "GEOGCS[\"DHDN\"," + + "DATUM[\"Deutsches_Hauptdreiecksnetz\"," + + "SPHEROID[\"Bessel 1841\", 6377397.155, 299.1528128, AUTHORITY[\"EPSG\", \"7004\"]]," + + "TOWGS84[612.4, 77, 440.2, -0.054, 0.057, -2.797, 0.525975255930096]," + + "AUTHORITY[\"EPSG\", \"6314\"]]," + + "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]]," + + "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9122\"]]," + + "AUTHORITY[\"EPSG\", \"4314\"]]," + + "PROJECTION[\"Transverse_Mercator\"]," + + "PARAMETER[\"latitude_of_origin\", 0]," + + "PARAMETER[\"central_meridian\", 9]," + + "PARAMETER[\"scale_factor\", 1]," + + "PARAMETER[\"false_easting\", 3500000]," + + "PARAMETER[\"false_northing\", 0]," + + "UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]]," + + "AUTHORITY[\"EPSG\", \"31467\"]]" + + "]"; + + // string gk_wkt = "PROJCS[\"DHDN / Gauss-Kruger zone 3\",GEOGCS[\"DHDN\",DATUM[\"Deutsches_Hauptdreiecksnetz\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6314\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4314\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",9],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",3500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"31467\"]]"; + FittedCoordinateSystem fcs = this.RequireCoordinateSystem(ft_wkt); + + // ICoordinateSystem gkcs = fac.CreateFromWkt (gk_wkt); + + // Transformation example (MNAU -> GK) + // Start point (MNAU) X=2040,000m Y=1590,000m] + // Target point (GK): X=3456926,640m Y=5481071,278m; + ICoordinateTransformation trans = this.CreateTransformation(fcs, fcs.BaseCoordinateSystem); + + var coords = new List + { + AffineTestPoint, + }; + + IList transformedCoords = trans.MathTransform.TransformList(coords); + Assert.Equal(3456926.640, transformedCoords[0][0], 0.00000001); + Assert.Equal(5481071.278, transformedCoords[0][1], 0.00000001); + } + + /// + /// Verifies WKT2-derived geographic CRS definitions integrate with the fitted runtime path when transforming to the parsed base CRS. + /// + [Fact] + public void TestTransformFromDerivedGeographicWkt2ToBaseCoordinateSystem() + { + string wkt = CreateDerivedGeographicRuntimeCoordinateSystem().ToWktNode(WktVersion.Wkt22019).ToString(); + FittedCoordinateSystem derived = this.RequireCoordinateSystem(wkt); + GeographicCoordinateSystem baseCoordinateSystem = Assert.IsType(derived.BaseCoordinateSystem); + + ICoordinateTransformation transformation = this.CreateTransformation(derived, baseCoordinateSystem); + + double[] localPoint = [12.5, 55.25]; + double[] expected = derived.ToBaseTransform.Transform(localPoint); + double[] actual = transformation.MathTransform.Transform(localPoint); + double[] roundTripped = transformation.MathTransform.Inverse().Transform(actual); + + Assert.Equal(expected[0], actual[0], 12); + Assert.Equal(expected[1], actual[1], 12); + Assert.Equal(localPoint[0], roundTripped[0], 12); + Assert.Equal(localPoint[1], roundTripped[1], 12); + } + + /// + /// Verifies WKT2-derived projected CRS definitions compose through the fitted runtime path when transforming to another projected CRS. + /// + [Fact] + public void TestTransformFromDerivedProjectedWkt2ToDifferentProjectedCoordinateSystem() + { + string wkt = CreateDerivedProjectedRuntimeCoordinateSystem().ToWktNode(WktVersion.Wkt22019).ToString(); + FittedCoordinateSystem derived = this.RequireCoordinateSystem(wkt); + ProjectedCoordinateSystem baseCoordinateSystem = Assert.IsType(derived.BaseCoordinateSystem); + var targetCoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + ICoordinateTransformation transformation = this.CreateTransformation(derived, targetCoordinateSystem); + ICoordinateTransformation baseTransformation = this.CreateTransformation(baseCoordinateSystem, targetCoordinateSystem); + + double[] localPoint = [450000d, 6200000d]; + double[] expected = baseTransformation.MathTransform.Transform(derived.ToBaseTransform.Transform(localPoint)); + double[] actual = transformation.MathTransform.Transform(localPoint); + + Assert.Equal(expected[0], actual[0], 8); + Assert.Equal(expected[1], actual[1], 8); + } + + /// + /// Verifies PROJJSON-derived projected CRS definitions compose through the fitted runtime path when transforming from another projected CRS. + /// + [Fact] + public void TestTransformFromDifferentProjectedCoordinateSystemToDerivedProjectedProjJson() + { + FittedCoordinateSystem derived = Assert.IsType(ProjJsonReader.Parse(ProjJsonWriter.ToJson(CreateDerivedProjectedRuntimeCoordinateSystem()))); + var sourceCoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(33, true); + ProjectedCoordinateSystem baseCoordinateSystem = Assert.IsType(derived.BaseCoordinateSystem); + + ICoordinateTransformation transformation = this.CreateTransformation(sourceCoordinateSystem, derived); + ICoordinateTransformation sourceToBaseTransformation = this.CreateTransformation(sourceCoordinateSystem, baseCoordinateSystem); + + double[] sourcePoint = [500000d, 6100000d]; + double[] expected = derived.ToBaseTransform.Inverse().Transform(sourceToBaseTransformation.MathTransform.Transform(sourcePoint)); + double[] actual = transformation.MathTransform.Transform(sourcePoint); + + Assert.Equal(expected[0], actual[0], 8); + Assert.Equal(expected[1], actual[1], 8); + } + + /// + /// Tests the EPSG 21780 (Bern 1898 (Bern) / LV03C) projection with a non-Greenwich prime meridian. + /// + [Fact] + public void TestEPSG21780PrimeMeredianTransformation() + { + string wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]"; + string wkt21780 = "PROJCS[\"Bern 1898 (Bern) / LV03C\",GEOGCS[\"Bern 1898 (Bern)\",DATUM[\"CH1903_Bern\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6801\"]],PRIMEM[\"Bern\",7.439583333333333,AUTHORITY[\"EPSG\",\"8907\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4801\"]],PROJECTION[\"Hotine_Oblique_Mercator\"],PARAMETER[\"latitude_of_center\",46.95240555555556],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"azimuth\",90],PARAMETER[\"rectified_grid_angle\",90],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"21780\"]]"; + + // test data from http://spatialreference.org/ref/epsg/21780/ + double[] sourceCoord = [160443.329034, 23582.55586]; + double[] expectedTargetCoord = [9.5553588867188, 47.145080566406]; + + CoordinateSystem cs1 = Assert.IsType(CoordinateSystemWktReader.Parse(wkt21780), exactMatch: false); + CoordinateSystem cs2 = Assert.IsType(CoordinateSystemWktReader.Parse(wkt4326), exactMatch: false); + ICoordinateTransformation ict = this.CreateTransformation(cs1, cs2); + + double[] transformedCoord = ict.MathTransform.Transform(sourceCoord); + + Assert.True(transformedCoord.Length >= 2); + Assert.Equal(expectedTargetCoord[0], transformedCoord[0], 0.001); + Assert.Equal(expectedTargetCoord[1], transformedCoord[1], 0.001); + + // and back + ICoordinateTransformation ictb = this.CreateTransformation(cs2, cs1); + transformedCoord = ictb.MathTransform.Transform(transformedCoord); + + Assert.True(transformedCoord.Length >= 2); + Assert.Equal(sourceCoord[0], transformedCoord[0], 0.1); + Assert.Equal(sourceCoord[1], transformedCoord[1], 0.1); + } + + // https://github.com/NetTopologySuite/ProjNet4GeoAPI/issues/48 + + /// + /// Verifies the Hotine Oblique Mercator transformation for EPSG 2056 (CH1903+ / LV95, Switzerland). + /// + [Fact] + public void TestEPSG2056HotineObliqueMercatorAzimuthCenterSwitzerland() + { + GeographicCoordinateSystem csSrc = GeographicCoordinateSystem.WGS84; + CoordinateSystem csTgt = Assert.IsType(SRIDReader.GetCSbyID(2056), exactMatch: false); // CH1903+ / LV95 + ICoordinateTransformation transformer = this.CreateTransformation(csSrc, csTgt); + double x = 9.619803; + double y = 47.408735; + + transformer.MathTransform.Transform(ref x, ref y); + + // https://epsg.io/transform#s_srs=4326&t_srs=2056&x=9.6198031&y=47.4087350 + Assert.InRange(x, 2764607.79 - 0.1, 2764607.79 + 0.1); + Assert.InRange(y, 1253167.89 - 0.1, 1253167.89 + 0.1); + } + + /// + /// Verifies forward and inverse ellipsoidal Orthographic projection, including detection of points beyond the visible hemisphere. + /// + [Fact] + public void TestEllipsoidalOrthographicTransform() + { + // Check equatorial projection + GeographicCoordinateSystem csWgs84 = GeographicCoordinateSystem.WGS84; + var parameters = new List(5) + { + new("central_meridian", 0), + new("latitude_of_origin", 0), + new("scale_factor", 1), + new("false_easting", 0), + new("false_northing", 0), + }; + IProjection projection = this.CoordinateSystemFactory.CreateProjection("Orthographic", "Orthographic", parameters); + ProjectedCoordinateSystem orthographicSystem = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Orthographic centered", csWgs84, projection, LinearUnit.Metre, new AxisInfo("X", AxisOrientationEnum.East), new AxisInfo("Y", AxisOrientationEnum.North)); + ICoordinateTransformation trans = this.CreateTransformation(csWgs84, orthographicSystem); + + // Check origin remains in the same place + double[] origin = [0.0, 0.0]; + double[] transformedOrigin = trans.MathTransform.Transform(origin); + double[] inverseTransformedOrigin = trans.MathTransform.Inverse().Transform(transformedOrigin); + this.AssertCoordinateWithinTolerance("Orthographic", origin, transformedOrigin, 0.00001); + this.AssertCoordinateWithinTolerance("Orthograhpic", origin, inverseTransformedOrigin, 0.00001, reverse: true); + + // Check projection works as expected away from origin + double[] testEastWgs = [0.001, 0.0]; + double[] expectedXOrtho = [111, 0.0]; // We should expect that .001 degrees is equal to 111 meters at origin + double[] transEastWgs = trans.MathTransform.Transform(testEastWgs); + double[] invTransEastWgs = trans.MathTransform.Inverse().Transform(transEastWgs); + this.AssertCoordinateWithinTolerance("Orthographic", expectedXOrtho, transEastWgs, 1.0); + this.AssertCoordinateWithinTolerance("Orthographic", testEastWgs, invTransEastWgs, 1.0, reverse: true); + + // Check from guidance 7.2 + var parameters2 = new List(5) + { + new("central_meridian", 5.0), + new("latitude_of_origin", 55.0), + new("scale_factor", 1), + new("false_easting", 0), + new("false_northing", 0), + }; + IProjection projection2 = this.CoordinateSystemFactory.CreateProjection("Orthographic", "Orthographic", parameters2); + ProjectedCoordinateSystem orthoSystem2 = this.CoordinateSystemFactory.CreateProjectedCoordinateSystem("Orthographic", csWgs84, projection2, LinearUnit.Metre, new AxisInfo("X", AxisOrientationEnum.East), new AxisInfo("Y", AxisOrientationEnum.North)); + ICoordinateTransformation trans2 = this.CreateTransformation(csWgs84, orthoSystem2); + double[] test2 = [2.1295499950867, 53.809394412498]; + double[] expected2 = [-189011.711, -128640.567]; + double[] transTest2 = trans2.MathTransform.Transform(test2); + double[] invTransTest2 = trans2.MathTransform.Inverse().Transform(transTest2); + this.AssertCoordinateWithinTolerance("Orthographic", expected2, transTest2, 1.0); + this.AssertCoordinateWithinTolerance("Orthographic", test2, invTransTest2, 1.0, reverse: true); + + // Check that the algorithm correctly identifies a point that cannot be seen + Action action1 = () => trans.MathTransform.Transform(OrthographicHorizonTestPoint); + Assert.Throws(action1); + + Action action2 = () => trans2.MathTransform.Transform(OrthographicHorizonTestPoint); + Assert.Throws(action2); + } + + /// + /// Verifies transformation from WGS 1984 Web Mercator Auxiliary Sphere to a Lambert Conformal Conic state plane system. + /// + [Fact] + public static void TestMercatorAuxilarySphereTransformation() + { + string sourceWkt = "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_Auxiliary_Sphere\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"Standard_Parallel_1\",0.0],PARAMETER[\"Auxiliary_Sphere_Type\",0.0],UNIT[\"Meter\",1.0]]"; + CoordinateSystem sourceCoordinateSystem = GetCoordinateSystem(sourceWkt); + Assert.NotNull(sourceCoordinateSystem); + + string targetWkt = "PROJCS[\"TX83-NCF\",GEOGCS[\"LL83\",DATUM[\"NAD83\",SPHEROID[\"GRS1980\",6378137.000,298.25722210]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Lambert_Conformal_Conic_2SP\"],PARAMETER[\"false_easting\",1968500.000],PARAMETER[\"false_northing\",6561666.667],PARAMETER[\"central_meridian\",-98.50000000000000],PARAMETER[\"latitude_of_origin\",31.66666666666666],PARAMETER[\"standard_parallel_1\",33.96666666666667],PARAMETER[\"standard_parallel_2\",32.13333333333333],UNIT[\"Foot_US\",0.30480060960122]]"; + CoordinateSystem targetCoordinateSystem = GetCoordinateSystem(targetWkt); + Assert.NotNull(targetCoordinateSystem); + + ICoordinateTransformation transformation = GetTransformation(sourceCoordinateSystem, targetCoordinateSystem); + Assert.NotNull(transformation); + + (double X, double Y) tranformedPoint = transformation.MathTransform.Transform(-10775704.511, 3865240.329); + + Assert.Equal(2491034.95, tranformedPoint.X, 0.1); + Assert.Equal(6968468.98, tranformedPoint.Y, 0.1); + } + + /// + /// Verifies that the Popular Visualisation Pseudo Mercator projection is recognized and a transformation can be created. + /// + [Fact] + public void TestPopularVisualizationPseudoMercatorProjectionRegistry() + { + string sourceWkt = "GEOGCS[\"GCS_WGS_1984\", DATUM[\"D_WGS_1984\", SPHEROID[\"WGS_1984\",6378137.0,298.257223563]], PRIMEM[\"Greenwich\",0.0], UNIT[\"Degree\",0.0174532925199433]]"; + string targetWkt = "PROJCS[\"WGS84.PseudoMercator\",GEOGCS[\"LL84\",DATUM[\"WGS84\",SPHEROID[\"WGS84\",6378137.000,298.25722356]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Popular Visualisation Pseudo Mercator\"],PARAMETER[\"false_easting\",0.000],PARAMETER[\"false_northing\",0.000],PARAMETER[\"central_meridian\",0.00000000000000],UNIT[\"Meter\",1.00000000000000]]"; + + CoordinateSystem sourceCoordinateSystem = GetCoordinateSystem(sourceWkt); + Assert.NotNull(sourceCoordinateSystem); + + CoordinateSystem targetCoordinateSystem = GetCoordinateSystem(targetWkt); + Assert.NotNull(targetCoordinateSystem); + + ICoordinateTransformation transformation = GetTransformation(sourceCoordinateSystem, targetCoordinateSystem); + Assert.NotNull(transformation); + } + + /// + /// Verifies that the Lambert Tangential Conformal Conic projection is registered and that the transformation to Pseudo Mercator is within tolerance. + /// + [Fact] + public void TestLamberTangentialConformalConicProjectionRegistryAndTransformation() + { + string sourceWkt = "PROJCS[\"WORLD-LM-TAN\",GEOGCS[\"LL84\",DATUM[\"WGS84\",SPHEROID[\"WGS84\",6378137.000,298.25722356]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Lambert Tangential Conformal Conic Projection\"],PARAMETER[\"false_easting\",0.000],PARAMETER[\"false_northing\",0.000],PARAMETER[\"scale_factor\",1.000000000000],PARAMETER[\"central_meridian\",0.00000000000000],PARAMETER[\"latitude_of_origin\",1.00000000000000],UNIT[\"Meter\",1.00000000000000]]"; + string targetWkt = "PROJCS[\"WGS84.PseudoMercator\",GEOGCS[\"LL84\",DATUM[\"WGS84\",SPHEROID[\"WGS84\",6378137.000,298.25722356]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Popular Visualisation Pseudo Mercator\"],PARAMETER[\"false_easting\",0.000],PARAMETER[\"false_northing\",0.000],PARAMETER[\"central_meridian\",0.00000000000000],UNIT[\"Meter\",1.00000000000000]]"; + + CoordinateSystem sourceCoordinateSystem = GetCoordinateSystem(sourceWkt); + Assert.NotNull(sourceCoordinateSystem); + + CoordinateSystem targetCoordinateSystem = GetCoordinateSystem(targetWkt); + Assert.NotNull(targetCoordinateSystem); + + ICoordinateTransformation transformation = GetTransformation(sourceCoordinateSystem, targetCoordinateSystem); + Assert.NotNull(transformation); + + // Test the transformation with a known points. Tested with AutoCAD map 3D + double[] pGeo = [4101119.6855, -229063.8661]; // Nairobi, Kenya + double[] pUtm = transformation.MathTransform.Transform(pGeo); + + double[] expected = [4098998.6422, -142387.5532]; + this.AssertCoordinateWithinTolerance("LambertConicConformal2SP", expected, pUtm, 0.05); + } + + /// + /// Creates a coordinate system from WKT for test setup. + /// + /// Well-known text representation of the coordinate system. + /// Parsed coordinate system instance. + internal static CoordinateSystem GetCoordinateSystem(string wkt) + { + return CoordinateSystemTestHelpers.RequireCoordinateSystem(wkt); + } + + /// + /// Creates a transformation between source and target coordinate systems for test execution. + /// + /// Source coordinate system. + /// Target coordinate system. + /// Coordinate transformation instance. + internal static ICoordinateTransformation GetTransformation(CoordinateSystem sourceCoordinateSystem, CoordinateSystem targetCoordinateSystem) + { + CoordinateSystemServices coordinateService = CoordinateSystemTestHelpers.CreateCoordinateSystemServices(); + ICoordinateTransformation? transformation = coordinateService.CreateTransformation(sourceCoordinateSystem, targetCoordinateSystem); + return Assert.IsType(transformation, exactMatch: false); + } + + private static FittedCoordinateSystem CreateDerivedGeographicRuntimeCoordinateSystem() + { + GeographicCoordinateSystem baseCoordinateSystem = GeographicCoordinateSystem.WGS84; + return CoordinateSystemTestHelpers.CreateCoordinateSystemFactory().CreateFittedCoordinateSystem( + "Runtime derived geographic", + baseCoordinateSystem, + new AffineTransform(1, 0, 0.5, 0, 1, 1.5), + [ + new AxisInfo(baseCoordinateSystem.GetAxis(0).Name, baseCoordinateSystem.GetAxis(0).Orientation), + new AxisInfo(baseCoordinateSystem.GetAxis(1).Name, baseCoordinateSystem.GetAxis(1).Orientation), + ]); + } + + private static FittedCoordinateSystem CreateDerivedProjectedRuntimeCoordinateSystem() + { + var baseCoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(32, true); + return CoordinateSystemTestHelpers.CreateCoordinateSystemFactory().CreateFittedCoordinateSystem( + "Runtime derived projected", + baseCoordinateSystem, + new AffineTransform(1, 0, 100, 0, 1, -50), + [ + new AxisInfo(baseCoordinateSystem.GetAxis(0).Name, baseCoordinateSystem.GetAxis(0).Orientation), + new AxisInfo(baseCoordinateSystem.GetAxis(1).Name, baseCoordinateSystem.GetAxis(1).Orientation), + ]); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateTransformationFactoryNormalizationTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateTransformationFactoryNormalizationTests.cs new file mode 100644 index 00000000..848c8c73 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/CoordinateTransformationFactoryNormalizationTests.cs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using System.Reflection; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests the private operation-method normalization helper used by . +/// +public class CoordinateTransformationFactoryNormalizationTests +{ + private static readonly MethodInfo NormalizeOperationMethodNameMethod = typeof(CoordinateTransformationFactory) + .GetMethod("NormalizeOperationMethodName", BindingFlags.NonPublic | BindingFlags.Static) + ?? throw new InvalidOperationException("The CoordinateTransformationFactory.NormalizeOperationMethodName helper could not be located."); + + /// + /// Verifies that operation method names are normalized by stripping non-alphanumeric characters and lower-casing the remaining content. + /// + /// The input method name. + /// The expected normalized representation. + [Theory] + [InlineData(null, "")] + [InlineData("", "")] + [InlineData(" ", "")] + [InlineData("Coordinate Frame rotation", "coordinateframerotation")] + [InlineData("Geographic 2D offsets", "geographic2doffsets")] + [InlineData("Time-dependent Position Vector (geocentric)", "timedependentpositionvectorgeocentric")] + [InlineData("Molodensky-Badekas", "molodenskybadekas")] + public void NormalizeOperationMethodName_StripsPunctuationAndLowerCases(string? value, string expected) + { + string normalized = Assert.IsType(NormalizeOperationMethodNameMethod.Invoke(null, [value]), exactMatch: false); + + Assert.Equal(expected, normalized); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/HelmertRuntimeTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/HelmertRuntimeTests.cs new file mode 100644 index 00000000..86529dcd --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/HelmertRuntimeTests.cs @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for helmert. +/// +public class HelmertRuntimeTests +{ + /// + /// Verifies coordinate-frame Helmert vector from PROJ more_builtins.gie. + /// + [Fact] + public void HelmertCoordinateFrameMatchesMoreBuiltinsVector() + { + const string operation = "+proj=helmert +convention=coordinate_frame +x=0.67678 +y=0.65495 +z=-0.52827 +rx=-0.022742 +ry=0.012667 +rz=0.022704 +s=-0.01070"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(3565285.0d, 855949.0d, 5201383.0d)); + + Assert.InRange(Math.Abs(output[0] - 3565285.41342351d), 0d, 1e-6); + Assert.InRange(Math.Abs(output[1] - 855948.67986759d), 0d, 1e-6); + Assert.InRange(Math.Abs(output[2] - 5201382.72939791d), 0d, 1e-6); + } + + /// + /// Verifies exact-mode Helmert vector from PROJ more_builtins.gie. + /// + [Fact] + public void HelmertExactCoordinateFrameMatchesMoreBuiltinsVector() + { + const string operation = "+proj=helmert +exact +convention=coordinate_frame +x=-81.0703 +y=-89.3603 +z=-115.7526 +rx=-0.48488 +ry=-0.02436 +rz=-0.41321 +s=-0.540645"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(3494994.3012d, 1056601.9725d, 5212382.1666d)); + + Assert.InRange(Math.Abs(output[0] - 3494909.84026368d), 0d, 1e-6); + Assert.InRange(Math.Abs(output[1] - 1056506.78938633d), 0d, 1e-6); + Assert.InRange(Math.Abs(output[2] - 5212265.66699761d), 0d, 1e-6); + } + + /// + /// Verifies 4-parameter Helmert vector from PROJ more_builtins.gie. + /// + [Fact] + public void HelmertFourParameterMatchesMoreBuiltinsVector() + { + const string operation = "+proj=helmert +x=-9597.3572 +y=0.6112 +s=0.304794780637 +theta=-1.244048"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(2546506.957d, 542256.609d, 0d)); + + Assert.InRange(Math.Abs(output[0] - 766563.675d), 0d, 1e-3); + Assert.InRange(Math.Abs(output[1] - 165282.277d), 0d, 1e-3); + Assert.InRange(Math.Abs(output[2] - 0d), 0d, 1e-9); + } + + /// + /// Verifies inverse flag for static 7-parameter Helmert operations. + /// + [Fact] + public void HelmertInverseRecoversInput() + { + const string forwardOperation = "+proj=helmert +convention=coordinate_frame +x=0.67678 +y=0.65495 +z=-0.52827 +rx=-0.022742 +ry=0.012667 +rz=0.022704 +s=-0.01070"; + const string inverseOperation = "+proj=helmert +convention=coordinate_frame +x=0.67678 +y=0.65495 +z=-0.52827 +rx=-0.022742 +ry=0.012667 +rz=0.022704 +s=-0.01070 +inv"; + MathTransform forward = CreateTransform(forwardOperation); + MathTransform inverse = CreateTransform(inverseOperation); + + double[] source = CreatePoint(3565285.0d, 855949.0d, 5201383.0d); + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.InRange(Math.Abs(recovered[0] - source[0]), 0d, 1e-6); + Assert.InRange(Math.Abs(recovered[1] - source[1]), 0d, 1e-6); + Assert.InRange(Math.Abs(recovered[2] - source[2]), 0d, 1e-6); + } + + /// + /// Verifies error-path behavior for convention and obsolete transpose handling. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [InlineData("+proj=helmert +rx=1", "missing 'convention'")] + [InlineData("+proj=helmert +rx=1 +convention=foo", "invalid value for 'convention'")] + [InlineData("+proj=helmert +rx=1 +convention=1", "invalid value for 'convention'")] + [InlineData("+proj=helmert +towgs84=1,2,3,4,5,6,7 +convention=coordinate_frame", "towgs84 should only be used")] + [InlineData("+proj=helmert +transpose", "'transpose' argument is no longer valid")] + public void HelmertCreationFailsForInvalidConventionOrLegacyTranspose(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.Ordinal); + } + + /// + /// Verifies kinematic position-vector Helmert vectors from PROJ more_builtins.gie. + /// + /// Observation epoch carried in the 4th ordinate. + /// Expected X output. + /// Expected Y output. + /// Expected Z output. + [Theory] + [InlineData(2017.0d, 3370658.18890d, 711877.42370d, 5349787.12430d)] + [InlineData(2018.0d, 3370658.18087d, 711877.42750d, 5349787.12648d)] + public void HelmertKinematicPositionVectorMatchesMoreBuiltinsVectors( + double observationEpoch, + double expectedX, + double expectedY, + double expectedZ) + { + const string operation = "+proj=helmert +convention=position_vector +x=0.0127 +dx=-0.0029 +rx=-0.00039 +drx=-0.00011 +y=0.0065 +dy=-0.0002 +ry=0.00080 +dry=-0.00019 +z=-0.0209 +dz=-0.0006 +rz=-0.00114 +drz=0.00007 +s=0.00195 +ds=0.00001 +t_epoch=1988.0"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(3370658.37800d, 711877.31400d, 5349787.08600d, observationEpoch)); + + Assert.Equal(4, output.Length); + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, 1e-4d); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, 1e-4d); + Assert.InRange(Math.Abs(output[2] - expectedZ), 0d, 1e-4d); + Assert.InRange(Math.Abs(output[3] - observationEpoch), 0d, 1e-12d); + } + + /// + /// Verifies kinematic coordinate-frame Helmert vector from PROJ GDA.gie. + /// + [Fact] + public void HelmertKinematicCoordinateFrameMatchesGdaVector() + { + const string operation = "+proj=helmert +exact +convention=coordinate_frame +x=0 +rx=0 +dx=0 +drx=0.00150379 +y=0 +ry=0 +dy=0 +dry=0.00118346 +z=0 +rz=0 +dz=0 +drz=0.00120716 +s=0 +ds=0 +t_epoch=2020.0"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(-4052052.6588d, 4212835.9938d, -2545104.6946d, 2018.0d)); + + Assert.Equal(4, output.Length); + Assert.InRange(Math.Abs(output[0] - (-4052052.7373d)), 0d, 4e-5d); + Assert.InRange(Math.Abs(output[1] - 4212835.9835d), 0d, 4e-5d); + Assert.InRange(Math.Abs(output[2] - (-2545104.5867d)), 0d, 4e-5d); + Assert.InRange(Math.Abs(output[3] - 2018.0d), 0d, 1e-12d); + } + + /// + /// Verifies kinematic inverse with an explicit observation epoch. + /// + [Fact] + public void HelmertKinematicInverseRecoversInput() + { + const string forwardOperation = "+proj=helmert +convention=position_vector +x=0.0127 +dx=-0.0029 +rx=-0.00039 +drx=-0.00011 +y=0.0065 +dy=-0.0002 +ry=0.00080 +dry=-0.00019 +z=-0.0209 +dz=-0.0006 +rz=-0.00114 +drz=0.00007 +s=0.00195 +ds=0.00001 +t_epoch=1988.0"; + const string inverseOperation = "+proj=helmert +convention=position_vector +x=0.0127 +dx=-0.0029 +rx=-0.00039 +drx=-0.00011 +y=0.0065 +dy=-0.0002 +ry=0.00080 +dry=-0.00019 +z=-0.0209 +dz=-0.0006 +rz=-0.00114 +drz=0.00007 +s=0.00195 +ds=0.00001 +t_epoch=1988.0 +inv"; + MathTransform forward = CreateTransform(forwardOperation); + MathTransform inverse = CreateTransform(inverseOperation); + + double[] source = CreatePoint(3370658.37800d, 711877.31400d, 5349787.08600d, 2018.0d); + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.Equal(4, recovered.Length); + Assert.InRange(Math.Abs(recovered[0] - source[0]), 0d, 1e-6d); + Assert.InRange(Math.Abs(recovered[1] - source[1]), 0d, 1e-6d); + Assert.InRange(Math.Abs(recovered[2] - source[2]), 0d, 1e-6d); + Assert.InRange(Math.Abs(recovered[3] - source[3]), 0d, 1e-12d); + } + + /// + /// Verifies that inverse 4-parameter Helmert rejects zero scale at observation epoch. + /// + [Fact] + public void HelmertKinematicFourParameterInverseWithZeroScaleAtEpochThrows() + { + const string inverseOperation = "+proj=helmert +x=5 +y=-3 +theta=1 +s=1 +ds=-1 +t_epoch=2000 +inv"; + MathTransform inverse = CreateTransform(inverseOperation); + + ArgumentException exception = Assert.Throws(() => inverse.Transform(CreatePoint(100d, 200d, 0d, 2001d))); + Assert.Contains("scale", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies kinematic 4D calls do not leak observation-epoch state into 3D transforms. + /// + [Fact] + public void HelmertKinematicThreeOrdinateTransformIsStableAfterEpochSpecificCall() + { + const string operation = "+proj=helmert +x=0 +y=0 +z=0 +dx=1000 +dy=0 +dz=0 +t_epoch=0"; + MathTransform transform = CreateTransform(operation); + + double[] first3D = transform.Transform(CreatePoint(10d, 20d, 30d)); + double[] epochSpecific = transform.Transform(CreatePoint(10d, 20d, 30d, 2d)); + double[] second3D = transform.Transform(CreatePoint(10d, 20d, 30d)); + + Assert.InRange(Math.Abs(first3D[0] - 10d), 0d, 1e-9d); + Assert.InRange(Math.Abs(second3D[0] - 10d), 0d, 1e-9d); + Assert.InRange(Math.Abs(epochSpecific[0] - 2010d), 0d, 1e-9d); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; + + private static double[] CreatePoint(double x, double y, double z, double t) => [x, y, z, t]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/HornerRuntimeTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/HornerRuntimeTests.cs new file mode 100644 index 00000000..5e3746f0 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/HornerRuntimeTests.cs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for horner. +/// +public class HornerRuntimeTests +{ + private const string Tc32Utm32Operation = "+proj=horner +ellps=intl +range=500000 +fwd_origin=877605.269066,6125810.306769 +inv_origin=877605.760036,6125811.281773 +deg=4 +fwd_v=6.1258112678e+06,9.9999971567e-01,1.5372750011e-10,5.9300860915e-15,2.2609497633e-19,4.3188227445e-05,2.8225130416e-10,7.8740007114e-16,-1.7453997279e-19,1.6877465415e-10,-1.1234649773e-14,-1.7042333358e-18,-7.9303467953e-15,-5.2906832535e-19,3.9984284847e-19 +fwd_u=8.7760574982e+05,9.9999752475e-01,2.8817299305e-10,5.5641310680e-15,-1.5544700949e-18,-4.1357045890e-05,4.2106213519e-11,2.8525551629e-14,-1.9107771273e-18,3.3615590093e-10,2.4380247154e-14,-2.0241230315e-18,1.2429019719e-15,5.3886155968e-19,-1.0167505000e-18 +inv_v=6.1258103208e+06,1.0000002826e+00,-1.5372762184e-10,-5.9304261011e-15,-2.2612705361e-19,-4.3188331419e-05,-2.8225549995e-10,-7.8529116371e-16,1.7476576773e-19,-1.6875687989e-10,1.1236475299e-14,1.7042518057e-18,7.9300735257e-15,5.2881862699e-19,-3.9990736798e-19 +inv_u=8.7760527928e+05,1.0000024735e+00,-2.8817540032e-10,-5.5627059451e-15,1.5543637570e-18,4.1357152105e-05,-4.2114813612e-11,-2.8523713454e-14,1.9109017837e-18,-3.3616407783e-10,-2.4382678126e-14,2.0245020199e-18,-1.2441377565e-15,-5.3885232238e-19,1.0167203661e-18"; + private const string SbUtm32Operation = "+proj=horner +ellps=intl +range=500000 +tolerance=0.0005 +fwd_origin=4.94690026817276e+05,6.13342113183056e+06 +inv_origin=6.19480258923588e+05,6.13258568148837e+06 +deg=3 +fwd_c=6.13258562111350e+06,6.19480105709997e+05,9.99378966275206e-01,-2.82153291753490e-02,-2.27089979140026e-10,-1.77019590701470e-09,1.08522286274070e-14,2.11430298751604e-15 +inv_c=6.13342118787027e+06,4.94690181709311e+05,9.99824464710368e-01,2.82279070814774e-02,7.66123542220864e-11,1.78425334628927e-09,-1.05584823306400e-14,-3.32554258683744e-15"; + private const string Tc32Utm32ForwardOnlyOperation = "+proj=horner +ellps=intl +range=10000000 +fwd_origin=877605.269066,6125810.306769 +deg=4 +fwd_v=6.1258112678e+06,9.9999971567e-01,1.5372750011e-10,5.9300860915e-15,2.2609497633e-19,4.3188227445e-05,2.8225130416e-10,7.8740007114e-16,-1.7453997279e-19,1.6877465415e-10,-1.1234649773e-14,-1.7042333358e-18,-7.9303467953e-15,-5.2906832535e-19,3.9984284847e-19 +fwd_u=8.7760574982e+05,9.9999752475e-01,2.8817299305e-10,5.5641310680e-15,-1.5544700949e-18,-4.1357045890e-05,4.2106213519e-11,2.8525551629e-14,-1.9107771273e-18,3.3615590093e-10,2.4380247154e-14,-2.0241230315e-18,1.2429019719e-15,5.3886155968e-19,-1.0167505000e-18"; + private const string HattToGgrsOperation = "+proj=horner +ellps=bessel +fwd_origin=0.0,0.0 +deg=2 +range=10000000 +fwd_u=370552.68,0.9997155,-1.08e-09,0.0175123,2.04e-09,1.63e-09 +fwd_v=4511927.23,0.9996979,5.60e-10,-0.0174755,-1.65e-09,-6.50e-10"; + private const string SbUtm32ForwardOnlyOperation = "+proj=horner +ellps=intl +range=10000000 +fwd_origin=4.94690026817276e+05,6.13342113183056e+06 +deg=3 +fwd_c=6.13258562111350e+06,6.19480105709997e+05,9.99378966275206e-01,-2.82153291753490e-02,-2.27089979140026e-10,-1.77019590701470e-09,1.08522286274070e-14,2.11430298751604e-15"; + + /// + /// Verifies real-coefficient forward and inverse vectors from PROJ self-tests. + /// + [Fact] + public void HornerRealWithExplicitInverseMatchesSelfTestVectors() + { + double[] source = CreatePoint(878354.8539d, 6125305.4245d, 0d); + MathTransform transform = CreateTransform(Tc32Utm32Operation); + double[] forward = transform.Transform(source); + Assert.False(double.IsNaN(forward[0]) || double.IsInfinity(forward[0])); + Assert.False(double.IsNaN(forward[1]) || double.IsInfinity(forward[1])); + + MathTransform inverse = CreateTransform($"{Tc32Utm32Operation} +inv"); + double[] backward = inverse.Transform(forward); + double planarDistance = Math.Sqrt( + ((backward[0] - source[0]) * (backward[0] - source[0])) + + ((backward[1] - source[1]) * (backward[1] - source[1]))); + Assert.InRange(planarDistance, 0d, 1e-2); + } + + /// + /// Verifies complex-coefficient forward and inverse vectors from PROJ self-tests. + /// + [Fact] + public void HornerComplexWithExplicitInverseMatchesSelfTestVectors() + { + MathTransform transform = CreateTransform(SbUtm32Operation); + double[] forward = transform.Transform(CreatePoint(495136.8544d, 6130821.2945d, 0d)); + Assert.InRange(Math.Abs(forward[0] - 620000d), 0d, 1e-3); + Assert.InRange(Math.Abs(forward[1] - 6130000d), 0d, 1e-3); + + MathTransform inverse = CreateTransform($"{SbUtm32Operation} +inv"); + double[] backward = inverse.Transform(forward); + Assert.InRange(Math.Abs(backward[0] - 495136.8544d), 0d, 1e-3); + Assert.InRange(Math.Abs(backward[1] - 6130821.2945d), 0d, 1e-3); + } + + /// + /// Verifies iterative inverse behavior when only forward real coefficients exist. + /// + /// Horner operation text. + /// Source easting/longitude component. + /// Source northing/latitude component. + /// Maximum tolerated absolute coordinate error after roundtrip. + [Theory] + [InlineData(Tc32Utm32ForwardOnlyOperation, 878354.8539d, 6125305.4245d, 1e-2)] + [InlineData(HattToGgrsOperation, -10157.95d, -21121.093d, 1e-2)] + public void HornerRealForwardOnlyUsesIterativeInverse(string operation, double sourceX, double sourceY, double tolerance) + { + MathTransform forward = CreateTransform(operation); + double[] projected = forward.Transform(CreatePoint(sourceX, sourceY, 0d)); + + MathTransform inverse = CreateTransform($"{operation} +inv"); + double[] recovered = inverse.Transform(projected); + Assert.InRange(Math.Abs(recovered[0] - sourceX), 0d, tolerance); + Assert.InRange(Math.Abs(recovered[1] - sourceY), 0d, tolerance); + } + + /// + /// Verifies iterative inverse behavior when only forward complex coefficients exist. + /// + [Fact] + public void HornerComplexForwardOnlyUsesIterativeInverse() + { + MathTransform forward = CreateTransform(SbUtm32ForwardOnlyOperation); + double[] projected = forward.Transform(CreatePoint(495136.8544d, 6130821.2945d, 0d)); + Assert.InRange(Math.Abs(projected[0] - 620000d), 0d, 1e-3); + Assert.InRange(Math.Abs(projected[1] - 6130000d), 0d, 1e-3); + + MathTransform inverse = CreateTransform($"{SbUtm32ForwardOnlyOperation} +inv"); + double[] recovered = inverse.Transform(projected); + Assert.InRange(Math.Abs(recovered[0] - 495136.8544d), 0d, 1e-2); + Assert.InRange(Math.Abs(recovered[1] - 6130821.2945d), 0d, 1e-2); + } + + /// + /// Verifies key argument validation paths for horner setup. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [InlineData("+proj=horner +fwd_origin=0,0 +fwd_u=0,1,0 +fwd_v=0,1,0", "Must specify polynomial degree")] + [InlineData("+proj=horner +deg=1 +fwd_u=0,1,0 +fwd_v=0,1,0", "missing fwd_origin")] + [InlineData("+proj=horner +deg=1 +fwd_origin=0,0 +fwd_u=0,1 +fwd_v=0,1,0", "Malformed polynomium set fwd_u")] + [InlineData("+proj=horner +deg=1 +fwd_origin=0,0 +fwd_u=0,1,0 +fwd_v=0,1,0 +inv_u=0,1,0 +inv_v=0,1,0", "missing inv_origin")] + [InlineData("+proj=horner +deg=1 +fwd_origin=0,0 +fwd_u=0,1,0 +fwd_v=0,1,0 +range=-1", "Invalid value for +range")] + public void HornerCreationFailsForInvalidArguments(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.Ordinal); + } + + /// + /// Verifies out-of-range rejection behavior. + /// + [Fact] + public void HornerThrowsForCoordinatesOutsideConfiguredRange() + { + MathTransform transform = CreateTransform("+proj=horner +deg=1 +range=10 +fwd_origin=0,0 +fwd_u=0,1,0 +fwd_v=0,0,1"); + InvalidOperationException exception = Assert.Throws(() => transform.Transform(CreatePoint(0d, 11d, 0d))); + Assert.Contains("outside horner operation range", exception.Message, StringComparison.Ordinal); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/IdentityMathTransformTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/IdentityMathTransformTests.cs new file mode 100644 index 00000000..90ed848f --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/IdentityMathTransformTests.cs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies mutation-sensitive behavior of . +/// +public class IdentityMathTransformTests +{ + /// + /// Verifies that dimensions below 2 are promoted to 2. + /// + [Fact] + public void CtorWithDimensionLowerThanTwoPromotesToTwo() + { + var transform = new IdentityMathTransform(1); + + Assert.Equal(2, transform.DimSource); + Assert.Equal(2, transform.DimTarget); + } + + /// + /// Verifies that dimensions above 2 are preserved. + /// + [Fact] + public void CtorWithDimensionGreaterThanTwoPreservesRequestedDimension() + { + var transform = new IdentityMathTransform(3); + double[] output = transform.Transform([12d, 34d, 56d]); + + Assert.Equal(3, transform.DimSource); + Assert.Equal(3, transform.DimTarget); + Assert.Equal(3, output.Length); + Assert.Equal(56d, output[2], 12); + } + + /// + /// Verifies that the generated WKT carries the configured dimension. + /// + [Fact] + public void WktContainsConfiguredDimension() + { + var transform = new IdentityMathTransform(4); + + Assert.Equal("PARAM_MT[\"Identity\", PARAMETER[\"dimension\", 4]]", transform.WKT); + } + + /// + /// Verifies that the WKT node output matches the canonical identity WKT and that the string property delegates to it. + /// + [Fact] + public void ToWktNode_ProducesCanonicalIdentityWkt() + { + var transform = new IdentityMathTransform(4); + string nodeWkt = transform.ToWktNode().ToString(); + + Assert.Equal("PARAM_MT[\"Identity\", PARAMETER[\"dimension\", 4]]", nodeWkt); + Assert.Equal(nodeWkt, transform.WKT); + } + + /// + /// Verifies that the transform reports identity semantics. + /// + [Fact] + public void IdentityReturnsTrue() + { + var transform = new IdentityMathTransform(4); + + Assert.True(transform.Identity()); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/MathTransformDerivativeTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/MathTransformDerivativeTests.cs new file mode 100644 index 00000000..72748e42 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/MathTransformDerivativeTests.cs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies the default numerical derivative implementation for representative math transform types. +/// +public class MathTransformDerivativeTests +{ + /// + /// Gets representative projection operations used to validate local derivative linearization. + /// + public static TheoryData ProjectionDerivativeCases => + new() + { + { "+proj=merc +ellps=WGS84", 10d, 45d }, + { "+proj=tmerc +ellps=WGS84 +lat_0=0 +lon_0=9 +k_0=0.9996 +x_0=500000 +y_0=0", 10d, 45d }, + { "+proj=utm +ellps=GRS80 +zone=32", 10d, 55d }, + { "+proj=lcc +lon_0=3 +lat_0=46.5 +lat_1=44 +lat_2=49 +x_0=700000 +y_0=6600000 +ellps=GRS80", 3d, 47d }, + { "+proj=aea +ellps=GRS80 +lat_1=43 +lat_2=62 +lat_0=30 +lon_0=10 +x_0=0 +y_0=0", 12d, 50d }, + { "+proj=laea +ellps=GRS80 +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000", 12d, 50d }, + }; + + /// + /// Verifies that affine derivatives return the non-translating linear matrix with the expected dimensions. + /// + [Fact] + public void Derivative_OnAffineTransform_ReturnsLinearJacobian() + { + AffineTransform transform = new(2d, 3d, 5d, 7d, 11d, 13d); + + double[,] derivative = transform.Derivative([10d, 20d]); + + Assert.Equal(2, derivative.GetLength(0)); + Assert.Equal(2, derivative.GetLength(1)); + AssertInTolerance(derivative[0, 0], 2d, 1e-6d); + AssertInTolerance(derivative[0, 1], 3d, 1e-6d); + AssertInTolerance(derivative[1, 0], 7d, 1e-6d); + AssertInTolerance(derivative[1, 1], 11d, 1e-6d); + } + + /// + /// Verifies that EPSG:4326 to EPSG:3857 derivatives match the analytic Web Mercator Jacobian. + /// + [Fact] + public void Derivative_OnWebMercatorTransform_MatchesAnalyticJacobian() + { + CoordinateSystemServices services = new(); + ICoordinateTransformation transformation = Assert.IsType(services.CreateTransformation(4326, 3857), exactMatch: false); + double[,] derivative = transformation.MathTransform.Derivative([10d, 10d]); + double metresPerDegree = 6378137d * Math.PI / 180d; + double latitudeRadians = 10d * Math.PI / 180d; + double expectedLatitudeScale = metresPerDegree / Math.Cos(latitudeRadians); + + Assert.Equal(2, derivative.GetLength(0)); + Assert.Equal(2, derivative.GetLength(1)); + AssertInTolerance(derivative[0, 0], metresPerDegree, 1e-2d); + AssertInTolerance(derivative[0, 1], 0d, 1e-6d); + AssertInTolerance(derivative[1, 0], 0d, 1e-6d); + AssertInTolerance(derivative[1, 1], expectedLatitudeScale, 1e-2d); + } + + /// + /// Verifies representative projection derivatives locally linearize the transform around the sample point. + /// + /// The projection operation string. + /// The sample longitude in degrees. + /// The sample latitude in degrees. + [Theory] + [MemberData(nameof(ProjectionDerivativeCases))] + public void Derivative_OnProjectionTransforms_LinearizesLocalOffsets(string operation, double longitude, double latitude) + { + MathTransform transform = CreateTransform(operation); + double[] point = [longitude, latitude]; + double[] baseValue = transform.Transform(point); + double[,] derivative = transform.Derivative(point); + + Assert.Equal(baseValue.Length, derivative.GetLength(0)); + Assert.Equal(point.Length, derivative.GetLength(1)); + + AssertLocalLinearization([1e-6d, 0d]); + AssertLocalLinearization([0d, 1e-6d]); + AssertLocalLinearization([1e-6d, -2e-6d]); + + void AssertLocalLinearization(double[] delta) + { + double[] displacedPoint = [point[0] + delta[0], point[1] + delta[1]]; + double[] displacedValue = transform.Transform(displacedPoint); + + for (int targetIndex = 0; targetIndex < displacedValue.Length; targetIndex++) + { + double predictedOffset = 0d; + for (int sourceIndex = 0; sourceIndex < delta.Length; sourceIndex++) + { + predictedOffset += derivative[targetIndex, sourceIndex] * delta[sourceIndex]; + } + + double actualOffset = displacedValue[targetIndex] - baseValue[targetIndex]; + AssertInTolerance(actualOffset, predictedOffset, 5e-3d); + } + } + } + + private static void AssertInTolerance(double actual, double expected, double tolerance) + { + Assert.InRange(Math.Abs(actual - expected), 0d, tolerance); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/MathTransformInvertibilityTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/MathTransformInvertibilityTests.cs new file mode 100644 index 00000000..b9955654 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/MathTransformInvertibilityTests.cs @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies invertibility discovery through . +/// +public class MathTransformInvertibilityTests +{ + /// + /// Verifies Mercator projections report inverse support. + /// + [Fact] + public void MercatorProjectionReportsInvertible() + { + MapProjection projection = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("mercator", CreateMercatorParameters())); + + Assert.True(projection.IsInvertible); + Assert.IsAssignableFrom(projection.Inverse()); + } + + /// + /// Verifies that Mercator WKT formatting preserves the expected forward and inverse WKT shape. + /// + [Fact] + public void MercatorProjectionWkt_MatchesForwardAndInverseReferenceStrings() + { + MapProjection projection = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("mercator", CreateMercatorParameters())); + MapProjection inverse = Assert.IsAssignableFrom(projection.Inverse()); + + Assert.Equal(CreateExpectedProjectionWkt(projection), projection.WKT); + Assert.Equal(CreateExpectedProjectionWkt(inverse), inverse.WKT); + } + + /// + /// Verifies that Mercator XML formatting preserves the expected forward and inverse XML shape. + /// + [Fact] + public void MercatorProjectionXml_MatchesForwardAndInverseReferenceStrings() + { + MapProjection projection = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("mercator", CreateMercatorParameters())); + MapProjection inverse = Assert.IsAssignableFrom(projection.Inverse()); + + Assert.Equal(CreateExpectedProjectionXml(projection), projection.XML); + Assert.Equal(CreateExpectedProjectionXml(inverse), inverse.XML); + Assert.True(XNode.DeepEquals(XElement.Parse(CreateExpectedProjectionXml(projection)), projection.ToXml())); + Assert.True(XNode.DeepEquals(XElement.Parse(CreateExpectedProjectionXml(inverse)), inverse.ToXml())); + } + + /// + /// Verifies forward-only Airy projections report missing inverse support without requiring capability probes by exception. + /// + [Fact] + public void AiryProjectionReportsNotInvertibleAndInverseThrows() + { + MapProjection projection = Assert.IsAssignableFrom( + ProjectionsRegistry.CreateProjection("airy", CreateAiryParameters())); + + Assert.False(projection.IsInvertible); + Assert.Throws(() => projection.Inverse()); + } + + /// + /// Verifies non-projection math transforms keep reporting inverse support. + /// + [Fact] + public void HelmertTransformReportsInvertible() + { + MathTransform transform = CreateTransform("+proj=helmert +convention=coordinate_frame +x=0.67678 +y=0.65495 +z=-0.52827 +rx=-0.022742 +ry=0.012667 +rz=0.022704 +s=-0.01070"); + + Assert.True(transform.IsInvertible); + } + + private static List CreateMercatorParameters() + { + return + [ + new("semi_major", Ellipsoid.WGS84.SemiMajorAxis), + new("semi_minor", Ellipsoid.WGS84.SemiMinorAxis), + new("central_meridian", 0d), + new("latitude_of_origin", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + new("unit", 1d), + ]; + } + + private static List CreateAiryParameters() + { + return + [ + new("semi_major", 6400000d), + new("semi_minor", 6400000d), + new("central_meridian", 0d), + new("latitude_of_origin", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + new("unit", 1d), + ]; + } + + private static string CreateExpectedProjectionWkt(MapProjection projection) + { + string parameterizedWkt = "PARAM_MT[\"" + projection.Name + "\"" + + string.Concat(Enumerable.Range(0, projection.NumParameters).Select(i => ", " + projection.GetParameter(i).WKT)) + + "]"; + return projection.IsInverse + ? "INVERSE_MT[" + parameterizedWkt + "]" + : parameterizedWkt; + } + + private static string CreateExpectedProjectionXml(MapProjection projection) + { + string transformElementName = projection.IsInverse + ? "CT_InverseTransform" + : "CT_ParameterizedMathTransform"; + return "<" + transformElementName + " Name=\"" + projection.ClassName + "\">" + + string.Concat(Enumerable.Range(0, projection.NumParameters).Select(i => projection.GetParameter(i).ToXml().ToString(SaveOptions.DisableFormatting))) + + ""; + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/ObTranRuntimeTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/ObTranRuntimeTests.cs new file mode 100644 index 00000000..01a271b4 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/ObTranRuntimeTests.cs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates runtime pipeline support for ob_tran. +/// +public class ObTranRuntimeTests +{ + /// + /// Verifies builtins vector parity for spherical ob_tran with latlong child projection. + /// + /// PROJ operation string. + /// Input x. + /// Input y. + /// Expected x. + /// Expected y. + [Theory] + [InlineData("+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180", 2d, 1d, -2.685687214d, 1.237430235d)] + [InlineData("+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180", 2d, -1d, -2.695406975d, 1.202683395d)] + [InlineData("+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180", -2d, 1d, -2.899366393d, 1.237430235d)] + [InlineData("+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180", -2d, -1d, -2.889646631d, 1.202683395d)] + public void ObTranLatLonMatchesBuiltinsForward( + string operation, + double inputX, + double inputY, + double expectedX, + double expectedY) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + double[] projected = Assert.IsType(transform, exactMatch: false).Transform([inputX, inputY]); + Assert.InRange(Math.Abs(projected[0] - expectedX), 0d, 1e-9); + Assert.InRange(Math.Abs(projected[1] - expectedY), 0d, 1e-9); + } + + /// + /// Verifies builtins vector parity for ob_tran with mollweide child projection. + /// + [Fact] + public void ObTranMollMatchesMoreBuiltinsForward() + { + const string operation = "+proj=ob_tran +o_proj=moll +R=6378137.0 +o_lon_p=0 +o_lat_p=0 +lon_0=180"; + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + double[] projected = Assert.IsType(transform, exactMatch: false).Transform([10d, 20d]); + Assert.InRange(Math.Abs(projected[0] - (-1384841.18787d)), 0d, 1e-5); + Assert.InRange(Math.Abs(projected[1] - 7581707.88240d), 0d, 1e-5); + } + + /// + /// Verifies inverse runtime behavior for latlong child projection. + /// + /// Input x. + /// Input y. + /// Expected x. + /// Expected y. + [Theory] + [InlineData(200d, 100d, 121.551874841d, -2.536100157d)] + [InlineData(200d, -100d, 63.261184340d, 17.585319579d)] + [InlineData(-200d, 100d, -141.100733224d, 26.091712305d)] + [InlineData(-200d, -100d, -65.862385599d, 51.830295078d)] + public void ObTranLatLonMatchesBuiltinsInverse( + double inputX, + double inputY, + double expectedX, + double expectedY) + { + const string operation = "+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180 +inv"; + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + double[] projected = Assert.IsType(transform, exactMatch: false).Transform([inputX, inputY]); + Assert.InRange(Math.Abs(projected[0] - expectedX), 0d, 1e-6); + Assert.InRange(Math.Abs(projected[1] - expectedY), 0d, 1e-6); + } + + /// + /// Verifies malformed nested ob_tran setup returns runtime validation error. + /// + [Fact] + public void NestedObTranIsRejected() + { + const string operation = "+proj=ob_tran +R=6400000 +o_proj=ob_tran"; + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains("Nested ob_tran", skipReason, StringComparison.Ordinal); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/OperationCatalogProviderTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/OperationCatalogProviderTests.cs new file mode 100644 index 00000000..bcb63af8 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/OperationCatalogProviderTests.cs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System.Linq; +using ProjNet.Data; +using Xunit; + +/// +/// Tests for the coordinate operation catalog provider. +/// +public class OperationCatalogProviderTests +{ + /// + /// Verifies that the managed provider loads the generated operation catalog with a sufficient number of well-formed definitions. + /// + [Fact] + public void ManagedOperationProviderLoadsGeneratedOperationCatalog() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var definitions = provider.GetDefinitions().ToList(); + + Assert.True(definitions.Count > 2500); + Assert.Contains(definitions, operation => operation.SourceSrid > 0 && operation.TargetSrid > 0); + Assert.Contains(definitions, operation => !string.IsNullOrWhiteSpace(operation.MethodName)); + Assert.Contains(definitions, operation => !string.IsNullOrWhiteSpace(operation.ParameterFileName)); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/OperationResolutionEngineTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/OperationResolutionEngineTests.cs new file mode 100644 index 00000000..0ef6d99b --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/OperationResolutionEngineTests.cs @@ -0,0 +1,677 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using ProjNet.Data.Generated; +using Xunit; + +/// +/// Tests for the coordinate operation resolution engine and transformation factory. +/// +[Collection(GlobalEnvironmentTestIsolation.Name)] +public class OperationResolutionEngineTests +{ + private static readonly double[] GeographicSamplePoint = [13.1234d, 52.9876d]; + private static readonly double[] GeographicOffsetSamplePoint = [10d, 20d]; + private static readonly double[] TimeDependentGeocentricSamplePoint = [3657660.66d, 255768.55d, 5201382.11d, 2017d]; + private static readonly double[] UtmSamplePoint = [500000d, 4649776.22482d]; + + private readonly CoordinateTransformationFactory coordinateTransformationFactory = new(); + private readonly CoordinateSystemFactory coordinateSystemFactory = new(); + + /// + /// Verifies that creating a transformation between identical projected coordinate systems produces an identity transform. + /// + [Fact] + public void CreateFromCoordinateSystemsWithSameProjectedCoordinateSystemUsesIdentityTransform() + { + var source = ProjectedCoordinateSystem.WGS84_UTM(32, true); + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, source); + double[] output = transformation.MathTransform.Transform(UtmSamplePoint); + + Assert.True(transformation.MathTransform.Identity()); + Assert.Equal(500000d, output[0], 12); + Assert.Equal(4649776.22482d, output[1], 12); + } + + /// + /// Verifies that creating a transformation between equivalent geographic coordinate systems parsed from WKT produces an identity transform. + /// + [Fact] + public void CreateFromCoordinateSystemsWithEquivalentGeographicCoordinateSystemsUsesIdentityTransform() + { + GeographicCoordinateSystem source = GeographicCoordinateSystem.WGS84; + GeographicCoordinateSystem target = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, source.WKT); + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] output = transformation.MathTransform.Transform(GeographicSamplePoint); + + Assert.True(transformation.MathTransform.Identity()); + Assert.Equal(13.1234d, output[0], 12); + Assert.Equal(52.9876d, output[1], 12); + } + + /// + /// Verifies that creating a transformation rejects a null source coordinate system. + /// + [Fact] + public void CreateFromCoordinateSystemsWithNullSourceThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws( + () => this.coordinateTransformationFactory.CreateFromCoordinateSystems(null!, GeographicCoordinateSystem.WGS84)); + + Assert.Equal("source", exception.ParamName); + } + + /// + /// Verifies that creating a transformation rejects a null target coordinate system. + /// + [Fact] + public void CreateFromCoordinateSystemsWithNullTargetThrowsArgumentNullException() + { + ArgumentNullException exception = Assert.Throws( + () => this.coordinateTransformationFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, null!)); + + Assert.Equal("target", exception.ParamName); + } + + /// + /// Verifies that when projected coordinate systems carry EPSG authority codes, the engine selects the highest-ranked catalogued operation over the fallback path. + /// + [Fact] + public void CreateFromCoordinateSystemsWithProjectedPairHavingDirectMetadataPrefersMetadataCandidate() + { + ProjectedCoordinateSystem source = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(32, true), "EPSG", 28992); + ProjectedCoordinateSystem target = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(33, true), "EPSG", 23031); + + ICoordinateTransformation metadataTransformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] metadataOutput = metadataTransformation.MathTransform.Transform(UtmSamplePoint); + + ProjectedCoordinateSystem fallbackSource = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(32, true), string.Empty, -1); + ProjectedCoordinateSystem fallbackTarget = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(33, true), string.Empty, -1); + ICoordinateTransformation fallbackTransformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(fallbackSource, fallbackTarget); + double[] fallbackOutput = fallbackTransformation.MathTransform.Transform(UtmSamplePoint); + + Assert.Equal("EPSG", metadataTransformation.Authority); + Assert.Equal(1044, metadataTransformation.AuthorityCode); + Assert.Equal(fallbackOutput[0], metadataOutput[0], 9); + Assert.Equal(fallbackOutput[1], metadataOutput[1], 9); + + ConcatenatedTransform concatenated = Assert.IsType(metadataTransformation.MathTransform); + Assert.Equal(2, concatenated.CoordinateTransformationList.Count); + Assert.DoesNotContain(concatenated.CoordinateTransformationList, ContainsGeographicOrGeocentricCoordinateSystem); + } + + /// + /// Verifies that projected coordinate systems without EPSG authority codes produce a direct projected-to-projected transformation without intermediate geographic steps. + /// + [Fact] + public void CreateFromCoordinateSystemsWithProjectedFallbackPairUsesDirectProj2ProjCorePath() + { + ProjectedCoordinateSystem source = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(32, true), string.Empty, -1); + ProjectedCoordinateSystem target = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(33, true), string.Empty, -1); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + + ConcatenatedTransform concatenated = Assert.IsType(transformation.MathTransform); + Assert.Equal(2, concatenated.CoordinateTransformationList.Count); + Assert.DoesNotContain(concatenated.CoordinateTransformationList, ContainsGeographicOrGeocentricCoordinateSystem); + } + + /// + /// Verifies that projected coordinate systems without an EPSG authority fall back to a transformation with an empty authority and a code of -1. + /// + [Fact] + public void CreateFromCoordinateSystemsWithProjectedPairWithoutEpsgAuthorityUsesLegacyFallback() + { + ProjectedCoordinateSystem source = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(32, true), string.Empty, -1); + ProjectedCoordinateSystem target = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(33, true), string.Empty, -1); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + + Assert.Equal(string.Empty, transformation.Authority); + Assert.Equal(-1, transformation.AuthorityCode); + } + + /// + /// Verifies that operation ranking prefers the smaller area-of-use candidate when all other ranking inputs are equal. + /// + [Fact] + public void OperationDefinitionComparerWithSameAccuracyGridAndMethodPrefersSmallerAreaOfUse() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + CoordinateOperationDefinition largeAreaCandidate = provider.GetDefinitions().Single(definition => definition.OperationCode == 10392); + CoordinateOperationDefinition smallAreaCandidate = provider.GetDefinitions().Single(definition => definition.OperationCode == 10393); + + Assert.Equal(largeAreaCandidate.SourceSrid, smallAreaCandidate.SourceSrid); + Assert.Equal(largeAreaCandidate.TargetSrid, smallAreaCandidate.TargetSrid); + Assert.Equal(string.IsNullOrWhiteSpace(largeAreaCandidate.ParameterFileName), string.IsNullOrWhiteSpace(smallAreaCandidate.ParameterFileName)); + Assert.Equal(string.IsNullOrWhiteSpace(largeAreaCandidate.MethodName), string.IsNullOrWhiteSpace(smallAreaCandidate.MethodName)); + Assert.Equal( + largeAreaCandidate.Accuracy > 0d ? largeAreaCandidate.Accuracy : double.MaxValue, + smallAreaCandidate.Accuracy > 0d ? smallAreaCandidate.Accuracy : double.MaxValue); + + MethodInfo getCoverageMethod = typeof(CoordinateOperationDefinition).GetMethod("GetApproximateAreaOfUseCoverage", BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("CoordinateOperationDefinition.GetApproximateAreaOfUseCoverage is required for area-aware ranking."); + double largeAreaCoverage = Assert.IsType(getCoverageMethod.Invoke(largeAreaCandidate, [])); + double smallAreaCoverage = Assert.IsType(getCoverageMethod.Invoke(smallAreaCandidate, [])); + Assert.True(largeAreaCoverage > smallAreaCoverage); + + Type comparerType = typeof(CoordinateTransformationFactory).GetNestedType("OperationDefinitionComparer", BindingFlags.NonPublic) + ?? throw new InvalidOperationException("OperationDefinitionComparer type was not found."); + FieldInfo instanceField = comparerType.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("OperationDefinitionComparer.Instance field was not found."); + object comparer = instanceField.GetValue(null) + ?? throw new InvalidOperationException("OperationDefinitionComparer.Instance is null."); + MethodInfo compareMethod = comparerType.GetMethod("Compare", BindingFlags.Instance | BindingFlags.Public) + ?? throw new InvalidOperationException("OperationDefinitionComparer.Compare method was not found."); + int comparison = Assert.IsType(compareMethod.Invoke(comparer, [smallAreaCandidate, largeAreaCandidate])); + + Assert.True(comparison < 0); + } + + /// + /// Verifies that when only grid-based operations exist for a coordinate pair and PROJNET_GRID_REQUIRED is set, creating the transformation throws an with a DataUnavailable prefix. + /// + [Fact] + public void CreateFromCoordinateSystemsWithGridOnlyDirectOperationsThrowsDeterministicDataUnavailable() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + List? gridOnlyPair = provider.GetDefinitions() + .Where(definition => definition.SourceSrid > 0 && definition.TargetSrid > 0 && definition.SourceSrid != definition.TargetSrid) + .GroupBy(definition => new { definition.SourceSrid, definition.TargetSrid }) + .Select(group => group.ToList()) + .FirstOrDefault(group => group.All(definition => !string.IsNullOrWhiteSpace(definition.ParameterFileName))); + + Assert.NotNull(gridOnlyPair); + + ProjectedCoordinateSystem source = WithAuthority( + ProjectedCoordinateSystem.WGS84_UTM(32, true), + "EPSG", + Assert.IsType>(gridOnlyPair)[0].SourceSrid); + ProjectedCoordinateSystem target = WithAuthority( + ProjectedCoordinateSystem.WGS84_UTM(33, true), + "EPSG", + Assert.IsType>(gridOnlyPair)[0].TargetSrid); + + string? originalRequiredMode = Environment.GetEnvironmentVariable("PROJNET_GRID_REQUIRED"); + try + { + Environment.SetEnvironmentVariable("PROJNET_GRID_REQUIRED", "true"); + InvalidOperationException exception = Assert.Throws(() => this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target)); + Assert.StartsWith("DataUnavailable:", exception.Message, StringComparison.Ordinal); + } + finally + { + Environment.SetEnvironmentVariable("PROJNET_GRID_REQUIRED", originalRequiredMode); + } + } + + /// + /// Verifies that when a coordinate pair has both grid-based and parameter-based catalogued operations, the engine selects a non-grid operation. + /// + [Fact] + public void CreateFromCoordinateSystemsWithMixedGridAndNonGridDirectOperationsFallsBackToAvailableMetadataOperation() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + List? mixedPair = provider.GetDefinitions() + .Where(definition => definition.SourceSrid > 0 && definition.TargetSrid > 0 && definition.SourceSrid != definition.TargetSrid) + .GroupBy(definition => new { definition.SourceSrid, definition.TargetSrid }) + .Select(group => group.ToList()) + .FirstOrDefault(group => + group.Any(definition => !string.IsNullOrWhiteSpace(definition.ParameterFileName)) + && group.Any(definition => string.IsNullOrWhiteSpace(definition.ParameterFileName))); + + Assert.NotNull(mixedPair); + + ProjectedCoordinateSystem source = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(32, true), "EPSG", mixedPair[0].SourceSrid); + ProjectedCoordinateSystem target = WithAuthority(ProjectedCoordinateSystem.WGS84_UTM(33, true), "EPSG", mixedPair[0].TargetSrid); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + + Assert.Equal("EPSG", transformation.Authority); + Assert.DoesNotContain("Grid:", transformation.Remarks ?? string.Empty, StringComparison.Ordinal); + } + + /// + /// Verifies that a geographic-to-geographic transformation using a supported EPSG datum operation includes an explicit datum transform step. + /// + [Fact] + public void CreateFromCoordinateSystemsWithSupportedGeographicEpsgOperationUsesExplicitDatumTransform() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var services = new CoordinateSystemServices(); + CoordinateOperationDefinition operation = GetRankedOperations(provider) + .First(definition => + IsExplicitMethodSupported(definition.MethodName) + && string.IsNullOrWhiteSpace(definition.ParameterFileName) + && services.GetCoordinateSystem(definition.SourceSrid) is GeographicCoordinateSystem + && services.GetCoordinateSystem(definition.TargetSrid) is GeographicCoordinateSystem); + + GeographicCoordinateSystem source = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.SourceSrid)), + "EPSG", + operation.SourceSrid); + GeographicCoordinateSystem target = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.TargetSrid)), + "EPSG", + operation.TargetSrid); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + + Assert.Equal("EPSG", transformation.Authority); + Assert.True(IsExplicitMethodSupported(provider.GetDefinitions().First(definition => definition.OperationCode == transformation.AuthorityCode).MethodName)); + Assert.True(ContainsDatumTransform(transformation.MathTransform)); + } + + /// + /// Verifies that a projected-to-projected transformation whose base geographic coordinate systems have a supported EPSG datum operation includes an explicit datum transform step. + /// + [Fact] + public void CreateFromCoordinateSystemsWithProjectedPairUsingSupportedBaseGeographicOperationUsesExplicitDatumTransform() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var services = new CoordinateSystemServices(); + var projectedRecords = EnumerateProjectedCrsRecords().ToList(); + + var candidates = ( + from operation in GetRankedOperations(provider) + where IsExplicitMethodSupported(operation.MethodName) + && string.IsNullOrWhiteSpace(operation.ParameterFileName) + from sourceProjected in projectedRecords.Where(record => record.BaseSrid == operation.SourceSrid).Take(1) + from targetProjected in projectedRecords.Where(record => record.BaseSrid == operation.TargetSrid).Take(1) + select new + { + operation, + SourceProjectedSrid = sourceProjected.Srid, + TargetProjectedSrid = targetProjected.Srid, + }).Take(200); + + foreach (var candidate in candidates) + { + ProjectedCoordinateSystem sourceTemplate = Assert.IsType(services.GetCoordinateSystem(candidate.SourceProjectedSrid)); + ProjectedCoordinateSystem targetTemplate = Assert.IsType(services.GetCoordinateSystem(candidate.TargetProjectedSrid)); + ProjectedCoordinateSystem source = CoordinateSystemTestHelpers.WithBaseGeographicAuthority( + WithAuthority( + CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, sourceTemplate.WKT), + string.Empty, + -1), + "EPSG", + candidate.operation.SourceSrid); + ProjectedCoordinateSystem target = CoordinateSystemTestHelpers.WithBaseGeographicAuthority( + WithAuthority( + CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, targetTemplate.WKT), + string.Empty, + -1), + "EPSG", + candidate.operation.TargetSrid); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + if (!"EPSG".Equals(transformation.Authority, StringComparison.Ordinal)) + { + continue; + } + + if (!ContainsDatumTransform(transformation.MathTransform)) + { + continue; + } + + Assert.True(IsExplicitMethodSupported(provider.GetDefinitions().First(definition => definition.OperationCode == transformation.AuthorityCode).MethodName)); + return; + } + + Assert.Fail("No projected candidate produced an explicit EPSG datum transformation from base geographic metadata."); + } + + /// + /// Verifies that geocentric-domain EPSG translations route directly to a datum transform instead of legacy fallback composition. + /// + [Fact] + public void CreateFromCoordinateSystemsWithGeocentricTranslationOperationUsesExplicitGeocentricDatumTransform() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var services = new CoordinateSystemServices(); + CoordinateOperationDefinition operation = provider.GetDefinitions().First(definition => definition.OperationCode == 7817); + + GeocentricCoordinateSystem source = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.SourceSrid)), + "EPSG", + operation.SourceSrid); + GeocentricCoordinateSystem target = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.TargetSrid)), + "EPSG", + operation.TargetSrid); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + + Assert.Equal("EPSG", transformation.Authority); + Assert.Equal(operation.OperationCode, transformation.AuthorityCode); + Assert.IsType(transformation.MathTransform); + } + + /// + /// Verifies that EPSG Geographic2D offsets route directly to the managed geographic offset runtime. + /// + [Fact] + public void CreateFromCoordinateSystemsWithGeographicOffsetOperationUsesGeogOffsetMathTransform() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var services = new CoordinateSystemServices(); + CoordinateOperationDefinition operation = provider.GetDefinitions().First(definition => definition.OperationCode == 1447); + + GeographicCoordinateSystem source = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.SourceSrid)), + "EPSG", + operation.SourceSrid); + GeographicCoordinateSystem target = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.TargetSrid)), + "EPSG", + operation.TargetSrid); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] output = transformation.MathTransform.Transform(GeographicOffsetSamplePoint); + Dictionary parameters = GetOperationParameters(operation.OperationCode); + + Assert.Equal("EPSG", transformation.Authority); + Assert.Equal(operation.OperationCode, transformation.AuthorityCode); + Assert.True(ContainsMathTransform(transformation.MathTransform)); + Assert.InRange(Math.Abs(output[0] - (10d + (parameters["Longitude offset"] / 3600d))), 0d, 1e-12d); + Assert.InRange(Math.Abs(output[1] - (20d + (parameters["Latitude offset"] / 3600d))), 0d, 1e-12d); + } + + /// + /// Verifies that time-dependent geocentric EPSG operations route directly to the Helmert runtime and preserve the observation epoch ordinate. + /// + [Fact] + public void CreateFromCoordinateSystemsWithTimeDependentGeocentricOperationUsesHelmertMathTransform() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var services = new CoordinateSystemServices(); + CoordinateOperationDefinition operation = provider.GetDefinitions().First(definition => definition.OperationCode == 5900); + + GeocentricCoordinateSystem source = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.SourceSrid)), + "EPSG", + operation.SourceSrid); + GeocentricCoordinateSystem target = WithAuthority( + Assert.IsType(services.GetCoordinateSystem(operation.TargetSrid)), + "EPSG", + operation.TargetSrid); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] output = transformation.MathTransform.Transform(TimeDependentGeocentricSamplePoint); + + Assert.Equal("EPSG", transformation.Authority); + Assert.Equal(operation.OperationCode, transformation.AuthorityCode); + Assert.IsType(transformation.MathTransform); + Assert.Equal(4, output.Length); + Assert.InRange(Math.Abs(output[3] - 2017d), 0d, 1e-12d); + } + + /// + /// Verifies that Molodensky-Badekas EPSG operations route directly to the managed Molodensky-Badekas runtime. + /// + [Fact] + public void CreateFromCoordinateSystemsWithMolodenskyBadekasOperationUsesMolobadekasMathTransform() + { + var provider = new ManagedCoordinateOperationDefinitionProvider(); + var services = new CoordinateSystemServices(); + CoordinateOperationDefinition operation = provider.GetDefinitions().First(definition => definition.OperationCode == 1066); + + GeographicCoordinateSystem source = Assert.IsType(services.GetCoordinateSystem(operation.SourceSrid)); + GeographicCoordinateSystem target = Assert.IsType(services.GetCoordinateSystem(operation.TargetSrid)); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + + Assert.Equal("EPSG", transformation.Authority); + Assert.Equal(operation.OperationCode, transformation.AuthorityCode); + Assert.True(ContainsMathTransform(transformation.MathTransform)); + } + + /// + /// Verifies that concatenated EPSG geographic operations preserve the catalog step order and reverse later steps when required by the intermediate CRS chain. + /// + [Fact] + public void CreateFromCoordinateSystemsWithConcatenatedGeographicOperationUsesExpectedStepDirections() + { + var services = new CoordinateSystemServices(); + + ICoordinateTransformation transformation = Assert.IsAssignableFrom(services.CreateTransformation(4289, 4230)); + + Assert.Equal("EPSG", transformation.Authority); + Assert.Equal(4837, transformation.AuthorityCode); + + ConcatenatedTransform concatenated = Assert.IsType(transformation.MathTransform); + Assert.Collection( + concatenated.CoordinateTransformationList, + step => AssertConcatenatedStep(step, 1672, 4289, 4326), + step => AssertConcatenatedStep(step, 1311, 4326, 4230)); + } + + /// + /// Verifies that concatenated EPSG geocentric operations preserve the catalog step order and reverse intermediate step directions when required. + /// + [Fact] + public void CreateFromCoordinateSystemsWithConcatenatedGeocentricOperationUsesExpectedStepDirections() + { + var services = new CoordinateSystemServices(); + + ICoordinateTransformation transformation = Assert.IsAssignableFrom(services.CreateTransformation(9988, 4000)); + + Assert.Equal("EPSG", transformation.Authority); + Assert.Equal(11285, transformation.AuthorityCode); + + ConcatenatedTransform concatenated = Assert.IsType(transformation.MathTransform); + Assert.Collection( + concatenated.CoordinateTransformationList, + step => AssertConcatenatedStep(step, 10586, 9988, 7930), + step => AssertConcatenatedStep(step, 11228, 7930, 7928), + step => AssertConcatenatedStep(step, 11205, 7928, 4000)); + } + + /// + /// Verifies that a transformation between fitted coordinate systems is correctly composed by routing through the base coordinate systems. + /// + [Fact] + public void CreateFromCoordinateSystemsWithFittedSourceAndTargetComposesViaBaseCoordinateSystems() + { + var sourceBase = ProjectedCoordinateSystem.WGS84_UTM(32, true); + var targetBase = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + var sourceToBase = new AffineTransform(new double[,] + { + { 1d, 0d, 1000d }, + { 0d, 1d, -500d }, + { 0d, 0d, 1d }, + }); + + var targetToBase = new AffineTransform(new double[,] + { + { 1d, 0d, -2000d }, + { 0d, 1d, 250d }, + { 0d, 0d, 1d }, + }); + + FittedCoordinateSystem sourceFitted = this.coordinateSystemFactory.CreateFittedCoordinateSystem( + "source-fitted", + sourceBase, + sourceToBase, + []); + FittedCoordinateSystem targetFitted = this.coordinateSystemFactory.CreateFittedCoordinateSystem( + "target-fitted", + targetBase, + targetToBase, + []); + + ICoordinateTransformation transformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(sourceFitted, targetFitted); + ICoordinateTransformation baseTransformation = this.coordinateTransformationFactory.CreateFromCoordinateSystems(sourceBase, targetBase); + + double[] input = [500000d, 4649776.22482d]; + double[] transformed = transformation.MathTransform.Transform(input); + + double[] baseInput = sourceToBase.Transform(input); + double[] baseOutput = baseTransformation.MathTransform.Transform(baseInput); + double[] expected = targetToBase.Inverse().Transform(baseOutput); + + Assert.Equal(expected[0], transformed[0], 9); + Assert.Equal(expected[1], transformed[1], 9); + } + + private static ProjectedCoordinateSystem WithAuthority(ProjectedCoordinateSystem coordinateSystem, string authority, long authorityCode) + { + return coordinateSystem.WithAuthority(authority, authorityCode); + } + + private static GeographicCoordinateSystem WithAuthority(GeographicCoordinateSystem coordinateSystem, string authority, long authorityCode) + { + return coordinateSystem.WithAuthority(authority, authorityCode); + } + + private static GeocentricCoordinateSystem WithAuthority(GeocentricCoordinateSystem coordinateSystem, string authority, long authorityCode) + { + return coordinateSystem.WithAuthority(authority, authorityCode); + } + + private static IEnumerable EnumerateProjectedCrsRecords() + { + for (int cacheIndex = 0; cacheIndex < EpsgGeneratedCatalog.CoordinateReferenceCount; cacheIndex++) + { + if (!EpsgGeneratedCatalog.TryGetCoordinateSridByCacheIndex(cacheIndex, out int srid)) + { + continue; + } + + if (!EpsgGeneratedCatalog.TryGetCoordinateReference(srid, out EpsgCoordinateReferenceRecord reference, out _)) + { + continue; + } + + if (reference.Kind != EpsgCoordinateSystemKind.Projected) + { + continue; + } + + if (EpsgGeneratedCatalog.TryGetProjectedCrs(reference.RecordIndex, out EpsgProjectedCrsRecord projectedRecord)) + { + yield return projectedRecord; + } + } + } + + private static bool ContainsGeographicOrGeocentricCoordinateSystem(ICoordinateTransformationCore transformation) + { + if (transformation.SourceCS is GeographicCoordinateSystem || transformation.TargetCS is GeographicCoordinateSystem) + { + return true; + } + + if (transformation.SourceCS is GeocentricCoordinateSystem || transformation.TargetCS is GeocentricCoordinateSystem) + { + return true; + } + + if (transformation is ConcatenatedTransform nested) + { + foreach (ICoordinateTransformationCore item in nested.CoordinateTransformationList) + { + if (ContainsGeographicOrGeocentricCoordinateSystem(item)) + { + return true; + } + } + } + + return false; + } + + private static bool ContainsMathTransform(MathTransform mathTransform) + where TMathTransform : MathTransform + { + if (mathTransform is TMathTransform) + { + return true; + } + + return mathTransform is ConcatenatedTransform concatenated && concatenated.CoordinateTransformationList.Any(ContainsMathTransform); + } + + private static bool ContainsMathTransform(ICoordinateTransformationCore transformation) + where TMathTransform : MathTransform + { + if (transformation is CoordinateTransformation coordinateTransformation) + { + return ContainsMathTransform(coordinateTransformation.MathTransform); + } + + return transformation is ConcatenatedTransform concatenated && concatenated.CoordinateTransformationList.Any(ContainsMathTransform); + } + + private static void AssertConcatenatedStep( + ICoordinateTransformationCore step, + int expectedOperationCode, + int expectedSourceSrid, + int expectedTargetSrid) + { + CoordinateTransformation coordinateTransformation = Assert.IsType(step); + + Assert.Equal("EPSG", coordinateTransformation.Authority); + Assert.Equal(expectedOperationCode, coordinateTransformation.AuthorityCode); + Assert.Equal("EPSG", coordinateTransformation.SourceCS.Authority); + Assert.Equal(expectedSourceSrid, coordinateTransformation.SourceCS.AuthorityCode); + Assert.Equal("EPSG", coordinateTransformation.TargetCS.Authority); + Assert.Equal(expectedTargetSrid, coordinateTransformation.TargetCS.AuthorityCode); + } + + private static bool ContainsDatumTransform(MathTransform mathTransform) + { + return ContainsMathTransform(mathTransform); + } + + private static bool ContainsDatumTransform(ICoordinateTransformationCore transformation) + { + return ContainsMathTransform(transformation); + } + + private static bool IsExplicitMethodSupported(string methodName) + { + string normalized = NormalizeMethodName(methodName); + return normalized.Contains("geocentrictranslations", StringComparison.Ordinal) + || normalized.Contains("positionvectortransformation", StringComparison.Ordinal) + || normalized.Contains("coordinateframerotation", StringComparison.Ordinal) + || normalized.Contains("molodensky", StringComparison.Ordinal); + } + + private static string NormalizeMethodName(string value) + { + return string.IsNullOrWhiteSpace(value) + ? string.Empty + : new string([.. value.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant)]); + } + + private static IOrderedEnumerable GetRankedOperations(ManagedCoordinateOperationDefinitionProvider provider) + { + return provider.GetDefinitions() + .Where(definition => definition.SourceSrid > 0 && definition.TargetSrid > 0 && definition.SourceSrid != definition.TargetSrid) + .OrderBy(definition => definition.Accuracy > 0d ? definition.Accuracy : double.MaxValue) + .ThenBy(definition => string.IsNullOrWhiteSpace(definition.ParameterFileName) ? 0 : 1) + .ThenBy(definition => string.IsNullOrWhiteSpace(definition.MethodName) ? 1 : 0) + .ThenBy(definition => definition.OperationCode); + } + + private static Dictionary GetOperationParameters(int operationCode) + { + return EpsgGeneratedOperationsCatalog.OperationParameters + .Where(parameter => parameter.OperationCode == operationCode) + .ToDictionary(parameter => parameter.Name, parameter => parameter.Value, StringComparer.Ordinal); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/PipelineRuntimeTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/PipelineRuntimeTests.cs new file mode 100644 index 00000000..f679606e --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/PipelineRuntimeTests.cs @@ -0,0 +1,979 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for the PROJ pipeline math transform runtime, covering step execution, parameter parsing, and validation. +/// +public class PipelineRuntimeTests +{ + private const double GeocentricLatitudeAt45OnGrs80 = 44.807576783073245d; + private static readonly double[] GeocentRoundtripInput = [2d, 1d, 250d]; + private static readonly double[] GeocForwardInput = [12d, 45d]; + private static readonly double[] GeocInverseInput = [12d, GeocentricLatitudeAt45OnGrs80]; + private static readonly double[] CartAliasInput = [90d, 0d, 0d]; + private static readonly double[] GeogOffset2DInput = [10d, 20d]; + private static readonly double[] GeogOffset3DInput = [10d, 20d, 30d]; + private static readonly double[] GeogOffsetInverseInput = [11d, 19d, 33d]; + private static readonly double[] MolobadekasInput = [2550408.96d, -5749912.26d, 1054891.11d]; + private static readonly double[] AffineInput4D = [2d, 49d, 10d, 100d]; + private static readonly double[] PushPopInput4D = [12d, 56d, 0d, 2020d]; + private static readonly double[] PipelineNoopInput = [1.5d, 2.25d, 9d]; + private static readonly double[] PipelineSwapInput = [100d, 200d]; + + /// + /// Verifies that a pipeline combining unit conversion and axis swap correctly converts and reorders the output ordinates. + /// + [Fact] + public void PipelineWithUnitConvertAndAxisSwapConvertsAndSwaps() + { + const string operation = "+proj=pipeline +step +proj=unitconvert +xy_in=m +xy_out=ft +step +proj=axisswap +order=2,1"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(PipelineSwapInput); + + Assert.Equal(656.1679790026246d, transformed[0], 9); + Assert.Equal(328.0839895013123d, transformed[1], 9); + } + + /// + /// Verifies that a pipeline with a noop, a set step, and unit conversion applies the set value override to the third ordinate. + /// + [Fact] + public void PipelineWithNoopSetAndUnitConvertAppliesSetOverride() + { + const string operation = "+proj=pipeline +step +proj=noop +step +proj=set +v_3=17 +step +proj=unitconvert +xy_in=km +xy_out=m"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(PipelineNoopInput); + + Assert.Equal(1500d, transformed[0], 10); + Assert.Equal(2250d, transformed[1], 10); + Assert.Equal(17d, transformed[2], 10); + } + + /// + /// Verifies that an outer pipeline can execute a nested inner pipeline step and still round-trip through the inverse path. + /// + [Fact] + public void PipelineWithNestedPipelineStepExecutesForwardAndInverse() + { + const string operation = "+proj=pipeline +step +proj=pipeline +step +proj=affine +xoff=1 +step +proj=affine +yoff=-2"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] input = [10d, 20d]; + double[] forward = transform.Transform(input); + double[] inverse = transform.Inverse().Transform(forward); + + Assert.Equal(11d, forward[0], 12); + Assert.Equal(18d, forward[1], 12); + Assert.Equal(input[0], inverse[0], 12); + Assert.Equal(input[1], inverse[1], 12); + } + + /// + /// Verifies that the nested pipeline recursion guard rejects overly deep pipeline nesting. + /// + [Fact] + public void PipelineWithTooManyNestedPipelinesFailsValidation() + { + string operation = CreateNestedPipelineOperation(6); + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains("maximum of 4", Assert.IsType(skipReason), StringComparison.Ordinal); + } + + /// + /// Verifies that a nested datum-shift-and-projection pipeline produces the same result as the equivalent flattened pipeline. + /// + [Fact] + public void PipelineWithNestedDatumShiftAndProjectionMatchesFlattenedPipeline() + { + const string nestedOperation = "+proj=pipeline +step +proj=pipeline +step +proj=longlat +datum=GGRS87 +inv +step +proj=longlat +datum=WGS84 +step +proj=utm +zone=34 +datum=WGS84"; + const string flattenedOperation = "+proj=pipeline +step +proj=longlat +datum=GGRS87 +inv +step +proj=longlat +datum=WGS84 +step +proj=utm +zone=34 +datum=WGS84"; + + MathTransform nestedTransform = RequirePipelineMathTransform(nestedOperation); + MathTransform flattenedTransform = RequirePipelineMathTransform(flattenedOperation); + double[] input = [23.7275d, 37.9838d]; + double[] nestedOutput = nestedTransform.Transform(input); + double[] flattenedOutput = flattenedTransform.Transform(input); + + Assert.Equal(flattenedOutput[0], nestedOutput[0], 6); + Assert.Equal(flattenedOutput[1], nestedOutput[1], 6); + } + + /// + /// Verifies that a 4D axis swap step with negated indices correctly reorders and flips all four ordinates. + /// + [Fact] + public void PipelineWith4DAxisSwapReordersAndFlipsAllOrdinates() + { + const string operation = "+proj=axisswap +order=4,3,-2,1"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(AffineInput4D); + + Assert.Equal(100d, transformed[0], 12); + Assert.Equal(10d, transformed[1], 12); + Assert.Equal(-49d, transformed[2], 12); + Assert.Equal(2d, transformed[3], 12); + } + + /// + /// Verifies that push and pop steps correctly save and restore the first ordinate across intermediate transform steps. + /// + [Fact] + public void PipelineWithPushAndPopRestoresSavedHorizontalComponent() + { + const string operation = "+proj=pipeline +step +proj=push +v_1 +step +proj=utm +zone=32 +step +proj=utm +zone=33 +inv +step +proj=pop +v_1"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(PushPopInput4D); + + Assert.Equal(PushPopInput4D[0], transformed[0], 9); + Assert.Equal(PushPopInput4D[1], transformed[1], 9); + Assert.Equal(PushPopInput4D[2], transformed[2], 9); + Assert.Equal(PushPopInput4D[3], transformed[3], 9); + } + + /// + /// Verifies that global ellipsoid parameters are shared across all pipeline steps. + /// + [Fact] + public void PipelineUsesGlobalEllipsoidParametersAcrossSteps() + { + const string operation = "+proj=pipeline +ellps=GRS80 +step +proj=geocent +step +proj=geocent +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeocentRoundtripInput); + + Assert.Equal(GeocentRoundtripInput[0], transformed[0], 9); + Assert.Equal(GeocentRoundtripInput[1], transformed[1], 9); + Assert.Equal(GeocentRoundtripInput[2], transformed[2], 6); + } + + /// + /// Verifies that a UTM step in a pipeline inherits the global ellipsoid parameter. + /// + [Fact] + public void PipelineWithUtmStepUsesGlobalEllipsoid() + { + const string operation = "+proj=pipeline +ellps=GRS80 +step +proj=utm +zone=32 +step +proj=utm +zone=32 +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeocForwardInput); + + Assert.Equal(GeocForwardInput[0], transformed[0], 7); + Assert.Equal(GeocForwardInput[1], transformed[1], 7); + } + + /// + /// Verifies that synthetic UTM false offsets are expressed in the configured output units. + /// + /// UTM operation under test. + [Theory] + [InlineData("+proj=utm +ellps=GRS80 +zone=32 +to_meter=10")] + [InlineData("+proj=utm +ellps=GRS80 +zone=32 +to_meter=2.0/0.2")] + public void PipelineWithUtmStepHonorsCustomOutputUnitsForSyntheticOffsets(string operation) + { + MathTransform transform = RequirePipelineMathTransform(operation); + double[] output = transform.Transform([12d, 55d]); + + Assert.Equal(69187.5632d, output[0], 4); + Assert.Equal(609890.7825d, output[1], 4); + } + + /// + /// Verifies that explicit ellipsoid shape overrides are applied after resolving a named ellipsoid for UTM steps. + /// + /// UTM operation under test. + /// Equivalent UTM operation with the ellipsoid shape specified directly. + /// UTM operation that keeps the original named ellipsoid without an explicit shape override. + [Theory] + [InlineData("+proj=utm +ellps=GRS80 +zone=32 +b=6000000", "+proj=utm +a=6378137 +zone=32 +b=6000000", "+proj=utm +ellps=GRS80 +zone=32")] + [InlineData("+proj=utm +ellps=GRS80 +zone=32 +rf=300", "+proj=utm +a=6378137 +zone=32 +rf=300", "+proj=utm +ellps=GRS80 +zone=32")] + [InlineData("+proj=utm +ellps=GRS80 +zone=32 +f=0.00333333333333", "+proj=utm +a=6378137 +zone=32 +f=0.00333333333333", "+proj=utm +ellps=GRS80 +zone=32")] + public void PipelineWithUtmStepHonorsExplicitEllipsoidShapeOverrides( + string operation, + string equivalentOperation, + string baselineOperation) + { + MathTransform transform = RequirePipelineMathTransform(operation); + MathTransform equivalentTransform = RequirePipelineMathTransform(equivalentOperation); + MathTransform baselineTransform = RequirePipelineMathTransform(baselineOperation); + double[] output = transform.Transform([12d, 55d]); + double[] equivalentOutput = equivalentTransform.Transform([12d, 55d]); + double[] baselineOutput = baselineTransform.Transform([12d, 55d]); + + Assert.Equal(equivalentOutput[0], output[0], 12); + Assert.Equal(equivalentOutput[1], output[1], 12); + Assert.NotEqual(baselineOutput[0], output[0], 9); + Assert.NotEqual(baselineOutput[1], output[1], 9); + } + + /// + /// Verifies that invalid explicit ellipsoid shape overrides are rejected even when a named ellipsoid provides the base axes. + /// + /// Invalid UTM operation under test. + /// Expected token mentioned in the validation message. + [Theory] + [InlineData("+proj=utm +ellps=GRS80 +zone=32 +b=0", "+b")] + [InlineData("+proj=utm +ellps=GRS80 +zone=32 +es=1", "+es")] + public void PipelineWithUtmStepRejectsInvalidExplicitEllipsoidShapeOverrides(string operation, string expectedToken) + { + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains(expectedToken, skipReason, StringComparison.Ordinal); + } + + /// + /// Verifies that a global pipeline +inv flag reverses the step order and toggles each step inversion. + /// + [Fact] + public void PipelineWithGlobalInvInvertsWholePipeline() + { + const string operation = "+proj=pipeline +inv +step +proj=affine +xoff=1 +step +proj=affine +xoff=2"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform([10d, 20d]); + + Assert.Equal(7d, transformed[0], 12); + Assert.Equal(20d, transformed[1], 12); + } + + /// + /// Verifies that urm5 pipeline steps accept the required n parameter and optional shape parameters. + /// + [Fact] + public void PipelineWithUrm5StepAcceptsProjectionSpecificParameters() + { + const string operation = "+proj=urm5 +ellps=WGS84 +n=0.5 +q=0.2 +alpha=10"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform([12d, 56d]); + + Assert.False(double.IsNaN(transformed[0]) || double.IsInfinity(transformed[0])); + Assert.False(double.IsNaN(transformed[1]) || double.IsInfinity(transformed[1])); + } + + /// + /// Verifies that +pm shifts the longitude reference before the projection step is applied. + /// + [Fact] + public void PipelineProjectionStepWithPrimeMeridianUsesLocalLongitudeReference() + { + const string operation = "+proj=latlong +pm=paris"; + const double projParisLongitude = 2d + (20d / 60d) + (14.025d / 3600d); + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] input = [projParisLongitude, 48d]; + double[] forward = transform.Transform(input); + double[] inverse = transform.Inverse().Transform(forward); + + Assert.Equal(0d, forward[0], 12); + Assert.Equal(48d, forward[1], 12); + Assert.Equal(projParisLongitude, inverse[0], 12); + Assert.Equal(48d, inverse[1], 10); + } + + /// + /// Verifies that a transverse Mercator step round-trips correctly using the specified projection parameters. + /// + [Fact] + public void PipelineWithTmercStepRoundTripsUsingProjectionParameters() + { + const string operation = "+proj=pipeline +ellps=GRS80 +step +proj=tmerc +lon_0=9 +k_0=0.9996 +x_0=500000 +y_0=0 +step +proj=tmerc +lon_0=9 +k_0=0.9996 +x_0=500000 +y_0=0 +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeocForwardInput); + + Assert.Equal(GeocForwardInput[0], transformed[0], 7); + Assert.Equal(GeocForwardInput[1], transformed[1], 7); + } + + /// + /// Verifies that an LCC step respects the configured scale factor in both forward and inverse directions. + /// + [Fact] + public void PipelineWithLccStepRoundTripsUsingScaleFactor() + { + const string operation = "+proj=pipeline +step +proj=lcc +lon_0=0 +lat_0=46.8 +lat_1=46.8 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +ellps=clrk80ign +pm=paris +step +proj=lcc +lon_0=0 +lat_0=46.8 +lat_1=46.8 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +ellps=clrk80ign +pm=paris +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform([2.5969213d, 48d]); + + Assert.Equal(2.5969213d, transformed[0], 7); + Assert.Equal(48d, transformed[1], 7); + } + + /// + /// Verifies that a pushed value is not restored when no corresponding pop step is present. + /// + [Fact] + public void PipelineWithPushWithoutPopKeepsChangedValue() + { + const string operation = "+proj=pipeline +step +proj=push +v_1 +step +proj=set +v_1=18"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(PushPopInput4D); + + Assert.Equal(18d, transformed[0], 12); + Assert.Equal(PushPopInput4D[1], transformed[1], 12); + Assert.Equal(PushPopInput4D[2], transformed[2], 12); + Assert.Equal(PushPopInput4D[3], transformed[3], 12); + } + + /// + /// Verifies that a pop step with no prior push for the same component leaves the current ordinate value unchanged. + /// + [Fact] + public void PipelineWithPopFromEmptyStackKeepsCurrentValue() + { + const string operation = "+proj=pipeline +step +proj=set +v_1=18 +step +proj=pop +v_1"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(PushPopInput4D); + + Assert.Equal(18d, transformed[0], 12); + Assert.Equal(PushPopInput4D[1], transformed[1], 12); + Assert.Equal(PushPopInput4D[2], transformed[2], 12); + Assert.Equal(PushPopInput4D[3], transformed[3], 12); + } + + /// + /// Verifies that push and pop correctly save and restore the time component across an affine transform step. + /// + [Fact] + public void PipelineWithPushAndPopOnTimeComponentRestoresEpoch() + { + const string operation = "+proj=pipeline +step +proj=push +v_4 +step +proj=affine +toff=4 +tscale=34 +step +proj=pop +v_4"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(PushPopInput4D); + + Assert.Equal(PushPopInput4D[0], transformed[0], 12); + Assert.Equal(PushPopInput4D[1], transformed[1], 12); + Assert.Equal(PushPopInput4D[2], transformed[2], 12); + Assert.Equal(PushPopInput4D[3], transformed[3], 12); + } + + /// + /// Verifies that multiple push and pop steps for the same component follow last-in, first-out order. + /// + [Fact] + public void PipelineWithMultiplePushesAndPopsUsesLifoPerComponent() + { + const string operation = "+proj=pipeline +step +proj=push +v_1 +step +proj=set +v_1=20 +step +proj=push +v_1 +step +proj=set +v_1=30 +step +proj=pop +v_1 +step +proj=pop +v_1"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(PushPopInput4D); + + Assert.Equal(PushPopInput4D[0], transformed[0], 12); + Assert.Equal(PushPopInput4D[1], transformed[1], 12); + Assert.Equal(PushPopInput4D[2], transformed[2], 12); + Assert.Equal(PushPopInput4D[3], transformed[3], 12); + } + + /// + /// Verifies that the omit_inv flag causes a pipeline step to be applied in the forward direction but skipped in the inverse direction. + /// + [Fact] + public void PipelineWithOmitInvSkipsStepOnlyInInverseDirection() + { + const string operation = "+proj=pipeline +step +proj=affine +xoff=1 +yoff=1 +omit_inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] forward = transform.Transform(AffineInput4D); + double[] inverse = transform.Inverse().Transform(AffineInput4D); + + Assert.Equal(3d, forward[0], 12); + Assert.Equal(50d, forward[1], 12); + Assert.Equal(2d, inverse[0], 12); + Assert.Equal(49d, inverse[1], 12); + } + + /// + /// Verifies that the omit_fwd flag causes a pipeline step to be skipped in the forward direction but applied in the inverse direction. + /// + [Fact] + public void PipelineWithOmitFwdSkipsStepOnlyInForwardDirection() + { + const string operation = "+proj=pipeline +step +proj=affine +xoff=1 +yoff=1 +omit_fwd"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] forward = transform.Transform(AffineInput4D); + double[] inverse = transform.Inverse().Transform(AffineInput4D); + + Assert.Equal(2d, forward[0], 12); + Assert.Equal(49d, forward[1], 12); + Assert.Equal(1d, inverse[0], 12); + Assert.Equal(48d, inverse[1], 12); + } + + /// + /// Verifies that a push step without an ordinate flag produces a validation failure. + /// + [Fact] + public void PipelinePushWithoutOrdinateFlagReturnsValidationFailure() + { + const string operation = "+proj=pipeline +step +proj=push +step +proj=pop +v_1"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("v_1", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that a transverse Mercator step with a non-numeric parameter value produces a validation failure. + /// + [Fact] + public void PipelineWithInvalidTmercParameterReturnsValidationFailure() + { + const string operation = "+proj=pipeline +step +proj=tmerc +lon_0=abc"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("lon_0", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that an unsupported projection name produces a validation failure referencing the current builtins wave. + /// + [Fact] + public void PipelineWithUnsupportedProjectionKeepsBuiltinsWaveError() + { + const string operation = "+proj=pipeline +step +proj=unknown_projection"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("current builtins wave", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that an invalid UTM zone number produces a validation failure. + /// + [Fact] + public void PipelineWithInvalidUtmZoneReturnsValidationFailure() + { + const string operation = "+proj=pipeline +step +proj=utm +zone=99"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("zone", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that +lat_ts is mapped to a single projection parameter entry. + /// + [Fact] + public void PipelineProjectionStepWithLatTsDoesNotCreateDuplicateAliases() + { + const string operation = "+proj=wink1 +lat_ts=30"; + + MathTransform transform = RequirePipelineMathTransform(operation); + MapProjection projection = Assert.IsAssignableFrom(transform); + int latTsParameterCount = 0; + for (int i = 0; i < projection.NumParameters; i++) + { + string name = projection.GetParameter(i).Name; + if (name.Equals("lat_ts", StringComparison.OrdinalIgnoreCase) + || name.Equals("latitude_true_scale", StringComparison.OrdinalIgnoreCase)) + { + latTsParameterCount++; + } + } + + Assert.Equal(1, latTsParameterCount); + } + + /// + /// Verifies that a standalone push or pop step without a matching counterpart leaves all ordinates unchanged. + /// + [Theory] + [InlineData("+proj=push +v_3")] + [InlineData("+proj=pop +v_3")] + public void StandalonePushOrPopBehavesAsNoop(string operation) + { + MathTransform transform = RequirePipelineMathTransform($"+proj=pipeline +step {operation}"); + double[] transformed = transform.Transform(PushPopInput4D); + + Assert.Equal(PushPopInput4D[0], transformed[0], 12); + Assert.Equal(PushPopInput4D[1], transformed[1], 12); + Assert.Equal(PushPopInput4D[2], transformed[2], 12); + Assert.Equal(PushPopInput4D[3], transformed[3], 12); + } + + /// + /// Verifies that an axis swap step with duplicate axis indices in the order parameter produces a validation failure. + /// + [Fact] + public void PipelineInvalidAxisSwapOrderReturnsValidationFailure() + { + const string operation = "+proj=pipeline +step +proj=axisswap +order=1,1"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("+order", skipReason, StringComparison.Ordinal); + } + + /// + /// Verifies that an axis swap using the +axis parameter correctly interprets compass and vertical orientation codes. + /// + [Fact] + public void AxisSwapWithAxisParameterParsesOrientationCodes() + { + const string operation = "+proj=axisswap +axis=wsu"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform([2d, 3d, 4d]); + + Assert.Equal(-2d, transformed[0], 12); + Assert.Equal(-3d, transformed[1], 12); + Assert.Equal(4d, transformed[2], 12); + } + + /// + /// Verifies that an axis swap step without either +order or +axis produces a validation failure. + /// + [Fact] + public void AxisSwapWithoutOrderAndAxisReturnsValidationFailure() + { + const string operation = "+proj=axisswap"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("exactly one of +order or +axis", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that specifying both +order and +axis on an axis swap step produces a validation failure. + /// + [Fact] + public void AxisSwapWithOrderAndAxisReturnsValidationFailure() + { + const string operation = "+proj=axisswap +order=1,2 +axis=en"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("exactly one of +order or +axis", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that an axis swap step with an invalid character in the +axis value produces a validation failure. + /// + [Fact] + public void AxisSwapWithInvalidAxisTokenReturnsValidationFailure() + { + const string operation = "+proj=axisswap +axis=ee"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("+axis", skipReason, StringComparison.Ordinal); + } + + /// + /// Verifies that an axis swap step referencing an ordinate index beyond the input dimension produces a validation failure. + /// + [Fact] + public void AxisSwapWithOutOfRangeOrderReferenceReturnsValidationFailure() + { + const string operation = "+proj=axisswap +order=3,1"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("out-of-range axis", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies 4D epoch propagation through composite pipelines with kinematic Helmert. + /// + [Fact] + public void PipelineWithKinematicHelmertPreservesEpochAndAppliesDynamicParameters() + { + const string operation = "+proj=pipeline +step +proj=noop +step +proj=helmert +convention=position_vector +x=0.0127 +dx=-0.0029 +rx=-0.00039 +drx=-0.00011 +y=0.0065 +dy=-0.0002 +ry=0.00080 +dry=-0.00019 +z=-0.0209 +dz=-0.0006 +rz=-0.00114 +drz=0.00007 +s=0.00195 +ds=0.00001 +t_epoch=1988.0"; + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform([3370658.37800d, 711877.31400d, 5349787.08600d, 2018.0d]); + + Assert.Equal(4, transformed.Length); + Assert.InRange(Math.Abs(transformed[0] - 3370658.18087d), 0d, 1e-4d); + Assert.InRange(Math.Abs(transformed[1] - 711877.42750d), 0d, 1e-4d); + Assert.InRange(Math.Abs(transformed[2] - 5349787.12648d), 0d, 1e-4d); + Assert.InRange(Math.Abs(transformed[3] - 2018.0d), 0d, 1e-12d); + } + + /// + /// Verifies that a geocentric conversion followed by its inverse round-trips geodetic coordinates to the expected precision. + /// + [Fact] + public void PipelineWithGeocentAndInverseRoundTripsGeodeticCoordinates() + { + const string operation = "+proj=pipeline +step +proj=geocent +ellps=GRS80 +step +proj=geocent +ellps=GRS80 +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeocentRoundtripInput); + + Assert.Equal(2d, transformed[0], 9); + Assert.Equal(1d, transformed[1], 9); + Assert.Equal(250d, transformed[2], 6); + } + + /// + /// Verifies that the geoc alias step converts geodetic latitude to geocentric latitude. + /// + [Fact] + public void PipelineWithGeocAliasConvertsGeodeticToGeocentricLatitude() + { + const string operation = "+proj=geoc +ellps=GRS80"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeocForwardInput); + + Assert.Equal(12d, transformed[0], 12); + Assert.Equal(GeocentricLatitudeAt45OnGrs80, transformed[1], 12); + } + + /// + /// Verifies that the inverse geoc alias step converts geocentric latitude back to geodetic latitude. + /// + [Fact] + public void PipelineWithGeocAliasInverseConvertsGeocentricToGeodeticLatitude() + { + const string operation = "+proj=geoc +ellps=GRS80 +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeocInverseInput); + + Assert.Equal(12d, transformed[0], 12); + Assert.Equal(45d, transformed[1], 12); + } + + /// + /// Verifies that a legacy longlat +geoc pipeline step follows PROJ's spherical-ocentric semantics. + /// + [Fact] + public void PipelineWithLonglatGeocAndInverseConvertsGeodeticToGeocentricLatitude() + { + const string operation = "+proj=pipeline +step +proj=longlat +ellps=GRS80 +geoc +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform([12d, 55d, 0d, 0d]); + + Assert.Equal(12d, transformed[0], 12); + Assert.Equal(54.818973308324573d, transformed[1], 12); + Assert.Equal(0d, transformed[2], 12); + Assert.Equal(0d, transformed[3], 12); + } + + /// + /// Verifies that the cart alias step respects the +to_meter scaling parameter when converting to Cartesian coordinates. + /// + [Fact] + public void PipelineWithCartAliasAndToMeterScalesCartesianOutputUnits() + { + const string operation = "+proj=cart +a=1000 +b=1000 +to_meter=1000"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(CartAliasInput); + + Assert.Equal(0d, transformed[0], 12); + Assert.Equal(1d, transformed[1], 12); + Assert.Equal(0d, transformed[2], 12); + } + + /// + /// Verifies that Clarke 1880 PROJ ellipsoid tokens resolve to metric axes in cartesian runtime steps. + /// + /// The PROJ ellipsoid token. + /// The expected semi-major axis in metres. + [Theory] + [InlineData("clrk80", 6378249.145d)] + [InlineData("clrk80ign", 6378249.2d)] + public void PipelineWithCartStepUsesMetricClarke1880TokenAxes(string ellipsoidToken, double expectedSemiMajorAxis) + { + MathTransform transform = RequirePipelineMathTransform($"+proj=cart +ellps={ellipsoidToken}"); + double[] transformed = transform.Transform([0d, 0d, 0d]); + + Assert.Equal(expectedSemiMajorAxis, transformed[0], 9); + Assert.Equal(0d, transformed[1], 12); + Assert.Equal(0d, transformed[2], 12); + } + + /// + /// Verifies that the geogoffset step adds the configured arc-second longitude, latitude, and height offsets to both 2D and 3D input. + /// + [Fact] + public void PipelineWithGeogOffsetAddsConfiguredArcSecondAndHeightOffsets() + { + const string operation = "+proj=geogoffset +dlon=3600 +dlat=-3600 +dh=3"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed2D = transform.Transform(GeogOffset2DInput); + double[] transformed3D = transform.Transform(GeogOffset3DInput); + + Assert.Equal(11d, transformed2D[0], 12); + Assert.Equal(19d, transformed2D[1], 12); + Assert.Equal(11d, transformed3D[0], 12); + Assert.Equal(19d, transformed3D[1], 12); + Assert.Equal(33d, transformed3D[2], 12); + } + + /// + /// Verifies that the inverse geogoffset step subtracts the configured offsets. + /// + [Fact] + public void PipelineWithGeogOffsetInverseSubtractsConfiguredOffsets() + { + const string operation = "+proj=geogoffset +dlon=3600 +dlat=-3600 +dh=3 +inv"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeogOffsetInverseInput); + + Assert.Equal(10d, transformed[0], 12); + Assert.Equal(20d, transformed[1], 12); + Assert.Equal(30d, transformed[2], 12); + } + + /// + /// Verifies that a geogoffset step without any configured offsets acts as an identity transform. + /// + [Fact] + public void PipelineWithGeogOffsetWithoutOffsetsActsAsIdentity() + { + const string operation = "+proj=geogoffset"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(GeogOffset3DInput); + + Assert.Equal(GeogOffset3DInput[0], transformed[0], 12); + Assert.Equal(GeogOffset3DInput[1], transformed[1], 12); + Assert.Equal(GeogOffset3DInput[2], transformed[2], 12); + } + + /// + /// Verifies that the molobadekas step correctly applies coordinate frame rotation parameters to the input geocentric coordinates. + /// + [Fact] + public void PipelineWithMolobadekasAppliesCoordinateFrameParameters() + { + const string operation = "+proj=molobadekas +convention=coordinate_frame +x=-270.933 +y=115.599 +z=-360.226 +rx=-5.266 +ry=-1.238 +rz=2.381 +s=-5.109 +px=2464351.59 +py=-5783466.61 +pz=974809.81"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(MolobadekasInput); + + const double toleranceMeters = 0.01d; + Assert.InRange(Math.Abs(transformed[0] - 2550138.45d), 0d, toleranceMeters); + Assert.InRange(Math.Abs(transformed[1] - -5749799.87d), 0d, toleranceMeters); + Assert.InRange(Math.Abs(transformed[2] - 1054530.82d), 0d, toleranceMeters); + } + + /// + /// Verifies that a molobadekas step without a +convention parameter produces a validation failure. + /// + [Fact] + public void PipelineWithMolobadekasMissingConventionReturnsValidationFailure() + { + const string operation = "+proj=molobadekas"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("convention", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that an affine step with default parameters leaves all ordinates unchanged. + /// + [Fact] + public void PipelineWithAffineIdentityLeavesCoordinatesUnchanged() + { + const string operation = "+proj=affine"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(AffineInput4D); + + Assert.Equal(AffineInput4D[0], transformed[0], 12); + Assert.Equal(AffineInput4D[1], transformed[1], 12); + Assert.Equal(AffineInput4D[2], transformed[2], 12); + Assert.Equal(AffineInput4D[3], transformed[3], 12); + } + + /// + /// Verifies that an affine step applies the configured offset and matrix coefficients to all four input ordinates. + /// + [Fact] + public void PipelineWithAffineAppliesConfiguredSpatialAndTemporalTerms() + { + const string operation = "+proj=affine +xoff=1 +yoff=2 +zoff=3 +toff=4 +s11=11 +s12=12 +s13=13 +s21=21 +s22=22 +s23=23 +s31=-31 +s32=32 +s33=33 +tscale=34"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(AffineInput4D); + + Assert.Equal(741d, transformed[0], 12); + Assert.Equal(1352d, transformed[1], 12); + Assert.Equal(1839d, transformed[2], 12); + Assert.Equal(3404d, transformed[3], 12); + } + + /// + /// Verifies that an affine transform and its inverse correctly round-trip all four input ordinates. + /// + [Fact] + public void PipelineWithAffineInverseRoundTrips() + { + const string operation = "+proj=affine +xoff=1 +yoff=2 +zoff=3 +toff=4 +s11=11 +s12=12 +s13=13 +s21=21 +s22=22 +s23=23 +s31=-31 +s32=32 +s33=33 +tscale=34"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] transformed = transform.Transform(AffineInput4D); + double[] roundTripped = transform.Inverse().Transform(transformed); + + Assert.Equal(AffineInput4D[0], roundTripped[0], 9); + Assert.Equal(AffineInput4D[1], roundTripped[1], 9); + Assert.Equal(AffineInput4D[2], roundTripped[2], 9); + Assert.Equal(AffineInput4D[3], roundTripped[3], 9); + } + + /// + /// Verifies that an affine step with a non-invertible matrix produces a validation failure when inverted. + /// + [Fact] + public void PipelineWithAffineNonInvertibleMatrixRejectsInverse() + { + const string operation = "+proj=affine +s11=0 +s22=0 +s23=0 +inv"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("invertible", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that an affine step with a zero time scale produces a validation failure when inverted. + /// + [Fact] + public void PipelineWithAffineZeroTimeScaleRejectsInverse() + { + const string operation = "+proj=affine +tscale=0 +inv"; + + string skipReason = RequirePipelineValidationFailure(operation); + Assert.Contains("tscale", skipReason, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that geographic identity steps honor vertical unit scaling. + /// + [Fact] + public void PipelineWithLonglatVerticalUnitsScalesZ() + { + MathTransform transform = RequirePipelineMathTransform("+proj=longlat +a=1 +b=1 +vto_meter=1000"); + double[] output = transform.Transform([0d, 0d, 1000d]); + + Assert.Equal(0d, output[0], 12); + Assert.Equal(0d, output[1], 12); + Assert.Equal(1d, output[2], 12); + } + + /// + /// Verifies that projected steps honor vertical unit scaling independently from XY projection units. + /// + [Fact] + public void PipelineWithMercatorVerticalUnitsScalesZ() + { + MathTransform transform = RequirePipelineMathTransform("+proj=merc +a=1 +b=1 +vunits=km"); + double[] output = transform.Transform([0d, 0d, 1000d]); + + Assert.Equal(0d, output[0], 12); + Assert.Equal(0d, output[1], 12); + Assert.Equal(1d, output[2], 12); + } + + /// + /// Verifies that PROJ-style ellipsoid precedence treats +ellps as the base definition and applies +a as a later size override. + /// + [Fact] + public void PipelineWithHealpixEllipsoidAndSemiMajorOverridePreservesEllipsoidShape() + { + const string operation = "+proj=healpix +a=1 +ellps=WGS84 +lon_0=0"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] output = transform.Transform([-90d, 0d]); + + Assert.Equal(-1.569040407531329d, output[0], 12); + Assert.Equal(0d, output[1], 12); + } + + /// + /// Verifies that geographic identity steps honor longitude wrapping. + /// + [Fact] + public void PipelineWithLonglatLongitudeWrapNormalizesLongitude() + { + MathTransform transform = RequirePipelineMathTransform("+proj=longlat +ellps=WGS84 +lon_wrap=180"); + double[] output = transform.Transform([-1d, 10d, 0d]); + + Assert.Equal(359d, output[0], 12); + Assert.Equal(10d, output[1], 12); + Assert.Equal(0d, output[2], 12); + } + + /// + /// Verifies that spherical Azimuthal Equidistant keeps sub-metre near-center offsets instead of collapsing to the origin. + /// + [Fact] + public void PipelineWithAeqdStepNearCenterOnSphereMatchesProjReference() + { + const string operation = "+proj=aeqd +a=6371008.771415 +b=6371008.771415 +lat_0=30.2345 +lon_0=-120.2345"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] output = transform.Transform([-120.234501d, 30.234501d]); + + Assert.InRange(Math.Abs(output[0] - (-0.096d)), 0d, 1e-3d); + Assert.InRange(Math.Abs(output[1] - 0.111d), 0d, 1e-3d); + } + + /// + /// Verifies that Azimuthal Equidistant pipeline steps honor the PROJ +guam flag. + /// + [Fact] + public void PipelineWithAeqdGuamStepMatchesProjReference() + { + const string operation = "+proj=aeqd +guam +ellps=clrk66 +x_0=50000 +y_0=50000 +lon_0=144.74875069444445 +lat_0=13.47246633333333"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] output = transform.Transform([144.63533129166666d, 13.33903846111111d]); + + Assert.InRange(Math.Abs(output[0] - 37712.48d), 0d, 1e-2d); + Assert.InRange(Math.Abs(output[1] - 35242.00d), 0d, 1e-2d); + } + + /// + /// Verifies that Airocean pipeline steps honor the horizontal orientation flag. + /// + [Fact] + public void PipelineWithAiroceanHorizontalOrientationMatchesProjReference() + { + const string operation = "+proj=airocean +orient=horizontal +ellps=GRS80"; + + MathTransform transform = RequirePipelineMathTransform(operation); + double[] output = transform.Transform([23d, 28d]); + + Assert.InRange(Math.Abs(output[0] - 13391387.087562159d), 0d, 1e-3d); + Assert.InRange(Math.Abs(output[1] - 13572113.73386754d), 0d, 1e-3d); + } + + private static MathTransform RequirePipelineMathTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static string RequirePipelineValidationFailure(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + Assert.False(ok); + return Assert.IsType(skipReason); + } + + private static string CreateNestedPipelineOperation(int nestedDepth) + { + return nestedDepth <= 0 + ? "+proj=noop" + : $"+proj=pipeline +step {CreateNestedPipelineOperation(nestedDepth - 1)}"; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/SampleEncodingTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/SampleEncodingTests.cs new file mode 100644 index 00000000..c87961a9 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/SampleEncodingTests.cs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using BitMiracle.LibTiff.Classic; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for . +/// +public class SampleEncodingTests +{ + /// + /// Verifies that signed 16-bit samples are decoded correctly. + /// + [Fact] + public void TryCreate_SignedInt16_ReturnsTwoByteReader() + { + bool created = SampleEncoding.TryCreate(16, SampleFormat.INT, out SampleEncoding encoding); + byte[] buffer = CreatePaddedBuffer(BitConverter.GetBytes((short)-12345), 2); + + Assert.True(created); + Assert.Equal(2, encoding.BytesPerSample); + Assert.Equal(-12345d, encoding.ReadValue(buffer, 2)); + } + + /// + /// Verifies that signed 32-bit samples are decoded correctly. + /// + [Fact] + public void TryCreate_SignedInt32_ReturnsFourByteReader() + { + bool created = SampleEncoding.TryCreate(32, SampleFormat.INT, out SampleEncoding encoding); + byte[] buffer = CreatePaddedBuffer(BitConverter.GetBytes(-123456789), 1); + + Assert.True(created); + Assert.Equal(4, encoding.BytesPerSample); + Assert.Equal(-123456789d, encoding.ReadValue(buffer, 1)); + } + + /// + /// Verifies that unsupported signed integer widths are rejected. + /// + [Fact] + public void TryCreate_UnsupportedSignedWidth_ReturnsFalse() + { + bool created = SampleEncoding.TryCreate(8, SampleFormat.INT, out SampleEncoding encoding); + + Assert.False(created); + Assert.Equal(0, encoding.BytesPerSample); + } + + /// + /// Verifies that unsigned 16-bit samples are decoded correctly. + /// + [Fact] + public void TryCreate_UnsignedInt16_ReturnsTwoByteReader() + { + bool created = SampleEncoding.TryCreate(16, SampleFormat.UINT, out SampleEncoding encoding); + byte[] buffer = CreatePaddedBuffer(BitConverter.GetBytes((ushort)54321), 3); + + Assert.True(created); + Assert.Equal(2, encoding.BytesPerSample); + Assert.Equal(54321d, encoding.ReadValue(buffer, 3)); + } + + /// + /// Verifies that unsigned 32-bit samples are decoded correctly. + /// + [Fact] + public void TryCreate_UnsignedInt32_ReturnsFourByteReader() + { + bool created = SampleEncoding.TryCreate(32, SampleFormat.UINT, out SampleEncoding encoding); + byte[] buffer = CreatePaddedBuffer(BitConverter.GetBytes((uint)3456789012), 2); + + Assert.True(created); + Assert.Equal(4, encoding.BytesPerSample); + Assert.Equal(3456789012d, encoding.ReadValue(buffer, 2)); + } + + /// + /// Verifies that unsupported unsigned integer widths are rejected. + /// + [Fact] + public void TryCreate_UnsupportedUnsignedWidth_ReturnsFalse() + { + bool created = SampleEncoding.TryCreate(8, SampleFormat.UINT, out SampleEncoding encoding); + + Assert.False(created); + Assert.Equal(0, encoding.BytesPerSample); + } + + /// + /// Verifies that 32-bit floating-point samples are decoded correctly. + /// + [Fact] + public void TryCreate_Float32_ReturnsFourByteReader() + { + bool created = SampleEncoding.TryCreate(32, SampleFormat.IEEEFP, out SampleEncoding encoding); + byte[] buffer = CreatePaddedBuffer(BitConverter.GetBytes(123.25f), 1); + + Assert.True(created); + Assert.Equal(4, encoding.BytesPerSample); + Assert.Equal(123.25d, encoding.ReadValue(buffer, 1), 6); + } + + /// + /// Verifies that 64-bit floating-point samples are decoded correctly. + /// + [Fact] + public void TryCreate_Float64_ReturnsEightByteReader() + { + bool created = SampleEncoding.TryCreate(64, SampleFormat.IEEEFP, out SampleEncoding encoding); + byte[] buffer = CreatePaddedBuffer(BitConverter.GetBytes(-9876.5d), 4); + + Assert.True(created); + Assert.Equal(8, encoding.BytesPerSample); + Assert.Equal(-9876.5d, encoding.ReadValue(buffer, 4), 12); + } + + /// + /// Verifies that unsupported floating-point widths are rejected. + /// + [Fact] + public void TryCreate_UnsupportedFloatingWidth_ReturnsFalse() + { + bool created = SampleEncoding.TryCreate(16, SampleFormat.IEEEFP, out SampleEncoding encoding); + + Assert.False(created); + Assert.Equal(0, encoding.BytesPerSample); + } + + /// + /// Verifies that unsupported TIFF sample formats are rejected. + /// + [Fact] + public void TryCreate_UnsupportedSampleFormat_ReturnsFalse() + { + bool created = SampleEncoding.TryCreate(16, (SampleFormat)999, out SampleEncoding encoding); + + Assert.False(created); + Assert.Equal(0, encoding.BytesPerSample); + } + + private static byte[] CreatePaddedBuffer(byte[] valueBytes, int offset) + { + byte[] buffer = new byte[offset + valueBytes.Length + 1]; + Array.Copy(valueBytes, 0, buffer, offset, valueBytes.Length); + return buffer; + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/SetRuntimeTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/SetRuntimeTests.cs new file mode 100644 index 00000000..9487a7a5 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/SetRuntimeTests.cs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for set. +/// +public class SetRuntimeTests +{ + /// + /// Verifies that empty set operation behaves as identity. + /// + [Fact] + public void SetWithoutValuesIsIdentity() + { + MathTransform transform = CreateTransform("+proj=set"); + double[] output = transform.Transform(CreatePoint(1d, 2d, 3d)); + + Assert.Equal(1d, output[0], 12); + Assert.Equal(2d, output[1], 12); + Assert.Equal(3d, output[2], 12); + } + + /// + /// Verifies coordinate component overrides for v_1, v_2 and v_3. + /// + [Fact] + public void SetOverridesSpecifiedComponents() + { + MathTransform transform = CreateTransform("+proj=set +v_1=10 +v_2=20 +v_3=30 +v_4=40"); + double[] output = transform.Transform(CreatePoint(1d, 2d, 3d)); + + Assert.Equal(10d, output[0], 12); + Assert.Equal(20d, output[1], 12); + Assert.Equal(30d, output[2], 12); + } + + /// + /// Verifies that set behaves the same in inverse direction. + /// + [Fact] + public void SetInverseUsesSameOverrideRules() + { + MathTransform transform = CreateTransform("+proj=set +v_1=10 +v_2=20 +v_3=30 +v_4=40 +inv"); + double[] output = transform.Transform(CreatePoint(1d, 2d, 3d)); + + Assert.Equal(10d, output[0], 12); + Assert.Equal(20d, output[1], 12); + Assert.Equal(30d, output[2], 12); + } + + /// + /// Verifies coordinate component overrides also apply to the 4th ordinate. + /// + [Fact] + public void SetOverridesFourthComponentForFourDimensionalInput() + { + MathTransform transform = CreateTransform("+proj=set +v_1=10 +v_2=20 +v_3=30 +v_4=40"); + double[] output = transform.Transform([1d, 2d, 3d, 4d]); + + Assert.Equal(10d, output[0], 12); + Assert.Equal(20d, output[1], 12); + Assert.Equal(30d, output[2], 12); + Assert.Equal(40d, output[3], 12); + } + + /// + /// Verifies partial override semantics. + /// + /// Operation text. + /// Expected X output. + /// Expected Y output. + /// Expected Z output. + [Theory] + [InlineData("+proj=set +v_1=11", 11d, 2d, 3d)] + [InlineData("+proj=set +v_2=22", 1d, 22d, 3d)] + [InlineData("+proj=set +v_3=33", 1d, 2d, 33d)] + [InlineData("+proj=set +v_1=11 +v_3=33", 11d, 2d, 33d)] + public void SetCanOverrideSubsetOfComponents(string operation, double expectedX, double expectedY, double expectedZ) + { + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(1d, 2d, 3d)); + + Assert.Equal(expectedX, output[0], 12); + Assert.Equal(expectedY, output[1], 12); + Assert.Equal(expectedZ, output[2], 12); + } + + /// + /// Verifies validation errors for invalid set parameter values. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [InlineData("+proj=set +v_1=abc", "v_1")] + [InlineData("+proj=set +v_2=nan", "v_2")] + [InlineData("+proj=set +v_3=inf", "v_3")] + [InlineData("+proj=set +v_4=abc", "v_4")] + public void SetCreationFailsForInvalidValues(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.Ordinal); + } + + /// + /// Verifies that existing pipeline behavior now honors set overrides. + /// + [Fact] + public void PipelineWithNoopSetAndUnitConvertOverridesZOnly() + { + const string operation = "+proj=pipeline +step +proj=noop +step +proj=set +v_3=17 +step +proj=unitconvert +xy_in=km +xy_out=m"; + + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(1.5d, 2.25d, 9d)); + + Assert.Equal(1500d, output[0], 10); + Assert.Equal(2250d, output[1], 10); + Assert.Equal(17d, output[2], 10); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/TransformCoverageTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/TransformCoverageTests.cs new file mode 100644 index 00000000..5b60302a --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/TransformCoverageTests.cs @@ -0,0 +1,1295 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Geometries; +using Xunit; + +/// +/// Coverage-oriented tests for math transforms with low coverage. +/// Exercises GeocentricLatitude, PrimeMeridian, MathTransform base, +/// MapProjection base, ObTran, VertOffset, and Molodensky transforms. +/// +public class TransformCoverageTests +{ + private const double ParisLongitude = 2.5969213; + + private static readonly double[] OriginPoint = [0d, 0d]; + private static readonly double[] LonLat1045 = [10d, 45d]; + private static readonly double[] LonLat2060 = [20d, 60d]; + private static readonly double[] LonLat1552 = [15d, 52d]; + private static readonly double[] LonLat1653 = [16d, 53d]; + private static readonly double[] LonLat1020 = [10d, 20d]; + private static readonly double[] LonLat2030 = [20d, 30d]; + private static readonly double[] LonLatAlt00100 = [0d, 0d, 100d]; + private static readonly double[] LonLatAlt1045200 = [10d, 45d, 200d]; + + // ────────────────────────────────────────────────────────────────────── + // 1. GeocentricLatitudeMathTransform (43.3 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Forward geocentric latitude on a sphere produces an identity transform. + /// + [Fact] + public void GeocentricLatitudeSphereForwardIsIdentity() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +R=6371000"); + double[] result = transform.Transform([10d, 45d]); + + Assert.Equal(10d, result[0], 10); + Assert.Equal(45d, result[1], 10); + } + + /// + /// Forward geocentric latitude on WGS84 shifts latitude towards equator. + /// + [Fact] + public void GeocentricLatitudeWgs84ForwardReducesLatitude() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +ellps=WGS84"); + double[] result = transform.Transform([0d, 45d]); + + Assert.Equal(0d, result[0], 10); + Assert.True(result[1] < 45d, "Geocentric latitude should be less than geodetic for mid-latitudes."); + Assert.InRange(result[1], 44.7d, 44.9d); + } + + /// + /// At the equator and poles, geocentric latitude equals geodetic. + /// + /// Test latitude. + [Theory] + [InlineData(0d)] + [InlineData(90d)] + [InlineData(-90d)] + public void GeocentricLatitudeForwardPreservesEquatorAndPoles(double latitude) + { + MathTransform transform = CreateGeocTransform("+proj=geoc +ellps=WGS84"); + double[] result = transform.Transform([0d, latitude]); + + Assert.Equal(latitude, result[1], 6); + } + + /// + /// Forward/inverse round-trip recovers the original latitude. + /// + [Fact] + public void GeocentricLatitudeRoundTripRecoversInput() + { + MathTransform forward = CreateGeocTransform("+proj=geoc +ellps=WGS84"); + MathTransform inverse = CreateGeocTransform("+proj=geoc +ellps=WGS84 +inv"); + + double[] source = [12.5d, 48.3d]; + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.Equal(source[0], recovered[0], 10); + Assert.Equal(source[1], recovered[1], 10); + } + + /// + /// Inverse object returned by the transform API round-trips correctly. + /// + [Fact] + public void GeocentricLatitudeInverseMethodRoundTrips() + { + MathTransform forward = CreateGeocTransform("+proj=geoc +ellps=WGS84"); + MathTransform inverse = forward.Inverse(); + + double[] source = [5d, 60d]; + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.Equal(source[0], recovered[0], 10); + Assert.Equal(source[1], recovered[1], 10); + } + + /// + /// DimSource and DimTarget are both 2 for geocentric latitude. + /// + [Fact] + public void GeocentricLatitudeDimensionsAreTwo() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +ellps=WGS84"); + + Assert.Equal(2, transform.DimSource); + Assert.Equal(2, transform.DimTarget); + } + + /// + /// NaN latitude input passes through unchanged. + /// + [Fact] + public void GeocentricLatitudeNaNLatitudePassesThrough() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +ellps=WGS84"); + double[] result = transform.Transform([10d, double.NaN]); + + Assert.Equal(10d, result[0], 10); + Assert.True(double.IsNaN(result[1])); + } + + /// + /// Verifies that the cartesian inverse follows the PROJ Bowring-style formulation for a traced grid-shift intermediate. + /// + [Fact] + public void GeocentricCartesianInverseMatchesProjReferenceForFrenchGridIntermediate() + { + MathTransform inverse = CreatePipelineTransform("+proj=cart +ellps=GRS80 +inv"); + + double[] geographic = inverse.Transform([4581694.457606088d, 401007.47987491556d, 4404282.691540252d]); + + Assert.Equal(5.001999989309562d, geographic[0], 12); + Assert.Equal(43.95199999107799d, geographic[1], 12); + Assert.Equal(41.97526777628809d, geographic[2], 8); + } + + /// + /// Verifies that the cartesian inverse remains accurate for very high-altitude round-trips. + /// + [Fact] + public void GeocentricCartesianHighAltitudeRoundTripRemainsAccurate() + { + MathTransform forward = CreatePipelineTransform("+proj=cart +ellps=WGS84"); + MathTransform inverse = forward.Inverse(); + + double[] source = [12d, 45d, 30000000d]; + double[] cartesian = forward.Transform(source); + double[] roundtrip = inverse.Transform(cartesian); + + double latitudeError = Math.Abs(roundtrip[1] - source[1]); + double heightError = Math.Abs(roundtrip[2] - source[2]); + + Assert.InRange(latitudeError, 0d, 1e-8); + Assert.InRange(heightError, 0d, 0.05d); + } + + // ────────────────────────────────────────────────────────────────────── + // 2. PrimeMeridianTransform (50.0 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Greenwich-to-Greenwich is a no-op. + /// + [Fact] + public void PrimeMeridianGreenwichToGreenwichIsNoOp() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Greenwich); + double[] result = transform.Transform([10d, 50d, 100d]); + + Assert.Equal(10d, result[0], 12); + Assert.Equal(50d, result[1], 12); + Assert.Equal(100d, result[2], 12); + } + + /// + /// Greenwich-to-Paris shifts longitude by the Paris offset. + /// + [Fact] + public void PrimeMeridianGreenwichToParisShiftsLongitude() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + double[] result = transform.Transform([10d, 50d, 0d]); + + double expectedShift = PrimeMeridian.Greenwich.Longitude - PrimeMeridian.Paris.Longitude; + Assert.Equal(10d + expectedShift, result[0], 10); + Assert.Equal(50d, result[1], 12); + } + + /// + /// Inverse reverses the forward shift. + /// + [Fact] + public void PrimeMeridianInverseReversesForward() + { + var forward = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + MathTransform inverse = forward.Inverse(); + + double[] source = [15d, 48d, 0d]; + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.Equal(source[0], recovered[0], 10); + Assert.Equal(source[1], recovered[1], 12); + } + + /// + /// Prime meridian transforms reject in-place inversion because they are immutable. + /// + [Fact] + public void PrimeMeridianInvertThrowsNotSupportedException() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + + Assert.Throws(() => transform.Invert()); + } + + /// + /// DimSource and DimTarget are both 3 for prime meridian transforms. + /// + [Fact] + public void PrimeMeridianDimensionsAreThree() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + + Assert.Equal(3, transform.DimSource); + Assert.Equal(3, transform.DimTarget); + } + + /// + /// Prime meridian transforms reject mixed angular units. + /// + [Fact] + public void PrimeMeridianTransformWithDifferentAngularUnitsThrowsNotSupportedException() + { + var gradMeridian = new PrimeMeridian(2.5969213, AngularUnit.Grad, "Paris grad", "TEST", 1, string.Empty, string.Empty, string.Empty); + + Assert.Throws(() => new PrimeMeridianTransform(PrimeMeridian.Greenwich, gradMeridian)); + } + + /// + /// The default serialization APIs throw as documented. + /// + [Fact] + public void PrimeMeridianTransformUnimplementedSerializationMembersThrowNotSupportedException() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + + Assert.Throws(() => _ = transform.WKT); + Assert.Throws(() => _ = transform.XML); + } + + /// + /// Tests various well-known prime meridians for correct longitude offset. + /// + /// Expected resulting longitude. + [Theory] + [InlineData(-ParisLongitude)] + public void PrimeMeridianVariousMeridiansShiftCorrectly(double expectedLongitude) + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + double[] result = transform.Transform([0d, 0d, 0d]); + + Assert.Equal(expectedLongitude, result[0], 10); + } + + /// + /// Batch transform via span-based API. + /// + [Fact] + public void PrimeMeridianSpanBatchTransform() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + double[] xs = [0d, 10d, 20d]; + double[] ys = [50d, 51d, 52d]; + double[] zs = [0d, 0d, 0d]; + transform.Transform(xs.AsSpan(), ys.AsSpan(), zs.AsSpan()); + + double expectedShift = PrimeMeridian.Greenwich.Longitude - PrimeMeridian.Paris.Longitude; + Assert.Equal(0d + expectedShift, xs[0], 10); + Assert.Equal(10d + expectedShift, xs[1], 10); + Assert.Equal(20d + expectedShift, xs[2], 10); + } + + /// + /// Batch transform via span-based API respects the inverse transform direction. + /// + [Fact] + public void PrimeMeridianSpanBatchTransformWithInverseUsesReverseShift() + { + MathTransform transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris).Inverse(); + double[] xs = [0d, 10d, 20d]; + double[] ys = [50d, 51d, 52d]; + double[] zs = [0d, 0d, 0d]; + + transform.Transform(xs.AsSpan(), ys.AsSpan(), zs.AsSpan()); + + double expectedShift = PrimeMeridian.Paris.Longitude - PrimeMeridian.Greenwich.Longitude; + Assert.Equal(0d + expectedShift, xs[0], 10); + Assert.Equal(10d + expectedShift, xs[1], 10); + Assert.Equal(20d + expectedShift, xs[2], 10); + } + + // ────────────────────────────────────────────────────────────────────── + // 3. MathTransform base class (55.7 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// TransformList processes a batch of points correctly. + /// + [Fact] + public void MathTransformBaseTransformListProcessesBatch() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +ellps=WGS84"); + IList points = new List + { + OriginPoint, + LonLat1045, + LonLat2060, + }; + + IList results = transform.TransformList(points); + + Assert.Equal(3, results.Count); + Assert.Equal(0d, results[0][1], 6); + Assert.True(results[1][1] < 45d); + Assert.True(results[2][1] < 60d); + } + + /// + /// The MathTransform base serialization defaults surface + /// for transform types without dedicated serialization implementations. + /// + [Fact] + public void MathTransformBaseSerializationDefaultsThrowNotSupportedException() + { + var transform = new GeocentricTransform( + [ + new ProjectionParameter("semi_major", Ellipsoid.GRS80.SemiMajorAxis), + new ProjectionParameter("semi_minor", Ellipsoid.GRS80.SemiMinorAxis), + ], + false); + + Assert.Throws(() => _ = transform.WKT); + Assert.Throws(() => _ = transform.XML); + } + + /// + /// Affine transforms keep their XML member but now follow the shared unsupported contract. + /// + [Fact] + public void AffineTransformXml_UsesSharedNotSupportedContract() + { + var transform = new AffineTransform(1d, 0d, 5d, 0d, 1d, 10d); + + Assert.Throws(() => _ = transform.XML); + } + + /// + /// Transform with tuple API returns correct results. + /// + [Fact] + public void MathTransformBaseTupleTransform2D() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +R=6371000"); + (double x, double y) = transform.Transform(10d, 45d); + + Assert.Equal(10d, x, 10); + Assert.Equal(45d, y, 10); + } + + /// + /// Transform with 3D tuple API returns correct results. + /// + [Fact] + public void MathTransformBaseTupleTransform3D() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + (double x, double y, double z) = transform.Transform(10d, 50d, 100d); + + Assert.Equal(50d, y, 12); + Assert.Equal(100d, z, 12); + } + + /// + /// Transform with ref overload modifies values in-place. + /// + [Fact] + public void MathTransformBaseRefTransform() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + double x = 10d; + double y = 50d; + transform.Transform(ref x, ref y); + + double expected = 10d + PrimeMeridian.Greenwich.Longitude - PrimeMeridian.Paris.Longitude; + Assert.Equal(expected, x, 10); + Assert.Equal(50d, y, 12); + } + + /// + /// Span-based Transform overload with XY struct works. + /// + [Fact] + public void MathTransformBaseXYSpanTransform() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +R=6371000"); + XY[] points = [new XY(10d, 45d), new XY(20d, 60d)]; + transform.Transform(points.AsSpan()); + + Assert.Equal(10d, points[0].X, 10); + Assert.Equal(45d, points[0].Y, 10); + } + + /// + /// Span-based Transform overload with XYZ struct works. + /// + [Fact] + public void MathTransformBaseXYZSpanTransform() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + XYZ[] points = [new XYZ(10d, 50d, 100d)]; + transform.Transform(points.AsSpan()); + + double expected = 10d + PrimeMeridian.Greenwich.Longitude - PrimeMeridian.Paris.Longitude; + Assert.Equal(expected, points[0].X, 10); + Assert.Equal(50d, points[0].Y, 12); + Assert.Equal(100d, points[0].Z, 12); + } + + /// + /// Span-based Transform with stride works correctly. + /// + [Fact] + public void MathTransformBaseSpanStrideTransform() + { + var transform = new PrimeMeridianTransform(PrimeMeridian.Greenwich, PrimeMeridian.Paris); + double[] xs = [0d, 999d, 10d, 999d]; + double[] ys = [50d, 999d, 51d, 999d]; + + transform.Transform(xs.AsSpan(), ys.AsSpan(), 2, 2); + + double expectedShift = PrimeMeridian.Greenwich.Longitude - PrimeMeridian.Paris.Longitude; + Assert.Equal(0d + expectedShift, xs[0], 10); + Assert.Equal(10d + expectedShift, xs[2], 10); + } + + /// + /// ReadOnlySpan Transform throws for too-small input. + /// + [Fact] + public void MathTransformBaseSpanTransformThrowsForSingleOrdinate() + { + MathTransform transform = CreateGeocTransform("+proj=geoc +R=6371000"); + double[] input = [42d]; + double[] result = new double[2]; + + Assert.Throws(() => transform.Transform(new ReadOnlySpan(input), result.AsSpan())); + } + + // ────────────────────────────────────────────────────────────────────── + // 4. MapProjection base class (61.1 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Creates a Transverse Mercator via the factory and checks DimSource/DimTarget. + /// + [Fact] + public void MapProjectionTransverseMercatorDimensionsAreTwo() + { + var csFactory = new CoordinateSystemFactory(); + var ctFactory = new CoordinateTransformationFactory(); + GeographicCoordinateSystem wgs84 = GeographicCoordinateSystem.WGS84; + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + ICoordinateTransformation ct = ctFactory.CreateFromCoordinateSystems(wgs84, utm33); + + Assert.Equal(2, ct.MathTransform.DimSource); + Assert.Equal(2, ct.MathTransform.DimTarget); + } + + /// + /// TransformList through a concrete MapProjection transforms multiple points. + /// + [Fact] + public void MapProjectionTransformListBatchProcesses() + { + var csFactory = new CoordinateSystemFactory(); + var ctFactory = new CoordinateTransformationFactory(); + GeographicCoordinateSystem wgs84 = GeographicCoordinateSystem.WGS84; + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + ICoordinateTransformation ct = ctFactory.CreateFromCoordinateSystems(wgs84, utm33); + IList points = new List + { + LonLat1552, + LonLat1653, + }; + + IList results = ct.MathTransform.TransformList(points); + + Assert.Equal(2, results.Count); + Assert.NotEqual(15d, results[0][0]); + Assert.NotEqual(52d, results[0][1]); + } + + /// + /// Forward/inverse round-trip through a MapProjection-based transform. + /// + [Fact] + public void MapProjectionRoundTripRecoversInput() + { + var ctFactory = new CoordinateTransformationFactory(); + GeographicCoordinateSystem wgs84 = GeographicCoordinateSystem.WGS84; + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + ICoordinateTransformation ct = ctFactory.CreateFromCoordinateSystems(wgs84, utm33); + MathTransform forward = ct.MathTransform; + MathTransform inverse = forward.Inverse(); + + double[] source = [15d, 52d]; + double[] projected = forward.Transform(source); + double[] recovered = inverse.Transform(projected); + + Assert.Equal(source[0], recovered[0], 6); + Assert.Equal(source[1], recovered[1], 6); + } + + /// + /// Edge case: transforming the equator/prime meridian intersection. + /// + [Fact] + public void MapProjectionOriginPointTransforms() + { + var ctFactory = new CoordinateTransformationFactory(); + GeographicCoordinateSystem wgs84 = GeographicCoordinateSystem.WGS84; + var utm31 = ProjectedCoordinateSystem.WGS84_UTM(31, true); + + ICoordinateTransformation ct = ctFactory.CreateFromCoordinateSystems(wgs84, utm31); + double[] result = ct.MathTransform.Transform([3d, 0d]); + + Assert.True(result[0] > 0d); + } + + /// + /// NaN input to MapProjection results in NaN output. + /// + [Fact] + public void MapProjectionNaNInputProducesNaN() + { + var ctFactory = new CoordinateTransformationFactory(); + GeographicCoordinateSystem wgs84 = GeographicCoordinateSystem.WGS84; + var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); + + ICoordinateTransformation ct = ctFactory.CreateFromCoordinateSystems(wgs84, utm33); + double[] result = ct.MathTransform.Transform([double.NaN, double.NaN]); + + Assert.True(double.IsNaN(result[0]) || double.IsNaN(result[1])); + } + + // ────────────────────────────────────────────────────────────────────── + // 5. ObTranMathTransform (62.8 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Forward/inverse round-trip for ob_tran with latlon child. + /// + [Fact] + public void ObTranLatLonRoundTripRecoversInput() + { + const string operation = "+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180"; + MathTransform forward = CreatePipelineTransform(operation); + MathTransform inverse = forward.Inverse(); + + double[] source = [2d, 1d]; + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.Equal(source[0], recovered[0], 6); + Assert.Equal(source[1], recovered[1], 6); + } + + /// + /// DimSource and DimTarget are both 2 for ob_tran. + /// + [Fact] + public void ObTranDimensionsAreTwo() + { + const string operation = "+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180"; + MathTransform transform = CreatePipelineTransform(operation); + + Assert.Equal(2, transform.DimSource); + Assert.Equal(2, transform.DimTarget); + } + + /// + /// Ob_tran with Mollweide child — batch transform via TransformList. + /// + [Fact] + public void ObTranMollTransformListBatch() + { + const string operation = "+proj=ob_tran +o_proj=moll +R=6378137.0 +o_lon_p=0 +o_lat_p=0 +lon_0=180"; + MathTransform transform = CreatePipelineTransform(operation); + + IList points = new List + { + LonLat1020, + LonLat2030, + }; + + IList results = transform.TransformList(points); + + Assert.Equal(2, results.Count); + Assert.NotEqual(10d, results[0][0]); + } + + /// + /// Ob_tran with o_alpha rotation parameter. + /// + [Fact] + public void ObTranWithAlphaCreatesSuccessfully() + { + const string operation = "+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_c=0 +o_lat_c=30 +o_alpha=45"; + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform( + operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + double[] result = Assert.IsType(transform, exactMatch: false).Transform([10d, 20d]); + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + /// + /// Missing required o_proj parameter fails gracefully. + /// + [Fact] + public void ObTranMissingOProjFails() + { + const string operation = "+proj=ob_tran +R=6400000"; + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform( + operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.NotNull(skipReason); + } + + /// + /// Inverse flag in the operation string creates the inverse transform. + /// + [Fact] + public void ObTranInvFlagCreatesInverseTransform() + { + const string forwardOp = "+proj=ob_tran +R=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180"; + const string inverseOp = forwardOp + " +inv"; + + MathTransform forward = CreatePipelineTransform(forwardOp); + MathTransform inverse = CreatePipelineTransform(inverseOp); + + double[] source = [2d, 1d]; + double[] fwdResult = forward.Transform(source); + double[] recovered = inverse.Transform(fwdResult); + + Assert.Equal(source[0], recovered[0], 6); + Assert.Equal(source[1], recovered[1], 6); + } + + // ────────────────────────────────────────────────────────────────────── + // 6. VertOffsetMathTransform (62.7 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Forward/inverse round-trip preserves coordinates. + /// + [Fact] + public void VertOffsetRoundTripRecoversInput() + { + const string operation = "+proj=vertoffset +lat_0=46.9166666666666666 +lon_0=8.183333333333334 +dh=-0.245 +slope_lat=-0.210 +slope_lon=-0.032 +ellps=GRS80"; + MathTransform forward = CreatePipelineTransform(operation); + MathTransform inverse = forward.Inverse(); + + double[] source = [9.666666666666666d, 47.333333333333336d, 473.0d]; + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.Equal(source[0], recovered[0], 10); + Assert.Equal(source[1], recovered[1], 10); + Assert.Equal(source[2], recovered[2], 3); + } + + /// + /// DimSource and DimTarget are both 3 for vertoffset. + /// + [Fact] + public void VertOffsetDimensionsAreThree() + { + MathTransform transform = CreatePipelineTransform("+proj=vertoffset +ellps=GRS80"); + + Assert.Equal(3, transform.DimSource); + Assert.Equal(3, transform.DimTarget); + } + + /// + /// NaN z-input defaults to 0 before applying offset. + /// + [Fact] + public void VertOffsetNaNZInputDefaultsToZero() + { + const string operation = "+proj=vertoffset +dh=10 +ellps=GRS80"; + MathTransform transform = CreatePipelineTransform(operation); + double[] result = transform.Transform([0d, 0d, double.NaN]); + + Assert.False(double.IsNaN(result[2])); + Assert.InRange(result[2], 9.5d, 10.5d); + } + + /// + /// VertOffset rejects in-place inversion because the transform is immutable. + /// + [Fact] + public void VertOffsetInvertThrowsNotSupportedException() + { + const string operation = "+proj=vertoffset +dh=10 +ellps=GRS80"; + MathTransform transform = CreatePipelineTransform(operation); + + Assert.Throws(() => transform.Invert()); + } + + /// + /// TransformList processes batch for vertoffset. + /// + [Fact] + public void VertOffsetTransformListBatch() + { + const string operation = "+proj=vertoffset +dh=5 +ellps=GRS80"; + MathTransform transform = CreatePipelineTransform(operation); + + IList points = new List + { + LonLatAlt00100, + LonLatAlt1045200, + }; + + IList results = transform.TransformList(points); + + Assert.Equal(2, results.Count); + Assert.InRange(results[0][2], 104d, 106d); + Assert.InRange(results[1][2], 204d, 206d); + } + + /// + /// Ellipsoid resolution via +r (sphere radius) parameter. + /// + [Fact] + public void VertOffsetSphereRadiusWorks() + { + const string operation = "+proj=vertoffset +r=6371000 +dh=2.5"; + MathTransform transform = CreatePipelineTransform(operation); + double[] result = transform.Transform([10d, 45d, 50d]); + + Assert.InRange(result[2], 52d, 53d); + } + + /// + /// Ellipsoid resolution via +a +rf (semi-major and inverse flattening). + /// + [Fact] + public void VertOffsetSemiMajorInverseFlatteningWorks() + { + const string operation = "+proj=vertoffset +a=6378137 +rf=298.257223563 +dh=3"; + MathTransform transform = CreatePipelineTransform(operation); + double[] result = transform.Transform([0d, 0d, 100d]); + + Assert.InRange(result[2], 102.5d, 103.5d); + } + + // ────────────────────────────────────────────────────────────────────── + // 7. MolodenskyMathTransform (71.7 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Standard Molodensky round-trip (forward then inverse) recovers input. + /// + [Fact] + public void MolodenskyStandardRoundTripRecoversInput() + { + const string forwardOp = "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149"; + MathTransform forward = CreatePipelineTransform(forwardOp); + MathTransform inverse = forward.Inverse(); + + double[] source = [144.9667d, -37.8d, 50d]; + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.Equal(source[0], recovered[0], 3); + Assert.Equal(source[1], recovered[1], 3); + Assert.InRange(Math.Abs(recovered[2] - source[2]), 0d, 1d); + } + + /// + /// Abridged Molodensky produces slightly different results from standard. + /// + [Fact] + public void MolodenskyAbridgedDiffersFromStandard() + { + const string standard = "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149"; + const string abridged = standard + " +abridged"; + + MathTransform stdTransform = CreatePipelineTransform(standard); + MathTransform abrTransform = CreatePipelineTransform(abridged); + + double[] source = [144.9667d, -37.8d, 50d]; + double[] stdResult = stdTransform.Transform(source); + double[] abrResult = abrTransform.Transform(source); + + // Both should produce similar results + Assert.InRange(Math.Abs(stdResult[0] - abrResult[0]), 0d, 0.01d); + Assert.InRange(Math.Abs(stdResult[1] - abrResult[1]), 0d, 0.01d); + } + + /// + /// DimSource and DimTarget are both 3 for Molodensky. + /// + [Fact] + public void MolodenskyDimensionsAreThree() + { + MathTransform transform = CreatePipelineTransform( + "+proj=molodensky +a=6378160 +rf=298.25 +da=0 +df=0 +dx=0 +dy=0 +dz=0"); + + Assert.Equal(3, transform.DimSource); + Assert.Equal(3, transform.DimTarget); + } + + /// + /// Molodensky handles NaN z-input by treating it as zero. + /// + [Fact] + public void MolodenskyNaNZInputDefaultsToZero() + { + MathTransform transform = CreatePipelineTransform( + "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149"); + double[] result = transform.Transform([144.9667d, -37.8d, double.NaN]); + + Assert.False(double.IsNaN(result[0])); + Assert.False(double.IsNaN(result[1])); + } + + /// + /// Molodensky TransformList batch processing. + /// + [Fact] + public void MolodenskyTransformListBatch() + { + MathTransform transform = CreatePipelineTransform( + "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149"); + + IList points = new List + { + new[] { 144.9667d, -37.8d, 50d }, + new[] { 150d, -33.5d, 100d }, + }; + + IList results = transform.TransformList(points); + + Assert.Equal(2, results.Count); + Assert.NotEqual(144.9667d, results[0][0]); + Assert.NotEqual(150d, results[1][0]); + } + + /// + /// Molodensky rejects in-place inversion because the transform is immutable. + /// + [Fact] + public void MolodenskyInvertThrowsNotSupportedException() + { + MathTransform transform = CreatePipelineTransform( + "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149"); + + Assert.Throws(() => transform.Invert()); + } + + /// + /// Molodensky with sphere radius (+r) resolves correctly. + /// + [Fact] + public void MolodenskySphereRadiusCreatesSuccessfully() + { + const string operation = "+proj=molodensky +r=6371000 +da=0 +df=0 +dx=1 +dy=2 +dz=3"; + MathTransform transform = CreatePipelineTransform(operation); + double[] result = transform.Transform([0d, 0d, 0d]); + + Assert.False(double.IsNaN(result[0])); + } + + /// + /// Molodensky with +a +b resolves ellipsoid correctly. + /// + [Fact] + public void MolodenskySemiMajorMinorCreatesSuccessfully() + { + const string operation = "+proj=molodensky +a=6378137 +b=6356752.314245 +da=0 +df=0 +dx=1 +dy=2 +dz=3"; + MathTransform transform = CreatePipelineTransform(operation); + double[] result = transform.Transform([10d, 45d, 100d]); + + Assert.False(double.IsNaN(result[0])); + } + + // ────────────────────────────────────────────────────────────────────── + // 8. UnitConvertMathTransform (62.2 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Two-dimensional unit conversion scales only X/Y and leaves Z unchanged. + /// + [Fact] + public void UnitConvertTwoDimensionalTransformLeavesZUnchanged() + { + var transform = new UnitConvertMathTransform(2, 2d, 5d); + double x = 3d; + double y = 4d; + double z = 7d; + + transform.Transform(ref x, ref y, ref z); + + Assert.Equal(6d, x, 12); + Assert.Equal(8d, y, 12); + Assert.Equal(7d, z, 12); + } + + /// + /// Two-dimensional identity detection ignores the Z scale factor. + /// + [Fact] + public void UnitConvertTwoDimensionalIdentityIgnoresZScale() + { + var transform = new UnitConvertMathTransform(2, 1d, 5d); + + Assert.True(transform.Identity()); + } + + /// + /// The inverse transform uses the reciprocal XY/Z scale factors. + /// + [Fact] + public void UnitConvertInverseReturnsReciprocalTransform() + { + var transform = new UnitConvertMathTransform(3, 2d, 4d); + MathTransform inverse = transform.Inverse(); + + double[] result = inverse.Transform([8d, 12d, 20d]); + + Assert.Equal(4d, result[0], 12); + Assert.Equal(6d, result[1], 12); + Assert.Equal(5d, result[2], 12); + } + + /// + /// In-place inversion mutates the conversion scale factors. + /// + [Fact] + public void UnitConvertInvertMutatesScaleFactors() + { + var transform = new UnitConvertMathTransform(3, 2d, 4d); + + transform.Invert(); + + double[] result = transform.Transform([8d, 12d, 20d]); + Assert.Equal(4d, result[0], 12); + Assert.Equal(6d, result[1], 12); + Assert.Equal(5d, result[2], 12); + } + + /// + /// Invalid dimensions are rejected. + /// + [Fact] + public void UnitConvertInvalidDimensionThrowsArgumentOutOfRangeException() + { + ArgumentOutOfRangeException exception = Assert.Throws( + () => new UnitConvertMathTransform(4, 1d, 1d)); + + Assert.Equal("dimension", exception.ParamName); + } + + /// + /// Invalid XY scales are rejected. + /// + /// Invalid XY scale. + [Theory] + [InlineData(0d)] + [InlineData(-1d)] + [InlineData(double.NaN)] + [InlineData(double.PositiveInfinity)] + public void UnitConvertInvalidXyScaleThrowsArgumentOutOfRangeException(double xyScale) + { + ArgumentOutOfRangeException exception = Assert.Throws( + () => new UnitConvertMathTransform(3, xyScale, 1d)); + + Assert.Equal("xyScale", exception.ParamName); + } + + /// + /// Invalid Z scales are rejected. + /// + /// Invalid Z scale. + [Theory] + [InlineData(0d)] + [InlineData(-1d)] + [InlineData(double.NaN)] + [InlineData(double.PositiveInfinity)] + public void UnitConvertInvalidZScaleThrowsArgumentOutOfRangeException(double zScale) + { + ArgumentOutOfRangeException exception = Assert.Throws( + () => new UnitConvertMathTransform(3, 1d, zScale)); + + Assert.Equal("zScale", exception.ParamName); + } + + // ────────────────────────────────────────────────────────────────────── + // 9. PipelineOmitMathTransform (54.8 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Forward omission bypasses the wrapped transform for 3D coordinates. + /// + [Fact] + public void PipelineOmitSkipForwardBypassesInnerTransform() + { + var transform = new PipelineOmitMathTransform(new TrackingMathTransform(2d, 5d), skipForward: true, skipInverse: false); + double x = 1d; + double y = 2d; + double z = 3d; + + transform.Transform(ref x, ref y, ref z); + + Assert.Equal(1d, x, 12); + Assert.Equal(2d, y, 12); + Assert.Equal(3d, z, 12); + } + + /// + /// When forward omission is disabled, the wrapped transform is executed. + /// + [Fact] + public void PipelineOmitWithoutSkipForwardExecutesInnerTransform() + { + var transform = new PipelineOmitMathTransform(new TrackingMathTransform(2d, 5d), skipForward: false, skipInverse: false); + double x = 1d; + double y = 2d; + double z = 3d; + + transform.Transform(ref x, ref y, ref z); + + Assert.Equal(3d, x, 12); + Assert.Equal(4d, y, 12); + Assert.Equal(5d, z, 12); + } + + /// + /// A skipped forward step reports itself as an identity transform. + /// + [Fact] + public void PipelineOmitIdentityReturnsTrueWhenForwardIsSkipped() + { + var transform = new PipelineOmitMathTransform(new TrackingMathTransform(2d, 5d), skipForward: true, skipInverse: false); + + Assert.True(transform.Identity()); + } + + /// + /// The inverse is cached and swaps the forward/inverse omission flags. + /// + [Fact] + public void PipelineOmitInverseCachesAndSwapsSkipFlags() + { + var transform = new PipelineOmitMathTransform(new TrackingMathTransform(2d, 5d), skipForward: true, skipInverse: false); + MathTransform inverse = transform.Inverse(); + + Assert.Same(inverse, transform.Inverse()); + + double x = 1d; + double y = 2d; + double z = 3d; + double t = 4d; + inverse.Transform(ref x, ref y, ref z, ref t); + + Assert.Equal(-1d, x, 12); + Assert.Equal(0d, y, 12); + Assert.Equal(1d, z, 12); + Assert.Equal(-1d, t, 12); + } + + /// + /// Forward omission bypasses the wrapped transform for 4D coordinates as well. + /// + [Fact] + public void PipelineOmitSkipForwardBypassesInnerTransformForFourDimensions() + { + var transform = new PipelineOmitMathTransform(new TrackingMathTransform(2d, 5d), skipForward: true, skipInverse: false); + double x = 1d; + double y = 2d; + double z = 3d; + double t = 4d; + + transform.Transform(ref x, ref y, ref z, ref t); + + Assert.Equal(1d, x, 12); + Assert.Equal(2d, y, 12); + Assert.Equal(3d, z, 12); + Assert.Equal(4d, t, 12); + } + + /// + /// In-place inversion is intentionally not supported. + /// + [Fact] + public void PipelineOmitInvertThrowsNotSupportedException() + { + var transform = new PipelineOmitMathTransform(new TrackingMathTransform(2d, 5d), skipForward: true, skipInverse: false); + + Assert.Throws(() => transform.Invert()); + } + + // ────────────────────────────────────────────────────────────────────── + // 10. GeographicTransform (60.0 %) + // ────────────────────────────────────────────────────────────────────── + + /// + /// Greenwich-to-Paris shifts the longitude by the Paris prime meridian offset. + /// + [Fact] + public void GeographicTransformGreenwichToParisShiftsLongitudeInDegrees() + { + GeographicCoordinateSystem source = CreateGeographicCoordinateSystem(PrimeMeridian.Greenwich); + GeographicCoordinateSystem target = CreateGeographicCoordinateSystem(PrimeMeridian.Paris); + var transform = new GeographicTransform(source, target); + double x = 0d; + double y = 1d; + double z = 2d; + + transform.Transform(ref x, ref y, ref z); + + Assert.Equal(PrimeMeridian.Paris.Longitude, x, 12); + Assert.Equal(1d, y, 12); + Assert.Equal(2d, z, 12); + } + + /// + /// Paris-to-Greenwich removes the Paris prime meridian offset. + /// + [Fact] + public void GeographicTransformParisToGreenwichRemovesPrimeMeridianOffset() + { + GeographicCoordinateSystem source = CreateGeographicCoordinateSystem(PrimeMeridian.Paris); + GeographicCoordinateSystem target = CreateGeographicCoordinateSystem(PrimeMeridian.Greenwich); + var transform = new GeographicTransform(source, target); + double x = 10d; + double y = 0d; + double z = 0d; + + transform.Transform(ref x, ref y, ref z); + + Assert.Equal(10d - PrimeMeridian.Paris.Longitude, x, 12); + } + + /// + /// Source and target dimensions mirror the wrapped geographic coordinate systems. + /// + [Fact] + public void GeographicTransformDimensionsMatchCoordinateSystems() + { + GeographicCoordinateSystem source = CreateGeographicCoordinateSystem(PrimeMeridian.Greenwich); + GeographicCoordinateSystem target = CreateGeographicCoordinateSystem(PrimeMeridian.Paris); + var transform = new GeographicTransform(source, target); + + Assert.Equal(source.Dimension, transform.DimSource); + Assert.Equal(target.Dimension, transform.DimTarget); + } + + /// + /// Geographic transform serialization follows the math-transform defaults and the inverse reverses the longitude shift. + /// + [Fact] + public void GeographicTransformSerializationDefaultsAndInverseAreConsistent() + { + GeographicCoordinateSystem source = CreateGeographicCoordinateSystem(PrimeMeridian.Greenwich); + GeographicCoordinateSystem target = CreateGeographicCoordinateSystem(PrimeMeridian.Paris); + var transform = new GeographicTransform(source, target); + MathTransform inverse = transform.Inverse(); + double x = 10d; + double y = 1d; + double z = 2d; + + Assert.Throws(() => _ = transform.WKT); + Assert.Throws(() => _ = transform.XML); + + transform.Transform(ref x, ref y, ref z); + inverse.Transform(ref x, ref y, ref z); + + Assert.Equal(10d, x, 12); + Assert.Equal(1d, y, 12); + Assert.Equal(2d, z, 12); + Assert.Throws(() => transform.Invert()); + } + + // ────────────────────────────────────────────────────────────────────── + // Helpers + // ────────────────────────────────────────────────────────────────────── + private static MathTransform CreatePipelineTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform( + operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static MathTransform CreateGeocTransform(string operation) + { + return CreatePipelineTransform(operation); + } + + private static GeographicCoordinateSystem CreateGeographicCoordinateSystem(PrimeMeridian primeMeridian) + { + var coordinateSystemFactory = new CoordinateSystemFactory(); + return coordinateSystemFactory.CreateGeographicCoordinateSystem( + $"{primeMeridian.Name} test GCS", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + primeMeridian, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + private sealed class TrackingMathTransform : MathTransform + { + private readonly double xyzDelta; + private readonly double timeDelta; + + internal TrackingMathTransform(double xyzDelta, double timeDelta) + { + this.xyzDelta = xyzDelta; + this.timeDelta = timeDelta; + } + + public override int DimSource => 3; + + public override int DimTarget => 3; + + public override string WKT => string.Empty; + + public override string XML => string.Empty; + + public override bool Identity() + { + return this.xyzDelta == 0d && this.timeDelta == 0d; + } + + public override MathTransform Inverse() + { + return new TrackingMathTransform(-this.xyzDelta, -this.timeDelta); + } + + public override void Invert() + { + throw new NotSupportedException(); + } + + public override void Transform(ref double x, ref double y, ref double z) + { + x += this.xyzDelta; + y += this.xyzDelta; + z += this.xyzDelta; + } + + internal override void Transform(ref double x, ref double y, ref double z, ref double t) + { + this.Transform(ref x, ref y, ref z); + t += this.timeDelta; + } + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Transformations/TransformationMathTests.cs b/test/ProjNet.Tests/CoordinateSystems/Transformations/TransformationMathTests.cs new file mode 100644 index 00000000..6b78939b --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Transformations/TransformationMathTests.cs @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.CoordinateSystems.Transformations; + +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for static helper methods. +/// +public class TransformationMathTests +{ + // ---- IsFinite ---- + + /// + /// Verifies that finite values are recognized. + /// + [Theory] + [InlineData(0.0)] + [InlineData(1.0)] + [InlineData(-1.0)] + [InlineData(double.MaxValue)] + [InlineData(double.MinValue)] + [InlineData(double.Epsilon)] + public void IsFinite_FiniteValue_ReturnsTrue(double value) + { + Assert.True(TransformationMath.IsFinite(value)); + } + + /// + /// Verifies that NaN is not finite. + /// + [Fact] + public void IsFinite_NaN_ReturnsFalse() + { + Assert.False(TransformationMath.IsFinite(double.NaN)); + } + + /// + /// Verifies that positive infinity is not finite. + /// + [Fact] + public void IsFinite_PositiveInfinity_ReturnsFalse() + { + Assert.False(TransformationMath.IsFinite(double.PositiveInfinity)); + } + + /// + /// Verifies that negative infinity is not finite. + /// + [Fact] + public void IsFinite_NegativeInfinity_ReturnsFalse() + { + Assert.False(TransformationMath.IsFinite(double.NegativeInfinity)); + } + + // ---- IsValidObservationEpoch ---- + + /// + /// Verifies that a normal finite epoch that differs from the sentinel is valid. + /// + [Fact] + public void IsValidObservationEpoch_ValidEpoch_ReturnsTrue() + { + Assert.True(TransformationMath.IsValidObservationEpoch(2020.5, 0.0)); + } + + /// + /// Verifies that NaN is not a valid observation epoch. + /// + [Fact] + public void IsValidObservationEpoch_NaN_ReturnsFalse() + { + Assert.False(TransformationMath.IsValidObservationEpoch(double.NaN, 0.0)); + } + + /// + /// Verifies that infinity is not a valid observation epoch. + /// + [Fact] + public void IsValidObservationEpoch_Infinity_ReturnsFalse() + { + Assert.False(TransformationMath.IsValidObservationEpoch(double.PositiveInfinity, 0.0)); + } + + /// + /// Verifies that the missing sentinel value is not a valid observation epoch. + /// + [Fact] + public void IsValidObservationEpoch_MissingSentinel_ReturnsFalse() + { + const double sentinel = -999.0; + + Assert.False(TransformationMath.IsValidObservationEpoch(sentinel, sentinel)); + } + + /// + /// Verifies that zero is valid when the sentinel is a different value. + /// + [Fact] + public void IsValidObservationEpoch_ZeroWithNonZeroSentinel_ReturnsTrue() + { + Assert.True(TransformationMath.IsValidObservationEpoch(0.0, -1.0)); + } + + /// + /// Verifies that zero is invalid when the sentinel is also zero. + /// + [Fact] + public void IsValidObservationEpoch_ZeroWithZeroSentinel_ReturnsFalse() + { + Assert.False(TransformationMath.IsValidObservationEpoch(0.0, 0.0)); + } + + // ---- NormalizeLongitudeDegrees ---- + + /// + /// Verifies that values within [-180, 180] are returned unchanged. + /// + [Theory] + [InlineData(0.0)] + [InlineData(45.0)] + [InlineData(-45.0)] + [InlineData(180.0)] + [InlineData(-180.0)] + [InlineData(90.0)] + public void NormalizeLongitudeDegrees_InRange_ReturnsUnchanged(double longitude) + { + Assert.Equal(longitude, TransformationMath.NormalizeLongitudeDegrees(longitude), 12); + } + + /// + /// Verifies normalization of positive values exceeding 180. + /// + [Theory] + [InlineData(270.0, -90.0)] + [InlineData(360.0, 0.0)] + [InlineData(540.0, 180.0)] + [InlineData(181.0, -179.0)] + public void NormalizeLongitudeDegrees_PositiveExcess_NormalizesCorrectly(double input, double expected) + { + Assert.Equal(expected, TransformationMath.NormalizeLongitudeDegrees(input), 12); + } + + /// + /// Verifies normalization of negative values below -180. + /// + [Theory] + [InlineData(-270.0, 90.0)] + [InlineData(-360.0, 0.0)] + [InlineData(-540.0, -180.0)] + [InlineData(-181.0, 179.0)] + public void NormalizeLongitudeDegrees_NegativeExcess_NormalizesCorrectly(double input, double expected) + { + Assert.Equal(expected, TransformationMath.NormalizeLongitudeDegrees(input), 12); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/UnitTests.cs b/test/ProjNet.Tests/CoordinateSystems/UnitTests.cs new file mode 100644 index 00000000..77ae7f65 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/UnitTests.cs @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for and . +/// +public class UnitTests +{ + // ---- LinearUnit predefined constants ---- + + /// + /// Verifies that the metre constant has a conversion factor of 1. + /// + [Fact] + public void LinearUnit_Metre_MetersPerUnitIsOne() + { + Assert.Equal(1.0, LinearUnit.Metre.MetersPerUnit); + } + + /// + /// Verifies that the foot constant has the correct conversion factor. + /// + [Fact] + public void LinearUnit_Foot_MetersPerUnitIsCorrect() + { + Assert.Equal(0.3048, LinearUnit.Foot.MetersPerUnit, 12); + } + + /// + /// Verifies that the US survey foot constant has the correct conversion factor. + /// + [Fact] + public void LinearUnit_USSurveyFoot_MetersPerUnitIsCorrect() + { + Assert.Equal(0.304800609601219, LinearUnit.USSurveyFoot.MetersPerUnit, 12); + } + + /// + /// Verifies that the nautical mile constant has a conversion factor of 1852. + /// + [Fact] + public void LinearUnit_NauticalMile_MetersPerUnitIsCorrect() + { + Assert.Equal(1852.0, LinearUnit.NauticalMile.MetersPerUnit); + } + + /// + /// Verifies that Clarke's foot constant has the correct conversion factor. + /// + [Fact] + public void LinearUnit_ClarkesFoot_MetersPerUnitIsCorrect() + { + Assert.Equal(0.3047972654, LinearUnit.ClarkesFoot.MetersPerUnit, 12); + } + + // ---- LinearUnit construction ---- + + /// + /// Verifies that the constructor sets MetersPerUnit. + /// + [Fact] + public void LinearUnit_Constructor_SetsMetersPerUnit() + { + var unit = new LinearUnit(0.9144, "yard", "EPSG", 9096, "yd", string.Empty, string.Empty); + + Assert.Equal(0.9144, unit.MetersPerUnit, 12); + } + + // ---- LinearUnit WKT ---- + + /// + /// Verifies that WKT output contains unit name and conversion factor. + /// + [Fact] + public void LinearUnit_WKT_ContainsNameAndMetersPerUnit() + { + string wkt = LinearUnit.Metre.WKT; + + Assert.Contains("UNIT[", wkt, StringComparison.Ordinal); + Assert.Contains("\"metre\"", wkt, StringComparison.Ordinal); + Assert.Contains("1", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT output includes authority when set. + /// + [Fact] + public void LinearUnit_WKT_WithAuthority_ContainsAuthority() + { + string wkt = LinearUnit.Metre.WKT; + + Assert.Contains("AUTHORITY[\"EPSG\", \"9001\"]", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT output omits authority when not set. + /// + [Fact] + public void LinearUnit_WKT_WithoutAuthority_OmitsAuthority() + { + var unit = new LinearUnit(1.0, "test", string.Empty, -1, string.Empty, string.Empty, string.Empty); + string wkt = unit.WKT; + + Assert.DoesNotContain("AUTHORITY", wkt, StringComparison.Ordinal); + } + + // ---- LinearUnit XML ---- + + /// + /// Verifies that XML output contains the MetersPerUnit attribute. + /// + [Fact] + public void LinearUnit_XML_ContainsMetersPerUnit() + { + string xml = LinearUnit.Metre.XML; + + Assert.Contains("CS_LinearUnit", xml, StringComparison.Ordinal); + Assert.Contains("MetersPerUnit=\"1\"", xml, StringComparison.Ordinal); + } + + /// + /// Verifies that matches the XML property. + /// + [Fact] + public void LinearUnit_ToXml_MatchesXmlProperty() + { + LinearUnit unit = LinearUnit.USSurveyFoot; + XElement element = unit.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(unit.XML), element)); + } + + /// + /// Verifies that the linear unit WKT node includes authority information when it is available. + /// + [Fact] + public void LinearUnit_ToWktNode_WithAuthority_IncludesAuthorityNode() + { + LinearUnit unit = LinearUnit.Metre; + WktKeywordNode node = Assert.IsType(unit.ToWktNode()); + + Assert.Equal("UNIT", node.Keyword); + Assert.Equal(3, node.Children.Count); + Assert.Equal("AUTHORITY", Assert.IsType(node.Children[2]).Keyword); + } + + /// + /// Verifies that the linear unit WKT node omits authority information when it is unavailable. + /// + [Fact] + public void LinearUnit_ToWktNode_WithoutAuthority_OmitsAuthorityNode() + { + LinearUnit unit = new(1.0, "test", string.Empty, -1, string.Empty, string.Empty, string.Empty); + WktKeywordNode node = Assert.IsType(unit.ToWktNode()); + + Assert.Equal(2, node.Children.Count); + } + + // ---- LinearUnit EqualParams ---- + + /// + /// Verifies that EqualParams returns true for units with the same conversion factor. + /// + [Fact] + public void LinearUnit_EqualParams_SameMetersPerUnit_ReturnsTrue() + { + var a = new LinearUnit(1.0, "metre", "EPSG", 9001, string.Empty, string.Empty, string.Empty); + var b = new LinearUnit(1.0, "meter", "OTHER", 1, string.Empty, string.Empty, string.Empty); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for units with different conversion factors. + /// + [Fact] + public void LinearUnit_EqualParams_DifferentMetersPerUnit_ReturnsFalse() + { + Assert.False(LinearUnit.Metre.EqualParams(LinearUnit.Foot)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void LinearUnit_EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(LinearUnit.Metre.EqualParams("not a unit")); + } + + // ---- AngularUnit predefined constants ---- + + /// + /// Verifies that the radian constant has a conversion factor of 1. + /// + [Fact] + public void AngularUnit_Radian_RadiansPerUnitIsOne() + { + Assert.Equal(1.0, AngularUnit.Radian.RadiansPerUnit); + } + + /// + /// Verifies that the degree constant has the correct conversion factor. + /// + [Fact] + public void AngularUnit_Degrees_RadiansPerUnitIsCorrect() + { + Assert.Equal(Math.PI / 180.0, AngularUnit.Degrees.RadiansPerUnit, 15); + } + + /// + /// Verifies that the grad constant has the correct conversion factor. + /// + [Fact] + public void AngularUnit_Grad_RadiansPerUnitIsCorrect() + { + Assert.Equal(Math.PI / 200.0, AngularUnit.Grad.RadiansPerUnit, 15); + } + + /// + /// Verifies that the gon constant has the correct conversion factor (equal to grad). + /// + [Fact] + public void AngularUnit_Gon_RadiansPerUnitEqualsGrad() + { + Assert.Equal(AngularUnit.Grad.RadiansPerUnit, AngularUnit.Gon.RadiansPerUnit); + } + + // ---- AngularUnit construction ---- + + /// + /// Verifies that the public constructor sets RadiansPerUnit. + /// + [Fact] + public void AngularUnit_Constructor_SetsRadiansPerUnit() + { + var unit = new AngularUnit(0.5); + + Assert.Equal(0.5, unit.RadiansPerUnit); + } + + // ---- AngularUnit WKT ---- + + /// + /// Verifies that WKT output contains unit name and radians per unit. + /// + [Fact] + public void AngularUnit_WKT_ContainsNameAndRadiansPerUnit() + { + string wkt = AngularUnit.Degrees.WKT; + + Assert.Contains("UNIT[", wkt, StringComparison.Ordinal); + Assert.Contains("\"degree\"", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT output includes authority when set. + /// + [Fact] + public void AngularUnit_WKT_WithAuthority_ContainsAuthority() + { + string wkt = AngularUnit.Degrees.WKT; + + Assert.Contains("AUTHORITY[\"EPSG\", \"9102\"]", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that WKT output omits authority for a simple instance. + /// + [Fact] + public void AngularUnit_WKT_PublicConstructor_OmitsAuthority() + { + var unit = new AngularUnit(0.5); + string wkt = unit.WKT; + + Assert.DoesNotContain("AUTHORITY", wkt, StringComparison.Ordinal); + } + + // ---- AngularUnit XML ---- + + /// + /// Verifies that XML output contains the RadiansPerUnit attribute. + /// + [Fact] + public void AngularUnit_XML_ContainsRadiansPerUnit() + { + string xml = AngularUnit.Radian.XML; + + Assert.Contains("CS_AngularUnit", xml, StringComparison.Ordinal); + Assert.Contains("RadiansPerUnit=\"1\"", xml, StringComparison.Ordinal); + } + + /// + /// Verifies that matches the XML property. + /// + [Fact] + public void AngularUnit_ToXml_MatchesXmlProperty() + { + AngularUnit unit = AngularUnit.Grad; + XElement element = unit.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(unit.XML), element)); + } + + /// + /// Verifies that the angular unit WKT node includes authority information when it is available. + /// + [Fact] + public void AngularUnit_ToWktNode_WithAuthority_IncludesAuthorityNode() + { + AngularUnit unit = AngularUnit.Degrees; + WktKeywordNode node = Assert.IsType(unit.ToWktNode()); + + Assert.Equal("UNIT", node.Keyword); + Assert.Equal(3, node.Children.Count); + Assert.Equal("AUTHORITY", Assert.IsType(node.Children[2]).Keyword); + } + + /// + /// Verifies that the angular unit WKT node omits authority information when it is unavailable. + /// + [Fact] + public void AngularUnit_ToWktNode_WithoutAuthority_OmitsAuthorityNode() + { + AngularUnit unit = new(0.5); + WktKeywordNode node = Assert.IsType(unit.ToWktNode()); + + Assert.Equal(2, node.Children.Count); + } + + // ---- AngularUnit EqualParams ---- + + /// + /// Verifies that EqualParams returns true for units with the same radians per unit. + /// + [Fact] + public void AngularUnit_EqualParams_SameRadiansPerUnit_ReturnsTrue() + { + var a = new AngularUnit(Math.PI / 180.0); + + Assert.True(AngularUnit.Degrees.EqualParams(a)); + } + + /// + /// Verifies that EqualParams returns false for units with different radians per unit. + /// + [Fact] + public void AngularUnit_EqualParams_DifferentRadiansPerUnit_ReturnsFalse() + { + Assert.False(AngularUnit.Degrees.EqualParams(AngularUnit.Radian)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void AngularUnit_EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(AngularUnit.Degrees.EqualParams("not a unit")); + } + + /// + /// Verifies that EqualParams returns false when comparing AngularUnit to LinearUnit. + /// + [Fact] + public void AngularUnit_EqualParams_LinearUnit_ReturnsFalse() + { + Assert.False(AngularUnit.Radian.EqualParams(LinearUnit.Metre)); + } + + // ---- Generic Unit ---- + + /// + /// Verifies that the full constructor sets conversion factor and metadata. + /// + [Fact] + public void Unit_Constructor_SetsConversionFactorAndMetadata() + { + var unit = new Unit(2.5, "custom", "TEST", 42, "alias", "abbr", "remarks"); + + Assert.Equal(2.5, unit.ConversionFactor); + Assert.Equal("custom", unit.Name); + Assert.Equal("TEST", unit.Authority); + Assert.Equal(42, unit.AuthorityCode); + Assert.Equal("alias", unit.Alias); + Assert.Equal("abbr", unit.Abbreviation); + Assert.Equal("remarks", unit.Remarks); + } + + /// + /// Verifies that the simplified constructor sets name and conversion factor. + /// + [Fact] + public void Unit_SimpleConstructor_SetsNameAndConversionFactor() + { + var unit = new Unit("fathom", 1.8288); + + Assert.Equal("fathom", unit.Name); + Assert.Equal(1.8288, unit.ConversionFactor, 12); + } + + /// + /// Verifies that WKT output includes authority information when present. + /// + [Fact] + public void Unit_WKT_WithAuthority_ContainsAuthority() + { + var unit = new Unit(2.5, "custom", "TEST", 42, string.Empty, string.Empty, string.Empty); + string wkt = unit.WKT; + + Assert.Contains("UNIT[\"custom\", 2.5", wkt, StringComparison.Ordinal); + Assert.Contains("AUTHORITY[\"TEST\", \"42\"]", wkt, StringComparison.Ordinal); + Assert.Equal(wkt, unit.ToString()); + } + + /// + /// Verifies that WKT output omits authority information when not set. + /// + [Fact] + public void Unit_WKT_WithoutAuthority_OmitsAuthority() + { + var unit = new Unit("custom", 2.5); + string wkt = unit.WKT; + + Assert.DoesNotContain("AUTHORITY", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that the generic unit WKT node includes authority information when it is available. + /// + [Fact] + public void Unit_ToWktNode_WithAuthority_IncludesAuthorityNode() + { + Unit unit = new(2.5, "custom", "TEST", 42, string.Empty, string.Empty, string.Empty); + WktKeywordNode node = Assert.IsType(unit.ToWktNode()); + + Assert.Equal("UNIT", node.Keyword); + Assert.Equal(3, node.Children.Count); + Assert.Equal("AUTHORITY", Assert.IsType(node.Children[2]).Keyword); + } + + /// + /// Verifies that the generic unit WKT node omits authority information when it is unavailable. + /// + [Fact] + public void Unit_ToWktNode_WithoutAuthority_OmitsAuthorityNode() + { + Unit unit = new("custom", 2.5); + WktKeywordNode node = Assert.IsType(unit.ToWktNode()); + + Assert.Equal(2, node.Children.Count); + } + + /// + /// Verifies that XML serialization is not supported for generic units. + /// + [Fact] + public void Unit_XML_ThrowsNotSupportedException() + { + var unit = new Unit("custom", 2.5); + + Assert.Throws(() => _ = unit.XML); + } + + /// + /// Verifies that XML element serialization is not supported for generic units. + /// + [Fact] + public void Unit_ToXml_ThrowsNotSupportedException() + { + var unit = new Unit("custom", 2.5); + + Assert.Throws(() => unit.ToXml()); + } + + /// + /// Verifies that EqualParams returns true for generic units with the same conversion factor. + /// + [Fact] + public void Unit_EqualParams_SameConversionFactor_ReturnsTrue() + { + var a = new Unit(2.5, "custom-a", "TEST", 1, string.Empty, string.Empty, string.Empty); + var b = new Unit(2.5, "custom-b", "OTHER", 2, string.Empty, string.Empty, string.Empty); + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for generic units with different conversion factors. + /// + [Fact] + public void Unit_EqualParams_DifferentConversionFactor_ReturnsFalse() + { + var a = new Unit("custom-a", 2.5); + var b = new Unit("custom-b", 3.5); + + Assert.False(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void Unit_EqualParams_DifferentType_ReturnsFalse() + { + var unit = new Unit("custom", 2.5); + + Assert.False(unit.EqualParams("not a unit")); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/VerticalCoordinateSystemTests.cs b/test/ProjNet.Tests/CoordinateSystems/VerticalCoordinateSystemTests.cs new file mode 100644 index 00000000..2d2545cd --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/VerticalCoordinateSystemTests.cs @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class VerticalCoordinateSystemTests +{ + /// + /// Verifies that the constructor assigns all properties correctly. + /// + [Fact] + public void Constructor_SetsProperties() + { + LinearUnit linearUnit = LinearUnit.Metre; + VerticalDatum datum = VerticalDatum.ODN; + var axis = new AxisInfo("Up", AxisOrientationEnum.Up); + + var vcs = new VerticalCoordinateSystem( + linearUnit, + datum, + axis, + "TestVCS", + "TEST", + 1234, + "alias", + "abbr", + "remarks"); + + Assert.Equal("TestVCS", vcs.Name); + Assert.Equal("TEST", vcs.Authority); + Assert.Equal(1234, vcs.AuthorityCode); + Assert.Same(linearUnit, vcs.LinearUnit); + Assert.Same(datum, vcs.VerticalDatum); + } + + /// + /// Verifies that the ODN predefined constant has the correct name. + /// + [Fact] + public void ODN_HasCorrectName() + { + VerticalCoordinateSystem odn = VerticalCoordinateSystem.ODN; + + Assert.Equal("Newlyn", odn.Name); + } + + /// + /// Verifies that the ODN predefined constant has the correct authority. + /// + [Fact] + public void ODN_HasCorrectAuthority() + { + VerticalCoordinateSystem odn = VerticalCoordinateSystem.ODN; + + Assert.Equal("EPSG", odn.Authority); + Assert.Equal(5701, odn.AuthorityCode); + } + + /// + /// Verifies that a vertical coordinate system has exactly one dimension. + /// + [Fact] + public void Dimension_IsOne() + { + VerticalCoordinateSystem odn = VerticalCoordinateSystem.ODN; + + Assert.Equal(1, odn.Dimension); + } + + /// + /// Verifies that the ODN system uses metres as its linear unit. + /// + [Fact] + public void ODN_LinearUnit_IsMetres() + { + VerticalCoordinateSystem odn = VerticalCoordinateSystem.ODN; + + Assert.Equal("m", odn.LinearUnit.Alias); + Assert.Equal(string.Empty, odn.LinearUnit.Abbreviation); + Assert.Equal(1.0, odn.LinearUnit.MetersPerUnit); + Assert.True(odn.LinearUnit.EqualParams(LinearUnit.Metre)); + } + + /// + /// Verifies that returns the linear unit for dimension 0. + /// + [Fact] + public void GetUnits_DimensionZero_ReturnsLinearUnit() + { + VerticalCoordinateSystem odn = VerticalCoordinateSystem.ODN; + + IUnit unit = odn.GetUnits(0); + + Assert.IsType(unit); + Assert.True(odn.LinearUnit.EqualParams(unit)); + } + + /// + /// Verifies that throws for invalid dimensions. + /// + [Fact] + public void GetUnits_InvalidDimension_Throws() + { + VerticalCoordinateSystem odn = VerticalCoordinateSystem.ODN; + + Assert.ThrowsAny(() => odn.GetUnits(1)); + } + + // ---- WKT ---- + + /// + /// Verifies that the WKT output starts with VERT_CS and contains the system name. + /// + [Fact] + public void WKT_ContainsVertCsAndName() + { + string wkt = VerticalCoordinateSystem.ODN.WKT; + + Assert.StartsWith("VERT_CS[\"Newlyn\"", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that the WKT output contains the vertical datum. + /// + [Fact] + public void WKT_ContainsVerticalDatum() + { + string wkt = VerticalCoordinateSystem.ODN.WKT; + + Assert.Contains("DATUM[\"Ordnance Datum Newlyn\"", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that the WKT output contains the linear unit. + /// + [Fact] + public void WKT_ContainsLinearUnit() + { + string wkt = VerticalCoordinateSystem.ODN.WKT; + + Assert.Contains("UNIT[\"metre\", 1", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that the WKT output contains the authority code. + /// + [Fact] + public void WKT_ContainsAuthority() + { + string wkt = VerticalCoordinateSystem.ODN.WKT; + + Assert.Contains("AUTHORITY[\"EPSG\", \"5701\"]", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that the WKT omits the default axis info (Up/Up). + /// + [Fact] + public void WKT_OmitsDefaultAxis() + { + string wkt = VerticalCoordinateSystem.ODN.WKT; + + Assert.DoesNotContain("AXIS[", wkt, StringComparison.Ordinal); + } + + /// + /// Verifies that the WKT includes axis info when it differs from the default. + /// + [Fact] + public void WKT_NonDefaultAxis_IncludesAxisInfo() + { + var vcs = new VerticalCoordinateSystem( + LinearUnit.Metre, + VerticalDatum.ODN, + new AxisInfo("Height", AxisOrientationEnum.North), + "Custom", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + string wkt = vcs.WKT; + + Assert.Contains("AXIS[", wkt, StringComparison.Ordinal); + } + + // ---- XML ---- + + /// + /// Verifies that the XML output contains expected elements. + /// + [Fact] + public void XML_ContainsExpectedElements() + { + string xml = VerticalCoordinateSystem.ODN.XML; + + Assert.Contains("CS_CoordinateSystem", xml, StringComparison.Ordinal); + Assert.Contains("CS_VerticalCoordinateSystem", xml, StringComparison.Ordinal); + Assert.Contains("CS_VerticalDatum", xml, StringComparison.Ordinal); + Assert.Contains("CS_LinearUnit", xml, StringComparison.Ordinal); + } + + /// + /// Verifies that the XML output contains the dimension attribute. + /// + [Fact] + public void XML_ContainsDimension() + { + string xml = VerticalCoordinateSystem.ODN.XML; + + Assert.Contains("Dimension=\"1\"", xml, StringComparison.Ordinal); + } + + /// + /// Verifies that matches the XML property. + /// + [Fact] + public void ToXml_MatchesXmlProperty() + { + VerticalCoordinateSystem vcs = VerticalCoordinateSystem.ODN; + XElement element = vcs.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(vcs.XML), element)); + } + + /// + /// Verifies that the WKT node omits the default axis node when the default Up axis is used. + /// + [Fact] + public void ToWktNode_WithDefaultAxis_OmitsAxisNode() + { + VerticalCoordinateSystem vcs = VerticalCoordinateSystem.ODN; + WktKeywordNode node = Assert.IsType(vcs.ToWktNode()); + + Assert.Equal("VERT_CS", node.Keyword); + Assert.Equal(4, node.Children.Count); + Assert.Equal("AUTHORITY", Assert.IsType(node.Children[3]).Keyword); + } + + /// + /// Verifies that the WKT node omits authority information when the authority name is blank. + /// + [Fact] + public void ToWktNode_WithoutAuthority_OmitsAuthorityNode() + { + VerticalCoordinateSystem vcs = CreateVerticalCoordinateSystem(new AxisInfo("Up", AxisOrientationEnum.Up), string.Empty, -1); + WktKeywordNode node = Assert.IsType(vcs.ToWktNode()); + + Assert.Equal(3, node.Children.Count); + } + + /// + /// Verifies that the WKT node omits authority information when the code is not positive. + /// + [Fact] + public void ToWktNode_WithAuthorityButWithoutPositiveCode_OmitsAuthorityNode() + { + VerticalCoordinateSystem vcs = CreateVerticalCoordinateSystem(new AxisInfo("Up", AxisOrientationEnum.Up), "TEST", -1); + WktKeywordNode node = Assert.IsType(vcs.ToWktNode()); + + Assert.Equal(3, node.Children.Count); + } + + /// + /// Verifies that the WKT node includes an axis node when the axis collection count differs from the default. + /// + [Fact] + public void ToWktNode_WithMultipleAxes_IncludesFirstAxisNode() + { + VerticalCoordinateSystem vcs = CreateVerticalCoordinateSystem( + [ + new AxisInfo("Primary", AxisOrientationEnum.Up), + new AxisInfo("Secondary", AxisOrientationEnum.Down), + ], + string.Empty, + -1); + WktKeywordNode node = Assert.IsType(vcs.ToWktNode()); + + Assert.Equal(4, node.Children.Count); + Assert.Equal("AXIS", Assert.IsType(node.Children[3]).Keyword); + } + + /// + /// Verifies that the WKT node includes an axis node when the axis name differs from the default. + /// + [Fact] + public void ToWktNode_WithNonDefaultAxisName_IncludesAxisNode() + { + VerticalCoordinateSystem vcs = CreateVerticalCoordinateSystem(new AxisInfo("Height", AxisOrientationEnum.Up), string.Empty, -1); + WktKeywordNode node = Assert.IsType(vcs.ToWktNode()); + + Assert.Equal(4, node.Children.Count); + Assert.Equal("AXIS", Assert.IsType(node.Children[3]).Keyword); + } + + /// + /// Verifies that the WKT node includes an axis node when the axis orientation differs from the default. + /// + [Fact] + public void ToWktNode_WithNonDefaultAxisOrientation_IncludesAxisNode() + { + VerticalCoordinateSystem vcs = CreateVerticalCoordinateSystem(new AxisInfo("Up", AxisOrientationEnum.Down), string.Empty, -1); + WktKeywordNode node = Assert.IsType(vcs.ToWktNode()); + + Assert.Equal(4, node.Children.Count); + Assert.Equal("AXIS", Assert.IsType(node.Children[3]).Keyword); + } + + // ---- EqualParams ---- + + /// + /// Verifies that EqualParams returns true for equivalent systems. + /// + [Fact] + public void EqualParams_EquivalentSystems_ReturnsTrue() + { + VerticalCoordinateSystem a = VerticalCoordinateSystem.ODN; + VerticalCoordinateSystem b = VerticalCoordinateSystem.ODN; + + Assert.True(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false for systems with different linear units. + /// + [Fact] + public void EqualParams_DifferentLinearUnit_ReturnsFalse() + { + VerticalCoordinateSystem a = VerticalCoordinateSystem.ODN; + var b = new VerticalCoordinateSystem( + LinearUnit.Foot, + VerticalDatum.ODN, + new AxisInfo("Up", AxisOrientationEnum.Up), + "Newlyn", + "EPSG", + 5701, + string.Empty, + string.Empty, + string.Empty); + + Assert.False(a.EqualParams(b)); + } + + /// + /// Verifies that EqualParams returns false when the systems have different dimensions. + /// + [Fact] + public void EqualParams_DifferentDimension_ReturnsFalse() + { + VerticalCoordinateSystem first = CreateVerticalCoordinateSystem(new AxisInfo("Up", AxisOrientationEnum.Up)); + VerticalCoordinateSystem second = CreateVerticalCoordinateSystem( + [ + new AxisInfo("Up", AxisOrientationEnum.Up), + new AxisInfo("Down", AxisOrientationEnum.Down), + ]); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that EqualParams returns false when the axis orientation differs. + /// + [Fact] + public void EqualParams_DifferentAxisOrientation_ReturnsFalse() + { + VerticalCoordinateSystem first = CreateVerticalCoordinateSystem(new AxisInfo("Up", AxisOrientationEnum.Up)); + VerticalCoordinateSystem second = CreateVerticalCoordinateSystem(new AxisInfo("Up", AxisOrientationEnum.Down)); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that EqualParams returns false for a different type. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(VerticalCoordinateSystem.ODN.EqualParams("not a VCS")); + } + + private static VerticalCoordinateSystem CreateVerticalCoordinateSystem(AxisInfo axisInfo, string authority = "TEST", long authorityCode = 1234) + { + return new VerticalCoordinateSystem( + LinearUnit.Metre, + VerticalDatum.ODN, + axisInfo, + "Custom", + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } + + private static VerticalCoordinateSystem CreateVerticalCoordinateSystem(List axisInfo, string authority = "TEST", long authorityCode = 1234) + { + return new VerticalCoordinateSystem( + LinearUnit.Metre, + VerticalDatum.ODN, + axisInfo, + "Custom", + authority, + authorityCode, + string.Empty, + string.Empty, + string.Empty); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/VerticalDatumTests.cs b/test/ProjNet.Tests/CoordinateSystems/VerticalDatumTests.cs new file mode 100644 index 00000000..581865bc --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/VerticalDatumTests.cs @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for . +/// +public class VerticalDatumTests +{ + /// + /// Verifies that the predefined ODN datum exposes the expected metadata. + /// + [Fact] + public void ODN_HasExpectedMetadata() + { + VerticalDatum datum = VerticalDatum.ODN; + + Assert.Equal("Ordnance Datum Newlyn", datum.Name); + Assert.Equal("EPSG", datum.Authority); + Assert.Equal(5101, datum.AuthorityCode); + Assert.Equal(DatumType.VD_GeoidModelDerived, datum.DatumType); + } + + /// + /// Verifies that the constructor assigns all properties. + /// + [Fact] + public void Constructor_SetsProperties() + { + var datum = new VerticalDatum( + DatumType.VD_Orthometric, + "Custom datum", + "TEST", + 42, + "alias", + "remarks", + "abbr"); + + Assert.Equal(DatumType.VD_Orthometric, datum.DatumType); + Assert.Equal("Custom datum", datum.Name); + Assert.Equal("TEST", datum.Authority); + Assert.Equal(42, datum.AuthorityCode); + Assert.Equal("alias", datum.Alias); + Assert.Equal("remarks", datum.Remarks); + Assert.Equal("abbr", datum.Abbreviation); + } + + /// + /// Verifies that WKT includes authority information when it is available. + /// + [Fact] + public void WKT_WithAuthority_FormatsExpectedValue() + { + VerticalDatum datum = VerticalDatum.ODN; + + Assert.Equal("VERT_DATUM[\"Ordnance Datum Newlyn\", 2005, AUTHORITY[\"EPSG\", \"5101\"]]", datum.WKT); + } + + /// + /// Verifies that WKT omits authority information when it is unavailable. + /// + [Fact] + public void WKT_WithoutAuthority_OmitsAuthorityClause() + { + var datum = new VerticalDatum( + DatumType.VD_Orthometric, + "Custom datum", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + Assert.Equal("VERT_DATUM[\"Custom datum\", 2001]", datum.WKT); + } + + /// + /// Verifies that XML uses the expected element name and embedded info XML. + /// + [Fact] + public void XML_FormatsExpectedValue() + { + VerticalDatum datum = VerticalDatum.ODN; + var xml = XElement.Parse(datum.XML); + XElement info = Assert.Single(xml.Elements("CS_Info")); + + Assert.Equal("CS_VerticalDatum", xml.Name.LocalName); + Assert.Equal("2005", (string?)xml.Attribute("DatumType")); + Assert.Equal("Ordnance Datum Newlyn", (string?)info.Attribute("Name")); + Assert.Equal("EPSG", (string?)info.Attribute("Authority")); + Assert.Equal("5101", (string?)info.Attribute("AuthorityCode")); + } + + /// + /// Verifies that returns the expected XML element. + /// + [Fact] + public void ToXml_ReturnsExpectedElement() + { + VerticalDatum datum = VerticalDatum.ODN; + + XElement xml = datum.ToXml(); + + Assert.Equal("CS_VerticalDatum", xml.Name.LocalName); + Assert.Equal("2005", (string?)xml.Attribute("DatumType")); + Assert.NotNull(xml.Element("CS_Info")); + } + + /// + /// Verifies that returns the expected node structure. + /// + [Fact] + public void ToWktNode_WithAuthority_ReturnsExpectedKeywordNode() + { + VerticalDatum datum = VerticalDatum.ODN; + + WktKeywordNode node = Assert.IsType(datum.ToWktNode()); + + Assert.Equal("VERT_DATUM", node.Keyword); + Assert.Equal(3, node.Children.Count); + + WktQuotedString nameNode = Assert.IsType(node.Children[0]); + Assert.Equal("Ordnance Datum Newlyn", nameNode.Value); + + WktInteger typeNode = Assert.IsType(node.Children[1]); + Assert.Equal(2005, typeNode.Value); + + WktKeywordNode authorityNode = Assert.IsType(node.Children[2]); + Assert.Equal("AUTHORITY", authorityNode.Keyword); + } + + /// + /// Verifies that the WKT node omits authority when it is unavailable. + /// + [Fact] + public void ToWktNode_WithoutAuthority_OmitsAuthorityNode() + { + var datum = new VerticalDatum( + DatumType.VD_Orthometric, + "Custom datum", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + WktKeywordNode node = Assert.IsType(datum.ToWktNode()); + + Assert.Equal(2, node.Children.Count); + } + + /// + /// Verifies that equal datums compare equal when the datum type matches. + /// + [Fact] + public void EqualParams_SameDatumType_ReturnsTrue() + { + var first = new VerticalDatum( + DatumType.VD_Orthometric, + "A", + "EPSG", + 1, + string.Empty, + string.Empty, + string.Empty); + var second = new VerticalDatum( + DatumType.VD_Orthometric, + "B", + "OTHER", + 2, + string.Empty, + string.Empty, + string.Empty); + + Assert.True(first.EqualParams(second)); + } + + /// + /// Verifies that different datum types compare unequal. + /// + [Fact] + public void EqualParams_DifferentDatumType_ReturnsFalse() + { + var first = new VerticalDatum( + DatumType.VD_Orthometric, + "A", + "EPSG", + 1, + string.Empty, + string.Empty, + string.Empty); + var second = new VerticalDatum( + DatumType.VD_Depth, + "B", + "OTHER", + 2, + string.Empty, + string.Empty, + string.Empty); + + Assert.False(first.EqualParams(second)); + } + + /// + /// Verifies that can clear retained vertical ensemble metadata. + /// + [Fact] + public void WithEnsemble_WithNull_ClearsRetainedEnsemble() + { + DatumEnsemble ensemble = new( + "Example vertical ensemble", + [ + new DatumEnsembleMember("Datum A"), + new DatumEnsembleMember("Datum B"), + ], + 0.05d); + var original = new VerticalDatum( + DatumType.VD_Orthometric, + "Custom datum", + "TEST", + 42, + "alias", + "remarks", + "abbr", + ensemble); + VerticalDatum clone = Assert.IsType(original.WithEnsemble(null)); + + Assert.NotSame(original, clone); + Assert.NotNull(original.Ensemble); + Assert.Null(clone.Ensemble); + Assert.Equal(original.Name, clone.Name); + Assert.Equal(original.Authority, clone.Authority); + Assert.Equal(original.AuthorityCode, clone.AuthorityCode); + } + + /// + /// Verifies that different object types compare unequal. + /// + [Fact] + public void EqualParams_DifferentType_ReturnsFalse() + { + Assert.False(VerticalDatum.ODN.EqualParams("not a datum")); + } +} diff --git a/test/ProjNet.Tests/CoordinateSystems/Wgs84CatalogConsistencyTests.cs b/test/ProjNet.Tests/CoordinateSystems/Wgs84CatalogConsistencyTests.cs new file mode 100644 index 00000000..3ae64777 --- /dev/null +++ b/test/ProjNet.Tests/CoordinateSystems/Wgs84CatalogConsistencyTests.cs @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Reflection; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Verifies that public WGS84 convenience accessors stay semantically aligned with the generated EPSG catalog. +/// +public class Wgs84CatalogConsistencyTests +{ + private static readonly CoordinateSystemFactory Factory = new(); + private static readonly MethodInfo ResolveCatalogCoordinateSystemMethod = typeof(CoordinateSystem).Assembly + .GetType("ProjNet.Data.Generated.EpsgCoordinateSystemFactory", throwOnError: true)! + .GetMethod("TryResolveCoordinateSystem", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("The generated EPSG coordinate-system resolver could not be located."); + + /// + /// Verifies that the public WGS84 geocentric accessor matches the EPSG catalog entry. + /// + [Fact] + public void GeocentricWgs84_StaticMatchesCatalogLookup() + { + GeocentricCoordinateSystem catalog = ResolveCatalogCoordinateSystem(4978); + GeocentricCoordinateSystem runtime = GeocentricCoordinateSystem.WGS84; + + Assert.True(runtime.EqualParams(catalog)); + Assert.Equal(catalog.Authority, runtime.Authority); + Assert.Equal(catalog.AuthorityCode, runtime.AuthorityCode); + } + + /// + /// Verifies that the public WGS84 geographic accessor matches the EPSG catalog entry after legacy lon/lat normalization. + /// + [Fact] + public void GeographicWgs84_StaticMatchesLegacyNormalizedCatalogLookup() + { + GeographicCoordinateSystem catalog = ResolveCatalogCoordinateSystem(4326); + GeographicCoordinateSystem runtime = GeographicCoordinateSystem.WGS84; + GeographicCoordinateSystem normalizedCatalog = Factory.CreateGeographicCoordinateSystem( + catalog.Name, + catalog.AngularUnit, + catalog.HorizontalDatum, + catalog.PrimeMeridian, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + Assert.True(runtime.EqualParams(normalizedCatalog)); + Assert.Equal(catalog.Authority, runtime.Authority); + Assert.Equal(catalog.AuthorityCode, runtime.AuthorityCode); + } + + /// + /// Verifies that the normalized public WGS84 geographic accessor reuses the same immutable runtime instance. + /// + [Fact] + public void GeographicWgs84_StaticReturnsSameNormalizedInstance() + { + GeographicCoordinateSystem first = GeographicCoordinateSystem.WGS84; + GeographicCoordinateSystem second = GeographicCoordinateSystem.WGS84; + + Assert.Same(first, second); + Assert.Equal(AxisOrientationEnum.East, first.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.North, first.GetAxis(1).Orientation); + } + + /// + /// Verifies that the public Web Mercator accessor matches the EPSG catalog entry after legacy base-CRS normalization. + /// + [Fact] + public void WebMercator_StaticMatchesLegacyNormalizedCatalogLookup() + { + ProjectedCoordinateSystem catalog = ResolveCatalogCoordinateSystem(3857); + ProjectedCoordinateSystem runtime = ProjectedCoordinateSystem.WebMercator; + + AssertProjectedCoordinateSystemMatchesNormalizedCatalog(runtime, catalog); + } + + /// + /// Verifies that the public Web Mercator accessor reuses the same immutable normalized runtime instance. + /// + [Fact] + public void WebMercator_StaticReturnsSameNormalizedInstance() + { + ProjectedCoordinateSystem first = ProjectedCoordinateSystem.WebMercator; + ProjectedCoordinateSystem second = ProjectedCoordinateSystem.WebMercator; + + Assert.Same(first, second); + Assert.Same(GeographicCoordinateSystem.WGS84, first.GeographicCoordinateSystem); + Assert.Equal(AxisOrientationEnum.East, first.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.North, first.GetAxis(1).Orientation); + } + + /// + /// Verifies that the public WGS84 UTM accessor matches EPSG catalog entries after legacy base-CRS normalization. + /// + /// The UTM zone. + /// for the northern hemisphere; otherwise . + [Theory] + [InlineData(32, true)] + [InlineData(32, false)] + public void Wgs84Utm_StaticMatchesLegacyNormalizedCatalogLookup(int zone, bool zoneIsNorth) + { + int srid = 32600 + zone + (zoneIsNorth ? 0 : 100); + ProjectedCoordinateSystem catalog = ResolveCatalogCoordinateSystem(srid); + var runtime = ProjectedCoordinateSystem.WGS84_UTM(zone, zoneIsNorth); + + AssertProjectedCoordinateSystemMatchesNormalizedCatalog(runtime, catalog); + } + + private static void AssertProjectedCoordinateSystemMatchesNormalizedCatalog(ProjectedCoordinateSystem runtime, ProjectedCoordinateSystem catalog) + { + GeographicCoordinateSystem normalizedCatalogGeographic = Factory.CreateGeographicCoordinateSystem( + catalog.GeographicCoordinateSystem.Name, + catalog.GeographicCoordinateSystem.AngularUnit, + catalog.GeographicCoordinateSystem.HorizontalDatum, + catalog.GeographicCoordinateSystem.PrimeMeridian, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + IProjection normalizedCatalogProjection = Factory.CreateProjection( + catalog.Projection.Name, + catalog.Projection.ClassName, + CopyProjectionParameters(catalog.Projection)); + ProjectedCoordinateSystem normalizedCatalog = Factory.CreateProjectedCoordinateSystem( + catalog.Name, + normalizedCatalogGeographic, + normalizedCatalogProjection, + catalog.LinearUnit, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + Assert.True(runtime.EqualParams(normalizedCatalog)); + Assert.Same(GeographicCoordinateSystem.WGS84, runtime.GeographicCoordinateSystem); + Assert.Equal(catalog.Authority, runtime.Authority); + Assert.Equal(catalog.AuthorityCode, runtime.AuthorityCode); + } + + private static TCoordinateSystem ResolveCatalogCoordinateSystem(int srid) + where TCoordinateSystem : CoordinateSystem + { + object?[] arguments = [srid, null]; + bool resolved = (bool)(ResolveCatalogCoordinateSystemMethod.Invoke(null, arguments) ?? false); + Assert.True(resolved); + + return Assert.IsType(arguments[1]); + } + + private static List CopyProjectionParameters(IProjection projection) + { + var parameters = new List(projection.NumParameters); + for (int i = 0; i < projection.NumParameters; i++) + { + ProjectionParameter parameter = projection.GetParameter(i); + parameters.Add(new ProjectionParameter(parameter.Name, parameter.Value)); + } + + return parameters; + } +} diff --git a/test/ProjNet.Tests/CoordinateTransformTests.cs b/test/ProjNet.Tests/CoordinateTransformTests.cs deleted file mode 100644 index 493b6b1c..00000000 --- a/test/ProjNet.Tests/CoordinateTransformTests.cs +++ /dev/null @@ -1,1205 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using ProjNet.CoordinateSystems; -using ProjNet.CoordinateSystems.Projections; -using ProjNet.CoordinateSystems.Transformations; -using ProjNet.Geometries; -using ProjNet.IO.CoordinateSystems; - -namespace ProjNET.Tests -{ - [TestFixture] - public class CoordinateTransformTests : CoordinateTransformTestsBase - { - public CoordinateTransformTests() - { - Verbose = true; - } - - [Test] - public void TestTransformListOfCoordinates() - { - var csFact = new CoordinateSystemFactory(); - var ctFact = new CoordinateTransformationFactory(); - - var utm35ETRS = csFact.CreateFromWkt( - "PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); - - var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); - - var trans = ctFact.CreateFromCoordinateSystems(utm35ETRS, utm33); - - XY[] points = - { - new XY(290586.087, 6714000), new XY(290586.392, 6713996.224), - new XY(290590.133, 6713973.772), new XY(290594.111, 6713957.416), - new XY(290596.615, 6713943.567), new XY(290596.701, 6713939.485) - }; - - var tpoints = (XY[])points.Clone(); - trans.MathTransform.Transform(tpoints); - for (int i = 0; i < points.Length; i++) - { - double expectedX = points[i].X; - double expectedY = points[i].Y; - trans.MathTransform.Transform(ref expectedX, ref expectedY); - - double actualX = tpoints[i].X; - double actualY = tpoints[i].Y; - - Assert.That(actualX, Is.EqualTo(expectedX).Within(1E-8)); - Assert.That(actualY, Is.EqualTo(expectedY).Within(1E-8)); - } - } - - [Test] - public void TestTransformListOfDoubleArray() - { - var csFact = new CoordinateSystemFactory(); - var ctFact = new CoordinateTransformationFactory(); - - var utm35ETRS = csFact.CreateFromWkt( - "PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); - - var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); - - var trans = ctFact.CreateFromCoordinateSystems(utm35ETRS, utm33); - - double[][] points = - { - new[] {290586.087, 6714000 }, new[] {90586.392, 6713996.224}, - new[] {290590.133, 6713973.772}, new[] {290594.111, 6713957.416}, - new[] {290596.615, 6713943.567}, new[] {290596.701, 6713939.485} - }; - - double[][] tpoints = trans.MathTransform.TransformList(points).ToArray(); - for (int i = 0; i < points.Length; i++) - { - double expectedX = points[i][0]; - double expectedY = points[i][1]; - trans.MathTransform.Transform(ref expectedX, ref expectedY); - - double actualX = tpoints[i][0]; - double actualY = tpoints[i][1]; - - Assert.That(actualX, Is.EqualTo(expectedX).Within(1E-8)); - Assert.That(actualY, Is.EqualTo(expectedY).Within(1E-8)); - } - } - - [Test] - public void TestCentralMeridianParse() - { - const string strSouthPole = "PROJCS[\"South_Pole_Lambert_Azimuthal_Equal_Area\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Lambert_Azimuthal_Equal_Area\"],PARAMETER[\"False_Easting\",0],PARAMETER[\"False_Northing\",0],PARAMETER[\"Central_Meridian\",-127],PARAMETER[\"Latitude_Of_Origin\",-90],UNIT[\"Meter\",1]]"; - - var pCoordSysFactory = new CoordinateSystemFactory(); - var pSouthPole = pCoordSysFactory.CreateFromWkt(strSouthPole); - Assert.IsNotNull(pSouthPole); - } - - [Test] - public void TestAlbersProjection() - { - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1866", 6378206.4, 294.9786982138982, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Clarke 1866", DatumType.HD_Geocentric, ellipsoid, null); - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Clarke 1866", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - var parameters = new List(5) - { - new ProjectionParameter("central_meridian", -96), - new ProjectionParameter("latitude_of_center", 23), - new ProjectionParameter("standard_parallel_1", 29.5), - new ProjectionParameter("standard_parallel_2", 45.5), - new ProjectionParameter("false_easting", 0), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Albers Conical Equal Area", "albers", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Albers Conical Equal Area", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans1 = new CoordinateTransformationFactory().CreateFromCoordinateSystems(gcs, coordsys); - var trans2 = new CoordinateTransformationFactory().CreateFromCoordinateSystems(coordsys, gcs); - - double[] pGeo = new double[] { -75, 35 }; - double[] pUtm = trans1.MathTransform.Transform(pGeo); - double[] pGeo2 = trans2.MathTransform.Transform(pUtm); - - double[] expected = new[] { 1885472.7, 1535925 }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.05), TransformationError("Albers", expected, pUtm, false)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("Albers", pGeo, pGeo2, true)); - } - - [Test] - public void TestAlbersProjectionFeet() - { - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1866", 6378206.4, 294.9786982138982, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Clarke 1866", DatumType.HD_Geocentric, ellipsoid, null); - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Clarke 1866", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - var parameters = new List(5) - { - new ProjectionParameter("central_meridian", -96), - new ProjectionParameter("latitude_of_center", 23), - new ProjectionParameter("standard_parallel_1", 29.5), - new ProjectionParameter("standard_parallel_2", 45.5), - new ProjectionParameter("false_easting", 0), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Albers Conical Equal Area", "albers", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Albers Conical Equal Area", gcs, projection, LinearUnit.Foot, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcs, coordsys); - - double[] pGeo = new double[] { -75, 35 }; - double[] pUtm = trans.MathTransform.Transform(pGeo); - double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); - - double[] expected = new[] { 1885472.7 / LinearUnit.Foot.MetersPerUnit, 1535925 / LinearUnit.Foot.MetersPerUnit }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.1), TransformationError("Albers", expected, pUtm, false)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("Albers", pGeo, pGeo2, true)); - } - - [Test] - public void TestMercator_1SP_Projection() - { - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Bessel 1840", DatumType.HD_Geocentric, ellipsoid, null); - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Bessel 1840", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_origin", 0), - new ProjectionParameter("central_meridian", 110), - new ProjectionParameter("scale_factor", 0.997), - new ProjectionParameter("false_easting", 3900000), - new ProjectionParameter("false_northing", 900000) - }; - var projection = CoordinateSystemFactory.CreateProjection("Mercator_1SP", "Mercator_1SP", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Makassar / NEIEZ", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcs, coordsys); - - double[] pGeo = new double[] { 120, -3 }; - double[] pUtm = trans.MathTransform.Transform(pGeo); - double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); - - double[] expected = new[] { 5009726.58, 569150.82 }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.02), TransformationError("Mercator_1SP", expected, pUtm, false)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("Mercator_1SP", pGeo, pGeo2, true)); - } - [Test] - public void TestMercator_1SP_Projection_Feet() - { - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Bessel 1840", DatumType.HD_Geocentric, ellipsoid, null); - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Bessel 1840", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_origin", 0), - new ProjectionParameter("central_meridian", 110), - new ProjectionParameter("scale_factor", 0.997), - new ProjectionParameter("false_easting", 3900000/LinearUnit.Foot.MetersPerUnit), - new ProjectionParameter("false_northing", 900000/LinearUnit.Foot.MetersPerUnit) - }; - var projection = CoordinateSystemFactory.CreateProjection("Mercator_1SP", "Mercator_1SP", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Makassar / NEIEZ", gcs, projection, LinearUnit.Foot, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcs, coordsys); - - double[] pGeo = new[] { 120d, -3d }; - double[] pUtm = trans.MathTransform.Transform(pGeo); - double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); - - double[] expected = new[] { 5009726.58 / LinearUnit.Foot.MetersPerUnit, 569150.82 / LinearUnit.Foot.MetersPerUnit }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.02), TransformationError("Mercator_1SP", expected, pUtm, false)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("Mercator_1SP", pGeo, pGeo2, true)); - } - [Test] - public void TestMercator_2SP_Projection() - { - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Krassowski 1940", 6378245.0, 298.3, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Krassowski 1940", DatumType.HD_Geocentric, ellipsoid, null); - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Krassowski 1940", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_origin", 42), - new ProjectionParameter("central_meridian", 51), - new ProjectionParameter("false_easting", 0), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Mercator_2SP", "Mercator_2SP", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Pulkovo 1942 / Mercator Caspian Sea", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcs, coordsys); - - double[] pGeo = new[] { 53d, 53d }; - double[] pUtm = trans.MathTransform.Transform(pGeo); - double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); - - double[] expected = new[] { 165704.29, 5171848.07 }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.02), TransformationError("Mercator_2SP", expected, pUtm, false)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("Mercator_2SP", pGeo, pGeo2, true)); - } - [Test] - public void TestTransverseMercator_Projection() - { - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Airy 1830", 6377563.396, 299.32496, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Airy 1830", DatumType.HD_Geocentric, ellipsoid, null); - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Airy 1830", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_origin", 49), - new ProjectionParameter("central_meridian", -2), - new ProjectionParameter("scale_factor", 0.9996012717 /* 0.9996*/), - new ProjectionParameter("false_easting", 400000), - new ProjectionParameter("false_northing", -100000) - }; - var projection = CoordinateSystemFactory.CreateProjection("Transverse Mercator", "Transverse_Mercator", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("OSGB 1936 / British National Grid", gcs, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcs, coordsys); - - double[] pGeo = new[] { 0.5, 50.5 }; - double[] pUtm = trans.MathTransform.Transform(pGeo); - double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); - //"POINT(577393.372775651 69673.621953601)" - double[] expected = new[] { 577274.98, 69740.49 }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.01), TransformationError("TransverseMercator", expected, pUtm)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 1E-6), TransformationError("TransverseMercator", pGeo, pGeo2, true)); - } - [Test] - public void TestLambertConicConformal2SP_Projection() - { - var ellipsoid = /*Ellipsoid.Clarke1866;*/ - CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1866", 20925832.16, 294.97470, LinearUnit.USSurveyFoot); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Clarke 1866", DatumType.HD_Geocentric, ellipsoid, null); - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Clarke 1866", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_origin", 27.833333333), - new ProjectionParameter("central_meridian", -99), - new ProjectionParameter("standard_parallel_1", 28.3833333333), - new ProjectionParameter("standard_parallel_2", 30.2833333333), - new ProjectionParameter("false_easting", 2000000/LinearUnit.USSurveyFoot.MetersPerUnit), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Lambert Conic Conformal (2SP)", "lambert_conformal_conic_2sp", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("NAD27 / Texas South Central", gcs, projection, LinearUnit.USSurveyFoot, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcs, coordsys); - - double[] pGeo = new[] { -96, 28.5 }; - double[] pUtm = trans.MathTransform.Transform(pGeo); - double[] pGeo2 = trans.MathTransform.Inverse().Transform(pUtm); - - double[] expected = new[] { 2963503.91 / LinearUnit.USSurveyFoot.MetersPerUnit, 254759.80 / LinearUnit.USSurveyFoot.MetersPerUnit }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.05), TransformationError("LambertConicConformal2SP", expected, pUtm)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("LambertConicConformal2SP", pGeo, pGeo2, true)); - - } - - private ICoordinateTransformation CreateGeo2Laea(double centralMeridian, double latitudeOfOrigin) - { - var wgs84 = GeographicCoordinateSystem.WGS84; - - var coordsys = CoordinateSystemFactory.CreateFromWkt("" + - "PROJCS[\"Lambert_Azimuthal_Equal_Area_Custom\"," + - "GEOGCS[\"GCS_WGS_1984\"," + - "DATUM[\"D_WGS_1984\"," + - "SPHEROID[\"WGS_1984\",6378137.0,298.257223563]]," + - "PRIMEM[\"Greenwich\",0.0]," + - "UNIT[\"Degree\",0.0174532925199433]]," + - "PROJECTION[\"Lambert_Azimuthal_Equal_Area\"]," + - "PARAMETER[\"False_Easting\",0.0]," + - "PARAMETER[\"False_Northing\",0.0]," + - $"PARAMETER[\"Central_Meridian\",{centralMeridian}]," + - $"PARAMETER[\"Latitude_Of_Origin\",{latitudeOfOrigin}]," + - "UNIT[\"Meter\",1.0]]"); - - return CoordinateTransformationFactory.CreateFromCoordinateSystems(wgs84, coordsys); - } - - [Test] - [Repeat(1000)] - public void TestLambertAzimuthalEqualArea_Projection_round_trip_on_origin() - { - double centralMeridian = Random.Next(-180, +180); - double latitudeOfOrigin = Random.Next(-90, +90); - - var trans = CreateGeo2Laea(centralMeridian, latitudeOfOrigin); - - var forward = trans.MathTransform; - var reverse = forward.Inverse(); - - double[] pGeo = new[] { centralMeridian, latitudeOfOrigin }; - - double[] pLaea = forward.Transform(pGeo); - - double[] pGeo2 = reverse.Transform(pLaea); - - double[] expectedPLaea = new double[2] { 0, 0 }; - - Assert.IsTrue(ToleranceLessThan(pLaea, expectedPLaea, 0.05), TransformationError("Lambert_Azimuthal_Equal_Area", expectedPLaea, pLaea)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("Lambert_Azimuthal_Equal_Area", pGeo, pGeo2, true)); - } - - [Test] - [Repeat(1000)] - public void TestLambertAzimuthalEqualArea_Projection_round_trip_on_arbitrary_point() - { - int GetRandomSign() - { - return Random.Next() % 2 == 0 ? -1 : +1; - } - - double centralMeridian = Random.Next(-150, +150); - double latitudeOfOrigin = Random.Next(-70, +70); - - var trans = CreateGeo2Laea(centralMeridian, latitudeOfOrigin); - - var forward = trans.MathTransform; - var reverse = forward.Inverse(); - - double lat = latitudeOfOrigin + (0.01 + Random.NextDouble()) * GetRandomSign(); - double lon = centralMeridian + (0.01 + Random.NextDouble()) * GetRandomSign(); - - double[] pGeo = new[] { lon, lat }; - - double[] pLaea = forward.Transform(pGeo); - - double[] pGeo2 = reverse.Transform(pLaea); - - Assert.NotZero(pLaea[0]); - Assert.NotZero(pLaea[1]); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.0000001), TransformationError("Lambert_Azimuthal_Equal_Area", pGeo, pGeo2, true)); - } - - [Test] - public void TestGeocentric() - { - var gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem("ETRF89 Geographic", AngularUnit.Degrees, HorizontalDatum.ETRF89, PrimeMeridian.Greenwich, - new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - var gcenCs = CoordinateSystemFactory.CreateGeocentricCoordinateSystem("ETRF89 Geocentric", HorizontalDatum.ETRF89, LinearUnit.Metre, PrimeMeridian.Greenwich); - var ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcs, gcenCs); - double[] pExpected = new[] { 2 + 7.0 / 60 + 46.38 / 3600, 53 + 48.0 / 60 + 33.82/3600 }; // Point.FromDMS(2, 7, 46.38, 53, 48, 33.82); - double[] pExpected3D = new[] { pExpected[0], pExpected[1], 73.0 }; - double[] p0 = new[] { 3771793.97, 140253.34, 5124304.35 }; - double[] p1 = ct.MathTransform.Transform(pExpected3D); - double[] p2 = ct.MathTransform.Inverse().Transform(p1); - Assert.IsTrue(ToleranceLessThan(p1, p0, 0.01)); - Assert.IsTrue(ToleranceLessThan(p2, pExpected, 0.00001)); - } - - [Test] - public void TestDatumTransform() - { - //Define datums, set parameters - var wgs72 = HorizontalDatum.WGS72; - wgs72.Wgs84Parameters = new Wgs84ConversionInfo(0, 0, 4.5, 0, 0, 0.554, 0.219); - var ed50 = HorizontalDatum.ED50; - ed50.Wgs84Parameters = new Wgs84ConversionInfo(-81.0703, -89.3603, -115.7526, - -0.48488, -0.02436, -0.41321, - -0.540645); //Parameters for Denmark - //Define geographic coordinate systems - var gcsWGS72 = CoordinateSystemFactory.CreateGeographicCoordinateSystem("WGS72 Geographic", AngularUnit.Degrees, wgs72, PrimeMeridian.Greenwich, - new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var gcsWGS84 = CoordinateSystemFactory.CreateGeographicCoordinateSystem("WGS84 Geographic", AngularUnit.Degrees, HorizontalDatum.WGS84, PrimeMeridian.Greenwich, - new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var gcsED50 = CoordinateSystemFactory.CreateGeographicCoordinateSystem("ED50 Geographic", AngularUnit.Degrees, ed50, PrimeMeridian.Greenwich, - new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - //Define geocentric coordinate systems - var gcenCsWGS72 = CoordinateSystemFactory.CreateGeocentricCoordinateSystem("WGS72 Geocentric", wgs72, LinearUnit.Metre, PrimeMeridian.Greenwich); - var gcenCsWGS84 = CoordinateSystemFactory.CreateGeocentricCoordinateSystem("WGS84 Geocentric", HorizontalDatum.WGS84, LinearUnit.Metre, PrimeMeridian.Greenwich); - var gcenCsED50 = CoordinateSystemFactory.CreateGeocentricCoordinateSystem("ED50 Geocentric", ed50, LinearUnit.Metre, PrimeMeridian.Greenwich); - - //Define projections - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_origin", 0), - new ProjectionParameter("central_meridian", 9), - new ProjectionParameter("scale_factor", 0.9996), - new ProjectionParameter("false_easting", 500000), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Transverse Mercator", "Transverse_Mercator", parameters); - var utmED50 = CoordinateSystemFactory.CreateProjectedCoordinateSystem("ED50 UTM Zone 32N", gcsED50, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - var utmWGS84 = CoordinateSystemFactory.CreateProjectedCoordinateSystem("WGS84 UTM Zone 32N", gcsWGS84, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - ////Set up coordinate transformations - //var ctForw = _coordinateTransformationFactory.CreateFromCoordinateSystems(gcsWGS72, gcenCsWGS72); //Geographic->Geocentric (WGS72) - //var ctWGS84_Gcen2Geo = _coordinateTransformationFactory.CreateFromCoordinateSystems(gcenCsWGS84, gcsWGS84); //Geocentric->Geographic (WGS84) - //var ctWGS84_Geo2UTM = _coordinateTransformationFactory.CreateFromCoordinateSystems(gcsWGS84, utmWGS84); //UTM ->Geographic (WGS84) - //var ctED50_UTM2Geo = _coordinateTransformationFactory.CreateFromCoordinateSystems(utmED50, gcsED50); //UTM ->Geographic (ED50) - //var ctED50_Geo2Gcen = _coordinateTransformationFactory.CreateFromCoordinateSystems(gcsED50, gcenCsED50); //Geographic->Geocentric (ED50) - - //Test datum-shift from WGS72 to WGS84 - //Point3D pGeoCenWGS72 = ctForw.MathTransform.Transform(pLongLatWGS72) as Point3D; - double[] pGeoCenWGS72 = new[] {3657660.66, 255768.55, 5201382.11}; - var geocen_ed50_2_Wgs84 = CoordinateTransformationFactory.CreateFromCoordinateSystems(gcenCsWGS72, gcenCsWGS84); - double[] pGeoCenWGS84 = geocen_ed50_2_Wgs84.MathTransform.Transform(pGeoCenWGS72); - //Point3D pGeoCenWGS84 = wgs72.Wgs84Parameters.Apply(pGeoCenWGS72); - double[] pExpected = new[] {3657660.78, 255778.43, 5201387.75}; - Assert.IsTrue(ToleranceLessThan(pExpected, pGeoCenWGS84, 0.01), TransformationError("Datum WGS72->WGS84", pExpected, pGeoCenWGS84)); - //and inverse - double[] pGeoCenWGS72calc = geocen_ed50_2_Wgs84.MathTransform.Inverse().Transform(pGeoCenWGS84); - Assert.IsTrue(ToleranceLessThan(pGeoCenWGS72, pGeoCenWGS72calc, 0.001), TransformationError("Datum WGS84->WGS72", pGeoCenWGS72, pGeoCenWGS72calc)); - - var utm_ed50_2_Wgs84 = CoordinateTransformationFactory.CreateFromCoordinateSystems(utmED50, utmWGS84); - double[] pUTMED50 = new double[] {600000, 6100000}; - double[] pUTMWGS84 = utm_ed50_2_Wgs84.MathTransform.Transform(pUTMED50); - pExpected = new[] { 599928.6, 6099790.2}; - Assert.IsTrue(ToleranceLessThan(pExpected, pUTMWGS84, 0.1), TransformationError("Datum ED50->WGS84", pExpected, pUTMWGS84)); - //and inverse - double[] pUTMED50calc = utm_ed50_2_Wgs84.MathTransform.Inverse().Transform(pUTMWGS84); - Assert.IsTrue(ToleranceLessThan(pUTMED50, pUTMED50calc, 0.01), TransformationError("Datum WGS84->ED50", pUTMED50, pUTMED50calc)); - - - //Perform reverse - var utm_Wgs84_2_Ed50 = CoordinateTransformationFactory.CreateFromCoordinateSystems(utmWGS84, utmED50); - pUTMED50 = utm_Wgs84_2_Ed50.MathTransform.Transform(pUTMWGS84); - pExpected = new double[] {600000, 6100000}; - Assert.IsTrue(ToleranceLessThan(pExpected, pUTMED50, 0.1), TransformationError("Datum", pExpected, pUTMED50)); - //and inverse - double[] pUTMWGS84calc = utm_Wgs84_2_Ed50.MathTransform.Inverse().Transform(pUTMED50); - Assert.IsTrue(ToleranceLessThan(pUTMWGS84, pUTMWGS84calc, 0.1), TransformationError("Datum", pUTMWGS84, pUTMWGS84calc)); - - - //Assert.IsTrue(Math.Abs((pUTMWGS84 as Point3D).Z - 36.35) < 0.5); - //Point pExpected = Point.FromDMS(2, 7, 46.38, 53, 48, 33.82); - //ED50_to_WGS84_Denmark: datum.Wgs84Parameters = new Wgs84ConversionInfo(-89.5, -93.8, 127.6, 0, 0, 4.5, 1.2); - - } - - [Test] - public void TestKrovak_Greenwich_Projection() - { - //test case for epsg 5514 (102067) - - var gcsWGS84 = CoordinateSystemFactory.CreateGeographicCoordinateSystem("WGS84 Geographic", AngularUnit.Degrees, HorizontalDatum.WGS84, PrimeMeridian.Greenwich, - new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Bessel 1840", DatumType.HD_Geocentric, ellipsoid, null); - datum.Wgs84Parameters = new Wgs84ConversionInfo(570.8, 85.7, 462.8, 4.998, 1.587, 5.261, 3.56); - - var gcsKrovak = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Bessel 1840", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_center", 49.5), - new ProjectionParameter("longitude_of_center", 24.83333333333333), - new ProjectionParameter("azimuth", 30.28813972222222), - new ProjectionParameter("pseudo_standard_parallel_1", 78.5), - new ProjectionParameter("scale_factor", 0.9999), - new ProjectionParameter("false_easting", 0), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Krovak", "Krovak", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Krovak", gcsKrovak, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = new CoordinateTransformationFactory().CreateFromCoordinateSystems(gcsWGS84, coordsys); - var trans2 = new CoordinateTransformationFactory().CreateFromCoordinateSystems(gcsWGS84, coordsys); - - // test case 1 - double[] pGeo = new[] { 12d, 48d }; - double[] expected = new[] { -953116.2548718402, -1245513.5788112187 }; - - double[] pUtm = trans.MathTransform.Transform(pGeo); - //can't inverse trans - Inverse() of ConcateratedTransform makes shallow copy and call Invert on each ICoordinateTransformation.MathTransform - this changes original transformation! - double[] pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); - - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.2), TransformationError("Krovak", expected, pUtm)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.001), TransformationError("Krovak", pGeo, pGeo2, true)); - - // test case 2 - pGeo = new double[] { 18, 49 }; - expected = new double[] { -499143.4909304862, -1192340.009253714 }; - - pUtm = trans.MathTransform.Transform(pGeo); - pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); - - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.2), TransformationError("Krovak", expected, pUtm)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.001), TransformationError("Krovak", pGeo, pGeo2)); - } - - [Test] - public void TestKrovak_Ferro_Projection() - { - //test case for epsg 2065 (prime meridian at Ferro) - var gcsWGS84 = CoordinateSystemFactory.CreateGeographicCoordinateSystem("WGS84 Geographic", AngularUnit.Degrees, HorizontalDatum.WGS84, PrimeMeridian.Greenwich, - new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - - var ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1840", 6377397.155, 299.15281, LinearUnit.Metre); - - var datum = CoordinateSystemFactory.CreateHorizontalDatum("Bessel 1840", DatumType.HD_Geocentric, ellipsoid, null); - datum.Wgs84Parameters = new Wgs84ConversionInfo(570.8, 85.7, 462.8, 4.998, 1.587, 5.261, 3.56); - - var gcsKrovak = CoordinateSystemFactory.CreateGeographicCoordinateSystem("Bessel 1840", AngularUnit.Degrees, datum, - PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), - new AxisInfo("Lat", AxisOrientationEnum.North)); - gcsKrovak.PrimeMeridian = PrimeMeridian.Ferro; - - var parameters = new List(5) - { - new ProjectionParameter("latitude_of_center", 49.5), - new ProjectionParameter("longitude_of_center", 42.5), - new ProjectionParameter("azimuth", 30.28813972222222), - new ProjectionParameter("pseudo_standard_parallel_1", 78.5), - new ProjectionParameter("scale_factor", 0.9999), - new ProjectionParameter("false_easting", 0), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Krovak", "Krovak", parameters); - - var coordsys = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Krovak", gcsKrovak, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var trans = new CoordinateTransformationFactory().CreateFromCoordinateSystems(gcsWGS84, coordsys); - var trans2 = new CoordinateTransformationFactory().CreateFromCoordinateSystems(gcsWGS84, coordsys); - - // test case 1 - double[] pGeo = new[] { 12d, 48d }; - double[] expected = new[] { -953116.2548718402, -1245513.5788112187 }; - - double[] pUtm = trans.MathTransform.Transform(pGeo); - //can't inverse trans - Inverse() of ConcateratedTransform makes shallow copy and call Invert on each ICoordinateTransformation.MathTransform - this changes original transformation! - double[] pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); - - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.2), TransformationError("Krovak", expected, pUtm)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.001), TransformationError("Krovak", pGeo, pGeo2, true)); - - // test case 2 - pGeo = new double[] { 18, 49 }; - expected = new double[] { -499143.4909304862, -1192340.009253714 }; - - pUtm = trans.MathTransform.Transform(pGeo); - pGeo2 = trans2.MathTransform.Inverse().Transform(pUtm); - - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.2), TransformationError("Krovak", expected, pUtm)); - Assert.IsTrue(ToleranceLessThan(pGeo, pGeo2, 0.001), TransformationError("Krovak", pGeo, pGeo2)); - } - - [Test] - public void TestObliqueStereographicProjection() - { - //test data from http://www.spatialreference.org/ref/epsg/2171/ - double[] Coord2171 = new double[] { 4615496.325851, 5605702.221723 }; - double[] Coord4326 = new double[] { 20.78002815042, 50.25299100927 }; - - - string wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]"; - string wkt2171 = "PROJCS[\"Pulkovo 1942(58) / Poland zone I\",GEOGCS[\"Pulkovo 1942(58)\",DATUM[\"Pulkovo_1942_58\",SPHEROID[\"Krassowsky 1940\",6378245,298.3,AUTHORITY[\"EPSG\",\"7024\"]],TOWGS84[33.4,-146.6,-76.3,-0.359,-0.053,0.844,-0.84],AUTHORITY[\"EPSG\",\"6179\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4179\"]],PROJECTION[\"Oblique_Stereographic\"],PARAMETER[\"latitude_of_origin\",50.625],PARAMETER[\"central_meridian\",21.08333333333333],PARAMETER[\"scale_factor\",0.9998],PARAMETER[\"false_easting\",4637000],PARAMETER[\"false_northing\",5647000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"2171\"]]"; - - var cs1 = CoordinateSystemFactory.CreateFromWkt(wkt4326); - var cs2 = CoordinateSystemFactory.CreateFromWkt(wkt2171); - - var ctf = new CoordinateTransformationFactory(); - var ict = ctf.CreateFromCoordinateSystems(cs2, cs1); - - double[] transformedCoord4326 = ict.MathTransform.Transform(Coord2171); - - - Assert.AreEqual(Coord4326[0], transformedCoord4326[0], 0.01); - Assert.AreEqual(Coord4326[1], transformedCoord4326[1], 0.01); - - - var ict2 = ctf.CreateFromCoordinateSystems(cs1, cs2); - double[] transformedCoord2171 = ict2.MathTransform.Transform(Coord4326); - - Assert.AreEqual(Coord2171[0], transformedCoord2171[0], 1); - Assert.AreEqual(Coord2171[1], transformedCoord2171[1], 1); - } - - [Test] - public void TestUniversalPolarStereographicProjection() - { - //test data from http://epsg.io/transform - double[] Coord4326 = new double[] { 15.00, 73.00 }; - double[] Coord32661 = new double[] { 2491967.01029204, 163954.12194234435 }; - - string wkt4326 = "" + - "GEOGCS[\"WGS 84\"," + - "DATUM[\"WGS_1984\"," + - "SPHEROID[\"WGS 84\",6378137,298.257223563," + - "AUTHORITY[\"EPSG\",\"7030\"]]," + - "AUTHORITY[\"EPSG\",\"6326\"]]," + - "PRIMEM[\"Greenwich\",0," + - "AUTHORITY[\"EPSG\",\"8901\"]]," + - "UNIT[\"degree\",0.01745329251994328," + - "AUTHORITY[\"EPSG\",\"9122\"]]," + - "AUTHORITY[\"EPSG\",\"4326\"]]"; - - string wkt32661 = "" + - "PROJCS[\"WGS 84 / UPS North (N,E)\"," + - "GEOGCS[\"WGS 84\"," + - "DATUM[\"WGS_1984\"," + - "SPHEROID[\"WGS 84\",6378137,298.257223563," + - "AUTHORITY[\"EPSG\",\"7030\"]]," + - "AUTHORITY[\"EPSG\",\"6326\"]]," + - "PRIMEM[\"Greenwich\",0," + - "AUTHORITY[\"EPSG\",\"8901\"]]," + - "UNIT[\"degree\",0.0174532925199433," + - "AUTHORITY[\"EPSG\",\"9122\"]]," + - "AUTHORITY[\"EPSG\",\"4326\"]]," + - "PROJECTION[\"Polar_Stereographic\"]," + - "PARAMETER[\"latitude_of_origin\",90]," + - "PARAMETER[\"central_meridian\",0]," + - "PARAMETER[\"scale_factor\",0.994]," + - "PARAMETER[\"false_easting\",2000000]," + - "PARAMETER[\"false_northing\",2000000]," + - "UNIT[\"metre\",1," + - "AUTHORITY[\"EPSG\",\"9001\"]]," + - "AUTHORITY[\"EPSG\",\"32661\"]]"; - - var cs1 = CoordinateSystemFactory.CreateFromWkt(wkt4326); - var cs2 = CoordinateSystemFactory.CreateFromWkt(wkt32661); - var ctf = new CoordinateTransformationFactory(); - - var ict = ctf.CreateFromCoordinateSystems(cs2, cs1); - var ict2 = ctf.CreateFromCoordinateSystems(cs1, cs2); - double[] transformedCoord4326 = ict.MathTransform.Transform(Coord32661); - double[] transformedCoord32661 = ict2.MathTransform.Transform(Coord4326); - - Assert.AreEqual(Coord4326[0], transformedCoord4326[0], 0.01); - Assert.AreEqual(Coord4326[1], transformedCoord4326[1], 0.01); - Assert.AreEqual(Coord32661[0], transformedCoord32661[0], 1); - Assert.AreEqual(Coord32661[1], transformedCoord32661[1], 1); - } - - [Test] - public void TestAustralianAntarcticPolarStereographicProjection() - { - //test data from http://epsg.io/transform - double[] Coord4326 = new double[] { 15.00, -73.00 }; - double[] Coord3032 = new double[] { 4476201.247377692, 7066975.373300694 }; - - string wkt4326 = "" + - "GEOGCS[\"WGS 84\"," + - "DATUM[\"WGS_1984\"," + - "SPHEROID[\"WGS 84\",6378137,298.257223563," + - "AUTHORITY[\"EPSG\",\"7030\"]]," + - "AUTHORITY[\"EPSG\",\"6326\"]]," + - "PRIMEM[\"Greenwich\",0," + - "AUTHORITY[\"EPSG\",\"8901\"]]," + - "UNIT[\"degree\",0.01745329251994328," + - "AUTHORITY[\"EPSG\",\"9122\"]]," + - "AUTHORITY[\"EPSG\",\"4326\"]]"; - - string wkt3032 = "" + - "PROJCS[\"WGS 84 / Australian Antarctic Polar Stereographic\"," + - "GEOGCS[\"WGS 84\"," + - "DATUM[\"WGS_1984\"," + - "SPHEROID[\"WGS 84\",6378137,298.257223563," + - "AUTHORITY[\"EPSG\",\"7030\"]]," + - "AUTHORITY[\"EPSG\",\"6326\"]]," + - "PRIMEM[\"Greenwich\",0," + - "AUTHORITY[\"EPSG\",\"8901\"]]," + - "UNIT[\"degree\",0.0174532925199433," + - "AUTHORITY[\"EPSG\",\"9122\"]]," + - "AUTHORITY[\"EPSG\",\"4326\"]]," + - "PROJECTION[\"Polar_Stereographic\"]," + - "PARAMETER[\"latitude_of_origin\",-71]," + - "PARAMETER[\"central_meridian\",70]," + - "PARAMETER[\"false_easting\",6000000]," + - "PARAMETER[\"false_northing\",6000000]," + - "UNIT[\"metre\",1," + - "AUTHORITY[\"EPSG\",\"9001\"]]," + - "AUTHORITY[\"EPSG\",\"3032\"]]"; - - var cs1 = CoordinateSystemFactory.CreateFromWkt(wkt4326); - var cs2 = CoordinateSystemFactory.CreateFromWkt(wkt3032); - var ctf = new CoordinateTransformationFactory(); - - var ict = ctf.CreateFromCoordinateSystems(cs2, cs1); - var ict2 = ctf.CreateFromCoordinateSystems(cs1, cs2); - double[] transformedCoord4326 = ict.MathTransform.Transform(Coord3032); - double[] transformedCoord3032 = ict2.MathTransform.Transform(Coord4326); - - Assert.AreEqual(Coord4326[0], transformedCoord4326[0], 0.01); - Assert.AreEqual(Coord4326[1], transformedCoord4326[1], 0.01); - Assert.AreEqual(Coord3032[0], transformedCoord3032[0], 1); - Assert.AreEqual(Coord3032[1], transformedCoord3032[1], 1); - } - - [Test] - public void TestUnitTransforms() - { - var nadUTM = SRIDReader.GetCSbyID(2868); //UTM Arizona Central State Plane using Feet as units - var wgs84GCS = SRIDReader.GetCSbyID(4326); //GCS WGS84 - var trans = new CoordinateTransformationFactory().CreateFromCoordinateSystems(wgs84GCS, nadUTM); - - double[] p0 = new[] { -111.89, 34.165 }; - //var expected = new[] { 708066.19058, 1151461.51413 }; - double[] expected = new[] { 708066.19057935325, 1151426.4460563776 }; - - - double[] p1 = trans.MathTransform.Transform(p0); - double[] p2 = trans.MathTransform.Inverse().Transform(p1); - - Assert.IsTrue(ToleranceLessThan(p1, expected, 0.013), TransformationError("Unit", expected, p1)); - //WARNING: This accuracy is too poor! - Assert.IsTrue(ToleranceLessThan(p0, p2, 0.0001), TransformationError("Unit", expected, p1, true)); - } - - [Test, Description("Accuracy very poor!")] - public void TestPolyconicTransforms() - { - var wgs84GCS = SRIDReader.GetCSbyID(4326); //GCS WGS84 - string wkt = - //"PROJCS[\"SAD69 / Brazil Polyconic (deprecated)\",GEOGCS[\"SAD69\",DATUM[\"South_American_Datum_1969\",SPHEROID[\"GRS 1967\",6378160,298.247167427,AUTHORITY[\"EPSG\",\"7036\"]],TOWGS84[-57,1,-41,0,0,0,0],AUTHORITY[\"EPSG\",\"6291\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4291\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],PROJECTION[\"Polyconic\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-54],PARAMETER[\"false_easting\",5000000],PARAMETER[\"false_northing\",10000000],AUTHORITY[\"EPSG\",\"29100\"],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]"; - //"PROJCS[\"SAD69 / Brazil Polyconic\",GEOGCS[\"SAD69\",DATUM[\"South_American_Datum_1969\",SPHEROID[\"GRS 1967 Modified\",6378160,298.25,AUTHORITY[\"EPSG\",\"7050\"]],TOWGS84[-57,1,-41,0,0,0,0],AUTHORITY[\"EPSG\",\"6618\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4618\"]],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],PROJECTION[\"Polyconic\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-54],PARAMETER[\"false_easting\",5000000],PARAMETER[\"false_northing\",10000000],AUTHORITY[\"EPSG\",\"29101\"],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]]"; - "PROJCS[\"SAD69 / Brazil Polyconic\",GEOGCS[\"SAD69\",DATUM[\"South_American_Datum_1969\",SPHEROID[\"GRS 1967 (SAD69)\", 6378160, 298.25, AUTHORITY[\"EPSG\", \"7050\"]],AUTHORITY[\"EPSG\", \"6618\"]], PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]],UNIT[\"degree\", 0.01745329251994328, AUTHORITY[\"EPSG\", \"9122\"]], AUTHORITY[\"EPSG\", \"4618\"]], PROJECTION[\"Polyconic\"],PARAMETER[\"latitude_of_origin\", 0], PARAMETER[\"central_meridian\", -54],PARAMETER[\"false_easting\", 5000000], PARAMETER[\"false_northing\", 10000000],UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]], AXIS[\"X\", EAST], AXIS[\"Y\", NORTH],AUTHORITY[\"EPSG\", \"29101\"]]"; - var sad69 = CoordinateSystemFactory.CreateFromWkt(wkt); - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(wgs84GCS, sad69); - double[] p0 = new[] { -50.085, -14.32 }; - double[] expected = new[] { 5422386.5795, 8412674.8723 }; - //"POINT(5422386.57956145 8412722.92229278)" - double[] p1 = trans.MathTransform.Transform(p0); - trans.MathTransform.Invert(); - double[] p2 = trans.MathTransform.Transform(p1); - - Assert.IsTrue(ToleranceLessThan(p1, expected, 50), TransformationError("Polyconic", expected, p1)); - Assert.IsTrue(ToleranceLessThan(p0, p2, 0.0001), TransformationError("Polyconic", expected, p1, true)); - } - - [Test] - public void TestCassiniSoldner() - { - var csSource = GeographicCoordinateSystem.WGS84; - var csTarget = CoordinateSystemFactory.CreateFromWkt( - "PROJCS[\"DHDN / Soldner Berlin\",GEOGCS[\"DHDN\",DATUM[\"Deutsches_Hauptdreiecksnetz\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],TOWGS84[598.1,73.7,418.2,0.202,0.045,-2.455,6.7],AUTHORITY[\"EPSG\",\"6314\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4314\"]],PROJECTION[\"Cassini_Soldner\"],PARAMETER[\"latitude_of_origin\",52.41864827777778],PARAMETER[\"central_meridian\",13.62720366666667],PARAMETER[\"false_easting\",40000],PARAMETER[\"false_northing\",10000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"x\",NORTH],AXIS[\"y\",EAST],AUTHORITY[\"EPSG\",\"3068\"]]"); - - Test("CassiniSoldner", csSource, csTarget, - new[] { 13.408055555556, 52.518611111111 }, - new[] { 25244.540, 21300.969 }, 0.3, 1.0E-5); - - /* - var ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(csSource, csTarget); - var pgeo = new[] {13.408055555556, 52.518611111111}; - var pcs = ct.MathTransform.Transform(pgeo); - - //Evaluated using DotSpatial.Projections - var pcsExpected = new[] {25244.540, 21300.969}; - - Assert.IsTrue(ToleranceLessThan(pcsExpected, pcs, 0.3), TransformationError("CassiniSoldner", pcsExpected, pcs)); - var pgeo2 = ct.MathTransform.Inverse().Transform(pcs); - Assert.IsTrue(ToleranceLessThan(pgeo, pgeo2, 1.0E-5), TransformationError("CassiniSoldner", pgeo, pgeo2)); - */ - } - - [Test] - public void TestHotineObliqueMercator() - { - var csSource = GeographicCoordinateSystem.WGS84; - var csTarget = CoordinateSystemFactory.CreateFromWkt( - "PROJCS[\"NAD83(NSRS2007) / Alaska zone 1\",GEOGCS[\"NAD83(NSRS2007)\",DATUM[\"NAD83_National_Spatial_Reference_System_2007\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6759\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4759\"]],PROJECTION[\"Hotine_Oblique_Mercator\"],PARAMETER[\"latitude_of_center\",57],PARAMETER[\"longitude_of_center\",-133.6666666666667],PARAMETER[\"azimuth\",323.1301023611111],PARAMETER[\"rectified_grid_angle\",323.1301023611111],PARAMETER[\"scale_factor\",0.9999],PARAMETER[\"false_easting\",5000000],PARAMETER[\"false_northing\",-5000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],AUTHORITY[\"EPSG\",\"3468\"]]"); - //61.216667°, -149.883333° - //"POINT(4136805.82642057 -4424019.78560519)" - Test("HotineObliqueMercator", csSource, csTarget, - new[] { -149.883333, 61.216667 }, - new[] { 4136805.826, -4424019.786 }, 0.01, 1.0E-5); - - } - - [Test] - public void TestTransformListOnConcatenatedDoTransform() - { - var utm35ETRS = - CoordinateSystemFactory.CreateFromWkt( - "PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); - - var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(utm35ETRS, utm33); - - var coords = new XY[] { - new XY(290586.087, 6714000), - new XY(290586.392, 6713996.224), - new XY(290590.133, 6713973.772) - }; - - trans.MathTransform.Transform(coords); - Assert.AreNotEqual(290586.087, coords[0].X); - Assert.AreNotEqual(6714000, coords[0].Y); - } - - [Test] - public void TestTransformListOnConcatenatedDoTransformDoubleArr() - { - var utm35ETRS = - CoordinateSystemFactory.CreateFromWkt( - "PROJCS[\"ETRS89 / ETRS-TM35\",GEOGCS[\"ETRS89\",DATUM[\"D_ETRS_1989\",SPHEROID[\"GRS_1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",27],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]"); - - var utm33 = ProjectedCoordinateSystem.WGS84_UTM(33, true); - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(utm35ETRS, utm33); - - var coords = new List{ - new double[]{290586.087, 6714000}, - new double[]{290586.392, 6713996.224}, - new double[]{290590.133, 6713973.772} - }; - - var transformedCoords = trans.MathTransform.TransformList(coords); - Assert.AreNotEqual(290586.087, transformedCoords[0][0]); - Assert.AreNotEqual(6714000, transformedCoords[0][1]); - } - - /// - /// Test transformation for affine transformation - /// - [Test] - public void AffineTransformationTest () - { - //Local coordinate system MNAU (Kraftwerk Mäuserich) (based on Gauß-Krüger using affine transformation) - // affine transform - // 1) Offset: X=-3454886,640m Y=-5479481,278m; - // 2)Rotation: 332,0657, Rotation point X=3456926,640m Y=5481071,278m; - // 3) Scale: 1.0 - - //TODO MathTransformFactory fac = new MathTransformFactory (); - double[,] matrix = new double[,] {{0.883485346527455, -0.468458794848877, 3455869.17937689}, - {0.468458794848877, 0.883485346527455, 5478710.88035753}, - {0.0 , 0.0, 1},}; - var mt = new AffineTransform (matrix); - - Assert.IsNotNull (mt); - - Assert.AreEqual (2, mt.DimSource); - Assert.AreEqual (2, mt.DimTarget); - - //Transformation example (MNAU -> GK) - // Start point (MNAU) X=2040,000m Y=1590,000m] - // Target point (GK): X=3456926,640m Y=5481071,278m; - - double[] outPt = mt.Transform (new double[] { 2040.0, 1590.0 }); - - Assert.AreEqual (2, outPt.Length); - Assert.AreEqual (3456926.640, outPt[0], 0.00000001); - Assert.AreEqual (5481071.278, outPt[1], 0.00000001); - } - - /// - /// Test inverse transformation for affine transformation - /// - [Test] - public void InverseAffineTransformationTest () - { - //Local coordinate system MNAU (Kraftwerk Mäuserich) (based on Gauß-Krüger using affine transformation) - // affine transform - // 1) Offset: X=-3454886,640m Y=-5479481,278m; - // 2)Rotation: 332,0657, Rotation point X=3456926,640m Y=5481071,278m; - // 3) Scale: 1.0 - - //TODO MathTransformFactory fac = new MathTransformFactory (); - double[,] matrix = new double[,] {{0.883485346527455, -0.468458794848877, 3455869.17937689}, - {0.468458794848877, 0.883485346527455, 5478710.88035753}, - {0.0 , 0.0, 1},}; - var mt = new AffineTransform (matrix); - - Assert.IsNotNull (mt); - - Assert.AreEqual (2, mt.DimSource); - Assert.AreEqual (2, mt.DimTarget); - - //Transformation example (MNAU -> GK) - // Start point (MNAU) X=2040,000m Y=1590,000m] - // Target point (GK): X=3456926,640m Y=5481071,278m; - - //check source transform - double[] outPt = mt.Transform (new double[] { 2040.0, 1590.0 }); - - Assert.AreEqual (2, outPt.Length); - Assert.AreEqual (3456926.640, outPt[0], 0.00000001); - Assert.AreEqual (5481071.278, outPt[1], 0.00000001); - - var invMt = mt.Inverse (); - - double[] inPt = invMt.Transform (new double[] { 3456926.640, 5481071.278 }); - - Assert.AreEqual (2, inPt.Length); - Assert.AreEqual (2040.0, inPt[0], 0.00000001); - Assert.AreEqual (1590.0, inPt[1], 0.00000001); - - //check source transform - once more - double[] outPt2 = mt.Transform (new double[] { 2040.0, 1590.0 }); - - Assert.AreEqual (2, outPt2.Length); - Assert.AreEqual (3456926.640, outPt2[0], 0.00000001); - Assert.AreEqual (5481071.278, outPt2[1], 0.00000001); - } - - /// - /// Coordinate transformation test for fitted coordinate system - test CS - local coordinate system MNAU - /// - [Test] - public void TestTransformOnFittedCoordinateSystem () - { - - //Local coordinate system MNAU (Kraftwerk Mäuserich) (based on Gauß-Krüger using affine transformation) - // affine transform - // 1) Offset: X=-3454886,640m Y=-5479481,278m; - // 2)Rotation: 332,0657, Rotation point X=3456926,640m Y=5481071,278m; - // 3) Scale: 1.0 - - string ft_wkt = "FITTED_CS[\"Local coordinate system MNAU (based on Gauss-Krueger)\"," + - "PARAM_MT[\"Affine\"," + - "PARAMETER[\"num_row\",3],PARAMETER[\"num_col\",3],PARAMETER[\"elt_0_0\", 0.883485346527455],PARAMETER[\"elt_0_1\", -0.468458794848877],PARAMETER[\"elt_0_2\", 3455869.17937689],PARAMETER[\"elt_1_0\", 0.468458794848877],PARAMETER[\"elt_1_1\", 0.883485346527455],PARAMETER[\"elt_1_2\", 5478710.88035753],PARAMETER[\"elt_2_2\", 1]]," + - "PROJCS[\"DHDN / Gauss-Kruger zone 3\"," + - "GEOGCS[\"DHDN\"," + - "DATUM[\"Deutsches_Hauptdreiecksnetz\"," + - "SPHEROID[\"Bessel 1841\", 6377397.155, 299.1528128, AUTHORITY[\"EPSG\", \"7004\"]]," + - "TOWGS84[612.4, 77, 440.2, -0.054, 0.057, -2.797, 0.525975255930096]," + - "AUTHORITY[\"EPSG\", \"6314\"]]," + - "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]]," + - "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9122\"]]," + - "AUTHORITY[\"EPSG\", \"4314\"]]," + - "PROJECTION[\"Transverse_Mercator\"]," + - "PARAMETER[\"latitude_of_origin\", 0]," + - "PARAMETER[\"central_meridian\", 9]," + - "PARAMETER[\"scale_factor\", 1]," + - "PARAMETER[\"false_easting\", 3500000]," + - "PARAMETER[\"false_northing\", 0]," + - "UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]]," + - "AUTHORITY[\"EPSG\", \"31467\"]]" + - "]"; - - //string gk_wkt = "PROJCS[\"DHDN / Gauss-Kruger zone 3\",GEOGCS[\"DHDN\",DATUM[\"Deutsches_Hauptdreiecksnetz\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6314\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4314\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",9],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",3500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"31467\"]]"; - - var fac = new CoordinateSystemFactory (); - var fcs = fac.CreateFromWkt (ft_wkt) as FittedCoordinateSystem; - //ICoordinateSystem gkcs = fac.CreateFromWkt (gk_wkt); - - //Transformation example (MNAU -> GK) - // Start point (MNAU) X=2040,000m Y=1590,000m] - // Target point (GK): X=3456926,640m Y=5481071,278m; - - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems (fcs, fcs.BaseCoordinateSystem); - - var coords = new List{ - new double[]{2040.0, 1590.0}, - }; - - var transformedCoords = trans.MathTransform.TransformList (coords); - Assert.AreEqual (3456926.640, transformedCoords[0][0], 0.00000001); - Assert.AreEqual (5481071.278, transformedCoords[0][1], 0.00000001); - } - - /// - /// test for epsg 21780 projection (different prime meridian) - /// - [Test] - public void Test_EPSG_21780_PrimeMeredianTransformation() - { - string wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]"; - string wkt21780 = "PROJCS[\"Bern 1898 (Bern) / LV03C\",GEOGCS[\"Bern 1898 (Bern)\",DATUM[\"CH1903_Bern\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6801\"]],PRIMEM[\"Bern\",7.439583333333333,AUTHORITY[\"EPSG\",\"8907\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4801\"]],PROJECTION[\"Hotine_Oblique_Mercator\"],PARAMETER[\"latitude_of_center\",46.95240555555556],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"azimuth\",90],PARAMETER[\"rectified_grid_angle\",90],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"21780\"]]"; - - //test data from http://spatialreference.org/ref/epsg/21780/ - double[] sourceCoord = new double[] { 160443.329034, 23582.55586 }; - double[] expectedTargetCoord = new double[] { 9.5553588867188, 47.145080566406 }; - - var cs1 = CoordinateSystemWktReader.Parse(wkt21780) as CoordinateSystem; - var cs2 = CoordinateSystemWktReader.Parse(wkt4326) as CoordinateSystem; - var ctf = new CoordinateTransformationFactory(); - var ict = ctf.CreateFromCoordinateSystems(cs1, cs2); - - double[] transformedCoord = ict.MathTransform.Transform(sourceCoord); - - Assert.IsTrue(transformedCoord.Length >= 2); - Assert.AreEqual(expectedTargetCoord[0], transformedCoord[0], 0.001); - Assert.AreEqual(expectedTargetCoord[1], transformedCoord[1], 0.001); - - //and back - var ictb = ctf.CreateFromCoordinateSystems(cs2, cs1); - transformedCoord = ictb.MathTransform.Transform(transformedCoord); - - Assert.IsTrue(transformedCoord.Length >= 2); - Assert.AreEqual(sourceCoord[0], transformedCoord[0], 0.1); - Assert.AreEqual(sourceCoord[1], transformedCoord[1], 0.1); - - } - - // https://github.com/NetTopologySuite/ProjNet4GeoAPI/issues/48 - [Test] - public void Test_EPSG_2056_HotineObliqueMercatorAzimuthCenter_Switzerland() - { - var csSrc = GeographicCoordinateSystem.WGS84; - var csTgt = SRIDReader.GetCSbyID(2056); // CH1903+ / LV95 - var transformer = CoordinateTransformationFactory.CreateFromCoordinateSystems(csSrc, csTgt); - double x = 9.619803; - double y = 47.408735; - - transformer.MathTransform.Transform(ref x, ref y); - - // https://epsg.io/transform#s_srs=4326&t_srs=2056&x=9.6198031&y=47.4087350 - Assert.That(x, Is.EqualTo(2764607.79).Within(0.1)); - Assert.That(y, Is.EqualTo(1253167.89).Within(0.1)); - } - - [Test] - public void TestEllipsoidalOrthographicTransform() - { - //Check equatorial projection - var csWgs84 = GeographicCoordinateSystem.WGS84; - var parameters = new List(5) - { - new ProjectionParameter("central_meridian", 0), - new ProjectionParameter("latitude_of_origin", 0), - new ProjectionParameter("scale_factor", 1), - new ProjectionParameter("false_easting", 0), - new ProjectionParameter("false_northing", 0) - }; - var projection = CoordinateSystemFactory.CreateProjection("Orthographic", "Orthographic", parameters); - var orthographicSystem = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Orthographic centered", csWgs84, projection, LinearUnit.Metre, new AxisInfo("X", AxisOrientationEnum.East), new AxisInfo("Y", AxisOrientationEnum.North)); - var trans = CoordinateTransformationFactory.CreateFromCoordinateSystems(csWgs84, orthographicSystem); - - //Check origin remains in the same place - double[] origin = new[] { 0.0, 0.0 }; - double[] transformedOrigin = trans.MathTransform.Transform(origin); - double[] inverseTransformedOrigin = trans.MathTransform.Inverse().Transform(transformedOrigin); - Assert.That(ToleranceLessThan(origin, transformedOrigin, 0.00001), TransformationError("Orthographic", origin, transformedOrigin)); - Assert.That(ToleranceLessThan(origin, inverseTransformedOrigin, 0.00001), TransformationError("Orthograhpic", origin, inverseTransformedOrigin, true)); - - //Check projection works as expected away from origin - double[] testEastWgs = new[] { 0.001, 0.0 }; - double[] expectedXOrtho = new[] { 111, 0.0}; // We should expect that .001 degrees is equal to 111 meters at origin - double[] transEastWgs = trans.MathTransform.Transform(testEastWgs); - double[] invTransEastWgs = trans.MathTransform.Inverse().Transform(transEastWgs); - Assert.That(ToleranceLessThan(expectedXOrtho, transEastWgs, 1.0), TransformationError("Orthographic", expectedXOrtho, transEastWgs)); - Assert.That(ToleranceLessThan(testEastWgs, invTransEastWgs, 1.0), TransformationError("Orthographic", testEastWgs, invTransEastWgs, true)); - - - //Check from guidance 7.2 - var parameters2 = new List(5) - { - new ProjectionParameter("central_meridian", 5.0), - new ProjectionParameter("latitude_of_origin", 55.0), - new ProjectionParameter("scale_factor", 1), - new ProjectionParameter("false_easting", 0), - new ProjectionParameter("false_northing", 0) - }; - var projection2 = CoordinateSystemFactory.CreateProjection("Orthographic", "Orthographic", parameters2); - var orthoSystem2 = CoordinateSystemFactory.CreateProjectedCoordinateSystem("Orthographic", csWgs84, projection2, LinearUnit.Metre, new AxisInfo("X", AxisOrientationEnum.East), new AxisInfo("Y", AxisOrientationEnum.North)); - var trans2 = CoordinateTransformationFactory.CreateFromCoordinateSystems(csWgs84, orthoSystem2); - double[] test2 = new[] { 2.1295499950867, 53.809394412498 }; - double[] expected2 = new[] { -189011.711, -128640.567 }; - double[] transTest2 = trans2.MathTransform.Transform(test2); - double[] invTransTest2 = trans2.MathTransform.Inverse().Transform(transTest2); - Assert.That(ToleranceLessThan(expected2, transTest2, 1.0), TransformationError("Orthographic", expected2, transTest2)); - Assert.That(ToleranceLessThan(test2, invTransTest2, 1.0), TransformationError("Orthographic", test2, invTransTest2, true)); - - //Check that the algorithm correctly identifies a point that cannot be seen - var @delegate = new TestDelegate(() => trans.MathTransform.Transform(new[] { 180.0, 0.0 })); - Assert.Throws(@delegate); - - var @delegate2 = new TestDelegate(() => trans2.MathTransform.Transform(new[] { 180.0, 0.0 })); - Assert.Throws(@delegate2); - } - - [Test] - public static void TestMercatorAuxilarySphereTransformation() - { - string sourceWkt = "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_Auxiliary_Sphere\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"Standard_Parallel_1\",0.0],PARAMETER[\"Auxiliary_Sphere_Type\",0.0],UNIT[\"Meter\",1.0]]"; - var sourceCoordinateSystem = GetCoordinateSystem(sourceWkt); - Assert.NotNull(sourceCoordinateSystem); - - string targetWkt = "PROJCS[\"TX83-NCF\",GEOGCS[\"LL83\",DATUM[\"NAD83\",SPHEROID[\"GRS1980\",6378137.000,298.25722210]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Lambert_Conformal_Conic_2SP\"],PARAMETER[\"false_easting\",1968500.000],PARAMETER[\"false_northing\",6561666.667],PARAMETER[\"central_meridian\",-98.50000000000000],PARAMETER[\"latitude_of_origin\",31.66666666666666],PARAMETER[\"standard_parallel_1\",33.96666666666667],PARAMETER[\"standard_parallel_2\",32.13333333333333],UNIT[\"Foot_US\",0.30480060960122]]"; - var targetCoordinateSystem = GetCoordinateSystem(targetWkt); - Assert.NotNull(targetCoordinateSystem); - - var transformation = GetTransformation(sourceCoordinateSystem, targetCoordinateSystem); - Assert.NotNull(transformation); - - var tranformedPoint = transformation.MathTransform.Transform(-10775704.511, 3865240.329); - Assert.NotNull(tranformedPoint); - - Assert.AreEqual(2491034.95, tranformedPoint.x, 0.1); - Assert.AreEqual(6968468.98, tranformedPoint.y, 0.1); - } - - [Test] - public void TestPopularVisualizationPseudoMercatorProjectionRegistry() - { - string sourceWkt = "GEOGCS[\"GCS_WGS_1984\", DATUM[\"D_WGS_1984\", SPHEROID[\"WGS_1984\",6378137.0,298.257223563]], PRIMEM[\"Greenwich\",0.0], UNIT[\"Degree\",0.0174532925199433]]"; - string targetWkt = "PROJCS[\"WGS84.PseudoMercator\",GEOGCS[\"LL84\",DATUM[\"WGS84\",SPHEROID[\"WGS84\",6378137.000,298.25722356]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Popular Visualisation Pseudo Mercator\"],PARAMETER[\"false_easting\",0.000],PARAMETER[\"false_northing\",0.000],PARAMETER[\"central_meridian\",0.00000000000000],UNIT[\"Meter\",1.00000000000000]]"; - - var sourceCoordinateSystem = GetCoordinateSystem(sourceWkt); - Assert.NotNull(sourceCoordinateSystem); - - var targetCoordinateSystem = GetCoordinateSystem(targetWkt); - Assert.NotNull(targetCoordinateSystem); - - var transformation = GetTransformation(sourceCoordinateSystem, targetCoordinateSystem); - Assert.NotNull(transformation); - } - - [Test] - public void TestLamberTangentialConformalConicProjectionRegistryAndTransformation() - { - string sourceWkt = "PROJCS[\"WORLD-LM-TAN\",GEOGCS[\"LL84\",DATUM[\"WGS84\",SPHEROID[\"WGS84\",6378137.000,298.25722356]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Lambert Tangential Conformal Conic Projection\"],PARAMETER[\"false_easting\",0.000],PARAMETER[\"false_northing\",0.000],PARAMETER[\"scale_factor\",1.000000000000],PARAMETER[\"central_meridian\",0.00000000000000],PARAMETER[\"latitude_of_origin\",1.00000000000000],UNIT[\"Meter\",1.00000000000000]]"; - string targetWkt = "PROJCS[\"WGS84.PseudoMercator\",GEOGCS[\"LL84\",DATUM[\"WGS84\",SPHEROID[\"WGS84\",6378137.000,298.25722356]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Popular Visualisation Pseudo Mercator\"],PARAMETER[\"false_easting\",0.000],PARAMETER[\"false_northing\",0.000],PARAMETER[\"central_meridian\",0.00000000000000],UNIT[\"Meter\",1.00000000000000]]"; - - var sourceCoordinateSystem = GetCoordinateSystem(sourceWkt); - Assert.NotNull(sourceCoordinateSystem); - - var targetCoordinateSystem = GetCoordinateSystem(targetWkt); - Assert.NotNull(targetCoordinateSystem); - - var transformation = GetTransformation(sourceCoordinateSystem, targetCoordinateSystem); - Assert.NotNull(transformation); - - // Test the transformation with a known points. Tested with AutoCAD map 3D - double[] pGeo = new[] { 4101119.6855, -229063.8661 }; // Nairobi, Kenya - double[] pUtm = transformation.MathTransform.Transform(pGeo); - - double[] expected = new[] { 4098998.6422, -142387.5532 }; - Assert.IsTrue(ToleranceLessThan(pUtm, expected, 0.05), TransformationError("LambertConicConformal2SP", expected, pUtm)); - } - - internal static CoordinateSystem GetCoordinateSystem(string wkt) - { - var coordinateSystemFactory = new CoordinateSystemFactory(); - return coordinateSystemFactory.CreateFromWkt(wkt); - } - - internal static ICoordinateTransformation GetTransformation(CoordinateSystem sourceCoordinateSystem, CoordinateSystem targetCoordinateSystem) - { - var coordinateSystemFactory = new CoordinateSystemFactory(); - var coordinateService = new ProjNet.CoordinateSystemServices(coordinateSystemFactory, new CoordinateTransformationFactory()); - return coordinateService.CreateTransformation(sourceCoordinateSystem, targetCoordinateSystem); - } - } -} diff --git a/test/ProjNet.Tests/CoordinateTransformTestsBase.cs b/test/ProjNet.Tests/CoordinateTransformTestsBase.cs deleted file mode 100644 index d4b6e873..00000000 --- a/test/ProjNet.Tests/CoordinateTransformTestsBase.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Globalization; -using NUnit.Framework; -using ProjNet.CoordinateSystems; -using ProjNet.CoordinateSystems.Transformations; - -namespace ProjNET.Tests -{ - public class CoordinateTransformTestsBase - { - protected readonly CoordinateSystemFactory CoordinateSystemFactory = new CoordinateSystemFactory(); - protected readonly CoordinateTransformationFactory CoordinateTransformationFactory = new CoordinateTransformationFactory(); - protected readonly Random Random = new Random(); - - protected bool Verbose { get; set; } - - protected bool ToleranceLessThan(double[] p1, double[] p2, double tolerance) - { - double d0 = Math.Abs(p1[0] - p2[0]); - double d1 = Math.Abs(p1[1] - p2[1]); - if (p1.Length > 2 && p2.Length > 2) - { - double d2 = Math.Abs(p1[2] - p2[2]); - if (Verbose) - Console.WriteLine("Allowed Tolerance {3}; got dx: {0}, dy: {1}, dz {2}", d0, d1, d2, tolerance); - return d0 < tolerance && d1 < tolerance && d2 < tolerance; - } - Console.WriteLine(); - if (Verbose) - Console.WriteLine("Allowed tolerance {2}; got dx: {0}, dy: {1}", d0, d1, tolerance); - return d0 < tolerance && d1 < tolerance; - } - - protected string TransformationError(string projection, double[] pExpected, double[] pResult, bool reverse = false) - { - return string.Format(CultureInfo.InvariantCulture, - "{6} {7} transformation outside tolerance!\n\tExpected [{0}, {1}],\n\tgot [{2}, {3}],\n\tdelta [{4}, {5}]", - pExpected[0], pExpected[1], - pResult[0], pResult[1], - pExpected[0]-pResult[0], pExpected[1]-pResult[1], - projection, reverse ? "reverse" : "forward"); - } - - public void Test(string title, CoordinateSystem source, CoordinateSystem target, - double[] testPoint, double[] expectedPoint, - double tolerance, double reverseTolerance = double.NaN) - { - var ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); - - double[] forwardResult = ct.MathTransform.Transform(testPoint); - double[] reverseResult = double.IsNaN(reverseTolerance) - ? testPoint - : ct.MathTransform.Inverse().Transform(forwardResult); - - bool forward = ToleranceLessThan(forwardResult, expectedPoint, tolerance); - - bool reverse = double.IsNaN(reverseTolerance) || - ToleranceLessThan(reverseResult, testPoint, reverseTolerance); - - if (!forward) - TransformationError(title, expectedPoint, forwardResult); - if (!reverse) - TransformationError(title, testPoint, reverseResult, true); - - Assert.IsTrue(forward && reverse); - - - } - } -} diff --git a/test/ProjNet.Tests/Data/DatumEnsembleRuntimeTests.cs b/test/ProjNet.Tests/Data/DatumEnsembleRuntimeTests.cs new file mode 100644 index 00000000..eb956585 --- /dev/null +++ b/test/ProjNet.Tests/Data/DatumEnsembleRuntimeTests.cs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies runtime transformation resolution for ensemble-backed datum metadata. +/// +public class DatumEnsembleRuntimeTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + + /// + /// Verifies ensemble-backed geographic CRS reuse the existing identity path against the equivalent single datum. + /// + [Fact] + public void CreateTransformation_WithGeographicDatumEnsembleAndSingleDatum_UsesExistingIdentityPath() + { + GeographicCoordinateSystem source = CreateEnsembleBackedGeographicCoordinateSystem(); + GeographicCoordinateSystem target = GeographicCoordinateSystem.WGS84; + + ICoordinateTransformation transformation = CoordinateTransformTests.GetTransformation(source, target); + double[] transformed = transformation.MathTransform.Transform([12.5d, 55.7d]); + + Assert.Equal(12.5d, transformed[0], 12); + Assert.Equal(55.7d, transformed[1], 12); + } + + /// + /// Verifies ensemble-backed vertical CRS preserve the current resolver behavior of the equivalent single datum case. + /// + [Fact] + public void CreateTransformation_WithVerticalDatumEnsembleAndSingleDatum_PreservesCurrentResolverBehavior() + { + VerticalCoordinateSystem source = CreateEnsembleBackedVerticalCoordinateSystem(); + VerticalCoordinateSystem singleDatumSource = CoordinateSystemFactory.CreateVerticalCoordinateSystem( + "Example ensemble height", + CoordinateSystemFactory.CreateVerticalDatum("Single datum", DatumType.VD_GeoidModelDerived), + LinearUnit.Metre, + new AxisInfo("Gravity-related height", AxisOrientationEnum.Up)); + VerticalCoordinateSystem target = CoordinateSystemFactory.CreateVerticalCoordinateSystem( + "Example ensemble height", + CoordinateSystemFactory.CreateVerticalDatum("Single datum", DatumType.VD_GeoidModelDerived), + LinearUnit.Metre, + new AxisInfo("Gravity-related height", AxisOrientationEnum.Up)); + + NotSupportedException singleDatumException = Assert.Throws( + () => CoordinateTransformTests.GetTransformation(singleDatumSource, target)); + NotSupportedException ensembleException = Assert.Throws( + () => CoordinateTransformTests.GetTransformation(source, target)); + + Assert.Equal(singleDatumException.Message, ensembleException.Message); + } + + private static GeographicCoordinateSystem CreateEnsembleBackedGeographicCoordinateSystem() + { + DatumEnsemble ensemble = new( + "World Geodetic System 1984 ensemble", + [ + new DatumEnsembleMember("World Geodetic System 1984 (Transit)", "EPSG", 1166), + new DatumEnsembleMember("World Geodetic System 1984 (G730)", "EPSG", 1152), + ], + 2d, + HorizontalDatum.WGS84.Ellipsoid, + "EPSG", + 6326); + HorizontalDatum datum = HorizontalDatum.WGS84 + .WithName("World Geodetic System 1984 ensemble") + .WithEnsemble(ensemble); + + return CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS 84", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + private static VerticalCoordinateSystem CreateEnsembleBackedVerticalCoordinateSystem() + { + DatumEnsemble ensemble = new( + "Example vertical ensemble", + [ + new DatumEnsembleMember("Datum A"), + new DatumEnsembleMember("Datum B"), + ], + 0.05d); + VerticalDatum datum = Assert.IsType( + CoordinateSystemFactory.CreateVerticalDatum("Example vertical ensemble", DatumType.VD_GeoidModelDerived) + .WithEnsemble(ensemble)); + + return CoordinateSystemFactory.CreateVerticalCoordinateSystem( + "Example ensemble height", + datum, + LinearUnit.Metre, + new AxisInfo("Gravity-related height", AxisOrientationEnum.Up)); + } +} diff --git a/test/ProjNet.Tests/Data/DefModelRuntimeTests.cs b/test/ProjNet.Tests/Data/DefModelRuntimeTests.cs new file mode 100644 index 00000000..687150ba --- /dev/null +++ b/test/ProjNet.Tests/Data/DefModelRuntimeTests.cs @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.IO; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for defmodel. +/// +public class DefModelRuntimeTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + private static readonly Lazy MercatorInverseTransform = new(CreateMercatorInverseTransform); + + /// + /// Gets invalid creation scenarios. + /// + /// Invalid case dataset. + public static IEnumerable> GetInvalidCreationCases() + { + yield return new TheoryDataRow("+proj=defmodel", "+model"); + yield return new TheoryDataRow("+proj=defmodel +model=i_do_not_exist", "Cannot open"); + yield return new TheoryDataRow($"+proj=defmodel +model={FindFixturePath(Path.Combine("Fixtures", "gie", "defmodel.gie"))}", "invalid model"); + } + + /// + /// Gets forward vector scenarios from defmodel.gie. + /// + /// Forward case dataset. + public static IEnumerable> GetForwardCases() + { + yield return Case( + BuildDefModelOperation("simple_model_degree_horizontal.json"), + CreatePoint(2d, 49d, 30d, 2020d), + CreatePoint(3d, 51d, 30d, 2020d), + 1e-9d); + + yield return Case( + BuildDefModelOperation("simple_model_degree_3d.json"), + CreatePoint(2d, 49d, 30d, 2020d), + CreatePoint(3d, 51d, 33d, 2020d), + 1e-9d); + + yield return Case( + BuildDefModelOperation("simple_model_metre_horizontal.json"), + ConvertMercatorProjectedPointToGeographic(CreatePoint(10d, 20d, 30d, 2020d)), + ConvertMercatorProjectedPointToGeographic(CreatePoint(11d, 22d, 30d, 2020d)), + 1e-8d); + + yield return Case( + BuildDefModelOperation("simple_model_metre_3d.json"), + ConvertMercatorProjectedPointToGeographic(CreatePoint(10d, 20d, 30d, 2020d)), + ConvertMercatorProjectedPointToGeographic(CreatePoint(11d, 22d, 33d, 2020d)), + 1e-8d); + + string projectedOperation = BuildDefModelOperation("simple_model_projected.json"); + yield return Case(projectedOperation, CreatePoint(1500200.0, 5400400.0, 30d, 2020d), CreatePoint(1500200.588, 5400399.722, 30.6084, 2020d), 1e-6d); + yield return Case(projectedOperation, CreatePoint(1500000.0, 5400000.0, 30d, 2020d), CreatePoint(1500000.4, 5399999.8, 30.84, 2020d), 1e-6d); + yield return Case(projectedOperation, CreatePoint(1501000.0, 5400000.0, 30d, 2020d), CreatePoint(1501000.5, 5399999.75, 30.75, 2020d), 1e-6d); + yield return Case(projectedOperation, CreatePoint(1500000.0, 5401000.0, 30d, 2020d), CreatePoint(1500000.8, 5400999.6, 30.36, 2020d), 1e-6d); + yield return Case(projectedOperation, CreatePoint(1501000.0, 5401000.0, 30d, 2020d), CreatePoint(1501001.0, 5400999.7, 30d, 2020d), 1e-6d); + + yield return Case( + BuildDefModelOperation("simple_model_metre_3d_geocentric.json"), + ConvertMercatorProjectedPointToGeographic(CreatePoint(10d, 20d, 30d, 2020d)), + ConvertMercatorProjectedPointToGeographic(CreatePoint(11d, 22d, 33d, 2020d)), + 1e-8d); + + yield return Case( + BuildDefModelOperation("simple_model_metre_vertical.json"), + CreatePoint(2d, 49d, 30d, 2020d), + CreatePoint(2d, 49d, 33d, 2020d), + 1e-7d); + + yield return Case( + BuildDefModelOperation("simple_model_metre_vertical.json"), + CreatePoint(362d, 49d, 30d, 2020d), + CreatePoint(2d, 49d, 33d, 2020d), + 1e-7d); + + yield return Case( + BuildDefModelOperation("simple_model_wrap_east.json"), + CreatePoint(165.9d, -37.3d, 10d, 2020d), + CreatePoint(165.9d, -37.3d, 10.4525d, 2020d), + 1e-6d); + + yield return Case( + BuildDefModelOperation("simple_model_wrap_west.json"), + CreatePoint(165.9d, -37.3d, 10d, 2020d), + CreatePoint(165.9d, -37.3d, 10.4525d, 2020d), + 1e-6d); + + string polarOperation = BuildDefModelOperation("simple_model_polar.json"); + yield return Case(polarOperation, CreatePoint(20d, -90d, 15d, 2020d), CreatePoint(27.4743245365d, -89.9999747721d, 18d, 2020d), 3e-5d); + yield return Case(polarOperation, CreatePoint(120d, -90d, 15d, 2020d), CreatePoint(27.4737934098d, -89.9999747718d, 18d, 2020d), 3e-5d); + yield return Case(polarOperation, CreatePoint(235d, -89.5d, 15d, 2020d), CreatePoint(-124.9986638571d, -89.5000223708d, 17.375d, 2020d), 3e-5d); + yield return Case(polarOperation, CreatePoint(45d, -89.5d, 15d, 2020d), CreatePoint(44.9991295392d, -89.4999759438d, 18.5469d, 2020d), 3e-5d); + } + + /// + /// Gets representative roundtrip scenarios. + /// + /// Roundtrip case dataset. + public static IEnumerable> GetRoundtripCases() + { + yield return RoundtripCase(BuildDefModelOperation("simple_model_degree_horizontal.json"), CreatePoint(2d, 49d, 30d, 2020d), 1e-8d); + yield return RoundtripCase(BuildDefModelOperation("simple_model_degree_3d.json"), CreatePoint(2d, 49d, 30d, 2020d), 1e-8d); + yield return RoundtripCase(BuildDefModelOperation("simple_model_metre_horizontal.json"), ConvertMercatorProjectedPointToGeographic(CreatePoint(10d, 20d, 30d, 2020d)), 1e-8d); + yield return RoundtripCase(BuildDefModelOperation("simple_model_metre_3d.json"), ConvertMercatorProjectedPointToGeographic(CreatePoint(10d, 20d, 30d, 2020d)), 1e-8d); + yield return RoundtripCase(BuildDefModelOperation("simple_model_metre_3d_geocentric.json"), ConvertMercatorProjectedPointToGeographic(CreatePoint(10d, 20d, 30d, 2020d)), 1e-8d); + yield return RoundtripCase(BuildDefModelOperation("simple_model_metre_vertical.json"), CreatePoint(2d, 49d, 30d, 2020d), 1e-8d); + yield return RoundtripCase(BuildDefModelOperation("simple_model_polar.json"), CreatePoint(45d, -89.5d, 15d, 2020d), 2e-5d); + yield return RoundtripCase(BuildDefModelOperation("simple_model_projected.json"), CreatePoint(1500200d, 5400400d, 30d, 2020d), 1e-4d); + } + + /// + /// Verifies model argument validation paths. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [MemberData(nameof(GetInvalidCreationCases))] + public void DefModelCreationFailsForInvalidModelConfiguration(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies missing-time handling for both forward and inverse setup. + /// + /// Whether inverse setup is used. + [Theory] + [InlineData(false)] + [InlineData(true)] + public void DefModelRequiresObservationEpoch(bool inverse) + { + string operation = BuildDefModelOperation("simple_model_degree_horizontal.json"); + if (inverse) + { + operation += " +inv"; + } + + MathTransform transform = CreateTransform(operation); + ArgumentException exception = Assert.Throws(() => transform.Transform(CreatePoint(2d, 49d, 30d, double.MaxValue))); + Assert.Contains("observation epoch", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that the 3D transform overload reports missing observation time as unsupported. + /// + [Fact] + public void DefModelThreeDimensionalTransformRequiresObservationTime() + { + MathTransform transform = CreateTransform(BuildDefModelOperation("simple_model_degree_horizontal.json")); + double x = 2d; + double y = 49d; + double z = 30d; + + NotSupportedException exception = Assert.Throws(() => transform.Transform(ref x, ref y, ref z)); + Assert.Equal("defmodel requires observation time (4D input).", exception.Message); + } + + /// + /// Verifies forward vectors from defmodel.gie. + /// + /// Operation text. + /// Input coordinate. + /// Expected coordinate. + /// Maximum per-axis absolute tolerance. + [Theory] + [MemberData(nameof(GetForwardCases))] + public void DefModelForwardVectorsMatchGie(string operation, double[] input, double[] expected, double tolerance) + { + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(input); + AssertCoordinateClose(output, expected, tolerance); + } + + /// + /// Verifies roundtrip behavior using transform/inverse transform pairs. + /// + /// Operation text. + /// Input coordinate. + /// Maximum per-axis absolute tolerance. + [Theory] + [MemberData(nameof(GetRoundtripCases))] + public void DefModelRoundtripRecoversInput(string operation, double[] input, double tolerance) + { + MathTransform forward = CreateTransform(operation); + MathTransform inverse = forward.Inverse(); + + double[] projected = forward.Transform(input); + double[] recovered = inverse.Transform(projected); + AssertCoordinateClose(recovered, input, tolerance); + } + + /// + /// Verifies velocity-based time scaling against representative epochs. + /// + /// Observation epoch in decimal years. + /// Expected scale factor at the supplied epoch. + [Theory] + [InlineData(2020d, 0d)] + [InlineData(2021d, 1d)] + [InlineData(2022d, 2d)] + public void DefModelVelocityTimeFunction_ScalesOffsetsByEpochDelta(double observationEpoch, double expectedScaleFactor) + { + MathTransform transform = CreateTransform(BuildDefModelOperation("simple_model_degree_horizontal_velocity.json")); + double[] output = transform.Transform(CreatePoint(2d, 49d, 30d, observationEpoch)); + + AssertCoordinateClose(output, CreateScaledHorizontalExpected(expectedScaleFactor, observationEpoch), 1e-9d); + } + + /// + /// Verifies reverse-step time scaling before and after the configured epoch. + /// + /// Observation epoch in decimal years. + /// Expected scale factor at the supplied epoch. + [Theory] + [InlineData(2020d, -1d)] + [InlineData(2021d, 0d)] + [InlineData(2022d, 0d)] + public void DefModelReverseStepTimeFunction_SwitchesFromNegativeToZero(double observationEpoch, double expectedScaleFactor) + { + MathTransform transform = CreateTransform(BuildDefModelOperation("simple_model_degree_horizontal_reverse_step.json")); + double[] output = transform.Transform(CreatePoint(2d, 49d, 30d, observationEpoch)); + + AssertCoordinateClose(output, CreateScaledHorizontalExpected(expectedScaleFactor, observationEpoch), 1e-9d); + } + + /// + /// Verifies piecewise time scaling for interpolation and linear extrapolation. + /// + /// Observation epoch in decimal years. + /// Expected scale factor at the supplied epoch. + [Theory] + [InlineData(2019d, -1d)] + [InlineData(2021d, 1d)] + [InlineData(2023d, 3d)] + public void DefModelPiecewiseTimeFunction_InterpolatesAndExtrapolates(double observationEpoch, double expectedScaleFactor) + { + MathTransform transform = CreateTransform(BuildDefModelOperation("simple_model_degree_horizontal_piecewise.json")); + double[] output = transform.Transform(CreatePoint(2d, 49d, 30d, observationEpoch)); + + AssertCoordinateClose(output, CreateScaledHorizontalExpected(expectedScaleFactor, observationEpoch), 1e-9d); + } + + /// + /// Verifies exponential time scaling before the reference epoch, during relaxation, and after the end epoch clamp. + /// + /// Observation epoch in decimal years. + /// Expected scale factor at the supplied epoch. + [Theory] + [InlineData(2019d, 0d)] + [InlineData(2021d, 0.6321205588285577d)] + [InlineData(2030d, 0.9816843611112658d)] + public void DefModelExponentialTimeFunction_AppliesRelaxationCurve(double observationEpoch, double expectedScaleFactor) + { + MathTransform transform = CreateTransform(BuildDefModelOperation("simple_model_degree_horizontal_exponential.json")); + double[] output = transform.Transform(CreatePoint(2d, 49d, 30d, observationEpoch)); + + AssertCoordinateClose(output, CreateScaledHorizontalExpected(expectedScaleFactor, observationEpoch), 1e-9d); + } + + private static TheoryDataRow Case(string operation, double[] input, double[] expected, double tolerance) + { + return new TheoryDataRow(operation, input, expected, tolerance); + } + + private static TheoryDataRow RoundtripCase(string operation, double[] input, double tolerance) + { + return new TheoryDataRow(operation, input, tolerance); + } + + private static string BuildDefModelOperation(string modelFileName) + { + return $"+proj=defmodel +model={FindDefModelPath(modelFileName)}"; + } + + private static string FindDefModelPath(string modelFileName) + { + return FindFixturePath(Path.Combine("Fixtures", "defmodel", modelFileName)); + } + + private static string FindFixturePath(string relativePath) + { + string direct = Path.Combine(AppContext.BaseDirectory, relativePath); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", relativePath); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate local test fixture under test\\ProjNet.Tests\\.", relativePath); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z, double t) + { + return [x, y, z, t]; + } + + private static double[] CreateScaledHorizontalExpected(double scaleFactor, double observationEpoch) + { + return CreatePoint(2d + scaleFactor, 49d + (2d * scaleFactor), 30d, observationEpoch); + } + + private static double[] ConvertMercatorProjectedPointToGeographic(double[] point) + { + double[] geographic = MercatorInverseTransform.Value.Transform([point[0], point[1]]); + return CreatePoint(geographic[0], geographic[1], point[2], point[3]); + } + + private static MathTransform CreateMercatorInverseTransform() + { + var projectionParameters = new List + { + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + }; + + IProjection projection = CoordinateSystemFactory.CreateProjection("Mercator", "mercator", projectionParameters); + GeographicCoordinateSystem geographic = GeographicCoordinateSystem.WGS84; + ProjectedCoordinateSystem projected = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "Mercator", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + return CoordinateTransformationFactory.CreateFromCoordinateSystems(projected, geographic).MathTransform; + } + + private static void AssertCoordinateClose(double[] actual, double[] expected, double tolerance) + { + Assert.NotNull(actual); + Assert.NotNull(expected); + Assert.Equal(expected.Length, actual.Length); + for (int i = 0; i < expected.Length; i++) + { + double delta = Math.Abs(actual[i] - expected[i]); + Assert.InRange( + delta, + 0d, + tolerance); + } + } +} diff --git a/test/ProjNet.Tests/Data/DeformationRuntimeTests.cs b/test/ProjNet.Tests/Data/DeformationRuntimeTests.cs new file mode 100644 index 00000000..5a9f1d08 --- /dev/null +++ b/test/ProjNet.Tests/Data/DeformationRuntimeTests.cs @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for deformation. +/// +public class DeformationRuntimeTests +{ + /// + /// Gets invalid creation scenarios. + /// + /// Invalid case dataset. + public static IEnumerable> InvalidCreationCases + { + get + { + yield return new TheoryDataRow("+proj=deformation +dt=1 +ellps=GRS80", "xy_grids"); + yield return new TheoryDataRow("+proj=deformation +xy_grids=alaska +dt=1 +ellps=GRS80", "z_grids"); + yield return new TheoryDataRow("+proj=deformation +z_grids=egm96_15.gtx +dt=1 +ellps=GRS80", "xy_grids"); + yield return new TheoryDataRow("+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80", "+dt or +t_epoch"); + yield return new TheoryDataRow("+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +dt=1 +t_epoch=2016 +ellps=GRS80", "mutually exclusive"); + yield return new TheoryDataRow("+proj=deformation +xy_grids=nonexisting +z_grids=egm96_15.gtx +dt=1 +ellps=GRS80", "Required grid"); + yield return new TheoryDataRow("+proj=deformation +xy_grids=alaska +z_grids=nonexisting +dt=1 +ellps=GRS80", "Required grid"); + } + } + + /// + /// Gets forward vector scenarios from deformation.gie. + /// + /// Forward case dataset. + public static IEnumerable> ForwardCases + { + get + { + string legacyOperation = "+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80 +dt=16.0"; + yield return Case( + legacyOperation, + CreateCartesianPoint(-3004295.5882503074d, -1093474.1690603832d, 5500477.1338251457d), + CreateCartesianPoint(-3004295.7000d, -1093474.2097d, 5500477.3397d), + 1e-4d); + + string geotiffOperation = "+proj=deformation +grids=nkgrf03vel_realigned_extract.tif +ellps=GRS80 +dt=1"; + yield return Case( + geotiffOperation, + GeographicToCartesian(21.5d, 63d, 0d), + GeographicToCartesian(21.5000000049d, 62.9999999937d, 0.0083d), + 2e-4d); + } + } + + /// + /// Gets inverse 4D scenarios that rely on +t_epoch. + /// + /// Inverse case dataset. + public static IEnumerable> InverseCases + { + get + { + yield return Case( + "+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80 +t_epoch=2016.0 +inv", + CreateCartesianPointWithTime(-3004295.5882503074d, -1093474.1690603832d, 5500477.1338251457d, 2000.0d), + CreateCartesianPointWithTime(-3004295.7000d, -1093474.2097d, 5500477.3397d, 2000.0d), + 1e-4d); + } + } + + /// + /// Verifies argument-validation diagnostics for deformation setup. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [MemberData(nameof(InvalidCreationCases))] + public void DeformationCreationFailsForInvalidParameters(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies missing-time handling for both forward and inverse setup with +t_epoch. + /// + /// Whether inverse setup is used. + [Theory] + [InlineData(false)] + [InlineData(true)] + public void DeformationRequiresObservationEpochForTEpochMode(bool inverse) + { + string operation = "+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80 +t_epoch=2016.0"; + if (inverse) + { + operation += " +inv"; + } + + MathTransform transform = CreateTransform(operation); + ArgumentException exception = Assert.Throws( + () => transform.Transform(CreateCartesianPointWithTime(-3004295.5882503074d, -1093474.1690603832d, 5500477.1338251457d, double.MaxValue))); + Assert.Contains("time", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies the 3D overload is not supported when deformation requires an explicit observation epoch. + /// + [Fact] + public void Deformation3DTransformWithoutObservationEpochIsNotSupported() + { + MathTransform transform = CreateTransform("+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80 +t_epoch=2016.0"); + NotSupportedException exception = Assert.Throws( + () => transform.Transform(CreateCartesianPoint(-3004295.5882503074d, -1093474.1690603832d, 5500477.1338251457d))); + Assert.Contains("time", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies coordinates outside configured grid extents are rejected. + /// + [Fact] + public void DeformationOutsideGridFails() + { + MathTransform transform = CreateTransform("+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80 +dt=16"); + double[] input = GeographicToCartesian(-120d, 40d, 0d); + InvalidOperationException exception = Assert.Throws(() => transform.Transform(input)); + Assert.Contains("outside", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies legacy ctable2+gtx and GeoTIFF vectors from deformation.gie. + /// + /// Operation text. + /// Input coordinate. + /// Expected coordinate. + /// Maximum per-axis absolute tolerance. + [Theory] + [MemberData(nameof(ForwardCases))] + [MemberData(nameof(InverseCases))] + public void DeformationVectorsMatchGie(string operation, double[] input, double[] expected, double tolerance) + { + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(input); + AssertCoordinateClose(output, expected, tolerance); + } + + /// + /// Verifies direct/iterative pairings produce reversible roundtrips. + /// + /// Operation text. + [Theory] + [InlineData("+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80 +dt=16")] + [InlineData("+proj=deformation +grids=nkgrf03vel_realigned_extract.tif +ellps=GRS80 +dt=1")] + public void DeformationRoundtripRecoversInput(string operation) + { + ArgumentNullException.ThrowIfNull(operation); + double[] input = operation.Contains("nkgrf", StringComparison.OrdinalIgnoreCase) + ? GeographicToCartesian(21.5d, 63d, 0d) + : CreateCartesianPoint(-3004295.5882503074d, -1093474.1690603832d, 5500477.1338251457d); + + MathTransform forward = CreateTransform(operation); + MathTransform inverse = forward.Inverse(); + double[] projected = forward.Transform(input); + double[] recovered = inverse.Transform(projected); + AssertCoordinateClose(recovered, input, 2e-4d); + } + + /// + /// Verifies iterative deformation inverse remains stable for larger fixed delta-time values. + /// + [Fact] + public void DeformationInverseRoundtripWithLargeDeltaTimeRecoversInput() + { + MathTransform forward = CreateTransform("+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +ellps=GRS80 +dt=4096"); + MathTransform inverse = forward.Inverse(); + double[] input = CreateCartesianPoint(-3004295.5882503074d, -1093474.1690603832d, 5500477.1338251457d); + + double[] projected = forward.Transform(input); + double[] recovered = inverse.Transform(projected); + + AssertCoordinateClose(recovered, input, 1e-3d); + } + + private static TheoryDataRow Case(string operation, double[] input, double[] expected, double tolerance) + { + return new TheoryDataRow(operation, input, expected, tolerance); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreateCartesianPoint(double x, double y, double z) + { + return [x, y, z]; + } + + private static double[] CreateCartesianPointWithTime(double x, double y, double z, double t) + { + return [x, y, z, t]; + } + + private static double[] GeographicToCartesian(double longitudeDegrees, double latitudeDegrees, double ellipsoidalHeight) + { + var parameters = new List + { + new("semi_major", Ellipsoid.GRS80.SemiMajorAxis), + new("semi_minor", Ellipsoid.GRS80.SemiMinorAxis), + }; + var transform = new GeocentricTransform(parameters, false); + double x = longitudeDegrees; + double y = latitudeDegrees; + double z = ellipsoidalHeight; + transform.Transform(ref x, ref y, ref z); + return [x, y, z]; + } + + private static void AssertCoordinateClose(double[] actual, double[] expected, double tolerance) + { + Assert.NotNull(actual); + Assert.NotNull(expected); + Assert.Equal(expected.Length, actual.Length); + int dimensionsToCompare = Math.Min(actual.Length, 3); + for (int i = 0; i < dimensionsToCompare; i++) + { + double delta = Math.Abs(actual[i] - expected[i]); + Assert.InRange(delta, 0d, tolerance); + } + + if (actual.Length > 3) + { + Assert.Equal(expected[3], actual[3]); + } + } +} diff --git a/test/ProjNet.Tests/Data/GeoTiffGridRuntimeTests.cs b/test/ProjNet.Tests/Data/GeoTiffGridRuntimeTests.cs new file mode 100644 index 00000000..48e46903 --- /dev/null +++ b/test/ProjNet.Tests/Data/GeoTiffGridRuntimeTests.cs @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for GeoTIFF-based horizontal and vertical grid shift operations at runtime. +/// +public class GeoTiffGridRuntimeTests +{ + private static readonly double[] GeoTiffGridInput = [4.5d, 52.5d, 0d]; + private static readonly double[] GeoTiffNodataInput = [4.05d, 52.1d, 0d]; + private static readonly double[] ProjectedGeoTiffGridInput = [-598000d, -1160019.9999d, 0d]; + + /// + /// Verifies that a hgridshift operation backed by a GeoTIFF horizontal grid file applies the expected coordinate shift. + /// + /// GeoTIFF horizontal grid fixture file name. + [Theory] + [InlineData("test_hgrid.tif")] + [InlineData("test_hgrid_positive_west.tif")] + public void HgridshiftWithGeoTiffGridAppliesExpectedShift(string gridFileName) + { + string gridPath = FindGridPath(gridFileName); + string operation = $"+proj=hgridshift +grids={gridPath}"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + + double[] output = Assert.IsType(transform, exactMatch: false).Transform(GeoTiffGridInput); + Assert.Equal(5.875d, output[0], 9); + Assert.Equal(55.375d, output[1], 9); + } + + /// + /// Verifies that the direct path applies the expected forward shift for the 2-D horizontal fixtures. + /// + /// GeoTIFF horizontal grid fixture file name. + [Theory] + [InlineData("test_hgrid.tif")] + [InlineData("test_hgrid_positive_west.tif")] + public void GeoTiffHGridShiftMathTransformAppliesExpectedForwardShift(string gridFileName) + { + string gridPath = FindGridPath(gridFileName); + var transform = new GeoTiffHGridShiftMathTransform([gridPath]); + + double[] output = transform.Transform(GeoTiffGridInput); + + Assert.Equal(5.875d, output[0], 9); + Assert.Equal(55.375d, output[1], 9); + Assert.Equal(0d, output[2], 12); + } + + /// + /// Verifies that the inverse signals an outside-grid failure for the synthetic 2-D fixtures once the reverse iteration leaves the valid extent. + /// + /// GeoTIFF horizontal grid fixture file name. + [Theory] + [InlineData("test_hgrid.tif")] + [InlineData("test_hgrid_positive_west.tif")] + public void GeoTiffHGridShiftMathTransformInverseSignalsOutsideGridForSyntheticFixture(string gridFileName) + { + string gridPath = FindGridPath(gridFileName); + MathTransform inverse = new GeoTiffHGridShiftMathTransform([gridPath]).Inverse(); + + InvalidOperationException exception = Assert.Throws( + () => inverse.Transform([5d, 53d, 0d])); + + Assert.Contains("outside the horizontal GeoTIFF grid extent", exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies that a projected GeoTIFF gridshift grid applies both the raster delta and the metadata-defined constant offsets in projected coordinates. + /// + [Fact] + public void GridshiftWithProjectedGeoTiffGridAppliesProjectedShift() + { + string gridPath = FindGridPath("test_gridshift_projected.tif"); + string operation = $"+proj=gridshift +grids={gridPath}"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + + double[] output = Assert.IsType(transform, exactMatch: false).Transform(ProjectedGeoTiffGridInput); + Assert.InRange(output[0], -5597999.884994847700000d - 1e-9d, -5597999.884994847700000d + 1e-9d); + Assert.InRange(output[1], -6160019.977750200778246d - 1e-9d, -6160019.977750200778246d + 1e-9d); + Assert.Equal(0d, output[2], 12); + } + + /// + /// Verifies that a projected GeoTIFF gridshift grid can force PROJ-style bilinear interpolation explicitly. + /// + [Fact] + public void GridshiftWithProjectedGeoTiffGridCanForceBilinearInterpolation() + { + string gridPath = FindGridPath("test_gridshift_projected.tif"); + string operation = $"+proj=gridshift +grids={gridPath} +interpolation=bilinear"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + + double[] output = Assert.IsType(transform, exactMatch: false).Transform(ProjectedGeoTiffGridInput); + Assert.InRange(output[0], -5597999.884979997761548d - 1e-9d, -5597999.884979997761548d + 1e-9d); + Assert.InRange(output[1], -6160019.977770000696182d - 1e-9d, -6160019.977770000696182d + 1e-9d); + Assert.Equal(0d, output[2], 12); + } + + /// + /// Verifies that a vgridshift operation backed by a GeoTIFF vertical grid file applies the expected vertical shift and leaves horizontal coordinates unchanged. + /// + /// GeoTIFF vertical grid fixture file name. + [Theory] + [InlineData("test_vgrid_pixelispoint.tif")] + [InlineData("test_vgrid_uint16_with_scale_offset.tif")] + public void VgridshiftWithGeoTiffGridAppliesExpectedDefaultShift(string gridFileName) + { + string gridPath = FindGridPath(gridFileName); + string operation = $"+proj=vgridshift +grids={gridPath} +multiplier=1"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + + double[] output = Assert.IsType(transform, exactMatch: false).Transform(GeoTiffGridInput); + Assert.Equal(4.5d, output[0], 9); + Assert.Equal(52.5d, output[1], 9); + Assert.Equal(11.5d, output[2], 9); + } + + /// + /// Verifies that a vgridshift operation backed by a GeoTIFF grid with nodata cells performs weighted interpolation to produce the expected Z value. + /// + [Fact] + public void VgridshiftWithGeoTiffNodataPerformsWeightedInterpolation() + { + string gridPath = FindGridPath("test_vgrid_nodata.tif"); + string operation = $"+proj=vgridshift +grids={gridPath} +multiplier=1"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + + double[] output = Assert.IsType(transform, exactMatch: false).Transform(GeoTiffNodataInput); + Assert.Equal(10d, output[2], 7); + } + + /// + /// Verifies temporary GeoTIFF sample buffers are returned to the configured array pool on successful load. + /// + [Fact] + public void LoadHorizontalReturnsRentedSampleBuffersToArrayPoolOnSuccess() + { + string gridPath = FindGridPath("test_hgrid.tif"); + var pool = new TrackingDoubleArrayPool(); + + IReadOnlyList grids = GeoTiffGridLoader.LoadHorizontal(gridPath, pool); + + Assert.NotEmpty(grids); + Assert.Equal(pool.RentedArrays.Count, pool.ReturnedArrays.Count); + foreach (double[] rented in pool.RentedArrays) + { + int returnCount = 0; + foreach (double[] returned in pool.ReturnedArrays) + { + if (ReferenceEquals(rented, returned)) + { + returnCount++; + } + } + + Assert.Equal(1, returnCount); + } + } + + /// + /// Verifies partially-rented temporary GeoTIFF sample buffers are returned when loading fails. + /// + [Fact] + public void LoadHorizontalReturnsAlreadyRentedSampleBuffersWhenRentThrows() + { + string gridPath = FindGridPath("test_hgrid.tif"); + var pool = new ThrowingAfterFirstRentArrayPool(); + + InvalidOperationException exception = Assert.Throws( + () => GeoTiffGridLoader.LoadHorizontal(gridPath, pool)); + + Assert.Contains("rent", exception.Message, StringComparison.OrdinalIgnoreCase); + Assert.Single(pool.RentedArrays); + Assert.Single(pool.ReturnedArrays); + Assert.Same(pool.RentedArrays[0], pool.ReturnedArrays[0]); + } + + /// + /// Verifies raw grid coordinates beyond the last interpolable cell are rejected before interpolation can address cells outside the raster. + /// + [Fact] + public void TryMapToGridCoordinatesRejectsUpperBoundBeyondLastInterpolableCell() + { + var sampleData = new SampleData([[0d, 0d, 0d, 0d]], width: 2); + var grid = new TestGeoGrid( + width: 2, + height: 2, + west: 0d, + east: 2d, + south: 0d, + north: 1d, + a: 1d, + b: 0d, + c: 0d, + d: 0d, + e: 1d, + f: 0d, + sampleData: sampleData); + + bool inside = grid.TryMapToGridCoordinates(1.5d, 0.5d, out double gridX, out double gridY); + + Assert.True(grid.Contains(1.5d, 0.5d)); + Assert.False(inside); + Assert.True(double.IsFinite(gridX)); + Assert.True(double.IsFinite(gridY)); + } + + private static string FindGridPath(string fileName) + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "grids", fileName); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "grids", fileName); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate a GeoTIFF grid fixture under test\\ProjNet.Tests\\Fixtures\\grids.", fileName); + } + + private class TrackingDoubleArrayPool : ArrayPool + { + private readonly ArrayPool inner = ArrayPool.Shared; + + public List RentedArrays { get; } = []; + + public List ReturnedArrays { get; } = []; + + public override double[] Rent(int minimumLength) + { + double[] buffer = this.inner.Rent(minimumLength); + this.RentedArrays.Add(buffer); + return buffer; + } + + public override void Return(double[] array, bool clearArray = false) + { + this.ReturnedArrays.Add(array); + this.inner.Return(array, clearArray); + } + } + + private sealed class ThrowingAfterFirstRentArrayPool : TrackingDoubleArrayPool + { + private int rentCount; + + public override double[] Rent(int minimumLength) + { + this.rentCount++; + return this.rentCount > 1 ? throw new InvalidOperationException("Simulated rent failure") : base.Rent(minimumLength); + } + } + + private sealed class TestGeoGrid : BaseGeoGrid + { + internal TestGeoGrid( + int width, + int height, + double west, + double east, + double south, + double north, + double a, + double b, + double c, + double d, + double e, + double f, + SampleData sampleData) + : base("test", width, height, area: width * height, epsilon: 0d, west, east, south, north, a, b, c, d, e, f, sampleData) + { + } + } +} diff --git a/test/ProjNet.Tests/Data/GridLoaderHelperTests.cs b/test/ProjNet.Tests/Data/GridLoaderHelperTests.cs new file mode 100644 index 00000000..9f47ee8e --- /dev/null +++ b/test/ProjNet.Tests/Data/GridLoaderHelperTests.cs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies the shared grid loader helper used by grid-backed math transforms. +/// +public class GridLoaderHelperTests +{ + /// + /// Verifies the helper skips blank path entries and flattens the loaded items in input order. + /// + [Fact] + public void LoadMulti_SkipsBlankPaths_AndFlattensLoadedItems() + { + IReadOnlyList paths = ["first", string.Empty, " ", "second"]; + + System.Collections.ObjectModel.ReadOnlyCollection loaded = GridLoaderHelper.LoadMulti( + paths, + "gridPaths", + "No grids loaded.", + static path => path switch + { + "first" => [2, 1], + "second" => [3], + _ => Array.Empty(), + }); + + Assert.Equal([2, 1, 3], loaded); + } + + /// + /// Verifies the helper sorts the loaded items when a comparison is supplied. + /// + [Fact] + public void LoadMulti_SortsLoadedItems_WhenComparisonProvided() + { + IReadOnlyList paths = ["first", "second"]; + + System.Collections.ObjectModel.ReadOnlyCollection loaded = GridLoaderHelper.LoadMulti( + paths, + "gridPaths", + "No grids loaded.", + static path => path switch + { + "first" => [5, 1], + "second" => [4, 2, 3], + _ => Array.Empty(), + }, + static (left, right) => left.CompareTo(right)); + + Assert.Equal([1, 2, 3, 4, 5], loaded); + } + + /// + /// Verifies the helper reports an argument error when no grid items could be loaded. + /// + [Fact] + public void LoadMulti_NoLoadedItems_ThrowsArgumentException() + { + ArgumentException exception = Assert.Throws( + () => GridLoaderHelper.LoadMulti( + [string.Empty, " "], + "gridPaths", + "No grids loaded.", + static _ => Array.Empty())); + + Assert.Equal("gridPaths", exception.ParamName); + Assert.Contains("No grids loaded.", exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies the helper rejects a path collection. + /// + [Fact] + public void LoadMulti_NullPaths_ThrowsArgumentNullException() + { + Assert.Throws( + () => GridLoaderHelper.LoadMulti( + null!, + "gridPaths", + "No grids loaded.", + static _ => Array.Empty())); + } +} diff --git a/test/ProjNet.Tests/Data/GridResourceResolverTests.cs b/test/ProjNet.Tests/Data/GridResourceResolverTests.cs new file mode 100644 index 00000000..9c732a3f --- /dev/null +++ b/test/ProjNet.Tests/Data/GridResourceResolverTests.cs @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ProjNet.Resources; +using Xunit; + +/// +/// Tests for the resolution and caching behavior. +/// +public class GridResourceResolverTests +{ + /// + /// Verifies that TryResolve returns the local file path and makes no network calls when the requested grid file exists in a local search directory. + /// + [Fact] + public void TryResolveWithLocalGridFileResolvesWithoutNetwork() + { + string localDirectory = CreateTemporaryDirectory(); + try + { + string localGridPath = Path.Combine(localDirectory, "sample.gsb"); + File.WriteAllText(localGridPath, "local-grid"); + + var fetchClient = new RecordingFetchClient(); + var options = new GridResourceResolverOptions(new[] { localDirectory }, null, GridResourceResolutionMode.LocalOnly); + var resolver = new GridResourceResolver(options, fetchClient); + + bool resolved = resolver.TryResolve("sample.gsb", out string? resolvedPath); + + Assert.True(resolved); + Assert.NotNull(resolvedPath); + Assert.Equal(localGridPath, resolvedPath); + Assert.Equal(0, fetchClient.Calls); + } + finally + { + Directory.Delete(localDirectory, true); + } + } + + /// + /// Verifies that nested Windows-style grid names still resolve to their leaf file name on non-Windows runners. + /// + [Fact] + public void TryResolveWithWindowsStyleNestedGridNameResolvesLocalLeafFile() + { + string localDirectory = CreateTemporaryDirectory(); + try + { + string localGridPath = Path.Combine(localDirectory, "sample.gsb"); + File.WriteAllText(localGridPath, "local-grid"); + + var fetchClient = new RecordingFetchClient(); + var options = new GridResourceResolverOptions(new[] { localDirectory }, null, GridResourceResolutionMode.LocalOnly); + var resolver = new GridResourceResolver(options, fetchClient); + + bool resolved = resolver.TryResolve(@"nested\sample.gsb", out string? resolvedPath); + + Assert.True(resolved); + Assert.NotNull(resolvedPath); + Assert.Equal(localGridPath, resolvedPath); + Assert.Equal(0, fetchClient.Calls); + } + finally + { + Directory.Delete(localDirectory, true); + } + } + + /// + /// Verifies that TryResolve returns and does not invoke the network fetcher when configured with LocalOnly mode and the grid file is absent locally. + /// + [Fact] + public void TryResolveWithLocalOnlyModeDoesNotCallNetworkFetcher() + { + string localDirectory = CreateTemporaryDirectory(); + try + { + var fetchClient = new RecordingFetchClient(); + var options = new GridResourceResolverOptions(new[] { localDirectory }, null, GridResourceResolutionMode.LocalOnly); + var resolver = new GridResourceResolver(options, fetchClient); + + bool resolved = resolver.TryResolve("missing.gsb", out string? _); + + Assert.False(resolved); + Assert.Equal(0, fetchClient.Calls); + } + finally + { + Directory.Delete(localDirectory, true); + } + } + + /// + /// Verifies that TryResolve downloads the grid to the cache directory on the first call and reuses the cached file on subsequent calls without fetching again. + /// + [Fact] + public void TryResolveWithNetworkModeDownloadsToCacheAndReusesCachedFile() + { + string localDirectory = CreateTemporaryDirectory(); + string cacheDirectory = CreateTemporaryDirectory(); + try + { + var fetchClient = new RecordingFetchClient + { + OnFetch = path => File.WriteAllText(path, "downloaded-grid"), + }; + var options = new GridResourceResolverOptions(new[] { localDirectory }, cacheDirectory, GridResourceResolutionMode.LocalThenNetwork); + var resolver = new GridResourceResolver(options, fetchClient); + + bool firstResolved = resolver.TryResolve("network-grid.gsb", out string? firstPath); + bool secondResolved = resolver.TryResolve("network-grid.gsb", out string? secondPath); + + Assert.True(firstResolved); + Assert.True(secondResolved); + Assert.NotNull(firstPath); + Assert.NotNull(secondPath); + Assert.Equal(firstPath, secondPath); + Assert.True(File.Exists(firstPath)); + Assert.Equal(1, fetchClient.Calls); + } + finally + { + Directory.Delete(localDirectory, true); + Directory.Delete(cacheDirectory, true); + } + } + + /// + /// Verifies that an invalid cached manifest forces the resolver to discard the stale file and fetch a fresh copy. + /// + [Fact] + public void TryResolveWithInvalidCachedManifestRefetchesNetworkArtifact() + { + string localDirectory = CreateTemporaryDirectory(); + string cacheDirectory = CreateTemporaryDirectory(); + try + { + string cachedGridPath = Path.Combine(cacheDirectory, "network-grid.gsb"); + File.WriteAllText(cachedGridPath, "stale-grid"); + GridResourceCacheManifest.Write(cachedGridPath, "https://example.test/grids/network-grid.gsb"); + File.WriteAllText(cachedGridPath, "tampered-grid"); + + var fetchClient = new RecordingFetchClient + { + OnFetch = path => File.WriteAllText(path, "fresh-grid"), + }; + var options = new GridResourceResolverOptions(new[] { localDirectory }, cacheDirectory, GridResourceResolutionMode.LocalThenNetwork); + var resolver = new GridResourceResolver(options, fetchClient); + + bool resolved = resolver.TryResolve("network-grid.gsb", out string? resolvedPath); + + Assert.True(resolved); + Assert.NotNull(resolvedPath); + Assert.Equal("fresh-grid", File.ReadAllText(resolvedPath)); + Assert.Equal(1, fetchClient.Calls); + } + finally + { + Directory.Delete(localDirectory, true); + Directory.Delete(cacheDirectory, true); + } + } + + private static string CreateTemporaryDirectory() + { + string path = Path.Combine(Path.GetTempPath(), "projnet-grid-tests", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(path); + return path; + } + + private sealed class RecordingFetchClient : IGridResourceFetchClient + { + internal int Calls { get; private set; } + + internal Action? OnFetch { get; set; } + + public bool TryFetch(string gridName, string targetFilePath) + { + this.Calls++; + this.OnFetch?.Invoke(targetFilePath); + return this.OnFetch is not null; + } + + public Task TryFetchAsync(string gridName, string targetFilePath, CancellationToken cancellationToken = default) + { + return Task.FromResult(this.TryFetch(gridName, targetFilePath)); + } + } +} diff --git a/test/ProjNet.Tests/Data/HorizontalGridShiftRuntimeTests.cs b/test/ProjNet.Tests/Data/HorizontalGridShiftRuntimeTests.cs new file mode 100644 index 00000000..a3f1741a --- /dev/null +++ b/test/ProjNet.Tests/Data/HorizontalGridShiftRuntimeTests.cs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.IO; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for horizontal grid shift operations at runtime using NTv2 grid files. +/// +public class HorizontalGridShiftRuntimeTests +{ + private static readonly double[] HorizontalGridInput = [4.5d, 52.5d, 0d]; + private static readonly double[] HorizontalGridInverseInput = [5.875d, 55.375d, 0d]; + + /// + /// Verifies that a hgridshift operation backed by an NTv2 grid file applies the expected coordinate shift. + /// + /// NTv2 grid fixture file name. + [Theory] + [InlineData("test_hgrid_little_endian.gsb")] + [InlineData("test_hgrid_big_endian.gsb")] + public void HgridshiftWithNtv2GridAppliesExpectedShift(string gridFileName) + { + string gridPath = FindGridPath(gridFileName); + string operation = $"+proj=hgridshift +grids={gridPath}"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + double[] output = Assert.IsType(transform, exactMatch: false).Transform(HorizontalGridInput); + Assert.Equal(5.875d, output[0], 9); + Assert.Equal(55.375d, output[1], 9); + Assert.Equal(0d, output[2], 9); + } + + /// + /// Verifies that the inverse hgridshift operation on the synthetic fixture input signals that the point lies outside the grid. + /// + /// NTv2 grid fixture file name. + [Theory] + [InlineData("test_hgrid_little_endian.gsb")] + [InlineData("test_hgrid_big_endian.gsb")] + public void HgridshiftWithInverseFlagForSyntheticFixtureSignalsOutsideGrid(string gridFileName) + { + string gridPath = FindGridPath(gridFileName); + string operation = $"+inv +proj=hgridshift +grids={gridPath}"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + Assert.Throws(() => Assert.IsType(transform, exactMatch: false).Transform(HorizontalGridInverseInput)); + } + + /// + /// Verifies that a gridshift operation backed by an NTv2 grid file delegates to the horizontal shift implementation and produces the expected output. + /// + /// NTv2 grid fixture file name. + [Theory] + [InlineData("test_hgrid_little_endian.gsb")] + [InlineData("test_hgrid_big_endian.gsb")] + public void GridshiftWithNtv2GridUsesHorizontalShiftImplementation(string gridFileName) + { + string gridPath = FindGridPath(gridFileName); + string operation = $"+proj=gridshift +grids={gridPath}"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + double[] output = Assert.IsType(transform, exactMatch: false).Transform(HorizontalGridInput); + Assert.Equal(5.875d, output[0], 9); + Assert.Equal(55.375d, output[1], 9); + } + + private static string FindGridPath(string fileName) + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "grids", fileName); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "grids", fileName); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate local test grid fixture under test\\ProjNet.Tests\\Fixtures\\grids.", fileName); + } +} diff --git a/test/ProjNet.Tests/Data/MolodenskyRuntimeTests.cs b/test/ProjNet.Tests/Data/MolodenskyRuntimeTests.cs new file mode 100644 index 00000000..4e9af9c9 --- /dev/null +++ b/test/ProjNet.Tests/Data/MolodenskyRuntimeTests.cs @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for molodensky. +/// +public class MolodenskyRuntimeTests +{ + /// + /// Verifies abridged Molodensky vector from PROJ more_builtins.gie. + /// + [Fact] + public void MolodenskyAbridgedMatchesMoreBuiltinsVector() + { + const string operation = "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149 +abridged"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(144.9667d, -37.8d, 50d)); + + Assert.InRange(Math.Abs(output[0] - 144.968d), 0d, 2.5e-5); + Assert.InRange(Math.Abs(output[1] - (-37.79848d)), 0d, 2.5e-5); + Assert.InRange(Math.Abs(output[2] - 46.378d), 0d, 5e-3); + } + + /// + /// Verifies standard Molodensky vector from PROJ more_builtins.gie. + /// + [Fact] + public void MolodenskyStandardMatchesMoreBuiltinsVector() + { + const string operation = "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(144.9667d, -37.8d, 50d)); + + Assert.InRange(Math.Abs(output[0] - 144.968d), 0d, 2.5e-5); + Assert.InRange(Math.Abs(output[1] - (-37.79848d)), 0d, 2.5e-5); + Assert.InRange(Math.Abs(output[2] - 46.378d), 0d, 5e-3); + } + + /// + /// Verifies identity behavior for all-zero Molodensky parameter set. + /// + [Fact] + public void MolodenskyZeroParametersBehavesAsIdentity() + { + const string operation = "+proj=molodensky +a=6378160 +rf=298.25 +da=0 +df=0 +dx=0 +dy=0 +dz=0"; + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(CreatePoint(144.9667d, -37.8d, 50d)); + + Assert.InRange(Math.Abs(output[0] - 144.9667d), 0d, 1e-10); + Assert.InRange(Math.Abs(output[1] - (-37.8d)), 0d, 1e-10); + Assert.InRange(Math.Abs(output[2] - 50d), 0d, 1e-10); + } + + /// + /// Verifies inverse behavior for static Molodensky operation. + /// + [Fact] + public void MolodenskyInverseRecoversInputApproximately() + { + const string forwardOperation = "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149"; + const string inverseOperation = "+proj=molodensky +a=6378160 +rf=298.25 +da=-23 +df=-8.120449e-8 +dx=-134 +dy=-48 +dz=149 +inv"; + MathTransform forward = CreateTransform(forwardOperation); + MathTransform inverse = CreateTransform(inverseOperation); + + double[] source = CreatePoint(144.9667d, -37.8d, 50d); + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.InRange(Math.Abs(recovered[0] - source[0]), 0d, 2e-5); + Assert.InRange(Math.Abs(recovered[1] - source[1]), 0d, 2e-5); + Assert.InRange(Math.Abs(recovered[2] - source[2]), 0d, 2e-2); + } + + /// + /// Verifies abridged Molodensky rejects degenerate ellipsoid inputs that collapse Rm to zero. + /// + [Fact] + public void MolodenskyAbridgedThrowsWhenMeridionalRadiusIsZero() + { + const string operation = "+proj=molodensky +a=6378137 +b=1e-200 +da=0 +df=0 +dx=0 +dy=0 +dz=1 +abridged"; + MathTransform transform = CreateTransform(operation); + + InvalidOperationException exception = Assert.Throws(() => transform.Transform(CreatePoint(0d, 0d, 0d))); + Assert.Contains("dphi", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies required-parameter validation paths for Molodensky. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [InlineData("+proj=molodensky +a=6378160 +rf=298.25", "missing dx")] + [InlineData("+proj=molodensky +a=6378160 +rf=298.25 +dx=0", "missing dy")] + [InlineData("+proj=molodensky +a=6378160 +rf=298.25 +dx=0 +dy=0", "missing dz")] + [InlineData("+proj=molodensky +a=6378160 +rf=298.25 +dx=0 +dy=0 +dz=0", "missing da")] + [InlineData("+proj=molodensky +a=6378160 +rf=298.25 +dx=0 +dy=0 +dz=0 +da=0", "missing df")] + public void MolodenskyCreationFailsWhenMandatoryParametersAreMissing(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.Ordinal); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; +} diff --git a/test/ProjNet.Tests/Data/ProjEllipsoidResolverTests.cs b/test/ProjNet.Tests/Data/ProjEllipsoidResolverTests.cs new file mode 100644 index 00000000..dc02d8ab --- /dev/null +++ b/test/ProjNet.Tests/Data/ProjEllipsoidResolverTests.cs @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies the shared PROJ ellipsoid-resolution helpers used by runtime transformation argument parsing. +/// +public class ProjEllipsoidResolverTests +{ + /// + /// Verifies optional ellipsoid resolution falls back to WGS84 when no ellipsoid tokens are supplied. + /// + [Fact] + public void TryResolveEllipsoidOrDefault_NoDefinition_UsesWgs84() + { + bool resolved = ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + new Dictionary(), + includeDatumToken: true, + allowClarke1880Ign: false, + allowBessel: false, + out double semiMajor, + out double semiMinor); + + Assert.True(resolved); + Assert.Equal(Ellipsoid.WGS84.SemiMajorAxis, semiMajor, 9); + Assert.Equal(Ellipsoid.WGS84.SemiMinorAxis, semiMinor, 9); + } + + /// + /// Verifies invalid explicit-axis tokens are ignored by the lenient optional resolver and therefore fall back to WGS84. + /// + [Fact] + public void TryResolveEllipsoidOrDefault_InvalidExplicitAxis_DefaultsToWgs84() + { + var args = new Dictionary + { + ["a"] = "not-a-number", + }; + + bool resolved = ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + includeDatumToken: true, + allowClarke1880Ign: false, + allowBessel: false, + out double semiMajor, + out double semiMinor); + + Assert.True(resolved); + Assert.Equal(Ellipsoid.WGS84.SemiMajorAxis, semiMajor, 9); + Assert.Equal(Ellipsoid.WGS84.SemiMinorAxis, semiMinor, 9); + } + + /// + /// Verifies unsupported named ellipsoids still fail the lenient optional resolver. + /// + [Fact] + public void TryResolveEllipsoidOrDefault_UnsupportedNamedEllipsoid_ReturnsFalse() + { + var args = new Dictionary + { + ["ellps"] = "unknown", + }; + + bool resolved = ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + includeDatumToken: true, + allowClarke1880Ign: false, + allowBessel: false, + out _, + out _); + + Assert.False(resolved); + } + + /// + /// Verifies known Airy and Bessel tokens resolve through the shared ellipsoid accessors. + /// + /// The PROJ ellipsoid token. + /// when the resolver should accept Bessel tokens. + /// The expected accessor name. + [Theory] + [InlineData("airy", false, nameof(Ellipsoid.Airy1830))] + [InlineData("osgb36", false, nameof(Ellipsoid.Airy1830))] + [InlineData("bessel", true, nameof(Ellipsoid.Bessel1841))] + [InlineData("potsdam", true, nameof(Ellipsoid.Bessel1841))] + public void TryResolveKnownEllipsoid_KnownAccessorBackedTokens_UseEllipsoidStatics( + string token, + bool allowBessel, + string expectedAccessorName) + { + bool resolved = ProjEllipsoidResolver.TryResolveKnownEllipsoid( + token, + allowClarke1880Ign: false, + allowBessel, + out double semiMajor, + out double semiMinor); + + Ellipsoid expectedEllipsoid = expectedAccessorName switch + { + nameof(Ellipsoid.Airy1830) => Ellipsoid.Airy1830, + nameof(Ellipsoid.Bessel1841) => Ellipsoid.Bessel1841, + _ => throw new InvalidOperationException($"Unexpected ellipsoid accessor '{expectedAccessorName}'."), + }; + + Assert.True(resolved); + Assert.Equal(expectedEllipsoid.SemiMajorAxis, semiMajor, 12); + Assert.Equal(expectedEllipsoid.SemiMinorAxis, semiMinor, 12); + } + + /// + /// Verifies the diagnostic overload reports operation-specific messages for non-positive explicit axes. + /// + [Fact] + public void TryResolveEllipsoidOrDefault_DiagnosticNonPositiveAxis_ReturnsOperationSpecificReason() + { + var args = new Dictionary + { + ["a"] = "0", + }; + + bool resolved = ProjEllipsoidResolver.TryResolveEllipsoidOrDefault( + args, + operationName: "defmodel", + allowClarke1880Ign: true, + allowBessel: false, + out _, + out _, + out string? skipReason); + + Assert.False(resolved); + Assert.Equal("defmodel +a must be positive.", skipReason); + } + + /// + /// Verifies the required resolver honors named-ellipsoid lookup, semi-major overrides, and explicit shape overrides in PROJ order. + /// + [Fact] + public void TryResolveRequiredEllipsoidWithOverrides_NamedEllipsoidAndOverrides_AppliesProjOrder() + { + var args = new Dictionary + { + ["ellps"] = "grs80", + ["a"] = "7000000", + ["rf"] = "2", + }; + + bool resolved = ProjEllipsoidResolver.TryResolveRequiredEllipsoidWithOverrides( + args, + operationName: "xyzgridshift", + allowClarke1880Ign: true, + allowBessel: true, + out double semiMajor, + out double semiMinor, + out string? skipReason); + + Assert.True(resolved); + Assert.Null(skipReason); + Assert.Equal(7000000d, semiMajor, 9); + Assert.Equal(3500000d, semiMinor, 9); + } + + /// + /// Verifies the required resolver reports the operation-specific missing-definition message when no ellipsoid definition exists. + /// + [Fact] + public void TryResolveRequiredEllipsoidWithOverrides_MissingDefinition_ReturnsOperationSpecificReason() + { + var args = new Dictionary + { + ["rf"] = "298.257223563", + }; + + bool resolved = ProjEllipsoidResolver.TryResolveRequiredEllipsoidWithOverrides( + args, + operationName: "xyzgridshift", + allowClarke1880Ign: true, + allowBessel: true, + out _, + out _, + out string? skipReason); + + Assert.False(resolved); + Assert.Equal("xyzgridshift requires ellipsoid definition (+ellps, +datum, +r, or +a with optional +b/+rf/+f/+es).", skipReason); + } +} diff --git a/test/ProjNet.Tests/Data/TinShiftRuntimeTests.cs b/test/ProjNet.Tests/Data/TinShiftRuntimeTests.cs new file mode 100644 index 00000000..270c8755 --- /dev/null +++ b/test/ProjNet.Tests/Data/TinShiftRuntimeTests.cs @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.IO; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for tinshift. +/// +public class TinShiftRuntimeTests +{ + /// + /// Gets invalid creation scenarios. + /// + /// Invalid case dataset. + public static IEnumerable> GetInvalidCreationCases() + { + yield return new TheoryDataRow("+proj=tinshift", "+file"); + yield return new TheoryDataRow("+proj=tinshift +file=i_do_not_exist", "Cannot open"); + yield return new TheoryDataRow($"+proj=tinshift +file={FindFixturePath(Path.Combine("Fixtures", "gie", "tinshift.gie"))}", "invalid model"); + } + + /// + /// Gets forward vector scenarios from tinshift.gie. + /// + /// Forward case dataset. + public static IEnumerable> GetForwardCases() + { + yield return Case( + BuildTinShiftOperation("tinshift_crs_implicit.json"), + CreatePoint(2d, 49d, 0d), + CreatePoint(2.1d, 49.1d, 0d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_simplified_kkj_etrs.json"), + CreatePoint(3210000d, 6700000d, 0d), + CreatePoint(209948.3217d, 6697187.0009d, 0d), + 1e-4d); + + yield return Case( + BuildTinShiftOperation("tinshift_simplified_n60_n2000.json"), + CreatePoint(3210000d, 6700000d, 10d), + CreatePoint(3210000d, 6700000d, 10.2886d), + 1e-4d); + + yield return Case( + BuildTinShiftOperation("tinshift_fallback_nearest_side.json"), + CreatePoint(2d, 3d, 0d), + CreatePoint(4d, 6d, 0d), + 1e-9d); + + yield return Case( + BuildTinShiftOperation("tinshift_fallback_nearest_centroid.json"), + CreatePoint(3d, 0d, 0d), + CreatePoint(3d, 0d, 0d), + 1e-9d); + } + + /// + /// Gets representative roundtrip scenarios. + /// + /// Roundtrip case dataset. + public static IEnumerable> GetRoundtripCases() + { + yield return RoundtripCase(BuildTinShiftOperation("tinshift_crs_implicit.json"), CreatePoint(2d, 49d, 0d), 1e-9d); + yield return RoundtripCase(BuildTinShiftOperation("tinshift_simplified_kkj_etrs.json"), CreatePoint(3210000d, 6700000d, 0d), 1e-6d); + yield return RoundtripCase(BuildTinShiftOperation("tinshift_simplified_n60_n2000.json"), CreatePoint(3210000d, 6700000d, 10d), 1e-6d); + yield return RoundtripCase(BuildTinShiftOperation("tinshift_fallback_nearest_side.json"), CreatePoint(2d, 3d, 0d), 1e-9d); + yield return RoundtripCase(BuildTinShiftOperation("tinshift_fallback_nearest_centroid.json"), CreatePoint(3d, 0d, 0d), 1e-9d); + } + + /// + /// Gets portable vectors harvested from PROJ test_tinshift.cpp. + /// + /// Forward case dataset. + public static IEnumerable> GetCppUnitForwardCases() + { + yield return Case( + BuildTinShiftOperation("tinshift_unit_basic_horizontal.json"), + CreatePoint(0d, 0d, 1000d), + CreatePoint(101d, 101d, 1000d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_basic_horizontal.json"), + CreatePoint(0d, 0.5d, 1000d), + CreatePoint(100.5d, 101d, 1000d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_basic_horizontal.json"), + CreatePoint(0.5d, 0.5d, 1000d), + CreatePoint(100.5d, 100.5d, 1000d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_vertical_source_target.json"), + CreatePoint(0d, 0d, 1000d), + CreatePoint(0d, 0d, 1000.1d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_vertical_source_target.json"), + CreatePoint(0.5d, 0.75d, 1000d), + CreatePoint(0.5d, 0.75d, 1000.325d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_vertical_offset.json"), + CreatePoint(0d, 0d, 1000d), + CreatePoint(0d, 0d, 1000.1d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_vertical_offset.json"), + CreatePoint(0.5d, 0.75d, 1000d), + CreatePoint(0.5d, 0.75d, 1000.325d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_horizontal_vertical.json"), + CreatePoint(0d, 0d, 1000d), + CreatePoint(101d, 101d, 1000.1d), + 1e-12d); + + yield return Case( + BuildTinShiftOperation("tinshift_unit_horizontal_vertical.json"), + CreatePoint(0.5d, 0.75d, 1000d), + CreatePoint(100.25d, 100.5d, 1000.325d), + 1e-12d); + } + + /// + /// Gets portable inverse vectors harvested from PROJ test_tinshift.cpp. + /// + /// Inverse case dataset. + public static IEnumerable> GetCppUnitInverseCases() + { + yield return Case( + $"{BuildTinShiftOperation("tinshift_unit_basic_horizontal.json")} +inv", + CreatePoint(100.25d, 100.5d, 1000d), + CreatePoint(0.5d, 0.75d, 1000d), + 1e-12d); + + yield return Case( + $"{BuildTinShiftOperation("tinshift_unit_vertical_source_target.json")} +inv", + CreatePoint(0.5d, 0.75d, 1000.325d), + CreatePoint(0.5d, 0.75d, 1000d), + 1e-12d); + + yield return Case( + $"{BuildTinShiftOperation("tinshift_unit_vertical_offset.json")} +inv", + CreatePoint(0.5d, 0.75d, 1000.325d), + CreatePoint(0.5d, 0.75d, 1000d), + 1e-12d); + + yield return Case( + $"{BuildTinShiftOperation("tinshift_unit_horizontal_vertical.json")} +inv", + CreatePoint(100.25d, 100.5d, 1000.325d), + CreatePoint(0.5d, 0.75d, 1000d), + 1e-12d); + } + + /// + /// Verifies file argument validation paths. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [MemberData(nameof(GetInvalidCreationCases))] + public void TinShiftCreationFailsForInvalidModelConfiguration(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies points outside triangulation fail without fallback strategy. + /// + [Fact] + public void TinShiftOutsideTriangulationWithoutFallbackFails() + { + MathTransform transform = CreateTransform(BuildTinShiftOperation("tinshift_crs_implicit.json")); + InvalidOperationException exception = Assert.Throws(() => transform.Transform(CreatePoint(0d, 0d, 0d))); + Assert.Contains("failed", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies the PROJ unit-case outside-triangle failure vector. + /// + [Fact] + public void TinShiftCppUnitReferenceOutsideTriangleFails() + { + MathTransform transform = CreateTransform(BuildTinShiftOperation("tinshift_unit_basic_horizontal.json")); + Assert.Throws(() => transform.Transform(CreatePoint(-0.1d, 0d, 1000d))); + } + + /// + /// Verifies both forward and inverse fail outside triangulation when fallback is none. + /// + /// Indicates whether inverse direction is tested. + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TinShiftOutsideTriangulationFailsForDirection(bool inverse) + { + string operation = BuildTinShiftOperation("tinshift_crs_implicit.json"); + if (inverse) + { + operation += " +inv"; + } + + MathTransform transform = CreateTransform(operation); + InvalidOperationException exception = Assert.Throws(() => transform.Transform(CreatePoint(0d, 0d, 0d))); + Assert.Contains("failed", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies forward vectors from tinshift.gie. + /// + /// Operation text. + /// Input coordinate. + /// Expected coordinate. + /// Maximum per-axis absolute tolerance. + [Theory] + [MemberData(nameof(GetForwardCases))] + public void TinShiftForwardVectorsMatchGie(string operation, double[] input, double[] expected, double tolerance) + { + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(input); + AssertCoordinateClose(output, expected, tolerance); + } + + /// + /// Verifies roundtrip behavior using transform/inverse transform pairs. + /// + /// Operation text. + /// Input coordinate. + /// Maximum per-axis absolute tolerance. + [Theory] + [MemberData(nameof(GetRoundtripCases))] + public void TinShiftRoundtripRecoversInput(string operation, double[] input, double tolerance) + { + MathTransform forward = CreateTransform(operation); + MathTransform inverse = forward.Inverse(); + + double[] projected = forward.Transform(input); + double[] recovered = inverse.Transform(projected); + AssertCoordinateClose(recovered, input, tolerance); + } + + /// + /// Verifies harvested forward vectors from PROJ unit tests. + /// + /// Operation text. + /// Input coordinate. + /// Expected coordinate. + /// Maximum per-axis absolute tolerance. + [Theory] + [MemberData(nameof(GetCppUnitForwardCases))] + public void TinShiftForwardVectorsMatchCppUnitReference(string operation, double[] input, double[] expected, double tolerance) + { + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(input); + AssertCoordinateClose(output, expected, tolerance); + } + + /// + /// Verifies harvested inverse vectors from PROJ unit tests. + /// + /// Operation text. + /// Input coordinate. + /// Expected coordinate. + /// Maximum per-axis absolute tolerance. + [Theory] + [MemberData(nameof(GetCppUnitInverseCases))] + public void TinShiftInverseVectorsMatchCppUnitReference(string operation, double[] input, double[] expected, double tolerance) + { + MathTransform transform = CreateTransform(operation); + double[] output = transform.Transform(input); + AssertCoordinateClose(output, expected, tolerance); + } + + private static TheoryDataRow Case(string operation, double[] input, double[] expected, double tolerance) + { + return new TheoryDataRow(operation, input, expected, tolerance); + } + + private static TheoryDataRow RoundtripCase(string operation, double[] input, double tolerance) + { + return new TheoryDataRow(operation, input, tolerance); + } + + private static string BuildTinShiftOperation(string fileName) + { + return $"+proj=tinshift +file={FindTinShiftPath(fileName)}"; + } + + private static string FindTinShiftPath(string fileName) + { + return FindFixturePath(Path.Combine("Fixtures", "tinshift", fileName)); + } + + private static string FindFixturePath(string relativePath) + { + string direct = Path.Combine(AppContext.BaseDirectory, relativePath); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", relativePath); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate local test fixture under test\\ProjNet.Tests\\.", relativePath); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) + { + return [x, y, z]; + } + + private static void AssertCoordinateClose(double[] actual, double[] expected, double tolerance) + { + Assert.NotNull(actual); + Assert.NotNull(expected); + Assert.Equal(expected.Length, actual.Length); + for (int i = 0; i < expected.Length; i++) + { + double delta = Math.Abs(actual[i] - expected[i]); + Assert.InRange(delta, 0d, tolerance); + } + } +} diff --git a/test/ProjNet.Tests/Data/TopocentricRuntimeTests.cs b/test/ProjNet.Tests/Data/TopocentricRuntimeTests.cs new file mode 100644 index 00000000..eabc2691 --- /dev/null +++ b/test/ProjNet.Tests/Data/TopocentricRuntimeTests.cs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for topocentric. +/// +public class TopocentricRuntimeTests +{ + private const string TopocentricGeocentricOriginOperation = "+proj=topocentric +ellps=WGS84 +X_0=3652755.3058 +Y_0=319574.6799 +Z_0=5201547.3536"; + private const string TopocentricGeographicOriginOperation = "+proj=topocentric +ellps=WGS84 +lon_0=5 +lat_0=55 +h_0=200"; + private const string TopocentricGeographicOriginInverseOperation = "+proj=topocentric +ellps=WGS84 +lon_0=5 +lat_0=55 +h_0=200 +inv"; + + /// + /// Verifies geocentric input to topocentric output for the builtins vector. + /// + [Fact] + public void TopocentricWithXyzOriginMatchesBuiltinsVector() + { + MathTransform transform = CreateTransform(TopocentricGeocentricOriginOperation); + double[] output = transform.Transform(CreatePoint(3771793.968d, 140253.342d, 5124304.349d)); + + Assert.InRange(Math.Abs(output[0] - (-189013.869d)), 0d, 1e-3); + Assert.InRange(Math.Abs(output[1] - (-128642.040d)), 0d, 1e-3); + Assert.InRange(Math.Abs(output[2] - (-4220.171d)), 0d, 1e-3); + } + + /// + /// Verifies geographic-origin case from builtins pipeline vector. + /// + [Fact] + public void TopocentricWithLonLatOriginMatchesBuiltinsVector() + { + MathTransform transform = CreateTransform(TopocentricGeographicOriginOperation); + double[] output = transform.Transform(CreatePoint(3771793.968d, 140253.342d, 5124304.349d)); + + Assert.InRange(Math.Abs(output[0] - (-189013.869d)), 0d, 1e-3); + Assert.InRange(Math.Abs(output[1] - (-128642.040d)), 0d, 1e-3); + Assert.InRange(Math.Abs(output[2] - (-4220.171d)), 0d, 1e-3); + } + + /// + /// Verifies inverse conversion back to geocentric coordinates. + /// + [Fact] + public void TopocentricInverseRecoversGeocentricCoordinates() + { + MathTransform inverse = CreateTransform(TopocentricGeographicOriginInverseOperation); + double[] geocentric = inverse.Transform(CreatePoint(-189013.869d, -128642.040d, -4220.171d)); + + Assert.InRange(Math.Abs(geocentric[0] - 3771793.968d), 0d, 1e-3); + Assert.InRange(Math.Abs(geocentric[1] - 140253.342d), 0d, 1e-3); + Assert.InRange(Math.Abs(geocentric[2] - 5124304.349d), 0d, 1e-3); + } + + /// + /// Verifies required argument validation parity for topocentric. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [InlineData("+proj=topocentric +ellps=WGS84", "X_0 or lon_0")] + [InlineData("+proj=topocentric +ellps=WGS84 +X_0=0 +Y_0=0", "Y_0 and/or Z_0")] + [InlineData("+proj=topocentric +ellps=WGS84 +lon_0=0", "lat_0")] + [InlineData("+proj=topocentric +ellps=WGS84 +X_0=0 +lon_0=0", "mutually exclusive")] + public void TopocentricCreationFailsForMissingOrExclusiveParameters(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains(expectedToken, skipReason, StringComparison.Ordinal); + } + + /// + /// Verifies topocentric aliases and inversion flag are accepted. + /// + /// Operation text. + [Theory] + [InlineData("+proj=topocentric +ellps=WGS84 +X_0=3652755.3058 +Y_0=319574.6799 +Z_0=5201547.3536")] + [InlineData("+proj=topocentric +ellps=WGS84 +lon_0=5 +lat_0=55 +h_0=200 +inv")] + public void TopocentricOperationCanBeCreated(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + Assert.NotNull(transform); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; +} diff --git a/test/ProjNet.Tests/Data/VertOffsetRuntimeTests.cs b/test/ProjNet.Tests/Data/VertOffsetRuntimeTests.cs new file mode 100644 index 00000000..4ab922ff --- /dev/null +++ b/test/ProjNet.Tests/Data/VertOffsetRuntimeTests.cs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for vertoffset. +/// +public class VertOffsetRuntimeTests +{ + private const string BuiltinsOperation = "+proj=vertoffset +lat_0=46.9166666666666666 +lon_0=8.183333333333334 +dh=-0.245 +slope_lat=-0.210 +slope_lon=-0.032 +ellps=GRS80"; + + /// + /// Verifies the PROJ more_builtins.gie vertical offset and slope vector. + /// + [Fact] + public void VertOffsetMatchesMoreBuiltinsVector() + { + MathTransform transform = CreateTransform(BuiltinsOperation); + double[] output = transform.Transform(CreatePoint(9.666666666666666d, 47.333333333333336d, 473.0d)); + + Assert.InRange(Math.Abs(output[0] - 9.666666666666666d), 0d, 1e-12); + Assert.InRange(Math.Abs(output[1] - 47.333333333333336d), 0d, 1e-12); + Assert.InRange(Math.Abs(output[2] - 472.690d), 0d, 1e-3); + } + + /// + /// Verifies inverse operation recovers the original elevation. + /// + [Fact] + public void VertOffsetInverseRecoversInputHeight() + { + MathTransform forward = CreateTransform(BuiltinsOperation); + MathTransform inverse = CreateTransform($"{BuiltinsOperation} +inv"); + + double[] source = CreatePoint(9.666666666666666d, 47.333333333333336d, 473.0d); + double[] transformed = forward.Transform(source); + double[] recovered = inverse.Transform(transformed); + + Assert.InRange(Math.Abs(recovered[0] - source[0]), 0d, 1e-12); + Assert.InRange(Math.Abs(recovered[1] - source[1]), 0d, 1e-12); + Assert.InRange(Math.Abs(recovered[2] - source[2]), 0d, 1e-3); + } + + /// + /// Verifies optional parameters default to identity-like behavior when omitted. + /// + [Fact] + public void VertOffsetDefaultsToNoOffsetWhenParametersAreMissing() + { + MathTransform transform = CreateTransform("+proj=vertoffset +ellps=GRS80"); + double[] output = transform.Transform(CreatePoint(10d, 47d, 100d)); + + Assert.Equal(10d, output[0], 12); + Assert.Equal(47d, output[1], 12); + Assert.Equal(100d, output[2], 12); + } + + /// + /// Verifies validation failures for malformed numeric arguments. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [InlineData("+proj=vertoffset +lat_0=abc", "lat_0")] + [InlineData("+proj=vertoffset +lon_0=abc", "lon_0")] + [InlineData("+proj=vertoffset +dh=abc", "dh")] + [InlineData("+proj=vertoffset +slope_lat=abc", "slope_lat")] + [InlineData("+proj=vertoffset +slope_lon=abc", "slope_lon")] + public void VertOffsetCreationFailsForInvalidParameters(string operation, string expectedToken) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.Ordinal); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static double[] CreatePoint(double x, double y, double z) => [x, y, z]; +} diff --git a/test/ProjNet.Tests/Data/VerticalBoundCoordinateTransformationTests.cs b/test/ProjNet.Tests/Data/VerticalBoundCoordinateTransformationTests.cs new file mode 100644 index 00000000..d9408dbf --- /dev/null +++ b/test/ProjNet.Tests/Data/VerticalBoundCoordinateTransformationTests.cs @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.IO; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies compound runtime routes built from WKT2 vertical BOUNDCRS metadata with PARAMETERFILE. +/// +public class VerticalBoundCoordinateTransformationTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + /// + /// Verifies that a compound CRS using a parsed vertical BOUNDCRS tail converts gravity-related heights to ellipsoidal heights via the referenced grid. + /// + [Fact] + public void CreateFromCoordinateSystems_WithVerticalBoundCompoundSource_TransformsGravityRelatedHeightToEllipsoidalHeight() + { + string gridPath = FindGridPath("egm96_15.gtx"); + BoundCoordinateSystem boundVertical = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + CreateVerticalBoundWkt(gridPath)); + + CompoundCoordinateSystem source = CoordinateSystemFactory.CreateCompoundCoordinateSystem( + "WGS 84 + EGM96 height", + GeographicCoordinateSystem.WGS84, + boundVertical); + CompoundCoordinateSystem target = CoordinateSystemFactory.CreateCompoundCoordinateSystem( + "WGS 84 + ellipsoidal height", + GeographicCoordinateSystem.WGS84, + CreateEllipsoidalHeightVerticalCoordinateSystem()); + + ICoordinateTransformation transformation = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] output = transformation.MathTransform.Transform([12d, 56d, 0d]); + + Assert.Same(source, transformation.SourceCS); + Assert.Same(target, transformation.TargetCS); + Assert.Equal(12d, output[0], 12); + Assert.Equal(56d, output[1], 12); + Assert.Equal(36.9959410718d, output[2], 9); + } + + /// + /// Verifies that the inverse compound route converts ellipsoidal heights back to the bound gravity-related vertical CRS. + /// + [Fact] + public void CreateFromCoordinateSystems_WithEllipsoidalHeightCompoundSource_TransformsEllipsoidalHeightToBoundVerticalHeight() + { + string gridPath = FindGridPath("egm96_15.gtx"); + BoundCoordinateSystem boundVertical = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + CreateVerticalBoundWkt(gridPath)); + + CompoundCoordinateSystem source = CoordinateSystemFactory.CreateCompoundCoordinateSystem( + "WGS 84 + ellipsoidal height", + GeographicCoordinateSystem.WGS84, + CreateEllipsoidalHeightVerticalCoordinateSystem()); + CompoundCoordinateSystem target = CoordinateSystemFactory.CreateCompoundCoordinateSystem( + "WGS 84 + EGM96 height", + GeographicCoordinateSystem.WGS84, + boundVertical); + + ICoordinateTransformation transformation = CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + double[] output = transformation.MathTransform.Transform([12d, 56d, 0d]); + + Assert.Same(source, transformation.SourceCS); + Assert.Same(target, transformation.TargetCS); + Assert.Equal(12d, output[0], 12); + Assert.Equal(56d, output[1], 12); + Assert.Equal(-36.9959410718d, output[2], 9); + } + + private static VerticalCoordinateSystem CreateEllipsoidalHeightVerticalCoordinateSystem() + { + return CoordinateSystemFactory.CreateVerticalCoordinateSystem( + "Ellipsoidal height", + CoordinateSystemFactory.CreateVerticalDatum("Ellipsoidal height datum", DatumType.VD_Ellipsoidal), + LinearUnit.Metre, + new AxisInfo("Ellipsoidal height", AxisOrientationEnum.Up)); + } + + private static string CreateVerticalBoundWkt(string gridPath) + { + return $$""" + BOUNDCRS[ + SOURCECRS[ + VERTCRS["EGM96 height", + VDATUM["EGM96 geoid"], + CS[vertical,1], + AXIS["gravity-related height (H)",up, + LENGTHUNIT["metre",1]], + ID["EPSG",5773]]], + TARGETCRS[ + GEOGCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]], + CS[ellipsoidal,3], + AXIS["latitude",north, + ORDER[1], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["longitude",east, + ORDER[2], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["ellipsoidal height",up, + ORDER[3], + LENGTHUNIT["metre",1]], + ID["EPSG",4979]]], + ABRIDGEDTRANSFORMATION["WGS 84 to EGM96 height (1)", + METHOD["Geographic3D to GravityRelatedHeight (EGM)", + ID["EPSG",9661]], + PARAMETERFILE["Geoid (height correction) model file","{{gridPath}}"]]] + """; + } + + private static string FindGridPath(string fileName) + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "grids", fileName); + if (File.Exists(direct)) + { + return direct; + } + + DirectoryInfo? current = new(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "grids", fileName); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate local test grid fixture under test\\ProjNet.Tests\\Fixtures\\grids.", fileName); + } +} diff --git a/test/ProjNet.Tests/Data/VerticalGridShiftRuntimeTests.cs b/test/ProjNet.Tests/Data/VerticalGridShiftRuntimeTests.cs new file mode 100644 index 00000000..cbe6e98f --- /dev/null +++ b/test/ProjNet.Tests/Data/VerticalGridShiftRuntimeTests.cs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.IO; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Tests for vertical grid shift operations at runtime using GTX grid files. +/// +public class VerticalGridShiftRuntimeTests +{ + private static readonly double[] VerticalGridInput = [12d, 56d, 0d]; + + /// + /// Verifies that a vgridshift operation backed by a GTX grid file applies the expected vertical shift, respecting the +multiplier option. + /// + /// Multiplier token appended to operation. + /// Expected transformed Z value. + [Theory] + [InlineData("", -36.9959410718d)] + [InlineData(" +multiplier=1", 36.9959410718d)] + public void VgridshiftWithGtxGridAppliesExpectedVerticalShift(string multiplierToken, double expectedZ) + { + string gridPath = FindGridPath("egm96_15.gtx"); + string operation = $"+proj=vgridshift +grids={gridPath}{multiplierToken}"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + + Assert.True(ok, skipReason); + double[] output = Assert.IsType(transform, exactMatch: false).Transform(VerticalGridInput); + Assert.Equal(12d, output[0], 12); + Assert.Equal(56d, output[1], 12); + Assert.Equal(expectedZ, output[2], 9); + } + + /// + /// Verifies that applying the forward then inverse vgridshift operations round-trips a single point back to its original coordinates. + /// + [Fact] + public void VgridshiftWithInverseFlagRoundtripsSinglePoint() + { + string gridPath = FindGridPath("egm96_15.gtx"); + string forwardOperation = $"+proj=vgridshift +grids={gridPath}"; + string inverseOperation = $"+inv +proj=vgridshift +grids={gridPath}"; + + bool forwardOk = ProjPipelineMathTransformFactory.TryCreateMathTransform(forwardOperation, out MathTransform? forward, out string? forwardSkipReason); + bool inverseOk = ProjPipelineMathTransformFactory.TryCreateMathTransform(inverseOperation, out MathTransform? inverse, out string? inverseSkipReason); + + Assert.True(forwardOk, forwardSkipReason); + Assert.True(inverseOk, inverseSkipReason); + + double[] shifted = Assert.IsType(forward, exactMatch: false).Transform(VerticalGridInput); + double[] unshifted = Assert.IsType(inverse, exactMatch: false).Transform(shifted); + + Assert.Equal(12d, unshifted[0], 10); + Assert.Equal(56d, unshifted[1], 10); + Assert.Equal(0d, unshifted[2], 7); + } + + /// + /// Verifies that transforming a coordinate that falls outside the grid extent throws an . + /// + [Fact] + public void VgridshiftOutsideGridExtentThrowsArgumentException() + { + string gridPath = FindGridPath("test_nodata.gtx"); + string operation = $"+proj=vgridshift +grids={gridPath}"; + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + + Assert.Throws(() => Assert.IsType(transform, exactMatch: false).Transform(VerticalGridInput)); + } + + private static string FindGridPath(string fileName) + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "grids", fileName); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "grids", fileName); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate local test grid fixture under test\\ProjNet.Tests\\Fixtures\\grids.", fileName); + } +} diff --git a/test/ProjNet.Tests/Data/XyzGridShiftRuntimeTests.cs b/test/ProjNet.Tests/Data/XyzGridShiftRuntimeTests.cs new file mode 100644 index 00000000..c980f996 --- /dev/null +++ b/test/ProjNet.Tests/Data/XyzGridShiftRuntimeTests.cs @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.IO; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates M8 runtime parity for xyzgridshift. +/// +public class XyzGridShiftRuntimeTests +{ + private const string GridPlaceholder = "{GRID}"; + + /// + /// Verifies that xyzgridshift can be created and applies a measurable shift. + /// + /// Grid reference mode. + [Theory] + [InlineData("input_crs")] + [InlineData("output_crs")] + public void XyzGridShiftCanBeCreatedAndShiftsCoordinates(string gridRef) + { + string gridPath = FindGridPath("subset_of_gr3df97a.tif"); + string operation = $"+proj=xyzgridshift +grids={gridPath} +grid_ref={gridRef} +ellps=GRS80"; + string noShiftOperation = $"{operation} +multiplier=0"; + MathTransform transform = CreateTransform(operation); + MathTransform noShiftTransform = CreateTransform(noShiftOperation); + + double[] input = CreateSampleInputPoint(gridPath); + double[] output = transform.Transform(input); + double[] noShift = noShiftTransform.Transform(input); + + Assert.False(double.IsNaN(output[0]) || double.IsInfinity(output[0])); + Assert.False(double.IsNaN(output[1]) || double.IsInfinity(output[1])); + Assert.False(double.IsNaN(output[2]) || double.IsInfinity(output[2])); + + double shiftedDistance = Math.Sqrt( + ((output[0] - noShift[0]) * (output[0] - noShift[0])) + + ((output[1] - noShift[1]) * (output[1] - noShift[1])) + + ((output[2] - noShift[2]) * (output[2] - noShift[2]))); + Assert.InRange(shiftedDistance, 1e-6d, 5_000d); + } + + /// + /// Verifies multiplier scaling behaves linearly. + /// + [Fact] + public void XyzGridShiftMultiplierScalesShiftLinearly() + { + string gridPath = FindGridPath("subset_of_gr3df97a.tif"); + MathTransform half = CreateTransform($"+proj=xyzgridshift +grids={gridPath} +grid_ref=input_crs +ellps=GRS80 +multiplier=0.5"); + MathTransform full = CreateTransform($"+proj=xyzgridshift +grids={gridPath} +grid_ref=input_crs +ellps=GRS80 +multiplier=1"); + + double[] input = CreateSampleInputPoint(gridPath); + double[] outputHalf = half.Transform(input); + double[] outputFull = full.Transform(input); + + double halfDx = outputHalf[0] - input[0]; + double halfDy = outputHalf[1] - input[1]; + double halfDz = outputHalf[2] - input[2]; + double fullDx = outputFull[0] - input[0]; + double fullDy = outputFull[1] - input[1]; + double fullDz = outputFull[2] - input[2]; + + Assert.InRange(Math.Abs((2d * halfDx) - fullDx), 0d, 1e-4); + Assert.InRange(Math.Abs((2d * halfDy) - fullDy), 0d, 1e-4); + Assert.InRange(Math.Abs((2d * halfDz) - fullDz), 0d, 1e-4); + } + + /// + /// Verifies direct/iterative pairings produce reversible roundtrips. + /// + /// Grid reference mode. + [Theory] + [InlineData("input_crs")] + [InlineData("output_crs")] + public void XyzGridShiftRoundtripRecoversInput(string gridRef) + { + string gridPath = FindGridPath("subset_of_gr3df97a.tif"); + string baseOperation = $"+proj=xyzgridshift +grids={gridPath} +grid_ref={gridRef} +ellps=GRS80"; + MathTransform forward = CreateTransform(baseOperation); + MathTransform inverse = CreateTransform($"{baseOperation} +inv"); + + double[] input = CreateSampleInputPoint(gridPath); + double[] projected = forward.Transform(input); + double[] recovered = inverse.Transform(projected); + + Assert.InRange(Math.Abs(recovered[0] - input[0]), 0d, 1e-3); + Assert.InRange(Math.Abs(recovered[1] - input[1]), 0d, 1e-3); + Assert.InRange(Math.Abs(recovered[2] - input[2]), 0d, 1e-3); + } + + /// + /// Verifies argument-validation diagnostics for xyzgridshift setup. + /// + /// Operation text. + /// Expected diagnostic token. + [Theory] + [InlineData("+proj=xyzgridshift +ellps=GRS80", "+grids")] + [InlineData("+proj=xyzgridshift +grids={GRID} +ellps=GRS80 +grid_ref=invalid", "grid_ref")] + [InlineData("+proj=xyzgridshift +grids={GRID} +ellps=GRS80 +multiplier=abc", "multiplier")] + [InlineData("+proj=xyzgridshift +grids={GRID} +grid_ref=input_crs", "ellipsoid")] + public void XyzGridShiftCreationFailsForInvalidParameters(string operation, string expectedToken) + { + ArgumentNullException.ThrowIfNull(operation); + ArgumentNullException.ThrowIfNull(expectedToken); + if (operation.Contains(GridPlaceholder, StringComparison.Ordinal)) + { + operation = operation.Replace(GridPlaceholder, FindGridPath("subset_of_gr3df97a.tif"), StringComparison.Ordinal); + } + + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out _, out string? skipReason); + Assert.False(ok); + Assert.Contains(expectedToken, Assert.IsType(skipReason), StringComparison.OrdinalIgnoreCase); + } + + private static MathTransform CreateTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } + + private static string FindGridPath(string fileName) + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "grids", fileName); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "grids", fileName); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate local test grid fixture under test\\ProjNet.Tests\\Fixtures\\grids.", fileName); + } + + private static double[] CreateSampleInputPoint(string gridPath) + { + IReadOnlyList grids = GeoTiffGridLoader.LoadXyz(gridPath); + if (grids.Count == 0) + { + throw new InvalidOperationException("No xyz grid pages were loaded."); + } + + for (int i = 0; i < grids.Count; i++) + { + GeoTiffXyzGridShiftMathTransform.XyzGrid grid = grids[i]; + if (TrySelectSamplePoint(grid, out double lon, out double lat)) + { + return GeographicToGeocentric(lon, lat, 0d); + } + } + + throw new InvalidOperationException("Could not locate a stable in-grid xyzgridshift sample point."); + } + + private static bool TrySelectSamplePoint(GeoTiffXyzGridShiftMathTransform.XyzGrid grid, out double lon, out double lat) + { + lon = 0d; + lat = 0d; + for (int y = 0; y < grid.Height - 1; y++) + { + for (int x = 0; x < grid.Width - 1; x++) + { + if (GetCellShiftMagnitude(grid, x, y) <= 1e-12d) + { + continue; + } + + double candidateGridX = x + 0.5d; + double candidateGridY = y + 0.5d; + double candidateLon = (grid.A * candidateGridX) + (grid.B * candidateGridY) + grid.C; + double candidateLat = (grid.D * candidateGridX) + (grid.E * candidateGridY) + grid.F; + if (!grid.TryMapToGridCoordinates(candidateLon, candidateLat, out double mappedX, out double mappedY)) + { + continue; + } + + if (mappedX <= 0.25d + || mappedY <= 0.25d + || mappedX >= (grid.Width - 1.25d) + || mappedY >= (grid.Height - 1.25d)) + { + continue; + } + + lon = candidateLon; + lat = candidateLat; + return true; + } + } + + return false; + } + + private static double GetCellShiftMagnitude(GeoTiffXyzGridShiftMathTransform.XyzGrid grid, int x, int y) + { + double dx = Math.Abs(grid.GetXShift(x, y)) + + Math.Abs(grid.GetXShift(x + 1, y)) + + Math.Abs(grid.GetXShift(x, y + 1)) + + Math.Abs(grid.GetXShift(x + 1, y + 1)); + double dy = Math.Abs(grid.GetYShift(x, y)) + + Math.Abs(grid.GetYShift(x + 1, y)) + + Math.Abs(grid.GetYShift(x, y + 1)) + + Math.Abs(grid.GetYShift(x + 1, y + 1)); + double dz = Math.Abs(grid.GetZShift(x, y)) + + Math.Abs(grid.GetZShift(x + 1, y)) + + Math.Abs(grid.GetZShift(x, y + 1)) + + Math.Abs(grid.GetZShift(x + 1, y + 1)); + return dx + dy + dz; + } + + private static double[] GeographicToGeocentric(double longitudeDegrees, double latitudeDegrees, double height) + { + var parameters = new List + { + new("semi_major", Ellipsoid.GRS80.SemiMajorAxis), + new("semi_minor", Ellipsoid.GRS80.SemiMinorAxis), + }; + var transform = new GeocentricTransform(parameters, false); + double x = longitudeDegrees; + double y = latitudeDegrees; + double z = height; + transform.Transform(ref x, ref y, ref z); + return [x, y, z]; + } +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_3d.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_3d.json new file mode 100644 index 00000000..6a41ce5c --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_3d.json @@ -0,0 +1,54 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "degree", + "horizontal_offset_method": "addition", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "3d", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_degree_3d_grid.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} \ No newline at end of file diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal.json new file mode 100644 index 00000000..533c0054 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal.json @@ -0,0 +1,53 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "degree", + "horizontal_offset_method": "addition", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "horizontal", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_degree_3d_grid.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} \ No newline at end of file diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_exponential.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_exponential.json new file mode 100644 index 00000000..8ba34ce0 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_exponential.json @@ -0,0 +1,58 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "degree", + "horizontal_offset_method": "addition", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "exponential test", + "displacement_type": "horizontal", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_degree_3d_grid.tif" + }, + "time_function": { + "type": "exponential", + "parameters": { + "reference_epoch": "2020-01-01T00:00:00Z", + "end_epoch": "2024-01-01T00:00:00Z", + "relaxation_constant": 1.0, + "before_scale_factor": 0.0, + "initial_scale_factor": 0.0, + "final_scale_factor": 1.0 + } + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_piecewise.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_piecewise.json new file mode 100644 index 00000000..88aa206f --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_piecewise.json @@ -0,0 +1,64 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "degree", + "horizontal_offset_method": "addition", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "piecewise test", + "displacement_type": "horizontal", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_degree_3d_grid.tif" + }, + "time_function": { + "type": "piecewise", + "parameters": { + "before_first": "linear", + "after_last": "linear", + "model": [ + { + "epoch": "2020-01-01T00:00:00Z", + "scale_factor": 0.0 + }, + { + "epoch": "2022-01-01T00:00:00Z", + "scale_factor": 2.0 + } + ] + } + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_reverse_step.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_reverse_step.json new file mode 100644 index 00000000..f275dd99 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_reverse_step.json @@ -0,0 +1,53 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "degree", + "horizontal_offset_method": "addition", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "reverse step test", + "displacement_type": "horizontal", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_degree_3d_grid.tif" + }, + "time_function": { + "type": "reverse_step", + "parameters": { + "step_epoch": "2021-01-01T00:00:00Z" + } + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_velocity.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_velocity.json new file mode 100644 index 00000000..392eb625 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_degree_horizontal_velocity.json @@ -0,0 +1,53 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "degree", + "horizontal_offset_method": "addition", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "velocity test", + "displacement_type": "horizontal", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_degree_3d_grid.tif" + }, + "time_function": { + "type": "velocity", + "parameters": { + "reference_epoch": "2020-01-01T00:00:00Z" + } + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_3d.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_3d.json new file mode 100644 index 00000000..201aaebb --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_3d.json @@ -0,0 +1,54 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "metre", + "horizontal_offset_method": "addition", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "3d", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_metre_3d_grid.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} \ No newline at end of file diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_3d_geocentric.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_3d_geocentric.json new file mode 100644 index 00000000..1328ff5e --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_3d_geocentric.json @@ -0,0 +1,54 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "metre", + "horizontal_offset_method": "geocentric", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "3d", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_metre_3d_grid.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} \ No newline at end of file diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_horizontal.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_horizontal.json new file mode 100644 index 00000000..d0ac477c --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_horizontal.json @@ -0,0 +1,53 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "metre", + "horizontal_offset_method": "addition", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "horizontal", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_metre_3d_grid.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} \ No newline at end of file diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_vertical.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_vertical.json new file mode 100644 index 00000000..70574340 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_metre_vertical.json @@ -0,0 +1,49 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "vertical", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + -180, + -90, + 180, + 90 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/simple_model_metre_vertical_grid.tif" + }, + "time_function": { + "type": "constant" + } + } + ] +} \ No newline at end of file diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_polar.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_polar.json new file mode 100644 index 00000000..ef99a0cb --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_polar.json @@ -0,0 +1,54 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "horizontal_offset_unit": "metre", + "horizontal_offset_method": "geocentric", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + 0, + -90, + 360, + -89 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "3d", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + 0, + -90, + 360, + -89 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "geocentric_bilinear", + "filename": "tests/simple_model_polar.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_projected.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_projected.json new file mode 100644 index 00000000..c97a7c11 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_projected.json @@ -0,0 +1,51 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:2193", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:2193", + "horizontal_offset_unit": "metre", + "horizontal_offset_method": "addition", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + 1500000.0, + 5400000.0, + 1501000.0, + 5401000.0 + ] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "3d", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [ + 1500000.0, + 5400000.0, + 1501000.0, + 5401000.0 + ] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "bilinear", + "filename": "tests/test_3d_grid_projected.tif" + }, + "time_function": { + "type": "constant" + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_wrap_east.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_wrap_east.json new file mode 100644 index 00000000..2a0a2c0e --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_wrap_east.json @@ -0,0 +1,42 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [-194.2, -37.5, -193.8, -37.2] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "vertical", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [-194.2, -37.5, -193.8, -37.2] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "geocentric_bilinear", + "filename": "tests/simple_model_wrap_east.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/simple_model_wrap_west.json b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_wrap_west.json new file mode 100644 index 00000000..54a04bd2 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/defmodel/simple_model_wrap_west.json @@ -0,0 +1,42 @@ +{ + "file_type": "deformation_model_master_file", + "format_version": "1.0", + "source_crs": "EPSG:4326", + "target_crs": "foo:ignored", + "definition_crs": "EPSG:4326", + "vertical_offset_unit": "metre", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [525.8,-37.5,526.2,-37.2] + } + }, + "time_extent": { + "first": "1900-01-01T00:00:00Z", + "last": "2050-01-01T00:00:00Z" + }, + "components": [ + { + "description": "test", + "displacement_type": "vertical", + "uncertainty_type": "none", + "extent": { + "type": "bbox", + "parameters": { + "bbox": [525.8,-37.5,526.2,-37.2] + } + }, + "spatial_model": { + "type": "GeoTIFF", + "interpolation_method": "geocentric_bilinear", + "filename": "tests/simple_model_wrap_west.tif" + }, + "time_function": { + "type": "step", + "parameters": { + "step_epoch": "1900-01-01T00:00:00Z" + } + } + } + ] +} diff --git a/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_degree_3d_grid.tif b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_degree_3d_grid.tif new file mode 100644 index 00000000..3bbff0a6 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_degree_3d_grid.tif differ diff --git a/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_metre_3d_grid.tif b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_metre_3d_grid.tif new file mode 100644 index 00000000..40cf2d70 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_metre_3d_grid.tif differ diff --git a/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_metre_vertical_grid.tif b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_metre_vertical_grid.tif new file mode 100644 index 00000000..058b8081 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_metre_vertical_grid.tif differ diff --git a/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_polar.tif b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_polar.tif new file mode 100644 index 00000000..7371ca1e Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_polar.tif differ diff --git a/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_wrap_east.tif b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_wrap_east.tif new file mode 100644 index 00000000..816d8a7a Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_wrap_east.tif differ diff --git a/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_wrap_west.tif b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_wrap_west.tif new file mode 100644 index 00000000..3a8da6f6 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/defmodel/tests/simple_model_wrap_west.tif differ diff --git a/test/ProjNet.Tests/Fixtures/defmodel/tests/test_3d_grid_projected.tif b/test/ProjNet.Tests/Fixtures/defmodel/tests/test_3d_grid_projected.tif new file mode 100644 index 00000000..56138417 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/defmodel/tests/test_3d_grid_projected.tif differ diff --git a/test/ProjNet.Tests/Fixtures/gie/4D-API_cs2cs-style.gie b/test/ProjNet.Tests/Fixtures/gie/4D-API_cs2cs-style.gie new file mode 100644 index 00000000..1b0c1541 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/4D-API_cs2cs-style.gie @@ -0,0 +1,560 @@ + +------------------------------------------------------------------------------- +=============================================================================== + +Test the 4D API handling of cs2cs style transformation options. + +These tests are mostly based on the same material as those in +more_builtins.gie, since we are testing the same kinds of things, +but provided through a different interface. + +=============================================================================== + + + + +------------------------------------------------------------------------------- +# Test the handling of the +towgs84 parameter. +------------------------------------------------------------------------------- +# (additional tests of the towgs84 handling can be found in DHDN_ETRS89.gie) +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# This example is from Lotti Jivall: "Simplified transformations from +# ITRF2008/IGS08 to ETRS89 for maritime applications" (see also more_builtins.gie) +------------------------------------------------------------------------------- +operation proj=geocent \ + towgs84 = 0.676780, 0.654950, -0.528270, \ + -0.022742, 0.012667, 0.022704, \ + -0.01070 +------------------------------------------------------------------------------- +tolerance 1 um + +direction inverse + +# Broken test. FIXME +#accept 3565285.00000000 855949.00000000 5201383.00000000 +#expect 3565285.41342351 855948.67986759 5201382.72939791 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# This example is a random point, transformed from ED50 to ETRS89 using KMStrans2. +------------------------------------------------------------------------------- +operation proj=latlong ellps=intl \ + towgs84 = -081.07030, -089.36030, -115.75260, \ + 000.48488, 000.02436, 000.41321, -0.540645 +------------------------------------------------------------------------------- +tolerance 25 mm + +accept 16.82 55.17 61.0 +expect 16.8210462130 55.1705688946 29.0317 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +operation proj=latlong nadgrids=ntf_r93.gsb ellps=GRS80 +------------------------------------------------------------------------------- +# This functionality is also tested in more_builtins.gie +------------------------------------------------------------------------------- +tolerance 1 mm +accept 2.25 46.5 +expect 2.250704350387 46.500051597273 +direction inverse +accept 2.250704350387 46.500051597273 +expect 2.25 46.5 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widespread bad egm96 file + +accept 12.5 55.5 0 +expect 12.5 55.5 -36.3941 + +direction inverse + +accept 12.5 55.5 -36.3941 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=merc geoidgrids=egm96_15.gtx ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 12.5 55.5 0 +expect 1391493.63492 7424275.19462 -36.3941 +direction inverse +accept 1391493.63492 7424275.19462 -36.3941 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +# Same as the two above, but also do axis swapping. +------------------------------------------------------------------------------- +# NOTE: A number of the tests below are commented out. The actually do the +# right thing, but the gie distance computation is not yet able to cope +# with "unusual" axis orders +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx axis=neu ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widely distributed, bad egm96 file +# Broken test. FIXME +#accept 12.5 55.5 0 +#expect 55.5 12.5 -36.3941 +#direction inverse +#accept 55.5 12.5 -36.3941 +#expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx axis=dne ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widely distributed, bad egm96 file +# accept 12.5 55.5 0 +# expect 36.3941 55.5 12.5 +# direction inverse +# accept 36.3941 55.5 12.5 +# expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=merc geoidgrids=egm96_15.gtx ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 12.5 55.5 0 +expect 1391493.63492 7424275.19462 -36.3941 +direction inverse +accept 1391493.63492 7424275.19462 -36.3941 +expect 12.5 55.5 0 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Some more complex axis swapping. +------------------------------------------------------------------------------- +operation proj=latlong geoidgrids=egm96_15.gtx axis=nue ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm # lax tolerance due to widely distributed, bad egm96 file +# Broken test. FIXME +#accept 12.5 55.5 0 +#expect 55.5 -36.3941 12.5 +# direction inverse +# accept 55.5 -36.3941 12.5 +# expect 12.5 55.5 0 +------------------------------------------------------------------------------- +operation proj=merc geoidgrids=egm96_15.gtx axis=sue ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 15 cm +accept 12.5 55.5 0 +expect -7424275.1946 -36.3941 1391493.6349 0.0000 +# direction inverse +# accept -7424275.1946 -36.3941 1391493.6349 0.0000 +# expect 12.5 55.5 0 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +# A test case from a comment by Github user c0nk +------------------------------------------------------------------------------- +operation proj=somerc \ + lat_0=46.95240555555556 lon_0=7.439583333333333 k_0=1 \ + x_0=2600000 y_0=1200000 ellps=bessel \ + towgs84=674.374,15.056,405.346 +------------------------------------------------------------------------------- +tolerance 20 cm +accept 7.438632495 46.951082877 +expect 2600000.0 1200000.0 +------------------------------------------------------------------------------- +# Same test, but now implemented as a pipeline. This is for testing a nasty bug, +# where, at the end of pipeline creation, a false warning about missing ellps was +# left behind from the creation of the Helmert step (now repaired in pj_init). +------------------------------------------------------------------------------- +operation proj=pipeline \ + step proj=cart ellps=WGS84 \ + step proj=helmert x=674.37400 y=15.05600 z=405.34600 inv \ + step proj=cart ellps=bessel inv \ + step proj=somerc lat_0=46.95240555555556 lon_0=7.439583333333333 \ + k_0=1 x_0=2600000 y_0=1200000 ellps=bessel units=m +------------------------------------------------------------------------------- +tolerance 20 cm +accept 7.438632495 46.951082877 0 +expect 2600000.0 1200000.0 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Make sure that transient errors are returned correctly. +------------------------------------------------------------------------------- +operation +proj=geos +lon_0=0.00 +lat_0=0.00 +a=6378169.00 +b=6356583.80 +h=35785831.0 +------------------------------------------------------------------------------- +accept 85.05493299 46.5261074 +expect failure + +accept 85.05493299 46.5261074 0 +expect failure + +accept 85.05493299 46.5261074 0 0 +expect failure + +------------------------------------------------------------------------------- +# Test that Google's Web Mercator works as intended (see #834 for details). +------------------------------------------------------------------------------- +use_proj4_init_rules true +operation proj=pipeline step init=epsg:26915 inv step init=epsg:3857 +------------------------------------------------------------------------------- +tolerance 20 cm +accept 487147.594520173 4934316.46263998 +expect -10370728.80 5552839.74 + +accept 487147.594520173 4934316.46263998 0 +expect -10370728.80 5552839.74 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test Google's Web Mercator with +proj=webmerc +ellps=WGS84 +------------------------------------------------------------------------------- +use_proj4_init_rules true +operation proj=pipeline step init=epsg:26915 inv step proj=webmerc datum=WGS84 +------------------------------------------------------------------------------- +tolerance 20 cm +accept 487147.594520173 4934316.46263998 +expect -10370728.80 5552839.74 + +accept 487147.594520173 4934316.46263998 0 +expect -10370728.80 5552839.74 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Web Mercator test data from EPSG Guidance Note 7-2, p. 44. +------------------------------------------------------------------------------- +operation proj=webmerc +ellps=WGS84 +tolerance 1 cm + +accept -100.33333333 24.46358028 +expect -11169055.58 2810000.00 + +accept -100.33333333 24.38178694 +expect -11169055.58 2800000.00 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test that +datum parameters are handled correctly in pipelines. +# See #872 for details. +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=longlat +datum=GGRS87 +inv \ + +step +proj=longlat +datum=WGS84 +------------------------------------------------------------------------------- +tolerance 20 cm +accept 23.7275 37.9838 0 +expect 23.729194873180 37.986398897578 31.289740102 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Test that +towgs84=0,0,0 parameter is handled as still implying cart +# transformation +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=utm +zone=11 +ellps=clrk66 +towgs84=0,0,0 +inv \ + +step +proj=utm +zone=11 +datum=WGS84 + +------------------------------------------------------------------------------- +tolerance 20 cm +accept 440720 3751320 0 +expect 440719.958709357 3751294.2109841 -4.44340920541435 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test that pipelines with unit mismatch between steps can't be constructed. +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=merc \ + +step +proj=merc +expect failure pjd_err_malformed_pipeline + +operation +proj=pipeline \ + +step +proj=latlong \ + +step +proj=merc \ + +step +proj=helmert +x=200 +y=100 +expect failure pjd_err_malformed_pipeline + +operation +proj=pipeline \ + +step +proj=merc +ellps=WGS84 \ + +step +proj=unitconvert +xy_in=m +xy_out=km +accept 12 56 +expect 1335.8339 7522.963 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test invalid pipelines +------------------------------------------------------------------------------- +# proj= before first step +operation +proj=pipeline +proj=merc +step +inv +proj=merc +expect failure pjd_err_malformed_pipeline + +# o_proj= before first step +operation +proj=pipeline +o_proj=merc +step +proj=ob_tran +expect failure pjd_err_malformed_pipeline + +# nested pipeline +operation +proj=pipeline +step +proj=pipeline +step +proj=merc +expect failure pjd_err_malformed_pipeline + +------------------------------------------------------------------------------- +# Test Pipeline Coordinate Stack +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=push +v_1 \ + +step +proj=utm +zone=32 \ + +step +proj=utm +zone=33 +inv \ + +step +proj=pop +v_1 + +accept 12 56 0 2020 +expect 12 56 0 2020 +roundtrip 10 + +operation +proj=pipeline \ + +step +proj=latlon \ # dummy step + +step +proj=push +v_1 \ + +step +proj=utm +zone=32 \ + +step +proj=utm +zone=33 +inv \ + +step +proj=pop +v_1 \ + +step +proj=affine # dummy step + +accept 12 56 0 2020 +expect 12 56 0 2020 +roundtrip 10 + +# push value to stack without popping it again +operation +proj=pipeline \ + +step +proj=push +v_1 \ + +step +proj=utm +zone=32 \ + +step +proj=utm +zone=33 +inv + +accept 12 56 0 2020 +expect 18 56 0 2020 + +# test that multiple pushes and pops works +operation +proj=pipeline \ + +step +proj=push +v_1 \ + +step +proj=utm +zone=32 \ + +step +proj=push +v_1 \ + +step +proj=utm +zone=33 +inv \ + +step +proj=utm +zone=34 \ + +step +proj=pop +v_1 \ + +step +proj=utm +zone=32 +inv \ + +step +proj=pop +v_1 + +accept 12 56 0 2020 +expect 12 56 0 2020 + +# pop from empty stack +operation +proj=pipeline \ + +step +proj=utm +zone=32 \ + +step +proj=utm +zone=33 +inv \ + +step +proj=pop +v_1 + +accept 12 56 0 2020 +expect 18 56 0 2020 + +operation +proj=pipeline \ + +step +proj=push +v_2 \ + +step +inv +proj=eqearth \ + +step +proj=laea \ + +step +proj=pop +v_2 + +accept 900000 6000000 0 2020 +expect 896633.0226 6000000 0 2020 + +# Datum shift in cartesian space but keeping the height +# (simulates a datum-shift with affin since ISO19111 code +# currently obfuscates proj-strings using cart/helmert/invcart) +operation +proj=pipeline +ellps=GRS80 \ + +step +proj=push +v_3 \ + +step +proj=cart \ + +step +proj=affine +xoff=1000 +yoff=2000 +xoff=3000 \ + +step +proj=cart +inv \ + +step +proj=pop +v_3 +tolerance 50 cm + +accept 12 56 0 +expect 12.0280112877 55.9896187413 0 +roundtrip 1 + +operation +proj=push +v_3 +accept 12 56 0 0 +expect 12 56 0 0 + +operation +proj=pop +v_3 +accept 12 56 0 0 +expect 12 56 0 0 + +------------------------------------------------------------------------------- +# Test Pipeline +omit_inv +------------------------------------------------------------------------------- + +operation +proj=pipeline +step +proj=affine +xoff=1 +yoff=1 +omit_inv + +accept 2 49 0 0 +expect 3 50 0 0 + +direction inverse +accept 2 49 0 0 +expect 2 49 0 0 + + +operation +proj=pipeline +step +inv +proj=affine +xoff=1 +yoff=1 +omit_inv + +accept 2 49 0 0 +expect 1 48 0 0 + +direction inverse +accept 2 49 0 0 +expect 2 49 0 0 + +------------------------------------------------------------------------------- +# Test Pipeline +omit_fwd +------------------------------------------------------------------------------- + +operation +proj=pipeline +step +proj=affine +xoff=1 +yoff=1 +omit_fwd + +accept 2 49 0 0 +expect 2 49 0 0 + +direction inverse +accept 2 49 0 0 +expect 1 48 0 0 + + +operation +proj=pipeline +step +inv +proj=affine +xoff=1 +yoff=1 +omit_fwd + +accept 2 49 0 0 +expect 2 49 0 0 + +direction inverse +accept 2 49 0 0 +expect 3 50 0 0 + +------------------------------------------------------------------------------- +# Test bugfix of https://github.com/OSGeo/proj.4/issues/1002 +# (do not interpolate nodata values) +------------------------------------------------------------------------------- +operation +proj=latlong +ellps=WGS84 +geoidgrids=tests/test_nodata.gtx +------------------------------------------------------------------------------- +accept 4.05 52.1 0 +expect 4.05 52.1 -10 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test null grid with vgridshift +------------------------------------------------------------------------------- +operation proj=vgridshift grids=tests/test_nodata.gtx,null ellps=GRS80 +------------------------------------------------------------------------------- +accept 4.05 52.1 0 +expect 4.05 52.1 -10 + +# Outside validity area of test_nodata.gtx. Fallback on null +accept 4.05 -52.1 0 +expect 4.05 -52.1 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test bug fix of https://github.com/OSGeo/proj.4/issues/1025. +# Using geocent in the new API with a custom ellipsoid should return coordinates +# that correspond to that particular ellipsoid and not WGS84 as demonstrated in +# the bug report. +------------------------------------------------------------------------------- +operation +proj=pipeline +step \ + +proj=longlat +a=3396190 +b=3376200 +inv +step \ + +proj=geocent +a=3396190 +b=3376200 +lon_0=0 +units=m +accept 0.0 0.0 0.0 +expect 3396190.0 0.0 0.0 +roundtrip 1 + +operation +proj=geocent +a=3396190 +b=3376200 +lon_0=0 +units=m +accept 0.0 0.0 0.00 +expect 3396190.0 0.0 0.0 +roundtrip 1 + + +------------------------------------------------------------------------------- +# Check that geocent and cart take into account to_meter (#1053) +------------------------------------------------------------------------------- + +operation +proj=geocent +a=1000 +b=1000 +to_meter=1000 +accept 90 0 0 +expect 0 1 0 +roundtrip 1 + +operation +proj=cart +a=1000 +b=1000 +to_meter=1000 +accept 90 0 0 +expect 0 1 0 +roundtrip 1 + +------------------------------------------------------------------------------- +# Check that vunits / vto_meter is honored +------------------------------------------------------------------------------- + +operation +proj=longlat +a=1 +b=1 +vto_meter=1000 +accept 0 0 1000 +expect 0 0 1 +roundtrip 1 + +operation +proj=longlat +a=1 +b=1 +vto_meter=2000/2 +accept 0 0 1000 +expect 0 0 1 +roundtrip 1 + +operation +proj=longlat +a=1 +b=1 +vto_meter=1/0 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=longlat +a=1 +b=1 +vto_meter=1000 +geoc +accept 0 0 1000 +expect 0 0 1 +roundtrip 1 + +operation +proj=longlat +a=1 +b=1 +vunits=km +accept 0 0 1000 +expect 0 0 1 +roundtrip 1 + +operation +proj=merc +a=1 +b=1 +vto_meter=1000 +accept 0 0 1000 +expect 0 0 1 +roundtrip 1 + +operation +proj=merc +a=1 +b=1 +vunits=km +accept 0 0 1000 +expect 0 0 1 +roundtrip 1 + +------------------------------------------------------------------------------- +# Check that proj_create() returns a syntax error when an exception is caught +# in the creation of a PJ object. +------------------------------------------------------------------------------- +operation this is a bogus CRS meant to trigger a syntax error in proj_create() +expect failure errno invalid_op_wrong_syntax + +------------------------------------------------------------------------------- +# Test proj=set +------------------------------------------------------------------------------- + +operation +proj=set +accept 1 2 3 4 +expect 1 2 3 4 +roundtrip 1 + +operation +proj=set +v_1=10 +v_2=20 +v_3=30 +v_4=40 +accept 1 2 3 4 +expect 10 20 30 40 + +operation +proj=set +v_1=10 +v_2=20 +v_3=30 +v_4=40 +direction inverse +accept 1 2 3 4 +expect 10 20 30 40 + + diff --git a/test/ProjNet.Tests/Fixtures/gie/DHDN_ETRS89.gie b/test/ProjNet.Tests/Fixtures/gie/DHDN_ETRS89.gie new file mode 100644 index 00000000..0e88ad5d --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/DHDN_ETRS89.gie @@ -0,0 +1,375 @@ + + +------------------------------------------------------------------------------- +operation proj=latlong datum=potsdam ellps=bessel +------------------------------------------------------------------------------- +#DE_DHDN (BeTA, 2007) to ETRS89 using NTv2 grid. epsg:15948 +------------------------------------------------------------------------------- + +tolerance 1 mm +accept 7.482506019176 53.498461143331 # ETRS89_Lat-Lon +expect 7.483333333333 53.500000000000 # DE_DHDN_Lat-Lon + +direction inverse + +accept 7.483333333333 53.500000000000 # DE_DHDN_Lat-Lon +expect 7.482506019176 53.498461143331 # ETRS89_Lat-Lon +accept 10.333333333333 48.833333333333 # DE_DHDN_Lat-Lon +expect 10.332117283303 48.832327188640 # ETRS89_Lat-Lon +accept 8.000000000000 50.083333333333 # DE_DHDN_Lat-Lon +expect 7.999097344043 50.082172046476 # ETRS89_Lat-Lon +accept 10.016666666667 51.033333333333 # DE_DHDN_Lat-Lon +expect 10.015460839103 51.032075951188 # ETRS89_Lat-Lon +accept 10.466666666667 54.333333333333 # DE_DHDN_Lat-Lon +expect 10.465373788153 54.331696254077 # ETRS89_Lat-Lon +accept 10.750000000000 53.583333333333 # DE_DHDN_Lat-Lon +expect 10.748659705929 53.581781243436 # ETRS89_Lat-Lon +accept 10.016666666667 53.500000000000 # DE_DHDN_Lat-Lon +expect 10.015444367463 53.498457503620 # ETRS89_Lat-Lon +accept 11.000000000000 53.466666666667 # DE_DHDN_Lat-Lon +expect 10.998619309575 53.465127257963 # ETRS89_Lat-Lon +accept 13.466666666667 53.766666666667 # DE_DHDN_Lat-Lon +expect 13.464877774631 53.765109112396 # ETRS89_Lat-Lon +accept 10.983333333333 52.766666666667 # DE_DHDN_Lat-Lon +expect 10.981965431979 52.765211787713 # ETRS89_Lat-Lon +accept 13.000000000000 51.783333333333 # DE_DHDN_Lat-Lon +expect 12.998336654827 51.782006921265 # ETRS89_Lat-Lon +accept 10.466666666667 52.500000000000 # DE_DHDN_Lat-Lon +expect 10.465380298337 52.498573633365 # ETRS89_Lat-Lon +accept 10.550000000000 51.466666666667 # DE_DHDN_Lat-Lon +expect 10.548711467380 51.465361979987 # ETRS89_Lat-Lon +accept 10.450000000000 50.583333333333 # DE_DHDN_Lat-Lon +expect 10.448735275612 50.582129474187 # ETRS89_Lat-Lon +accept 10.416666666667 49.666666666667 # DE_DHDN_Lat-Lon +expect 10.415423634267 49.665566047661 # ETRS89_Lat-Lon +accept 10.550000000000 47.750000000000 # DE_DHDN_Lat-Lon +expect 10.548775945187 47.749120260296 # ETRS89_Lat-Lon +accept 13.450000000000 50.666666666667 # DE_DHDN_Lat-Lon +expect 13.448283429558 50.665476385913 # ETRS89_Lat-Lon +accept 13.550000000000 51.333333333333 # DE_DHDN_Lat-Lon +expect 13.548264242652 51.332063317958 # ETRS89_Lat-Lon +accept 13.566666666667 52.050000000000 # DE_DHDN_Lat-Lon +expect 13.564906713066 52.048646469731 # ETRS89_Lat-Lon +accept 13.433333333333 53.166666666667 # DE_DHDN_Lat-Lon +expect 13.431569610583 53.165185284138 # ETRS89_Lat-Lon +accept 13.466666666667 52.483333333333 # DE_DHDN_Lat-Lon +expect 13.464913254978 52.481930297429 # ETRS89_Lat-Lon +accept 13.133333333333 49.066666666667 # DE_DHDN_Lat-Lon +expect 13.131706947050 49.065661709281 # ETRS89_Lat-Lon +accept 8.666666666667 53.116666666667 # DE_DHDN_Lat-Lon +expect 8.665654272188 53.115169791635 # ETRS89_Lat-Lon +accept 12.950000000000 47.650000000000 # DE_DHDN_Lat-Lon +expect 12.948437185277 47.649155713893 # ETRS89_Lat-Lon +accept 8.500000000000 54.716666666667 # DE_DHDN_Lat-Lon +expect 8.499027339833 54.714992333813 # ETRS89_Lat-Lon +accept 7.483333333333 51.983333333333 # DE_DHDN_Lat-Lon +expect 7.482494584516 51.981965147975 # ETRS89_Lat-Lon +accept 7.516666666667 51.016666666667 # DE_DHDN_Lat-Lon +expect 7.515823996992 51.015402184493 # ETRS89_Lat-Lon +accept 7.466666666667 50.500000000000 # DE_DHDN_Lat-Lon +expect 7.465834308888 50.498791390585 # ETRS89_Lat-Lon +accept 7.533333333333 49.333333333333 # DE_DHDN_Lat-Lon +expect 7.532503616986 49.332250779407 # ETRS89_Lat-Lon +accept 7.250000000000 49.333333333333 # DE_DHDN_Lat-Lon +expect 7.249209260581 49.332249456364 # ETRS89_Lat-Lon +accept 7.533333333333 47.666666666667 # DE_DHDN_Lat-Lon +expect 7.532530252396 47.665765608135 # ETRS89_Lat-Lon +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +operation proj=latlong \ + towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 ellps=bessel +------------------------------------------------------------------------------- +# DE_DHDN to ETRS89 using deprecated 7 parameter Helmert transform. The results +# agree at the 3 m level. +------------------------------------------------------------------------------- + +require_grid BETA2007.gsb +tolerance 3 m + +accept 7.482506019176 53.498461143331 # ETRS89_Lat-Lon +expect 7.483333333333 53.500000000000 # DE_DHDN_Lat-Lon + +direction inverse + +accept 7.483333333333 53.500000000000 # DE_DHDN_Lat-Lon +expect 7.482506019176 53.498461143331 # ETRS89_Lat-Lon + +accept 10.333333333333 48.833333333333 # DE_DHDN_Lat-Lon +expect 10.332117283303 48.832327188640 # ETRS89_Lat-Lon +accept 8.000000000000 50.083333333333 # DE_DHDN_Lat-Lon +expect 7.999097344043 50.082172046476 # ETRS89_Lat-Lon +accept 10.016666666667 51.033333333333 # DE_DHDN_Lat-Lon +expect 10.015460839103 51.032075951188 # ETRS89_Lat-Lon +accept 10.466666666667 54.333333333333 # DE_DHDN_Lat-Lon +expect 10.465373788153 54.331696254077 # ETRS89_Lat-Lon +accept 10.750000000000 53.583333333333 # DE_DHDN_Lat-Lon +expect 10.748659705929 53.581781243436 # ETRS89_Lat-Lon +accept 10.016666666667 53.500000000000 # DE_DHDN_Lat-Lon +expect 10.015444367463 53.498457503620 # ETRS89_Lat-Lon +accept 11.000000000000 53.466666666667 # DE_DHDN_Lat-Lon +expect 10.998619309575 53.465127257963 # ETRS89_Lat-Lon +accept 13.466666666667 53.766666666667 # DE_DHDN_Lat-Lon +expect 13.464877774631 53.765109112396 # ETRS89_Lat-Lon +accept 10.983333333333 52.766666666667 # DE_DHDN_Lat-Lon +expect 10.981965431979 52.765211787713 # ETRS89_Lat-Lon +accept 13.000000000000 51.783333333333 # DE_DHDN_Lat-Lon +expect 12.998336654827 51.782006921265 # ETRS89_Lat-Lon +accept 10.466666666667 52.500000000000 # DE_DHDN_Lat-Lon +expect 10.465380298337 52.498573633365 # ETRS89_Lat-Lon +accept 10.550000000000 51.466666666667 # DE_DHDN_Lat-Lon +expect 10.548711467380 51.465361979987 # ETRS89_Lat-Lon +accept 10.450000000000 50.583333333333 # DE_DHDN_Lat-Lon +expect 10.448735275612 50.582129474187 # ETRS89_Lat-Lon +accept 10.416666666667 49.666666666667 # DE_DHDN_Lat-Lon +expect 10.415423634267 49.665566047661 # ETRS89_Lat-Lon +accept 10.550000000000 47.750000000000 # DE_DHDN_Lat-Lon +expect 10.548775945187 47.749120260296 # ETRS89_Lat-Lon +accept 13.450000000000 50.666666666667 # DE_DHDN_Lat-Lon +expect 13.448283429558 50.665476385913 # ETRS89_Lat-Lon +accept 13.550000000000 51.333333333333 # DE_DHDN_Lat-Lon +expect 13.548264242652 51.332063317958 # ETRS89_Lat-Lon +accept 13.566666666667 52.050000000000 # DE_DHDN_Lat-Lon +expect 13.564906713066 52.048646469731 # ETRS89_Lat-Lon +accept 13.433333333333 53.166666666667 # DE_DHDN_Lat-Lon +expect 13.431569610583 53.165185284138 # ETRS89_Lat-Lon +accept 13.466666666667 52.483333333333 # DE_DHDN_Lat-Lon +expect 13.464913254978 52.481930297429 # ETRS89_Lat-Lon +accept 13.133333333333 49.066666666667 # DE_DHDN_Lat-Lon +expect 13.131706947050 49.065661709281 # ETRS89_Lat-Lon +accept 8.666666666667 53.116666666667 # DE_DHDN_Lat-Lon +expect 8.665654272188 53.115169791635 # ETRS89_Lat-Lon +accept 12.950000000000 47.650000000000 # DE_DHDN_Lat-Lon +expect 12.948437185277 47.649155713893 # ETRS89_Lat-Lon +accept 8.500000000000 54.716666666667 # DE_DHDN_Lat-Lon +expect 8.499027339833 54.714992333813 # ETRS89_Lat-Lon +accept 7.483333333333 51.983333333333 # DE_DHDN_Lat-Lon +expect 7.482494584516 51.981965147975 # ETRS89_Lat-Lon +accept 7.516666666667 51.016666666667 # DE_DHDN_Lat-Lon +expect 7.515823996992 51.015402184493 # ETRS89_Lat-Lon +accept 7.466666666667 50.500000000000 # DE_DHDN_Lat-Lon +expect 7.465834308888 50.498791390585 # ETRS89_Lat-Lon +accept 7.533333333333 49.333333333333 # DE_DHDN_Lat-Lon +expect 7.532503616986 49.332250779407 # ETRS89_Lat-Lon +accept 7.250000000000 49.333333333333 # DE_DHDN_Lat-Lon +expect 7.249209260581 49.332249456364 # ETRS89_Lat-Lon +accept 7.533333333333 47.666666666667 # DE_DHDN_Lat-Lon +expect 7.532530252396 47.665765608135 # ETRS89_Lat-Lon +------------------------------------------------------------------------------- + + + + + + + + + + + +------------------------------------------------------------------------------- +The numerical material in this file is based on the contents of the +BKG test data file over at http://crs.bkg.bund.de/crseu/crs/descrtrans/BeTA/BETA2007testdaten.csv + +The conversion was carried out as follows: + +set insertkey=gawk 'BEGIN {FS=","}; {print $3","$0} +set reformat=gawk 'BEGIN {FS=","}; {print "accept " $6 " " $5 " # " $4 "\nexpect " $9 " " $8 " # " $7}' +cat BETA2007testdaten.csv | %insertkey% | sort | %reformat% >DHDN_ETRS89.gie +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +Tests for GK system zones to UTM32/33 not implemented yet +------------------------------------------------------------------------------- +accept 2598417.333192 5930677.980308 # DE_DHDN_3GK2 +expect 399340.601863 5928794.177992 # ETRS89_UTM32 +accept 2643120.946052 5551463.861308 # DE_DHDN_3GK2 +expect 428391.209209 5548246.766868 # ETRS89_UTM32 +accept 2678509.791823 5890320.494547 # DE_DHDN_3GK2 +expect 477621.722498 5885134.566909 # ETRS89_UTM32 +accept 2661073.960381 6067930.993896 # DE_DHDN_3GK2 +expect 467726.896146 6063191.974102 # ETRS89_UTM32 +accept 2601895.024514 5761935.671777 # DE_DHDN_3GK2 +expect 395783.496871 5760119.715259 # ETRS89_UTM32 +accept 2606412.760026 5654454.411797 # DE_DHDN_3GK2 +expect 395892.865206 5652585.895428 # ETRS89_UTM32 +accept 2604044.332230 5596917.811668 # DE_DHDN_3GK2 +expect 391195.030128 5595215.127880 # ETRS89_UTM32 +accept 2611430.565041 5467270.623504 # DE_DHDN_3GK2 +expect 393381.121595 5465427.351346 # ETRS89_UTM32 +accept 2590840.678885 5466891.206854 # DE_DHDN_3GK2 +expect 372799.647928 5465865.755414 # ETRS89_UTM32 +accept 2615145.447136 5281966.148083 # DE_DHDN_3GK2 +expect 389829.267589 5280195.601333 # ETRS89_UTM32 +accept 3399371.190396 5930724.531323 # DE_DHDN_3GK3 +expect 399340.601862 5928794.177992 # ETRS89_UTM32 +accept 3597874.421966 5411397.512092 # DE_DHDN_3GK3 +expect 597759.898637 5409672.239612 # ETRS89_UTM32 +accept 3428437.612810 5550026.645035 # DE_DHDN_3GK3 +expect 428391.209209 5548246.766869 # ETRS89_UTM32 +accept 3571307.006323 5655705.338031 # DE_DHDN_3GK3 +expect 571204.563344 5653882.476948 # ETRS89_UTM32 +accept 3595392.782000 6023387.959898 # DE_DHDN_3GK3 +expect 595286.044398 6021417.376973 # ETRS89_UTM32 +accept 3615881.001454 5940351.727710 # DE_DHDN_3GK3 +expect 615764.364007 5938413.819150 # ETRS89_UTM32 +accept 3615881.001454 5940351.727710 # DE_DHDN_3GK3 +expect 218617.111391 5945399.220269 # ETRS89_UTM33 +accept 3567455.742115 5930134.904864 # DE_DHDN_3GK3 +expect 567358.390548 5928201.976543 # ETRS89_UTM32 +accept 3632798.076882 5927807.051283 # DE_DHDN_3GK3 +expect 632674.379672 5925873.747901 # ETRS89_UTM32 +accept 3632798.076882 5927807.051283 # DE_DHDN_3GK3 +expect 234423.486615 5931470.592457 # ETRS89_UTM33 +accept 3633848.721200 5849896.198513 # DE_DHDN_3GK3 +expect 633723.734075 5847994.536970 # ETRS89_UTM32 +accept 3633848.721200 5849896.198513 # DE_DHDN_3GK3 +expect 228947.171966 5853725.067987 # ETRS89_UTM33 +accept 3599586.686397 5819391.659845 # DE_DHDN_3GK3 +expect 599474.934168 5817502.626999 # ETRS89_UTM32 +accept 3607695.214682 5704557.217497 # DE_DHDN_3GK3 +expect 607578.857121 5702714.405562 # ETRS89_UTM32 +accept 3607695.214682 5704557.217497 # DE_DHDN_3GK3 +expect 190859.292094 5710978.842070 # ETRS89_UTM33 +accept 3602680.921862 5606162.921133 # DE_DHDN_3GK3 +expect 602565.455313 5604359.618990 # ETRS89_UTM32 +accept 3602680.921862 5606162.921133 # DE_DHDN_3GK3 +expect 177845.139712 5613251.897383 # ETRS89_UTM33 +accept 3602255.364740 5504172.212483 # DE_DHDN_3GK3 +expect 602139.527314 5502409.680191 # ETRS89_UTM32 +accept 3602255.364740 5504172.212483 # DE_DHDN_3GK3 +expect 169220.450101 5511545.700292 # ETRS89_UTM33 +accept 3616211.566778 5291255.078896 # DE_DHDN_3GK3 +expect 616089.408439 5289578.131826 # ETRS89_UTM32 +accept 3616211.566778 5291255.078896 # DE_DHDN_3GK3 +expect 166384.067958 5298018.237122 # ETRS89_UTM33 +accept 3477684.063162 5887048.676718 # DE_DHDN_3GK3 +expect 477621.722499 5885134.566914 # ETRS89_UTM32 +accept 3467781.947036 6065176.417740 # DE_DHDN_3GK3 +expect 467726.896147 6063191.974105 # ETRS89_UTM32 +accept 3395815.326925 5761982.907482 # DE_DHDN_3GK3 +expect 395783.496872 5760119.715259 # ETRS89_UTM32 +accept 3395925.872234 5654406.808724 # DE_DHDN_3GK3 +expect 395892.865206 5652585.895428 # ETRS89_UTM32 +accept 3391226.589718 5597013.366086 # DE_DHDN_3GK3 +expect 391195.030128 5595215.127881 # ETRS89_UTM32 +accept 3393414.080125 5467174.397245 # DE_DHDN_3GK3 +expect 393381.121595 5465427.351346 # ETRS89_UTM32 +accept 3372824.499428 5467612.907301 # DE_DHDN_3GK3 +expect 372799.647928 5465865.755413 # ETRS89_UTM32 +accept 3389860.774004 5281869.239226 # DE_DHDN_3GK3 +expect 389829.267590 5280195.601333 # ETRS89_UTM32 +accept 4377657.794741 5411879.839992 # DE_DHDN_3GK4 +expect 597759.898636 5409672.239612 # ETRS89_UTM32 +accept 4360897.154310 5657085.679344 # DE_DHDN_3GK4 +expect 571204.563343 5653882.476947 # ETRS89_UTM32 +accept 4400271.505998 6023480.198072 # DE_DHDN_3GK4 +expect 595286.044399 6021417.376972 # ETRS89_UTM32 +accept 4417225.999425 5939654.081375 # DE_DHDN_3GK4 +expect 615764.364007 5938413.819151 # ETRS89_UTM32 +accept 4417225.999425 5939654.081375 # DE_DHDN_3GK4 +expect 218617.111391 5945399.220269 # ETRS89_UTM33 +accept 4368411.664264 5931484.902370 # DE_DHDN_3GK4 +expect 567358.390548 5928201.976543 # ETRS89_UTM32 +accept 4433598.021986 5926410.006980 # DE_DHDN_3GK4 +expect 632674.379671 5925873.747901 # ETRS89_UTM32 +accept 4433598.021986 5926410.006980 # DE_DHDN_3GK4 +expect 234423.486614 5931470.592457 # ETRS89_UTM33 +accept 4596699.814954 5960328.296681 # DE_DHDN_3GK4 +expect 794226.051532 5966642.993890 # ETRS89_UTM32 +accept 4596699.814954 5960328.296681 # DE_DHDN_3GK4 +expect 398811.452821 5958481.617326 # ETRS89_UTM33 +accept 4431385.771953 5848536.122437 # DE_DHDN_3GK4 +expect 633723.734074 5847994.536971 # ETRS89_UTM32 +accept 4431385.771953 5848536.122437 # DE_DHDN_3GK4 +expect 228947.171966 5853725.067987 # ETRS89_UTM33 +accept 4568999.833703 5739119.060681 # DE_DHDN_3GK4 +expect 775766.817929 5744357.999264 # ETRS89_UTM32 +accept 4568999.833703 5739119.060681 # DE_DHDN_3GK4 +expect 361924.813552 5738688.111797 # ETRS89_UTM33 +accept 4395886.918912 5819485.694352 # DE_DHDN_3GK4 +expect 599474.934169 5817502.626999 # ETRS89_UTM32 +accept 4399252.521454 5704414.901133 # DE_DHDN_3GK4 +expect 607578.857121 5702714.405563 # ETRS89_UTM32 +accept 4399252.521454 5704414.901133 # DE_DHDN_3GK4 +expect 190859.292094 5710978.842070 # ETRS89_UTM33 +accept 4390237.957560 5606306.171667 # DE_DHDN_3GK4 +expect 602565.455313 5604359.618990 # ETRS89_UTM32 +accept 4390237.957560 5606306.171667 # DE_DHDN_3GK4 +expect 177845.139712 5613251.897384 # ETRS89_UTM33 +accept 4385715.060070 5504412.338975 # DE_DHDN_3GK4 +expect 602139.527314 5502409.680191 # ETRS89_UTM32 +accept 4385715.060070 5504412.338975 # DE_DHDN_3GK4 +expect 169220.450101 5511545.700292 # ETRS89_UTM33 +accept 4391285.796869 5291109.755123 # DE_DHDN_3GK4 +expect 616089.408439 5289578.131827 # ETRS89_UTM32 +accept 4391285.796869 5291109.755123 # DE_DHDN_3GK4 +expect 166384.067958 5298018.237122 # ETRS89_UTM33 +accept 4602499.566145 5615431.379860 # DE_DHDN_3GK4 +expect 814311.364242 5622071.326313 # ETRS89_UTM32 +accept 4602499.566145 5615431.379860 # DE_DHDN_3GK4 +expect 390338.211462 5613774.353256 # ETRS89_UTM33 +accept 4608008.855658 5689725.987089 # DE_DHDN_3GK4 +expect 816793.461724 5696579.298817 # ETRS89_UTM32 +accept 4608008.855658 5689725.987089 # DE_DHDN_3GK4 +expect 398863.493307 5687753.129020 # ETRS89_UTM33 +accept 4607459.254388 5769472.054323 # DE_DHDN_3GK4 +expect 812962.846098 5776288.882564 # ETRS89_UTM32 +accept 4607459.254388 5769472.054323 # DE_DHDN_3GK4 +expect 401589.388273 5767420.751372 # ETRS89_UTM33 +accept 4595844.509596 5893520.178529 # DE_DHDN_3GK4 +expect 796184.889876 5899821.806119 # ETRS89_UTM32 +accept 4595844.509596 5893520.178529 # DE_DHDN_3GK4 +expect 395147.893839 5891795.036022 # ETRS89_UTM33 +accept 4599624.347102 5817537.418158 # DE_DHDN_3GK4 +expect 803137.012417 5824018.671556 # ETRS89_UTM32 +accept 4599624.347102 5817537.418158 # DE_DHDN_3GK4 +expect 395754.092849 5815749.835902 # ETRS89_UTM33 +accept 4582806.457775 5437104.667215 # DE_DHDN_3GK4 +expect 801769.133341 5442981.626260 # ETRS89_UTM32 +accept 4582806.457775 5437104.667215 # DE_DHDN_3GK4 +expect 363531.446507 5436436.282581 # ETRS89_UTM33 +accept 4571363.304563 5279411.440581 # DE_DHDN_3GK4 +expect 796505.582915 5284862.664428 # ETRS89_UTM32 +accept 4571363.304563 5279411.440581 # DE_DHDN_3GK4 +expect 345930.907036 5279345.459526 # ETRS89_UTM33 +accept 5398905.047545 5960421.130827 # DE_DHDN_3GK5 +expect 794226.051532 5966642.993889 # ETRS89_UTM32 +accept 5398905.047545 5960421.130827 # DE_DHDN_3GK5 +expect 398811.452821 5958481.617326 # ETRS89_UTM33 +accept 5362005.247500 5740538.568445 # DE_DHDN_3GK5 +expect 775766.817929 5744357.999262 # ETRS89_UTM32 +accept 5362005.247500 5740538.568445 # DE_DHDN_3GK5 +expect 361924.813551 5738688.111796 # ETRS89_UTM33 +accept 5390431.824773 5615574.548074 # DE_DHDN_3GK5 +expect 814311.364241 5622071.326313 # ETRS89_UTM32 +accept 5390431.824773 5615574.548074 # DE_DHDN_3GK5 +expect 390338.211462 5613774.353256 # ETRS89_UTM33 +accept 5398959.121385 5689583.521018 # DE_DHDN_3GK5 +expect 816793.461724 5696579.298817 # ETRS89_UTM32 +accept 5398959.121385 5689583.521018 # DE_DHDN_3GK5 +expect 398863.493307 5687753.129020 # ETRS89_UTM33 +accept 5401685.729154 5769283.220752 # DE_DHDN_3GK5 +expect 812962.846098 5776288.882564 # ETRS89_UTM32 +accept 5401685.729154 5769283.220752 # DE_DHDN_3GK5 +expect 401589.388272 5767420.751372 # ETRS89_UTM33 +accept 5395240.318989 5893707.029636 # DE_DHDN_3GK5 +expect 796184.889876 5899821.806119 # ETRS89_UTM32 +accept 5395240.318989 5893707.029636 # DE_DHDN_3GK5 +expect 395147.893840 5891795.036022 # ETRS89_UTM33 +accept 5395847.545864 5817631.467237 # DE_DHDN_3GK5 +expect 803137.012417 5824018.671556 # ETRS89_UTM32 +accept 5395847.545864 5817631.467237 # DE_DHDN_3GK5 +expect 395754.092849 5815749.835902 # ETRS89_UTM33 +accept 5363615.032963 5438164.610427 # DE_DHDN_3GK5 +expect 801769.133341 5442981.626260 # ETRS89_UTM32 +accept 5363615.032963 5438164.610427 # DE_DHDN_3GK5 +expect 363531.446506 5436436.282581 # ETRS89_UTM33 +accept 5346007.854521 5281010.564511 # DE_DHDN_3GK5 +expect 796505.582915 5284862.664427 # ETRS89_UTM32 +accept 5346007.854521 5281010.564511 # DE_DHDN_3GK5 +expect 345930.907036 5279345.459525 # ETRS89_UTM33 +------------------------------------------------------------------------------- diff --git a/test/ProjNet.Tests/Fixtures/gie/GDA.gie b/test/ProjNet.Tests/Fixtures/gie/GDA.gie new file mode 100644 index 00000000..a21b37e4 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/GDA.gie @@ -0,0 +1,77 @@ +----------------------------------------------------------------------------------- +Australian datum transformations +----------------------------------------------------------------------------------- +Based on material from: + +Intergovernmental Committee on Surveying and Mapping (ICSM) +Permanent Committee on Geodesy (PCG): + +Geocentric Datum of Australia 2020 Technical Manual +Version 1.0, 25 July 2017 + +Which is distributed under Creative Commons CC-BY 4.0 + +These tests will probably be useful as a template for an AU setup file, defining +transformations for Australian systems, but I'm reluctant to provide such a file +myself - it probably should come from official AU sources. + +Thomas Knudsen, thokn@sdfe.dk, 2017-11-27 +----------------------------------------------------------------------------------- + + + +----------------------------------------------------------------------------------- +# GDA94 to GDA2020 +----------------------------------------------------------------------------------- +# Just the Helmert transformation, to verify that we are within 100 um +----------------------------------------------------------------------------------- +operation proj=helmert \ + convention=coordinate_frame \ + x = 0.06155 rx = -0.0394924 \ + y = -0.01087 ry = -0.0327221 \ + z = -0.04019 rz = -0.0328979 s = -0.009994 + +----------------------------------------------------------------------------------- +tolerance 75 um +accept -4052051.7643 4212836.2017 -2545106.0245 +expect -4052052.7379 4212835.9897 -2545104.5898 +------------------------------------------------------------------------------- + + +----------------------------------------------------------------------------------- +# GDA94 to GDA2020 +----------------------------------------------------------------------------------- +# All the way from geographic-to-cartesian-and-back-to-geographic +----------------------------------------------------------------------------------- +operation proj = pipeline ellps=GRS80; \ + step proj = cart; \ + step proj = helmert \ + convention=coordinate_frame \ + x = 0.06155; rx = -0.0394924; \ + y = -0.01087; ry = -0.0327221; \ + z = -0.04019; rz = -0.0328979; s = -0.009994; \ + step proj = cart inv; +----------------------------------------------------------------------------------- +tolerance 2 mm +accept 133.88551329 -23.67012389 603.3466 0 # Alice Springs GDA94 +expect 133.8855216 -23.67011014 603.2489 0 # Alice Springs GDA2020 +------------------------------------------------------------------------------- + + +----------------------------------------------------------------------------------- +# ITRF2014@2018 to GDA2020 - Test point ALIC (Alice Springs) +----------------------------------------------------------------------------------- +# Just the Helmert transformation, to verify that we are within 100 um +----------------------------------------------------------------------------------- +operation proj = helmert exact convention=coordinate_frame \ + x = 0 rx = 0 dx = 0 drx = 0.00150379 \ + y = 0 ry = 0 dy = 0 dry = 0.00118346 \ + z = 0 rz = 0 dz = 0 drz = 0.00120716 \ + s = 0 ds = 0 t_epoch=2020.0 +----------------------------------------------------------------------------------- +tolerance 40 um +accept -4052052.6588 4212835.9938 -2545104.6946 2018.0 # ITRF2014@2018.0 +expect -4052052.7373 4212835.9835 -2545104.5867 # GDA2020 +----------------------------------------------------------------------------------- + + diff --git a/test/ProjNet.Tests/Fixtures/gie/adams_hemi.gie b/test/ProjNet.Tests/Fixtures/gie/adams_hemi.gie new file mode 100644 index 00000000..9d23b939 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/adams_hemi.gie @@ -0,0 +1,2120 @@ + +------------------------------------------------------------ +# This gie file was automatically generated using libproject +#where the adams_hemi code was adapted from +------------------------------------------------------------ + +------------------------------------------------------------ +operation +proj=adams_hemi +R=6370997 +tolerance 1 mm +------------------------------------------------------------ +accept -179.0512914938 -90.1445918836 +expect failure errno coord_transfm_invalid_coord + +accept -169.5842217825 -89.1738195765 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.8126151474 -88.9303357409 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.8486678837 -88.7598088570 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.3978823413 -88.5424937255 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.4584139907 -88.3017941113 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.6382190434 -88.1579367749 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.1571755829 -87.4911657719 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.4449870732 -87.1707570236 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.9433443609 -87.0825895518 +expect -2032451.307 -14670658.595 + +accept -79.1196204797 -86.6146886067 +expect -1972952.703 -14316663.749 + +accept -69.9188804603 -86.0423885378 +expect -1919210.763 -13959619.296 + +accept -59.2140342623 -85.2062438921 +expect -1821078.174 -13499207.260 + +accept -49.3771402997 -84.6064875075 +expect -1632786.696 -13151268.681 + +accept -39.7523563839 -84.2227887345 +expect -1375189.719 -12898281.230 + +accept -29.1378956446 -84.1916000473 +expect -1019955.835 -12777203.156 + +accept -19.8973002908 -83.3255313811 +expect -750646.386 -12420097.177 + +accept -9.0586917767 -82.5740757930 +expect -361764.270 -12130784.486 + +accept 0.6658150243 -81.6306426596 +expect 28245.397 -11833545.201 + +accept 10.3916472934 -81.3826632020 +expect 446703.237 -11781755.067 + +accept 20.1494345130 -81.0829408522 +expect 877880.087 -11752832.089 + +accept 30.9020031853 -80.1029516039 +expect 1408756.445 -11595274.577 + +accept 40.6784961632 -79.5385879087 +expect 1890606.237 -11591903.266 + +accept 50.0047749416 -79.2557693956 +expect 2331531.206 -11694280.098 + +accept 60.6444819104 -78.7437592966 +expect 2853286.437 -11817317.500 + +accept 70.0847779691 -77.7507873749 +expect 3388704.151 -11865524.800 + +accept 80.5231675121 -77.0660588617 +expect 3923735.969 -12068158.388 + +accept 90.0066580543 -76.6161050638 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.5350349572 -76.3746207928 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.7544963160 -76.2761103137 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.0734970456 -75.8866598636 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.5824490023 -75.8761529489 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.8041765005 -75.3058724059 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.3183273801 -74.4580538960 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.0892519416 -73.7178034782 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.3366715442 -72.7342346131 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.6302993811 -72.6551561090 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.1498353863 -79.8617775679 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.6126454375 -78.9973036997 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.5376706591 -78.3099466224 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.7206409942 -77.9762123582 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.5308439004 -77.1568269429 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.5655598613 -76.3111768828 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.7726048543 -75.3290637518 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.6962230001 -74.3833782562 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.6722260401 -73.9288741111 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.0043085338 -73.3335214021 +expect -4844180.674 -11775270.656 + +accept -79.1663300454 -72.4399400853 +expect -4517737.562 -11230872.680 + +accept -69.4926078623 -71.8461023317 +expect -4102914.567 -10768468.225 + +accept -59.2657055315 -71.2092093538 +expect -3613458.773 -10316718.559 + +accept -49.8213060755 -70.4770080858 +expect -3129758.562 -9913931.998 + +accept -39.4219744153 -70.0556232602 +expect -2525390.621 -9588638.786 + +accept -29.2292837281 -69.7843638102 +expect -1896816.371 -9350212.714 + +accept -19.3122799108 -68.8398158747 +expect -1286419.314 -9045554.926 + +accept -9.6128015323 -68.4874678083 +expect -646826.577 -8904263.293 + +accept 0.2782436804 -67.7652217315 +expect 19032.119 -8747432.621 + +accept 10.0150948909 -66.8884770151 +expect 697224.743 -8618447.314 + +accept 20.3145279457 -66.3089232102 +expect 1427884.878 -8604766.655 + +accept 30.1158431277 -65.8422273022 +expect 2129234.668 -8665589.397 + +accept 40.3813237731 -65.2868591410 +expect 2871280.128 -8782328.275 + +accept 50.3256474182 -64.5263189423 +expect 3606565.288 -8925940.412 + +accept 60.0397335630 -64.2204936386 +expect 4289216.964 -9202136.643 + +accept 70.8347119838 -64.2006837236 +expect 4994689.905 -9631301.887 + +accept 80.1994904845 -64.1621304539 +expect 5574892.739 -10059995.184 + +accept 90.4107676644 -63.6583206132 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.7435829266 -62.9292855324 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.6722999581 -62.8950940661 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.8104324458 -62.4946649855 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.0315355771 -61.8657176607 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.7842333846 -61.7278379595 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.5202047210 -61.0467237730 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.7596965102 -60.2885251988 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.1658638654 -60.0997063537 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.2502374139 -59.7318603713 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.9256771826 -69.2161805164 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.2003519382 -68.5724302106 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.5787597594 -67.9857475830 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.8429607974 -67.2794853256 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.5406690392 -67.1131557084 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.7848559822 -66.2388824806 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.1305416029 -65.8739932573 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.5840686978 -65.1388713461 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.0639263051 -65.0873010186 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.1823111373 -64.6956687932 +expect -6023804.986 -10592902.952 + +accept -79.1164233302 -64.2716519504 +expect -5497921.434 -10022147.140 + +accept -69.2574458208 -63.9075720894 +expect -4922828.801 -9520562.405 + +accept -59.8783718706 -63.9038815291 +expect -4304901.029 -9147659.032 + +accept -49.7289524443 -63.8500661196 +expect -3612059.924 -8800325.048 + +accept -39.1403094008 -63.8422040109 +expect -2862966.181 -8516003.259 + +accept -29.1777068994 -63.1667304050 +expect -2169833.127 -8203288.513 + +accept -19.2977455865 -63.0553679417 +expect -1441725.328 -8043555.407 + +accept -9.9255569976 -62.9647601040 +expect -743731.598 -7948394.774 + +accept 0.1796078505 -62.5085487744 +expect 13569.082 -7844710.542 + +accept 10.1904676971 -61.9758669545 +expect 776353.440 -7788440.095 + +accept 20.6226352769 -61.7448218808 +expect 1574820.972 -7846337.019 + +accept 30.2475627489 -61.1393337334 +expect 2327457.261 -7896980.364 + +accept 40.5688849715 -60.3721116419 +expect 3149169.433 -8001812.738 + +accept 50.3372781690 -60.3371608659 +expect 3889524.441 -8275997.858 + +accept 60.0304323790 -60.0341705989 +expect 4627914.366 -8575277.072 + +accept 70.8357778632 -59.4751248220 +expect 5450054.504 -8962420.257 + +accept 80.4614530617 -58.9769415123 +expect 6157836.688 -9386832.221 + +accept 90.1145386062 -58.4070158299 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.7207579758 -58.0174867644 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.5210303471 -57.2190055708 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.6967482820 -56.4709263993 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.0815389060 -55.4932687347 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.1058922487 -54.7233777967 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.7972249220 -54.0392541094 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.4001197642 -53.4894321537 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.7522870277 -52.8073510172 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.9552557119 -51.9628267761 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.8457680307 -59.7627027888 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.9866964666 -59.3961476674 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.7470584020 -58.4027994525 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.6934351691 -57.7899236032 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.8251656458 -57.1675573730 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.7548669173 -56.7627741202 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.8194582121 -56.1851853006 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.9910713909 -55.2204505294 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.5100607596 -54.5311736441 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.8644038896 -53.9245170355 +expect -7342928.224 -9343992.584 + +accept -79.9635057664 -53.6948723497 +expect -6667476.192 -8689307.261 + +accept -69.9915897328 -53.5080881233 +expect -5920106.317 -8113696.635 + +accept -59.9878237924 -53.1347486229 +expect -5137251.168 -7595504.352 + +accept -49.6910260363 -52.6201247567 +expect -4300887.397 -7131351.272 + +accept -39.7880824067 -52.5359539692 +expect -3452174.116 -6823690.285 + +accept -29.2778055547 -51.6296886545 +expect -2568030.082 -6461876.918 + +accept -19.4306837146 -51.1069811525 +expect -1713350.228 -6239079.500 + +accept -9.3856113762 -50.6351985324 +expect -831338.016 -6085131.749 + +accept 0.8939490530 -50.4873507691 +expect 79288.786 -6038354.630 + +accept 10.1840000169 -50.4621600892 +expect 903758.620 -6065670.749 + +accept 20.0863846360 -50.3906053109 +expect 1785191.886 -6146622.254 + +accept 30.3281308173 -49.6297449138 +expect 2720752.037 -6201183.856 + +accept 40.5225439155 -49.1883193839 +expect 3657308.119 -6373152.042 + +accept 50.0774176880 -48.8514336226 +expect 4539427.520 -6619769.484 + +accept 60.4611757563 -48.8337125195 +expect 5475204.679 -7027995.280 + +accept 70.0359400468 -48.6327927027 +expect 6332662.310 -7473372.039 + +accept 80.9191136756 -48.5703968561 +expect 7251379.374 -8116860.291 + +accept 90.3784337844 -48.2622816670 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.0644440138 -47.5531771971 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.4395693884 -47.1020565364 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.8787420308 -46.5648775386 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.8192016133 -45.7254461234 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.6056889736 -45.1737240013 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.1175388463 -44.6690953878 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.6179845473 -44.6641530409 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.3909883495 -43.8609446159 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.3536945715 -43.4346766235 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.6728016921 -49.7927741248 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8899091213 -49.5948095572 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.7193834386 -48.9692766397 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3161804706 -48.3900540417 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.3059860307 -47.5443099911 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.2607005772 -47.3426945690 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.0464442962 -46.3637609990 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.3006137776 -46.2470536112 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.1086587357 -45.3788313212 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.6418391342 -44.9558227898 +expect -8329227.919 -8319528.025 + +accept -79.5922401709 -44.0666175390 +expect -7575608.504 -7475396.243 + +accept -69.7678517742 -43.8414369161 +expect -6693103.012 -6832178.393 + +accept -59.9773741961 -43.6178971244 +expect -5769594.561 -6303851.937 + +accept -49.0224540140 -42.7382586843 +expect -4742402.857 -5752204.163 + +accept -39.0799820528 -42.4267915714 +expect -3774284.368 -5417401.690 + +accept -29.7065845030 -42.3050919341 +expect -2859905.278 -5199352.562 + +accept -19.4093864480 -41.7132409855 +expect -1870549.029 -4971910.558 + +accept -9.5019946065 -41.0671902488 +expect -918157.515 -4807120.737 + +accept 0.7256633705 -40.8863960679 +expect 70159.362 -4759033.602 + +accept 10.1160300689 -40.8493938089 +expect 979223.286 -4782540.672 + +accept 20.7134315844 -40.8364519203 +expect 2011034.581 -4873376.186 + +accept 30.5509837736 -40.3124354704 +expect 2991370.345 -4953502.965 + +accept 40.6811168119 -39.4330828245 +expect 4036041.984 -5061633.604 + +accept 50.5408477188 -39.0992426534 +expect 5062748.637 -5317244.256 + +accept 60.8602526703 -38.6119802236 +expect 6165904.838 -5673423.296 + +accept 70.8028881731 -37.7864477876 +expect 7266962.017 -6097356.033 + +accept 80.6995146991 -37.6252342058 +expect 8295075.544 -6755019.382 + +accept 90.0888221682 -36.9847575823 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.7780137471 -36.6711774624 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.5677826175 -36.0072187827 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.4571639125 -35.5987514678 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.4994689409 -35.0078880309 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.4973246226 -34.7603698284 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.2185553402 -33.7827115000 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.0049830768 -33.6576606654 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.1113538332 -32.7218296414 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.0413323536 -32.6458132046 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.7614880705 -39.3083971924 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.2195161001 -38.8120777840 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.8162149104 -38.0579151517 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.1975646236 -38.0219346503 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.4875552064 -37.4333640809 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.3174577073 -37.3101077795 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.3659376731 -37.0018039210 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.4951561921 -36.7266176768 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.0146344791 -36.1766584469 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.4535006021 -35.4158065520 +expect -9377248.574 -7225961.706 + +accept -79.0387416065 -34.4648659946 +expect -8422693.344 -6224371.305 + +accept -69.2215350809 -34.3740808096 +expect -7350614.857 -5544861.367 + +accept -59.2745531980 -33.8274972528 +expect -6265513.674 -4955207.335 + +accept -49.7169986740 -32.9249999485 +expect -5234261.572 -4467029.348 + +accept -39.3938152675 -32.5464412753 +expect -4109883.252 -4132559.187 + +accept -29.3299550856 -32.1522584471 +expect -3038342.418 -3890348.809 + +accept -19.7672186473 -31.5562360216 +expect -2040849.804 -3696547.750 + +accept -9.6757428807 -31.5381986939 +expect -994652.380 -3622557.798 + +accept 0.9267949652 -30.8907292201 +expect 95467.453 -3522841.136 + +accept 10.2711979528 -30.1415999097 +expect 1063857.348 -3456961.986 + +accept 20.3037759043 -30.0366136211 +expect 2114356.936 -3516056.014 + +accept 30.7604872284 -29.7526562583 +expect 3235403.228 -3612623.593 + +accept 40.6115143350 -28.8411062530 +expect 4342408.974 -3683312.580 + +accept 50.4426160267 -28.7604876417 +expect 5473507.294 -3935831.832 + +accept 60.0667408454 -28.0150409892 +expect 6658598.640 -4193291.049 + +accept 70.8986641601 -27.4846012044 +expect 8045113.983 -4691740.858 + +accept 80.7902728211 -26.9566814760 +expect 9340735.895 -5359874.908 + +accept 90.7774660604 -26.3781900961 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.9677349413 -25.6947295847 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.4045415377 -24.8858012124 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.9106581244 -24.6760450197 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.1070741983 -24.3713625355 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.5983717849 -23.4696540641 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.9695044815 -23.2859623288 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.2904693231 -22.4177486612 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.9786886104 -22.3889418243 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.4135817841 -21.4141834167 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.1529257481 -29.4728615966 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.2570543721 -28.7440325834 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.4124846191 -27.8743740104 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.9984258738 -27.2290510016 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.4767743694 -27.0546870326 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.5641039139 -26.6837015309 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.3705764793 -25.8611684419 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.5378587803 -25.4624789903 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.3734787689 -24.9304945684 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.3465946697 -24.1700440971 +expect -10704395.428 -5847361.236 + +accept -79.7404033820 -23.7748908886 +expect -9513574.581 -4810463.511 + +accept -69.8876497415 -23.5742092130 +expect -8180418.594 -4058430.736 + +accept -59.3891167783 -23.0862162961 +expect -6800548.565 -3472782.360 + +accept -49.0962705404 -23.0040787983 +expect -5498406.531 -3127392.477 + +accept -39.5604939819 -22.8514526819 +expect -4357597.539 -2896084.387 + +accept -29.6190870010 -22.2716044223 +expect -3224919.475 -2674683.087 + +accept -19.2436583074 -22.1221079553 +expect -2074925.707 -2558243.836 + +accept -9.3541697716 -21.2322237727 +expect -1006009.651 -2403546.370 + +accept 0.3565245677 -20.3671378180 +expect 38384.574 -2289133.435 + +accept 10.4893794068 -19.6098192847 +expect 1134672.480 -2219784.886 + +accept 20.1245536415 -19.4921243712 +expect 2191535.285 -2254510.891 + +accept 30.7062162966 -19.3927174348 +expect 3384477.986 -2336722.926 + +accept 40.8666533485 -18.6123732185 +expect 4592279.592 -2378596.017 + +accept 50.0951841039 -17.9250502345 +expect 5759194.930 -2468249.825 + +accept 60.3777794968 -17.5681367463 +expect 7155127.802 -2710926.594 + +accept 70.7020153713 -16.8114869486 +expect 8722490.928 -3052901.356 + +accept 80.8671865869 -16.2424776439 +expect 10422654.067 -3712280.432 + +accept 90.2461291404 -15.5645924718 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.9711143384 -14.9057684714 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.5061775944 -13.9999376124 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.6646869394 -13.8762395278 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.4786384854 -13.6641917166 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.4992250881 -12.7990197875 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.0366235813 -12.4696803942 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.5278707110 -12.2720940753 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.3484529579 -12.2138862792 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.2280212650 -11.3213090122 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.9887698836 -19.8434403334 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.6164294546 -19.0221825561 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.5308654294 -18.1879041212 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.8050966059 -17.4019446265 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.1612469827 -16.6625595025 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.4619391187 -16.4594418864 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.8383311004 -16.1675065524 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.3335459376 -15.6868347454 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.7976668059 -15.6854972046 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.7267294244 -14.8632370454 +expect -12053401.390 -4568307.176 + +accept -79.1896256706 -14.3035788003 +expect -10318651.920 -3211294.118 + +accept -69.8238721868 -13.6606252292 +expect -8752793.203 -2483025.217 + +accept -59.9341884732 -13.0163995934 +expect -7239853.279 -2016848.066 + +accept -49.2718060884 -12.0872143872 +expect -5769460.920 -1657984.518 + +accept -39.0612457278 -11.2146109871 +expect -4471191.256 -1415302.828 + +accept -29.7861799219 -10.9508322090 +expect -3354764.734 -1309138.609 + +accept -19.4145569922 -10.5133139512 +expect -2160417.521 -1206521.486 + +accept -9.5198292959 -9.7964336368 +expect -1053132.786 -1099465.773 + +accept 0.8412200908 -8.9185740153 +expect 92973.787 -993763.563 + +accept 10.4291884083 -8.3013059591 +expect 1156683.321 -932334.794 + +accept 20.1434736553 -7.4540137611 +expect 2253230.418 -856389.581 + +accept 30.9337103769 -7.4330940402 +expect 3510477.410 -893006.725 + +accept 40.1244867612 -6.5706097227 +expect 4641586.907 -835473.434 + +accept 50.1188628580 -6.0246021551 +expect 5960644.364 -835727.421 + +accept 60.8442179646 -5.0882786722 +expect 7543977.493 -808597.717 + +accept 70.9106152137 -5.0040354490 +expect 9276405.717 -966062.038 + +accept 80.6138637845 -4.9732504580 +expect 11379290.973 -1327935.989 + +accept 90.6032354861 -4.1370495786 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.1644532030 -3.7428216159 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.6488410247 -3.6483998900 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.4200133401 -3.5679026480 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.4611248753 -2.8417810196 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.5117693002 -2.1994039429 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.8812291858 -1.4289441365 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.7607399916 -1.4071810906 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.8773135555 -1.0552787291 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.0261501820 -0.4477561568 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.6651296552 -9.8752237332 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8040567331 -9.5090347854 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.2369236977 -8.7812050100 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.4668694945 -8.7411685014 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.4921425087 -8.4838585832 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.0489643890 -8.3165131291 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.2267713387 -8.2419396541 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.8259504273 -7.2718622518 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.9614377002 -7.1037710855 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.8090238071 -6.4232987368 +expect -13640751.484 -2974873.944 + +accept -79.3641569088 -6.0589975081 +expect -11010026.860 -1514839.115 + +accept -69.4104381605 -5.3415079971 +expect -8989236.834 -994787.772 + +accept -59.1035311505 -4.7601449911 +expect -7276854.155 -737312.363 + +accept -49.7514136971 -4.1714583763 +expect -5923537.462 -576783.461 + +accept -39.2161406618 -3.9369832151 +expect -4539636.330 -497351.785 + +accept -29.7503799768 -2.9883217265 +expect -3383505.746 -356659.413 + +accept -19.3042659436 -2.0693367961 +expect -2166506.455 -236875.349 + +accept -9.8550183317 -1.1339666688 +expect -1098434.101 -127035.957 + +accept 0.3698643768 -0.7850645975 +expect 41125.238 -87297.441 + +accept 10.5309879356 -0.2442969095 +expect 1174302.709 -27396.310 + +accept 20.3483686169 0.5355680008 +expect 2286903.911 61502.678 + +accept 30.9157771281 1.5084105801 +expect 3524936.009 181088.864 + +accept 40.8345369145 1.9690445225 +expect 4750388.872 251710.802 + +accept 50.0083884196 2.0400972654 +expect 5968514.060 282935.766 + +accept 60.5564326069 2.5146830998 +expect 7522513.981 398583.466 + +accept 70.0242529688 2.6387476779 +expect 9150344.687 501095.492 + +accept 80.6476764259 3.6310845229 +expect 11462634.636 984489.812 + +accept 90.5849650699 4.1063917523 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.6238525596 4.1230600723 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.6594697851 5.0495381196 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.0930076215 5.2661796574 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.5000547754 5.3939702157 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.4751452989 5.8876297478 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.7452375246 6.3021488806 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.1359677924 6.9173640629 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.0302177328 7.6378853558 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.8170157951 8.3472735471 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.1943150364 0.7596010223 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.6391212860 1.0795717122 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.2152700578 1.5596096157 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3566080854 2.3935395760 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.6411690265 2.8792042895 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.0884027419 3.3736512451 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.6412398776 3.5609941446 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.8410023851 4.3613065976 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.5554211656 5.1657533479 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.3773565828 5.9157121060 +expect -13652078.995 2749284.275 + +accept -79.5182008966 5.9890761386 +expect -11050370.051 1507839.328 + +accept -69.5064826337 6.4698255786 +expect -8978882.758 1203806.785 + +accept -59.5169821631 7.3693412472 +expect -7304721.114 1145373.248 + +accept -49.9241519045 7.8049591034 +expect -5916373.828 1079808.719 + +accept -39.3530693426 7.8141712724 +expect -4536367.181 988115.206 + +accept -29.0573163195 8.4892787230 +expect -3282841.554 1010642.286 + +accept -19.3808304987 8.6780787506 +expect -2162785.190 995072.997 + +accept -9.7889836306 9.1980318083 +expect -1084005.841 1032425.841 + +accept 0.7401919089 9.6999375489 +expect 81716.177 1081216.951 + +accept 10.2922798343 10.2080075121 +expect 1138278.757 1147216.412 + +accept 20.8409423708 11.0731980134 +expect 2320143.899 1276786.428 + +accept 30.8315703906 11.4319423563 +expect 3474811.307 1373967.408 + +accept 40.8302365060 12.2114488494 +expect 4680441.778 1560453.893 + +accept 50.8938950488 12.4827231183 +expect 5980086.590 1739463.351 + +accept 60.1452074812 12.8049940379 +expect 7276948.599 1990687.541 + +accept 70.4026750861 13.0768132535 +expect 8877755.135 2411628.800 + +accept 80.2140199400 13.5624272700 +expect 10571534.837 3165721.762 + +accept 90.4576635954 13.9718076506 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.6449925924 14.8800104945 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.2075367326 15.0521133300 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.4913041985 15.0889224537 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.1088876084 15.6744717063 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.6091054692 15.9789322360 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.8827038395 16.1118935785 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.8584892232 16.9655055987 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.7081605352 17.1424915281 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.4875952646 17.7286920734 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.2192819040 10.8569245931 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.0657035958 11.2213297367 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.1659774291 11.5431930234 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.9545173238 12.5029285949 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.3117413904 12.6753804804 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.3194639426 13.3306225335 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.8562785026 13.8682936931 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.1061461002 14.0130001331 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.6079217864 14.7921669533 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.3223757109 14.9467935082 +expect -11977848.908 4521084.682 + +accept -79.5614292778 15.5269071903 +expect -10271311.258 3464674.481 + +accept -69.5919903841 16.3857419518 +expect -8573212.789 2923178.600 + +accept -59.6477980679 16.9839343080 +expect -7073184.422 2599846.459 + +accept -49.1834673639 17.2588986357 +expect -5657105.754 2357772.162 + +accept -39.8262428613 17.9830247197 +expect -4476942.024 2281920.882 + +accept -29.2381466616 18.7499060636 +expect -3223466.080 2243030.787 + +accept -19.1786678435 19.0226677902 +expect -2090004.567 2193284.527 + +accept -9.6334690404 19.4761386210 +expect -1042142.311 2201616.494 + +accept 0.6321060114 20.3870396192 +expect 68050.548 2291463.687 + +accept 10.9421661895 20.4924024395 +expect 1180518.031 2323367.738 + +accept 20.4864473342 21.0164763452 +expect 2220079.189 2436668.067 + +accept 30.1291797254 21.2284428591 +expect 3296035.229 2553633.126 + +accept 40.6200421317 21.5203653800 +expect 4508950.471 2745825.410 + +accept 50.6582942940 21.9446471796 +expect 5723200.728 3026932.927 + +accept 60.9730121398 22.4243870208 +expect 7038314.007 3442040.060 + +accept 70.2091499688 22.5967062068 +expect 8288776.818 3931269.871 + +accept 80.7948402736 23.3693033859 +expect 9696270.644 4844753.993 + +accept 90.9423588456 23.9305098127 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.5427479545 24.6568371578 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.6483359564 25.2988879331 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.6254581560 25.7897168715 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.6198305384 26.5579713234 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.5223099495 27.4665102983 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.1770507578 27.9150551786 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.8757513817 28.5236724280 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.8877826460 29.0924417375 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.6425321330 29.5764205068 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.9444701529 20.8756448666 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.6232517758 21.6942653703 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.5830169036 22.4130383475 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.4619550763 22.6339632787 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.7058201004 23.2553831478 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.7766004320 23.8189681109 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.3228272091 24.7032636217 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.5551540716 24.7745254301 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.9135718461 25.5385802763 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.1841069049 26.2083957447 +expect -10431143.612 6091127.422 + +accept -79.7910279462 26.6431680702 +expect -9245823.651 5229352.217 + +accept -69.8818215995 27.1405016091 +expect -7939848.940 4579107.633 + +accept -59.7043872074 27.3432782139 +expect -6646772.640 4084211.760 + +accept -49.0682511760 28.2506774891 +expect -5330443.447 3825676.830 + +accept -39.4506559191 29.1409725672 +expect -4204573.962 3696283.632 + +accept -29.9015556332 29.7574405692 +expect -3142385.818 3600000.101 + +accept -19.0693346875 30.4852506297 +expect -1979564.068 3559098.611 + +accept -9.6233626432 30.8903459176 +expect -992683.054 3544093.220 + +accept 0.8232394368 31.3744079922 +expect 84584.215 3580932.776 + +accept 10.4236001839 31.4267077934 +expect 1072418.207 3612634.442 + +accept 20.9980760459 31.8605558626 +expect 2165806.548 3746351.172 + +accept 30.8255462270 32.3520551790 +expect 3193447.531 3939445.335 + +accept 40.8324168382 32.9127578763 +expect 4256012.184 4213848.131 + +accept 50.0837143989 33.6950660037 +expect 5244572.185 4581526.452 + +accept 60.4197804189 34.0559290352 +expect 6381932.867 5038103.593 + +accept 70.4867056300 34.8824703453 +expect 7453835.177 5690136.852 + +accept 80.9991967787 34.8943272959 +expect 8589604.803 6432883.803 + +accept 90.5925677009 35.3510670805 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.0148954666 36.2711932097 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.4283207815 36.6017613088 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.6777960225 37.3666811945 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.2891249681 37.8530786142 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.8212942434 38.5395736955 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.7430926180 38.7113483726 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.9509577553 39.6821505727 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.3061167023 40.4192356875 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.9886935244 41.3233715669 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.3343647471 30.6447124879 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.9318029695 30.8088173910 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.9351347433 31.4030041187 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.1603401895 31.7370508292 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.8411358400 31.9883214220 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.6487283950 32.2540792459 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.3377596938 32.5474903700 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.4052121056 32.6499740509 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.9645719240 33.2634048792 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.3430796801 33.3255757200 +expect -9605176.890 6972552.471 + +accept -79.6121040164 33.9110503188 +expect -8536182.225 6196716.591 + +accept -69.4414539606 34.6980659140 +expect -7351641.813 5601824.874 + +accept -59.4871790876 35.1364758272 +expect -6218131.412 5141950.349 + +accept -49.8946468200 36.1100227223 +expect -5124886.495 4896415.322 + +accept -39.8692181364 36.8503178238 +expect -4035583.381 4702140.850 + +accept -29.1761644239 37.2204561349 +expect -2922531.225 4529762.914 + +accept -19.7409728105 37.3611622287 +expect -1965561.511 4418245.538 + +accept -9.2203420334 38.0828885726 +expect -910444.166 4426682.810 + +accept 0.6679908400 38.2706333082 +expect 65814.006 4427674.481 + +accept 10.6257647236 39.2684671305 +expect 1040789.082 4583898.094 + +accept 20.2366080925 40.1629979305 +expect 1974657.219 4780940.306 + +accept 30.5069203139 40.3074104493 +expect 2987108.844 4952040.049 + +accept 40.7821801102 41.0561850499 +expect 3990538.047 5279018.731 + +accept 50.9837826453 42.0495032526 +expect 4969682.427 5728168.352 + +accept 60.0140580662 42.5571623885 +expect 5839203.037 6163270.290 + +accept 70.7254208788 42.7772722042 +expect 6868275.710 6748247.633 + +accept 80.6903986951 43.0741360152 +expect 7768806.954 7429525.503 + +accept 90.6634321726 43.5837411887 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.8304968356 44.3478015738 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.2734219112 44.4218804789 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.7981801963 44.9674252843 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.6206535666 45.4286113875 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.3730391283 46.0714239353 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.9426457881 46.5091534230 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.6115790352 47.4101077572 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.3277640796 48.0638373590 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.8477425796 48.8442357472 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.2598149320 40.1705017627 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.6056153255 40.1727881259 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.3396275493 41.0355246248 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3430287138 41.0573932198 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.3972998819 41.0866734660 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.2347342883 41.3220478306 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.9384666450 41.9087201484 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.1765297705 42.5137977627 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.7425660165 43.1240737964 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.3631524858 43.6013437171 +expect -8456598.089 8145902.918 + +accept -79.1184854139 44.4465168591 +expect -7497596.784 7490230.178 + +accept -69.3713987852 45.2639704879 +expect -6544218.717 6996093.996 + +accept -59.4171409115 45.4132014431 +expect -5602984.158 6519916.313 + +accept -49.3100677093 45.7526223682 +expect -4626331.424 6169411.113 + +accept -39.3187102974 46.6936897182 +expect -3644320.114 5998514.674 + +accept -29.9742251826 46.9036255354 +expect -2766108.322 5819685.981 + +accept -19.2850914312 47.8240485822 +expect -1759883.555 5782937.269 + +accept -9.4670802435 48.1827130036 +expect -859898.761 5747174.232 + +accept 0.9511694165 49.1770930435 +expect 85514.729 5856972.276 + +accept 10.9297395133 49.6986510413 +expect 977791.001 5964314.135 + +accept 20.6644197363 50.6277034455 +expect 1831934.709 6186995.533 + +accept 30.1619319357 50.8191455831 +expect 2670361.605 6364198.402 + +accept 40.7545399266 51.8106566282 +expect 3567452.494 6746371.295 + +accept 50.2966459346 52.3403869111 +expect 4368473.680 7112624.685 + +accept 60.8853952926 52.6411504842 +expect 5247040.469 7565966.065 + +accept 70.9660756955 53.5899082901 +expect 5989886.277 8174678.498 + +accept 80.1015409967 53.6234824949 +expect 6684866.077 8688618.853 + +accept 90.9390534980 54.4032676618 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.2275975816 54.6540829630 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.2799875321 55.5050863665 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.5746083149 55.8365526658 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.5493283922 55.8603600954 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.1036826064 56.0703761781 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.2683487285 56.7530037053 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.2871251380 57.2145257183 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.1375706366 57.2304506879 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.2551410057 57.5449642224 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.4163113130 50.3425538005 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.9733110986 50.4458299488 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.7576358296 50.9581504113 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3417213155 51.6042289093 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.0195163151 51.8452436385 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.9037457234 52.0925641350 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.8773360504 52.9710431510 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.3390467545 53.2106639106 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.5444768748 53.6966176349 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.1508332331 54.2933793206 +expect -7253089.374 9338825.070 + +accept -79.5075548350 54.5417649044 +expect -6548869.079 8768667.084 + +accept -69.4795328221 54.7159059038 +expect -5776149.289 8249326.048 + +accept -59.0478968386 55.5204260857 +expect -4890946.145 7889320.902 + +accept -49.1881407070 56.1072337490 +expect -4060119.862 7613040.252 + +accept -39.4101841829 56.8828347097 +expect -3227458.953 7444966.031 + +accept -29.4613810419 57.5239937456 +expect -2395806.485 7324970.138 + +accept -19.1085034175 58.4436055844 +expect -1535705.803 7310415.489 + +accept -9.9177724571 59.0951662260 +expect -790135.180 7330448.667 + +accept 0.8275131479 59.4326708329 +expect 65618.406 7353747.937 + +accept 10.2968451911 60.3586661957 +expect 804903.589 7530433.615 + +accept 20.4902700839 60.9790884371 +expect 1584299.446 7722395.231 + +accept 30.8639040743 61.2280864786 +expect 2370997.263 7922432.098 + +accept 40.0936831128 61.5458193623 +expect 3052952.372 8172497.353 + +accept 50.1049528540 62.3327697572 +expect 3741270.462 8574038.080 + +accept 60.0465199812 62.7356240065 +expect 4412884.709 8976470.296 + +accept 70.2410137014 63.2120813131 +expect 5054093.961 9462122.654 + +accept 80.2667195466 63.4462194528 +expect 5659364.075 9965995.331 + +accept 90.1958404963 64.0831209279 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.5787966151 64.7750993019 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.5253826702 65.0311314831 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.1812962078 65.3475412110 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.6287227669 65.6970336106 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.0181654960 66.0623612621 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.7856466511 66.3591210923 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.9724593003 66.6162174492 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.0946608724 66.6386212957 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.4742461481 67.2758505348 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.3409209668 60.7971917835 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8189576120 61.1637771517 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.5389911401 61.2887975834 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.8226015684 61.6437737052 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.2361655032 62.2457658007 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.4677006422 63.0421916813 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.2765495979 63.0575080285 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.6674914716 63.2917945558 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.7670971881 63.3834861911 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.8162159435 64.1409584458 +expect -6128005.443 10556995.120 + +accept -79.1187835657 64.5588989031 +expect -5465931.391 10061962.599 + +accept -69.5357774042 64.7792984398 +expect -4855109.193 9660585.643 + +accept -59.4761740671 64.8974706901 +expect -4194775.341 9286417.697 + +accept -49.9280128167 65.6204314486 +expect -3502178.047 9090538.544 + +accept -39.3823903629 65.9211509100 +expect -2766684.454 8864998.247 + +accept -29.7679870260 66.9026625522 +expect -2059983.349 8842303.129 + +accept -19.9897475317 67.3766053532 +expect -1374622.796 8788777.482 + +accept -9.9442064094 68.2526920687 +expect -672566.109 8863134.950 + +accept 0.7509772840 69.1829974388 +expect 49780.475 9007858.113 + +accept 10.6585809296 69.8835157088 +expect 694405.780 9169808.493 + +accept 20.3911658798 70.0037343002 +expect 1321293.723 9273335.219 + +accept 30.6722125463 70.6594377768 +expect 1946504.130 9536648.767 + +accept 40.6299540574 71.5624775878 +expect 2501652.005 9893454.187 + +accept 50.8721494614 71.8337784376 +expect 3079786.371 10187475.653 + +accept 60.0291284696 71.8501773949 +expect 3593103.464 10453205.799 + +accept 70.8431571594 72.0862933562 +expect 4145342.784 10857384.689 + +accept 80.1791569395 72.7183017021 +expect 4529430.842 11315638.668 + +accept 90.8791736411 72.9059905421 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.0478827489 73.6259975169 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.4087249466 74.2803681973 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.3401612468 74.8365621394 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.1826062634 75.5694271641 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.9483568214 75.9187393924 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.4892277622 76.6692051191 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.6382868956 77.4573927636 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.7717121538 77.8061856572 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.3573673565 78.0660690876 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.6675977383 70.0957203052 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.7405185971 70.8584256587 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.5167125707 71.8322204655 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.0042143904 72.1158106531 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.0329602286 72.2542294354 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.7801336775 73.2077231869 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.3119539671 73.8550759705 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.4758176356 74.3266149256 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.6607202273 75.0169362990 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.4753530769 75.9617981968 +expect -4457168.999 12206697.987 + +accept -79.5887435008 76.2786099898 +expect -4003489.308 11893940.766 + +accept -69.5002491752 76.5782690095 +expect -3522337.539 11617780.097 + +accept -59.6915787173 77.1315486475 +expect -3007598.216 11449732.407 + +accept -49.4552848899 77.1769236310 +expect -2520444.564 11214194.197 + +accept -39.2403833701 77.8832488521 +expect -1964697.049 11174495.756 + +accept -29.4506836219 78.3637643336 +expect -1456404.136 11143594.629 + +accept -19.5010832381 78.4638811594 +expect -965580.048 11064519.470 + +accept -9.5727750302 79.0649065844 +expect -463128.369 11154178.989 + +accept 0.3501026786 79.9268552980 +expect 16280.988 11359716.588 + +accept 10.8083498797 80.7734576072 +expect 480582.301 11612067.445 + +accept 20.8098672542 81.2830486223 +expect 896200.874 11813880.882 + +accept 30.3588816681 81.4543384377 +expect 1287049.335 11952125.248 + +accept 40.8217587675 82.0376694327 +expect 1655654.948 12249255.684 + +accept 50.5425294111 82.6836589307 +expect 1943629.387 12583714.312 + +accept 60.1931997991 83.6237398675 +expect 2131995.937 13024459.333 + +accept 70.6113860432 84.0910108055 +expect 2365890.947 13363173.126 + +accept 80.9787521274 84.9230149078 +expect 2463890.570 13818835.453 + +accept 90.9143604315 85.3871192550 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.4734880731 86.0042464514 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.9680822795 86.3859461851 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.8622100732 87.2340544489 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.2558456040 87.3620502284 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.2081210889 87.9608446770 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.4963196965 88.3752399281 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.1141812758 89.1315428504 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.8037412086 89.6150717843 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.1602592915 90.5979873573 +expect failure errno coord_transfm_invalid_coord + +accept -179.4988010038 80.2382292021 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.7323895837 80.2920174872 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.9503807693 80.8196927273 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.0925061081 81.4663152862 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.5716369758 82.4286410234 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.8787197288 82.8456226975 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.4793423714 83.5516191256 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.2664761985 84.4756117750 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.3415573570 85.3222226381 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.5621686999 86.1477384313 +expect -2328002.776 14359252.241 + +accept -79.7459074624 86.3337679988 +expect -2066809.293 14230691.128 + +accept -69.5924016054 86.7873849130 +expect -1721998.690 14226866.593 + +accept -59.3424378796 87.4693622383 +expect -1325649.584 14378063.176 + +accept -49.4008006097 87.4815472674 +expect -1116300.148 14277850.382 + +accept -39.0859175285 87.8616846009 +expect -823391.287 14385214.623 + +accept -29.4922393933 88.2891416248 +expect -560419.081 14575750.266 + +accept -19.1948975670 88.7931017780 +expect -308317.578 14881668.127 + +accept -9.7223365758 89.3832521756 +expect -112026.906 15387878.248 + +accept 0.6641448256 90.0106423220 +expect failure errno coord_transfm_invalid_coord + +accept 10.1860723801 90.3688642972 +expect failure errno coord_transfm_invalid_coord + +accept 20.9490167192 90.7173958262 +expect failure errno coord_transfm_invalid_coord + +accept 30.5649867370 90.9925163187 +expect failure errno coord_transfm_invalid_coord + +accept 40.4458702150 91.4734308311 +expect failure errno coord_transfm_invalid_coord + +accept 50.5856921606 91.9158720484 +expect failure errno coord_transfm_invalid_coord + +accept 60.1363202035 92.2767051552 +expect failure errno coord_transfm_invalid_coord + +accept 70.0710227099 93.2758942081 +expect failure errno coord_transfm_invalid_coord + +accept 80.2222434482 93.6733349750 +expect failure errno coord_transfm_invalid_coord + +accept 90.0483434140 94.4740404396 +expect failure errno coord_transfm_invalid_coord + +accept 100.6823676393 95.0641687155 +expect failure errno coord_transfm_invalid_coord + +accept 110.6403276588 95.6156935259 +expect failure errno coord_transfm_invalid_coord + +accept 120.2581625576 96.0431766104 +expect failure errno coord_transfm_invalid_coord + +accept 130.4609264126 96.4854267472 +expect failure errno coord_transfm_invalid_coord + +accept 140.6441294534 96.7262713213 +expect failure errno coord_transfm_invalid_coord + +accept 150.3252472008 97.1420609214 +expect failure errno coord_transfm_invalid_coord + +accept 160.7668616164 97.4790143988 +expect failure errno coord_transfm_invalid_coord + +accept 170.7465062128 97.6550567817 +expect failure errno coord_transfm_invalid_coord + +accept 180.7542323137 98.2872938097 +expect failure errno coord_transfm_invalid_coord + +accept -179.4933532172 89.0462961156 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8505428520 89.8903183246 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.6997771019 89.9583403191 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.2613507188 90.8395350848 +expect failure errno coord_transfm_invalid_coord + +accept -139.1842997548 91.1573946186 +expect failure errno coord_transfm_invalid_coord + +accept -129.6568991775 91.9446769249 +expect failure errno coord_transfm_invalid_coord + +accept -119.3115606256 92.6597043587 +expect failure errno coord_transfm_invalid_coord + +accept -109.6274703609 93.6500222571 +expect failure errno coord_transfm_invalid_coord + +accept -99.3955363318 93.6790731121 +expect failure errno coord_transfm_invalid_coord + +accept -89.4902277654 93.7101118679 +expect failure errno coord_transfm_invalid_coord + +accept -79.2055095189 93.8973426839 +expect failure errno coord_transfm_invalid_coord + +accept -69.2694919752 94.5715889948 +expect failure errno coord_transfm_invalid_coord + +accept -59.2096313695 94.8767204910 +expect failure errno coord_transfm_invalid_coord + +accept -49.3346426547 95.7619370286 +expect failure errno coord_transfm_invalid_coord + +accept -39.2940192313 95.9177686800 +expect failure errno coord_transfm_invalid_coord + +accept -29.1265263906 96.4025342806 +expect failure errno coord_transfm_invalid_coord + +accept -19.8149677195 96.5067828223 +expect failure errno coord_transfm_invalid_coord + +accept -9.2806942519 96.8402148749 +expect failure errno coord_transfm_invalid_coord + +accept 0.9337491530 97.5569468760 +expect failure errno coord_transfm_invalid_coord + +accept 10.2800898790 97.9514714385 +expect failure errno coord_transfm_invalid_coord + +accept 20.9290209494 98.9284832763 +expect failure errno coord_transfm_invalid_coord + +accept 30.3939457169 99.5719752144 +expect failure errno coord_transfm_invalid_coord + +accept 40.6958590705 100.0328567981 +expect failure errno coord_transfm_invalid_coord + +accept 50.1473239826 100.6776574030 +expect failure errno coord_transfm_invalid_coord + +accept 60.6429472168 100.7044060498 +expect failure errno coord_transfm_invalid_coord + +accept 70.8017862719 101.2635238404 +expect failure errno coord_transfm_invalid_coord + +accept 80.3144473172 101.9663622692 +expect failure errno coord_transfm_invalid_coord + +accept 90.7459660200 102.3134423218 +expect failure errno coord_transfm_invalid_coord + +accept 100.1161002435 103.1947683448 +expect failure errno coord_transfm_invalid_coord + +accept 110.1928396624 104.0579352787 +expect failure errno coord_transfm_invalid_coord + +accept 120.2928981698 104.8100792607 +expect failure errno coord_transfm_invalid_coord + +accept 130.4257286255 105.4176918707 +expect failure errno coord_transfm_invalid_coord + +accept 140.8110132830 105.4248870814 +expect failure errno coord_transfm_invalid_coord + +accept 150.8802025406 106.2350153626 +expect failure errno coord_transfm_invalid_coord + +accept 160.3540927190 106.6211948814 +expect failure errno coord_transfm_invalid_coord + +accept 170.1006211763 106.7429949781 +expect failure errno coord_transfm_invalid_coord + +accept 180.9080349563 107.0582862622 +expect failure errno coord_transfm_invalid_coord + + diff --git a/test/ProjNet.Tests/Fixtures/gie/adams_ws1.gie b/test/ProjNet.Tests/Fixtures/gie/adams_ws1.gie new file mode 100644 index 00000000..3593fe12 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/adams_ws1.gie @@ -0,0 +1,2121 @@ + + +------------------------------------------------------------ +# This gie file was automatically generated using libproject +# where the adams_ws1 code was adapted from +------------------------------------------------------------ + +------------------------------------------------------------ +operation +proj=adams_ws1 +R=6370997 +tolerance 1 mm +------------------------------------------------------------ +accept -179.5170670673 -90.3642618405 +expect failure errno coord_transfm_invalid_coord + +accept -169.6193301609 -90.0089826784 +expect failure errno coord_transfm_invalid_coord + +accept -159.5146913398 -89.9552061084 +expect -350717.162 -11748881.092 + +accept -149.6430387202 -89.4598171312 +expect -1198165.030 -11484132.922 + +accept -139.3420792457 -88.5689205573 +expect -1900383.673 -11089869.954 + +accept -129.5512190326 -88.2843232228 +expect -2002093.387 -10839580.396 + +accept -119.5849519031 -87.8113050000 +expect -2149637.897 -10510603.217 + +accept -109.8164701029 -87.6292839331 +expect -2103837.772 -10270387.260 + +accept -99.0070325468 -87.4860856350 +expect -1996207.278 -10030480.328 + +accept -89.8710202810 -87.0734859278 +expect -1978905.833 -9725415.443 + +accept -79.9177626050 -86.8409810985 +expect -1848776.887 -9483178.045 + +accept -69.6832972131 -86.4913255195 +expect -1709649.451 -9206995.664 + +accept -59.5324497291 -86.3836989068 +expect -1493076.994 -9040595.732 + +accept -49.7885614755 -86.1711430633 +expect -1288297.308 -8857404.772 + +accept -39.2736625770 -85.9311099723 +expect -1048159.783 -8676577.940 + +accept -29.9883661697 -85.1267277663 +expect -864602.075 -8328578.049 + +accept -19.7354612657 -84.2995572008 +expect -606385.822 -8009717.908 + +accept -9.8320712389 -83.8158727667 +expect -311874.893 -7833551.758 + +accept 0.1529149340 -83.6786769068 +expect 4891.700 -7783359.345 + +accept 10.8075764102 -83.0220228165 +expect 358525.175 -7610748.071 + +accept 20.2756666263 -82.4050031966 +expect 693297.426 -7478128.281 + +accept 30.5717416484 -82.1078093242 +expect 1059291.615 -7458462.299 + +accept 40.0217776423 -81.4996688835 +expect 1423231.064 -7381432.285 + +accept 50.4439084395 -80.9178750042 +expect 1835831.195 -7348906.778 + +accept 60.2928202530 -80.8948879488 +expect 2195784.908 -7466766.931 + +accept 70.1422375374 -79.9793295792 +expect 2644125.293 -7411053.135 + +accept 80.0281082734 -79.9307378831 +expect 3023172.557 -7577579.052 + +accept 90.1359566782 -79.0548748136 +expect 3514571.202 -7604717.836 + +accept 100.9983207780 -78.4899667014 +expect 4020295.243 -7757494.376 + +accept 110.7127101938 -77.8772548871 +expect 4506704.120 -7915020.711 + +accept 120.9499015457 -77.6414595729 +expect 4975451.712 -8212762.236 + +accept 130.7914974914 -77.0932634434 +expect 5498089.726 -8493219.908 + +accept 140.6080188527 -77.0119938017 +expect 5942639.654 -8934467.649 + +accept 150.6995918182 -76.9369794197 +expect 6388836.279 -9482287.585 + +accept 160.9763179459 -76.4102449261 +expect 6955592.369 -10093789.725 + +accept 170.5761545704 -76.3749705670 +expect 7276212.519 -10898661.639 + +accept 180.4778073139 -75.5923496826 +expect -7735120.306 -11759677.243 + +accept -179.9594024463 -79.1850172406 +expect -6267808.862 -11809346.696 + +accept -169.3792405914 -78.6847392352 +expect -6375466.776 -11010285.716 + +accept -159.3418105960 -77.9452575038 +expect -6399742.566 -10210740.446 + +accept -149.6506452375 -77.3145139162 +expect -6244446.337 -9487525.415 + +accept -139.5282281225 -76.8064299491 +expect -5941042.263 -8840534.760 + +accept -129.3590935485 -75.8784716103 +expect -5652577.184 -8195427.069 + +accept -119.3829013254 -75.5762740726 +expect -5225585.846 -7754976.336 + +accept -109.0050595456 -74.8490162848 +expect -4822333.815 -7284106.174 + +accept -99.6562286237 -73.8531237046 +expect -4472856.920 -6851420.623 + +accept -89.4153824527 -73.5246290838 +expect -4008400.544 -6558443.533 + +accept -79.6133647306 -73.1453487971 +expect -3570120.993 -6303696.366 + +accept -69.6398208021 -72.3341230538 +expect -3146057.888 -6010826.112 + +accept -59.8706106341 -71.3971340090 +expect -2728166.758 -5736410.313 + +accept -49.3205605438 -70.7335340397 +expect -2256529.742 -5524593.729 + +accept -39.3411926381 -70.6365159073 +expect -1795311.675 -5424479.920 + +accept -29.5083854002 -70.4758592857 +expect -1345403.678 -5337336.817 + +accept -19.0279013677 -70.0084283415 +expect -870594.276 -5225973.870 + +accept -9.2887899729 -69.7394095461 +expect -425827.654 -5164538.802 + +accept 0.8271647968 -69.2722085113 +expect 38107.829 -5095338.557 + +accept 10.4036887646 -68.3592860451 +expect 484296.312 -4988000.264 + +accept 20.2060343192 -68.2238727038 +expect 943249.674 -4997881.278 + +accept 30.5698496420 -67.8806308159 +expect 1435688.678 -5002119.817 + +accept 40.1495377913 -67.4553827915 +expect 1899965.927 -5010162.059 + +accept 50.3602778968 -67.0362523329 +expect 2404182.204 -5042085.202 + +accept 60.9974900881 -66.4077946779 +expect 2947890.396 -5072684.473 + +accept 70.9319955374 -65.8210021247 +expect 3471665.520 -5121479.201 + +accept 80.4863012420 -65.1950301635 +expect 3994821.648 -5179465.208 + +accept 90.6983519178 -64.8794465969 +expect 4561938.454 -5308162.118 + +accept 100.9396101906 -64.2819828297 +expect 5168400.393 -5424158.734 + +accept 110.3740821515 -63.7921797585 +expect 5753378.431 -5559987.662 + +accept 120.9952802105 -63.1814589291 +expect 6456367.698 -5728557.242 + +accept 130.5262638944 -62.9745552631 +expect 7113536.253 -5951928.183 + +accept 140.8208531113 -62.1723630793 +expect 7922447.639 -6121121.677 + +accept 150.0998916715 -61.6016244915 +expect 8715442.276 -6298866.943 + +accept 160.9286198483 -61.2084726717 +expect 9734201.834 -6531135.754 + +accept 170.5710819967 -60.6197664775 +expect 10759563.471 -6612968.425 + +accept 180.0430694902 -60.0407055339 +expect -11807509.426 -6558808.816 + +accept -179.5390947423 -69.0648460645 +expect -11698244.368 -9734562.708 + +accept -169.1901512061 -68.4729959428 +expect -9950971.024 -8797882.041 + +accept -159.9508405867 -68.3071525125 +expect -8958082.142 -8161514.970 + +accept -149.8939906743 -67.9903394603 +expect -8122002.603 -7560968.287 + +accept -139.3084750889 -67.6932300903 +expect -7358441.192 -7046235.673 + +accept -129.4903340896 -66.9738174802 +expect -6754279.469 -6566632.394 + +accept -119.3732549373 -66.1666702364 +expect -6167465.568 -6131803.466 + +accept -109.9122840717 -65.4626199327 +expect -5635053.049 -5784623.911 + +accept -99.0894792417 -64.9328397796 +expect -5031244.298 -5474194.879 + +accept -89.0379071438 -63.9724755049 +expect -4506394.832 -5162764.903 + +accept -79.8512236201 -63.1650388913 +expect -4030262.713 -4918826.251 + +accept -69.0024968791 -62.7959282508 +expect -3459473.930 -4731573.132 + +accept -59.6285901198 -62.7061946763 +expect -2970786.932 -4617341.636 + +accept -49.7082574527 -62.1571694174 +expect -2471206.184 -4465370.277 + +accept -39.4924979763 -61.7459110848 +expect -1958931.958 -4346771.831 + +accept -29.6363032106 -61.6957692779 +expect -1464984.333 -4287075.197 + +accept -19.5739110331 -61.6019886294 +expect -965552.532 -4238215.163 + +accept -9.6576498976 -61.0164571209 +expect -477467.663 -4155301.252 + +accept 0.9700961170 -60.1204628592 +expect 48205.814 -4057834.175 + +accept 10.7639703855 -59.1654390696 +expect 538264.315 -3972144.031 + +accept 20.0541166979 -58.2221709542 +expect 1009809.314 -3901769.484 + +accept 30.8822551157 -57.4323767288 +expect 1566666.238 -3866147.789 + +accept 40.9150221936 -56.7476936019 +expect 2091877.250 -3852786.699 + +accept 50.0130584672 -56.6268047719 +expect 2571289.842 -3900890.372 + +accept 60.1810460294 -56.5806776760 +expect 3115889.942 -3978273.297 + +accept 70.0095293626 -56.4903844848 +expect 3655288.464 -4064302.468 + +accept 80.2965290229 -56.4254295515 +expect 4235148.992 -4174494.751 + +accept 90.0684823849 -55.5094217862 +expect 4825173.693 -4205785.231 + +accept 100.2589575221 -54.7080863095 +expect 5463679.888 -4267283.094 + +accept 110.9472297773 -54.0819702617 +expect 6161497.112 -4367866.466 + +accept 120.8869363890 -53.8315420800 +expect 6836546.011 -4509924.510 + +accept 130.9603256931 -53.6395383232 +expect 7559494.803 -4670554.878 + +accept 140.1257246691 -53.4446807335 +expect 8259667.150 -4816997.141 + +accept 150.2071621174 -52.9722495199 +expect 9089603.867 -4934074.676 + +accept 160.6088986258 -52.1795359483 +expect 10009756.247 -4978006.469 + +accept 170.2183758483 -51.5758826477 +expect 10895284.078 -4985209.390 + +accept 180.3089859974 -51.2476311235 +expect -11783292.311 -4970550.366 + +accept -179.3405213764 -59.1949594465 +expect -11740654.113 -6374577.500 + +accept -169.0421166099 -59.1562784619 +expect -10642169.105 -6280796.295 + +accept -159.3788558458 -58.9714540198 +expect -9692814.995 -6062325.471 + +accept -149.0662979992 -58.4548170890 +expect -8795516.955 -5733211.410 + +accept -139.3706367143 -58.1144715199 +expect -8025985.315 -5449082.903 + +accept -129.1106150079 -57.8422608500 +expect -7273101.480 -5175957.712 + +accept -119.2867946872 -57.5287479364 +expect -6602894.857 -4927624.634 + +accept -109.8617968496 -56.9893254243 +expect -6002387.156 -4682302.572 + +accept -99.4159322812 -56.2424930053 +expect -5370022.362 -4421472.280 + +accept -89.1699101036 -56.1663221081 +expect -4755991.161 -4262687.067 + +accept -79.7513340456 -55.7410486547 +expect -4218798.697 -4097773.078 + +accept -69.3859719372 -55.4898494102 +expect -3639082.164 -3958567.253 + +accept -59.6570132581 -54.5322069534 +expect -3118583.817 -3776942.947 + +accept -49.1598655048 -54.4599218486 +expect -2552713.105 -3691062.494 + +accept -39.1574191031 -54.3046314680 +expect -2023877.704 -3616745.668 + +accept -29.6721934217 -53.6562199308 +expect -1531972.573 -3515412.233 + +accept -19.8662059139 -53.4687850046 +expect -1023531.105 -3467209.179 + +accept -9.5466027093 -53.0271646961 +expect -491871.463 -3409466.748 + +accept 0.5571469345 -53.0212050870 +expect 28691.149 -3403138.420 + +accept 10.2150991679 -52.8267091529 +expect 526790.549 -3393151.184 + +accept 20.9184560187 -52.1945236902 +expect 1083680.938 -3360440.100 + +accept 30.4122790805 -51.8430079370 +expect 1582353.162 -3361293.005 + +accept 40.9466629257 -51.6378144374 +expect 2141968.613 -3391259.162 + +accept 50.9012994718 -51.6157642742 +expect 2677989.259 -3447645.551 + +accept 60.2635223516 -50.8239592102 +expect 3201005.434 -3444708.197 + +accept 70.3461813446 -50.7667566814 +expect 3768769.080 -3523960.462 + +accept 80.9078900718 -50.1766356883 +expect 4389398.947 -3573130.194 + +accept 90.1074711799 -49.9117875315 +expect 4944447.058 -3649776.255 + +accept 100.1252105382 -48.9204454031 +expect 5584999.550 -3676552.405 + +accept 110.8672357396 -48.1515257195 +expect 6298134.023 -3741552.205 + +accept 120.3694000933 -47.4449038605 +expect 6960325.242 -3800735.139 + +accept 130.4622216350 -47.1679496656 +expect 7689059.250 -3914199.981 + +accept 140.5086830302 -47.1237216006 +expect 8449190.695 -4050734.799 + +accept 150.1476683134 -46.3595419843 +expect 9231198.383 -4086961.656 + +accept 160.2556473416 -46.2916624279 +expect 10076699.984 -4186410.441 + +accept 170.8522091884 -46.1574282136 +expect 10999670.471 -4243488.850 + +accept 180.2570720688 -46.0614416637 +expect -11789409.557 -4252598.873 + +accept -179.4511464235 -49.1459116777 +expect -11761990.229 -4665890.121 + +accept -169.9257769945 -49.0470611950 +expect -10894262.669 -4621088.416 + +accept -159.9907938476 -48.8074825653 +expect -10017224.172 -4504606.959 + +accept -149.4585690847 -48.4774901965 +expect -9135164.524 -4334099.962 + +accept -139.2453966436 -47.8692552329 +expect -8335827.552 -4118376.932 + +accept -129.9085174848 -46.8854494051 +expect -7654250.412 -3875947.564 + +accept -119.8893057705 -46.5651334714 +expect -6944562.132 -3704152.951 + +accept -109.0958379349 -45.7583483056 +expect -6225548.239 -3485487.807 + +accept -99.2790124089 -45.4028322295 +expect -5592633.486 -3337133.510 + +accept -89.5363705206 -44.7944152576 +expect -4990785.214 -3180396.161 + +accept -79.5060440658 -44.5224968249 +expect -4385934.870 -3064005.444 + +accept -69.4515180819 -43.5521936119 +expect -3804302.240 -2904123.456 + +accept -59.1288687363 -42.8251932286 +expect -3217542.420 -2777920.839 + +accept -49.5480487278 -42.2232882391 +expect -2682493.692 -2679394.683 + +accept -39.1528197403 -42.0524649460 +expect -2108296.850 -2621044.912 + +accept -29.4378045622 -41.1418430583 +expect -1582000.485 -2522351.315 + +accept -19.7178288379 -40.6492312711 +expect -1057685.973 -2464591.380 + +accept -9.4090896450 -40.5893893271 +expect -503836.212 -2446387.785 + +accept 0.9610981916 -39.6877677601 +expect 51540.292 -2379140.432 + +accept 10.9883708721 -39.3404897159 +expect 590154.416 -2360351.587 + +accept 20.8051789783 -39.1539543744 +expect 1120024.260 -2361313.374 + +accept 30.4192741493 -38.5008875049 +expect 1644931.536 -2337542.552 + +accept 40.3869890035 -37.5480251892 +expect 2197952.841 -2301604.532 + +accept 50.8557015620 -37.0223436721 +expect 2787383.069 -2305516.111 + +accept 60.6790393928 -36.1726044193 +expect 3354525.829 -2291237.179 + +accept 70.2878480471 -35.4333317594 +expect 3922441.096 -2289959.081 + +accept 80.5852221343 -34.7195179750 +expect 4547835.373 -2300447.284 + +accept 90.8953318146 -34.0606127868 +expect 5194014.701 -2320692.817 + +accept 100.1645347304 -33.2806968649 +expect 5796224.654 -2327994.783 + +accept 110.9256034679 -32.3021983595 +expect 6521787.796 -2332216.561 + +accept 120.5411285918 -31.9198181206 +expect 7190613.992 -2375521.402 + +accept 130.2284841486 -31.5333210976 +expect 7889339.253 -2417443.002 + +accept 140.4220823477 -30.6970967059 +expect 8655106.833 -2418067.478 + +accept 150.9227263702 -30.4375246674 +expect 9466130.022 -2460197.858 + +accept 160.0295580243 -29.5659253879 +expect 10191991.350 -2424567.041 + +accept 170.6808093605 -29.1021932503 +expect 11052824.977 -2413781.540 + +accept 180.1099977864 -29.0325047616 +expect -11803327.121 -2416468.990 + +accept -179.6225370823 -39.8953975091 +expect -11780081.121 -3517460.085 + +accept -169.6109651378 -39.4882092166 +expect -10930063.239 -3453245.362 + +accept -159.1192163384 -39.1718824306 +expect -10057200.020 -3364669.762 + +accept -149.1119232566 -38.9176058199 +expect -9251011.551 -3261533.260 + +accept -139.5731057891 -38.9121786221 +expect -8508507.983 -3173185.997 + +accept -129.7628486402 -38.0200948217 +expect -7786640.589 -2991489.483 + +accept -119.7677289390 -37.8134438608 +expect -7073265.858 -2874240.367 + +accept -109.1746441433 -36.9333699237 +expect -6356287.199 -2698249.042 + +accept -99.9602516727 -36.5407037317 +expect -5751426.148 -2585442.621 + +accept -89.8203767870 -35.5930072217 +expect -5112766.564 -2430581.015 + +accept -79.3526596151 -35.2401507154 +expect -4468536.158 -2331237.714 + +accept -69.9140939731 -34.4711213307 +expect -3906901.211 -2218161.925 + +accept -59.5564783999 -33.9615774789 +expect -3302614.749 -2129304.529 + +accept -49.4856159772 -33.3921969402 +expect -2727641.771 -2047863.180 + +accept -39.5601849133 -33.2219810003 +expect -2168968.599 -2003059.820 + +accept -29.6206503521 -32.2387656553 +expect -1619499.618 -1912794.298 + +accept -19.8260643144 -31.2411754077 +expect -1082312.059 -1831061.273 + +accept -9.6980379834 -31.1493347964 +expect -528492.040 -1814844.632 + +accept 0.1021998532 -30.2109056883 +expect 5573.607 -1752134.034 + +accept 10.1241618952 -30.0590713215 +expect 552610.811 -1745957.315 + +accept 20.7210235248 -29.1582457860 +expect 1134768.700 -1699801.253 + +accept 30.8569113997 -28.5816818553 +expect 1696753.177 -1680040.760 + +accept 40.8690942845 -28.2388918529 +expect 2258557.806 -1680827.372 + +accept 50.7976512101 -28.0352374493 +expect 2824229.726 -1696198.158 + +accept 60.6880476492 -27.3593892155 +expect 3400671.784 -1686010.997 + +accept 70.3971704737 -26.5193483221 +expect 3980673.819 -1668358.964 + +accept 80.7537743358 -25.6284840207 +expect 4616331.727 -1653268.127 + +accept 90.9008404807 -25.6193405937 +expect 5253601.247 -1701776.097 + +accept 100.6019282805 -25.1073764643 +expect 5885059.255 -1715983.750 + +accept 110.2074536086 -24.7371256820 +expect 6529985.921 -1741419.588 + +accept 120.8735021013 -24.6200753121 +expect 7269663.058 -1792634.308 + +accept 130.3337268181 -24.1982546793 +expect 7950714.246 -1812019.053 + +accept 140.0292803756 -24.1036603231 +expect 8668942.211 -1854994.840 + +accept 150.2932445511 -23.9158664525 +expect 9452746.490 -1886388.628 + +accept 160.0080010509 -23.0912677563 +expect 10215210.151 -1850466.772 + +accept 170.5235832568 -22.1201165276 +expect 11053033.448 -1789486.248 + +accept 180.6560052356 -22.0885409872 +expect -11759672.392 -1793318.350 + +accept -179.4804785664 -29.6306479674 +expect -11769827.502 -2472486.935 + +accept -169.5969137916 -29.4470720507 +expect -10963965.497 -2443636.516 + +accept -159.7905342089 -29.3575038499 +expect -10173853.235 -2404475.146 + +accept -149.0451837035 -29.3191918216 +expect -9326864.232 -2349131.811 + +accept -139.2634124047 -29.2174079662 +expect -8578812.250 -2281416.940 + +accept -129.4780322813 -29.1095444632 +expect -7854782.176 -2207222.010 + +accept -119.0212234055 -28.1534517213 +expect -7115133.213 -2058739.607 + +accept -109.3989071392 -27.9851170141 +expect -6453382.370 -1982663.067 + +accept -99.1605926399 -27.3804651341 +expect -5776201.695 -1874182.968 + +accept -89.1612152313 -27.3765954441 +expect -5132743.215 -1817444.101 + +accept -79.5274133946 -26.6685619062 +expect -4535248.349 -1719079.927 + +accept -69.2474535764 -26.1885685364 +expect -3913186.952 -1641486.835 + +accept -59.9970146255 -26.1431540924 +expect -3365047.885 -1603639.182 + +accept -49.7059448492 -25.7380450818 +expect -2769264.371 -1544962.373 + +accept -39.1015835447 -24.9244904554 +expect -2167463.036 -1467333.079 + +accept -29.0839025659 -24.0290019262 +expect -1606736.238 -1393727.229 + +accept -19.0559621671 -23.5834403073 +expect -1050009.512 -1354116.104 + +accept -9.4796954418 -22.7008051540 +expect -521897.404 -1294226.360 + +accept 0.8467653094 -22.2437237249 +expect 46612.070 -1264810.191 + +accept 10.1002957100 -21.4069947198 +expect 556780.318 -1217517.853 + +accept 20.6390095332 -21.0427895850 +expect 1140438.287 -1203323.480 + +accept 30.9472401536 -20.1902202762 +expect 1717070.228 -1164328.832 + +accept 40.6934322431 -20.0098914684 +expect 2268176.645 -1168727.630 + +accept 50.7817131415 -19.2822125520 +expect 2848736.826 -1144124.118 + +accept 60.5453409920 -18.5217559886 +expect 3421842.893 -1119498.292 + +accept 70.9306917526 -17.5496804837 +expect 4046179.805 -1085105.639 + +accept 80.9437168988 -16.8999064340 +expect 4663411.774 -1071645.011 + +accept 90.2760894813 -16.3256791261 +expect 5254544.808 -1062173.444 + +accept 100.3952892244 -15.3687880606 +expect 5915778.505 -1029396.074 + +accept 110.8716860238 -15.1135676547 +expect 6620486.296 -1045435.781 + +accept 120.2754329194 -14.7635849177 +expect 7273908.238 -1050938.104 + +accept 130.9205644215 -13.8870075430 +expect 8038711.991 -1019018.441 + +accept 140.5279151075 -13.8598832588 +expect 8747404.377 -1043857.108 + +accept 150.1724830315 -13.4001676763 +expect 9478091.722 -1031350.442 + +accept 160.7119668695 -12.8946537206 +expect 10293471.165 -1010309.510 + +accept 170.7445455520 -12.4806498073 +expect 11080986.451 -987879.955 + +accept 180.0860875981 -12.0984902614 +expect -11805496.888 -960233.297 + +accept -179.3916734336 -19.7014586301 +expect -11763712.607 -1588768.200 + +accept -169.6454904074 -19.4996536478 +expect -10986824.032 -1564928.001 + +accept -159.3116700662 -18.9771320814 +expect -10172088.280 -1502117.477 + +accept -149.2431319392 -18.5239809302 +expect -9392904.689 -1437107.722 + +accept -139.9543854131 -18.2509646291 +expect -8690226.526 -1383821.794 + +accept -129.7568071343 -17.3990196444 +expect -7942027.033 -1280164.910 + +accept -119.1614046516 -16.8619769504 +expect -7188040.064 -1200364.046 + +accept -109.2977430033 -16.3891311149 +expect -6508726.059 -1130383.184 + +accept -99.9990872016 -15.4549241428 +expect -5889334.034 -1034038.536 + +accept -89.1925625691 -14.8368103445 +expect -5189840.029 -960130.042 + +accept -79.0808010606 -14.6385640035 +expect -4553964.924 -920479.551 + +accept -69.2443866324 -14.1456687273 +expect -3952562.317 -866676.412 + +accept -59.0066198816 -13.5814519067 +expect -3341600.979 -812246.810 + +accept -49.6855331551 -13.0942516406 +expect -2796564.477 -768323.810 + +accept -39.1133813450 -12.2378694514 +expect -2189428.295 -705006.114 + +accept -29.7742120709 -11.7887487551 +expect -1660234.233 -670669.573 + +accept -19.9711155501 -11.6767395032 +expect -1110226.679 -658130.627 + +accept -9.9497447301 -11.3258048935 +expect -552158.854 -634498.402 + +accept 0.5509033260 -11.1626324486 +expect 30555.312 -624081.026 + +accept 10.5660459562 -10.2780961917 +expect 586666.653 -575356.975 + +accept 20.2035769485 -9.2879705389 +expect 1124313.735 -522411.222 + +accept 30.1804649932 -8.9615586081 +expect 1685072.306 -508721.741 + +accept 40.7493880077 -8.5624561325 +expect 2286291.364 -492777.586 + +accept 50.2789516772 -8.3745080127 +expect 2836608.706 -489680.573 + +accept 60.8385203257 -8.2963597858 +expect 3457824.160 -495534.087 + +accept 70.9443512805 -8.0934149371 +expect 4066101.521 -494895.161 + +accept 80.2007278148 -7.3297477878 +expect 4637580.892 -458880.962 + +accept 90.2330138853 -6.3835404074 +expect 5273690.558 -410871.266 + +accept 100.0849613278 -5.7704772952 +expect 5916391.051 -382372.515 + +accept 110.4993556234 -5.2096342892 +expect 6616996.779 -356342.641 + +accept 120.8300166158 -4.8886947314 +expect 7334310.897 -345088.331 + +accept 130.0108888901 -4.6741162050 +expect 7990761.455 -338928.560 + +accept 140.0244639639 -4.5796883247 +expect 8726333.645 -341133.044 + +accept 150.2062193251 -3.8424983415 +expect 9493863.039 -292863.578 + +accept 160.9138221527 -3.6801453162 +expect 10317707.231 -285699.001 + +accept 170.8709639263 -3.0673481366 +expect 11095020.712 -240557.828 + +accept 180.1668965748 -2.1055585251 +expect -11799179.686 -165599.448 + +accept -179.1485401294 -9.0050262787 +expect -11745147.622 -711689.062 + +accept -169.6824431551 -8.1575200565 +expect -11000115.042 -641511.177 + +accept -159.6715880943 -7.6221099815 +expect -10218824.936 -592292.988 + +accept -149.3051183917 -7.4397903953 +expect -9422033.576 -567343.940 + +accept -139.2134827169 -6.9710994505 +expect -8663402.254 -519006.901 + +accept -129.2596978253 -6.6939068551 +expect -7934011.410 -484946.005 + +accept -119.6918931722 -6.2610035871 +expect -7252563.925 -440785.572 + +accept -109.4245072379 -5.7027048567 +expect -6543136.053 -388878.776 + +accept -99.0230201699 -5.3407381847 +expect -5846751.038 -352675.119 + +accept -89.3070231490 -5.1039965720 +expect -5215688.995 -327406.437 + +accept -79.7971543944 -4.4916537572 +expect -4615264.044 -280462.038 + +accept -69.3324157511 -3.9437385342 +expect -3971856.878 -239654.016 + +accept -59.5648134684 -3.8977574735 +expect -3385627.891 -231608.529 + +accept -49.0877260741 -3.6480235522 +expect -2770346.534 -212306.063 + +accept -39.8261302552 -3.1309666481 +expect -2236162.052 -179431.131 + +accept -29.9044251723 -2.1430339033 +expect -1671913.963 -121205.794 + +accept -19.0798027396 -1.2466271084 +expect -1063209.017 -69795.001 + +accept -9.7521679387 -1.0322328827 +expect -542511.865 -57496.172 + +accept 0.9430496675 -0.6908437587 +expect 52430.968 -38410.610 + +accept 10.9467094331 -0.6297633697 +expect 609067.346 -35093.759 + +accept 20.4348082857 0.1325785640 +expect 1139135.679 7429.710 + +accept 30.7578824509 0.9786091010 +expect 1720312.179 55393.172 + +accept 40.1304133943 1.3184899313 +expect 2253938.856 75567.836 + +accept 50.6339926465 1.7803646722 +expect 2860936.487 103859.561 + +accept 60.7806464601 1.9656672403 +expect 3458637.676 117050.207 + +accept 70.1424618467 2.0349290359 +expect 4021938.226 123840.505 + +accept 80.3515740385 2.0389166997 +expect 4651228.515 127409.428 + +accept 90.5030006919 2.6127341537 +expect 5294231.959 168032.745 + +accept 100.0361988664 2.7788325854 +expect 5915751.683 183883.148 + +accept 110.4290203898 3.4171783192 +expect 6613830.450 233508.030 + +accept 120.0868391150 4.1107421068 +expect 7282720.013 289418.148 + +accept 130.8358443481 4.7269030447 +expect 8050540.773 343566.765 + +accept 140.4771116425 4.7459157253 +expect 8759896.875 353947.419 + +accept 150.6050881294 4.8179867316 +expect 9523608.686 367693.374 + +accept 160.2822850737 4.8901762814 +expect 10268142.143 379540.358 + +accept 170.8847288206 5.6488167761 +expect 11095489.666 443644.671 + +accept 180.4963027772 6.1617137742 +expect -11773225.028 485642.177 + +accept -179.7711795627 0.4973936135 +expect -11794312.860 39108.932 + +accept -169.1314097124 1.1380806183 +expect -10958975.952 89092.114 + +accept -159.9956088875 1.3785514183 +expect -10247153.847 106804.510 + +accept -149.5081205584 1.7169216947 +expect -9441654.288 130579.933 + +accept -139.3350755264 2.6978007803 +expect -8676365.930 200450.001 + +accept -129.9999093970 3.1714349364 +expect -7991182.579 229813.581 + +accept -119.2025789331 3.1937958085 +expect -7221314.251 224181.555 + +accept -109.2754065272 3.9790540663 +expect -6534751.689 270991.754 + +accept -99.2176596815 4.4421245388 +expect -5860471.809 293385.606 + +accept -89.3282286284 4.7715139132 +expect -5217353.706 306050.669 + +accept -79.7812956950 5.1451934064 +expect -4613741.333 321348.393 + +accept -69.9603490759 5.4631533571 +expect -4008936.444 332711.261 + +accept -59.2124036536 5.9473211127 +expect -3363430.499 353449.079 + +accept -49.1946821277 6.3240504109 +expect -2775155.416 368554.729 + +accept -39.5188729974 7.1419837480 +expect -2216820.895 409863.534 + +accept -29.1883557585 7.8744650207 +expect -1629648.240 446150.067 + +accept -19.1594428055 8.7442679237 +expect -1066133.170 491231.789 + +accept -9.5636574635 9.2026796862 +expect -531158.530 514475.669 + +accept 0.0205997186 9.2921681107 +expect 1143.393 518613.658 + +accept 10.6474003585 9.3325749918 +expect 591403.379 522011.049 + +accept 20.0440697721 9.7920834357 +expect 1115183.582 550935.839 + +accept 30.1210940174 10.3126985648 +expect 1680869.076 586066.073 + +accept 40.9703333564 10.5122361845 +expect 2297293.386 606203.052 + +accept 50.1242819935 10.7592956093 +expect 2825113.486 630238.088 + +accept 60.5237375468 11.0581832240 +expect 3435579.925 661638.044 + +accept 70.0186459905 11.8235504151 +expect 4004121.295 723779.234 + +accept 80.3592940251 12.0855798348 +expect 4639465.013 760177.897 + +accept 90.5376970710 12.9622135342 +expect 5281027.214 840054.684 + +accept 100.6214357026 12.9800321873 +expect 5937912.442 867143.481 + +accept 110.6668058012 13.4013916345 +expect 6611924.171 924135.479 + +accept 120.8660619260 13.6620144115 +expect 7319051.165 972687.117 + +accept 130.0979222590 14.6001907539 +expect 7976726.161 1070012.208 + +accept 140.4720834014 14.9027374310 +expect 8740255.766 1124185.971 + +accept 150.7487344905 15.7591910245 +expect 9516541.903 1219443.082 + +accept 160.7309215820 16.6928847546 +expect 10288334.979 1317274.424 + +accept 170.7171183093 17.1256054857 +expect 11074727.024 1367681.457 + +accept 180.0522042065 17.7180717900 +expect -11808147.730 1421784.637 + +accept -179.7714315704 10.4228351993 +expect -11794256.988 825223.859 + +accept -169.5070586751 10.6771709914 +expect -10984834.587 842079.978 + +accept -159.4580308434 11.3886412993 +expect -10198009.570 888630.633 + +accept -149.0300160729 12.0779530345 +expect -9393645.594 925474.868 + +accept -139.9457181785 13.0366592822 +expect -8706116.490 979150.471 + +accept -129.2494581925 13.4021731716 +expect -7918892.896 978007.743 + +accept -119.6924991127 13.6081016459 +expect -7236591.544 965299.698 + +accept -109.9549521935 14.0589477898 +expect -6561371.199 968205.406 + +accept -99.7560216207 14.8951827161 +expect -5875106.633 995021.878 + +accept -89.4929629341 14.9878946198 +expect -5208590.397 970965.202 + +accept -79.7718646359 15.7860515996 +expect -4593786.261 996113.294 + +accept -69.1787032576 16.1396339481 +expect -3943904.709 991435.170 + +accept -59.5415515235 16.9323624559 +expect -3366393.956 1018559.978 + +accept -49.4027735513 17.7467818790 +expect -2772187.794 1047604.534 + +accept -39.4592345235 18.6907045458 +expect -2200328.007 1087110.348 + +accept -29.3138791949 19.4420397181 +expect -1626432.141 1117581.487 + +accept -19.0263697499 20.4143066442 +expect -1051476.817 1164568.814 + +accept -9.3715816928 21.0493545179 +expect -516728.380 1196014.238 + +accept 0.8183051284 21.2475093875 +expect 45086.676 1205740.085 + +accept 10.2436153128 21.9004386412 +expect 564436.455 1246884.830 + +accept 20.9411485051 22.8923059091 +expect 1155233.946 1314342.262 + +accept 30.8909786046 23.0333873010 +expect 1709465.640 1335784.571 + +accept 40.6622924750 23.1458260628 +expect 2259937.246 1360334.792 + +accept 50.2165426895 23.2813833636 +expect 2805957.899 1390914.743 + +accept 60.2763462023 24.1180873989 +expect 3388956.040 1473204.648 + +accept 70.9671286324 24.5104273631 +expect 4023912.266 1536627.432 + +accept 80.1106583316 25.2622593363 +expect 4578414.346 1625284.387 + +accept 90.1352434237 25.7041000035 +expect 5204358.499 1703872.872 + +accept 100.7659799608 26.3884977784 +expect 5887972.662 1810511.107 + +accept 110.3627285832 26.5971550981 +expect 6528536.584 1882676.289 + +accept 120.5747336187 27.3361344725 +expect 7230359.893 2003955.277 + +accept 130.0334936740 27.5388791002 +expect 7906998.039 2080891.049 + +accept 140.6618802190 27.9206554725 +expect 8693452.038 2178619.988 + +accept 150.8057175364 28.8815698894 +expect 9466666.514 2319595.955 + +accept 160.9794787031 29.3286919896 +expect 10269064.515 2406494.682 + +accept 170.8635008015 30.0881534347 +expect 11065436.193 2506392.939 + +accept 180.7801778816 30.7919429173 +expect -11748279.400 2582524.107 + +accept -179.3089746696 20.7349232283 +expect -11757005.796 1676812.324 + +accept -169.3508199207 21.5333791040 +expect -10960347.629 1737356.209 + +accept -159.4055152547 22.3252032326 +expect -10170125.304 1783292.652 + +accept -149.8454884475 22.4734039129 +expect -9424453.386 1763417.112 + +accept -139.3941906099 23.1082873232 +expect -8626570.035 1770372.898 + +accept -129.6731808706 24.0100345748 +expect -7903751.181 1793442.280 + +accept -119.5653357447 24.9154429650 +expect -7175704.391 1808079.370 + +accept -109.4049571753 25.6530253182 +expect -6469736.987 1805594.553 + +accept -99.2573928302 26.1761469511 +expect -5790274.499 1786445.122 + +accept -89.1936009216 26.8478033007 +expect -5137966.077 1779983.008 + +accept -79.9319952951 27.0219083563 +expect -4558285.962 1745405.889 + +accept -69.7891755607 27.9650852011 +expect -3937249.698 1763328.521 + +accept -59.9224508827 28.7378521521 +expect -3349925.184 1774568.738 + +accept -49.2934775668 29.7032670105 +expect -2731923.368 1800437.192 + +accept -39.7585640043 30.6226892016 +expect -2188798.477 1831989.503 + +accept -29.7786441638 31.0043819908 +expect -1631282.400 1832903.391 + +accept -19.9767642691 31.2638249380 +expect -1090543.436 1832723.009 + +accept -9.4508778589 31.3720263871 +expect -514837.245 1828891.334 + +accept 0.9277524134 31.4797046691 +expect 50503.011 1832688.099 + +accept 10.8523072346 31.6410734176 +expect 591047.237 1847096.134 + +accept 20.9701082341 32.0077728355 +expect 1143775.602 1882087.570 + +accept 30.4584904014 32.2524388516 +expect 1665803.096 1915504.146 + +accept 40.6480244298 32.2809624159 +expect 2233201.781 1943617.167 + +accept 50.8580656871 32.6668467052 +expect 2809043.442 2003759.375 + +accept 60.0108771272 33.5702558206 +expect 3331147.200 2104097.315 + +accept 70.5034398056 33.6252927324 +expect 3947557.082 2160701.818 + +accept 80.8803135822 34.2081349569 +expect 4569870.780 2264387.101 + +accept 90.6363798105 35.0454420959 +expect 5169067.110 2394326.391 + +accept 100.7095824201 35.0925204648 +expect 5814871.525 2475230.813 + +accept 110.4928526153 35.3747862433 +expect 6462291.225 2579639.068 + +accept 120.4119184030 36.0949196788 +expect 7138879.871 2729237.832 + +accept 130.3776922680 36.6637391210 +expect 7848033.791 2872450.680 + +accept 140.7776214259 37.0211653163 +expect 8622838.330 3001065.131 + +accept 150.9988070526 37.4312106652 +expect 9416320.865 3126629.034 + +accept 160.1799904005 37.7026348392 +expect 10156009.588 3217300.330 + +accept 170.7924717408 38.0391644104 +expect 11035694.135 3300682.151 + +accept 180.2988991065 38.8552843618 +expect -11786931.659 3402926.729 + +accept -179.1544921899 30.5865766668 +expect -11742964.963 2562920.940 + +accept -169.3849428190 30.6760590330 +expect -10943452.496 2558666.832 + +accept -159.4339156279 30.6775291474 +expect -10138949.929 2524905.799 + +accept -149.5559422097 31.4197140165 +expect -9352568.633 2541799.021 + +accept -139.0897465732 32.2480021869 +expect -8541501.925 2546249.903 + +accept -129.2432705270 33.1672095397 +expect -7802297.946 2550865.482 + +accept -119.6591996700 33.2608566981 +expect -7115874.023 2480432.270 + +accept -109.9331600482 33.2921667980 +expect -6445294.904 2404312.074 + +accept -99.3485704747 34.1878235263 +expect -5734796.733 2392907.698 + +accept -89.8642843810 35.1664811803 +expect -5119362.394 2398013.740 + +accept -79.2554014378 36.1141613383 +expect -4455555.225 2396032.770 + +accept -69.1149563301 37.0376191064 +expect -3841378.648 2400749.345 + +accept -59.1032039467 37.4528523762 +expect -3255415.569 2375467.923 + +accept -49.2271359812 37.5837083768 +expect -2692342.061 2338370.137 + +accept -39.6916503252 37.7843555404 +expect -2158326.616 2315768.979 + +accept -29.8938804478 38.4387691365 +expect -1616406.889 2331809.199 + +accept -19.6362961239 39.2643368046 +expect -1056535.053 2366905.008 + +accept -9.4374584268 39.5396992359 +expect -506540.235 2372796.930 + +accept 0.3678100982 40.0842150295 +expect 19706.992 2406799.601 + +accept 10.7782532604 40.3498890996 +expect 577565.478 2430780.292 + +accept 20.3511128947 41.0134441926 +expect 1090910.801 2491722.447 + +accept 30.4333653857 41.5670977874 +expect 1634463.912 2555980.666 + +accept 40.6127074692 41.8606872838 +expect 2189539.048 2612578.512 + +accept 50.1387387629 41.9862708713 +expect 2717075.610 2664468.878 + +accept 60.0313937859 42.2155826206 +expect 3273928.502 2736271.447 + +accept 70.3375377370 42.9334405924 +expect 3862149.979 2861074.643 + +accept 80.5122630521 43.3972517538 +expect 4459635.506 2979024.926 + +accept 90.2998133187 44.2542302837 +expect 5045422.749 3141116.429 + +accept 100.0647551775 44.4815518568 +expect 5657295.328 3262535.857 + +accept 110.6066455294 44.8720342864 +expect 6341643.269 3419834.403 + +accept 120.1724623585 45.4033516879 +expect 6986444.476 3591181.437 + +accept 130.7707977218 46.0009809664 +expect 7735330.463 3793501.018 + +accept 140.0357246677 46.4664188350 +expect 8425607.646 3969868.107 + +accept 150.3890392442 46.5461497362 +expect 9247658.444 4111964.848 + +accept 160.9163061323 46.7398186016 +expect 10127353.584 4248204.315 + +accept 170.1397977064 47.5954473956 +expect 10925947.772 4426508.240 + +accept 180.2351988738 48.0474540668 +expect -11790980.142 4514546.436 + +accept -179.6179355770 40.1429577199 +expect -11779642.671 3545074.378 + +accept -169.3994668623 40.9727631907 +expect -10904739.639 3617207.971 + +accept -159.0744411429 41.2851805506 +expect -10034213.428 3592885.528 + +accept -149.8423978548 41.7994923976 +expect -9274861.042 3569932.623 + +accept -139.3764020016 42.6414085288 +expect -8441459.370 3548024.189 + +accept -129.8834678056 43.3974758995 +expect -7717279.673 3512850.000 + +accept -119.2273385573 43.9271323417 +expect -6946608.719 3434404.081 + +accept -109.0271864056 44.8644031164 +expect -6236376.125 3400177.924 + +accept -99.2759252254 45.1195335801 +expect -5596913.783 3311405.157 + +accept -89.6168562261 45.3669011811 +expect -4987642.638 3231158.191 + +accept -79.1400549649 45.8028162130 +expect -4348106.593 3168953.470 + +accept -69.1272034531 45.8946112129 +expect -3760612.414 3092810.191 + +accept -59.5914817326 46.6423497334 +expect -3209252.229 3085015.399 + +accept -49.7829967571 47.4970577378 +expect -2655798.711 3093450.893 + +accept -39.2258939434 48.1273210284 +expect -2076130.612 3090330.169 + +accept -29.0459159714 48.7731368364 +expect -1527463.826 3102071.708 + +accept -19.4563259884 49.0147747663 +expect -1019421.009 3094379.980 + +accept -9.2974861965 49.6391756401 +expect -485234.163 3127485.443 + +accept 0.8002954696 50.4949000479 +expect 41618.406 3191862.866 + +accept 10.5449464536 50.7566160438 +expect 548218.021 3220010.469 + +accept 20.2148839237 51.7062683926 +expect 1049059.717 3317338.698 + +accept 30.3826788909 51.7914032104 +expect 1581115.436 3356787.020 + +accept 40.4566276504 52.4962620813 +expect 2108580.613 3463100.568 + +accept 50.7914497367 53.3942556548 +expect 2652555.049 3604990.575 + +accept 60.6693710568 53.6277898414 +expect 3186853.012 3700944.562 + +accept 70.8983905024 54.4541071246 +expect 3741818.938 3872995.556 + +accept 80.0921298391 54.7777207304 +expect 4258319.040 4004738.180 + +accept 90.8264889463 55.1332494774 +expect 4879297.684 4176785.355 + +accept 100.4617594180 55.2946750932 +expect 5460672.139 4333801.395 + +accept 110.3843836904 56.2425900762 +expect 6060244.396 4604234.950 + +accept 120.9357632899 56.9127950267 +expect 6737581.209 4883511.960 + +accept 130.5662521586 57.0909748951 +expect 7408708.016 5106858.830 + +accept 140.5130223329 57.4425849230 +expect 8144289.862 5377321.687 + +accept 150.1055981470 58.0701974311 +expect 8901145.374 5695571.850 + +accept 160.5298175128 58.2923369725 +expect 9829016.530 5963428.705 + +accept 170.7716226761 58.3931749475 +expect 10840592.637 6151742.796 + +accept 180.3673574365 58.5523508398 +expect -11773057.974 6240881.263 + +accept -179.1819571823 50.5244358587 +expect -11736162.165 4863184.254 + +accept -169.7451954874 51.1460877943 +expect -10856391.238 4918273.752 + +accept -159.2560731724 51.1659719222 +expect -9910743.214 4816238.037 + +accept -149.9672222696 51.3661570651 +expect -9112154.958 4711683.179 + +accept -139.8591282059 52.1411020613 +expect -8277768.355 4644021.152 + +accept -129.7970972042 52.4993373959 +expect -7508721.894 4510492.172 + +accept -119.8747965281 52.7309515249 +expect -6798679.387 4365407.674 + +accept -109.1716302230 53.6677897735 +expect -6057181.907 4293188.653 + +accept -99.0064373966 53.8072556867 +expect -5408756.759 4153094.469 + +accept -89.2833040728 54.6106266193 +expect -4799568.155 4102012.106 + +accept -79.5985918526 55.1027737429 +expect -4223329.559 4031502.907 + +accept -69.2277148796 55.7622851795 +expect -3625399.148 3983778.291 + +accept -59.7190082964 55.9016291208 +expect -3101569.537 3908067.342 + +accept -49.8523842515 55.9381520101 +expect -2571672.615 3833924.898 + +accept -39.0306006434 56.2379702434 +expect -1998881.092 3793906.979 + +accept -29.4386358660 56.7861006916 +expect -1497679.518 3798815.953 + +accept -19.4990519776 57.6029946160 +expect -985017.408 3841130.737 + +accept -9.7556385919 58.2846118408 +expect -490207.195 3885471.247 + +accept 0.2410422753 58.3298871859 +expect 12102.460 3882913.235 + +accept 10.5236020030 58.9487998679 +expect 526883.142 3950657.847 + +accept 20.5151795661 59.2605128471 +expect 1027091.833 4004099.328 + +accept 30.5940481955 60.2189305606 +expect 1527348.143 4138882.981 + +accept 40.7900054843 61.1999722752 +expect 2031933.355 4297245.113 + +accept 50.5958144484 62.0053970071 +expect 2519299.307 4456118.987 + +accept 60.8762100151 62.4317915615 +expect 3041726.936 4599170.199 + +accept 70.4775836656 63.4288167842 +expect 3520271.382 4823700.333 + +accept 80.4849589897 63.7092243462 +expect 4046810.715 4994145.001 + +accept 90.9223489138 64.1528226880 +expect 4604230.542 5218233.343 + +accept 100.8413924261 65.0793496826 +expect 5124544.719 5529805.227 + +accept 110.4292321534 65.5632643921 +expect 5660125.988 5811449.898 + +accept 120.3403456476 66.5295382674 +expect 6202409.500 6215253.879 + +accept 130.8994551518 67.0090839235 +expect 6844751.778 6619354.810 + +accept 140.8028440361 67.1546072874 +expect 7520909.896 7003616.860 + +accept 150.1461657011 67.4794680766 +expect 8205195.583 7463336.596 + +accept 160.1948043463 67.9727812220 +expect 9033005.154 8088126.442 + +accept 170.2236262055 68.9001498060 +expect 10002490.427 9028299.844 + +accept 180.4510871253 69.6301310123 +expect -11672105.068 10174167.501 + +accept -179.6928502294 60.7606385646 +expect -11777314.779 6722857.577 + +accept -169.1226136208 61.3072659363 +expect -10577772.207 6741718.347 + +accept -159.3917581951 61.6554659753 +expect -9558954.738 6580581.575 + +accept -149.9215059659 62.2899028506 +expect -8655627.202 6419091.419 + +accept -139.4229348418 63.2282931041 +expect -7745441.279 6255362.342 + +accept -129.6145782219 63.6056278696 +expect -7010285.489 6024060.105 + +accept -119.4455633048 63.7880033811 +expect -6321410.142 5777303.154 + +accept -109.2885142234 64.7874725222 +expect -5635268.808 5674008.141 + +accept -99.8106343476 65.7546653268 +expect -5032017.479 5601114.476 + +accept -89.7332690529 65.9905292469 +expect -4461000.004 5437610.544 + +accept -79.7061602957 66.5799499364 +expect -3900842.013 5346675.577 + +accept -69.8078719866 67.2349525670 +expect -3366364.151 5288124.095 + +accept -59.9278061065 67.4707153106 +expect -2863723.058 5195929.041 + +accept -49.6499709362 68.0783549484 +expect -2344265.721 5168345.324 + +accept -39.7785355635 68.6589802179 +expect -1858546.630 5161258.347 + +accept -29.2733130411 69.2346381936 +expect -1354425.808 5169177.366 + +accept -19.7520445226 69.7447199105 +expect -906653.264 5193341.582 + +accept -9.4546150406 70.4233082073 +expect -429913.564 5256559.645 + +accept 0.4048610966 70.6236052708 +expect 18357.153 5275509.126 + +accept 10.1670362925 70.8976936786 +expect 459603.254 5322872.830 + +accept 20.8647562320 71.3607521955 +expect 938809.251 5419110.193 + +accept 30.9620313275 72.3532797491 +expect 1376843.290 5613329.925 + +accept 40.2436883822 72.6448737185 +expect 1786361.071 5722579.786 + +accept 50.0800145195 72.7023402174 +expect 2228603.224 5821470.902 + +accept 60.8106370898 73.3031896004 +expect 2692182.927 6039226.272 + +accept 70.6025437547 73.5707955278 +expect 3126302.133 6221575.805 + +accept 80.4428196695 74.5629968499 +expect 3514896.350 6555403.995 + +accept 90.9705345155 75.3895826260 +expect 3928491.141 6916551.843 + +accept 100.7042769293 76.1944062131 +expect 4287723.041 7301533.036 + +accept 110.6051178518 77.0216158345 +expect 4624306.800 7743103.312 + +accept 120.5528704261 77.5805939292 +expect 4968887.256 8186300.038 + +accept 130.0043694164 78.1214527556 +expect 5261992.760 8661662.569 + +accept 140.4219455785 78.5676823388 +expect 5567546.429 9220412.007 + +accept 150.9607701203 78.6584076186 +expect 5913255.760 9797358.275 + +accept 160.8565770466 78.9854274121 +expect 6094257.816 10453353.669 + +accept 170.4873432082 79.5034695093 +expect 6085224.011 11149120.017 + +accept 180.3649630983 79.8293737054 +expect -6018407.263 11787437.041 + +accept -179.3073985765 70.8163938435 +expect -10817280.529 11465490.561 + +accept -169.9897278030 71.2934701929 +expect -9276875.736 9863919.805 + +accept -159.2365467427 71.8590802946 +expect -8184178.135 9023921.272 + +accept -149.5868788879 72.5692211678 +expect -7360942.175 8539328.713 + +accept -139.1323352132 73.1055180323 +expect -6629486.281 8091534.479 + +accept -129.7742695161 73.8196458860 +expect -6000140.533 7815451.358 + +accept -119.7041959034 74.7579440452 +expect -5353400.778 7611475.920 + +accept -109.7008776088 75.3901899809 +expect -4792518.394 7404785.570 + +accept -99.2314898828 75.9111536135 +expect -4252036.425 7210319.564 + +accept -89.5888146775 76.2792517885 +expect -3785659.112 7048200.366 + +accept -79.3077687162 76.5665040629 +expect -3313318.429 6890921.119 + +accept -69.4337987883 76.6621432071 +expect -2883922.823 6737102.185 + +accept -59.0261934568 76.9594965901 +expect -2426688.222 6640668.630 + +accept -49.3115987100 77.8323229554 +expect -1979275.955 6688559.534 + +accept -39.4459740795 78.6210665264 +expect -1547696.694 6747296.835 + +accept -29.2899383303 78.8023227358 +expect -1142238.026 6708292.313 + +accept -19.1326451258 79.5725050055 +expect -728892.599 6814228.120 + +accept -9.5853469917 80.5460704598 +expect -353566.181 6996661.780 + +accept 0.8129663669 80.7466782310 +expect 29772.656 7032318.930 + +accept 10.8102522601 81.0089111161 +expect 392092.798 7104986.336 + +accept 20.8020824140 81.8177026408 +expect 730326.797 7332200.488 + +accept 30.3227561837 81.8668144912 +expect 1061996.648 7396680.294 + +accept 40.1192116699 82.5591024355 +expect 1359653.273 7647195.746 + +accept 50.7225485024 83.3689210619 +expect 1642887.530 7969766.384 + +accept 60.1580162716 83.8192679216 +expect 1890049.083 8210844.252 + +accept 70.1731315267 84.1714314114 +expect 2143235.010 8455663.246 + +accept 80.1231823107 85.0909307831 +expect 2259322.517 8893244.179 + +accept 90.1299725257 85.3982223970 +expect 2445628.938 9170008.600 + +accept 100.9345367116 86.3293017336 +expect 2430413.592 9671881.713 + +accept 110.8967536886 86.5596667511 +expect 2542278.654 9949902.835 + +accept 120.1922834115 86.7776311985 +expect 2614369.629 10217813.641 + +accept 130.8551063009 87.2011682062 +expect 2577941.936 10571347.314 + +accept 140.5883122989 87.9808140533 +expect 2273302.478 10967527.442 + +accept 150.7238822230 88.4956617003 +expect 2017487.009 11270794.839 + +accept 160.6971562328 89.4208328233 +expect 1268680.941 11594325.039 + +accept 170.3991577317 90.3170479552 +expect failure errno coord_transfm_invalid_coord + +accept 180.5908903286 90.7796418583 +expect failure errno coord_transfm_invalid_coord + +accept -179.6672737749 80.9670130376 +expect -5579975.368 11792148.700 + +accept -169.9437203572 81.8738988668 +expect -5187300.642 11262109.091 + +accept -159.3629518885 82.2036176232 +expect -4941095.530 10742757.395 + +accept -149.7114004837 82.3010483165 +expect -4738960.212 10299021.779 + +accept -139.8871282560 82.3318724399 +expect -4514031.774 9880652.443 + +accept -129.1103803259 83.1345581099 +expect -4013186.362 9627836.752 + +accept -119.1303161403 83.9704390214 +expect -3530043.701 9489966.226 + +accept -109.5992779696 84.5102002899 +expect -3148868.130 9358019.001 + +accept -99.5340303810 84.8783360216 +expect -2804982.087 9214341.492 + +accept -89.9555755860 85.8080149104 +expect -2340101.503 9294021.159 + +accept -79.4199716924 86.1128031464 +expect -2020424.674 9213221.394 + +accept -69.1379478051 86.2852242994 +expect -1740870.664 9123538.176 + +accept -59.4862558744 86.8907264218 +expect -1395372.231 9240228.437 + +accept -49.7224425586 87.3050907988 +expect -1102476.832 9323956.320 + +accept -39.0166782385 88.0295172740 +expect -756214.155 9606239.667 + +accept -29.9931677373 88.8762393331 +expect -449807.586 10100826.611 + +accept -19.5461864449 89.0119403988 +expect -277206.486 10175661.602 + +accept -9.5857052257 89.5105564736 +expect -97172.766 10643561.020 + +accept 0.2546693029 89.8300875130 +expect 1535.284 11119443.552 + +accept 10.1878191712 89.8306694493 +expect 61235.456 11123325.873 + +accept 20.3202271141 90.0891258483 +expect failure errno coord_transfm_invalid_coord + +accept 30.4076531874 90.1655009485 +expect failure errno coord_transfm_invalid_coord + +accept 40.4697816170 90.3073014721 +expect failure errno coord_transfm_invalid_coord + +accept 50.0046280736 90.9081150272 +expect failure errno coord_transfm_invalid_coord + +accept 60.2882853272 91.3234973005 +expect failure errno coord_transfm_invalid_coord + +accept 70.1983790734 92.0487871293 +expect failure errno coord_transfm_invalid_coord + +accept 80.2016298087 92.7883781050 +expect failure errno coord_transfm_invalid_coord + +accept 90.1380649347 93.4193661081 +expect failure errno coord_transfm_invalid_coord + +accept 100.9727810821 94.0036131508 +expect failure errno coord_transfm_invalid_coord + +accept 110.2124518442 94.5588879261 +expect failure errno coord_transfm_invalid_coord + +accept 120.9027437830 95.4070516655 +expect failure errno coord_transfm_invalid_coord + +accept 130.4370128971 95.6659394457 +expect failure errno coord_transfm_invalid_coord + +accept 140.5025123065 95.8655824422 +expect failure errno coord_transfm_invalid_coord + +accept 150.4051276696 96.5597126498 +expect failure errno coord_transfm_invalid_coord + +accept 160.6794810904 97.5509003421 +expect failure errno coord_transfm_invalid_coord + +accept 170.0804614782 97.7962770171 +expect failure errno coord_transfm_invalid_coord + +accept 180.7350988655 97.9297507566 +expect failure errno coord_transfm_invalid_coord + +accept -179.6509243766 89.9594690425 +expect -339016.824 11811270.987 + +accept -169.1462360158 90.2259103522 +expect failure errno coord_transfm_invalid_coord + +accept -159.1237371257 91.0966186475 +expect failure errno coord_transfm_invalid_coord + +accept -149.3864893992 92.0018593591 +expect failure errno coord_transfm_invalid_coord + +accept -139.8836795793 92.7310580140 +expect failure errno coord_transfm_invalid_coord + +accept -129.2447556879 93.0874941458 +expect failure errno coord_transfm_invalid_coord + +accept -119.5691328385 94.0016439421 +expect failure errno coord_transfm_invalid_coord + +accept -109.5375733271 94.6275672079 +expect failure errno coord_transfm_invalid_coord + +accept -99.6767551820 94.9362396518 +expect failure errno coord_transfm_invalid_coord + +accept -89.0765267674 95.5300020136 +expect failure errno coord_transfm_invalid_coord + +accept -79.7404375474 95.8979843707 +expect failure errno coord_transfm_invalid_coord + +accept -69.2186423862 96.1670313079 +expect failure errno coord_transfm_invalid_coord + +accept -59.3116550382 97.0176191327 +expect failure errno coord_transfm_invalid_coord + +accept -49.2194140278 97.9598960425 +expect failure errno coord_transfm_invalid_coord + +accept -39.4423952109 97.9675789614 +expect failure errno coord_transfm_invalid_coord + +accept -29.2846350035 98.7361321642 +expect failure errno coord_transfm_invalid_coord + +accept -19.5599253582 98.8248133304 +expect failure errno coord_transfm_invalid_coord + +accept -9.7751102831 99.2905137713 +expect failure errno coord_transfm_invalid_coord + +accept 0.4459912613 99.9348079001 +expect failure errno coord_transfm_invalid_coord + +accept 10.2556901904 100.0679369696 +expect failure errno coord_transfm_invalid_coord + +accept 20.2129499464 100.4339484435 +expect failure errno coord_transfm_invalid_coord + +accept 30.5120716507 100.8173987820 +expect failure errno coord_transfm_invalid_coord + +accept 40.9186217475 101.3362169174 +expect failure errno coord_transfm_invalid_coord + +accept 50.4499309674 101.7519777648 +expect failure errno coord_transfm_invalid_coord + +accept 60.9618169609 101.9681616248 +expect failure errno coord_transfm_invalid_coord + +accept 70.5136938969 102.7263743079 +expect failure errno coord_transfm_invalid_coord + +accept 80.1112365376 103.4930742925 +expect failure errno coord_transfm_invalid_coord + +accept 90.0656487088 104.2687818917 +expect failure errno coord_transfm_invalid_coord + +accept 100.4737302350 105.1672304167 +expect failure errno coord_transfm_invalid_coord + +accept 110.0826941374 105.9578331609 +expect failure errno coord_transfm_invalid_coord + +accept 120.5038465053 106.4653112353 +expect failure errno coord_transfm_invalid_coord + +accept 130.1946022249 107.2836000330 +expect failure errno coord_transfm_invalid_coord + +accept 140.1612060920 108.2561565502 +expect failure errno coord_transfm_invalid_coord + +accept 150.8381796528 108.3785139275 +expect failure errno coord_transfm_invalid_coord + +accept 160.5344804952 109.2381208530 +expect failure errno coord_transfm_invalid_coord + +accept 170.6816130052 110.0255190870 +expect failure errno coord_transfm_invalid_coord + +accept 180.9197969760 110.0988929845 +expect failure errno coord_transfm_invalid_coord + + diff --git a/test/ProjNet.Tests/Fixtures/gie/adams_ws2.gie b/test/ProjNet.Tests/Fixtures/gie/adams_ws2.gie new file mode 100644 index 00000000..8ba04755 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/adams_ws2.gie @@ -0,0 +1,2177 @@ + + +------------------------------------------------------------ +# This gie file was initially generated from "random" test points +# got by using libproject where the adams_ws2 code was adapted from +# It can be edited. +------------------------------------------------------------ + +------------------------------------------------------------ +operation +proj=adams_ws2 +R=6370997 +tolerance 1 mm +------------------------------------------------------------ +accept -179.7092450238 -90.0290393775 +expect failure errno coord_transfm_invalid_coord + +accept -169.9316998581 -89.6983443874 +expect -2757243.603 -13694037.516 + +accept -159.9839735761 -89.3853376439 +expect -3135302.955 -12966682.177 + +accept -149.0127901515 -88.4812940559 +expect -3702020.595 -11830322.541 + +accept -139.1470666283 -87.4944214849 +expect -3951802.110 -10998803.477 + +accept -129.1647510809 -86.6732359620 +expect -3966830.515 -10396244.146 + +accept -119.8872640416 -86.0311019572 +expect -3870042.307 -9946966.226 + +accept -109.1071943611 -85.4385747937 +expect -3666639.936 -9527264.855 + +accept -99.1726204643 -85.1105131986 +expect -3405494.414 -9249877.167 + +accept -89.9699823716 -84.7014602402 +expect -3161293.332 -8969222.654 + +accept -79.0142432052 -84.6183360959 +expect -2796763.716 -8803380.868 + +accept -69.3746588999 -83.8694127295 +expect -2538194.969 -8434335.838 + +accept -59.1116247628 -83.4382281692 +expect -2201397.875 -8194239.985 + +accept -49.9832824786 -82.7776695863 +expect -1905027.516 -7914064.933 + +accept -39.2446354074 -82.2684783422 +expect -1520355.973 -7692624.558 + +accept -29.1591843717 -81.2712614892 +expect -1160852.682 -7367506.867 + +accept -19.7518374124 -80.8288036882 +expect -795019.539 -7219158.095 + +accept -9.9255150926 -80.7634661276 +expect -400180.112 -7180839.863 + +accept 0.1884288619 -80.2758766316 +expect 7681.127 -7053424.532 + +accept 10.3463227493 -79.3698907818 +expect 429721.976 -6850344.331 + +accept 20.5976448498 -78.7037640543 +expect 866348.961 -6727849.736 + +accept 30.3082205513 -78.5268124103 +expect 1278848.718 -6727598.466 + +accept 40.4562460745 -78.1491297266 +expect 1718510.989 -6703423.050 + +accept 50.9458720501 -77.1822355756 +expect 2199782.851 -6583628.613 + +accept 60.7726090586 -77.0349803149 +expect 2630476.462 -6641198.553 + +accept 70.5081156610 -76.3842150797 +expect 3083737.575 -6619890.177 + +accept 80.2365671846 -75.4841051853 +expect 3557787.939 -6575918.481 + +accept 90.5217667884 -74.7807003525 +expect 4055523.822 -6599908.601 + +accept 100.8213322615 -74.0327498320 +expect 4564995.375 -6643366.399 + +accept 110.5063711430 -73.2571688604 +expect 5056479.354 -6698955.863 + +accept 120.3163752614 -72.4393741465 +expect 5563998.804 -6778747.641 + +accept 130.6066962791 -72.2383125997 +expect 6050103.198 -6992464.809 + +accept 140.9842139999 -72.0604912080 +expect 6533723.092 -7241474.496 + +accept 150.7407483672 -71.8220094179 +expect 6988261.604 -7493795.727 + +accept 160.2333513818 -71.3988285471 +expect 7442114.989 -7741103.770 + +accept 170.2174368056 -70.4342119870 +expect 7967287.710 -7968253.770 + +accept 180.4880362185 -69.4423545276 +expect -8459022.028 -8206959.559 + +accept -179.6765866679 -79.9829089371 +expect -6985694.074 -9698942.155 + +accept -169.3697449847 -79.1533653138 +expect -6788658.846 -9227367.628 + +accept -159.3398127142 -78.6158200646 +expect -6518830.741 -8830410.965 + +accept -149.2529018703 -77.9038978934 +expect -6242199.759 -8415628.859 + +accept -139.1270212047 -77.3123241499 +expect -5919170.448 -8040244.161 + +accept -129.9719878520 -77.1799395743 +expect -5563633.984 -7792039.926 + +accept -119.8002013346 -76.4574860662 +expect -5211249.405 -7431807.092 + +accept -109.5059723182 -75.7415088186 +expect -4829803.114 -7094468.982 + +accept -99.6093198617 -75.2549327376 +expect -4430664.339 -6828988.008 + +accept -89.7930338075 -74.9639973582 +expect -4012235.208 -6620268.234 + +accept -79.4586895446 -74.2118180651 +expect -3586854.329 -6347250.691 + +accept -69.3436462673 -74.1262304089 +expect -3131838.678 -6210695.025 + +accept -59.8993905791 -73.3233934105 +expect -2730484.498 -5982995.653 + +accept -49.7522174506 -73.2363687589 +expect -2268190.621 -5882547.028 + +accept -39.2685447989 -72.6142200904 +expect -1801294.844 -5714042.490 + +accept -29.0364706467 -72.5237014490 +expect -1332211.352 -5647707.712 + +accept -19.8296434180 -72.1886326428 +expect -912551.389 -5563488.488 + +accept -9.8077325645 -71.7629261845 +expect -453152.448 -5478207.867 + +accept 0.4315853824 -71.5575178388 +expect 19979.237 -5441012.049 + +accept 10.8411958509 -70.9073113064 +expect 505138.396 -5355652.524 + +accept 20.7152781983 -70.1134571802 +expect 972838.442 -5266833.183 + +accept 30.0599851854 -69.8795545612 +expect 1415773.291 -5269134.697 + +accept 40.5438377621 -69.1450433006 +expect 1924403.608 -5223807.773 + +accept 50.0307069046 -68.4333233270 +expect 2392850.994 -5193157.214 + +accept 60.5578535026 -67.8341598034 +expect 2917014.530 -5202936.346 + +accept 70.5486167487 -67.0039300601 +expect 3429776.377 -5197720.065 + +accept 80.6104642107 -66.2045341279 +expect 3955430.510 -5217814.475 + +accept 90.2841546043 -65.8422559530 +expect 4456136.396 -5308137.385 + +accept 100.1453713101 -65.8321633336 +expect 4958133.076 -5466790.857 + +accept 110.3255223842 -65.6999143010 +expect 5484630.668 -5641098.630 + +accept 120.2695614591 -65.4373463883 +expect 6008718.351 -5822579.879 + +accept 130.1617028260 -64.7707999563 +expect 6557379.569 -5983944.031 + +accept 140.4485877356 -64.1566350257 +expect 7128044.250 -6198238.582 + +accept 150.3105224098 -63.5579868289 +expect 7676672.874 -6441225.256 + +accept 160.5091429791 -62.6650596939 +expect 8262531.987 -6705693.227 + +accept 170.8981621301 -61.7219845816 +expect 8855010.278 -7022513.736 + +accept 180.3558199030 -61.1527410473 +expect -9320622.179 -7351806.789 + +accept -179.1949344790 -69.7752745189 +expect -8408251.537 -8232691.330 + +accept -169.9844730348 -68.9207602047 +expect -8120078.404 -7775444.464 + +accept -159.6630800816 -68.8474900484 +expect -7673728.921 -7392368.231 + +accept -149.1514601079 -68.2067669189 +expect -7247339.088 -6962408.917 + +accept -139.0879724776 -67.2990645502 +expect -6835622.742 -6542934.347 + +accept -129.7364301888 -67.0709630928 +expect -6389191.557 -6259081.373 + +accept -119.2525297571 -66.7130216826 +expect -5885127.012 -5959615.515 + +accept -109.9942126502 -65.9323791506 +expect -5456298.788 -5663479.323 + +accept -99.3718189580 -65.0050609815 +expect -4952897.215 -5351531.121 + +accept -89.2126537463 -64.8003181492 +expect -4438651.521 -5164919.484 + +accept -79.9699918529 -64.7417604712 +expect -3968262.754 -5031339.112 + +accept -69.2852704168 -64.0719899001 +expect -3442756.784 -4828682.323 + +accept -59.7713311869 -63.5883281794 +expect -2971057.023 -4681634.618 + +accept -49.4591068015 -63.3603315571 +expect -2455101.158 -4574825.541 + +accept -39.9221210763 -62.7166029149 +expect -1985110.513 -4444066.132 + +accept -29.4033816775 -62.2459162224 +expect -1463165.847 -4343593.454 + +accept -19.4562997110 -62.1057544781 +expect -967613.655 -4296313.938 + +accept -9.6356219787 -61.1466320407 +expect -481338.112 -4176929.015 + +accept 0.6588822473 -60.1858259817 +expect 33069.831 -4072773.374 + +accept 10.9123408994 -59.3942535459 +expect 550076.991 -4001105.274 + +accept 20.9249231576 -58.4264590285 +expect 1060832.702 -3926177.072 + +accept 30.8182477174 -57.5436346739 +expect 1571551.690 -3873152.034 + +accept 40.8062160819 -57.3120408186 +expect 2088248.634 -3896213.274 + +accept 50.5783407631 -57.2502847616 +expect 2597056.641 -3948016.223 + +accept 60.3795380424 -57.2275967096 +expect 3112185.488 -4018270.594 + +accept 70.5341633320 -56.6610699237 +expect 3662178.471 -4054556.527 + +accept 80.2621021518 -56.0832576961 +expect 4200156.553 -4102899.263 + +accept 90.9924788950 -55.3105280599 +expect 4810349.300 -4164560.690 + +accept 100.1850294843 -54.9771464696 +expect 5337043.742 -4270248.554 + +accept 110.2445797630 -54.0658285398 +expect 5943289.082 -4356935.761 + +accept 120.6069857779 -53.3088500534 +expect 6579954.717 -4496181.899 + +accept 130.2789945294 -52.8143911635 +expect 7180795.395 -4682156.275 + +accept 140.3311899960 -52.3084536190 +expect 7816505.414 -4918688.318 + +accept 150.9519295109 -52.0098336142 +expect 8481836.319 -5247130.328 + +accept 160.6630418532 -51.6702008173 +expect 9089733.809 -5596578.481 + +accept 170.5027767145 -51.5189598167 +expect 9675637.514 -6026539.807 + +accept 180.8758506742 -51.2201444439 +expect -10177185.136 -6434755.421 + +accept -179.8411044403 -59.9393178899 +expect -9443976.994 -7246273.236 + +accept -169.3270885266 -59.1985019770 +expect -8995948.520 -6701742.553 + +accept -159.0120452761 -59.1242618883 +expect -8464180.924 -6272112.895 + +accept -149.5670531797 -58.7286202895 +expect -7979472.299 -5884420.870 + +accept -139.0970719139 -58.1489537744 +expect -7430300.432 -5483894.856 + +accept -129.1196020868 -57.2913818657 +expect -6907494.210 -5112397.388 + +accept -119.7410720497 -56.9161549702 +expect -6387194.447 -4845091.157 + +accept -109.7214510325 -55.9267756586 +expect -5851170.964 -4534131.372 + +accept -99.1928708683 -55.4401681713 +expect -5267711.955 -4300230.895 + +accept -89.2702698097 -55.2997481025 +expect -4714550.947 -4139814.556 + +accept -79.4864123616 -54.8136229276 +expect -4183440.927 -3971202.629 + +accept -69.3453324893 -54.6311145369 +expect -3632724.847 -3848910.729 + +accept -59.8339416819 -53.6386169191 +expect -3133296.168 -3676377.089 + +accept -49.3010973212 -52.9141596041 +expect -2577599.457 -3539651.763 + +accept -39.8985039869 -52.8539647879 +expect -2079543.627 -3483996.852 + +accept -29.3961250522 -52.3417298211 +expect -1530564.484 -3397269.368 + +accept -19.5014059179 -51.3773597952 +expect -1016792.801 -3288257.869 + +accept -9.9116288496 -50.4755131648 +expect -517724.688 -3198035.336 + +accept 0.9743282127 -49.9184212313 +expect 50962.745 -3147322.949 + +accept 10.6508090388 -49.0463066485 +expect 558876.345 -3082894.951 + +accept 20.2170315032 -48.8012100804 +expect 1063007.281 -3078773.024 + +accept 30.9229311437 -48.2225550224 +expect 1632723.266 -3061426.633 + +accept 40.7489150310 -47.7931114403 +expect 2161132.006 -3064760.741 + +accept 50.9882474140 -47.6183917612 +expect 2717126.369 -3102179.138 + +accept 60.4951083293 -46.6454001087 +expect 3249189.082 -3083060.891 + +accept 70.9554669962 -45.9763718998 +expect 3843598.409 -3108377.147 + +accept 80.8848766764 -45.7846392844 +expect 4415566.167 -3184255.726 + +accept 90.7402232455 -45.6088952189 +expect 4996535.515 -3278794.703 + +accept 100.8696089733 -45.3585308636 +expect 5610895.632 -3392002.899 + +accept 110.9170212513 -44.9478951297 +expect 6242312.338 -3516148.140 + +accept 120.9353901019 -44.7761977319 +expect 6885637.054 -3692702.792 + +accept 130.5310034140 -44.6491174665 +expect 7518026.186 -3902292.936 + +accept 140.4911819383 -44.5558487119 +expect 8188523.026 -4170828.177 + +accept 150.4583065377 -44.0055960197 +expect 8893017.639 -4454795.754 + +accept 160.9810806536 -43.8499619826 +expect 9618889.130 -4871178.683 + +accept 170.9230729547 -43.0316016948 +expect 10335543.716 -5284759.370 + +accept 180.3381763034 -42.9767512080 +expect -10889181.304 -5775308.758 + +accept -179.6530173733 -49.3872071390 +expect -10359694.674 -6307512.780 + +accept -169.2223492311 -49.3351161906 +expect -9766541.338 -5765269.396 + +accept -159.0508327192 -48.4914027020 +expect -9200440.534 -5224400.981 + +accept -149.1969312324 -48.3204060368 +expect -8581410.142 -4822470.693 + +accept -139.4509254495 -48.2401083822 +expect -7956818.463 -4491703.439 + +accept -129.3953824841 -47.8590661722 +expect -7326697.432 -4175808.479 + +accept -119.8005497638 -46.8591030615 +expect -6750164.464 -3860038.014 + +accept -109.6371217071 -46.1165362624 +expect -6133381.272 -3598151.680 + +accept -99.7572070447 -45.2128579221 +expect -5546071.645 -3363379.628 + +accept -89.9756152550 -44.7940088251 +expect -4964897.006 -3200898.955 + +accept -79.8767538149 -44.0954679134 +expect -4380500.181 -3035551.265 + +accept -69.7070595303 -43.9155165619 +expect -3796215.985 -2932502.304 + +accept -59.4162002941 -43.4002905582 +expect -3219205.574 -2819636.846 + +accept -49.0092629276 -43.1455519878 +expect -2642338.036 -2741942.445 + +accept -39.2149367905 -43.1396428570 +expect -2105326.203 -2698721.256 + +accept -29.1962960438 -42.8806359633 +expect -1563179.016 -2646665.138 + +accept -19.9520584705 -41.9858293563 +expect -1068083.171 -2560330.542 + +accept -9.1048749080 -41.9710426109 +expect -486662.244 -2545041.394 + +accept 0.7345999991 -41.2273949585 +expect 39313.660 -2488176.296 + +accept 10.3358537029 -40.3051838201 +expect 554536.579 -2427518.000 + +accept 20.9085122254 -40.0048024560 +expect 1124375.953 -2420626.128 + +accept 30.8006774663 -39.6693852781 +expect 1661863.596 -2419220.994 + +accept 40.8129777308 -39.2404566099 +expect 2212297.195 -2420473.575 + +accept 50.5680928423 -38.6381409742 +expect 2757518.213 -2417613.121 + +accept 60.1150468046 -38.1919404446 +expect 3299910.135 -2434340.337 + +accept 70.2009887167 -37.7503319244 +expect 3885091.635 -2465277.945 + +accept 80.9433725042 -37.6092654477 +expect 4521685.524 -2537447.427 + +accept 90.5450445832 -36.8932371421 +expect 5114490.299 -2572080.702 + +accept 100.2810830549 -36.2548338054 +expect 5735525.829 -2629983.784 + +accept 110.9555828878 -35.3701130040 +expect 6448221.387 -2701332.745 + +accept 120.1071621348 -34.4239368091 +expect 7092033.959 -2769933.898 + +accept 130.3959151631 -33.5728740994 +expect 7849212.306 -2898252.702 + +accept 140.3966149747 -33.1007607627 +expect 8616443.259 -3102144.372 + +accept 150.7804103414 -32.1498899431 +expect 9474137.117 -3341084.536 + +accept 160.1294242200 -31.8677972120 +expect 10255119.633 -3696347.819 + +accept 170.4926366887 -31.4760758031 +expect 11130499.187 -4208944.238 + +accept 180.0855468805 -30.6845915110 +expect -11924104.915 -4768423.647 + +accept -179.7273734266 -39.1658927524 +expect -11204917.679 -5465459.947 + +accept -169.0184062476 -39.0055263654 +expect -10493158.815 -4820337.657 + +accept -159.3652618381 -38.5207051121 +expect -9823706.550 -4298001.622 + +accept -149.2442235779 -37.7437811442 +expect -9109090.826 -3817330.153 + +accept -139.7582139899 -37.0859345868 +expect -8430885.041 -3451397.102 + +accept -129.6848433059 -36.3175720184 +expect -7723246.989 -3125416.425 + +accept -119.9915203582 -35.8537888060 +expect -7052847.569 -2889355.259 + +accept -109.3741418826 -35.0916931594 +expect -6347630.931 -2655717.550 + +accept -99.4860284168 -34.9974460643 +expect -5702866.657 -2521419.373 + +accept -89.2610432155 -34.3775651948 +expect -5064932.880 -2367691.750 + +accept -79.8980045489 -33.8084333270 +expect -4496443.808 -2246842.786 + +accept -69.7744908122 -33.0535274429 +expect -3897431.224 -2124030.733 + +accept -59.1741793523 -33.0245833220 +expect -3279277.127 -2064611.925 + +accept -49.3252940718 -32.7520928002 +expect -2718021.990 -2003727.901 + +accept -39.9837216743 -32.6820581131 +expect -2193100.779 -1967477.081 + +accept -29.6634260976 -32.4559533116 +expect -1621044.860 -1926376.015 + +accept -19.0425557421 -31.6151992048 +expect -1038939.460 -1853758.302 + +accept -9.3464940981 -30.7676922872 +expect -509764.792 -1790359.701 + +accept 0.2271197201 -29.8880719395 +expect 12396.124 -1731908.926 + +accept 10.3155486517 -29.7265492803 +expect 563481.490 -1725116.999 + +accept 20.1839066538 -29.2623085092 +expect 1105114.379 -1705401.818 + +accept 30.7346251189 -29.0674505554 +expect 1688443.872 -1710201.130 + +accept 40.3481516035 -28.9492608062 +expect 2225766.274 -1724893.934 + +accept 50.0990389861 -28.2777777134 +expect 2780666.281 -1710825.486 + +accept 60.6952552496 -28.1677069370 +expect 3393248.667 -1743873.889 + +accept 70.5261359994 -27.5835336013 +expect 3977728.481 -1751038.589 + +accept 80.1654103496 -27.3421123535 +expect 4564865.482 -1789330.795 + +accept 90.0545702452 -26.7993470554 +expect 5189164.780 -1818900.217 + +accept 100.4846205812 -26.3321377541 +expect 5873417.372 -1871564.464 + +accept 110.1454350755 -25.9649549970 +expect 6535803.844 -1941828.510 + +accept 120.7008393450 -25.1526201253 +expect 7304855.220 -2009386.765 + +accept 130.2389402517 -24.5243234665 +expect 8043003.839 -2105873.386 + +accept 140.5571385114 -23.6272475940 +expect 8905477.819 -2232473.683 + +accept 150.7016104068 -22.6662177106 +expect 9831511.358 -2411871.251 + +accept 160.2968564724 -22.4382538673 +expect 10756906.363 -2747796.799 + +accept 170.0748311294 -22.3860117924 +expect 11733461.723 -3272290.561 + +accept 180.6506603946 -22.3728965349 +expect -12617064.805 -3974167.403 + +accept -179.6121212366 -29.7691402382 +expect -11979921.495 -4667118.695 + +accept -169.7119531770 -29.2443283665 +expect -11222154.909 -3947901.934 + +accept -159.9169552740 -28.8913170762 +expect -10396184.341 -3393588.505 + +accept -149.4387461093 -28.3593158878 +expect -9515015.598 -2929955.771 + +accept -139.1669991056 -27.7889137233 +expect -8680487.806 -2582510.633 + +accept -129.3433138149 -27.6725871069 +expect -7909885.992 -2361735.805 + +accept -119.4082102166 -27.0902195884 +expect -7179377.517 -2149357.741 + +accept -109.1270697537 -26.4319377776 +expect -6459215.181 -1966619.626 + +accept -99.5563015601 -26.1428020328 +expect -5813582.857 -1849104.872 + +accept -89.7150051747 -25.6767220293 +expect -5176646.600 -1736438.788 + +accept -79.8755275705 -25.5910332550 +expect -4558775.834 -1666806.999 + +accept -69.2123495570 -25.4383454527 +expect -3910739.366 -1600921.909 + +accept -59.5658576308 -24.9358234812 +expect -3341553.359 -1527967.578 + +accept -49.6169420229 -24.6444229699 +expect -2765720.514 -1476703.148 + +accept -39.6482793765 -24.5549160001 +expect -2198313.929 -1445718.653 + +accept -29.9777355053 -24.4560828087 +expect -1655580.275 -1421139.862 + +accept -19.1713792157 -23.8256788800 +expect -1056119.979 -1368757.459 + +accept -9.7201400616 -22.9994139064 +expect -535034.881 -1312183.508 + +accept 0.5529890539 -22.3142121451 +expect 30441.381 -1269018.253 + +accept 10.1921781407 -21.7066906983 +expect 561728.285 -1235326.876 + +accept 20.3795548881 -21.4584354172 +expect 1125554.829 -1227765.197 + +accept 30.9486641454 -20.5179662552 +expect 1716375.316 -1183995.648 + +accept 40.8135756255 -19.5974702869 +expect 2275225.833 -1144577.227 + +accept 50.6799989922 -19.0578134683 +expect 2842703.974 -1132076.757 + +accept 60.3954481899 -18.9092744312 +expect 3411747.962 -1147888.925 + +accept 70.4453750849 -18.1833498293 +expect 4016856.952 -1133502.787 + +accept 80.7669954500 -18.0082544937 +expect 4655570.265 -1161429.470 + +accept 90.3894774181 -17.1937702409 +expect 5275686.661 -1150673.533 + +accept 100.6108192539 -16.6801050032 +expect 5961256.060 -1170474.059 + +accept 110.0973977891 -16.5576038471 +expect 6627078.423 -1224759.106 + +accept 120.1744128824 -16.1385356593 +expect 7378167.388 -1276012.928 + +accept 130.9958002425 -15.9988390562 +expect 8242075.942 -1381187.077 + +accept 140.7561364481 -15.5140276310 +expect 9095834.646 -1478681.653 + +accept 150.6609169826 -14.6810594541 +expect 10062376.739 -1592616.483 + +accept 160.4729986277 -13.9641207869 +expect 11146505.848 -1801865.434 + +accept 170.2580177319 -13.4019318955 +expect 12380828.321 -2216425.290 + +accept 180.9723789714 -13.2206420941 +expect -13517837.926 -2963599.665 + +accept -179.3731911762 -19.9754368483 +expect -12847531.905 -3741083.544 + +accept -169.7161399275 -19.5104972567 +expect -11896617.727 -2931048.939 + +accept -159.8730648764 -19.1040998843 +expect -10868152.967 -2367829.374 + +accept -149.7374470773 -18.6126354884 +expect -9867014.825 -1975013.098 + +accept -139.9375452903 -17.7675516184 +expect -8981245.860 -1675376.283 + +accept -129.7815708228 -17.6801059141 +expect -8119555.541 -1510161.646 + +accept -119.9989025368 -16.8565473881 +expect -7357650.403 -1331626.610 + +accept -109.4668005307 -16.5581522944 +expect -6581807.300 -1220164.823 + +accept -99.7153754334 -15.6910883516 +expect -5906017.650 -1094954.342 + +accept -89.4547033980 -15.0712716331 +expect -5224831.274 -1002148.542 + +accept -79.7184992899 -14.3419936196 +expect -4603946.126 -917271.900 + +accept -69.1303448651 -14.1075269728 +expect -3949591.288 -871282.018 + +accept -59.3174344116 -13.5400304440 +expect -3361462.250 -813740.055 + +accept -49.5741035561 -13.3755321856 +expect -2789994.364 -786454.715 + +accept -39.3784173894 -12.8080837456 +expect -2203910.826 -739131.430 + +accept -29.0461086285 -11.9729370492 +expect -1619004.622 -680848.917 + +accept -19.4469305321 -11.4082175114 +expect -1081054.702 -642575.882 + +accept -9.8174680795 -10.9835914719 +expect -544889.181 -615082.754 + +accept 0.5832005675 -10.8244025391 +expect 32351.518 -604970.269 + +accept 10.1821355116 -10.3112442518 +expect 565312.650 -577140.866 + +accept 20.2673282337 -9.6748015367 +expect 1127704.663 -544392.415 + +accept 30.6144784184 -9.1704395398 +expect 1709477.558 -521063.652 + +accept 40.9538042404 -8.8387995357 +expect 2298042.701 -509435.004 + +accept 50.3542282310 -8.5281632402 +expect 2841826.819 -499951.327 + +accept 60.3333585361 -8.0618147294 +expect 3430944.123 -483342.239 + +accept 70.6491293618 -7.1912559947 +expect 4056498.671 -443543.575 + +accept 80.6385279900 -6.8438270481 +expect 4680976.678 -436529.709 + +accept 90.2779063923 -5.9477536652 +expect 5306879.206 -394211.155 + +accept 100.1664295698 -5.3498071015 +expect 5976711.329 -371637.191 + +accept 110.7526797683 -5.0501900022 +expect 6732766.131 -372725.699 + +accept 120.9395236376 -4.1674482190 +expect 7511218.339 -330114.528 + +accept 130.9122756981 -3.3925202280 +expect 8335031.912 -292646.657 + +accept 140.8622591730 -2.6308107403 +expect 9239878.756 -252678.754 + +accept 150.8989218927 -2.4542899296 +expect 10271402.485 -272075.289 + +accept 160.6447981803 -1.8622854664 +expect 11459900.021 -252282.512 + +accept 170.0641946050 -1.8083280762 +expect 12937298.765 -340309.531 + +accept 180.2204067090 -0.9025887443 +expect -15802698.304 -708580.364 + +accept -179.6574960805 -9.4004926352 +expect -14071690.323 -2539687.191 + +accept -169.9633593449 -9.3485472828 +expect -12600921.441 -1621732.042 + +accept -159.8318959140 -9.0791301932 +expect -11229195.403 -1182926.612 + +accept -149.3318176481 -8.2240809358 +expect -10048132.890 -884380.748 + +accept -139.7502220731 -8.1745353165 +expect -9100377.717 -773484.631 + +accept -129.6865020082 -7.4201521250 +expect -8212868.503 -632791.243 + +accept -119.8341799056 -7.3634864983 +expect -7413306.708 -578750.081 + +accept -109.3372260355 -6.6955324842 +expect -6624672.935 -490071.015 + +accept -99.2316583050 -6.5126588093 +expect -5909647.259 -450435.980 + +accept -89.1084063299 -5.8865293624 +expect -5229890.468 -388173.334 + +accept -79.5816448608 -4.9451058817 +expect -4616528.397 -313937.264 + +accept -69.7454406840 -4.1892409654 +expect -4004251.255 -257320.628 + +accept -59.1123214226 -3.4679775330 +expect -3362108.879 -206832.813 + +accept -49.6901763540 -2.4894793557 +expect -2807061.182 -145333.833 + +accept -39.7692356409 -2.0816660918 +expect -2233545.578 -119370.078 + +accept -29.1740796470 -2.0439733787 +expect -1630731.974 -115537.821 + +accept -19.4550859282 -1.5656815613 +expect -1084212.955 -87689.967 + +accept -9.7922127612 -1.0233548058 +expect -544742.779 -57002.643 + +accept 0.9474400360 -0.4509174844 +expect 52675.338 -25070.513 + +accept 10.5907531926 0.4791919877 +expect 589235.938 26699.153 + +accept 20.4649033892 1.2637472104 +expect 1140802.061 70831.592 + +accept 30.7336324422 1.4750458750 +expect 1719011.039 83523.161 + +accept 40.5647880296 1.7200843296 +expect 2279244.812 98755.090 + +accept 50.0975358484 2.4206654883 +expect 2830862.498 141431.370 + +accept 60.5029363117 2.7469759008 +expect 3445462.137 164373.737 + +accept 70.2466016765 3.0425721977 +expect 4035802.541 187111.823 + +accept 80.9459783007 3.1839332730 +expect 4704897.806 203036.380 + +accept 90.5736386070 3.8575317925 +expect 5329437.218 255822.534 + +accept 100.2061055405 4.6282142532 +expect 5980767.943 321504.886 + +accept 110.3986145890 5.0514461622 +expect 6706729.320 371990.508 + +accept 120.2464205180 5.7344268609 +expect 7451933.549 451953.904 + +accept 130.8487160288 6.0228137911 +expect 8319391.922 519216.608 + +accept 140.7982222585 6.8874824898 +expect 9210050.487 660196.582 + +accept 150.4954910508 7.7970420656 +expect 10177774.848 854370.803 + +accept 160.4294052652 8.2748648014 +expect 11325245.938 1096444.978 + +accept 170.1405366440 9.1299598915 +expect 12640596.907 1598916.038 + +accept 180.3816637148 9.5293212197 +expect -14048784.308 2552595.319 + +accept -179.7984994410 0.1294010891 +expect -16146197.188 164013.021 + +accept -169.9075691637 0.6099770177 +expect -12921454.488 114297.725 + +accept -159.8158750237 0.7517410096 +expect -11353744.941 99828.675 + +accept -149.2779203136 0.9860535478 +expect -10098753.673 106507.361 + +accept -139.0081254157 1.7045245488 +expect -9066221.814 160142.630 + +accept -129.4952290080 2.3401856085 +expect -8216193.058 199202.767 + +accept -119.6801874031 2.7438994101 +expect -7415046.859 215250.709 + +accept -109.8061814794 3.3602853603 +expect -6666538.461 246453.448 + +accept -99.4072778605 4.2027372999 +expect -5926167.742 290710.723 + +accept -89.4794192866 4.3761667210 +expect -5256523.244 288879.125 + +accept -79.0507721337 5.0765421386 +expect -4582771.048 321680.244 + +accept -69.6180300324 6.0161102274 +expect -3994695.190 369666.719 + +accept -59.1757895169 6.6698983501 +expect -3363484.198 398396.059 + +accept -49.5137470044 7.3709595382 +expect -2793929.460 431038.915 + +accept -39.8808038489 7.5057731743 +expect -2237570.835 431453.557 + +accept -29.1954492510 7.5302153709 +expect -1630244.834 426646.570 + +accept -19.1423038522 7.9594801079 +expect -1065438.676 446891.077 + +accept -9.1680642851 8.1530010191 +expect -509342.190 455357.512 + +accept 0.1739948339 8.7651393303 +expect 9659.433 488991.424 + +accept 10.6214035743 9.0690282061 +expect 590010.824 507153.456 + +accept 20.6145954060 9.2785749606 +expect 1147299.618 522064.856 + +accept 30.7022010899 9.4344357510 +expect 1714266.145 536235.946 + +accept 40.6920436024 9.6859857008 +expect 2282298.409 558407.819 + +accept 50.0779641219 10.4703837993 +expect 2823489.061 614432.905 + +accept 60.7862954507 10.8675657776 +expect 3453869.707 653748.275 + +accept 70.7316745409 11.5482616810 +expect 4053689.320 714785.183 + +accept 80.5969392822 11.6377813068 +expect 4667778.775 744691.155 + +accept 90.7414911957 12.5339468232 +expect 5319510.529 835981.260 + +accept 100.1954199623 13.4045312190 +expect 5951099.489 935712.315 + +accept 110.4203894507 14.1556288162 +expect 6667631.291 1047159.283 + +accept 120.0358650247 14.9021123434 +expect 7379023.541 1176295.202 + +accept 130.5020570647 15.6380412243 +expect 8205734.023 1344014.139 + +accept 140.0437745429 16.3999265763 +expect 9015988.290 1549714.704 + +accept 150.1545875606 17.2596103884 +expect 9945182.384 1847801.749 + +accept 160.8486565585 17.2719148256 +expect 11052859.415 2202730.719 + +accept 170.5318144721 17.4322460420 +expect 12132375.221 2742993.334 + +accept 180.3687704920 18.1343689010 +expect -13055638.227 3577371.152 + +accept -179.0206911010 10.9105813395 +expect -13789973.226 2666800.487 + +accept -169.3809207620 11.7944347582 +expect -12368824.571 1941584.924 + +accept -159.6203666385 12.0516777085 +expect -11114668.475 1542870.752 + +accept -149.3198962805 12.6359180213 +expect -9971639.315 1349414.366 + +accept -139.5233350739 13.5860975895 +expect -9014835.165 1278723.421 + +accept -129.7243722634 14.3491243406 +expect -8156662.238 1224564.849 + +accept -119.1501250857 14.5216128511 +expect -7314420.668 1138694.060 + +accept -109.0371133259 15.0539906817 +expect -6561854.668 1105088.504 + +accept -99.9179969843 15.7258631745 +expect -5919649.304 1098546.238 + +accept -89.9773066160 16.3601816158 +expect -5252963.299 1091861.766 + +accept -79.9963611264 16.9875171909 +expect -4611500.971 1091043.258 + +accept -69.8357048214 17.5002106365 +expect -3982187.532 1087834.694 + +accept -59.7680108732 18.2166379428 +expect -3376669.886 1102916.942 + +accept -49.6200663069 18.3027049517 +expect -2783116.693 1083573.630 + +accept -39.6880697045 18.9352023262 +expect -2212450.852 1102685.583 + +accept -29.7767726581 19.4447171460 +expect -1652161.342 1118356.380 + +accept -19.4001109678 19.4907667291 +expect -1073007.877 1110251.090 + +accept -9.2283971215 19.7074346080 +expect -509423.729 1116879.960 + +accept 0.4114648584 20.3715526988 +expect 22689.511 1154092.449 + +accept 10.2069053765 20.5961272596 +expect 563089.088 1169589.074 + +accept 20.2755153863 21.5012785791 +expect 1119736.801 1230222.492 + +accept 30.7133762328 21.6331993543 +expect 1701464.196 1250677.021 + +accept 40.8056510679 21.9458705640 +expect 2269913.206 1287337.418 + +accept 50.1343812652 22.7322724754 +expect 2801331.228 1358020.376 + +accept 60.8279717815 23.6042650645 +expect 3421001.843 1446782.481 + +accept 70.8396198168 23.6161521840 +expect 4017785.556 1487799.299 + +accept 80.0150193753 24.3176168344 +expect 4575413.559 1580619.042 + +accept 90.0092672617 25.2892002351 +expect 5198545.082 1711055.973 + +accept 100.9112248813 25.3488952172 +expect 5911825.956 1802460.981 + +accept 110.6217357170 26.3130477559 +expect 6564636.348 1974281.767 + +accept 120.8513137656 27.2915307690 +expect 7281361.160 2187092.016 + +accept 130.4863483330 27.3717431335 +expect 8004199.764 2357290.762 + +accept 140.8500841767 27.7063050682 +expect 8819787.218 2617012.258 + +accept 150.1012624908 28.3597926319 +expect 9571394.994 2952268.869 + +accept 160.6866246900 28.7569504248 +expect 10470505.204 3415900.295 + +accept 170.4254637193 28.9327804979 +expect 11304554.466 3961238.714 + +accept 180.5169825410 29.4454995234 +expect -11997898.864 4629333.323 + +accept -179.9618334794 20.6361252181 +expect -12837832.007 3860315.017 + +accept -169.0544424157 21.5377240973 +expect -11689194.203 3115133.126 + +accept -159.5209982370 22.3343317585 +expect -10685600.977 2702674.102 + +accept -149.4879824588 23.0136475051 +expect -9708605.390 2409089.845 + +accept -139.0901904339 23.0833518677 +expect -8794463.480 2149634.537 + +accept -129.5571592628 23.7534110208 +expect -8003843.252 2027714.928 + +accept -119.3172537231 24.1047564008 +expect -7218181.243 1905508.759 + +accept -109.6905533732 24.4734381465 +expect -6521952.824 1821700.764 + +accept -99.8910561910 25.1938730946 +expect -5845160.963 1781991.443 + +accept -89.4462296168 25.8863398753 +expect -5157803.362 1749387.629 + +accept -79.8103593109 26.1645676522 +expect -4550997.853 1705809.308 + +accept -69.6977621502 26.6287263876 +expect -3933504.408 1682619.842 + +accept -59.8282049770 27.3537990267 +expect -3346444.748 1686544.376 + +accept -49.1454058737 27.7787492517 +expect -2728032.304 1675388.053 + +accept -39.3139281426 28.6345630085 +expect -2168591.423 1702020.943 + +accept -29.4615301172 29.5694962171 +expect -1616690.922 1739665.855 + +accept -19.8075997700 29.9373943713 +expect -1083433.316 1747604.085 + +accept -9.6080286962 30.8618954465 +expect -523974.378 1796510.976 + +accept 0.2364142241 31.2844282188 +expect 12878.465 1820358.353 + +accept 10.8007276009 32.1455048270 +expect 588003.896 1879462.172 + +accept 20.9914890120 32.9845571828 +expect 1143393.118 1945198.869 + +accept 30.5649011670 33.3185372073 +expect 1668534.713 1985021.750 + +accept 40.9928890043 33.9545285454 +expect 2244749.779 2055570.711 + +accept 50.5960695735 34.5487194112 +expect 2781374.579 2130923.541 + +accept 60.7989209340 34.6635486089 +expect 3363023.887 2186540.557 + +accept 70.4270746519 35.2029582655 +expect 3919596.726 2281175.829 + +accept 80.5548872020 35.6698817700 +expect 4518398.282 2388864.639 + +accept 90.4057879600 36.1979951235 +expect 5114553.759 2517121.960 + +accept 100.9114892728 36.5847249808 +expect 5770875.600 2663935.228 + +accept 110.7309087130 37.4367897060 +expect 6394670.813 2868606.494 + +accept 120.3064304930 38.2603562874 +expect 7018617.432 3101989.836 + +accept 130.7451971478 39.2489093279 +expect 7711939.115 3412678.003 + +accept 140.5750239477 39.4115866274 +expect 8403813.025 3690760.565 + +accept 150.9679439696 39.5896177639 +expect 9149846.161 4055027.485 + +accept 160.6524332599 40.0167951770 +expect 9830011.049 4497209.502 + +accept 170.4722281589 40.1946244182 +expect 10510426.457 5006824.562 + +accept 180.6722342584 40.4624940717 +expect -11073707.514 5547471.043 + +accept -179.2831936865 30.3756797354 +expect -11903513.624 4695492.667 + +accept -169.3418436736 30.6105366204 +expect -11096888.315 4057764.336 + +accept -159.1006283258 31.5893799475 +expect -10184786.130 3622256.417 + +accept -149.1140289956 31.8719516081 +expect -9350613.109 3255936.184 + +accept -139.6996779260 31.9484140590 +expect -8599676.794 2977225.067 + +accept -129.5556705121 32.3449943633 +expect -7818577.975 2772332.434 + +accept -119.9724472555 32.9392845832 +expect -7113428.652 2642433.850 + +accept -109.7343311582 33.1372998541 +expect -6404617.901 2502642.905 + +accept -99.9994915413 33.6225969013 +expect -5754979.598 2420019.593 + +accept -89.2957244981 34.5626482766 +expect -5064999.094 2381966.292 + +accept -79.0486888283 34.5979570645 +expect -4437892.568 2298043.032 + +accept -69.3692667775 34.8321767388 +expect -3860329.808 2247797.371 + +accept -59.5430097857 35.6428903265 +expect -3284552.144 2248836.044 + +accept -49.1028389556 36.0212581738 +expect -2689697.467 2226461.965 + +accept -39.5707710224 36.0991805410 +expect -2157256.671 2196683.027 + +accept -29.2950741715 36.7864032717 +expect -1588948.254 2214928.423 + +accept -19.8971059232 37.5191207820 +expect -1075080.881 2246281.883 + +accept -9.1855199225 38.0327790292 +expect -495017.437 2268788.188 + +accept 0.3119584346 38.1079667572 +expect 16801.964 2270513.428 + +accept 10.9783425364 38.7495700245 +expect 590921.384 2319580.204 + +accept 20.7896762683 39.3106928548 +expect 1119567.208 2371677.736 + +accept 30.1643265779 40.2243862141 +expect 1625278.990 2456916.428 + +accept 40.5328985021 41.0696713596 +expect 2188008.492 2551119.506 + +accept 50.2300300588 41.3865813447 +expect 2721620.789 2616277.410 + +accept 60.6521695116 42.3133443814 +expect 3298047.285 2744030.130 + +accept 70.7309222598 43.1274592749 +expect 3863356.586 2878383.982 + +accept 80.8866702938 43.7702855800 +expect 4443854.438 3018951.902 + +accept 90.5499599785 44.5851564761 +expect 5002600.615 3190179.530 + +accept 100.5821513570 44.6219279790 +expect 5608351.970 3324185.783 + +accept 110.0283698987 45.0603376392 +expect 6183798.502 3510751.426 + +accept 120.9059581322 45.6167638758 +expect 6858556.379 3768914.576 + +accept 130.8382094225 46.2623868954 +expect 7479609.418 4061298.725 + +accept 140.3050870951 46.3222134245 +expect 8098901.115 4333302.419 + +accept 150.7692866141 46.9256636829 +expect 8759282.574 4745456.067 + +accept 160.6423483126 47.3983214513 +expect 9371550.773 5189253.049 + +accept 170.3660263984 48.1704941243 +expect 9921744.591 5715878.904 + +accept 180.1728485128 48.3869676380 +expect -10452810.767 6233145.886 + +accept -179.3366303542 40.5764857623 +expect -11064985.137 5557446.981 + +accept -169.9016120813 40.9858722485 +expect -10415017.640 5046984.188 + +accept -159.8421069000 41.3352033138 +expect -9693683.857 4584354.161 + +accept -149.9878180879 41.8238911228 +expect -8970272.939 4230268.129 + +accept -139.8603273594 42.3826076660 +expect -8236029.698 3946841.156 + +accept -129.0166066782 42.7333243644 +expect -7482247.804 3687948.837 + +accept -119.3388991582 43.6373394362 +expect -6814813.949 3556870.147 + +accept -109.3805129308 43.9116610208 +expect -6170341.281 3398585.005 + +accept -99.7947564839 44.3361622416 +expect -5565812.101 3288505.423 + +accept -89.9466466666 45.2719659306 +expect -4955176.241 3240762.956 + +accept -79.0227489515 46.1464400004 +expect -4302718.131 3195776.905 + +accept -69.8148943004 46.5943287210 +expect -3771570.667 3149477.147 + +accept -59.7413792811 47.1807640935 +expect -3202028.794 3121345.514 + +accept -49.8815279751 47.1898937016 +expect -2660293.312 3061471.431 + +accept -39.6116669497 47.2588276162 +expect -2103308.236 3017259.465 + +accept -29.2023113842 48.0824833396 +expect -1541812.718 3044777.159 + +accept -19.5149453395 48.6918139540 +expect -1026306.480 3068551.199 + +accept -9.6336458263 49.0434719530 +expect -505461.907 3081586.975 + +accept 0.4543975995 49.7836456546 +expect 23777.504 3136347.935 + +accept 10.9700141403 50.3323164289 +expect 573327.428 3187475.308 + +accept 20.2945040946 50.5294422839 +expect 1061285.060 3219471.341 + +accept 30.9938167603 50.9444135429 +expect 1622296.392 3284285.467 + +accept 40.8850179873 51.0892523800 +expect 2145326.320 3336819.857 + +accept 50.8160896333 51.7186327718 +expect 2670455.247 3444230.679 + +accept 60.5975018363 51.7472739987 +expect 3198356.358 3513374.312 + +accept 70.9211780937 52.2961472542 +expect 3755656.007 3649240.500 + +accept 80.4886876286 52.4940883205 +expect 4283198.775 3765359.201 + +accept 90.6063621158 52.8917619975 +expect 4845216.723 3926850.931 + +accept 100.0213726456 53.0072065012 +expect 5381474.017 4075643.010 + +accept 110.7666710262 53.1761320615 +expect 6002387.050 4279156.043 + +accept 120.4133749558 53.6140202991 +expect 6556934.536 4522304.309 + +accept 130.4689014096 54.3464457214 +expect 7124048.584 4841940.061 + +accept 140.1319502152 55.1999540459 +expect 7653482.073 5205936.544 + +accept 150.0406833955 55.2259750645 +expect 8231914.631 5536917.537 + +accept 160.6632656727 55.6152942863 +expect 8813145.718 5981687.838 + +accept 170.1022885868 55.6436461375 +expect 9328803.345 6391660.249 + +accept 180.5533384236 56.5237207562 +expect -9735738.829 6914894.208 + +accept -179.2247622429 50.9111169957 +expect -10208607.447 6413665.425 + +accept -169.7924972767 51.7536923319 +expect -9617111.948 6014189.958 + +accept -159.6893113052 52.2420602240 +expect -8992940.568 5611018.295 + +accept -149.2250378095 52.6608417101 +expect -8338627.076 5249471.462 + +accept -139.5235597836 52.6880191938 +expect -7748063.996 4931880.210 + +accept -129.8722920081 52.7891165561 +expect -7157279.641 4668965.541 + +accept -119.6585401358 53.3027824621 +expect -6523737.589 4474430.220 + +accept -109.4717350642 53.5457403772 +expect -5914835.972 4290989.912 + +accept -99.3049585904 53.8711625087 +expect -5317309.229 4147683.328 + +accept -89.1067456005 53.9601597136 +expect -4736479.726 4008096.136 + +accept -79.6629562504 54.6547978698 +expect -4196286.812 3958062.567 + +accept -69.8279241005 55.2371531832 +expect -3648862.266 3910629.370 + +accept -59.9635334446 55.6192322641 +expect -3113545.379 3860627.969 + +accept -49.9264269173 56.5572332986 +expect -2571251.312 3877332.211 + +accept -39.0317197762 56.9105729373 +expect -2000116.599 3848938.560 + +accept -29.8485414955 57.1405754683 +expect -1524573.629 3831116.411 + +accept -19.7173893031 57.8895638096 +expect -1001946.186 3871323.208 + +accept -9.5284598326 58.3169856731 +expect -482748.075 3893995.092 + +accept 0.5519708680 59.1698787655 +expect 27843.757 3971499.805 + +accept 10.3102576934 59.9070126591 +expect 518383.957 4051426.264 + +accept 20.2471682871 60.1178484569 +expect 1017884.749 4092029.150 + +accept 30.7186291790 60.9667316568 +expect 1539950.301 4213756.217 + +accept 40.5361666024 61.0726031598 +expect 2035176.439 4271402.857 + +accept 50.1593218026 61.5209991783 +expect 2518306.294 4378497.037 + +accept 60.6426706803 61.6933237333 +expect 3051461.747 4479017.707 + +accept 70.0635629430 62.5101492783 +expect 3519070.146 4659116.189 + +accept 80.7525125683 62.7158368686 +expect 4066565.729 4805766.003 + +accept 90.3486210746 63.2666959744 +expect 4549151.501 5000629.808 + +accept 100.3103261370 63.6630728713 +expect 5055042.336 5206928.884 + +accept 110.4333486366 64.5625075877 +expect 5544588.666 5503654.246 + +accept 120.2948792100 65.1058556815 +expect 6028251.873 5782340.030 + +accept 130.5927832047 65.6748652993 +expect 6522940.319 6105850.437 + +accept 140.6012624076 66.3132526636 +expect 6983093.737 6463380.696 + +accept 150.8560042350 67.2695080089 +expect 7407700.123 6899834.886 + +accept 160.4312759692 67.7825785445 +expect 7809736.690 7288594.640 + +accept 170.6182278472 67.8589725624 +expect 8257493.223 7675010.400 + +accept 180.6346540849 67.9886371340 +expect -8615169.523 8037735.613 + +accept -179.3862724658 60.0765397412 +expect -9409858.860 7237943.395 + +accept -169.8319368721 60.7048897911 +expect -8892462.330 6874089.420 + +accept -159.7586035106 60.8099423764 +expect -8373658.128 6477781.193 + +accept -149.4451039042 60.8141416010 +expect -7830654.586 6105061.877 + +accept -139.0047357806 61.2863951253 +expect -7238746.216 5824802.025 + +accept -129.8203349111 61.6531963428 +expect -6721103.700 5609206.409 + +accept -119.9476298931 62.1392105751 +expect -6163537.132 5422258.227 + +accept -109.5579719203 62.6892094638 +expect -5582691.055 5264366.147 + +accept -99.2658471038 63.6397776749 +expect -5001335.481 5186237.501 + +accept -89.3190408834 64.2596249008 +expect -4462613.903 5101850.571 + +accept -79.8712042695 64.9388669541 +expect -3957350.660 5053673.793 + +accept -69.5143458832 64.9984151350 +expect -3431555.358 4940124.081 + +accept -59.7549744076 65.5198053592 +expect -2930531.346 4907235.702 + +accept -49.4522584709 65.6036762180 +expect -2417789.843 4834516.760 + +accept -39.6862901889 66.0522555435 +expect -1930302.252 4825742.348 + +accept -29.0676277333 66.9411073706 +expect -1402365.485 4882358.630 + +accept -19.2947838600 67.6760455583 +expect -924758.269 4940769.295 + +accept -9.8908486233 68.4759462496 +expect -470791.969 5023788.451 + +accept 0.7684849115 68.4820083161 +expect 36570.245 5017696.478 + +accept 10.9619070788 69.1271846096 +expect 518988.202 5110914.008 + +accept 20.8669367778 69.4597522731 +expect 985678.393 5178041.292 + +accept 30.9015874823 70.2228485919 +expect 1450977.848 5320350.646 + +accept 40.3644592677 70.4447947660 +expect 1893313.797 5401286.949 + +accept 50.4426463863 70.9370001765 +expect 2357733.911 5540352.544 + +accept 60.8571701571 71.2042128565 +expect 2840863.185 5668499.100 + +accept 70.2207367933 71.7513035300 +expect 3263071.274 5846454.476 + +accept 80.9197701425 72.2949102709 +expect 3742012.412 6060269.554 + +accept 90.9968622399 72.8817626201 +expect 4182037.591 6296350.257 + +accept 100.6140322356 73.0673811891 +expect 4615350.939 6484139.751 + +accept 110.0937440863 73.5055307300 +expect 5020529.626 6730333.834 + +accept 120.1366082758 74.4237054204 +expect 5401515.154 7088596.371 + +accept 130.7310123929 74.8225035758 +expect 5829427.956 7399976.843 + +accept 140.7557697839 75.3904414554 +expect 6198183.044 7749882.172 + +accept 150.8261707368 75.4797965998 +expect 6601790.754 8045032.409 + +accept 160.3617186765 76.0800713976 +expect 6904851.750 8427523.609 + +accept 170.3723095047 76.9487430385 +expect 7160097.253 8883978.785 + +accept 180.8353501206 77.0774046082 +expect -7451033.788 9196865.430 + +accept -179.7332624578 70.4393887237 +expect -8352576.455 8331537.300 + +accept -169.3475485875 71.1990434220 +expect -7846291.115 8032842.415 + +accept -159.2878573247 71.7560324305 +expect -7364104.304 7757743.784 + +accept -149.0633836490 72.7063403889 +expect -6827251.647 7569191.962 + +accept -139.6942131577 73.5163807564 +expect -6341247.119 7423166.084 + +accept -129.8134586050 73.9643420027 +expect -5867435.003 7238589.421 + +accept -119.5111667574 74.4925642590 +expect -5368260.354 7086271.451 + +accept -109.3071655658 74.6108323823 +expect -4906478.988 6896010.407 + +accept -99.5978241183 75.1275985390 +expect -4438705.543 6806711.516 + +accept -89.2137798701 75.6445071712 +expect -3946153.966 6730726.096 + +accept -79.1346028255 76.0354738901 +expect -3479800.008 6660516.066 + +accept -69.0638837521 76.5148306463 +expect -3014377.593 6628057.905 + +accept -59.1693658687 77.4423799967 +expect -2543880.527 6705578.610 + +accept -49.4634180765 78.1864565763 +expect -2099555.264 6773693.535 + +accept -39.8508153072 78.5203765703 +expect -1681563.114 6777405.489 + +accept -29.6114412219 78.7797877191 +expect -1243660.143 6778541.162 + +accept -19.1867860823 79.6454776348 +expect -792468.866 6932699.289 + +accept -9.9582630982 80.1780113525 +expect -406784.347 7037337.973 + +accept 0.8045877956 80.9823584569 +expect 32275.315 7229013.962 + +accept 10.6198537389 81.4079136814 +expect 421565.709 7348340.200 + +accept 20.7119981828 82.2150056342 +expect 804489.400 7594469.330 + +accept 30.3505244935 82.6478465607 +expect 1163485.615 7757650.909 + +accept 40.8655993799 82.9850228503 +expect 1548903.569 7915659.478 + +accept 50.7222864861 83.6349635667 +expect 1878353.123 8190395.486 + +accept 60.9484292190 84.1578297365 +expect 2209073.061 8452785.649 + +accept 70.9281849579 84.2418711822 +expect 2556434.364 8578888.109 + +accept 80.6700039410 84.7042458603 +expect 2843072.410 8854459.520 + +accept 90.2195191862 85.4123304310 +expect 3062193.052 9248148.617 + +accept 100.8308186795 86.2993989176 +expect 3233512.836 9778762.722 + +accept 110.2610774864 87.2488149984 +expect 3271018.801 10404221.588 + +accept 120.0282638556 87.9552070544 +expect 3287869.350 10991486.485 + +accept 130.8896115077 88.8068669405 +expect 3109942.957 11856621.688 + +accept 140.8963110473 89.7228156694 +expect 2304894.417 13439579.058 + +accept 150.4318097094 90.4391164019 +expect failure errno coord_transfm_invalid_coord + +accept 160.7127363327 90.6605815936 +expect failure errno coord_transfm_invalid_coord + +accept 170.7492804294 90.8176634524 +expect failure errno coord_transfm_invalid_coord + +accept 180.4583398460 90.9109976584 +expect failure errno coord_transfm_invalid_coord + +accept -179.7591323884 80.4424115493 +expect -6903393.952 9786682.886 + +accept -169.8822070628 80.7500588713 +expect -6531575.755 9548129.356 + +accept -159.9945746863 80.9496010459 +expect -6169272.314 9309595.559 + +accept -149.9417464353 81.4902616414 +expect -5735389.987 9165799.932 + +accept -139.6978190662 82.2239127957 +expect -5260169.587 9097892.645 + +accept -129.3012716885 83.0429415409 +expect -4766013.283 9092128.730 + +accept -119.1898998617 83.3279294780 +expect -4372133.574 8977351.665 + +accept -109.9862107641 83.5308131176 +expect -4021828.937 8875471.473 + +accept -99.5860433196 84.1789855873 +expect -3565728.493 8919632.792 + +accept -89.2677089488 84.1934914271 +expect -3206581.559 8779174.807 + +accept -79.3268775666 84.9722014413 +expect -2762678.704 8940646.845 + +accept -69.8212102616 85.0076410054 +expect -2434287.034 8852607.599 + +accept -59.3204702001 85.5856170742 +expect -2014364.675 8995544.515 + +accept -49.6961911803 86.4721128657 +expect -1603812.213 9344086.957 + +accept -39.6159279805 86.7872101641 +expect -1252848.694 9454262.547 + +accept -29.6502801920 87.1555761043 +expect -912288.029 9626048.356 + +accept -19.4496938483 87.5951755895 +expect -575538.601 9883989.014 + +accept -9.7937838350 88.5280609570 +expect -257529.241 10651674.770 + +accept 0.9510780379 88.6715513574 +expect 24396.904 10799028.318 + +accept 10.4788996031 89.0882607425 +expect 245024.182 11333021.357 + +accept 20.0454282778 89.3798616914 +expect 425823.124 11838688.204 + +accept 30.8762585995 89.9894216623 +expect 237234.260 14954822.615 + +accept 40.2272544657 90.2854737833 +expect failure errno coord_transfm_invalid_coord + +accept 50.3342208278 90.6201781373 +expect failure errno coord_transfm_invalid_coord + +accept 60.0620171885 91.1323497706 +expect failure errno coord_transfm_invalid_coord + +accept 70.7871678571 91.2021231110 +expect failure errno coord_transfm_invalid_coord + +accept 80.7237355733 91.8207335323 +expect failure errno coord_transfm_invalid_coord + +accept 90.5359055804 91.8495346522 +expect failure errno coord_transfm_invalid_coord + +accept 100.2370378259 92.3201685216 +expect failure errno coord_transfm_invalid_coord + +accept 110.7018248262 92.7082390660 +expect failure errno coord_transfm_invalid_coord + +accept 120.4791498907 92.8320642395 +expect failure errno coord_transfm_invalid_coord + +accept 130.7292413039 93.7863129954 +expect failure errno coord_transfm_invalid_coord + +accept 140.1002623482 94.2304861566 +expect failure errno coord_transfm_invalid_coord + +accept 150.7401582820 94.4002034978 +expect failure errno coord_transfm_invalid_coord + +accept 160.9690930362 95.0432445572 +expect failure errno coord_transfm_invalid_coord + +accept 170.5238000008 95.9332496636 +expect failure errno coord_transfm_invalid_coord + +accept 180.5593997844 96.9295538910 +expect failure errno coord_transfm_invalid_coord + +accept -179.2788799656 89.7588830795 +expect -2720982.745 13966946.704 + +accept -169.2920835775 90.4241930348 +expect failure errno coord_transfm_invalid_coord + +accept -159.9895526197 91.4107597532 +expect failure errno coord_transfm_invalid_coord + +accept -149.2463523987 92.1662912669 +expect failure errno coord_transfm_invalid_coord + +accept -139.5441662785 93.0270602663 +expect failure errno coord_transfm_invalid_coord + +accept -129.2489121030 93.8575161591 +expect failure errno coord_transfm_invalid_coord + +accept -119.2218964968 94.3245277139 +expect failure errno coord_transfm_invalid_coord + +accept -109.6391371233 94.8605218216 +expect failure errno coord_transfm_invalid_coord + +accept -99.7022656135 95.4188372117 +expect failure errno coord_transfm_invalid_coord + +accept -89.9236110047 96.1459344102 +expect failure errno coord_transfm_invalid_coord + +accept -79.3053114467 96.4284727639 +expect failure errno coord_transfm_invalid_coord + +accept -69.9403123372 97.1204524330 +expect failure errno coord_transfm_invalid_coord + +accept -59.8919954903 98.0976036625 +expect failure errno coord_transfm_invalid_coord + +accept -49.4360361183 99.0132534146 +expect failure errno coord_transfm_invalid_coord + +accept -39.1296215520 99.7082663882 +expect failure errno coord_transfm_invalid_coord + +accept -29.7429317093 100.3804719805 +expect failure errno coord_transfm_invalid_coord + +accept -19.9518617483 100.7090523427 +expect failure errno coord_transfm_invalid_coord + +accept -9.8094666546 100.7731576636 +expect failure errno coord_transfm_invalid_coord + +accept 0.6789399156 101.1890038152 +expect failure errno coord_transfm_invalid_coord + +accept 10.3002789517 101.6303167682 +expect failure errno coord_transfm_invalid_coord + +accept 20.3122591998 101.9746447499 +expect failure errno coord_transfm_invalid_coord + +accept 30.8403181671 102.1868812356 +expect failure errno coord_transfm_invalid_coord + +accept 40.5913833272 102.9224326125 +expect failure errno coord_transfm_invalid_coord + +accept 50.4094599185 103.4226263618 +expect failure errno coord_transfm_invalid_coord + +accept 60.2807542048 103.6337815648 +expect failure errno coord_transfm_invalid_coord + +accept 70.1179652096 103.9375646519 +expect failure errno coord_transfm_invalid_coord + +accept 80.0709906538 104.9002368302 +expect failure errno coord_transfm_invalid_coord + +accept 90.4905546467 104.9051484801 +expect failure errno coord_transfm_invalid_coord + +accept 100.8677613905 105.1778053719 +expect failure errno coord_transfm_invalid_coord + +accept 110.7875504667 106.1651152124 +expect failure errno coord_transfm_invalid_coord + +accept 120.3531685812 106.2777498204 +expect failure errno coord_transfm_invalid_coord + +accept 130.5786379336 106.8735960149 +expect failure errno coord_transfm_invalid_coord + +accept 140.2390732913 107.1035922681 +expect failure errno coord_transfm_invalid_coord + +accept 150.2230766093 107.9665006223 +expect failure errno coord_transfm_invalid_coord + +accept 160.0106199737 108.8731447468 +expect failure errno coord_transfm_invalid_coord + +accept 170.2666681817 109.4591245928 +expect failure errno coord_transfm_invalid_coord + +accept 180.2003062924 109.9750225424 +expect failure errno coord_transfm_invalid_coord + +------------------------------------------------------------------------------- +# Test inverse +------------------------------------------------------------------------------- +operation +proj=adams_ws2 +ellps=WGS84 +------------------------------------------------------------------------------- +direction forward +tolerance 1 mm + +accept 0 0 +expect 0 0 +roundtrip 1 + +accept 40 60 +expect 2021909.611 4162291.966 +roundtrip 1 + +accept 179.999 0 +expect 16686159.356 0.000 +roundtrip 1 + +accept -179.999 0 +expect -16686159.356 0.000 +roundtrip 1 + +accept 0 89.999 +expect 0 15743336.122 +roundtrip 1 + +accept 0 -89.999 +expect 0 -15743336.122 +roundtrip 1 + +# Results a bit different on x86 +tolerance 3 mm +accept 179.999 89.999 +expect 693320.704 16030515.906 +roundtrip 1 + +accept 179.999 -89.999 +expect 693320.702 -16030515.904 +roundtrip 1 + +accept -179.999 89.999 +expect -693320.702 16030515.904 +roundtrip 1 + +# This test fails with "roundtrip deviation: inf mm, expected: 3.000000 mm" on MacOS 13 x64 / clang 16.0.6 +#accept -179.999 -89.999 +#expect -693320.704 -16030515.906 +#roundtrip 1 + +direction inverse +accept 0.000005801264 16722285.492330472916 +expect failure errno coord_transfm_outside_projection_domain + + diff --git a/test/ProjNet.Tests/Fixtures/gie/axisswap.gie b/test/ProjNet.Tests/Fixtures/gie/axisswap.gie new file mode 100644 index 00000000..8555b52a --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/axisswap.gie @@ -0,0 +1,115 @@ +------------------------------------------------------------------------------- + Tests for the axisswap operation +------------------------------------------------------------------------------- + + + +operation proj=axisswap order=1,2,3,4 +tolerance 0.000001 m +accept 1 2 3 4 +expect 1 2 3 4 +roundtrip 100 + +operation proj=axisswap order=4,3,2,1 +tolerance 0.000001 m +accept 1 2 3 4 +expect 4 3 2 1 +roundtrip 100 + +operation proj=axisswap order=-1,-2,-3,-4 +tolerance 0.000001 m +accept 1 2 3 4 +expect -1 -2 -3 -4 +roundtrip 100 + +operation proj=axisswap order=1,2,-3,4 +tolerance 0.000001 m +accept 1 2 3 4 +expect 1 2 -3 4 +roundtrip 100 + +operation proj=axisswap order=-1,2,3,4 +tolerance 0.000001 m +accept 1 2 3 4 +expect -1 2 3 4 +roundtrip 100 + +operation proj=axisswap order=1,2,3,-4 +tolerance 0.000001 m +accept 1 2 3 4 +expect 1 2 3 -4 +roundtrip 100 + +operation proj=axisswap order=-2,1 +tolerance 0.000001 m +accept 1 2 3 4 +expect -2 1 3 4 +roundtrip 100 + +operation proj=axisswap order=3,-2,1 +tolerance 0.000001 m +accept 1 2 3 4 +expect 3 -2 1 4 +roundtrip 100 + +operation proj=axisswap axis=neu +tolerance 0 m +accept 1 2 3 +expect 2 1 3 + +# when using the +axis parameter we specify the order of the INPUT coordinate, +# as opposed to +order which relates to the OUTPUT coordinate. Here we test +# that n(1), u(2) and e(3) are swapped correctly to enu ordering. +operation proj=axisswap axis=nue +tolerance 0 m +accept 1 2 3 +expect 2 3 1 + +operation proj=axisswap axis=swd +tolerance 0.000001 m +accept 1 2 3 4 +expect -2 -1 -3 4 + +operation proj=pipeline \ + step proj=latlong +ellps=WGS84 \ + step proj=axisswap \ + order=1,2,3,4 + +tolerance 0.00001 m +accept 12 55 0 0 +expect 12 55 0 0 + +operation proj=pipeline \ + step proj=latlong +ellps=WGS84 \ + step proj=axisswap \ + order=-2,-1,3,4 + +tolerance 0.00001 m +accept 12 55 0 0 +expect -55 -12 0 0 + +operation proj=axisswap order=1,2,3,4 axis=enu +expect failure pjd_err_axis + +operation proj=axisswap +expect failure pjd_err_axis + +operation proj=axisswap order=1,2,1,4 +expect failure pjd_err_axis + +operation proj=axisswap order=2,3 +expect failure pjd_err_axis + +operation proj=axisswap order=2,3,4 +expect failure pjd_err_axis + +operation proj=axisswap order=4,3,-2,1 +tolerance 0.000001 m +accept 1 2 3 4 +expect 4 3 -2 1 +roundtrip 100 + +operation proj=axisswap order=1,2,3,5 +expect failure pjd_err_axis + + diff --git a/test/ProjNet.Tests/Fixtures/gie/builtins.gie b/test/ProjNet.Tests/Fixtures/gie/builtins.gie new file mode 100644 index 00000000..886bd0eb --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/builtins.gie @@ -0,0 +1,8352 @@ +=============================================================================== + +Test material, mostly converted from selftest entries in PJ_xxx.c + +Most of this material was autogenerated, and does not attempt to exercise +corner cases etc. + +See more_builtins.gie for some test cases with a more human touch. + +=============================================================================== + + +# First test non strict gie + + + +operation +proj=aea + +ellps=GRS80 +lat_1=0 +lat_2=2 +tolerance 0.1 mm +accept 2 1 +expect 222571.608757106 110653.326743030 + +unknown_keyword + + + + + + +=============================================================================== +# Albers Equal Area +# Conic Sph&Ell +# lat_1= lat_2= +=============================================================================== +------------------------------------------------------------------------------- +operation +proj=aea +ellps=GRS80 +lat_1=0 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222571.608757106 110653.326743030 +accept 2 -1 +expect 222706.306508391 -110484.267144400 +accept -2 1 +expect -222571.608757106 110653.326743030 +accept -2 -1 +expect -222706.306508391 -110484.267144400 +accept 150 50 +expect 16468399.3582 5275043.9815 + +direction inverse +accept 200 100 +expect 0.001796631 0.000904369 +accept 200 -100 +expect 0.001796630 -0.000904370 +accept -200 100 +expect -0.001796631 0.000904369 +accept -200 -100 +expect -0.001796630 -0.000904370 +accept 16468399.3582 5275043.9815 +expect 150 50 + +------------------------------------------------------------------------------- +operation +proj=aea +R=6400000 +lat_1=0 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223334.085170885 111780.431884472 +accept 2 -1 +expect 223470.154991687 -111610.339430990 +accept -2 1 +expect -223334.085170885 111780.431884472 +accept -2 -1 +expect -223470.154991687 -111610.339430990 + +direction inverse +accept 200 100 +expect 0.001790494 0.000895246 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790494 0.000895246 +accept -200 -100 +expect -0.001790493 -0.000895247 + +operation +proj=aea +ellps=GRS80 +lat_1=900 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=aea +ellps=GRS80 +lat_2=900 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=aea +R=6400000 +lat_1=1 +lat_2=-1 +expect failure errno invalid_op_illegal_arg_value + +------------------------------------------------------------------------------- +operation +proj=aea +a=9999999 +b=.9 +lat_2=1 +------------------------------------------------------------------------- +expect failure errno invalid_op_illegal_arg_value + +=============================================================================== +# Azimuthal Equidistant +# Azi, Sph&Ell +# lat_0 guam +=============================================================================== + +------------------------------------------------------------------------------- +# Test equatorial aspect of the spherical azimuthal equidistant. Test data from +# Snyder pp. 196-197, table 30. +------------------------------------------------------------------------------- +operation +proj=aeqd +R=1 +lat_0=0 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 0 +roundtrip 100 +accept 0 90 +expect 0 1.57080 +roundtrip 100 +accept 10 80 +expect 0.04281 1.39829 +roundtrip 100 +accept 40 30 +expect 0.62896 0.56493 +roundtrip 100 +accept 90 0 +expect 1.57080 0 +roundtrip 100 +accept 90 90 +expect 0 1.57080 +roundtrip 100 + +# point opposite projection center is undefined +accept 180 0 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +# Test equatorial aspect of the ellipsoidal azimuthal equidistant. Test data from +# Snyder pp. 196-197, table 30. +------------------------------------------------------------------------------- +operation +proj=aeqd +ellps=GRS80 +lat_0=0 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 90 +expect 0 10001965.7292 +roundtrip 100 +accept 0 0 +expect 0 0 +roundtrip 100 +accept 90 0 +expect 10_018_754.1714 0 +roundtrip 100 +accept 90 0 +expect 10_018_754.1714 0 +roundtrip 100 +accept 45 45 +expect 3_860_398.3783 5_430_089.0490 +roundtrip 100 + + +# Test oblique aeqd with point very close lon_0, lat_0, on a perfect sphere +operation +proj=aeqd +a=6371008.771415 +b=6371008.771415 +lat_0=30.2345 +lon_0=-120.2345 +tolerance 1 mm +accept -120.234501 30.234501 +expect -0.096 0.111 +roundtrip 1 + +accept -120.2345 30.2345 +expect 0.000 0.000 +roundtrip 1 + +# Same on an ellipsoid very close to the sphere +operation +proj=aeqd +a=6371008.771415 +b=6371008.771414 +lat_0=30.2345 +lon_0=-120.2345 +tolerance 1 mm +accept -120.234501 30.234501 +expect -0.096 0.111 +roundtrip 1 + +accept -120.2345 30.2345 +expect 0.000 0.000 +roundtrip 1 + +------------------------------------------------------------------------------- +# Test the Modified Azimuthal Equidistant / EPSG 9832. Test data from the EPSG +# Guidance Note 7 part 2, April 2018, p. 85 +------------------------------------------------------------------------------- +operation +proj=aeqd +ellps=clrk66 +lat_0=9.546708325068591 +lon_0=138.1687444500492 +x_0=40000.00 +y_0=60000.00 +------------------------------------------------------------------------------- +tolerance 1 cm +accept 138.19303001104092 9.596525859439623 +expect 42665.90 65509.82 +roundtrip 100 + +direction inverse +accept 42665.90 65509.82 +expect 138.19303001104092 9.596525859439623 + +------------------------------------------------------------------------------- +# Test the azimuthal equidistant modified for Guam. Test data from the EPSG +# Guidance Note 7 part 2, September 2016, p. 85 +------------------------------------------------------------------------------- +operation +proj=aeqd +guam +ellps=clrk66 +x_0=50000.00 +y_0=50000.00 \ + +lon_0=144.74875069444445 +lat_0=13.47246633333333 +------------------------------------------------------------------------------- +tolerance 1 cm +accept 144.635331291666660 13.33903846111111 +expect 37712.48 35242.00 +roundtrip 100 + +direction inverse +accept 37712.48 35242.00 +expect 144.635331291666660 13.33903846111111 + +------------------------------------------------------------------------------- +# Test northern polar aspect of the ellipsoidal azimuthal equidistant. Test data +# from Snyder p. 198, table 31. +------------------------------------------------------------------------------- +operation +proj=aeqd +ellps=intl +lat_0=90 +------------------------------------------------------------------------------- +tolerance 0.1 m +accept 0 90 +expect 0 0 +roundtrip 100 +accept 0 85 +expect 0 -558_485.4 +roundtrip 100 +accept 0 80 +expect 0 -1_116_885.2 +roundtrip 100 +accept 0 70 +expect 0 -2_233_100.9 +roundtrip 100 + +------------------------------------------------------------------------------- +# Test southern polar aspect of the ellipsoidal azimuthal equidistant. Test data +# from Snyder p. 198, table 31. +------------------------------------------------------------------------------- +operation +proj=aeqd +ellps=intl +lat_0=-90 +------------------------------------------------------------------------------- +tolerance 0.1 m +accept 0 -90 +expect 0 0 +roundtrip 100 +accept 0 -85 +expect 0 558_485.4 +roundtrip 100 +accept 0 -80 +expect 0 1_116_885.2 +roundtrip 100 +accept 0 -70 +expect 0 2_233_100.9 +roundtrip 100 + +------------------------------------------------------------------------------- +# Test northern polar aspect of the spherical azimuthal equidistant. +------------------------------------------------------------------------------- +operation +proj=aeqd +R=1 +lat_0=90 +------------------------------------------------------------------------------- +tolerance 0.1 m +accept 0 0 +expect 0 -1.5708 +roundtrip 100 +accept 0 90 +expect 0 0 +roundtrip 100 +accept 90 90 +expect 0 0 +roundtrip 100 +accept 90 0 +expect 1.5708 0 +roundtrip 100 +accept 45 45 +expect 0.5554 -0.5554 +roundtrip 100 + +#point opposite of projection center is undefined +accept 0 -90 +expect failure errno coord_transfm_outside_projection_domain + +direction inverse +accept 0 5 +expect failure errno coord_transfm_outside_projection_domain + +accept 0 3.14159265359 +expect 180 -90 + +------------------------------------------------------------------------------- +# Test sourthnern polar aspect of the spherical azimuthal equidistant. +------------------------------------------------------------------------------- +operation +proj=aeqd +R=1 +lat_0=-90 +------------------------------------------------------------------------------- +tolerance 0.1 m +accept 0 0 +expect 0 1.5708 +roundtrip 100 +accept 0 -90 +expect 0 0 +roundtrip 100 +accept 90 -90 +expect 0 0 +roundtrip 100 +accept 90 0 +expect 1.5708 0 +roundtrip 100 +accept 45 -45 +expect 0.5554 0.5554 +roundtrip 100 + +#point opposite of projection center is undefined +accept 0 90 +expect failure errno coord_transfm_outside_projection_domain + + +------------------------------------------------------------------------------- +# Test oblique aspect of the spherical azimuthal equidistant. +------------------------------------------------------------------------------- +operation +proj=aeqd +R=1 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.1 m +accept 0 0 +expect 0.0000 -0.7854 +roundtrip 100 +accept 0 45 +expect 0.0000 0.0000 +roundtrip 100 +accept 0 90 +expect 0.0000 0.7854 +roundtrip 100 +accept 90 0 +expect 1.5708 -0.0000 +roundtrip 100 +accept 90 45 +expect 0.8550 0.6046 +#roundtrip 100 # roundtrip performs badly for this test on some platforms +accept 90 90 +expect 0.0000 0.7854 +roundtrip 100 + +------------------------------------------------------------------------------- +# Test oblique aspect of the ellipsoidal azimuthal equidistant. +------------------------------------------------------------------------------- +operation +proj=aeqd +ellps=GRS80 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.1 m +accept 0 0 +expect 0.0000 -4984944.3779 +roundtrip 100 +accept 0 45 +expect 0.0000 0.0000 +roundtrip 100 +accept 0 90 +expect 0.0000 5017021.3514 +roundtrip 100 +accept 90 0 +expect 10010351.5666 26393.3781 +roundtrip 100 +accept 90 45 +expect 5461910.9128 3863514.7047 +roundtrip 100 +accept 90 90 +expect 0.0000 5017021.3514 +roundtrip 100 + +=============================================================================== +# Airy +# Misc Sph, no inv. +# no_cut lat_b= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=airy +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 189109.886908621 94583.752387504 +accept 2 -1 +expect 189109.886908621 -94583.752387504 +accept -2 1 +expect -189109.886908621 94583.752387504 +accept -2 -1 +expect -189109.886908621 -94583.752387504 + +------------------------------------------------------------------------------- +# Test north polar aspect +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +lat_0=90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 -1.3863 +accept 0 90 +expect 0 0 +accept 0 -90 +expect failure errno coord_transfm_outside_projection_domain + + +------------------------------------------------------------------------------- +# Test south polar aspect +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +lat_0=-90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 1.3863 +accept 0 -90 +expect 0 0 +accept 0 90 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +# Test oblique aspect +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +lon_0=45 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 45 45 +expect 0 0 +accept 0 0 +expect -0.7336 -0.5187 +accept -45 -45 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +# Test that coordinates on the opposing hemisphere are projected when using +# +no_cut. +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +lat_0=-90 +no_cut +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 10 +expect 0 1.5677 + + +------------------------------------------------------------------------------- +# Test the +lat_b parameter +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +lat_b=89.99999999 # check tolerance +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 0 +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +lat_b=30 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 0 +accept 25 25 +expect 0.3821 0.4216 + +------------------------------------------------------------------------------- +operation +proj=airy +R=1 +no_cut +------------------------------------------------------------------------------- +accept -180 0 +expect failure errno coord_transfm_outside_projection_domain + +=============================================================================== +# Aitoff +# Misc Sph +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=aitoff +R=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223379.458811696 111706.742883853 +accept 2 -1 +expect 223379.458811696 -111706.742883853 +accept -2 1 +expect -223379.458811696 111706.742883853 +accept -2 -1 +expect -223379.458811696 -111706.742883853 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +=============================================================================== +# Mod. Stereographic of Alaska +# Azi(mod) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=alsk +ellps=clrk66 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept -160.000000000 55.000000000 +expect -513253.146950842 -968928.031867943 +accept -160.000000000 70.000000000 +expect -305001.133897637 687494.464958651 +accept -145.000000000 70.000000000 +expect 266454.305088600 683423.477493031 +accept -145.000000000 60.000000000 +expect 389141.322439244 -423913.251230397 + +direction inverse +accept -500000.000000000 -950000.000000000 +expect -159.830804303 55.183195262 +accept -305000.000000000 700000.000000000 +expect -160.042203156 70.111086864 +accept 250000.000000000 700000.000000000 +expect -145.381043551 70.163900908 +accept 400000.000000000 -400000.000000000 +expect -144.758985461 60.202929201 + +------------------------------------------------------------------------------- +operation +proj=alsk +R=6370997 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept -160.000000000 55.000000000 +expect -511510.319410844 -967150.991676078 +accept -160.000000000 70.000000000 +expect -303744.771290369 685439.745941123 +accept -145.000000000 70.000000000 +expect 265354.974019663 681386.892874573 +accept -145.000000000 60.000000000 +expect 387711.995394027 -422980.685505463 + +direction inverse +accept -500000.000000000 -950000.000000000 +expect -159.854014458 55.165653849 +accept -305000.000000000 700000.000000000 +expect -160.082332372 70.128307618 +accept 250000.000000000 700000.000000000 +expect -145.347827407 70.181566919 +accept 400000.000000000 -400000.000000000 +expect -144.734239827 60.193564733 + + +=============================================================================== +# Apian Globular I +# Misc Sph, no inv. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=apian +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223374.577355253 111701.072127637 +accept 2 -1 +expect 223374.577355253 -111701.072127637 +accept -2 1 +expect -223374.577355253 111701.072127637 +accept -2 -1 +expect -223374.577355253 -111701.072127637 + + +=============================================================================== +# August Epicycloidal +# Misc Sph, no inv. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=august +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223404.978180972 111722.340289763 +accept 2 -1 +expect 223404.978180972 -111722.340289763 +accept -2 1 +expect -223404.978180972 111722.340289763 +accept -2 -1 +expect -223404.978180972 -111722.340289763 + + +=============================================================================== +# Bacon Globular +# Misc Sph, no inv. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=bacon +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223334.132555965 175450.725922666 +accept 2 -1 +expect 223334.132555965 -175450.725922666 +accept -2 1 +expect -223334.132555965 175450.725922666 +accept -2 -1 +expect -223334.132555965 -175450.725922666 + + +=============================================================================== +# Bipolar conic of western hemisphere +# Conic Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=bipc +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 2452160.217725756 -14548450.759654747 +accept 2 -1 +expect 2447915.213725341 -14763427.212798730 +accept -2 1 +expect 2021695.522934909 -14540413.695283702 +accept -2 -1 +expect 2018090.503004699 -14755620.651414108 + +direction inverse +accept 200 100 +expect -73.038700285 17.248118466 +accept 200 -100 +expect -73.037303739 17.249414978 +accept -200 100 +expect -73.035893173 17.245536403 +accept -200 -100 +expect -73.034496627 17.246832896 + +------------------------------------------------------------------------------- +operation +proj=bipc +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 2460565.740974965 -14598319.989330800 +accept 2 -1 +expect 2456306.185935200 -14814033.339502094 +accept -2 1 +expect 2028625.497819099 -14590255.375482792 +accept -2 -1 +expect 2025008.120589143 -14806200.018759441 + +direction inverse +accept 200 100 +expect -73.038693105 17.248116270 +accept 200 -100 +expect -73.037301330 17.249408353 +accept -200 100 +expect -73.035895582 17.245543028 +accept -200 -100 +expect -73.034503807 17.246835092 + + +=============================================================================== +# Boggs Eumorphic +# PCyl., no inv., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=boggs +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 211949.700808182 117720.998305411 +accept 2 -1 +expect 211949.700808182 -117720.998305411 +accept -2 1 +expect -211949.700808182 117720.998305411 +accept -2 -1 +expect -211949.700808182 -117720.998305411 + + +=============================================================================== +# Bonne (Werner lat_1=90) +# Conic Sph&Ell +# lat_1= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=bonne +ellps=GRS80 +lat_1=0.5 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222605.296097157 55321.139565495 +accept 2 -1 +expect 222605.296099239 -165827.647799052 +accept -2 1 +expect -222605.296097157 55321.139565495 +accept -2 -1 +expect -222605.296099239 -165827.647799052 + +direction inverse +accept 200 100 +expect 0.001796699 0.500904369 +accept 200 -100 +expect 0.001796698 0.499095631 +accept -200 100 +expect -0.001796699 0.500904369 +accept -200 -100 +expect -0.001796698 0.499095631 + +------------------------------------------------------------------------------- +operation +proj=bonne +ellps=GRS80 +lat_1=-0.5 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222605.2961 165827.6478 +roundtrip 1 + +accept 2 -1 +expect 222605.2961 -55321.1396 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=bonne +ellps=GRS80 +lat_1=90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 90 +expect 0 0 + +direction inverse +accept 0 0 +expect 0 90 + +------------------------------------------------------------------------------- +operation +proj=bonne +ellps=GRS80 +lat_1=-90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 -90 +expect 0 0 + +direction inverse +accept 0 0 +expect 0 -90 + +------------------------------------------------------------------------------- +operation +proj=bonne +R=6400000 +lat_1=0.5 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223368.115572528 55884.555246394 +accept 2 -1 +expect 223368.115574632 -167517.599369694 +accept -2 1 +expect -223368.115572528 55884.555246394 +accept -2 -1 +expect -223368.115574632 -167517.599369694 + +direction inverse +accept 200 100 +expect 0.001790562 0.500895246 +accept 200 -100 +expect 0.001790561 0.499104753 +accept -200 100 +expect -0.001790562 0.500895246 +accept -200 -100 +expect -0.001790561 0.499104753 + +------------------------------------------------------------------------------- +operation +proj=bonne +R=6400000 +lat_1=-0.5 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223368.1156 167517.5994 +roundtrip 1 + +accept 2 -1 +expect 223368.1156 -55884.5552 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=bonne +R=6400000 +lat_1=90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 90 +expect 0 0 + +direction inverse +accept 0 0 +expect 0 90 + +------------------------------------------------------------------------------- +operation +proj=bonne +R=6400000 +lat_1=-90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 -90 +expect 0 0 + +direction inverse +accept 0 0 +expect 0 -90 + +=============================================================================== +# Cal Coop Ocean Fish Invest Lines/Stations +# Cyl, Sph&Ell +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=calcofi +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 508.444872150 -1171.764860418 +accept 2 -1 +expect 514.999168152 -1145.821981468 +accept -2 1 +expect 500.685384125 -1131.445377920 +accept -2 -1 +expect 507.369719137 -1106.178201483 + +direction inverse +accept 200 100 +expect -110.363307925 12.032056976 +accept 200 -100 +expect -98.455008863 18.698723643 +accept -200 100 +expect -207.447024504 81.314089279 +accept -200 -100 +expect -62.486322854 87.980755945 + +------------------------------------------------------------------------------- +operation +proj=calcofi +R=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 507.090507488 -1164.727375198 +accept 2 -1 +expect 513.686136375 -1138.999268217 +accept -2 1 +expect 499.336261476 -1124.435130997 +accept -2 -1 +expect 506.060570393 -1099.375665067 + +direction inverse +accept 200 100 +expect -110.305190410 12.032056976 +accept 200 -100 +expect -98.322360950 18.698723643 +accept -200 100 +expect -207.544906814 81.314089279 +accept -200 -100 +expect -62.576950372 87.980755945 + +operation +proj=calcofi +lon_0=50 +ellps=WGS84 +accept 10 50 +expect 303.525850 -1576.974388 +roundtrip 100 + +operation +proj=calcofi +ellps=GRS80 +lon_0=50 +accept 10 50 +expect 303.525850 -1576.974388 +roundtrip 100 + +operation +proj=calcofi +R=400 +lon_0=50 +x_0=10000 +y_0=500000 +accept 10 50 +expect 301.769827 -1567.849822 +roundtrip 100 + + +=============================================================================== +# Cassini +# Cyl, Sph&Ell +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=cass +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222605.285776991 110642.229253999 +roundtrip 1 +accept 2 -1 +expect 222605.285776991 -110642.229253999 +accept -2 1 +expect -222605.285776991 110642.229253999 +accept -2 -1 +expect -222605.285776991 -110642.229253999 + +direction inverse +accept 200 100 +expect 0.001796631 0.000904369 +accept 200 -100 +expect 0.001796631 -0.000904369 +accept -200 100 +expect -0.001796631 0.000904369 +accept -200 -100 +expect -0.001796631 -0.000904369 + +------------------------------------------------------------------------------- +operation +proj=cass +R=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223368.105203484 111769.145040586 +accept 2 -1 +expect 223368.105203484 -111769.145040586 +accept -2 1 +expect -223368.105203484 111769.145040586 +accept -2 -1 +expect -223368.105203484 -111769.145040586 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + +------------------------------------------------------------------------------- +# test point from EPSG Guidance Note 7.2 +operation +proj=cass +lat_0=10.4416666666667 +lon_0=-61.3333333333333 \ + +x_0=86501.46392052 +y_0=65379.0134283 \ + +a=6378293.64520876 +b=6356617.98767984 +to_meter=0.201166195164 +------------------------------------------------------------------------------- + +tolerance 0.1 mm +accept -62 10 +expect 66644.94040882 82536.21873655 +roundtrip 1 + +------------------------------------------------------------------------------- +# Hyperbolic variant: test point from EPSG Guidance Note 7.2 +operation +proj=cass +hyperbolic +a=6378306.376305601 +rf=293.466307 \ + +lat_0=-16.25 +lon_0=179.33333333333333 +to_meter=20.1168 \ + +x_0=251727.9155424 +y_0=334519.953768 +------------------------------------------------------------------------------- + +tolerance 0.1 mm +accept 179.99433652777776 -16.841456527777776 +expect 16015.28901692 13369.66005367 +roundtrip 1 + + +------------------------------------------------------------------------------- +# Scenario of https://github.com/OSGeo/PROJ/issues/4385 +------------------------------------------------------------------------------- +operation +proj=cass +lat_0=50.6177 +lon_0=-1.19725 +x_0=500000 +y_0=100000 +ellps=airy +units=m + +tolerance 0.1 mm +direction inverse + +accept 300000 100000 +expect -4.022094267169 50.583438725252 + +accept 500000 100000 +expect -1.19725 50.6177 + +=============================================================================== +# Central Conic +# Sph +# lat_1 +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=pipeline +R=6390000 \ + +step +proj=ccon +lat_1=52 +lat_0=52 +lon_0=19 +x_0=330000 +y_0=-350000 \ + +step +proj=axisswap +order=1,-2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 24 55 +expect 650031.54109413219363 4106.1617770643609028 +accept 15 49 +expect 37074.189007307473069 676826.23559270039774 +accept 24 49 +expect 696053.36061617843913 672294.56795827199940 +accept 19 52 +expect 330000.00000000000000 350000.00000000000000 + +direction inverse +accept 0 0 +expect 13.840227318521004431 55.030403993648806391 +accept 0 700000 +expect 14.514453594615022781 48.773847834747808675 +accept 700000 0 +expect 24.782707184271129766 55.003515505218481835 +accept 700000 700000 +expect 24.027610763560529927 48.750476070495021286 +accept 330000 350000 +expect 19.000000000000000000 52.000000000000000000 + + +=============================================================================== +# Central Cylindrical +# Cyl, Sph +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=cc +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223402.144255274 111712.415540593 +accept 2 -1 +expect 223402.144255274 -111712.415540593 +accept -2 1 +expect -223402.144255274 111712.415540593 +accept -2 -1 +expect -223402.144255274 -111712.415540593 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +=============================================================================== +# Equal Area Cylindrical +# Cyl, Sph&Ell +# lat_ts= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=cea +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222638.981586547 110568.812396267 +accept 2 -1 +expect 222638.981586547 -110568.812396266 +accept -2 1 +expect -222638.981586547 110568.812396267 +accept -2 -1 +expect -222638.981586547 -110568.812396266 +accept 150 50 +expect 16697923.6190 4865983.5552 + +direction inverse +accept 200 100 +expect 0.001796631 0.000904369 +accept 200 -100 +expect 0.001796631 -0.000904369 +accept -200 100 +expect -0.001796631 0.000904369 +accept -200 -100 +expect -0.001796631 -0.000904369 +accept 16697923.6190 4865983.5552 +expect 150 50 + +------------------------------------------------------------------------------- +operation +proj=cea +R=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223402.144255274 111695.401198614 +accept 2 -1 +expect 223402.144255274 -111695.401198614 +accept -2 1 +expect -223402.144255274 111695.401198614 +accept -2 -1 +expect -223402.144255274 -111695.401198614 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +=============================================================================== +# Chamberlin Trimetric +# Misc Sph, no inv. +# lat_1= lon_1= lat_2= lon_2= lat_3= lon_3= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=chamb +R=6400000 +lat_1=0.5 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 2.5 mm +accept 2 1 +expect -27864.779586801 -223364.324593274 +accept 2 -1 +expect -251312.283053493 -223402.145526208 +accept -2 1 +expect -27864.785649105 223364.327328827 +accept -2 -1 +expect -251312.289116443 223402.142197287 + + +=============================================================================== +# Collignon +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=collg +a=6400000 +lat_1=0.5 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 249872.921577930 99423.174788460 +accept 2 -1 +expect 254272.532301245 -98559.307760743 +accept -2 1 +expect -249872.921577930 99423.174788460 +accept -2 -1 +expect -254272.532301245 -98559.307760743 + +direction inverse +accept 200 100 +expect 0.001586797 0.001010173 +accept 200 -100 +expect 0.001586769 -0.001010182 +accept -200 100 +expect -0.001586797 0.001010173 +accept -200 -100 +expect -0.001586769 -0.001010182 + + +=============================================================================== +# Compact Miller +# Cyl., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=comill +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223402.144255274 110611.859089459 +accept 2 -1 +expect 223402.144255274 -110611.859089459 +accept -2 1 +expect -223402.144255274 110611.859089459 +accept -2 -1 +expect -223402.144255274 -110611.859089459 + +direction inverse +accept 200 100 +expect 0.001790493 0.000904107 +accept 200 -100 +expect 0.001790493 -0.000904107 +accept -200 100 +expect -0.001790493 0.000904107 +accept -200 -100 +expect -0.001790493 -0.000904107 + + +=============================================================================== +# Craster Parabolic (Putnins P4) +# PCyl., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=crast +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 218280.142056781 114306.045604280 +accept 2 -1 +expect 218280.142056781 -114306.045604280 +accept -2 1 +expect -218280.142056781 114306.045604280 +accept -2 -1 +expect -218280.142056781 -114306.045604280 + +direction inverse +accept 200 100 +expect 0.001832259 0.000874839 +accept 200 -100 +expect 0.001832259 -0.000874839 +accept -200 100 +expect -0.001832259 0.000874839 +accept -200 -100 +expect -0.001832259 -0.000874839 + + +=============================================================================== +# Denoyer Semi-Elliptical +# PCyl., no inv., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=denoy +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223377.422876954 111701.072127637 +accept 2 -1 +expect 223377.422876954 -111701.072127637 +accept -2 1 +expect -223377.422876954 111701.072127637 +accept -2 -1 +expect -223377.422876954 -111701.072127637 + + +=============================================================================== +# Airocean +# Sph., Ellps. +# (Each of the 23 faces tested separately around their center, inverse included) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=airocean +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 23 28 +expect 13572113.73386754 23493648.55327798 +accept 71 46 +expect 9714915.991790695 23488176.361173604 +accept 147 75 +expect 7723484.49359606 20087141.837650128 +accept -77 61 +expect 9679376.816000767 16802749.593532257 +accept -26 35 +expect 15458567.83864155 20091165.592037637 +accept 29 -13 +expect 15471813.400558881 26802282.415074058 +accept 71 -25 +expect 9737210.823606653 30219178.19260869 +accept 97 10 +expect 7670302.042847798 26816601.848991044 +accept 169 35 +expect 3883710.702444233 20135415.72144515 +accept -151 13 +expect 3859776.9744116343 13387384.422000753 +accept -109 24 +expect 7674343.074326526 13366009.083146008 +accept -84 -9 +expect 9673007.441581018 10144952.26955531 +accept -42 -4 +expect 13562062.520622183 10107761.706502315 +accept -11 -34 +expect 13627060.52678455 3383645.5697278716 +accept 155 -35 +expect 1873264.8705730252 30211340.763352156 +accept -158 -28 +expect 1871227.8450291778 10115901.323020123 +accept -109 -46 +expect 7708744.672461299 6722251.06988263 +accept -36 -75 +expect 9665810.798055789 3381177.9821538515 +accept 98 -49 +expect 4806946.337586326 33007546.454859577 +accept 114 -72 +expect 7708905.600709579 1101689.019137724 +accept 143 -9 +expect 3219027.0687154396 27948068.75709961 +accept 123 7 +expect 5239165.493429321 26821978.017945066 +accept 147 16 +expect 2635947.740851659 22373572.978527334 + +direction inverse +accept 13600000 23500000 +expect 22.77346472511832 27.745464601997153 +accept 9700000 23500000 +expect 71.26673004703193 45.89205035111361 +accept 7700000 20100000 +expect 146.99339940860168 74.69909794660227 +accept 9700000 16800000 +expect -76.55528563752168 60.90966578454296 +accept 15500000 20100000 +expect -26.125789701735282 34.531335035632864 +accept 15500000 26800000 +expect 28.72566754254401 -13.176397846758185 +accept 9700000 30200000 +expect 71.49135806675328 -24.84162689595362 +accept 7700000 26800000 +expect 96.67476470896398 10.214265110489109 +accept 3900000 20100000 +expect 169.4467058181239 35.245717462371594 +accept 3900000 13400000 +expect -150.6222299120939 13.304599775998279 +accept 7700000 13400000 +expect -108.74281284723317 24.422067806064522 +accept 9700000 10100000 +expect -83.65325201216521 -9.486900253798344 +accept 13600000 10100000 +expect -41.56143010477453 -4.013493146314863 +accept 13600000 3400000 +expect -11.279582965366556 -34.27261608163502 +accept 1900000 30200000 +expect 154.64715194333021 -34.84574824559832 +accept 1900000 10100000 +expect -157.58387651437764 -28.052389289696965 +accept 7700000 6700000 +expect -109.19369493541197 -46.23421830648926 +accept 9700000 3400000 +expect -35.93009713541779 -74.56175824137314 +accept 4800000 33000000 +expect 98.172013849367 -49.00298561868703 +accept 7700000 1100000 +expect 114.26109340373671 -71.94195405675616 +accept 3200000 27900000 +expect 143.30076636407907 -8.522097079186306 +accept 5200000 26800000 +expect 123.44730422061694 7.179239072128023 +accept 2600000 22400000 +expect 146.8547812565557 15.542304306692937 +accept 0 0 +expect failure + + +------------------------------------------------------------------------------- +operation +proj=airocean +orient=horizontal +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 23 28 +expect 13391387.087562159 13572113.73386754 +accept 71 46 +expect 13396859.279666536 9714915.991790695 +accept 147 75 +expect 16797893.80319001 7723484.49359606 +accept -77 61 +expect 20082286.04730788 9679376.816000767 +accept -26 35 +expect 16793870.048802502 15458567.83864155 +accept 29 -13 +expect 10082753.22576608 15471813.400558881 +accept 71 -25 +expect 6665857.448231446 9737210.823606653 +accept 97 10 +expect 10068433.791849095 7670302.042847798 +accept 169 35 +expect 16749619.919394989 3883710.702444233 +accept -151 13 +expect 23497651.218839385 3859776.9744116343 +accept -109 24 +expect 23519026.557694133 7674343.074326526 +accept -84 -9 +expect 26740083.371284828 9673007.441581018 +accept -42 -4 +expect 26777273.934337825 13562062.520622183 +accept -11 -34 +expect 33501390.07111227 13627060.52678455 +accept 155 -35 +expect 6673694.877487984 1873264.8705730252 +accept -158 -28 +expect 26769134.317820016 1871227.8450291778 +accept -109 -46 +expect 30162784.570957504 7708744.672461299 +accept -36 -75 +expect 33503857.658686288 9665810.798055789 +accept 98 -49 +expect 3877489.1859805635 4806946.337586326 +accept 114 -72 +expect 35783346.62170241 7708905.600709579 +accept 143 -9 +expect 8936966.883740531 3219027.0687154396 +accept 123 7 +expect 10063057.622895071 5239165.493429321 +accept 147 16 +expect 14511462.662312808 2635947.740851659 + +direction inverse +accept 13400000 13600000 +expect 22.653513921934305 27.877587719075937 +accept 13400000 9700000 +expect 71.23213038171733 46.05944622180928 +accept 16800000 7700000 +expect 147.55671447322464 74.77832986646499 +accept 20100000 9700000 +expect -76.64598925873727 60.747020624548 +accept 16800000 15500000 +expect -26.3124065099563 34.601485830443536 +accept 10100000 15500000 +expect 28.619135182474427 -13.042018999526977 +accept 6700000 9700000 +expect 71.5162610671907 -24.673252485600123 +accept 10100000 7700000 +expect 96.68789658312737 10.383985604100156 +accept 16800000 3900000 +expect 169.65090726985764 35.27199233196341 +accept 23500000 3900000 +expect -150.55720908958426 13.14679150488858 +accept 23500000 7700000 +expect -108.71768234825969 24.253726008211544 +accept 26800000 9700000 +expect -83.64031642722364 -9.65664821408901 +accept 26800000 13600000 +expect -41.53248336979641 -4.181271680064457 +accept 33500000 13600000 +expect -11.077997959623605 -34.30009883727707 +accept 6700000 1900000 +expect 154.6653022651957 -34.676851253860285 +accept 26800000 1900000 +expect -157.5153533577128 -28.210938432335496 +accept 30200000 7700000 +expect -109.22990606962236 -46.40145478927908 +accept 33500000 9700000 +expect -35.386955975332214 -74.64821453762985 +accept 3900000 4800000 +expect 98.362008540559 -48.89629332838504 +accept 35800000 7700000 +expect 114.04215001020711 -71.79634907735154 +accept 9000000 3200000 +expect 143.33006363443351 -8.36301544647104 +accept 10100000 5200000 +expect 123.47123951316074 7.342196526699235 +accept 14500000 2600000 +expect 147.01335056698537 15.59184037944909 +accept 0 0 +expect failure + +=============================================================================== +# Eckert I +# PCyl., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=eck1 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 204680.888202951 102912.178426065 +accept 2 -1 +expect 204680.888202951 -102912.178426065 +accept -2 1 +expect -204680.888202951 102912.178426065 +accept -2 -1 +expect -204680.888202951 -102912.178426065 + +direction inverse +accept 200 100 +expect 0.001943415 0.000971702 +accept 200 -100 +expect 0.001943415 -0.000971702 +accept -200 100 +expect -0.001943415 0.000971702 +accept -200 -100 +expect -0.001943415 -0.000971702 + + +=============================================================================== +# Eckert II +# PCyl. Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=eck2 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 204472.870907960 121633.734975242 +accept 2 -1 +expect 204472.870907960 -121633.734975242 +accept -2 1 +expect -204472.870907960 121633.734975242 +accept -2 -1 +expect -204472.870907960 -121633.734975242 + +direction inverse +accept 200 100 +expect 0.001943415 0.000824804 +accept 200 -100 +expect 0.001943415 -0.000824804 +accept -200 100 +expect -0.001943415 0.000824804 +accept -200 -100 +expect -0.001943415 -0.000824804 + + +=============================================================================== +# Eckert III +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=eck3 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 188652.015721538 94328.919337031 +accept 2 -1 +expect 188652.015721538 -94328.919337031 +accept -2 1 +expect -188652.015721538 94328.919337031 +accept -2 -1 +expect -188652.015721538 -94328.919337031 + +direction inverse +accept 200 100 +expect 0.002120241 0.001060120 +accept 200 -100 +expect 0.002120241 -0.001060120 +accept -200 100 +expect -0.002120241 0.001060120 +accept -200 -100 +expect -0.002120241 -0.001060120 + + +=============================================================================== +# Eckert IV +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=eck4 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 188646.389356416 132268.540174065 +accept 2 -1 +expect 188646.389356416 -132268.540174065 +accept -2 1 +expect -188646.389356416 132268.540174065 +accept -2 -1 +expect -188646.389356416 -132268.540174065 + +accept -180 90 +expect -8489602.7403 8489602.7403 + +accept 180 90 +expect 8489602.7403 8489602.7403 + +accept -180 0 +expect -16979205.4807 0 + +accept 180 0 +expect 16979205.4807 0 + +accept -180 -90 +expect -8489602.7403 -8489602.7403 + +accept 180 -90 +expect 8489602.7403 -8489602.7403 + +direction inverse +accept 200 100 +expect 0.002120241 0.000756015 +accept 200 -100 +expect 0.002120241 -0.000756015 +accept -200 100 +expect -0.002120241 0.000756015 +accept -200 -100 +expect -0.002120241 -0.000756015 + +accept -8489602.74033281 8489602.74033281 +expect -180 90 + +accept -8489602.75 8489602.74033281 +expect failure errno coord_transfm_outside_projection_domain + +accept 8489602.74033281 8489602.74033281 +expect 180 90 + +accept 8489602.75 8489602.74033281 +expect failure errno coord_transfm_outside_projection_domain + +accept 0 8489602.75 +expect failure errno coord_transfm_outside_projection_domain + +accept -16979205.4807 0 +expect -180 0 + +accept -16979205.49 0 +expect failure errno coord_transfm_outside_projection_domain + +accept 16979205.4807 0 +expect 180 0 + +accept 16979205.49 0 +expect failure errno coord_transfm_outside_projection_domain + +accept -8489602.74033281 -8489602.74033281 +expect -180 -90 + +accept -8489602.75 -8489602.74033281 +expect failure errno coord_transfm_outside_projection_domain + +accept 8489602.74033281 -8489602.74033281 +expect 180 -90 + +accept 8489602.75 -8489602.74033281 +expect failure errno coord_transfm_outside_projection_domain + +accept 0 -8489602.75 +expect failure errno coord_transfm_outside_projection_domain + +=============================================================================== +# Eckert V +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=eck5 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 197031.392134061 98523.198847227 +accept 2 -1 +expect 197031.392134061 -98523.198847227 +accept -2 1 +expect -197031.392134061 98523.198847227 +accept -2 -1 +expect -197031.392134061 -98523.198847227 + +direction inverse +accept 200 100 +expect 0.002029979 0.001014989 +accept 200 -100 +expect 0.002029979 -0.001014989 +accept -200 100 +expect -0.002029979 0.001014989 +accept -200 -100 +expect -0.002029979 -0.001014989 + + +=============================================================================== +# Eckert VI +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=eck6 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 197021.605628992 126640.420733174 +accept 2 -1 +expect 197021.605628992 -126640.420733174 +accept -2 1 +expect -197021.605628992 126640.420733174 +accept -2 -1 +expect -197021.605628992 -126640.420733174 + +direction inverse +accept 200 100 +expect 0.002029979 0.000789630 +accept 200 -100 +expect 0.002029979 -0.000789630 +accept -200 100 +expect -0.002029979 0.000789630 +accept -200 -100 +expect -0.002029979 -0.000789630 + + +=============================================================================== +# Equidistant Cylindrical (Plate Carree) +# Cyl, Sph&Ell +# lat_ts=[, lat_0=0] +=============================================================================== + +------------------------------------------------------------------------------- +# Spherical case +operation +proj=eqc +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223402.144255274 111701.072127637 +accept 2 -1 +expect 223402.144255274 -111701.072127637 +accept -2 1 +expect -223402.144255274 111701.072127637 +accept -2 -1 +expect -223402.144255274 -111701.072127637 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + +------------------------------------------------------------------------------- +# Ellipsoidal case (EPSG:1028) +# Test values from IOGP Guidance Note 7-2, Section 3.2.5 +# WGS84 ellipsoid: a=6378137, 1/f=298.257223563 +# Standard parallel: 0° (lat_ts=0) +# Input: lat=55°, lon=10° +# Expected: E=1113194.91, N=6097230.31 +operation +proj=eqc +ellps=WGS84 +lat_ts=0 +------------------------------------------------------------------------------- +tolerance 0.01 m +accept 10 55 +expect 1113194.91 6097230.31 + +direction inverse +accept 1113194.91 6097230.31 +expect 10 55 + +------------------------------------------------------------------------------- +# Ellipsoidal case - high latitude test +# WGS84 ellipsoid +# Input: lat=71.9993230521°, lon=-143.9999611505° +# Expected: +# E=-16030002.350, N=7992053.817 +operation +proj=eqc +ellps=WGS84 +lat_ts=0 +------------------------------------------------------------------------------- +tolerance 0.1 m +accept -143.9999611505 71.9993230521 +expect -16030002.350 7992053.817 + +direction inverse +accept -16030002.350 7992053.817 +expect -143.9999611505 71.9993230521 + +------------------------------------------------------------------------------- +# Ellipsoidal case - Edge cases +# WGS84 ellipsoid, lat_ts=0 +# Tests for origin, hemispheres, date line, near-pole +operation +proj=eqc +ellps=WGS84 +lat_ts=0 +------------------------------------------------------------------------------- +tolerance 0.001 m + +# Origin (0, 0) +accept 0 0 +expect 0.0 0.0 + +# Southern hemisphere (10, -45) +accept 10 -45 +expect 1113194.90793 -4984944.37798 + +# Date line (180, 30) +accept 180 30 +expect 20037508.34279 3320113.39794 + +# Near north pole (0, 89) +accept 0 89 +expect 0.0 9890271.86440 + +# San Francisco (-122.4194, 37.7749) +accept -122.4194 37.7749 +expect -13627665.27122 4182513.19136 + +direction inverse +# Inverse tests for edge cases +accept 0.0 0.0 +expect 0 0 + +accept 1113194.90793 -4984944.37798 +expect 10 -45 + +accept 20037508.34279 3320113.39794 +expect 180 30 + +accept 0.0 9890271.86440 +expect 0 89 + +accept -13627665.27122 4182513.19136 +expect -122.4194 37.7749 + +------------------------------------------------------------------------------- +# Ellipsoidal case with non-zero standard parallel (lat_ts=45) +# WGS84 ellipsoid +# Tests the ν₁ cos(φ₁) scaling factor for easting +operation +proj=eqc +ellps=WGS84 +lat_ts=45 +------------------------------------------------------------------------------- +tolerance 0.01 m + +# Paris region (2, 49) +accept 2 49 +expect 157693.670 5429627.632 + +# Origin (0, 0) - northing should still be 0 +accept 0 0 +expect 0.0 0.0 + +# High latitude (10, 70) +accept 10 70 +expect 788468.351 7768980.728 + +direction inverse +accept 157693.670 5429627.632 +expect 2 49 + +accept 0.0 0.0 +expect 0 0 + +accept 788468.351 7768980.728 +expect 10 70 + +------------------------------------------------------------------------------- +# Ellipsoidal case with lat_ts=30 and lat_0=45 (non-zero origin) +# Tests meridional arc offset M₀ +operation +proj=eqc +ellps=WGS84 +lat_ts=30 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.01 m + +# At the origin latitude, northing should be 0 +accept 0 45 +expect 0.0 0.0 + +# Above origin +accept 0 60 +expect 0.0 1669128.442 + +# Below origin +accept 0 30 +expect 0.0 -1664830.980 + +direction inverse +accept 0.0 0.0 +expect 0 45 + +accept 0.0 1669128.442 +expect 0 60 + +accept 0.0 -1664830.980 +expect 0 30 + + +=============================================================================== +# Equidistant Conic +# Conic, Sph&Ell +# lat_1= lat_2= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=eqdc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222588.440269286 110659.134907347 +accept 2 -1 +expect 222756.836702042 -110489.578087221 +accept -2 1 +expect -222588.440269286 110659.134907347 +accept -2 -1 +expect -222756.836702042 -110489.578087221 + +direction inverse +accept 200 100 +expect 0.001796359 0.000904369 +accept 200 -100 +expect 0.001796358 -0.000904370 +accept -200 100 +expect -0.001796359 0.000904369 +accept -200 -100 +expect -0.001796358 -0.000904370 + +------------------------------------------------------------------------------- +operation +proj=eqdc +R=6400000 +lat_1=0.5 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223351.088175114 111786.108747174 +accept 2 -1 +expect 223521.200266735 -111615.970741241 +accept -2 1 +expect -223351.088175114 111786.108747174 +accept -2 -1 +expect -223521.200266735 -111615.970741241 + +direction inverse +accept 200 100 +expect 0.001790221 0.000895246 +accept 200 -100 +expect 0.001790220 -0.000895247 +accept -200 100 +expect -0.001790221 0.000895246 +accept -200 -100 +expect -0.001790220 -0.000895247 + +operation +proj=eqdc +a=9999999 +b=.9 +lat_2=1 +expect failure + +operation +proj=eqdc +R=6400000 +lat_1=1 +lat_2=-1 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=eqdc +R=6400000 +lat_1=91 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=eqdc +R=6400000 +lat_2=91 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=eqdc +R=1 +lat_1=1e-9 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=eqdc +lat_1=1 +ellps=GRS80 +b=.1 +expect failure errno invalid_op_illegal_arg_value + +=============================================================================== +# Euler +# Conic, Sph +# lat_1= and lat_2= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=euler +ellps=GRS80 +lat_1=0.5 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222597.634659108 111404.240549919 +accept 2 -1 +expect 222767.165631876 -111234.676491018 +accept -2 1 +expect -222597.634659108 111404.240549919 +accept -2 -1 +expect -222767.165631876 -111234.676491018 + +direction inverse +accept 200 100 +expect 0.001796281 0.000898315 +accept 200 -100 +expect 0.001796279 -0.000898316 +accept -200 100 +expect -0.001796281 0.000898315 +accept -200 -100 +expect -0.001796279 -0.000898316 + +------------------------------------------------------------------------------- +operation +proj=euler +a=6400000 +lat_1=0.5 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223360.655598694 111786.112389791 +accept 2 -1 +expect 223530.767690316 -111615.967098624 +accept -2 1 +expect -223360.655598694 111786.112389791 +accept -2 -1 +expect -223530.767690316 -111615.967098624 + +direction inverse +accept 200 100 +expect 0.001790144 0.000895246 +accept 200 -100 +expect 0.001790143 -0.000895247 +accept -200 100 +expect -0.001790144 0.000895246 +accept -200 -100 +expect -0.001790143 -0.000895247 + + +=============================================================================== +# Extended Transverse Mercator +# Cyl, Sph +# lat_ts=(0) +# lat_0=(0) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=etmerc +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 50 nm +accept 2 1 +expect 222650.796797586 110642.229411933 +accept 2 -1 +expect 222650.796797586 -110642.229411933 +accept -2 1 +expect -222650.796797586 110642.229411933 +accept -2 -1 +expect -222650.796797586 -110642.229411933 +# near pole +accept 30 89.9999 +expect 5.584698978 10001956.056248082 +# 3900 km from central meridian +accept 44.69 35.37 +expect 4168136.489446198 4985511.302287407 + +direction inverse +accept 200 100 +expect 0.00179663056816 0.00090436947663 +accept 200 -100 +expect 0.00179663056816 -0.00090436947663 +accept -200 100 +expect -0.00179663056816 0.00090436947663 +accept -200 -100 +expect -0.00179663056816 -0.00090436947663 +# near pole +accept 6 1.0001e7 +expect 0.35596960759234 89.99135362646302 +# 3900 km from central meridian +accept 4168136.489446198 4985511.302287407 +expect 44.69 35.37 + +=============================================================================== +# Fahey +# Pcyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=fahey +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 182993.344649124 101603.193569884 +accept 2 -1 +expect 182993.344649124 -101603.193569884 +accept -2 1 +expect -182993.344649124 101603.193569884 +accept -2 -1 +expect -182993.344649124 -101603.193569884 + +direction inverse +accept 200 100 +expect 0.002185789 0.000984246 +accept 200 -100 +expect 0.002185789 -0.000984246 +accept -200 100 +expect -0.002185789 0.000984246 +accept -200 -100 +expect -0.002185789 -0.000984246 + + +=============================================================================== +# Foucaut +# PCyl., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=fouc +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222588.120675892 111322.316700694 +accept 2 -1 +expect 222588.120675892 -111322.316700694 +accept -2 1 +expect -222588.120675892 111322.316700694 +accept -2 -1 +expect -222588.120675892 -111322.316700694 + +direction inverse +accept 200 100 +expect 0.001796631 0.000898315 +accept 200 -100 +expect 0.001796631 -0.000898315 +accept -200 100 +expect -0.001796631 0.000898315 +accept -200 -100 +expect -0.001796631 -0.000898315 + +------------------------------------------------------------------------------- +operation +proj=fouc +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223351.109003414 111703.907721713 +accept 2 -1 +expect 223351.109003414 -111703.907721713 +accept -2 1 +expect -223351.109003414 111703.907721713 +accept -2 -1 +expect -223351.109003414 -111703.907721713 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +=============================================================================== +# Foucaut Sinusoidal +# PCyl., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=fouc_s +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223402.144255274 111695.401198614 +accept 2 -1 +expect 223402.144255274 -111695.401198614 +accept -2 1 +expect -223402.144255274 111695.401198614 +accept -2 -1 +expect -223402.144255274 -111695.401198614 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +=============================================================================== +# Gall (Gall Stereographic) +# Cyl, Sph +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=gall +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 157969.171134520 95345.249178386 +accept 2 -1 +expect 157969.171134520 -95345.249178386 +accept -2 1 +expect -157969.171134520 95345.249178386 +accept -2 -1 +expect -157969.171134520 -95345.249178386 + +direction inverse +accept 200 100 +expect 0.002532140 0.001048847 +accept 200 -100 +expect 0.002532140 -0.001048847 +accept -200 100 +expect -0.002532140 0.001048847 +accept -200 -100 +expect -0.002532140 -0.001048847 + + +=============================================================================== +# Geocentric + +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=geocent +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 0 +expect 6373287.27950247 222560.09599219 110568.77482092 +accept 2 -1 0 +expect 6373287.27950247 222560.09599219 -110568.77482092 +accept -2 1 0 +expect 6373287.27950247 -222560.09599219 110568.77482092 +accept -2 -1 0 +expect 6373287.27950247 -222560.09599219 -110568.77482092 + +direction inverse +accept 6373287.27950247 222560.09599219 110568.77482092 +expect 2 1 0 + +------------------------------------------------------------------------------- +operation +proj=geocent +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm + + +=============================================================================== +# Geostationary Satellite View +# Azi, Sph&Ell +# h= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=geos +ellps=GRS80 +h=35785831 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222527.070365800 110551.303413329 +accept 2 -1 +expect 222527.070365800 -110551.303413329 +accept -2 1 +expect -222527.070365800 110551.303413329 +accept -2 -1 +expect -222527.070365800 -110551.303413329 + +direction inverse +accept 200 100 +expect 0.001796631 0.000904369 +accept 200 -100 +expect 0.001796631 -0.000904369 +accept -200 100 +expect -0.001796631 0.000904369 +accept -200 -100 +expect -0.001796631 -0.000904369 + +------------------------------------------------------------------------------- +operation +proj=geos +R=6400000 +h=35785831 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223289.457635795 111677.657456537 +accept 2 -1 +expect 223289.457635795 -111677.657456537 +accept -2 1 +expect -223289.457635795 111677.657456537 +accept -2 -1 +expect -223289.457635795 -111677.657456537 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + +------------------------------------------------------------------------------- +operation +proj=geos +R=1 +h=0 +------------------------------------------------------------------------------- +expect failure errno invalid_op_illegal_arg_value + +------------------------------------------------------------------------------- +operation +proj=geos +R=1 +h=1e11 +------------------------------------------------------------------------------- +expect failure errno invalid_op_illegal_arg_value + + + +=============================================================================== +# Ginsburg VIII (TsNIIGAiK) +# PCyl, Sph., no inv. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=gins8 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 194350.250939590 111703.907635335 +accept 2 -1 +expect 194350.250939590 -111703.907635335 +accept -2 1 +expect -194350.250939590 111703.907635335 +accept -2 -1 +expect -194350.250939590 -111703.907635335 + + +=============================================================================== +# General Sinusoidal Series +# PCyl, Sph. +# m= n= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=gn_sinu +a=6400000 +m=1 +n=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223385.132504696 111698.236447187 +accept 2 -1 +expect 223385.132504696 -111698.236447187 +accept -2 1 +expect -223385.132504696 111698.236447187 +accept -2 -1 +expect -223385.132504696 -111698.236447187 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +=============================================================================== +# Gnomonic +# Azi, Sph*Ell +=============================================================================== + +------------------------------------------------------------------------------- +# Test material from Snyder p. 168, table 26. Repeat tests with ellispoid of +# flattening 1/200. +# Tests the equatorial aspect of the projection. +------------------------------------------------------------------------------- +operation +proj=gnom +R=1 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 0 +roundtrip 100 +accept 10 80 +expect 0.1763 5.7588 +roundtrip 100 +accept 20 70 +expect 0.3640 2.9238 +roundtrip 100 +accept 30 60 +expect 0.5774 2.0000 +roundtrip 100 +accept 40 50 +expect 0.8391 1.5557 +roundtrip 100 +accept 50 40 +expect 1.1918 1.3054 +roundtrip 100 +accept 60 30 +expect 1.7321 1.1547 +roundtrip 100 +accept 70 20 +expect 2.7475 1.0642 +roundtrip 100 +accept 80 10 +expect 5.6713 1.0154 +roundtrip 100 +accept 80 80 +expect 5.6713 32.6596 +roundtrip 100 +accept 0 90 +expect failure errno coord_transfm_outside_projection_domain + +# test that extreme northings are mapped to the sphere +direction inverse +accept 0 1e8 +expect 0 90 + +------------------------------------------------------------------------------- +operation +proj=gnom +a=1 +rf=200 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 0 +roundtrip 100 +accept 10 80 +expect 0.1763 5.7232 +roundtrip 100 +accept 20 70 +expect 0.3641 2.9037 +roundtrip 100 +accept 30 60 +expect 0.5778 1.9861 +roundtrip 100 +accept 40 50 +expect 0.8405 1.5459 +roundtrip 100 +accept 50 40 +expect 1.1958 1.2994 +roundtrip 100 +accept 60 30 +expect 1.7435 1.1534 +roundtrip 100 +accept 70 20 +expect 2.7852 1.0711 +roundtrip 100 +accept 80 10 +expect 5.8813 1.0465 +roundtrip 100 +accept 80 80 +expect 5.7134 32.7298 +roundtrip 100 +accept 0 89.99 +expect 0 5700.9222 +roundtrip 100 +accept 180 89.99 +expect failure errno coord_transfm_outside_projection_domain + +# test that extreme northings are mapped to the sphere +direction inverse +accept 0 1e8 +expect 0 90 + +------------------------------------------------------------------------------- +# Test the northern polar aspect of the gnonomic projection +------------------------------------------------------------------------------- +operation +proj=gnom +R=1 +lat_0=90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 90 +expect 0 0 +roundtrip 100 +accept 45 45 +expect 0.7071 -0.7071 +roundtrip 100 +accept 0 0 +expect failure errno coord_transfm_outside_projection_domain +accept 90 0 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +operation +proj=gnom +a=1 +rf=200 +lat_0=90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 90 +expect 0 0 +roundtrip 100 +accept 45 45 +expect 0.7079 -0.7079 +roundtrip 100 +accept 0 0 +expect 0 -127.4835 +roundtrip 100 +accept 90 0 +expect 127.4835 0 +roundtrip 100 +accept 0 -0.5 +expect failure errno coord_transfm_outside_projection_domain +accept 90 -0.5 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +# Test the southern polar aspect of the gnonomic projection +------------------------------------------------------------------------------- +operation +proj=gnom +R=1 +lat_0=-90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 -90 +expect 0 0 +roundtrip 100 +accept 45 -45 +expect 0.7071 0.7071 +roundtrip 100 +accept 0 0 +expect failure errno coord_transfm_outside_projection_domain +accept 90 0 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +operation +proj=gnom +a=1 +rf=200 +lat_0=-90 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 -90 +expect 0 0 +roundtrip 100 +accept 45 -45 +expect 0.7079 0.7079 +roundtrip 100 +accept 0 0 +expect 0 127.4835 +roundtrip 100 +accept 90 0 +expect 127.4835 0 +roundtrip 100 +accept 0 0.5 +expect failure errno coord_transfm_outside_projection_domain +accept 90 0.5 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +# Test the oblique aspect of the gnonomic projection +------------------------------------------------------------------------------- +operation +proj=gnom +R=1 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 45 +expect 0 0 +roundtrip 100 +accept 0 0 +expect 0 -1 +roundtrip 100 +accept 0 90 +expect 0 1 +roundtrip 100 +accept 0 -45 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +operation +proj=gnom +a=1 +rf=200 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 45 +expect 0 0 +roundtrip 100 +accept 0 0 +expect 0 -0.9897 +roundtrip 100 +accept 0 90 +expect 0 1.0025 +roundtrip 100 +accept 0 -45 +expect 0 -154.8623 +roundtrip 100 +accept 0 -45.5 +expect failure errno coord_transfm_outside_projection_domain +=============================================================================== +# Goode Homolosine +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=goode +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223368.119026632 111701.072127637 +accept 2 -1 +expect 223368.119026632 -111701.072127637 +accept -2 1 +expect -223368.119026632 111701.072127637 +accept -2 -1 +expect -223368.119026632 -111701.072127637 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +=============================================================================== +# Mod. Stereographic of 48 U.S. +# Azi(mod) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=gs48 +R=6370997 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept -119.000000000 40.000000000 +expect -1923908.446529346 355874.658944479 +accept -70.000000000 64.000000000 +expect 1354020.375109298 3040846.007866525 +accept -80.000000000 25.000000000 +expect 1625139.160484320 -1413614.894029108 +accept -95.000000000 35.000000000 +expect 90241.658071458 -439595.048485902 + +direction inverse +accept -1923000.000000000 355000.000000000 +expect -118.987112613 39.994449789 +accept 1354000.000000000 3040000.000000000 +expect -70.005208999 63.993387836 +accept 1625000.000000000 -1413000.000000000 +expect -80.000346610 25.005602547 +accept 90000.000000000 -439000.000000000 +expect -95.002606473 35.005424705 + + +=============================================================================== +# Mod. Stereographic of 50 U.S. +# Azi(mod) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=gs50 +ellps=clrk66 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept -160.000000000 65.000000000 +expect -1874628.537740233 2660907.942291015 +accept -130.000000000 45.000000000 +expect -771831.518853336 48465.166491305 +accept -65.000000000 45.000000000 +expect 4030931.833981509 1323687.864777399 +accept -80.000000000 36.000000000 +expect 3450764.261536101 -175619.041820732 + +# For some reason, does not fail on MacOSX +#accept 60 -45 +#expect failure errno coord_transfm_outside_projection_domain + +direction inverse +accept -1800000.000000000 2600000.000000000 +expect -157.989285000 64.851559610 +accept -800000.000000000 500000.000000000 +expect -131.171390467 49.084969746 +accept 4000000.000000000 1300000.000000000 +expect -65.491568685 44.992837924 +accept 3900000.000000000 -170000.000000000 +expect -75.550660091 34.191114076 + +------------------------------------------------------------------------------- +operation +proj=gs50 +R=6370997 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept -160.000000000 65.000000000 +expect -1867268.253460009 2656506.230401823 +accept -130.000000000 45.000000000 +expect -769572.189672994 48324.312440864 +accept -65.000000000 45.000000000 +expect 4019393.068680791 1320191.309350289 +accept -80.000000000 36.000000000 +expect 3442685.615172346 -178760.423489429 + +direction inverse +accept -1800000.000000000 2600000.000000000 +expect -158.163295045 64.854288365 +accept -800000.000000000 500000.000000000 +expect -131.206816960 49.082915351 +accept 4000000.000000000 1300000.000000000 +expect -65.348945221 44.957292682 +accept 3900000.000000000 -170000.000000000 +expect -75.446820242 34.185406226 + + +=============================================================================== +# Hammer & Eckert-Greifendorff +# Misc Sph, +# W= M= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=hammer +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223373.788703241 111703.907397767 +accept 2 -1 +expect 223373.788703241 -111703.907397767 +accept -2 1 +expect -223373.788703241 111703.907397767 +accept -2 -1 +expect -223373.788703241 -111703.907397767 + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + + +------------------------------------------------------------------------------- +operation +proj=hammer +a=6400000 +W=1 +------------------------------------------------------------------------------- +accept -180 0 +expect failure errno coord_transfm_outside_projection_domain + +=============================================================================== +# Hatano Asymmetrical Equal Area +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=hatano +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 189878.878946528 131409.802440626 +accept 2 -1 +expect 189881.081952445 -131409.142276074 +accept -2 1 +expect -189878.878946528 131409.802440626 +accept -2 -1 +expect -189881.081952445 -131409.142276074 + +direction inverse +accept 200 100 +expect 0.002106462 0.000760957 +accept 200 -100 +expect 0.002106462 -0.000760958 +accept -200 100 +expect -0.002106462 0.000760957 +accept -200 -100 +expect -0.002106462 -0.000760958 + + +=============================================================================== +# HEALPix +# Sph., Ellps. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=healpix +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222390.103949239 130406.588664482 +accept 2 -1 +expect 222390.103949239 -130406.588664481 +accept -2 1 +expect -222390.103949239 130406.588664482 +accept -2 -1 +expect -222390.103949239 -130406.588664481 + +direction inverse +accept 200 100 +expect 0.001798641 0.000766795 +accept 200 -100 +expect 0.001798641 -0.000766795 +accept -200 100 +expect -0.001798641 0.000766795 +accept -200 -100 +expect -0.001798641 -0.000766795 + +------------------------------------------------------------------------------- +operation +proj=healpix +R=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223402.144255274 131588.044441999 +accept 2 -1 +expect 223402.144255274 -131588.044441999 +accept -2 1 +expect -223402.144255274 131588.044441999 +accept -2 -1 +expect -223402.144255274 -131588.044441999 + +direction inverse +accept 200 100 +expect 0.001790493 0.000759909 +accept 200 -100 +expect 0.001790493 -0.000759909 +accept -200 100 +expect -0.001790493 0.000759909 +accept -200 -100 +expect -0.001790493 -0.000759909 + +------------------------------------------------------------------------------- +operation +proj=healpix +R=6400000 +lat_1=0.5 +lat_2=2 +rot_xy=42 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 254069.735470912856 -51696.237925639456 +accept 2 -1 +expect 77970.559536809917 -247274.186569161975 +accept -2 1 +expect -77970.559536809917 247274.186569161975 +accept -2 -1 +expect -254069.735470912856 51696.237925639456 + +direction inverse +accept 254069.735470912856 -51696.237925639456 +expect 2 1 +accept 77970.559536809917 -247274.186569161975 +expect 2 -1 +accept -77970.559536809917 247274.186569161975 +expect -2 1 +accept -254069.735470912856 51696.237925639456 +expect -2 -1 + + +=============================================================================== +# rHEALPix +# Sph., Ellps. +# north_square= south_square= +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=rhealpix +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222390.103949239 130406.588664482 +accept 2 -1 +expect 222390.103949239 -130406.588664481 +accept -2 1 +expect -222390.103949239 130406.588664482 +accept -2 -1 +expect -222390.103949239 -130406.588664481 + +direction inverse +accept 200 100 +expect 0.001798641 0.000766795 +accept 200 -100 +expect 0.001798641 -0.000766795 +accept -200 100 +expect -0.001798641 0.000766795 +accept -200 -100 +expect -0.001798641 -0.000766795 + +------------------------------------------------------------------------------- +operation +proj=rhealpix +R=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223402.144255274 131588.044441999 +accept 2 -1 +expect 223402.144255274 -131588.044441999 +accept -2 1 +expect -223402.144255274 131588.044441999 +accept -2 -1 +expect -223402.144255274 -131588.044441999 + +direction inverse +accept 200 100 +expect 0.001790493 0.000759909 +accept 200 -100 +expect 0.001790493 -0.000759909 +accept -200 100 +expect -0.001790493 0.000759909 +accept -200 -100 +expect -0.001790493 -0.000759909 + +------------------------------------------------------------------------------- +operation +proj=rhealpix +south_square=2 +north_square=3 +ellps=WGS84 +------------------------------------------------------------------------------- +tolerance 1 m +accept 45 50 +expect 10806592 10007554 +accept 45 -50 +expect 5003777 -5802815 +accept 135 50 +expect 15011332 5802815 + +direction inverse +accept 10806592 10007554 +expect 45 50 +accept 5003777 -5802815 +expect 45 -50 +accept 15011332 5802815 +expect 135 50 + + +=============================================================================== +# Interrupted Goode Homolosine +# PCyl, Sph. +# (Each of the 12 sub-projections tested separately) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=igh +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223878.497456271 111701.072127637 +roundtrip 1 +accept 2 -1 +expect 223708.371313058 -111701.072127637 +roundtrip 1 +accept -2 1 +expect -222857.740596992 111701.072127637 +roundtrip 1 +accept -2 -1 +expect -223027.866740205 -111701.072127637 +roundtrip 1 + +accept -100.0 22.0 +expect -11170107.212763708 2457423.5868080168 +roundtrip 1 +accept -30.0 22.0 +expect -2863013.673043605 2457423.586808016 +roundtrip 1 +accept -100.0 67.0 +expect -11170107.212763708 7205942.523056464 +roundtrip 1 +accept -30.0 67.0 +expect 17045.719482862 7205942.523056464 +roundtrip 1 +accept -160.0 -22.0 +expect -17872171.540421933 -2457423.586808016 +roundtrip 1 +accept -60.0 -22.0 +expect -6702064.327658225 -2457423.586808016 +roundtrip 1 +accept 20.0 -22.0 +expect 2234021.442552742 -2457423.586808016 +roundtrip 1 +accept 140.0 -22.0 +expect 15638150.097869191 -2457423.586808016 +roundtrip 1 +accept -160.0 -67.0 +expect -17872171.540421933 -7205942.523056464 +roundtrip 1 +accept -60.0 -67.0 +expect -6702064.327658225 -7205942.523056464 +roundtrip 1 +accept 20.0 -67.0 +expect 2234021.442552742 -7205942.523056464 +roundtrip 1 +accept 140.0 -67.0 +expect 15638150.097869191 -7205942.523056464 +roundtrip 1 + +direction inverse +accept 200 100 +expect 0.001790489 0.000895247 +accept 200 -100 +expect 0.001790491 -0.000895247 +accept -200 100 +expect -0.001790497 0.000895247 +accept -200 -100 +expect -0.001790496 -0.000895247 + +=============================================================================== +# Interrupted Goode Homolosine Ocean View +# PCyl, Sph. +# (Each of the 12 sub-projections tested separately) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=igh_o +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223197.992883418 111701.072127637 +roundtrip 1 +accept 2 -1 +expect 223708.371313058 -111701.072127637 +roundtrip 1 +accept -2 1 +expect -223538.245169845 111701.072127637 +roundtrip 1 +accept -2 -1 +expect -223027.866740205 -111701.072127637 +roundtrip 1 + +accept -140.0 22.0 +expect -15638150.097869192 2457423.586808016 +roundtrip 1 +accept 170.0 70.0 +expect 16560870.317293623 7463176.386461447 +roundtrip 1 +accept -10.0 22.0 +expect -1117010.721276371 2457423.586808016 +roundtrip 1 +accept 130.0 22.0 +expect 14521139.376592822 2457423.586808016 +roundtrip 1 +accept -170.0 70.0 +expect -17167948.303394791 7463176.386461447 +roundtrip 1 +accept -140.0 67.0 +expect -15638150.097869191 7205942.523056464 +roundtrip 1 +accept -10.0 67.0 +expect -1117010.721276371 7205942.523056464 +roundtrip 1 +accept 130.0 67.0 +expect 14521139.376592822 7205942.523056464 +roundtrip 1 +accept -110.0 -22.0 +expect -12287117.934040081 -2457423.586808016 +roundtrip 1 +accept 20.0 -22.0 +expect 2234021.442552742 -2457423.586808016 +roundtrip 1 +accept 150.0 -22.0 +expect 16755160.819145568 -2457423.586808016 +roundtrip 1 +accept -110.0 -67.0 +expect -12287117.934040081 -7205942.523056464 +roundtrip 1 +accept 20.0 -67.0 +expect 2234021.442552742 -7205942.523056464 +roundtrip 1 +accept 95.0 -67.0 +expect 13699006.578494834 -7205942.523056464 +roundtrip 1 +accept 150.0 -67.0 +expect 16755160.819145564 -7205942.523056464 +roundtrip 1 + +direction inverse +accept 200 100 +expect 0.001790494 0.000895247 +accept 200 -100 +expect 0.001790491 -0.000895247 +accept -200 100 +expect -0.001790492 0.000895247 +accept -200 -100 +expect -0.001790496 -0.000895247 + +=============================================================================== +# Interrupted Mollweide +# PCyl, Sph. +# (Each of the 6 sub-projections tested separately) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=imoll +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect -912080.283811148372 124066.283433859542 +roundtrip 1 +accept 2 -1 +expect -912174.768289615284 -124066.283433859542 +roundtrip 1 +accept -2 1 +expect -1314307.681094774744 124066.283433859542 +roundtrip 1 +accept -2 -1 +expect -1314402.165573241888 -124066.283433859542 +roundtrip 1 +accept -39.99 0.1 +expect -5135117.0707450127 12406.8672748194 +roundtrip 1 +accept -40.01 0.1 +expect -5137140.6776947584 12406.8672748194 +roundtrip 1 +accept -99.99 -0.1 +expect -11169097.7713819221 -12406.8672748194 +roundtrip 1 +accept -100.01 -0.1 +expect -11171118.5438199658 -12406.8672748194 +roundtrip 1 +accept -19.99 -0.1 +expect -3123793.9498816459 -12406.8672748194 +roundtrip 1 +accept -20.01 -0.1 +expect -3125812.8326452221 -12406.8672748194 +roundtrip 1 +accept 79.99 -0.1 +expect 6930815.0545556545 -12406.8672748194 +roundtrip 1 +accept 80.01 -0.1 +expect 6932837.7166681662 -12406.8672748194 +roundtrip 1 + +accept -100.0 22.0 +expect -11170107.212763708085 2703699.326638640370 +roundtrip 1 +accept -30.0 22.0 +expect -3854960.906400005333 2703699.326638640370 +roundtrip 1 +accept -160.0 -22.0 +expect -17204085.078888915479 -2703699.326638640370 +roundtrip 1 +accept -60.0 -22.0 +expect -7147455.302013571374 -2703699.326638640370 +roundtrip 1 +accept 20.0 -22.0 +expect 897848.519486704026 -2703699.326638640370 +roundtrip 1 +accept 140.0 -22.0 +expect 12965804.251737114042 -2703699.326638640370 +roundtrip 1 + +direction inverse +accept 200 100 +expect 11.074062190626 0.000806005080 +accept 200 -100 +expect 11.074062191236 -0.000806005080 +accept -200 100 +expect 11.070084714982 0.000806005080 +accept -200 -100 +expect 11.070084715592 -0.000806005080 + +=============================================================================== +# Interrupted Mollweide Ocean View +# PCyl, Sph. +# (Each of the 6 sub-projections tested separately) +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=imoll_o +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect -1357849.196080365917 124066.283433859542 +roundtrip 1 +accept 2 -1 +expect -1357565.742644961691 -124066.283433859542 +roundtrip 1 +accept -2 1 +expect -1760076.593363992404 124066.283433859542 +roundtrip 1 +accept -2 -1 +expect -1759793.139928588411 -124066.283433859542 +roundtrip 1 +accept -89.99 0.1 +expect -10608821.9887007959 12406.8672748194 +roundtrip 1 +accept -90.01 0.1 +expect -10610845.5956505425 12406.8672748194 +roundtrip 1 +accept 59.99 0.1 +expect 4474097.1799880061 12406.8672748194 +roundtrip 1 +accept 60.01 0.1 +expect 4476121.7317749839 12406.8672748194 +roundtrip 1 +accept -59.99 -0.1 +expect -7591833.0556381932 -12406.8672748194 +roundtrip 1 +accept -60.01 -0.1 +expect -7593856.6625879407 -12406.8672748194 +roundtrip 1 +accept 89.99 -0.1 +expect 7491086.1130506080 -12406.8672748194 +roundtrip 1 +accept 90.01 -0.1 +expect 7493109.7200003546 -12406.8672748194 +roundtrip 1 + +accept -140.0 22.0 +expect -15638150.097869191319 2703699.326638640370 +roundtrip 1 +accept -10.0 22.0 +expect -2564531.387931245379 2703699.326638640370 +roundtrip 1 +accept 130.0 22.0 +expect 11514750.299694234505 2703699.326638640370 +roundtrip 1 +accept -110.0 -22.0 +expect -12621161.164806591347 -2703699.326638640370 +roundtrip 1 +accept 20.0 -22.0 +expect 452457.545131357736 -2703699.326638640370 +roundtrip 1 +accept 150.0 -22.0 +expect 13526076.255069304258 -2703699.326638640370 +roundtrip 1 + +direction inverse +accept 200 100 +expect 15.502891574921 0.000806005080 +accept 200 -100 +expect 15.502891573090 -0.000806005080 +accept -200 100 +expect 15.498914099277 0.000806005080 +accept -200 -100 +expect 15.498914097446 -0.000806005080 + +=============================================================================== +# International Map of the World Polyconic +# Mod. Polyconic, Ell +# lat_1= and lat_2= [lon_1=] +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=imw_p +ellps=GRS80 +lat_1=0.5 +lat_2=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222588.441139376 55321.128653810 +accept 2 -1 +expect 222756.906377687 -165827.584288324 +accept -2 1 +expect -222588.441139376 55321.128653810 +accept -2 -1 +expect -222756.906377687 -165827.584288324 + +direction inverse +accept 200 100 +expect 0.001796699 0.500904924 +accept 200 -100 +expect 0.001796698 0.499095076 +accept -200 100 +expect -0.001796699 0.500904924 +accept -200 -100 +expect -0.001796698 0.499095076 + +------------------------------------------------------------------------------- +operation +proj=imw_p +ellps=GRS80 +lat_1=0 +lat_2=10 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 0 +expect 0 0 +accept 0.000000000000 0.000904928485 +expect 0 100 +accept 0.000898315284 0.000000000000 +expect 100 0 + +direction inverse +accept 0 0 +expect 0 0 +accept 0 100 +expect 0.000000000000 0.000904928485 +accept 100 0 +expect 0.000898315284 0.000000000000 + + +=============================================================================== +# Icosahedral Snyder Equal Area +# Sph +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=isea +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.2 mm +accept 2 1 +expect -1097074.948153475765139 3442909.309747453313321 +roundtrip 1 + +accept 2 -1 +expect -1097074.948149705072865 3233611.728292400948703 +roundtrip 1 + +accept -2 1 +expect -1575486.353775786235929 3442168.342736063525081 +roundtrip 1 + +accept -2 -1 +expect -1575486.353772019501776 3234352.695310209877789 +roundtrip 1 + +operation +proj=isea +mode=hex +resolution=31 +accept 0 0 +expect failure + +------------------------------------------------------------------------------- +operation +proj=isea +R=6371007.18091875 +------------------------------------------------------------------------------- +tolerance 0.2 mm + +accept -168.75 58.282525588539 +expect -19186144.870842020958662 3323137.771944524254650 +roundtrip 1 + +accept 11.25 58.282525588539 +expect -15348915.896747918799520 9969413.315350906923413 +roundtrip 1 + +accept -110 54 +expect -15321401.505530973896384 3338358.859094056300819 +roundtrip 1 + +accept -75 45 +expect -12774358.709073608741164 4373188.646695702336729 +roundtrip 1 + +accept 2 49 +expect -642252.939347098814324 8796229.009143760427833 +roundtrip 1 + +accept 0 0 +expect -1331454.074623266700655 3323137.771634854841977 +roundtrip 1 + +accept 90 0 +expect 8564460.639100870117545 593869.297485541785136 +roundtrip 1 + +accept 0 45 +expect -837334.699958428042009 8323409.759132191538811 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=isea +R=6371007.18091875 +orient=pole +------------------------------------------------------------------------------- +tolerance 0.2 mm + +accept -168.75 58.282525588539 +expect -16702163.549901897087693 6386395.630649688653648 +roundtrip 1 + +accept 11.25 58.282525588539 +expect 619648.646531744743697 6212947.536539182066917 +roundtrip 1 + +accept -110 54 +expect -13285649.857057726010680 6149501.348902118392289 +roundtrip 1 + +accept -75 45 +expect -7921366.529368571005762 4728387.055336073972285 +roundtrip 1 + +accept 2 49 +expect 152616.434999307675753 5152048.791301283054054 +roundtrip 1 + +accept 0 0 +expect 0 -195097.133640714135254 +roundtrip 1 + +accept 90 0 +expect 9593072.435467451811 0 +roundtrip 1 + +accept 0 45 +expect 0 4726854.770339427515864 +roundtrip 1 + +=============================================================================== +# Kavrayskiy V +# PCyl., Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=kav5 +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 200360.905308829 123685.082476998 +accept 2 -1 +expect 200360.905308829 -123685.082476998 +accept -2 1 +expect -200360.905308829 123685.082476998 +accept -2 -1 +expect -200360.905308829 -123685.082476998 + +direction inverse +accept 200 100 +expect 0.001996259 0.000808483 +accept 200 -100 +expect 0.001996259 -0.000808483 +accept -200 100 +expect -0.001996259 0.000808483 +accept -200 -100 +expect -0.001996259 -0.000808483 + +------------------------------------------------------------------------------- +operation +proj=kav5 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 201047.703110878 124109.050629171 +accept 2 -1 +expect 201047.703110878 -124109.050629171 +accept -2 1 +expect -201047.703110878 124109.050629171 +accept -2 -1 +expect -201047.703110878 -124109.050629171 + +direction inverse +accept 200 100 +expect 0.001989440 0.000805721 +accept 200 -100 +expect 0.001989440 -0.000805721 +accept -200 100 +expect -0.001989440 0.000805721 +accept -200 -100 +expect -0.001989440 -0.000805721 + + +=============================================================================== +# Kavrayskiy VII +# PCyl, Sph. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=kav7 +a=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 193462.974943729 111701.072127637 +accept 2 -1 +expect 193462.974943729 -111701.072127637 +accept -2 1 +expect -193462.974943729 111701.072127637 +accept -2 -1 +expect -193462.974943729 -111701.072127637 + +direction inverse +accept 200 100 +expect 0.002067483 0.000895247 +accept 200 -100 +expect 0.002067483 -0.000895247 +accept -200 100 +expect -0.002067483 0.000895247 +accept -200 -100 +expect -0.002067483 -0.000895247 + + +=============================================================================== +# Krovak +# PCyl., Ellps. +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=krovak +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect -3196535.232563641 -6617878.867551444 +accept 2 -1 +expect -3260035.440552109 -6898873.614878031 +accept -2 1 +expect -3756305.328869175 -6478142.561571511 +accept -2 -1 +expect -3831703.658501982 -6759107.170155395 +accept 24.833333333333 59.757598563058 +expect 0 0 + +direction inverse +accept 200 100 +expect 24.836218919 59.758403933 +accept 200 -100 +expect 24.836315485 59.756888426 +accept -200 100 +expect 24.830447748 59.758403933 +accept -200 -100 +expect 24.830351182 59.756888426 +accept 0 0 +expect 24.833333333333 59.757598563058 + +------------------------------------------------------------------------------- +operation +proj=krovak +lat_0=-90 +------------------------------------------------------------------------------- +expect failure errno invalid_op_illegal_arg_value + + +# Test point from EPSG Guidance Note 7-2 +------------------------------------------------------------------------------- +operation +proj=krovak +lat_0=49.5 +lon_0=42.5 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +pm=ferro +------------------------------------------------------------------------------- +tolerance 1.1 cm +# 16°50'59.179"E, 50°12'32.442"N +accept 16.849771944444445 50.20901166666667 +expect -568991.00 -1050538.64 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=krovak +lat_0=49.5 +lon_0=42.5 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +pm=ferro +czech +------------------------------------------------------------------------------- +tolerance 1.1 cm +# 16°50'59.179"E, 50°12'32.442"N +accept 16.849771944444445 50.20901166666667 +expect 568991.00 1050538.64 +roundtrip 1 + +=============================================================================== +# Krovak Modified +# PCyl., Ellps. +=============================================================================== + +# Test point from EPSG Guidance Note 7-2 +# Note: all longitudes below are east of Ferro +------------------------------------------------------------------------------- +operation +proj=mod_krovak +lat_0=49.5 +lon_0=42.5 +k=0.9999 +x_0=5000000 +y_0=5000000 +ellps=bessel +------------------------------------------------------------------------------- +tolerance 1 cm +# 34°30'59.179"E of Ferro, 50°12'32.442"N +accept 34.51643861111111 50.20901166666667 +expect -5568990.91 -6050538.71 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=mod_krovak +lat_0=49.5 +lon_0=42.5 +k=0.9999 +x_0=5000000 +y_0=5000000 +ellps=bessel +czech +------------------------------------------------------------------------------- +tolerance 1 cm +accept 34.51643861111111 50.20901166666667 +expect 5568990.91 6050538.71 +roundtrip 1 + +=============================================================================== +# Laborde +# Cyl, Sph +# Special for Madagascar +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=labrd +ellps=GRS80 +lon_0=0.5 +lat_0=2 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 166973.166090228 -110536.912730266 +accept 2 -1 +expect 166973.168287157 -331761.993650884 +accept -2 1 +expect -278345.500519976 -110469.032642032 +accept -2 -1 +expect -278345.504185270 -331829.870790275 + +direction inverse +accept 200 100 +expect 0.501797719 2.000904357 +accept 200 -100 +expect 0.501797717 1.999095641 +accept -200 100 +expect 0.498202281 2.000904357 +accept -200 -100 +expect 0.498202283 1.999095641 + +------------------------------------------------------------------------------- +operation +proj=labrd +ellps=GRS80 +lat_0=0 +accept 0 0 +expect failure errno invalid_op_illegal_arg_value + +=============================================================================== +# Lambert Azimuthal Equal Area +# Azi, Sph&Ell +=============================================================================== + +------------------------------------------------------------------------------- +operation +proj=laea +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 222602.471450095 110589.827224410 +accept 2 -1 +expect 222602.471450095 -110589.827224409 +accept -2 1 +expect -222602.471450095 110589.827224410 +accept -2 -1 +expect -222602.471450095 -110589.827224409 +accept 150 50 +expect 4372597.1888 10352365.4614 + +accept 180 0 +expect failure errno coord_transfm_outside_projection_domain + +direction inverse +accept 200 100 +expect 0.001796631 0.000904369 +accept 200 -100 +expect 0.001796631 -0.000904369 +accept -200 100 +expect -0.001796631 0.000904369 +accept -200 -100 +expect -0.001796631 -0.000904369 +accept 4372597.1888 10352365.4614 +expect 150 50 + +accept 13000000 0 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +operation +proj=laea +R=6400000 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 2 1 +expect 223365.281370125 111716.668072916 +accept 2 -1 +expect 223365.281370125 -111716.668072916 +accept -2 1 +expect -223365.281370125 111716.668072916 +accept -2 -1 +expect -223365.281370125 -111716.668072916 + +accept 180 0 +expect failure errno coord_transfm_outside_projection_domain + +direction inverse +accept 200 100 +expect 0.001790493 0.000895247 +accept 200 -100 +expect 0.001790493 -0.000895247 +accept -200 100 +expect -0.001790493 0.000895247 +accept -200 -100 +expect -0.001790493 -0.000895247 + +------------------------------------------------------------------------------- +# Test oblique aspect of the spherical form +------------------------------------------------------------------------------- +operation +proj=laea +R=1 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 45 +expect 0 0 +accept 0 0 +expect 0 -0.7654 +accept 0 90 +expect 0 0.7654 +accept 0 -45 +expect 0 -1.4142 +accept 45 45 +expect 0.5194 0.1521 + +tolerance 0.1 mm +accept 45 45 +roundtrip 100 + +# error when waaay outside the sphere +direction inverse +accept 0 10 +expect failure errno coord_transfm_outside_projection_domain + +------------------------------------------------------------------------------- +# Test oblique aspect of the ellipsoidal form +------------------------------------------------------------------------------- +operation +proj=laea +ellps=GRS80 +lat_0=45 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 0 45 +expect 0 0 +accept 0 0 +expect 0 -4860248.8602 +accept 0 -45 +expect 0 -8984728.0442 +accept 45 45 +expect 3318800.8682 968788.2336 + +# Passes 0.1 mm except on i386. Cf https://github.com/OSGeo/PROJ/pull/4441#issuecomment-2744141103 +tolerance 50 mm +accept 0 90 +expect 0 4886594.2207 + +tolerance 10 cm +accept 45 45 +roundtrip 100 + +# test rho diff --git a/test/ProjNet.Tests/Fixtures/gie/defmodel.gie b/test/ProjNet.Tests/Fixtures/gie/defmodel.gie new file mode 100644 index 00000000..64f3cd72 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/defmodel.gie @@ -0,0 +1,134 @@ + +------------------------------------------------------------------------------- +=============================================================================== +Test +proj=defmodel +=============================================================================== + + + +# Missing +model +operation +proj=defmodel +expect failure errno invalid_op_missing_arg + +# +model doesn't point to an existing file +operation +proj=defmodel +model=i_do_not_exist +expect failure errno invalid_op_file_not_found_or_invalid + +# Not a JSON file +operation +proj=defmodel +model=proj.ini +expect failure errno invalid_op_file_not_found_or_invalid + +# Missing time +operation +proj=defmodel +model=tests/simple_model_degree_horizontal.json +accept 2 49 30 HUGE_VAL +expect failure errno coord_transfm_missing_time + +operation +proj=defmodel +model=tests/simple_model_degree_horizontal.json +direction inverse +accept 2 49 30 HUGE_VAL +expect failure errno coord_transfm_missing_time + +# Horizontal deformation with horizontal unit = degree +operation +proj=defmodel +model=tests/simple_model_degree_horizontal.json +tolerance 0.1 mm +accept 2 49 30 2020 +expect 3 51 30 2020 +roundtrip 1 + +# 3D deformation with horizontal unit = degree +operation +proj=defmodel +model=tests/simple_model_degree_3d.json +tolerance 0.1 mm +accept 2 49 30 2020 +expect 3 51 33 2020 +roundtrip 1 + +# Horizontal deformation with horizontal unit = metre +operation +proj=pipeline +step +inv +proj=merc +step +proj=defmodel +model=tests/simple_model_metre_horizontal.json +step +proj=merc +tolerance 0.1 mm +accept 10 20 30 2020 +expect 11 22 30 2020 +roundtrip 1 + +# 3D deformation with horizontal unit = metre +operation +proj=pipeline +step +inv +proj=merc +step +proj=defmodel +model=tests/simple_model_metre_3d.json +step +proj=merc +tolerance 0.1 mm +accept 10 20 30 2020 +expect 11 22 33 2020 +roundtrip 1 + +# 3D deformation with horizontal unit = metre and a projeced grid +operation +proj=pipeline +step +proj=defmodel +model=tests/simple_model_projected.json +tolerance 0.1 mm + +accept 1500200.0 5400400.0 30 2020 +expect 1500200.588 5400399.722 30.6084 2020 +roundtrip 1 + +# South-west corner +accept 1500000.0 5400000.0 30 2020 +expect 1500000.4 5399999.8 30.84 2020 +roundtrip 1 + +# South-east corner +accept 1501000.0 5400000.0 30 2020 +expect 1501000.5 5399999.75 30.75 2020 +roundtrip 1 + +# North-west corner +accept 1500000.0 5401000.0 30 2020 +expect 1500000.8 5400999.6 30.36 2020 +roundtrip 1 + +# North-east corner +accept 1501000.0 5401000.0 30 2020 +expect 1501001.0 5400999.7 30 2020 +roundtrip 1 + +# Test geocentric addition of components +operation +proj=pipeline +step +inv +proj=merc +step +proj=defmodel +model=tests/simple_model_metre_3d_geocentric.json +step +proj=merc +tolerance 0.1 mm +accept 10 20 30 2020 +expect 11 22 33 2020 +roundtrip 1 + +# Vertical deformation with vertical unit = metre +operation +proj=defmodel +model=tests/simple_model_metre_vertical.json +tolerance 0.1 mm +accept 2 49 30 2020 +expect 2 49 33 2020 +roundtrip 1 + +# Adjust for 360 degree longitude offsets +operation +proj=defmodel +model=tests/simple_model_metre_vertical.json +tolerance 0.1 mm + +accept 362 49 30 2020 +expect 2 49 33 2020 + +operation +proj=defmodel +model=tests/simple_model_wrap_east.json + +accept 165.9 -37.3 10 2020 +expect 165.9 -37.3 10.4525 2020 + +operation +proj=defmodel +model=tests/simple_model_wrap_west.json + +accept 165.9 -37.3 10 2020 +expect 165.9 -37.3 10.4525 2020 + +# Test geocentric bilinear interpolation method +operation +proj=defmodel +model=tests/simple_model_polar.json +tolerance 0.1 mm + +accept 20 -90 15 2020 +expect 27.4743245365 -89.9999747721 18.0000 2020 + +accept 120 -90 15 2020 +expect 27.4737934098 -89.9999747718 18.0000 2020 + +accept 235 -89.5 15 2020 +expect -124.9986638571 -89.5000223708 17.3750 2020 + +accept 45 -89.5 15 2020 +expect 44.9991295392 -89.4999759438 18.5469 2020 + + diff --git a/test/ProjNet.Tests/Fixtures/gie/deformation.gie b/test/ProjNet.Tests/Fixtures/gie/deformation.gie new file mode 100644 index 00000000..25b5cdc8 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/deformation.gie @@ -0,0 +1,176 @@ +=============================================================================== +Test for the deformation operation - Kinematic Gridshifting + +For all the deformation tests the alaska and egm96_15.gtx grids are used even +though they are not parts of a deformation model, they are in the proper format +and for testing purposes it doesn't really matter all that much... + +The input coordinate is located at long=60, lam=-160 - somewhere in Alaska. + +=============================================================================== + + + +------------------------------------------------------------------------------- +# Test with an extract of nkgrf03vel_realigned with ctable2+gtx +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=cart +ellps=GRS80 \ + +step +proj=deformation \ + +xy_grids=tests/nkgrf03vel_realigned_xy_extract.ct2 \ + +z_grids=tests/nkgrf03vel_realigned_z_extract.gtx +ellps=GRS80 +dt=1 \ + +step +proj=cart +ellps=GRS80 +inv +------------------------------------------------------------------------------- +tolerance 0.05 mm +accept 21.5 63 0 +expect 21.5000000049 62.9999999937 0.0083 +roundtrip 5 + +------------------------------------------------------------------------------- +# Test with an extract of nkgrf03vel_realigned with GeoTIFF +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=cart +ellps=GRS80 \ + +step +proj=deformation \ + +grids=tests/nkgrf03vel_realigned_extract.tif +ellps=GRS80 +dt=1 \ + +step +proj=cart +ellps=GRS80 +inv +------------------------------------------------------------------------------- +tolerance 0.05 mm +accept 21.5 63 0 +expect 21.5000000049 62.9999999937 0.0083 +roundtrip 5 + +------------------------------------------------------------------------------- +# Test the +dt parameter +------------------------------------------------------------------------------- +operation +proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx \ + +ellps=GRS80 +dt=16.0 # 2016.0 - 2000.0 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457 +expect -3004295.7000 -1093474.2097 5500477.3397 +roundtrip 5 + +# Test that errors are reported for coordinates outside the grid. +# Here we test 120W 40N which is well outside the alaska grid. +accept -2446353.8001 -4237209.0750 4077985.572 +expect failure errno coord_transfm_outside_grid +accept -2446353.8001 -4237209.0750 4077985.572 +expect failure errno coord_transfm_outside_grid + + +------------------------------------------------------------------------------- +# Test using both horizontal and vertical grids +------------------------------------------------------------------------------- +operation +proj=deformation \ + +xy_grids=alaska +z_grids=egm96_15.gtx +t_epoch=2016.0 +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 0.1 mm +direction inverse +accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457 2000.0 +expect -3004295.7000 -1093474.2097 5500477.3397 2000.0 +roundtrip 5 + +# Missing time +direction forward +accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457 HUGE_VAL +expect failure errno coord_transfm_missing_time + +direction inverse +accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457 HUGE_VAL +expect failure errno coord_transfm_missing_time + +------------------------------------------------------------------------------- +operation proj=deformation xy_grids=alaska +dt=1.0 ellps=GRS80 +expect failure errno invalid_op_missing_arg + +operation proj=deformation z_grids=egm96_15.gtx +dt=1.0 ellps=GRS80 +expect failure errno invalid_op_missing_arg + +operation proj=deformation xy_grids=nonexisting z_grids=egm96_15.gtx \ + +dt=1.0 ellps=GRS80 +expect failure errno invalid_op_file_not_found_or_invalid + +operation proj=deformation xy_grids=alaska z_grids=nonexisting \ + +dt=1.0 ellps=GRS80 +expect failure errno invalid_op_file_not_found_or_invalid + +operation proj=deformation xy_grids=alaska z_grids=nonexisting ellps=GRS80 +expect failure errno invalid_op_file_not_found_or_invalid + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=egm96_15.gtx +t_epoch=2010.0 +t_final=2018.0 +------------------------------------------------------------------------------- +tolerance 0.1 mm + +accept 12 56 0.0 2000.0 +expect 12 56 -36.9960 2000.0 +roundtrip 100 + +accept 12 56 0.0 2011.0 +expect 12 56 0.0 2011.0 +roundtrip 100 + +accept 12 56 0.0 2019.0 +expect 12 56 0.0 2019.0 +roundtrip 100 + +accept 12 56 0.0 +expect 12 56 -36.9960 +roundtrip 100 + + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=egm96_15.gtx +t_epoch=2010.0 +t_final=now +------------------------------------------------------------------------------- +tolerance 0.1 mm + +accept 12 56 0.0 2000.0 +expect 12 56 -36.9960 2000.0 +roundtrip 100 + +accept 12 56 0.0 2011.0 +expect 12 56 0.0 2011.0 +roundtrip 1000 + +accept 12 56 0.0 3011.0 +expect 12 56 0.0 3011.0 +roundtrip 100 + + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=alaska +t_epoch=2010.0 +t_final=2018.0 +------------------------------------------------------------------------------- +tolerance 0.1 mm + +accept -147.0 64.0 0.0 2000.0 +expect -147.0023233121 63.9995792119 0.0 2000.0 +roundtrip 100 + +accept -147.0 64.0 0.0 2011.0 +expect -147.0 64.0 0.0 2011.0 +roundtrip 100 + +accept -147.0 64.0 0.0 2011.0 +expect -147.0 64.0 0.0 2020.0 +roundtrip 100 + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=alaska +t_epoch=2010.0 +t_final=now +------------------------------------------------------------------------------- +tolerance 0.1 mm + +accept -147.0 64.0 0.0 2000.0 +expect -147.0023233121 63.9995792119 0.0 2000.0 +roundtrip 100 + +accept -147.0 64.0 0.0 2011.0 +expect -147.0 64.0 0.0 2011.0 +roundtrip 100 + +accept -147.0 64.0 0.0 3011.0 +expect -147.0 64.0 0.0 3011.0 +roundtrip 100 + + + diff --git a/test/ProjNet.Tests/Fixtures/gie/ellipsoid.gie b/test/ProjNet.Tests/Fixtures/gie/ellipsoid.gie new file mode 100644 index 00000000..ea0e326a --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/ellipsoid.gie @@ -0,0 +1,188 @@ +=============================================================================== + +Test pj_ellipsoid, the reimplementation of pj_ell_set + +=============================================================================== + + + + +------------------------------------------------------------------------------- +# First a spherical example +------------------------------------------------------------------------------- +operation proj=merc R=6400000 +------------------------------------------------------------------------------- +tolerance 10 nm +accept 1 2 +expect 111701.0721276371 223447.5262032605 + +accept 12 55 +expect 1340412.8655316452 7387101.1430967357 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Then an explicitly defined ellipsoidal example +------------------------------------------------------------------------------- +operation proj=merc a=6400000 rf=297 +------------------------------------------------------------------------------- +tolerance 10 nm +accept 1 2 +expect 111701.0721276371 221945.9681832088 + +accept 12 55 +expect 1340412.8655316452 7351803.9151705895 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Then try using a built in ellipsoid +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 10 nm +accept 1 2 +expect 111319.4907932736 221194.0771604237 + +accept 12 55 +expect 1335833.8895192828 7326837.7148738774 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Then try to fail deliberately +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80000000000 +expect failure errno invalid_op_illegal_arg_value +operation proj=merc +a=-1 +expect failure errno invalid_op_illegal_arg_value + +operation proj=merc +accept 0 0 +expect 0 0 + +operation proj=merc +a=1 +es=-1 +expect failure errno invalid_op_illegal_arg_value + +operation proj=merc +R=0 +expect failure errno invalid_op_illegal_arg_value + +operation +proj=merc +R_a +a=2 +f=2 +expect failure errno invalid_op_illegal_arg_value + +operation +expect failure +operation cobra +expect failure +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Finally test the spherification functionality +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 R_A +tolerance 10 nm +accept 12 55 +expect 1334340.6237297705 7353636.6296552019 +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 R_V +tolerance 10 nm +accept 12 55 +expect 1334339.2852675652 7353629.2533042720 +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 R_a +tolerance 10 nm +accept 12 55 +expect 1333594.4904527504 7349524.6413825499 +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 R_g +tolerance 10 nm +accept 12 55 +expect 1333592.6102291327 7349514.2793497816 +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 R_h +tolerance 10 nm +accept 12 55 +expect 1333590.7300081658 7349503.9173316229 +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 R_lat_a=60 +tolerance 10 nm +accept 12 55 +expect 1338073.7436268919 7374210.0924803326 +------------------------------------------------------------------------------- +operation proj=merc ellps=GRS80 R_lat_g=60 +tolerance 10 nm +accept 12 55 +expect 1338073.2696101593 7374207.4801437631 +------------------------------------------------------------------------------- + +operation proj=merc a=1E77 R_lat_a=90 b=1 +expect failure + +------------------------------------------------------------------------------- +# This one from testvarious failed at first version of the pull request +------------------------------------------------------------------------------- +operation proj=healpix a=1 lon_0=0 ellps=WGS84 +------------------------------------------------------------------------------- +accept 0 41.937853904844985 +expect 0 0.78452 +accept -90 0 +expect -1.56904 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Shape parameters +------------------------------------------------------------------------------- +operation proj=utm zone=32 ellps=GRS80 rf=0 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm zone=32 ellps=GRS80 e=-0.5 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm zone=32 ellps=GRS80 e=1 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm zone=32 ellps=GRS80 es=1 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm zone=32 a=1 es=1.1 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm zone=32 ellps=GRS80 b=0 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm zone=32 ellps=GRS80 f=1 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm zone=32 ellps=GRS80 b=6000000 +accept 12 55 +expect 699293.0880 5674591.5295 + +operation proj=utm zone=32 ellps=GRS80 rf=300 +accept 12 55 +expect 691873.1212 6099054.9661 + +operation proj=utm zone=32 ellps=GRS80 f=0.00333333333333 +accept 12 55 +expect 691873.1212 6099054.9661 + +operation proj=utm zone=32 ellps=GRS80 b=6000000 +accept 12 55 +expect 699293.0880 5674591.5295 + +operation proj=utm zone=32 a=6400000 b=6000000 +accept 12 55 +expect 700416.5900 5669475.8884 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test that flattening can be set to zero +------------------------------------------------------------------------------- +operation proj=merc +a=1.0 +f=0.0 +------------------------------------------------------------------------------- +accept 12 56 +expect 0.20944 1.18505 +------------------------------------------------------------------------------- + + + diff --git a/test/ProjNet.Tests/Fixtures/gie/geotiff_grids.gie b/test/ProjNet.Tests/Fixtures/gie/geotiff_grids.gie new file mode 100644 index 00000000..f222c2ae --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/geotiff_grids.gie @@ -0,0 +1,364 @@ + +------------------------------------------------------------------------------- +=============================================================================== +Test GeoTIFF grids +=============================================================================== + + + +# Those first tests using +proj=vgridshift only test the capability of reading +# correctly a value from various formulations of GeoTIFF file, hence only the +# forward path is tested (reverse path is tested in other files) + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_pixelispoint.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_pixelisarea.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_deflate.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_deflate_floatingpointpredictor.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_uint16.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_uint16_with_scale_offset.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_int16.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_int32.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_uint32.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_float64.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +# The overview should be ignored +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_with_overview.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_in_second_channel.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_bigtiff.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_bigendian.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_bigendian_bigtiff.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_bottomup_with_scale.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_bottomup_with_matrix.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_with_subgrid.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.5 52.5 0 +expect 4.5 52.5 11.5 + +# In subgrid +accept 5.5 53.5 0 +expect 5.5 53.5 110.0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_nodata.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 4.05 52.1 0 +expect 4.05 52.1 10 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_hydroid_height.tif +multiplier=1 +------------------------------------------------------------------------------- +accept 2 49 0 +expect 2 49 44.643493652 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_invalid_channel_type.tif +multiplier=1 +------------------------------------------------------------------------------- +expect failure errno invalid_op_file_not_found_or_invalid +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_vgrid_unsupported_byte.tif +multiplier=1 +------------------------------------------------------------------------------- +expect failure errno invalid_op_file_not_found_or_invalid +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/us_noaa_geoid06_ak_subset_at_antimeridian.tif +multiplier=1 +------------------------------------------------------------------------------- +tolerance 1 mm + +accept 179.99 54.5 0 +expect 179.99 54.5 -2.2226 + +accept -179.99 54.5 0 +expect -179.99 54.5 -2.3488 + +accept 179.999999 54.5 0 +expect 179.999999 54.5 -2.2872 + +accept -179.999999 54.5 0 +expect -179.999999 54.5 -2.2872 + +accept 179.8 54.5 0 +expect 179.8 54.5 -0.7011 + +accept 179.799 54.5 0 +expect failure errno coord_transfm_outside_grid + +accept 180.1833333 54.5 0 +expect -179.8166667 54.5 -3.1933 + +accept -179.8166667 54.5 0 +expect -179.8166667 54.5 -3.1933 + +accept 180.184 54.5 0 +expect failure errno coord_transfm_outside_grid + +accept -179.816 54.5 0 +expect failure errno coord_transfm_outside_grid + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_separate.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_strip.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_tiled.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_tiled_separate.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_positive_west.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_lon_shift_first.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_radian.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_degree.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +# The overview should be ignored +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_with_overview.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_extra_ifd_with_other_info.tif +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +# Subset of NTv2_0.gsb +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_with_subgrid.tif +------------------------------------------------------------------------------- +# In subgrid ALbanff, of parent CAwest +accept -115.5416667 51.1666667 0 +expect -115.5427092888 51.1666899972 0 + +# In subgrid ONtronto, of parent CAeast +accept -80.5041667 44.5458333 0 +expect -80.50401615833 44.5458827236 0 +------------------------------------------------------------------------------- + +# Subset of NTv2_0.gsb +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_with_subgrid_no_grid_name.tif +------------------------------------------------------------------------------- +# In subgrid ALbanff, of parent CAwest +accept -115.5416667 51.1666667 0 +expect -115.5427092888 51.1666899972 0 + +# In subgrid ONtronto, of parent CAeast +accept -80.5041667 44.5458333 0 +expect -80.50401615833 44.5458827236 0 +------------------------------------------------------------------------------- + +# Check a nested grid of a nested grid only based on spatial extent analysis +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_with_two_level_of_subgrids_no_grid_name.tif +------------------------------------------------------------------------------- +accept -45.0 22.5 +accept -44.9983333334 22.5013888889 + +# Check a nested grid of a nested grid only based on spatial extent analysis +------------------------------------------------------------------------------- +operation +proj=vgridshift +grids=tests/test_hgrid_with_two_level_of_subgrids_no_grid_name.tif +multiplier=1 +------------------------------------------------------------------------------- +accept -45.0 22.5 0 +accept -45.0 22.5 5 + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_vgrid.tif +------------------------------------------------------------------------------- +expect failure errno invalid_op_file_not_found_or_invalid +------------------------------------------------------------------------------- + + +# IGNF:LAMBE to IGNF:LAMB93 using xyzgridshift operation +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +inv +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 \ + +k_0=0.99987742 +x_0=600000 +y_0=2200000 +ellps=clrk80ign +pm=paris \ + +step +proj=push +v_3 \ + +step +proj=cart +ellps=clrk80ign \ + +step +proj=xyzgridshift +grids=tests/subset_of_gr3df97a.tif +grid_ref=output_crs +ellps=GRS80 \ + +step +proj=cart +ellps=GRS80 +inv \ + +step +proj=pop +v_3 \ + +step +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 \ + +x_0=700000 +y_0=6600000 +ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 1 mm + +accept 814149.529 1887019.768 0 +expect 860690.804 6319036.849 0 +# If using ntf_r93.gsb, one gets: 860690.805 6319036.850 + +roundtrip 1 +------------------------------------------------------------------------------- + + + diff --git a/test/ProjNet.Tests/Fixtures/gie/gridshift.gie b/test/ProjNet.Tests/Fixtures/gie/gridshift.gie new file mode 100644 index 00000000..98cf0657 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/gridshift.gie @@ -0,0 +1,295 @@ + +------------------------------------------------------------------------------- +=============================================================================== +Test generalized shift grid method +=============================================================================== + + + +----------------------------- +# Classic lat-lon shift grids +----------------------------- + +# Subset of NTv2_0.gsb +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/test_hgrid_with_subgrid.tif +------------------------------------------------------------------------------- + +accept 179.799 54.5 0 +expect failure errno coord_transfm_outside_grid + +# In subgrid ALbanff, of parent CAwest +accept -115.5416667 51.1666667 0 +expect -115.5427092888 51.1666899972 0 +roundtrip 1 + +# In subgrid ONtronto, of parent CAeast +accept -80.5041667 44.5458333 0 +expect -80.50401615833 44.5458827236 0 +roundtrip 1 +------------------------------------------------------------------------------- + +# Subset of NTv2_0.gsb +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/test_hgrid_with_subgrid_no_grid_name.tif +------------------------------------------------------------------------------- + +# In subgrid ALbanff, of parent CAwest +accept -115.5416667 51.1666667 0 +expect -115.5427092888 51.1666899972 0 +roundtrip 1 + +# In subgrid ONtronto, of parent CAeast +accept -80.5041667 44.5458333 0 +expect -80.50401615833 44.5458827236 0 +roundtrip 1 +------------------------------------------------------------------------------- + + + +-------------------- +# Classic geoidgrids +-------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_geoid06_ak_subset_at_antimeridian.tif +------------------------------------------------------------------------------- +tolerance 1 mm + +accept 179.99 54.5 0 +expect 179.99 54.5 -2.2226 +roundtrip 1 + +accept -179.99 54.5 0 +expect -179.99 54.5 -2.3488 +roundtrip 1 + +accept 179.999999 54.5 0 +expect 179.999999 54.5 -2.2872 +roundtrip 1 + +accept -179.999999 54.5 0 +expect -179.999999 54.5 -2.2872 +roundtrip 1 + +accept 179.8 54.5 0 +expect 179.8 54.5 -0.7011 +roundtrip 1 + +accept 179.799 54.5 0 +expect failure errno coord_transfm_outside_grid + +accept 180.1833333 54.5 0 +expect -179.8166667 54.5 -3.1933 +roundtrip 1 + +accept -179.8166667 54.5 0 +expect -179.8166667 54.5 -3.1933 +roundtrip 1 + +accept 180.184 54.5 0 +expect failure errno coord_transfm_outside_grid + +accept -179.816 54.5 0 +expect failure errno coord_transfm_outside_grid + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/test_hydroid_height.tif +------------------------------------------------------------------------------- +accept 2 49 0 +expect 2 49 44.643493652 +------------------------------------------------------------------------------- + + +---------------------------------------------------------------------- +# Geographic 3D offsets with quadratic interpolation (defined in file) +---------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_2007_nad83_2011_conus_extract.tif +------------------------------------------------------------------------------- +tolerance 1 mm + +# Test point from https://www.ngs.noaa.gov/NCAT, exactly at one grid node + +accept -95.5000000000 37.0000000000 10.000 +expect -95.4999998219 37.0000000147 9.984 +roundtrip 1 + +# Test point from https://www.ngs.noaa.gov/NCAT +# specifically selected to be close to the middle of a pixel + +accept -95.4916666666 37.0083333333 10.000 +expect -95.4916664889 37.0083333484 9.984 +roundtrip 1 + +# Test point from https://www.ngs.noaa.gov/NCAT +# specifically selected to be close to the middle of a pixel (but +# other side of previous test point) + +accept -95.4916666667 37.0083333334 10.000 +expect -95.4916664890 37.0083333485 9.984 +roundtrip 1 + +# Test point at north-east of truncated grid + +accept -95.416667 37.083333 0.000 +expect -95.4166668251 37.0833330159 -0.0157 + +# Test point at south-west of truncated grid + +accept -95.58333 36.91667 0.000 +expect -95.5833298166 36.9166700108 -0.0157 +roundtrip 1 + + + +---------------------------------------------------------------------- +# Geographic 3D offsets, but split in one grid with horizontal offset and +# another one with ellipsoidal height offset, with quadratic interpolation (defined in file) +---------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_2007_nad83_2011_alaska_extract.tif +------------------------------------------------------------------------------- +tolerance 1 mm + +# Test point from https://www.ngs.noaa.gov/NCAT, exactly at one grid node + +accept -158.0 61.5 10.000 +expect -157.9999996115 61.499999564 9.987 +roundtrip 1 + +# Test point from https://www.ngs.noaa.gov/NCAT + +accept -158.1 61.51 10.000 +expect -158.0999996011 61.5099995458 9.987 +roundtrip 1 + + +---------------------------------------------------------------------- +# Combine 2 above type of grids +---------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_2007_nad83_2011_conus_extract.tif,tests/us_noaa_nadcon5_nad83_2007_nad83_2011_alaska_extract.tif +------------------------------------------------------------------------------- +tolerance 1 mm + +# Test point from https://www.ngs.noaa.gov/NCAT, exactly at one grid node + +accept -95.5000000000 37.0000000000 10.000 +expect -95.4999998219 37.0000000147 9.984 +roundtrip 1 + +# Test point from https://www.ngs.noaa.gov/NCAT, exactly at one grid node + +accept -158.0 61.5 10.000 +expect -157.9999996115 61.499999564 9.987 +roundtrip 1 + + +---------------------------------------------------------------------- +# Test +no_z_transform +---------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_2007_nad83_2011_conus_extract.tif +no_z_transform +------------------------------------------------------------------------------- +tolerance 1 mm + +accept -95.5000000000 37.0000000000 10.000 +expect -95.4999998219 37.0000000147 10.000 +roundtrip 1 + +------------------------------ +# Test bilinear vs biquadratic +------------------------------ + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_2007_nad83_2011_conus_extract.tif +interpolation=biquadratic +------------------------------------------------------------------------------- +tolerance 0.005 mm + +accept -95.4916666666 37.0083333333 10.000 +expect -95.49166648893 37.00833334837 9.984340 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_2007_nad83_2011_conus_extract.tif +interpolation=bilinear +------------------------------------------------------------------------------- +tolerance 0.001 mm + +accept -95.4916666666 37.0083333333 10.000 +expect -95.49166648893 37.00833334838 9.984341 +roundtrip 1 + +---------------------------------------------------------------------------------------------------------------------- +# Test case with inverse biquadratic convergence where we are around a location where the interpolation window changes +---------------------------------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_1986_nad83_harn_conus_extract_sanfrancisco.tif +interpolation=biquadratic +------------------------------------------------------------------------------- +direction inverse +tolerance 0.005 mm + +accept -122.4250009683 37.8286740788 +expect -122.4249999391 37.8286728006 + +---------------------------------------------------------------------- +# Test a grid referenced in a projected CRS, with an additional constant offset +---------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/test_gridshift_projected.tif +------------------------------------------------------------------------------- + +tolerance 0.5 mm + +accept -598000.000 -1160020.000 0.000 +expect -5597999.885 -6160019.978 0.000 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=krovak +lat_0=49.5 +lon_0=24.8333333333333 +alpha=30.2881397527778 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel \ + +step +proj=gridshift +grids=tests/test_gridshift_projected.tif \ + +step +inv +proj=mod_krovak +lat_0=49.5 +lon_0=24.8333333333333 +alpha=30.2881397222222 +k=0.9999 +x_0=5000000 +y_0=5000000 +ellps=bessel +------------------------------------------------------------------------------- + +tolerance 0.5 mm + +accept 16.610452439 49.202425040 0.000 +expect 16.610455233 49.202425034 0.000 +roundtrip 1 + +------------- +# Error cases +------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/us_noaa_nadcon5_nad83_2007_nad83_2011_conus_extract.tif +interpolation=invalid +------------------------------------------------------------------------------- +expect failure errno invalid_op_illegal_arg_value +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +------------------------------------------------------------------------------- +expect failure errno invalid_op_missing_arg +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/test_vgrid_unsupported_byte.tif +------------------------------------------------------------------------------- +expect failure errno invalid_op_file_not_found_or_invalid +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=gridshift +grids=tests/i_do_not_exist.tif +------------------------------------------------------------------------------- +expect failure errno invalid_op_file_not_found_or_invalid +------------------------------------------------------------------------------- + + diff --git a/test/ProjNet.Tests/Fixtures/gie/guyou.gie b/test/ProjNet.Tests/Fixtures/gie/guyou.gie new file mode 100644 index 00000000..2ed2ab49 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/guyou.gie @@ -0,0 +1,2133 @@ + +------------------------------------------------------------ +# This gie file was automatically generated using libproject +# where the guyou code was adapted from +------------------------------------------------------------ + +------------------------------------------------------------ +operation +proj=guyou +R=6370997 +tolerance 1 mm +------------------------------------------------------------ +accept -179.2338274749 -90.7265739758 +expect failure errno coord_transfm_invalid_coord + +accept -169.3015609686 -90.0683270041 +expect failure errno coord_transfm_invalid_coord + +accept -159.4420546811 -89.5695551279 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.0045856345 -89.3536369188 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.7153960960 -88.6283945950 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.2286632319 -88.1551228787 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.6286953558 -87.5083524092 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.3328522812 -86.7510648640 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.1598524965 -86.5788079857 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.3858632536 -85.7390309668 +expect -671252.534 -11805089.168 + +accept -79.5087279110 -85.6235707551 +expect -677891.869 -11686402.401 + +accept -69.4424228202 -84.9021341881 +expect -751854.643 -11529210.346 + +accept -59.8852518498 -84.8051735959 +expect -707106.316 -11400468.436 + +accept -49.2060555595 -84.5394950727 +expect -649513.005 -11249221.343 + +accept -39.5503275201 -84.2989454792 +expect -569372.760 -11119412.618 + +accept -29.3360829537 -83.5760929301 +expect -492270.211 -10930876.977 + +accept -19.3394041666 -83.2992154597 +expect -346317.029 -10818825.415 + +accept -9.2056484989 -82.3824201970 +expect -189497.900 -10632767.439 + +accept 0.1120336125 -81.7746272088 +expect 2495.102 -10523224.283 + +accept 10.5665512094 -81.6687644685 +expect 237086.343 -10527998.139 + +accept 20.1722250768 -81.3102866550 +expect 465363.960 -10531084.361 + +accept 30.5265377635 -80.9100945160 +expect 718603.642 -10578303.571 + +accept 40.4565955888 -79.9253199265 +expect 1020024.277 -10597524.229 + +accept 50.1397967283 -79.1591500985 +expect 1304571.493 -10703166.350 + +accept 60.7532284194 -78.3809992723 +expect 1600003.385 -10897309.840 + +accept 70.7513325682 -77.7277296032 +expect 1840823.214 -11154024.272 + +accept 80.2085188693 -77.6809008485 +expect 1936586.409 -11469925.164 + +accept 90.8009658259 -76.9680414794 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.6742326194 -76.5942100817 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.5209403479 -75.7585741711 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.5896919383 -74.7649093703 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.7851397036 -73.8582467239 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.7197125638 -72.9106110624 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.4163065521 -72.4059990981 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.4383759680 -71.9757412863 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.5708534042 -71.9525642223 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.2751947006 -71.5923171899 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.7095201992 -79.9687467646 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.0436424710 -79.2424043855 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.2911675882 -78.8917009107 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.2815042820 -78.0224816760 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.5027225646 -77.9987560452 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.2026665027 -77.1138023157 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.6161746714 -76.2954327600 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.9384914753 -75.7674050764 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.2183181307 -75.3730011624 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.2190150125 -74.7701795551 +expect -2454071.146 -11777557.324 + +accept -79.9068086129 -74.7070663318 +expect -2420802.270 -11364639.564 + +accept -69.8146319687 -74.4419812746 +expect -2332536.533 -10920591.181 + +accept -59.6461988138 -73.5860650374 +expect -2238931.921 -10443654.878 + +accept -49.5451009633 -73.4954549764 +expect -1957322.263 -10070951.521 + +accept -39.9329751143 -72.8013395667 +expect -1694139.274 -9695298.489 + +accept -29.5097310678 -72.3497881968 +expect -1313051.961 -9382518.879 + +accept -19.9921230083 -71.8366964443 +expect -925602.320 -9142855.035 + +accept -9.2490763384 -71.0229026677 +expect -448842.813 -8910142.058 + +accept 0.7720814716 -70.5972765500 +expect 38276.499 -8815699.571 + +accept 10.0262635630 -70.0895817655 +expect 507116.076 -8777330.134 + +accept 20.7479812506 -69.9032152988 +expect 1049893.591 -8874298.589 + +accept 30.3631890384 -69.4998925877 +expect 1545301.219 -9005805.601 + +accept 40.5162040333 -69.3056454098 +expect 2041397.015 -9262605.098 + +accept 50.6243675940 -68.6167358219 +expect 2562297.663 -9551642.297 + +accept 60.0486043403 -67.6600969757 +expect 3069614.536 -9891241.853 + +accept 70.4774261179 -66.9833418981 +expect 3532980.732 -10438619.729 + +accept 80.8997122704 -66.2858473379 +expect 3898590.190 -11120572.688 + +accept 90.1834036836 -65.9440561722 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.1984006355 -65.8229326488 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.0486236854 -65.7960345782 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.0510835725 -65.5190096678 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.0896340570 -64.7428784330 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.3678752942 -64.2229928140 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.9150615805 -63.6990548356 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.7280406636 -63.2384418688 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.9183869916 -62.3774990742 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.5619551786 -61.8063907044 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.5943789042 -69.9992085183 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.2134281484 -69.9776667645 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.4518981501 -69.3934026425 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3420413989 -68.4537518490 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.1131932532 -67.9826718761 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.7785737569 -67.0804523760 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.8160590971 -66.1963102135 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.4603476745 -65.8550220266 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.0748524068 -64.9756976432 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.9206313411 -64.7601702734 +expect -4268828.098 -11805632.912 + +accept -79.4513980949 -64.0272106872 +expect -4293419.464 -10904353.202 + +accept -69.0843325805 -63.4870607362 +expect -4057816.546 -10051354.989 + +accept -59.7650575505 -63.4512934361 +expect -3634811.426 -9423524.250 + +accept -49.8561841911 -63.1756677100 +expect -3130112.256 -8850654.578 + +accept -39.7345386469 -62.7259798066 +expect -2564843.692 -8361549.352 + +accept -29.4900121781 -62.6544841838 +expect -1922148.138 -8030239.835 + +accept -19.6711096687 -62.5940745250 +expect -1289744.456 -7810760.172 + +accept -9.2778801891 -62.3661044165 +expect -613541.964 -7649920.107 + +accept 0.6845208231 -61.4547664483 +expect 46410.846 -7484124.735 + +accept 10.3009385171 -60.8552091241 +expect 708981.245 -7444183.153 + +accept 20.9408599577 -60.7500016629 +expect 1443253.248 -7573067.407 + +accept 30.2033715333 -60.0297971335 +expect 2116701.190 -7683450.314 + +accept 40.1756048990 -59.0918222414 +expect 2877171.027 -7881749.372 + +accept 50.6849086093 -58.5125230628 +expect 3670248.004 -8282000.517 + +accept 60.3342813246 -57.7415828952 +expect 4433950.014 -8770184.848 + +accept 70.3874078634 -56.9198966570 +expect 5230934.598 -9486837.736 + +accept 80.0904535261 -56.6354279372 +expect 5822685.735 -10504356.087 + +accept 90.6634211024 -56.5251359813 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.1452342443 -55.8401169432 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.6864799856 -55.5116401637 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.3347823427 -55.2601958124 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.5134926951 -55.0238926067 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.3265242174 -54.4313834318 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.3685976599 -54.1552793246 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.9436711470 -53.1842316256 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.4034003030 -52.8621841373 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.0812683568 -52.6818511960 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.2224188803 -59.7295306861 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.4132895133 -59.2803345438 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.2336212919 -58.3674875157 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.6086565801 -57.6885943847 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.4537382278 -57.2767531329 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.8015642819 -57.0799116685 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.7728590554 -56.2047588377 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.7735055519 -55.4019628150 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.4371631073 -55.2867551270 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.8854290226 -55.2220077227 +expect -6418775.726 -11794916.199 + +accept -79.1497539952 -54.3143207583 +expect -6295512.226 -10173525.949 + +accept -69.3905433066 -53.3718201615 +expect -5756144.839 -8936634.008 + +accept -59.2835867353 -53.2932693342 +expect -4907385.858 -8087491.004 + +accept -49.4130759901 -52.3805314789 +expect -4141087.186 -7360481.030 + +accept -39.8824125488 -52.1928991404 +expect -3328366.078 -6910061.242 + +accept -29.4030406405 -51.2671252739 +expect -2475693.404 -6448925.355 + +accept -19.4382561500 -50.9645531478 +expect -1635897.800 -6198331.601 + +accept -9.1883916217 -50.6933815720 +expect -773969.285 -6040410.862 + +accept 0.9421744209 -50.4553454248 +expect 79572.416 -5974811.404 + +accept 10.2598469192 -50.3729595701 +expect 868653.957 -6006249.038 + +accept 20.5264309044 -49.7736213847 +expect 1760454.588 -6056357.985 + +accept 30.1563304273 -49.0546876336 +expect 2631434.958 -6165824.779 + +accept 40.6791957468 -48.4680700697 +expect 3620466.862 -6416267.044 + +accept 50.4781583451 -48.0216578802 +expect 4589763.311 -6780810.836 + +accept 60.0996876001 -47.2258755796 +expect 5649013.583 -7224720.556 + +accept 70.5609958631 -47.1401905695 +expect 6849278.900 -8064195.707 + +accept 80.4369996020 -46.9794332723 +expect 8182405.160 -9269149.720 + +accept 90.9151039880 -46.9029866463 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.4373534616 -46.3943619602 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.2842129880 -46.0308507793 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.6486866778 -45.8054277747 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.9364857762 -44.8554259969 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.6165699073 -43.9676693909 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.9528142413 -43.4405627423 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.5641245537 -42.8137396224 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.0938980656 -42.4864250646 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.3769971687 -41.5989802375 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.6808058361 -49.7903016746 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.7237623059 -49.1268921477 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.4102418582 -48.8849591656 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.2254953136 -48.1844242863 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.6041758915 -48.1179744801 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.3754143228 -47.1336832541 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.9746970079 -46.6736994965 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.7581919902 -46.0366124472 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.3823846098 -45.2381613350 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.0277852820 -44.5174266340 +expect -11094288.600 -10444365.724 + +accept -79.1875075496 -44.2312628392 +expect -8556831.915 -8448188.432 + +accept -69.3884076627 -43.7260948924 +expect -7143035.210 -7330162.787 + +accept -59.0994244861 -42.9591919136 +expect -5919312.756 -6474855.414 + +accept -49.2684633342 -42.4441379846 +expect -4837843.073 -5897052.170 + +accept -39.8960036873 -42.3148188142 +expect -3850941.078 -5525083.810 + +accept -29.6345258809 -41.8794443052 +expect -2831674.368 -5185997.953 + +accept -19.0064372737 -41.6217897025 +expect -1802198.153 -4961332.000 + +accept -9.6098419837 -41.1640449046 +expect -910539.796 -4807542.914 + +accept 0.3859851257 -40.6879294032 +expect 36674.106 -4715715.978 + +accept 10.6732390311 -40.2577682910 +expect 1020571.561 -4700734.253 + +accept 20.4749360982 -39.4261884171 +expect 1985510.706 -4700304.672 + +accept 30.7185540788 -39.1851869067 +expect 3019577.777 -4853020.985 + +accept 40.6403024621 -38.8583130891 +expect 4072800.273 -5067567.533 + +accept 50.3930386020 -38.6899841653 +expect 5171620.590 -5386102.158 + +accept 60.7920764042 -38.4864124906 +expect 6459723.244 -5832754.996 + +accept 70.9818120585 -38.2419029781 +expect 7920365.597 -6384561.316 + +accept 80.3791490145 -38.1923095026 +expect 9574134.022 -7007709.059 + +accept 90.5297378114 -37.5687159826 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.5373276614 -37.4573903187 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.0551604465 -36.8785291472 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.5920897044 -36.3504262236 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.1692367892 -36.3071095311 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.0034604349 -35.8053875550 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.9162515055 -35.0369229256 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.4006102901 -34.7824559736 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.8020614665 -34.6367632672 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.1238278697 -34.5735242626 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.3392011550 -39.0089519711 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.9896135260 -38.7758352491 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.9464206150 -38.3643075290 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.8580885141 -38.2565849818 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.0005407033 -37.8175552179 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.9744744916 -36.9749101428 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.4953757022 -36.8251466679 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.8732055905 -36.2644266473 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.2563126423 -35.6283177384 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.2720021301 -34.8337636531 +expect -11652944.582 -6432012.406 + +accept -79.9152588062 -34.3347175023 +expect -9740630.763 -6049660.979 + +accept -69.0059405699 -34.0306344677 +expect -7905352.539 -5468386.167 + +accept -59.9476909228 -33.2328890516 +expect -6646979.903 -4929914.448 + +accept -49.4912957918 -32.6555467192 +expect -5330421.040 -4465548.182 + +accept -39.4661117786 -32.0831702107 +expect -4167577.289 -4109295.788 + +accept -29.8207115548 -31.3399185713 +expect -3111531.284 -3817143.482 + +accept -19.8496271346 -31.2738366265 +expect -2047137.004 -3670990.375 + +accept -9.6527246528 -30.5326610745 +expect -992826.938 -3500250.586 + +accept 0.5995552142 -29.7920841190 +expect 61796.178 -3387838.259 + +accept 10.8411608718 -29.0671095836 +expect 1125114.712 -3331112.646 + +accept 20.3367215658 -28.5199148717 +expect 2132236.809 -3339194.703 + +accept 30.2505986215 -28.1864696949 +expect 3217328.415 -3423954.421 + +accept 40.5431469837 -27.9403197244 +expect 4399349.482 -3578155.060 + +accept 50.7509056230 -27.8467488939 +expect 5649446.347 -3807241.841 + +accept 60.6370271381 -27.3001909011 +expect 6983544.451 -4006532.475 + +accept 70.8220482203 -26.5371650435 +expect 8516309.152 -4185254.482 + +accept 80.2959404087 -25.8737294352 +expect 10093881.725 -4297066.952 + +accept 90.1903848581 -25.5035070096 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.5725497895 -24.6749860350 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.0757522922 -23.8621044004 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.4910038636 -23.3154304481 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.4609905705 -22.3944602016 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.6308884892 -22.1941484220 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.0358167607 -21.7789499055 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.5878069076 -21.7481812197 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.4050593367 -20.8432353205 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.6001699300 -20.8118177919 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.3361000782 -29.8463222037 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8999068245 -28.9429338477 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.7592377898 -28.1615925042 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.9913638701 -28.0372332017 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.2301202473 -27.0565876723 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.1268966632 -26.9865340393 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.2460222852 -26.3197565889 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.2062914741 -26.1682287226 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.9045914125 -25.7799438486 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.4037956256 -25.6991927518 +expect -11705355.483 -4359448.179 + +accept -79.8043265456 -25.1818797146 +expect -10024094.636 -4156724.889 + +accept -69.7140423873 -24.8449696747 +expect -8395540.010 -3863379.945 + +accept -59.0016643714 -23.9868520946 +expect -6856108.329 -3450250.183 + +accept -49.1644096637 -23.9073140300 +expect -5551692.748 -3211628.519 + +accept -39.4087456167 -23.3838764285 +expect -4359255.995 -2956380.419 + +accept -29.5780322984 -22.5310742645 +expect -3226303.975 -2709281.660 + +accept -19.4970507012 -21.7271569228 +expect -2106709.007 -2516125.488 + +accept -9.2309552615 -21.1686043260 +expect -992105.870 -2396128.627 + +accept 0.8151388636 -20.3765117338 +expect 87667.538 -2289827.020 + +accept 10.7150561883 -20.1052727537 +expect 1156777.413 -2278355.080 + +accept 20.3899871763 -20.0634670217 +expect 2218350.011 -2325602.798 + +accept 30.0598555833 -19.6984569649 +expect 3315266.258 -2366374.830 + +accept 40.9334924529 -18.8831525011 +expect 4616943.129 -2395016.592 + +accept 50.3705152580 -17.9548619999 +expect 5824857.949 -2409796.249 + +accept 60.7629076148 -17.7461797647 +expect 7240990.735 -2550212.531 + +accept 70.5049492221 -17.5449804169 +expect 8675560.230 -2678367.876 + +accept 80.3956836685 -17.4271102390 +expect 10234009.562 -2784279.451 + +accept 90.6808556357 -16.9481935094 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.3669156655 -16.2433923188 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.3790328662 -15.6932515137 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.0882522480 -15.6441029970 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.9326190128 -15.1072978015 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.4297399110 -14.7647959439 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.6670621082 -14.1800360544 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.3083279810 -13.5438931052 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.8801051896 -12.6909069291 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.9127621133 -11.7358777998 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.9634704329 -19.2242789077 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.1616383718 -18.5021923570 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.5989242161 -17.7569846767 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.7658699571 -17.6838689314 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.2283007229 -17.1203530837 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.9856069274 -16.5121558117 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.7066160149 -15.8271303941 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.0808307002 -15.7236132624 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.7051569128 -15.2351555878 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.3984343202 -14.2830109769 +expect -11714485.527 -2294583.435 + +accept -79.3161212354 -13.6551887362 +expect -10093909.091 -2148649.820 + +accept -69.3369852179 -13.5338940098 +expect -8554065.078 -2033627.111 + +accept -59.8017669014 -12.9486645585 +expect -7176185.386 -1835041.574 + +accept -49.8326118713 -12.2344952465 +expect -5830239.587 -1625964.636 + +accept -39.8183008451 -11.3212564171 +expect -4563020.124 -1417370.862 + +accept -29.4511324259 -11.1046029410 +expect -3315770.257 -1321269.119 + +accept -19.3421368019 -10.5209818176 +expect -2152759.066 -1206758.050 + +accept -9.7794195765 -10.4434857815 +expect -1080920.071 -1172971.439 + +accept 0.8324676722 -10.2027272199 +expect 91827.144 -1137551.112 + +accept 10.3172409138 -9.5930872101 +expect 1142198.103 -1077875.142 + +accept 20.7373198297 -8.6767624883 +expect 2317698.619 -998481.282 + +accept 30.8106039881 -8.1660651538 +expect 3491268.014 -975920.697 + +accept 40.2140519607 -7.4699626803 +expect 4637259.093 -935187.118 + +accept 50.8253614228 -6.9240926089 +expect 6003942.796 -922291.065 + +accept 60.3499171589 -6.7875741453 +expect 7307879.342 -959056.886 + +accept 70.7538618658 -6.4717900189 +expect 8823316.944 -969586.196 + +accept 80.1896127919 -6.1835665729 +expect 10268415.013 -962009.778 + +accept 90.2901075635 -5.6342271039 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.1017071129 -4.7732910406 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.6453720127 -4.6247740358 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.7065181706 -4.2131506322 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.0549243427 -3.6680122287 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.9588650575 -3.4693258990 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.3128054514 -2.8622073994 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.6250791828 -2.1132082532 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.1249865639 -1.7779699685 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.4636820369 -1.0447468723 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.0903238876 -9.3809572676 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8021514554 -8.8200596604 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.6897632216 -8.8151211116 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3642571872 -7.9578518334 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.1869613409 -7.1376473645 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.8103143949 -6.6533639455 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.0042762894 -6.5424897944 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.3222469320 -6.2626405858 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.2447484897 -5.4322064514 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.6583934020 -4.8520491055 +expect -11758391.270 -764820.107 + +accept -79.0822097483 -4.4980133305 +expect -10100660.047 -696260.990 + +accept -69.2589747329 -4.0562562622 +expect -8610130.179 -601975.105 + +accept -59.2607881882 -3.2433764780 +expect -7170184.717 -454392.257 + +accept -49.3268655448 -2.9707739252 +expect -5821746.865 -391509.219 + +accept -39.9159421567 -2.4973268139 +expect -4617029.524 -311670.569 + +accept -29.1976699326 -1.6650273067 +expect -3316547.033 -197263.637 + +accept -19.0027434133 -1.1195085022 +expect -2132221.527 -127924.555 + +accept -9.1234117067 -0.6240596549 +expect -1016591.323 -69833.247 + +accept 0.4677460038 0.2151624258 +expect 52011.067 23925.387 + +accept 10.7004848538 0.3165167866 +expect 1193291.206 35502.455 + +accept 20.1052099885 0.6126391439 +expect 2258534.300 70229.157 + +accept 30.3541137581 0.7990141229 +expect 3454411.853 95128.412 + +accept 40.7710878998 0.9567675855 +expect 4725772.464 119944.290 + +accept 50.7725952624 1.4294037508 +expect 6015746.643 189992.540 + +accept 60.5997615005 1.6613265138 +expect 7361750.248 234549.877 + +accept 70.4466727438 1.9824185118 +expect 8791561.281 295710.236 + +accept 80.4040420231 2.1516254056 +expect 10309233.052 333889.458 + +accept 90.0745563464 2.5008083079 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.1323994743 3.4798778581 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.7633392793 3.6524418056 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.4935453458 3.8138602324 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.1832453461 4.1827817395 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.6879708211 4.3228601350 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.2872780471 5.0096390922 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.4567844437 5.3289862640 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.1798195714 5.7690110660 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.6237066153 6.6873727446 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.1879408995 0.2925675717 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8193242429 0.5456299185 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.6143094365 0.8491614798 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.1327424918 1.5360082778 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.9118046279 2.2304783506 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.0707848713 3.1774300866 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.0155367869 3.7108516861 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.7138958240 4.2367325538 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.7626123411 4.9489248449 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.0617378460 5.0261998535 +expect -11664192.229 792317.110 + +accept -79.6065498494 5.4925196315 +expect -10179589.971 852372.934 + +accept -69.7109936494 5.4974120433 +expect -8672018.782 818565.715 + +accept -59.3987361622 6.4168117979 +expect -7176296.816 901167.992 + +accept -49.6550438823 6.6122077057 +expect -5850898.508 874299.311 + +accept -39.5175223106 6.6442757043 +expect -4554622.398 828384.475 + +accept -29.9263368298 7.1910087068 +expect -3390376.767 855728.094 + +accept -19.6951353173 7.2342593616 +expect -2202778.650 829386.822 + +accept -9.3597241491 7.7249502728 +expect -1038303.808 866022.563 + +accept 0.9926766371 7.7430181007 +expect 109876.645 862359.585 + +accept 10.4808252097 7.8032784791 +expect 1163217.791 876306.606 + +accept 20.2149078300 8.7141462778 +expect 2257974.529 1001202.962 + +accept 30.6115985045 8.7806411209 +expect 3464879.027 1048739.004 + +accept 40.9406024668 9.0709137304 +expect 4718612.849 1141091.327 + +accept 50.2246468449 9.2715613620 +expect 5908642.815 1232215.612 + +accept 60.6212081762 10.1366134196 +expect 7321174.335 1439008.711 + +accept 70.0896353531 11.0850079977 +expect 8692101.993 1665083.891 + +accept 80.6785108917 11.6138802990 +expect 10322728.590 1826238.813 + +accept 90.9684446145 11.7088334392 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.8216260873 12.6522648050 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.9341322494 12.8091489452 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.9598573820 12.9840580805 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.1838852888 13.9674113487 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.0163435591 14.2583869011 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.3440941987 15.1977215509 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.6373902313 15.9779535702 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.7489315029 16.0224553269 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.4315909707 16.2341190017 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.9613846400 10.9181102314 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.2464750707 11.2011582425 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.3961353214 12.0814824633 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.9176675016 12.1332225594 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.0300120770 12.7448426145 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.1479377942 13.2115900349 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.6509465671 13.5121287506 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.3485953585 13.9838806488 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.8025484668 14.3000196702 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.6183447418 14.3419323455 +expect -11750225.007 2304558.947 + +accept -79.3692412058 14.8824779140 +expect -10092517.831 2350135.174 + +accept -69.8245239316 15.4247668791 +expect -8603644.646 2333402.982 + +accept -59.9411333586 16.1857250660 +expect -7151141.941 2307064.350 + +accept -49.5439751158 16.6663945754 +expect -5737011.937 2221425.397 + +accept -39.8565220229 16.7491842544 +expect -4513398.389 2106838.116 + +accept -29.0387587694 16.7636024414 +expect -3226356.467 1999372.842 + +accept -19.3072208645 17.5503784835 +expect -2114944.499 2022939.979 + +accept -9.7495596439 18.3236674818 +expect -1058075.992 2069782.832 + +accept 0.3372939259 19.1939757716 +expect 36418.336 2154300.403 + +accept 10.4541089513 19.4557925775 +expect 1130896.269 2202407.957 + +accept 20.3450540441 19.6776140317 +expect 2216235.240 2279684.922 + +accept 30.5262355523 20.1057317982 +expect 3364502.237 2421313.756 + +accept 40.6436957307 20.6888876489 +expect 4554114.195 2625171.436 + +accept 50.8390922621 21.5842676384 +expect 5818691.558 2921121.736 + +accept 60.4025168492 21.7910320981 +expect 7107138.574 3149887.076 + +accept 70.7863531388 22.4913165287 +expect 8622256.722 3492000.228 + +accept 80.2433369822 23.0164275848 +expect 10137749.549 3763259.969 + +accept 90.9377687408 23.1511733038 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.5407405945 23.7761284488 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.0059145808 24.0087396721 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.0839708022 24.8505061022 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.5684827033 25.5457759639 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.2735017629 25.7841586919 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.4960718541 26.4511385776 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.5086557650 26.5248940712 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.1010951379 26.6437577891 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.4667032708 27.5171288037 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.2332673731 20.5208357556 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.9852102849 20.6884242583 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.0918834805 20.7807287727 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3554284700 21.5195488802 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.0517203599 22.3228377599 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.7889045882 22.9579802676 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.2864956877 23.1541372478 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.1249371549 23.9554652672 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.7886236408 24.2156608317 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.8158531726 24.4254617574 +expect -11779801.464 4109615.321 + +accept -79.1765040759 24.4730100405 +expect -9932163.575 4013012.232 + +accept -69.4905958240 25.3275320515 +expect -8347069.306 3939510.329 + +accept -59.3321746682 25.8062815391 +expect -6849836.851 3736718.987 + +accept -49.4529549960 26.7200008826 +expect -5517196.147 3613456.872 + +accept -39.1868721751 27.3024639669 +expect -4254482.013 3466079.063 + +accept -29.6354019373 27.4373224654 +expect -3161470.540 3320471.268 + +accept -19.1695661946 28.3567542308 +expect -2009357.470 3307900.622 + +accept -9.9833263319 28.6603499308 +expect -1037944.407 3278172.915 + +accept 0.5787958629 28.7930766257 +expect 59988.867 3269432.632 + +accept 10.1908900667 29.4473999043 +expect 1055041.336 3373090.240 + +accept 20.4711672585 29.9558795250 +expect 2129353.829 3515969.801 + +accept 30.8531334707 30.4060992834 +expect 3242994.151 3715442.228 + +accept 40.4755923878 31.2284502498 +expect 4307211.258 4017976.832 + +accept 50.0364462297 31.8692054690 +expect 5426487.569 4368373.509 + +accept 60.5819735012 32.1294434320 +expect 6785271.674 4776262.501 + +accept 70.8279928055 32.4558417273 +expect 8275156.713 5258027.307 + +accept 80.8754092571 33.4499285913 +expect 9965465.329 5886025.407 + +accept 90.9417476435 34.2241428842 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.8763555867 34.9730810876 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.3824336404 35.8924849218 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.3907138338 36.0772654252 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.2213236652 36.3411661168 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.6643127929 37.0777875387 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.5696707234 38.0028546554 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.7330142926 38.8399381962 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.7575452122 39.6058463190 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.9074447347 39.7506383769 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.8967233384 30.5573004598 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.4186924041 30.7925389719 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.3550554816 31.7173298465 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3340973772 32.5661389704 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.8793305042 33.1451975000 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.0718020283 34.1077930148 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.3765659597 34.6652275838 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.7130644703 34.6829381126 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.5574458139 35.6024191831 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.7509673047 36.5904991465 +expect -11753853.356 6923417.921 + +accept -79.5569260283 36.6832637281 +expect -9529017.479 6583316.301 + +accept -69.4216766587 36.7592109391 +expect -7793705.647 6002236.199 + +accept -59.8993383836 36.9575776083 +expect -6439637.617 5535272.788 + +accept -49.2671447978 37.9494837339 +expect -5077618.232 5232434.984 + +accept -39.6598693615 38.9483055378 +expect -3963735.765 5050631.148 + +accept -29.7093809090 39.1622288957 +expect -2917074.123 4828275.833 + +accept -19.9357360497 39.2854809247 +expect -1934827.091 4674852.040 + +accept -9.5411208555 39.4087263276 +expect -919053.532 4586404.063 + +accept 0.8211876318 39.4440823975 +expect 78929.176 4560557.981 + +accept 10.4970331716 40.3224517995 +expect 1003036.028 4707596.187 + +accept 20.6917357286 40.5466917960 +expect 1985755.247 4847003.414 + +accept 30.0757101328 40.6533776922 +expect 2912090.233 5033105.330 + +accept 40.4799159627 40.9888819952 +expect 3968966.207 5358999.089 + +accept 50.8203716780 41.3416456225 +expect 5074769.526 5800574.001 + +accept 60.6789776888 42.2933132797 +expect 6163689.909 6459786.753 + +accept 70.4598227375 42.9662236851 +expect 7378970.615 7275378.446 + +accept 80.5212319721 43.4298814980 +expect 8945156.756 8415826.102 + +accept 90.6029060873 44.3367368198 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.2968163089 44.7347899226 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.5453757907 45.2325884903 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.7132187645 46.1810408682 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.6036848279 46.5953112427 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.8567165951 47.1100600825 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.2452206431 47.5590007593 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.4264735249 47.6243197261 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.1158223427 47.9911994181 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.2100320112 48.9423016681 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.5621188253 40.3822966969 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.0785389235 40.7397870392 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.9453288766 41.0637989828 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.0072837113 41.2595851413 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.3677527689 41.6743754043 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.1116359654 41.9054883189 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.2367048985 42.7084205060 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.9662473469 42.7707453425 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.5437449083 43.0239640060 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.4757325990 43.2756199739 +expect -11568961.170 9589089.555 + +accept -79.0930156514 43.3605739941 +expect -8689821.361 8221563.160 + +accept -69.6623710654 43.7575713592 +expect -7176557.127 7359145.048 + +accept -59.8551383188 44.0946182610 +expect -5913531.749 6705033.126 + +accept -49.6085838009 44.4913127126 +expect -4748242.269 6215794.411 + +accept -39.1320462959 45.2007809057 +expect -3641766.743 5902607.826 + +accept -29.9547583046 46.1598354389 +expect -2721962.231 5767450.222 + +accept -19.6901625335 47.0694221597 +expect -1753442.745 5682225.919 + +accept -9.5530275863 47.7859862485 +expect -838921.885 5658994.019 + +accept 0.0222239630 47.9106126700 +expect 1945.836 5639486.547 + +accept 10.3786030568 48.2669975286 +expect 905735.036 5728642.671 + +accept 20.3163155558 49.0365488340 +expect 1761298.591 5954059.585 + +accept 30.0523609099 49.2604161517 +expect 2613977.300 6191207.840 + +accept 40.2733019916 49.6471765112 +expect 3515610.607 6567126.763 + +accept 50.6235974778 49.9195971337 +expect 4454811.565 7067214.001 + +accept 60.0640817595 50.7570760574 +expect 5269948.907 7769377.867 + +accept 70.2894928952 51.6926294743 +expect 6110607.979 8783958.836 + +accept 80.7661690288 51.9879378395 +expect 6957594.499 10145750.592 + +accept 90.2760527960 52.6279152662 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.4470132582 53.2519801847 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.1282829759 53.4607562483 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.5033443580 53.9989335755 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.6561885293 54.6823982531 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.8127091472 54.7255186711 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.0054753699 55.4459165788 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.8312787949 56.2470115579 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.4614439985 57.0241073917 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.7047485406 57.6022388310 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.1586219943 50.1365490236 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.1490260788 50.5023613434 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.9535088204 50.9035428509 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.0803324654 51.7753015653 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.7669273536 52.0799612604 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.3599142098 52.6974765371 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.6940989810 52.7976100393 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.7805542206 53.6394720461 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.9951721199 54.3490528856 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.4965032257 55.2735286995 +expect -6404341.484 11736182.738 + +accept -79.2351509797 56.0182014529 +expect -5919230.308 10347709.180 + +accept -69.2279142296 57.0024522025 +expect -5142475.359 9389304.843 + +accept -59.7270769476 57.4201565369 +expect -4433291.933 8686026.154 + +accept -49.4089463064 57.7914752628 +expect -3650409.699 8117631.819 + +accept -39.6433533072 58.1804486015 +expect -2906321.369 7735544.810 + +accept -29.8165100218 58.5388210940 +expect -2168952.954 7465348.952 + +accept -19.0157349252 58.9403079367 +expect -1370812.070 7285860.519 + +accept -9.1047501907 59.4124031484 +expect -649264.745 7231297.403 + +accept 0.8510548344 60.1631637832 +expect 59614.314 7301680.904 + +accept 10.7118819188 60.6049338062 +expect 741931.784 7412559.974 + +accept 20.4398525342 61.3600300433 +expect 1386377.568 7649843.890 + +accept 30.3017880976 62.2930069600 +expect 1995284.860 8001431.858 + +accept 40.2742910491 62.4260882546 +expect 2622736.428 8340981.454 + +accept 50.1679836632 63.3416128275 +expect 3130090.680 8886842.807 + +accept 60.1315518295 64.0854342654 +expect 3566294.596 9516574.314 + +accept 70.2671672611 64.9885817623 +expect 3854824.970 10266355.614 + +accept 80.0566574740 65.2466871256 +expect 4076741.126 11011949.460 + +accept 90.0598623933 65.7380193724 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.2499385302 65.8274446207 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.0091445253 66.5841928232 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.1938557094 67.3045114589 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.1756983494 67.6825233842 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.0774508004 68.5260112188 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.3642244888 69.0087889372 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.0390197585 69.5064813197 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.9882511230 69.9663419458 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.7855662108 70.7558001565 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.8630282894 60.2684905661 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.8774561096 60.7063957678 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.1639611249 61.1269337981 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.0814548820 61.9063878006 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.0476545160 62.7911475081 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.8276916999 62.8515546512 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.9464541970 63.7277849393 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.6864148265 63.9317823343 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.4184495170 64.2249537529 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.6146334566 64.3967878406 +expect -4340586.273 11779220.401 + +accept -79.8288741622 64.8985190713 +expect -4137365.737 10977840.319 + +accept -69.3484116496 65.6222831765 +expect -3718954.598 10255930.505 + +accept -59.0699348251 66.3652733965 +expect -3206977.816 9703730.126 + +accept -49.7020758778 66.6389497206 +expect -2744067.651 9275193.520 + +accept -39.0292679218 66.7793834162 +expect -2190174.896 8881394.899 + +accept -29.9077443640 67.3270538543 +expect -1664513.475 8692728.991 + +accept -19.0042693882 67.9048158312 +expect -1045199.569 8559031.056 + +accept -9.5338704651 68.3771424633 +expect -517496.904 8521368.334 + +accept 0.2238591227 69.2477228904 +expect 11757.034 8614778.137 + +accept 10.2978890619 69.7426835928 +expect 528603.064 8728166.019 + +accept 20.2917095900 70.1793788385 +expect 1014867.798 8907115.689 + +accept 30.9719421826 70.5868181503 +expect 1499411.306 9170515.903 + +accept 40.5524514587 71.5715697583 +expect 1833434.912 9555789.645 + +accept 50.3484489213 71.8505547121 +expect 2175943.593 9913399.521 + +accept 60.1086300862 72.0684099625 +expect 2460956.915 10322590.056 + +accept 70.6228216997 73.0662960367 +expect 2561247.727 10868246.628 + +accept 80.5645741906 73.7542661119 +expect 2585249.402 11363593.076 + +accept 90.6177784757 74.6865930919 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.2695381559 74.9397012116 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.3724185381 75.3340827987 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.0440856622 75.6509550283 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.4765915697 75.8794035471 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.8273607225 76.0863350217 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.7446122566 76.4130503173 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.9066279973 77.2046671411 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.7039755420 77.5254695021 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.7301487821 78.3643690894 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.8424303971 70.7640164273 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.3071682617 71.3534426809 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.9256766432 72.0788134529 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.4389257633 72.9048071827 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.4466193836 73.7144086150 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.4240513471 74.3140345936 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.2324314226 75.1789245938 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.4080456874 75.9740320316 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.9624349535 76.7254677659 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.8454838458 77.0914030368 +expect -2065538.883 11806583.302 + +accept -79.3200173559 77.5923156814 +expect -1944930.535 11436411.248 + +accept -69.2214782512 77.6455638940 +expect -1833901.665 11099482.695 + +accept -59.3345608489 77.6726989114 +expect -1672461.604 10796813.087 + +accept -49.4571426800 77.9149250146 +expect -1437767.183 10554465.582 + +accept -39.0382734207 78.6675565544 +expect -1110318.758 10416013.135 + +accept -29.1097584433 78.7699850319 +expect -845013.962 10265588.005 + +accept -19.0735922396 78.8979558943 +expect -558786.152 10166121.532 + +accept -9.0280312502 79.8097469605 +expect -246483.187 10236709.513 + +accept 0.5580111982 79.8802703269 +expect 15185.199 10229124.254 + +accept 10.8112184284 80.0989872186 +expect 286673.574 10288907.138 + +accept 20.1411665329 80.3961610028 +expect 512145.694 10396401.075 + +accept 30.8632607000 81.0343489380 +expect 716121.219 10599328.302 + +accept 40.5566028935 81.1124451300 +expect 903295.715 10744008.638 + +accept 50.8831926047 81.3748819436 +expect 1050654.228 10948141.605 + +accept 60.0527293155 81.6666268419 +expect 1137426.454 11149953.262 + +accept 70.4854280716 82.6250504715 +expect 1097137.468 11420195.697 + +accept 80.6482540792 83.6156947131 +expect 994412.198 11647508.801 + +accept 90.5789951029 84.1124942551 +expect failure errno coord_transfm_outside_projection_domain + +accept 100.9443622402 84.2131541284 +expect failure errno coord_transfm_outside_projection_domain + +accept 110.5781783058 84.7127066970 +expect failure errno coord_transfm_outside_projection_domain + +accept 120.6025664533 85.5661224284 +expect failure errno coord_transfm_outside_projection_domain + +accept 130.0976840056 86.3318417998 +expect failure errno coord_transfm_outside_projection_domain + +accept 140.6428111911 86.3329325553 +expect failure errno coord_transfm_outside_projection_domain + +accept 150.5251209004 86.5841388479 +expect failure errno coord_transfm_outside_projection_domain + +accept 160.4225603060 86.8828039057 +expect failure errno coord_transfm_outside_projection_domain + +accept 170.2581319411 87.4171568183 +expect failure errno coord_transfm_outside_projection_domain + +accept 180.2641439484 88.1608036446 +expect failure errno coord_transfm_outside_projection_domain + +accept -179.8401984185 80.4164439367 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.6918872432 81.0641431423 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.9211815920 81.7949103274 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.6075138921 82.1028453149 +expect failure errno coord_transfm_outside_projection_domain + +accept -139.7372920424 82.6385276754 +expect failure errno coord_transfm_outside_projection_domain + +accept -129.7998353504 83.1090136759 +expect failure errno coord_transfm_outside_projection_domain + +accept -119.2572393635 83.4744235117 +expect failure errno coord_transfm_outside_projection_domain + +accept -109.3246360912 84.2608342272 +expect failure errno coord_transfm_outside_projection_domain + +accept -99.9601976700 84.4677069243 +expect failure errno coord_transfm_outside_projection_domain + +accept -89.7505869053 85.0897168462 +expect -774050.983 11808922.435 + +accept -79.8910338348 85.9672408001 +expect -625258.031 11700550.054 + +accept -69.1554779909 86.8865771916 +expect -457830.975 11637725.026 + +accept -59.6158025559 87.0075919956 +expect -406020.652 11573918.187 + +accept -49.5091296711 87.2889393354 +expect -324158.396 11535225.643 + +accept -39.6799799687 87.3973978464 +expect -261177.241 11497165.712 + +accept -29.0273299961 87.9089486985 +expect -159462.940 11524757.163 + +accept -19.1380418786 88.8203526189 +expect -60802.011 11637057.505 + +accept -9.0084093906 88.8893310433 +expect -27340.998 11639811.223 + +accept 0.0213854656 89.1326873925 +expect 50.899 11675921.557 + +accept 10.6007672189 89.4603893825 +expect 15609.535 11728897.894 + +accept 20.8972133561 90.3008662748 +expect failure errno coord_transfm_invalid_coord + +accept 30.3236847010 90.9360697392 +expect failure errno coord_transfm_invalid_coord + +accept 40.5211596030 91.7425546179 +expect failure errno coord_transfm_invalid_coord + +accept 50.9530657025 92.7167872262 +expect failure errno coord_transfm_invalid_coord + +accept 60.2966089164 93.5518747322 +expect failure errno coord_transfm_invalid_coord + +accept 70.7058277145 93.9478756274 +expect failure errno coord_transfm_invalid_coord + +accept 80.2842419408 94.5029189079 +expect failure errno coord_transfm_invalid_coord + +accept 90.7046514943 94.5870657217 +expect failure errno coord_transfm_invalid_coord + +accept 100.5757828394 94.7544968060 +expect failure errno coord_transfm_invalid_coord + +accept 110.0784957493 95.6326996288 +expect failure errno coord_transfm_invalid_coord + +accept 120.4420696450 95.7426558832 +expect failure errno coord_transfm_invalid_coord + +accept 130.0586467430 96.7026620164 +expect failure errno coord_transfm_invalid_coord + +accept 140.4982918026 97.2453640881 +expect failure errno coord_transfm_invalid_coord + +accept 150.5137836610 97.2825819689 +expect failure errno coord_transfm_invalid_coord + +accept 160.8117951323 97.8473653755 +expect failure errno coord_transfm_invalid_coord + +accept 170.3402740510 98.3587698302 +expect failure errno coord_transfm_invalid_coord + +accept 180.9378053935 98.8743871260 +expect failure errno coord_transfm_invalid_coord + +accept -179.1153600127 89.6136396944 +expect failure errno coord_transfm_outside_projection_domain + +accept -169.2261536485 89.9072554901 +expect failure errno coord_transfm_outside_projection_domain + +accept -159.1164133392 89.9075872145 +expect failure errno coord_transfm_outside_projection_domain + +accept -149.3960866375 90.6652890542 +expect failure errno coord_transfm_invalid_coord + +accept -139.1720109722 91.2726639403 +expect failure errno coord_transfm_invalid_coord + +accept -129.0729383513 91.6124307897 +expect failure errno coord_transfm_invalid_coord + +accept -119.9896731679 92.3534538297 +expect failure errno coord_transfm_invalid_coord + +accept -109.8793121665 92.7446796075 +expect failure errno coord_transfm_invalid_coord + +accept -99.4084806305 92.8846710223 +expect failure errno coord_transfm_invalid_coord + +accept -89.0674703286 93.0281709121 +expect failure errno coord_transfm_invalid_coord + +accept -79.1149414160 93.4632295476 +expect failure errno coord_transfm_invalid_coord + +accept -69.5261517902 94.2811379653 +expect failure errno coord_transfm_invalid_coord + +accept -59.0417407292 95.2403005497 +expect failure errno coord_transfm_invalid_coord + +accept -49.3488539326 95.3977809221 +expect failure errno coord_transfm_invalid_coord + +accept -39.5548627592 96.1026759897 +expect failure errno coord_transfm_invalid_coord + +accept -29.0476034815 96.4820442045 +expect failure errno coord_transfm_invalid_coord + +accept -19.8278954645 96.7461121139 +expect failure errno coord_transfm_invalid_coord + +accept -9.5488366916 97.4860281314 +expect failure errno coord_transfm_invalid_coord + +accept 0.2307496667 97.5054708483 +expect failure errno coord_transfm_invalid_coord + +accept 10.6032378382 97.7247157965 +expect failure errno coord_transfm_invalid_coord + +accept 20.8959156966 98.0573474237 +expect failure errno coord_transfm_invalid_coord + +accept 30.7104889319 98.5114024172 +expect failure errno coord_transfm_invalid_coord + +accept 40.1762654017 98.8114138429 +expect failure errno coord_transfm_invalid_coord + +accept 50.8420835837 99.5737438963 +expect failure errno coord_transfm_invalid_coord + +accept 60.4391516649 99.6023781174 +expect failure errno coord_transfm_invalid_coord + +accept 70.3833096116 99.8129052911 +expect failure errno coord_transfm_invalid_coord + +accept 80.6796681432 100.7784977140 +expect failure errno coord_transfm_invalid_coord + +accept 90.8893235311 100.9639616716 +expect failure errno coord_transfm_invalid_coord + +accept 100.8499388037 101.2529844461 +expect failure errno coord_transfm_invalid_coord + +accept 110.5488903609 101.9706798836 +expect failure errno coord_transfm_invalid_coord + +accept 120.1208651509 102.2817021553 +expect failure errno coord_transfm_invalid_coord + +accept 130.1003249085 102.6010355936 +expect failure errno coord_transfm_invalid_coord + +accept 140.8624898562 102.6311753718 +expect failure errno coord_transfm_invalid_coord + +accept 150.3852952852 103.0749738073 +expect failure errno coord_transfm_invalid_coord + +accept 160.7214099599 103.8391207142 +expect failure errno coord_transfm_invalid_coord + +accept 170.6439119665 104.6357199780 +expect failure errno coord_transfm_invalid_coord + +accept 180.2173889904 105.2754168153 +expect failure errno coord_transfm_invalid_coord +------------------------------------------------------------ + +------------------------------------------------------------ +operation +proj=guyou +R=1 +tolerance 1 mm +------------------------------------------------------------ + +accept 0 90 +expect 0 1.85407 + +accept 0 -90 +expect 0 -1.85407 +------------------------------------------------------------ + + diff --git a/test/ProjNet.Tests/Fixtures/gie/more_builtins.gie b/test/ProjNet.Tests/Fixtures/gie/more_builtins.gie new file mode 100644 index 00000000..69d01c18 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/more_builtins.gie @@ -0,0 +1,1045 @@ +=============================================================================== + +Various test material, mostly converted from selftest entries in PJ_xxx.c + +Contrary to the material in builtins.gie, this material is handwritten and +intends to exercise corner cases. + +=============================================================================== + + + + +------------------------------------------------------------------------------- +# Two ob_tran tests from data/testvarious +------------------------------------------------------------------------------- +operation +proj=ob_tran +o_proj=moll +R=6378137.0 +o_lon_p=0 +o_lat_p=0 +lon_0=180 +------------------------------------------------------------------------------- +tolerance 1 mm +direction inverse + +accept 300000 400000 +expect -42.7562158333 85.5911341667 + +direction forward + +accept 10 20 +expect -1384841.18787 7581707.88240 +------------------------------------------------------------------------------- + + + + + +------------------------------------------------------------------------------- +# Two tests from PJ_molodensky.c +------------------------------------------------------------------------------- +operation proj=molodensky a=6378160 rf=298.25 \ + da=-23 df=-8.120449e-8 dx=-134 dy=-48 dz=149 \ + abridged +------------------------------------------------------------------------------- +tolerance 2 m + +accept 144.9667 -37.8 50 0 +expect 144.968 -37.79848 46.378 0 + +roundtrip 100 1 m +------------------------------------------------------------------------------- +# Same thing once more, but this time unabridged +------------------------------------------------------------------------------- +operation proj=molodensky a=6378160 rf=298.25 \ + da=-23 df=-8.120449e-8 dx=-134 dy=-48 dz=149 +------------------------------------------------------------------------------- +tolerance 2 m + +accept 144.9667 -37.8 50 0 +expect 144.968 -37.79848 46.378 0 + +roundtrip 100 1 m +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +# Molodensky with all 0 parameters +------------------------------------------------------------------------------- +operation proj=molodensky a=6378160 rf=298.25 \ + da=0 df=0 dx=0 dy=0 dz=0 +------------------------------------------------------------------------------- +tolerance 1 mm + +accept 144.9667 -37.8 50 0 +expect 144.9667 -37.8 50 0 + +roundtrip 1 +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +# Test error cases of molodensky +------------------------------------------------------------------------------- +# No arguments +operation proj=molodensky a=6378160 rf=298.25 +expect failure errno invalid_op_missing_arg + +# Missing arguments +operation proj=molodensky a=6378160 rf=298.25 dx=0 +expect failure errno invalid_op_missing_arg + + +------------------------------------------------------------------------------- +# Tests for PJ_bertin1953.c +------------------------------------------------------------------------------- +operation proj=bertin1953 +R=1 +------------------------------------------------------------------------------- +accept 0 0 +expect -0.260206554508 -0.685226058142 + +accept 16.5 42 +expect 0.0 0.0 + +accept -180 90 +expect 0.0 0.813473286152 + +accept 0 90 +expect 0.0 0.813473286152 + +accept 10 -35 +expect -0.138495501548 -1.221408328101 + +accept -70 -35 +expect -1.504967424950 -0.522846035499 + +accept 80 7 +expect 0.929377425352 -0.215443296201 + +accept 128 35 +expect 0.920230566844 0.713170409026 + +accept 170 -41 +expect 2.162845830414 -0.046534568425 + +------------------------------------------------------------------------------- + + + + + +------------------------------------------------------------------------------- +# Some tests from PJ_pipeline.c +------------------------------------------------------------------------------- +# Forward-reverse geo->utm->geo (4D functions) +------------------------------------------------------------------------------- +operation proj=pipeline zone=32 step \ + proj=utm ellps=GRS80 step \ + proj=utm ellps=GRS80 inv +------------------------------------------------------------------------------- +tolerance 0.1 mm + +accept 12 55 0 0 +expect 12 55 0 0 + +# Now the inverse direction (still same result: the pipeline is symmetrical) + +direction inverse +expect 12 55 0 0 +------------------------------------------------------------------------------- +# And now the back-to-back situation utm->geo->utm (4D functions) +------------------------------------------------------------------------------- +operation proj=pipeline zone=32 ellps=GRS80 step \ + proj=utm inv step \ + proj=utm +------------------------------------------------------------------------------- +accept 691875.63214 6098907.82501 0 0 +expect 691875.63214 6098907.82501 0 0 +direction inverse +expect 691875.63214 6098907.82501 0 0 +------------------------------------------------------------------------------- +# Forward-reverse geo->utm->geo (3D functions) +------------------------------------------------------------------------------- +operation proj=pipeline zone=32 step \ + proj=utm ellps=GRS80 step \ + proj=utm ellps=GRS80 inv +------------------------------------------------------------------------------- +tolerance 0.1 mm + +accept 12 55 0 +expect 12 55 0 + +# Now the inverse direction (still same result: the pipeline is symmetrical) + +direction inverse +expect 12 55 0 +------------------------------------------------------------------------------- +# And now the back-to-back situation utm->geo->utm (3D functions) +------------------------------------------------------------------------------- +operation proj=pipeline zone=32 ellps=GRS80 step \ + proj=utm inv step \ + proj=utm +------------------------------------------------------------------------------- +accept 691875.63214 6098907.82501 0 +expect 691875.63214 6098907.82501 0 +direction inverse +expect 691875.63214 6098907.82501 0 +------------------------------------------------------------------------------- +# Test a corner case: A rather pointless one-step pipeline geo->utm +------------------------------------------------------------------------------- +operation proj=pipeline step proj=utm zone=32 ellps=GRS80 +------------------------------------------------------------------------------- +accept 12 55 0 0 +expect 691875.63214 6098907.82501 0 0 +direction inverse +accept 691875.63214 6098907.82501 0 0 +expect 12 55 0 0 +------------------------------------------------------------------------------- +# Finally test a pipeline with more than one init step +------------------------------------------------------------------------------- +use_proj4_init_rules true +operation proj=pipeline \ + step init=epsg:25832 inv \ + step init=epsg:25833 \ + step init=epsg:25833 inv \ + step init=epsg:25832 +------------------------------------------------------------------------------- +accept 691875.63214 6098907.82501 0 0 +expect 691875.63214 6098907.82501 0 0 +direction inverse +accept 12 55 0 0 +expect 12 55 0 0 +------------------------------------------------------------------------------- +# Test a few inversion scenarios (urm5 has no inverse operation) +------------------------------------------------------------------------------- +operation proj=pipeline step \ + proj=urm5 n=0.5 inv +expect failure pjd_err_malformed_pipeline + +operation proj=pipeline inv step \ + proj=urm5 n=0.5 +expect failure pjd_err_malformed_pipeline + +operation proj=pipeline inv step \ + proj=urm5 n=0.5 ellps=WGS84 inv +accept 12 56 +expect 1215663.2814182492 5452209.5424045017 + +operation proj=pipeline step \ + proj=urm5 ellps=WGS84 n=0.5 +accept 12 56 +expect 1215663.2814182492 5452209.5424045017 +------------------------------------------------------------------------------- +# Test various failing scenarios. +------------------------------------------------------------------------------- +operation proj=pipeline step \ + proj=pipeline step \ + proj=merc +expect failure pjd_err_malformed_pipeline + +operation step proj=pipeline step proj=merc +expect failure pjd_err_malformed_pipeline + +operation proj=pipeline +expect failure pjd_err_malformed_pipeline + + +------------------------------------------------------------------------------- +# Some tests from PJ_vgridshift.c +------------------------------------------------------------------------------- +operation proj=vgridshift grids=egm96_15.gtx ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 1 cm +accept 12.5 55.5 0 0 +expect 12.5 55.5 -36.394090697 0 + +accept -180.1 0 0 +expect -180.1 0 -20.835222268 + +accept 179.9 0 0 +expect 179.9 0 -20.835222268 + +accept 180 0 0 +expect 180 0 -20.756538510 + +accept 540 0 0 +expect 540 0 -20.756538510 + +accept -180 0 0 +expect -180 0 -20.756538510 + +accept -540 0 0 +expect -540 0 -20.756538510 + +roundtrip 100 1 nm +------------------------------------------------------------------------------- +# Fail on purpose: +grids parameter is mandatory +operation proj=vgridshift +expect failure errno invalid_op_missing_arg + +# Fail on purpose: open non-existing grid +operation proj=vgridshift grids=nonexistinggrid.gtx +expect failure errno invalid_op_file_not_found_or_invalid +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation proj=vgridshift grids=egm96_15.gtx ellps=GRS80 multiplier=0.1 +tolerance 15 cm +accept 12.5 55.5 0 0 +expect 12.5 55.5 3.6021305084228516 0 + +------------------------------------------------------------------------------- +# Some tests from PJ_hgridshift.c +------------------------------------------------------------------------------- +operation proj=hgridshift +grids=ntf_r93.gsb ellps=GRS80 +------------------------------------------------------------------------------- +tolerance 1 mm +accept 2.250704350387 46.500051597273 +expect 2.25 46.5 +direction inverse +accept 2.25 46.5 +expect 2.250704350387 46.500051597273 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +# Fail on purpose: open non-existing grid: +operation proj=hgridshift grids=@nonexistinggrid.gsb,anothernonexistinggrid.gsb +expect failure errno invalid_op_file_not_found_or_invalid + +# Fail on purpose: +grids parameter is mandatory: +operation proj=hgridshift +expect failure errno invalid_op_missing_arg +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +# Tests for LCC 2SP Michigan (from PJ_lcc.c) +------------------------------------------------------------------------------- +# This test is taken from EPSG guidance note 7-2 (version 54, August 2018, +# page 25) +------------------------------------------------------------------------------- +operation +proj=lcc +ellps=clrk66 +lat_1=44d11'N +lat_2=45d42'N +x_0=609601.2192 +lon_0=84d20'W +lat_0=43d19'N +k_0=1.0000382 +units=us-ft +------------------------------------------------------------------------------- +tolerance 5 mm +accept 83d10'W 43d45'N +expect 2308335.75 160210.48 + +direction inverse +accept 2308335.75 160210.48 +expect 83d10'W 43d45'N +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# A number of tests from PJ_helmert.c +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# This example is from Lotti Jivall: "Simplified transformations from +# ITRF2008/IGS08 to ETRS89 for maritime applications" +------------------------------------------------------------------------------- +operation proj=helmert convention=coordinate_frame \ + x=0.67678 y=0.65495 z=-0.52827 \ + rx=-0.022742 ry=0.012667 rz=0.022704 s=-0.01070 +------------------------------------------------------------------------------- +tolerance 1 um +accept 3565285.00000000 855949.00000000 5201383.00000000 +expect 3565285.41342351 855948.67986759 5201382.72939791 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# This example is a random point, transformed from ED50 to ETRS89 using KMStrans2 +------------------------------------------------------------------------------- +operation proj=helmert exact convention=coordinate_frame \ + x=-081.0703 rx=-0.48488 \ + y=-089.3603 ry=-0.02436 \ + z=-115.7526 rz=-0.41321 s=-0.540645 +------------------------------------------------------------------------------- +tolerance 1 um +accept 3494994.30120000 1056601.97250000 5212382.16660000 +expect 3494909.84026368 1056506.78938633 5212265.66699761 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# This example is a coordinate from the geodetic observatory in Onsala, +# Sweden transformed from ITRF2000 @ 2017.0 to ITRF93 @ 2017.0. + +# The test coordinate was transformed using GNSStrans, using transformation +# parameters published by ITRF: ftp://itrf.ensg.ign.fr/pub/itrf/ITRF.TP +------------------------------------------------------------------------------- +operation proj=helmert convention=position_vector \ + x = 0.0127 dx = -0.0029 rx = -0.00039 drx = -0.00011 \ + y = 0.0065 dy = -0.0002 ry = 0.00080 dry = -0.00019 \ + z = -0.0209 dz = -0.0006 rz = -0.00114 drz = 0.00007 \ + s = 0.00195 ds = 0.00001 t_epoch = 1988.0 +------------------------------------------------------------------------------- +tolerance 0.03 mm +accept 3370658.37800 711877.31400 5349787.08600 2017.0 # ITRF2000@2017.0 +expect 3370658.18890 711877.42370 5349787.12430 2017.0 # ITRF93@2017.0 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# This example is from "A mathematical relationship between NAD27 and NAD83 (91) +# State Plane coordinates in Southeastern Wisconsin": +# http://www.sewrpc.org/SEWRPCFiles/Publications/TechRep/tr-034-Mathematical-Relationship-Between-NAD27-and-NAD83-91-State-Plane-Coordinates-Southeastern-Wisconsin.pdf + +# The test data is taken from p. 29. Here we are using point 203 and converting it +# from NAD27 (ft) -> NAD83 (m). The paper reports a difference of 0.0014 m from +# measured to computed coordinates, hence the test tolerance is set accordingly. +------------------------------------------------------------------------------- +operation proj=helmert \ + x=-9597.3572 y=.6112 \ + s=0.304794780637 theta=-1.244048 +------------------------------------------------------------------------------- +tolerance 1 mm +accept 2546506.957 542256.609 0 +expect 766563.675 165282.277 0 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Finally test the 4D-capabilities of the proj.h API, especially that the +# rotation matrix is updated when necessary. + +# Test coordinates from GNSStrans. +------------------------------------------------------------------------------- +operation proj=helmert convention=position_vector \ + x = 0.01270 dx =-0.0029 rx =-0.00039 drx =-0.00011 \ + y = 0.00650 dy =-0.0002 ry = 0.00080 dry =-0.00019 \ + z =-0.0209 dz =-0.0006 rz =-0.00114 drz = 0.00007 \ + s = 0.00195 ds = 0.00001 \ + t_epoch=1988.0 +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 3370658.378 711877.314 5349787.086 2017.0 +expect 3370658.18890 711877.42370 5349787.12430 2017.0 +accept 3370658.378 711877.314 5349787.086 2018.0 +expect 3370658.18087 711877.42750 5349787.12648 2018.0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test case of https://github.com/OSGeo/PROJ/issues/2333 +------------------------------------------------------------------------------- +operation +proj=helmert +x=-0.0019 +y=-0.0017 +z=-0.0105 +s=0.00134 \ + +dx=0.0001 +dy=0.0001 +dz=-0.0018 +ds=0.00008 +t_epoch=2000.0 \ + +convention=position_vector +------------------------------------------------------------------------------- +tolerance 0.1 mm +accept 3513638.1938 778956.4525 5248216.4690 2008.75 +expect 3513638.1999 778956.4533 5248216.4535 2008.75 +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Test error cases of helmert +------------------------------------------------------------------------------- +# A rotational term implies an explicit convention to be specified +operation proj=helmert rx=1 +expect failure errno invalid_op_missing_arg + +operation proj=helmert rx=1 convention=foo +expect failure errno invalid_op_illegal_arg_value + +operation proj=helmert rx=1 convention=1 +expect failure errno invalid_op_illegal_arg_value + +# towgs84 in helmert context should always be position_vector +operation proj=helmert towgs84=1,2,3,4,5,6,7 convention=coordinate_frame +expect failure errno invalid_op_illegal_arg_value + +# Transpose no longer accepted +operation proj=helmert transpose +expect failure errno invalid_op_illegal_arg_value + +# Use of 2D Helmert interface with 3D Helmert setup +operation +proj=ob_tran +o_proj=helmert +o_lat_p=0 +direction inverse +accept 0 0 +expect failure errno no_inverse_op + +------------------------------------------------------------------------------- +# Molodensky-Badekas from IOGP Guidance 7.2, Transformation from La Canoa to REGVEN +# between geographic 2D coordinate reference systems (EPSG Dataset transformation code 1771). +# Here just taking the Cartesian step of the transformation. +------------------------------------------------------------------------------- +operation proj=molobadekas convention=coordinate_frame \ + x=-270.933 y=115.599 z=-360.226 rx=-5.266 ry=-1.238 rz=2.381 \ + s=-5.109 px=2464351.59 py=-5783466.61 pz=974809.81 +------------------------------------------------------------------------------- +tolerance 1 cm +roundtrip 1 +accept 2550408.96 -5749912.26 1054891.11 +expect 2550138.45 -5749799.87 1054530.82 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test error cases of molobadekas +------------------------------------------------------------------------------- + +# Missing convention +operation proj=molobadekas +expect failure errno invalid_op_missing_arg + + +------------------------------------------------------------------------------- +# geocentric latitude +------------------------------------------------------------------------------- +operation proj=geoc ellps=GRS80 +accept 12 55 0 0 +expect 12 54.818973308324573 0 0 +roundtrip 1000 + +accept 12 90 0 0 +expect 12 90 0 0 + +accept 12 -90 0 0 +expect 12 -90 0 0 + +accept 12 89.99999999999 0 0 +expect 12 89.999999999989996 0 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# geocentric latitude using old +geoc flag +------------------------------------------------------------------------------- +operation proj=pipeline step proj=longlat ellps=GRS80 geoc inv +accept 12 55 0 0 +expect 12 54.818973308324573 0 0 +roundtrip 1 +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +# some less used options +------------------------------------------------------------------------------- +operation proj=utm ellps=GRS80 zone=32 to_meter=0 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm ellps=GRS80 zone=32 to_meter=10 +accept 12 55 +expect 69187.5632 609890.7825 + +operation proj=utm ellps=GRS80 zone=32 to_meter=1/0 +expect failure errno invalid_op_illegal_arg_value + +operation proj=utm ellps=GRS80 zone=32 to_meter=2.0/0.2 +accept 12 55 +expect 69187.5632 609890.7825 + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test that gie can read DMS style coordinates as well as coordinates where _ +# is used as a thousands separator. +------------------------------------------------------------------------------- +operation +step +proj=latlong +ellps=WGS84 +------------------------------------------------------------------------------- +tolerance 1 m + +accept -64d43'75.34 17d32'45.6 +expect -64.737589 17.546000 + +accept 164d43'75.34 17d32'45.6 +expect 164.737589 17.546000 + +accept 164d43'75.34 17d32'45.6 +expect 164d43'75.34 17d32'45.6 + +accept 164d43'75.34W 17d32'45.6S +expect -164.737589 -17.546000 + +accept 90d00'00.00 0d00'00.00 +expect 90.0 0.0 + +accept 0d00'00.00 0d00'00.00 +expect 0.0 0.0 + + + +operation +proj=pipeline \ + +step +proj=latlong +datum=NAD27 +inv \ + +step +units=us-ft +init=nad27:3901 +tolerance 1 mm + +accept -80d32'30.000 34d32'30.000 0.0 +expect 2_138_028.224 561_330.721 0.0 + +accept -81d00'00.000 34d32'30.000 0.0 +expect 2_000_000.000 561_019.077 0.0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Some tests from PJ_eqearth.c +------------------------------------------------------------------------------- +operation +proj=eqearth +ellps=WGS84 +------------------------------------------------------------------------------- +direction forward +tolerance 1cm + +accept 0 0 +expect 0 0 + +accept -180 90 +expect -10216474.79 8392927.6 + +accept 0 90 +expect 0 8392927.6 + +accept 180 90 +expect 10216474.79 8392927.6 + +accept 180 45 +expect 14792474.75 5466867.76 + +accept 180 0 +expect 17243959.06 0 + +accept -70 -31.2 +expect -6241081.64 -3907019.16 + + +direction inverse + +accept -6241081.64 -3907019.16 +expect -70 -31.2 + +accept 17243959.06 0 +expect 180 0 + +accept 14792474.75 5466867.76 +expect 180 45 + +accept 0 0 +expect 0 0 + +accept -10216474.79 8392927.6 +expect -180 90 + +accept 0 8392927.6 +expect 0 90 + +accept 10216474.79 8392927.6 +expect 180 90 + + +operation +proj=eqearth +R=6378137 +direction forward +tolerance 1cm + +accept 0 0 +expect 0 0 + +accept -180 90 +expect -10227908.09 8402320.16 + +accept 0 90 +expect 0.00 8402320.16 + +accept 180 90 +expect 10227908.09 8402320.16 + +accept 180 45 +expect 14795421.79 5486671.72 + +accept 180 0 +expect 17263256.84 0.00 + +accept -70 -31.2 +expect -6244707.88 -3924893.29 + +direction inverse + +accept -6244707.88 -3924893.29 +expect -70 -31.2 + +accept 17263256.84 0.00 +expect 180 0 + +accept 14795421.79 5486671.72 +expect 180 45 + +accept 0 0 +expect 0 0 + +accept -10227908.09 8402320.16 +expect -180 90 + +accept 0.00 8402320.16 +expect 0 90 + +accept 10227908.09 8402320.16 +expect 180 90 + +operation +proj=eqearth +R=1 +direction inverse + +# coordinate in valid region +accept 0 -1.3 +expect 0 -82.318 + +# coordinate on edge +accept 0 -1.3173627591574 +expect 0 -90 + +# coordinate outside valid region, should be clamped +accept 0 -1.4 +expect 0 -90 + +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +# Test for PJ_affine +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +operation +proj=geogoffset +------------------------------------------------------------------------------- +direction forward +tolerance 1mm + +accept 10 20 +expect 10 20 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=geogoffset +dlon=3600 +dlat=-3600 +dh=3 +------------------------------------------------------------------------------- +direction forward +tolerance 1mm + +accept 10 20 +expect 11 19 +roundtrip 1 + +accept 10 20 30 +expect 11 19 33 +roundtrip 1 + +accept 10 20 30 40 +expect 11 19 33 40 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=affine +------------------------------------------------------------------------------- +direction forward +tolerance 1mm + +accept 10 20 30 40 +expect 10 20 30 40 +roundtrip 1 + +------------------------------------------------------------------------------- +operation +proj=affine +xoff=1 +yoff=2 +zoff=3 +toff=4 +s11=11 +s12=12 +s13=13 +s21=21 +s22=22 +s23=23 +s31=-31 +s32=32 +s33=33 +tscale=34 +------------------------------------------------------------------------------- +direction forward +tolerance 1mm + +accept 2 49 10 100 +expect 741.0000 1352.0000 1839.0000 3404.0000 +roundtrip 1 + +accept 2 49 10 +expect 741.0000 1352.0000 1839.0000 +roundtrip 1 + +accept 2 49 +expect 611.0000 1122.0000 +roundtrip 1 + +------------------------------------------------------------------------------- +# Non invertible +operation +proj=affine +s11=0 +s22=0 +s23=0 +------------------------------------------------------------------------------- +direction reverse +accept 0 0 0 0 +expect failure + +------------------------------------------------------------------------------- +# Non invertible +operation +proj=affine +tscale=0 +------------------------------------------------------------------------------- +direction reverse +accept 0 0 0 0 +expect failure + +------------------------------------------------------------------------------- +# Test lon_wrap +operation +proj=longlat +ellps=WGS84 +lon_wrap=180 +------------------------------------------------------------------------------- +direction forward +accept -1 10 0 +expect 359 10 0 + +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +# Test for vertoffset (Vertical Offset And Slope) +# Test point for EPSG Guidance note 7.2 +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +operation +proj=vertoffset +lat_0=46.9166666666666666 +lon_0=8.183333333333334 +dh=-0.245 +slope_lat=-0.210 +slope_lon=-0.032 +ellps=GRS80 +------------------------------------------------------------------------------- +direction forward +tolerance 1mm + +accept 9.666666666666666 47.333333333333336 473.000 +expect 9.666666666666666 47.333333333333336 472.690 +roundtrip 1 + +------------------------------------------------------------------------------- +# Test NaN handling +# When given NaNs, return NaNs +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +operation +proj=laea +lat_0=90 +lon_0=-150 +datum=WGS84 +units=m +------------------------------------------------------------------------------- +direction forward +tolerance 0 + +accept NaN NaN NaN NaN +expect NaN NaN NaN NaN +roundtrip 1 + +------------------------------------------------------------------------------- +# No-op +------------------------------------------------------------------------------- +operation +proj=noop +direction forward +accept 25 25 +expect 25 25 + +accept 25 25 25 +expect 25 25 25 + +accept 25 25 25 25 +expect 25 25 25 25 +------------------------------------------------------------------------------- + + +# Test invalid lat_0 +operation +proj=aeqd +R=1 +lat_0=91 +expect failure errno invalid_op_illegal_arg_value + +------------------------------------------------------------------------------- +# cart +------------------------------------------------------------------------------- + +operation +proj=cart +ellps=GRS80 +tolerance 0.001mm + +accept 0 0 0 +expect 6378137 0 0 + +accept 0 90 0 +expect 0 0 6356752.314140347 + +accept 0 -90 0 +expect 0 0 -6356752.314140347 + +accept 90 0 0 +expect 0 6378137 0 + +accept -90 0 0 +expect 0 -6378137 0 + +accept 180 0 0 +expect -6378137 0 0 + +accept -180 0 0 +expect -6378137 0 0 + +# Center of Earth ! +accept 0 0 -6378137 +expect 0 0 0 + +accept 0 90 -6356752.314140347 +expect 0 0 0 + +direction inverse + +accept 6378137 0 0 +expect 0 0 0 + +accept 0 0 6356752.314140347 +expect 0 90 0 + +accept 0 0 -6356752.314140347 +expect 0 -90 0 + +accept 0 6378137 0 +expect 90 0 0 + +accept 0 -6378137 0 +expect -90 0 0 + +accept -6378137 0 0 +expect 180 0 0 + +# Center of Earth ! +accept 0 0 0 +expect 0 90 -6356752.314140356 + +accept 0 0 1e-6 +expect 0 90 -6356752.314139356 + +accept 0 0 -1e-6 +expect 0 -90 -6356752.314139356 + + +------------------------------------------------------------------------------- +# Test handling of endianness of NTv2 grids +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_little_endian.gsb +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +operation +proj=hgridshift +grids=tests/test_hgrid_big_endian.gsb +------------------------------------------------------------------------------- +tolerance 2 mm +accept 4.5 52.5 0 +expect 5.875 55.375 0 +------------------------------------------------------------------------------- + +=============================================================================== +# Tests for testing that +omit_fwd, +omit_inv and +inv work together like they +# should. +# +# +omit_fwd specifies that a step should be omitted when running forwards. +# +# +omit_inv specifies that a step should be omitted when running backwards. +# +# +inv specifies that a step should be inverted. The forward path would do +# inverse operation and inverse path would do forward operation. +# +# | | invertible step | non-invertible step | +# | flags | forward path | inverse path | forward path | inverse path | +# | -------------- | ------------ | ------------ | ------------ | ------------ | +# | +omit_fwd | omit | inv | omit | runtime err | +# | +omit_fwd +inv | omit | fwd | omit | fwd | +# | +omit_inv | fwd | omit | fwd | omit | +# | +omit_inv +inv | inv | omit | pipeline creation error | +# +# From the table we can see that invertible steps should work pretty much all +# the time. Non-invertible steps on the other hand make either the forward path +# or inverse path undefined depending on which flags the step has. +# +# A non-invertible step is for example an affine transformation where we cannot +# calculate the inverse of the matrix. +=============================================================================== + +------------------------------------------------------------------------------- +# Test that +omit_fwd, +omit_inv and +inv work correctly with an invertible step. +------------------------------------------------------------------------------- + +# Test that forward path does nothing and inverse path does inverse transformation + +operation proj=pipeline step proj=affine s11=2 omit_fwd + +direction forward +accept 1 2 3 +expect 1 2 3 + +direction inverse +accept 1 2 3 +expect 0.5 2 3 + +# Test that forward path does nothing and inverse path does forward transformation + +operation proj=pipeline step proj=affine s11=2 omit_fwd inv + +direction forward +accept 1 2 3 +expect 1 2 3 + +direction inverse +accept 1 2 3 +expect 2 2 3 + +# Test that forward path does forward transformation and inverse path does nothing + +operation proj=pipeline step proj=affine s11=2 omit_inv + +direction forward +accept 1 2 3 +expect 2 2 3 + +direction inverse +accept 1 2 3 +expect 1 2 3 + +# Test that forward path does inverse transformation and inverse path does nothing + +operation proj=pipeline step proj=affine s11=2 omit_inv inv + +direction forward +accept 1 2 3 +expect 0.5 2 3 + +direction inverse +accept 1 2 3 +expect 1 2 3 + +------------------------------------------------------------------------------- +# Test that +omit_fwd, +omit_inv and +inv work correctly with a non-invertible step. +------------------------------------------------------------------------------- + +# Test that forward path does nothing and inverse path is not defined. +# +# The affine transformation is not invertible so this pipeline cannot be executed in +# reverse. + +operation proj=pipeline step proj=affine s11=1 s12=1 s13=1 s22=0 s33=0 omit_fwd + +direction forward +accept 1 2 3 +expect 1 2 3 + +direction inverse +accept 1 2 3 +expect failure errno no_inverse_op + +# Test that forward path does nothing and inverse path does forward transformation. +# +# The affine transformation does not have an inverse, but inv specifies that the +# step should be done in inverse order relative to our pipeline direction. When +# we execute the pipeline in reverse, we should call the forward transformation of +# the step which is defined so the pipeline should be valid in reverse. + +operation proj=pipeline step proj=affine s11=1 s12=1 s13=1 s22=0 s33=0 omit_fwd inv + +direction forward +accept 1 2 3 +expect 1 2 3 + +direction inverse +accept 1 2 3 +expect 6 0 0 + +# Test that the forward path does forward transformation and inverse path does nothing. + +operation proj=pipeline step proj=affine s11=1 s12=1 s13=1 s22=0 s33=0 omit_inv + +direction forward +accept 1 2 3 +expect 6 0 0 + +direction inverse +accept 1 2 3 +expect 1 2 3 + +# Test that the forward path is not defined +# +# When going through the forward path, inv specifies that we should execute the +# step in reverse. Because the affine transformation does not have an inverse, +# this means that the forward path does not exist. + +operation proj=pipeline step proj=affine s11=1 s12=1 s13=1 s22=0 s33=0 omit_inv inv +expect failure errno no_inverse_op + + diff --git a/test/ProjNet.Tests/Fixtures/gie/nkg.gie b/test/ProjNet.Tests/Fixtures/gie/nkg.gie new file mode 100644 index 00000000..1a5429ef --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/nkg.gie @@ -0,0 +1,270 @@ + + +# ------------------------------------------------------------------------------- +# NKG +# ------------------------------------------------------------------------------- +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_NKG_ETRF00 +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9311 948983.7980 5201383.2227 2020.5 + + +#------------------------------------------------------------------------------- +# DENMARK +#------------------------------------------------------------------------------- + +# 2008 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_DK +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9362 948983.7825 5201383.2292 2020.5 + + +operation urn:ogc:def:coordinateOperation:NKG::ETRF00_TO_DK +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.3829 948984.2188 5201383.5296 2020.5 + + +# 2020 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_DK +tolerance 0.1 mm + +# BUDD +accept 3513638.0964 778956.5470 5248216.5248 2015.0 +expect 3513638.5607 778956.1875 5248216.2477 2015.0 + +#ESBC +accept 3582104.8458 532590.0946 5232755.0863 2015.0 +expect 3582105.2916 532589.7310 5232754.8057 2015.0 + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_NKG_ETRF14 +tolerance 0.1 mm + +# BUDD +accept 3513638.0964 778956.5470 5248216.5248 2015.0 +expect 3513638.5071 778956.1528 5248216.2870 2015.0 + +# ESBC +accept 3582104.8458 532590.0946 5232755.0863 2015.0 +expect 3582105.2401 532589.6950 5232754.8507 2015.0 + +operation urn:ogc:def:coordinateOperation:NKG::ETRF14_TO_DK +tolerance 0.1 mm + +# BUDD +accept 3513638.5071 778956.1528 5248216.2870 2015.0 +expect 3513638.5607 778956.1875 5248216.2477 2015.0 + +# ESBC +accept 3582105.2401 532589.6950 5232754.8507 2015.0 +expect 3582105.2916 532589.7310 5232754.8057 2015.0 + +# ------------------------------------------------------------------------------- +# ESTONIA +# ------------------------------------------------------------------------------- + +# 2008 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_EE +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9395 948983.8006 5201383.2242 2020.5 + + +operation urn:ogc:def:coordinateOperation:NKG::ETRF00_TO_EE +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.3862 948984.2370 5201383.5246 2020.5 + + +# 2020 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_EE +tolerance 0.1 mm + +# AJOE +accept 2922027.7409 1516183.8589 5444680.6502 2015.0 +expect 2922028.2730 1516183.5457 5444680.4094 2015.0 + +# ------------------------------------------------------------------------------- +# FINLAND +# ------------------------------------------------------------------------------- + +# 2008 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_FI +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9522 948983.7911 5201383.2230 2020.5 + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_FI_EUREF-FIN +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9522 948983.7911 5201383.2230 2020.5 + + +operation urn:ogc:def:coordinateOperation:NKG::ETRF00_TO_FI +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.3989 948984.2274 5201383.5235 2020.5 + + +# 2020 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_FI +tolerance 0.1 mm + +# DEGE +accept 2994012.0569 1112559.9272 5502272.0863 2015.0 +expect 2994012.5170 1112559.5902 5502271.7683 2015.0 + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_FI_EUREF-FIN +tolerance 0.1 mm + +# DEGE +accept 2994012.0569 1112559.9272 5502272.0863 2015.0 +expect 2994012.5170 1112559.5902 5502271.7683 2015.0 + + +# ------------------------------------------------------------------------------- +# LATVIA +# ------------------------------------------------------------------------------- + +# 2008 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_LV +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9806 948983.8606 5201383.3118 2020.5 + + +operation urn:ogc:def:coordinateOperation:NKG::ETRF00_TO_LV +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.4273 948984.2970 5201383.6122 2020.5 + +# 2020 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_LV +tolerance 0.1 mm + +# BAUS +accept 3226814.4746 1449250.4615 5289639.6134 2015.0 +expect 3226814.9950 1449250.1841 5289639.3779 2015.0 + + +# ------------------------------------------------------------------------------- +# LITHUANIA +# ------------------------------------------------------------------------------- + +# 2008 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_LT +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9358 948983.8042 5201383.2294 2020.5 + + +operation urn:ogc:def:coordinateOperation:NKG::ETRF00_TO_LT +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.3826 948984.2405 5201383.5299 2020.5 + +# 2020 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_LT +tolerance 0.1 mm + +# VLNS +accept 3343600.4221 1580417.8797 5179337.3696 2015.0 +expect 3343600.9945 1580417.5661 5179337.1637 2015.0 + + +# ------------------------------------------------------------------------------- +# NORWAY +# ------------------------------------------------------------------------------- + +# 2008 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_NO +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9204 948983.8049 5201383.2054 2020.5 + + +operation urn:ogc:def:coordinateOperation:NKG::ETRF00_TO_NO +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.3671 948984.2412 5201383.5058 2020.5 + +# 2020 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_NO +tolerance 0.1 mm + +# STAS +accept 3275753.4135 321111.2481 5445042.2134 2020.0 +expect 3275753.9094 321110.8626 5445041.8818 2020.0 + +# BOD3 +accept 2391773.9918 615615.1837 5860966.1279 2020.0 +expect 2391774.5481 615614.9063 5860965.8185 2020.0 + +# KAUS +accept 2107888.9134 895603.4769 5933242.6269 2020.0 +expect 2107889.5014 895603.2055 5933242.3208 2020.0 + + +# ------------------------------------------------------------------------------- +# SWEDEN +# ------------------------------------------------------------------------------- + + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_SE +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.9223 948983.7912 5201383.2025 2020.5 + + +operation urn:ogc:def:coordinateOperation:NKG::ETRF00_TO_SE +tolerance 1 mm + +accept 3541657.3778 948984.2343 5201383.5231 2020.5 +expect 3541657.3690 948984.2275 5201383.5030 2020.5 + +# 2020 Transformations + +operation urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_SE +tolerance 0.1 mm + +# ARJ0 +accept 2441774.9791 799268.3078 5818729.4941 2015.0 +expect 2441775.4338 799268.0336 5818729.1635 2015.0 + +# BOD3 +accept 2391774.0738 615615.1324 5860966.0796 2015.0 +expect 2391774.5409 615614.8770 5860965.8078 2015.0 + +# KIR0 +accept 2248123.0276 865686.7906 5886425.8928 2015.0 +expect 2248123.5028 865686.5301 5886425.5928 2015.0 + + diff --git a/test/ProjNet.Tests/Fixtures/gie/peirce_q.gie b/test/ProjNet.Tests/Fixtures/gie/peirce_q.gie new file mode 100644 index 00000000..0fac30ed --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/peirce_q.gie @@ -0,0 +1,1334 @@ + +------------------------------------------------------------ +# This gie file was originally automatically generated using libproject +# from where the peirce_q code was adapted +------------------------------------------------------------ + +------------------------------------------------------------ +# These test values were selected from 60 points +# based on the from the original libproj test. Note that +# Peirce_q distances include considerable distortion away +# from axes, a feature of the projection to allow +# tessellation but means that there is no uniform scale +# across the coordinate space, so tolerance can be high. +------------------------------------------------------------ + +------------------------------------------------------------ +operation +proj=peirce_q +R=6370997 +shape=square +tolerance 10 mm +------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect -16684778.66 16659858.26 +accept -139.6233037328 -87.8821294926 +expect -16686136.62 16470363.98 +accept -119.6070748182 -86.3003323104 +expect -16595886.22 -16308355.92 +accept -99.5789095738 -85.2121814625 +expect -16396383.18 -16271023.42 +accept -79.2799350968 -83.8692118030 +expect -16141287.47 -16320788.59 +accept -59.1007316490 -82.6429522913 +expect -15910611.60 -16505541.92 +accept -39.7694988813 -81.1240616181 +expect 15720298.10 -16614965.01 +accept -19.4219986373 -80.4596653260 +expect 15746034.38 -16246050.72 +accept 0.0372192405 -79.1830001774 +expect 15852642.31 -15851534.09 +accept 10.8072116146 -78.5286202425 +expect 15985877.10 -15646515.07 +accept 30.6949420481 -77.5251356225 +expect 16361000.46 -15355648.15 +accept 50.8838172783 -77.2075414406 +expect 16558682.76 15284231.71 +accept 70.9123606882 -75.6020670736 +expect 16001764.31 15257501.18 +accept 90.9423960847 -75.1187688922 +expect 15509280.35 15547980.96 +accept 110.1071594840 -74.5087602471 +expect 15133049.74 15975564.05 +accept 130.0104641839 -73.3709657282 +expect 14849947.11 16543126.46 +accept 150.5931126765 -73.1105693985 +expect -14882991.09 16196524.65 +accept 170.3454770498 -72.3687427114 +expect -15093352.82 15561916.35 +accept -179.4283603569 -79.2780817976 +expect -15868107.22 15851237.11 +accept -159.6140419013 -78.9064235928 +expect -16189722.98 15580148.89 +accept -139.5485387788 -78.1437071317 +expect -16600189.56 15386190.03 +accept -119.3040589991 -77.1517896758 +expect -16316973.21 -15323941.76 +accept -99.1733290138 -76.2249333909 +expect -15804208.98 -15457233.95 +accept -79.5627247417 -75.2438944725 +expect -15346437.66 -15769094.64 +accept -59.2065888779 -74.4787205556 +expect -15021689.81 -16278889.61 +accept -39.4027491539 -73.6693258790 +expect 14885488.22 -16526762.78 +accept -19.3829588944 -72.8068670782 +expect 14968262.72 -15872213.73 +accept 0.5951648774 -71.1110965727 +expect 15222108.42 -15190983.68 +accept 10.0089578122 -71.1061903043 +expect 15489314.77 -14968374.05 +accept 30.2470524798 -69.7155529444 +expect 16124447.67 -14500816.44 +accept 50.0855077954 -69.3112248587 +expect 16498844.48 14388231.66 +accept 70.3496352985 -68.4437853274 +expect 15666358.72 14513249.85 +accept 90.4893702644 -66.7652070914 +expect 14837383.82 14868997.15 +accept 110.3952984186 -65.8768496172 +expect 14229937.28 15571030.23 +accept 130.3231133025 -64.8242212878 +expect 13868599.66 16472840.15 +accept 150.8384853690 -63.8483921906 +expect -13857834.28 15896552.84 +accept 170.7991687984 -62.8345530934 +expect -14209244.97 14904320.39 +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect -1542258.49 1501537.48 +accept -159.9563893280 71.5181717183 +expect -1879442.18 874815.08 +accept -139.1100287608 72.5807306253 +expect -1947072.52 139940.96 +accept -119.6844932150 73.8160145972 +expect -1747316.08 -478587.18 +accept -99.2835557046 74.0204664516 +expect -1452001.59 -1044052.58 +accept -79.7589672970 75.0775591038 +expect -951397.60 -1370919.39 +accept -59.3247479059 76.5851745566 +expect -370785.20 -1451930.29 +accept -39.7902268868 77.0657936158 +expect 131161.56 -1438411.11 +accept -19.7666667754 78.0413206451 +expect 568957.13 -1207234.94 +accept 0.2731990430 79.0490755914 +expect 867766.11 -859530.19 +accept 10.9115419099 79.7152157770 +expect 949655.50 -642692.40 +accept 30.7420972758 80.5901686038 +expect 1016382.12 -258281.56 +accept 50.1425480782 81.1493341375 +expect 982144.24 88390.48 +accept 70.9517084975 81.7646094071 +expect 824812.58 401430.41 +accept 90.1847984189 82.9573597164 +expect 552645.55 556222.02 +accept 110.4406311958 83.6703375043 +expect 292833.81 640803.17 +accept 130.8429657164 84.7838043163 +expect 42074.56 578888.43 +accept 150.8488588999 85.8764976286 +expect -125274.18 441272.71 +accept 170.3972098164 87.6222109978 +expect -153172.25 215556.52 + + ------------------------------------------------------------ + operation +proj=peirce_q +R=6370997 +shape=diamond + tolerance 10 mm + ------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect -17621.38 23578218.89 +accept -139.6233037328 -87.8821294926 +expect -152574.30 23445186.42 +accept -119.6070748182 -86.3003323104 +expect -23266812.75 203314.63 +accept -99.5789095738 -85.2121814625 +expect -23099344.73 88642.74 +accept -79.2799350968 -83.8692118030 +expect -22954154.12 -126926.46 +accept -59.1007316490 -82.6429522913 +expect -22921681.97 -420679.26 +accept -39.7694988813 -81.1240616181 +expect -632625.04 -22864483.82 +accept -19.4219986373 -80.4596653260 +expect -353564.94 -22621820.31 +accept 0.0372192405 -79.1830001774 +expect 783.63 -22418238.13 +accept 10.8072116146 -78.5286202425 +expect 239965.19 -22367479.01 +accept 30.6949420481 -77.5251356225 +expect 710891.44 -22427057.31 +accept 50.8838172783 -77.2075414406 +expect 22516340.76 -901172.98 +accept 70.9123606882 -75.6020670736 +expect 22103638.61 -526273.50 +accept 90.9423960847 -75.1187688922 +expect 21960800.08 27365.47 +accept 110.1071594840 -74.5087602471 +expect 21997111.76 595747.59 +accept 130.0104641839 -73.3709657282 +expect 22198255.20 1197258.60 +accept 150.5931126765 -73.1105693985 +expect 928808.49 21976536.34 +accept 170.3454770498 -72.3687427114 +expect 331324.45 21676548.71 +accept -179.4283603569 -79.2780817976 +expect -11928.97 22428963.47 +accept -159.6140419013 -78.9064235928 +expect -431033.97 22464691.83 +accept -139.5485387788 -78.1437071317 +expect -858427.30 22617785.91 +accept -119.3040589991 -77.1517896758 +expect -22373505.54 702179.27 +accept -99.1733290138 -76.2249333909 +expect -22105178.28 245348.39 +accept -79.5627247417 -75.2438944725 +expect -22002003.89 -298863.61 +accept -59.2065888779 -74.4787205556 +expect -22132851.96 -888974.50 +accept -39.4027491539 -73.6693258790 +expect -1160556.37 -22211815.70 +accept -19.3829588944 -72.8068670782 +expect -639189.89 -21807510.03 +accept 0.5951648774 -71.1110965727 +expect 22008.52 -21505303.66 +accept 10.0089578122 -71.1061903043 +expect 368360.71 -21536838.30 +accept 30.2470524798 -69.7155529444 +expect 1148080.66 -21655331.93 +accept 50.0855077954 -69.3112248587 +expect 21840460.99 -1492428.63 +accept 70.3496352985 -68.4437853274 +expect 21340205.88 -815371.10 +accept 90.4893702644 -66.7652070914 +expect 21005583.43 22354.00 +accept 110.3952984186 -65.8768496172 +expect 21072466.21 948295.92 +accept 130.3231133025 -64.8242212878 +expect 21454637.84 1841476.11 +accept 150.8384853690 -63.8483921906 +expect 1441591.72 21039528.90 +accept 170.7991687984 -62.8345530934 +expect 491492.54 20586399.49 +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect -28794.10 2152288.77 +accept -159.9563893280 71.5181717183 +expect -710378.63 1947553.99 +accept -139.1100287608 72.5807306253 +expect -1277834.99 1475741.38 +accept -119.6844932150 73.8160145972 +expect -1573951.28 897126.81 +accept -99.2835557046 74.0204664516 +expect -1764976.83 288463.51 +accept -79.7589672970 75.0775591038 +expect -1642126.09 -296646.71 +accept -59.3247479059 76.5851745566 +expect -1288854.48 -764485.02 +accept -39.7902268868 77.0657936158 +expect -924365.02 -1109855.48 +accept -19.7666667754 78.0413206451 +expect -451330.57 -1255957.46 +accept 0.2731990430 79.0490755914 +expect 5823.67 -1221382.92 +accept 10.9115419099 79.7152157770 +expect 217055.69 -1125960.00 +accept 30.7420972758 80.5901686038 +expect 536058.05 -901323.33 +accept 50.1425480782 81.1493341375 +expect 756982.36 -631979.34 +accept 70.9517084975 81.7646094071 +expect 867084.74 -299376.41 +accept 90.1847984189 82.9573597164 +expect 784087.78 2528.94 +accept 110.4406311958 83.6703375043 +expect 660181.04 246051.50 +accept 130.8429657164 84.7838043163 +expect 439087.14 379584.73 +accept 150.8488588999 85.8764976286 +expect 223444.70 400609.15 +accept 170.3972098164 87.6222109978 +expect 44112.34 260730.62 + +------------------------------------------------------------ +operation +proj=peirce_q +R=6370997 +shape=horizontal +tolerance 10 mm +------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect 11829925.59 46389.53 +accept -139.6233037328 -87.8821294926 +expect 11964878.50 179422.00 +accept -119.6070748182 -86.3003323104 +expect 12170099.87 203314.63 +accept -99.5789095738 -85.2121814625 +expect 12337567.89 88642.74 +accept -79.2799350968 -83.8692118030 +expect 12482758.50 -126926.46 +accept -59.1007316490 -82.6429522913 +expect 12515230.65 -420679.26 +accept -39.7694988813 -81.1240616181 +expect 12444929.25 -760124.60 +accept -19.4219986373 -80.4596653260 +expect 12165869.15 -1002788.10 +accept 0.0372192405 -79.1830001774 +expect 11811520.58 -1206370.29 +accept 10.8072116146 -78.5286202425 +expect 11572339.01 -1257129.40 +accept 30.6949420481 -77.5251356225 +expect 11101412.77 -1197551.10 +accept 50.8838172783 -77.2075414406 +expect 10704036.55 -901172.98 +accept 70.9123606882 -75.6020670736 +expect 10291334.40 -526273.50 +accept 90.9423960847 -75.1187688922 +expect 10148495.87 27365.47 +accept 110.1071594840 -74.5087602471 +expect 10184807.56 595747.59 +accept 130.0104641839 -73.3709657282 +expect 10385951.00 1197258.60 +accept 150.5931126765 -73.1105693985 +expect 10883495.72 1648072.07 +accept 170.3454770498 -72.3687427114 +expect 11480979.75 1948059.70 +accept -179.4283603569 -79.2780817976 +expect 11824233.18 1195644.94 +accept -159.6140419013 -78.9064235928 +expect 12243338.18 1159916.58 +accept -139.5485387788 -78.1437071317 +expect 12670731.51 1006822.50 +accept -119.3040589991 -77.1517896758 +expect 13063407.08 702179.27 +accept -99.1733290138 -76.2249333909 +expect 13331734.34 245348.39 +accept -79.5627247417 -75.2438944725 +expect 13434908.73 -298863.61 +accept -59.2065888779 -74.4787205556 +expect 13304060.66 -888974.50 +accept -39.4027491539 -73.6693258790 +expect 12972860.58 -1412792.72 +accept -19.3829588944 -72.8068670782 +expect 12451494.09 -1817098.38 +accept 0.5951648774 -71.1110965727 +expect 11790295.69 -2119304.75 +accept 10.0089578122 -71.1061903043 +expect 11443943.49 -2087770.11 +accept 30.2470524798 -69.7155529444 +expect 10664223.55 -1969276.48 +accept 50.0855077954 -69.3112248587 +expect 10028156.79 -1492428.63 +accept 70.3496352985 -68.4437853274 +expect 9527901.67 -815371.10 +accept 90.4893702644 -66.7652070914 +expect 9193279.23 22354.00 +accept 110.3952984186 -65.8768496172 +expect 9260162.01 948295.92 +accept 130.3231133025 -64.8242212878 +expect 9642333.63 1841476.11 +accept 150.8384853690 -63.8483921906 +expect 10370712.49 2585079.51 +accept 170.7991687984 -62.8345530934 +expect 11320811.66 3038208.93 +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect -11841098.31 2152288.77 +accept -159.9563893280 71.5181717183 +expect -12522682.84 1947553.99 +accept -139.1100287608 72.5807306253 +expect -13090139.19 1475741.38 +accept -119.6844932150 73.8160145972 +expect -13386255.49 897126.81 +accept -99.2835557046 74.0204664516 +expect -13577281.04 288463.51 +accept -79.7589672970 75.0775591038 +expect -13454430.30 -296646.71 +accept -59.3247479059 76.5851745566 +expect -13101158.69 -764485.02 +accept -39.7902268868 77.0657936158 +expect -12736669.23 -1109855.48 +accept -19.7666667754 78.0413206451 +expect -12263634.78 -1255957.46 +accept 0.2731990430 79.0490755914 +expect -11806480.53 -1221382.92 +accept 10.9115419099 79.7152157770 +expect -11595248.52 -1125960.00 +accept 30.7420972758 80.5901686038 +expect -11276246.16 -901323.33 +accept 50.1425480782 81.1493341375 +expect -11055321.85 -631979.34 +accept 70.9517084975 81.7646094071 +expect -10945219.47 -299376.41 +accept 90.1847984189 82.9573597164 +expect -11028216.43 2528.94 +accept 110.4406311958 83.6703375043 +expect -11152123.17 246051.50 +accept 130.8429657164 84.7838043163 +expect -11373217.07 379584.73 +accept 150.8488588999 85.8764976286 +expect -11588859.50 400609.15 +accept 170.3972098164 87.6222109978 +expect -11768191.87 260730.62 + + ------------------------------------------------------------ + operation +proj=peirce_q +R=6370997 +shape=horizontal +scrollx=0.75 + tolerance 10 mm + ------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect 17621.38 46389.53 +accept -139.6233037328 -87.8821294926 +expect 152574.30 179422.00 +accept -119.6070748182 -86.3003323104 +expect 357795.67 203314.63 +accept -99.5789095738 -85.2121814625 +expect 525263.69 88642.74 +accept -79.2799350968 -83.8692118030 +expect 670454.30 -126926.46 +accept -59.1007316490 -82.6429522913 +expect 702926.44 -420679.26 +accept -39.7694988813 -81.1240616181 +expect 632625.04 -760124.60 +accept -19.4219986373 -80.4596653260 +expect 353564.94 -1002788.10 +accept 0.0372192405 -79.1830001774 +expect -783.63 -1206370.29 +accept 10.8072116146 -78.5286202425 +expect -239965.19 -1257129.40 +accept 30.6949420481 -77.5251356225 +expect -710891.44 -1197551.10 +accept 50.8838172783 -77.2075414406 +expect -1108267.66 -901172.98 +accept 70.9123606882 -75.6020670736 +expect -1520969.81 -526273.50 +accept 90.9423960847 -75.1187688922 +expect -1663808.33 27365.47 +accept 110.1071594840 -74.5087602471 +expect -1627496.65 595747.59 +accept 130.0104641839 -73.3709657282 +expect -1426353.21 1197258.60 +accept 150.5931126765 -73.1105693985 +expect -928808.49 1648072.07 +accept 170.3454770498 -72.3687427114 +expect -331324.45 1948059.70 +accept -179.4283603569 -79.2780817976 +expect 11928.97 1195644.94 +accept -159.6140419013 -78.9064235928 +expect 431033.97 1159916.58 +accept -139.5485387788 -78.1437071317 +expect 858427.30 1006822.50 +accept -119.3040589991 -77.1517896758 +expect 1251102.88 702179.27 +accept -99.1733290138 -76.2249333909 +expect 1519430.13 245348.39 +accept -79.5627247417 -75.2438944725 +expect 1622604.52 -298863.61 +accept -59.2065888779 -74.4787205556 +expect 1491756.45 -888974.50 +accept -39.4027491539 -73.6693258790 +expect 1160556.37 -1412792.72 +accept -19.3829588944 -72.8068670782 +expect 639189.89 -1817098.38 +accept 0.5951648774 -71.1110965727 +expect -22008.52 -2119304.75 +accept 10.0089578122 -71.1061903043 +expect -368360.71 -2087770.11 +accept 30.2470524798 -69.7155529444 +expect -1148080.66 -1969276.48 +accept 50.0855077954 -69.3112248587 +expect -1784147.42 -1492428.63 +accept 70.3496352985 -68.4437853274 +expect -2284402.54 -815371.10 +accept 90.4893702644 -66.7652070914 +expect -2619024.98 22354.00 +accept 110.3952984186 -65.8768496172 +expect -2552142.20 948295.92 +accept 130.3231133025 -64.8242212878 +expect -2169970.58 1841476.11 +accept 150.8384853690 -63.8483921906 +expect -1441591.72 2585079.51 +accept 170.7991687984 -62.8345530934 +expect -491492.54 3038208.93 +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect 23595814.31 2152288.77 +accept -159.9563893280 71.5181717183 +expect 22914229.78 1947553.99 +accept -139.1100287608 72.5807306253 +expect 22346773.43 1475741.38 +accept -119.6844932150 73.8160145972 +expect 22050657.13 897126.81 +accept -99.2835557046 74.0204664516 +expect 21859631.58 288463.51 +accept -79.7589672970 75.0775591038 +expect 21982482.33 -296646.71 +accept -59.3247479059 76.5851745566 +expect 22335753.93 -764485.02 +accept -39.7902268868 77.0657936158 +expect 22700243.39 -1109855.48 +accept -19.7666667754 78.0413206451 +expect 23173277.84 -1255957.46 +accept 0.2731990430 79.0490755914 +expect -23618784.74 -1221382.92 +accept 10.9115419099 79.7152157770 +expect -23407552.72 -1125960.00 +accept 30.7420972758 80.5901686038 +expect -23088550.36 -901323.33 +accept 50.1425480782 81.1493341375 +expect -22867626.05 -631979.34 +accept 70.9517084975 81.7646094071 +expect -22757523.68 -299376.41 +accept 90.1847984189 82.9573597164 +expect -22840520.64 2528.94 +accept 110.4406311958 83.6703375043 +expect -22964427.38 246051.50 +accept 130.8429657164 84.7838043163 +expect -23185521.28 379584.73 +accept 150.8488588999 85.8764976286 +expect -23401163.71 400609.15 +accept 170.3972098164 87.6222109978 +expect -23580496.07 260730.62 + + ------------------------------------------------------------ + operation +proj=peirce_q +R=6370997 +shape=vertical + tolerance 10 mm + ------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect -17621.38 11765914.68 +accept -139.6233037328 -87.8821294926 +expect -152574.30 11632882.21 +accept -119.6070748182 -86.3003323104 +expect -357795.67 11608989.58 +accept -99.5789095738 -85.2121814625 +expect -525263.69 11723661.47 +accept -79.2799350968 -83.8692118030 +expect -670454.30 11939230.67 +accept -59.1007316490 -82.6429522913 +expect -702926.44 12232983.46 +accept -39.7694988813 -81.1240616181 +expect -632625.04 12572428.81 +accept -19.4219986373 -80.4596653260 +expect -353564.94 12815092.31 +accept 0.0372192405 -79.1830001774 +expect 783.63 13018674.49 +accept 10.8072116146 -78.5286202425 +expect 239965.19 13069433.61 +accept 30.6949420481 -77.5251356225 +expect 710891.44 13009855.31 +accept 50.8838172783 -77.2075414406 +expect 1108267.66 12713477.19 +accept 70.9123606882 -75.6020670736 +expect 1520969.81 12338577.71 +accept 90.9423960847 -75.1187688922 +expect 1663808.33 11784938.74 +accept 110.1071594840 -74.5087602471 +expect 1627496.65 11216556.62 +accept 130.0104641839 -73.3709657282 +expect 1426353.21 10615045.61 +accept 150.5931126765 -73.1105693985 +expect 928808.49 10164232.13 +accept 170.3454770498 -72.3687427114 +expect 331324.45 9864244.50 +accept -179.4283603569 -79.2780817976 +expect -11928.97 10616659.27 +accept -159.6140419013 -78.9064235928 +expect -431033.97 10652387.63 +accept -139.5485387788 -78.1437071317 +expect -858427.30 10805481.70 +accept -119.3040589991 -77.1517896758 +expect -1251102.88 11110124.94 +accept -99.1733290138 -76.2249333909 +expect -1519430.13 11566955.81 +accept -79.5627247417 -75.2438944725 +expect -1622604.52 12111167.82 +accept -59.2065888779 -74.4787205556 +expect -1491756.45 12701278.71 +accept -39.4027491539 -73.6693258790 +expect -1160556.37 13225096.92 +accept -19.3829588944 -72.8068670782 +expect -639189.89 13629402.59 +accept 0.5951648774 -71.1110965727 +expect 22008.52 13931608.96 +accept 10.0089578122 -71.1061903043 +expect 368360.71 13900074.32 +accept 30.2470524798 -69.7155529444 +expect 1148080.66 13781580.69 +accept 50.0855077954 -69.3112248587 +expect 1784147.42 13304732.84 +accept 70.3496352985 -68.4437853274 +expect 2284402.54 12627675.31 +accept 90.4893702644 -66.7652070914 +expect 2619024.98 11789950.21 +accept 110.3952984186 -65.8768496172 +expect 2552142.20 10864008.28 +accept 130.3231133025 -64.8242212878 +expect 2169970.58 9970828.10 +accept 150.8384853690 -63.8483921906 +expect 1441591.72 9227224.69 +accept 170.7991687984 -62.8345530934 +expect 491492.54 8774095.28 +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect -28794.10 -9660015.44 +accept -159.9563893280 71.5181717183 +expect -710378.63 -9864750.22 +accept -139.1100287608 72.5807306253 +expect -1277834.99 -10336562.82 +accept -119.6844932150 73.8160145972 +expect -1573951.28 -10915177.40 +accept -99.2835557046 74.0204664516 +expect -1764976.83 -11523840.70 +accept -79.7589672970 75.0775591038 +expect -1642126.09 -12108950.91 +accept -59.3247479059 76.5851745566 +expect -1288854.48 -12576789.23 +accept -39.7902268868 77.0657936158 +expect -924365.02 -12922159.68 +accept -19.7666667754 78.0413206451 +expect -451330.57 -13068261.66 +accept 0.2731990430 79.0490755914 +expect 5823.67 -13033687.13 +accept 10.9115419099 79.7152157770 +expect 217055.69 -12938264.21 +accept 30.7420972758 80.5901686038 +expect 536058.05 -12713627.54 +accept 50.1425480782 81.1493341375 +expect 756982.36 -12444283.55 +accept 70.9517084975 81.7646094071 +expect 867084.74 -12111680.61 +accept 90.1847984189 82.9573597164 +expect 784087.78 -11809775.26 +accept 110.4406311958 83.6703375043 +expect 660181.04 -11566252.71 +accept 130.8429657164 84.7838043163 +expect 439087.14 -11432719.48 +accept 150.8488588999 85.8764976286 +expect 223444.70 -11411695.05 +accept 170.3972098164 87.6222109978 +expect 44112.34 -11551573.59 + + ------------------------------------------------------------ + operation +proj=peirce_q +R=6370997 +shape=vertical +scrolly=-0.25 + tolerance 10 mm + ------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect -17621.38 -46389.53 +accept -139.6233037328 -87.8821294926 +expect -152574.30 -179422.00 +accept -119.6070748182 -86.3003323104 +expect -357795.67 -203314.63 +accept -99.5789095738 -85.2121814625 +expect -525263.69 -88642.74 +accept -79.2799350968 -83.8692118030 +expect -670454.30 126926.46 +accept -59.1007316490 -82.6429522913 +expect -702926.44 420679.26 +accept -39.7694988813 -81.1240616181 +expect -632625.04 760124.60 +accept -19.4219986373 -80.4596653260 +expect -353564.94 1002788.10 +accept 0.0372192405 -79.1830001774 +expect 783.63 1206370.29 +accept 10.8072116146 -78.5286202425 +expect 239965.19 1257129.40 +accept 30.6949420481 -77.5251356225 +expect 710891.44 1197551.10 +accept 50.8838172783 -77.2075414406 +expect 1108267.66 901172.98 +accept 70.9123606882 -75.6020670736 +expect 1520969.81 526273.50 +accept 90.9423960847 -75.1187688922 +expect 1663808.33 -27365.47 +accept 110.1071594840 -74.5087602471 +expect 1627496.65 -595747.59 +accept 130.0104641839 -73.3709657282 +expect 1426353.21 -1197258.60 +accept 150.5931126765 -73.1105693985 +expect 928808.49 -1648072.07 +accept 170.3454770498 -72.3687427114 +expect 331324.45 -1948059.70 +accept -179.4283603569 -79.2780817976 +expect -11928.97 -1195644.94 +accept -159.6140419013 -78.9064235928 +expect -431033.97 -1159916.58 +accept -139.5485387788 -78.1437071317 +expect -858427.30 -1006822.50 +accept -119.3040589991 -77.1517896758 +expect -1251102.88 -702179.27 +accept -99.1733290138 -76.2249333909 +expect -1519430.13 -245348.39 +accept -79.5627247417 -75.2438944725 +expect -1622604.52 298863.61 +accept -59.2065888779 -74.4787205556 +expect -1491756.45 888974.50 +accept -39.4027491539 -73.6693258790 +expect -1160556.37 1412792.72 +accept -19.3829588944 -72.8068670782 +expect -639189.89 1817098.38 +accept 0.5951648774 -71.1110965727 +expect 22008.52 2119304.75 +accept 10.0089578122 -71.1061903043 +expect 368360.71 2087770.11 +accept 30.2470524798 -69.7155529444 +expect 1148080.66 1969276.48 +accept 50.0855077954 -69.3112248587 +expect 1784147.42 1492428.63 +accept 70.3496352985 -68.4437853274 +expect 2284402.54 815371.10 +accept 90.4893702644 -66.7652070914 +expect 2619024.98 -22354.00 +accept 110.3952984186 -65.8768496172 +expect 2552142.20 -948295.92 +accept 130.3231133025 -64.8242212878 +expect 2169970.58 -1841476.11 +accept 150.8384853690 -63.8483921906 +expect 1441591.72 -2585079.51 +accept 170.7991687984 -62.8345530934 +expect 491492.54 -3038208.93 +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect -28794.10 -21472319.64 +accept -159.9563893280 71.5181717183 +expect -710378.63 -21677054.43 +accept -139.1100287608 72.5807306253 +expect -1277834.99 -22148867.03 +accept -119.6844932150 73.8160145972 +expect -1573951.28 -22727481.60 +accept -99.2835557046 74.0204664516 +expect -1764976.83 -23336144.90 +accept -79.7589672970 75.0775591038 +expect -1642126.09 23327961.71 +accept -59.3247479059 76.5851745566 +expect -1288854.48 22860123.39 +accept -39.7902268868 77.0657936158 +expect -924365.02 22514752.94 +accept -19.7666667754 78.0413206451 +expect -451330.57 22368650.96 +accept 0.2731990430 79.0490755914 +expect 5823.67 22403225.49 +accept 10.9115419099 79.7152157770 +expect 217055.69 22498648.41 +accept 30.7420972758 80.5901686038 +expect 536058.05 22723285.08 +accept 50.1425480782 81.1493341375 +expect 756982.36 22992629.07 +accept 70.9517084975 81.7646094071 +expect 867084.74 23325232.01 +accept 90.1847984189 82.9573597164 +expect 784087.78 -23622079.47 +accept 110.4406311958 83.6703375043 +expect 660181.04 -23378556.92 +accept 130.8429657164 84.7838043163 +expect 439087.14 -23245023.69 +accept 150.8488588999 85.8764976286 +expect 223444.70 -23223999.26 +accept 170.3972098164 87.6222109978 +expect 44112.34 -23363877.80 + + ------------------------------------------------------------ + operation +proj=peirce_q +R=6370997 +shape=nhemisphere + tolerance 10 mm + ------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect failure errno coord_transfm_outside_projection_domain +accept -139.6233037328 -87.8821294926 +expect failure errno coord_transfm_outside_projection_domain +accept -119.6070748182 -86.3003323104 +expect failure errno coord_transfm_outside_projection_domain +accept -99.5789095738 -85.2121814625 +expect failure errno coord_transfm_outside_projection_domain +accept -79.2799350968 -83.8692118030 +expect failure errno coord_transfm_outside_projection_domain +accept -59.1007316490 -82.6429522913 +expect failure errno coord_transfm_outside_projection_domain +accept -39.7694988813 -81.1240616181 +expect failure errno coord_transfm_outside_projection_domain +accept -19.4219986373 -80.4596653260 +expect failure errno coord_transfm_outside_projection_domain +accept 0.0372192405 -79.1830001774 +expect failure errno coord_transfm_outside_projection_domain +accept 10.8072116146 -78.5286202425 +expect failure errno coord_transfm_outside_projection_domain +accept 30.6949420481 -77.5251356225 +expect failure errno coord_transfm_outside_projection_domain +accept 50.8838172783 -77.2075414406 +expect failure errno coord_transfm_outside_projection_domain +accept 70.9123606882 -75.6020670736 +expect failure errno coord_transfm_outside_projection_domain +accept 90.9423960847 -75.1187688922 +expect failure errno coord_transfm_outside_projection_domain +accept 110.1071594840 -74.5087602471 +expect failure errno coord_transfm_outside_projection_domain +accept 130.0104641839 -73.3709657282 +expect failure errno coord_transfm_outside_projection_domain +accept 150.5931126765 -73.1105693985 +expect failure errno coord_transfm_outside_projection_domain +accept 170.3454770498 -72.3687427114 +expect failure errno coord_transfm_outside_projection_domain +accept -179.4283603569 -79.2780817976 +expect failure errno coord_transfm_outside_projection_domain +accept -159.6140419013 -78.9064235928 +expect failure errno coord_transfm_outside_projection_domain +accept -139.5485387788 -78.1437071317 +expect failure errno coord_transfm_outside_projection_domain +accept -119.3040589991 -77.1517896758 +expect failure errno coord_transfm_outside_projection_domain +accept -99.1733290138 -76.2249333909 +expect failure errno coord_transfm_outside_projection_domain +accept -79.5627247417 -75.2438944725 +expect failure errno coord_transfm_outside_projection_domain +accept -59.2065888779 -74.4787205556 +expect failure errno coord_transfm_outside_projection_domain +accept -39.4027491539 -73.6693258790 +expect failure errno coord_transfm_outside_projection_domain +accept -19.3829588944 -72.8068670782 +expect failure errno coord_transfm_outside_projection_domain +accept 0.5951648774 -71.1110965727 +expect failure errno coord_transfm_outside_projection_domain +accept 10.0089578122 -71.1061903043 +expect failure errno coord_transfm_outside_projection_domain +accept 30.2470524798 -69.7155529444 +expect failure errno coord_transfm_outside_projection_domain +accept 50.0855077954 -69.3112248587 +expect failure errno coord_transfm_outside_projection_domain +accept 70.3496352985 -68.4437853274 +expect failure errno coord_transfm_outside_projection_domain +accept 90.4893702644 -66.7652070914 +expect failure errno coord_transfm_outside_projection_domain +accept 110.3952984186 -65.8768496172 +expect failure errno coord_transfm_outside_projection_domain +accept 130.3231133025 -64.8242212878 +expect failure errno coord_transfm_outside_projection_domain +accept 150.8384853690 -63.8483921906 +expect failure errno coord_transfm_outside_projection_domain +accept 170.7991687984 -62.8345530934 +expect failure errno coord_transfm_outside_projection_domain +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect -28794.10 2152288.77 +accept -159.9563893280 71.5181717183 +expect -710378.63 1947553.99 +accept -139.1100287608 72.5807306253 +expect -1277834.99 1475741.38 +accept -119.6844932150 73.8160145972 +expect -1573951.28 897126.81 +accept -99.2835557046 74.0204664516 +expect -1764976.83 288463.51 +accept -79.7589672970 75.0775591038 +expect -1642126.09 -296646.71 +accept -59.3247479059 76.5851745566 +expect -1288854.48 -764485.02 +accept -39.7902268868 77.0657936158 +expect -924365.02 -1109855.48 +accept -19.7666667754 78.0413206451 +expect -451330.57 -1255957.46 +accept 0.2731990430 79.0490755914 +expect 5823.67 -1221382.92 +accept 10.9115419099 79.7152157770 +expect 217055.69 -1125960.00 +accept 30.7420972758 80.5901686038 +expect 536058.05 -901323.33 +accept 50.1425480782 81.1493341375 +expect 756982.36 -631979.34 +accept 70.9517084975 81.7646094071 +expect 867084.74 -299376.41 +accept 90.1847984189 82.9573597164 +expect 784087.78 2528.94 +accept 110.4406311958 83.6703375043 +expect 660181.04 246051.50 +accept 130.8429657164 84.7838043163 +expect 439087.14 379584.73 +accept 150.8488588999 85.8764976286 +expect 223444.70 400609.15 +accept 170.3972098164 87.6222109978 +expect 44112.34 260730.62 + + ------------------------------------------------------------ + operation +proj=peirce_q +R=6370997 +shape=shemisphere + tolerance 10 mm + ------------------------------------------------------------ + +accept -179.6126302052 -90.2440064745 +expect failure errno coord_transfm_invalid_coord +accept -159.2003712209 -89.5537263306 +expect -17621.38 46389.53 +accept -139.6233037328 -87.8821294926 +expect -152574.30 179422.00 +accept -119.6070748182 -86.3003323104 +expect -357795.67 203314.63 +accept -99.5789095738 -85.2121814625 +expect -525263.69 88642.74 +accept -79.2799350968 -83.8692118030 +expect -670454.30 -126926.46 +accept -59.1007316490 -82.6429522913 +expect -702926.44 -420679.26 +accept -39.7694988813 -81.1240616181 +expect -632625.04 -760124.60 +accept -19.4219986373 -80.4596653260 +expect -353564.94 -1002788.10 +accept 0.0372192405 -79.1830001774 +expect 783.63 -1206370.29 +accept 10.8072116146 -78.5286202425 +expect 239965.19 -1257129.40 +accept 30.6949420481 -77.5251356225 +expect 710891.44 -1197551.10 +accept 50.8838172783 -77.2075414406 +expect 1108267.66 -901172.98 +accept 70.9123606882 -75.6020670736 +expect 1520969.81 -526273.50 +accept 90.9423960847 -75.1187688922 +expect 1663808.33 27365.47 +accept 110.1071594840 -74.5087602471 +expect 1627496.65 595747.59 +accept 130.0104641839 -73.3709657282 +expect 1426353.21 1197258.60 +accept 150.5931126765 -73.1105693985 +expect 928808.49 1648072.07 +accept 170.3454770498 -72.3687427114 +expect 331324.45 1948059.70 +accept -179.4283603569 -79.2780817976 +expect -11928.97 1195644.94 +accept -159.6140419013 -78.9064235928 +expect -431033.97 1159916.58 +accept -139.5485387788 -78.1437071317 +expect -858427.30 1006822.50 +accept -119.3040589991 -77.1517896758 +expect -1251102.88 702179.27 +accept -99.1733290138 -76.2249333909 +expect -1519430.13 245348.39 +accept -79.5627247417 -75.2438944725 +expect -1622604.52 -298863.61 +accept -59.2065888779 -74.4787205556 +expect -1491756.45 -888974.50 +accept -39.4027491539 -73.6693258790 +expect -1160556.37 -1412792.72 +accept -19.3829588944 -72.8068670782 +expect -639189.89 -1817098.38 +accept 0.5951648774 -71.1110965727 +expect 22008.52 -2119304.75 +accept 10.0089578122 -71.1061903043 +expect 368360.71 -2087770.11 +accept 30.2470524798 -69.7155529444 +expect 1148080.66 -1969276.48 +accept 50.0855077954 -69.3112248587 +expect 1784147.42 -1492428.63 +accept 70.3496352985 -68.4437853274 +expect 2284402.54 -815371.10 +accept 90.4893702644 -66.7652070914 +expect 2619024.98 22354.00 +accept 110.3952984186 -65.8768496172 +expect 2552142.20 948295.92 +accept 130.3231133025 -64.8242212878 +expect 2169970.58 1841476.11 +accept 150.8384853690 -63.8483921906 +expect 1441591.72 2585079.51 +accept 170.7991687984 -62.8345530934 +expect 491492.54 3038208.93 +accept 170.6832502669 -105.0174505020 +expect failure errno coord_transfm_invalid_coord +accept 180.7137917600 105.8174218935 +expect failure errno coord_transfm_invalid_coord +accept -179.2332724818 70.8217746040 +expect failure errno coord_transfm_outside_projection_domain +accept -159.9563893280 71.5181717183 +expect failure errno coord_transfm_outside_projection_domain +accept -139.1100287608 72.5807306253 +expect failure errno coord_transfm_outside_projection_domain +accept -119.6844932150 73.8160145972 +expect failure errno coord_transfm_outside_projection_domain +accept -99.2835557046 74.0204664516 +expect failure errno coord_transfm_outside_projection_domain +accept -79.7589672970 75.0775591038 +expect failure errno coord_transfm_outside_projection_domain +accept -59.3247479059 76.5851745566 +expect failure errno coord_transfm_outside_projection_domain +accept -39.7902268868 77.0657936158 +expect failure errno coord_transfm_outside_projection_domain +accept -19.7666667754 78.0413206451 +expect failure errno coord_transfm_outside_projection_domain +accept 0.2731990430 79.0490755914 +expect failure errno coord_transfm_outside_projection_domain +accept 10.9115419099 79.7152157770 +expect failure errno coord_transfm_outside_projection_domain +accept 30.7420972758 80.5901686038 +expect failure errno coord_transfm_outside_projection_domain +accept 50.1425480782 81.1493341375 +expect failure errno coord_transfm_outside_projection_domain +accept 70.9517084975 81.7646094071 +expect failure errno coord_transfm_outside_projection_domain +accept 90.1847984189 82.9573597164 +expect failure errno coord_transfm_outside_projection_domain +accept 110.4406311958 83.6703375043 +expect failure errno coord_transfm_outside_projection_domain +accept 130.8429657164 84.7838043163 +expect failure errno coord_transfm_outside_projection_domain +accept 150.8488588999 85.8764976286 +expect failure errno coord_transfm_outside_projection_domain +accept 170.3972098164 87.6222109978 +expect failure errno coord_transfm_outside_projection_domain + +# Test inverse +------------------------------------------------------------ +operation +proj=peirce_q +shape=square +------------------------------------------------------------ + +#tolerance 1 mm +# has to bump to this for i386 +tolerance 150 mm + +accept 0 90 +expect 0 0 +roundtrip 1 + +accept 0 0 +expect 8361921.234827487729 -8361921.234827487729 +roundtrip 1 + +accept 0 -90 +expect 16723842.303160080686 -16723842.303160080686 +#tolerance 2 mm +roundtrip 1 +#tolerance 1 mm + +accept 0 45 +expect 3725360.212758612353 -3725360.212758612353 +roundtrip 1 + +accept 0 -45 +expect 12998482.090401465073 -12998482.090401465073 +roundtrip 1 + +accept 45 0 +tolerance 200 mm +expect 16723842.564696932212 -0.095041956369 +roundtrip 1 +tolerance 150 mm + +accept -45 0 +expect 0 -16723842.469654975459 +#roundtrip 1 + +accept 90 0 +expect 8361921.329869444482 8361921.329869444482 +roundtrip 1 + +accept -90 0 +expect -8361921.234827487729 -8361921.234827487729 +roundtrip 1 + +accept 135 0 +expect 0.095041956369 16723842.564696932212 +roundtrip 1 + +accept -135 0 +expect -16723842.430287310854 -0.039367665210 +#roundtrip 1 + +accept 179.99 0 +expect -8360808.039828131907 8363034.429826845415 +#roundtrip 1 + +accept -179.99 0 +expect -8363034.429826845415 8360808.039828131907 +#roundtrip 1 + +accept 45 45 +expect 5299570.257319082506 0 +roundtrip 1 + +accept -45 45 +expect 0 -5299570.257319079712 +roundtrip 1 + +accept 90 45 +expect 3725360.212758610491 3725360.212758610491 +roundtrip 1 + +accept -90 45 +expect -3725360.212758613285 -3725360.212758613285 +roundtrip 1 + +accept 135 45 +expect 0 5299570.257319079712 +roundtrip 1 + +accept -135 45 +expect -5299570.257319079712 0 +#roundtrip 1 + +accept 179.99 45 +expect -3724717.456456150394 3726002.863303491380 +roundtrip 1 + +accept -179.99 45 +expect -3726002.863303492777 3724717.456456151791 +roundtrip 1 + +accept 45 -45 +expect 16723842.303160080686 11424272.045840997249 +roundtrip 1 + +accept -45 -45 +expect 11424272.045840999112 -16723842.303160080686 +roundtrip 1 + +accept 90 -45 +expect 12998482.090401468799 12998482.090401468799 +roundtrip 1 + +accept -90 -45 +expect -12998482.090401465073 -12998482.090401465073 +roundtrip 1 + +accept 135 -45 +expect -11424272.045840999112 16723842.303160080686 +roundtrip 1 + +accept -135 -45 +expect -16723842.303160080686 -11424272.045840999112 +roundtrip 1 + +accept 179.99 -45 +expect -12997839.439856586978 12999124.846703927964 +roundtrip 1 + +accept -179.99 -45 +expect -12999124.846703927964 12997839.439856585115 +roundtrip 1 + +accept 45 -89.999 +expect 16723842.303160080686 16723730.983657168224 +#roundtrip 1 + +accept -45 -89.999 +expect 16723730.983657168224 -16723842.303160080686 +#roundtrip 1 + +accept 90 -89.999 +expect 16723763.588384689763 16723763.588384689763 +roundtrip 1 + +accept -90 -89.999 +expect -16723763.588384689763 -16723763.588384689763 +roundtrip 1 + +accept 135 -89.999 +expect -16723730.983657168224 16723842.303160080686 +#roundtrip 1 + +accept -135 -89.999 +expect -16723842.303160080686 -16723730.983657168224 +#roundtrip 1 + +accept 179.99 -89.999 +expect -16723763.588384689763 16723763.588384689763 +#roundtrip 1 + +accept -179.99 -89.999 +expect -16723763.588384689763 16723763.588384689763 +#roundtrip 1 + +# Test inverse +------------------------------------------------------------ +operation +proj=peirce_q +shape=diamond +------------------------------------------------------------ + +#tolerance 1 mm +# has to bump to this for i386 +tolerance 150 mm + +accept 0 90 +expect 0 0 +roundtrip 1 + +accept 0 -90 +#tolerance 10 mm +expect 0 -23651084.600117880851 +roundtrip 1 +#tolerance 1 mm + +accept 0 45 +expect 0 -5268454.937608348206 +roundtrip 1 + +accept 0 -45 +expect 0 -18382629.662509534508 +roundtrip 1 + +accept 45 0 +tolerance 200 mm +expect 11825542.417788611725 -11825542.552198234946 +roundtrip 1 +tolerance 150 mm + +accept -45 0 +expect -11825542.417788611725 -11825542.417788611725 +roundtrip 1 + +accept 90 0 +expect 11825542.552198234946 0.000000000000 +roundtrip 1 + +accept -90 0 +expect -11825542.417788611725 0.000000000000 +#tolerance 20 mm +#roundtrip 1 +#tolerance 1 mm + +accept 135 0 +expect 11825542.552198234946 11825542.417788611725 +roundtrip 1 + +accept -135 0 +expect -11825542.417788611725 11825542.362114325166 +roundtrip 1 + +accept 179.99 0 +expect 1574.295465656175 11825542.417788611725 +#tolerance 200 mm +#roundtrip 1 +#tolerance 1 mm + +accept -179.99 0 +expect -1574.295465656175 11825542.417788611725 +#tolerance 30 mm +#roundtrip 1 +#tolerance 1 mm + +accept 45 45 +expect 3747362.066324858926 -3747362.066324859392 +roundtrip 1 + +accept -45 45 +expect -3747362.066324857529 -3747362.066324857995 +roundtrip 1 + +accept 90 45 +expect 5268454.937608345412 0.000000000000 +#roundtrip 1 + +accept -90 45 +expect -5268454.937608350068 0.000000000000 +#roundtrip 1 + +accept 135 45 +expect 3747362.066324858926 3747362.066324857995 +roundtrip 1 + +accept -135 45 +expect -3747362.066324857529 3747362.066324857529 +roundtrip 1 + +accept 179.99 45 +expect 908.919898338959 5268454.862826444209 +roundtrip 1 + +accept -179.99 45 +expect -908.919898338959 5268454.862826446071 +roundtrip 1 + +accept 45 -45 +expect 19903722.533793020993 -3747362.066324859392 +roundtrip 1 + +accept -45 -45 +expect -3747362.066324857529 -19903722.533793020993 +roundtrip 1 + +accept 90 -45 +expect 18382629.662509534508 0.000000000000 +roundtrip 1 + +accept -90 -45 +expect -18382629.662509530783 0.000000000000 +#tolerance 3 mm +#roundtrip 1 +#tolerance 1 mm + +accept 135 -45 +expect 3747362.066324858926 19903722.533793020993 +roundtrip 1 + +accept -135 -45 +expect -19903722.533793020993 3747362.066324857529 +roundtrip 1 + +accept 179.99 -45 +expect 908.919898338959 18382629.737291436642 +#roundtrip 1 + +accept -179.99 -45 +expect -908.919898338959 18382629.737291432917 +roundtrip 1 + +accept 45 -89.999 +expect 23651005.885342493653 -78.714775386137 +#roundtrip 1 + +accept -45 -89.999 +expect -78.714775386137 -23651005.885342493653 +#roundtrip 1 + +accept 90 -89.999 +expect 23650973.280614964664 0.000000000000 +#roundtrip 1 + +accept -90 -89.999 +expect -23650973.280614964664 0.000000000000 +#roundtrip 1 + +accept 135 -89.999 +expect 78.714775386137 23651005.885342493653 +#roundtrip 1 + +accept -135 -89.999 +expect -23651005.885342493653 78.714775386137 +#roundtrip 1 + +accept 179.99 -89.999 +expect 0.000000000000 23650973.280614964664 +#roundtrip 1 + +accept -179.99 -89.999 +expect 0.000000000000 23650973.280614964664 +#roundtrip 1 + + diff --git a/test/ProjNet.Tests/Fixtures/gie/proj-generated/edge-cases.gie b/test/ProjNet.Tests/Fixtures/gie/proj-generated/edge-cases.gie new file mode 100644 index 00000000..d61ead8d --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/proj-generated/edge-cases.gie @@ -0,0 +1,324 @@ +------------------------------------------------------------------------------- +Edge-case reference vectors generated by PROJ 9.9.0 via cct.exe. +Covers low-coverage projections at boundary coordinates. +Generator: cct -d 10 (forward/inverse). +------------------------------------------------------------------------------- + + + +------------------------------------------------------------------------------- +Orthographic — +proj=ortho +R=6370997 +lat_0=45 +lon_0=10 +Points on the back hemisphere are excluded (out of domain). +------------------------------------------------------------------------------- + +operation +proj=ortho +R=6370997 +lat_0=45 +lon_0=10 +tolerance 1 mm + +accept 0 0 +expect -1106312.0189714802 -4436534.4859861191 + +accept 10 5 +expect 0.0000000000 -4095197.9329501125 + +accept 10 45 +expect 0.0000000000 0.0000000000 + +accept -73.9857 40.7484 +expect -4799993.9049607944 2582979.5396105675 + +accept 0 85 +expect -96421.4457241206 4101162.9326121765 + +accept 179.5 45 +expect 820966.5195514496 6317655.5331626162 + +direction inverse + +accept -1106312.0189714802 -4436534.4859861191 +expect 0.0000000000 0.0000000000 + +accept 0.0000000000 -4095197.9329501125 +expect 10.0000000000 5.0000000000 + +accept 0.0000000000 0.0000000000 +expect 10.0000000000 45.0000000000 + +accept -4799993.9049607944 2582979.5396105675 +expect -73.9857000000 40.7484000000 + +accept -96421.4457241206 4101162.9326121765 +expect 0.0000000000 85.0000000000 + +accept 820966.5195514496 6317655.5331626162 +expect 179.5000000000 45.0000000000 + + + + + +------------------------------------------------------------------------------- +Lambert Azimuthal Equal-Area — +proj=laea +R=6370997 +lat_0=45 +lon_0=10 +All eight test coordinates are within domain. +------------------------------------------------------------------------------- + +operation +proj=laea +R=6370997 +lat_0=45 +lon_0=10 +tolerance 1 mm + +accept 0 0 +expect -1201249.0963815092 -4817251.3278043093 + +accept 10 5 +expect 0.0000000000 -4358018.6141348099 + +accept 10 45 +expect 0.0000000000 0.0000000000 + +accept -73.9857 40.7484 +expect -5510170.2127958462 2965140.6233482552 + +accept 0 85 +expect -102636.7695428270 4365523.7858231086 + +accept 0 -85 +expect -228452.1670666874 -11549199.4802993704 + +accept 179.5 45 +expect 1156191.9398504556 8897345.0588279236 + +accept -179.5 -45 +expect 12698231.6534334421 -746097.8393536973 + +direction inverse + +accept -1201249.0963815092 -4817251.3278043093 +expect 0.0000000000 0.0000000000 + +accept 0.0000000000 -4358018.6141348099 +expect 10.0000000000 5.0000000000 + +accept 0.0000000000 0.0000000000 +expect 10.0000000000 45.0000000000 + +accept -5510170.2127958462 2965140.6233482552 +expect -73.9857000000 40.7484000000 + +accept -102636.7695428270 4365523.7858231086 +expect 0.0000000000 85.0000000000 + +accept -228452.1670666874 -11549199.4802993704 +expect 0.0000000000 -85.0000000000 + +accept 1156191.9398504556 8897345.0588279236 +expect 179.5000000000 45.0000000000 + +accept 12698231.6534334421 -746097.8393536973 +expect -179.5000000000 -45.0000000000 + + + + + +------------------------------------------------------------------------------- +Quadrilateralized Spherical Cube — +proj=qsc +R=6370997 +lat_0=0 +lon_0=0 +All eight test coordinates are within domain. +------------------------------------------------------------------------------- + +operation +proj=qsc +R=6370997 +lat_0=0 +lon_0=0 +tolerance 1 mm + +accept 0 0 +expect 0.0000000000 0.0000000000 + +accept 10 5 +expect 1517347.4144793469 828323.6755029238 + +accept 10 45 +expect 1244074.5233314342 6429188.2055819593 + +accept -73.9857 40.7484 +expect -8928365.0074037258 8160510.0489384755 + +accept 0 85 +expect 0.0000000007 11247372.6896084920 + +accept 0 -85 +expect -0.0000000021 -11247372.6896084920 + +accept 179.5 45 +expect 150157.7874660831 15380472.6238484345 + +accept -179.5 -45 +expect -150157.7874660850 -15380472.6238484345 + +direction inverse + +accept 0.0000000000 0.0000000000 +expect 0.0000000000 0.0000000000 + +accept 1517347.4144793469 828323.6755029238 +expect 10.0000000000 5.0000000000 + +accept 1244074.5233314342 6429188.2055819593 +expect 10.0000000000 45.0000000000 + +accept -8928365.0074037258 8160510.0489384755 +expect -73.9857000000 40.7484000000 + +accept 0.0000000007 11247372.6896084920 +expect 0.0000000000 85.0000000000 + +accept -0.0000000021 -11247372.6896084920 +expect 0.0000000000 -85.0000000000 + +accept 150157.7874660831 15380472.6238484345 +expect 179.5000000000 45.0000000000 + +accept -150157.7874660850 -15380472.6238484345 +expect -179.5000000000 -45.0000000000 + + + + + +------------------------------------------------------------------------------- +HEALPix — +proj=healpix +R=6370997 +All eight test coordinates are within domain. +------------------------------------------------------------------------------- + +operation +proj=healpix +R=6370997 +tolerance 1 mm + +accept 0 0 +expect 0.0000000000 0.0000000000 + +accept 10 5 +expect 1111948.7428468117 654160.8504159357 + +accept 10 45 +expect 1355657.8867869137 5317109.6707336409 + +accept -73.9857 40.7484 +expect -8226830.6103641354 4899230.0708290245 + +accept 0 85 +expect 4469140.3967767190 9472909.7395873722 + +accept 0 -85 +expect 4469140.3967767190 -9472909.7395873722 + +accept 179.5 45 +expect 19649621.1653764248 5317109.6707336409 + +accept -179.5 -45 +expect -19649621.1653764248 -5317109.6707336409 + +direction inverse + +accept 0.0000000000 0.0000000000 +expect 0.0000000000 0.0000000000 + +accept 1111948.7428468117 654160.8504159357 +expect 10.0000000000 5.0000000000 + +accept 1355657.8867869137 5317109.6707336409 +expect 10.0000000000 45.0000000000 + +accept -8226830.6103641354 4899230.0708290245 +expect -73.9857000000 40.7484000000 + +accept 4469140.3967767190 9472909.7395873722 +expect 0.0000000000 85.0000000000 + +accept 4469140.3967767190 -9472909.7395873722 +expect 0.0000000000 -85.0000000000 + +accept 19649621.1653764248 5317109.6707336409 +expect 179.5000000000 45.0000000000 + +accept -19649621.1653764248 -5317109.6707336409 +expect -179.5000000000 -45.0000000000 + + + + + +------------------------------------------------------------------------------- +Adams World in a Square I — +proj=adams_ws1 +R=6370997 +Forward only (PROJ does not provide an inverse for this projection). +------------------------------------------------------------------------------- + +operation +proj=adams_ws1 +R=6370997 +tolerance 1 mm + +accept 0 0 +expect 0.0000000000 0.0000000000 + +accept 10 5 +expect 556061.8511854805 278826.4440874406 + +accept 10 45 +expect 529573.8817805215 2767666.3220736105 + +accept -73.9857 40.7484 +expect -4098106.6219961834 2714845.1465957672 + +accept 0 85 +expect 0.0000000000 8195153.0458618123 + +accept 0 -85 +expect 0.0000000000 -8195153.0458618123 + +accept 179.5 45 +expect 11768154.5674999636 4118186.5674702218 + +accept -179.5 -45 +expect -11768154.5675001685 -4118186.5674702218 + + + + + +------------------------------------------------------------------------------- +Datum shift: DHDN (Potsdam) → WGS 84 via 7-parameter Helmert transform. +towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 +Forward: WGS 84 accept → DHDN expect. +Inverse: DHDN accept → WGS 84 expect. +Generated with cct pipeline +proj=cart +ellps=bessel +proj=helmert ... +inv +proj=cart +ellps=WGS84. +------------------------------------------------------------------------------- + +operation proj=latlong datum=potsdam ellps=bessel +tolerance 1 mm + +accept 13.403257186385 52.518593017976 +expect 13.4050 52.5200 + +accept 7.437800097995 46.950297137962 +expect 7.4386 46.9511 + +accept 9.992470035441 53.549550587589 +expect 9.9937 53.5511 + +accept 11.580611162224 48.134179058334 +expect 11.582 48.1351 + +accept 6.959557791969 50.936241326236 +expect 6.9603 50.9375 + +direction inverse + +accept 13.4050 52.5200 +expect 13.403257186385 52.518593017976 + +accept 7.4386 46.9511 +expect 7.437800097995 46.950297137962 + +accept 9.9937 53.5511 +expect 9.992470035441 53.549550587589 + +accept 11.582 48.1351 +expect 11.580611162224 48.134179058334 + +accept 6.9603 50.9375 +expect 6.959557791969 50.936241326236 + + diff --git a/test/ProjNet.Tests/Fixtures/gie/spilhaus.gie b/test/ProjNet.Tests/Fixtures/gie/spilhaus.gie new file mode 100644 index 00000000..5c2c4394 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/spilhaus.gie @@ -0,0 +1,449 @@ + + +------------------------------------------------------------ +# Roundtrips over the whole globe +------------------------------------------------------------ +operation +proj=spilhaus +tolerance 1.5 mm + +accept -20.1 74.1 +roundtrip 10 + +tolerance 10 mm + +accept -170.0000 -80.0000 +roundtrip 10 + +accept -170.0000 -50.0000 +roundtrip 10 + +accept -170.0000 -20.0000 +roundtrip 10 + +accept -170.0000 10.0000 +roundtrip 10 + +accept -170.0000 40.0000 +roundtrip 10 + +accept -170.0000 70.0000 +roundtrip 10 + +accept -121.0000 -80.0000 +roundtrip 10 + +accept -121.0000 -50.0000 +roundtrip 10 + +accept -121.0000 -20.0000 +roundtrip 10 + +accept -121.0000 10.0000 +roundtrip 10 + +accept -121.0000 40.0000 +roundtrip 10 + +accept -121.0000 70.0000 +roundtrip 10 + +accept -72.0000 -80.0000 +roundtrip 10 + +accept -72.0000 -50.0000 +roundtrip 10 + +accept -72.0000 -20.0000 +roundtrip 10 + +accept -72.0000 10.0000 +roundtrip 10 + +accept -72.0000 40.0000 +roundtrip 10 + +accept -72.0000 70.0000 +roundtrip 10 + +accept -23.0000 -80.0000 +roundtrip 10 + +accept -23.0000 -50.0000 +roundtrip 10 + +accept -23.0000 -20.0000 +roundtrip 10 + +accept -23.0000 10.0000 +roundtrip 10 + +accept -23.0000 40.0000 +roundtrip 10 + +accept -23.0000 70.0000 +roundtrip 10 + +accept 26.0000 -80.0000 +roundtrip 10 + +accept 26.0000 -50.0000 +roundtrip 10 + +accept 26.0000 -20.0000 +roundtrip 10 + +accept 26.0000 10.0000 +roundtrip 10 + +accept 26.0000 40.0000 +roundtrip 10 + +accept 26.0000 70.0000 +roundtrip 10 + +accept 75.0000 -80.0000 +roundtrip 10 + +accept 75.0000 -50.0000 +roundtrip 10 + +accept 75.0000 -20.0000 +roundtrip 10 + +accept 75.0000 10.0000 +roundtrip 10 + +accept 75.0000 40.0000 +roundtrip 10 + +accept 75.0000 70.0000 +roundtrip 10 + +accept 124.0000 -80.0000 +roundtrip 10 + +accept 124.0000 -50.0000 +roundtrip 10 + +accept 124.0000 -20.0000 +roundtrip 10 + +accept 124.0000 10.0000 +roundtrip 10 + +accept 124.0000 40.0000 +roundtrip 10 + +accept 124.0000 70.0000 +roundtrip 10 + +accept 173.0000 -80.0000 +roundtrip 10 + +accept 173.0000 -50.0000 +roundtrip 10 + +accept 173.0000 -20.0000 +roundtrip 10 + +accept 173.0000 10.0000 +roundtrip 10 + +accept 173.0000 40.0000 +roundtrip 10 + +accept 173.0000 70.0000 +roundtrip 10 + +------------------------------------------------------------ +# This gie part was initially generated with a python library +# provided in the issue #1851, correcting the applying a factor +# due to the conformal latitude (explained in the issue). +# It can be edited. +------------------------------------------------------------ +operation +proj=spilhaus +tolerance 1 mm +------------------------------------------------------------ +accept -170.0000 -80.0000 +expect 437478.9752 -2678050.3019 + +accept -170.0000 -50.0000 +expect 2186914.6725 -3372185.4149 + +accept -170.0000 -20.0000 +expect 4059707.8321 -3830282.4180 + +accept -170.0000 10.0000 +expect 6210065.9010 -4208321.1110 + +accept -170.0000 40.0000 +expect 8929858.8196 -4592610.8117 + +accept -170.0000 70.0000 +expect -4757306.7162 11243170.7712 + +accept -121.0000 -80.0000 +expect 9363.8168 -3012575.4761 + +accept -121.0000 -50.0000 +expect 861573.2313 -5086159.8537 + +accept -121.0000 -20.0000 +expect 2601135.1803 -6940740.0711 + +accept -121.0000 10.0000 +expect 5016978.6482 -8304150.6170 + +accept -121.0000 40.0000 +expect 8632125.8964 -9423801.4294 + +accept -121.0000 70.0000 +expect -6555705.6060 10246990.6251 + +accept -72.0000 -80.0000 +expect -551816.0501 -2880353.7707 + +accept -72.0000 -50.0000 +expect -2313351.2791 -5115437.4974 + +accept -72.0000 -20.0000 +expect -1486391.8298 -11562191.5568 + +accept -72.0000 10.0000 +expect -10414594.4936 2712423.6966 + +accept -72.0000 40.0000 +expect -9084246.6998 5766099.6436 + +accept -72.0000 70.0000 +expect -6598697.0383 8193941.7590 + +accept -23.0000 -80.0000 +expect -780311.9008 -2349988.7659 + +accept -23.0000 -50.0000 +expect -3008092.3663 -1795023.1904 + +accept -23.0000 -20.0000 +expect -4925434.4371 14366.0251 + +accept -23.0000 10.0000 +expect -5706896.4172 2337607.2418 + +accept -23.0000 40.0000 +expect -5776514.0258 4647657.5555 + +accept -23.0000 70.0000 +expect -5300606.7236 7143600.3698 + +accept 26.0000 -80.0000 +expect -524250.2015 -1871449.2525 + +accept 26.0000 -50.0000 +expect -1461547.3198 -290174.5921 + +accept 26.0000 -20.0000 +expect -2077751.7222 1349250.8096 + +accept 26.0000 10.0000 +expect -2553361.5158 3054667.1747 + +accept 26.0000 40.0000 +expect -3067668.6963 4947317.2297 + +accept 26.0000 70.0000 +expect -3829081.3051 7195256.6513 + +accept 75.0000 -80.0000 +expect -30320.7348 -1747777.5703 + +accept 75.0000 -50.0000 +expect 283794.2879 -63915.9519 + +accept 75.0000 -20.0000 +expect 570564.7027 1640770.3960 + +accept 75.0000 10.0000 +expect 613441.1718 3718159.4411 + +accept 75.0000 40.0000 +expect -339352.8544 6281442.7842 + +accept 75.0000 70.0000 +expect -2680263.1124 8214231.7220 + +accept 124.0000 -80.0000 +expect 391373.4223 -2010028.8376 + +accept 124.0000 -50.0000 +expect 1778858.7692 -974740.5396 + +accept 124.0000 -20.0000 +expect 3251942.8279 194105.3840 + +accept 124.0000 10.0000 +expect 5502810.9250 1869559.2284 + +accept 124.0000 40.0000 +expect 11560747.4458 1351438.2908 + +accept 124.0000 70.0000 +expect -2631686.3455 9979887.6971 + +accept 173.0000 -80.0000 +expect 497943.3567 -2503256.4284 + +accept 173.0000 -50.0000 +expect 2305199.5165 -2711772.3441 + +accept 173.0000 -20.0000 +expect 4178078.2257 -2771571.4407 + +accept 173.0000 10.0000 +expect 6367897.0128 -2860121.0611 + +accept 173.0000 40.0000 +expect 9145287.0568 -3248602.9252 + +accept 173.0000 70.0000 +expect -4081581.4885 11169069.7425 + +------------------------------------------------------------ +# This gie part was got from ESRI computations +# provided in the issue #1851 +# It can be edited. +------------------------------------------------------------ +operation +proj=spilhaus +k_0=1.4142135623730951 +tolerance 0.9 m +------------------------------------- +accept 14.47226253 -84.71287749 +expect -546875 -3046875 + +accept 84.55256518 -37.93882855 +expect 1171875 703125 + +accept -66.58783346 27.86168989 +expect -13046875 6171875 + +accept 12.77715082 51.22645041 +expect -5546875 7890625 + +accept 114.35091069 28.44647901 +expect 10703125 10234375 + +accept -58.76182587 -12.11844904 +expect -13046875 -703125 + +accept 141.57916998 16.05031911 +expect 9765625 78125 + +accept -64.1956924 -30.60226899 +expect -11796875 -10859375 + +accept -83.61985956 -31.09509756 +expect -1796875 -11796875 + +accept -118.96768373 14.44661994 +expect 7578125 -12265625 + +accept -145.850344 50.26449114 +expect 14296875 -9296875 + +accept -116.22716741 44.95066182 +expect 13515625 -14609375 + +accept -112.96622187 49.30990506 +expect 15859375 -16484375 + +accept 114.98472216 29.97596772 +expect 14453125 14453125 + +accept -112.21827126 50.16610427 +expect -15546875 15703125 + +accept -64.99929833 -30.00238885 +expect -15390625 -15546875 + +------------------------------------------------------------ +# Stable for default parameters +------------------------------------------------------------ +operation +proj=spilhaus +rot=45 +k_0=1 +lat_0=-49.56371678 +lon_0=66.94970198 +azi=40.17823482 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 3733410.0118 -9320.8573 +roundtrip 1 + +------------------------------------------------------------ +# Sentitive to input parameters +------------------------------------------------------------ +operation +proj=spilhaus +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 3733410.0118 -9320.8573 +roundtrip 1 +------------------------------------------------------------ +operation +proj=spilhaus +lon_0=10.1 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 4343770.7991 -3701935.6242 +roundtrip 1 +------------------------------------------------------------ +operation +proj=spilhaus +lat_0=30.1 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 3637341.2895 -2571368.8666 +roundtrip 1 +------------------------------------------------------------ +operation +proj=spilhaus +azi=9.1 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 3061806.4542 -1678791.7428 +roundtrip 1 +------------------------------------------------------------ +operation +proj=spilhaus +rot=40.1 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 3720561.6630 309609.603620 +roundtrip 1 +------------------------------------------------------------ +operation +proj=spilhaus +k_0=0.9 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 3360069.0106 -8388.7716 +roundtrip 1 + +------------------------------------------------------------ +# Sphere +------------------------------------------------------------ +operation +proj=spilhaus +R=6378137 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 3737644.5177 -7049.7883 +roundtrip 1 + +------------------------------------------------------------ +# vs Adams WS2 +------------------------------------------------------------ +operation +proj=adams_ws2 +R=6378137 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 8199312.0391 -1392652.9172 +roundtrip 1 +------------------------------------------------------------ +operation +proj=spilhaus +R=6378137 +lon_0=0 +lat_0=0 +azi=0 +rot=0 +tolerance 1 mm +------------------------------------------------------------ +accept 130.4 -16.2 +expect 8199312.0391 -1392652.9172 +roundtrip 1 + + diff --git a/test/ProjNet.Tests/Fixtures/gie/tinshift.gie b/test/ProjNet.Tests/Fixtures/gie/tinshift.gie new file mode 100644 index 00000000..85009537 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/tinshift.gie @@ -0,0 +1,62 @@ + +------------------------------------------------------------------------------- +=============================================================================== +Test +proj=tinshift +=============================================================================== + + + +# Missing +file +operation +proj=tinshift +expect failure errno invalid_op_missing_arg + +# +file doesn't point to an existing file +operation +proj=tinshift +file=i_do_not_exist +expect failure errno invalid_op_file_not_found_or_invalid + +# Not a JSON file +operation +proj=tinshift +file=proj.ini +expect failure errno invalid_op_file_not_found_or_invalid + + +# Tests on a file without explicit CRS +operation +proj=tinshift +file=tests/tinshift_crs_implicit.json +accept 2 49 +expect 2.1 49.1 +roundtrip 1 + +accept 0 0 +expect failure + +direction inverse +accept 0 0 +expect failure + + +# Tests on a file with explicit CRS +operation +proj=tinshift +file=tests/tinshift_simplified_kkj_etrs.json +tolerance 0.1 mm +# Verified with https://kartta.paikkatietoikkuna.fi/?lang=en with EPSG:2393 to EPSG:3067 +accept 3210000.0000 6700000.0000 +expect 209948.3217 6697187.0009 +roundtrip 1 + +operation +proj=tinshift +file=tests/tinshift_simplified_n60_n2000.json +tolerance 0.1 mm +accept 3210000.0000 6700000.0000 10.0 +expect 3210000.0000 6700000.0000 10.2886 +roundtrip 1 + +# Test fallback strategy nearest_side +operation +proj=tinshift +file=tests/tinshift_fallback_nearest_side.json +accept 2 3 +expect 4 6 +roundtrip 1 + +# Test fallback strategy nearest_centroid +operation +proj=tinshift +file=tests/tinshift_fallback_nearest_centroid.json +accept 3 0 +expect 3 0 +roundtrip 1 + + diff --git a/test/ProjNet.Tests/Fixtures/gie/unitconvert.gie b/test/ProjNet.Tests/Fixtures/gie/unitconvert.gie new file mode 100644 index 00000000..9c7a9e4a --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gie/unitconvert.gie @@ -0,0 +1,70 @@ +------------------------------------------------------------------------------- + Tests for the unitconvert operation +------------------------------------------------------------------------------- + + + +operation proj=unitconvert xy_in=m xy_out=dm z_in=cm z_out=mm +tolerance 0.1 +accept 55.25 23.23 45.5 +expect 552.5 232.3 455.0 + +operation proj=unitconvert +xy_in=m +xy_out=m +z_in=m +z_out=m +tolerance 0.1 +accept 12.3 45.6 7.89 +expect 12.3 45.6 7.89 + +operation proj=unitconvert xy_in=dm xy_out=dm +tolerance 0.1 +accept 1 1 1 1 +expect 1 1 1 1 + + +operation proj=unitconvert xy_in=2.0 xy_out=4.0 +tolerance 0.1 +accept 1 1 1 1 +expect 0.5 0.5 1 1 + +operation proj=unitconvert xy_in=deg xy_out=rad +tolerance 0.0000001 +accept 1 1 1 1 +expect 1 1 1 1 # gie does a rad->deg conversion behind the scenes + +operation proj=unitconvert xy_in=grad xy_out=deg +tolerance 0.000000000001 +accept 50 50 1 1 +expect 45 45 1 1 + +operation proj=unitconvert xy_in=m xy_out=rad +accept 1 1 1 1 +expect failure + +operation proj=unitconvert z_in=rad z_out=m +accept 1 1 1 1 +expect failure + +operation proj=unitconvert xy_in=0 +expect failure + +operation proj=unitconvert xy_out=0 +expect failure + +operation proj=unitconvert xy_in=1e400 +expect failure + +operation proj=unitconvert xy_out=1e400 +expect failure + +operation proj=unitconvert z_in=0 +expect failure + +operation proj=unitconvert z_out=0 +expect failure + +operation proj=unitconvert z_in=1e400 +expect failure + +operation proj=unitconvert z_out=1e400 +expect failure + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5101.1-jhs.gie b/test/ProjNet.Tests/Fixtures/gigs/5101.1-jhs.gie new file mode 100644 index 00000000..e3fcff6e --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5101.1-jhs.gie @@ -0,0 +1,734 @@ +-------------------------------------------------------------------------------- + +Test 5101 (part 1), Transverse Mercator, v2-0_2011-06-28, recommended JHS formula + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4326 +inv \ + +step +proj=etmerc +lat_0=49 +lon_0=-2 +k_0=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=WGS84 +units=m +no_def +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 3 80 +expect 496813.178 3358297.326 + +tolerance 0.03 m +accept 2.9999999 60 +expect 678711.584 1134498.83 + +tolerance 0.03 m +accept 3 49 +expect 765648.501 -87944.74 + +tolerance 0.03 m +accept 3.0000001 40 +expect 826893.845 -1087710.121 + +tolerance 0.03 m +accept 3 20 +expect 923539.353 -3308151.625 + +tolerance 0.03 m +accept 3 0 +expect 957087.829 -5527462.686 + +tolerance 0.03 m +accept 3 -20 +expect 923539.353 -7746773.748 + +tolerance 0.03 m +accept 3 -40 +expect 826893.845 -9967215.251 + +tolerance 0.03 m +accept 3 -60 +expect 678711.584 -12189424.202 + +tolerance 0.03 m +accept 3 -80 +expect 496813.178 -14413222.698 + +tolerance 0.03 m +accept -2 80 +expect 400000 3354134.429 + +tolerance 0.03 m +accept -2 60 +expect 400000 1123956.966 + +tolerance 0.03 m +accept -2 49 +expect 400000 -100000 + +tolerance 0.03 m +accept -2 40 +expect 400000 -1099699.834 + +tolerance 0.03 m +accept -2 20 +expect 400000 -3315978.565 + +tolerance 0.03 m +accept -2 0 +expect 400000 -5527462.686 + +tolerance 0.03 m +accept -2 -20 +expect 400000 -7738946.807 + +tolerance 0.03 m +accept -2 -40 +expect 400000 -9955225.538 + +tolerance 0.03 m +accept -2 -60 +expect 400000 -12178882.338 + +tolerance 0.03 m +accept -2 -80 +expect 400000 -14409059.801 + +tolerance 0.03 m +accept -5 80 +expect 341867.711 3355633.571 + +tolerance 0.03 m +accept -5 60 +expect 232704.966 1127751.264 + +tolerance 0.03 m +accept -5 49 +expect 180586.02 -95662.911 + +tolerance 0.03 m +accept -5 40 +expect 143900.026 -1095387.991 + +tolerance 0.03 m +accept -5 20 +expect 86073.28 -3313165.843 + +tolerance 0.03 m +accept -5 0 +expect 66021.018 -5527462.686 + +tolerance 0.03 m +accept -5 -20 +expect 86073.28 -7741759.529 + +tolerance 0.03 m +accept -5 -40 +expect 143900.026 -9959537.381 + +tolerance 0.03 m +accept -5 -60 +expect 232704.966 -12182676.637 + +tolerance 0.03 m +accept -5 -80 +expect 341867.711 -14410558.943 + +tolerance 0.03 m +accept -7.5559037 49.7661327 +expect 0 0 + +tolerance 0.03 m +accept -5 0 +expect 66021.018 -5527462.686 + +tolerance 0.03 m +accept -4 0 +expect 177404.277 -5527462.686 + +tolerance 0.03 m +accept -3 0 +expect 288719.208 -5527462.686 + +tolerance 0.03 m +accept -2 0 +expect 400000.0 -5527462.686 + +tolerance 0.03 m +accept -1 0 +expect 511280.792 -5527462.686 + +tolerance 0.03 m +accept 0 0 +expect 622595.723 -5527462.686 + +tolerance 0.03 m +accept 1 0 +expect 733978.982 -5527462.686 + +tolerance 0.03 m +accept 2 0 +expect 845464.865 -5527462.686 + +tolerance 0.03 m +accept 3 0 +expect 957087.829 -5527462.686 + +tolerance 0.03 m +accept 4 0 +expect 1068882.539 -5527462.686 + +tolerance 0.03 m +accept 5 0 +expect 1180883.933 -5527462.686 + +tolerance 0.03 m +accept 6 0 +expect 1293127.266 -5527462.686 + +tolerance 0.03 m +accept 7 0 +expect 1405648.179 -5527462.686 + +tolerance 0.03 m +accept 8 0 +expect 1518482.747 -5527462.686 + +tolerance 0.03 m +accept -5 60 +expect 232704.966 1127751.264 + +tolerance 0.03 m +accept -4 60 +expect 288455.816 1125643.213 + +tolerance 0.03 m +accept -3 60 +expect 344223.662 1124378.512 + +tolerance 0.03 m +accept -2 60 +expect 400000 1123956.966 + +tolerance 0.03 m +accept -1 60 +expect 455776.338 1124378.512 + +tolerance 0.03 m +accept 0 60 +expect 511544.184 1125643.213 + +tolerance 0.03 m +accept 1 60 +expect 567295.034 1127751.264 + +tolerance 0.03 m +accept 2 60 +expect 623020.357 1130702.987 + +tolerance 0.03 m +accept 3 60 +expect 678711.584 1134498.83 + +tolerance 0.03 m +accept 4.0 60.0 +expect 734360.093 1139139.367 + +tolerance 0.03 m +accept 5.0 60.0 +expect 789957.197 1144625.296 + +tolerance 0.03 m +accept 6.0 60.0 +expect 845494.132 1150957.434 + +tolerance 0.03 m +accept 7.0 60.0 +expect 900962.042 1158136.713 + +tolerance 0.03 m +accept 8.0 60.0 +expect 956351.967 1166164.18 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=etmerc +lat_0=49 +lon_0=-2 +k_0=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=WGS84 +units=m +no_def +inv \ + +step +init=epsg:4326 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 496813.178 3358297.326 +expect 3 80 + +tolerance 0.03 m +accept 678711.584 1134498.83 +expect 2.9999999 60 + +tolerance 0.03 m +accept 765648.501 -87944.74 +expect 3 49 + +tolerance 0.03 m +accept 826893.845 -1087710.121 +expect 3.0000001 40 + +tolerance 0.03 m +accept 923539.353 -3308151.625 +expect 3 20 + +tolerance 0.03 m +accept 957087.829 -5527462.686 +expect 3 0 + +tolerance 0.03 m +accept 923539.353 -7746773.748 +expect 3 -20 + +tolerance 0.03 m +accept 826893.845 -9967215.251 +expect 3 -40 + +tolerance 0.03 m +accept 678711.584 -12189424.202 +expect 3 -60 + +tolerance 0.03 m +accept 496813.178 -14413222.698 +expect 3 -80 + +tolerance 0.03 m +accept 400000 3354134.429 +expect -2 80 + +tolerance 0.03 m +accept 400000 1123956.966 +expect -2 60 + +tolerance 0.03 m +accept 400000 -100000 +expect -2 49 + +tolerance 0.03 m +accept 400000 -1099699.834 +expect -2 40 + +tolerance 0.03 m +accept 400000 -3315978.565 +expect -2 20 + +tolerance 0.03 m +accept 400000 -5527462.686 +expect -2 0 + +tolerance 0.03 m +accept 400000 -7738946.807 +expect -2 -20 + +tolerance 0.03 m +accept 400000 -9955225.538 +expect -2 -40 + +tolerance 0.03 m +accept 400000 -12178882.338 +expect -2 -60 + +tolerance 0.03 m +accept 400000 -14409059.801 +expect -2 -80 + +tolerance 0.03 m +accept 341867.711 3355633.571 +expect -5 80 + +tolerance 0.03 m +accept 232704.966 1127751.264 +expect -5 60 + +tolerance 0.03 m +accept 180586.02 -95662.911 +expect -5 49 + +tolerance 0.03 m +accept 143900.026 -1095387.991 +expect -5 40 + +tolerance 0.03 m +accept 86073.28 -3313165.843 +expect -5 20 + +tolerance 0.03 m +accept 66021.018 -5527462.686 +expect -5 0 + +tolerance 0.03 m +accept 86073.28 -7741759.529 +expect -5 -20 + +tolerance 0.03 m +accept 143900.026 -9959537.381 +expect -5 -40 + +tolerance 0.03 m +accept 232704.966 -12182676.637 +expect -5 -60 + +tolerance 0.03 m +accept 341867.711 -14410558.943 +expect -5 -80 + +tolerance 0.03 m +accept 0 0 +expect -7.5559037 49.7661327 + +tolerance 0.03 m +accept 66021.018 -5527462.686 +expect -5 0 + +tolerance 0.03 m +accept 177404.277 -5527462.686 +expect -4 0 + +tolerance 0.03 m +accept 288719.208 -5527462.686 +expect -3 0 + +tolerance 0.03 m +accept 400000.0 -5527462.686 +expect -2 0 + +tolerance 0.03 m +accept 511280.792 -5527462.686 +expect -1 0 + +tolerance 0.03 m +accept 622595.723 -5527462.686 +expect 0 0 + +tolerance 0.03 m +accept 733978.982 -5527462.686 +expect 1 0 + +tolerance 0.03 m +accept 845464.865 -5527462.686 +expect 2 0 + +tolerance 0.03 m +accept 957087.829 -5527462.686 +expect 3 0 + +tolerance 0.03 m +accept 1068882.539 -5527462.686 +expect 4 0 + +tolerance 0.03 m +accept 1180883.933 -5527462.686 +expect 5 0 + +tolerance 0.03 m +accept 1293127.266 -5527462.686 +expect 6 0 + +tolerance 0.03 m +accept 1405648.179 -5527462.686 +expect 7 0 + +tolerance 0.03 m +accept 1518482.747 -5527462.686 +expect 8 0 + +tolerance 0.03 m +accept 232704.966 1127751.264 +expect -5 60 + +tolerance 0.03 m +accept 288455.816 1125643.213 +expect -4 60 + +tolerance 0.03 m +accept 344223.662 1124378.512 +expect -3 60 + +tolerance 0.03 m +accept 400000 1123956.966 +expect -2 60 + +tolerance 0.03 m +accept 455776.338 1124378.512 +expect -1 60 + +tolerance 0.03 m +accept 511544.184 1125643.213 +expect 0 60 + +tolerance 0.03 m +accept 567295.034 1127751.264 +expect 1 60 + +tolerance 0.03 m +accept 623020.357 1130702.987 +expect 2 60 + +tolerance 0.03 m +accept 678711.584 1134498.83 +expect 3 60 + +tolerance 0.03 m +accept 734360.093 1139139.367 +expect 4.0 60.0 + +tolerance 0.03 m +accept 789957.197 1144625.296 +expect 5.0 60.0 + +tolerance 0.03 m +accept 845494.132 1150957.434 +expect 6.0 60.0 + +tolerance 0.03 m +accept 900962.042 1158136.713 +expect 7.0 60.0 + +tolerance 0.03 m +accept 956351.967 1166164.18 +expect 8.0 60.0 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4326 +inv \ + +step +proj=etmerc +lat_0=49 +lon_0=-2 +k_0=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=WGS84 +units=m +no_def +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 3 80 +roundtrip 1000 + +tolerance 0.006 m +accept 2.9999999 60 +roundtrip 1000 + +tolerance 0.006 m +accept 3 49 +roundtrip 1000 + +tolerance 0.006 m +accept 3.0000001 40 +roundtrip 1000 + +tolerance 0.006 m +accept 3 20 +roundtrip 1000 + +tolerance 0.006 m +accept 3 0 +roundtrip 1000 + +tolerance 0.006 m +accept 3 -20 +roundtrip 1000 + +tolerance 0.006 m +accept 3 -40 +roundtrip 1000 + +tolerance 0.006 m +accept 3 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 3 -80 +roundtrip 1000 + +tolerance 0.006 m +accept -2 80 +roundtrip 1000 + +tolerance 0.006 m +accept -2 60 +roundtrip 1000 + +tolerance 0.006 m +accept -2 49 +roundtrip 1000 + +tolerance 0.006 m +accept -2 40 +roundtrip 1000 + +tolerance 0.006 m +accept -2 20 +roundtrip 1000 + +tolerance 0.006 m +accept -2 0 +roundtrip 1000 + +tolerance 0.006 m +accept -2 -20 +roundtrip 1000 + +tolerance 0.006 m +accept -2 -40 +roundtrip 1000 + +tolerance 0.006 m +accept -2 -60 +roundtrip 1000 + +tolerance 0.006 m +accept -2 -80 +roundtrip 1000 + +tolerance 0.006 m +accept -5 80 +roundtrip 1000 + +tolerance 0.006 m +accept -5 60 +roundtrip 1000 + +tolerance 0.006 m +accept -5 49 +roundtrip 1000 + +tolerance 0.006 m +accept -5 40 +roundtrip 1000 + +tolerance 0.006 m +accept -5 20 +roundtrip 1000 + +tolerance 0.006 m +accept -5 0 +roundtrip 1000 + +tolerance 0.006 m +accept -5 -20 +roundtrip 1000 + +tolerance 0.006 m +accept -5 -40 +roundtrip 1000 + +tolerance 0.006 m +accept -5 -60 +roundtrip 1000 + +tolerance 0.006 m +accept -5 -80 +roundtrip 1000 + +tolerance 0.006 m +accept -7.5559037 49.7661327 +roundtrip 1000 + +tolerance 0.006 m +accept -5 0 +roundtrip 1000 + +tolerance 0.006 m +accept -4 0 +roundtrip 1000 + +tolerance 0.006 m +accept -3 0 +roundtrip 1000 + +tolerance 0.006 m +accept -2 0 +roundtrip 1000 + +tolerance 0.006 m +accept -1 0 +roundtrip 1000 + +tolerance 0.006 m +accept 0 0 +roundtrip 1000 + +tolerance 0.006 m +accept 1 0 +roundtrip 1000 + +tolerance 0.006 m +accept 2 0 +roundtrip 1000 + +tolerance 0.006 m +accept 3 0 +roundtrip 1000 + +tolerance 0.006 m +accept 4 0 +roundtrip 1000 + +tolerance 0.006 m +accept 5 0 +roundtrip 1000 + +tolerance 0.006 m +accept 6 0 +roundtrip 1000 + +tolerance 0.006 m +accept 7 0 +roundtrip 1000 + +tolerance 0.006 m +accept 8 0 +roundtrip 1000 + +tolerance 0.006 m +accept -5 60 +roundtrip 1000 + +tolerance 0.006 m +accept -4 60 +roundtrip 1000 + +tolerance 0.006 m +accept -3 60 +roundtrip 1000 + +tolerance 0.006 m +accept -2 60 +roundtrip 1000 + +tolerance 0.006 m +accept -1 60 +roundtrip 1000 + +tolerance 0.006 m +accept 0 60 +roundtrip 1000 + +tolerance 0.006 m +accept 1 60 +roundtrip 1000 + +tolerance 0.006 m +accept 2 60 +roundtrip 1000 + +tolerance 0.006 m +accept 3 60 +roundtrip 1000 + +tolerance 0.006 m +accept 4.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 5.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 6.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 7.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 8.0 60.0 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5101.2-jhs.gie b/test/ProjNet.Tests/Fixtures/gigs/5101.2-jhs.gie new file mode 100644 index 00000000..76c72b6f --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5101.2-jhs.gie @@ -0,0 +1,302 @@ +-------------------------------------------------------------------------------- + +Test 5101 (part 2), Transverse Mercator, v2-0_2011-06-28, recommended JHS formula + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4326 +inv \ + +step +init=epsg:32631 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept -2.0 80.0 +expect 403186.945 8885748.708 + +tolerance 0.03 m +accept -2.0 60.0 +expect 221288.77 6661953.041 + +tolerance 0.03 m +accept -2.0 40.0 +expect 73106.698 4439746.917 + +tolerance 0.03 m +accept -2.0 20.0 +expect -23538.687 2219308.238 + +tolerance 0.03 m +accept -2.0 0.0 +expect -57087.12 0.0 + +tolerance 0.03 m +accept -2.0 -20.0 +expect -23538.687 -2219308.238 + +tolerance 0.03 m +accept -2.0 -40.0 +expect 73106.698 -4439746.917 + +tolerance 0.03 m +accept -2.0 -60.0 +expect 221288.77 -6661953.041 + +tolerance 0.03 m +accept -2.0 -80.0 +expect 403186.945 -8885748.708 + +tolerance 0.03 m +accept -5.0 60.0 +expect 54506.435 6678411.623 + +tolerance 0.03 m +accept -4.0 60.0 +expect 110043.299 6672079.494 + +tolerance 0.03 m +accept -3.0 60.0 +expect 165640.332 6666593.572 + +tolerance 0.03 m +accept -2.0 60.0 +expect 221288.77 6661953.041 + +tolerance 0.03 m +accept -1.0 60.0 +expect 276979.926 6658157.202 + +tolerance 0.03 m +accept 0.0 60.0 +expect 332705.179 6655205.484 + +tolerance 0.03 m +accept 1.0 60.0 +expect 388455.958 6653097.435 + +tolerance 0.03 m +accept 2.0 60.0 +expect 444223.733 6651832.735 + +tolerance 0.03 m +accept 3.0 60.0 +expect 500000.0 6651411.19 + +tolerance 0.03 m +accept 4.0 60.0 +expect 555776.267 6651832.735 + +tolerance 0.03 m +accept 5.0 60.0 +expect 611544.042 6653097.435 + +tolerance 0.03 m +accept 6.0 60.0 +expect 667294.821 6655205.484 + +tolerance 0.03 m +accept 7.0 60.0 +expect 723020.074 6658157.202 + +tolerance 0.03 m +accept 8.0 60.0 +expect 778711.23 6661953.041 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:32631 +inv \ + +step +init=epsg:4326 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 403186.945 8885748.708 +expect -2.0 80.0 + +tolerance 0.03 m +accept 221288.77 6661953.041 +expect -2.0 60.0 + +tolerance 0.03 m +accept 73106.698 4439746.917 +expect -2.0 40.0 + +tolerance 0.03 m +accept -23538.687 2219308.238 +expect -2.0 20.0 + +tolerance 0.03 m +accept -57087.12 0.0 +expect -2.0 0.0 + +tolerance 0.03 m +accept -23538.687 -2219308.238 +expect -2.0 -20.0 + +tolerance 0.03 m +accept 73106.698 -4439746.917 +expect -2.0 -40.0 + +tolerance 0.03 m +accept 221288.77 -6661953.041 +expect -2.0 -60.0 + +tolerance 0.03 m +accept 403186.945 -8885748.708 +expect -2.0 -80.0 + +tolerance 0.03 m +accept 54506.435 6678411.623 +expect -5.0 60.0 + +tolerance 0.03 m +accept 110043.299 6672079.494 +expect -4.0 60.0 + +tolerance 0.03 m +accept 165640.332 6666593.572 +expect -3.0 60.0 + +tolerance 0.03 m +accept 221288.77 6661953.041 +expect -2.0 60.0 + +tolerance 0.03 m +accept 276979.926 6658157.202 +expect -1.0 60.0 + +tolerance 0.03 m +accept 332705.179 6655205.484 +expect 0.0 60.0 + +tolerance 0.03 m +accept 388455.958 6653097.435 +expect 1.0 60.0 + +tolerance 0.03 m +accept 444223.733 6651832.735 +expect 2.0 60.0 + +tolerance 0.03 m +accept 500000.0 6651411.19 +expect 3.0 60.0 + +tolerance 0.03 m +accept 555776.267 6651832.735 +expect 4.0 60.0 + +tolerance 0.03 m +accept 611544.042 6653097.435 +expect 5.0 60.0 + +tolerance 0.03 m +accept 667294.821 6655205.484 +expect 6.0 60.0 + +tolerance 0.03 m +accept 723020.074 6658157.202 +expect 7.0 60.0 + +tolerance 0.03 m +accept 778711.23 6661953.041 +expect 8.0 60.0 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4326 +inv \ + +step +init=epsg:32631 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept -2.0 80.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 40.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 20.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 0.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 -20.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 -40.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 -60.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 -80.0 +roundtrip 1000 + +tolerance 0.006 m +accept -5.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept -4.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept -3.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept -2.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept -1.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 0.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 1.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 2.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 3.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 4.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 5.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 6.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 7.0 60.0 +roundtrip 1000 + +tolerance 0.006 m +accept 8.0 60.0 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5101.3-jhs.gie b/test/ProjNet.Tests/Fixtures/gigs/5101.3-jhs.gie new file mode 100644 index 00000000..f3434597 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5101.3-jhs.gie @@ -0,0 +1,302 @@ +-------------------------------------------------------------------------------- + +Test 5101 (part 3), Transverse Mercator, v2-0_2011-06-28, recommended JHS formula + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4283 +inv \ + +step +init=epsg:28354 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 146 80 +expect 596813.055 18885748.71 + +tolerance 0.03 m +accept 146 60 +expect 778711.23 16661953.04 + +tolerance 0.03 m +accept 146 40 +expect 926893.302 14439746.92 + +tolerance 0.03 m +accept 146 20 +expect 1023538.687 12219308.24 + +tolerance 0.03 m +accept 146 0 +expect 1057087.12 10000000.0 + +tolerance 0.03 m +accept 146 -20 +expect 1023538.687 7780691.762 + +tolerance 0.03 m +accept 146 -40 +expect 926893.302 5560253.083 + +tolerance 0.03 m +accept 146 -60 +expect 778711.23 3338046.96 + +tolerance 0.03 m +accept 146 -80 +expect 596813.055 1114251.292 + +tolerance 0.03 m +accept 136 -60 +expect 221288.77 3338046.96 + +tolerance 0.03 m +accept 137 -60 +expect 276979.926 3341842.798 + +tolerance 0.03 m +accept 138 -60 +expect 332705.179 3344794.516 + +tolerance 0.03 m +accept 139 -60 +expect 388455.958 3346902.565 + +tolerance 0.03 m +accept 140 -60 +expect 444223.733 3348167.265 + +tolerance 0.03 m +accept 141 -60 +expect 500000.0 3348588.81 + +tolerance 0.03 m +accept 142 -60 +expect 555776.267 3348167.265 + +tolerance 0.03 m +accept 143 -60 +expect 611544.042 3346902.565 + +tolerance 0.03 m +accept 144 -60 +expect 667294.821 3344794.516 + +tolerance 0.03 m +accept 145 -60 +expect 723020.074 3341842.798 + +tolerance 0.03 m +accept 146 -60 +expect 778711.23 3338046.96 + +tolerance 0.03 m +accept 147 -60 +expect 834359.668 3333406.428 + +tolerance 0.03 m +accept 148 -60 +expect 889956.701 3327920.506 + +tolerance 0.03 m +accept 149 -60 +expect 945493.565 3321588.377 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:28354 +inv \ + +step +init=epsg:4283 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 596813.055 18885748.71 +expect 146 80 + +tolerance 0.03 m +accept 778711.23 16661953.04 +expect 146 60 + +tolerance 0.03 m +accept 926893.302 14439746.92 +expect 146 40 + +tolerance 0.03 m +accept 1023538.687 12219308.24 +expect 146 20 + +tolerance 0.03 m +accept 1057087.12 10000000.0 +expect 146 0 + +tolerance 0.03 m +accept 1023538.687 7780691.762 +expect 146 -20 + +tolerance 0.03 m +accept 926893.302 5560253.083 +expect 146 -40 + +tolerance 0.03 m +accept 778711.23 3338046.96 +expect 146 -60 + +tolerance 0.03 m +accept 596813.055 1114251.292 +expect 146 -80 + +tolerance 0.03 m +accept 221288.77 3338046.96 +expect 136 -60 + +tolerance 0.03 m +accept 276979.926 3341842.798 +expect 137 -60 + +tolerance 0.03 m +accept 332705.179 3344794.516 +expect 138 -60 + +tolerance 0.03 m +accept 388455.958 3346902.565 +expect 139 -60 + +tolerance 0.03 m +accept 444223.733 3348167.265 +expect 140 -60 + +tolerance 0.03 m +accept 500000.0 3348588.81 +expect 141 -60 + +tolerance 0.03 m +accept 555776.267 3348167.265 +expect 142 -60 + +tolerance 0.03 m +accept 611544.042 3346902.565 +expect 143 -60 + +tolerance 0.03 m +accept 667294.821 3344794.516 +expect 144 -60 + +tolerance 0.03 m +accept 723020.074 3341842.798 +expect 145 -60 + +tolerance 0.03 m +accept 778711.23 3338046.96 +expect 146 -60 + +tolerance 0.03 m +accept 834359.668 3333406.428 +expect 147 -60 + +tolerance 0.03 m +accept 889956.701 3327920.506 +expect 148 -60 + +tolerance 0.03 m +accept 945493.565 3321588.377 +expect 149 -60 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4283 +inv \ + +step +init=epsg:28354 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 146 80 +roundtrip 1000 + +tolerance 0.006 m +accept 146 60 +roundtrip 1000 + +tolerance 0.006 m +accept 146 40 +roundtrip 1000 + +tolerance 0.006 m +accept 146 20 +roundtrip 1000 + +tolerance 0.006 m +accept 146 0 +roundtrip 1000 + +tolerance 0.006 m +accept 146 -20 +roundtrip 1000 + +tolerance 0.006 m +accept 146 -40 +roundtrip 1000 + +tolerance 0.006 m +accept 146 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 146 -80 +roundtrip 1000 + +tolerance 0.006 m +accept 136 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 137 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 138 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 139 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 140 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 141 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 142 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 143 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 144 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 145 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 146 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 147 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 148 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 149 -60 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5101.4-jhs-etmerc.gie b/test/ProjNet.Tests/Fixtures/gigs/5101.4-jhs-etmerc.gie new file mode 100644 index 00000000..a50905d4 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5101.4-jhs-etmerc.gie @@ -0,0 +1,302 @@ +-------------------------------------------------------------------------------- + +Test 5101 (part 4), Transverse Mercator, v2-0_2011-06-28, recommended JHS formula + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4190 +inv \ + +step +proj=etmerc +lat_0=-90 +lon_0=-60 +k=1 +x_0=5500000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept -63.9993433 80.0002644 +expect 5422500.0 18889800.0 + +tolerance 0.03 m +accept -63.9998472 60.0001191 +expect 5276900.0 16662800.0 + +tolerance 0.03 m +accept -63.9997361 40.0003081 +expect 5158399.999 14439199.99 + +tolerance 0.03 m +accept -64.0004605 19.9996448 +expect 5081100.0 12219300.0 + +tolerance 0.03 m +accept -63.9996186 0.0003092 +expect 5054400.005 10002000.0 + +tolerance 0.03 m +accept -64.0004675 -19.9999283 +expect 5081100.017 7784599.993 + +tolerance 0.03 m +accept -63.9997001 -39.9996924 +expect 5158400.0 5564800.0 + +tolerance 0.03 m +accept -63.9998814 -60.0004008 +expect 5276899.994 3341099.995 + +tolerance 0.03 m +accept -63.9991006 -79.9996521 +expect 5422500.0 1114200.0 + +tolerance 0.03 m +accept -70.0002089 -40.000215 +expect 4645300.113 5524200.123 + +tolerance 0.03 m +accept -69.0001441 -40.0002935 +expect 4730900.0 5533400.0 + +tolerance 0.03 m +accept -67.9995333 -39.9996136 +expect 4816500.043 5541700.028 + +tolerance 0.03 m +accept -66.9998073 -39.9999313 +expect 4902000.0 5548900.0 + +tolerance 0.03 m +accept -65.9996522 -39.9995894 +expect 4987500.009 5555200.001 + +tolerance 0.03 m +accept -64.9992796 -40.000411 +expect 5073000.0 5560400.0 + +tolerance 0.03 m +accept -63.9997 -39.9996925 +expect 5158400.01 5564799.987 + +tolerance 0.03 m +accept -62.9999842 -40.0002087 +expect 5243800.0 5568100.0 + +tolerance 0.03 m +accept -62.0000778 -40.0001803 +expect 5329199.995 5570500.009 + +tolerance 0.03 m +accept -61.0000574 -39.9996182 +expect 5414600.0 5572000.0 + +tolerance 0.03 m +accept -60.0 -40.0003306 +expect 5500000.0 5572399.996 + +tolerance 0.03 m +accept -58.9999426 -39.9996182 +expect 5585400.0 5572000.0 + +tolerance 0.03 m +accept -57.9999222 -40.0001803 +expect 5670800.005 5570500.009 + +tolerance 0.03 m +accept -57.0000158 -40.0002087 +expect 5756200.0 5568100.0 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=etmerc +lat_0=-90 +lon_0=-60 +k=1 +x_0=5500000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +inv \ + +step +init=epsg:4190 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 5422500.0 18889800.0 +expect -63.9993433 80.0002644 + +tolerance 0.03 m +accept 5276900.0 16662800.0 +expect -63.9998472 60.0001191 + +tolerance 0.03 m +accept 5158399.999 14439199.99 +expect -63.9997361 40.0003081 + +tolerance 0.03 m +accept 5081100.0 12219300.0 +expect -64.0004605 19.9996448 + +tolerance 0.03 m +accept 5054400.005 10002000.0 +expect -63.9996186 0.0003092 + +tolerance 0.03 m +accept 5081100.017 7784599.993 +expect -64.0004675 -19.9999283 + +tolerance 0.03 m +accept 5158400.0 5564800.0 +expect -63.9997001 -39.9996924 + +tolerance 0.03 m +accept 5276899.994 3341099.995 +expect -63.9998814 -60.0004008 + +tolerance 0.03 m +accept 5422500.0 1114200.0 +expect -63.9991006 -79.9996521 + +tolerance 0.03 m +accept 4645300.113 5524200.123 +expect -70.0002089 -40.000215 + +tolerance 0.03 m +accept 4730900.0 5533400.0 +expect -69.0001441 -40.0002935 + +tolerance 0.03 m +accept 4816500.043 5541700.028 +expect -67.9995333 -39.9996136 + +tolerance 0.03 m +accept 4902000.0 5548900.0 +expect -66.9998073 -39.9999313 + +tolerance 0.03 m +accept 4987500.009 5555200.001 +expect -65.9996522 -39.9995894 + +tolerance 0.03 m +accept 5073000.0 5560400.0 +expect -64.9992796 -40.000411 + +tolerance 0.03 m +accept 5158400.01 5564799.987 +expect -63.9997 -39.9996925 + +tolerance 0.03 m +accept 5243800.0 5568100.0 +expect -62.9999842 -40.0002087 + +tolerance 0.03 m +accept 5329199.995 5570500.009 +expect -62.0000778 -40.0001803 + +tolerance 0.03 m +accept 5414600.0 5572000.0 +expect -61.0000574 -39.9996182 + +tolerance 0.03 m +accept 5500000.0 5572399.996 +expect -60.0 -40.0003306 + +tolerance 0.03 m +accept 5585400.0 5572000.0 +expect -58.9999426 -39.9996182 + +tolerance 0.03 m +accept 5670800.005 5570500.009 +expect -57.9999222 -40.0001803 + +tolerance 0.03 m +accept 5756200.0 5568100.0 +expect -57.0000158 -40.0002087 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4190 +inv \ + +step +proj=etmerc +lat_0=-90 +lon_0=-60 +k=1 +x_0=5500000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept -63.9993433 80.0002644 +roundtrip 1000 + +tolerance 0.006 m +accept -63.9998472 60.0001191 +roundtrip 1000 + +tolerance 0.006 m +accept -63.9997361 40.0003081 +roundtrip 1000 + +tolerance 0.006 m +accept -64.0004605 19.9996448 +roundtrip 1000 + +tolerance 0.006 m +accept -63.9996186 0.0003092 +roundtrip 1000 + +tolerance 0.006 m +accept -64.0004675 -19.9999283 +roundtrip 1000 + +tolerance 0.006 m +accept -63.9997001 -39.9996924 +roundtrip 1000 + +tolerance 0.006 m +accept -63.9998814 -60.0004008 +roundtrip 1000 + +tolerance 0.006 m +accept -63.9991006 -79.9996521 +roundtrip 1000 + +tolerance 0.006 m +accept -70.0002089 -40.000215 +roundtrip 1000 + +tolerance 0.006 m +accept -69.0001441 -40.0002935 +roundtrip 1000 + +tolerance 0.006 m +accept -67.9995333 -39.9996136 +roundtrip 1000 + +tolerance 0.006 m +accept -66.9998073 -39.9999313 +roundtrip 1000 + +tolerance 0.006 m +accept -65.9996522 -39.9995894 +roundtrip 1000 + +tolerance 0.006 m +accept -64.9992796 -40.000411 +roundtrip 1000 + +tolerance 0.006 m +accept -63.9997 -39.9996925 +roundtrip 1000 + +tolerance 0.006 m +accept -62.9999842 -40.0002087 +roundtrip 1000 + +tolerance 0.006 m +accept -62.0000778 -40.0001803 +roundtrip 1000 + +tolerance 0.006 m +accept -61.0000574 -39.9996182 +roundtrip 1000 + +tolerance 0.006 m +accept -60.0 -40.0003306 +roundtrip 1000 + +tolerance 0.006 m +accept -58.9999426 -39.9996182 +roundtrip 1000 + +tolerance 0.006 m +accept -57.9999222 -40.0001803 +roundtrip 1000 + +tolerance 0.006 m +accept -57.0000158 -40.0002087 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5102.1.gie b/test/ProjNet.Tests/Fixtures/gigs/5102.1.gie new file mode 100644 index 00000000..3d9de743 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5102.1.gie @@ -0,0 +1,254 @@ +-------------------------------------------------------------------------------- + +Test 5102, Lambert Conic Conformal (1SP), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4230 +inv \ + +step +init=epsg:2192 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 5 58 +expect 760722.92 3457368.68 + +tolerance 0.03 m +accept 5 57 +expect 764566.844 3343948.93 + +tolerance 0.03 m +accept 5 56 +expect 768396.683 3230944.812 + +tolerance 0.03 m +accept 5 55 +expect 772213.973 3118310.947 + +tolerance 0.03 m +accept 5 54 +expect 776020.189 3006003.839 + +tolerance 0.03 m +accept 5 53 +expect 779816.748 2893981.68 + +tolerance 0.03 m +accept 4 51 +expect 717027.292 2668695.784 + +tolerance 0.03 m +accept 4 50 +expect 719385.249 2557252.841 + +tolerance 0.03 m +accept 4 49 +expect 721740.43 2445941.161 + +tolerance 0.03 m +accept 4 46.8 +expect 726915.752 2201342.519 + +tolerance 0.03 m +accept 3 53 +expect 644764.905 2891124.195 + +tolerance 0.03 m +accept 4 53 +expect 712299.916 2892123.369 + +tolerance 0.03 m +accept 5 53 +expect 779816.748 2893981.68 + +tolerance 0.03 m +accept 6 53 +expect 847304.473 2896698.827 + +tolerance 0.03 m +accept 7 53 +expect 914752.168 2900274.371 + +tolerance 0.03 m +accept 8 53 +expect 982148.913 2904707.734 + +tolerance 0.03 m +accept 9 53 +expect 1049483.8 2909998.196 + +tolerance 0.03 m +accept 10 53 +expect 1116745.929 2916144.902 + +tolerance 0.03 m +accept 11 53 +expect 1183924.412 2923146.858 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:2192 +inv \ + +step +init=epsg:4230 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 760722.92 3457368.68 +expect 5 58 + +tolerance 0.03 m +accept 764566.844 3343948.93 +expect 5 57 + +tolerance 0.03 m +accept 768396.683 3230944.812 +expect 5 56 + +tolerance 0.03 m +accept 772213.973 3118310.947 +expect 5 55 + +tolerance 0.03 m +accept 776020.189 3006003.839 +expect 5 54 + +tolerance 0.03 m +accept 779816.748 2893981.68 +expect 5 53 + +tolerance 0.03 m +accept 717027.292 2668695.784 +expect 4 51 + +tolerance 0.03 m +accept 719385.249 2557252.841 +expect 4 50 + +tolerance 0.03 m +accept 721740.43 2445941.161 +expect 4 49 + +tolerance 0.03 m +accept 726915.752 2201342.519 +expect 4 46.8 + +tolerance 0.03 m +accept 644764.905 2891124.195 +expect 3 53 + +tolerance 0.03 m +accept 712299.916 2892123.369 +expect 4 53 + +tolerance 0.03 m +accept 779816.748 2893981.68 +expect 5 53 + +tolerance 0.03 m +accept 847304.473 2896698.827 +expect 6 53 + +tolerance 0.03 m +accept 914752.168 2900274.371 +expect 7 53 + +tolerance 0.03 m +accept 982148.913 2904707.734 +expect 8 53 + +tolerance 0.03 m +accept 1049483.8 2909998.196 +expect 9 53 + +tolerance 0.03 m +accept 1116745.929 2916144.902 +expect 10 53 + +tolerance 0.03 m +accept 1183924.412 2923146.858 +expect 11 53 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4230 +inv \ + +step +init=epsg:2192 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 5 58 +roundtrip 1000 + +tolerance 0.006 m +accept 5 57 +roundtrip 1000 + +tolerance 0.006 m +accept 5 56 +roundtrip 1000 + +tolerance 0.006 m +accept 5 55 +roundtrip 1000 + +tolerance 0.006 m +accept 5 54 +roundtrip 1000 + +tolerance 0.006 m +accept 5 53 +roundtrip 1000 + +tolerance 0.006 m +accept 4 51 +roundtrip 1000 + +tolerance 0.006 m +accept 4 50 +roundtrip 1000 + +tolerance 0.006 m +accept 4 49 +roundtrip 1000 + +tolerance 0.006 m +accept 4 46.8 +roundtrip 1000 + +tolerance 0.006 m +accept 3 53 +roundtrip 1000 + +tolerance 0.006 m +accept 4 53 +roundtrip 1000 + +tolerance 0.006 m +accept 5 53 +roundtrip 1000 + +tolerance 0.006 m +accept 6 53 +roundtrip 1000 + +tolerance 0.006 m +accept 7 53 +roundtrip 1000 + +tolerance 0.006 m +accept 8 53 +roundtrip 1000 + +tolerance 0.006 m +accept 9 53 +roundtrip 1000 + +tolerance 0.006 m +accept 10 53 +roundtrip 1000 + +tolerance 0.006 m +accept 11 53 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5102.2.gie b/test/ProjNet.Tests/Fixtures/gigs/5102.2.gie new file mode 100644 index 00000000..d41280e1 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5102.2.gie @@ -0,0 +1,266 @@ +-------------------------------------------------------------------------------- + +Test 5102 (part 2), Lambert Conic Conformal (1SP), v2-0_2011-06-28. +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +# We need to add this grad->rad step as +init=epsg:4807 assumes +# degrees (if front operation), or radians (if non-front), as this was the case +# in PROJ < 6 era +# Note: "cs2cs EPSG:4807 EPSG:27572" does the right job. +operation +proj=pipeline \ + +step +proj=unitconvert +xy_in=grad +xy_out=rad \ + +step +init=epsg:4807 +inv \ + +step +init=epsg:27572 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 2.9586342556 64.4444444444 +expect 760724.023 3457334.864 + +tolerance 0.03 m +accept 2.9586342556 63.3333333333 +expect 764567.882 3343917.044 + +tolerance 0.03 m +accept 2.9586342556 62.2222222222 +expect 768397.648 3230915.06 + +tolerance 0.03 m +accept 2.9586342556 61.1111111111 +expect 772214.859 3118283.535 + +tolerance 0.03 m +accept 2.9586342556 60 +expect 776020.989 3005978.979 + +tolerance 0.03 m +accept 2.9586342556 58.8888888889 +expect 779817.454 2893959.584 + +tolerance 0.03 m +accept 1.8475231444 56.6666666667 +expect 717027.602 2668679.866 + +tolerance 0.03 m +accept 1.8475231444 55.5555555556 +expect 719385.487 2557240.347 + +tolerance 0.03 m +accept 1.8475231444 54.4444444444 +expect 721740.59 2445932.319 + +tolerance 0.03 m +accept 1.8475231444 52 +expect 726915.726 2201342.51839 + +tolerance 0.03 m +accept 0.7364120333 58.8888888889 +expect 644765.081 2891102.088 + +tolerance 0.03 m +accept 1.8475231444 58.8888888889 +expect 712300.356 2892101.266 + +tolerance 0.03 m +accept 2.9586342556 58.8888888889 +expect 779817.454 2893959.584 + +tolerance 0.03 m +accept 4.0697453667 58.8888888889 +expect 847305.444 2896676.742 + +tolerance 0.03 m +accept 5.1808564778 58.8888888889 +expect 914753.403 2900252.301 + +tolerance 0.03 m +accept 6.2919675889 58.8888888889 +expect 982150.413 2904685.68 + +tolerance 0.03 m +accept 7.4030787 58.8888888889 +expect 1049485.565 2909976.163 + +tolerance 0.03 m +accept 8.5141898111 58.8888888889 +expect 1116747.958 2916122.894 + +tolerance 0.03 m +accept 9.6253009222 58.8888888889 +expect 1183926.705 2923124.876 + +-------------------------------------------------------------------------------- +# We need to add this rad->grad step as +init=epsg:4807 assumes +# degrees (if last operation), or radians (if non-last), as this was the case +# in PROJ < 6 era +operation +proj=pipeline \ + +step +init=epsg:27572 +inv \ + +step +init=epsg:4807 \ + +step +proj=unitconvert +xy_in=rad +xy_out=grad +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 760724.023 3457334.864 +expect 2.9586342556 64.4444444444 + +tolerance 0.03 m +accept 764567.882 3343917.044 +expect 2.9586342556 63.3333333333 + +tolerance 0.03 m +accept 768397.648 3230915.06 +expect 2.9586342556 62.2222222222 + +tolerance 0.03 m +accept 772214.859 3118283.535 +expect 2.9586342556 61.1111111111 + +tolerance 0.03 m +accept 776020.989 3005978.979 +expect 2.9586342556 60 + +tolerance 0.03 m +accept 779817.454 2893959.584 +expect 2.9586342556 58.8888888889 + +tolerance 0.03 m +accept 717027.602 2668679.866 +expect 1.8475231444 56.6666666667 + +tolerance 0.03 m +accept 719385.487 2557240.347 +expect 1.8475231444 55.5555555556 + +tolerance 0.03 m +accept 721740.59 2445932.319 +expect 1.8475231444 54.4444444444 + +tolerance 0.03 m +accept 726915.726 2201342.51839 +expect 1.8475231444 52 + +tolerance 0.03 m +accept 644765.081 2891102.088 +expect 0.7364120333 58.8888888889 + +tolerance 0.03 m +accept 712300.356 2892101.266 +expect 1.8475231444 58.8888888889 + +tolerance 0.03 m +accept 779817.454 2893959.584 +expect 2.9586342556 58.8888888889 + +tolerance 0.03 m +accept 847305.444 2896676.742 +expect 4.0697453667 58.8888888889 + +tolerance 0.03 m +accept 914753.403 2900252.301 +expect 5.1808564778 58.8888888889 + +tolerance 0.03 m +accept 982150.413 2904685.68 +expect 6.2919675889 58.8888888889 + +tolerance 0.03 m +accept 1049485.565 2909976.163 +expect 7.4030787 58.8888888889 + +tolerance 0.03 m +accept 1116747.958 2916122.894 +expect 8.5141898111 58.8888888889 + +tolerance 0.03 m +accept 1183926.705 2923124.876 +expect 9.6253009222 58.8888888889 + +-------------------------------------------------------------------------------- +# We need to add this grad->rad step as +init=epsg:4807 assumes +# degrees (if front operation), or radians (if non-front), as this was the case +# in PROJ < 6 era +operation +proj=pipeline \ + +step +proj=unitconvert +xy_in=grad +xy_out=rad \ + +step +init=epsg:4807 +inv \ + +step +init=epsg:27572 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 2.9586342556 64.4444444444 +roundtrip 1000 + +tolerance 0.006 m +accept 2.9586342556 63.3333333333 +roundtrip 1000 + +tolerance 0.006 m +accept 2.9586342556 62.2222222222 +roundtrip 1000 + +tolerance 0.006 m +accept 2.9586342556 61.1111111111 +roundtrip 1000 + +tolerance 0.006 m +accept 2.9586342556 60 +roundtrip 1000 + +tolerance 0.006 m +accept 2.9586342556 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 1.8475231444 56.6666666667 +roundtrip 1000 + +tolerance 0.006 m +accept 1.8475231444 55.5555555556 +roundtrip 1000 + +tolerance 0.006 m +accept 1.8475231444 54.4444444444 +roundtrip 1000 + +tolerance 0.006 m +accept 1.8475231444 52 +roundtrip 1000 + +tolerance 0.006 m +accept 0.7364120333 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 1.8475231444 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 2.9586342556 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 4.0697453667 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 5.1808564778 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 6.2919675889 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 7.4030787 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 8.5141898111 58.8888888889 +roundtrip 1000 + +tolerance 0.006 m +accept 9.6253009222 58.8888888889 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5103.1.gie b/test/ProjNet.Tests/Fixtures/gigs/5103.1.gie new file mode 100644 index 00000000..b194e2e0 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5103.1.gie @@ -0,0 +1,213 @@ +-------------------------------------------------------------------------------- + +Test 5103 (part 1), Lambert Conic Conformal (2SP), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4313 +inv \ + +step +init=epsg:31370 + +tolerance 30 mm +-------------------------------------------------------------------------------- +accept 5 58 +expect 187742.7 969521.653 + +accept 5 57 +expect 188698.877 857277.135 + +accept 5 56 +expect 189652.853 745291.184 + +accept 5 55 +expect 190604.967 633523.672 + +accept 5 54 +expect 191555.55 521935.9 + +accept 5 53 +expect 192504.921 410490.433 + +accept 5.3876389 52.1561606 +expect 219843.841 316827.604 + +accept 4 51 +expect 124202.936 187756.876 + +accept 4 50 +expect 123652.406 76521.628 + +accept 4 49 +expect 123101.889 -34711.068 + +accept 3.3137281 47.9752611 +expect 71254.553 -148236.592 + +accept 3 53 +expect 58108.966 411155.591 + +accept 4 53 +expect 125304.704 410370.504 + +accept 5 53 +expect 192504.921 410490.433 + +accept 6 53 +expect 259697.429 411515.356 + +accept 7 53 +expect 326870.04 413445.087 + +accept 8 53 +expect 394010.571 416279.276 + +accept 9 53 +expect 461106.844 420017.408 + +accept 10 53 +expect 528146.69 424658.807 + +accept 11 53 +expect 595117.95 430202.63 + + +-------------------------------------------------------------------------------- +operation proj=pipeline \ + step init=epsg:31370 inv \ + step init=epsg:4313 + +tolerance 30 mm +-------------------------------------------------------------------------------- +accept 187742.7 969521.653 +expect 5 58 + +accept 188698.877 857277.135 +expect 5 57 + +accept 189652.853 745291.184 +expect 5 56 + +accept 190604.967 633523.672 +expect 5 55 + +accept 191555.55 521935.9 +expect 5 54 + +accept 192504.921 410490.433 +expect 5 53 + +accept 219843.841 316827.604 +expect 5.3876389 52.1561606 + +accept 124202.936 187756.876 +expect 4 51 + +accept 123652.406 76521.628 +expect 4 50 + +accept 123101.889 -34711.068 +expect 4 49 + +accept 71254.553 -148236.592 +expect 3.3137281 47.9752611 + +accept 58108.966 411155.591 +expect 3 53 + +accept 125304.704 410370.504 +expect 4 53 + +accept 192504.921 410490.433 +expect 5 53 + +accept 259697.429 411515.356 +expect 6 53 + +accept 326870.04 413445.087 +expect 7 53 + +accept 394010.571 416279.276 +expect 8 53 + +accept 461106.844 420017.408 +expect 9 53 + +accept 528146.69 424658.807 +expect 10 53 + +accept 595117.95 430202.63 +expect 11 53 + +-------------------------------------------------------------------------------- +operation +proj=pipeline towgs84=0,0,0 \ # turn off dual datum shift + +step +init=epsg:4313 +inv \ + +step +init=epsg:31370 + +tolerance 6 mm +-------------------------------------------------------------------------------- +accept 5 58 +roundtrip 1000 + +accept 5 57 +roundtrip 1000 + +accept 5 56 +roundtrip 1000 + +accept 5 55 +roundtrip 1000 + +accept 5 54 +roundtrip 1000 + +accept 5 53 +roundtrip 1000 + +accept 5.3876389 52.1561606 +roundtrip 1000 + +accept 4 51 +roundtrip 1000 + +accept 4 50 +roundtrip 1000 + +accept 4 49 +roundtrip 1000 + +accept 3.3137281 47.9752611 +roundtrip 1000 + +accept 3 53 +roundtrip 1000 + +accept 4 53 +roundtrip 1000 + +accept 5 53 +roundtrip 1000 + +accept 6 53 +roundtrip 1000 + +accept 7 53 +roundtrip 1000 + +accept 8 53 +roundtrip 1000 + +accept 9 53 +roundtrip 1000 + +accept 10 53 +roundtrip 1000 + +accept 11 53 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5103.2.gie b/test/ProjNet.Tests/Fixtures/gigs/5103.2.gie new file mode 100644 index 00000000..4419a6cc --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5103.2.gie @@ -0,0 +1,146 @@ +-------------------------------------------------------------------------------- + +Test 5103 (part 2), Lambert Conic Conformal (2SP), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4152 +inv \ + +step +init=epsg:2921 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept -110 49 +expect 2003937.27 6452491.7 + +tolerance 0.03 m +accept -110 47 +expect 2016621.93 5717728.61 + +tolerance 0.03 m +accept -110 45 +expect 2029255.57 4985920.56 + +tolerance 0.03 m +accept -110 43 +expect 2041855.08 4256089.74 + +tolerance 0.03 m +accept -110 41 +expect 2054436.57 3527302.73 + +tolerance 0.03 m +accept -110 41 +expect 2054436.57 3527302.73 + +tolerance 0.03 m +accept -108 41 +expect 2606245.52 3543182.55 + +tolerance 0.03 m +accept -106 41 +expect 3157542.86 3571757.39 + +tolerance 0.03 m +accept -104 41 +expect 3708036.57 3613012.12 + +tolerance 0.03 m +accept -102 41 +expect 4257435.06 3666924.89 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:2921 +inv \ + +step +init=epsg:4152 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 2003937.27 6452491.7 +expect -110 49 + +tolerance 0.03 m +accept 2016621.93 5717728.61 +expect -110 47 + +tolerance 0.03 m +accept 2029255.57 4985920.56 +expect -110 45 + +tolerance 0.03 m +accept 2041855.08 4256089.74 +expect -110 43 + +tolerance 0.03 m +accept 2054436.57 3527302.73 +expect -110 41 + +tolerance 0.03 m +accept 2054436.57 3527302.73 +expect -110 41 + +tolerance 0.03 m +accept 2606245.52 3543182.55 +expect -108 41 + +tolerance 0.03 m +accept 3157542.86 3571757.39 +expect -106 41 + +tolerance 0.03 m +accept 3708036.57 3613012.12 +expect -104 41 + +tolerance 0.03 m +accept 4257435.06 3666924.89 +expect -102 41 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4152 +inv \ + +step +init=epsg:2921 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept -110 49 +roundtrip 1000 + +tolerance 0.006 m +accept -110 47 +roundtrip 1000 + +tolerance 0.006 m +accept -110 45 +roundtrip 1000 + +tolerance 0.006 m +accept -110 43 +roundtrip 1000 + +tolerance 0.006 m +accept -110 41 +roundtrip 1000 + +tolerance 0.006 m +accept -110 41 +roundtrip 1000 + +tolerance 0.006 m +accept -108 41 +roundtrip 1000 + +tolerance 0.006 m +accept -106 41 +roundtrip 1000 + +tolerance 0.006 m +accept -104 41 +roundtrip 1000 + +tolerance 0.006 m +accept -102 41 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5103.3.gie b/test/ProjNet.Tests/Fixtures/gigs/5103.3.gie new file mode 100644 index 00000000..e73ede45 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5103.3.gie @@ -0,0 +1,146 @@ +-------------------------------------------------------------------------------- + +Test 5103 (part 3), Lambert Conic Conformal (2SP), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4152 +inv \ + +step +init=epsg:3568 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept -110 49 +expect 2003933.27 6452478.8 + +tolerance 0.03 m +accept -110 47 +expect 2016617.9 5717717.18 + +tolerance 0.03 m +accept -110 45 +expect 2029251.51 4985910.59 + +tolerance 0.03 m +accept -110 43 +expect 2041851.0 4256081.23 + +tolerance 0.03 m +accept -110 41 +expect 2054432.46 3527295.67 + +tolerance 0.03 m +accept -110 41 +expect 2054432.46 3527295.67 + +tolerance 0.03 m +accept -108 41 +expect 2606240.3 3543175.46 + +tolerance 0.03 m +accept -106 41 +expect 3157536.54 3571750.25 + +tolerance 0.03 m +accept -104 41 +expect 3708029.16 3613004.9 + +tolerance 0.03 m +accept -102 41 +expect 4257426.54 3666917.56 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:3568 +inv \ + +step +init=epsg:4152 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 2003933.27 6452478.8 +expect -110 49 + +tolerance 0.03 m +accept 2016617.9 5717717.18 +expect -110 47 + +tolerance 0.03 m +accept 2029251.51 4985910.59 +expect -110 45 + +tolerance 0.03 m +accept 2041851.0 4256081.23 +expect -110 43 + +tolerance 0.03 m +accept 2054432.46 3527295.67 +expect -110 41 + +tolerance 0.03 m +accept 2054432.46 3527295.67 +expect -110 41 + +tolerance 0.03 m +accept 2606240.3 3543175.46 +expect -108 41 + +tolerance 0.03 m +accept 3157536.54 3571750.25 +expect -106 41 + +tolerance 0.03 m +accept 3708029.16 3613004.9 +expect -104 41 + +tolerance 0.03 m +accept 4257426.54 3666917.56 +expect -102 41 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4152 +inv \ + +step +init=epsg:3568 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept -110 49 +roundtrip 1000 + +tolerance 0.006 m +accept -110 47 +roundtrip 1000 + +tolerance 0.006 m +accept -110 45 +roundtrip 1000 + +tolerance 0.006 m +accept -110 43 +roundtrip 1000 + +tolerance 0.006 m +accept -110 41 +roundtrip 1000 + +tolerance 0.006 m +accept -110 41 +roundtrip 1000 + +tolerance 0.006 m +accept -108 41 +roundtrip 1000 + +tolerance 0.006 m +accept -106 41 +roundtrip 1000 + +tolerance 0.006 m +accept -104 41 +roundtrip 1000 + +tolerance 0.006 m +accept -102 41 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5104.gie b/test/ProjNet.Tests/Fixtures/gigs/5104.gie new file mode 100644 index 00000000..b2a4241d --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5104.gie @@ -0,0 +1,266 @@ +-------------------------------------------------------------------------------- + +Test 5104, Oblique stereographic, v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4289 +inv \ + +step +init=epsg:28992 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 5 58 +expect 132023.27 1114054.87 + +tolerance 0.05 m +accept 5 57 +expect 131405.466 1002468.081 + +tolerance 0.05 m +accept 5 56 +expect 130792.264 890981.281 + +tolerance 0.05 m +accept 5 55 +expect 130183.56 779577.7 + +tolerance 0.05 m +accept 5 54 +expect 129579.26 668240.58 + +tolerance 0.05 m +accept 5 53 +expect 128979.26 556953.19 + +tolerance 0.05 m +accept 5.38763888889 52.1561605556 +expect 155000 463000 + +tolerance 0.05 m +accept 4 51 +expect 57605.946 335312.662 + +tolerance 0.05 m +accept 4 50 +expect 55502.306 224086.514 + +tolerance 0.05 m +accept 4.0 49.0 +expect 53412.76 112842.73 + +tolerance 0.05 m +accept 3.31372805556 47.9752611111 +expect 0 0 + +tolerance 0.05 m +accept 3 53 +expect -5253.06 559535.55 + +tolerance 0.05 m +accept 4 53 +expect 61856.78 557779.12 + +tolerance 0.05 m +accept 5 53 +expect 128979.26 556953.19 + +tolerance 0.05 m +accept 6 53 +expect 196105.28 557057.74 + +tolerance 0.05 m +accept 7 53 +expect 263225.72 558092.77 + +tolerance 0.05 m +accept 8 53 +expect 330331.46 560058.31 + +tolerance 0.05 m +accept 9 53 +expect 397413.385 562954.436 + +tolerance 0.05 m +accept 10 53 +expect 464462.35 566781.24 + +tolerance 0.05 m +accept 11 53 +expect 531469.2 571538.84 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:28992 +inv \ + +step +init=epsg:4289 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 132023.27 1114054.87 +expect 5 58 + +tolerance 0.05 m +accept 131405.466 1002468.081 +expect 5 57 + +tolerance 0.05 m +accept 130792.264 890981.281 +expect 5 56 + +tolerance 0.05 m +accept 130183.56 779577.7 +expect 5 55 + +tolerance 0.05 m +accept 129579.26 668240.58 +expect 5 54 + +tolerance 0.05 m +accept 128979.26 556953.19 +expect 5 53 + +tolerance 0.05 m +accept 155000 463000 +expect 5.38763888889 52.1561605556 + +tolerance 0.05 m +accept 57605.946 335312.662 +expect 4 51 + +tolerance 0.05 m +accept 55502.306 224086.514 +expect 4 50 + +tolerance 0.05 m +accept 53412.76 112842.73 +expect 4.0 49.0 + +tolerance 0.05 m +accept 0 0 +expect 3.31372805556 47.9752611111 + +tolerance 0.05 m +accept -5253.06 559535.55 +expect 3 53 + +tolerance 0.05 m +accept 61856.78 557779.12 +expect 4 53 + +tolerance 0.05 m +accept 128979.26 556953.19 +expect 5 53 + +tolerance 0.05 m +accept 196105.28 557057.74 +expect 6 53 + +tolerance 0.05 m +accept 263225.72 558092.77 +expect 7 53 + +tolerance 0.05 m +accept 330331.46 560058.31 +expect 8 53 + +tolerance 0.05 m +accept 397413.385 562954.436 +expect 9 53 + +tolerance 0.05 m +accept 464462.35 566781.24 +expect 10 53 + +tolerance 0.05 m +accept 531469.2 571538.84 +expect 11 53 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4289 +inv \ + +step +init=epsg:28992 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 5 58 +roundtrip 1000 + +tolerance 0.006 m +accept 5 57 +roundtrip 1000 + +tolerance 0.006 m +accept 5 56 +roundtrip 1000 + +tolerance 0.006 m +accept 5 55 +roundtrip 1000 + +tolerance 0.006 m +accept 5 54 +roundtrip 1000 + +tolerance 0.006 m +accept 5 53 +roundtrip 1000 + +tolerance 0.006 m +accept 5.38763888889 52.1561605556 +roundtrip 1000 + +tolerance 0.006 m +accept 4 51 +roundtrip 1000 + +tolerance 0.006 m +accept 4 50 +roundtrip 1000 + +tolerance 0.006 m +accept 4.0 49.0 +roundtrip 1000 + +tolerance 0.006 m +accept 3.31372805556 47.9752611111 +roundtrip 1000 + +tolerance 0.006 m +accept 3 53 +roundtrip 1000 + +tolerance 0.006 m +accept 4 53 +roundtrip 1000 + +tolerance 0.006 m +accept 5 53 +roundtrip 1000 + +tolerance 0.006 m +accept 6 53 +roundtrip 1000 + +tolerance 0.006 m +accept 7 53 +roundtrip 1000 + +tolerance 0.006 m +accept 8 53 +roundtrip 1000 + +tolerance 0.006 m +accept 9 53 +roundtrip 1000 + +tolerance 0.006 m +accept 10 53 +roundtrip 1000 + +tolerance 0.006 m +accept 11 53 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5105.2.gie b/test/ProjNet.Tests/Fixtures/gigs/5105.2.gie new file mode 100644 index 00000000..d24c739d --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5105.2.gie @@ -0,0 +1,170 @@ +-------------------------------------------------------------------------------- + +Test 5105 (part 2), Oblique Mercator (variant B), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4237 +inv \ + +step +init=epsg:23700 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 16 48.5 +expect 424714.235 355124.6 + +tolerance 0.05 m +accept 17.2 48.0 +expect 512056.188 296756.716 + +tolerance 0.05 m +accept 17.5826505556 47.6361347222 +expect 539847.765 255701.086 + +tolerance 0.05 m +accept 19.0485716667 47.1443936111 +expect 650000 200000 + +tolerance 0.05 m +accept 19.2234294444 46.8756683333 +expect 663329.053 170142.318 + +tolerance 0.05 m +accept 20.1357405556 46.3703011111 +expect 733651.455 114532.099 + +tolerance 0.05 m +accept 21.4 45.7 +expect 833148.855 42191.482 + +tolerance 0.05 m +accept 22.3 49.3 +expect 886565.935 444656.613 + +tolerance 0.05 m +accept 21.2941986111 48.4899747222 +expect 815999.993 351999.998 + +tolerance 0.05 m +accept 19.2234294444 46.8756683333 +expect 663329.053 170142.318 + +tolerance 0.05 m +accept 17.6191536111 46.0687463889 +expect 539403.958 81440.103 + +tolerance 0.05 m +accept 16.36 45.5 +expect 439836.709 20816.456 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:23700 +inv \ + +step +init=epsg:4237 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 424714.235 355124.6 +expect 16 48.5 + +tolerance 0.05 m +accept 512056.188 296756.716 +expect 17.2 48.0 + +tolerance 0.05 m +accept 539847.765 255701.086 +expect 17.5826505556 47.6361347222 + +tolerance 0.05 m +accept 650000 200000 +expect 19.0485716667 47.1443936111 + +tolerance 0.05 m +accept 663329.053 170142.318 +expect 19.2234294444 46.8756683333 + +tolerance 0.05 m +accept 733651.455 114532.099 +expect 20.1357405556 46.3703011111 + +tolerance 0.05 m +accept 833148.855 42191.482 +expect 21.4 45.7 + +tolerance 0.05 m +accept 886565.935 444656.613 +expect 22.3 49.3 + +tolerance 0.05 m +accept 815999.993 351999.998 +expect 21.2941986111 48.4899747222 + +tolerance 0.05 m +accept 663329.053 170142.318 +expect 19.2234294444 46.8756683333 + +tolerance 0.05 m +accept 539403.958 81440.103 +expect 17.6191536111 46.0687463889 + +tolerance 0.05 m +accept 439836.709 20816.456 +expect 16.36 45.5 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4237 +inv \ + +step +init=epsg:23700 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 16 48.5 +roundtrip 1000 + +tolerance 0.006 m +accept 17.2 48.0 +roundtrip 1000 + +tolerance 0.006 m +accept 17.5826505556 47.6361347222 +roundtrip 1000 + +tolerance 0.006 m +accept 19.0485716667 47.1443936111 +roundtrip 1000 + +tolerance 0.006 m +accept 19.2234294444 46.8756683333 +roundtrip 1000 + +tolerance 0.006 m +accept 20.1357405556 46.3703011111 +roundtrip 1000 + +tolerance 0.006 m +accept 21.4 45.7 +roundtrip 1000 + +tolerance 0.006 m +accept 22.3 49.3 +roundtrip 1000 + +tolerance 0.006 m +accept 21.2941986111 48.4899747222 +roundtrip 1000 + +tolerance 0.006 m +accept 19.2234294444 46.8756683333 +roundtrip 1000 + +tolerance 0.006 m +accept 17.6191536111 46.0687463889 +roundtrip 1000 + +tolerance 0.006 m +accept 16.36 45.5 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5106.gie b/test/ProjNet.Tests/Fixtures/gigs/5106.gie new file mode 100644 index 00000000..fd23b109 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5106.gie @@ -0,0 +1,302 @@ +-------------------------------------------------------------------------------- + +Test 5106, Hotine Oblique Mercator (variant A), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4742 +inv \ + +step +init=epsg:3376 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 117 12 +expect 807919.144 1329535.334 + +tolerance 0.05 m +accept 117 10 +expect 808784.981 1107678.473 + +tolerance 0.05 m +accept 117 9 +expect 809334.177 996918.212 + +tolerance 0.05 m +accept 117 8 +expect 809939.302 886240.183 + +tolerance 0.05 m +accept 116.846552222 6.87845833333 +expect 793704.631 762081.047 + +tolerance 0.05 m +accept 117 6 +expect 811253.303 665041.265 + +tolerance 0.05 m +accept 117 5 +expect 811930.345 554475.627 + +tolerance 0.05 m +accept 117 4 +expect 812599.582 443902.706 + +tolerance 0.05 m +accept 115 4 +expect 590521.147 442890.861 + +tolerance 0.05 m +accept 117 3 +expect 813245.133 333300.13 + +tolerance 0.05 m +accept 117 2 +expect 813851.067 222645.511 + +tolerance 0.05 m +accept 117 1 +expect 814401.375 111916.452 + +tolerance 0.05 m +accept 109.685820833 -0.000173333333333 +expect 0 0 + +tolerance 0.05 m +accept 123 6 +expect 1475669.281 673118.573 + +tolerance 0.05 m +accept 122 6 +expect 1364854.862 671146.254 + +tolerance 0.05 m +accept 121 6 +expect 1254086.173 669446.249 + +tolerance 0.05 m +accept 120 6 +expect 1143352.598 668002.074 + +tolerance 0.05 m +accept 119 6 +expect 1032643.312 666797.354 + +tolerance 0.05 m +accept 118 6 +expect 921947.286 665815.815 + +tolerance 0.05 m +accept 117 6 +expect 811253.303 665041.265 + +tolerance 0.05 m +accept 116 6 +expect 700549.965 664457.586 + +tolerance 0.05 m +accept 115 6 +expect 589825.706 664048.715 + +tolerance 0.05 m +accept 114 6 +expect 479068.802 663798.63 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:3376 +inv \ + +step +init=epsg:4742 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 807919.144 1329535.334 +expect 117 12 + +tolerance 0.05 m +accept 808784.981 1107678.473 +expect 117 10 + +tolerance 0.05 m +accept 809334.177 996918.212 +expect 117 9 + +tolerance 0.05 m +accept 809939.302 886240.183 +expect 117 8 + +tolerance 0.05 m +accept 793704.631 762081.047 +expect 116.846552222 6.87845833333 + +tolerance 0.05 m +accept 811253.303 665041.265 +expect 117 6 + +tolerance 0.05 m +accept 811930.345 554475.627 +expect 117 5 + +tolerance 0.05 m +accept 812599.582 443902.706 +expect 117 4 + +tolerance 0.05 m +accept 590521.147 442890.861 +expect 115 4 + +tolerance 0.05 m +accept 813245.133 333300.13 +expect 117 3 + +tolerance 0.05 m +accept 813851.067 222645.511 +expect 117 2 + +tolerance 0.05 m +accept 814401.375 111916.452 +expect 117 1 + +tolerance 0.05 m +accept 0 0 +expect 109.685820833 -0.000173333333333 + +tolerance 0.05 m +accept 1475669.281 673118.573 +expect 123 6 + +tolerance 0.05 m +accept 1364854.862 671146.254 +expect 122 6 + +tolerance 0.05 m +accept 1254086.173 669446.249 +expect 121 6 + +tolerance 0.05 m +accept 1143352.598 668002.074 +expect 120 6 + +tolerance 0.05 m +accept 1032643.312 666797.354 +expect 119 6 + +tolerance 0.05 m +accept 921947.286 665815.815 +expect 118 6 + +tolerance 0.05 m +accept 811253.303 665041.265 +expect 117 6 + +tolerance 0.05 m +accept 700549.965 664457.586 +expect 116 6 + +tolerance 0.05 m +accept 589825.706 664048.715 +expect 115 6 + +tolerance 0.05 m +accept 479068.802 663798.63 +expect 114 6 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4742 +inv \ + +step +init=epsg:3376 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 117 12 +roundtrip 1000 + +tolerance 0.006 m +accept 117 10 +roundtrip 1000 + +tolerance 0.006 m +accept 117 9 +roundtrip 1000 + +tolerance 0.006 m +accept 117 8 +roundtrip 1000 + +tolerance 0.006 m +accept 116.846552222 6.87845833333 +roundtrip 1000 + +tolerance 0.006 m +accept 117 6 +roundtrip 1000 + +tolerance 0.006 m +accept 117 5 +roundtrip 1000 + +tolerance 0.006 m +accept 117 4 +roundtrip 1000 + +tolerance 0.006 m +accept 115 4 +roundtrip 1000 + +tolerance 0.006 m +accept 117 3 +roundtrip 1000 + +tolerance 0.006 m +accept 117 2 +roundtrip 1000 + +tolerance 0.006 m +accept 117 1 +roundtrip 1000 + +tolerance 0.006 m +accept 109.685820833 -0.000173333333333 +roundtrip 1000 + +tolerance 0.006 m +accept 123 6 +roundtrip 1000 + +tolerance 0.006 m +accept 122 6 +roundtrip 1000 + +tolerance 0.006 m +accept 121 6 +roundtrip 1000 + +tolerance 0.006 m +accept 120 6 +roundtrip 1000 + +tolerance 0.006 m +accept 119 6 +roundtrip 1000 + +tolerance 0.006 m +accept 118 6 +roundtrip 1000 + +tolerance 0.006 m +accept 117 6 +roundtrip 1000 + +tolerance 0.006 m +accept 116 6 +roundtrip 1000 + +tolerance 0.006 m +accept 115 6 +roundtrip 1000 + +tolerance 0.006 m +accept 114 6 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5107.gie b/test/ProjNet.Tests/Fixtures/gigs/5107.gie new file mode 100644 index 00000000..917b8016 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5107.gie @@ -0,0 +1,182 @@ +-------------------------------------------------------------------------------- + +Test 5107, American Polyconic, v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4674 +inv \ + +step +proj=poly +lat_0=0 +lon_0=-54 +x_0=5000000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept -54 0 +expect 5000000.0 10000000.0 + +tolerance 0.05 m +accept -45 6 +expect 5996378.70982 10671650.0559 + +tolerance 0.05 m +accept -45 0 +expect 6001875.41714 10000000.0 + +tolerance 0.05 m +accept -45 -6 +expect 5996378.70982 9328349.94408 + +tolerance 0.05 m +accept -41 -13 +expect 6409689.58688 8526306.26193 + +tolerance 0.05 m +accept -38 -20 +expect 6671808.91963 7707735.72988 + +tolerance 0.05 m +accept -37 -24 +expect 6725584.49173 7240461.99578 + +tolerance 0.05 m +accept -36 -30 +expect 6729619.73995 6543762.57644 + +tolerance 0.05 m +accept -57 -30 +expect 4710574.22344 6676097.81117 + +tolerance 0.05 m +accept -54 -29.3674766667 +expect 5000000.0 6750000.0 + +tolerance 0.05 m +accept -47 -27.5 +expect 5691318.14689 6937461.05067 + +tolerance 0.05 m +accept -37 -24 +expect 6725584.49173 7240461.99578 + +tolerance 0.05 m +accept -30 -22.5 +expect 7458947.70133 7313327.31691 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +proj=poly +lat_0=0 +lon_0=-54 +x_0=5000000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +inv \ + +step +init=epsg:4674 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 5000000.0 10000000.0 +expect -54 0 + +tolerance 0.05 m +accept 5996378.70982 10671650.0559 +expect -45 6 + +tolerance 0.05 m +accept 6001875.41714 10000000.0 +expect -45 0 + +tolerance 0.05 m +accept 5996378.70982 9328349.94408 +expect -45 -6 + +tolerance 0.05 m +accept 6409689.58688 8526306.26193 +expect -41 -13 + +tolerance 0.05 m +accept 6671808.91963 7707735.72988 +expect -38 -20 + +tolerance 0.05 m +accept 6725584.49173 7240461.99578 +expect -37 -24 + +tolerance 0.05 m +accept 6729619.73995 6543762.57644 +expect -36 -30 + +tolerance 0.05 m +accept 4710574.22344 6676097.81117 +expect -57 -30 + +tolerance 0.05 m +accept 5000000.0 6750000.0 +expect -54 -29.3674766667 + +tolerance 0.05 m +accept 5691318.14689 6937461.05067 +expect -47 -27.5 + +tolerance 0.05 m +accept 6725584.49173 7240461.99578 +expect -37 -24 + +tolerance 0.05 m +accept 7458947.70133 7313327.31691 +expect -30 -22.5 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4674 +inv \ + +step +proj=poly +lat_0=0 +lon_0=-54 +x_0=5000000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept -54 0 +roundtrip 1000 + +tolerance 0.006 m +accept -45 6 +roundtrip 1000 + +tolerance 0.006 m +accept -45 0 +roundtrip 1000 + +tolerance 0.006 m +accept -45 -6 +roundtrip 1000 + +tolerance 0.006 m +accept -41 -13 +roundtrip 1000 + +tolerance 0.006 m +accept -38 -20 +roundtrip 1000 + +tolerance 0.006 m +accept -37 -24 +roundtrip 1000 + +tolerance 0.006 m +accept -36 -30 +roundtrip 1000 + +tolerance 0.006 m +accept -57 -30 +roundtrip 1000 + +tolerance 0.006 m +accept -54 -29.3674766667 +roundtrip 1000 + +tolerance 0.006 m +accept -47 -27.5 +roundtrip 1000 + +tolerance 0.006 m +accept -37 -24 +roundtrip 1000 + +tolerance 0.006 m +accept -30 -22.5 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5108.gie b/test/ProjNet.Tests/Fixtures/gigs/5108.gie new file mode 100644 index 00000000..2abdfc9d --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5108.gie @@ -0,0 +1,236 @@ +-------------------------------------------------------------------------------- + +Test 5108, Cassini-Soldner, v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +# GDM2000 +# <4742> +proj=longlat +ellps=GRS80 <> + +# GDM2000 / Johor Grid +# <3377> +proj=cass +lat_0=2.121679744444445 +lon_0=103.4279362361111 +x_0=-14810.562 +y_0=8758.32 +ellps=GRS80 +units=m <> + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4742 +inv \ + +step +init=epsg:3377 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 106 10 +expect 267186.017 881108.902 + +tolerance 0.05 m +accept 106 9 +expect 268006.024 770398.186 + +tolerance 0.05 m +accept 106 8 +expect 268740.351 659692.254 + +tolerance 0.05 m +accept 106 7 +expect 269388.786 548990.588 + +tolerance 0.05 m +accept 106 6 +expect 269951.141 438292.666 + +tolerance 0.05 m +accept 106 5 +expect 270427.255 327597.962 + +tolerance 0.05 m +accept 106 4 +expect 270816.99 216905.945 + +tolerance 0.05 m +accept 106 3 +expect 271120.234 106216.081 + +tolerance 0.05 m +accept 103.561065778 2.0424676812 +expect 0 0 + +tolerance 0.05 m +accept 103.64025984 1.82776484381 +expect 8813.252 -23740.095 + +tolerance 0.05 m +accept 106 1 +expect 271466.923 -115159.332 + +tolerance 0.05 m +accept 109 5 +expect 603116.703 329668.599 + +tolerance 0.05 m +accept 108 5 +expect 492221.308 328807.336 + +tolerance 0.05 m +accept 107 5 +expect 381324.74 328117.472 + +tolerance 0.05 m +accept 106 5 +expect 270427.255 327597.962 + +tolerance 0.05 m +accept 105 5 +expect 159529.111 327248.012 + +tolerance 0.05 m +accept 104 5 +expect 48630.563 327067.097 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:3377 +inv \ + +step +init=epsg:4742 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 267186.017 881108.902 +expect 106 10 + +tolerance 0.05 m +accept 268006.024 770398.186 +expect 106 9 + +tolerance 0.05 m +accept 268740.351 659692.254 +expect 106 8 + +tolerance 0.05 m +accept 269388.786 548990.588 +expect 106 7 + +tolerance 0.05 m +accept 269951.141 438292.666 +expect 106 6 + +tolerance 0.05 m +accept 270427.255 327597.962 +expect 106 5 + +tolerance 0.05 m +accept 270816.99 216905.945 +expect 106 4 + +tolerance 0.05 m +accept 271120.234 106216.081 +expect 106 3 + +tolerance 0.05 m +accept 0 0 +expect 103.561065778 2.0424676812 + +tolerance 0.05 m +accept 8813.252 -23740.095 +expect 103.64025984 1.82776484381 + +tolerance 0.05 m +accept 271466.923 -115159.332 +expect 106 1 + +tolerance 0.05 m +accept 603116.703 329668.599 +expect 109 5 + +tolerance 0.05 m +accept 492221.308 328807.336 +expect 108 5 + +tolerance 0.05 m +accept 381324.74 328117.472 +expect 107 5 + +tolerance 0.05 m +accept 270427.255 327597.962 +expect 106 5 + +tolerance 0.05 m +accept 159529.111 327248.012 +expect 105 5 + +tolerance 0.05 m +accept 48630.563 327067.097 +expect 104 5 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4742 +inv \ + +step +init=epsg:3377 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 106 10 +roundtrip 1000 + +tolerance 0.006 m +accept 106 9 +roundtrip 1000 + +tolerance 0.006 m +accept 106 8 +roundtrip 1000 + +tolerance 0.006 m +accept 106 7 +roundtrip 1000 + +tolerance 0.006 m +accept 106 6 +roundtrip 1000 + +tolerance 0.006 m +accept 106 5 +roundtrip 1000 + +tolerance 0.006 m +accept 106 4 +roundtrip 1000 + +tolerance 0.006 m +accept 106 3 +roundtrip 1000 + +tolerance 0.006 m +accept 103.561065778 2.0424676812 +roundtrip 1000 + +tolerance 0.006 m +accept 103.64025984 1.82776484381 +roundtrip 1000 + +tolerance 0.006 m +accept 106 1 +roundtrip 1000 + +tolerance 0.006 m +accept 109 5 +roundtrip 1000 + +tolerance 0.006 m +accept 108 5 +roundtrip 1000 + +tolerance 0.006 m +accept 107 5 +roundtrip 1000 + +tolerance 0.006 m +accept 106 5 +roundtrip 1000 + +tolerance 0.006 m +accept 105 5 +roundtrip 1000 + +tolerance 0.006 m +accept 104 5 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5109.gie b/test/ProjNet.Tests/Fixtures/gigs/5109.gie new file mode 100644 index 00000000..457c903c --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5109.gie @@ -0,0 +1,182 @@ +-------------------------------------------------------------------------------- + +Test 5109, Albers Equal Area, v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4283 +inv \ + +step +init=epsg:3577 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 132 0 +expect 0 0 + +tolerance 0.05 m +accept 132 -27 +expect 0 -2926820.89 + +tolerance 0.05 m +accept 140 0 +expect 966973.98 -30285.6 + +tolerance 0.05 m +accept 140 -20 +expect 832799.36 -2170181.93 + +tolerance 0.05 m +accept 140 -40 +expect 693250.21 -4395794.49 + +tolerance 0.05 m +accept 140 -60 +expect 567313.29 -6404311.16 + +tolerance 0.05 m +accept 140 -80 +expect 486878.674 -7687130.029 + +tolerance 0.05 m +accept 120 -60 +expect -850274.75 -6426505.13 + +tolerance 0.05 m +accept 130 -60 +expect -141915.26 -6387653.78 + +tolerance 0.05 m +accept 140 -60 +expect 567313.29 -6404311.16 + +tolerance 0.05 m +accept 150 -60 +expect 1273067.747 -6476375.276 + +tolerance 0.05 m +accept 160 -60 +expect 1971026.26 -6603404.82 + +tolerance 0.05 m +accept 170 -60 +expect 2656914.716 -6784621.89 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:3577 +inv \ + +step +init=epsg:4283 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 0 0 +expect 132 0 + +tolerance 0.05 m +accept 0 -2926820.89 +expect 132 -27 + +tolerance 0.05 m +accept 966973.98 -30285.6 +expect 140 0 + +tolerance 0.05 m +accept 832799.36 -2170181.93 +expect 140 -20 + +tolerance 0.05 m +accept 693250.21 -4395794.49 +expect 140 -40 + +tolerance 0.05 m +accept 567313.29 -6404311.16 +expect 140 -60 + +tolerance 0.05 m +accept 486878.674 -7687130.029 +expect 140 -80 + +tolerance 0.05 m +accept -850274.75 -6426505.13 +expect 120 -60 + +tolerance 0.05 m +accept -141915.26 -6387653.78 +expect 130 -60 + +tolerance 0.05 m +accept 567313.29 -6404311.16 +expect 140 -60 + +tolerance 0.05 m +accept 1273067.747 -6476375.276 +expect 150 -60 + +tolerance 0.05 m +accept 1971026.26 -6603404.82 +expect 160 -60 + +tolerance 0.05 m +accept 2656914.716 -6784621.89 +expect 170 -60 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4283 +inv \ + +step +init=epsg:3577 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 132 0 +roundtrip 1000 + +tolerance 0.006 m +accept 132 -27 +roundtrip 1000 + +tolerance 0.006 m +accept 140 0 +roundtrip 1000 + +tolerance 0.006 m +accept 140 -20 +roundtrip 1000 + +tolerance 0.006 m +accept 140 -40 +roundtrip 1000 + +tolerance 0.006 m +accept 140 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 140 -80 +roundtrip 1000 + +tolerance 0.006 m +accept 120 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 130 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 140 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 150 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 160 -60 +roundtrip 1000 + +tolerance 0.006 m +accept 170 -60 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5111.1.gie b/test/ProjNet.Tests/Fixtures/gigs/5111.1.gie new file mode 100644 index 00000000..09cc4c0c --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5111.1.gie @@ -0,0 +1,452 @@ +-------------------------------------------------------------------------------- + +Test 5111 (part 1), Mercator (variant A), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + +# Batavia +<4211> +proj=longlat +ellps=bessel +towgs84=-377,681,-50,0,0,0,0 <> +# Batavia / NEIEZ +<3001> +proj=merc +lon_0=110 +k=0.997 +x_0=3900000 +y_0=900000 +ellps=bessel +towgs84=-377,681,-50,0,0,0,0 +units=m <> + + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline towgs84=0,0,0 \ + +step +init=epsg:4211 +inv \ + +step +init=epsg:3001 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 100.0876483 77.6534822 +expect 2800000.0 15000000.0 + +tolerance 0.055 m +accept 100.0876483 73.1442856 +expect 2800000.0 13000000.0 + +tolerance 0.05 m +accept 100.0876483 67.0518325 +expect 2800000.0 11000000.0 + +tolerance 0.05 m +accept 100.0876483 58.9140458 +expect 2800000.0 9000000.0 + +tolerance 0.05 m +accept 100.0876483 48.2638981 +expect 2800000.0 7000000.0 + +tolerance 0.05 m +accept 100.0876483 34.8029044 +expect 2800000.0 5000000.0 + +tolerance 0.05 m +accept 100.0876483 18.7048581 +expect 2800000.0 3000000.0 + +tolerance 0.05 m +accept 100.0876483 0.9071392 +expect 2800000.0 1000000.0 + +tolerance 0.05 m +accept 110.0 0.0 +expect 3900000.0 900000.0 + +tolerance 0.05 m +accept 100.0876483 -0.9071392 +expect 2800000.0 800000.0 + +tolerance 0.05 m +accept 100.0876483 -1.8140483 +expect 2800000.0 700000.0 + +tolerance 0.05 m +accept 100.0876483 -2.0 +expect 2800000.0 679490.65 + +tolerance 0.05 m +accept 100.0876483 -3.6262553 +expect 2800000.0 500000.0 + +tolerance 0.05 m +accept 100.0876483 -4.531095 +expect 2800000.0 400000.0 + +tolerance 0.05 m +accept 100.0876483 -5.4347892 +expect 2800000.0 300000.0 + +tolerance 0.05 m +accept 100.0876483 -6.3371111 +expect 2800000.0 200000.0 + +tolerance 0.05 m +accept 100.0876483 -7.2378372 +expect 2800000.0 100000.0 + +tolerance 0.05 m +accept 74.8562083 -8.136745 +expect 0.0 0.0 + +tolerance 0.05 m +accept -71.0 -2.0 +expect 23764105.84 679490.65 + +tolerance 0.05 m +accept -90.0 -2.0 +expect 21655625.33 679490.65 + +tolerance 0.05 m +accept -120.0 -2.0 +expect 18326445.58 679490.65 + +tolerance 0.05 m +accept -150.0 -2.0 +expect 14997265.83 679490.65 + +tolerance 0.05 m +accept 180.0 -2.0 +expect 11668086.08 679490.65 + +tolerance 0.05 m +accept 150.0 -2.0 +expect 8338906.33 679490.65 + +tolerance 0.05 m +accept 120.0 -2.0 +expect 5009726.58 679490.65 + +tolerance 0.05 m +accept 110.0 -2.0 +expect 3900000.0 679490.65 + +tolerance 0.05 m +accept 106.8077194 -2.0 +expect 3545744.14 679490.65 + +tolerance 0.05 m +accept 100.0876483 -2.0 +expect 2800000.0 679490.65 + +tolerance 0.05 m +accept 90.0 -2.0 +expect 1680546.83 679490.65 + +tolerance 0.05 m +accept 60.0 -2.0 +expect -1648632.92 679490.65 + +tolerance 0.05 m +accept 30.0 -2.0 +expect -4977812.67 679490.65 + +tolerance 0.05 m +accept 0.0 -2.0 +expect -8306992.42 679490.65 + +tolerance 0.05 m +accept -30.0 -2.0 +expect -11636172.17 679490.65 + +tolerance 0.05 m +accept -60.0 -2.0 +expect -14965351.92 679490.65 + +tolerance 0.05 m +accept -69.0 -2.0 +expect -15964105.84 679490.65 + +-------------------------------------------------------------------------------- +operation +proj=pipeline towgs84=0,0,0 \ + +step +init=epsg:3001 +inv \ + +step +init=epsg:4211 +-------------------------------------------------------------------------------- +tolerance 0.05 m +accept 2800000.0 15000000.0 +expect 100.0876483 77.6534822 + +tolerance 0.05 m +accept 2800000.0 13000000.0 +expect 100.0876483 73.1442856 + +tolerance 0.05 m +accept 2800000.0 11000000.0 +expect 100.0876483 67.0518325 + +tolerance 0.05 m +accept 2800000.0 9000000.0 +expect 100.0876483 58.9140458 + +tolerance 0.05 m +accept 2800000.0 7000000.0 +expect 100.0876483 48.2638981 + +tolerance 0.05 m +accept 2800000.0 5000000.0 +expect 100.0876483 34.8029044 + +tolerance 0.05 m +accept 2800000.0 3000000.0 +expect 100.0876483 18.7048581 + +tolerance 0.05 m +accept 2800000.0 1000000.0 +expect 100.0876483 0.9071392 + +tolerance 0.05 m +accept 3900000.0 900000.0 +expect 110.0 0.0 + +tolerance 0.05 m +accept 2800000.0 800000.0 +expect 100.0876483 -0.9071392 + +tolerance 0.05 m +accept 2800000.0 700000.0 +expect 100.0876483 -1.8140483 + +tolerance 0.05 m +accept 2800000.0 679490.65 +expect 100.0876483 -2.0 + +tolerance 0.05 m +accept 2800000.0 500000.0 +expect 100.0876483 -3.6262553 + +tolerance 0.05 m +accept 2800000.0 400000.0 +expect 100.0876483 -4.531095 + +tolerance 0.05 m +accept 2800000.0 300000.0 +expect 100.0876483 -5.4347892 + +tolerance 0.05 m +accept 2800000.0 200000.0 +expect 100.0876483 -6.3371111 + +tolerance 0.05 m +accept 2800000.0 100000.0 +expect 100.0876483 -7.2378372 + +tolerance 0.05 m +accept 0.0 0.0 +expect 74.8562083 -8.136745 + +tolerance 0.05 m +accept 23764105.84 679490.65 +expect -71.0 -2.0 + +tolerance 0.05 m +accept 21655625.33 679490.65 +expect -90.0 -2.0 + +tolerance 0.05 m +accept 18326445.58 679490.65 +expect -120.0 -2.0 + +tolerance 0.05 m +accept 14997265.83 679490.65 +expect -150.0 -2.0 + +tolerance 0.05 m +accept 11668086.08 679490.65 +expect 180.0 -2.0 + +tolerance 0.05 m +accept 8338906.33 679490.65 +expect 150.0 -2.0 + +tolerance 0.05 m +accept 5009726.58 679490.65 +expect 120.0 -2.0 + +tolerance 0.05 m +accept 3900000.0 679490.65 +expect 110.0 -2.0 + +tolerance 0.05 m +accept 3545744.14 679490.65 +expect 106.8077194 -2.0 + +tolerance 0.05 m +accept 2800000.0 679490.65 +expect 100.0876483 -2.0 + +tolerance 0.05 m +accept 1680546.83 679490.65 +expect 90.0 -2.0 + +tolerance 0.05 m +accept -1648632.92 679490.65 +expect 60.0 -2.0 + +tolerance 0.05 m +accept -4977812.67 679490.65 +expect 30.0 -2.0 + +tolerance 0.05 m +accept -8306992.42 679490.65 +expect 0.0 -2.0 + +tolerance 0.05 m +accept -11636172.17 679490.65 +expect -30.0 -2.0 + +tolerance 0.05 m +accept -14965351.92 679490.65 +expect -60.0 -2.0 + +tolerance 0.05 m +accept -15964105.84 679490.65 +expect -69.0 -2.0 + +-------------------------------------------------------------------------------- +operation +proj=pipeline towgs84=0,0,0 \ + +step +init=epsg:4211 +inv \ + +step +init=epsg:3001 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 100.0876483 77.6534822 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 73.1442856 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 67.0518325 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 58.9140458 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 48.2638981 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 34.8029044 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 18.7048581 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 0.9071392 +roundtrip 1000 + +tolerance 0.006 m +accept 110.0 0.0 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -0.9071392 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -1.8140483 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -3.6262553 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -4.531095 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -5.4347892 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -6.3371111 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -7.2378372 +roundtrip 1000 + +tolerance 0.006 m +accept 74.8562083 -8.136745 +roundtrip 1000 + +tolerance 0.006 m +accept -71.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept -90.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept -120.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept -150.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 180.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 150.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 120.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 110.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 106.8077194 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 100.0876483 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 90.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 60.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 30.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept 0.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept -30.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept -60.0 -2.0 +roundtrip 1000 + +tolerance 0.006 m +accept -69.0 -2.0 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5112.gie b/test/ProjNet.Tests/Fixtures/gigs/5112.gie new file mode 100644 index 00000000..02e3c920 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5112.gie @@ -0,0 +1,77 @@ +-------------------------------------------------------------------------------- + +Test 5112, Mercator (variant B), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation proj=pipeline \ + step init=epsg:4284 inv \ + step init=epsg:3388 + +tolerance 50 mm +-------------------------------------------------------------------------------- +accept 51.0 42.0 +expect 0.0 3819897.85 + +accept 51.0 0.0 +expect 0.0 0.0 + +accept 57.0 0.0 +expect 497112.88 0.0 + +accept 54.0 20.5 +expect 248556.44 1724781.5 + +accept 67.0 -41.0 +expect 1325634.35 -3709687.25 + +-------------------------------------------------------------------------------- +operation proj=pipeline \ + step init=epsg:3388 inv \ + step init=epsg:4284 + +tolerance 50 mm +-------------------------------------------------------------------------------- +accept 0.0 3819897.85 +expect 51.0 42.0 + +accept 0.0 0.0 +expect 51.0 0.0 + +accept 497112.88 0.0 +expect 57.0 0.0 + +accept 248556.44 1724781.5 +expect 54.0 20.5 + +accept 1325634.35 -3709687.25 +expect 67.0 -41.0 + +-------------------------------------------------------------------------------- +operation proj=pipeline towgs84=0,0,0 \ + step init=epsg:4284 inv \ + step init=epsg:3388 + +tolerance 6 mm +-------------------------------------------------------------------------------- +accept 51.0 42.0 +roundtrip 1000 + +accept 51.0 0.0 +roundtrip 1000 + +accept 57.0 0.0 +roundtrip 1000 + +accept 54.0 20.5 +roundtrip 1000 + +accept 67.0 -41.0 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5113.gie b/test/ProjNet.Tests/Fixtures/gigs/5113.gie new file mode 100644 index 00000000..c7ee6737 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5113.gie @@ -0,0 +1,86 @@ +-------------------------------------------------------------------------------- + +Test 5113, Transverse Mercator (South Oriented), v2-0_2011-06-28. + +-------------------------------------------------------------------------------- + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4148 +inv \ + +step +init=epsg:2049 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept 22.5 0.0 +expect -166998.44 0.0 + +tolerance 0.03 m +accept 21.5 -25.0 +expect -50475.46 2766147.25 + +tolerance 0.03 m +accept 20.5 -30.0 +expect 48243.45 3320218.65 + +tolerance 0.03 m +accept 19.5 -35.0 +expect 136937.65 3875621.18 + +tolerance 0.03 m +accept 19.5 -35.0 +expect 136937.65 3875621.18 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:2049 +inv \ + +step +init=epsg:4148 +-------------------------------------------------------------------------------- +tolerance 0.03 m +accept -166998.44 0.0 +expect 22.5 0.0 + +tolerance 0.03 m +accept -50475.46 2766147.25 +expect 21.5 -25.0 + +tolerance 0.03 m +accept 48243.45 3320218.65 +expect 20.5 -30.0 + +tolerance 0.03 m +accept 136937.65 3875621.18 +expect 19.5 -35.0 + +tolerance 0.03 m +accept 136937.65 3875621.18 +expect 19.5 -35.0 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4148 +inv \ + +step +init=epsg:2049 +-------------------------------------------------------------------------------- +tolerance 0.006 m +accept 22.5 0.0 +roundtrip 1000 + +tolerance 0.006 m +accept 21.5 -25.0 +roundtrip 1000 + +tolerance 0.006 m +accept 20.5 -30.0 +roundtrip 1000 + +tolerance 0.006 m +accept 19.5 -35.0 +roundtrip 1000 + +tolerance 0.006 m +accept 19.5 -35.0 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5201.gie b/test/ProjNet.Tests/Fixtures/gigs/5201.gie new file mode 100644 index 00000000..504921d0 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5201.gie @@ -0,0 +1,356 @@ +-------------------------------------------------------------------------------- + +Test 5201, Geographic Geocentric conversions, v2.0_2011-09-28. (EPSG 4979 - WGS84 3d has been replaced with EPSG code 4326 WGS84 2d). + +-------------------------------------------------------------------------------- + +# WGS 84 +<4978> +proj=geocent +datum=WGS84 +units=m <> +# WGS 84 +<4326> +proj=longlat +datum=WGS84 <> + + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4978 +inv \ + +step +init=epsg:4326 +-------------------------------------------------------------------------------- +tolerance 0.01 m +accept -962479.5924 555687.8517 6260738.6526 +expect 150 80 1214.137 + +tolerance 0.01 m +accept -962297.0059 555582.4354 6259542.961 +expect 150 80 0 + +tolerance 0.01 m +accept -1598248.169 2768777.623 5501278.468 +expect 119.99524538 60.00475191 619.6317 + +tolerance 0.01 m +accept -1598023.169 2768387.912 5500499.045 +expect 119.9952447 60.00475258 -280.3683 + +tolerance 0.01 m +accept 2764210.4054 4787752.865 3170468.5199 +expect 60 30 189.569 + +tolerance 0.01 m +accept 2764128.3196 4787610.6883 3170373.7354 +expect 60 30 0 + +tolerance 0.01 m +accept 6377934.396 -112 434 +expect -0.00100615 0.00392509 -202.5882 + +tolerance 0.01 m +accept 6374934.396 -112 434 +expect -0.00100662 0.00392695 -3202.5881 + +tolerance 0.01 m +accept 6367934.396 -112 434 +expect -0.00100773 0.00393129 -10202.5881 + +tolerance 0.01 m +accept 2764128.3196 -4787610.6883 -3170373.7354 +expect -60 -30 0 + +tolerance 0.01 m +accept 2763900.3489 -4787215.8313 -3170110.4974 +expect -60 -30 -526.476 + +tolerance 0.01 m +accept 2763880.8633 -4787182.0813 -3170087.9974 +expect -60 -30 -571.476 + +tolerance 0.01 m +accept -1598023.169 -2768611.912 -5499631.045 +expect -119.99323757 -59.99934884 -935.0995 + +tolerance 0.01 m +accept -1597798.169 -2768222.201 -5498851.622 +expect -119.99323663 -59.99934874 -1835.0995 + +tolerance 0.01 m +accept -962297.0059 -555582.4354 -6259542.961 +expect -150 -80 0 + +tolerance 0.01 m +accept -962150.945 -555498.1071 -6258586.4616 +expect -150 -80 -971.255 + +tolerance 0.01 m +accept -961798.2951 -555294.5046 -6256277.0874 +expect -150 -80 -3316.255 + +tolerance 0.01 m +accept -2187336.719 -112 5971017.093 +expect -179.99706624 70.00490733 -223.6178 + +tolerance 0.01 m +accept -2904698.5551 -2904698.5551 4862789.0377 +expect -135 50 0 + +tolerance 0.01 m +accept 371 -5783593.614 2679326.11 +expect -89.99632465 25.00366329 -274.7286 + +tolerance 0.01 m +accept 6378137 0 0 +expect 0 0 0 + +tolerance 0.01 m +accept -4087095.478 2977467.559 -3875457.429 +expect 143.92649252 -37.65282217 737.7182 + +tolerance 0.01 m +accept -4085919.959 2976611.233 -3874335.274 +expect 143.92649211 -37.65282206 -1099.2288 + +tolerance 0.01 m +accept -4084000.165 2975212.729 -3872502.631 +expect 143.92649143 -37.65282187 -4099.2288 + +tolerance 0.01 m +accept -4079520.647 2971949.553 -3868226.465 +expect 143.92648984 -37.65282143 -11099.2288 + +tolerance 0.01 m +accept -2904698.5551 2904698.5551 -4862789.0377 +expect 135 -50 0 + +tolerance 0.01 m +accept -2187336.719 -112 -5970149.093 +expect -179.99706624 -70.00224647 -1039.2896 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4326 +inv \ + +step +init=epsg:4978 +-------------------------------------------------------------------------------- +tolerance 0.01 m +accept 150 80 1214.137 +expect -962479.5924 555687.8517 6260738.6526 + +tolerance 0.01 m +accept 150 80 0 +expect -962297.0059 555582.4354 6259542.961 + +tolerance 0.01 m +accept 119.99524538 60.00475191 619.6317 +expect -1598248.169 2768777.623 5501278.468 + +tolerance 0.01 m +accept 119.9952447 60.00475258 -280.3683 +expect -1598023.169 2768387.912 5500499.045 + +tolerance 0.01 m +accept 60 30 189.569 +expect 2764210.4054 4787752.865 3170468.5199 + +tolerance 0.01 m +accept 60 30 0 +expect 2764128.3196 4787610.6883 3170373.7354 + +tolerance 0.01 m +accept -0.00100615 0.00392509 -202.5882 +expect 6377934.396 -112 434 + +tolerance 0.01 m +accept -0.00100662 0.00392695 -3202.5881 +expect 6374934.396 -112 434 + +tolerance 0.01 m +accept -0.00100773 0.00393129 -10202.5881 +expect 6367934.396 -112 434 + +tolerance 0.01 m +accept -60 -30 0 +expect 2764128.3196 -4787610.6883 -3170373.7354 + +tolerance 0.01 m +accept -60 -30 -526.476 +expect 2763900.3489 -4787215.8313 -3170110.4974 + +tolerance 0.01 m +accept -60 -30 -571.476 +expect 2763880.8633 -4787182.0813 -3170087.9974 + +tolerance 0.01 m +accept -119.99323757 -59.99934884 -935.0995 +expect -1598023.169 -2768611.912 -5499631.045 + +tolerance 0.01 m +accept -119.99323663 -59.99934874 -1835.0995 +expect -1597798.169 -2768222.201 -5498851.622 + +tolerance 0.01 m +accept -150 -80 0 +expect -962297.0059 -555582.4354 -6259542.961 + +tolerance 0.01 m +accept -150 -80 -971.255 +expect -962150.945 -555498.1071 -6258586.4616 + +tolerance 0.01 m +accept -150 -80 -3316.255 +expect -961798.2951 -555294.5046 -6256277.0874 + +tolerance 0.01 m +accept -179.99706624 70.00490733 -223.6178 +expect -2187336.719 -112 5971017.093 + +tolerance 0.01 m +accept -135 50 0 +expect -2904698.5551 -2904698.5551 4862789.0377 + +tolerance 0.01 m +accept -89.99632465 25.00366329 -274.7286 +expect 371 -5783593.614 2679326.11 + +tolerance 0.01 m +accept 0 0 0 +expect 6378137 0 0 + +tolerance 0.01 m +accept 143.92649252 -37.65282217 737.7182 +expect -4087095.478 2977467.559 -3875457.429 + +tolerance 0.01 m +accept 143.92649211 -37.65282206 -1099.2288 +expect -4085919.959 2976611.233 -3874335.274 + +tolerance 0.01 m +accept 143.92649143 -37.65282187 -4099.2288 +expect -4084000.165 2975212.729 -3872502.631 + +tolerance 0.01 m +accept 143.92648984 -37.65282143 -11099.2288 +expect -4079520.647 2971949.553 -3868226.465 + +tolerance 0.01 m +accept 135 -50 0 +expect -2904698.5551 2904698.5551 -4862789.0377 + +tolerance 0.01 m +accept -179.99706624 -70.00224647 -1039.2896 +expect -2187336.719 -112 -5970149.093 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4978 +inv \ + +step +init=epsg:4326 +-------------------------------------------------------------------------------- +tolerance 0.01 m +accept -962479.5924 555687.8517 6260738.6526 +roundtrip 1000 + +tolerance 0.01 m +accept -962297.0059 555582.4354 6259542.961 +roundtrip 1000 + +tolerance 0.01 m +accept -1598248.169 2768777.623 5501278.468 +roundtrip 1000 + +tolerance 0.01 m +accept -1598023.169 2768387.912 5500499.045 +roundtrip 1000 + +tolerance 0.01 m +accept 2764210.4054 4787752.865 3170468.5199 +roundtrip 1000 + +tolerance 0.01 m +accept 2764128.3196 4787610.6883 3170373.7354 +roundtrip 1000 + +tolerance 0.01 m +accept 6377934.396 -112 434 +roundtrip 1000 + +tolerance 0.01 m +accept 6374934.396 -112 434 +roundtrip 1000 + +tolerance 0.01 m +accept 6367934.396 -112 434 +roundtrip 1000 + +tolerance 0.01 m +accept 2764128.3196 -4787610.6883 -3170373.7354 +roundtrip 1000 + +tolerance 0.01 m +accept 2763900.3489 -4787215.8313 -3170110.4974 +roundtrip 1000 + +tolerance 0.01 m +accept 2763880.8633 -4787182.0813 -3170087.9974 +roundtrip 1000 + +tolerance 0.01 m +accept -1598023.169 -2768611.912 -5499631.045 +roundtrip 1000 + +tolerance 0.01 m +accept -1597798.169 -2768222.201 -5498851.622 +roundtrip 1000 + +tolerance 0.01 m +accept -962297.0059 -555582.4354 -6259542.961 +roundtrip 1000 + +tolerance 0.01 m +accept -962150.945 -555498.1071 -6258586.4616 +roundtrip 1000 + +tolerance 0.01 m +accept -961798.2951 -555294.5046 -6256277.0874 +roundtrip 1000 + +tolerance 0.01 m +accept -2187336.719 -112 5971017.093 +roundtrip 1000 + +tolerance 0.01 m +accept -2904698.5551 -2904698.5551 4862789.0377 +roundtrip 1000 + +tolerance 0.01 m +accept 371 -5783593.614 2679326.11 +roundtrip 1000 + +tolerance 0.01 m +accept 6378137 0 0 +roundtrip 1000 + +tolerance 0.01 m +accept -4087095.478 2977467.559 -3875457.429 +roundtrip 1000 + +tolerance 0.01 m +accept -4085919.959 2976611.233 -3874335.274 +roundtrip 1000 + +tolerance 0.01 m +accept -4084000.165 2975212.729 -3872502.631 +roundtrip 1000 + +tolerance 0.01 m +accept -4079520.647 2971949.553 -3868226.465 +roundtrip 1000 + +tolerance 0.01 m +accept -2904698.5551 2904698.5551 -4862789.0377 +roundtrip 1000 + +tolerance 0.01 m +accept -2187336.719 -112 -5970149.093 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/gigs/5208.gie b/test/ProjNet.Tests/Fixtures/gigs/5208.gie new file mode 100644 index 00000000..5ff492c4 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/gigs/5208.gie @@ -0,0 +1,210 @@ +-------------------------------------------------------------------------------- + +Test 5208, Longitude Rotation, v2.0_2011-06-28. + +The test tolerance is 0.01". Since gie can only use linear tolerances we +convert that to an approximate liniar distance instead, by multiplying with +111km: + + 0.01" * 111 km = 2.777777778-7 * 111000 m = 0.03 m + +To be on the safe side we, use 0.01 m as the tolerance. + +-------------------------------------------------------------------------------- + +# NTF +<4275> +proj=longlat +a=6378249.2 +b=6356515 +towgs84=-168,-60,320,0,0,0,0 <> + +# NTF (Paris) +<4807> +proj=longlat +a=6378249.2 +b=6356515 +towgs84=-168,-60,320,0,0,0,0 +pm=paris <> + + + + + +use_proj4_init_rules true + +-------------------------------------------------------------------------------- +operation +proj=pipeline\ + +step +init=epsg:4275 +inv\ + +step +init=epsg:4807 +-------------------------------------------------------------------------------- +tolerance 0.01 m +accept 5 58 +expect 2.66277083 58 + +tolerance 0.01 m +accept 5 56 +expect 2.66277083 56 + +tolerance 0.01 m +accept 5 55 +expect 2.66277083 55 + +tolerance 0.01 m +accept 5 53 +expect 2.66277083 53 + +tolerance 0.01 m +accept 4 51 +expect 1.66277083 51 + +tolerance 0.01 m +accept 4 49 +expect 1.66277083 49 + +tolerance 0.01 m +accept 2.33722917 46.8 +expect 0 46.8 + +tolerance 0.01 m +accept 3 53 +expect 0.66277083 53 + +tolerance 0.01 m +accept 4 53 +expect 1.66277083 53 + +tolerance 0.01 m +accept 6 53 +expect 3.66277083 53 + +tolerance 0.01 m +accept 7 53 +expect 4.66277083 53 + +tolerance 0.01 m +accept 9 53 +expect 6.66277083 53 + +tolerance 0.01 m +accept 10 53 +expect 7.66277083 53 + +tolerance 0.01 m +accept 11 53 +expect 8.66277083 53 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4807 +inv \ + +step +init=epsg:4275 +-------------------------------------------------------------------------------- +tolerance 0.01 m +accept 2.66277083 58 +expect 5 58 + +tolerance 0.01 m +accept 2.66277083 56 +expect 5 56 + +tolerance 0.01 m +accept 2.66277083 55 +expect 5 55 + +tolerance 0.01 m +accept 2.66277083 53 +expect 5 53 + +tolerance 0.01 m +accept 1.66277083 51 +expect 4 51 + +tolerance 0.01 m +accept 1.66277083 49 +expect 4 49 + +tolerance 0.01 m +accept 0 46.8 +expect 2.33722917 46.8 + +tolerance 0.01 m +accept 0.66277083 53 +expect 3 53 + +tolerance 0.01 m +accept 1.66277083 53 +expect 4 53 + +tolerance 0.01 m +accept 3.66277083 53 +expect 6 53 + +tolerance 0.01 m +accept 4.66277083 53 +expect 7 53 + +tolerance 0.01 m +accept 6.66277083 53 +expect 9 53 + +tolerance 0.01 m +accept 7.66277083 53 +expect 10 53 + +tolerance 0.01 m +accept 8.66277083 53 +expect 11 53 + +-------------------------------------------------------------------------------- +operation +proj=pipeline \ + +step +init=epsg:4275 +inv \ + +step +init=epsg:4807 +-------------------------------------------------------------------------------- +tolerance 0.01 m +accept 5 58 +roundtrip 1000 + +tolerance 0.01 m +accept 5 56 +roundtrip 1000 + +tolerance 0.01 m +accept 5 55 +roundtrip 1000 + +tolerance 0.01 m +accept 5 53 +roundtrip 1000 + +tolerance 0.01 m +accept 4 51 +roundtrip 1000 + +tolerance 0.01 m +accept 4 49 +roundtrip 1000 + +tolerance 0.01 m +accept 2.33722917 46.8 +roundtrip 1000 + +tolerance 0.01 m +accept 3 53 +roundtrip 1000 + +tolerance 0.01 m +accept 4 53 +roundtrip 1000 + +tolerance 0.01 m +accept 6 53 +roundtrip 1000 + +tolerance 0.01 m +accept 7 53 +roundtrip 1000 + +tolerance 0.01 m +accept 9 53 +roundtrip 1000 + +tolerance 0.01 m +accept 10 53 +roundtrip 1000 + +tolerance 0.01 m +accept 11 53 +roundtrip 1000 + + diff --git a/test/ProjNet.Tests/Fixtures/grids/BETA2007.gsb b/test/ProjNet.Tests/Fixtures/grids/BETA2007.gsb new file mode 100644 index 00000000..69cd3346 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/BETA2007.gsb differ diff --git a/test/ProjNet.Tests/Fixtures/grids/NKG b/test/ProjNet.Tests/Fixtures/grids/NKG new file mode 100644 index 00000000..1381079c --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/grids/NKG @@ -0,0 +1,186 @@ +################################################################################ +# +# +# NKG Common Nordic Reference Frame +# +# Transformations to and from the common Nordic referenc frame NKG_ETRF00. +# This init-file describes transformations between global reference frames +# and NKG_ETRF00 as well as transformations from NKG_ERTF00 to the local +# realisations of ETRS89 in each of the countries involved with NKG. +# +# All transformations in this init-file uses the common Nordic frame as a +# pivot datum. Exempt from this dogma are transformations with labels +# starting with "_". Those transformations are "private" to this file and are +# only used as steps in more complicated transformations. +# +# Sources: +# +# [0] Häkli, P. et al, 2016, The NKG2008 GPS Campaign - final transformation +# results and a new common Nordic reference frame. +# https://www.degruyter.com/downloadpdf/j/jogs.2016.6.issue-1/jogs-2016-0001/jogs-2016-0001.pdf +# +# [1] Häkli, P., 2019, ETRS89 Transformations in Fennoscandia (NKG transformation), +# EUREF2019 Tutorial. +# http://www.euref.eu/documentation/Tutorial2019/t-03-Hakli.pdf +############################################################################### + +############################################################################### +# +# Global Frames +# +# Input: Cartesian coordinates +# Output: Cartesian coordinates +# +############################################################################### + +# NKG_ETRF00 -> ITRF2008 + proj = pipeline ellps = GRS80 + + # NKG_ETRF00@2000.0 -> ETRF00@t_obs + step proj = deformation t_epoch = 2000.0 + grids = eur_nkg_nkgrf03vel_realigned.tif + + + # ETRF00@t_obs -> ITRF2000@t_obs + step init = NKG:ITRF2000_ETRF2000 inv + + # ITRF2000@t_obs -> ITRF2008@t_obs + step init = ITRF2008:ITRF2000 inv + + +# NKG_ETRF00 -> ITRF2014 + proj = pipeline ellps = GRS80 + + # NKG_ETRF00@2000.0 -> ETRF00@t_obs + step proj = deformation t_epoch = 2000.0 + grids = eur_nkg_nkgrf03vel_realigned.tif + + # ETRF00@t_obs -> ITRF2000@t_obs + step init = NKG:ITRF2000_ETRF2000 inv + + # ITRF2000@t_obs -> ITRF2014@t_obs + step init = ITRF2014:ITRF2000 inv + +# ITRF2000 -> ETRF2000 +# Source: Specifications for reference frame fixing in the analysis of a +# EUREF GPS campaign - http://etrs89.ensg.ign.fr/memo-V8.pdf + proj=helmert x=0.054 +y=0.051 z=-0.048 rx=0.000891 ry=0.00539 rz=-0.008712 + drx=8.1e-05 dry=0.00049 drz=-0.000792 t_epoch=2000.0 convention=position_vector + + +############################################################################### +# +# National ETRS89 Realizations +# +# Input: Cartesian coordinates +# Output: Geodetic coordinates +# +############################################################################### + +# NKG_ETRF00 -> ETRS89(DK) [ETRF92@1994.704] + proj = pipeline ellps=GRS80 + + step init = NKG:_P1DK + + step proj = deformation dt = -5.296 + grids = eur_nkg_nkgrf03vel_realigned.tif + + step proj=cart inv + +# NKG_ETRF00 -> ETRS89(EE) [ETRF96@1997.56] + proj = pipeline ellps = GRS80 + + step init = NKG:_P1EE + + step proj = deformation dt = -2.44 + grids = eur_nkg_nkgrf03vel_realigned.tif + + step proj = cart inv + +# The Faroese Islands are outside the defined area for the uplift model and +# should be treated accordingly. +# # NKG_ETRF00 -> ETRS89(FO) [ETRS2000@2008.75] +# + +# NKG_ETRF00 -> ETRS89(FI) [ETRF96@1997.0] + proj = pipeline ellps = GRS80 + + step init = NKG:_P1FI + + step proj = deformation dt = -3 + grids = eur_nkg_nkgrf03vel_realigned.tif + + step proj=cart inv + +# NKG_ETRF00 -> ETRS89(LV) [ETRF89@1992.75] + proj = pipeline ellps = GRS80 + + step init = NKG:_P1LV + + step proj = deformation dt = -7.25 + grids = eur_nkg_nkgrf03vel_realigned.tif + + step proj = cart inv + +# NKG_ETRF00 -> ETRS89(LT) [ETRF2000@2003.75] + proj = pipeline ellps = GRS80 + + step init = NKG:_P1LT + + step proj = deformation dt = 3.75 + grids = eur_nkg_nkgrf03vel_realigned.tif + + step proj=cart inv + +# NKG_ETRF00 -> ETRS89(NO) [ETRF93@1995.0] + proj = pipeline ellps = GRS80 + + step init = NKG:_P1NO + + step proj = deformation dt = -5 + grids = eur_nkg_nkgrf03vel_realigned.tif + + step proj=cart inv + +# NKG_ETRF00 -> ETRS89(SE) [ETRF97@1999.5] + proj = pipeline ellps = GRS80 + + step init = NKG:_P1SE + + step proj = deformation -0.5 + grids = eur_nkg_nkgrf03vel_realigned.tif + + step proj = cart inv + + +############################################################################### +# +# "Private" transformations +# +############################################################################### + +# The Helmert definitions below are taken from table 8 in [0]. The table lists +# parameters for Helmert transformations between NKG_ERTF00@2000.0 and the +# local realisation of ETRS89 at epoch 2000.0. Transformations starting with +# "_P1" are only to be used with the realigned velocity model, whereas +# transformations starting with "_P2" are to be used with the original velocity +# model of 2003. + +<_P1DK> proj=helmert convention=position_vector x= 0.03863 y= 0.147 z= 0.02776 s=-0.009420 rx= 0.00617753 ry= 5.064e-05 rz= 4.729e-05 +<_P1EE> proj=helmert convention=position_vector x= 0.12194 y= 0.02225 z=-0.03541 s=-0.005626 rx= 0.00227196 ry=-0.00323934 rz= 0.00247008 +<_P1FO> proj=helmert convention=position_vector x=-0.10947 y= 0.235 z= 0.09432 s=-0.002626 rx= 0.00734019 ry= 0.00454595 rz=-0.00253141 +<_P1FI> proj=helmert convention=position_vector x= 0.07251 y=-0.13019 z=-0.11323 s= 0.013012 rx=-0.00157399 ry=-0.00308833 rz= 0.00410332 +<_P1LV> proj=helmert convention=position_vector x= 0.41812 y=-0.78105 z=-0.01335 s= 0.000757 rx=-0.0216436 ry=-0.0115184 rz= 0.01719911 +<_P1LT> proj=helmert convention=position_vector x= 0.05692 y= 0.11549 z=-0.00078 s=-0.006182 rx= 0.00314291 ry=-0.00147975 rz=-0.00134758 +<_P1NO> proj=helmert convention=position_vector x=-0.13116 y=-0.02817 z=0.02036 s= 0.006569 rx=-0.00038674 ry= 0.00408947 rz= 0.00103588 +<_P1SE> proj=helmert convention=position_vector x=-0.01642 y=-0.00064 z=-0.0305 s= 0.001861 rx= 0.00187431 ry= 0.00046382 rz= 0.00228487 + + +<_P2DK> proj=helmert convention=position_vector x= 0.02746 y= 0.14404 z= 0.02104 s=-0.006958 rx= 0.00609221 ry= 0.00021292 rz=-2.866e-05 +<_P2EE> proj=helmert convention=position_vector x= 0.1168 y= 0.02088 z=-0.03851 s= 0.004492 rx= 0.00223263 ry=-0.00316453 rz= 0.00243507 +<_P2FO> proj=helmert convention=position_vector x=-0.10947 y= 0.235 z= 0.09432 s=-0.002626 rx= 0.00734019 ry= 0.00454595 rz=-0.00253141 +<_P2FI> proj=helmert convention=position_vector x= 0.06618 y=-0.13187 z=-0.11704 s= 0.14407 rx=-0.00162235 ry=-0.00299635 rz= 0.00406027 +<_P2LV> proj=helmert convention=position_vector x= 0.40283 y=-0.78511 z=-0.02256 s= 0.004128 rx=-0.02176047 ry=-0.01129611 rz= 0.01709507 +<_P2LT> proj=helmert convention=position_vector x= 0.06483 y= 0.11759 z= 0.00398 s=-0.007925 rx= 0.00320336 ry=-0.00159472 rz=-0.00129376 +<_P2NO> proj=helmert convention=position_vector x=-0.14171 y=-0.03097 z= 0.01401 s= 0.008894 rx=-0.00046734 ry= 0.00424277 rz= 0.00096413 +<_P2SE> proj=helmert convention=position_vector x=-0.01748 y=-0.00092 z=-0.03114 s= 2.093 rx= 0.00186625 ry= 0.00047915 rz= 0.00227769 diff --git a/test/ProjNet.Tests/Fixtures/grids/alaska b/test/ProjNet.Tests/Fixtures/grids/alaska new file mode 100644 index 00000000..bb6be2ff Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/alaska differ diff --git a/test/ProjNet.Tests/Fixtures/grids/egm96_15.gtx b/test/ProjNet.Tests/Fixtures/grids/egm96_15.gtx new file mode 100644 index 00000000..ea53ab10 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/egm96_15.gtx differ diff --git a/test/ProjNet.Tests/Fixtures/grids/eur_nkg_nkgrf03vel_realigned.tif b/test/ProjNet.Tests/Fixtures/grids/eur_nkg_nkgrf03vel_realigned.tif new file mode 100644 index 00000000..9d1ac8ad Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/eur_nkg_nkgrf03vel_realigned.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/eur_nkg_nkgrf17vel.tif b/test/ProjNet.Tests/Fixtures/grids/eur_nkg_nkgrf17vel.tif new file mode 100644 index 00000000..5da857e9 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/eur_nkg_nkgrf17vel.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_extract.tif b/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_extract.tif new file mode 100644 index 00000000..6db8eae4 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_extract.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_xy_extract.ct2 b/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_xy_extract.ct2 new file mode 100644 index 00000000..89232b9f Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_xy_extract.ct2 differ diff --git a/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_z_extract.gtx b/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_z_extract.gtx new file mode 100644 index 00000000..5ea8aac7 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/nkgrf03vel_realigned_z_extract.gtx differ diff --git a/test/ProjNet.Tests/Fixtures/grids/no_kv_NKGETRF14_EPSG7922_2000.tif b/test/ProjNet.Tests/Fixtures/grids/no_kv_NKGETRF14_EPSG7922_2000.tif new file mode 100644 index 00000000..808d0775 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/no_kv_NKGETRF14_EPSG7922_2000.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/subset_of_gr3df97a.tif b/test/ProjNet.Tests/Fixtures/grids/subset_of_gr3df97a.tif new file mode 100644 index 00000000..a98783f3 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/subset_of_gr3df97a.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_gridshift_projected.tif b/test/ProjNet.Tests/Fixtures/grids/test_gridshift_projected.tif new file mode 100644 index 00000000..bcead1bc Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_gridshift_projected.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_hgrid.tif b/test/ProjNet.Tests/Fixtures/grids/test_hgrid.tif new file mode 100644 index 00000000..94718c21 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_hgrid.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_hgrid_big_endian.gsb b/test/ProjNet.Tests/Fixtures/grids/test_hgrid_big_endian.gsb new file mode 100644 index 00000000..91f2189d Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_hgrid_big_endian.gsb differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_hgrid_little_endian.gsb b/test/ProjNet.Tests/Fixtures/grids/test_hgrid_little_endian.gsb new file mode 100644 index 00000000..13b37392 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_hgrid_little_endian.gsb differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_hgrid_positive_west.tif b/test/ProjNet.Tests/Fixtures/grids/test_hgrid_positive_west.tif new file mode 100644 index 00000000..4ebc17cc Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_hgrid_positive_west.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_nodata.gtx b/test/ProjNet.Tests/Fixtures/grids/test_nodata.gtx new file mode 100644 index 00000000..e439e5f4 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_nodata.gtx differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_vgrid_nodata.tif b/test/ProjNet.Tests/Fixtures/grids/test_vgrid_nodata.tif new file mode 100644 index 00000000..65ec5343 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_vgrid_nodata.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_vgrid_pixelispoint.tif b/test/ProjNet.Tests/Fixtures/grids/test_vgrid_pixelispoint.tif new file mode 100644 index 00000000..cfeb598f Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_vgrid_pixelispoint.tif differ diff --git a/test/ProjNet.Tests/Fixtures/grids/test_vgrid_uint16_with_scale_offset.tif b/test/ProjNet.Tests/Fixtures/grids/test_vgrid_uint16_with_scale_offset.tif new file mode 100644 index 00000000..b08fa4a3 Binary files /dev/null and b/test/ProjNet.Tests/Fixtures/grids/test_vgrid_uint16_with_scale_offset.tif differ diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_crs_implicit.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_crs_implicit.json new file mode 100644 index 00000000..b3a64e10 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_crs_implicit.json @@ -0,0 +1,46 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.0", + "name": "Name", + "version": "Version", + "publication_date": "2018-07-01T00:00:00Z", + "license": "Creative Commons Attribution 4.0 International", + "description": "Test triangulation", + "authority": { + "name": "Authority name", + "url": "http://example.com", + "address": "Address", + "email": "test@example.com" + }, + "links": [ + { + "href": "https://example.com/about.html", + "rel": "about", + "type": "text/html", + "title": "About" + }, + { + "href": "https://example.com/download", + "rel": "source", + "type": "application/zip", + "title": "Authoritative source" + }, + { + "href": "https://creativecommons.org/licenses/by/4.0/", + "rel": "license", + "type": "text/html", + "title": "Creative Commons Attribution 4.0 International license" + }, + { + "href": "https://example.com/metadata.xml", + "rel": "metadata", + "type": "application/xml", + "title": " ISO 19115 XML encoded metadata regarding the deformation model" + } + ], + "transformed_components": [ "horizontal" ], + "vertices_columns": [ "source_x", "source_y", "target_x", "target_y" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ [2,49,2.1,49.1], [3,50,3.1,50.1], [2, 50, 2.1,50.1] ], + "triangles": [ [0, 1, 2] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_fallback_nearest_centroid.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_fallback_nearest_centroid.json new file mode 100644 index 00000000..9751ab51 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_fallback_nearest_centroid.json @@ -0,0 +1,17 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.1", + "fallback_strategy": "nearest_centroid", + "transformed_components": [ "horizontal" ], + "vertices_columns": [ "source_x", "source_y", "target_x", "target_y" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ + [0, 0, 0, 0], + [1, 0, 1, 0], + [1, 1, 1, 1], + [4, 0, 100, 0], + [100, 0, 100, 1], + [100, 1, 4, 0] + ], + "triangles": [ [0, 1, 2], [3, 4, 5] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_fallback_nearest_side.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_fallback_nearest_side.json new file mode 100644 index 00000000..59e5b6f2 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_fallback_nearest_side.json @@ -0,0 +1,15 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.1", + "fallback_strategy": "nearest_side", + "transformed_components": [ "horizontal" ], + "vertices_columns": [ "source_x", "source_y", "target_x", "target_y" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ + [0, 0, 0, 0], + [1, 0, 2, 0], + [1, 1, 2, 2], + [0, 1, 0, 2] + ], + "triangles": [ [0, 1, 2], [0, 2, 3] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_simplified_kkj_etrs.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_simplified_kkj_etrs.json new file mode 100644 index 00000000..3efc4fac --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_simplified_kkj_etrs.json @@ -0,0 +1,50 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.0", + "name": "Name", + "version": "Version", + "publication_date": "2018-07-01T00:00:00Z", + "license": "Creative Commons Attribution 4.0 International", + "description": "Test triangulation", + "authority": { + "name": "Authority name", + "url": "http://example.com", + "address": "Address", + "email": "test@example.com" + }, + "links": [ + { + "href": "https://example.com/about.html", + "rel": "about", + "type": "text/html", + "title": "About" + }, + { + "href": "https://example.com/download", + "rel": "source", + "type": "application/zip", + "title": "Authoritative source" + }, + { + "href": "https://creativecommons.org/licenses/by/4.0/", + "rel": "license", + "type": "text/html", + "title": "Creative Commons Attribution 4.0 International license" + }, + { + "href": "https://example.com/metadata.xml", + "rel": "metadata", + "type": "application/xml", + "title": " ISO 19115 XML encoded metadata regarding the triangulation" + } + ], + "input_crs": "EPSG:2393", + "output_crs": "EPSG:3067", + "transformed_components": [ "horizontal" ], + "vertices_columns": [ "source_x", "source_y", "target_x", "target_y" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ [3244102.707, 6693710.937, 244037.137, 6690900.686], + [3205290.722, 6715311.822, 205240.895, 6712492.577], + [3218328.492, 6649538.429, 218273.648, 6646745.973] ], + "triangles": [ [0, 1, 2] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_simplified_n60_n2000.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_simplified_n60_n2000.json new file mode 100644 index 00000000..7f133df5 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_simplified_n60_n2000.json @@ -0,0 +1,50 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.0", + "name": "Name", + "version": "Version", + "publication_date": "2018-07-01T00:00:00Z", + "license": "Creative Commons Attribution 4.0 International", + "description": "Test triangulation", + "authority": { + "name": "Authority name", + "url": "http://example.com", + "address": "Address", + "email": "test@example.com" + }, + "links": [ + { + "href": "https://example.com/about.html", + "rel": "about", + "type": "text/html", + "title": "About" + }, + { + "href": "https://example.com/download", + "rel": "source", + "type": "application/zip", + "title": "Authoritative source" + }, + { + "href": "https://creativecommons.org/licenses/by/4.0/", + "rel": "license", + "type": "text/html", + "title": "Creative Commons Attribution 4.0 International license" + }, + { + "href": "https://example.com/metadata.xml", + "rel": "metadata", + "type": "application/xml", + "title": " ISO 19115 XML encoded metadata regarding the tirangulation" + } + ], + "input_crs": "EPSG:2393+5717", + "output_crs": "EPSG:2393+5941", + "transformed_components": [ "vertical" ], + "vertices_columns": [ "source_x", "source_y", "source_z", "target_z" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ [3188607.0, 6688748.0, 23.123, 23.4133], + [3184981.0, 6725255.0, 8.044, 8.34499], + [3220912.0, 6699508.0, 1.724, 2.0101] ], + "triangles": [ [0, 1, 2] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_basic_horizontal.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_basic_horizontal.json new file mode 100644 index 00000000..d68a9d18 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_basic_horizontal.json @@ -0,0 +1,11 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.0", + "input_crs": "EPSG:2393", + "output_crs": "EPSG:3067", + "transformed_components": [ "horizontal" ], + "vertices_columns": [ "source_x", "source_y", "target_x", "target_y" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ [ 0, 0, 101, 101 ], [ 0, 1, 100, 101 ], [ 1, 1, 100, 100 ] ], + "triangles": [ [ 0, 1, 2 ] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_horizontal_vertical.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_horizontal_vertical.json new file mode 100644 index 00000000..472f8485 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_horizontal_vertical.json @@ -0,0 +1,9 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.0", + "transformed_components": [ "horizontal", "vertical" ], + "vertices_columns": [ "source_x", "source_y", "target_x", "target_y", "offset_z" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ [ 0, 0, 101, 101, 0.1 ], [ 0, 1, 100, 101, 0.2 ], [ 1, 1, 100, 100, 0.5 ] ], + "triangles": [ [ 0, 1, 2 ] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_vertical_offset.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_vertical_offset.json new file mode 100644 index 00000000..2da3fe35 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_vertical_offset.json @@ -0,0 +1,9 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.0", + "transformed_components": [ "vertical" ], + "vertices_columns": [ "source_x", "source_y", "offset_z" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ [ 0, 0, 0.1 ], [ 0, 1, 0.2 ], [ 1, 1, 0.5 ] ], + "triangles": [ [ 0, 1, 2 ] ] +} diff --git a/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_vertical_source_target.json b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_vertical_source_target.json new file mode 100644 index 00000000..c2664851 --- /dev/null +++ b/test/ProjNet.Tests/Fixtures/tinshift/tinshift_unit_vertical_source_target.json @@ -0,0 +1,9 @@ +{ + "file_type": "triangulation_file", + "format_version": "1.0", + "transformed_components": [ "vertical" ], + "vertices_columns": [ "source_x", "source_y", "source_z", "target_z" ], + "triangles_columns": [ "idx_vertex1", "idx_vertex2", "idx_vertex3" ], + "vertices": [ [ 0, 0, 10.5, 10.6 ], [ 0, 1, 15.0, 15.2 ], [ 1, 1, 17.5, 18.0 ] ], + "triangles": [ [ 0, 1, 2 ] ] +} diff --git a/test/ProjNet.Tests/Generated/EpsgCatalogCoverageTests.cs b/test/ProjNet.Tests/Generated/EpsgCatalogCoverageTests.cs new file mode 100644 index 00000000..aa2ed0a6 --- /dev/null +++ b/test/ProjNet.Tests/Generated/EpsgCatalogCoverageTests.cs @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.Generated; + +using System; +using System.Collections.Generic; +using System.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.Data; +using ProjNet.Data.Generated; +using Xunit; + +/// +/// Verifies broad managed EPSG catalog instantiation coverage and representative structural invariants. +/// +public class EpsgCatalogCoverageTests +{ + private static readonly Dictionary ExpectedKindCounts = + new Dictionary + { + [EpsgCoordinateSystemKind.Geographic2D] = 900, + [EpsgCoordinateSystemKind.Geocentric] = 252, + [EpsgCoordinateSystemKind.Projected] = 5398, + [EpsgCoordinateSystemKind.Vertical] = 265, + [EpsgCoordinateSystemKind.Compound] = 402, + }; + + private static readonly Lazy> CatalogCoordinateReferences = new(GetCatalogCoordinateReferences); + private static readonly Lazy> InstantiatedCoordinateSystems = new(GetInstantiatedCoordinateSystems); + + /// + /// Verifies that the managed EPSG provider instantiates every coordinate reference in the generated catalog with the expected runtime type. + /// + [Fact] + public void ManagedProviderShouldInstantiateEveryCatalogCoordinateReference() + { + Dictionary instantiated = InstantiatedCoordinateSystems.Value; + List catalog = CatalogCoordinateReferences.Value; + List failures = []; + var instantiatedByKind = new Dictionary(); + + foreach (CatalogCoordinateReference entry in catalog) + { + if (!instantiated.TryGetValue(entry.Srid, out CoordinateSystem? coordinateSystem)) + { + failures.Add($"{entry.Srid} ({entry.Kind}) was not instantiated."); + continue; + } + + if (!CoordinateSystemMatchesKind(coordinateSystem, entry.Kind)) + { + failures.Add($"{entry.Srid} expected {entry.Kind} but instantiated as {coordinateSystem.GetType().Name}."); + continue; + } + + instantiatedByKind[entry.Kind] = GetKindCount(instantiatedByKind, entry.Kind) + 1; + } + + Assert.True(failures.Count == 0, BuildFailureMessage(failures)); + Assert.Equal(EpsgGeneratedCatalog.CoordinateReferenceCount, instantiated.Count); + + foreach (KeyValuePair expected in ExpectedKindCounts) + { + Assert.Equal(expected.Value, GetKindCount(instantiatedByKind, expected.Key)); + } + } + + /// + /// Verifies that all generated compound coordinate systems instantiate and retain the expected horizontal-plus-vertical structure. + /// + [Fact] + public void ManagedProviderShouldInstantiateEveryCompoundCoordinateReferenceWithExpectedStructure() + { + Dictionary instantiated = InstantiatedCoordinateSystems.Value; + var compounds = new List<(int Srid, CompoundCoordinateSystem CoordinateSystem)>(); + + foreach (CatalogCoordinateReference entry in CatalogCoordinateReferences.Value.Where(entry => entry.Kind == EpsgCoordinateSystemKind.Compound)) + { + Assert.True(instantiated.TryGetValue(entry.Srid, out CoordinateSystem? coordinateSystem), $"Compound SRID {entry.Srid} was not instantiated."); + compounds.Add((entry.Srid, Assert.IsType(coordinateSystem))); + } + + Assert.Equal(ExpectedKindCounts[EpsgCoordinateSystemKind.Compound], compounds.Count); + + Assert.All(compounds, compoundEntry => + { + CompoundCoordinateSystem compound = compoundEntry.CoordinateSystem; + VerticalCoordinateSystem vertical = Assert.IsType(compound.TailCoordinateSystem); + + Assert.Equal(compoundEntry.Srid, compound.AuthorityCode); + Assert.True(compound.HeadCoordinateSystem.Dimension >= 2); + Assert.Equal(1, vertical.Dimension); + Assert.Equal(compound.HeadCoordinateSystem.Dimension + vertical.Dimension, compound.Dimension); + }); + } + + /// + /// Verifies that generated vertical CRS with a downward axis are classified as depth datums instead of generic geoid-model-derived datums. + /// + [Fact] + public void ManagedProviderShouldClassifyDownAxisVerticalCoordinateSystemsAsDepth() + { + Dictionary instantiated = InstantiatedCoordinateSystems.Value; + List downAxisVerticalCoordinateSystems = []; + + foreach (CatalogCoordinateReference entry in CatalogCoordinateReferences.Value.Where(entry => entry.Kind == EpsgCoordinateSystemKind.Vertical)) + { + Assert.True(instantiated.TryGetValue(entry.Srid, out CoordinateSystem? coordinateSystem), $"Vertical SRID {entry.Srid} was not instantiated."); + VerticalCoordinateSystem vertical = Assert.IsType(coordinateSystem); + if (vertical.GetAxis(0).Orientation == AxisOrientationEnum.Down) + { + downAxisVerticalCoordinateSystems.Add(vertical); + } + } + + Assert.Equal(24, downAxisVerticalCoordinateSystems.Count); + Assert.All(downAxisVerticalCoordinateSystems, coordinateSystem => + Assert.Equal(DatumType.VD_Depth, coordinateSystem.VerticalDatum.DatumType)); + } + + private static List GetCatalogCoordinateReferences() + { + var result = new List(EpsgGeneratedCatalog.CoordinateReferenceCount); + for (int cacheIndex = 0; cacheIndex < EpsgGeneratedCatalog.CoordinateReferenceCount; cacheIndex++) + { + if (!EpsgGeneratedCatalog.TryGetCoordinateSridByCacheIndex(cacheIndex, out int srid)) + { + throw new InvalidOperationException($"Catalog cache index {cacheIndex} did not map to an SRID."); + } + + if (!EpsgGeneratedCatalog.TryGetCoordinateReference(srid, out EpsgCoordinateReferenceRecord reference, out _)) + { + throw new InvalidOperationException($"Catalog SRID {srid} could not be resolved back to a coordinate reference."); + } + + result.Add(new CatalogCoordinateReference(srid, reference.Kind)); + } + + return result; + } + + private static Dictionary GetInstantiatedCoordinateSystems() + { + return new ManagedCoordinateSystemDefinitionProvider() + .GetCoordinateSystems() + .ToDictionary(entry => entry.Srid, entry => entry.CoordinateSystem); + } + + private static bool CoordinateSystemMatchesKind(CoordinateSystem coordinateSystem, EpsgCoordinateSystemKind kind) + { + return coordinateSystem switch + { + GeographicCoordinateSystem when kind == EpsgCoordinateSystemKind.Geographic2D => true, + GeocentricCoordinateSystem when kind == EpsgCoordinateSystemKind.Geocentric => true, + ProjectedCoordinateSystem when kind == EpsgCoordinateSystemKind.Projected => true, + VerticalCoordinateSystem when kind == EpsgCoordinateSystemKind.Vertical => true, + CompoundCoordinateSystem when kind == EpsgCoordinateSystemKind.Compound => true, + _ => false, + }; + } + + private static string BuildFailureMessage(List failures) + { + const int failurePreviewLimit = 20; + IEnumerable preview = failures.Take(failurePreviewLimit); + string suffix = failures.Count > failurePreviewLimit + ? $"{Environment.NewLine}... and {failures.Count - failurePreviewLimit} more." + : string.Empty; + return $"Managed EPSG provider failed to instantiate {failures.Count} coordinate references:{Environment.NewLine}{string.Join(Environment.NewLine, preview)}{suffix}"; + } + + private static int GetKindCount(Dictionary counts, EpsgCoordinateSystemKind kind) + { + return counts.TryGetValue(kind, out int count) ? count : 0; + } + + private readonly record struct CatalogCoordinateReference(int Srid, EpsgCoordinateSystemKind Kind); +} diff --git a/test/ProjNet.Tests/Generated/StructuredEpsgCatalogTests.cs b/test/ProjNet.Tests/Generated/StructuredEpsgCatalogTests.cs new file mode 100644 index 00000000..412d1ba3 --- /dev/null +++ b/test/ProjNet.Tests/Generated/StructuredEpsgCatalogTests.cs @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests.Generated; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using ProjNet.Data; +using ProjNet.Data.Generated; +using Xunit; + +/// +/// Tests for the structured EPSG catalog, covering generated lookup behaviour, cache layout, and provider contracts. +/// +public class StructuredEpsgCatalogTests +{ + /// + /// Verifies that EpsgGeneratedCatalog does not expose a StringPool field. + /// + [Fact] + public void GeneratedCatalogShouldNotExposeExplicitStringPool() + { + FieldInfo? stringPoolField = typeof(EpsgGeneratedCatalog).GetField("StringPool", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + Assert.Null(stringPoolField); + } + + /// + /// Verifies that EpsgGeneratedCatalog resolves SRID 4326 through a switch-mapped lookup and that the returned cache index round-trips correctly. + /// + [Fact] + public void GeneratedCatalogShouldExposeSwitchMappedSridLookup() + { + bool found = EpsgGeneratedCatalog.TryGetCoordinateReference(4326, out EpsgCoordinateReferenceRecord reference, out int cacheIndex); + + Assert.True(found); + Assert.Equal(4326, reference.Srid); + Assert.True(cacheIndex >= 0); + Assert.True(EpsgGeneratedCatalog.TryGetCoordinateSridByCacheIndex(cacheIndex, out int mappedSrid)); + Assert.Equal(4326, mappedSrid); + } + + /// + /// Verifies that EpsgGeneratedCatalog does not expose a CoordinateReferenceSrids array field. + /// + [Fact] + public void GeneratedCatalogShouldNotExposeSridArray() + { + FieldInfo? sridArrayField = typeof(EpsgGeneratedCatalog).GetField("CoordinateReferenceSrids", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + Assert.Null(sridArrayField); + } + + /// + /// Verifies that EpsgCoordinateSystemFactory holds no static Dictionary<,> fields, confirming it does not rely on dictionary-based lookup caches. + /// + [Fact] + public void CoordinateSystemFactoryShouldNotUseDictionaryLookupCaches() + { + var dictionaryFields = typeof(EpsgCoordinateSystemFactory) + .GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + .Where(field => field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + .ToList(); + + Assert.Empty(dictionaryFields); + } + + /// + /// Verifies that ManagedCoordinateSystemDefinitionProvider implements IManagedCoordinateSystemProvider and exposes more than 7,000 coordinate systems, including SRID 4326 and 3857. + /// + [Fact] + public void ManagedProviderShouldExposeStructuredCoordinateSystems() + { + var provider = new ManagedCoordinateSystemDefinitionProvider(); + IManagedCoordinateSystemProvider managedProvider = Assert.IsType(provider, exactMatch: false); + + var coordinateSystems = managedProvider.GetCoordinateSystems().ToList(); + Assert.True(coordinateSystems.Count > 7000); + Assert.Contains(coordinateSystems, item => item.Srid == 4326); + Assert.Contains(coordinateSystems, item => item.Srid == 3857); + } + + /// + /// Verifies that EpsgGeneratedCatalog does not expose Conversions, ConversionParameters, or ExplicitOperations array fields. + /// + [Fact] + public void GeneratedCatalogShouldNotExposeConversionAndExplicitOperationArrays() + { + FieldInfo? conversionsField = typeof(EpsgGeneratedCatalog).GetField("Conversions", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + FieldInfo? conversionParametersField = typeof(EpsgGeneratedCatalog).GetField("ConversionParameters", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + FieldInfo? explicitOperationsField = typeof(EpsgGeneratedCatalog).GetField("ExplicitOperations", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + + Assert.Null(conversionsField); + Assert.Null(conversionParametersField); + Assert.Null(explicitOperationsField); + } + + /// + /// Verifies that operation arrays and explicit-operation lookup moved to EpsgGeneratedOperationsCatalog rather than living on EpsgGeneratedCatalog. + /// + [Fact] + public void GeneratedOperationArtifactsShouldLiveInDedicatedOperationsCatalog() + { + FieldInfo? catalogOperationsField = typeof(EpsgGeneratedCatalog).GetField("Operations", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + FieldInfo? catalogOperationParametersField = typeof(EpsgGeneratedCatalog).GetField("OperationParameters", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + MethodInfo? catalogExplicitMethod = typeof(EpsgGeneratedCatalog).GetMethod("TryGetExplicitOperationParameters", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + FieldInfo? dedicatedOperationsField = typeof(EpsgGeneratedOperationsCatalog).GetField("Operations", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + FieldInfo? dedicatedOperationParametersField = typeof(EpsgGeneratedOperationsCatalog).GetField("OperationParameters", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + MethodInfo? dedicatedExplicitMethod = typeof(EpsgGeneratedOperationsCatalog).GetMethod("TryGetExplicitOperationParameters", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + + Assert.Null(catalogOperationsField); + Assert.Null(catalogOperationParametersField); + Assert.Null(catalogExplicitMethod); + Assert.NotNull(dedicatedOperationsField); + Assert.NotNull(dedicatedOperationParametersField); + Assert.NotNull(dedicatedExplicitMethod); + } + + /// + /// Verifies that EpsgGeneratedCatalog resolves the projected CRS for SRID 3857 through a switch-based conversion lookup and that all associated conversion parameters are present and named. + /// + [Fact] + public void GeneratedCatalogShouldExposeSwitchBasedConversionLookup() + { + bool hasReference = EpsgGeneratedCatalog.TryGetCoordinateReference(3857, out EpsgCoordinateReferenceRecord reference, out _); + Assert.True(hasReference); + Assert.Equal(2, (int)reference.Kind); + + bool hasProjectedRecord = EpsgGeneratedCatalog.TryGetProjectedCrs(reference.RecordIndex, out EpsgProjectedCrsRecord projectedRecord); + Assert.True(hasProjectedRecord); + + bool hasConversion = EpsgGeneratedCatalog.TryGetConversion(projectedRecord.ConversionCode, out EpsgConversionRecord conversion); + Assert.True(hasConversion); + Assert.True(conversion.ParameterCount > 0); + + for (int i = 0; i < conversion.ParameterCount; i++) + { + bool hasParameter = EpsgGeneratedCatalog.TryGetConversionParameter(projectedRecord.ConversionCode, i, out EpsgConversionParameterRecord parameter); + Assert.True(hasParameter); + Assert.False(string.IsNullOrWhiteSpace(parameter.Name)); + } + + Assert.False(EpsgGeneratedCatalog.TryGetConversionParameter(projectedRecord.ConversionCode, conversion.ParameterCount, out _)); + Assert.False(EpsgGeneratedCatalog.TryGetConversion(-1, out _)); + } + + /// + /// Verifies that every projected coordinate reference in the catalog has a consistent conversion record with valid, named parameters. + /// + [Fact] + public void GeneratedCatalogProjectedConversionsShouldBeConsistentAcrossCatalog() + { + for (int cacheIndex = 0; cacheIndex < EpsgGeneratedCatalog.CoordinateReferenceCount; cacheIndex++) + { + Assert.True(EpsgGeneratedCatalog.TryGetCoordinateSridByCacheIndex(cacheIndex, out int srid)); + Assert.True(EpsgGeneratedCatalog.TryGetCoordinateReference(srid, out EpsgCoordinateReferenceRecord reference, out _)); + + if (reference.Kind != EpsgCoordinateSystemKind.Projected) + { + continue; + } + + Assert.True(EpsgGeneratedCatalog.TryGetProjectedCrs(reference.RecordIndex, out EpsgProjectedCrsRecord projectedRecord)); + Assert.True(EpsgGeneratedCatalog.TryGetConversion(projectedRecord.ConversionCode, out EpsgConversionRecord conversion)); + + for (int parameterIndex = 0; parameterIndex < conversion.ParameterCount; parameterIndex++) + { + Assert.True(EpsgGeneratedCatalog.TryGetConversionParameter(projectedRecord.ConversionCode, parameterIndex, out EpsgConversionParameterRecord parameter)); + Assert.False(string.IsNullOrWhiteSpace(parameter.Name)); + } + + Assert.False(EpsgGeneratedCatalog.TryGetConversionParameter(projectedRecord.ConversionCode, conversion.ParameterCount, out _)); + } + } + + /// + /// Verifies that EpsgGeneratedCatalog resolves explicit operation parameters via a fast-path lookup and that the returned translation values are valid numbers. + /// + [Fact] + public void GeneratedCatalogShouldExposeExplicitOperationFastPath() + { + EpsgOperationRecord explicitOperation = EpsgGeneratedOperationsCatalog.Operations.First(record => + record.MethodName.Contains("Geocentric translations", StringComparison.OrdinalIgnoreCase) + || record.MethodName.Contains("Position Vector transformation", StringComparison.OrdinalIgnoreCase) + || record.MethodName.Contains("Coordinate Frame rotation", StringComparison.OrdinalIgnoreCase)); + + bool found = EpsgGeneratedOperationsCatalog.TryGetExplicitOperationParameters(explicitOperation.OperationCode, out EpsgExplicitOperationRecord parameters); + + Assert.True(found); + Assert.Equal(explicitOperation.OperationCode, parameters.OperationCode); + Assert.False(double.IsNaN(parameters.Dx)); + Assert.False(double.IsNaN(parameters.Dy)); + Assert.False(double.IsNaN(parameters.Dz)); + } + + /// + /// Verifies that concatenated operations do not carry a synthetic method name from their first sub-step. + /// + [Fact] + public void GeneratedOperationsCatalogShouldLeaveConcatenatedMethodNamesEmpty() + { + EpsgOperationRecord concatenatedOperation = EpsgGeneratedOperationsCatalog.Operations.First(record => record.OperationType == EpsgOperationType.ConcatenatedOperation); + + Assert.True(string.IsNullOrEmpty(concatenatedOperation.MethodName)); + } + + /// + /// Verifies that TryGetExplicitOperationParameters returns for an unknown operation code. + /// + [Fact] + public void GeneratedCatalogShouldReturnFalseForUnknownExplicitOperationCode() + { + bool found = EpsgGeneratedOperationsCatalog.TryGetExplicitOperationParameters(-1, out _); + Assert.False(found); + } +} diff --git a/test/ProjNet.Tests/Generated/epsg-wkt-equivalence-fixture.json b/test/ProjNet.Tests/Generated/epsg-wkt-equivalence-fixture.json new file mode 100644 index 00000000..0f1a8e7b --- /dev/null +++ b/test/ProjNet.Tests/Generated/epsg-wkt-equivalence-fixture.json @@ -0,0 +1,202 @@ +[ + { + "srid": 4121, + "wkt": "GEOGCRS[\"GGRS87\",DATUM[\"Greek Geodetic Reference System 1987\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6121]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4121]]" + }, + { + "srid": 4230, + "wkt": "GEOGCRS[\"ED50\",DATUM[\"European Datum 1950\",ELLIPSOID[\"International 1924\",6378388,297,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7022]],ID[\"EPSG\",6230]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4230]]" + }, + { + "srid": 4258, + "wkt": "GEOGCRS[\"ETRS89\",ENSEMBLE[\"European Terrestrial Reference System 1989 ensemble\",MEMBER[\"European Terrestrial Reference Frame 1989\",ID[\"EPSG\",1178]],MEMBER[\"European Terrestrial Reference Frame 1990\",ID[\"EPSG\",1179]],MEMBER[\"European Terrestrial Reference Frame 1991\",ID[\"EPSG\",1180]],MEMBER[\"European Terrestrial Reference Frame 1992\",ID[\"EPSG\",1181]],MEMBER[\"European Terrestrial Reference Frame 1993\",ID[\"EPSG\",1182]],MEMBER[\"European Terrestrial Reference Frame 1994\",ID[\"EPSG\",1183]],MEMBER[\"European Terrestrial Reference Frame 1996\",ID[\"EPSG\",1184]],MEMBER[\"European Terrestrial Reference Frame 1997\",ID[\"EPSG\",1185]],MEMBER[\"European Terrestrial Reference Frame 2000\",ID[\"EPSG\",1186]],MEMBER[\"European Terrestrial Reference Frame 2005\",ID[\"EPSG\",1204]],MEMBER[\"European Terrestrial Reference Frame 2014\",ID[\"EPSG\",1206]],MEMBER[\"European Terrestrial Reference Frame 2020\",ID[\"EPSG\",1382]],MEMBER[\"ETRS89-DNK\",ID[\"EPSG\",1412]],MEMBER[\"Estonia 1997\",ID[\"EPSG\",6180]],MEMBER[\"Albanian Geodetic Reference Frame 2010\",ID[\"EPSG\",1429]],MEMBER[\"ETRS89-ALB [CORS]\",ID[\"EPSG\",1430]],MEMBER[\"ETRS89-AUT [2002]\",ID[\"EPSG\",1431]],MEMBER[\"Belgian Reference Frame 2002\",ID[\"EPSG\",1432]],MEMBER[\"Belgian Reference Frame 2011\",ID[\"EPSG\",1447]],MEMBER[\"BH_ETRS89\",ID[\"EPSG\",1358]],MEMBER[\"Bulgaria Geodetic System 2005\",ID[\"EPSG\",1167]],MEMBER[\"Croatian Terrestrial Reference System 1996\",ID[\"EPSG\",6761]],MEMBER[\"AGRS2010 (ETRF2000)\",ID[\"EPSG\",1427]],MEMBER[\"CROPOS\",ID[\"EPSG\",1445]],MEMBER[\"ETRF2000 Poland\",ID[\"EPSG\",1305]],MEMBER[\"ETRS89/DREF91 Realization 2016\",ID[\"EPSG\",1353]],MEMBER[\"ETRS89/DREF91 Realization 2025\",ID[\"EPSG\",1446]],MEMBER[\"ETRS89-CZE [2007]\",ID[\"EPSG\",1433]],MEMBER[\"ETRS89-ESP [REGENTE]\",ID[\"EPSG\",1441]],MEMBER[\"ETRS89-ESP [ERGNSS]\",ID[\"EPSG\",1442]],MEMBER[\"ETRS89-FRO [2008]\",ID[\"EPSG\",1436]],MEMBER[\"ETRS89-GRC [HTRS07]\",ID[\"EPSG\",1437]],MEMBER[\"ETRS89-HUN [ETRF2000]\",ID[\"EPSG\",1444]],MEMBER[\"ETRS89-IRE [ETRF2000]\",ID[\"EPSG\",6173]],MEMBER[\"ETRS89-MKD [EUREF-MAK2010]\",ID[\"EPSG\",1438]],MEMBER[\"EUREF89\",ID[\"EPSG\",1407]],MEMBER[\"ETRS89-PRT [1995]\",ID[\"EPSG\",1439]],MEMBER[\"ETRS89-ROU [ETRF2000]\",ID[\"EPSG\",1440]],MEMBER[\"ETRS89-SVK [SKTRF09]\",ID[\"EPSG\",1434]],MEMBER[\"ETRS89-SVK [SKTRF2022]\",ID[\"EPSG\",1435]],MEMBER[\"EUREF-FIN\",ID[\"EPSG\",1391]],MEMBER[\"Istituto Geografico Militare 1995\",ID[\"EPSG\",6670]],MEMBER[\"Kosovo Reference System 2001\",ID[\"EPSG\",1251]],MEMBER[\"Latvian geodetic coordinate system 1992\",ID[\"EPSG\",6661]],MEMBER[\"Latvian coordinate system 2020\",ID[\"EPSG\",1356]],MEMBER[\"Lithuania 1994 (ETRS89)\",ID[\"EPSG\",6126]],MEMBER[\"MOLDREF99\",ID[\"EPSG\",1032]],MEMBER[\"OSNet v2009\",ID[\"EPSG\",1425]],MEMBER[\"Reseau Geodesique Francais 1993 v1\",ID[\"EPSG\",6171]],MEMBER[\"Reseau Geodesique Francais 1993 v2\",ID[\"EPSG\",1312]],MEMBER[\"Reseau Geodesique Francais 1993 v2b\",ID[\"EPSG\",1313]],MEMBER[\"Rete Dinamica Nazionale 2008\",ID[\"EPSG\",1132]],MEMBER[\"Serbian Reference Network 1998\",ID[\"EPSG\",1034]],MEMBER[\"Serbian Spatial Reference System 2000\",ID[\"EPSG\",1214]],MEMBER[\"Slovenia Geodetic Datum 1996\",ID[\"EPSG\",6765]],MEMBER[\"SWEREF 99\",ID[\"EPSG\",6619]],MEMBER[\"Swiss Terrestrial Reference Frame 1995\",ID[\"EPSG\",1449]],MEMBER[\"ETRS89-LUX [ETRF2000]\",ID[\"EPSG\",1453]],MEMBER[\"Nordic Geodetic Commission ETRF14\",ID[\"EPSG\",1403]],ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ENSEMBLEACCURACY[0.1],ID[\"EPSG\",6258]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4258]]" + }, + { + "srid": 4267, + "wkt": "GEOGCRS[\"NAD27\",DATUM[\"North American Datum 1927\",ELLIPSOID[\"Clarke 1866\",6378206.4,294.978698213898,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7008]],ID[\"EPSG\",6267]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4267]]" + }, + { + "srid": 4269, + "wkt": "GEOGCRS[\"NAD83\",DATUM[\"North American Datum 1983\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6269]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4269]]" + }, + { + "srid": 4277, + "wkt": "GEOGCRS[\"OSGB36\",DATUM[\"Ordnance Survey of Great Britain 1936\",ELLIPSOID[\"Airy 1830\",6377563.396,299.3249646,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7001]],ID[\"EPSG\",6277]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4277]]" + }, + { + "srid": 4283, + "wkt": "GEOGCRS[\"GDA94\",DATUM[\"Geocentric Datum of Australia 1994\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6283]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4283]]" + }, + { + "srid": 4314, + "wkt": "GEOGCRS[\"DHDN\",DATUM[\"Deutsches Hauptdreiecksnetz\",ELLIPSOID[\"Bessel 1841\",6377397.155,299.1528128,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7004]],ID[\"EPSG\",6314]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4314]]" + }, + { + "srid": 4326, + "wkt": "GEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4326]]" + }, + { + "srid": 4807, + "wkt": "GEOGCRS[\"NTF (Paris)\",DATUM[\"Nouvelle Triangulation Francaise (Paris)\",ELLIPSOID[\"Clarke 1880 (IGN)\",6378249.2,293.466021293627,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7011]],ID[\"EPSG\",6807]],PRIMEM[\"Paris\",0.040792344,ANGLEUNIT[\"radian\",1,ID[\"EPSG\",9101]],ID[\"EPSG\",8903]],CS[ellipsoidal,2,ID[\"EPSG\",6403]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"grad\",0.015707963267949,ID[\"EPSG\",9105]],ID[\"EPSG\",4807]]" + }, + { + "srid": 2193, + "wkt": "PROJCRS[\"NZGD2000 / New Zealand Transverse Mercator 2000\",BASEGEOGCRS[\"NZGD2000\",DATUM[\"New Zealand Geodetic Datum 2000\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6167]],ID[\"EPSG\",4167]],CONVERSION[\"New Zealand Transverse Mercator 2000\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",173,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",1600000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",10000000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19971]],CS[Cartesian,2,ID[\"EPSG\",4500]],AXIS[\"Northing (N)\",north],AXIS[\"Easting (E)\",east],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",2193]]" + }, + { + "srid": 27700, + "wkt": "PROJCRS[\"OSGB36 / British National Grid\",BASEGEOGCRS[\"OSGB36\",DATUM[\"Ordnance Survey of Great Britain 1936\",ELLIPSOID[\"Airy 1830\",6377563.396,299.3249646,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7001]],ID[\"EPSG\",6277]],ID[\"EPSG\",4277]],CONVERSION[\"British National Grid\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",49,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",-2,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996012717,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",400000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",-100000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19916]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",27700]]" + }, + { + "srid": 3031, + "wkt": "PROJCRS[\"WGS 84 / Antarctic Polar Stereographic\",BASEGEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],ID[\"EPSG\",4326]],CONVERSION[\"Antarctic Polar Stereographic\",METHOD[\"Polar Stereographic (variant B)\",ID[\"EPSG\",9829]],PARAMETER[\"Latitude of standard parallel\",-71,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8832]],PARAMETER[\"Longitude of origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8833]],PARAMETER[\"False easting\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19992]],CS[Cartesian,2,ID[\"EPSG\",4490]],AXIS[\"Easting (E)\",North,MERIDIAN[90.0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]]]],AXIS[\"Northing (N)\",North,MERIDIAN[0.0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]]]],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3031]]" + }, + { + "srid": 3035, + "wkt": "PROJCRS[\"ETRS89-extended / LAEA Europe\",BASEGEOGCRS[\"ETRS89\",ENSEMBLE[\"European Terrestrial Reference System 1989 ensemble\",MEMBER[\"European Terrestrial Reference Frame 1989\",ID[\"EPSG\",1178]],MEMBER[\"European Terrestrial Reference Frame 1990\",ID[\"EPSG\",1179]],MEMBER[\"European Terrestrial Reference Frame 1991\",ID[\"EPSG\",1180]],MEMBER[\"European Terrestrial Reference Frame 1992\",ID[\"EPSG\",1181]],MEMBER[\"European Terrestrial Reference Frame 1993\",ID[\"EPSG\",1182]],MEMBER[\"European Terrestrial Reference Frame 1994\",ID[\"EPSG\",1183]],MEMBER[\"European Terrestrial Reference Frame 1996\",ID[\"EPSG\",1184]],MEMBER[\"European Terrestrial Reference Frame 1997\",ID[\"EPSG\",1185]],MEMBER[\"European Terrestrial Reference Frame 2000\",ID[\"EPSG\",1186]],MEMBER[\"European Terrestrial Reference Frame 2005\",ID[\"EPSG\",1204]],MEMBER[\"European Terrestrial Reference Frame 2014\",ID[\"EPSG\",1206]],MEMBER[\"European Terrestrial Reference Frame 2020\",ID[\"EPSG\",1382]],MEMBER[\"ETRS89-DNK\",ID[\"EPSG\",1412]],MEMBER[\"Estonia 1997\",ID[\"EPSG\",6180]],MEMBER[\"Albanian Geodetic Reference Frame 2010\",ID[\"EPSG\",1429]],MEMBER[\"ETRS89-ALB [CORS]\",ID[\"EPSG\",1430]],MEMBER[\"ETRS89-AUT [2002]\",ID[\"EPSG\",1431]],MEMBER[\"Belgian Reference Frame 2002\",ID[\"EPSG\",1432]],MEMBER[\"Belgian Reference Frame 2011\",ID[\"EPSG\",1447]],MEMBER[\"BH_ETRS89\",ID[\"EPSG\",1358]],MEMBER[\"Bulgaria Geodetic System 2005\",ID[\"EPSG\",1167]],MEMBER[\"Croatian Terrestrial Reference System 1996\",ID[\"EPSG\",6761]],MEMBER[\"AGRS2010 (ETRF2000)\",ID[\"EPSG\",1427]],MEMBER[\"CROPOS\",ID[\"EPSG\",1445]],MEMBER[\"ETRF2000 Poland\",ID[\"EPSG\",1305]],MEMBER[\"ETRS89/DREF91 Realization 2016\",ID[\"EPSG\",1353]],MEMBER[\"ETRS89/DREF91 Realization 2025\",ID[\"EPSG\",1446]],MEMBER[\"ETRS89-CZE [2007]\",ID[\"EPSG\",1433]],MEMBER[\"ETRS89-ESP [REGENTE]\",ID[\"EPSG\",1441]],MEMBER[\"ETRS89-ESP [ERGNSS]\",ID[\"EPSG\",1442]],MEMBER[\"ETRS89-FRO [2008]\",ID[\"EPSG\",1436]],MEMBER[\"ETRS89-GRC [HTRS07]\",ID[\"EPSG\",1437]],MEMBER[\"ETRS89-HUN [ETRF2000]\",ID[\"EPSG\",1444]],MEMBER[\"ETRS89-IRE [ETRF2000]\",ID[\"EPSG\",6173]],MEMBER[\"ETRS89-MKD [EUREF-MAK2010]\",ID[\"EPSG\",1438]],MEMBER[\"EUREF89\",ID[\"EPSG\",1407]],MEMBER[\"ETRS89-PRT [1995]\",ID[\"EPSG\",1439]],MEMBER[\"ETRS89-ROU [ETRF2000]\",ID[\"EPSG\",1440]],MEMBER[\"ETRS89-SVK [SKTRF09]\",ID[\"EPSG\",1434]],MEMBER[\"ETRS89-SVK [SKTRF2022]\",ID[\"EPSG\",1435]],MEMBER[\"EUREF-FIN\",ID[\"EPSG\",1391]],MEMBER[\"Istituto Geografico Militare 1995\",ID[\"EPSG\",6670]],MEMBER[\"Kosovo Reference System 2001\",ID[\"EPSG\",1251]],MEMBER[\"Latvian geodetic coordinate system 1992\",ID[\"EPSG\",6661]],MEMBER[\"Latvian coordinate system 2020\",ID[\"EPSG\",1356]],MEMBER[\"Lithuania 1994 (ETRS89)\",ID[\"EPSG\",6126]],MEMBER[\"MOLDREF99\",ID[\"EPSG\",1032]],MEMBER[\"OSNet v2009\",ID[\"EPSG\",1425]],MEMBER[\"Reseau Geodesique Francais 1993 v1\",ID[\"EPSG\",6171]],MEMBER[\"Reseau Geodesique Francais 1993 v2\",ID[\"EPSG\",1312]],MEMBER[\"Reseau Geodesique Francais 1993 v2b\",ID[\"EPSG\",1313]],MEMBER[\"Rete Dinamica Nazionale 2008\",ID[\"EPSG\",1132]],MEMBER[\"Serbian Reference Network 1998\",ID[\"EPSG\",1034]],MEMBER[\"Serbian Spatial Reference System 2000\",ID[\"EPSG\",1214]],MEMBER[\"Slovenia Geodetic Datum 1996\",ID[\"EPSG\",6765]],MEMBER[\"SWEREF 99\",ID[\"EPSG\",6619]],MEMBER[\"Swiss Terrestrial Reference Frame 1995\",ID[\"EPSG\",1449]],MEMBER[\"ETRS89-LUX [ETRF2000]\",ID[\"EPSG\",1453]],MEMBER[\"Nordic Geodetic Commission ETRF14\",ID[\"EPSG\",1403]],ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ENSEMBLEACCURACY[0.1],ID[\"EPSG\",6258]],ID[\"EPSG\",4258]],CONVERSION[\"Europe Equal Area 2001\",METHOD[\"Lambert Azimuthal Equal Area\",ID[\"EPSG\",9820]],PARAMETER[\"Latitude of natural origin\",52,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",10,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"False easting\",4321000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",3210000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19986]],CS[Cartesian,2,ID[\"EPSG\",4532]],AXIS[\"Northing (Y)\",north],AXIS[\"Easting (X)\",east],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3035]]" + }, + { + "srid": 3413, + "wkt": "PROJCRS[\"WGS 84 / NSIDC Sea Ice Polar Stereographic North\",BASEGEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],ID[\"EPSG\",4326]],CONVERSION[\"US NSIDC Sea Ice polar stereographic north\",METHOD[\"Polar Stereographic (variant B)\",ID[\"EPSG\",9829]],PARAMETER[\"Latitude of standard parallel\",70,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8832]],PARAMETER[\"Longitude of origin\",-45,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8833]],PARAMETER[\"False easting\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19865]],CS[Cartesian,2,ID[\"EPSG\",4468]],AXIS[\"Easting (X)\",South,MERIDIAN[45.0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]]]],AXIS[\"Northing (Y)\",South,MERIDIAN[135.0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]]]],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3413]]" + }, + { + "srid": 3857, + "wkt": "PROJCRS[\"WGS 84 / Pseudo-Mercator\",BASEGEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],ID[\"EPSG\",4326]],CONVERSION[\"Popular Visualisation Pseudo-Mercator\",METHOD[\"Popular Visualisation Pseudo Mercator\",ID[\"EPSG\",1024]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"False easting\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",3856]],CS[Cartesian,2,ID[\"EPSG\",4499]],AXIS[\"Easting (X)\",east],AXIS[\"Northing (Y)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3857]]" + }, + { + "srid": 5514, + "wkt": "PROJCRS[\"S-JTSK / Krovak East North\",BASEGEOGCRS[\"S-JTSK\",DATUM[\"System of the Unified Trigonometrical Cadastral Network\",ELLIPSOID[\"Bessel 1841\",6377397.155,299.1528128,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7004]],ID[\"EPSG\",6156]],ID[\"EPSG\",4156]],CONVERSION[\"Krovak East North (Greenwich)\",METHOD[\"Krovak (North Orientated)\",ID[\"EPSG\",1041]],PARAMETER[\"Latitude of projection centre\",49.5000000000003,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8811]],PARAMETER[\"Longitude of origin\",24.8333333333336,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8833]],PARAMETER[\"Co-latitude of cone axis\",30.2881397527781,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",1036]],PARAMETER[\"Latitude of pseudo standard parallel\",78.5000000000003,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8818]],PARAMETER[\"Scale factor on pseudo standard parallel\",0.9999,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8819]],PARAMETER[\"False easting\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",5510]],CS[Cartesian,2,ID[\"EPSG\",4499]],AXIS[\"Easting (X)\",east],AXIS[\"Northing (Y)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5514]]" + }, + { + "srid": 32632, + "wkt": "PROJCRS[\"WGS 84 / UTM zone 32N\",BASEGEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],ID[\"EPSG\",4326]],CONVERSION[\"UTM zone 32N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",9,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",16032]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",32632]]" + }, + { + "srid": 32633, + "wkt": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],ID[\"EPSG\",4326]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",16033]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",32633]]" + }, + { + "srid": 32733, + "wkt": "PROJCRS[\"WGS 84 / UTM zone 33S\",BASEGEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],ID[\"EPSG\",4326]],CONVERSION[\"UTM zone 33S\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",10000000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",16133]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",32733]]" + }, + { + "srid": 3822, + "wkt": "GEODCRS[\"TWD97\",DATUM[\"Taiwan Datum 1997\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",1026]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",3822]]" + }, + { + "srid": 3887, + "wkt": "GEODCRS[\"IGRS\",DATUM[\"Iraqi Geospatial Reference System\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",1029]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",3887]]" + }, + { + "srid": 4039, + "wkt": "GEODCRS[\"RGRDC 2005\",DATUM[\"Reseau Geodesique de la RDC 2005\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",1033]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4039]]" + }, + { + "srid": 4079, + "wkt": "GEODCRS[\"REGCAN95\",DATUM[\"Red Geodesica de Canarias 1995\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",1035]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4079]]" + }, + { + "srid": 4479, + "wkt": "GEODCRS[\"China Geodetic Coordinate System 2000\",DATUM[\"China 2000\",ELLIPSOID[\"CGCS2000\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",1024]],ID[\"EPSG\",1043]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4479]]" + }, + { + "srid": 4481, + "wkt": "GEODCRS[\"Mexico ITRF92\",DATUM[\"Mexico ITRF92\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",1042]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4481]]" + }, + { + "srid": 4896, + "wkt": "GEODCRS[\"ITRF2005\",DYNAMIC[FRAMEEPOCH[2000.0]],DATUM[\"International Terrestrial Reference Frame 2005\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6896]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4896]]" + }, + { + "srid": 4915, + "wkt": "GEODCRS[\"ITRF93\",DYNAMIC[FRAMEEPOCH[1993.0]],DATUM[\"International Terrestrial Reference Frame 1993\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6652]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4915]]" + }, + { + "srid": 4936, + "wkt": "GEODCRS[\"ETRS89\",ENSEMBLE[\"European Terrestrial Reference System 1989 ensemble\",MEMBER[\"European Terrestrial Reference Frame 1989\",ID[\"EPSG\",1178]],MEMBER[\"European Terrestrial Reference Frame 1990\",ID[\"EPSG\",1179]],MEMBER[\"European Terrestrial Reference Frame 1991\",ID[\"EPSG\",1180]],MEMBER[\"European Terrestrial Reference Frame 1992\",ID[\"EPSG\",1181]],MEMBER[\"European Terrestrial Reference Frame 1993\",ID[\"EPSG\",1182]],MEMBER[\"European Terrestrial Reference Frame 1994\",ID[\"EPSG\",1183]],MEMBER[\"European Terrestrial Reference Frame 1996\",ID[\"EPSG\",1184]],MEMBER[\"European Terrestrial Reference Frame 1997\",ID[\"EPSG\",1185]],MEMBER[\"European Terrestrial Reference Frame 2000\",ID[\"EPSG\",1186]],MEMBER[\"European Terrestrial Reference Frame 2005\",ID[\"EPSG\",1204]],MEMBER[\"European Terrestrial Reference Frame 2014\",ID[\"EPSG\",1206]],MEMBER[\"European Terrestrial Reference Frame 2020\",ID[\"EPSG\",1382]],MEMBER[\"ETRS89-DNK\",ID[\"EPSG\",1412]],MEMBER[\"Estonia 1997\",ID[\"EPSG\",6180]],MEMBER[\"Albanian Geodetic Reference Frame 2010\",ID[\"EPSG\",1429]],MEMBER[\"ETRS89-ALB [CORS]\",ID[\"EPSG\",1430]],MEMBER[\"ETRS89-AUT [2002]\",ID[\"EPSG\",1431]],MEMBER[\"Belgian Reference Frame 2002\",ID[\"EPSG\",1432]],MEMBER[\"Belgian Reference Frame 2011\",ID[\"EPSG\",1447]],MEMBER[\"BH_ETRS89\",ID[\"EPSG\",1358]],MEMBER[\"Bulgaria Geodetic System 2005\",ID[\"EPSG\",1167]],MEMBER[\"Croatian Terrestrial Reference System 1996\",ID[\"EPSG\",6761]],MEMBER[\"AGRS2010 (ETRF2000)\",ID[\"EPSG\",1427]],MEMBER[\"CROPOS\",ID[\"EPSG\",1445]],MEMBER[\"ETRF2000 Poland\",ID[\"EPSG\",1305]],MEMBER[\"ETRS89/DREF91 Realization 2016\",ID[\"EPSG\",1353]],MEMBER[\"ETRS89/DREF91 Realization 2025\",ID[\"EPSG\",1446]],MEMBER[\"ETRS89-CZE [2007]\",ID[\"EPSG\",1433]],MEMBER[\"ETRS89-ESP [REGENTE]\",ID[\"EPSG\",1441]],MEMBER[\"ETRS89-ESP [ERGNSS]\",ID[\"EPSG\",1442]],MEMBER[\"ETRS89-FRO [2008]\",ID[\"EPSG\",1436]],MEMBER[\"ETRS89-GRC [HTRS07]\",ID[\"EPSG\",1437]],MEMBER[\"ETRS89-HUN [ETRF2000]\",ID[\"EPSG\",1444]],MEMBER[\"ETRS89-IRE [ETRF2000]\",ID[\"EPSG\",6173]],MEMBER[\"ETRS89-MKD [EUREF-MAK2010]\",ID[\"EPSG\",1438]],MEMBER[\"EUREF89\",ID[\"EPSG\",1407]],MEMBER[\"ETRS89-PRT [1995]\",ID[\"EPSG\",1439]],MEMBER[\"ETRS89-ROU [ETRF2000]\",ID[\"EPSG\",1440]],MEMBER[\"ETRS89-SVK [SKTRF09]\",ID[\"EPSG\",1434]],MEMBER[\"ETRS89-SVK [SKTRF2022]\",ID[\"EPSG\",1435]],MEMBER[\"EUREF-FIN\",ID[\"EPSG\",1391]],MEMBER[\"Istituto Geografico Militare 1995\",ID[\"EPSG\",6670]],MEMBER[\"Kosovo Reference System 2001\",ID[\"EPSG\",1251]],MEMBER[\"Latvian geodetic coordinate system 1992\",ID[\"EPSG\",6661]],MEMBER[\"Latvian coordinate system 2020\",ID[\"EPSG\",1356]],MEMBER[\"Lithuania 1994 (ETRS89)\",ID[\"EPSG\",6126]],MEMBER[\"MOLDREF99\",ID[\"EPSG\",1032]],MEMBER[\"OSNet v2009\",ID[\"EPSG\",1425]],MEMBER[\"Reseau Geodesique Francais 1993 v1\",ID[\"EPSG\",6171]],MEMBER[\"Reseau Geodesique Francais 1993 v2\",ID[\"EPSG\",1312]],MEMBER[\"Reseau Geodesique Francais 1993 v2b\",ID[\"EPSG\",1313]],MEMBER[\"Rete Dinamica Nazionale 2008\",ID[\"EPSG\",1132]],MEMBER[\"Serbian Reference Network 1998\",ID[\"EPSG\",1034]],MEMBER[\"Serbian Spatial Reference System 2000\",ID[\"EPSG\",1214]],MEMBER[\"Slovenia Geodetic Datum 1996\",ID[\"EPSG\",6765]],MEMBER[\"SWEREF 99\",ID[\"EPSG\",6619]],MEMBER[\"Swiss Terrestrial Reference Frame 1995\",ID[\"EPSG\",1449]],MEMBER[\"ETRS89-LUX [ETRF2000]\",ID[\"EPSG\",1453]],MEMBER[\"Nordic Geodetic Commission ETRF14\",ID[\"EPSG\",1403]],ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ENSEMBLEACCURACY[0.1],ID[\"EPSG\",6258]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4936]]" + }, + { + "srid": 4978, + "wkt": "GEODCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],CS[Cartesian,3,ID[\"EPSG\",6500]],AXIS[\"Geocentric X (X)\",geocentricX,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Y (Y)\",geocentricY,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],AXIS[\"Geocentric Z (Z)\",geocentricZ,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]]],ID[\"EPSG\",4978]]" + }, + { + "srid": 3855, + "wkt": "VERTCRS[\"EGM2008 height\",VDATUM[\"EGM2008 geoid\",ID[\"EPSG\",1027]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3855]]" + }, + { + "srid": 3900, + "wkt": "VERTCRS[\"N2000 height\",VDATUM[\"N2000\",ID[\"EPSG\",1030]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3900]]" + }, + { + "srid": 4440, + "wkt": "VERTCRS[\"NZVD2009 height\",VDATUM[\"New Zealand Vertical Datum 2009\",ID[\"EPSG\",1039]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"NZGD2000 to NZVD2009 height (2)\",ID[\"EPSG\",9325]],ID[\"EPSG\",4440]]" + }, + { + "srid": 5608, + "wkt": "VERTCRS[\"IGLD 1955 height\",VDATUM[\"International Great Lakes Datum 1955\",ID[\"EPSG\",5204]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5608]]" + }, + { + "srid": 5701, + "wkt": "VERTCRS[\"ODN height\",VDATUM[\"Ordnance Datum Newlyn\",ID[\"EPSG\",5101]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"ETRS89-GBR [OSNet v2009] to ODN height (2)\",ID[\"EPSG\",7711]],GEOIDMODEL[\"ETRS89 to ODN height (1)\",ID[\"EPSG\",10021]],ID[\"EPSG\",5701]]" + }, + { + "srid": 5714, + "wkt": "VERTCRS[\"MSL height\",VDATUM[\"Mean Sea Level\",ID[\"EPSG\",5100]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5714]]" + }, + { + "srid": 5739, + "wkt": "VERTCRS[\"HKCD depth\",VDATUM[\"Hong Kong Chart Datum\",ID[\"EPSG\",5136]],CS[vertical,1,ID[\"EPSG\",6498]],AXIS[\"Depth (D)\",down],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5739]]" + }, + { + "srid": 5861, + "wkt": "VERTCRS[\"LAT depth\",VDATUM[\"Lowest Astronomical Tide\",ID[\"EPSG\",1080]],CS[vertical,1,ID[\"EPSG\",6498]],AXIS[\"Depth (D)\",down],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5861]]" + }, + { + "srid": 10150, + "wkt": "VERTCRS[\"MSL UK & Ireland VORF08 depth\",VDATUM[\"Mean Sea Level UK & Ireland VORF08\",ID[\"EPSG\",1330]],CS[vertical,1,ID[\"EPSG\",6498]],AXIS[\"Depth (D)\",down],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"ETRS89 to MSL UK & Ireland VORF08 depth (1)\",ID[\"EPSG\",10154]],ID[\"EPSG\",10150]]" + }, + { + "srid": 10190, + "wkt": "VERTCRS[\"NGA 2022 height\",VDATUM[\"Nivellement General de l'Algerie 2022\",ID[\"EPSG\",1354]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",10190]]" + }, + { + "srid": 3902, + "wkt": "COMPOUNDCRS[\"ETRS89-FIN [EUREF-FIN] / TM35FIN(N,E) + N60 height\",PROJCRS[\"ETRS89-FIN [EUREF-FIN] / TM35FIN(N,E)\",BASEGEOGCRS[\"ETRS89-FIN [EUREF-FIN]\",DATUM[\"EUREF-FIN\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ANCHOREPOCH[1997],ID[\"EPSG\",1391]],ID[\"EPSG\",10690]],CONVERSION[\"TM35FIN\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",27,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",16065]],CS[Cartesian,2,ID[\"EPSG\",4500]],AXIS[\"Northing (N)\",north],AXIS[\"Easting (E)\",east],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5048]],VERTCRS[\"N60 height\",VDATUM[\"Helsinki 1960\",ID[\"EPSG\",5116]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5717]],ID[\"EPSG\",3902]]" + }, + { + "srid": 3903, + "wkt": "COMPOUNDCRS[\"ETRS89-FIN [EUREF-FIN] / TM35FIN(N,E) + N2000 height\",PROJCRS[\"ETRS89-FIN [EUREF-FIN] / TM35FIN(N,E)\",BASEGEOGCRS[\"ETRS89-FIN [EUREF-FIN]\",DATUM[\"EUREF-FIN\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ANCHOREPOCH[1997],ID[\"EPSG\",1391]],ID[\"EPSG\",10690]],CONVERSION[\"TM35FIN\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",27,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",16065]],CS[Cartesian,2,ID[\"EPSG\",4500]],AXIS[\"Northing (N)\",north],AXIS[\"Easting (E)\",east],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5048]],VERTCRS[\"N2000 height\",VDATUM[\"N2000\",ID[\"EPSG\",1030]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3900]],ID[\"EPSG\",3903]]" + }, + { + "srid": 5318, + "wkt": "COMPOUNDCRS[\"ETRS89-FRO [2008] / Faroe TM + FVR09 height\",PROJCRS[\"ETRS89-FRO [2008] / Faroe TM\",BASEGEOGCRS[\"ETRS89-FRO [2008]\",DATUM[\"ETRS89-FRO [2008]\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ANCHOREPOCH[2008.75],ID[\"EPSG\",1436]],ID[\"EPSG\",11087]],CONVERSION[\"Faroe Transverse Mercator\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",-7,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.999997,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",200000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",-6000000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",5315]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5316]],VERTCRS[\"FVR09 height\",VDATUM[\"Faroe Islands Vertical Reference 2009\",ID[\"EPSG\",1059]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5317]],ID[\"EPSG\",5318]]" + }, + { + "srid": 7405, + "wkt": "COMPOUNDCRS[\"OSGB36 / British National Grid + ODN height\",PROJCRS[\"OSGB36 / British National Grid\",BASEGEOGCRS[\"OSGB36\",DATUM[\"Ordnance Survey of Great Britain 1936\",ELLIPSOID[\"Airy 1830\",6377563.396,299.3249646,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7001]],ID[\"EPSG\",6277]],ID[\"EPSG\",4277]],CONVERSION[\"British National Grid\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",49,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",-2,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996012717,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",400000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",-100000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19916]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",27700]],VERTCRS[\"ODN height\",VDATUM[\"Ordnance Datum Newlyn\",ID[\"EPSG\",5101]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"ETRS89-GBR [OSNet v2009] to ODN height (2)\",ID[\"EPSG\",7711]],GEOIDMODEL[\"ETRS89 to ODN height (1)\",ID[\"EPSG\",10021]],ID[\"EPSG\",5701]],ID[\"EPSG\",7405]]" + }, + { + "srid": 7415, + "wkt": "COMPOUNDCRS[\"Amersfoort / RD New + NAP height\",PROJCRS[\"Amersfoort / RD New\",BASEGEOGCRS[\"Amersfoort\",DATUM[\"Amersfoort\",ELLIPSOID[\"Bessel 1841\",6377397.155,299.1528128,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7004]],ID[\"EPSG\",6289]],DEFININGTRANSFORMATION[\"Amersfoort to ETRS89-NLD [AGRS2010] (9)\",ID[\"EPSG\",9282]],ID[\"EPSG\",4289]],CONVERSION[\"RD New\",METHOD[\"Oblique Stereographic\",ID[\"EPSG\",9809]],PARAMETER[\"Latitude of natural origin\",52.1561605555558,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",5.38763888888917,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9999079,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",155000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",463000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19914]],CS[Cartesian,2,ID[\"EPSG\",1054]],AXIS[\"Easting (x)\",east],AXIS[\"Northing (y)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",28992]],VERTCRS[\"NAP height\",VDATUM[\"Normaal Amsterdams Peil\",ID[\"EPSG\",5109]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",5709]],ID[\"EPSG\",7415]]" + }, + { + "srid": 7956, + "wkt": "COMPOUNDCRS[\"SHMG2015 + SHVD2015 height\",PROJCRS[\"SHMG2015\",BASEGEOGCRS[\"SHGD2015\",DATUM[\"St. Helena Geodetic Datum 2015\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",1174]],ID[\"EPSG\",7886]],CONVERSION[\"UTM zone 30S\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",-3,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",10000000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",16130]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7887]],VERTCRS[\"SHVD2015 height\",VDATUM[\"St. Helena Vertical Datum 2015\",ID[\"EPSG\",1177]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"SHGD2015 to SHVD2015 height (1)\",ID[\"EPSG\",7891]],ID[\"EPSG\",7890]],ID[\"EPSG\",7956]]" + }, + { + "srid": 8801, + "wkt": "COMPOUNDCRS[\"NAD83 / Alabama East + NAVD88 height\",PROJCRS[\"NAD83 / Alabama East\",BASEGEOGCRS[\"NAD83\",DATUM[\"North American Datum 1983\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6269]],ID[\"EPSG\",4269]],CONVERSION[\"SPCS83 Alabama East zone (meter)\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",30.5000000000003,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",-85.8333333333336,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.99996,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",200000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",10131]],CS[Cartesian,2,ID[\"EPSG\",4499]],AXIS[\"Easting (X)\",east],AXIS[\"Northing (Y)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",26929]],VERTCRS[\"NAVD88 height\",VDATUM[\"North American Vertical Datum 1988\",ID[\"EPSG\",5103]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"NAD83(2011) to NAVD88 height (1)\",ID[\"EPSG\",6326]],GEOIDMODEL[\"NAD83(2011) to NAVD88 height (2)\",ID[\"EPSG\",6327]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (1)\",ID[\"EPSG\",9160]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (2)\",ID[\"EPSG\",9161]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (3)\",ID[\"EPSG\",9162]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (4)\",ID[\"EPSG\",9163]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (5)\",ID[\"EPSG\",9164]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (6)\",ID[\"EPSG\",9165]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (7)\",ID[\"EPSG\",9166]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (8)\",ID[\"EPSG\",9167]],GEOIDMODEL[\"NAD83(FBN) to NAVD88 height (1)\",ID[\"EPSG\",9168]],GEOIDMODEL[\"NAD83(HARN) to NAVD88 height (9)\",ID[\"EPSG\",9169]],GEOIDMODEL[\"NAD83(NSRS2007) to NAVD88 height (1)\",ID[\"EPSG\",9173]],GEOIDMODEL[\"NAD83(NSRS2007) to NAVD88 height (2)\",ID[\"EPSG\",9174]],GEOIDMODEL[\"NAD83(2011) to NAVD88 height (3)\",ID[\"EPSG\",9229]],ID[\"EPSG\",5703]],ID[\"EPSG\",8801]]" + }, + { + "srid": 9289, + "wkt": "COMPOUNDCRS[\"ETRS89-NLD [AGRS2010] + LAT-NLD depth\",GEOGCRS[\"ETRS89-NLD [AGRS2010]\",DATUM[\"AGRS2010 (ETRF2000)\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ANCHOREPOCH[2010.5],ID[\"EPSG\",1427]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",11037]],VERTCRS[\"LAT-NLD depth\",VDATUM[\"Lowest Astronomical Tide Netherlands\",ID[\"EPSG\",1290]],CS[vertical,1,ID[\"EPSG\",6498]],AXIS[\"Depth (D)\",down],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"ETRS89-NLD [AGRS2010] to LAT-NLD depth (1)\",ID[\"EPSG\",10350]],ID[\"EPSG\",9287]],ID[\"EPSG\",9289]]" + }, + { + "srid": 9518, + "wkt": "COMPOUNDCRS[\"WGS 84 + EGM2008 height\",GEOGCRS[\"WGS 84\",ENSEMBLE[\"World Geodetic System 1984 ensemble\",MEMBER[\"World Geodetic System 1984 (Transit)\",ID[\"EPSG\",1166]],MEMBER[\"World Geodetic System 1984 (G730)\",ID[\"EPSG\",1152]],MEMBER[\"World Geodetic System 1984 (G873)\",ID[\"EPSG\",1153]],MEMBER[\"World Geodetic System 1984 (G1150)\",ID[\"EPSG\",1154]],MEMBER[\"World Geodetic System 1984 (G1674)\",ID[\"EPSG\",1155]],MEMBER[\"World Geodetic System 1984 (G1762)\",ID[\"EPSG\",1156]],MEMBER[\"World Geodetic System 1984 (G2139)\",ID[\"EPSG\",1309]],MEMBER[\"World Geodetic System 1984 (G2296)\",ID[\"EPSG\",1383]],ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7030]],ENSEMBLEACCURACY[2],ID[\"EPSG\",6326]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4326]],VERTCRS[\"EGM2008 height\",VDATUM[\"EGM2008 geoid\",ID[\"EPSG\",1027]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",3855]],ID[\"EPSG\",9518]]" + }, + { + "srid": 9527, + "wkt": "COMPOUNDCRS[\"NZGD2000 + NZVD2009 height\",GEOGCRS[\"NZGD2000\",DATUM[\"New Zealand Geodetic Datum 2000\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7019]],ID[\"EPSG\",6167]],CS[ellipsoidal,2,ID[\"EPSG\",6422]],AXIS[\"Geodetic latitude (Lat)\",north],AXIS[\"Geodetic longitude (Lon)\",east],ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",4167]],VERTCRS[\"NZVD2009 height\",VDATUM[\"New Zealand Vertical Datum 2009\",ID[\"EPSG\",1039]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"NZGD2000 to NZVD2009 height (2)\",ID[\"EPSG\",9325]],ID[\"EPSG\",4440]],ID[\"EPSG\",9527]]" + } +] diff --git a/test/ProjNet.Tests/Generated/proj2proj-direct-parity-fixture.json b/test/ProjNet.Tests/Generated/proj2proj-direct-parity-fixture.json new file mode 100644 index 00000000..92ef122e --- /dev/null +++ b/test/ProjNet.Tests/Generated/proj2proj-direct-parity-fixture.json @@ -0,0 +1,318 @@ +{ + "fixtureVersion": 1, + "generator": "Generate-ProjReferenceFixtures.ps1", + "cases": [ + { + "operationCode": 6305, + "sourceSrid": 23031, + "targetSrid": 31300, + "sourceWkt": "PROJCS[\"ED50 / UTM zone 31N\",GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"23031\"]]", + "targetWkt": "PROJCS[\"BD72 / Belge Lambert 72\",GEOGCS[\"BD72\",DATUM[\"Reseau_National_Belge_1972\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6313\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4313\"]],PROJECTION[\"Lambert_Conformal_Conic_2SP_Belgium\"],PARAMETER[\"latitude_of_origin\",90],PARAMETER[\"central_meridian\",4.35693972222222],PARAMETER[\"standard_parallel_1\",49.8333333333333],PARAMETER[\"standard_parallel_2\",51.1666666666667],PARAMETER[\"false_easting\",150000.01256],PARAMETER[\"false_northing\",5400088.4378],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"31300\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": -43816.59273464215, + "expectedY": -6131458.559343959, + "toleranceMeters": 366.0 + }, + { + "operationCode": 15861, + "sourceSrid": 3367, + "targetSrid": 32628, + "sourceWkt": "PROJCS[\"IGN Astro 1960 / UTM zone 28N\",GEOGCS[\"IGN Astro 1960\",DATUM[\"IGN_Astro_1960\",SPHEROID[\"Clarke 1880 (RGS)\",6378249.145,293.465,AUTHORITY[\"EPSG\",\"7012\"]],AUTHORITY[\"EPSG\",\"6700\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4700\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-15],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3367\"]]", + "targetWkt": "PROJCS[\"WGS 84 / UTM zone 28N\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-15],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"32628\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 511903.12879036064, + "expectedY": 23380.55121846928, + "toleranceMeters": 555.0 + }, + { + "operationCode": 10244, + "sourceSrid": 3118, + "targetSrid": 11118, + "sourceWkt": "PROJCS[\"MAGNA-SIRGAS / Colombia East zone\",GEOGCS[\"MAGNA-SIRGAS\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6686\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4686\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620041666667],PARAMETER[\"central_meridian\",-68.0775079166667],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3118\"]]", + "targetWkt": "PROJCS[\"MAGNA-SIRGAS 2018 / Colombia East zone\",GEOGCS[\"MAGNA-SIRGAS 2018\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia_2018\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1329\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"20046\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620322222222],PARAMETER[\"central_meridian\",-68.0775077694444],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"11118\"]]", + "inputX": 1012345.678, + "inputY": 1023456.789, + "expectedX": 1012345.678, + "expectedY": 1023456.789, + "toleranceMeters": 100.0 + }, + { + "operationCode": 15858, + "sourceSrid": 3368, + "targetSrid": 3344, + "sourceWkt": "PROJCS[\"IGN Astro 1960 / UTM zone 29N\",GEOGCS[\"IGN Astro 1960\",DATUM[\"IGN_Astro_1960\",SPHEROID[\"Clarke 1880 (RGS)\",6378249.145,293.465,AUTHORITY[\"EPSG\",\"7012\"]],AUTHORITY[\"EPSG\",\"6700\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4700\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3368\"]]", + "targetWkt": "PROJCS[\"Mauritania 1999 / UTM zone 29N\",GEOGCS[\"Mauritania 1999\",DATUM[\"Mauritania_1999\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6702\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4702\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3344\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 512027.8727011453, + "expectedY": 23325.21781742058, + "toleranceMeters": 399.0 + }, + { + "operationCode": 1050, + "sourceSrid": 28992, + "targetSrid": 23095, + "sourceWkt": "PROJCS[\"Amersfoort / RD New\",GEOGCS[\"Amersfoort\",DATUM[\"Amersfoort\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6289\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4289\"]],PROJECTION[\"Oblique_Stereographic\"],PARAMETER[\"latitude_of_origin\",52.1561605555556],PARAMETER[\"central_meridian\",5.38763888888889],PARAMETER[\"scale_factor\",0.9999079],PARAMETER[\"false_easting\",155000],PARAMETER[\"false_northing\",463000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"28992\"]]", + "targetWkt": "PROJCS[\"ED50 / TM 5 NE\",GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",5],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"23095\"]]", + "inputX": 167345.678, + "inputY": 486456.789, + "expectedX": 538794.9748652225, + "expectedY": 5802091.04261039, + "toleranceMeters": 226.0 + }, + { + "operationCode": 10087, + "sourceSrid": 24100, + "targetSrid": 24200, + "sourceWkt": "PROJCS[\"Jamaica 1875 / Jamaica (Old Grid)\",GEOGCS[\"Jamaica 1875\",DATUM[\"Jamaica_1875\",SPHEROID[\"Clarke 1880\",6378249.14480801,293.466307655636,AUTHORITY[\"EPSG\",\"7034\"]],AUTHORITY[\"EPSG\",\"6241\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4241\"]],PROJECTION[\"Lambert_Conformal_Conic_1SP\"],PARAMETER[\"latitude_of_origin\",18],PARAMETER[\"central_meridian\",-77],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",550000],PARAMETER[\"false_northing\",400000],UNIT[\"Clarke's foot\",0.3047972654,AUTHORITY[\"EPSG\",\"9005\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"24100\"]]", + "targetWkt": "PROJCS[\"JAD69 / Jamaica National Grid\",GEOGCS[\"JAD69\",DATUM[\"Jamaica_1969\",SPHEROID[\"Clarke 1866\",6378206.4,294.978698213898,AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6242\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4242\"]],PROJECTION[\"Lambert_Conformal_Conic_1SP\"],PARAMETER[\"latitude_of_origin\",18],PARAMETER[\"central_meridian\",-77],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",250000],PARAMETER[\"false_northing\",150000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"24200\"]]", + "inputX": 562345.678, + "inputY": 423456.789, + "expectedX": 253763.78169917234, + "expectedY": 157149.8988797065, + "toleranceMeters": 100.0 + }, + { + "operationCode": 1048, + "sourceSrid": 31300, + "targetSrid": 23031, + "sourceWkt": "PROJCS[\"BD72 / Belge Lambert 72\",GEOGCS[\"BD72\",DATUM[\"Reseau_National_Belge_1972\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6313\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4313\"]],PROJECTION[\"Lambert_Conformal_Conic_2SP_Belgium\"],PARAMETER[\"latitude_of_origin\",90],PARAMETER[\"central_meridian\",4.35693972222222],PARAMETER[\"standard_parallel_1\",49.8333333333333],PARAMETER[\"standard_parallel_2\",51.1666666666667],PARAMETER[\"false_easting\",150000.01256],PARAMETER[\"false_northing\",5400088.4378],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"31300\"]]", + "targetWkt": "PROJCS[\"ED50 / UTM zone 31N\",GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"23031\"]]", + "inputX": 162345.69056, + "inputY": 5423545.2268, + "expectedX": 498595.44715994317, + "expectedY": 10002878.742317818, + "toleranceMeters": 100.0 + }, + { + "operationCode": 10517, + "sourceSrid": 10516, + "targetSrid": 8162, + "sourceWkt": "PROJCS[\"NAD83(2011) / Adjusted Jackson (ftUS)\",GEOGCS[\"NAD83(2011)\",DATUM[\"NAD83_National_Spatial_Reference_System_2011\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1116\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"6318\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",44.2533353222222],PARAMETER[\"central_meridian\",-90.8442965138889],PARAMETER[\"scale_factor\",1.0000353],PARAMETER[\"false_easting\",88582.5],PARAMETER[\"false_northing\",82020.833],UNIT[\"US survey foot\",0.304800609601219,AUTHORITY[\"EPSG\",\"9003\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"10516\"]]", + "targetWkt": "PROJCS[\"NAD83(HARN) / WISCRS Jackson (ftUS)\",GEOGCS[\"NAD83(HARN)\",DATUM[\"NAD83_High_Accuracy_Reference_Network\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6152\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4152\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",44.2533351277778],PARAMETER[\"central_meridian\",-90.8442965194444],PARAMETER[\"scale_factor\",1.0000353],PARAMETER[\"false_easting\",88582.5],PARAMETER[\"false_northing\",82020.833],UNIT[\"US survey foot\",0.304800609601219,AUTHORITY[\"EPSG\",\"9003\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"8162\"]]", + "inputX": 100928.178, + "inputY": 105477.622, + "expectedX": 100928.178, + "expectedY": 105477.622, + "toleranceMeters": 100.0 + }, + { + "operationCode": 15859, + "sourceSrid": 3369, + "targetSrid": 3345, + "sourceWkt": "PROJCS[\"IGN Astro 1960 / UTM zone 30N\",GEOGCS[\"IGN Astro 1960\",DATUM[\"IGN_Astro_1960\",SPHEROID[\"Clarke 1880 (RGS)\",6378249.145,293.465,AUTHORITY[\"EPSG\",\"7012\"]],AUTHORITY[\"EPSG\",\"6700\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4700\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3369\"]]", + "targetWkt": "PROJCS[\"Mauritania 1999 / UTM zone 30N\",GEOGCS[\"Mauritania 1999\",DATUM[\"Mauritania_1999\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6702\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4702\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3345\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 512151.3904422305, + "expectedY": 23266.684503320386, + "toleranceMeters": 244.0 + }, + { + "operationCode": 15922, + "sourceSrid": 24500, + "targetSrid": 3414, + "sourceWkt": "PROJCS[\"Kertau 1968 / Singapore Grid\",GEOGCS[\"Kertau 1968\",DATUM[\"Kertau_1968\",SPHEROID[\"Everest 1830 Modified\",6377304.063,300.8017,AUTHORITY[\"EPSG\",\"7018\"]],AUTHORITY[\"EPSG\",\"6245\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4245\"]],PROJECTION[\"Cassini_Soldner\"],PARAMETER[\"latitude_of_origin\",1.28764666666667],PARAMETER[\"central_meridian\",103.853002222222],PARAMETER[\"false_easting\",30000],PARAMETER[\"false_northing\",30000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"24500\"]]", + "targetWkt": "PROJCS[\"SVY21 / Singapore TM\",GEOGCS[\"SVY21\",DATUM[\"SVY21\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6757\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4757\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",1.36666666666667],PARAMETER[\"central_meridian\",103.833333333333],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",28001.642],PARAMETER[\"false_northing\",38744.572],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3414\"]]", + "inputX": 42345.678, + "inputY": 53456.789, + "expectedX": 53456.789, + "expectedY": 42345.678, + "toleranceMeters": 13914.0 + }, + { + "operationCode": 10242, + "sourceSrid": 3116, + "targetSrid": 11116, + "sourceWkt": "PROJCS[\"MAGNA-SIRGAS / Colombia Bogota zone\",GEOGCS[\"MAGNA-SIRGAS\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6686\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4686\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620041666667],PARAMETER[\"central_meridian\",-74.0775079166667],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3116\"]]", + "targetWkt": "PROJCS[\"MAGNA-SIRGAS 2018 / Colombia Bogota zone\",GEOGCS[\"MAGNA-SIRGAS 2018\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia_2018\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1329\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"20046\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620322222222],PARAMETER[\"central_meridian\",-74.0775077694444],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"11116\"]]", + "inputX": 1012345.678, + "inputY": 1023456.789, + "expectedX": 1012345.678, + "expectedY": 1023456.789, + "toleranceMeters": 100.0 + }, + { + "operationCode": 15487, + "sourceSrid": 3828, + "targetSrid": 3826, + "sourceWkt": "PROJCS[\"TWD67 / TM2 zone 121\",GEOGCS[\"TWD67\",DATUM[\"Taiwan_Datum_1967\",SPHEROID[\"GRS 1967 Modified\",6378160,298.25,AUTHORITY[\"EPSG\",\"7050\"]],AUTHORITY[\"EPSG\",\"1025\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"3821\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",121],PARAMETER[\"scale_factor\",0.9999],PARAMETER[\"false_easting\",250000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3828\"]]", + "targetWkt": "PROJCS[\"TWD97 / TM2 zone 121\",GEOGCS[\"TWD97\",DATUM[\"Taiwan_Datum_1997\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1026\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"3824\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",121],PARAMETER[\"scale_factor\",0.9999],PARAMETER[\"false_easting\",250000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3826\"]]", + "inputX": 262345.678, + "inputY": 23456.789, + "expectedX": 263174.267, + "expectedY": 23249.874, + "toleranceMeters": 1038.0 + }, + { + "operationCode": 10221, + "sourceSrid": 3115, + "targetSrid": 11115, + "sourceWkt": "PROJCS[\"MAGNA-SIRGAS / Colombia West zone\",GEOGCS[\"MAGNA-SIRGAS\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6686\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4686\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620041666667],PARAMETER[\"central_meridian\",-77.0775079166667],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3115\"]]", + "targetWkt": "PROJCS[\"MAGNA-SIRGAS 2018 / Colombia West zone\",GEOGCS[\"MAGNA-SIRGAS 2018\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia_2018\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1329\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"20046\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620322222222],PARAMETER[\"central_meridian\",-77.0775077694444],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"11115\"]]", + "inputX": 1012345.678, + "inputY": 1023456.789, + "expectedX": 1012345.678, + "expectedY": 1023456.789, + "toleranceMeters": 100.0 + }, + { + "operationCode": 4072, + "sourceSrid": 3392, + "targetSrid": 3891, + "sourceWkt": "PROJCS[\"Karbala 1979 / UTM zone 38N\",GEOGCS[\"Karbala 1979\",DATUM[\"Karbala_1979\",SPHEROID[\"Clarke 1880 (RGS)\",6378249.145,293.465,AUTHORITY[\"EPSG\",\"7012\"]],AUTHORITY[\"EPSG\",\"6743\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4743\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",45],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3392\"]]", + "targetWkt": "PROJCS[\"IGRS / UTM zone 38N\",GEOGCS[\"IGRS\",DATUM[\"Iraqi_Geospatial_Reference_System\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1029\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"3889\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",45],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3891\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 512058.2038394232, + "expectedY": 23719.861977205095, + "toleranceMeters": 361.0 + }, + { + "operationCode": 5166, + "sourceSrid": 23031, + "targetSrid": 25831, + "sourceWkt": "PROJCS[\"ED50 / UTM zone 31N\",GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"23031\"]]", + "targetWkt": "PROJCS[\"ETRS89 / UTM zone 31N\",GEOGCS[\"ETRS89\",DATUM[\"European_Terrestrial_Reference_System_1989\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6258\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4258\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"25831\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 512345.19217476185, + "expectedY": 23456.533988739906, + "toleranceMeters": 100.0 + }, + { + "operationCode": 10216, + "sourceSrid": 3114, + "targetSrid": 11114, + "sourceWkt": "PROJCS[\"MAGNA-SIRGAS / Colombia Far West zone\",GEOGCS[\"MAGNA-SIRGAS\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6686\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4686\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620041666667],PARAMETER[\"central_meridian\",-80.0775079166667],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3114\"]]", + "targetWkt": "PROJCS[\"MAGNA-SIRGAS 2018 / Colombia Far West zone\",GEOGCS[\"MAGNA-SIRGAS 2018\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia_2018\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1329\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"20046\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620322222222],PARAMETER[\"central_meridian\",-80.0775077694444],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"11114\"]]", + "inputX": 1012345.678, + "inputY": 1023456.789, + "expectedX": 1012345.3677527758, + "expectedY": 1023456.7726661008, + "toleranceMeters": 100.0 + }, + { + "operationCode": 6303, + "sourceSrid": 23031, + "targetSrid": 28992, + "sourceWkt": "PROJCS[\"ED50 / UTM zone 31N\",GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"23031\"]]", + "targetWkt": "PROJCS[\"Amersfoort / RD New\",GEOGCS[\"Amersfoort\",DATUM[\"Amersfoort\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6289\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4289\"]],PROJECTION[\"Oblique_Stereographic\"],PARAMETER[\"latitude_of_origin\",52.1561605555556],PARAMETER[\"central_meridian\",5.38763888888889],PARAMETER[\"scale_factor\",0.9999079],PARAMETER[\"false_easting\",155000],PARAMETER[\"false_northing\",463000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"28992\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": -158260.1770976053, + "expectedY": -5717268.281082358, + "toleranceMeters": 100.0 + }, + { + "operationCode": 1044, + "sourceSrid": 28992, + "targetSrid": 23031, + "sourceWkt": "PROJCS[\"Amersfoort / RD New\",GEOGCS[\"Amersfoort\",DATUM[\"Amersfoort\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6289\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4289\"]],PROJECTION[\"Oblique_Stereographic\"],PARAMETER[\"latitude_of_origin\",52.1561605555556],PARAMETER[\"central_meridian\",5.38763888888889],PARAMETER[\"scale_factor\",0.9999079],PARAMETER[\"false_easting\",155000],PARAMETER[\"false_northing\",463000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"28992\"]]", + "targetWkt": "PROJCS[\"ED50 / UTM zone 31N\",GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"23031\"]]", + "inputX": 167345.678, + "inputY": 486456.789, + "expectedX": 674963.4276649378, + "expectedY": 5805046.435951662, + "toleranceMeters": 224.0 + }, + { + "operationCode": 10243, + "sourceSrid": 3117, + "targetSrid": 11117, + "sourceWkt": "PROJCS[\"MAGNA-SIRGAS / Colombia East Central zone\",GEOGCS[\"MAGNA-SIRGAS\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6686\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4686\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620041666667],PARAMETER[\"central_meridian\",-71.0775079166667],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3117\"]]", + "targetWkt": "PROJCS[\"MAGNA-SIRGAS 2018 / Colombia East Central zone\",GEOGCS[\"MAGNA-SIRGAS 2018\",DATUM[\"Marco_Geocentrico_Nacional_de_Referencia_2018\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"1329\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"20046\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",4.59620322222222],PARAMETER[\"central_meridian\",-71.0775077694444],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1000000],PARAMETER[\"false_northing\",1000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"11117\"]]", + "inputX": 1012345.678, + "inputY": 1023456.789, + "expectedX": 1012345.678, + "expectedY": 1023456.789, + "toleranceMeters": 100.0 + }, + { + "operationCode": 15863, + "sourceSrid": 3369, + "targetSrid": 32630, + "sourceWkt": "PROJCS[\"IGN Astro 1960 / UTM zone 30N\",GEOGCS[\"IGN Astro 1960\",DATUM[\"IGN_Astro_1960\",SPHEROID[\"Clarke 1880 (RGS)\",6378249.145,293.465,AUTHORITY[\"EPSG\",\"7012\"]],AUTHORITY[\"EPSG\",\"6700\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4700\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3369\"]]", + "targetWkt": "PROJCS[\"WGS 84 / UTM zone 30N\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-3],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"32630\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 512151.3904422305, + "expectedY": 23266.684503320386, + "toleranceMeters": 244.0 + }, + { + "operationCode": 1072, + "sourceSrid": 28193, + "targetSrid": 2039, + "sourceWkt": "PROJCS[\"Palestine 1923 / Israeli CS Grid\",GEOGCS[\"Palestine 1923\",DATUM[\"Palestine_1923\",SPHEROID[\"Clarke 1880 (Benoit)\",6378300.789,293.466315538981,AUTHORITY[\"EPSG\",\"7010\"]],AUTHORITY[\"EPSG\",\"6281\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4281\"]],PROJECTION[\"Cassini_Soldner\"],PARAMETER[\"latitude_of_origin\",31.7340969444444],PARAMETER[\"central_meridian\",35.2120805555556],PARAMETER[\"false_easting\",170251.555],PARAMETER[\"false_northing\",1126867.909],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"28193\"]]", + "targetWkt": "PROJCS[\"Israel 1993 / Israeli TM Grid\",GEOGCS[\"Israel 1993\",DATUM[\"Israel_1993\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6141\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4141\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",31.7343936111111],PARAMETER[\"central_meridian\",35.2045169444444],PARAMETER[\"scale_factor\",1.0000067],PARAMETER[\"false_easting\",219529.584],PARAMETER[\"false_northing\",626907.39],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"2039\"]]", + "inputX": 182597.233, + "inputY": 1150324.698, + "expectedX": 232595.17839278348, + "expectedY": 650326.1034248897, + "toleranceMeters": 385.0 + }, + { + "operationCode": 6306, + "sourceSrid": 23095, + "targetSrid": 28992, + "sourceWkt": "PROJCS[\"ED50 / TM 5 NE\",GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",5],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"23095\"]]", + "targetWkt": "PROJCS[\"Amersfoort / RD New\",GEOGCS[\"Amersfoort\",DATUM[\"Amersfoort\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6289\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4289\"]],PROJECTION[\"Oblique_Stereographic\"],PARAMETER[\"latitude_of_origin\",52.1561605555556],PARAMETER[\"central_meridian\",5.38763888888889],PARAMETER[\"scale_factor\",0.9999079],PARAMETER[\"false_easting\",155000],PARAMETER[\"false_northing\",463000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"28992\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 116929.69941484436, + "expectedY": -5720286.231299018, + "toleranceMeters": 100.0 + }, + { + "operationCode": 15857, + "sourceSrid": 3367, + "targetSrid": 3343, + "sourceWkt": "PROJCS[\"IGN Astro 1960 / UTM zone 28N\",GEOGCS[\"IGN Astro 1960\",DATUM[\"IGN_Astro_1960\",SPHEROID[\"Clarke 1880 (RGS)\",6378249.145,293.465,AUTHORITY[\"EPSG\",\"7012\"]],AUTHORITY[\"EPSG\",\"6700\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4700\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-15],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3367\"]]", + "targetWkt": "PROJCS[\"Mauritania 1999 / UTM zone 28N\",GEOGCS[\"Mauritania 1999\",DATUM[\"Mauritania_1999\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6702\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4702\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-15],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3343\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 511903.12879036576, + "expectedY": 23380.551218469518, + "toleranceMeters": 555.0 + }, + { + "operationCode": 3951, + "sourceSrid": 3912, + "targetSrid": 3794, + "sourceWkt": "PROJCS[\"MGI 1901 / Slovene National Grid\",GEOGCS[\"MGI 1901\",DATUM[\"MGI_1901\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"1031\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"3906\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",15],PARAMETER[\"scale_factor\",0.9999],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",-5000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3912\"]]", + "targetWkt": "PROJCS[\"ETRS89-SVN [D96] / Slovene National Grid\",GEOGCS[\"ETRS89-SVN [D96]\",DATUM[\"Slovenia_Geodetic_Datum_1996\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6765\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4765\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",15],PARAMETER[\"scale_factor\",0.9999],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",-5000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3794\"]]", + "inputX": 512345.678, + "inputY": -4976543.211, + "expectedX": 511968.8019588575, + "expectedY": -4976193.543919171, + "toleranceMeters": 475.0 + }, + { + "operationCode": 15862, + "sourceSrid": 3368, + "targetSrid": 32629, + "sourceWkt": "PROJCS[\"IGN Astro 1960 / UTM zone 29N\",GEOGCS[\"IGN Astro 1960\",DATUM[\"IGN_Astro_1960\",SPHEROID[\"Clarke 1880 (RGS)\",6378249.145,293.465,AUTHORITY[\"EPSG\",\"7012\"]],AUTHORITY[\"EPSG\",\"6700\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4700\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3368\"]]", + "targetWkt": "PROJCS[\"WGS 84 / UTM zone 29N\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"32629\"]]", + "inputX": 512345.678, + "inputY": 23456.789, + "expectedX": 512027.8727011504, + "expectedY": 23325.217817420817, + "toleranceMeters": 399.0 + }, + { + "operationCode": 15950, + "sourceSrid": 3140, + "targetSrid": 3460, + "sourceWkt": "PROJCS[\"Viti Levu 1912 / Viti Levu Grid\",GEOGCS[\"Viti Levu 1912\",DATUM[\"Viti_Levu_1912\",SPHEROID[\"Clarke 1880 (international foot)\",6378306.3696,293.466307655635,AUTHORITY[\"EPSG\",\"7055\"]],AUTHORITY[\"EPSG\",\"6752\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4752\"]],PROJECTION[\"Cassini_Soldner\"],PARAMETER[\"latitude_of_origin\",-18],PARAMETER[\"central_meridian\",178],PARAMETER[\"false_easting\",544000],PARAMETER[\"false_northing\",704000],UNIT[\"link\",0.201168,AUTHORITY[\"EPSG\",\"9098\"]],AUTHORITY[\"EPSG\",\"3140\"]]", + "targetWkt": "PROJCS[\"Fiji 1986 / Fiji Map Grid\",GEOGCS[\"Fiji 1986\",DATUM[\"Fiji_Geodetic_Datum_1986\",SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],AUTHORITY[\"EPSG\",\"6720\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4720\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",-17],PARAMETER[\"central_meridian\",178.75],PARAMETER[\"scale_factor\",0.99985],PARAMETER[\"false_easting\",2000000],PARAMETER[\"false_northing\",4000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3460\"]]", + "inputX": 556345.678, + "inputY": 727456.789, + "expectedX": 1957193.4981927003, + "expectedY": 3859784.1782650417, + "toleranceMeters": 42915.0 + } + ] +} diff --git a/test/ProjNet.Tests/Generated/roundtrip-accuracy-fixture.json b/test/ProjNet.Tests/Generated/roundtrip-accuracy-fixture.json new file mode 100644 index 00000000..9319eafa --- /dev/null +++ b/test/ProjNet.Tests/Generated/roundtrip-accuracy-fixture.json @@ -0,0 +1,186 @@ +{ + "fixtureVersion": 1, + "generator": "PROJ 9.9.0 via cs2cs", + "cases": [ + { + "description": "WGS84 to Web Mercator - Bern", + "sourceSrid": 4326, + "targetSrid": 3857, + "inputLon": 7.4386, + "inputLat": 46.9511, + "forwardX": 828061.164214844815, + "forwardY": 5934095.997176224366, + "inverseBackLon": 7.438600000000, + "inverseBackLat": 46.951100000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Web Mercator - Berlin", + "sourceSrid": 4326, + "targetSrid": 3857, + "inputLon": 13.405, + "inputLat": 52.52, + "forwardX": 1492237.774083832046, + "forwardY": 6894699.801282424480, + "inverseBackLon": 13.405000000000, + "inverseBackLat": 52.520000000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Web Mercator - London", + "sourceSrid": 4326, + "targetSrid": 3857, + "inputLon": -0.1276, + "inputLat": 51.5074, + "forwardX": -14204.367025221705, + "forwardY": 6711542.475587636232, + "inverseBackLon": -0.127600000000, + "inverseBackLat": 51.507400000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Web Mercator - New York", + "sourceSrid": 4326, + "targetSrid": 3857, + "inputLon": -73.9857, + "inputLat": 40.7484, + "forwardX": -8236050.449983900413, + "forwardY": 4975301.253789808601, + "inverseBackLon": -73.985700000000, + "inverseBackLat": 40.748400000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Web Mercator - Tokyo", + "sourceSrid": 4326, + "targetSrid": 3857, + "inputLon": 139.6917, + "inputLat": 35.6895, + "forwardX": 15550408.912046732381, + "forwardY": 4257980.732184108347, + "inverseBackLon": 139.691700000000, + "inverseBackLat": 35.689500000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to UTM 32N - Bern", + "sourceSrid": 4326, + "targetSrid": 32632, + "inputLon": 7.4386, + "inputLat": 46.9511, + "forwardX": 381186.386245490809, + "forwardY": 5200913.262866788544, + "inverseBackLon": 7.438600000000, + "inverseBackLat": 46.951100000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to UTM 32N - Zurich", + "sourceSrid": 4326, + "targetSrid": 32632, + "inputLon": 8.5417, + "inputLat": 47.3769, + "forwardX": 465403.284465976758, + "forwardY": 5247150.839424513280, + "inverseBackLon": 8.541700000000, + "inverseBackLat": 47.376900000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to UTM 32N - Milan", + "sourceSrid": 4326, + "targetSrid": 32632, + "inputLon": 9.19, + "inputLat": 45.4642, + "forwardX": 514853.495940465713, + "forwardY": 5034536.796248704195, + "inverseBackLon": 9.190000000000, + "inverseBackLat": 45.464200000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to UTM 32N - Hamburg", + "sourceSrid": 4326, + "targetSrid": 32632, + "inputLon": 9.9937, + "inputLat": 53.5511, + "forwardX": 565834.364010827267, + "forwardY": 5934037.951124841347, + "inverseBackLon": 9.993700000000, + "inverseBackLat": 53.551100000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to UTM 32N - Oslo", + "sourceSrid": 4326, + "targetSrid": 32632, + "inputLon": 10.7522, + "inputLat": 59.9139, + "forwardX": 597979.902882698923, + "forwardY": 6643118.991493064910, + "inverseBackLon": 10.752200000000, + "inverseBackLat": 59.913900000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Lambert 93 - Bern", + "sourceSrid": 4326, + "targetSrid": 2154, + "inputLon": 7.4386, + "inputLat": 46.9511, + "forwardX": 1037401.209604420001, + "forwardY": 6659585.030590299517, + "inverseBackLon": 7.438600000000, + "inverseBackLat": 46.951100000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Lambert 93 - Berlin", + "sourceSrid": 4326, + "targetSrid": 2154, + "inputLon": 13.405, + "inputLat": 52.52, + "forwardX": 1407596.859445317183, + "forwardY": 7316849.160239100456, + "inverseBackLon": 13.405000000000, + "inverseBackLat": 52.520000000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Lambert 93 - London", + "sourceSrid": 4326, + "targetSrid": 2154, + "inputLon": -0.1276, + "inputLat": 51.5074, + "forwardX": 482266.475388611318, + "forwardY": 7161372.233056876808, + "inverseBackLon": -0.127600000000, + "inverseBackLat": 51.507400000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Lambert 93 - New York", + "sourceSrid": 4326, + "targetSrid": 2154, + "inputLon": -73.9857, + "inputLat": 40.7484, + "forwardX": -4841411.200356817804, + "forwardY": 8898350.653161048889, + "inverseBackLon": -73.985700000000, + "inverseBackLat": 40.748400000000, + "toleranceMeters": 0.01 + }, + { + "description": "WGS84 to Lambert 93 - Tokyo", + "sourceSrid": 4326, + "targetSrid": 2154, + "inputLon": 139.6917, + "inputLat": 35.6895, + "forwardX": 7868806.799923328683, + "forwardY": 13814722.823682885617, + "inverseBackLon": 139.691700000000, + "inverseBackLat": 35.689500000000, + "toleranceMeters": 0.01 + } + ] +} diff --git a/test/ProjNet.Tests/Geometries/EllipsoidalGeodesicTests.cs b/test/ProjNet.Tests/Geometries/EllipsoidalGeodesicTests.cs new file mode 100644 index 00000000..343c21d3 --- /dev/null +++ b/test/ProjNet.Tests/Geometries/EllipsoidalGeodesicTests.cs @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using Xunit; + +/// +/// Verifies the shared ellipsoidal geodesic helpers used by multiple projection kernels. +/// +public class EllipsoidalGeodesicTests +{ + /// + /// Verifies Vincenty's inverse solution against the published Flinders Peak to Buninyong benchmark. + /// + [Fact] + public void TryVincentyInverse_WithPublishedWgs84Benchmark_ReturnsExpectedDistanceAndAzimuth() + { + GeodesicParameters wgs84 = CreateWgs84Parameters(); + double latitude1 = DmsToRadians(-37, 57, 3.7203d); + double longitude1 = DmsToRadians(144, 25, 29.5244d); + double latitude2 = DmsToRadians(-37, 39, 10.1561d); + double longitude2 = DmsToRadians(143, 55, 35.3839d); + + bool solved = EllipsoidalGeodesic.TryVincentyInverse( + wgs84.SemiMinor, + wgs84.Flattening, + wgs84.EccentricityPrimeSquared, + latitude1, + longitude1, + latitude2, + longitude2, + out double distance, + out double azimuth); + + Assert.True(solved); + Assert.Equal(54972.271d, distance, 3); + Assert.Equal(DmsToRadians(306, 52, 5.37d), NormalizePositiveAzimuth(azimuth), 7); + } + + /// + /// Verifies Vincenty's direct solution against the published Flinders Peak to Buninyong benchmark. + /// + [Fact] + public void TryVincentyDirect_WithPublishedWgs84Benchmark_ReturnsExpectedEndpoint() + { + GeodesicParameters wgs84 = CreateWgs84Parameters(); + double latitude1 = DmsToRadians(-37, 57, 3.7203d); + double longitude1 = DmsToRadians(144, 25, 29.5244d); + double azimuth1 = DmsToRadians(306, 52, 5.37d); + + bool solved = EllipsoidalGeodesic.TryVincentyDirect( + wgs84.SemiMinor, + wgs84.Flattening, + wgs84.EccentricityPrimeSquared, + latitude1, + longitude1, + azimuth1, + 54972.271d, + out double latitude2, + out double longitude2); + + Assert.True(solved); + Assert.Equal(DmsToRadians(-37, 39, 10.1561d), latitude2, 9); + Assert.Equal(DmsToRadians(143, 55, 35.3839d), longitude2, 9); + } + + /// + /// Verifies that Vincenty's inverse solver reports non-convergence for a nearly antipodal case. + /// + [Fact] + public void TryVincentyInverse_WithNearlyAntipodalPoints_ReturnsFalse() + { + GeodesicParameters wgs84 = CreateWgs84Parameters(); + + bool solved = EllipsoidalGeodesic.TryVincentyInverse( + wgs84.SemiMinor, + wgs84.Flattening, + wgs84.EccentricityPrimeSquared, + 0d, + 0d, + DegreesToRadians(0.5d), + DegreesToRadians(179.7d), + out double distance, + out double azimuth); + + Assert.False(solved); + Assert.Equal(0d, distance); + Assert.Equal(0d, azimuth); + } + + /// + /// Verifies that the shared helpers reduce to the expected great-circle behavior on a sphere. + /// + [Fact] + public void VincentyHelpers_WithZeroFlattening_MatchEquatorialGreatCircleExpectations() + { + const double radius = 6371000d; + const double distance = 1000000d; + double latitude1 = 0d; + double longitude1 = DegreesToRadians(10d); + double azimuth1 = DegreesToRadians(90d); + double expectedLongitude2 = longitude1 + (distance / radius); + + bool directSolved = EllipsoidalGeodesic.TryVincentyDirect( + radius, + 0d, + 0d, + latitude1, + longitude1, + azimuth1, + distance, + out double latitude2, + out double longitude2); + + Assert.True(directSolved); + Assert.Equal(0d, latitude2, 12); + Assert.Equal(expectedLongitude2, longitude2, 12); + + bool inverseSolved = EllipsoidalGeodesic.TryVincentyInverse( + radius, + 0d, + 0d, + latitude1, + longitude1, + latitude2, + longitude2, + out double inverseDistance, + out double inverseAzimuth); + + Assert.True(inverseSolved); + Assert.Equal(distance, inverseDistance, 6); + Assert.Equal(azimuth1, inverseAzimuth, 12); + } + + private static double DegreesToRadians(double degrees) + => degrees * (Math.PI / 180d); + + private static double DmsToRadians(int degrees, int minutes, double seconds) + { + double sign = degrees < 0 ? -1d : 1d; + double absoluteDegrees = Math.Abs(degrees) + (minutes / 60d) + (seconds / 3600d); + return sign * DegreesToRadians(absoluteDegrees); + } + + private static double NormalizePositiveAzimuth(double azimuth) + => azimuth >= 0d ? azimuth : azimuth + (2d * Math.PI); + + private static GeodesicParameters CreateWgs84Parameters() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + double semiMajor = ellipsoid.SemiMajorAxis; + double semiMinor = ellipsoid.SemiMinorAxis; + double flattening = 1d / ellipsoid.InverseFlattening; + double eccentricityPrimeSquared = ((semiMajor * semiMajor) / (semiMinor * semiMinor)) - 1d; + return new GeodesicParameters(semiMajor, semiMinor, flattening, eccentricityPrimeSquared); + } + + private readonly record struct GeodesicParameters( + double SemiMajor, + double SemiMinor, + double Flattening, + double EccentricityPrimeSquared); +} diff --git a/test/ProjNet.Tests/Geometries/XYTests.cs b/test/ProjNet.Tests/Geometries/XYTests.cs new file mode 100644 index 00000000..9c803976 --- /dev/null +++ b/test/ProjNet.Tests/Geometries/XYTests.cs @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using ProjNet.Geometries; +using Xunit; + +/// +/// Tests for the and geometry structs. +/// +public class XYTests +{ + /// + /// Verifies that the constructor assigns X and Y correctly. + /// + [Fact] + public void XY_Constructor_SetsXAndY() + { + var xy = new XY(3.5, -7.2); + + Assert.Equal(3.5, xy.X); + Assert.Equal(-7.2, xy.Y); + } + + /// + /// Verifies that the default constructor initializes both fields to zero. + /// + [Fact] + public void XY_DefaultConstructor_InitializesToZero() + { + XY xy = default; + + Assert.Equal(0.0, xy.X); + Assert.Equal(0.0, xy.Y); + } + + /// + /// Verifies that fields can be assigned after construction. + /// + [Fact] + public void XY_FieldAssignment_UpdatesValues() + { + var xy = new XY(1.0, 2.0); + xy.X = 10.0; + xy.Y = 20.0; + + Assert.Equal(10.0, xy.X); + Assert.Equal(20.0, xy.Y); + } + + /// + /// Verifies the equality operator for various value combinations. + /// + [Theory] + [InlineData(1.0, 2.0, 1.0, 2.0, true)] + [InlineData(1.0, 2.0, 1.0, 3.0, false)] + [InlineData(1.0, 2.0, 3.0, 2.0, false)] + [InlineData(0.0, 0.0, 0.0, 0.0, true)] + [InlineData(-1.0, -2.0, -1.0, -2.0, true)] + [InlineData(-1.0, 2.0, 1.0, 2.0, false)] + public void XY_EqualityOperator_ReturnsExpected(double x1, double y1, double x2, double y2, bool expected) + { + var a = new XY(x1, y1); + var b = new XY(x2, y2); + + Assert.Equal(expected, a == b); + } + + /// + /// Verifies the inequality operator for various value combinations. + /// + [Theory] + [InlineData(1.0, 2.0, 1.0, 2.0, false)] + [InlineData(1.0, 2.0, 1.0, 3.0, true)] + [InlineData(0.0, 0.0, 0.0, 0.0, false)] + [InlineData(-1.0, 2.0, 1.0, 2.0, true)] + public void XY_InequalityOperator_ReturnsExpected(double x1, double y1, double x2, double y2, bool expected) + { + var a = new XY(x1, y1); + var b = new XY(x2, y2); + + Assert.Equal(expected, a != b); + } + + /// + /// Verifies that returns for identical values. + /// + [Fact] + public void XY_EqualsTyped_SameValues_ReturnsTrue() + { + var a = new XY(1.5, 2.5); + var b = new XY(1.5, 2.5); + + Assert.True(a.Equals(b)); + } + + /// + /// Verifies that returns for different values. + /// + [Fact] + public void XY_EqualsTyped_DifferentValues_ReturnsFalse() + { + var a = new XY(1.5, 2.5); + var b = new XY(3.5, 2.5); + + Assert.False(a.Equals(b)); + } + + /// + /// Verifies that returns for a boxed XY with the same values. + /// + [Fact] + public void XY_EqualsObject_SameXY_ReturnsTrue() + { + var a = new XY(1.0, 2.0); + object b = new XY(1.0, 2.0); + + Assert.True(a.Equals(b)); + } + + /// + /// Verifies that returns for a different type. + /// + [Fact] + public void XY_EqualsObject_DifferentType_ReturnsFalse() + { + var a = new XY(1.0, 2.0); + + Assert.False(a.Equals("not an XY")); + } + + /// + /// Verifies that returns for null. + /// + [Fact] + public void XY_EqualsObject_Null_ReturnsFalse() + { + var a = new XY(1.0, 2.0); + + Assert.False(a.Equals(null)); + } + + /// + /// Verifies that equal XY values produce the same hash code. + /// + [Fact] + public void XY_GetHashCode_SameValues_ReturnsSameHash() + { + var a = new XY(1.5, 2.5); + var b = new XY(1.5, 2.5); + + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + + /// + /// Verifies that different XY values produce different hash codes (not guaranteed, but expected for distinct values). + /// + [Fact] + public void XY_GetHashCode_DifferentValues_ReturnsDifferentHash() + { + var a = new XY(1.0, 2.0); + var b = new XY(3.0, 4.0); + + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + } + + /// + /// Verifies that produces the expected parenthesized format. + /// + [Fact] + public void XY_ToString_ReturnsParenthesizedCoordinates() + { + var xy = new XY(3.5, -7.2); + + // Use identical interpolation to ensure culture-independence. + Assert.Equal($"({3.5}, {-7.2})", xy.ToString()); + } + + /// + /// Verifies that formats zero values correctly. + /// + [Fact] + public void XY_ToString_ZeroValues_FormatsCorrectly() + { + var xy = new XY(0.0, 0.0); + + Assert.Equal("(0, 0)", xy.ToString()); + } + + // ---- XYZ tests ---- + + /// + /// Verifies that the constructor assigns X, Y, and Z correctly. + /// + [Fact] + public void XYZ_Constructor_SetsXYZ() + { + var xyz = new XYZ(3.5, -7.2, 1.0); + + Assert.Equal(3.5, xyz.X); + Assert.Equal(-7.2, xyz.Y); + Assert.Equal(1.0, xyz.Z); + } + + /// + /// Verifies that the default constructor initializes all fields to zero. + /// + [Fact] + public void XYZ_DefaultConstructor_InitializesToZero() + { + XYZ xyz = default; + + Assert.Equal(0.0, xyz.X); + Assert.Equal(0.0, xyz.Y); + Assert.Equal(0.0, xyz.Z); + } + + /// + /// Verifies that fields can be assigned after construction. + /// + [Fact] + public void XYZ_FieldAssignment_UpdatesValues() + { + var xyz = new XYZ(1.0, 2.0, 3.0); + xyz.X = 10.0; + xyz.Y = 20.0; + xyz.Z = 30.0; + + Assert.Equal(10.0, xyz.X); + Assert.Equal(20.0, xyz.Y); + Assert.Equal(30.0, xyz.Z); + } + + /// + /// Verifies the equality operator for various XYZ value combinations. + /// + [Theory] + [InlineData(1.0, 2.0, 3.0, 1.0, 2.0, 3.0, true)] + [InlineData(1.0, 2.0, 3.0, 1.0, 2.0, 4.0, false)] + [InlineData(1.0, 2.0, 3.0, 1.0, 4.0, 3.0, false)] + [InlineData(1.0, 2.0, 3.0, 4.0, 2.0, 3.0, false)] + [InlineData(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, true)] + [InlineData(-1.0, -2.0, -3.0, -1.0, -2.0, -3.0, true)] + public void XYZ_EqualityOperator_ReturnsExpected(double x1, double y1, double z1, double x2, double y2, double z2, bool expected) + { + var a = new XYZ(x1, y1, z1); + var b = new XYZ(x2, y2, z2); + + Assert.Equal(expected, a == b); + } + + /// + /// Verifies the inequality operator for various XYZ value combinations. + /// + [Theory] + [InlineData(1.0, 2.0, 3.0, 1.0, 2.0, 3.0, false)] + [InlineData(1.0, 2.0, 3.0, 1.0, 2.0, 4.0, true)] + [InlineData(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)] + public void XYZ_InequalityOperator_ReturnsExpected(double x1, double y1, double z1, double x2, double y2, double z2, bool expected) + { + var a = new XYZ(x1, y1, z1); + var b = new XYZ(x2, y2, z2); + + Assert.Equal(expected, a != b); + } + + /// + /// Verifies that returns for identical values. + /// + [Fact] + public void XYZ_EqualsTyped_SameValues_ReturnsTrue() + { + var a = new XYZ(1.5, 2.5, 3.5); + var b = new XYZ(1.5, 2.5, 3.5); + + Assert.True(a.Equals(b)); + } + + /// + /// Verifies that returns for different values. + /// + [Fact] + public void XYZ_EqualsTyped_DifferentValues_ReturnsFalse() + { + var a = new XYZ(1.5, 2.5, 3.5); + var b = new XYZ(1.5, 2.5, 4.5); + + Assert.False(a.Equals(b)); + } + + /// + /// Verifies that returns for a boxed XYZ with the same values. + /// + [Fact] + public void XYZ_EqualsObject_SameXYZ_ReturnsTrue() + { + var a = new XYZ(1.0, 2.0, 3.0); + object b = new XYZ(1.0, 2.0, 3.0); + + Assert.True(a.Equals(b)); + } + + /// + /// Verifies that returns for a different type. + /// + [Fact] + public void XYZ_EqualsObject_DifferentType_ReturnsFalse() + { + var a = new XYZ(1.0, 2.0, 3.0); + + Assert.False(a.Equals("not an XYZ")); + } + + /// + /// Verifies that returns for null. + /// + [Fact] + public void XYZ_EqualsObject_Null_ReturnsFalse() + { + var a = new XYZ(1.0, 2.0, 3.0); + + Assert.False(a.Equals(null)); + } + + /// + /// Verifies that equal XYZ values produce the same hash code. + /// + [Fact] + public void XYZ_GetHashCode_SameValues_ReturnsSameHash() + { + var a = new XYZ(1.5, 2.5, 3.5); + var b = new XYZ(1.5, 2.5, 3.5); + + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + + /// + /// Verifies that different XYZ values produce different hash codes (not guaranteed, but expected for distinct values). + /// + [Fact] + public void XYZ_GetHashCode_DifferentValues_ReturnsDifferentHash() + { + var a = new XYZ(1.0, 2.0, 3.0); + var b = new XYZ(4.0, 5.0, 6.0); + + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + } + + /// + /// Verifies that produces the expected parenthesized format. + /// + [Fact] + public void XYZ_ToString_ReturnsParenthesizedCoordinates() + { + var xyz = new XYZ(3.5, -7.2, 1.0); + + Assert.Equal($"({3.5}, {-7.2}, {1.0})", xyz.ToString()); + } + + /// + /// Verifies that formats zero values correctly. + /// + [Fact] + public void XYZ_ToString_ZeroValues_FormatsCorrectly() + { + var xyz = new XYZ(0.0, 0.0, 0.0); + + Assert.Equal("(0, 0, 0)", xyz.ToString()); + } + + /// + /// Verifies that an XY value does not equal an XYZ value when boxed. + /// + [Fact] + public void XY_EqualsObject_XYZ_ReturnsFalse() + { + var xy = new XY(1.0, 2.0); + object xyz = new XYZ(1.0, 2.0, 0.0); + + Assert.False(xy.Equals(xyz)); + } + + /// + /// Verifies that an XYZ value does not equal an XY value when boxed. + /// + [Fact] + public void XYZ_EqualsObject_XY_ReturnsFalse() + { + var xyz = new XYZ(1.0, 2.0, 0.0); + object xy = new XY(1.0, 2.0); + + Assert.False(xyz.Equals(xy)); + } +} diff --git a/test/ProjNet.Tests/GitHub/GitHubIssueRegressionTests.cs b/test/ProjNet.Tests/GitHub/GitHubIssueRegressionTests.cs new file mode 100644 index 00000000..8756d014 --- /dev/null +++ b/test/ProjNet.Tests/GitHub/GitHubIssueRegressionTests.cs @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.GitHub; + +using System; +using System.Collections.Generic; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for issues reported on the GitHub issue tracker. +/// +public class GitHubIssueRegressionTests +{ + private static readonly CoordinateSystemServices Css = new(CoordinateSystemServicesTests.LoadCsv()); + + /// + /// Verifies that GitHub issue #10 is fixed: creates a new + /// child transformation list and does not mutate the forward transform's state. + /// + [GitHubIssue(10)] + [Fact(DisplayName = "Issue #10, ConcatenatedTransform.Inverse() method destroys the state of child transformations")] + public void TestConcatenatedTransformInvert() + { + CoordinateSystem epsg31466 = Assert.IsType(Css.GetCoordinateSystem(31466), exactMatch: false); + CoordinateSystem epsg25832 = Assert.IsType(Css.GetCoordinateSystem(25832), exactMatch: false); + + ConcatenatedTransform ctFwd = Assert.IsType(Assert.IsType(Css.CreateTransformation(epsg31466, epsg25832), exactMatch: false).MathTransform); + var ctRev = (ConcatenatedTransform)ctFwd.Inverse(); + + IList ctlFwd = ctFwd.CoordinateTransformationList; + IList ctlRev = ctRev.CoordinateTransformationList; + + Assert.False(ReferenceEquals(ctlFwd, ctlRev)); + Assert.Equal(ctlRev.Count, ctlFwd.Count); + for (int i = 0, j = ctlFwd.Count - 1; i < ctlFwd.Count; i++, j--) + { + Assert.False(ReferenceEquals(ctlFwd[i], ctlRev[j])); + } + } + + /// + /// Verifies that GitHub issue #10 is fixed: repeated calls to + /// return the same cached instance and produce consistent round-trip results. + /// + [GitHubIssue(10)] + [Fact(DisplayName = "Issue #10, Repeated Inverse() calls keep ConcatenatedTransform stable")] + public void TestConcatenatedTransformInverseIsStableAcrossRepeatedCalls() + { + CoordinateSystem epsg31466 = Assert.IsType(Css.GetCoordinateSystem(31466), exactMatch: false); + CoordinateSystem epsg25832 = Assert.IsType(Css.GetCoordinateSystem(25832), exactMatch: false); + + ConcatenatedTransform ctFwd = Assert.IsType(Assert.IsType(Css.CreateTransformation(epsg31466, epsg25832), exactMatch: false).MathTransform); + + (double X, double Y) source = (3500000d, 5640000d); + (double X, double Y) projected = ctFwd.Transform(source.X, source.Y); + + ConcatenatedTransform inverse1 = Assert.IsType(ctFwd.Inverse()); + (double X, double Y) roundtrip1 = inverse1.Transform(projected.X, projected.Y); + + ConcatenatedTransform inverse2 = Assert.IsType(ctFwd.Inverse()); + (double X, double Y) roundtrip2 = inverse2.Transform(projected.X, projected.Y); + (double X, double Y) projectedAgain = ctFwd.Transform(source.X, source.Y); + + const double roundtripTolerance = 2d; + const double stabilityTolerance = TestTolerances.StableResult; + + Assert.Same(inverse1, inverse2); + + Assert.InRange(Math.Abs(roundtrip1.X - source.X), 0d, roundtripTolerance); + Assert.InRange(Math.Abs(roundtrip1.Y - source.Y), 0d, roundtripTolerance); + + Assert.InRange(Math.Abs(roundtrip2.X - source.X), 0d, roundtripTolerance); + Assert.InRange(Math.Abs(roundtrip2.Y - source.Y), 0d, roundtripTolerance); + + Assert.InRange(Math.Abs(roundtrip1.X - roundtrip2.X), 0d, stabilityTolerance); + Assert.InRange(Math.Abs(roundtrip1.Y - roundtrip2.Y), 0d, stabilityTolerance); + Assert.InRange(Math.Abs(projected.X - projectedAgain.X), 0d, stabilityTolerance); + Assert.InRange(Math.Abs(projected.Y - projectedAgain.Y), 0d, stabilityTolerance); + } + + /// + /// Verifies that inverting a concatenated transform invalidates any previously cached inverse instance. + /// + [Fact(DisplayName = "ConcatenatedTransform.Invert clears stale inverse cache")] + public void TestConcatenatedTransformInvertInvalidatesCachedInverse() + { + CoordinateSystem epsg31466 = Assert.IsType(Css.GetCoordinateSystem(31466), exactMatch: false); + CoordinateSystem epsg25832 = Assert.IsType(Css.GetCoordinateSystem(25832), exactMatch: false); + + ConcatenatedTransform forward = Assert.IsType(Assert.IsType(Css.CreateTransformation(epsg31466, epsg25832), exactMatch: false).MathTransform); + MathTransform cachedInverse = forward.Inverse(); + forward.Invert(); + MathTransform inverseAfterInvert = forward.Inverse(); + Assert.NotSame(cachedInverse, inverseAfterInvert); + Assert.Same(inverseAfterInvert, forward.Inverse()); + } + + /// + /// Verifies that concatenated transforms can invert child math transforms that only support . + /// + [Fact(DisplayName = "ConcatenatedTransform uses child Inverse() for immutable math transforms")] + public void ConcatenatedTransformSupportsImmutableChildMathTransforms() + { + var child = new CoordinateTransformation( + GeographicCoordinateSystem.WGS84, + GeographicCoordinateSystem.WGS84, + TransformType.Conversion, + new ImmutableOffsetMathTransform(5d), + "immutable", + string.Empty, + -1, + string.Empty, + string.Empty); + var concatenated = new ConcatenatedTransform([child]); + + MathTransform inverse = concatenated.Inverse(); + double[] projected = concatenated.Transform([10d, 20d, 0d]); + double[] roundtrip = inverse.Transform(projected); + + Assert.Equal(10d, roundtrip[0], 12); + Assert.Equal(20d, roundtrip[1], 12); + + concatenated.Invert(); + (double x, double y, double z) = concatenated.Transform(15d, 25d, 0d); + Assert.Equal(10d, x, 12); + Assert.Equal(20d, y, 12); + Assert.Equal(0d, z, 12); + } + + /// + /// Verifies that empty concatenated transforms fail with a clear exception instead of index errors. + /// + [Fact(DisplayName = "ConcatenatedTransform empty chain throws clear exception on metadata access")] + public void ConcatenatedTransformEmptyChainThrowsClearException() + { + var transform = new ConcatenatedTransform(); + + const string expectedMessage = "Concatenated transform does not contain any child transformations."; + + InvalidOperationException dimSourceException = Assert.Throws(() => _ = transform.DimSource); + InvalidOperationException dimTargetException = Assert.Throws(() => _ = transform.DimTarget); + InvalidOperationException sourceCsException = Assert.Throws(() => _ = transform.SourceCS); + InvalidOperationException targetCsException = Assert.Throws(() => _ = transform.TargetCS); + + Assert.Equal(expectedMessage, dimSourceException.Message); + Assert.Equal(expectedMessage, dimTargetException.Message); + Assert.Equal(expectedMessage, sourceCsException.Message); + Assert.Equal(expectedMessage, targetCsException.Message); + } + + /// + /// Verifies that applies the scale factor only once to rotation terms. + /// + [Fact(DisplayName = "DatumTransform applies single scale factor on rotation terms")] + public void DatumTransformRotationTermsUseSingleScaleFactor() + { + const double secondsToRadians = 4.84813681109535993589914102357e-6; + const double dx = -81.0703; + const double dy = -89.3603; + const double dz = -115.7526; + const double ex = -0.48488; + const double ey = -0.02436; + const double ez = -0.41321; + const double ppm = -540.645; + const double x = 3657660.66; + const double y = 255768.55; + const double z = 5201382.11; + + double scale = 1d + (ppm * 0.000001d); + double rx = ex * secondsToRadians; + double ry = ey * secondsToRadians; + double rz = ez * secondsToRadians; + + double expectedX = (scale * x) - (scale * rz * y) + (scale * ry * z) + dx; + double expectedY = (scale * rz * x) + (scale * y) - (scale * rx * z) + dy; + double expectedZ = (-scale * ry * x) + (scale * rx * y) + (scale * z) + dz; + + var transform = new DatumTransform(new Wgs84ConversionInfo(dx, dy, dz, ex, ey, ez, ppm)); + double[] actual = transform.Transform([x, y, z]); + + Assert.Equal(expectedX, actual[0], 9); + Assert.Equal(expectedY, actual[1], 9); + Assert.Equal(expectedZ, actual[2], 9); + } + + /// + /// Verifies that GitHub issue #20 is fixed: calling does not corrupt + /// subsequent results of the forward transform. + /// + [GitHubIssue(20)] + [Fact(DisplayName = "Issue #20, Math transform bug")] + public void TestMathTransformBug() + { + _ = new CoordinateTransformationFactory(); + var coordinateSystemFactory = new CoordinateSystemFactory(); + var itmParameters = new List + { + new("latitude_of_origin", 31.734393611111109123611111111111), + new("central_meridian", 35.204516944444442572222222222222), + new("false_northing", 626907.390), + new("false_easting", 219529.584), + new("scale_factor", 1.0000067), + }; + + HorizontalDatum itmDatum = coordinateSystemFactory.CreateHorizontalDatum( + "Isreal 1993", + DatumType.HD_Geocentric, + Ellipsoid.GRS80, + new Wgs84ConversionInfo(-24.0024, -17.1032, -17.8444, -0.33077, -1.85269, 1.66969, 5.4248)); + + GeographicCoordinateSystem itmGeo = coordinateSystemFactory.CreateGeographicCoordinateSystem( + "ITM", + AngularUnit.Degrees, + itmDatum, + PrimeMeridian.Greenwich, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + IProjection itmProjection = coordinateSystemFactory.CreateProjection("Transverse_Mercator", "Transverse_Mercator", itmParameters); + ProjectedCoordinateSystem itm = coordinateSystemFactory.CreateProjectedCoordinateSystem( + "ITM", + itmGeo, + itmProjection, + LinearUnit.Metre, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + GeographicCoordinateSystem wgs84 = ProjectedCoordinateSystem.WGS84_UTM(36, true).GeographicCoordinateSystem; + + MathTransform ctFwd = Assert.IsType(Css.CreateTransformation(itm, wgs84), exactMatch: false).MathTransform; + (double X, double Y) pt1a = (X: 200000.0, Y: 600000.0); + (double X, double Y) pt2a = ctFwd.Transform(pt1a.X, pt1a.Y); + (double X, double Y) pt1b = ctFwd.Inverse().Transform(pt2a.X, pt2a.Y); + (double X, double Y) pt2b = ctFwd.Transform(pt1a.X, pt1a.Y); + + Assert.InRange(pt1b.X, pt1a.X - 0.01, pt1a.X + 0.01); + Assert.InRange(pt1b.Y, pt1a.Y - 0.01, pt1a.Y + 0.01); + Assert.Equal(pt2b, pt2a); + } + + /// + /// Verifies that transformations between EPSG 25832 (UTM zone 32N) and EPSG 3857 (Web Mercator) + /// produce accurate results regardless of how the Web Mercator system is obtained. + /// + [Fact] + public void TestIssuesWith3857To25832() + { + ProjectedCoordinateSystem epsg_3857 = ProjectedCoordinateSystem.WebMercator; + Console.WriteLine(epsg_3857.Projection.ClassName); + Console.WriteLine(epsg_3857.WKT); + + CoordinateSystem epsg25832 = Assert.IsType(Css.GetCoordinateSystem(25832), exactMatch: false); + + MathTransform mt1 = Assert.IsType(Css.CreateTransformation(epsg25832, epsg_3857), exactMatch: false).MathTransform; + (int X, int Y) pt25832 = (X: 702575, Y: 6153153); + (double X, double Y) pt_3857ex = (X: 1358761.89, Y: 7456070.47); + + (double X, double Y) pt_3857 = mt1.Transform(pt25832.X, pt25832.Y); + Assert.InRange(pt_3857.X, pt_3857ex.X - 0.015, pt_3857ex.X + 0.015); + Assert.InRange(pt_3857.Y, pt_3857ex.Y - 0.015, pt_3857ex.Y + 0.015); + + epsg_3857 = Assert.IsType(Css.GetCoordinateSystem(3857)); + Console.WriteLine(epsg_3857.Projection.ClassName); + Console.WriteLine(epsg_3857.WKT); + + MathTransform mt2 = Assert.IsType(Css.CreateTransformation(epsg25832, epsg_3857), exactMatch: false).MathTransform; + pt_3857 = mt2.Transform(pt25832.X, pt25832.Y); + Assert.InRange(pt_3857.X, pt_3857ex.X - 0.015, pt_3857ex.X + 0.015); + Assert.InRange(pt_3857.Y, pt_3857ex.Y - 0.015, pt_3857ex.Y + 0.015); + } + + /// + /// Verifies that transforming coordinates from EPSG 26910 (NAD83 / UTM zone 10N) to + /// EPSG 4326 (WGS 84 geographic) produces accurate results. + /// + [Fact(DisplayName = "Convert latitude/longitude to Canada grid NAD83 (epsg:26910)")] + public void TestConvertWgs84ToEPSG26910() + { + CoordinateSystem epsg26910 = Assert.IsType(Css.GetCoordinateSystem("EPSG", 26910), exactMatch: false); + CoordinateSystem epsg_4326 = Assert.IsType(Css.GetCoordinateSystem("EPSG", 4326), exactMatch: false); + + double[] ptI = [3523562.711189, 6246615.391161]; + + ICoordinateTransformation ct = Assert.IsType(Css.CreateTransformation(epsg26910, epsg_4326), exactMatch: false); + (double x, double y) = ct.MathTransform.Transform(ptI[0], ptI[1]); + Assert.InRange(x, -82.0479097d - 0.01d, -82.0479097d + 0.01d); + Assert.InRange(y, 48.4185597d - 0.01d, 48.4185597d + 0.01d); + } + + /// + /// Verifies that transforming EPSG 27700 coordinates to WGS 84 stays within the expected tolerance. + /// + [GitHubIssue(67)] + [Fact(DisplayName = "Issue #67, OSGB36 to WGS84 stays within expected accuracy tolerance")] + public void Osgb36ToWgs84TransformationMatchesExpectedCoordinate() + { + CoordinateSystem source = Assert.IsType(Css.GetCoordinateSystem("EPSG", 27700), exactMatch: false); + CoordinateSystem target = Assert.IsType(Css.GetCoordinateSystem("EPSG", 4326), exactMatch: false); + + ICoordinateTransformation transformation = Assert.IsType(Css.CreateTransformation(source, target), exactMatch: false); + (double longitude, double latitude) = transformation.MathTransform.Transform(362895d, 155602d); + + Assert.InRange(longitude, -2.5335813d - 0.0005d, -2.5335813d + 0.0005d); + Assert.InRange(latitude, 51.2983258d - 0.0005d, 51.2983258d + 0.0005d); + } + + /// + /// Verifies that the NAD83 WKT from GitHub issue #51 transforms to WGS 84 with the expected offset. + /// + [GitHubIssue(51)] + [Fact(DisplayName = "Issue #51, NAD83 WKT transforms to WGS84 within expected tolerance")] + public void Nad83WktToWgs84MatchesExpectedCoordinate() + { + const string nad83Wkt = + """ + GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] + """; + + var transformationFactory = new CoordinateTransformationFactory(); + CoordinateSystem source = CoordinateSystemTestHelpers.RequireCoordinateSystem(nad83Wkt); + + ICoordinateTransformation transformation = transformationFactory.CreateFromCoordinateSystems(source, GeographicCoordinateSystem.WGS84); + (double longitude, double latitude) = transformation.MathTransform.Transform(-120.5757999d, 47.4073238d); + + Assert.InRange(longitude, -120.575814456652d - 0.001d, -120.575814456652d + 0.001d); + Assert.InRange(latitude, 47.4073295963295d - 0.001d, 47.4073295963295d + 0.001d); + } + + /// + /// Verifies that EPSG 28992 coordinates can be transformed to WGS 84 and remain within the Netherlands. + /// + [GitHubIssue(126)] + [Fact(DisplayName = "Issue #126, Amersfoort / RD New transforms to a plausible WGS84 coordinate")] + public void AmersfoortToWgs84TransformationProducesCoordinateInTheNetherlands() + { + CoordinateSystem source = Assert.IsType(Css.GetCoordinateSystem("EPSG", 28992), exactMatch: false); + CoordinateSystem target = Assert.IsType(Css.GetCoordinateSystem("EPSG", 4326), exactMatch: false); + + ICoordinateTransformation transformation = Assert.IsType(Css.CreateTransformation(source, target), exactMatch: false); + (double longitude, double latitude) = transformation.MathTransform.Transform(155000d, 463000d); + + Assert.False(double.IsNaN(longitude)); + Assert.False(double.IsNaN(latitude)); + Assert.InRange(longitude, 3d, 8d); + Assert.InRange(latitude, 50d, 54d); + } + + /// + /// Verifies that GitHub issue #64 is fixed: and + /// correctly store abbreviation and remarks + /// passed to their constructors. + /// + [GitHubIssue(64)] + [Fact(DisplayName = "Issue #64, Wrong parameter order when calling base constructor (in systems extending HorizontalCoordinateSystem)")] + public void TestHorizontalCoordinateSystemImplementationsAbbreviationAndRemarks() + { + string abbreviation = "TestAbbreviation"; + string remarks = "This is a test remark."; + + // construct a GeographicCoordinateSystem to test + var gcsAxes = new List(2) + { + new("Lon", AxisOrientationEnum.East), + new("Lat", AxisOrientationEnum.North), + }; + + var geographicCoordinateSystem = + new GeographicCoordinateSystem( + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + gcsAxes, + "WGS 84", + "EPSG", + 4326, + string.Empty, + abbreviation, + remarks); + + Assert.Equal(abbreviation, geographicCoordinateSystem.Abbreviation); + Assert.Equal(remarks, geographicCoordinateSystem.Remarks); + + // construct a ProjectedCoordinateSystem to test + var pInfo = new List + { + new("latitude_of_origin", 0.0), + new("central_meridian", 0.0), + new("false_easting", 0.0), + new("false_northing", 0.0), + }; + + var proj = new Projection( + "Popular Visualisation Pseudo-Mercator", + pInfo, + "Popular Visualisation Pseudo-Mercator", + "EPSG", + 3856, + "Pseudo-Mercator", + string.Empty, + string.Empty); + + var pcsAxes = new List + { + new("East", AxisOrientationEnum.East), + new("North", AxisOrientationEnum.North), + }; + + var projectedCoordinateSystem = + new ProjectedCoordinateSystem( + HorizontalDatum.WGS84, + GeographicCoordinateSystem.WGS84, + LinearUnit.Metre, + proj, + pcsAxes, + "WGS 84 / Pseudo-Mercator", + "EPSG", + 3857, + "WGS 84 / Popular Visualisation Pseudo-Mercator", + remarks, + abbreviation); + + Assert.Equal(abbreviation, projectedCoordinateSystem.Abbreviation); + Assert.Equal(remarks, projectedCoordinateSystem.Remarks); + } + + private sealed class ImmutableOffsetMathTransform : MathTransform + { + private readonly double offset; + + public ImmutableOffsetMathTransform(double offset) + { + this.offset = offset; + } + + public override int DimSource => 2; + + public override int DimTarget => 2; + + public override string WKT => throw new NotImplementedException(); + + public override string XML => throw new NotImplementedException(); + + public override bool Identity() => this.offset == 0d; + + public override MathTransform Inverse() => new ImmutableOffsetMathTransform(-this.offset); + + public override void Invert() => throw new NotSupportedException(); + + public override void Transform(ref double x, ref double y, ref double z) + { + x += this.offset; + y += this.offset; + } + } +} diff --git a/test/ProjNet.Tests/GitHub/GitHubIssueWktRegressionTests.cs b/test/ProjNet.Tests/GitHub/GitHubIssueWktRegressionTests.cs new file mode 100644 index 00000000..5fb85cb1 --- /dev/null +++ b/test/ProjNet.Tests/GitHub/GitHubIssueWktRegressionTests.cs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.GitHub; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Regression tests for GitHub issues that focus on WKT parsing behavior. +/// +public class GitHubIssueWktRegressionTests +{ + private const string ExtensionWkt1 = + """ + PROJCS["NAD27/BLM59N(ftUS)",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke1866",6378206.4,294.978698213898],EXTENSION["PROJ4_GRIDS","NTv2_0.gsb"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",171],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",1640416.67],PARAMETER["false_northing",0],UNIT["USsurveyfoot",0.304800609601219],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","4399"]] + """; + + private const string ExtensionWkt2 = + """ + PROJCS["NAD27/BLM60N(ftUS)",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke1866",6378206.4,294.978698213898],EXTENSION["PROJ4_GRIDS","NTv2_0.gsb"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",177],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",1640416.67],PARAMETER["false_northing",0],UNIT["USsurveyfoot",0.304800609601219],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","4400"]] + """; + + private const string ExtensionWkt3 = + """ + PROJCS["WGS84/Pseudo-Mercator",GEOGCS["WGS84",DATUM["WGS_1984",SPHEROID["WGS84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],EXTENSION["PROJ4","+proj=merc+a=6378137+b=6378137+lat_ts=0+lon_0=0+x_0=0+y_0=0+k=1+units=m+nadgrids=@null+wktext+no_defs"],AUTHORITY["EPSG","3857"]] + """; + + private const string ExtensionWkt4 = + """ + COMPD_CS["WGS84/Pseudo-Mercator+EGM2008geoidheight",PROJCS["WGS84/Pseudo-Mercator",GEOGCS["WGS84",DATUM["WGS_1984",SPHEROID["WGS84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],EXTENSION["PROJ4","+proj=merc+a=6378137+b=6378137+lat_ts=0+lon_0=0+x_0=0+y_0=0+k=1+units=m+nadgrids=@null+wktext+no_defs"],AUTHORITY["EPSG","3857"]],VERT_CS["EGM2008height",VERT_DATUM["EGM2008geoid",2005,AUTHORITY["EPSG","1027"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Gravity-relatedheight",UP],AUTHORITY["EPSG","3855"]],AUTHORITY["EPSG","6871"]] + """; + + private const string Xian1980Wkt = + """ + PROJCS["Xian_1980_GK_CM_105E",GEOGCS["GCS_Xian_1980",DATUM["Xian_1980",SPHEROID["Xian_1980",6332140,398.257,AUTHORITY["EPSG","7049"]],AUTHORITY["EPSG","6610"]],PRIMEM["Greenwich",0],UNIT["degree",0.0171234925199433]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",105],PARAMETER["scale_factor",1],PARAMETER["false_easting",500000],PARAMETER["false_northing",0]] + """; + + /// + /// Gets DATUM-level EXTENSION samples from upstream pull request #111. + /// + public static IEnumerable> DatumExtensionCases + { + get + { + yield return new TheoryDataRow(ExtensionWkt1, 4399L); + yield return new TheoryDataRow(ExtensionWkt2, 4400L); + } + } + + /// + /// Verifies that GitHub issue #106 is fixed for DATUM-level EXTENSION nodes from PR #111. + /// + /// Projected WKT with a DATUM-level EXTENSION node. + /// Expected projected authority code. + [GitHubIssue(106)] + [Theory(DisplayName = "Issue #106, DATUM-level EXTENSION WKTs parse successfully")] + [MemberData(nameof(DatumExtensionCases))] + public void DatumLevelExtensionsParseAsProjectedCoordinateSystems(string wkt, long expectedAuthorityCode) + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(wkt); + + Assert.Equal(expectedAuthorityCode, projected.AuthorityCode); + Assert.Equal("Transverse_Mercator", projected.Projection.ClassName); + Assert.Equal(4267, projected.GeographicCoordinateSystem.AuthorityCode); + } + + /// + /// Verifies that GitHub issue #106 is fixed for the PROJCS-level EXTENSION sample from PR #111. + /// + [GitHubIssue(106)] + [Fact(DisplayName = "Issue #106, PROJCS-level EXTENSION WKT parses successfully")] + public void ProjcsLevelExtensionParsesAsProjectedCoordinateSystem() + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(ExtensionWkt3); + + Assert.Equal(3857, projected.AuthorityCode); + Assert.Equal("Mercator_1SP", projected.Projection.ClassName); + Assert.Equal(4326, projected.GeographicCoordinateSystem.AuthorityCode); + } + + /// + /// Verifies that GitHub issue #106 is fixed for the compound CRS sample from PR #111. + /// + [GitHubIssue(106)] + [Fact(DisplayName = "Issue #106, COMPD_CS with nested EXTENSION parses successfully")] + public void CompoundCoordinateSystemWithExtensionParsesSuccessfully() + { + CompoundCoordinateSystem compound = CoordinateSystemTestHelpers.RequireCoordinateSystem(ExtensionWkt4); + + Assert.Equal(6871, compound.AuthorityCode); + Assert.IsType(compound.HeadCoordinateSystem); + Assert.IsType(compound.TailCoordinateSystem); + } + + /// + /// Verifies that the Xian 1980 WKT from GitHub issue #65 parses as a projected coordinate system. + /// + [GitHubIssue(65)] + [Fact(DisplayName = "Issue #65, Xian_1980 projected WKT parses successfully")] + public void Xian1980ProjectedCoordinateSystemParsesSuccessfully() + { + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(Xian1980Wkt); + + Assert.Equal("Transverse_Mercator", projected.Projection.ClassName); + Assert.Equal(6332140d, projected.GeographicCoordinateSystem.HorizontalDatum.Ellipsoid.SemiMajorAxis); + } +} diff --git a/test/ProjNet.Tests/GitHub/Issues.cs b/test/ProjNet.Tests/GitHub/Issues.cs deleted file mode 100644 index a74ed5e8..00000000 --- a/test/ProjNet.Tests/GitHub/Issues.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using ProjNet; -using ProjNet.CoordinateSystems; -using ProjNet.CoordinateSystems.Transformations; - -namespace ProjNET.Tests.GitHub -{ - [Category("GitHub Issue")] - public class Issues - { - // - private static CoordinateSystemServices _css = new CoordinateSystemServices(CoordinateSystemServicesTest.LoadCsv()); - - [Test(Description = "Issue #10, ConcatenatedTransform.Inverse() method destroys the state of child transformations")] - public void TestConcatenatedTransformInvert() - { - - var epsg31466 = _css.GetCoordinateSystem(31466); - var epsg25832 = _css.GetCoordinateSystem(25832); - - var ctFwd = (ConcatenatedTransform)_css.CreateTransformation(epsg31466, epsg25832).MathTransform; - var ctRev = (ConcatenatedTransform)ctFwd.Inverse(); - - var ctlFwd = ctFwd.CoordinateTransformationList; - var ctlRev = ctRev.CoordinateTransformationList; - - Assert.That(ReferenceEquals(ctlFwd, ctlRev), Is.False); - Assert.That(ctlFwd.Count, Is.EqualTo(ctlRev.Count)); - for (int i = 0, j = ctlFwd.Count - 1; i < ctlFwd.Count; i++, j--) - Assert.That(ReferenceEquals(ctlFwd[i], ctlRev[j]), Is.False); - } - - [Test(Description = "Issue #20, Math transform bug")] - public void TestMathTransformBug() - { - var coordinateTransformFactory = new CoordinateTransformationFactory(); - var coordinateSystemFactory = new CoordinateSystemFactory(); - var itmParameters = new List - { - new ProjectionParameter("latitude_of_origin", 31.734393611111109123611111111111), - new ProjectionParameter("central_meridian", 35.204516944444442572222222222222), - new ProjectionParameter("false_northing", 626907.390), - new ProjectionParameter("false_easting", 219529.584), - new ProjectionParameter("scale_factor", 1.0000067) - }; - - var itmDatum = coordinateSystemFactory.CreateHorizontalDatum("Isreal 1993", DatumType.HD_Geocentric, - Ellipsoid.GRS80, new Wgs84ConversionInfo(-24.0024, -17.1032, -17.8444, -0.33077, -1.85269, 1.66969, 5.4248)); - - var itmGeo = coordinateSystemFactory.CreateGeographicCoordinateSystem("ITM", AngularUnit.Degrees, itmDatum, - PrimeMeridian.Greenwich, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var itmProjection = coordinateSystemFactory.CreateProjection("Transverse_Mercator", "Transverse_Mercator", itmParameters); - var itm = coordinateSystemFactory.CreateProjectedCoordinateSystem("ITM", itmGeo, itmProjection, LinearUnit.Metre, - new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North)); - - var wgs84 = ProjectedCoordinateSystem.WGS84_UTM(36, true).GeographicCoordinateSystem; - - var ctFwd = _css.CreateTransformation(itm, wgs84).MathTransform; - var pt1a = (x: 200000.0, y: 600000.0); - var pt2a = ctFwd.Transform(pt1a.x, pt1a.y); - var pt1b = ctFwd.Inverse().Transform(pt2a.x, pt2a.y); - var pt2b = ctFwd.Transform(pt1a.x, pt1a.y); - - Assert.That(pt1a.x, Is.EqualTo(pt1b.x).Within(0.01)); - Assert.That(pt1a.y, Is.EqualTo(pt1b.y).Within(0.01)); - Assert.That(pt2a, Is.EqualTo(pt2b)); - - } - - [Test] - public void TestIssuesWith3857To25832() - { - var epsg_3857 = ProjectedCoordinateSystem.WebMercator; - Console.WriteLine(epsg_3857.Projection.ClassName); - Console.WriteLine(epsg_3857.WKT); - - var epsg25832 = _css.GetCoordinateSystem(25832); - - var mt1 = _css.CreateTransformation(epsg25832, epsg_3857).MathTransform; - var pt25832 = (x: 702575, y: 6153153); - var pt_3857ex = (x: 1358761.89, y: 7456070.47); - - var pt_3857 = mt1.Transform(pt25832.x, pt25832.y); - Assert.That(pt_3857.x, Is.EqualTo(pt_3857ex.x).Within(0.015)); - Assert.That(pt_3857.y, Is.EqualTo(pt_3857ex.y).Within(0.015)); - - epsg_3857 = (ProjectedCoordinateSystem)_css.GetCoordinateSystem(3857); - Console.WriteLine(epsg_3857.Projection.ClassName); - Console.WriteLine(epsg_3857.WKT); - - var mt2 = _css.CreateTransformation(epsg25832, epsg_3857).MathTransform; - pt_3857 = mt2.Transform(pt25832.x, pt25832.y); - Assert.That(pt_3857.x, Is.EqualTo(pt_3857ex.x).Within(0.015)); - Assert.That(pt_3857.y, Is.EqualTo(pt_3857ex.y).Within(0.015)); - } - - [Test, Description("Convert latitude/longitude to Canada grid NAD83 (epsg:26910)")] - public void TestConvertWgs84ToEPSG26910() - { - var epsg26910 = _css.GetCoordinateSystem("EPSG", 26910); - var epsg_4326 = _css.GetCoordinateSystem("EPSG", 4326); - - double[] ptI = { 3523562.711189, 6246615.391161 }; - - - var ct = _css.CreateTransformation(epsg26910, epsg_4326); - var pt1a = ct.MathTransform.Transform(ptI[0], ptI[1]); - Assert.That(pt1a.x, Is.EqualTo(-82.0479097).Within(0.01), "Longitude"); - Assert.That(pt1a.y, Is.EqualTo(48.4185597).Within(0.01), "Latitude"); - /* - var pt1b = ct.MathTransform.Inverse().Transform(pt1a); - Assert.That(pt1b[0], Is.EqualTo(3523562.711189).Within(0.01), "Easting"); - Assert.That(pt1b[1], Is.EqualTo(6246615.391161).Within(0.01), "Northing"); - */ - } - - [Test, Ignore("Requires DotSpatial.Projections, Result same as in TestConvertWgs84ToEPSG26910")] - public void TestConvertWgs84ToEPSG26910_DS() - { - /* - var epsg26910 = DotSpatial.Projections.ProjectionInfo.FromEpsgCode(26910); - var epsg_4326 = DotSpatial.Projections.ProjectionInfo.FromEpsgCode(4326); - - var ptI = new double[] { 3523562.711189, 6246615.391161 }; - - DotSpatial.Projections.Reproject.ReprojectPoints(ptI, null, epsg26910, epsg_4326, 0, 1); - Assert.That(ptI[0], Is.EqualTo(-82.0479097).Within(0.01), "Longitude"); - Assert.That(ptI[1], Is.EqualTo(48.4185597).Within(0.01), "Latitude"); - - - DotSpatial.Projections.Reproject.ReprojectPoints(ptI, null, epsg_4326, epsg26910, 0, 1); - Assert.That(ptI[0], Is.EqualTo(3523562.711189).Within(0.01), "Easting"); - Assert.That(ptI[1], Is.EqualTo(6246615.391161).Within(0.01), "Northing"); - */ - } - - [Test(Description = - "Issue #64, Wrong parameter order when calling base constructor (in systems extending HorizontalCoordinateSystem)")] - public void TestHorizontalCoordinateSystemImplementationsAbbreviationAndRemarks() - { - string abbreviation = "TestAbbreviation"; - string remarks = "This is a test remark."; - - // construct a GeographicCoordinateSystem to test - var gcsAxes = new List(2); - gcsAxes.Add(new AxisInfo("Lon", AxisOrientationEnum.East)); - gcsAxes.Add(new AxisInfo("Lat", AxisOrientationEnum.North)); - - var geographicCoordinateSystem = - new GeographicCoordinateSystem(AngularUnit.Degrees, HorizontalDatum.WGS84, PrimeMeridian.Greenwich, gcsAxes, - "WGS 84", "EPSG", 4326, string.Empty, abbreviation, remarks); - - Assert.That(geographicCoordinateSystem.Abbreviation, Is.EqualTo(abbreviation)); - Assert.That(geographicCoordinateSystem.Remarks, Is.EqualTo(remarks)); - - // construct a ProjectedCoordinateSystem to test - var pInfo = new List - { - new ProjectionParameter("latitude_of_origin", 0.0), - new ProjectionParameter("central_meridian", 0.0), - new ProjectionParameter("false_easting", 0.0), - new ProjectionParameter("false_northing", 0.0) - }; - - var proj = new Projection("Popular Visualisation Pseudo-Mercator", pInfo, "Popular Visualisation Pseudo-Mercator", "EPSG", 3856, - "Pseudo-Mercator", string.Empty, string.Empty); - - var pcsAxes = new List - { - new AxisInfo("East", AxisOrientationEnum.East), - new AxisInfo("North", AxisOrientationEnum.North) - }; - - var projectedCoordinateSystem = - new ProjectedCoordinateSystem(HorizontalDatum.WGS84, GeographicCoordinateSystem.WGS84, LinearUnit.Metre, proj, pcsAxes, - "WGS 84 / Pseudo-Mercator", "EPSG", 3857, "WGS 84 / Popular Visualisation Pseudo-Mercator", - remarks, abbreviation); - - Assert.That(projectedCoordinateSystem.Abbreviation, Is.EqualTo(abbreviation)); - Assert.That(projectedCoordinateSystem.Remarks, Is.EqualTo(remarks)); - } - } -} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/CoordinateSystemWktReaderWkt2Tests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/CoordinateSystemWktReaderWkt2Tests.cs new file mode 100644 index 00000000..d2617293 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/CoordinateSystemWktReaderWkt2Tests.cs @@ -0,0 +1,1316 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.Data; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Verifies native WKT2 coordinate-system parsing against real EPSG export examples and PROJ test fixtures. +/// +public class CoordinateSystemWktReaderWkt2Tests +{ + private const string ProjectedProjBoundCrs = """ + BOUNDCRS[ + SOURCECRS[ + PROJCRS["NAD83 / California zone 3 (ftUS)", + BASEGEODCRS["NAD83", + DATUM["North American Datum 1983", + ELLIPSOID["GRS 1980",6378137,298.257222101, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]]], + CONVERSION["SPCS83 California zone 3 (US Survey feet)", + METHOD["Lambert Conic Conformal (2SP)", + ID["EPSG",9802]], + PARAMETER["Latitude of false origin",36.5, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8821]], + PARAMETER["Longitude of false origin",-120.5, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8822]], + PARAMETER["Latitude of 1st standard parallel",38.4333333333333, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8823]], + PARAMETER["Latitude of 2nd standard parallel",37.0666666666667, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8824]], + PARAMETER["Easting at false origin",6561666.667, + LENGTHUNIT["US survey foot",0.304800609601219], + ID["EPSG",8826]], + PARAMETER["Northing at false origin",1640416.667, + LENGTHUNIT["US survey foot",0.304800609601219], + ID["EPSG",8827]]], + CS[Cartesian,2], + AXIS["easting (X)",east, + ORDER[1], + LENGTHUNIT["US survey foot",0.304800609601219]], + AXIS["northing (Y)",north, + ORDER[2], + LENGTHUNIT["US survey foot",0.304800609601219]], + SCOPE["unknown"], + AREA["USA - California - SPCS - 3"], + BBOX[36.73,-123.02,38.71,-117.83], + ID["EPSG",2227]]], + TARGETCRS[ + GEODCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]], + CS[ellipsoidal,2], + AXIS["latitude",north, + ORDER[1], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["longitude",east, + ORDER[2], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4326]]], + ABRIDGEDTRANSFORMATION["NAD83 to WGS 84 (1)", + METHOD["Geocentric translations (geog2D domain)", + ID["EPSG",9603]], + PARAMETER["X-axis translation",0, + ID["EPSG",8605]], + PARAMETER["Y-axis translation",0, + ID["EPSG",8606]], + PARAMETER["Z-axis translation",0, + ID["EPSG",8607]], + SCOPE["unknown"], + AREA["North America - Canada and USA (CONUS, Alaska mainland)"], + BBOX[23.81,-172.54,86.46,-47.74], + ID["EPSG",1188]]] + """; + + private const string VerticalProjBoundCrs = """ + BOUNDCRS[ + SOURCECRS[ + VERTCRS["EGM96 height", + VDATUM["EGM96 geoid"], + CS[vertical,1], + AXIS["gravity-related height (H)",up, + LENGTHUNIT["metre",1]], + USAGE[ + SCOPE["Geodesy."], + AREA["World."], + BBOX[-90,-180,90,180]], + ID["EPSG",5773]]], + TARGETCRS[ + GEOGCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]], + CS[ellipsoidal,3], + AXIS["latitude",north, + ORDER[1], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["longitude",east, + ORDER[2], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["ellipsoidal height",up, + ORDER[3], + LENGTHUNIT["metre",1]], + ID["EPSG",4979]]], + ABRIDGEDTRANSFORMATION["WGS 84 to EGM96 height (1)", + METHOD["Geographic3D to GravityRelatedHeight (EGM)", + ID["EPSG",9661]], + PARAMETERFILE["Geoid (height correction) model file","us_nga_egm96_15.tif"]]] + """; + + private const string EllipsoidalHeightBoundCrs = """ + BOUNDCRS[ + SOURCECRS[ + GEOGCRS["TWD97",DATUM["Taiwan Datum 1997",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,3],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],AXIS["ellipsoidal height (h)",up,ORDER[3],LENGTHUNIT["metre",1]],USAGE[SCOPE["unknown"],AREA["Taiwan"],BBOX[17.36,114.32,26.96,123.61]],ID["EPSG",3823]]], + TARGETCRS[ + GEOGCRS["WGS 84",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["latitude",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["longitude",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]]], + ABRIDGEDTRANSFORMATION["TWD97 to WGS 84 (1)",VERSION["OGP-Twn"],METHOD["Geocentric translations (geog2D domain)",ID["EPSG",9603]],PARAMETER["X-axis translation",0,ID["EPSG",8605]],PARAMETER["Y-axis translation",0,ID["EPSG",8606]],PARAMETER["Z-axis translation",0,ID["EPSG",8607]],USAGE[SCOPE["unknown"],AREA["Taiwan"],BBOX[17.36,114.32,26.96,123.61]],ID["DERIVED_FROM(EPSG)",3830]]] + """; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly Lazy> CatalogEntries = new(() => + new ManagedCoordinateSystemDefinitionProvider() + .GetCoordinateSystems() + .OrderBy(entry => entry.Srid) + .ToList()); + + private static readonly Lazy> CatalogDefinitions = new(() => + new ManagedCoordinateSystemDefinitionProvider() + .GetDefinitions() + .GroupBy(item => item.Srid) + .ToDictionary(group => group.Key, group => group.Last().Wkt)); + + /// + /// Provides representative EPSG-backed WKT2 geodetic CRS examples. + /// + /// SRID/WKT pairs that should parse successfully. + public static IEnumerable> SupportedWkt2Rows() + => EpsgArchiveWktFixtureSource.GetTheoryDataRows( + 4230, + 4267, + 4277, + 4312, + 4314, + 4322, + 10176, + 10412); + + /// + /// Provides representative top-level ellipsoidal 3D WKT2 geographic CRS examples from the PostGIS failure set. + /// + /// SRID/WKT pairs that should now parse successfully as operational compounds. + public static IEnumerable> SupportedWkt2Ellipsoidal3dRows() + { + return + [ + new TheoryDataRow(4329, """GEOGCRS["WGS 84 (3D)",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,3],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree minute second hemisphere",0.0174532925199433]],AXIS["geodetic longitude (Long)",east,ORDER[2],ANGLEUNIT["degree minute second hemisphere",0.0174532925199433]],AXIS["ellipsoidal height (h)",up,ORDER[3],LENGTHUNIT["metre",1]],USAGE[SCOPE["unknown"],AREA["World (by country)"],BBOX[-90,-180,90,180]],ID["EPSG",4329]]"""), + new TheoryDataRow(4979, """GEOGCRS["WGS 84",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,3],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],AXIS["ellipsoidal height (h)",up,ORDER[3],LENGTHUNIT["metre",1]],USAGE[SCOPE["unknown"],AREA["World (by country)"],BBOX[-90,-180,90,180]],ID["EPSG",4979]]"""), + ]; + } + + /// + /// Provides representative EPSG-backed datum-backed WKT2 projected CRS examples. + /// + /// SRID/WKT pairs that should parse successfully. + public static IEnumerable> SupportedWkt2ProjectedRows() + => EpsgArchiveWktFixtureSource.GetTheoryDataRows( + 27700, + 31370, + 2169, + 23032, + 31467); + + /// + /// Provides representative EPSG-backed top-level projected 3D WKT2 CRS examples. + /// + /// SRID/WKT pairs that should now parse successfully as operational compounds. + public static IEnumerable> SupportedWkt2Projected3dRows() + => EpsgArchiveWktFixtureSource.GetTheoryDataRows(9895); + + /// + /// Provides representative EPSG-backed WKT2 vertical CRS examples. + /// + /// SRID/WKT pairs that should parse successfully. + public static IEnumerable> SupportedWkt2VerticalRows() + => EpsgArchiveWktFixtureSource.GetTheoryDataRows(10150, 10190, 10352); + + /// + /// Provides representative EPSG-backed datum-backed WKT2 compound CRS examples. + /// + /// SRID/WKT pairs that should parse successfully. + public static IEnumerable> SupportedWkt2CompoundRows() + => EpsgArchiveWktFixtureSource.GetTheoryDataRows(10162, 10163, 10164); + + /// + /// Provides representative supported engineering CRS examples. + /// + /// WKT2 engineering CRS strings that should parse successfully and roundtrip semantically. + public static IEnumerable> SupportedEngineeringWkt2Rows() + { + return + [ + new TheoryDataRow("""ENGCRS["Engineering example",EDATUM["Local engineering datum",ID["TEST",1001]],CS[Cartesian,2],AXIS["x",east],AXIS["y",north],LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["TEST",5800]]"""), + ]; + } + + /// + /// Provides representative supported temporal CRS examples. + /// + /// WKT2 temporal CRS strings that should parse successfully and roundtrip semantically. + public static IEnumerable> SupportedTemporalWkt2Rows() + { + return + [ + new TheoryDataRow("""TIMECRS["Temporal example",TDATUM["Unix epoch",TIMEORIGIN["1970-01-01T00:00:00Z"],ID["TEST",1040]],CS[temporal,1],AXIS["time",north],TIMEUNIT["second",1,ID["EPSG",1040]],ID["TEST",1041]]"""), + ]; + } + + /// + /// Provides representative supported parametric CRS examples. + /// + /// WKT2 parametric CRS strings that should parse successfully and roundtrip semantically. + public static IEnumerable> SupportedParametricWkt2Rows() + { + return + [ + new TheoryDataRow("""PARAMETRICCRS["Reservoir pressure",PDATUM["Reservoir datum",ID["TEST",2001]],CS[parametric,1],AXIS["pressure",up],PARAMETRICUNIT["bar",100000,ID["TEST",2002]],ID["TEST",2003]]"""), + ]; + } + + /// + /// Provides representative supported coordinate-operation examples. + /// + /// WKT2 coordinate operation strings that should parse successfully and roundtrip semantically. + public static IEnumerable> SupportedCoordinateOperationWkt2Rows() + { + return + [ + new TheoryDataRow("""COORDINATEOPERATION["Axis swap",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],METHOD["Axis Order Reversal"],PARAMETER["Order reversal",1],ID["TEST",3001]]"""), + ]; + } + + /// + /// Provides representative supported concatenated-operation examples. + /// + /// WKT2 concatenated operation strings that should parse successfully and roundtrip semantically. + public static IEnumerable> SupportedConcatenatedOperationWkt2Rows() + { + return + [ + new TheoryDataRow("""CONCATENATEDOPERATION["Two-step chain",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],STEP[COORDINATEOPERATION["Step 1",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Intermediate CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],METHOD["Axis swap"],PARAMETER["Order reversal",1]]],STEP[COORDINATEOPERATION["Step 2",SOURCECRS[GEOGCRS["Intermediate CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],METHOD["Unit scale"],PARAMETER["Scale difference",1]]],ID["TEST",3002]]"""), + ]; + } + + /// + /// Provides representative unsupported top-level WKT2 roots that are intentionally outside the current native or normalized reader surface. + /// + /// Keyword/WKT pairs that should still be rejected explicitly. + public static IEnumerable> UnsupportedTopLevelWkt2Rows() + { + return + [ + new TheoryDataRow("COORDINATEMETADATA", """COORDINATEMETADATA["Metadata example"]""", "COORDINATEMETADATA"), + ]; + } + + /// + /// Provides real supported BOUNDCRS fixtures extracted from the checked-in PROJ test suite. + /// + /// Fixture source labels and WKT2 strings that should parse onto the current source-CRS model. + public static IEnumerable> SupportedProjBoundCrsRows() + { + return + [ + new TheoryDataRow("PROJ test_operationfactory.cpp:3815", ProjectedProjBoundCrs), + ]; + } + + /// + /// Provides real supported vertical BOUNDCRS fixtures extracted from the checked-in PROJ test suite. + /// + /// Fixture source labels and WKT2 strings that should parse onto the current source-CRS model. + public static IEnumerable> SupportedVerticalBoundCrsRows() + { + return + [ + new TheoryDataRow("PROJ test_operationfactory.cpp:9132", VerticalProjBoundCrs), + ]; + } + + /// + /// Verifies supported WKT2 CRS parse to the same semantic model as the committed catalog reference. + /// + /// Expected EPSG SRID. + /// WKT2 CRS from the EPSG export. + [Theory] + [MemberData(nameof(SupportedWkt2Rows))] + public void CreateFromWkt_ParsesSupportedWkt2CrsEquivalentToCatalogReference(int srid, string wkt) + { + CoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + CoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.IsType(reference.GetType(), parsed); + Assert.True(parsed.EqualParams(reference), $"WKT2 parse mismatch for EPSG:{srid}."); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + } + + /// + /// Verifies supported WKT2 projected CRS parse to the same semantic model as the committed catalog reference. + /// + /// Expected EPSG SRID. + /// WKT2 projected CRS from the EPSG export. + [Theory] + [MemberData(nameof(SupportedWkt2ProjectedRows))] + public void CreateFromWkt_ParsesSupportedWkt2ProjectedCrsEquivalentToCatalogReference(int srid, string wkt) + { + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.True(parsed.EqualParams(reference), $"WKT2 projected CRS parse mismatch for EPSG:{srid}."); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + } + + /// + /// Verifies supported WKT2 vertical CRS parse to the same semantic model as the committed catalog reference. + /// + /// Expected EPSG SRID. + /// WKT2 vertical CRS from the EPSG export. + [Theory] + [MemberData(nameof(SupportedWkt2VerticalRows))] + public void CreateFromWkt_ParsesSupportedWkt2VerticalCrsEquivalentToCatalogReference(int srid, string wkt) + { + VerticalCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + VerticalCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.True(parsed.EqualParams(reference), $"WKT2 vertical CRS parse mismatch for EPSG:{srid}."); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + } + + /// + /// Verifies supported WKT2 compound CRS parse to the same semantic model as the committed catalog reference. + /// + /// Expected EPSG SRID. + /// WKT2 compound CRS from the EPSG export. + [Theory] + [MemberData(nameof(SupportedWkt2CompoundRows))] + public void CreateFromWkt_ParsesSupportedWkt2CompoundCrsEquivalentToCatalogReference(int srid, string wkt) + { + CompoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + CompoundCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.True(parsed.EqualParams(reference), $"WKT2 compound CRS parse mismatch for EPSG:{srid}."); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + } + + /// + /// Verifies top-level ellipsoidal 3D WKT2 geographic CRS now parse through the existing operational compound representation. + /// + /// Expected EPSG SRID. + /// WKT2 geographic CRS from the PostGIS failure set. + [Theory] + [MemberData(nameof(SupportedWkt2Ellipsoidal3dRows))] + public void CreateFromWkt_ParsesTopLevelEllipsoidal3dGeographicCrsAsOperationalCompound(int srid, string wkt) + { + CompoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem horizontal = Assert.IsType(parsed.HeadCoordinateSystem); + VerticalCoordinateSystem vertical = Assert.IsType(parsed.TailCoordinateSystem); + + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + Assert.Equal(3, parsed.Dimension); + Assert.Equal(AxisOrientationEnum.North, parsed.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.East, parsed.GetAxis(1).Orientation); + Assert.Equal(AxisOrientationEnum.Up, parsed.GetAxis(2).Orientation); + Assert.Equal("World Geodetic System 1984", horizontal.HorizontalDatum.Name); + Assert.True(horizontal.HorizontalDatum.Ellipsoid.EqualParams(Ellipsoid.WGS84)); + Assert.Equal(DatumType.VD_Ellipsoidal, vertical.VerticalDatum.DatumType); + Assert.Equal("metre", vertical.LinearUnit.Name); + } + + /// + /// Verifies top-level projected 3D WKT2 CRS now parse through the existing operational compound representation. + /// + /// Expected EPSG SRID. + /// WKT2 projected CRS from the EPSG export. + [Theory] + [MemberData(nameof(SupportedWkt2Projected3dRows))] + public void CreateFromWkt_ParsesTopLevelProjected3dCrsAsOperationalCompound(int srid, string wkt) + { + CompoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ProjectedCoordinateSystem horizontal = Assert.IsType(parsed.HeadCoordinateSystem); + VerticalCoordinateSystem vertical = Assert.IsType(parsed.TailCoordinateSystem); + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + Assert.Equal(3, parsed.Dimension); + Assert.True(horizontal.EqualParams(reference), $"WKT2 projected 3D head mismatch for EPSG:{srid}."); + Assert.Equal(AxisOrientationEnum.North, parsed.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.East, parsed.GetAxis(1).Orientation); + Assert.Equal(AxisOrientationEnum.Up, parsed.GetAxis(2).Orientation); + Assert.Equal(DatumType.VD_Ellipsoidal, vertical.VerticalDatum.DatumType); + Assert.Equal("metre", vertical.LinearUnit.Name); + } + + /// + /// Verifies direct optional metadata blocks are tolerated on projected WKT2 nodes. + /// + [Fact] + public void CreateFromWkt_ParsesProjectedCrsWithDirectOptionalMetadataBlocksEquivalentToCatalogReference() + { + const string wkt = """PROJCRS["OSGB36 / British National Grid",BASEGEOGCRS["OSGB36",DATUM["Ordnance Survey of Great Britain 1936",REMARK["datum remark"],ELLIPSOID["Airy 1830",6377563.396,299.3249646,LENGTHUNIT["metre",1,REMARK["ellipsoid unit remark"],ID["EPSG",9001]],REMARK["ellipsoid remark"],ID["EPSG",7001]],ID["EPSG",6277]],ID["EPSG",4277]],CONVERSION["British National Grid",REMARK["conversion remark"],METHOD["Transverse Mercator",REMARK["method remark"],ID["EPSG",9807]],PARAMETER["Latitude of natural origin",49,ANGLEUNIT["degree",0.0174532925199433,REMARK["angle remark"],ID["EPSG",9102]],REMARK["latitude parameter remark"],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",-2,ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996012717,SCALEUNIT["unity",1,REMARK["scale unit remark"],ID["EPSG",9201]],ID["EPSG",8805]],PARAMETER["False easting",400000,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8806]],PARAMETER["False northing",-100000,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8807]],ID["EPSG",19916]],CS[Cartesian,2,ID["EPSG",4499]],AXIS["Easting (E)",east,ORDER[1]],AXIS["Northing (N)",north,ORDER[2]],LENGTHUNIT["metre",1,REMARK["root unit remark"],ID["EPSG",9001]],REMARK["projected root remark"],SCOPE["Engineering survey, topographic mapping."],AREA["United Kingdom (UK) - offshore to boundary of UKCS within 49°45'N to 61°N and 9°W to 2°E; onshore Great Britain (England, Wales and Scotland). Isle of Man onshore."],BBOX[49.75,-9.01,61.01,2.01],ID["EPSG",27700]]"""; + + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(27700)); + + Assert.True(parsed.EqualParams(reference)); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(27700, parsed.AuthorityCode); + } + + /// + /// Verifies USAGE-wrapped optional metadata blocks are tolerated on vertical WKT2 nodes. + /// + [Fact] + public void CreateFromWkt_ParsesVerticalCrsWithUsageMetadataEquivalentToCatalogReference() + { + const string wkt = """VERTCRS["NGA 2022 height",REMARK["vertical root remark"],VDATUM["Nivellement General de l'Algerie 2022",REMARK["vertical datum remark"],ID["EPSG",1354]],CS[vertical,1,ID["EPSG",6499]],AXIS["Gravity-related height (H)",up,ORDER[1]],LENGTHUNIT["metre",1,REMARK["vertical unit remark"],ID["EPSG",9001]],USAGE[SCOPE["Geodesy."],AREA["Algeria - onshore."],BBOX[18.97,-8.67,37.09,11.99]],ID["EPSG",10190]]"""; + + VerticalCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + VerticalCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(10190)); + + Assert.True(parsed.EqualParams(reference)); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(10190, parsed.AuthorityCode); + } + + /// + /// Verifies affine derived geographic WKT2 definitions parse onto and retain the fitted axis metadata. + /// + [Fact] + public void CreateFromWkt_WithDerivedGeographicCrs_ParsesFittedCoordinateSystemAndRetainsAxisMetadata() + { + const string wkt = """GEOGCRS["Local WGS 84",BASEGEOGCRS["WGS 84",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ID["EPSG",6326]],ID["EPSG",4326]],DERIVINGCONVERSION["unnamed",METHOD["Affine parametric transformation"],PARAMETER["A0",0.5,ANGLEUNIT["degree",0.0174532925199433]],PARAMETER["A1",1,SCALEUNIT["unity",1]],PARAMETER["A2",0,SCALEUNIT["unity",1]],PARAMETER["B0",1.5,ANGLEUNIT["degree",0.0174532925199433]],PARAMETER["B1",0,SCALEUNIT["unity",1]],PARAMETER["B2",1,SCALEUNIT["unity",1]]],CS[ellipsoidal,2],AXIS["Local latitude",north,ORDER[1]],AXIS["Local longitude",east,ORDER[2]],ANGLEUNIT["degree",0.0174532925199433],ID["TEST",2001]]"""; + + FittedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem baseCoordinateSystem = Assert.IsType(parsed.BaseCoordinateSystem); + + Assert.Equal("Local WGS 84", parsed.Name); + Assert.Equal("TEST", parsed.Authority); + Assert.Equal(2001, parsed.AuthorityCode); + Assert.Equal("WGS 84", baseCoordinateSystem.Name); + Assert.Equal("Local latitude", parsed.GetAxis(0).Name); + Assert.Equal(AxisOrientationEnum.North, parsed.GetAxis(0).Orientation); + Assert.Equal("Local longitude", parsed.GetAxis(1).Name); + Assert.Equal(AxisOrientationEnum.East, parsed.GetAxis(1).Orientation); + Assert.StartsWith("PARAM_MT[\"Affine\"", parsed.ToBase(), StringComparison.Ordinal); + } + + /// + /// Verifies affine derived projected WKT2 definitions parse onto and retain the base projected CRS. + /// + [Fact] + public void CreateFromWkt_WithDerivedProjectedCrs_ParsesFittedCoordinateSystemAndRetainsBaseProjectedCrs() + { + const string wkt = """DERIVEDPROJCRS["Local projected",BASEPROJCRS["WGS 84 / UTM zone 32N",BASEGEOGCRS["WGS 84",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ID["EPSG",6326]],ID["EPSG",4326]],CONVERSION["UTM zone 32N",METHOD["Transverse Mercator"],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433]],PARAMETER["Longitude of natural origin",9,ANGLEUNIT["degree",0.0174532925199433]],PARAMETER["Scale factor at natural origin",0.9996,SCALEUNIT["unity",1]],PARAMETER["False easting",500000,LENGTHUNIT["metre",1]],PARAMETER["False northing",0,LENGTHUNIT["metre",1]]],CS[Cartesian,2],AXIS["Easting",east,ORDER[1]],AXIS["Northing",north,ORDER[2]],LENGTHUNIT["metre",1],ID["EPSG",32632]],DERIVINGCONVERSION["unnamed",METHOD["Affine parametric transformation"],PARAMETER["A0",100,LENGTHUNIT["metre",1]],PARAMETER["A1",1,SCALEUNIT["unity",1]],PARAMETER["A2",0,SCALEUNIT["unity",1]],PARAMETER["B0",-50,LENGTHUNIT["metre",1]],PARAMETER["B1",0,SCALEUNIT["unity",1]],PARAMETER["B2",1,SCALEUNIT["unity",1]]],CS[Cartesian,2],AXIS["Local easting",east,ORDER[1]],AXIS["Local northing",north,ORDER[2]],LENGTHUNIT["metre",1],ID["TEST",3001]]"""; + + FittedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ProjectedCoordinateSystem baseCoordinateSystem = Assert.IsType(parsed.BaseCoordinateSystem); + + Assert.Equal("Local projected", parsed.Name); + Assert.Equal("TEST", parsed.Authority); + Assert.Equal(3001, parsed.AuthorityCode); + Assert.Equal("WGS 84 / UTM zone 32N", baseCoordinateSystem.Name); + Assert.Equal("Local easting", parsed.GetAxis(0).Name); + Assert.Equal("Local northing", parsed.GetAxis(1).Name); + Assert.StartsWith("PARAM_MT[\"Affine\"", parsed.ToBase(), StringComparison.Ordinal); + } + + /// + /// Verifies legacy hybrid GEODCRS definitions without CS[...] still fall back to the older normalization path. + /// + [Fact] + public void CreateFromWkt_FallsBackForLegacyHybridGeodCrsWithoutCoordinateSystemBlock() + { + const string wkt = """GEODCRS["WGS 84",DATUM["WGS_1984",ELLIPSOID["WGS 84",6378137,298.257223563,ID["EPSG","7030"]],ID["EPSG","6326"]],PRIMEM["Greenwich",0,ID["EPSG","8901"]],UNIT["degree",0.0174532925199433,ID["EPSG","9122"]],ID["EPSG","4326"]]"""; + + GeographicCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(4326, parsed.AuthorityCode); + Assert.Equal("EPSG", parsed.HorizontalDatum.Authority); + Assert.Equal(6326, parsed.HorizontalDatum.AuthorityCode); + Assert.Equal("EPSG", parsed.HorizontalDatum.Ellipsoid.Authority); + Assert.Equal(7030, parsed.HorizontalDatum.Ellipsoid.AuthorityCode); + Assert.Equal("EPSG", parsed.PrimeMeridian.Authority); + Assert.Equal(8901, parsed.PrimeMeridian.AuthorityCode); + Assert.Equal("EPSG", parsed.AngularUnit.Authority); + Assert.Equal(9122, parsed.AngularUnit.AuthorityCode); + } + + /// + /// Verifies explicit prime-meridian units and non-degree angular units survive native WKT2 parsing. + /// + [Fact] + public void CreateFromWkt_ParsesPrimeMeridianWithExplicitAngularUnit() + { + string wkt = GetArchiveWkt(4807); + + GeographicCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + Assert.Equal("NTF (Paris)", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(4807, parsed.AuthorityCode); + Assert.Equal("grad", parsed.AngularUnit.Name); + Assert.Equal("EPSG", parsed.AngularUnit.Authority); + Assert.Equal(9105, parsed.AngularUnit.AuthorityCode); + Assert.Equal(0.015707963267949d, parsed.AngularUnit.RadiansPerUnit, 15); + Assert.Equal("Paris", parsed.PrimeMeridian.Name); + Assert.Equal("EPSG", parsed.PrimeMeridian.Authority); + Assert.Equal(8903, parsed.PrimeMeridian.AuthorityCode); + Assert.True(parsed.PrimeMeridian.AngularUnit.EqualParams(AngularUnit.Radian)); + Assert.Equal(0.040792344d, parsed.PrimeMeridian.Longitude); + Assert.Equal(AxisOrientationEnum.North, parsed.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.East, parsed.GetAxis(1).Orientation); + } + + /// + /// Verifies projected WKT2 parsing preserves base-CRS angular units and explicit prime-meridian units. + /// + [Fact] + public void CreateFromWkt_ParsesProjectedCrsBasePrimeMeridianAndAngularUnit() + { + string wkt = GetArchiveWkt(27561); + + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + Assert.Equal("NTF (Paris) / Lambert Nord France", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(27561, parsed.AuthorityCode); + Assert.Equal("grad", parsed.GeographicCoordinateSystem.AngularUnit.Name); + Assert.Equal("EPSG", parsed.GeographicCoordinateSystem.AngularUnit.Authority); + Assert.Equal(9105, parsed.GeographicCoordinateSystem.AngularUnit.AuthorityCode); + Assert.Equal("Paris", parsed.GeographicCoordinateSystem.PrimeMeridian.Name); + Assert.True(parsed.GeographicCoordinateSystem.PrimeMeridian.AngularUnit.EqualParams(AngularUnit.Radian)); + Assert.Equal(0.040792344d, parsed.GeographicCoordinateSystem.PrimeMeridian.Longitude); + Assert.Equal("Lambert Conic Conformal (1SP)", parsed.Projection.ClassName); + Assert.Equal("EPSG", parsed.Projection.Authority); + Assert.Equal(18091, parsed.Projection.AuthorityCode); + Assert.Equal(55d, parsed.Projection.GetParameter("latitude_of_origin")?.Value); + Assert.Equal(0d, parsed.Projection.GetParameter("central_meridian")?.Value); + Assert.Equal(0.999877341d, parsed.Projection.GetParameter("scale_factor")?.Value); + Assert.Equal(600000d, parsed.Projection.GetParameter("false_easting")?.Value); + Assert.Equal(200000d, parsed.Projection.GetParameter("false_northing")?.Value); + } + + /// + /// Verifies ensemble-backed WKT2 geographic CRS now retain ensemble metadata on the parsed datum. + /// + [Fact] + public void CreateFromWkt_WithDatumEnsemble_ParsesGeographicCrsAndRetainsEnsembleMetadata() + { + string wkt = GetArchiveWkt(4979); + + CompoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + GeographicCoordinateSystem horizontal = Assert.IsType(parsed.HeadCoordinateSystem); + DatumEnsemble ensemble = Assert.IsType(horizontal.HorizontalDatum.Ensemble); + + Assert.Equal("World Geodetic System 1984 ensemble", horizontal.HorizontalDatum.Name); + Assert.Equal("EPSG", horizontal.HorizontalDatum.Authority); + Assert.Equal(6326, horizontal.HorizontalDatum.AuthorityCode); + Assert.Equal(8, ensemble.Members.Count); + Assert.Equal(2d, ensemble.Accuracy); + Assert.NotNull(ensemble.Ellipsoid); + Assert.Equal("WGS 84", Assert.IsType(ensemble.Ellipsoid).Name); + } + + /// + /// Verifies ensemble-backed projected WKT2 CRS now retain ensemble metadata on the base datum. + /// + [Fact] + public void CreateFromWkt_WithEnsembleBasedProjCrs_ParsesProjectedCoordinateSystemAndRetainsBaseEnsembleMetadata() + { + string wkt = GetArchiveWkt(32632); + + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + DatumEnsemble ensemble = Assert.IsType(parsed.GeographicCoordinateSystem.HorizontalDatum.Ensemble); + + Assert.Equal("World Geodetic System 1984 ensemble", parsed.GeographicCoordinateSystem.HorizontalDatum.Name); + Assert.Equal("EPSG", parsed.GeographicCoordinateSystem.HorizontalDatum.Authority); + Assert.Equal(6326, parsed.GeographicCoordinateSystem.HorizontalDatum.AuthorityCode); + Assert.Equal(8, ensemble.Members.Count); + Assert.Equal(2d, ensemble.Accuracy); + Assert.NotNull(ensemble.Ellipsoid); + } + + /// + /// Verifies vertical ensemble-backed WKT2 CRS retain ensemble metadata on the parsed datum. + /// + [Fact] + public void CreateFromWkt_WithVerticalDatumEnsemble_ParsesVerticalCoordinateSystemAndRetainsEnsembleMetadata() + { + const string wkt = """VERTCRS["Example ensemble height",ENSEMBLE["Example vertical ensemble",MEMBER["Datum A",ID["TEST",1]],MEMBER["Datum B",ID["TEST",2]],ENSEMBLEACCURACY[0.05],ID["TEST",1001]],CS[vertical,1],AXIS["Gravity-related height (H)",up],LENGTHUNIT["metre",1],ID["TEST",2001]]"""; + + VerticalCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + DatumEnsemble ensemble = Assert.IsType(parsed.VerticalDatum.Ensemble); + + Assert.Equal("Example vertical ensemble", parsed.VerticalDatum.Name); + Assert.Equal("TEST", parsed.VerticalDatum.Authority); + Assert.Equal(1001, parsed.VerticalDatum.AuthorityCode); + Assert.Equal(2, ensemble.Members.Count); + Assert.Equal(0.05d, ensemble.Accuracy); + Assert.Null(ensemble.Ellipsoid); + } + + /// + /// Verifies supported engineering CRS examples parse and roundtrip through the engineering model. + /// + /// The engineering CRS WKT2 input. + [Theory] + [MemberData(nameof(SupportedEngineeringWkt2Rows))] + public void CreateFromWkt_WithSupportedEngineeringCrs_ParsesEngineeringCoordinateSystem(string wkt) + { + EngineeringCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + EngineeringCoordinateSystem roundTripped = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + parsed.ToWktNode(WktVersion.Wkt22019).ToString()); + + Assert.Equal("Engineering example", parsed.Name); + Assert.Equal("Local engineering datum", parsed.EngineeringDatum.Name); + Assert.Equal("Cartesian", parsed.CoordinateSystemType); + Assert.True(parsed.EqualParams(roundTripped)); + } + + /// + /// Verifies supported temporal CRS examples parse and roundtrip through the temporal model. + /// + /// The temporal CRS WKT2 input. + [Theory] + [MemberData(nameof(SupportedTemporalWkt2Rows))] + public void CreateFromWkt_WithSupportedTemporalCrs_ParsesTemporalCoordinateSystem(string wkt) + { + TemporalCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + TemporalCoordinateSystem roundTripped = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + parsed.ToWktNode(WktVersion.Wkt22019).ToString()); + + Assert.Equal("Temporal example", parsed.Name); + Assert.Equal("Unix epoch", parsed.TemporalDatum.Name); + Assert.Equal("1970-01-01T00:00:00Z", parsed.TemporalDatum.TimeOrigin); + Assert.True(parsed.EqualParams(roundTripped)); + } + + /// + /// Verifies supported parametric CRS examples parse and roundtrip through the parametric model. + /// + /// The parametric CRS WKT2 input. + [Theory] + [MemberData(nameof(SupportedParametricWkt2Rows))] + public void CreateFromWkt_WithSupportedParametricCrs_ParsesParametricCoordinateSystem(string wkt) + { + ParametricCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ParametricCoordinateSystem roundTripped = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + parsed.ToWktNode(WktVersion.Wkt22019).ToString()); + + Assert.Equal("Reservoir pressure", parsed.Name); + Assert.Equal("Reservoir datum", parsed.ParametricDatum.Name); + Assert.True(parsed.EqualParams(roundTripped)); + } + + /// + /// Verifies supported coordinate operation examples parse and roundtrip through the operation model. + /// + /// The coordinate operation WKT2 input. + [Theory] + [MemberData(nameof(SupportedCoordinateOperationWkt2Rows))] + public void CreateFromWkt_WithSupportedCoordinateOperation_ParsesCoordinateOperation(string wkt) + { + IInfo parsedInfo = CoordinateSystemWktReader.Parse(wkt); + CoordinateOperation parsed = Assert.IsType(parsedInfo); + CoordinateOperation roundTripped = Assert.IsType(CoordinateSystemWktReader.Parse(parsed.WKT)); + + Assert.Equal("Axis swap", parsed.Name); + Assert.Equal("Axis Order Reversal", parsed.MethodName); + Assert.Single(parsed.Parameters); + Assert.True(parsed.EqualParams(roundTripped)); + } + + /// + /// Verifies supported concatenated operation examples parse and roundtrip through the operation-chain model. + /// + /// The concatenated operation WKT2 input. + [Theory] + [MemberData(nameof(SupportedConcatenatedOperationWkt2Rows))] + public void CreateFromWkt_WithSupportedConcatenatedOperation_ParsesConcatenatedOperation(string wkt) + { + IInfo parsedInfo = CoordinateSystemWktReader.Parse(wkt); + ConcatenatedOperation parsed = Assert.IsType(parsedInfo); + ConcatenatedOperation roundTripped = Assert.IsType(CoordinateSystemWktReader.Parse(parsed.WKT)); + + Assert.Equal("Two-step chain", parsed.Name); + Assert.Equal(2, parsed.Steps.Count); + Assert.Equal("Step 1", parsed.Steps[0].Name); + Assert.True(parsed.EqualParams(roundTripped)); + } + + /// + /// Verifies the new CRS readers reject missing datum blocks explicitly. + /// + /// The malformed WKT2 input. + /// The expected diagnostic fragment. + [Theory] + [InlineData("""ENGCRS["Broken engineering",CS[Cartesian,2],AXIS["x",east],AXIS["y",north],LENGTHUNIT["metre",1]]""", "EDATUM")] + [InlineData("""TIMECRS["Broken temporal",CS[temporal,1],AXIS["time",north],TIMEUNIT["second",1]]""", "TDATUM")] + [InlineData("""PARAMETRICCRS["Broken parametric",CS[parametric,1],AXIS["pressure",up],PARAMETRICUNIT["bar",100000]]""", "PDATUM")] + public void CreateFromWkt_WithMissingDatumBlock_ThrowsArgumentException(string wkt, string expectedMessageFragment) + { + Exception exception = Assert.ThrowsAny(() => CoordinateSystemFactory.CreateFromWkt(wkt)); + + Assert.Contains(expectedMessageFragment, exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies temporal and parametric CRS reject invalid coordinate-system types. + /// + /// The malformed WKT2 input. + /// The expected diagnostic fragment. + [Theory] + [InlineData("""TIMECRS["Temporal example",TDATUM["Unix epoch",TIMEORIGIN["1970-01-01T00:00:00Z"]],CS[Cartesian,1],AXIS["time",north],TIMEUNIT["second",1]]""", "coordinate system type")] + [InlineData("""PARAMETRICCRS["Reservoir pressure",PDATUM["Reservoir datum"],CS[Cartesian,1],AXIS["pressure",up],PARAMETRICUNIT["bar",100000]]""", "coordinate system type")] + public void CreateFromWkt_WithInvalidTemporalOrParametricCsType_ThrowsNotSupportedException(string wkt, string expectedMessageFragment) + { + NotSupportedException exception = Assert.Throws(() => CoordinateSystemFactory.CreateFromWkt(wkt)); + + Assert.Contains(expectedMessageFragment, exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies higher-dimensional engineering CRS definitions are retained. + /// + [Fact] + public void CreateFromWkt_WithThreeDimensionalEngineeringCrs_ParsesEngineeringCoordinateSystem() + { + const string wkt = """ENGCRS["Engineering 3D",EDATUM["Local engineering datum"],CS[Cartesian,3],AXIS["x",east],AXIS["y",north],AXIS["z",up],LENGTHUNIT["metre",1]]"""; + + EngineeringCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + Assert.Equal(3, parsed.Dimension); + Assert.True(parsed.GetUnits(2).EqualParams(LinearUnit.Metre)); + Assert.Equal(AxisOrientationEnum.Up, parsed.GetAxis(2).Orientation); + } + + /// + /// Verifies concatenated operations reject empty step wrappers. + /// + [Fact] + public void Parse_WithEmptyStepBlock_ThrowsNotSupportedException() + { + const string wkt = """CONCATENATEDOPERATION["Broken chain",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],STEP[]]"""; + + NotSupportedException exception = Assert.Throws(() => CoordinateSystemWktReader.Parse(wkt)); + + Assert.Contains("STEP", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies coordinate operations and concatenated operations reject missing endpoint blocks explicitly. + /// + /// The malformed WKT2 input. + /// The expected diagnostic fragment. + [Theory] + [InlineData("""COORDINATEOPERATION["Broken op",TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],METHOD["Axis swap"]]""", "SOURCECRS")] + [InlineData("""CONCATENATEDOPERATION["Broken chain",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],STEP[COORDINATEOPERATION["Step 1",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],METHOD["Axis swap"]]]]""", "TARGETCRS")] + public void Parse_WithMissingSourceOrTargetBlock_ThrowsArgumentException(string wkt, string expectedMessageFragment) + { + Exception exception = Assert.ThrowsAny(() => CoordinateSystemWktReader.Parse(wkt)); + + Assert.Contains(expectedMessageFragment, exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies concatenated operations with a single step remain valid. + /// + [Fact] + public void Parse_WithSingleStepConcatenatedOperation_ReturnsConcatenatedOperation() + { + const string wkt = """CONCATENATEDOPERATION["Single-step chain",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],STEP[COORDINATEOPERATION["Step 1",SOURCECRS[GEOGCRS["Source CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Latitude",north],AXIS["Longitude",east],ANGLEUNIT["degree",0.0174532925199433]]],TARGETCRS[GEOGCRS["Target CRS",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],CS[ellipsoidal,2],AXIS["Longitude",east],AXIS["Latitude",north],ANGLEUNIT["degree",0.0174532925199433]]],METHOD["Axis swap"]]]]"""; + + ConcatenatedOperation parsed = Assert.IsType(CoordinateSystemWktReader.Parse(wkt)); + + Assert.Single(parsed.Steps); + Assert.Equal("Step 1", parsed.Steps[0].Name); + } + + /// + /// Verifies representative unsupported WKT2 root keywords remain rejected instead of silently normalizing to an unrelated WKT1 path. + /// + /// The unsupported top-level WKT2 keyword. + /// The representative WKT2 input. + /// The keyword fragment currently surfaced by the reader path. + [Theory] + [MemberData(nameof(UnsupportedTopLevelWkt2Rows))] + public void CreateFromWkt_WithUnsupportedTopLevelWkt2Keyword_ThrowsWktParseException(string keyword, string wkt, string expectedMessageFragment) + { + WktParseException exception = Assert.Throws(() => CoordinateSystemFactory.CreateFromWkt(wkt)); + + Assert.True( + exception.Message.Contains(expectedMessageFragment, StringComparison.OrdinalIgnoreCase), + $"Expected the current reader boundary for {keyword} to surface '{expectedMessageFragment}', but got '{exception.Message}'."); + } + + /// + /// Verifies projected BOUNDCRS examples now retain first-class bound metadata. + /// + /// Source file and line for the extracted fixture. + /// BOUNDCRS WKT2 example from the PROJ test corpus. + [Theory] + [MemberData(nameof(SupportedProjBoundCrsRows))] + public void CreateFromWkt_WithSupportedProjBoundCrsFixture_ParsesBoundCoordinateSystem(string fixtureSource, string wkt) + { + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + ProjectedCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + GeographicCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + Wgs84ConversionInfo parameters = Assert.IsType(parsed.Transformation.Wgs84Parameters); + + Assert.Equal("NAD83 / California zone 3 (ftUS)", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(2227, parsed.AuthorityCode); + Assert.Equal("NAD83", source.GeographicCoordinateSystem.Name); + Assert.Equal("North American Datum 1983", source.GeographicCoordinateSystem.HorizontalDatum.Name); + Assert.Null(source.GeographicCoordinateSystem.HorizontalDatum.Wgs84Parameters); + Assert.Equal(new Wgs84ConversionInfo(0, 0, 0, 0, 0, 0, 0), parameters); + Assert.Equal("Lambert Conic Conformal (2SP)", source.Projection.ClassName); + Assert.Equal(6561666.667d, source.Projection.GetParameter("false_easting")?.Value); + Assert.Equal(1640416.667d, source.Projection.GetParameter("false_northing")?.Value); + Assert.Equal("WGS 84", target.Name); + Assert.True(target.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(target.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)); + Assert.True(target.AngularUnit.EqualParams(AngularUnit.Degrees)); + Assert.Equal(AxisOrientationEnum.North, target.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.East, target.GetAxis(1).Orientation); + Assert.True(parsed.Transformation.UsesWgs84Parameters); + Assert.False(string.IsNullOrWhiteSpace(fixtureSource)); + } + + /// + /// Verifies horizontal BOUNDCRS examples with an ellipsoidal 3D source retain a bound wrapper instead of flattening onto the source CRS. + /// + [Fact] + public void CreateFromWkt_WithEllipsoidal3dSourceBoundCrs_ParsesBoundCoordinateSystem() + { + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, EllipsoidalHeightBoundCrs); + CompoundCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + GeographicCoordinateSystem horizontal = Assert.IsType(source.HeadCoordinateSystem); + VerticalCoordinateSystem vertical = Assert.IsType(source.TailCoordinateSystem); + Wgs84ConversionInfo parameters = Assert.IsType(parsed.Transformation.Wgs84Parameters); + + Assert.Equal("TWD97", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(3823, parsed.AuthorityCode); + Assert.Equal(AxisOrientationEnum.North, source.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.East, source.GetAxis(1).Orientation); + Assert.Equal(AxisOrientationEnum.Up, source.GetAxis(2).Orientation); + Assert.Equal("Taiwan Datum 1997", horizontal.HorizontalDatum.Name); + Assert.True(horizontal.HorizontalDatum.Ellipsoid.EqualParams(Ellipsoid.GRS80)); + Assert.Null(horizontal.HorizontalDatum.Wgs84Parameters); + Assert.Equal(new Wgs84ConversionInfo(0, 0, 0, 0, 0, 0, 0), parameters); + Assert.Equal(DatumType.VD_Ellipsoidal, vertical.VerticalDatum.DatumType); + Assert.Equal("ellipsoidal height (h)", vertical.Name); + Assert.True(parsed.Transformation.UsesWgs84Parameters); + } + + /// + /// Verifies vertical BOUNDCRS examples from the checked-in PROJ tests parse as first-class bound coordinate systems. + /// + /// Source file and line for the extracted fixture. + /// BOUNDCRS WKT2 example from the PROJ test corpus. + [Theory] + [MemberData(nameof(SupportedVerticalBoundCrsRows))] + public void CreateFromWkt_WithSupportedVerticalBoundCrsFixture_ParsesBoundCoordinateSystem(string fixtureSource, string wkt) + { + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + VerticalCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + CompoundCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + Assert.Equal("EGM96 height", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(5773, parsed.AuthorityCode); + Assert.Equal("EGM96 geoid", source.VerticalDatum.Name); + Assert.Equal(DatumType.VD_GeoidModelDerived, source.VerticalDatum.DatumType); + Assert.Equal("metre", source.LinearUnit.Name); + Assert.Null(source.BoundGridTransformation); + Assert.True(parsed.Transformation.UsesParameterFile); + Assert.Equal("us_nga_egm96_15.tif", parsed.Transformation.ParameterFileName); + Assert.Equal(3, target.Dimension); + Assert.False(string.IsNullOrWhiteSpace(fixtureSource)); + } + + /// + /// Verifies equivalent PARAMETERFILE paths on repeated vertical BOUNDCRS wrappers do not report a false conflict. + /// + [Fact] + public void CreateFromWkt_WithEquivalentVerticalBoundParameterFilePaths_DoesNotReportConflict() + { + string nested = CreateNestedVerticalBoundCrs(@"grids\us_nga_egm96_15.tif", "GRIDS/us_nga_egm96_15.tif"); + + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, nested); + BoundCoordinateSystem inner = Assert.IsType(parsed.SourceCoordinateSystem); + + Assert.Equal("EGM96 height", parsed.Name); + Assert.Equal(@"grids\us_nga_egm96_15.tif", inner.Transformation.ParameterFileName); + Assert.Equal("GRIDS/us_nga_egm96_15.tif", parsed.Transformation.ParameterFileName); + } + + /// + /// Verifies that every managed EPSG catalog coordinate system can roundtrip through WKT2 serialization and parsing + /// without losing its semantic model. + /// + [Fact] + public void BulkCatalogRoundTrip_AllEpsgCrs_ShouldParseWkt2AndMatchOriginal() + { + var failures = new List(); + int successfulRoundTrips = 0; + + foreach (CoordinateSystemEntry entry in CatalogEntries.Value) + { + CoordinateSystem original = entry.CoordinateSystem; + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + CoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + if (!AreCoordinateSystemsSemanticallyEquivalent(original, parsed)) + { + failures.Add(FormattableString.Invariant($"EPSG:{entry.Srid} ({original.GetType().Name}) failed WKT2 catalog roundtrip.")); + continue; + } + + successfulRoundTrips++; + } + + Assert.True( + failures.Count == 0, + $"Expected all {CatalogEntries.Value.Count} catalog CRS definitions to roundtrip through WKT2. Failures ({failures.Count}): {string.Join("; ", failures)}"); + Assert.Equal(CatalogEntries.Value.Count, successfulRoundTrips); + } + + /// + /// Verifies structural WKT2 parse failures are treated as a native-reader miss instead of leaking out of the fallback probe. + /// + [Fact] + public void TryParseNativeWkt2_WithStructuralParseFailure_ReturnsFalse() + { + const string malformedWkt = "GEODCRS[\"WGS 84\""; + + bool parsed = TryInvokeNativeWkt2(malformedWkt, out IInfo? info); + + Assert.False(parsed); + Assert.Null(info); + } + + /// + /// Verifies non-parse exceptions are not swallowed by the native-reader probe. + /// + [Fact] + public void TryParseNativeWkt2_WithNullInput_PropagatesArgumentNullException() + { + MethodInfo method = GetTryParseNativeWkt2Method(); + object?[] arguments = [null, null]; + + TargetInvocationException exception = Assert.Throws(() => method.Invoke(null, arguments)); + + Assert.IsType(exception.InnerException); + } + + private static string GetCatalogWkt(int srid) + { + Assert.True(CatalogDefinitions.Value.TryGetValue(srid, out string? wkt), $"SRID {srid} not found in managed EPSG catalog."); + return wkt ?? string.Empty; + } + + private static string GetArchiveWkt(int srid) + => EpsgArchiveWktFixtureSource.GetFixture(srid).Wkt; + + private static string CreateNestedVerticalBoundCrs(string innerParameterFileName, string outerParameterFileName) + { + string inner = VerticalProjBoundCrs.Replace("us_nga_egm96_15.tif", innerParameterFileName, StringComparison.Ordinal); + return $$""" + BOUNDCRS[ + SOURCECRS[ + {{inner}}], + TARGETCRS[ + GEOGCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]], + CS[ellipsoidal,3], + AXIS["latitude",north, + ORDER[1], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["longitude",east, + ORDER[2], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["ellipsoidal height",up, + ORDER[3], + LENGTHUNIT["metre",1]], + ID["EPSG",4979]]], + ABRIDGEDTRANSFORMATION["WGS 84 to EGM96 height (1)", + METHOD["Geographic3D to GravityRelatedHeight (EGM)", + ID["EPSG",9661]], + PARAMETERFILE["Geoid (height correction) model file","{{outerParameterFileName}}"]]] + """; + } + + private static bool TryInvokeNativeWkt2(string wkt, out IInfo? info) + { + MethodInfo method = GetTryParseNativeWkt2Method(); + object?[] arguments = [wkt, null]; + + bool result = Assert.IsType(method.Invoke(null, arguments)); + info = (IInfo?)arguments[1]; + return result; + } + + private static MethodInfo GetTryParseNativeWkt2Method() + { + MethodInfo? method = typeof(CoordinateSystemWktReader).GetMethod( + "TryParseNativeWkt2", + BindingFlags.Static | BindingFlags.NonPublic, + binder: null, + types: [typeof(string), typeof(IInfo).MakeByRefType()], + modifiers: null); + + Assert.NotNull(method); + return method; + } + + private static bool AreCoordinateSystemsSemanticallyEquivalent(CoordinateSystem original, CoordinateSystem parsed) + { + if (original.GetType() != parsed.GetType()) + { + return false; + } + + return original switch + { + ProjectedCoordinateSystem originalProjected when parsed is ProjectedCoordinateSystem parsedProjected => AreProjectedCoordinateSystemsSemanticallyEquivalent(originalProjected, parsedProjected), + GeographicCoordinateSystem originalGeographic when parsed is GeographicCoordinateSystem parsedGeographic => AreGeographicCoordinateSystemsSemanticallyEquivalent(originalGeographic, parsedGeographic), + CompoundCoordinateSystem originalCompound when parsed is CompoundCoordinateSystem parsedCompound => AreCompoundCoordinateSystemsSemanticallyEquivalent(originalCompound, parsedCompound), + _ => original.EqualParams(parsed), + }; + } + + private static bool AreProjectedCoordinateSystemsSemanticallyEquivalent(ProjectedCoordinateSystem original, ProjectedCoordinateSystem parsed) + { + return AreAxesSemanticallyEquivalent(original, parsed) + && original.LinearUnit.EqualParams(parsed.LinearUnit) + && AreCoordinateSystemsSemanticallyEquivalent(original.GeographicCoordinateSystem, parsed.GeographicCoordinateSystem) + && AreProjectionsSemanticallyEquivalent(original.Projection, parsed.Projection); + } + + private static bool AreGeographicCoordinateSystemsSemanticallyEquivalent(GeographicCoordinateSystem original, GeographicCoordinateSystem parsed) + { + return AreAxesSemanticallyEquivalent(original, parsed) + && original.AngularUnit.EqualParams(parsed.AngularUnit) + && original.PrimeMeridian.EqualParams(parsed.PrimeMeridian) + && AreHorizontalDatumsSemanticallyEquivalent(original.HorizontalDatum, parsed.HorizontalDatum); + } + + private static bool AreCompoundCoordinateSystemsSemanticallyEquivalent(CompoundCoordinateSystem original, CompoundCoordinateSystem parsed) + { + return AreAxesSemanticallyEquivalent(original, parsed) + && AreCoordinateSystemsSemanticallyEquivalent(original.HeadCoordinateSystem, parsed.HeadCoordinateSystem) + && AreCoordinateSystemsSemanticallyEquivalent(original.TailCoordinateSystem, parsed.TailCoordinateSystem); + } + + private static bool AreAxesSemanticallyEquivalent(CoordinateSystem original, CoordinateSystem parsed) + { + if (original.Dimension != parsed.Dimension) + { + return false; + } + + for (int i = 0; i < original.Dimension; i++) + { + AxisInfo originalAxis = original.GetAxis(i); + AxisInfo parsedAxis = parsed.GetAxis(i); + if (!string.Equals(originalAxis.Name, parsedAxis.Name, StringComparison.Ordinal) + || originalAxis.Orientation != parsedAxis.Orientation + || !original.GetUnits(i).EqualParams(parsed.GetUnits(i))) + { + return false; + } + } + + return true; + } + + private static bool AreHorizontalDatumsSemanticallyEquivalent(HorizontalDatum original, HorizontalDatum parsed) + { + if (original.EqualParams(parsed)) + { + return true; + } + + if (!string.Equals(original.Name, parsed.Name, StringComparison.Ordinal) + || (original.Wgs84Parameters is null) != (parsed.Wgs84Parameters is null) + || (original.Ensemble is null) != (parsed.Ensemble is null) + || !AreEllipsoidsSemanticallyEquivalent(original.Ellipsoid, parsed.Ellipsoid)) + { + return false; + } + + if (original.Wgs84Parameters is not null + && parsed.Wgs84Parameters is not null + && !AreWgs84ConversionInfosEquivalent(original.Wgs84Parameters, parsed.Wgs84Parameters)) + { + return false; + } + + if (original.Ensemble is not null + && parsed.Ensemble is not null + && !AreDatumEnsemblesSemanticallyEquivalent(original.Ensemble, parsed.Ensemble)) + { + return false; + } + + return true; + } + + private static bool AreDatumEnsemblesSemanticallyEquivalent(DatumEnsemble original, DatumEnsemble parsed) + { + if (!string.Equals(original.Name, parsed.Name, StringComparison.Ordinal) + || !string.Equals(original.Authority, parsed.Authority, StringComparison.Ordinal) + || original.AuthorityCode != parsed.AuthorityCode + || original.Accuracy != parsed.Accuracy + || (original.Ellipsoid is null) != (parsed.Ellipsoid is null) + || original.Members.Count != parsed.Members.Count) + { + return false; + } + + if (original.Ellipsoid is not null + && parsed.Ellipsoid is not null + && !AreEllipsoidsSemanticallyEquivalent(original.Ellipsoid, parsed.Ellipsoid)) + { + return false; + } + + for (int i = 0; i < original.Members.Count; i++) + { + if (!original.Members[i].Equals(parsed.Members[i])) + { + return false; + } + } + + return true; + } + + private static bool AreEllipsoidsSemanticallyEquivalent(Ellipsoid original, Ellipsoid parsed) + { + return original.SemiMajorAxis.Equals(parsed.SemiMajorAxis) + && original.SemiMinorAxis.Equals(parsed.SemiMinorAxis) + && original.AxisUnit.EqualParams(parsed.AxisUnit); + } + + private static bool AreWgs84ConversionInfosEquivalent(Wgs84ConversionInfo original, Wgs84ConversionInfo parsed) + { + return original.Dx.Equals(parsed.Dx) + && original.Dy.Equals(parsed.Dy) + && original.Dz.Equals(parsed.Dz) + && original.Ex.Equals(parsed.Ex) + && original.Ey.Equals(parsed.Ey) + && original.Ez.Equals(parsed.Ez) + && original.Ppm.Equals(parsed.Ppm); + } + + private static bool AreProjectionsSemanticallyEquivalent(IProjection original, IProjection parsed) + { + if (!string.Equals(original.ClassName, parsed.ClassName, StringComparison.Ordinal) + || original.NumParameters != parsed.NumParameters) + { + return false; + } + + string[] originalParameters = GetCanonicalProjectionParameters(original); + string[] parsedParameters = GetCanonicalProjectionParameters(parsed); + + Array.Sort(originalParameters, StringComparer.Ordinal); + Array.Sort(parsedParameters, StringComparer.Ordinal); + + if (originalParameters.Length != parsedParameters.Length) + { + return false; + } + + for (int i = 0; i < originalParameters.Length; i++) + { + if (!string.Equals(originalParameters[i], parsedParameters[i], StringComparison.Ordinal)) + { + return false; + } + } + + return true; + } + + private static string[] GetCanonicalProjectionParameters(IProjection projection) + { + string[] parameters = new string[projection.NumParameters]; + for (int i = 0; i < projection.NumParameters; i++) + { + ProjectionParameter parameter = projection.GetParameter(i); + string canonicalName = NormalizeProjectionParameterName(parameter.Name); + string canonicalValue = parameter.Value.ToString("R", CultureInfo.InvariantCulture); + parameters[i] = $"{canonicalName}={canonicalValue}"; + } + + return parameters; + } + + private static string NormalizeProjectionParameterName(string parameterName) + { + if (string.IsNullOrWhiteSpace(parameterName)) + { + return string.Empty; + } + + string normalized = parameterName + .ToUpperInvariant() + .Trim() + .Replace("(", string.Empty, StringComparison.Ordinal) + .Replace(")", string.Empty, StringComparison.Ordinal) + .Replace("-", "_", StringComparison.Ordinal) + .Replace("/", "_", StringComparison.Ordinal) + .Replace(" ", "_", StringComparison.Ordinal) + .Replace(".", "_", StringComparison.Ordinal); + + while (normalized.Contains("__", StringComparison.Ordinal)) + { + normalized = normalized.Replace("__", "_", StringComparison.Ordinal); + } + + return normalized switch + { + "LONGITUDE_OF_NATURAL_ORIGIN" => "CENTRAL_MERIDIAN", + "LONGITUDE_OF_FALSE_ORIGIN" => "CENTRAL_MERIDIAN", + "LONGITUDE_OF_PROJECTION_CENTRE" => "CENTRAL_MERIDIAN", + "LONGITUDE_OF_ORIGIN" => "CENTRAL_MERIDIAN", + "LATITUDE_OF_NATURAL_ORIGIN" => "LATITUDE_OF_ORIGIN", + "LATITUDE_OF_FALSE_ORIGIN" => "LATITUDE_OF_ORIGIN", + "LATITUDE_OF_PROJECTION_CENTRE" => "LATITUDE_OF_ORIGIN", + "LATITUDE_OF_ORIGIN" => "LATITUDE_OF_ORIGIN", + "LATITUDE_OF_1ST_STANDARD_PARALLEL" => "STANDARD_PARALLEL_1", + "LATITUDE_OF_2ND_STANDARD_PARALLEL" => "STANDARD_PARALLEL_2", + "LATITUDE_OF_PSEUDO_STANDARD_PARALLEL" => "STANDARD_PARALLEL_1", + "LATITUDE_OF_STANDARD_PARALLEL" => "LATITUDE_OF_STANDARD_PARALLEL", + "EASTING_AT_FALSE_ORIGIN" => "FALSE_EASTING", + "EASTING_AT_PROJECTION_CENTRE" => "FALSE_EASTING", + "EASTING_AT_NATURAL_ORIGIN" => "FALSE_EASTING", + "FALSE_EASTING" => "FALSE_EASTING", + "NORTHING_AT_FALSE_ORIGIN" => "FALSE_NORTHING", + "NORTHING_AT_PROJECTION_CENTRE" => "FALSE_NORTHING", + "NORTHING_AT_NATURAL_ORIGIN" => "FALSE_NORTHING", + "FALSE_NORTHING" => "FALSE_NORTHING", + "SCALE_FACTOR_AT_NATURAL_ORIGIN" => "SCALE_FACTOR", + "SCALE_FACTOR_AT_PROJECTION_CENTRE" => "SCALE_FACTOR", + "SCALE_FACTOR_ON_INITIAL_LINE" => "SCALE_FACTOR", + "SCALE_FACTOR_ON_PSEUDO_STANDARD_PARALLEL" => "SCALE_FACTOR_ON_PSEUDO_STANDARD_PARALLEL", + "AZIMUTH_OF_INITIAL_LINE" => "AZIMUTH", + "AZIMUTH_AT_PROJECTION_CENTRE" => "AZIMUTH", + "ANGLE_FROM_RECTIFIED_TO_SKEW_GRID" => "RECTIFIED_GRID_ANGLE", + "RECTIFIED_GRID_ANGLE" => "RECTIFIED_GRID_ANGLE", + _ => normalized, + }; + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/CoverageReferenceAssert.cs b/test/ProjNet.Tests/IO/CoordinateSystems/CoverageReferenceAssert.cs new file mode 100644 index 00000000..e02c6fd4 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/CoverageReferenceAssert.cs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Text.RegularExpressions; +using Xunit; + +/// +/// Guards that coverage-matrix references stay on stable symbol paths instead of file line ranges. +/// +internal static partial class CoverageReferenceAssert +{ + /// + /// Verifies a comma-separated reference list only contains symbol references. + /// + /// The stored matrix reference list. + internal static void AssertSymbolReferenceList(string referenceList) + { + Assert.False(string.IsNullOrWhiteSpace(referenceList)); + Assert.DoesNotContain(".cs:", referenceList, StringComparison.OrdinalIgnoreCase); + + foreach (string reference in referenceList.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + { + Assert.Matches(SymbolReferencePattern(), reference); + } + } + + [GeneratedRegex(@"^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+$", RegexOptions.CultureInvariant)] + private static partial Regex SymbolReferencePattern(); +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktFixture.cs b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktFixture.cs new file mode 100644 index 00000000..ef0ec657 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktFixture.cs @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +/// +/// Represents one supported coordinate-system fixture sourced from the checked-in EPSG WKT archive. +/// +/// The final EPSG identifier for the coordinate system entry. +/// The archive entry path the WKT was loaded from. +/// The top-level WKT keyword for the coordinate system entry. +/// The WKT payload extracted from the archive entry. +internal sealed record EpsgArchiveWktFixture( + int Srid, + string ArchiveEntryPath, + string RootKeyword, + string Wkt); diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktFixtureSource.cs b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktFixtureSource.cs new file mode 100644 index 00000000..c97cf8e6 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktFixtureSource.cs @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using ProjNet.Data; +using ProjNet.Data.Generated; +using Xunit; + +/// +/// Lazily indexes supported coordinate-system fixtures from the checked-in EPSG WKT archive. +/// +internal static class EpsgArchiveWktFixtureSource +{ + private static readonly Regex EpsgIdRegex = new("""ID\["EPSG",(?\d+)\]""", RegexOptions.Compiled | RegexOptions.CultureInvariant); + private static readonly HashSet CoordinateSystemRoots = + [ + "COMPOUNDCRS", + "ENGINEERINGCRS", + "ENGCRS", + "GEODCRS", + "GEODETICCRS", + "GEOGCRS", + "PROJCRS", + "VERTCRS", + ]; + + private static readonly Lazy Fixtures = new(CreateFixtureState, true); + private static readonly Lazy> SupportedSrids = new( + () => new ManagedCoordinateSystemDefinitionProvider() + .GetCoordinateSystems() + .Select(entry => entry.Srid) + .ToHashSet(), + true); + + /// + /// Gets the resolved path to the checked-in EPSG WKT archive. + /// + internal static string ArchivePath => Fixtures.Value.ArchivePath; + + /// + /// Gets one supported EPSG archive fixture by SRID. + /// + /// The EPSG SRID to resolve. + /// The cached fixture for . + internal static EpsgArchiveWktFixture GetFixture(int srid) + { + FixtureIndex fixtureIndex = RequireFixtureIndex(); + if (!fixtureIndex.FixturesBySrid.TryGetValue(srid, out EpsgArchiveWktFixture? fixture)) + { + throw new InvalidOperationException( + FormattableString.Invariant( + $"Could not locate a supported EPSG:{srid} fixture in {Path.GetFileName(ArchivePath)}.")); + } + + return fixture; + } + + /// + /// Gets all supported EPSG archive fixtures in deterministic SRID order. + /// + /// The cached supported fixtures. + internal static IReadOnlyList GetAllSupportedFixtures() + => RequireFixtureIndex().OrderedFixtures; + + /// + /// Gets xUnit theory rows for the requested EPSG SRIDs, or for all supported fixtures when none are specified. + /// + /// Optional EPSG SRIDs to project into theory rows. + /// The requested SRID/WKT theory rows. + internal static IEnumerable> GetTheoryDataRows(params int[] srids) + { + ArgumentNullException.ThrowIfNull(srids); + + FixtureState fixtureState = Fixtures.Value; + if (fixtureState.Index is null) + { + yield return CreateSkippedTheoryDataRow(fixtureState.SkipReason); + yield break; + } + + FixtureIndex fixtureIndex = Assert.IsType(fixtureState.Index); + + if (srids.Length == 0) + { + foreach (EpsgArchiveWktFixture fixture in fixtureIndex.OrderedFixtures) + { + yield return CreateTheoryDataRow(fixture); + } + + yield break; + } + + foreach (int srid in srids) + { + if (!fixtureIndex.FixturesBySrid.TryGetValue(srid, out EpsgArchiveWktFixture? fixture)) + { + throw new InvalidOperationException( + FormattableString.Invariant( + $"Could not locate a supported EPSG:{srid} fixture in {Path.GetFileName(ArchivePath)}.")); + } + + yield return CreateTheoryDataRow(fixture); + } + } + + private static TheoryDataRow CreateTheoryDataRow(EpsgArchiveWktFixture fixture) + { + ArgumentNullException.ThrowIfNull(fixture); + return new TheoryDataRow(fixture.Srid, fixture.Wkt); + } + + private static FixtureState CreateFixtureState() + { + string archivePath = GetArchivePath(); + if (!File.Exists(archivePath)) + { + return new FixtureState( + archivePath, + null, + FormattableString.Invariant($"EPSG archive fixture '{archivePath}' is unavailable in this checkout.")); + } + + var fixturesBySrid = new Dictionary(); + var duplicates = new List(); + + using ZipArchive archive = ZipFile.OpenRead(archivePath); + foreach (ZipArchiveEntry entry in archive.Entries.OrderBy(item => item.FullName, StringComparer.Ordinal)) + { + if (!entry.FullName.EndsWith(".wkt", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + string wkt = ReadEntryText(entry); + string rootKeyword = GetRootKeyword(wkt); + if (!CoordinateSystemRoots.Contains(rootKeyword)) + { + continue; + } + + int? srid = TryGetFinalEpsgId(wkt); + if (srid is null || !SupportedSrids.Value.Contains(srid.Value)) + { + continue; + } + + var fixture = new EpsgArchiveWktFixture(srid.Value, entry.FullName, rootKeyword, wkt); + if (!fixturesBySrid.TryAdd(fixture.Srid, fixture)) + { + duplicates.Add( + FormattableString.Invariant( + $"{fixture.ArchiveEntryPath} duplicates supported EPSG:{fixture.Srid}.")); + } + } + + EnsureNoDuplicateFixtures(archivePath, duplicates); + EnsureAllSupportedSridsWereMatched(archivePath, fixturesBySrid); + + EpsgArchiveWktFixture[] orderedFixtures = fixturesBySrid.Values + .OrderBy(fixture => fixture.Srid) + .ToArray(); + + return new FixtureState( + archivePath, + new FixtureIndex( + archivePath, + Array.AsReadOnly(orderedFixtures), + fixturesBySrid), + null); + } + + private static TheoryDataRow CreateSkippedTheoryDataRow(string? skipReason) + { + return new TheoryDataRow(0, string.Empty) + { + Skip = skipReason ?? "EPSG archive fixtures are unavailable in this checkout.", + }; + } + + private static FixtureIndex RequireFixtureIndex() + { + FixtureState fixtureState = Fixtures.Value; + if (fixtureState.Index is null) + { + Assert.Skip(fixtureState.SkipReason ?? "EPSG archive fixtures are unavailable in this checkout."); + } + + return Assert.IsType(fixtureState.Index); + } + + private static void EnsureNoDuplicateFixtures(string archivePath, List duplicates) + { + if (duplicates.Count == 0) + { + return; + } + + throw new InvalidOperationException( + $"Found duplicate supported EPSG WKT entries in {Path.GetFileName(archivePath)}:{Environment.NewLine}{string.Join(Environment.NewLine, duplicates)}"); + } + + private static void EnsureAllSupportedSridsWereMatched(string archivePath, Dictionary fixturesBySrid) + { + if (fixturesBySrid.Count == SupportedSrids.Value.Count) + { + return; + } + + int[] missingSrids = SupportedSrids.Value + .Except(fixturesBySrid.Keys) + .OrderBy(srid => srid) + .ToArray(); + + const int maxMissingToPrint = 20; + string printedSrids = string.Join(", ", missingSrids + .Take(maxMissingToPrint) + .Select(srid => srid.ToString(CultureInfo.InvariantCulture))); + string remainder = missingSrids.Length > maxMissingToPrint + ? FormattableString.Invariant($", ... {missingSrids.Length - maxMissingToPrint} more") + : string.Empty; + + throw new InvalidOperationException( + FormattableString.Invariant( + $"Matched {fixturesBySrid.Count} supported EPSG WKT fixtures from {Path.GetFileName(archivePath)}, but {missingSrids.Length} managed SRIDs were missing: {printedSrids}{remainder}.")); + } + + private static string GetArchivePath() + { + string projectRoot = GetProjectRoot(); + return Path.GetFullPath(Path.Combine(projectRoot, "..", "..", "spec", "epsg", EpsgGeneratedCatalog.SourceArchive)); + } + + private static string GetProjectRoot() + { + DirectoryInfo? current = new(AppContext.BaseDirectory); + while (current is not null) + { + if (File.Exists(Path.Combine(current.FullName, "ProjNet4GeoAPI.sln"))) + { + return current.FullName; + } + + current = current.Parent; + } + + throw new InvalidOperationException("Unable to locate project root from test output directory."); + } + + private static string ReadEntryText(ZipArchiveEntry entry) + { + using Stream stream = entry.Open(); + using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); + return reader.ReadToEnd(); + } + + private static string GetRootKeyword(string wkt) + { + ReadOnlySpan trimmed = wkt.AsSpan().TrimStart(); + int bracketIndex = trimmed.IndexOf('['); + return bracketIndex > 0 + ? trimmed[..bracketIndex].ToString() + : string.Empty; + } + + private static int? TryGetFinalEpsgId(string wkt) + { + MatchCollection matches = EpsgIdRegex.Matches(wkt); + if (matches.Count == 0) + { + return null; + } + + Group id = matches[matches.Count - 1].Groups["id"]; + return int.TryParse(id.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int srid) + ? srid + : null; + } + + private sealed class FixtureIndex + { + internal FixtureIndex( + string archivePath, + ReadOnlyCollection orderedFixtures, + Dictionary fixturesBySrid) + { + this.ArchivePath = archivePath; + this.OrderedFixtures = orderedFixtures; + this.FixturesBySrid = fixturesBySrid; + } + + internal string ArchivePath { get; } + + internal ReadOnlyCollection OrderedFixtures { get; } + + internal Dictionary FixturesBySrid { get; } + } + + private sealed class FixtureState + { + internal FixtureState(string archivePath, FixtureIndex? index, string? skipReason) + { + this.ArchivePath = archivePath; + this.Index = index; + this.SkipReason = skipReason; + } + + internal string ArchivePath { get; } + + internal FixtureIndex? Index { get; } + + internal string? SkipReason { get; } + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktParserSweepTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktParserSweepTests.cs new file mode 100644 index 00000000..428a294d --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgArchiveWktParserSweepTests.cs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; +using Xunit.Sdk; + +/// +/// Verifies that every managed EPSG coordinate system still parses from the current upstream WKT archive. +/// +public class EpsgArchiveWktParserSweepTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + + /// + /// Verifies the current EPSG WKT ZIP parses cleanly for every coordinate system exposed by the managed catalog. + /// + [Fact] + public void CreateFromWkt_ParsesAllSupportedCoordinateSystemEntriesFromCurrentEpsgArchive() + { + IReadOnlyList fixtures = EpsgArchiveWktFixtureSource.GetAllSupportedFixtures(); + var failures = new List(); + + foreach (EpsgArchiveWktFixture fixture in fixtures) + { + string? failure = TryParseArchiveEntry(fixture.Wkt, fixture.Srid); + if (failure is not null) + { + failures.Add( + $"{fixture.ArchiveEntryPath} (EPSG:{fixture.Srid.ToString(CultureInfo.InvariantCulture)}, {fixture.RootKeyword}) failed: {failure}"); + } + } + + Assert.True( + failures.Count == 0, + BuildFailureMessage(EpsgArchiveWktFixtureSource.ArchivePath, fixtures.Count, failures)); + } + + private static string BuildFailureMessage(string archivePath, int parsedCount, List failures) + { + string header = string.Format( + CultureInfo.InvariantCulture, + "Failed to parse {0} supported coordinate system entr{1} from {2} after matching {3} managed SRIDs.", + failures.Count, + failures.Count == 1 ? "y" : "ies", + Path.GetFileName(archivePath), + parsedCount); + + if (failures.Count == 0) + { + return header; + } + + const int maxFailuresToPrint = 20; + IEnumerable lines = failures.Take(maxFailuresToPrint); + string remainder = failures.Count > maxFailuresToPrint + ? $"{Environment.NewLine}... {failures.Count - maxFailuresToPrint} more failure(s) omitted." + : string.Empty; + + return $"{header}{Environment.NewLine}{string.Join(Environment.NewLine, lines)}{remainder}"; + } + + private static string? TryParseArchiveEntry(string wkt, int srid) + { + try + { + CoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + return null; + } + catch (XunitException ex) + { + return ex.Message; + } + catch (WktParseException ex) + { + return ex.Message; + } + catch (NotSupportedException ex) + { + return ex.Message; + } + catch (ArgumentException ex) + { + return ex.Message; + } + catch (InvalidOperationException ex) + { + return ex.Message; + } + catch (FormatException ex) + { + return ex.Message; + } + catch (OverflowException ex) + { + return ex.Message; + } + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/EpsgBulkFunctionalSweepTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgBulkFunctionalSweepTests.cs new file mode 100644 index 00000000..a762eb12 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/EpsgBulkFunctionalSweepTests.cs @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; +using Xunit.Sdk; + +/// +/// Verifies that every supported EPSG archive fixture materializes into a basic, structurally valid coordinate system. +/// +public class EpsgBulkFunctionalSweepTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + + /// + /// Verifies the current EPSG WKT ZIP materializes every supported coordinate system with core runtime invariants intact. + /// + [Fact] + public void CreateFromWkt_MaterializesAllSupportedCoordinateSystemEntriesWithBasicInvariants() + { + IReadOnlyList fixtures = EpsgArchiveWktFixtureSource.GetAllSupportedFixtures(); + var failures = new List(); + + foreach (EpsgArchiveWktFixture fixture in fixtures) + { + string? failure = TryValidateArchiveEntry(fixture); + if (failure is not null) + { + failures.Add(failure); + } + } + + Assert.True( + failures.Count == 0, + BuildFailureMessage(EpsgArchiveWktFixtureSource.ArchivePath, fixtures.Count, failures)); + } + + private static string BuildFailureMessage(string archivePath, int materializedCount, List failures) + { + string header = + $"Failed to materialize or validate {failures.Count.ToString(CultureInfo.InvariantCulture)} supported coordinate system entr{(failures.Count == 1 ? "y" : "ies")} from {Path.GetFileName(archivePath)} after matching {materializedCount.ToString(CultureInfo.InvariantCulture)} managed SRIDs."; + + if (failures.Count == 0) + { + return header; + } + + const int maxFailuresToPrint = 20; + IEnumerable lines = failures.Take(maxFailuresToPrint); + string remainder = failures.Count > maxFailuresToPrint + ? $"{Environment.NewLine}... {failures.Count - maxFailuresToPrint} more failure(s) omitted." + : string.Empty; + + return $"{header}{Environment.NewLine}{string.Join(Environment.NewLine, lines)}{remainder}"; + } + + private static string? TryValidateArchiveEntry(EpsgArchiveWktFixture fixture) + { + try + { + CoordinateSystem coordinateSystem = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, fixture.Wkt); + List failures = ValidateCoordinateSystem(fixture, coordinateSystem); + return failures.Count == 0 + ? null + : BuildFixtureFailure(fixture, string.Join("; ", failures)); + } + catch (XunitException ex) + { + return BuildFixtureFailure(fixture, ex.Message); + } + catch (WktParseException ex) + { + return BuildFixtureFailure(fixture, ex.Message); + } + catch (NotSupportedException ex) + { + return BuildFixtureFailure(fixture, ex.Message); + } + catch (ArgumentException ex) + { + return BuildFixtureFailure(fixture, ex.Message); + } + catch (InvalidOperationException ex) + { + return BuildFixtureFailure(fixture, ex.Message); + } + catch (FormatException ex) + { + return BuildFixtureFailure(fixture, ex.Message); + } + catch (OverflowException ex) + { + return BuildFixtureFailure(fixture, ex.Message); + } + } + + private static List ValidateCoordinateSystem(EpsgArchiveWktFixture fixture, CoordinateSystem coordinateSystem) + { + var failures = new List(); + + if (!string.Equals("EPSG", coordinateSystem.Authority, StringComparison.Ordinal)) + { + failures.Add($"Authority was '{coordinateSystem.Authority}' instead of 'EPSG'."); + } + + if (coordinateSystem.AuthorityCode <= 0) + { + failures.Add($"Authority code was {coordinateSystem.AuthorityCode.ToString(CultureInfo.InvariantCulture)}, expected a positive EPSG SRID."); + } + else if (coordinateSystem.AuthorityCode != fixture.Srid) + { + failures.Add( + $"Authority code was {coordinateSystem.AuthorityCode.ToString(CultureInfo.InvariantCulture)}, expected EPSG:{fixture.Srid.ToString(CultureInfo.InvariantCulture)}."); + } + + if (string.IsNullOrWhiteSpace(coordinateSystem.Name)) + { + failures.Add("Name was empty."); + } + + if (coordinateSystem is VerticalCoordinateSystem verticalCoordinateSystem) + { + if (verticalCoordinateSystem.Dimension != 1) + { + failures.Add($"Vertical coordinate system dimension was {verticalCoordinateSystem.Dimension.ToString(CultureInfo.InvariantCulture)}, expected 1."); + } + } + else if (coordinateSystem.Dimension < 2) + { + failures.Add($"Dimension was {coordinateSystem.Dimension.ToString(CultureInfo.InvariantCulture)}, expected at least 2."); + } + + switch (coordinateSystem) + { + case ProjectedCoordinateSystem projectedCoordinateSystem: + IProjection? projection = projectedCoordinateSystem.Projection; + if (projection is null) + { + failures.Add("Projected coordinate system had no projection."); + } + + break; + case GeographicCoordinateSystem geographicCoordinateSystem: + HorizontalDatum? datum = geographicCoordinateSystem.HorizontalDatum; + if (datum is null) + { + failures.Add("Geographic coordinate system had no datum."); + break; + } + + Ellipsoid? ellipsoid = datum.Ellipsoid; + if (ellipsoid is null) + { + failures.Add("Geographic coordinate system datum had no ellipsoid."); + } + + break; + } + + return failures; + } + + private static string BuildFixtureFailure(EpsgArchiveWktFixture fixture, string failure) + { + return + $"{fixture.ArchiveEntryPath} (EPSG:{fixture.Srid.ToString(CultureInfo.InvariantCulture)}, {fixture.RootKeyword}) failed: {failure}"; + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/PostGisSpatialRefSysTableParserTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/PostGisSpatialRefSysTableParserTests.cs new file mode 100644 index 00000000..1976461f --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/PostGisSpatialRefSysTableParserTests.cs @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Data; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Text.Json; +using Npgsql; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Tests for parsing WKT coordinate system definitions from a PostGIS spatial_ref_sys table. +/// +public class PostGisSpatialRefSysTableParserTests +{ + private const string AppSettingsFileName = "appsettings.json"; + private const string ConnectionStringEnvironmentVariableName = "PROJNET_POSTGIS_CONNECTION"; + private const string MissingConnectionSkipReason = "No PostGIS connection string provided or configured connection string is invalid. Set PROJNET_POSTGIS_CONNECTION or add appsettings.json with ConnectionString."; + + private static readonly Lazy CoordinateSystemFactory = + new(() => new CoordinateSystemFactory()); + + private static readonly Lazy TrackedWebMercatorWkt = + new(() => ProjectedCoordinateSystem.WebMercator.WKT); + + private static string? connectionString; + + private static string? ConnectionString + { + get + { + if (!string.IsNullOrWhiteSpace(PostGisSpatialRefSysTableParserTests.connectionString)) + { + return PostGisSpatialRefSysTableParserTests.connectionString; + } + + foreach (string candidate in GetConfiguredConnectionStrings()) + { + if (!TryValidateConnectionString(candidate)) + { + continue; + } + + PostGisSpatialRefSysTableParserTests.connectionString = candidate; + return PostGisSpatialRefSysTableParserTests.connectionString; + } + + return null; + } + } + + /// + /// Verifies that all WKT definitions in the PostGIS spatial_ref_sys table can be parsed without errors. + /// + [Fact] + public void TestParsePostgisDefinitions() + { + if (string.IsNullOrWhiteSpace(ConnectionString)) + { + Xunit.Assert.Skip(MissingConnectionSkipReason); + } + + using (var cn = new NpgsqlConnection(ConnectionString)) + { + cn.Open(); + NpgsqlCommand cmd = cn.CreateCommand(); + cmd.CommandText = "SELECT \"srid\", \"srtext\" FROM \"public\".\"spatial_ref_sys\" ORDER BY \"srid\";"; + + int counted = 0; + int failed = 0; + int tested = 0; + using (NpgsqlDataReader? r = cmd.ExecuteReader(CommandBehavior.CloseConnection)) + { + if (r is not null) + { + while (r.Read()) + { + counted++; + int srid = r.GetInt32(0); + string srtext = r.GetString(1); + if (string.IsNullOrWhiteSpace(srtext)) + { + continue; + } + + if (srtext.StartsWith("COMPD_CS", StringComparison.Ordinal)) + { + continue; + } + + tested++; + if (!TestParse(srid, srtext)) + { + failed++; + } + } + } + } + + Console.WriteLine("\n\nTotal number of Tests {0}, failed {1}", tested, failed); + Assert.True(failed == 0); + } + } + + /// + /// Generates the tracked SRID.csv file containing SRID and WKT pairs from the PostGIS spatial_ref_sys table. + /// Known problematic EPSG rows are normalized back to the tracked canonical WKT so legacy PostGIS spellings do not regress semantics. + /// + [Fact] // Ignore("Only run this if you want a new SRID.csv file") + public void TestCreateSridCsv() + { + if (string.IsNullOrWhiteSpace(ConnectionString)) + { + Xunit.Assert.Skip(MissingConnectionSkipReason); + } + + string outputPath = GetTrackedTestFilePath("SRID.csv"); + + if (File.Exists(outputPath)) + { + File.Delete(outputPath); + } + + using (var sw = new StreamWriter(File.OpenWrite(outputPath))) + using (var cn = new NpgsqlConnection(ConnectionString)) + { + cn.Open(); + NpgsqlCommand cm = cn.CreateCommand(); + cm.CommandText = "SELECT \"srid\", \"srtext\" FROM \"public\".\"spatial_ref_sys\" ORDER BY srid;"; + using (NpgsqlDataReader dr = cm.ExecuteReader(CommandBehavior.SequentialAccess)) + { + while (dr.Read()) + { + int srid = dr.GetInt32(0); + if (dr.IsDBNull(1)) + { + continue; + } + + string srtext = dr.GetString(1); + if (string.IsNullOrWhiteSpace(srtext)) + { + continue; + } + + if (!TryCreateCoordinateSystem(srtext, out CoordinateSystem? coordinateSystem)) + { + continue; + } + + if (ShouldIncludeInTrackedSridCsv(coordinateSystem)) + { + sw.WriteLine($"{srid};{GetTrackedSridCsvWkt(srid, srtext, coordinateSystem)}"); + } + } + } + + cm.Dispose(); + } + } + + /// + /// Verifies that the legacy PostGIS EPSG:3857 row is normalized to the tracked Web Mercator WKT. + /// + [Fact] + public void GetTrackedSridCsvWkt_WithDifferentManagedDefinition_PrefersTrackedWebMercatorWkt() + { + const string legacyPseudoMercator = """PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]"""; + CoordinateSystem parsed = CreateRequiredCoordinateSystem(legacyPseudoMercator); + + string normalized = GetTrackedSridCsvWkt(3857, legacyPseudoMercator, parsed); + + Assert.Equal(TrackedWebMercatorWkt.Value, normalized); + Assert.NotEqual(legacyPseudoMercator, normalized); + Assert.DoesNotContain("Mercator_1SP", normalized, StringComparison.Ordinal); + } + + /// + /// Verifies that semantically equivalent managed definitions keep the original PostGIS WKT in the tracked export. + /// + [Fact] + public void GetTrackedSridCsvWkt_WithEquivalentManagedDefinition_PreservesOriginalWkt() + { + const string wgs84 = """GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]"""; + CoordinateSystem parsed = CreateRequiredCoordinateSystem(wgs84); + + Assert.Equal(wgs84, GetTrackedSridCsvWkt(4326, wgs84, parsed)); + } + + /// + /// Verifies that SRIDs not present in the managed catalog keep their original WKT unchanged. + /// + [Fact] + public void GetTrackedSridCsvWkt_WithoutManagedDefinition_PreservesOriginalWkt() + { + const string customWkt = """GEOGCS["Custom CRS",DATUM["Custom datum",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]"""; + CoordinateSystem parsed = CreateRequiredCoordinateSystem(customWkt); + + Assert.Equal(customWkt, GetTrackedSridCsvWkt(999999, customWkt, parsed)); + } + + private static IEnumerable GetConfiguredConnectionStrings() + { + string? environmentConnectionString = Environment.GetEnvironmentVariable(ConnectionStringEnvironmentVariableName); + if (!string.IsNullOrWhiteSpace(environmentConnectionString)) + { + yield return environmentConnectionString; + } + + string? appSettingsConnectionString = TryReadAppSettingsConnectionString(); + if (!string.IsNullOrWhiteSpace(appSettingsConnectionString) + && !string.Equals(appSettingsConnectionString, environmentConnectionString, StringComparison.Ordinal)) + { + yield return appSettingsConnectionString; + } + } + + private static string? TryReadAppSettingsConnectionString() + { + if (!File.Exists(AppSettingsFileName)) + { + return null; + } + + using (FileStream fs = File.OpenRead(AppSettingsFileName)) + using (var doc = JsonDocument.Parse(fs)) + { + if (doc.RootElement.TryGetProperty(nameof(ConnectionString), out JsonElement connElement)) + { + string? connectionStringValue = connElement.GetString(); + if (!string.IsNullOrWhiteSpace(connectionStringValue)) + { + return connectionStringValue; + } + } + + if (doc.RootElement.TryGetProperty("ConnectionStrings", out JsonElement connectionStringsElement) + && connectionStringsElement.ValueKind == JsonValueKind.Object + && connectionStringsElement.TryGetProperty("PostGisSpatialRefSys", out JsonElement namedConnectionElement)) + { + string? connectionStringValue = namedConnectionElement.GetString(); + if (!string.IsNullOrWhiteSpace(connectionStringValue)) + { + return connectionStringValue; + } + } + + return null; + } + } + + private static bool TryValidateConnectionString(string candidate) + { + try + { + using (var connection = new NpgsqlConnection(candidate)) + { + connection.Open(); + } + + return true; + } + catch (ArgumentException) + { + return false; + } + catch (FormatException) + { + return false; + } + catch (InvalidOperationException) + { + return false; + } + catch (NpgsqlException) + { + return false; + } + } + + private static bool ShouldIncludeInTrackedSridCsv(CoordinateSystem coordinateSystem) + => coordinateSystem is GeographicCoordinateSystem or ProjectedCoordinateSystem or GeocentricCoordinateSystem; + + private static string GetTrackedSridCsvWkt(int srid, string srtext, CoordinateSystem parsedCoordinateSystem) + { + if (srid != 3857 + || parsedCoordinateSystem is not ProjectedCoordinateSystem projectedCoordinateSystem + || string.Equals(projectedCoordinateSystem.Projection.ClassName, "Popular Visualisation Pseudo-Mercator", StringComparison.Ordinal) + || string.IsNullOrWhiteSpace(TrackedWebMercatorWkt.Value)) + { + return srtext; + } + + return TrackedWebMercatorWkt.Value; + } + + private static CoordinateSystem CreateRequiredCoordinateSystem(string wkt) + => CoordinateSystemFactory.Value.CreateFromWkt(wkt) + ?? throw new InvalidOperationException("Expected WKT to parse into a coordinate system."); + + private static bool TryCreateCoordinateSystem(string srtext, [NotNullWhen(true)] out CoordinateSystem? coordinateSystem) + { + try + { + coordinateSystem = CoordinateSystemFactory.Value.CreateFromWkt(srtext); + return coordinateSystem is not null; + } + catch (ArgumentException) + { + coordinateSystem = null; + return false; + } + catch (FormatException) + { + coordinateSystem = null; + return false; + } + catch (InvalidOperationException) + { + coordinateSystem = null; + return false; + } + catch (NotSupportedException) + { + coordinateSystem = null; + return false; + } + } + + private static string GetTrackedTestFilePath(string fileName) + { + string? directory = AppContext.BaseDirectory; + while (!string.IsNullOrWhiteSpace(directory)) + { + if (File.Exists(Path.Combine(directory, "ProjNET.Tests.csproj"))) + { + return Path.Combine(directory, fileName); + } + + directory = Path.GetDirectoryName(directory); + } + + throw new InvalidOperationException("Unable to locate the ProjNET.Tests project directory."); + } + + private static bool TestParse(int srid, string srtext) + { + try + { + CoordinateSystemFactory.Value.CreateFromWkt(srtext); + + // CoordinateSystemWktReader.Parse(srtext); + return true; + } + catch (Exception ex) + { + Console.WriteLine("Test {0} failed:\n {1}\n {2}", srid, srtext, ex.Message); + return false; + } + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageMatrix.cs b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageMatrix.cs new file mode 100644 index 00000000..5a185088 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageMatrix.cs @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Linq; +using ProjNet.IO.CoordinateSystems; + +/// +/// Provides the milestone-40 PROJJSON coverage matrix across reader and writer surfaces. +/// +internal static class ProjJsonCoverageMatrix +{ + private const string Reader = nameof(ProjJsonReader); + private const string Writer = nameof(ProjJsonWriter); + private const string ReaderRootReference = Reader + ".Parse, " + Reader + ".ReadInfo"; + private const string ReaderGeographicReference = Reader + ".ReadGeographicCoordinateSystem"; + private const string ReaderGeodeticReference = Reader + ".ReadGeodeticCoordinateSystem, " + Reader + ".ReadGeocentricCoordinateSystem"; + private const string ReaderProjectedReference = Reader + ".ReadProjectedCoordinateSystem"; + private const string ReaderDerivedReference = Reader + ".ReadDerivedGeodeticCoordinateSystem, " + Reader + ".ReadDerivedProjectedCoordinateSystem, " + Reader + ".CreateDerivedCoordinateSystem"; + private const string ReaderBoundReference = Reader + ".ReadBoundCoordinateSystem, " + Reader + ".ReadBoundTransformation, " + Reader + ".ReadCoordinateSystemElement"; + private const string ReaderVerticalReference = Reader + ".ReadVerticalCoordinateSystem"; + private const string ReaderCompoundReference = Reader + ".ReadCompoundCoordinateSystem"; + private const string ReaderConversionReference = Reader + ".ReadConversion, " + Reader + ".NormalizeProjectionParameterName"; + private const string ReaderCoordinateSystemReference = Reader + ".ReadCoordinateSystemDefinition, " + Reader + ".ReadAxis, " + Reader + ".ParseAxisOrientation"; + private const string ReaderDatumReference = Reader + ".ReadHorizontalDatumOrEnsemble, " + Reader + ".ReadVerticalDatumOrEnsemble, " + Reader + ".ReadHorizontalDatum, " + Reader + ".ReadVerticalDatum, " + Reader + ".ReadHorizontalDatumEnsemble, " + Reader + ".ReadVerticalDatumEnsemble, " + Reader + ".ReadDatumEnsemble, " + Reader + ".ReadDatumEnsembleMember, " + Reader + ".ReadEllipsoid"; + private const string ReaderUnitReference = Reader + ".ReadAngularUnit, " + Reader + ".ReadLinearUnit, " + Reader + ".IsAngularUnit, " + Reader + ".IsLinearUnit"; + private const string ReaderIdentifierReference = Reader + ".ReadIdentifier, " + Reader + ".ReadSingleIdentifier"; + private const string ReaderEnsembleReference = Reader + ".ReadHorizontalDatumOrEnsemble, " + Reader + ".ReadVerticalDatumOrEnsemble, " + Reader + ".ReadHorizontalDatumEnsemble, " + Reader + ".ReadVerticalDatumEnsemble, " + Reader + ".ReadDatumEnsemble, " + Reader + ".ReadDatumEnsembleMember"; + private const string WriterRootReference = Writer + ".ToJson, " + Writer + ".WriteTo, " + Writer + ".WriteCoordinateSystem"; + private const string WriterGeographicReference = Writer + ".WriteGeographicCoordinateSystem"; + private const string WriterGeocentricReference = Writer + ".WriteGeocentricCoordinateSystem"; + private const string WriterProjectedReference = Writer + ".WriteProjectedCoordinateSystem"; + private const string WriterDerivedReference = Writer + ".WriteDerivedCoordinateSystem, " + Writer + ".WriteDerivedAffineConversion, " + Writer + ".WriteDerivedAffineParameter"; + private const string WriterVerticalReference = Writer + ".WriteVerticalCoordinateSystem"; + private const string WriterCompoundReference = Writer + ".WriteCompoundCoordinateSystem, " + Writer + ".WriteCompoundComponents, " + Writer + ".WriteCompoundComponent"; + private const string WriterAxisReference = Writer + ".WriteCoordinateSystemDefinition, " + Writer + ".WriteAxis, " + Writer + ".GetAxisDirection"; + private const string WriterDatumReference = Writer + ".WriteHorizontalDatumProperty, " + Writer + ".WriteVerticalDatumProperty, " + Writer + ".WriteHorizontalDatum, " + Writer + ".WriteDatumEnsemble, " + Writer + ".WriteDatumEnsembleMember, " + Writer + ".WriteEllipsoid, " + Writer + ".WritePrimeMeridian, " + Writer + ".WriteVerticalDatum"; + private const string WriterConversionReference = Writer + ".WriteConversion, " + Writer + ".WriteMethod, " + Writer + ".WriteProjectionParameter, " + Writer + ".WriteUnit, " + Writer + ".WriteAngularUnit, " + Writer + ".WriteLinearUnit, " + Writer + ".WriteScaleUnit"; + private const string WriterBoundReference = Writer + ".WriteBoundCoordinateSystem, " + Writer + ".WriteBoundCoordinateSystemComponent, " + Writer + ".WriteBoundTransformation, " + Writer + ".WriteBoundTransformationParameters, " + Writer + ".TryWriteLegacyBoundCoordinateSystem"; + private const string WriterIdentifierReference = Writer + ".WriteIdentifier"; + + /// + /// Gets the current milestone-40 PROJJSON coverage rows. + /// + internal static IReadOnlyList Rows { get; } = CreateRows(); + + private static ProjJsonCoverageRow[] CreateRows() => + new ProjJsonCoverageRow[] + { + Row( + "BoundCRS", + ProjJsonCoverageStatus.Supported, + $"{ReaderRootReference}, {ReaderBoundReference}", + ProjJsonCoverageStatus.Supported, + WriterBoundReference, + "Reader and writer both dispatch BoundCRS objects into the first-class bound model, and the writer also bridges retained legacy bound metadata through the same BoundCRS path."), + Row( + "CompoundCRS", + ProjJsonCoverageStatus.Supported, + ReaderCompoundReference, + ProjJsonCoverageStatus.Supported, + WriterCompoundReference, + "Compound CRS objects are parsed and emitted natively."), + Row( + "conversion", + ProjJsonCoverageStatus.Supported, + $"{ReaderConversionReference}, {ReaderDerivedReference}", + ProjJsonCoverageStatus.Supported, + $"{WriterConversionReference}, {WriterDerivedReference}", + "Nested conversion objects are parsed and emitted natively for projected CRS and the supported affine derived CRS slice."), + Row( + "conversion.parameters[].unit", + ProjJsonCoverageStatus.Ignored, + ReaderConversionReference, + ProjJsonCoverageStatus.Supported, + WriterConversionReference, + "Reader keeps parameter names and values but does not consume parameter unit objects, while the writer emits angular, linear, and scale units."), + Row( + "coordinate_system.axis", + ProjJsonCoverageStatus.Supported, + ReaderCoordinateSystemReference, + ProjJsonCoverageStatus.Supported, + WriterAxisReference, + "Axis definitions are parsed and emitted natively."), + Row( + "coordinate_system.axis.unit object", + ProjJsonCoverageStatus.Supported, + $"{ReaderCoordinateSystemReference}, {ReaderUnitReference}", + ProjJsonCoverageStatus.Supported, + $"{WriterAxisReference}, {WriterConversionReference}", + "Object-form angular and linear units are supported on both sides."), + Row( + "coordinate_system.axis.unit string shorthand", + ProjJsonCoverageStatus.Supported, + ReaderUnitReference, + ProjJsonCoverageStatus.Unsupported, + WriterAxisReference, + "Reader accepts degree/metre shorthand strings, while the writer always emits structured unit objects."), + Row( + "CoordinateMetadata", + ProjJsonCoverageStatus.Unsupported, + ReaderRootReference, + ProjJsonCoverageStatus.Unsupported, + WriterRootReference, + "No coordinate metadata type path exists yet."), + Row( + "datum.type = DynamicGeodeticReferenceFrame", + ProjJsonCoverageStatus.Partial, + ReaderDatumReference, + ProjJsonCoverageStatus.Unsupported, + WriterDatumReference, + "Reader accepts the type token but does not retain dynamic metadata, and the writer only emits GeodeticReferenceFrame."), + Row( + "datum.type = DynamicVerticalReferenceFrame", + ProjJsonCoverageStatus.Partial, + ReaderDatumReference, + ProjJsonCoverageStatus.Unsupported, + WriterDatumReference, + "Reader accepts the type token but does not retain dynamic metadata, and the writer only emits VerticalReferenceFrame."), + Row( + "datum.type = GeodeticReferenceFrame", + ProjJsonCoverageStatus.Supported, + ReaderDatumReference, + ProjJsonCoverageStatus.Supported, + WriterDatumReference, + "Static geodetic reference frames are fully supported."), + Row( + "datum.type = VerticalReferenceFrame", + ProjJsonCoverageStatus.Supported, + ReaderDatumReference, + ProjJsonCoverageStatus.Supported, + WriterDatumReference, + "Static vertical reference frames are fully supported."), + Row( + "datum_ensemble", + ProjJsonCoverageStatus.Supported, + ReaderEnsembleReference, + ProjJsonCoverageStatus.Supported, + WriterDatumReference, + "Reader and writer retain datum_ensemble metadata for supported geodetic and vertical CRS definitions."), + Row( + "DerivedCRS/FittedCoordinateSystem", + ProjJsonCoverageStatus.Supported, + $"{ReaderRootReference}, {ReaderDerivedReference}", + ProjJsonCoverageStatus.Supported, + $"{WriterRootReference}, {WriterDerivedReference}", + "Derived geographic and projected CRS now roundtrip onto FittedCoordinateSystem for the supported affine 2D slice."), + Row( + "EngineeringCRS", + ProjJsonCoverageStatus.Unsupported, + ReaderRootReference, + ProjJsonCoverageStatus.Unsupported, + WriterRootReference, + "Engineering CRS objects are outside the current PROJJSON surface."), + Row( + "GeodeticCRS.cartesian", + ProjJsonCoverageStatus.Supported, + ReaderGeodeticReference, + ProjJsonCoverageStatus.Supported, + WriterGeocentricReference, + "Cartesian geodetic CRS are handled as geocentric coordinate systems on both sides."), + Row( + "GeodeticCRS.ellipsoidal", + ProjJsonCoverageStatus.Supported, + ReaderGeodeticReference, + ProjJsonCoverageStatus.Unsupported, + WriterGeographicReference, + "Reader accepts ellipsoidal GeodeticCRS, but the writer emits GeographicCRS instead of this type value."), + Row( + "GeographicCRS", + ProjJsonCoverageStatus.Supported, + ReaderGeographicReference, + ProjJsonCoverageStatus.Supported, + WriterGeographicReference, + "Geographic CRS are fully supported on read and write."), + Row( + "id", + ProjJsonCoverageStatus.Supported, + ReaderIdentifierReference, + ProjJsonCoverageStatus.Supported, + WriterIdentifierReference, + "Single identifier objects are parsed and emitted natively."), + Row( + "ids[]", + ProjJsonCoverageStatus.Supported, + ReaderIdentifierReference, + ProjJsonCoverageStatus.Unsupported, + WriterIdentifierReference, + "Reader supports ids[] with EPSG preference, while the writer only emits a singular id."), + Row( + "ParametricCRS", + ProjJsonCoverageStatus.Unsupported, + ReaderRootReference, + ProjJsonCoverageStatus.Unsupported, + WriterRootReference, + "Parametric CRS objects are outside the current PROJJSON surface."), + Row( + "prime_meridian", + ProjJsonCoverageStatus.Supported, + Reader + ".ReadGeographicCoordinateSystem, " + Reader + ".ReadGeodeticCoordinateSystem, " + Reader + ".ReadPrimeMeridian", + ProjJsonCoverageStatus.Supported, + Writer + ".WriteGeographicCoordinateSystem, " + Writer + ".WriteGeocentricCoordinateSystem, " + Writer + ".WritePrimeMeridian", + "Prime meridians are parsed and emitted natively."), + Row( + "ProjectedCRS", + ProjJsonCoverageStatus.Supported, + ReaderProjectedReference, + ProjJsonCoverageStatus.Supported, + WriterProjectedReference, + "Projected CRS are fully supported on read and write."), + Row( + "retained bound metadata on existing CRS", + ProjJsonCoverageStatus.Unsupported, + ReaderRootReference, + ProjJsonCoverageStatus.Unsupported, + WriterBoundReference, + "The current PROJJSON surface cannot preserve retained WGS84 or bound-grid metadata on existing CRS objects."), + Row( + "Standalone operation objects", + ProjJsonCoverageStatus.Unsupported, + ReaderRootReference, + ProjJsonCoverageStatus.Unsupported, + WriterRootReference, + "Standalone coordinate-operation or concatenated-operation objects are not exposed as top-level PROJJSON parse/write targets."), + Row( + "TemporalCRS", + ProjJsonCoverageStatus.Unsupported, + ReaderRootReference, + ProjJsonCoverageStatus.Unsupported, + WriterRootReference, + "Temporal CRS objects are outside the current PROJJSON surface."), + Row( + "unit.type = Unit", + ProjJsonCoverageStatus.Supported, + ReaderUnitReference, + ProjJsonCoverageStatus.Unsupported, + WriterConversionReference, + "Reader accepts generic Unit objects for angular and linear units, while the writer emits concrete unit types."), + Row( + "usage metadata", + ProjJsonCoverageStatus.Ignored, + ReaderRootReference, + ProjJsonCoverageStatus.Unsupported, + WriterRootReference, + "Reader ignores extra metadata properties such as scope, area, bbox, remarks, and usages; writer does not emit them."), + Row( + "VerticalCRS", + ProjJsonCoverageStatus.Supported, + ReaderVerticalReference, + ProjJsonCoverageStatus.Supported, + WriterVerticalReference, + "Vertical CRS are fully supported on read and write."), + } + .OrderBy(row => row.Feature, StringComparer.Ordinal) + .ToArray(); + + private static ProjJsonCoverageRow Row( + string feature, + ProjJsonCoverageStatus readerStatus, + string readerReference, + ProjJsonCoverageStatus writerStatus, + string writerReference, + string notes) => + new(feature, readerStatus, readerReference, writerStatus, writerReference, notes); +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageMatrixTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageMatrixTests.cs new file mode 100644 index 00000000..09e07872 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageMatrixTests.cs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Linq; +using Xunit; + +/// +/// Guards the milestone-40 PROJJSON coverage matrix. +/// +public class ProjJsonCoverageMatrixTests +{ + /// + /// Ensures the matrix contains the expected feature rows exactly once. + /// + [Fact] + public void Rows_CoverExpectedFeaturesExactlyOnce() + { + string[] expectedFeatures = + [ + "BoundCRS", + "CompoundCRS", + "conversion", + "conversion.parameters[].unit", + "coordinate_system.axis", + "coordinate_system.axis.unit object", + "coordinate_system.axis.unit string shorthand", + "CoordinateMetadata", + "datum.type = DynamicGeodeticReferenceFrame", + "datum.type = DynamicVerticalReferenceFrame", + "datum.type = GeodeticReferenceFrame", + "datum.type = VerticalReferenceFrame", + "datum_ensemble", + "DerivedCRS/FittedCoordinateSystem", + "EngineeringCRS", + "GeodeticCRS.cartesian", + "GeodeticCRS.ellipsoidal", + "GeographicCRS", + "id", + "ids[]", + "ParametricCRS", + "prime_meridian", + "ProjectedCRS", + "retained bound metadata on existing CRS", + "Standalone operation objects", + "TemporalCRS", + "unit.type = Unit", + "usage metadata", + "VerticalCRS", + ]; + + Assert.Equal( + expectedFeatures.OrderBy(feature => feature, StringComparer.Ordinal), + ProjJsonCoverageMatrix.Rows.Select(row => row.Feature)); + + Assert.Equal( + ProjJsonCoverageMatrix.Rows.Count, + ProjJsonCoverageMatrix.Rows.Select(row => row.Feature).Distinct(StringComparer.Ordinal).Count()); + } + + /// + /// Ensures the matrix remains fully populated and sorted. + /// + [Fact] + public void Rows_AreSortedAndPopulated() + { + Assert.Equal( + ProjJsonCoverageMatrix.Rows.OrderBy(row => row.Feature, StringComparer.Ordinal).Select(row => row.Feature), + ProjJsonCoverageMatrix.Rows.Select(row => row.Feature)); + + Assert.All( + ProjJsonCoverageMatrix.Rows, + row => + { + CoverageReferenceAssert.AssertSymbolReferenceList(row.ReaderReference); + CoverageReferenceAssert.AssertSymbolReferenceList(row.WriterReference); + Assert.False(string.IsNullOrWhiteSpace(row.Notes)); + }); + } + + /// + /// Ensures the matrix keeps the expected milestone anchor classifications. + /// + [Fact] + public void AnchorFeatures_KeepExpectedStatuses() + { + var lookup = ProjJsonCoverageMatrix.Rows.ToDictionary(row => row.Feature, StringComparer.Ordinal); + + Assert.Equal(ProjJsonCoverageStatus.Supported, lookup["GeographicCRS"].ReaderStatus); + Assert.Equal(ProjJsonCoverageStatus.Supported, lookup["GeographicCRS"].WriterStatus); + Assert.Equal(ProjJsonCoverageStatus.Supported, lookup["BoundCRS"].ReaderStatus); + Assert.Equal(ProjJsonCoverageStatus.Supported, lookup["BoundCRS"].WriterStatus); + Assert.Equal(ProjJsonCoverageStatus.Supported, lookup["datum_ensemble"].ReaderStatus); + Assert.Equal(ProjJsonCoverageStatus.Supported, lookup["datum_ensemble"].WriterStatus); + Assert.Equal(ProjJsonCoverageStatus.Ignored, lookup["usage metadata"].ReaderStatus); + Assert.Equal(ProjJsonCoverageStatus.Unsupported, lookup["usage metadata"].WriterStatus); + Assert.Equal(ProjJsonCoverageStatus.Supported, lookup["ids[]"].ReaderStatus); + Assert.Equal(ProjJsonCoverageStatus.Unsupported, lookup["ids[]"].WriterStatus); + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageRow.cs b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageRow.cs new file mode 100644 index 00000000..870515fb --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageRow.cs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +/// +/// Represents one row of the milestone-40 PROJJSON coverage matrix. +/// +/// The tracked PROJJSON type or structural feature. +/// The current reader support status. +/// The primary reader symbol reference. +/// The current writer support status. +/// The primary writer symbol reference. +/// A short explanation of the current behavior. +internal sealed record ProjJsonCoverageRow( + string Feature, + ProjJsonCoverageStatus ReaderStatus, + string ReaderReference, + ProjJsonCoverageStatus WriterStatus, + string WriterReference, + string Notes); diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageStatus.cs b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageStatus.cs new file mode 100644 index 00000000..2c461ff7 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonCoverageStatus.cs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +/// +/// Describes the current PROJJSON support level for one reader or writer surface. +/// +internal enum ProjJsonCoverageStatus +{ + /// + /// The feature is supported as-is. + /// + Supported, + + /// + /// The feature is accepted only partially or is emitted with reduced fidelity. + /// + Partial, + + /// + /// The feature is tolerated but ignored. + /// + Ignored, + + /// + /// The feature is not currently supported. + /// + Unsupported, +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonReaderTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonReaderTests.cs new file mode 100644 index 00000000..0af074c9 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonReaderTests.cs @@ -0,0 +1,1260 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.Json; +using ProjNet.CoordinateSystems; +using ProjNet.Data; +using ProjNet.IO.CoordinateSystems; +using Xunit; + +/// +/// Verifies native PROJJSON coordinate-system parsing against EPSG-backed catalog references. +/// +public class ProjJsonReaderTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly Lazy> CatalogDefinitions = new(() => + new ManagedCoordinateSystemDefinitionProvider() + .GetDefinitions() + .GroupBy(item => item.Srid) + .ToDictionary(group => group.Key, group => group.Last().Wkt)); + + /// + /// Provides PROJJSON geographic CRS examples aligned with real EPSG catalog entries. + /// + /// SRID/PROJJSON pairs that should parse successfully. + public static IEnumerable> SupportedGeographicRows() + { + object degreeUnit = "degree"; + object gradUnit = AngularUnitObject("grad", 0.015707963267949d, 9105); + object metreUnit = "metre"; + + return + [ + new TheoryDataRow( + 4230, + Serialize( + GeographicCrsObject( + 4230, + "ED50", + GeodeticDatumObject("European Datum 1950", EllipsoidObject("International 1924", 6378388d, 297d, metreUnit, 7022), 6230), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")))), + new TheoryDataRow( + 4277, + Serialize( + GeographicCrsObject( + 4277, + "OSGB36", + GeodeticDatumObject("Ordnance Survey of Great Britain 1936", EllipsoidObject("Airy 1830", 6377563.396d, 299.3249646d, metreUnit, 7001), 6277), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")))), + new TheoryDataRow( + 4314, + Serialize( + GeographicCrsObject( + 4314, + "DHDN", + GeodeticDatumObject("Deutsches Hauptdreiecksnetz", EllipsoidObject("Bessel 1841", 6377397.155d, 299.1528128d, metreUnit, 7004), 6314), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")))), + new TheoryDataRow( + 4322, + Serialize( + GeographicCrsObject( + 4322, + "WGS 72", + GeodeticDatumObject("World Geodetic System 1972", EllipsoidObject("WGS 72", 6378135d, 298.26d, metreUnit, 7043), 6322), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")))), + new TheoryDataRow( + 4807, + Serialize( + GeographicCrsObject( + 4807, + "NTF (Paris)", + GeodeticDatumObject("Nouvelle Triangulation Francaise (Paris)", EllipsoidObject("Clarke 1880 (IGN)", 6378249.2d, 293.466021293627d, metreUnit, 7011), 6807), + PrimeMeridianObject("Paris", 0.040792344d, degreeUnit, 8903, useIds: true, stringCode: true), + gradUnit, + ("Geodetic latitude (Lat)", "Lat", "north"), + ("Geodetic longitude (Lon)", "Lon", "east"), + useIds: true))), + ]; + } + + /// + /// Provides PROJJSON projected CRS examples aligned with real EPSG catalog entries. + /// + /// SRID/PROJJSON pairs that should parse successfully. + public static IEnumerable> SupportedProjectedRows() + { + object degreeUnit = "degree"; + object metreUnit = "metre"; + object unityUnit = ScaleUnitObject(); + + return + [ + new TheoryDataRow( + 27700, + Serialize( + ProjectedCrsObject( + 27700, + "OSGB36 / British National Grid", + GeographicCrsObject( + 4277, + "OSGB36", + GeodeticDatumObject("Ordnance Survey of Great Britain 1936", EllipsoidObject("Airy 1830", 6377563.396d, 299.3249646d, metreUnit, 7001), 6277), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + ConversionObject( + "British National Grid", + "Transverse Mercator", + 19916, + ProjectionParameterObject("Latitude of natural origin", 49d, degreeUnit, 8801), + ProjectionParameterObject("Longitude of natural origin", -2d, degreeUnit, 8802), + ProjectionParameterObject("Scale factor at natural origin", 0.9996012717d, unityUnit, 8805), + ProjectionParameterObject("False easting", 400000d, metreUnit, 8806), + ProjectionParameterObject("False northing", -100000d, metreUnit, 8807)), + metreUnit, + ("Easting", "E", "east"), + ("Northing", "N", "north")))), + new TheoryDataRow( + 31370, + Serialize( + ProjectedCrsObject( + 31370, + "BD72 / Belgian Lambert 72", + GeographicCrsObject( + 4313, + "BD72", + GeodeticDatumObject("Reseau National Belge 1972", EllipsoidObject("International 1924", 6378388d, 297d, metreUnit, 7022), 6313), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + ConversionObject( + "Belgian Lambert 72", + "Lambert Conic Conformal (2SP)", + 19961, + ProjectionParameterObject("Latitude of false origin", 90d, degreeUnit, 8821), + ProjectionParameterObject("Longitude of false origin", 4.36748666666694d, degreeUnit, 8822), + ProjectionParameterObject("Latitude of 1st standard parallel", 51.1666672333336d, degreeUnit, 8823), + ProjectionParameterObject("Latitude of 2nd standard parallel", 49.8333339000003d, degreeUnit, 8824), + ProjectionParameterObject("Easting at false origin", 150000.013d, metreUnit, 8826), + ProjectionParameterObject("Northing at false origin", 5400088.438d, metreUnit, 8827)), + metreUnit, + ("Easting", "X", "east"), + ("Northing", "Y", "north")))), + new TheoryDataRow( + 2169, + Serialize( + ProjectedCrsObject( + 2169, + "LUREF / Luxembourg TM", + GeographicCrsObject( + 4181, + "LUREF", + GeodeticDatumObject("Luxembourg Reference Frame", EllipsoidObject("International 1924", 6378388d, 297d, metreUnit, 7022), 6181), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + ConversionObject( + "Luxembourg TM", + "Transverse Mercator", + 19966, + ProjectionParameterObject("Latitude of natural origin", 49.8333333333336d, degreeUnit, 8801), + ProjectionParameterObject("Longitude of natural origin", 6.16666666666694d, degreeUnit, 8802), + ProjectionParameterObject("Scale factor at natural origin", 1d, unityUnit, 8805), + ProjectionParameterObject("False easting", 80000d, metreUnit, 8806), + ProjectionParameterObject("False northing", 100000d, metreUnit, 8807)), + metreUnit, + ("Northing", "X", "north"), + ("Easting", "Y", "east")))), + new TheoryDataRow( + 23032, + Serialize( + ProjectedCrsObject( + 23032, + "ED50 / UTM zone 32N", + GeographicCrsObject( + 4230, + "ED50", + GeodeticDatumObject("European Datum 1950", EllipsoidObject("International 1924", 6378388d, 297d, metreUnit, 7022), 6230), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + ConversionObject( + "UTM zone 32N", + "Transverse Mercator", + 16032, + ProjectionParameterObject("Latitude of natural origin", 0d, degreeUnit, 8801), + ProjectionParameterObject("Longitude of natural origin", 9d, degreeUnit, 8802), + ProjectionParameterObject("Scale factor at natural origin", 0.9996d, unityUnit, 8805), + ProjectionParameterObject("False easting", 500000d, metreUnit, 8806), + ProjectionParameterObject("False northing", 0d, metreUnit, 8807)), + metreUnit, + ("Easting", "E", "east"), + ("Northing", "N", "north")))), + new TheoryDataRow( + 31467, + Serialize( + ProjectedCrsObject( + 31467, + "DHDN / 3-degree Gauss-Kruger zone 3", + GeographicCrsObject( + 4314, + "DHDN", + GeodeticDatumObject("Deutsches Hauptdreiecksnetz", EllipsoidObject("Bessel 1841", 6377397.155d, 299.1528128d, metreUnit, 7004), 6314), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + ConversionObject( + "3-degree Gauss-Kruger zone 3", + "Transverse Mercator", + 16263, + ProjectionParameterObject("Latitude of natural origin", 0d, degreeUnit, 8801), + ProjectionParameterObject("Longitude of natural origin", 9d, degreeUnit, 8802), + ProjectionParameterObject("Scale factor at natural origin", 1d, unityUnit, 8805), + ProjectionParameterObject("False easting", 3500000d, metreUnit, 8806), + ProjectionParameterObject("False northing", 0d, metreUnit, 8807)), + metreUnit, + ("Northing", "X", "north"), + ("Easting", "Y", "east")))), + ]; + } + + /// + /// Provides PROJJSON geocentric, vertical, and compound CRS examples aligned with real EPSG catalog entries. + /// + /// SRID/PROJJSON pairs that should parse successfully. + public static IEnumerable> SupportedRemainingRows() + { + object degreeUnit = "degree"; + object metreUnit = "metre"; + + return + [ + new TheoryDataRow( + 4978, + Serialize( + GeocentricCrsObject( + 4978, + "WGS 84", + GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, metreUnit, 7030), 6326), + GreenwichPrimeMeridianObject(), + metreUnit)), + typeof(GeocentricCoordinateSystem)), + new TheoryDataRow( + 5701, + Serialize( + VerticalCrsObject( + 5701, + "Newlyn", + VerticalDatumObject("Ordnance Datum Newlyn", 5101), + metreUnit, + "Up", + "up")), + typeof(VerticalCoordinateSystem)), + new TheoryDataRow( + 9518, + Serialize( + CompoundCrsObject( + 9518, + "WGS 84 + EGM96 height", + GeographicCrsObject( + 4326, + "WGS 84", + GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, metreUnit, 7030), 6326), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + VerticalCrsObject( + 5773, + "EGM96 height", + VerticalDatumObject("EGM96 geoid", -1), + metreUnit, + "Gravity-related height", + "up"))), + typeof(CompoundCoordinateSystem)), + ]; + } + + /// + /// Provides representative unsupported top-level PROJJSON type values from the milestone-40 coverage matrix. + /// + /// Type values that should still report the current unsupported boundary explicitly. + public static IEnumerable> UnsupportedTopLevelTypeRows() + { + return + [ + new TheoryDataRow("CoordinateMetadata"), + new TheoryDataRow("EngineeringCRS"), + new TheoryDataRow("ParametricCRS"), + new TheoryDataRow("TemporalCRS"), + new TheoryDataRow("DerivedVerticalCRS"), + new TheoryDataRow("ConcatenatedOperation"), + ]; + } + + /// + /// Verifies supported PROJJSON geographic CRS parse to the same semantic model as the committed catalog reference. + /// + /// Expected EPSG SRID. + /// PROJJSON CRS definition. + [Theory] + [MemberData(nameof(SupportedGeographicRows))] + public void Parse_ParsesSupportedGeographicCrsEquivalentToCatalogReference(int srid, string json) + { + GeographicCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + GeographicCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.True(parsed.EqualParams(reference), $"PROJJSON geographic CRS parse mismatch for EPSG:{srid}."); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + } + + /// + /// Verifies supported PROJJSON projected CRS parse to the same semantic model as the committed catalog reference. + /// + /// Expected EPSG SRID. + /// PROJJSON CRS definition. + [Theory] + [MemberData(nameof(SupportedProjectedRows))] + public void Parse_ParsesSupportedProjectedCrsEquivalentToCatalogReference(int srid, string json) + { + ProjectedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.True(parsed.EqualParams(reference), $"PROJJSON projected CRS parse mismatch for EPSG:{srid}."); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + } + + /// + /// Verifies supported remaining PROJJSON CRS types parse to the same semantic model as the committed catalog reference. + /// + /// Expected EPSG SRID. + /// PROJJSON CRS definition. + /// Expected coordinate system type. + [Theory] + [MemberData(nameof(SupportedRemainingRows))] + public void Parse_ParsesSupportedRemainingCrsEquivalentToCatalogReference(int srid, string json, Type expectedType) + { + CoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json), exactMatch: false); + CoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.Equal(expectedType, parsed.GetType()); + Assert.Equal(expectedType, reference.GetType()); + Assert.True(parsed.EqualParams(reference), $"PROJJSON remaining CRS parse mismatch for EPSG:{srid}."); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(srid, parsed.AuthorityCode); + } + + /// + /// Verifies supported PROJJSON BoundCRS definitions parse into the first-class bound model. + /// + [Fact] + public void Parse_ParsesSupportedBoundCrs() + { + object degreeUnit = "degree"; + string json = Serialize( + BoundCrsObject( + 4269, + "NAD83", + GeographicCrsObject( + 4269, + "NAD83", + GeodeticDatumObject("North American Datum 1983", EllipsoidObject("GRS 1980", 6378137d, 298.257222101d, "metre", 7019), 6269), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + GeographicCrsObject( + 4326, + "WGS 84", + GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, "metre", 7030), 6326), + GreenwichPrimeMeridianObject(), + degreeUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + AbridgedTransformationObject( + "NAD83 to WGS 84 (1)", + "Geocentric translations", + BoundParameterValueObject("X-axis translation", 0d), + BoundParameterValueObject("Y-axis translation", 0d), + BoundParameterValueObject("Z-axis translation", 0d)))); + + BoundCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + GeographicCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + GeographicCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + Wgs84ConversionInfo parameters = Assert.IsType(parsed.Transformation.Wgs84Parameters); + + Assert.Equal("NAD83", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(4269, parsed.AuthorityCode); + Assert.Equal("North American Datum 1983", source.HorizontalDatum.Name); + Assert.Null(source.HorizontalDatum.Wgs84Parameters); + Assert.Equal("WGS 84", target.Name); + Assert.True(target.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + Assert.True(target.PrimeMeridian.EqualParams(PrimeMeridian.Greenwich)); + Assert.True(target.AngularUnit.EqualParams(AngularUnit.Degrees)); + Assert.Equal(AxisOrientationEnum.North, target.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.East, target.GetAxis(1).Orientation); + Assert.Equal("Geocentric translations", parsed.Transformation.MethodName); + Assert.Equal(new Wgs84ConversionInfo(0, 0, 0, 0, 0, 0, 0), parameters); + } + + /// + /// Verifies supported PROJJSON vertical BoundCRS definitions parse into the first-class bound model. + /// + [Fact] + public void Parse_ParsesSupportedVerticalBoundCrs() + { + string json = Serialize( + BoundCrsObject( + 3855, + "EGM2008 height", + VerticalCrsObject( + 3855, + "EGM2008 height", + VerticalDatumObject("EGM2008 geoid", 1027), + "metre", + "Gravity-related height", + "up"), + CompoundCrsObject( + 9518, + "WGS 84 + ODN height", + GeographicCrsObject( + 4326, + "WGS 84", + GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, "metre", 7030), 6326), + GreenwichPrimeMeridianObject(), + "degree", + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + VerticalCrsObject( + 5701, + "ODN height", + VerticalDatumObject("Ordnance Datum Newlyn", 5101), + "metre", + "Gravity-related height", + "up")), + AbridgedTransformationObject( + "WGS 84 to EGM2008 height", + "Geographic3D to GravityRelatedHeight (EGM)", + BoundParameterValueObject("Geoid (height correction) model file", "egm96_15.gtx")))); + + BoundCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + VerticalCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + CompoundCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + Assert.Equal("EGM2008 height", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(3855, parsed.AuthorityCode); + Assert.Equal("EGM2008 geoid", source.VerticalDatum.Name); + Assert.Null(source.BoundGridTransformation); + Assert.Equal("Geographic3D to GravityRelatedHeight (EGM)", parsed.Transformation.MethodName); + Assert.Equal("egm96_15.gtx", parsed.Transformation.ParameterFileName); + Assert.Equal(3, target.Dimension); + } + + /// + /// Verifies projected PROJJSON parsing preserves base CRS prime meridian and angular unit semantics. + /// + [Fact] + public void Parse_ParsesProjectedCrsBasePrimeMeridianAndAngularUnit() + { + object metreUnit = "metre"; + object gradUnit = AngularUnitObject("grad", 0.015707963267949d, 9105); + string json = Serialize( + ProjectedCrsObject( + 27561, + "NTF (Paris) / Lambert Nord France", + GeographicCrsObject( + 4807, + "NTF (Paris)", + GeodeticDatumObject("Nouvelle Triangulation Francaise (Paris)", EllipsoidObject("Clarke 1880 (IGN)", 6378249.2d, 293.466021293627d, metreUnit, 7011), 6807), + PrimeMeridianObject("Paris", 0.040792344d, AngularUnitObject("radian", 1d, 9101), 8903), + gradUnit, + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + ConversionObject( + "Lambert Nord France", + "Lambert Conic Conformal (1SP)", + 18091, + ProjectionParameterObject("Latitude of natural origin", 55d, gradUnit, 8801), + ProjectionParameterObject("Longitude of natural origin", 0d, gradUnit, 8802), + ProjectionParameterObject("Scale factor at natural origin", 0.999877341d, ScaleUnitObject(), 8805), + ProjectionParameterObject("False easting", 600000d, metreUnit, 8806), + ProjectionParameterObject("False northing", 200000d, metreUnit, 8807)), + metreUnit, + ("Easting", "X", "east"), + ("Northing", "Y", "north"))); + + ProjectedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.Equal("NTF (Paris) / Lambert Nord France", parsed.Name); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(27561, parsed.AuthorityCode); + Assert.Equal("grad", parsed.GeographicCoordinateSystem.AngularUnit.Name); + Assert.Equal("EPSG", parsed.GeographicCoordinateSystem.AngularUnit.Authority); + Assert.Equal(9105, parsed.GeographicCoordinateSystem.AngularUnit.AuthorityCode); + Assert.Equal("Paris", parsed.GeographicCoordinateSystem.PrimeMeridian.Name); + Assert.True(parsed.GeographicCoordinateSystem.PrimeMeridian.AngularUnit.EqualParams(AngularUnit.Radian)); + Assert.Equal(0.040792344d, parsed.GeographicCoordinateSystem.PrimeMeridian.Longitude); + Assert.Equal("Lambert Conic Conformal (1SP)", parsed.Projection.ClassName); + Assert.Equal("EPSG", parsed.Projection.Authority); + Assert.Equal(18091, parsed.Projection.AuthorityCode); + Assert.Equal(55d, parsed.Projection.GetParameter("latitude_of_origin")?.Value); + Assert.Equal(0d, parsed.Projection.GetParameter("central_meridian")?.Value); + Assert.Equal(0.999877341d, parsed.Projection.GetParameter("scale_factor")?.Value); + Assert.Equal(600000d, parsed.Projection.GetParameter("false_easting")?.Value); + Assert.Equal(200000d, parsed.Projection.GetParameter("false_northing")?.Value); + } + + /// + /// Verifies PROJJSON identifiers can be sourced from an ids array and prefer the EPSG identifier. + /// + [Fact] + public void Parse_WithIdsArray_PrefersEpsgIdentifier() + { + object[] ids = + [ + IdObject("IGNF", "NTFP"), + IdObject("EPSG", 4807), + ]; + + string json = Serialize( + Obj( + ("type", "GeographicCRS"), + ("name", "NTF (Paris)"), + ("datum", GeodeticDatumObject("Nouvelle Triangulation Francaise (Paris)", EllipsoidObject("Clarke 1880 (IGN)", 6378249.2d, 293.466021293627d, "metre", 7011), 6807)), + ("prime_meridian", PrimeMeridianObject("Paris", 0.040792344d, AngularUnitObject("radian", 1d, 9101), 8903, useIds: true, stringCode: true)), + ("coordinate_system", CoordinateSystemObject("ellipsoidal", AxisObject("Geodetic latitude", "Lat", "north", "degree"), AxisObject("Geodetic longitude", "Lon", "east", "degree"))), + ("ids", ids))); + + GeographicCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(4807, parsed.AuthorityCode); + Assert.Equal("EPSG", parsed.PrimeMeridian.Authority); + Assert.Equal(8903, parsed.PrimeMeridian.AuthorityCode); + } + + /// + /// Verifies optional usage metadata blocks are tolerated on PROJJSON CRS objects. + /// + [Fact] + public void Parse_ParsesProjectedCrsWithUsageMetadataEquivalentToCatalogReference() + { + Dictionary baseCrs = GeographicCrsObject( + 4277, + "OSGB36", + GeodeticDatumObject("Ordnance Survey of Great Britain 1936", EllipsoidObject("Airy 1830", 6377563.396d, 299.3249646d, "metre", 7001), 6277), + GreenwichPrimeMeridianObject(), + "degree", + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")); + + Dictionary conversion = ConversionObject( + "British National Grid", + "Transverse Mercator", + 19916, + ProjectionParameterObject("Latitude of natural origin", 49d, "degree", 8801), + ProjectionParameterObject("Longitude of natural origin", -2d, "degree", 8802), + ProjectionParameterObject("Scale factor at natural origin", 0.9996012717d, ScaleUnitObject(), 8805), + ProjectionParameterObject("False easting", 400000d, "metre", 8806), + ProjectionParameterObject("False northing", -100000d, "metre", 8807)); + + object[] usages = + [ + Obj( + ("scope", "Topographic mapping."), + ("area", "United Kingdom."), + ("bbox", Obj(("south_latitude", 49.75d), ("west_longitude", -9.01d), ("north_latitude", 61.01d), ("east_longitude", 2.01d)))), + ]; + + string json = Serialize( + Obj( + ("type", "ProjectedCRS"), + ("name", "OSGB36 / British National Grid"), + ("base_crs", baseCrs), + ("conversion", conversion), + ("coordinate_system", CoordinateSystemObject("Cartesian", AxisObject("Easting", "E", "east", "metre"), AxisObject("Northing", "N", "north", "metre"))), + ("scope", "Engineering survey, topographic mapping."), + ("area", "United Kingdom."), + ("bbox", Obj(("south_latitude", 49.75d), ("west_longitude", -9.01d), ("north_latitude", 61.01d), ("east_longitude", 2.01d))), + ("remarks", "metadata remark"), + ("usages", usages), + ("id", IdObject("EPSG", 27700)))); + + ProjectedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(27700)); + + Assert.True(parsed.EqualParams(reference)); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(27700, parsed.AuthorityCode); + } + + /// + /// Verifies geographic PROJJSON datum_ensemble objects retain ensemble metadata on the parsed datum. + /// + [Fact] + public void Parse_WithGeographicDatumEnsemble_RetainsEnsembleMetadata() + { + object[] members = + [ + DatumEnsembleMemberObject("World Geodetic System 1984 (Transit)", "EPSG", 1166), + DatumEnsembleMemberObject("World Geodetic System 1984 (G730)", "EPSG", 1152), + ]; + + string json = Serialize( + Obj( + ("type", "GeographicCRS"), + ("name", "WGS 84"), + ("datum_ensemble", DatumEnsembleObject("World Geodetic System 1984 ensemble", members, "2", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, "metre", 7030), IdObject("EPSG", 6326))), + ("coordinate_system", CoordinateSystemObject("ellipsoidal", AxisObject("Geodetic latitude", "Lat", "north", "degree"), AxisObject("Geodetic longitude", "Lon", "east", "degree"))), + ("id", IdObject("EPSG", 4326)))); + + GeographicCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + DatumEnsemble ensemble = Assert.IsType(parsed.HorizontalDatum.Ensemble); + + Assert.Equal("World Geodetic System 1984 ensemble", parsed.HorizontalDatum.Name); + Assert.Equal("EPSG", parsed.HorizontalDatum.Authority); + Assert.Equal(6326, parsed.HorizontalDatum.AuthorityCode); + Assert.Equal(2, ensemble.Members.Count); + Assert.Equal(2d, ensemble.Accuracy); + Assert.NotNull(ensemble.Ellipsoid); + Assert.True(parsed.HorizontalDatum.EqualParams(HorizontalDatum.WGS84)); + } + + /// + /// Verifies ETRS89-style PROJJSON datum_ensemble objects retain their identifier and ellipsoid metadata. + /// + [Fact] + public void Parse_WithEtrs89DatumEnsemble_RetainsIdentifierAndEllipsoid() + { + object[] members = + [ + DatumEnsembleMemberObject("European Terrestrial Reference Frame 1989", "EPSG", 1178), + DatumEnsembleMemberObject("European Terrestrial Reference Frame 1990", "EPSG", 1179), + ]; + + string json = Serialize( + Obj( + ("type", "GeographicCRS"), + ("name", "ETRS89"), + ("datum_ensemble", DatumEnsembleObject("European Terrestrial Reference System 1989 ensemble", members, "0.1", EllipsoidObject("GRS 1980", 6378137d, 298.257222101d, "metre", 7019), IdObject("EPSG", 6258))), + ("coordinate_system", CoordinateSystemObject("ellipsoidal", AxisObject("Geodetic latitude", "Lat", "north", "degree"), AxisObject("Geodetic longitude", "Lon", "east", "degree"))), + ("id", IdObject("EPSG", 4258)))); + + GeographicCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + DatumEnsemble ensemble = Assert.IsType(parsed.HorizontalDatum.Ensemble); + + Assert.Equal("European Terrestrial Reference System 1989 ensemble", parsed.HorizontalDatum.Name); + Assert.Equal(6258, parsed.HorizontalDatum.AuthorityCode); + Assert.Equal(0.1d, ensemble.Accuracy); + Assert.NotNull(ensemble.Ellipsoid); + Assert.Equal("GRS 1980", Assert.IsType(ensemble.Ellipsoid).Name); + } + + /// + /// Verifies vertical PROJJSON datum_ensemble objects retain ensemble metadata without requiring an ellipsoid. + /// + [Fact] + public void Parse_WithVerticalDatumEnsemble_RetainsEnsembleMetadata() + { + object[] members = + [ + DatumEnsembleMemberObject("Datum A", "TEST", 1), + DatumEnsembleMemberObject("Datum B", "TEST", 2), + ]; + + string json = Serialize( + Obj( + ("type", "VerticalCRS"), + ("name", "Example ensemble height"), + ("datum_ensemble", DatumEnsembleObject("Example vertical ensemble", members, "0.05", null, IdObject("TEST", 1001))), + ("coordinate_system", CoordinateSystemObject("vertical", AxisObject("Gravity-related height", "H", "up", "metre"))), + ("id", IdObject("TEST", 2001)))); + + VerticalCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + DatumEnsemble ensemble = Assert.IsType(parsed.VerticalDatum.Ensemble); + + Assert.Equal("Example vertical ensemble", parsed.VerticalDatum.Name); + Assert.Equal("TEST", parsed.VerticalDatum.Authority); + Assert.Equal(1001, parsed.VerticalDatum.AuthorityCode); + Assert.Equal(2, ensemble.Members.Count); + Assert.Equal(0.05d, ensemble.Accuracy); + Assert.Null(ensemble.Ellipsoid); + } + + /// + /// Verifies derived geographic PROJJSON objects map onto with preserved axis metadata. + /// + [Fact] + public void Parse_WithDerivedGeographicCrs_ReturnsFittedCoordinateSystem() + { + string json = Serialize( + DerivedGeographicCrsObject( + 2001, + "Local WGS 84", + GeographicCrsObject( + 4326, + "WGS 84", + GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, "metre", 7030), 6326), + GreenwichPrimeMeridianObject(), + "degree", + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + AffineConversionObject("unnamed", "degree", 0.5d, 1d, 0d, 1.5d, 0d, 1d), + "degree", + ("Local latitude", "Lat", "north"), + ("Local longitude", "Lon", "east"))); + + FittedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + GeographicCoordinateSystem baseCoordinateSystem = Assert.IsType(parsed.BaseCoordinateSystem); + + Assert.Equal("Local WGS 84", parsed.Name); + Assert.Equal("TEST", parsed.Authority); + Assert.Equal(2001, parsed.AuthorityCode); + Assert.Equal("WGS 84", baseCoordinateSystem.Name); + Assert.Equal("Local latitude", parsed.GetAxis(0).Name); + Assert.Equal("Local longitude", parsed.GetAxis(1).Name); + Assert.StartsWith("PARAM_MT[\"Affine\"", parsed.ToBase(), StringComparison.Ordinal); + } + + /// + /// Verifies derived projected PROJJSON objects map onto with a projected base CRS. + /// + [Fact] + public void Parse_WithDerivedProjectedCrs_ReturnsFittedCoordinateSystem() + { + string json = Serialize( + DerivedProjectedCrsObject( + 3001, + "Local projected", + ProjectedCrsObject( + 32632, + "WGS 84 / UTM zone 32N", + GeographicCrsObject( + 4326, + "WGS 84", + GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, "metre", 7030), 6326), + GreenwichPrimeMeridianObject(), + "degree", + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "east")), + ConversionObject( + "UTM zone 32N", + "Transverse Mercator", + 16032, + ProjectionParameterObject("Latitude of natural origin", 0d, "degree", 8801), + ProjectionParameterObject("Longitude of natural origin", 9d, "degree", 8802), + ProjectionParameterObject("Scale factor at natural origin", 0.9996d, ScaleUnitObject(), 8805), + ProjectionParameterObject("False easting", 500000d, "metre", 8806), + ProjectionParameterObject("False northing", 0d, "metre", 8807)), + "metre", + ("Easting", "E", "east"), + ("Northing", "N", "north")), + AffineConversionObject("unnamed", "metre", 100d, 1d, 0d, -50d, 0d, 1d), + "metre", + ("Local easting", "X", "east"), + ("Local northing", "Y", "north"))); + + FittedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + ProjectedCoordinateSystem baseCoordinateSystem = Assert.IsType(parsed.BaseCoordinateSystem); + + Assert.Equal("Local projected", parsed.Name); + Assert.Equal("TEST", parsed.Authority); + Assert.Equal(3001, parsed.AuthorityCode); + Assert.Equal("WGS 84 / UTM zone 32N", baseCoordinateSystem.Name); + Assert.Equal("Local easting", parsed.GetAxis(0).Name); + Assert.Equal("Local northing", parsed.GetAxis(1).Name); + Assert.StartsWith("PARAM_MT[\"Affine\"", parsed.ToBase(), StringComparison.Ordinal); + } + + /// + /// Verifies representative unsupported top-level PROJJSON types remain explicit reader boundaries. + /// + /// The unsupported PROJJSON type value. + [Theory] + [MemberData(nameof(UnsupportedTopLevelTypeRows))] + public void Parse_WithUnsupportedTopLevelType_ThrowsNotSupportedException(string type) + { + string json = Serialize(Obj(("type", type), ("name", "Unsupported test object"))); + + NotSupportedException exception = Assert.Throws(() => ProjJsonReader.Parse(json)); + + Assert.Contains(type, exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies non-object PROJJSON roots report the public json parameter. + /// + [Fact] + public void Parse_WithNonObjectRoot_ThrowsArgumentExceptionForJson() + { + ArgumentException exception = Assert.Throws(() => ProjJsonReader.Parse("[]")); + + Assert.Equal("json", exception.ParamName); + } + + /// + /// Verifies missing required top-level members report the parsed element parameter. + /// + [Fact] + public void Parse_WithMissingType_ThrowsArgumentExceptionForElement() + { + string json = Serialize(Obj(("name", "Missing type"))); + + ArgumentException exception = Assert.Throws(() => ProjJsonReader.Parse(json)); + + Assert.Equal("element", exception.ParamName); + } + + /// + /// Verifies invalid axis direction tokens report the orientation-token helper parameter. + /// + [Fact] + public void Parse_WithInvalidAxisOrientation_ThrowsArgumentExceptionForOrientationToken() + { + string json = Serialize( + GeographicCrsObject( + 4326, + "WGS 84", + GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, "metre", 7030), 6326), + GreenwichPrimeMeridianObject(), + "degree", + ("Geodetic latitude", "Lat", "north"), + ("Geodetic longitude", "Lon", "sideways"))); + + ArgumentException exception = Assert.Throws(() => ProjJsonReader.Parse(json)); + + Assert.Equal("orientationToken", exception.ParamName); + } + + /// + /// Verifies inconsistent angular units report the conflicting candidate unit parameter. + /// + [Fact] + public void Parse_WithMismatchedAxisUnits_ThrowsArgumentExceptionForCandidate() + { + object[] axes = + [ + AxisObject("Geodetic latitude", "Lat", "north", AngularUnitObject("degree", 0.0174532925199433d, 9122)), + AxisObject("Geodetic longitude", "Lon", "east", AngularUnitObject("grad", 0.015707963267949d, 9105)), + ]; + + Dictionary coordinateSystem = Obj( + ("subtype", "ellipsoidal"), + ("axis", axes)); + + string json = Serialize( + Obj( + ("type", "GeographicCRS"), + ("name", "WGS 84"), + ("datum", GeodeticDatumObject("World Geodetic System 1984", EllipsoidObject("WGS 84", 6378137d, 298.257223563d, "metre", 7030), 6326)), + ("prime_meridian", GreenwichPrimeMeridianObject()), + ("coordinate_system", coordinateSystem), + ("id", IdObject("EPSG", 4326)))); + + ArgumentException exception = Assert.Throws(() => ProjJsonReader.Parse(json)); + + Assert.Equal("candidate", exception.ParamName); + } + + private static string GetCatalogWkt(int srid) + { + if (!CatalogDefinitions.Value.TryGetValue(srid, out string? wkt)) + { + throw new InvalidOperationException($"No catalog definition found for EPSG:{srid}."); + } + + return wkt; + } + + private static string Serialize(object value) => JsonSerializer.Serialize(value); + + private static Dictionary GeographicCrsObject( + int srid, + string name, + Dictionary datum, + Dictionary primeMeridian, + object angularUnit, + (string Name, string Abbreviation, string Direction) axis1, + (string Name, string Abbreviation, string Direction) axis2, + bool useIds = false) + { + Dictionary coordinateSystem = CoordinateSystemObject( + "ellipsoidal", + AxisObject(axis1.Name, axis1.Abbreviation, axis1.Direction, angularUnit), + AxisObject(axis2.Name, axis2.Abbreviation, axis2.Direction, angularUnit)); + + if (useIds) + { + object[] ids = + [ + IdObject("ESRI", $"GCS_{name.Replace(" ", "_", StringComparison.Ordinal)}"), + IdObject("EPSG", srid), + ]; + + return Obj( + ("type", "GeographicCRS"), + ("name", name), + ("datum", datum), + ("prime_meridian", primeMeridian), + ("coordinate_system", coordinateSystem), + ("ids", ids)); + } + + return Obj( + ("type", "GeographicCRS"), + ("name", name), + ("datum", datum), + ("prime_meridian", primeMeridian), + ("coordinate_system", coordinateSystem), + ("id", IdObject("EPSG", srid))); + } + + private static Dictionary ProjectedCrsObject( + int srid, + string name, + Dictionary baseCrs, + Dictionary conversion, + object linearUnit, + (string Name, string Abbreviation, string Direction) axis1, + (string Name, string Abbreviation, string Direction) axis2) + { + Dictionary coordinateSystem = CoordinateSystemObject( + "Cartesian", + AxisObject(axis1.Name, axis1.Abbreviation, axis1.Direction, linearUnit), + AxisObject(axis2.Name, axis2.Abbreviation, axis2.Direction, linearUnit)); + + return Obj( + ("type", "ProjectedCRS"), + ("name", name), + ("base_crs", baseCrs), + ("conversion", conversion), + ("coordinate_system", coordinateSystem), + ("id", IdObject("EPSG", srid))); + } + + private static Dictionary DerivedGeographicCrsObject( + int code, + string name, + Dictionary baseCrs, + Dictionary conversion, + object angularUnit, + (string Name, string Abbreviation, string Direction) axis1, + (string Name, string Abbreviation, string Direction) axis2) + { + Dictionary coordinateSystem = CoordinateSystemObject( + "ellipsoidal", + AxisObject(axis1.Name, axis1.Abbreviation, axis1.Direction, angularUnit), + AxisObject(axis2.Name, axis2.Abbreviation, axis2.Direction, angularUnit)); + + return Obj( + ("type", "DerivedGeographicCRS"), + ("name", name), + ("base_crs", baseCrs), + ("conversion", conversion), + ("coordinate_system", coordinateSystem), + ("id", IdObject("TEST", code))); + } + + private static Dictionary DerivedProjectedCrsObject( + int code, + string name, + Dictionary baseCrs, + Dictionary conversion, + object linearUnit, + (string Name, string Abbreviation, string Direction) axis1, + (string Name, string Abbreviation, string Direction) axis2) + { + Dictionary coordinateSystem = CoordinateSystemObject( + "Cartesian", + AxisObject(axis1.Name, axis1.Abbreviation, axis1.Direction, linearUnit), + AxisObject(axis2.Name, axis2.Abbreviation, axis2.Direction, linearUnit)); + + return Obj( + ("type", "DerivedProjectedCRS"), + ("name", name), + ("base_crs", baseCrs), + ("conversion", conversion), + ("coordinate_system", coordinateSystem), + ("id", IdObject("TEST", code))); + } + + private static Dictionary GeocentricCrsObject( + int srid, + string name, + Dictionary datum, + Dictionary primeMeridian, + object linearUnit) + { + Dictionary coordinateSystem = CoordinateSystemObject( + "Cartesian", + AxisObject("Geocentric X", "X", "geocentricX", linearUnit), + AxisObject("Geocentric Y", "Y", "geocentricY", linearUnit), + AxisObject("Geocentric Z", "Z", "geocentricZ", linearUnit)); + + return Obj( + ("type", "GeodeticCRS"), + ("name", name), + ("datum", datum), + ("prime_meridian", primeMeridian), + ("coordinate_system", coordinateSystem), + ("id", IdObject("EPSG", srid))); + } + + private static Dictionary VerticalCrsObject( + int srid, + string name, + Dictionary datum, + object linearUnit, + string axisName, + string axisDirection) + { + return Obj( + ("type", "VerticalCRS"), + ("name", name), + ("datum", datum), + ("coordinate_system", CoordinateSystemObject("vertical", AxisObject(axisName, "H", axisDirection, linearUnit))), + ("id", IdObject("EPSG", srid))); + } + + private static Dictionary DatumEnsembleObject( + string name, + object[] members, + string accuracy, + Dictionary? ellipsoid, + Dictionary? id) + { + Dictionary datumEnsemble = Obj( + ("type", "DatumEnsemble"), + ("name", name), + ("members", members), + ("accuracy", accuracy)); + + if (ellipsoid is not null) + { + datumEnsemble["ellipsoid"] = ellipsoid; + } + + if (id is not null) + { + datumEnsemble["id"] = id; + } + + return datumEnsemble; + } + + private static Dictionary DatumEnsembleMemberObject(string name, string authority, int code) + { + return Obj(("name", name), ("id", IdObject(authority, code))); + } + + private static Dictionary BoundCrsObject( + int srid, + string name, + Dictionary sourceCrs, + Dictionary targetCrs, + Dictionary transformation) + { + return Obj( + ("type", "BoundCRS"), + ("name", name), + ("source_crs", sourceCrs), + ("target_crs", targetCrs), + ("transformation", transformation), + ("id", IdObject("EPSG", srid))); + } + + private static Dictionary CompoundCrsObject(int srid, string name, params Dictionary[] components) + { + return Obj( + ("type", "CompoundCRS"), + ("name", name), + ("components", components.Cast().ToArray()), + ("id", IdObject("EPSG", srid))); + } + + private static Dictionary GeodeticDatumObject(string name, Dictionary ellipsoid, int code) + { + return Obj( + ("type", "GeodeticReferenceFrame"), + ("name", name), + ("ellipsoid", ellipsoid), + ("id", IdObject("EPSG", code))); + } + + private static Dictionary VerticalDatumObject(string name, int code) + { + return Obj( + ("type", "VerticalReferenceFrame"), + ("name", name), + code > 0 ? ("id", IdObject("EPSG", code)) : ("id", null)); + } + + private static Dictionary EllipsoidObject(string name, double semiMajorAxis, double inverseFlattening, object unit, int code) + { + return Obj( + ("type", "Ellipsoid"), + ("name", name), + ("semi_major_axis", semiMajorAxis), + ("inverse_flattening", inverseFlattening), + ("unit", unit), + ("id", IdObject("EPSG", code))); + } + + private static Dictionary PrimeMeridianObject(string name, double longitude, object unit, int code, bool useIds = false, bool stringCode = false) + { + object codeValue = stringCode ? code.ToString(CultureInfo.InvariantCulture) : code; + return useIds + ? Obj( + ("name", name), + ("longitude", longitude), + ("unit", unit), + ("ids", new object[] { IdObject("IGNF", name.ToUpperInvariant()), IdObject("EPSG", codeValue) })) + : Obj( + ("name", name), + ("longitude", longitude), + ("unit", unit), + ("id", IdObject("EPSG", codeValue))); + } + + private static Dictionary GreenwichPrimeMeridianObject() => PrimeMeridianObject("Greenwich", 0d, "degree", 8901); + + private static Dictionary CoordinateSystemObject(string subtype, params Dictionary[] axes) + { + return Obj( + ("subtype", subtype), + ("axis", axes.Cast().ToArray())); + } + + private static Dictionary AxisObject(string name, string abbreviation, string direction, object unit) + { + return Obj( + ("name", name), + ("abbreviation", abbreviation), + ("direction", direction), + ("unit", unit)); + } + + private static Dictionary ConversionObject(string name, string methodName, int conversionCode, params Dictionary[] parameters) + { + return Obj( + ("type", "Conversion"), + ("name", name), + ("method", Obj(("name", methodName))), + ("parameters", parameters.Cast().ToArray()), + ("id", IdObject("EPSG", conversionCode))); + } + + private static Dictionary AffineConversionObject(string name, object translationUnit, double a0, double a1, double a2, double b0, double b1, double b2) + { + return ConversionObject( + name, + "Affine parametric transformation", + 9624, + ProjectionParameterObject("A0", a0, translationUnit, 8623), + ProjectionParameterObject("A1", a1, ScaleUnitObject(), 8624), + ProjectionParameterObject("A2", a2, ScaleUnitObject(), 8625), + ProjectionParameterObject("B0", b0, translationUnit, 8639), + ProjectionParameterObject("B1", b1, ScaleUnitObject(), 8640), + ProjectionParameterObject("B2", b2, ScaleUnitObject(), 8641)); + } + + private static Dictionary AbridgedTransformationObject(string name, string methodName, params Dictionary[] parameters) + { + return Obj( + ("type", "AbridgedTransformation"), + ("name", name), + ("method", Obj(("name", methodName))), + ("parameters", parameters.Cast().ToArray())); + } + + private static Dictionary ProjectionParameterObject(string name, double value, object unit, int code) + { + return Obj( + ("name", name), + ("value", value), + ("unit", unit), + ("id", IdObject("EPSG", code))); + } + + private static Dictionary BoundParameterValueObject(string name, object value) + { + return Obj( + ("name", name), + ("value", value)); + } + + private static Dictionary AngularUnitObject(string name, double conversionFactor, int code) + { + return Obj( + ("type", "AngularUnit"), + ("name", name), + ("conversion_factor", conversionFactor), + ("id", IdObject("EPSG", code))); + } + + private static Dictionary ScaleUnitObject() + { + return Obj( + ("type", "ScaleUnit"), + ("name", "unity"), + ("conversion_factor", 1d), + ("id", IdObject("EPSG", 9201))); + } + + private static Dictionary IdObject(string authority, object code) + { + return Obj( + ("authority", authority), + ("code", code)); + } + + private static Dictionary Obj(params (string Key, object? Value)[] members) + { + var result = new Dictionary(StringComparer.Ordinal); + foreach ((string key, object? value) in members) + { + if (value is not null) + { + result[key] = value; + } + } + + return result; + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonWriterTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonWriterTests.cs new file mode 100644 index 00000000..e087c6d5 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/ProjJsonWriterTests.cs @@ -0,0 +1,1012 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Verifies native PROJJSON coordinate-system writing for the currently supported CRS slices. +/// +public class ProjJsonWriterTests +{ + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly Lazy> CatalogDefinitions = new(() => + new ManagedCoordinateSystemDefinitionProvider() + .GetDefinitions() + .GroupBy(item => item.Srid) + .ToDictionary(group => group.Key, group => group.Last().Wkt)); + + private static readonly IReadOnlyDictionary CreateSample)> WriterTypeCoverage = new Dictionary CreateSample)> + { + [typeof(BoundCoordinateSystem)] = (true, CreateSupportedBoundCoordinateSystem), + [typeof(CompoundCoordinateSystem)] = (true, () => CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, GetCatalogWkt(9518))), + [typeof(EngineeringCoordinateSystem)] = (false, CreateUnsupportedEngineeringCoordinateSystem), + [typeof(FittedCoordinateSystem)] = (true, CreateDerivedGeographicCoordinateSystem), + [typeof(GeocentricCoordinateSystem)] = (true, () => CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, GetCatalogWkt(4978))), + [typeof(GeographicCoordinateSystem)] = (true, () => CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, GetCatalogWkt(4230))), + [typeof(ParametricCoordinateSystem)] = (false, CreateUnsupportedParametricCoordinateSystem), + [typeof(ProjectedCoordinateSystem)] = (true, () => CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, GetCatalogWkt(27700))), + [typeof(TemporalCoordinateSystem)] = (false, CreateUnsupportedTemporalCoordinateSystem), + [typeof(VerticalCoordinateSystem)] = (true, () => CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, GetCatalogWkt(5701))), + }; + + /// + /// Provides EPSG geographic CRS examples that should roundtrip through the initial PROJJSON writer slice. + /// + /// EPSG SRIDs for supported geographic CRS roundtrip coverage. + public static IEnumerable> SupportedGeographicWriterRows() + { + return + [ + new TheoryDataRow(4230), + new TheoryDataRow(4277), + new TheoryDataRow(4314), + new TheoryDataRow(4322), + new TheoryDataRow(4807), + ]; + } + + /// + /// Provides EPSG projected CRS examples that should roundtrip through the projected PROJJSON writer slice. + /// + /// EPSG SRIDs for supported projected CRS roundtrip coverage. + public static IEnumerable> SupportedProjectedWriterRows() + { + return + [ + new TheoryDataRow(27700), + new TheoryDataRow(31370), + new TheoryDataRow(2169), + new TheoryDataRow(23032), + new TheoryDataRow(31467), + ]; + } + + /// + /// Provides EPSG geocentric, vertical, and compound CRS examples that should roundtrip through the remaining PROJJSON writer slice. + /// + /// EPSG SRIDs for supported remaining CRS roundtrip coverage. + public static IEnumerable> SupportedRemainingWriterRows() + { + return + [ + new TheoryDataRow(4978, typeof(GeocentricCoordinateSystem)), + new TheoryDataRow(5701, typeof(VerticalCoordinateSystem)), + new TheoryDataRow(9518, typeof(CompoundCoordinateSystem)), + ]; + } + + /// + /// Provides EPSG CRS examples that should survive a WKT1 -> PROJJSON -> WKT1 cross-format roundtrip. + /// + /// EPSG SRIDs for supported cross-format writer coverage. + public static IEnumerable> SupportedCrossFormatWriterRows() + { + return + [ + new TheoryDataRow(4230), + new TheoryDataRow(4277), + new TheoryDataRow(4314), + new TheoryDataRow(4322), + new TheoryDataRow(4807), + new TheoryDataRow(27700), + new TheoryDataRow(31370), + new TheoryDataRow(2169), + new TheoryDataRow(23032), + new TheoryDataRow(31467), + new TheoryDataRow(4978), + new TheoryDataRow(5701), + new TheoryDataRow(9518), + ]; + } + + /// + /// Discovers all public concrete types for PROJJSON writer coverage. + /// + /// The discovered coordinate-system runtime types. + public static IEnumerable> ConcreteCoordinateSystemWriterRows() + { + return GetConcreteCoordinateSystemWriterTypes() + .Select(type => new TheoryDataRow(type)); + } + + /// + /// Verifies the initial PROJJSON writer slice roundtrips supported geographic CRS back to the same semantic model. + /// + /// Expected EPSG SRID. + [Theory] + [MemberData(nameof(SupportedGeographicWriterRows))] + public void ToJson_RoundtripsSupportedGeographicCrsEquivalentToCatalogReference(int srid) + { + GeographicCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + string json = ProjJsonWriter.ToJson(reference); + GeographicCoordinateSystem reparsed = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.True(reparsed.EqualParams(reference), $"PROJJSON geographic CRS write/read mismatch for EPSG:{srid}."); + Assert.Equal(reference.Authority, reparsed.Authority); + Assert.Equal(reference.AuthorityCode, reparsed.AuthorityCode); + } + + /// + /// Verifies the projected PROJJSON writer slice roundtrips supported projected CRS back to the same semantic model. + /// + /// Expected EPSG SRID. + [Theory] + [MemberData(nameof(SupportedProjectedWriterRows))] + public void ToJson_RoundtripsSupportedProjectedCrsEquivalentToCatalogReference(int srid) + { + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + string json = ProjJsonWriter.ToJson(reference); + ProjectedCoordinateSystem reparsed = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.True(reparsed.EqualParams(reference), $"PROJJSON projected CRS write/read mismatch for EPSG:{srid}."); + Assert.Equal(reference.Authority, reparsed.Authority); + Assert.Equal(reference.AuthorityCode, reparsed.AuthorityCode); + } + + /// + /// Verifies the remaining PROJJSON writer slice roundtrips supported non-projected CRS back to the same semantic model. + /// + /// Expected EPSG SRID. + /// Expected coordinate-system runtime type. + [Theory] + [MemberData(nameof(SupportedRemainingWriterRows))] + public void ToJson_RoundtripsSupportedRemainingCrsEquivalentToCatalogReference(int srid, Type expectedType) + { + CoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + string json = ProjJsonWriter.ToJson(reference); + CoordinateSystem reparsed = Assert.IsAssignableFrom(ProjJsonReader.Parse(json)); + Assert.IsType(expectedType, reparsed); + + Assert.True(reparsed.EqualParams(reference), $"PROJJSON remaining CRS write/read mismatch for EPSG:{srid}."); + Assert.Equal(reference.Authority, reparsed.Authority); + Assert.Equal(reference.AuthorityCode, reparsed.AuthorityCode); + } + + /// + /// Verifies supported CRS survive a WKT1 -> PROJJSON -> WKT1 cross-format roundtrip without semantic drift. + /// + /// Expected EPSG SRID. + [Theory] + [MemberData(nameof(SupportedCrossFormatWriterRows))] + public void ToJson_RoundtripsSupportedCrsAcrossWkt1AndProjJsonWithoutSemanticDrift(int srid) + { + CoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + string json = ProjJsonWriter.ToJson(reference); + CoordinateSystem fromProjJson = Assert.IsAssignableFrom(ProjJsonReader.Parse(json)); + CoordinateSystem roundTrippedFromWkt = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + fromProjJson.WKT); + + Assert.IsType(fromProjJson.GetType(), roundTrippedFromWkt); + Assert.True(fromProjJson.EqualParams(roundTrippedFromWkt), $"WKT1 cross-format roundtrip mismatch for EPSG:{srid}."); + Assert.True(reference.EqualParams(roundTrippedFromWkt), $"Reference mismatch after WKT1 -> PROJJSON -> WKT1 roundtrip for EPSG:{srid}."); + Assert.Equal(reference.Authority, roundTrippedFromWkt.Authority); + Assert.Equal(reference.AuthorityCode, roundTrippedFromWkt.AuthorityCode); + } + + /// + /// Verifies emits the expected PROJJSON object shape for a geographic CRS. + /// + [Fact] + public void WriteTo_WritesGeographicCrsWithExpectedProjJsonShape() + { + GeographicCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(4807)); + + string json; + using (var stream = new MemoryStream()) + { + using (var writer = new Utf8JsonWriter(stream)) + { + ProjJsonWriter.WriteTo(writer, reference); + writer.Flush(); + } + + json = Encoding.UTF8.GetString(stream.ToArray()); + } + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + + Assert.Equal("GeographicCRS", root.GetProperty("type").GetString()); + Assert.Equal("NTF (Paris)", root.GetProperty("name").GetString()); + Assert.Equal("Nouvelle Triangulation Francaise (Paris)", root.GetProperty("datum").GetProperty("name").GetString()); + Assert.Equal("Clarke 1880 (IGN)", root.GetProperty("datum").GetProperty("ellipsoid").GetProperty("name").GetString()); + Assert.Equal("Paris", root.GetProperty("prime_meridian").GetProperty("name").GetString()); + Assert.Equal("ellipsoidal", root.GetProperty("coordinate_system").GetProperty("subtype").GetString()); + Assert.Equal(2, root.GetProperty("coordinate_system").GetProperty("axis").GetArrayLength()); + Assert.Equal("EPSG", root.GetProperty("id").GetProperty("authority").GetString()); + Assert.Equal(4807, root.GetProperty("id").GetProperty("code").GetInt32()); + } + + /// + /// Verifies emits the expected PROJJSON object shape for a projected CRS. + /// + [Fact] + public void WriteTo_WritesProjectedCrsWithExpectedProjJsonShape() + { + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(27700)); + + string json; + using (var stream = new MemoryStream()) + { + using (var writer = new Utf8JsonWriter(stream)) + { + ProjJsonWriter.WriteTo(writer, reference); + writer.Flush(); + } + + json = Encoding.UTF8.GetString(stream.ToArray()); + } + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + JsonElement parameters = root.GetProperty("conversion").GetProperty("parameters"); + JsonElement scaleFactorParameter = parameters.EnumerateArray() + .Single(element => string.Equals(element.GetProperty("name").GetString(), "Scale factor at natural origin", StringComparison.Ordinal)); + + Assert.Equal("ProjectedCRS", root.GetProperty("type").GetString()); + Assert.Equal("OSGB36 / British National Grid", root.GetProperty("name").GetString()); + Assert.Equal("GeographicCRS", root.GetProperty("base_crs").GetProperty("type").GetString()); + Assert.Equal("Conversion", root.GetProperty("conversion").GetProperty("type").GetString()); + Assert.Equal("Transverse Mercator", root.GetProperty("conversion").GetProperty("method").GetProperty("name").GetString()); + Assert.Equal(5, parameters.GetArrayLength()); + Assert.Equal("ScaleUnit", scaleFactorParameter.GetProperty("unit").GetProperty("type").GetString()); + Assert.Equal("Cartesian", root.GetProperty("coordinate_system").GetProperty("subtype").GetString()); + Assert.Equal("EPSG", root.GetProperty("id").GetProperty("authority").GetString()); + Assert.Equal(27700, root.GetProperty("id").GetProperty("code").GetInt32()); + } + + /// + /// Verifies null JSON writers are rejected. + /// + [Fact] + public void WriteTo_WithNullWriter_ThrowsArgumentNullException() + { + GeographicCoordinateSystem reference = GeographicCoordinateSystem.WGS84; + + Assert.Throws(() => ProjJsonWriter.WriteTo(null!, reference)); + } + + /// + /// Verifies null coordinate systems are rejected by . + /// + [Fact] + public void WriteTo_WithNullCoordinateSystem_ThrowsArgumentNullException() + { + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + Assert.Throws(() => ProjJsonWriter.WriteTo(writer, null!)); + } + + /// + /// Verifies null coordinate systems are rejected by . + /// + [Fact] + public void ToJson_WithNullCoordinateSystem_ThrowsArgumentNullException() + { + Assert.Throws(() => ProjJsonWriter.ToJson(null!)); + } + + /// + /// Verifies delegates to for supported CRS shapes. + /// + /// Expected EPSG SRID. + [Theory] + [MemberData(nameof(SupportedCrossFormatWriterRows))] + public void ToProjJson_MatchesProjJsonWriterForSupportedCoordinateSystems(int srid) + { + CoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + CoordinateSystemFactory, + GetCatalogWkt(srid)); + + Assert.Equal(ProjJsonWriter.ToJson(reference), reference.ToProjJson()); + } + + /// + /// Verifies the PROJJSON writer coverage map stays in lockstep with the public concrete coordinate-system model types. + /// + [Fact] + public void WriterTypeCoverage_MatchesConcreteCoordinateSystemTypes() + { + string[] discoveredTypes = GetConcreteCoordinateSystemWriterTypes() + .Select(type => type.FullName!) + .ToArray(); + string[] coveredTypes = WriterTypeCoverage.Keys + .OrderBy(type => type.FullName, StringComparer.Ordinal) + .Select(type => type.FullName!) + .ToArray(); + + Assert.Equal(discoveredTypes, coveredTypes); + } + + /// + /// Verifies every public concrete coordinate-system type is explicitly classified as supported or unsupported for PROJJSON writing. + /// + /// The concrete coordinate-system runtime type under test. + [Theory] + [MemberData(nameof(ConcreteCoordinateSystemWriterRows))] + public void ToJson_ConcreteCoordinateSystemTypesRemainExplicitlyClassified(Type coordinateSystemType) + { + ArgumentNullException.ThrowIfNull(coordinateSystemType); + + Assert.True( + WriterTypeCoverage.ContainsKey(coordinateSystemType), + $"Add a PROJJSON writer coverage entry for coordinate-system type '{coordinateSystemType.FullName}'."); + + (bool shouldSerialize, Func createSample) = WriterTypeCoverage[coordinateSystemType]; + CoordinateSystem sample = createSample(); + Assert.Equal(coordinateSystemType, sample.GetType()); + + Exception? exception = Record.Exception(() => _ = ProjJsonWriter.ToJson(sample)); + + if (shouldSerialize) + { + Assert.Null(exception); + return; + } + + NotSupportedException notSupportedException = Assert.IsType(exception); + Assert.Contains(coordinateSystemType.Name, notSupportedException.Message, StringComparison.Ordinal); + } + + /// + /// Verifies affine fitted coordinate systems with geographic bases serialize as PROJJSON derived geographic CRS objects. + /// + [Fact] + public void ToJson_WithDerivedGeographicCoordinateSystem_EmitsDerivedGeographicCrs() + { + FittedCoordinateSystem fitted = CreateDerivedGeographicCoordinateSystem(); + + string json = ProjJsonWriter.ToJson(fitted); + FittedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + + Assert.Equal("DerivedGeographicCRS", root.GetProperty("type").GetString()); + Assert.Equal("GeographicCRS", root.GetProperty("base_crs").GetProperty("type").GetString()); + Assert.Equal("Affine parametric transformation", root.GetProperty("conversion").GetProperty("method").GetProperty("name").GetString()); + Assert.Equal("Local latitude", root.GetProperty("coordinate_system").GetProperty("axis")[0].GetProperty("name").GetString()); + Assert.Equal("Local longitude", root.GetProperty("coordinate_system").GetProperty("axis")[1].GetProperty("name").GetString()); + Assert.True(parsed.EqualParams(fitted)); + Assert.Equal("Local latitude", parsed.GetAxis(0).Name); + Assert.Equal("Local longitude", parsed.GetAxis(1).Name); + } + + /// + /// Verifies affine fitted coordinate systems with projected bases serialize as PROJJSON derived projected CRS objects. + /// + [Fact] + public void ToJson_WithDerivedProjectedCoordinateSystem_EmitsDerivedProjectedCrs() + { + FittedCoordinateSystem fitted = CreateDerivedProjectedCoordinateSystem(); + + string json = ProjJsonWriter.ToJson(fitted); + FittedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + + Assert.Equal("DerivedProjectedCRS", root.GetProperty("type").GetString()); + Assert.Equal("ProjectedCRS", root.GetProperty("base_crs").GetProperty("type").GetString()); + Assert.Equal("Affine parametric transformation", root.GetProperty("conversion").GetProperty("method").GetProperty("name").GetString()); + Assert.Equal("Local easting", root.GetProperty("coordinate_system").GetProperty("axis")[0].GetProperty("name").GetString()); + Assert.Equal("Local northing", root.GetProperty("coordinate_system").GetProperty("axis")[1].GetProperty("name").GetString()); + Assert.True(parsed.EqualParams(fitted)); + Assert.Equal("Local easting", parsed.GetAxis(0).Name); + Assert.Equal("Local northing", parsed.GetAxis(1).Name); + } + + /// + /// Verifies legacy WKT1 FITTED_CS geographic definitions survive the WKT2 derived-CRS and PROJJSON derived-CRS pipeline without semantic drift. + /// + [Fact] + public void ToJson_RoundtripsDerivedGeographicCrsAcrossWkt1Wkt2AndProjJson() + { + FittedCoordinateSystem original = CreateWkt1CompatibleDerivedGeographicCoordinateSystem(); + string wkt1 = original.WKT; + FittedCoordinateSystem fromWkt1 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt1); + + string wkt2 = fromWkt1.ToWktNode(WktVersion.Wkt22019).ToString(); + FittedCoordinateSystem fromWkt2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt2); + + string json = ProjJsonWriter.ToJson(fromWkt2); + FittedCoordinateSystem fromProjJson = Assert.IsType(ProjJsonReader.Parse(json)); + + using var document = JsonDocument.Parse(json); + + Assert.StartsWith("FITTED_CS[", wkt1, StringComparison.Ordinal); + Assert.StartsWith("GEOGCRS[", wkt2, StringComparison.Ordinal); + Assert.Equal("DerivedGeographicCRS", document.RootElement.GetProperty("type").GetString()); + Assert.True(fromProjJson.EqualParams(fromWkt2)); + AssertFittedCoordinateSystemSemanticsEqual(fromWkt1, fromProjJson); + AssertDerivedGeographicBaseSemanticsEqual( + Assert.IsType(fromWkt1.BaseCoordinateSystem), + Assert.IsType(fromProjJson.BaseCoordinateSystem)); + } + + /// + /// Verifies legacy WKT1 FITTED_CS projected definitions survive the WKT2 derived-CRS and PROJJSON derived-CRS pipeline without semantic drift. + /// + [Fact] + public void ToJson_RoundtripsDerivedProjectedCrsAcrossWkt1Wkt2AndProjJson() + { + FittedCoordinateSystem original = CreateWkt1CompatibleDerivedProjectedCoordinateSystem(); + string wkt1 = original.WKT; + FittedCoordinateSystem fromWkt1 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt1); + + string wkt2 = fromWkt1.ToWktNode(WktVersion.Wkt22019).ToString(); + FittedCoordinateSystem fromWkt2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt2); + + string json = ProjJsonWriter.ToJson(fromWkt2); + FittedCoordinateSystem fromProjJson = Assert.IsType(ProjJsonReader.Parse(json)); + + using var document = JsonDocument.Parse(json); + + Assert.StartsWith("FITTED_CS[", wkt1, StringComparison.Ordinal); + Assert.StartsWith("DERIVEDPROJCRS[", wkt2, StringComparison.Ordinal); + Assert.Equal("DerivedProjectedCRS", document.RootElement.GetProperty("type").GetString()); + Assert.True(fromProjJson.EqualParams(fromWkt2)); + AssertFittedCoordinateSystemSemanticsEqual(fromWkt1, fromProjJson); + AssertDerivedProjectedBaseSemanticsEqual( + Assert.IsType(fromWkt1.BaseCoordinateSystem), + Assert.IsType(fromProjJson.BaseCoordinateSystem)); + } + + /// + /// Verifies geographic CRS with retained WGS84 conversion metadata serialize as PROJJSON BoundCRS. + /// + [Fact] + public void ToJson_WithGeographicCoordinateSystemUsingBoundMetadata_EmitsBoundCrs() + { + GeographicCoordinateSystem geographic = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "ED50 test", + AngularUnit.Degrees, + HorizontalDatum.ED50, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + Wgs84ConversionInfo expectedParameters = Assert.IsType(geographic.HorizontalDatum.Wgs84Parameters); + + string json = ProjJsonWriter.ToJson(geographic); + BoundCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + GeographicCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + GeographicCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + + Assert.Equal("BoundCRS", root.GetProperty("type").GetString()); + Assert.Equal("GeographicCRS", root.GetProperty("source_crs").GetProperty("type").GetString()); + Assert.Equal("GeographicCRS", root.GetProperty("target_crs").GetProperty("type").GetString()); + Assert.Equal("AbridgedTransformation", root.GetProperty("transformation").GetProperty("type").GetString()); + Assert.Equal(parsed.Transformation.MethodName, root.GetProperty("transformation").GetProperty("method").GetProperty("name").GetString()); + Assert.Equal(geographic.Name, parsed.Name); + Assert.Equal(geographic.HorizontalDatum.Name, source.HorizontalDatum.Name); + Assert.Null(source.HorizontalDatum.Wgs84Parameters); + Assert.Equal(expectedParameters, parsed.Transformation.Wgs84Parameters); + Assert.Equal("WGS 84", target.Name); + } + + /// + /// Verifies projected CRS whose base datum retains WGS84 conversion metadata serialize as PROJJSON BoundCRS. + /// + [Fact] + public void ToJson_WithProjectedCoordinateSystemUsingBoundMetadata_EmitsBoundCrs() + { + GeographicCoordinateSystem geographic = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "ED50 test", + AngularUnit.Degrees, + HorizontalDatum.ED50, + PrimeMeridian.Greenwich, + new AxisInfo("Geodetic latitude (Lat)", AxisOrientationEnum.North), + new AxisInfo("Geodetic longitude (Lon)", AxisOrientationEnum.East)); + IProjection projection = CoordinateSystemFactory.CreateProjection( + "ED50 TM", + "Transverse_Mercator", + new List + { + new("latitude_of_origin", 0), + new("central_meridian", 9), + new("scale_factor", 0.9996), + new("false_easting", 500000), + new("false_northing", 0), + }); + ProjectedCoordinateSystem projected = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "ED50 / TM test", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("Easting", AxisOrientationEnum.East), + new AxisInfo("Northing", AxisOrientationEnum.North)); + Wgs84ConversionInfo expectedParameters = Assert.IsType(projected.GeographicCoordinateSystem.HorizontalDatum.Wgs84Parameters); + + string json = ProjJsonWriter.ToJson(projected); + BoundCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + ProjectedCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + GeographicCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + + Assert.Equal("BoundCRS", root.GetProperty("type").GetString()); + Assert.Equal("ProjectedCRS", root.GetProperty("source_crs").GetProperty("type").GetString()); + Assert.Equal("GeographicCRS", root.GetProperty("target_crs").GetProperty("type").GetString()); + Assert.Equal(projected.Name, parsed.Name); + Assert.Equal("Transverse Mercator", source.Projection.ClassName); + Assert.Equal(expectedParameters, parsed.Transformation.Wgs84Parameters); + Assert.Equal("WGS 84", target.Name); + } + + /// + /// Verifies vertical CRS with retained bound-grid metadata serialize as PROJJSON BoundCRS. + /// + [Fact] + public void ToJson_WithVerticalCoordinateSystemUsingBoundGridMetadata_EmitsBoundCrs() + { + VerticalCoordinateSystem vertical = CreateBoundVerticalCoordinateSystem(); + + string json = ProjJsonWriter.ToJson(vertical); + BoundCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + VerticalCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + CompoundCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + JsonElement parameters = root.GetProperty("transformation").GetProperty("parameters"); + + Assert.Equal("BoundCRS", root.GetProperty("type").GetString()); + Assert.Equal("VerticalCRS", root.GetProperty("source_crs").GetProperty("type").GetString()); + Assert.Equal("CompoundCRS", root.GetProperty("target_crs").GetProperty("type").GetString()); + Assert.Equal("AbridgedTransformation", root.GetProperty("transformation").GetProperty("type").GetString()); + Assert.Equal("Geographic3D to GravityRelatedHeight (EGM)", root.GetProperty("transformation").GetProperty("method").GetProperty("name").GetString()); + Assert.Equal(1, parameters.GetArrayLength()); + Assert.Equal("egm96_15.gtx", parameters[0].GetProperty("value").GetString()); + Assert.Equal(vertical.Name, parsed.Name); + Assert.Equal(vertical.VerticalDatum.Name, source.VerticalDatum.Name); + Assert.Null(source.BoundGridTransformation); + Assert.Equal("egm96_15.gtx", parsed.Transformation.ParameterFileName); + Assert.Equal(3, target.Dimension); + Assert.True(GeographicCoordinateSystem.WGS84.EqualParams(Assert.IsType(target.HeadCoordinateSystem))); + Assert.Equal("Ellipsoidal height datum", Assert.IsType(target.TailCoordinateSystem).VerticalDatum.Name); + } + + /// + /// Verifies geographic CRS with retained datum-ensemble metadata serialize as PROJJSON datum_ensemble. + /// + [Fact] + public void ToJson_WithGeographicCoordinateSystemUsingDatumEnsemble_EmitsDatumEnsemble() + { + GeographicCoordinateSystem geographic = CreateEnsembleBackedGeographicCoordinateSystem(); + DatumEnsemble expectedEnsemble = Assert.IsType(geographic.HorizontalDatum.Ensemble); + + string json = ProjJsonWriter.ToJson(geographic); + GeographicCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + + Assert.Equal("GeographicCRS", root.GetProperty("type").GetString()); + Assert.True(root.TryGetProperty("datum_ensemble", out JsonElement datumEnsemble)); + Assert.False(root.TryGetProperty("datum", out _)); + Assert.Equal(expectedEnsemble.Name, datumEnsemble.GetProperty("name").GetString()); + Assert.Equal(expectedEnsemble.Members.Count, datumEnsemble.GetProperty("members").GetArrayLength()); + AssertDatumEnsembleEqual(expectedEnsemble, Assert.IsType(parsed.HorizontalDatum.Ensemble)); + } + + /// + /// Verifies projected CRS with ensemble-backed base datums emit datum_ensemble on the nested base CRS. + /// + [Fact] + public void ToJson_WithProjectedCoordinateSystemUsingDatumEnsemble_EmitsDatumEnsembleOnBaseCrs() + { + ProjectedCoordinateSystem projected = CreateEnsembleBackedProjectedCoordinateSystem(); + DatumEnsemble expectedEnsemble = Assert.IsType(projected.GeographicCoordinateSystem.HorizontalDatum.Ensemble); + + string json = ProjJsonWriter.ToJson(projected); + ProjectedCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + JsonElement baseCrs = root.GetProperty("base_crs"); + + Assert.Equal("ProjectedCRS", root.GetProperty("type").GetString()); + Assert.True(baseCrs.TryGetProperty("datum_ensemble", out JsonElement datumEnsemble)); + Assert.False(baseCrs.TryGetProperty("datum", out _)); + Assert.Equal(expectedEnsemble.Name, datumEnsemble.GetProperty("name").GetString()); + AssertDatumEnsembleEqual(expectedEnsemble, Assert.IsType(parsed.GeographicCoordinateSystem.HorizontalDatum.Ensemble)); + } + + /// + /// Verifies vertical CRS with retained datum-ensemble metadata serialize as PROJJSON datum_ensemble. + /// + [Fact] + public void ToJson_WithVerticalCoordinateSystemUsingDatumEnsemble_EmitsDatumEnsemble() + { + VerticalCoordinateSystem vertical = CreateEnsembleBackedVerticalCoordinateSystem(); + DatumEnsemble expectedEnsemble = Assert.IsType(vertical.VerticalDatum.Ensemble); + + string json = ProjJsonWriter.ToJson(vertical); + VerticalCoordinateSystem parsed = Assert.IsType(ProjJsonReader.Parse(json)); + + using var document = JsonDocument.Parse(json); + JsonElement root = document.RootElement; + + Assert.Equal("VerticalCRS", root.GetProperty("type").GetString()); + Assert.True(root.TryGetProperty("datum_ensemble", out JsonElement datumEnsemble)); + Assert.False(root.TryGetProperty("datum", out _)); + Assert.Equal(expectedEnsemble.Name, datumEnsemble.GetProperty("name").GetString()); + AssertDatumEnsembleEqual(expectedEnsemble, Assert.IsType(parsed.VerticalDatum.Ensemble)); + } + + /// + /// Verifies WKT2 horizontal BOUNDCRS definitions survive a PROJJSON BoundCRS roundtrip without semantic drift. + /// + [Fact] + public void ToJson_RoundtripsHorizontalBoundCrsAcrossWkt2AndProjJson() + { + GeographicCoordinateSystem original = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "ED50 test", + AngularUnit.Degrees, + HorizontalDatum.ED50, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + BoundCoordinateSystem fromWkt2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + string json = ProjJsonWriter.ToJson(fromWkt2); + BoundCoordinateSystem fromProjJson = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.True(fromProjJson.EqualParams(fromWkt2)); + } + + /// + /// Verifies WKT2 vertical BOUNDCRS definitions survive a PROJJSON BoundCRS roundtrip without semantic drift. + /// + [Fact] + public void ToJson_RoundtripsVerticalBoundCrsAcrossWkt2AndProjJson() + { + VerticalCoordinateSystem original = CreateBoundVerticalCoordinateSystem(); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + BoundCoordinateSystem fromWkt2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + string json = ProjJsonWriter.ToJson(fromWkt2); + BoundCoordinateSystem fromProjJson = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.True(fromProjJson.EqualParams(fromWkt2)); + } + + /// + /// Verifies ensemble-backed geographic CRS survive a WKT2 -> PROJJSON roundtrip without losing ensemble metadata. + /// + [Fact] + public void ToJson_RoundtripsGeographicDatumEnsembleAcrossWkt2AndProjJson() + { + GeographicCoordinateSystem original = CreateEnsembleBackedGeographicCoordinateSystem(); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + GeographicCoordinateSystem fromWkt2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + string json = ProjJsonWriter.ToJson(fromWkt2); + GeographicCoordinateSystem fromProjJson = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.True(fromProjJson.EqualParams(fromWkt2)); + AssertDatumEnsembleEqual( + Assert.IsType(fromWkt2.HorizontalDatum.Ensemble), + Assert.IsType(fromProjJson.HorizontalDatum.Ensemble)); + } + + /// + /// Verifies ensemble-backed vertical CRS survive a WKT2 -> PROJJSON roundtrip without losing ensemble metadata. + /// + [Fact] + public void ToJson_RoundtripsVerticalDatumEnsembleAcrossWkt2AndProjJson() + { + VerticalCoordinateSystem original = CreateEnsembleBackedVerticalCoordinateSystem(); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + VerticalCoordinateSystem fromWkt2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + + string json = ProjJsonWriter.ToJson(fromWkt2); + VerticalCoordinateSystem fromProjJson = Assert.IsType(ProjJsonReader.Parse(json)); + + Assert.True(fromProjJson.EqualParams(fromWkt2)); + AssertDatumEnsembleEqual( + Assert.IsType(fromWkt2.VerticalDatum.Ensemble), + Assert.IsType(fromProjJson.VerticalDatum.Ensemble)); + } + + private static string GetCatalogWkt(int srid) + { + if (!CatalogDefinitions.Value.TryGetValue(srid, out string? wkt)) + { + throw new InvalidOperationException($"No catalog definition found for EPSG:{srid}."); + } + + return wkt; + } + + private static Type[] GetConcreteCoordinateSystemWriterTypes() + { + return typeof(CoordinateSystem).Assembly.GetTypes() + .Where(type => type.IsPublic && !type.IsAbstract && typeof(CoordinateSystem).IsAssignableFrom(type)) + .OrderBy(type => type.FullName, StringComparer.Ordinal) + .ToArray(); + } + + private static BoundCoordinateSystem CreateSupportedBoundCoordinateSystem() + { + string wkt = CreateBoundVerticalCoordinateSystem().ToWktNode(WktVersion.Wkt22019).ToString(); + return CoordinateSystemTestHelpers.RequireCoordinateSystem(CoordinateSystemFactory, wkt); + } + + private static EngineeringCoordinateSystem CreateUnsupportedEngineeringCoordinateSystem() + { + return new EngineeringCoordinateSystem( + new EngineeringDatum("Local plant", "EPSG", 1098, string.Empty, string.Empty, string.Empty), + "Cartesian", + [new AxisInfo("x", AxisOrientationEnum.East), new AxisInfo("y", AxisOrientationEnum.North)], + [LinearUnit.Metre, LinearUnit.Metre], + "Plant grid", + "EPSG", + 5800, + string.Empty, + string.Empty, + string.Empty); + } + + private static ParametricCoordinateSystem CreateUnsupportedParametricCoordinateSystem() + { + return new ParametricCoordinateSystem( + new ParametricUnit(0.1d, "pressure", "EPSG", 0, string.Empty, string.Empty, string.Empty), + new ParametricDatum("Reservoir datum", "EPSG", 0, string.Empty, string.Empty, string.Empty), + new AxisInfo("pressure", AxisOrientationEnum.Up), + "Reservoir pressure", + "EPSG", + 0, + string.Empty, + string.Empty, + string.Empty); + } + + private static TemporalCoordinateSystem CreateUnsupportedTemporalCoordinateSystem() + { + return new TemporalCoordinateSystem( + new TimeUnit(1d, "second", "EPSG", 1040, string.Empty, string.Empty, string.Empty), + new TemporalDatum("1950-01-01T00:00:00Z", "Unix epoch", "EPSG", 1040, string.Empty, string.Empty, string.Empty), + new AxisInfo("time", AxisOrientationEnum.Other), + "Temporal axis", + "EPSG", + 1041, + string.Empty, + string.Empty, + string.Empty); + } + + private static FittedCoordinateSystem CreateDerivedGeographicCoordinateSystem() + { + return CoordinateSystemFactory.CreateFittedCoordinateSystem( + "Local WGS 84", + GeographicCoordinateSystem.WGS84, + new AffineTransform(1, 0, 0.5, 0, 1, 1.5), + [ + new AxisInfo("Local latitude", AxisOrientationEnum.North), + new AxisInfo("Local longitude", AxisOrientationEnum.East), + ]); + } + + private static FittedCoordinateSystem CreateWkt1CompatibleDerivedGeographicCoordinateSystem() + { + GeographicCoordinateSystem baseCoordinateSystem = GeographicCoordinateSystem.WGS84; + return CoordinateSystemFactory.CreateFittedCoordinateSystem( + "WGS 84 fitted", + baseCoordinateSystem, + new AffineTransform(1, 0, 0.5, 0, 1, 1.5), + [ + new AxisInfo(baseCoordinateSystem.GetAxis(0).Name, baseCoordinateSystem.GetAxis(0).Orientation), + new AxisInfo(baseCoordinateSystem.GetAxis(1).Name, baseCoordinateSystem.GetAxis(1).Orientation), + ]); + } + + private static FittedCoordinateSystem CreateDerivedProjectedCoordinateSystem() + { + var baseCoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(32, true); + return CoordinateSystemFactory.CreateFittedCoordinateSystem( + "Local projected", + baseCoordinateSystem, + new AffineTransform(1, 0, 100, 0, 1, -50), + [ + new AxisInfo("Local easting", AxisOrientationEnum.East), + new AxisInfo("Local northing", AxisOrientationEnum.North), + ]); + } + + private static FittedCoordinateSystem CreateWkt1CompatibleDerivedProjectedCoordinateSystem() + { + var baseCoordinateSystem = ProjectedCoordinateSystem.WGS84_UTM(32, true); + return CoordinateSystemFactory.CreateFittedCoordinateSystem( + "UTM 32N fitted", + baseCoordinateSystem, + new AffineTransform(1, 0, 100, 0, 1, -50), + [ + new AxisInfo(baseCoordinateSystem.GetAxis(0).Name, baseCoordinateSystem.GetAxis(0).Orientation), + new AxisInfo(baseCoordinateSystem.GetAxis(1).Name, baseCoordinateSystem.GetAxis(1).Orientation), + ]); + } + + private static void AssertFittedCoordinateSystemSemanticsEqual(FittedCoordinateSystem expected, FittedCoordinateSystem actual) + { + Assert.Equal(expected.Name, actual.Name); + Assert.Equal(expected.ToBase(), actual.ToBase()); + AssertCoordinateSystemAxisMetadataEqual(expected, actual); + } + + private static void AssertDerivedGeographicBaseSemanticsEqual(GeographicCoordinateSystem expected, GeographicCoordinateSystem actual) + { + Assert.Equal(expected.Name, actual.Name); + Assert.True(actual.HorizontalDatum.EqualParams(expected.HorizontalDatum)); + Assert.True(actual.PrimeMeridian.EqualParams(expected.PrimeMeridian)); + Assert.True(actual.AngularUnit.EqualParams(expected.AngularUnit)); + } + + private static void AssertDerivedProjectedBaseSemanticsEqual(ProjectedCoordinateSystem expected, ProjectedCoordinateSystem actual) + { + Assert.Equal(expected.Name, actual.Name); + AssertCoordinateSystemAxisMetadataEqual(expected, actual); + Assert.True(actual.HorizontalDatum.EqualParams(expected.HorizontalDatum)); + Assert.True(actual.LinearUnit.EqualParams(expected.LinearUnit)); + Assert.True(actual.Projection.EqualParams(expected.Projection)); + AssertDerivedGeographicBaseSemanticsEqual(expected.GeographicCoordinateSystem, actual.GeographicCoordinateSystem); + } + + private static void AssertCoordinateSystemAxisMetadataEqual(CoordinateSystem expected, CoordinateSystem actual) + { + Assert.Equal(expected.Dimension, actual.Dimension); + for (int dimension = 0; dimension < expected.Dimension; dimension++) + { + Assert.Equal(expected.GetAxis(dimension).Name, actual.GetAxis(dimension).Name); + Assert.Equal(expected.GetAxis(dimension).Orientation, actual.GetAxis(dimension).Orientation); + Assert.True(actual.GetUnits(dimension).EqualParams(expected.GetUnits(dimension))); + } + } + + private static VerticalCoordinateSystem CreateBoundVerticalCoordinateSystem() + { + VerticalCoordinateSystem vertical = CoordinateSystemFactory.CreateVerticalCoordinateSystem( + "EGM96 height", + CoordinateSystemFactory.CreateVerticalDatum("EGM96 geoid", DatumType.VD_GeoidModelDerived), + LinearUnit.Metre, + new AxisInfo("gravity-related height (H)", AxisOrientationEnum.Up)); + CompoundCoordinateSystem hub = CoordinateSystemFactory.CreateCompoundCoordinateSystem( + "WGS 84 + ellipsoidal height", + GeographicCoordinateSystem.WGS84, + CreateEllipsoidalHeightVerticalCoordinateSystem()); + + return vertical.WithBoundGridTransformation(new VerticalBoundGridTransformation( + "Geographic3D to GravityRelatedHeight (EGM)", + "egm96_15.gtx", + hub)); + } + + private static VerticalCoordinateSystem CreateEllipsoidalHeightVerticalCoordinateSystem() + { + return CoordinateSystemFactory.CreateVerticalCoordinateSystem( + "Ellipsoidal height", + CoordinateSystemFactory.CreateVerticalDatum("Ellipsoidal height datum", DatumType.VD_Ellipsoidal), + LinearUnit.Metre, + new AxisInfo("Ellipsoidal height", AxisOrientationEnum.Up)); + } + + private static GeographicCoordinateSystem CreateEnsembleBackedGeographicCoordinateSystem() + { + DatumEnsemble ensemble = new( + "World Geodetic System 1984 ensemble", + [ + new DatumEnsembleMember("World Geodetic System 1984 (Transit)", "EPSG", 1166), + new DatumEnsembleMember("World Geodetic System 1984 (G730)", "EPSG", 1152), + ], + 2d, + HorizontalDatum.WGS84.Ellipsoid, + "EPSG", + 6326); + HorizontalDatum datum = HorizontalDatum.WGS84 + .WithName("World Geodetic System 1984 ensemble") + .WithEnsemble(ensemble); + + GeographicCoordinateSystem geographic = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS 84", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Geodetic latitude", AxisOrientationEnum.North), + new AxisInfo("Geodetic longitude", AxisOrientationEnum.East)); + return geographic.WithAuthority("EPSG", 4326); + } + + private static ProjectedCoordinateSystem CreateEnsembleBackedProjectedCoordinateSystem() + { + GeographicCoordinateSystem geographic = CreateEnsembleBackedGeographicCoordinateSystem(); + IProjection projection = CoordinateSystemFactory.CreateProjection( + "UTM zone 32N", + "Transverse_Mercator", + new List + { + new("latitude_of_origin", 0), + new("central_meridian", 9), + new("scale_factor", 0.9996), + new("false_easting", 500000), + new("false_northing", 0), + }); + + return CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "WGS 84 / UTM zone 32N", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("Easting", AxisOrientationEnum.East), + new AxisInfo("Northing", AxisOrientationEnum.North)); + } + + private static VerticalCoordinateSystem CreateEnsembleBackedVerticalCoordinateSystem() + { + DatumEnsemble ensemble = new( + "Example vertical ensemble", + [ + new DatumEnsembleMember("Datum A", "TEST", 1), + new DatumEnsembleMember("Datum B", "TEST", 2), + ], + 0.05d, + null, + "TEST", + 1001); + VerticalDatum datum = Assert.IsType( + Assert.IsType( + CoordinateSystemFactory.CreateVerticalDatum("Example vertical ensemble", DatumType.VD_GeoidModelDerived) + .WithAuthority("TEST", 1001)) + .WithEnsemble(ensemble)); + + return CoordinateSystemFactory.CreateVerticalCoordinateSystem( + "Example ensemble height", + datum, + LinearUnit.Metre, + new AxisInfo("Gravity-related height", AxisOrientationEnum.Up)); + } + + private static void AssertDatumEnsembleEqual(DatumEnsemble expected, DatumEnsemble actual) + { + Assert.True(expected.Equals(actual)); + Assert.Equal(expected.Name, actual.Name); + Assert.Equal(expected.Authority, actual.Authority); + Assert.Equal(expected.AuthorityCode, actual.AuthorityCode); + Assert.Equal(expected.Members.Count, actual.Members.Count); + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/ProjectionParameterNameNormalizerTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/ProjectionParameterNameNormalizerTests.cs new file mode 100644 index 00000000..e6bcf5e5 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/ProjectionParameterNameNormalizerTests.cs @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using ProjNet.IO.CoordinateSystems; +using Xunit; + +/// +/// Verifies normalization of projection parameter names imported from WKT2 and PROJJSON. +/// +public class ProjectionParameterNameNormalizerTests +{ + /// + /// Verifies that known WKT2 parameter aliases map to the internal canonical names. + /// + /// External parameter name. + /// Expected internal canonical name. + [Theory] + [InlineData("Longitude of natural origin", "central_meridian")] + [InlineData("longitude-of-projection-centre", "central_meridian")] + [InlineData("Latitude of 1st standard parallel", "standard_parallel_1")] + [InlineData("Latitude of 2nd standard parallel", "standard_parallel_2")] + [InlineData("Scale factor at projection centre", "scale_factor")] + [InlineData("Azimuth of initial line", "azimuth")] + [InlineData("Angle from rectified to skew grid", "rectified_grid_angle")] + public void Normalize_WithKnownAliases_ReturnsCanonicalName(string parameterName, string expected) + { + string actual = ProjectionParameterNameNormalizer.Normalize(parameterName); + + Assert.Equal(expected, actual); + } + + /// + /// Verifies that unknown parameter names are still normalized deterministically. + /// + [Fact] + public void Normalize_WithUnknownName_ReturnsNormalizedToken() + { + string actual = ProjectionParameterNameNormalizer.Normalize("Semi-major.axis/length"); + + Assert.Equal("SEMI_MAJOR_AXIS_LENGTH", actual); + } + + /// + /// Verifies that the raw lookup-token normalization preserves the legacy single-pass underscore-collapse semantics. + /// + /// External parameter name. + /// Expected normalized lookup token. + [Theory] + [InlineData("Semi-major.axis/length", "SEMI_MAJOR_AXIS_LENGTH")] + [InlineData("Alpha__beta", "ALPHA_BETA")] + [InlineData("Alpha___beta", "ALPHA__BETA")] + [InlineData(" (Alpha) / beta ", "ALPHA__BETA")] + public void NormalizeLookupToken_WithMixedSeparators_ReturnsExpectedToken(string parameterName, string expected) + { + string actual = ProjectionParameterNameNormalizer.NormalizeLookupToken(parameterName); + + Assert.Equal(expected, actual); + } + + /// + /// Verifies that empty and whitespace-only names normalize to an empty string. + /// + [Theory] + [InlineData("")] + [InlineData(" ")] + public void Normalize_WithEmptyOrWhitespace_ReturnsEmptyString(string parameterName) + { + string actual = ProjectionParameterNameNormalizer.Normalize(parameterName); + + Assert.Equal(string.Empty, actual); + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/WKTCoordSysParserTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/WKTCoordSysParserTests.cs new file mode 100644 index 00000000..2b63f1e9 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/WKTCoordSysParserTests.cs @@ -0,0 +1,822 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for parsing WKT coordinate system definitions. +/// +public class WKTCoordSysParserTests +{ + private static readonly Lazy> CatalogDefinitions = new(() => + new ManagedCoordinateSystemDefinitionProvider() + .GetDefinitions() + .GroupBy(item => item.Srid) + .ToDictionary(group => group.Key, group => group.Last().Wkt)); + + private readonly CoordinateSystemFactory coordinateSystemFactory = new(); + + /// + /// Tests parsing of the shared EPSG archive fixture for EPSG:2918. + /// + [Fact] + public void TestProjectedCoordinateSystemEPSG2918() + { + string wkt = GetArchiveWkt(2918); + + ProjectedCoordinateSystem pcs = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt); + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + this.coordinateSystemFactory, + GetManagedWkt(2918)); + + ProjectedCoordinateSystem pcs2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt.Replace("[", "(", StringComparison.Ordinal).Replace("]", ")", StringComparison.Ordinal)); + Assert.True(pcs.EqualParams(reference)); + Assert.True(pcs.EqualParams(pcs2)); + + CheckInfo(pcs, "NAD83(HARN) / Texas Central (ftUS)", "EPSG", 2918); + + GeographicCoordinateSystem gcs = pcs.GeographicCoordinateSystem; + CheckInfo(gcs, "NAD83(HARN)", "EPSG", 4152); + CheckDatum(gcs.HorizontalDatum, "NAD83 (High Accuracy Reference Network)", "EPSG", 6152); + CheckEllipsoid(gcs.HorizontalDatum.Ellipsoid, "GRS 1980", 6378137, 298.257222101, "EPSG", 7019); + this.CheckPrimem(gcs.PrimeMeridian, "Greenwich", 0, "EPSG", 8901); + CheckUnit(gcs.AngularUnit, "degree", 0.017453292519943295, "EPSG", 9102); + + Assert.Equal("Lambert Conic Conformal (2SP)", pcs.Projection.ClassName); + Assert.Equal("SPCS83 Texas Central zone (US survey foot)", pcs.Projection.Name); + Assert.Equal("EPSG", pcs.Projection.Authority); + Assert.Equal(15359L, pcs.Projection.AuthorityCode); + CheckProjectionParameters( + pcs.Projection, + [ + Tuple.Create("standard_parallel_1", 31.8833333333336d), + Tuple.Create("standard_parallel_2", 30.1166666666669d), + Tuple.Create("latitude_of_origin", 29.6666666666669d), + Tuple.Create("central_meridian", -100.333333333334d), + Tuple.Create("false_easting", 2296583.333d), + Tuple.Create("false_northing", 9842500d), + ]); + + CheckUnit(pcs.LinearUnit, "US survey foot", 0.304800609601219, "EPSG", 9003); + } + + /// + /// This test reads in a file with 2671 pre-defined coordinate systems and projections, + /// and tries to parse them. + /// + [Fact] + public void ParseAllWKTs() + { + int parseCount = 0; + foreach (SRIDReader.WktString wkt in SRIDReader.GetSrids()) + { + CoordinateSystem? cs1 = this.coordinateSystemFactory.CreateFromWkt(wkt.Wkt); + Assert.NotNull(cs1); + CoordinateSystem cs2 = CoordinateSystemTestHelpers.RequireCoordinateSystem( + this.coordinateSystemFactory, + wkt.Wkt.Replace("[", "(", StringComparison.Ordinal).Replace("]", ")", StringComparison.Ordinal)); + Assert.True(cs1.EqualParams(cs2)); + parseCount++; + } + + Assert.True(parseCount > 2671, "Not all WKT was parsed"); + } + + /// + /// Verifies that non-coordinate-system WKT returns instead of throwing. + /// + [Fact] + public void CreateFromWktReturnsNullForNonCoordinateSystemWkt() + { + const string wkt = """UNIT["metre",1,AUTHORITY["EPSG","9001"]]"""; + + CoordinateSystem? coordinateSystem = this.coordinateSystemFactory.CreateFromWkt(wkt); + + Assert.Null(coordinateSystem); + } + + /// + /// Verifies mixed-case WKT2 keywords are normalized and parsed correctly. + /// + [Fact] + public void CreateFromWktParsesMixedCaseGeodeticCrsAndEllipsoid() + { + const string wkt = """geodeticcrs["WGS 84",DATUM["WGS_1984",Ellipsoid["WGS 84",6378137,298.257223563],id["EPSG","6326"]],PRIMEM["Greenwich",0,id["EPSG","8901"]],UNIT["degree",0.0174532925199433,id["EPSG","9122"]],id["EPSG","4326"]]"""; + + GeographicCoordinateSystem coordinateSystem = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt); + + CheckInfo(coordinateSystem, "WGS 84", "EPSG", 4326); + CheckDatum(coordinateSystem.HorizontalDatum, "WGS_1984", "EPSG", 6326); + CheckEllipsoid(coordinateSystem.HorizontalDatum.Ellipsoid, "WGS 84", 6378137, 298.257223563, string.Empty, -1); + } + + /// + /// Verifies SPHEROID parsing succeeds when AUTHORITY is omitted. + /// + [Fact] + public void CreateFromWktParsesSpheroidWithoutAuthority() + { + const string wkt = """GEOGCS["Custom",DATUM["Custom_Datum",SPHEROID["Custom Spheroid",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]"""; + + GeographicCoordinateSystem coordinateSystem = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt); + + CheckInfo(coordinateSystem, "Custom", string.Empty, -1); + CheckDatum(coordinateSystem.HorizontalDatum, "Custom_Datum", string.Empty, -1); + CheckEllipsoid(coordinateSystem.HorizontalDatum.Ellipsoid, "Custom Spheroid", 6378137, 298.257223563, string.Empty, -1); + } + + /// + /// Verifies malformed SPHEROID definitions without AUTHORITY are rejected when bracket types do not match. + /// + [Fact] + public void ParseSpheroidWithoutAuthorityRejectsMismatchedBrackets() + { + const string malformedWkt = """SPHEROID("WGS 84",6378137,298.257223563]"""; + + Assert.Throws(() => ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(malformedWkt)); + } + + /// + /// Verifies malformed WKT1 TOWGS84 parameter counts surface as parser failures. + /// + [Fact] + public void ParseTowgs84WithInvalidValueCountThrowsWktParseException() + { + const string malformedWkt = + """GEOGCS["Custom",DATUM["Custom_Datum",SPHEROID["Custom Spheroid",6378137,298.257223563],TOWGS84[1,2]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]"""; + + Assert.Throws(() => ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(malformedWkt)); + } + + /// + /// Verifies non-numeric AUTHORITY codes are represented as unknown authority code -1. + /// + [Fact] + public void CreateFromWktUsesMinusOneForNonNumericAuthorityCodes() + { + const string wkt = + """GEOGCS["Custom",DATUM["Custom_Datum",SPHEROID["Custom Spheroid",6378137,298.257223563,AUTHORITY["LOCAL","abc"]],AUTHORITY["LOCAL","abc"]],PRIMEM["Greenwich",0,AUTHORITY["LOCAL","abc"]],UNIT["degree",0.0174532925199433,AUTHORITY["LOCAL","abc"]],AUTHORITY["LOCAL","abc"]]"""; + + GeographicCoordinateSystem coordinateSystem = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt); + + Assert.Equal(-1, coordinateSystem.AuthorityCode); + Assert.Equal(-1, coordinateSystem.HorizontalDatum.AuthorityCode); + Assert.Equal(-1, coordinateSystem.HorizontalDatum.Ellipsoid.AuthorityCode); + Assert.Equal(-1, coordinateSystem.PrimeMeridian.AuthorityCode); + Assert.Equal(-1, coordinateSystem.AngularUnit.AuthorityCode); + } + + /// + /// Verifies projected coordinate systems without PARAMETER entries parse successfully. + /// + [Fact] + public void CreateFromWktParsesProjectedCoordinateSystemWithoutParameters() + { + const string wkt = + """PROJCS["Custom",GEOGCS["Custom GCS",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Mercator_1SP"],UNIT["metre",1]]"""; + + ProjectedCoordinateSystem coordinateSystem = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt); + Assert.Equal("Mercator_1SP", coordinateSystem.Projection.ClassName); + Assert.Equal(0, coordinateSystem.Projection.NumParameters); + } + + /// + /// Verifies WKT2 root keyword aliases are normalized to equivalent WKT1 coordinate system roots. + /// + [Fact] + public void CreateFromWktParsesWkt2RootKeywordAliases() + { + const string projectedWkt = + """PROJCRS["Custom Projected",GEOGCS["Custom GCS",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],PROJECTION["Mercator_1SP"],UNIT["metre",1]]"""; + const string verticalWkt = + """VERTCRS["Custom Height",VERT_DATUM["Custom Vertical Datum",2005],UNIT["metre",1],AXIS["Up",UP]]"""; + const string compoundWkt = + """COMPOUNDCRS["Compound",GEOGCS["Custom GCS",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],VERT_CS["Custom Height",VERT_DATUM["Custom Vertical Datum",2005],UNIT["metre",1],AXIS["Up",UP]]]"""; + + ProjectedCoordinateSystem projected = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, projectedWkt); + VerticalCoordinateSystem vertical = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, verticalWkt); + CompoundCoordinateSystem compound = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, compoundWkt); + + Assert.Equal("Custom Projected", projected.Name); + Assert.Equal("Custom Height", vertical.Name); + Assert.Equal("Compound", compound.Name); + } + + /// + /// Verifies BOUNDCRS roots are surfaced as explicitly unsupported instead of unrecognized. + /// + [Fact] + public void CreateFromWktBoundCrsThrowsNotSupported() + { + const string wkt = "BoundCrs[]"; + + NotSupportedException exception = Assert.Throws(() => this.coordinateSystemFactory.CreateFromWkt(wkt)); + Assert.Contains("BOUNDCRS", exception.Message, StringComparison.Ordinal); + } + + /// + /// This test reads in a file with 2671 pre-defined coordinate systems and projections, + /// and tries to create a transformation with them. + /// + [Fact] + public void TestCreateCoordinateTransformationForWktInCsv() + { + // GeographicCoordinateSystem.WGS84 + var fac = new CoordinateSystemFactory(); + int parseCount = 0; + int failedCss = 0; + var failedProjections = new HashSet(); + using Stream stream = Assert.IsType(Assembly.GetExecutingAssembly().GetManifestResourceStream("ProjNET.Tests.SRID.csv"), exactMatch: false); + using (var sr = new StreamReader(stream, Encoding.UTF8)) + { + var ctFactory = new CoordinateTransformationFactory(); + while (!sr.EndOfStream) + { + string line = Assert.IsType(sr.ReadLine()); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + int split = line.IndexOf(';', StringComparison.Ordinal); + if (split > -1) + { + string wkt = line[(split + 1)..]; + CoordinateSystem? cs = fac.CreateFromWkt(wkt); + if (cs is null) + { + continue; // We check this in another test. + } + + if (cs is ProjectedCoordinateSystem pcs) + { + switch (pcs.Projection.ClassName) + { + // Skip not supported projections + case "Oblique_Stereographic": + case "Transverse_Mercator_South_Orientated": + case "Lambert_Conformal_Conic_1SP": + case "Lambert_Azimuthal_Equal_Area": + case "Tunisia_Mining_Grid": + case "New_Zealand_Map_Grid": + case "Polyconic": + case "Lambert_Conformal_Conic_2SP_Belgium": + case "Polar_Stereographic": + case "Hotine_Oblique_Mercator_Azimuth_Center": + case "Mercator_1SP": + case "Mercator_2SP": + case "Cylindrical_Equal_Area": + case "Equirectangular": + case "Laborde_Oblique_Mercator": + continue; + } + } + + try + { + ctFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, cs); + } + catch (Exception) + { + if (cs is ProjectedCoordinateSystem ics) + { + if (!failedProjections.Contains(ics.Projection.ClassName)) + { + failedProjections.Add(ics.Projection.ClassName); + } + } + else + { + Assert.True(false); + } + + failedCss += 1; + + // Assert.Fail( + // $"Could not create transformation from:\r\n{wkt}\r\n{ex.Message}\r\nClass name:{ics.Projection.ClassName}"); + // else + // Assert.Fail($"Could not create transformation from:\r\n{wkt}\r\n{ex.Message}"); + } + + parseCount++; + } + } + } + + Assert.True(parseCount >= 2556, "Not all WKT was processed"); + if (failedCss > 0) + { + Console.WriteLine($"Failed to create transfroms for {failedCss} coordinate systems"); + foreach (string fp in failedProjections) + { + Console.WriteLine($"case \"{fp}\":"); + } + } + } + + /// + /// Tests parsing of the shared EPSG archive fixture for EPSG:27700. + /// + [Fact] + public void TestProjectedCoordinateSystemEPSG27700() + { + string wkt = GetArchiveWkt(27700); + + ProjectedCoordinateSystem pcs = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt); + ProjectedCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + this.coordinateSystemFactory, + GetManagedWkt(27700)); + + ProjectedCoordinateSystem pcs2 = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, wkt.Replace("[", "(", StringComparison.Ordinal).Replace("]", ")", StringComparison.Ordinal)); + Assert.True(pcs.EqualParams(reference)); + Assert.True(pcs.EqualParams(pcs2)); + + CheckInfo(pcs, "OSGB36 / British National Grid", "EPSG", 27700); + + GeographicCoordinateSystem gcs = pcs.GeographicCoordinateSystem; + CheckInfo(gcs, "OSGB36", "EPSG", 4277); + CheckDatum(gcs.HorizontalDatum, "Ordnance Survey of Great Britain 1936", "EPSG", 6277); + CheckEllipsoid(gcs.HorizontalDatum.Ellipsoid, "Airy 1830", 6377563.396, 299.3249646, "EPSG", 7001); + this.CheckPrimem(gcs.PrimeMeridian, "Greenwich", 0, "EPSG", 8901); + CheckUnit(gcs.AngularUnit, "degree", 0.017453292519943295, "EPSG", 9102); + + Assert.Equal("Transverse Mercator", pcs.Projection.ClassName); + Assert.Equal("British National Grid", pcs.Projection.Name); + Assert.Equal("EPSG", pcs.Projection.Authority); + Assert.Equal(19916L, pcs.Projection.AuthorityCode); + CheckProjectionParameters( + pcs.Projection, + [ + Tuple.Create("latitude_of_origin", 49d), + Tuple.Create("central_meridian", -2d), + Tuple.Create("scale_factor", 0.9996012717), + Tuple.Create("false_easting", 400000d), + Tuple.Create("false_northing", -100000d), + ]); + + CheckUnit(pcs.LinearUnit, "metre", 1d, "EPSG", 9001); + + ProjectedCoordinateSystem roundTripped = CoordinateSystemTestHelpers.RequireCoordinateSystem(this.coordinateSystemFactory, pcs.WKT); + Assert.True(pcs.EqualParams(roundTripped)); + } + + /// + /// Verifies that a WGS 84 Pseudo-Mercator WKT definition sourced from spatialreference.org can be parsed without errors. + /// + [Fact] + public void TestParseSrOrg() + { + const string wkt = + """ + PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["Popular Visualisation CRS", + DATUM["Popular_Visualisation_Datum",SPHEROID["Popular Visualisation Sphere", + 6378137,0,AUTHORITY["EPSG","7059"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG", + "6055"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree", + 0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4055"]], + PROJECTION["Mercator_1SP"], + PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER[ + "false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH]],AUTHORITY["EPSG","3785"]] + """; + + Assert.Null(Record.Exception(() => this.coordinateSystemFactory.CreateFromWkt(wkt))); + } + + /// + /// Verifies that known problematic WKT definitions can be parsed without errors. + /// + [Fact] + public void TestProjNetIssues() + { + const string firstIssueWkt = + """ + PROJCS["International_Terrestrial_Reference_Frame_1992Lambert_Conformal_Conic_2SP", + GEOGCS["GCS_International_Terrestrial_Reference_Frame_1992", + DATUM["International_Terrestrial_Reference_Frame_1992", + SPHEROID["GRS_1980",6378137,298.257222101], + TOWGS84[0,0,0,0,0,0,0]], + PRIMEM["Greenwich",0], + UNIT["Degree",0.0174532925199433]], + PROJECTION["Lambert_Conformal_Conic_2SP",AUTHORITY["EPSG","9802"]], + PARAMETER["Central_Meridian",-102], + PARAMETER["Latitude_Of_Origin",12], + PARAMETER["False_Easting",2500000], + PARAMETER["False_Northing",0], + PARAMETER["Standard_Parallel_1",17.5], + PARAMETER["Standard_Parallel_2",29.5], + PARAMETER["Scale_Factor",1], + UNIT["Meter",1,AUTHORITY["EPSG","9001"]]] + """; + const string secondIssueWkt = + """ + PROJCS["Google Maps Global Mercator", + GEOGCS["WGS 84", + DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]], + AUTHORITY["EPSG","6326"]], + PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]], + UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]], + AUTHORITY["EPSG","4326"]], + PROJECTION["Mercator_2SP"], + PARAMETER["standard_parallel_1",0], + PARAMETER["latitude_of_origin",0], + PARAMETER["central_meridian",0], + PARAMETER["false_easting",0], + PARAMETER["false_northing",0], + UNIT["Meter",1], + EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"], + AUTHORITY["EPSG","900913"]] + """; + + Assert.Null(Record.Exception(() => this.coordinateSystemFactory.CreateFromWkt(firstIssueWkt))); + Assert.Null(Record.Exception(() => this.coordinateSystemFactory.CreateFromWkt(secondIssueWkt))); + } + + /// + /// Test parsing of a from WKT. + /// + [Fact] + public void TestFittedCoordinateSystemWkt() + { + var fac = new CoordinateSystemFactory(); + FittedCoordinateSystem fcs = default!; + const string wkt = + """ + FITTED_CS["Local coordinate system MNAU (based on Gauss-Krueger)", + PARAM_MT["Affine", + PARAMETER["num_row",3],PARAMETER["num_col",3],PARAMETER["elt_0_0", 0.883485346527455],PARAMETER["elt_0_1", -0.468458794848877],PARAMETER["elt_0_2", 3455869.17937689],PARAMETER["elt_1_0", 0.468458794848877],PARAMETER["elt_1_1", 0.883485346527455],PARAMETER["elt_1_2", 5478710.88035753],PARAMETER["elt_2_2", 1]], + PROJCS["DHDN / Gauss-Kruger zone 3", + GEOGCS["DHDN", + DATUM["Deutsches_Hauptdreiecksnetz", + SPHEROID["Bessel 1841", 6377397.155, 299.1528128, AUTHORITY["EPSG", "7004"]], + TOWGS84[612.4, 77, 440.2, -0.054, 0.057, -2.797, 0.525975255930096], + AUTHORITY["EPSG", "6314"]], + PRIMEM["Greenwich", 0, AUTHORITY["EPSG", "8901"]], + UNIT["degree", 0.0174532925199433, AUTHORITY["EPSG", "9122"]], + AUTHORITY["EPSG", "4314"]], + PROJECTION["Transverse_Mercator"], + PARAMETER["latitude_of_origin", 0], + PARAMETER["central_meridian", 9], + PARAMETER["scale_factor", 1], + PARAMETER["false_easting", 3500000], + PARAMETER["false_northing", 0], + UNIT["metre", 1, AUTHORITY["EPSG", "9001"]], + AUTHORITY["EPSG", "31467"]] + ] + """; + + try + { + fcs = CoordinateSystemTestHelpers.RequireCoordinateSystem(fac, wkt); + } + catch (Exception ex) + { + Assert.Fail($"Could not create fitted coordinate system from:\r\n{wkt}\r\n{ex.Message}"); + } + + Assert.NotNull(fcs); + Assert.False(string.IsNullOrEmpty(fcs.ToBase())); + Assert.NotNull(fcs.BaseCoordinateSystem); + + Assert.Equal("Local coordinate system MNAU (based on Gauss-Krueger)", fcs.Name); + + // Assert.AreEqual ("CUSTOM", fcs.Authority); + // Assert.AreEqual (123456, fcs.AuthorityCode); + Assert.Equal("EPSG", fcs.BaseCoordinateSystem.Authority); + Assert.Equal(31467, fcs.BaseCoordinateSystem.AuthorityCode); + } + + /// + /// Tests parsing of the shared EPSG archive fixture for EPSG:5250. + /// + [Fact] + public void TestGeocentricCoordinateSystem() + { + var fac = new CoordinateSystemFactory(); + GeocentricCoordinateSystem fcs = default!; + + string wkt = GetArchiveWkt(5250); + GeocentricCoordinateSystem reference = CoordinateSystemTestHelpers.RequireCoordinateSystem( + fac, + GetManagedWkt(5250)); + + try + { + fcs = CoordinateSystemTestHelpers.RequireCoordinateSystem(fac, wkt); + } + catch (Exception ex) + { + Assert.Fail($"Could not create geocentric coordinate system from:\r\n{wkt}\r\n{ex.Message}"); + } + + Assert.NotNull(fcs); + Assert.True(fcs.EqualParams(reference)); + Assert.True(CheckInfo(fcs, "TUREF", "EPSG", 5250L)); + Assert.True(CheckDatum(fcs.HorizontalDatum, "Turkish National Reference Frame", "EPSG", 1057L)); + Assert.True(CheckEllipsoid(fcs.HorizontalDatum.Ellipsoid, "GRS 1980", 6378137, 298.257222101, "EPSG", 7019)); + Assert.True(this.CheckPrimem(fcs.PrimeMeridian, "Greenwich", 0, "EPSG", 8901L)); + Assert.True(CheckUnit(fcs.PrimeMeridian.AngularUnit, "degree", 0.017453292519943295, "EPSG", 9102L)); + Assert.True(CheckUnit(fcs.LinearUnit, "metre", 1, "EPSG", 9001L)); + + Assert.Equal("EPSG", fcs.Authority); + Assert.Equal(5250L, fcs.AuthorityCode); + } + + /// + /// Verifies that WKT produced by a coordinate system object can be round-tripped back into an equivalent projected coordinate system. + /// + [Fact] + public void ParseWktCreatedByCoordinateSystem() + { + // Sample WKT from an external source. + const string sampleWKT = + """ + PROJCS["", + GEOGCS["", + DATUM["", + SPHEROID["GRS_1980", 6378137, 298.2572221010042] + ], + PRIMEM["Greenwich", 0], + UNIT["Degree", 0.017453292519943295] + ], + PROJECTION["Transverse_Mercator"], + PARAMETER["False_Easting", 500000], + PARAMETER["False_Northing", 0], + PARAMETER["Central_Meridian", -75], + PARAMETER["Scale_Factor", 0.9996], + UNIT["Meter", 1] + ] + """; + + var csFromSample = (CoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(sampleWKT); + string wktFromProjNetCS = csFromSample.WKT; + IInfo parsed = ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(wktFromProjNetCS); + Assert.IsType(parsed); + var projCS = (ProjectedCoordinateSystem)parsed; + Assert.NotNull(projCS.LinearUnit); + Assert.Equal("Meter", projCS.LinearUnit.Name); + Assert.Equal(1, projCS.LinearUnit.MetersPerUnit); + } + + /// + /// Verifies that a PROJECTEDCRS WKT using WKT2-style root and ID tokens can be parsed into a projected coordinate system with correct authority metadata. + /// + [Fact] + public void ParseProjectedCrsWithWkt2LikeRootAndIdentifiers() + { + const string wkt = + """ + PROJECTEDCRS["WGS 84 / Pseudo-Mercator", + GEODCRS["WGS 84", + DATUM["WGS_1984",ELLIPSOID["WGS 84",6378137,298.257223563,ID["EPSG","7030"]],ID["EPSG","6326"]], + PRIMEM["Greenwich",0,ID["EPSG","8901"]], + UNIT["degree",0.0174532925199433,ID["EPSG","9122"]], + ID["EPSG","4326"]], + PROJECTION["Mercator_1SP"], + PARAMETER["central_meridian",0], + PARAMETER["scale_factor",1], + PARAMETER["false_easting",0], + PARAMETER["false_northing",0], + UNIT["metre",1,ID["EPSG","9001"]], + AXIS["X",EAST], + AXIS["Y",NORTH], + ID["EPSG","3857"]] + """; + + var parsed = (ProjectedCoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(wkt); + Assert.NotNull(parsed); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(3857L, parsed.AuthorityCode); + } + + /// + /// Verifies that a GEODCRS WKT using ELLIPSOID and ID tokens can be parsed into a geographic coordinate system with correct datum and ellipsoid. + /// + [Fact] + public void ParseGeodCrsWithEllipsoidAndIdTokens() + { + const string wkt = + """ + GEODCRS["WGS 84", + DATUM["WGS_1984",ELLIPSOID["WGS 84",6378137,298.257223563,ID["EPSG","7030"]],ID["EPSG","6326"]], + PRIMEM["Greenwich",0,ID["EPSG","8901"]], + UNIT["degree",0.0174532925199433,ID["EPSG","9122"]], + ID["EPSG","4326"]] + """; + + var parsed = (GeographicCoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(wkt); + Assert.NotNull(parsed); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(4326L, parsed.AuthorityCode); + Assert.NotNull(parsed.HorizontalDatum); + Assert.NotNull(parsed.HorizontalDatum.Ellipsoid); + } + + /// + /// Verifies that a PROJECTEDCRS WKT using the GEODETICCRS keyword and space-separated ID tokens can be parsed with correct authority metadata. + /// + [Fact] + public void ParseProjectedCrsWithSpacedIdTokens() + { + const string wkt = + """ + PROJECTEDCRS["WGS 84 / Pseudo-Mercator", + GEODETICCRS["WGS 84", + DATUM["WGS_1984",ELLIPSOID["WGS 84",6378137,298.257223563,ID ["EPSG","7030"]],ID ["EPSG","6326"]], + PRIMEM["Greenwich",0,ID ["EPSG","8901"]], + UNIT["degree",0.0174532925199433,ID ["EPSG","9122"]], + ID ["EPSG","4326"]], + PROJECTION["Mercator_1SP"], + PARAMETER["central_meridian",0], + PARAMETER["scale_factor",1], + PARAMETER["false_easting",0], + PARAMETER["false_northing",0], + UNIT["metre",1,ID ["EPSG","9001"]], + AXIS["X",EAST], + AXIS["Y",NORTH], + ID ["EPSG","3857"]] + """; + + var parsed = (ProjectedCoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(wkt); + Assert.NotNull(parsed); + Assert.Equal("EPSG", parsed.Authority); + Assert.Equal(3857L, parsed.AuthorityCode); + } + + /// + /// Verifies that span-based WKT parsing returns the same coordinate system metadata as string parsing. + /// + [Fact] + public void ParseReadOnlySpanWktMatchesStringParse() + { + const string wkt = + """ + PROJECTEDCRS["WGS 84 / Pseudo-Mercator", + GEODCRS["WGS 84", + DATUM["WGS_1984",ELLIPSOID["WGS 84",6378137,298.257223563,ID["EPSG","7030"]],ID["EPSG","6326"]], + PRIMEM["Greenwich",0,ID["EPSG","8901"]], + UNIT["degree",0.0174532925199433,ID["EPSG","9122"]], + ID["EPSG","4326"]], + PROJECTION["Mercator_1SP"], + PARAMETER["central_meridian",0], + PARAMETER["scale_factor",1], + PARAMETER["false_easting",0], + PARAMETER["false_northing",0], + UNIT["metre",1,ID["EPSG","9001"]], + AXIS["X",EAST], + AXIS["Y",NORTH], + ID["EPSG","3857"]] + """; + + var fromString = (ProjectedCoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(wkt); + var fromSpan = (ProjectedCoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(wkt.AsSpan()); + + Assert.NotNull(fromString); + Assert.NotNull(fromSpan); + Assert.True(fromString.EqualParams(fromSpan)); + Assert.Equal(fromString.Authority, fromSpan.Authority); + Assert.Equal(fromString.AuthorityCode, fromSpan.AuthorityCode); + } + + /// + /// Verifies that whitespace-only span input is rejected by the span-based parser overload. + /// + [Fact] + public void ParseReadOnlySpanWhitespaceThrowsArgumentNullException() + { + const string whitespaceWkt = " \t\r\n"; + + ArgumentNullException exception = Assert.Throws( + () => ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(whitespaceWkt.AsSpan())); + + Assert.Equal("wkt", exception.ParamName); + } + + private static string GetArchiveWkt(int srid) + => EpsgArchiveWktFixtureSource.GetFixture(srid).Wkt; + + private static string GetManagedWkt(int srid) + { + if (!CatalogDefinitions.Value.TryGetValue(srid, out string? wkt)) + { + throw new InvalidOperationException($"SRID {srid} not found in the managed EPSG catalog."); + } + + return wkt; + } + + private bool CheckPrimem(PrimeMeridian primeMeridian, string name, double? longitude, string authority, long? code) + { + Assert.NotNull(primeMeridian); + Assert.True(CheckInfo(primeMeridian, name, authority, code)); + Assert.Equal(longitude, primeMeridian.Longitude); + return true; + } + + private static bool CheckUnit(IUnit unit, string name, double? value, string? authority, long? code) + { + Assert.NotNull(unit); + Assert.True(CheckInfo(unit, name, authority, code)); + Assert.True(unit is LinearUnit || unit is AngularUnit); + + if (!value.HasValue) + { + return true; + } + + if (unit is LinearUnit lunit) + { + Assert.Equal(value, lunit.MetersPerUnit); + } + else if (unit is AngularUnit aunit) + { + Assert.Equal(value, aunit.RadiansPerUnit); + } + + return true; + } + + private static bool CheckEllipsoid(Ellipsoid ellipsoid, string name, double? semiMajor, double? inverseFlattening, string authority, long? code) + { + Assert.NotNull(ellipsoid); + Assert.True(CheckInfo(ellipsoid, name, authority, code)); + if (semiMajor.HasValue) + { + Assert.Equal(semiMajor, ellipsoid.SemiMajorAxis); + } + + if (inverseFlattening.HasValue) + { + Assert.Equal(inverseFlattening, ellipsoid.InverseFlattening); + } + + return true; + } + + private static bool CheckDatum(Datum datum, string name, string authority, long? code) + { + Assert.NotNull(datum); + Assert.IsType(datum); + + Assert.True(CheckInfo(datum, name, authority, code)); + + return true; + } + + private static bool CheckInfo(IInfo info, string name, string? authority = null, long? code = null) + { + Assert.NotNull(info); + if (!string.IsNullOrWhiteSpace(name)) + { + Assert.Equal(name, info.Name); + } + + if (!string.IsNullOrWhiteSpace(authority)) + { + Assert.Equal(authority, info.Authority); + } + + if (code.HasValue) + { + Assert.Equal(code, info.AuthorityCode); + } + + return true; + } + + private static void CheckProjection(IProjection projection, string name, Tuple[]? pp = null, string? authority = null, long? code = null) + { + Assert.NotNull(projection); + Assert.Equal(name, projection.ClassName); + CheckInfo(projection, name, authority, code); + CheckProjectionParameters(projection, pp); + } + + private static void CheckProjectionParameters(IProjection projection, Tuple[]? pp = null) + { + if (pp is null) + { + return; + } + + Assert.Equal(pp.Length, projection.NumParameters); + + for (int i = 0; i < pp.Length; i++) + { + ProjectionParameter par = Assert.IsType(projection.GetParameter(pp[i].Item1)); + Assert.Equal(pp[i].Item1, par.Name, ignoreCase: true); + Assert.Equal(pp[i].Item2, par.Value); + } + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/WKTMathTransformParserTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/WKTMathTransformParserTests.cs new file mode 100644 index 00000000..2cd41bec --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/WKTMathTransformParserTests.cs @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for parsing WKT math transform definitions. +/// +public class WKTMathTransformParserTests +{ + private static readonly double[] Origin2D = [0.0, 0.0]; + private static readonly double[] AffineSamplePoint = [2040.0, 1590.0]; + private static readonly double[] GeographicSamplePoint = [12.0, 45.0]; + private static readonly double[] IdentitySamplePoint = [1.0, 2.0, 3.0, 4.0]; + + /// + /// Test parsing of affine math transform from WKT. + /// + [Fact] + public void ParseAffineTransformWkt() + { + MathTransform mt = default!; + string wkt = "PARAM_MT[\"Affine\"," + + "PARAMETER[\"num_row\",3]," + + "PARAMETER[\"num_col\",3]," + + "PARAMETER[\"elt_0_0\", 0.883485346527455]," + + "PARAMETER[\"elt_0_1\", -0.468458794848877]," + + "PARAMETER[\"elt_0_2\", 3455869.17937689]," + + "PARAMETER[\"elt_1_0\", 0.468458794848877]," + + "PARAMETER[\"elt_1_1\", 0.883485346527455]," + + "PARAMETER[\"elt_1_2\", 5478710.88035753]," + + "PARAMETER[\"elt_2_2\", 1]]"; + + try + { + mt = MathTransformWktReader.Parse(wkt); + } + catch (Exception ex) + { + Assert.Fail($"Could not create affine math transformation from:\r\n{wkt}\r\n{ex.Message}"); + } + + Assert.NotNull(mt); + Assert.NotNull(mt as AffineTransform); + + Assert.Equal(2, mt.DimSource); + Assert.Equal(2, mt.DimTarget); + + // test simple transform + double[] outPt = mt.Transform(Origin2D); + + Assert.Equal(2, outPt.Length); + Assert.Equal(3455869.17937689, outPt[0], 0.00000001); + Assert.Equal(5478710.88035753, outPt[1], 0.00000001); + } + + /// + /// Verifies that affine transforms now emit canonical WKT with the shared separator formatting. + /// + [Fact] + public void AffineTransformWkt_UsesCanonicalSpacingAndParameterOrder() + { + AffineTransform transform = CreateAffineTransform(); + string expected = + "PARAM_MT[\"Affine\", " + + "PARAMETER[\"num_row\", 3], " + + "PARAMETER[\"num_col\", 3], " + + "PARAMETER[\"elt_0_0\", 0.883485346527455], " + + "PARAMETER[\"elt_0_1\", -0.468458794848877], " + + "PARAMETER[\"elt_0_2\", 3455869.17937689], " + + "PARAMETER[\"elt_1_0\", 0.468458794848877], " + + "PARAMETER[\"elt_1_1\", 0.883485346527455], " + + "PARAMETER[\"elt_1_2\", 5478710.88035753], " + + "PARAMETER[\"elt_2_0\", 0], " + + "PARAMETER[\"elt_2_1\", 0], " + + "PARAMETER[\"elt_2_2\", 1]]"; + + Assert.Equal(expected, transform.WKT); + } + + /// + /// Verifies that affine transform WKT roundtrips through the parser without losing matrix values. + /// + [Fact] + public void AffineTransformWkt_RoundTripsThroughParser() + { + AffineTransform original = CreateAffineTransform(); + + MathTransform parsedTransform = MathTransformWktReader.Parse(original.WKT); + AffineTransform parsed = Assert.IsType(parsedTransform); + double[] originalResult = original.Transform(AffineSamplePoint); + double[] parsedResult = parsed.Transform(AffineSamplePoint); + + Assert.Equal(original.WKT, parsed.WKT); + Assert.Equal(originalResult[0], parsedResult[0], 12); + Assert.Equal(originalResult[1], parsedResult[1], 12); + } + + /// + /// Verifies that map projection WKT roundtrips through the parser without losing the projection behavior. + /// + [Fact] + public void MapProjectionWkt_RoundTripsThroughParser() + { + MapProjection original = CreateMercatorProjection(); + + MathTransform parsedTransform = MathTransformWktReader.Parse(original.WKT); + MapProjection parsed = Assert.IsAssignableFrom(parsedTransform); + double[] originalResult = original.Transform(GeographicSamplePoint); + double[] parsedResult = parsed.Transform(GeographicSamplePoint); + + Assert.Equal(original.WKT, parsed.WKT); + Assert.Equal(originalResult[0], parsedResult[0], 12); + Assert.Equal(originalResult[1], parsedResult[1], 12); + } + + /// + /// Verifies that inverse map projection WKT roundtrips through the parser and preserves inverse behavior. + /// + [Fact] + public void InverseMapProjectionWkt_RoundTripsThroughParser() + { + MapProjection forward = CreateMercatorProjection(); + MapProjection original = Assert.IsAssignableFrom(forward.Inverse()); + double[] projectedSamplePoint = forward.Transform(GeographicSamplePoint); + + MathTransform parsedTransform = MathTransformWktReader.Parse(original.WKT); + MapProjection parsed = Assert.IsAssignableFrom(parsedTransform); + double[] originalResult = original.Transform(projectedSamplePoint); + double[] parsedResult = parsed.Transform(projectedSamplePoint); + + Assert.Equal(original.WKT, parsed.WKT); + Assert.Equal(originalResult[0], parsedResult[0], 12); + Assert.Equal(originalResult[1], parsedResult[1], 12); + } + + /// + /// Verifies that affine transform nodes can be imported through both the string and node readers. + /// + [Fact] + public void AffineTransformNodeImport_RoundTripsThroughStringAndNodeReaders() + { + AffineTransform original = CreateAffineTransform(); + WktKeywordNode node = Assert.IsType(original.ToWktNode()); + + AffineTransform parsedFromString = Assert.IsType(MathTransformWktReader.Parse(node.ToString())); + AffineTransform parsedFromNode = Assert.IsType(MathTransformWktReader.ReadMathTransform(node)); + + AssertTransformMatchesExpected(original, parsedFromString, AffineSamplePoint); + AssertTransformMatchesExpected(original, parsedFromNode, AffineSamplePoint); + } + + /// + /// Verifies that identity transform nodes can be imported through both the string and node readers. + /// + [Fact] + public void IdentityTransformNodeImport_RoundTripsThroughStringAndNodeReaders() + { + var original = new IdentityMathTransform(IdentitySamplePoint.Length); + WktKeywordNode node = Assert.IsType(original.ToWktNode()); + + IdentityMathTransform parsedFromString = Assert.IsType(MathTransformWktReader.Parse(node.ToString())); + IdentityMathTransform parsedFromNode = Assert.IsType(MathTransformWktReader.ReadMathTransform(node)); + + AssertTransformMatchesExpected(original, parsedFromString, IdentitySamplePoint); + AssertTransformMatchesExpected(original, parsedFromNode, IdentitySamplePoint); + } + + /// + /// Verifies that map projection nodes can be imported through both the string and node readers. + /// + [Fact] + public void MapProjectionNodeImport_RoundTripsThroughStringAndNodeReaders() + { + MapProjection original = CreateMercatorProjection(); + WktKeywordNode node = Assert.IsType(original.ToWktNode()); + + MapProjection parsedFromString = Assert.IsAssignableFrom(MathTransformWktReader.Parse(node.ToString())); + MapProjection parsedFromNode = Assert.IsAssignableFrom(MathTransformWktReader.ReadMathTransform(node)); + + AssertTransformMatchesExpected(original, parsedFromString, GeographicSamplePoint); + AssertTransformMatchesExpected(original, parsedFromNode, GeographicSamplePoint); + } + + /// + /// Verifies that inverse map projection nodes can be imported through both the string and node readers. + /// + [Fact] + public void InverseMapProjectionNodeImport_RoundTripsThroughStringAndNodeReaders() + { + MapProjection forward = CreateMercatorProjection(); + MapProjection original = Assert.IsAssignableFrom(forward.Inverse()); + WktKeywordNode node = Assert.IsType(original.ToWktNode()); + double[] projectedSamplePoint = forward.Transform(GeographicSamplePoint); + + MapProjection parsedFromString = Assert.IsAssignableFrom(MathTransformWktReader.Parse(node.ToString())); + MapProjection parsedFromNode = Assert.IsAssignableFrom(MathTransformWktReader.ReadInverseMathTransform(node)); + + AssertTransformMatchesExpected(original, parsedFromString, projectedSamplePoint); + AssertTransformMatchesExpected(original, parsedFromNode, projectedSamplePoint); + } + + /// + /// Verifies that nested inverse map projection nodes resolve back to the forward projection for both readers. + /// + [Fact] + public void NestedInverseMapProjectionNodeImport_RoundTripsThroughStringAndNodeReaders() + { + MapProjection original = CreateMercatorProjection(); + WktKeywordNode inverseNode = Assert.IsType(original.Inverse().ToWktNode()); + var nestedInverseNode = new WktKeywordNode("INVERSE_MT", inverseNode); + + MapProjection parsedFromString = Assert.IsAssignableFrom(MathTransformWktReader.Parse(nestedInverseNode.ToString())); + MapProjection parsedFromNode = Assert.IsAssignableFrom(MathTransformWktReader.ReadInverseMathTransform(nestedInverseNode)); + + AssertTransformMatchesExpected(original, parsedFromString, GeographicSamplePoint, assertWkt: false); + AssertTransformMatchesExpected(original, parsedFromNode, GeographicSamplePoint, assertWkt: false); + Assert.Equal(parsedFromString.WKT, parsedFromNode.WKT); + } + + /// + /// Verifies that identity transform WKT roundtrips through the parser without changing dimensionality. + /// + [Fact] + public void IdentityTransformWkt_RoundTripsThroughParser() + { + var original = new IdentityMathTransform(IdentitySamplePoint.Length); + + MathTransform parsedTransform = MathTransformWktReader.Parse(original.WKT); + IdentityMathTransform parsed = Assert.IsType(parsedTransform); + double[] parsedResult = parsed.Transform(IdentitySamplePoint); + + Assert.Equal(original.WKT, parsed.WKT); + Assert.Equal(IdentitySamplePoint, parsedResult); + } + + /// + /// Verifies unknown top-level math transform keywords surface as parser failures. + /// + [Fact] + public void ParseWithUnknownRootKeywordThrowsWktParseException() + { + const string wkt = """UNKNOWN_MT["Custom"]"""; + + WktParseException exception = Assert.Throws(() => MathTransformWktReader.Parse(wkt)); + + Assert.Contains("not recognized", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies malformed affine metadata is reported as a parser failure. + /// + [Fact] + public void ParseAffineTransformWithInvalidRowCountThrowsWktParseException() + { + const string wkt = """PARAM_MT["Affine",PARAMETER["num_row",0],PARAMETER["num_col",3]]"""; + + WktParseException exception = Assert.Throws(() => MathTransformWktReader.Parse(wkt)); + + Assert.Contains("num_row", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + /// + /// MathTransformWktReader parses real number with exponent incorrectly. + /// + /// The wkt value. + [Theory] + [InlineData("PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 6.12303176911189E-17]]")] + [InlineData("PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 5.235E4]]")] + [InlineData("PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 5.235E+4]]")] + public void TestMathTransformWktReaderExponencialNumberParsingIssue(string wkt) + { + // string wkt = "PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 6.12303176911189E-17]]"; + MathTransform mt = default!; + + try + { + mt = MathTransformWktReader.Parse(wkt); + } + catch (ArgumentException ex) + { + Assert.Fail($"Failed to parse WKT of affine math transformation from:\r\n{wkt}\r\n{ex.Message}"); + } + catch (Exception e) + { + Assert.Fail($"Could not create affine math transformation from:\r\n{wkt}\r\n{e.Message}"); + } + + Assert.NotNull(mt); + Assert.NotNull(mt as AffineTransform); + } + + private static AffineTransform CreateAffineTransform() + { + return new AffineTransform( + 0.883485346527455, + -0.468458794848877, + 3455869.17937689, + 0.468458794848877, + 0.883485346527455, + 5478710.88035753); + } + + private static MapProjection CreateMercatorProjection() + { + return Assert.IsAssignableFrom(ProjectionsRegistry.CreateProjection("mercator", CreateMercatorParameters())); + } + + private static List CreateMercatorParameters() + { + return + [ + new("semi_major", Ellipsoid.WGS84.SemiMajorAxis), + new("semi_minor", Ellipsoid.WGS84.SemiMinorAxis), + new("central_meridian", 0d), + new("latitude_of_origin", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + new("unit", 1d), + ]; + } + + private static void AssertTransformMatchesExpected(MathTransform expected, MathTransform actual, double[] samplePoint, bool assertWkt = true) + { + double[] expectedResult = expected.Transform(samplePoint); + double[] actualResult = actual.Transform(samplePoint); + + Assert.Equal(expected.DimSource, actual.DimSource); + Assert.Equal(expected.DimTarget, actual.DimTarget); + if (assertWkt) + { + Assert.Equal(expected.WKT, actual.WKT); + } + + Assert.Equal(expectedResult.Length, actualResult.Length); + + for (int index = 0; index < expectedResult.Length; index++) + { + Assert.Equal(expectedResult[index], actualResult[index], 12); + } + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageMatrix.cs b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageMatrix.cs new file mode 100644 index 00000000..487ad56a --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageMatrix.cs @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Linq; +using ProjNet.IO.CoordinateSystems; + +/// +/// Provides the milestone-40 WKT2 keyword coverage matrix. +/// +internal static class Wkt2KeywordCoverageMatrix +{ + private const string WktReader = nameof(CoordinateSystemWktReader); + private const string RootDispatchReference = WktReader + ".Parse, " + WktReader + ".TryParseNativeWkt2"; + private const string GeodeticReference = WktReader + ".ReadWkt2GeodeticCoordinateReferenceSystem"; + private const string CoordinateSystemReference = WktReader + ".ReadWkt2CoordinateSystemDefinition"; + private const string AxisReference = WktReader + ".ReadWkt2Axis, " + WktReader + ".ReadWkt2AxisDefinition, " + WktReader + ".ParseWkt2AxisOrientation"; + private const string HorizontalDatumReference = WktReader + ".ReadWkt2HorizontalDatum"; + private const string EngineeringReference = WktReader + ".ReadWkt2EngineeringCoordinateSystem, " + WktReader + ".ReadWkt2EngineeringDatum, " + WktReader + ".ReadWkt2AxisDefinition, " + WktReader + ".ReadWkt2Unit"; + private const string EnsembleReference = WktReader + ".ReadWkt2HorizontalDatumEnsemble, " + WktReader + ".ReadWkt2VerticalDatumEnsemble, " + WktReader + ".ReadWkt2DatumEnsemble, " + WktReader + ".ReadWkt2DatumEnsembleMember, " + WktReader + ".ReadWkt2DatumEnsembleAccuracy"; + private const string EllipsoidReference = WktReader + ".ReadWkt2Ellipsoid"; + private const string PrimeMeridianReference = WktReader + ".ReadWkt2PrimeMeridian"; + private const string AngularUnitReference = WktReader + ".ReadWkt2AngularUnit, " + WktReader + ".ReadWkt2Unit"; + private const string LinearUnitReference = WktReader + ".ReadWkt2LinearUnit, " + WktReader + ".ReadWkt2Unit"; + private const string MetadataSkipReference = WktReader + ".ShouldSkipWkt2MetadataNode"; + private const string TemporalReference = WktReader + ".ReadWkt2TemporalCoordinateSystem, " + WktReader + ".ReadWkt2TemporalDatum, " + WktReader + ".ReadWkt2TimeUnit, " + WktReader + ".ReadWkt2Unit"; + private const string ParametricReference = WktReader + ".ReadWkt2ParametricCoordinateSystem, " + WktReader + ".ReadWkt2ParametricDatum, " + WktReader + ".ReadWkt2ParametricUnit, " + WktReader + ".ReadWkt2Unit"; + private const string ProjectedReference = WktReader + ".ReadWkt2ProjectedCoordinateSystem"; + private const string DerivedProjectedReference = WktReader + ".ReadWkt2DerivedProjectedCoordinateSystem"; + private const string BaseGeographicReference = WktReader + ".ReadWkt2BaseGeographicCoordinateSystem"; + private const string BaseProjectedReference = WktReader + ".ReadWkt2BaseProjectedCoordinateSystem"; + private const string ConversionReference = WktReader + ".ReadWkt2Conversion, " + WktReader + ".ReadWkt2ProjectionMethod, " + WktReader + ".ReadWkt2ProjectionParameter, " + WktReader + ".NormalizeWkt2ProjectionParameterName"; + private const string DerivingConversionReference = WktReader + ".ReadWkt2DerivingConversion, " + WktReader + ".ReadWkt2ProjectionMethod, " + WktReader + ".ReadWkt2ProjectionParameter, " + WktReader + ".NormalizeWkt2ProjectionParameterName"; + private const string OperationReference = WktReader + ".ReadWkt2CoordinateOperation, " + WktReader + ".ReadWkt2CoordinateOperationParameter, " + WktReader + ".ReadWkt2ConcatenatedOperation, " + WktReader + ".ReadWkt2ConcatenatedOperationStep"; + private const string VerticalReference = WktReader + ".ReadWkt2VerticalCoordinateSystem, " + WktReader + ".ReadWkt2VerticalDatum"; + private const string CompoundReference = WktReader + ".ReadWkt2CompoundCoordinateSystem"; + private const string BoundReference = WktReader + ".ReadWkt2BoundCoordinateSystem, " + WktReader + ".ReadWkt2AbridgedTransformationDefinition, " + WktReader + ".ReadWkt2AbridgedTransformationParameter, " + WktReader + ".ReadWkt2AbridgedTransformationParameterFile, BoundCoordinateSystemSupport.CreateBoundTransformation, BoundCoordinateSystemSupport.AssignTransformationParameter"; + private const string IdentifierReference = WktReader + ".ReadWkt2Axis, " + WktReader + ".ReadWkt2HorizontalDatum, " + WktReader + ".ReadWkt2HorizontalDatumEnsemble, " + WktReader + ".ReadWkt2DatumEnsemble, " + WktReader + ".ReadWkt2DatumEnsembleMember, " + WktReader + ".ReadWkt2Ellipsoid, " + WktReader + ".ReadWkt2PrimeMeridian, " + WktReader + ".ReadWkt2ProjectedCoordinateSystem, " + WktReader + ".ReadWkt2DerivedProjectedCoordinateSystem, " + WktReader + ".ReadWkt2BaseProjectedCoordinateSystem, " + WktReader + ".ReadWkt2VerticalCoordinateSystem, " + WktReader + ".ReadWkt2VerticalDatumEnsemble, " + WktReader + ".ReadWkt2CompoundCoordinateSystem, " + WktReader + ".ReadWkt2AbridgedTransformationDefinition, " + WktReader + ".ReadIdentifierWithUnknownCode"; + private const string DefaultUnsupportedReference = WktReader + ".ReadWkt2GeodeticCoordinateReferenceSystem, " + WktReader + ".ReadWkt2ProjectedCoordinateSystem, " + WktReader + ".ReadWkt2VerticalCoordinateSystem, " + WktReader + ".ReadWkt2BoundCoordinateSystemComponent, " + WktReader + ".ParseNormalizedWkt"; + private const string UnsupportedTopLevelReference = WktReader + ".Parse, " + WktReader + ".TryParseNativeWkt2, " + WktReader + ".ParseNormalizedWkt"; + private const string NormalizationReference = WktReader + ".NormalizeWkt, " + WktReader + ".ParseNormalizedWkt"; + + /// + /// Gets the current WKT2 coverage rows for all tracked milestone-40 keywords. + /// + internal static IReadOnlyList Rows { get; } = CreateRows(); + + private static Wkt2KeywordCoverageRow[] CreateRows() => + new Wkt2KeywordCoverageRow[] + { + Native("ABRIDGEDTRANSFORMATION", BoundReference, "Parsed by the first-class BoundCRS transformation reader."), + Ignored("ANCHOR", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Ignored("ANCHOREPOCH", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("ANGLEUNIT", AngularUnitReference, "Parsed directly at CRS, axis, and parameter sites."), + Ignored("AREA", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("AXIS", AxisReference, "Parsed directly by the native axis reader."), + Unsupported("AXISMAXVALUE", AxisReference, "No native axis-range branch exists."), + Unsupported("AXISMINVALUE", AxisReference, "No native axis-range branch exists."), + Partial("BASEENGCRS", EngineeringReference, "Engineering CRS components are now parsed natively, but no derived reader consumes BASEENGCRS yet."), + Native("BASEGEODCRS", $"{GeodeticReference}, {ProjectedReference}, {BaseGeographicReference}", "Handled natively for supported derived geodetic and projected CRS definitions."), + Native("BASEGEOGCRS", $"{GeodeticReference}, {ProjectedReference}, {BaseGeographicReference}", "Handled natively for supported derived geodetic and projected CRS definitions."), + Partial("BASEPARAMCRS", ParametricReference, "Parametric CRS components are now parsed natively, but no derived reader consumes BASEPARAMCRS yet."), + Native("BASEPROJCRS", $"{DerivedProjectedReference}, {BaseProjectedReference}", "Handled natively inside the supported affine derived projected CRS slice."), + Partial("BASETIMECRS", TemporalReference, "Temporal CRS components are now parsed natively, but no derived reader consumes BASETIMECRS yet."), + Unsupported("BASEVERTCRS", UnsupportedTopLevelReference, "No derived vertical CRS reader path exists yet."), + Ignored("BBOX", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Unsupported("BEARING", PrimeMeridianReference, "The prime-meridian reader has no BEARING branch."), + Native("BOUNDCRS", $"{RootDispatchReference}, {BoundReference}", "Handled natively and retained as a first-class bound coordinate system for the currently supported subset."), + Unsupported("CALENDAR", TemporalReference, "Temporal datum parsing still does not consume CALENDAR metadata."), + Unsupported("CITATION", DefaultUnsupportedReference, "The native reader does not classify CITATION as skippable metadata."), + Native("COMPOUNDCRS", $"{RootDispatchReference}, {CompoundReference}", "Handled natively by the compound CRS reader."), + Native("CONCATENATEDOPERATION", $"{RootDispatchReference}, {OperationReference}", "Handled natively by the concatenated operation reader."), + Native("CONVERSION", ConversionReference, "Handled natively inside projected CRS definitions."), + Unsupported("COORDEPOCH", UnsupportedTopLevelReference, "Coordinate metadata parsing is not implemented."), + Unsupported("COORDINATEMETADATA", UnsupportedTopLevelReference, "No coordinate metadata root reader path exists yet."), + Native("COORDINATEOPERATION", $"{RootDispatchReference}, {OperationReference}", "Handled natively by the standalone coordinate operation reader."), + Native("CS", CoordinateSystemReference, "Parsed directly by the native coordinate-system definition reader."), + Native("DATUM", HorizontalDatumReference, "Parsed directly by the native horizontal-datum reader."), + Ignored("DEFININGTRANSFORMATION", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("DERIVEDPROJCRS", $"{RootDispatchReference}, {DerivedProjectedReference}", "Handled natively for the supported affine derived projected CRS slice."), + Native("DERIVINGCONVERSION", $"{GeodeticReference}, {DerivedProjectedReference}, {DerivingConversionReference}", "Handled natively for supported affine derived geographic and projected CRS definitions."), + Ignored("DYNAMIC", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("EDATUM", EngineeringReference, "Parsed directly by the engineering datum reader."), + Native("ELLIPSOID", EllipsoidReference, "Parsed directly by the native ellipsoid reader."), + Native("ENGCRS", $"{RootDispatchReference}, {EngineeringReference}", "Handled natively through the engineering CRS reader."), + Native("ENGINEERINGCRS", $"{RootDispatchReference}, {EngineeringReference}", "Handled natively through the engineering CRS reader alias."), + Native("ENGINEERINGDATUM", EngineeringReference, "Parsed directly by the engineering datum reader alias."), + Native("ENSEMBLE", EnsembleReference, "Parsed directly for supported geodetic and vertical datum ensemble definitions."), + Native("ENSEMBLEACCURACY", EnsembleReference, "Parsed directly as part of supported datum ensemble definitions."), + Unsupported("EPOCH", UnsupportedTopLevelReference, "Coordinate metadata parsing is not implemented."), + Ignored("FRAMEEPOCH", MetadataSkipReference, "Tolerated transitively inside skipped DYNAMIC metadata."), + Native("GEODCRS", $"{RootDispatchReference}, {GeodeticReference}", "Handled natively through the geodetic CRS reader."), + Native("GEODETICCRS", $"{RootDispatchReference}, {GeodeticReference}", "Handled natively through the geodetic CRS reader."), + Unsupported("GEODETICDATUM", DefaultUnsupportedReference, "The native reader expects DATUM rather than GEODETICDATUM."), + Native("GEOGCRS", $"{RootDispatchReference}, {GeodeticReference}", "Handled natively through the geodetic CRS reader."), + Unsupported("GEOGRAPHICCRS", UnsupportedTopLevelReference, "No direct GEOGRAPHICCRS branch or normalization map exists."), + Ignored("GEOIDMODEL", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("ID", IdentifierReference, "Parsed directly at CRS, unit, datum, vertical datum, and BoundCRS operation sites."), + Unsupported("INTERPOLATIONCRS", DefaultUnsupportedReference, "No coordinate-operation reader path consumes interpolation CRS blocks yet."), + Native("LENGTHUNIT", LinearUnitReference, "Parsed directly at CRS, axis, parameter, and vertical sites."), + Native("MEMBER", EnsembleReference, "Parsed directly as part of supported datum ensemble definitions."), + Ignored("MERIDIAN", MetadataSkipReference, "Currently tolerated as skippable metadata instead of being retained."), + Native("METHOD", ConversionReference, "Parsed directly in conversion and abridged-transformation blocks."), + Ignored("MODEL", MetadataSkipReference, "Tolerated transitively inside skipped DYNAMIC metadata."), + Unsupported("OPERATIONACCURACY", BoundReference, "The abridged transformation reader does not consume operation-accuracy nodes."), + Ignored("ORDER", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("PARAMETER", $"{ConversionReference}, {OperationReference}", "Parsed directly in projection, abridged-transformation, and coordinate-operation blocks."), + Native("PARAMETERFILE", BoundReference, "Parsed directly for supported BoundCRS parameter-file transformations."), + Native("PARAMETRICCRS", $"{RootDispatchReference}, {ParametricReference}", "Handled natively through the parametric CRS reader."), + Native("PARAMETRICDATUM", ParametricReference, "Parsed directly by the parametric datum reader alias."), + Native("PARAMETRICUNIT", ParametricReference, "Parsed directly by the parametric unit reader."), + Native("PDATUM", ParametricReference, "Parsed directly by the parametric datum reader."), + Unsupported("POINTMOTIONOPERATION", UnsupportedTopLevelReference, "No point-motion operation reader path exists yet."), + Native("PRIMEM", PrimeMeridianReference, "Parsed directly by the native prime-meridian reader."), + Unsupported("PRIMEMERIDIAN", DefaultUnsupportedReference, "The native reader expects PRIMEM rather than PRIMEMERIDIAN."), + Native("PROJCRS", $"{RootDispatchReference}, {ProjectedReference}", "Handled natively through the projected CRS reader."), + LegacyNormalized("PROJECTEDCRS", NormalizationReference, "Only handled through NormalizeWkt -> PROJCS fallback."), + Unsupported("RANGEMEANING", AxisReference, "The native axis reader has no range-meaning branch."), + Ignored("REMARK", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("SCALEUNIT", $"{ConversionReference}, {EngineeringReference}", "Parsed directly for WKT2 projection, BoundCRS parameter, and engineering CRS units."), + Ignored("SCOPE", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("SOURCECRS", $"{BoundReference}, {OperationReference}", "Parsed directly inside supported BoundCRS and coordinate-operation definitions."), + Native("STEP", OperationReference, "Parsed directly inside supported concatenated operation definitions."), + Native("TARGETCRS", $"{BoundReference}, {OperationReference}", "Parsed directly inside supported BoundCRS and coordinate-operation definitions."), + Native("TDATUM", TemporalReference, "Parsed directly by the temporal datum reader."), + Unsupported("TEMPORALQUANTITY", TemporalReference, "Temporal CRS parsing still expects TIMEUNIT rather than TEMPORALQUANTITY."), + Native("TIMECRS", $"{RootDispatchReference}, {TemporalReference}", "Handled natively through the temporal CRS reader."), + Native("TIMEDATUM", TemporalReference, "Parsed directly by the temporal datum reader alias."), + Ignored("TIMEEXTENT", MetadataSkipReference, "Tolerated transitively inside skipped USAGE metadata."), + Native("TIMEORIGIN", TemporalReference, "Parsed directly as part of temporal datum definitions."), + Native("TIMEUNIT", TemporalReference, "Parsed directly by the temporal unit reader."), + Unsupported("TRF", UnsupportedTopLevelReference, "No dedicated terrestrial reference-frame reader path exists yet."), + Unsupported("URI", DefaultUnsupportedReference, "The native reader does not classify URI as skippable metadata."), + Ignored("USAGE", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("VDATUM", VerticalReference, "Parsed directly by the native vertical-datum reader."), + Ignored("VELOCITYGRID", MetadataSkipReference, "Tolerated transitively inside skipped DYNAMIC metadata."), + Ignored("VERSION", MetadataSkipReference, "Accepted as non-operational metadata and skipped."), + Native("VERTCRS", $"{RootDispatchReference}, {VerticalReference}", "Handled natively through the vertical CRS reader."), + Ignored("VERTICALEXTENT", MetadataSkipReference, "Tolerated transitively inside skipped USAGE metadata."), + Unsupported("VERTICALCRS", UnsupportedTopLevelReference, "No direct VERTICALCRS branch or normalization map exists."), + Unsupported("VERTICALDATUM", UnsupportedTopLevelReference, "The native reader expects VDATUM rather than VERTICALDATUM."), + Unsupported("VRF", UnsupportedTopLevelReference, "No dedicated vertical reference-frame reader path exists yet."), + } + .OrderBy(row => row.Keyword, StringComparer.Ordinal) + .ToArray(); + + private static Wkt2KeywordCoverageRow Native(string keyword, string reference, string notes) => + new(keyword, Wkt2KeywordSupportStatus.Native, reference, notes); + + private static Wkt2KeywordCoverageRow LegacyNormalized(string keyword, string reference, string notes) => + new(keyword, Wkt2KeywordSupportStatus.LegacyNormalized, reference, notes); + + private static Wkt2KeywordCoverageRow Partial(string keyword, string reference, string notes) => + new(keyword, Wkt2KeywordSupportStatus.Partial, reference, notes); + + private static Wkt2KeywordCoverageRow Ignored(string keyword, string reference, string notes) => + new(keyword, Wkt2KeywordSupportStatus.Ignored, reference, notes); + + private static Wkt2KeywordCoverageRow Unsupported(string keyword, string reference, string notes) => + new(keyword, Wkt2KeywordSupportStatus.Unsupported, reference, notes); +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageMatrixTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageMatrixTests.cs new file mode 100644 index 00000000..5342e08e --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageMatrixTests.cs @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +/// +/// Guards the milestone-40 WKT2 coverage matrix. +/// +public class Wkt2KeywordCoverageMatrixTests +{ + /// + /// Ensures every tracked keyword has exactly one coverage row. + /// + [Fact] + public void Rows_CoverEveryTrackedKeywordExactlyOnce() + { + Assert.Equal( + Wkt2KeywordInventoryReference.AllKeywords.OrderBy(keyword => keyword, StringComparer.Ordinal), + Wkt2KeywordCoverageMatrix.Rows.Select(row => row.Keyword)); + + Assert.Equal( + Wkt2KeywordCoverageMatrix.Rows.Count, + Wkt2KeywordCoverageMatrix.Rows.Select(row => row.Keyword).Distinct(StringComparer.Ordinal).Count()); + } + + /// + /// Ensures the matrix remains diff-friendly and fully populated. + /// + [Fact] + public void Rows_AreSortedAndPopulated() + { + Assert.Equal( + Wkt2KeywordCoverageMatrix.Rows.OrderBy(row => row.Keyword, StringComparer.Ordinal).Select(row => row.Keyword), + Wkt2KeywordCoverageMatrix.Rows.Select(row => row.Keyword)); + + Assert.All( + Wkt2KeywordCoverageMatrix.Rows, + row => + { + CoverageReferenceAssert.AssertSymbolReferenceList(row.Reference); + Assert.False(string.IsNullOrWhiteSpace(row.Notes)); + }); + } + + /// + /// Ensures the matrix keeps the expected milestone anchor classifications. + /// + [Fact] + public void AnchorKeywords_KeepTheirExpectedStatuses() + { + var lookup = Wkt2KeywordCoverageMatrix.Rows + .ToDictionary(row => row.Keyword, row => row.Status, StringComparer.Ordinal); + + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["BOUNDCRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["GEOGCRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["PROJCRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.LegacyNormalized, lookup["PROJECTEDCRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Ignored, lookup["USAGE"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["ENSEMBLE"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["CONCATENATEDOPERATION"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["COORDINATEOPERATION"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["ENGCRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["PARAMETRICCRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Native, lookup["TIMECRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Partial, lookup["BASEENGCRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Partial, lookup["BASETIMECRS"]); + Assert.Equal(Wkt2KeywordSupportStatus.Partial, lookup["BASEPARAMCRS"]); + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageRow.cs b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageRow.cs new file mode 100644 index 00000000..3400380b --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordCoverageRow.cs @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +/// +/// Represents one row of the milestone-40 WKT2 coverage inventory. +/// +/// The WKT2 keyword. +/// The current support classification. +/// The primary reader symbol reference for that classification. +/// A short explanation of the current behavior. +internal sealed record Wkt2KeywordCoverageRow( + string Keyword, + Wkt2KeywordSupportStatus Status, + string Reference, + string Notes); diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordInventoryReference.cs b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordInventoryReference.cs new file mode 100644 index 00000000..25689d36 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordInventoryReference.cs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System.Collections.Generic; + +/// +/// Provides the milestone-40 reference keyword set for the WKT2 inventory work. +/// +/// +/// The primary inventory rows are derived from the ISO 19162:2019 CRS/operation surface and +/// cross-checked against the vendored PROJ WKT2 grammar in +/// spec\PROJ\src\wkt2_generated_parser.h plus the related PROJ WKT2 unit fixtures. +/// Additional keywords capture alternate long-form spellings and reference-frame forms that +/// appear in the local PROJ reference material and still need explicit coverage classification +/// in the next inventory steps. +/// +internal static class Wkt2KeywordInventoryReference +{ + /// + /// Gets the primary milestone-40 inventory rows. + /// + internal static IReadOnlyList InventoryKeywords { get; } = + [ + "ABRIDGEDTRANSFORMATION", + "ANCHOR", + "ANCHOREPOCH", + "ANGLEUNIT", + "AREA", + "AXIS", + "AXISMAXVALUE", + "AXISMINVALUE", + "BASEENGCRS", + "BASEGEODCRS", + "BASEGEOGCRS", + "BASEPARAMCRS", + "BASEPROJCRS", + "BASETIMECRS", + "BASEVERTCRS", + "BBOX", + "BEARING", + "BOUNDCRS", + "CALENDAR", + "CITATION", + "COMPOUNDCRS", + "CONCATENATEDOPERATION", + "CONVERSION", + "COORDEPOCH", + "COORDINATEMETADATA", + "COORDINATEOPERATION", + "CS", + "DATUM", + "DEFININGTRANSFORMATION", + "DERIVEDPROJCRS", + "DERIVINGCONVERSION", + "DYNAMIC", + "EDATUM", + "ELLIPSOID", + "ENGCRS", + "ENSEMBLE", + "ENSEMBLEACCURACY", + "EPOCH", + "FRAMEEPOCH", + "GEODCRS", + "GEOGCRS", + "GEOIDMODEL", + "ID", + "INTERPOLATIONCRS", + "LENGTHUNIT", + "MEMBER", + "MERIDIAN", + "METHOD", + "MODEL", + "OPERATIONACCURACY", + "ORDER", + "PARAMETER", + "PARAMETERFILE", + "PARAMETRICCRS", + "PARAMETRICUNIT", + "PDATUM", + "POINTMOTIONOPERATION", + "PRIMEM", + "PROJCRS", + "RANGEMEANING", + "REMARK", + "SCALEUNIT", + "SCOPE", + "SOURCECRS", + "STEP", + "TARGETCRS", + "TDATUM", + "TEMPORALQUANTITY", + "TIMECRS", + "TIMEEXTENT", + "TIMEORIGIN", + "TIMEUNIT", + "URI", + "USAGE", + "VDATUM", + "VELOCITYGRID", + "VERSION", + "VERTCRS", + "VERTICALEXTENT", + ]; + + /// + /// Gets additional spellings that occur in the local PROJ WKT2 references. + /// + internal static IReadOnlyList AdditionalKeywords { get; } = + [ + "ENGINEERINGCRS", + "ENGINEERINGDATUM", + "GEODETICCRS", + "GEODETICDATUM", + "GEOGRAPHICCRS", + "PARAMETRICDATUM", + "PRIMEMERIDIAN", + "PROJECTEDCRS", + "TIMEDATUM", + "TRF", + "VERTICALCRS", + "VERTICALDATUM", + "VRF", + ]; + + /// + /// Gets the full milestone-40 WKT2 keyword universe tracked so far. + /// + internal static IReadOnlyList AllKeywords { get; } = + [ + .. InventoryKeywords, + .. AdditionalKeywords, + ]; +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordInventoryReferenceTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordInventoryReferenceTests.cs new file mode 100644 index 00000000..d1f5f4ea --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordInventoryReferenceTests.cs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +/// +/// Guards the milestone-40 WKT2 keyword reference set. +/// +public class Wkt2KeywordInventoryReferenceTests +{ + /// + /// Ensures the primary inventory rows stay stable and easy to diff. + /// + [Fact] + public void InventoryKeywords_AreUniqueSortedAndUppercase() + { + Assert.Equal( + Wkt2KeywordInventoryReference.InventoryKeywords.OrderBy(keyword => keyword, StringComparer.Ordinal), + Wkt2KeywordInventoryReference.InventoryKeywords); + + Assert.Equal( + Wkt2KeywordInventoryReference.InventoryKeywords.Count, + Wkt2KeywordInventoryReference.InventoryKeywords.Distinct(StringComparer.Ordinal).Count()); + + Assert.All( + Wkt2KeywordInventoryReference.InventoryKeywords, + keyword => Assert.Equal(keyword, keyword.ToUpperInvariant())); + } + + /// + /// Ensures the secondary spellings are tracked separately from the primary inventory rows. + /// + [Fact] + public void AdditionalKeywords_DoNotOverlapWithPrimaryInventoryRows() + { + IReadOnlyCollection overlap = Wkt2KeywordInventoryReference.InventoryKeywords + .Intersect(Wkt2KeywordInventoryReference.AdditionalKeywords, StringComparer.Ordinal) + .ToArray(); + + Assert.Empty(overlap); + } + + /// + /// Ensures the inventory contains the milestone anchor keywords for the current WKT2 backlog. + /// + [Fact] + public void AllKeywords_ContainMilestone40AnchorKeywords() + { + string[] expectedKeywords = + [ + "ABRIDGEDTRANSFORMATION", + "BOUNDCRS", + "COMPOUNDCRS", + "COORDINATEMETADATA", + "DERIVEDPROJCRS", + "ENGCRS", + "ENSEMBLE", + "GEODCRS", + "GEOGCRS", + "PARAMETERFILE", + "PARAMETRICCRS", + "PROJCRS", + "TIMECRS", + "VERTCRS", + ]; + + foreach (string keyword in expectedKeywords) + { + Assert.Contains(keyword, Wkt2KeywordInventoryReference.AllKeywords); + } + } + + /// + /// Ensures the WKT2 reference set does not silently pull in WKT1-only structural keywords. + /// + /// A WKT1-only compatibility keyword. + [Theory] + [InlineData("PROJECTION")] + [InlineData("SPHEROID")] + [InlineData("UNIT")] + public void AllKeywords_ExcludeWkt1OnlyStructuralKeywords(string keyword) + { + Assert.DoesNotContain(keyword, Wkt2KeywordInventoryReference.AllKeywords); + } +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordSupportStatus.cs b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordSupportStatus.cs new file mode 100644 index 00000000..bc7f7eaf --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/Wkt2KeywordSupportStatus.cs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +/// +/// Describes how a WKT2 keyword is currently handled by the milestone-40 inventory. +/// +internal enum Wkt2KeywordSupportStatus +{ + /// + /// The keyword is handled directly by the native WKT2 reader. + /// + Native, + + /// + /// The keyword is only handled through the legacy normalization fallback. + /// + LegacyNormalized, + + /// + /// The keyword is partially supported through reusable native reader components but not yet as a complete standalone construct. + /// + Partial, + + /// + /// The keyword is accepted but skipped as non-operational metadata. + /// + Ignored, + + /// + /// The keyword is currently outside the supported reader surface. + /// + Unsupported, +} diff --git a/test/ProjNet.Tests/IO/CoordinateSystems/WktTokenizerTests.cs b/test/ProjNet.Tests/IO/CoordinateSystems/WktTokenizerTests.cs new file mode 100644 index 00000000..43c52244 --- /dev/null +++ b/test/ProjNet.Tests/IO/CoordinateSystems/WktTokenizerTests.cs @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.CoordinateSystems; + +using System; +using System.Text; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Verifies tokenizer behavior for edge-case WKT inputs. +/// +public class WktTokenizerTests +{ + /// + /// Verifies that empty input immediately reports end-of-file. + /// + [Fact] + public void NextTokenOnEmptyInputReturnsEof() + { + var tokenizer = new WktTokenizer(string.Empty); + + TokenType token = tokenizer.NextToken(); + + Assert.Equal(TokenType.Eof, token); + Assert.True(tokenizer.IsEndOfInput); + Assert.Equal(string.Empty, tokenizer.GetStringValue()); + } + + /// + /// Verifies that whitespace and end-of-line tokens are emitted when whitespace is significant. + /// + [Fact] + public void NextTokenWhenWhitespaceIsSignificantReturnsWhitespaceAndEolTokens() + { + var tokenizer = new WktTokenizer("A \r\nB", ignoreWhitespaceByDefault: false); + + Assert.Equal(TokenType.Word, tokenizer.NextToken()); + Assert.Equal("A", tokenizer.GetStringValue()); + + Assert.Equal(TokenType.Whitespace, tokenizer.NextToken()); + Assert.Equal(TokenType.Eol, tokenizer.NextToken()); + + Assert.Equal(TokenType.Word, tokenizer.NextToken()); + Assert.Equal("B", tokenizer.GetStringValue()); + Assert.Equal(2, tokenizer.LineNumber); + Assert.Equal(1, tokenizer.Column); + } + + /// + /// Verifies numeric parsing for precision-boundary and signed scientific values. + /// + /// Input token to parse. + /// Expected parsed value. + [Theory] + [InlineData("-1.7976931348623157E+308", -1.7976931348623157E+308)] + [InlineData("2.2250738585072014E-308", 2.2250738585072014E-308)] + [InlineData("5.235E+4", 52350d)] + [InlineData("1.", 1d)] + [InlineData("-.5", -0.5d)] + [InlineData("+.75", 0.75d)] + [InlineData("-0.0", -0d)] + public void GetNumericValueParsesBoundaryNumbers(string tokenText, double expected) + { + var tokenizer = new WktTokenizer(tokenText); + + Assert.Equal(TokenType.Number, tokenizer.NextToken()); + Assert.True(tokenizer.TryGetNumericValue(out double parsed)); + Assert.Equal(expected, parsed); + Assert.Equal(expected, tokenizer.GetNumericValue()); + } + + /// + /// Verifies malformed scientific notation is split at the valid numeric prefix instead of consuming the invalid suffix. + /// + /// Input token stream to parse. + /// Expected numeric prefix token. + /// Expected remaining token texts after the numeric prefix. + [Theory] + [InlineData("1e", "1", "e")] + [InlineData(".5e-", ".5", "e", "-")] + public void NextTokenOnMalformedScientificNotationStopsAtValidNumericPrefix(string tokenText, string expectedNumberToken, params string[] expectedRemainderTokens) + { + ArgumentNullException.ThrowIfNull(expectedRemainderTokens); + var tokenizer = new WktTokenizer(tokenText); + + Assert.Equal(TokenType.Number, tokenizer.NextToken()); + Assert.Equal(expectedNumberToken, tokenizer.GetStringValue()); + Assert.True(tokenizer.TryGetNumericValue(out _)); + + foreach (string expectedToken in expectedRemainderTokens) + { + Assert.NotEqual(TokenType.Eof, tokenizer.NextToken()); + Assert.Equal(expectedToken, tokenizer.GetStringValue()); + } + + Assert.Equal(TokenType.Eof, tokenizer.NextToken()); + } + + /// + /// Verifies malformed quoted input surfaces an explicit parse error. + /// + [Fact] + public void ReadDoubleQuotedWordWithUnterminatedInputThrows() + { + var tokenizer = new WktTokenizer("\"unterminated"); + + WktParseException exception = Assert.Throws(() => tokenizer.ReadDoubleQuotedWord()); + Assert.Contains("Unterminated quoted string", exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies malformed bracket closure reports the expected mismatch. + /// + [Fact] + public void ReadCloserWithMismatchedBracketThrows() + { + var tokenizer = new WktTokenizer("(]"); + + WktBracket opener = tokenizer.ReadOpener(WktBracket.Round); + Assert.Equal(WktBracket.Round, opener); + + WktParseException exception = Assert.Throws(() => tokenizer.ReadCloser(WktBracket.Round)); + Assert.Contains("Expecting (')')", exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies numeric reads on non-numeric tokens surface a structural parse exception. + /// + [Fact] + public void GetNumericValueWithWordTokenThrowsWktParseException() + { + var tokenizer = new WktTokenizer("WORD"); + + Assert.Equal(TokenType.Word, tokenizer.NextToken()); + + WktParseException exception = Assert.Throws(() => tokenizer.GetNumericValue()); + Assert.Contains("is not a number", exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies named non-finite values are treated as words rather than numeric tokens. + /// + /// Named non-finite token candidate. + [Theory] + [InlineData("NaN")] + [InlineData("Infinity")] + public void GetNumericValueWithNamedNonFiniteTokenThrowsWktParseException(string tokenText) + { + var tokenizer = new WktTokenizer(tokenText); + + Assert.Equal(TokenType.Word, tokenizer.NextToken()); + Assert.False(tokenizer.TryGetNumericValue(out _)); + + WktParseException exception = Assert.Throws(() => tokenizer.GetNumericValue()); + Assert.Contains("is not a number", exception.Message, StringComparison.Ordinal); + } + + /// + /// Verifies AUTHORITY parsing for both numeric and quoted authority codes. + /// + /// AUTHORITY fragment. + /// Expected authority name. + /// Expected authority code, or 0 for non-numeric codes. + [Theory] + [InlineData("AUTHORITY[\"EPSG\",4326]", "EPSG", 4326L)] + [InlineData("AUTHORITY[\"EPSG\",\"3857\"]", "EPSG", 3857L)] + [InlineData("AUTHORITY[\"LOCAL\",\"abc\"]", "LOCAL", 0L)] + public void ReadAuthorityParsesNumericAndQuotedCodes(string authorityWkt, string expectedAuthority, long expectedCode) + { + var tokenizer = new WktTokenizer(authorityWkt); + + tokenizer.ReadAuthority(out string authority, out long authorityCode); + + Assert.Equal(expectedAuthority, authority); + Assert.Equal(expectedCode, authorityCode); + } + + /// + /// Verifies deeply nested WKT-like bracket sequences are tokenized without losing bracket balance. + /// + [Fact] + public void NextTokenOnDeeplyNestedInputMaintainsBracketBalance() + { + const int depth = 256; + var builder = new StringBuilder("ROOT"); + for (int i = 0; i < depth; i++) + { + builder.Append('['); + } + + builder.Append("\"N\""); + + for (int i = 0; i < depth; i++) + { + builder.Append(']'); + } + + var tokenizer = new WktTokenizer(builder.ToString()); + int openCount = 0; + int closeCount = 0; + while (tokenizer.NextToken() != TokenType.Eof) + { + string token = tokenizer.GetStringValue(); + if (token == "[") + { + openCount++; + } + else if (token == "]") + { + closeCount++; + } + } + + Assert.Equal(depth, openCount); + Assert.Equal(depth, closeCount); + } + + /// + /// Verifies tokenization remains stable for large WKT strings with long quoted names. + /// + [Fact] + public void ReadDoubleQuotedWordHandlesLargeInput() + { + const int nameLength = 32768; + string longName = new('X', nameLength); + string wkt = $"GEOGCS[\"{longName}\"]"; + var tokenizer = new WktTokenizer(wkt); + + Assert.Equal(TokenType.Word, tokenizer.NextToken()); + Assert.Equal("GEOGCS", tokenizer.GetStringValue()); + Assert.Equal(WktBracket.Square, tokenizer.ReadOpener()); + + string parsedName = tokenizer.ReadDoubleQuotedWord(); + Assert.Equal(nameLength, parsedName.Length); + Assert.Equal(longName, parsedName); + + tokenizer.ReadCloser(WktBracket.Square); + Assert.Equal(TokenType.Eof, tokenizer.NextToken()); + } + + /// + /// Verifies WKT2 escaped double quotes ("") inside quoted values are unescaped. + /// + [Fact] + public void ReadDoubleQuotedWordUnescapesDoubledQuotes() + { + var tokenizer = new WktTokenizer("\"He said \"\"Hello\"\"\""); + + string parsedValue = tokenizer.ReadDoubleQuotedWord(); + + Assert.Equal("He said \"Hello\"", parsedValue); + } + + /// + /// Verifies malformed WKT with a missing closing bracket throws for both string and span parse paths. + /// + [Fact] + public void ParseMalformedWktWithMissingCloserThrowsForStringAndSpan() + { + const string malformedWkt = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]]"; + + WktParseException stringException = Assert.Throws(() => CoordinateSystemWktReader.Parse(malformedWkt)); + WktParseException spanException = Assert.Throws(() => CoordinateSystemWktReader.Parse(malformedWkt.AsSpan())); + + Assert.Contains("Expecting", stringException.Message, StringComparison.Ordinal); + Assert.Contains("Expecting", spanException.Message, StringComparison.Ordinal); + } +} diff --git a/test/ProjNet.Tests/IO/Wkt/WktNodeTests.cs b/test/ProjNet.Tests/IO/Wkt/WktNodeTests.cs new file mode 100644 index 00000000..615831f6 --- /dev/null +++ b/test/ProjNet.Tests/IO/Wkt/WktNodeTests.cs @@ -0,0 +1,1375 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.Wkt; + +using System; +using System.Collections.Generic; +using System.Reflection; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.IO.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Tests for the WKT node types and the ToWktNode methods. +/// +public class WktNodeTests +{ + private static readonly CoordinateSystemServices CoordinateSystemServices = new(); + private static readonly Type[] IntParameterTypes = [typeof(int)]; + private static readonly Type[] StringParameterTypes = [typeof(string)]; + private static readonly Type[] StringArrayParameterTypes = [typeof(string[])]; + private static readonly Type[] WktTokenizerParameterTypes = [typeof(WktTokenizer)]; + private static readonly string[] AuthorityOrIdKeywords = ["AUTHORITY", "ID"]; + private static readonly object[] AuthorityOrIdArguments = [AuthorityOrIdKeywords]; + + /// + /// Verifies that wraps the stored value in double quotes. + /// + [Fact] + public void WktQuotedString_ToString_WrapsValueInQuotes() + { + var node = new WktQuotedString("WGS 84"); + Assert.Equal("\"WGS 84\"", node.ToString()); + } + + /// + /// Verifies that returns the original string without surrounding quotes. + /// + [Fact] + public void WktQuotedString_Value_ReturnsUnquotedValue() + { + var node = new WktQuotedString("WGS 84"); + Assert.Equal("WGS 84", node.Value); + } + + /// + /// Verifies that escapes embedded double quotes while keeping the decoded value intact. + /// + [Fact] + public void WktQuotedString_ToString_EscapesEmbeddedQuotes() + { + const string value = "A \"quoted\" value"; + var node = new WktQuotedString(value); + + Assert.Equal("\"A \"\"quoted\"\" value\"", node.ToString()); + Assert.Equal(value, node.Value); + } + + /// + /// Verifies that matches the compact representation for a leaf node. + /// + [Fact] + public void WktQuotedString_ToFormattedString_ReturnsQuotedValue() + { + var node = new WktQuotedString("WGS 84"); + Assert.Equal("\"WGS 84\"", node.ToFormattedString()); + } + + /// + /// Verifies that formats a positive integer value without a decimal point. + /// + [Fact] + public void WktNumber_ToString_FormatsPositiveInteger() + { + var node = new WktNumber(6378137); + Assert.Equal("6378137", node.ToString()); + } + + /// + /// Verifies that formats a negative value with a leading minus sign. + /// + [Fact] + public void WktNumber_ToString_FormatsNegativeValue() + { + var node = new WktNumber(-87); + Assert.Equal("-87", node.ToString()); + } + + /// + /// Verifies that preserves the full precision of a decimal value. + /// + [Fact] + public void WktNumber_ToString_FormatsDecimalValue() + { + var node = new WktNumber(298.257223563); + Assert.Equal("298.257223563", node.ToString()); + } + + /// + /// Verifies that retains sufficient precision for a very small decimal value + /// such as the radian-per-degree conversion factor. + /// + [Fact] + public void WktNumber_ToString_FormatsVerySmallValue() + { + var node = new WktNumber(0.017453292519943295); + string result = node.ToString(); + Assert.Contains("0.017453292519943", result, StringComparison.Ordinal); + } + + /// + /// Verifies that formats zero as 0. + /// + [Fact] + public void WktNumber_ToString_FormatsZero() + { + var node = new WktNumber(0); + Assert.Equal("0", node.ToString()); + } + + /// + /// Verifies that formats a large integer value without scientific notation. + /// + [Fact] + public void WktNumber_ToString_FormatsLargeValue() + { + var node = new WktNumber(10000000); + Assert.Equal("10000000", node.ToString()); + } + + /// + /// Verifies that returns the original numeric value. + /// + [Fact] + public void WktNumber_Value_ReturnsOriginalValue() + { + var node = new WktNumber(298.257223563); + Assert.Equal(298.257223563, node.Value, 12); + } + + /// + /// Verifies that matches the compact representation for a leaf node. + /// + [Fact] + public void WktNumber_ToFormattedString_MatchesToString() + { + var node = new WktNumber(298.257223563); + Assert.Equal(node.ToString(), node.ToFormattedString()); + } + + /// + /// Verifies that formats a positive integer value correctly. + /// + [Fact] + public void WktInteger_ToString_FormatsPositiveValue() + { + var node = new WktInteger(4326); + Assert.Equal("4326", node.ToString()); + } + + /// + /// Verifies that formats a negative integer value with a leading minus sign. + /// + [Fact] + public void WktInteger_ToString_FormatsNegativeValue() + { + var node = new WktInteger(-1); + Assert.Equal("-1", node.ToString()); + } + + /// + /// Verifies that formats zero as 0. + /// + [Fact] + public void WktInteger_ToString_FormatsZero() + { + var node = new WktInteger(0); + Assert.Equal("0", node.ToString()); + } + + /// + /// Verifies that returns the original integer value. + /// + [Fact] + public void WktInteger_Value_ReturnsOriginalValue() + { + var node = new WktInteger(4326); + Assert.Equal(4326, node.Value); + } + + /// + /// Verifies that matches the compact representation for a leaf node. + /// + [Fact] + public void WktInteger_ToFormattedString_MatchesToString() + { + var node = new WktInteger(4326); + Assert.Equal(node.ToString(), node.ToFormattedString()); + } + + /// + /// Verifies that returns the identifier name without any quoting or modification. + /// + [Fact] + public void WktIdentifier_ToString_ReturnsNameAsIs() + { + var node = new WktIdentifier("NORTH"); + Assert.Equal("NORTH", node.ToString()); + } + + /// + /// Verifies that returns the original identifier text. + /// + [Fact] + public void WktIdentifier_Name_ReturnsOriginalText() + { + var node = new WktIdentifier("NORTH"); + Assert.Equal("NORTH", node.Name); + } + + /// + /// Verifies that matches the compact representation for a leaf node. + /// + [Fact] + public void WktIdentifier_ToFormattedString_MatchesToString() + { + var node = new WktIdentifier("NORTH"); + Assert.Equal(node.ToString(), node.ToFormattedString()); + } + + /// + /// Verifies that produces a compact single-line WKT expression with the + /// keyword name followed by bracket-enclosed, comma-separated children. + /// + [Fact] + public void WktKeywordNode_ToString_CompactFormat() + { + var node = new WktKeywordNode( + "UNIT", + new WktQuotedString("degree"), + new WktNumber(0.0174532925199433)); + + string result = node.ToString(); + Assert.StartsWith("UNIT[\"degree\", 0.017453292519943", result, StringComparison.Ordinal); + Assert.EndsWith("]", result, StringComparison.Ordinal); + } + + /// + /// Verifies that correctly serializes quoted-string children into a + /// single-line compact WKT expression. + /// + [Fact] + public void WktKeywordNode_ToString_NestedKeywords() + { + var node = new WktKeywordNode( + "AUTHORITY", + new WktQuotedString("EPSG"), + new WktQuotedString("4326")); + + Assert.Equal("AUTHORITY[\"EPSG\", \"4326\"]", node.ToString()); + } + + /// + /// Verifies that produces an empty-bracket expression when the node + /// has no children. + /// + [Fact] + public void WktKeywordNode_ToString_EmptyChildren() + { + var node = new WktKeywordNode("EMPTY"); + Assert.Equal("EMPTY[]", node.ToString()); + } + + /// + /// Verifies that produces the same single-line output as + /// when all children are leaf nodes. + /// + [Fact] + public void WktKeywordNode_ToFormattedString_SimpleNode_NoIndentation() + { + var node = new WktKeywordNode( + "AUTHORITY", + new WktQuotedString("EPSG"), + new WktQuotedString("4326")); + + string result = node.ToFormattedString(); + Assert.Equal("AUTHORITY[\"EPSG\", \"4326\"]", result); + } + + /// + /// Verifies that introduces newlines and indentation when the + /// node contains nested keyword children. + /// + [Fact] + public void WktKeywordNode_ToFormattedString_WithComplexChildren_UsesNewlines() + { + var inner = new WktKeywordNode( + "AUTHORITY", + new WktQuotedString("EPSG"), + new WktQuotedString("7030")); + + var node = new WktKeywordNode( + "SPHEROID", + new WktQuotedString("WGS 84"), + new WktNumber(6378137), + new WktNumber(298.257223563), + inner); + + string result = node.ToFormattedString(); + Assert.Contains("\n", result, StringComparison.Ordinal); + Assert.Contains(" ", result, StringComparison.Ordinal); + Assert.Contains("AUTHORITY", result, StringComparison.Ordinal); + } + + /// + /// Verifies that the constructor accepting an + /// of correctly builds the node and + /// produces the expected compact WKT string. + /// + [Fact] + public void WktKeywordNode_IReadOnlyListConstructor_Works() + { + IReadOnlyList children = new WktNode[] + { + new WktQuotedString("test"), + new WktNumber(42), + }; + + var node = new WktKeywordNode("TEST", children); + Assert.Equal("TEST[\"test\", 42]", node.ToString()); + } + + /// + /// Verifies that the internal tree builder parses a simple WKT1 structure into the expected node hierarchy. + /// + [Fact] + public void WktKeywordNode_ParseTree_SimpleWkt1_ReturnsExpectedStructure() + { + const string wkt = """GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],UNIT["degree",0.0174532925199433]]"""; + + WktKeywordNode root = ParseTree(wkt); + Assert.Equal("GEOGCS", root.Keyword); + Assert.Equal("GEOGCS[\"WGS 84\", DATUM[\"WGS_1984\", SPHEROID[\"WGS 84\", 6378137, 298.257223563]], UNIT[\"degree\", 0.0174532925199433]]", root.ToString()); + + WktKeywordNode datum = Assert.IsType(root.Children[1]); + Assert.Equal("DATUM", datum.Keyword); + + WktKeywordNode spheroid = Assert.IsType(datum.Children[1]); + Assert.Equal("SPHEROID", spheroid.Keyword); + Assert.IsType(spheroid.Children[1]); + Assert.IsType(spheroid.Children[2]); + } + + /// + /// Verifies that the internal tree builder parses a complex WKT2 structure and preserves all supported node kinds. + /// + [Fact] + public void WktKeywordNode_ParseTree_ComplexWkt2_ReturnsExpectedNodeKinds() + { + const string wkt = """PROJCRS["WGS 84 / UTM zone 32N",BASEGEOGCRS["WGS 84",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ID["EPSG",6326]],ID["EPSG",4326]],CONVERSION["UTM zone 32N",METHOD["Transverse Mercator"],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433]]],CS[Cartesian,2],AXIS["Easting (E)",east,ORDER[1]],AXIS["Northing (N)",north,ORDER[2]],LENGTHUNIT["metre",1],ID["EPSG",32632]]"""; + + WktKeywordNode root = ParseTree(wkt); + Assert.Equal("PROJCRS", root.Keyword); + + WktKeywordNode conversion = Assert.IsType(root.Children[2]); + WktKeywordNode parameter = Assert.IsType(conversion.Children[2]); + Assert.Equal("PARAMETER", parameter.Keyword); + Assert.IsType(parameter.Children[0]); + Assert.IsType(parameter.Children[1]); + Assert.IsType(parameter.Children[2]); + + WktKeywordNode cs = Assert.IsType(root.Children[3]); + Assert.Equal("CS", cs.Keyword); + Assert.IsType(cs.Children[0]); + Assert.IsType(cs.Children[1]); + + WktKeywordNode axis = Assert.IsType(root.Children[4]); + Assert.Equal("AXIS", axis.Keyword); + Assert.IsType(axis.Children[0]); + Assert.IsType(axis.Children[1]); + Assert.IsType(axis.Children[2]); + } + + /// + /// Verifies that the internal tree builder supports empty keyword nodes. + /// + [Fact] + public void WktKeywordNode_ParseTree_EmptyNode_ReturnsNodeWithoutChildren() + { + WktKeywordNode root = ParseTree("STEP[]"); + + Assert.Equal("STEP", root.Keyword); + Assert.Empty(root.Children); + Assert.Equal("STEP[]", root.ToString()); + } + + /// + /// Verifies that the internal tree builder preserves deep nesting. + /// + [Fact] + public void WktKeywordNode_ParseTree_DeepNesting_PreservesHierarchy() + { + WktKeywordNode root = ParseTree("""ROOT[LEVEL1[LEVEL2[LEVEL3["value"]]]]"""); + WktKeywordNode level1 = Assert.IsType(root.Children[0]); + WktKeywordNode level2 = Assert.IsType(level1.Children[0]); + WktKeywordNode level3 = Assert.IsType(level2.Children[0]); + + Assert.Equal("ROOT", root.Keyword); + Assert.Equal("LEVEL1", level1.Keyword); + Assert.Equal("LEVEL3", level3.Keyword); + } + + /// + /// Verifies that tree parsing roundtrips back to the compact WKT representation apart from whitespace normalization. + /// + [Fact] + public void WktKeywordNode_ParseTree_RoundTripsToCompactForm() + { + const string wkt = """ + GEOGCS[ + "WGS 84", + DATUM["WGS_1984"], + UNIT["degree", 0.0174532925199433] + ] + """; + + WktKeywordNode root = ParseTree(wkt); + Assert.Equal("GEOGCS[\"WGS 84\", DATUM[\"WGS_1984\"], UNIT[\"degree\", 0.0174532925199433]]", root.ToString()); + } + + /// + /// Verifies that quoted strings with escaped double quotes preserve their raw WKT form and decode to the expected value. + /// + [Fact] + public void WktKeywordNode_ParseTree_QuotedStringWithEscapedQuotes_RoundTripsAndDecodesValue() + { + const string wkt = """REMARK["A ""quoted"" value"]"""; + + WktKeywordNode root = ParseTree(wkt); + WktQuotedString valueNode = Assert.IsType(root.Children[0]); + + Assert.Equal("REMARK[\"A \"\"quoted\"\" value\"]", root.ToString()); + Assert.Equal("A \"quoted\" value", valueNode.Value); + } + + /// + /// Verifies that the internal helper methods expose typed positional and keyword-based child access. + /// + [Fact] + public void WktKeywordNode_InternalHelpers_ReturnExpectedValues() + { + const string wkt = """PROJCRS["WGS 84 / UTM zone 32N",BASEGEOGCRS["WGS 84",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ID["EPSG",6326]],ID["EPSG",4326]],CONVERSION["UTM zone 32N",METHOD["Transverse Mercator"],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433]]],CS[Cartesian,2],AXIS["Easting (E)",east,ORDER[1]],AXIS["Northing (N)",north,ORDER[2]],LENGTHUNIT["metre",1],ID["EPSG",32632]]"""; + + WktKeywordNode root = ParseTree(wkt); + + Assert.Equal("WGS 84 / UTM zone 32N", InvokeNonPublicInstance(root, "GetString", IntParameterTypes, 0)); + + WktKeywordNode? cs = InvokeNonPublicInstance(root, "FindChild", StringParameterTypes, "CS"); + Assert.NotNull(cs); + Assert.Equal("Cartesian", InvokeNonPublicInstance(cs, "GetIdentifierChild", IntParameterTypes, 0)); + Assert.Equal(2d, InvokeNonPublicInstance(cs, "GetNumber", IntParameterTypes, 0)); + + int axisCount = 0; + for (int i = 0; i < root.Children.Count; i++) + { + if (root.Children[i] is WktKeywordNode keywordChild && string.Equals(keywordChild.Keyword, "AXIS", StringComparison.Ordinal)) + { + axisCount++; + } + } + + Assert.Equal(2, axisCount); + + (string Authority, string Code)? authority = InvokeNonPublicInstance<(string Authority, string Code)?>(root, "GetAuthority", Type.EmptyTypes); + Assert.True(authority.HasValue); + Assert.Equal("EPSG", authority.Value.Authority); + Assert.Equal("32632", authority.Value.Code); + + WktKeywordNode? idNode = InvokeNonPublicInstance(root, "FindChild", StringArrayParameterTypes, AuthorityOrIdArguments); + Assert.NotNull(idNode); + Assert.Equal("ID", idNode.Keyword); + } + + /// + /// Verifies that AngularUnit.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the unit. + /// + [Fact] + public void AngularUnit_ToWktNode_MatchesWkt() + { + AngularUnit unit = AngularUnit.Degrees; + var node = unit.ToWktNode(); + Assert.Equal(unit.WKT, node.ToString()); + } + + /// + /// Verifies that helper objects keep their existing WKT1 node output when routed through the versioned API. + /// + [Fact] + public void HelperObjects_ToWktNode_WithWkt1Version_MatchesParameterlessOutput() + { + AxisInfo axis = new("Lon", AxisOrientationEnum.East); + Projection projection = Assert.IsType(ProjectedCoordinateSystem.WebMercator.Projection); + ProjectionParameter parameter = new("central_meridian", 15d); + Wgs84ConversionInfo wgs84 = new(-87, -98, -121, 0, 0, 0, 0); + + Assert.Equal(AngularUnit.Degrees.ToWktNode().ToString(), AngularUnit.Degrees.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(LinearUnit.Metre.ToWktNode().ToString(), LinearUnit.Metre.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(Ellipsoid.WGS84.ToWktNode().ToString(), Ellipsoid.WGS84.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(PrimeMeridian.Greenwich.ToWktNode().ToString(), PrimeMeridian.Greenwich.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(HorizontalDatum.WGS84.ToWktNode().ToString(), HorizontalDatum.WGS84.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(VerticalDatum.ODN.ToWktNode().ToString(), VerticalDatum.ODN.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(axis.ToWktNode().ToString(), axis.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(projection.ToWktNode().ToString(), projection.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(parameter.ToWktNode().ToString(), parameter.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(wgs84.ToWktNode().ToString(), wgs84.ToWktNode(WktVersion.Wkt1).ToString()); + } + + /// + /// Verifies that LinearUnit.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the unit. + /// + [Fact] + public void LinearUnit_ToWktNode_MatchesWkt() + { + LinearUnit unit = LinearUnit.Metre; + var node = unit.ToWktNode(); + Assert.Equal(unit.WKT, node.ToString()); + } + + /// + /// Verifies that Unit.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the unit. + /// + [Fact] + public void Unit_ToWktNode_MatchesWkt() + { + Unit unit = new("custom", 2.5); + var node = unit.ToWktNode(); + Assert.Equal(unit.WKT, node.ToString()); + } + + /// + /// Verifies that Ellipsoid.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the ellipsoid. + /// + [Fact] + public void Ellipsoid_ToWktNode_MatchesWkt() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + var node = ellipsoid.ToWktNode(); + Assert.Equal(ellipsoid.WKT, node.ToString()); + } + + /// + /// Verifies that PrimeMeridian.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the prime meridian. + /// + [Fact] + public void PrimeMeridian_ToWktNode_MatchesWkt() + { + PrimeMeridian pm = PrimeMeridian.Greenwich; + var node = pm.ToWktNode(); + Assert.Equal(pm.WKT, node.ToString()); + } + + /// + /// Verifies that HorizontalDatum.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the datum. + /// + [Fact] + public void HorizontalDatum_ToWktNode_MatchesWkt() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + var node = datum.ToWktNode(); + Assert.Equal(datum.WKT, node.ToString()); + } + + /// + /// Verifies that HorizontalDatum.ToWktNode() correctly encodes Bursa-Wolf TOWGS84 parameters when + /// they are present, using the ED50 datum as the test case. + /// + [Fact] + public void HorizontalDatum_ToWktNode_WithWgs84Parameters_MatchesWkt() + { + HorizontalDatum datum = HorizontalDatum.ED50; + var node = datum.ToWktNode(); + Assert.Equal(datum.WKT, node.ToString()); + } + + /// + /// Verifies that VerticalDatum.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the datum. + /// + [Fact] + public void VerticalDatum_ToWktNode_MatchesWkt() + { + VerticalDatum datum = VerticalDatum.ODN; + var node = datum.ToWktNode(); + Assert.Equal(datum.WKT, node.ToString()); + } + + /// + /// Verifies that AxisInfo.ToWktNode() produces a node whose compact string representation + /// matches the WKT property for a named east-oriented axis. + /// + [Fact] + public void AxisInfo_ToWktNode_MatchesWkt() + { + var axis = new AxisInfo("Lon", AxisOrientationEnum.East); + var node = axis.ToWktNode(); + Assert.Equal(axis.WKT, node.ToString()); + } + + /// + /// Verifies that AxisInfo.ToWktNode() produces a node matching the WKT property for every + /// defined value. + /// + [Fact] + public void AxisInfo_ToWktNode_AllOrientations() + { + AxisOrientationEnum[] orientations = new[] + { + AxisOrientationEnum.North, + AxisOrientationEnum.South, + AxisOrientationEnum.East, + AxisOrientationEnum.West, + AxisOrientationEnum.Up, + AxisOrientationEnum.Down, + AxisOrientationEnum.Other, + }; + + foreach (AxisOrientationEnum orientation in orientations) + { + var axis = new AxisInfo("Test", orientation); + var node = axis.ToWktNode(); + Assert.Equal(axis.WKT, node.ToString()); + } + } + + /// + /// Verifies that ProjectionParameter.ToWktNode() produces a node whose compact string representation + /// matches the WKT property of the parameter. + /// + [Fact] + public void ProjectionParameter_ToWktNode_MatchesWkt() + { + var param = new ProjectionParameter("central_meridian", 15.0); + var node = param.ToWktNode(); + Assert.Equal(param.WKT, node.ToString()); + } + + /// + /// Verifies that Wgs84ConversionInfo.ToWktNode() produces a node matching the WKT property + /// for a non-zero Bursa-Wolf parameter set. + /// + [Fact] + public void Wgs84ConversionInfo_ToWktNode_MatchesWkt() + { + var info = new Wgs84ConversionInfo(-87, -98, -121, 0, 0, 0, 0); + var node = info.ToWktNode(); + Assert.Equal(info.WKT, node.ToString()); + } + + /// + /// Verifies that Wgs84ConversionInfo.ToWktNode() produces a node matching the WKT property + /// when all Bursa-Wolf parameters are zero. + /// + [Fact] + public void Wgs84ConversionInfo_ToWktNode_AllZeros_MatchesWkt() + { + var info = new Wgs84ConversionInfo(); + var node = info.ToWktNode(); + Assert.Equal(info.WKT, node.ToString()); + } + + /// + /// Verifies that GeographicCoordinateSystem.ToWktNode() produces a node whose compact string + /// representation matches the WKT property for the WGS84 geographic coordinate system. + /// + [Fact] + public void GeographicCoordinateSystem_ToWktNode_MatchesWkt() + { + GeographicCoordinateSystem gcs = GeographicCoordinateSystem.WGS84; + var node = gcs.ToWktNode(); + Assert.Equal(gcs.WKT, node.ToString()); + } + + /// + /// Verifies that ProjectedCoordinateSystem.ToWktNode() produces a node whose compact string + /// representation matches the WKT property for the Web Mercator projection. + /// + [Fact] + public void ProjectedCoordinateSystem_ToWktNode_MatchesWkt() + { + ProjectedCoordinateSystem pcs = ProjectedCoordinateSystem.WebMercator; + var node = pcs.ToWktNode(); + Assert.Equal(pcs.WKT, node.ToString()); + } + + /// + /// Verifies that ProjectedCoordinateSystem.ToWktNode() produces a node whose compact string + /// representation matches the WKT property for a WGS84 UTM projected coordinate system. + /// + [Fact] + public void ProjectedCoordinateSystem_UTM_ToWktNode_MatchesWkt() + { + var pcs = ProjectedCoordinateSystem.WGS84_UTM(33, true); + var node = pcs.ToWktNode(); + Assert.Equal(pcs.WKT, node.ToString()); + } + + /// + /// Verifies that VerticalCoordinateSystem.ToWktNode() produces a node whose compact string + /// representation matches the WKT property of the coordinate system. + /// + [Fact] + public void VerticalCoordinateSystem_ToWktNode_MatchesWkt() + { + VerticalCoordinateSystem vcs = VerticalCoordinateSystem.ODN; + var node = vcs.ToWktNode(); + Assert.Equal(vcs.WKT, node.ToString()); + } + + /// + /// Verifies that GeocentricCoordinateSystem.ToWktNode() produces a node whose compact string + /// representation matches the WKT property of the coordinate system. + /// + [Fact] + public void GeocentricCoordinateSystem_ToWktNode_MatchesWkt() + { + GeocentricCoordinateSystem gcc = GeocentricCoordinateSystem.WGS84; + var node = gcc.ToWktNode(); + Assert.Equal(gcc.WKT, node.ToString()); + } + + /// + /// Verifies that coordinate systems keep their existing WKT1 node output when routed through the versioned API. + /// + [Fact] + public void CoordinateSystems_ToWktNode_WithWkt1Version_MatchesParameterlessOutput() + { + GeographicCoordinateSystem geographic = GeographicCoordinateSystem.WGS84; + ProjectedCoordinateSystem projected = ProjectedCoordinateSystem.WebMercator; + VerticalCoordinateSystem vertical = VerticalCoordinateSystem.ODN; + GeocentricCoordinateSystem geocentric = GeocentricCoordinateSystem.WGS84; + CompoundCoordinateSystem compound = new CoordinateSystemFactory().CreateCompoundCoordinateSystem("WGS84 + ODN", geographic, vertical); + + Assert.Equal(geographic.ToWktNode().ToString(), geographic.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(projected.ToWktNode().ToString(), projected.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(vertical.ToWktNode().ToString(), vertical.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(geocentric.ToWktNode().ToString(), geocentric.ToWktNode(WktVersion.Wkt1).ToString()); + Assert.Equal(compound.ToWktNode().ToString(), compound.ToWktNode(WktVersion.Wkt1).ToString()); + } + + /// + /// Verifies that GEOGCRS WKT2 output for WGS84 roundtrips through the native WKT2 reader without changing parameters. + /// + [Fact] + public void GeographicCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsWgs84() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem original = GeographicCoordinateSystem.WGS84; + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + GeographicCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("GEOGCRS[", wkt, StringComparison.Ordinal); + Assert.DoesNotContain("PRIMEM[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that WKT2 GEOGCRS output preserves non-Greenwich prime meridians and roundtrips through the reader. + /// + [Fact] + public void GeographicCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsCustomPrimeMeridian() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem original = factory.CreateGeographicCoordinateSystem( + "Custom Paris geographic", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Paris, + new AxisInfo("Lat", AxisOrientationEnum.North), + new AxisInfo("Lon", AxisOrientationEnum.East)); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + GeographicCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.Contains("PRIMEM[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that datum shifts encoded as WGS84 conversion parameters serialize as WKT2 BOUNDCRS. + /// + [Fact] + public void GeographicCoordinateSystem_ToWktNode_WithWkt22019AndWgs84Parameters_EmitsBoundCrs() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem original = factory.CreateGeographicCoordinateSystem( + "ED50 test", + AngularUnit.Degrees, + HorizontalDatum.ED50, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + Wgs84ConversionInfo expectedParameters = Assert.IsType(original.HorizontalDatum.Wgs84Parameters); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + GeographicCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + GeographicCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + Assert.StartsWith("BOUNDCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("ABRIDGEDTRANSFORMATION[", wkt, StringComparison.Ordinal); + Assert.Equal(original.Name, parsed.Name); + Assert.Equal(original.HorizontalDatum.Name, source.HorizontalDatum.Name); + Assert.Null(source.HorizontalDatum.Wgs84Parameters); + Assert.Equal(expectedParameters, parsed.Transformation.Wgs84Parameters); + Assert.Equal("WGS 84", target.Name); + } + + /// + /// Verifies that WKT2 PROJCRS output roundtrips a transverse Mercator projected CRS through the native WKT2 reader. + /// + [Fact] + public void ProjectedCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsTransverseMercator() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem geographic = factory.CreateGeographicCoordinateSystem( + "WGS 84", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Geodetic latitude (Lat)", AxisOrientationEnum.North), + new AxisInfo("Geodetic longitude (Lon)", AxisOrientationEnum.East)); + IProjection projection = factory.CreateProjection( + "UTM zone 33N", + "Transverse_Mercator", + new List + { + new("latitude_of_origin", 0), + new("central_meridian", 15), + new("scale_factor", 0.9996), + new("false_easting", 500000), + new("false_northing", 0), + }); + ProjectedCoordinateSystem original = factory.CreateProjectedCoordinateSystem( + "WGS 84 / UTM zone 33N", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("Easting", AxisOrientationEnum.East), + new AxisInfo("Northing", AxisOrientationEnum.North)); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("PROJCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("BASEGEOGCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("METHOD[\"Transverse Mercator\"]", wkt, StringComparison.Ordinal); + Assert.DoesNotContain("GEOGCS[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that WKT2 PROJCRS output uses method-specific false-origin parameter names for Lambert Conic Conformal (2SP). + /// + [Fact] + public void ProjectedCoordinateSystem_ToWktNode_WithWkt22019_UsesLambert2SpFalseOriginParameterNames() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem geographic = factory.CreateGeographicCoordinateSystem( + "BD72 geographic", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Geodetic latitude (Lat)", AxisOrientationEnum.North), + new AxisInfo("Geodetic longitude (Lon)", AxisOrientationEnum.East)); + IProjection projection = factory.CreateProjection( + "Belgian Lambert 72", + "Lambert_Conformal_Conic_2SP", + new List + { + new("latitude_of_origin", 90), + new("central_meridian", 4.36748666666694), + new("standard_parallel_1", 51.1666672333336), + new("standard_parallel_2", 49.8333339000003), + new("false_easting", 150000.013), + new("false_northing", 5400088.438), + }); + ProjectedCoordinateSystem original = factory.CreateProjectedCoordinateSystem( + "BD72 / Belgian Lambert 72", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("Easting", AxisOrientationEnum.East), + new AxisInfo("Northing", AxisOrientationEnum.North)); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.Contains("METHOD[\"Lambert Conic Conformal (2SP)\"]", wkt, StringComparison.Ordinal); + Assert.Contains("PARAMETER[\"Latitude of false origin\"", wkt, StringComparison.Ordinal); + Assert.Contains("PARAMETER[\"Longitude of false origin\"", wkt, StringComparison.Ordinal); + Assert.Contains("PARAMETER[\"Easting at false origin\"", wkt, StringComparison.Ordinal); + Assert.Contains("PARAMETER[\"Northing at false origin\"", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that WKT2 PROJCRS output preserves non-Greenwich prime meridians and non-degree parameter units. + /// + [Fact] + public void ProjectedCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsCustomPrimeMeridianAndGradParameters() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem geographic = factory.CreateGeographicCoordinateSystem( + "NTF (Paris)", + AngularUnit.Grad, + HorizontalDatum.WGS84, + PrimeMeridian.Paris, + new AxisInfo("Geodetic latitude (Lat)", AxisOrientationEnum.North), + new AxisInfo("Geodetic longitude (Lon)", AxisOrientationEnum.East)); + IProjection projection = factory.CreateProjection( + "Lambert Nord France", + "Lambert_Conformal_Conic_1SP", + new List + { + new("latitude_of_origin", 55), + new("central_meridian", 0), + new("scale_factor", 0.999877341), + new("false_easting", 600000), + new("false_northing", 200000), + }); + ProjectedCoordinateSystem original = factory.CreateProjectedCoordinateSystem( + "NTF (Paris) / Lambert Nord France", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("Easting", AxisOrientationEnum.East), + new AxisInfo("Northing", AxisOrientationEnum.North)); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.Contains("PRIMEM[\"Paris\"", wkt, StringComparison.Ordinal); + Assert.Contains("ANGLEUNIT[\"grad\"", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that projected CRSs whose base datum carries WGS84 parameters serialize as WKT2 BOUNDCRS. + /// + [Fact] + public void ProjectedCoordinateSystem_ToWktNode_WithWkt22019AndWgs84Parameters_EmitsBoundCrs() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem geographic = factory.CreateGeographicCoordinateSystem( + "ED50 test", + AngularUnit.Degrees, + HorizontalDatum.ED50, + PrimeMeridian.Greenwich, + new AxisInfo("Geodetic latitude (Lat)", AxisOrientationEnum.North), + new AxisInfo("Geodetic longitude (Lon)", AxisOrientationEnum.East)); + IProjection projection = factory.CreateProjection( + "ED50 TM", + "Transverse_Mercator", + new List + { + new("latitude_of_origin", 0), + new("central_meridian", 9), + new("scale_factor", 0.9996), + new("false_easting", 500000), + new("false_northing", 0), + }); + ProjectedCoordinateSystem original = factory.CreateProjectedCoordinateSystem( + "ED50 / TM test", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("Easting", AxisOrientationEnum.East), + new AxisInfo("Northing", AxisOrientationEnum.North)); + Wgs84ConversionInfo expectedParameters = Assert.IsType(original.GeographicCoordinateSystem.HorizontalDatum.Wgs84Parameters); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + ProjectedCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + GeographicCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + Assert.StartsWith("BOUNDCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("SOURCECRS[PROJCRS[", wkt, StringComparison.Ordinal); + Assert.Equal(original.Name, parsed.Name); + Assert.Equal("Transverse Mercator", source.Projection.ClassName); + Assert.Equal(9d, source.Projection.GetParameter("central_meridian")?.Value); + Assert.Equal(0.9996d, source.Projection.GetParameter("scale_factor")?.Value); + Assert.Equal(original.GeographicCoordinateSystem.HorizontalDatum.Name, source.GeographicCoordinateSystem.HorizontalDatum.Name); + Assert.Null(source.GeographicCoordinateSystem.HorizontalDatum.Wgs84Parameters); + Assert.Equal(expectedParameters, parsed.Transformation.Wgs84Parameters); + Assert.Equal("WGS 84", target.Name); + } + + /// + /// Verifies that WKT2 GEODCRS output roundtrips a geocentric coordinate system through the native WKT2 reader. + /// + [Fact] + public void GeocentricCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsWgs84() + { + CoordinateSystemFactory factory = new(); + GeocentricCoordinateSystem original = factory.CreateGeocentricCoordinateSystem( + "WGS 84 geocentric", + HorizontalDatum.WGS84, + LinearUnit.Metre, + PrimeMeridian.Greenwich); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + GeocentricCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("GEODCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("AXIS[\"X\", geocentricX]", wkt, StringComparison.Ordinal); + Assert.Contains("AXIS[\"Y\", geocentricY]", wkt, StringComparison.Ordinal); + Assert.Contains("AXIS[\"Z\", geocentricZ]", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that WKT2 VERTCRS output roundtrips a vertical coordinate system through the native WKT2 reader. + /// + [Fact] + public void VerticalCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsOdn() + { + CoordinateSystemFactory factory = new(); + VerticalCoordinateSystem original = VerticalCoordinateSystem.ODN; + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + VerticalCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("VERTCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("VDATUM[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that retained vertical bound-grid metadata serializes as WKT2 BOUNDCRS. + /// + [Fact] + public void VerticalCoordinateSystem_ToWktNode_WithWkt22019AndBoundGridTransformation_EmitsBoundCrs() + { + CoordinateSystemFactory factory = new(); + VerticalCoordinateSystem original = CreateBoundVerticalCoordinateSystem(); + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + BoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + VerticalCoordinateSystem source = Assert.IsType(parsed.SourceCoordinateSystem); + CompoundCoordinateSystem target = Assert.IsType(parsed.TargetCoordinateSystem); + + Assert.StartsWith("BOUNDCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("PARAMETERFILE[", wkt, StringComparison.Ordinal); + Assert.Equal(original.Name, parsed.Name); + Assert.Equal(original.VerticalDatum.Name, source.VerticalDatum.Name); + Assert.Null(source.BoundGridTransformation); + Assert.Equal("egm96_15.gtx", parsed.Transformation.ParameterFileName); + Assert.Equal(3, target.Dimension); + } + + /// + /// Verifies that WKT2 COMPOUNDCRS output roundtrips a compound coordinate system through the native WKT2 reader. + /// + [Fact] + public void CompoundCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsGeographicAndVerticalComponents() + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem geographic = factory.CreateGeographicCoordinateSystem( + "WGS 84", + AngularUnit.Degrees, + HorizontalDatum.WGS84, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + CompoundCoordinateSystem original = factory.CreateCompoundCoordinateSystem("WGS 84 + ODN", geographic, VerticalCoordinateSystem.ODN); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + CompoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("COMPOUNDCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("GEOGCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("VERTCRS[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies affine fitted coordinate systems now emit WKT2 derived geographic CRS output and roundtrip back to the same semantic model. + /// + [Fact] + public void FittedCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsAffineDerivedGeographicCrs() + { + CoordinateSystemFactory factory = new(); + FittedCoordinateSystem original = factory.CreateFittedCoordinateSystem( + "Fitted test", + GeographicCoordinateSystem.WGS84, + new AffineTransform(1, 0, 0.5, 0, 1, 1.5), + new List + { + new("Local latitude", AxisOrientationEnum.North), + new("Local longitude", AxisOrientationEnum.East), + }); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + FittedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + GeographicCoordinateSystem parsedBase = Assert.IsType(parsed.BaseCoordinateSystem); + GeographicCoordinateSystem originalBase = Assert.IsType(original.BaseCoordinateSystem); + + Assert.StartsWith("GEOGCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("BASEGEOGCRS[", wkt, StringComparison.Ordinal); + Assert.Contains("DERIVINGCONVERSION[", wkt, StringComparison.Ordinal); + Assert.Equal(original.ToBase(), parsed.ToBase()); + Assert.True(parsedBase.HorizontalDatum.EqualParams(originalBase.HorizontalDatum)); + Assert.True(parsedBase.PrimeMeridian.EqualParams(originalBase.PrimeMeridian)); + Assert.True(parsedBase.AngularUnit.EqualParams(originalBase.AngularUnit)); + Assert.Equal("Local latitude", parsed.GetAxis(0).Name); + Assert.Equal("Local longitude", parsed.GetAxis(1).Name); + } + + /// + /// Verifies that a catalog geographic CRS can roundtrip through WKT1 parsing and WKT2 writing without losing parameters. + /// + /// The EPSG SRID to validate. + [Theory] + [InlineData(4326)] + public void GeographicCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsCatalogWkt1(int srid) + { + CoordinateSystemFactory factory = new(); + GeographicCoordinateSystem original = ParseCatalogWkt1(factory, srid); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + GeographicCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("GEOGCRS[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that a catalog geocentric CRS can roundtrip through WKT1 parsing and WKT2 writing without losing parameters. + /// + /// The EPSG SRID to validate. + [Theory] + [InlineData(4978)] + public void GeocentricCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsCatalogWkt1(int srid) + { + CoordinateSystemFactory factory = new(); + GeocentricCoordinateSystem original = ParseCatalogWkt1(factory, srid); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + GeocentricCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("GEODCRS[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that a catalog projected CRS can roundtrip through WKT1 parsing and WKT2 writing while preserving the projection definition. + /// + /// The EPSG SRID to validate. + [Theory] + [InlineData(32632)] + public void ProjectedCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsCatalogWkt1(int srid) + { + CoordinateSystemFactory factory = new(); + ProjectedCoordinateSystem original = ParseCatalogWkt1(factory, srid); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + ProjectedCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("PROJCRS[", wkt, StringComparison.Ordinal); + AssertProjectedRoundTripEquivalent(original, parsed); + } + + /// + /// Verifies that a catalog vertical CRS can roundtrip through WKT1 parsing and WKT2 writing without losing parameters. + /// + /// The EPSG SRID to validate. + [Theory] + [InlineData(5701)] + public void VerticalCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsCatalogWkt1(int srid) + { + CoordinateSystemFactory factory = new(); + VerticalCoordinateSystem original = ParseCatalogWkt1(factory, srid); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + VerticalCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("VERTCRS[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that a catalog compound CRS can roundtrip through WKT1 parsing and WKT2 writing without losing parameters. + /// + /// The EPSG SRID to validate. + [Theory] + [InlineData(9518)] + public void CompoundCoordinateSystem_ToWktNode_WithWkt22019_RoundTripsCatalogWkt1(int srid) + { + CoordinateSystemFactory factory = new(); + CompoundCoordinateSystem original = ParseCatalogWkt1(factory, srid); + + string wkt = original.ToWktNode(WktVersion.Wkt22019).ToString(); + CompoundCoordinateSystem parsed = CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, wkt); + + Assert.StartsWith("COMPOUNDCRS[", wkt, StringComparison.Ordinal); + Assert.True(original.EqualParams(parsed)); + } + + /// + /// Verifies that GeographicCoordinateSystem.ToWktNode() returns a + /// with the GEOGCS keyword and a quoted system name as its first child. + /// + [Fact] + public void GeographicCoordinateSystem_ToWktNode_ReturnsKeywordNode() + { + GeographicCoordinateSystem gcs = GeographicCoordinateSystem.WGS84; + var node = gcs.ToWktNode(); + + WktKeywordNode keywordNode = Assert.IsType(node); + Assert.Equal("GEOGCS", keywordNode.Keyword); + Assert.True(keywordNode.Children.Count > 0); + + WktQuotedString nameNode = Assert.IsType(keywordNode.Children[0]); + Assert.Equal("WGS 84", nameNode.Value); + } + + /// + /// Verifies that the returned by GeographicCoordinateSystem.ToWktNode() + /// has a DATUM keyword node as its second child. + /// + [Fact] + public void GeographicCoordinateSystem_ToWktNode_ContainsDatumChild() + { + GeographicCoordinateSystem gcs = GeographicCoordinateSystem.WGS84; + var keywordNode = (WktKeywordNode)gcs.ToWktNode(); + + WktKeywordNode datumNode = Assert.IsType(keywordNode.Children[1]); + Assert.Equal("DATUM", datumNode.Keyword); + } + + /// + /// Verifies that a containing another as a child + /// correctly serializes both the outer and inner keyword nodes in the compact WKT output. + /// + [Fact] + public void NestedKeywordNodes_ProduceCorrectWkt() + { + var authorityNode = new WktKeywordNode( + "AUTHORITY", + new WktQuotedString("EPSG"), + new WktQuotedString("9102")); + + var unitNode = new WktKeywordNode( + "UNIT", + new WktQuotedString("degree"), + new WktNumber(0.0174532925199433), + authorityNode); + + string result = unitNode.ToString(); + Assert.Contains("UNIT[", result, StringComparison.Ordinal); + Assert.Contains("AUTHORITY[\"EPSG\", \"9102\"]", result, StringComparison.Ordinal); + } + + /// + /// Verifies that retains all semantic content present in the + /// compact representation and introduces newlines when the node tree contains nested keyword children. + /// + [Fact] + public void ToFormattedString_PreservesNodeContent() + { + GeographicCoordinateSystem gcs = GeographicCoordinateSystem.WGS84; + var node = (WktKeywordNode)gcs.ToWktNode(); + + string compact = node.ToString(); + string formatted = node.ToFormattedString(); + + Assert.Contains("GEOGCS", formatted, StringComparison.Ordinal); + Assert.Contains("DATUM", formatted, StringComparison.Ordinal); + Assert.Contains("WGS 84", formatted, StringComparison.Ordinal); + + // Formatted version should have newlines when there are keyword children + Assert.Contains("\n", formatted, StringComparison.Ordinal); + } + + private static WktKeywordNode ParseTree(string wkt) + { + var tokenizer = new WktTokenizer(wkt); + MethodInfo? method = typeof(WktKeywordNode).GetMethod( + "ParseTree", + BindingFlags.Static | BindingFlags.NonPublic, + binder: null, + types: WktTokenizerParameterTypes, + modifiers: null); + + Assert.NotNull(method); + return Assert.IsType(method.Invoke(null, [tokenizer])); + } + + private static T InvokeNonPublicInstance(WktKeywordNode node, string methodName, Type[] parameterTypes, params object[] arguments) + { + MethodInfo? method = typeof(WktKeywordNode).GetMethod( + methodName, + BindingFlags.Instance | BindingFlags.NonPublic, + binder: null, + types: parameterTypes, + modifiers: null); + + Assert.NotNull(method); + object? result = method.Invoke(node, arguments); + return result is null ? default! : (T)result; + } + + private static TCoordinateSystem ParseCatalogWkt1(CoordinateSystemFactory factory, int srid) + where TCoordinateSystem : CoordinateSystem + { + CoordinateSystem coordinateSystem = Assert.IsAssignableFrom(CoordinateSystemServices.GetCoordinateSystem(srid)); + return CoordinateSystemTestHelpers.RequireCoordinateSystem(factory, coordinateSystem.WKT); + } + + private static void AssertProjectedRoundTripEquivalent(ProjectedCoordinateSystem original, ProjectedCoordinateSystem parsed) + { + Assert.Equal(original.Name, parsed.Name); + Assert.Equal(original.Authority, parsed.Authority); + Assert.Equal(original.AuthorityCode, parsed.AuthorityCode); + Assert.True(original.LinearUnit.EqualParams(parsed.LinearUnit)); + Assert.True(original.Projection.EqualParams(parsed.Projection)); + Assert.True(original.GeographicCoordinateSystem.HorizontalDatum.EqualParams(parsed.GeographicCoordinateSystem.HorizontalDatum)); + Assert.True(original.GeographicCoordinateSystem.AngularUnit.EqualParams(parsed.GeographicCoordinateSystem.AngularUnit)); + Assert.True(original.GeographicCoordinateSystem.PrimeMeridian.EqualParams(parsed.GeographicCoordinateSystem.PrimeMeridian)); + Assert.Equal(original.AxisInfo.Count, parsed.AxisInfo.Count); + + for (int i = 0; i < original.AxisInfo.Count; i++) + { + Assert.Equal(original.AxisInfo[i].Name, parsed.AxisInfo[i].Name); + Assert.Equal(original.AxisInfo[i].Orientation, parsed.AxisInfo[i].Orientation); + } + } + + private static VerticalCoordinateSystem CreateBoundVerticalCoordinateSystem() + { + CoordinateSystemFactory factory = new(); + VerticalCoordinateSystem vertical = factory.CreateVerticalCoordinateSystem( + "EGM96 height", + factory.CreateVerticalDatum("EGM96 geoid", DatumType.VD_GeoidModelDerived), + LinearUnit.Metre, + new AxisInfo("gravity-related height (H)", AxisOrientationEnum.Up)); + CompoundCoordinateSystem hub = factory.CreateCompoundCoordinateSystem( + "WGS 84 + ellipsoidal height", + GeographicCoordinateSystem.WGS84, + CreateEllipsoidalHeightVerticalCoordinateSystem(factory)); + + return vertical.WithBoundGridTransformation(new VerticalBoundGridTransformation( + "Geographic3D to GravityRelatedHeight (EGM)", + "egm96_15.gtx", + hub)); + } + + private static VerticalCoordinateSystem CreateEllipsoidalHeightVerticalCoordinateSystem(CoordinateSystemFactory factory) + { + return factory.CreateVerticalCoordinateSystem( + "Ellipsoidal height", + factory.CreateVerticalDatum("Ellipsoidal height datum", DatumType.VD_Ellipsoidal), + LinearUnit.Metre, + new AxisInfo("Ellipsoidal height", AxisOrientationEnum.Up)); + } +} diff --git a/test/ProjNet.Tests/IO/Xml/XmlSerializationTests.cs b/test/ProjNet.Tests/IO/Xml/XmlSerializationTests.cs new file mode 100644 index 00000000..795207f7 --- /dev/null +++ b/test/ProjNet.Tests/IO/Xml/XmlSerializationTests.cs @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.IO.Xml; + +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Tests that verify the ToXml() methods produce the correct XML structure +/// and match the existing XML property output. +/// +public class XmlSerializationTests +{ + /// + /// Verifies that produces a CS_AxisInfo element + /// with the correct Name and Orientation attributes and that its serialized + /// form matches the output of the XML property. + /// + [Fact] + public void AxisInfo_ToXml_MatchesXmlProperty() + { + var axis = new AxisInfo("Lon", AxisOrientationEnum.East); + + XElement element = axis.ToXml(); + + Assert.Equal("CS_AxisInfo", element.Name.LocalName); + Assert.Equal("Lon", element.Attribute("Name")!.Value); + Assert.Equal("EAST", element.Attribute("Orientation")!.Value); + Assert.Equal(NormalizeXml(axis.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that produces a CS_ProjectionParameter + /// element with the correct Name and Value attributes and that its serialized + /// form matches the output of the XML property. + /// + [Fact] + public void ProjectionParameter_ToXml_MatchesXmlProperty() + { + var param = new ProjectionParameter("central_meridian", -93.5); + + XElement element = param.ToXml(); + + Assert.Equal("CS_ProjectionParameter", element.Name.LocalName); + Assert.Equal("central_meridian", element.Attribute("Name")!.Value); + Assert.Equal("-93.5", element.Attribute("Value")!.Value); + Assert.Equal(NormalizeXml(param.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that produces a CS_WGS84ConversionInfo + /// element with the correct translation and rotation attributes (Dx, Dy, Dz, + /// Ex, Ppm) and that its serialized form matches the output of the XML property. + /// + [Fact] + public void Wgs84ConversionInfo_ToXml_MatchesXmlProperty() + { + var info = new Wgs84ConversionInfo(-87, -98, -121, 0, 0, 0, 0); + + XElement element = info.ToXml(); + + Assert.Equal("CS_WGS84ConversionInfo", element.Name.LocalName); + Assert.Equal("-87", element.Attribute("Dx")!.Value); + Assert.Equal("-98", element.Attribute("Dy")!.Value); + Assert.Equal("-121", element.Attribute("Dz")!.Value); + Assert.Equal("0", element.Attribute("Ex")!.Value); + Assert.Equal("0", element.Attribute("Ppm")!.Value); + Assert.Equal(NormalizeXml(info.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the degree unit produces a + /// CS_AngularUnit element with a RadiansPerUnit attribute and that its + /// serialized form matches the output of the XML property. + /// + [Fact] + public void AngularUnit_ToXml_MatchesXmlProperty() + { + AngularUnit unit = AngularUnit.Degrees; + + XElement element = unit.ToXml(); + + Assert.Equal("CS_AngularUnit", element.Name.LocalName); + Assert.NotNull(element.Attribute("RadiansPerUnit")); + Assert.Equal(NormalizeXml(unit.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the metre unit produces a + /// CS_LinearUnit element with a MetersPerUnit attribute and that its + /// serialized form matches the output of the XML property. + /// + [Fact] + public void LinearUnit_ToXml_MatchesXmlProperty() + { + LinearUnit unit = LinearUnit.Metre; + + XElement element = unit.ToXml(); + + Assert.Equal("CS_LinearUnit", element.Name.LocalName); + Assert.NotNull(element.Attribute("MetersPerUnit")); + Assert.Equal(NormalizeXml(unit.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the WGS84 ellipsoid produces a + /// CS_Ellipsoid element with SemiMajorAxis, SemiMinorAxis, + /// InverseFlattening, and IvfDefinitive attributes and that its serialized + /// form matches the output of the XML property. + /// + [Fact] + public void Ellipsoid_ToXml_MatchesXmlProperty() + { + Ellipsoid ellipsoid = Ellipsoid.WGS84; + + XElement element = ellipsoid.ToXml(); + + Assert.Equal("CS_Ellipsoid", element.Name.LocalName); + Assert.NotNull(element.Attribute("SemiMajorAxis")); + Assert.NotNull(element.Attribute("SemiMinorAxis")); + Assert.NotNull(element.Attribute("InverseFlattening")); + Assert.NotNull(element.Attribute("IvfDefinitive")); + Assert.Equal(NormalizeXml(ellipsoid.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the Greenwich meridian produces + /// a CS_PrimeMeridian element with a Longitude attribute and the required + /// CS_Info and CS_AngularUnit child elements. + /// + [Fact] + public void PrimeMeridian_ToXml_MatchesXmlProperty() + { + PrimeMeridian pm = PrimeMeridian.Greenwich; + + XElement element = pm.ToXml(); + + Assert.Equal("CS_PrimeMeridian", element.Name.LocalName); + Assert.NotNull(element.Attribute("Longitude")); + + // The existing XML property has a trailing space before '>' in the attribute: + // vs + // XElement won't produce that trailing space, so we compare structure not exact string. + Assert.NotNull(element.Element("CS_Info")); + Assert.NotNull(element.Element("CS_AngularUnit")); + } + + /// + /// Verifies that for the WGS84 datum (which has no + /// WGS84 conversion parameters) produces a CS_HorizontalDatum element with a + /// DatumType attribute and the required CS_Info and CS_Ellipsoid + /// child elements, and that its serialized form matches the output of the XML property. + /// + [Fact] + public void HorizontalDatum_ToXml_MatchesXmlProperty() + { + HorizontalDatum datum = HorizontalDatum.WGS84; + + XElement element = datum.ToXml(); + + Assert.Equal("CS_HorizontalDatum", element.Name.LocalName); + Assert.NotNull(element.Attribute("DatumType")); + Assert.NotNull(element.Element("CS_Info")); + Assert.NotNull(element.Element("CS_Ellipsoid")); + Assert.Equal(NormalizeXml(datum.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the ED50 datum (which carries + /// WGS84 conversion parameters) emits a CS_WGS84ConversionInfo child element and + /// that its serialized form matches the output of the XML property. + /// + [Fact] + public void HorizontalDatum_WithWgs84Parameters_ToXml_MatchesXmlProperty() + { + HorizontalDatum datum = HorizontalDatum.ED50; + + XElement element = datum.ToXml(); + + Assert.Equal("CS_HorizontalDatum", element.Name.LocalName); + Assert.NotNull(element.Element("CS_WGS84ConversionInfo")); + Assert.Equal(NormalizeXml(datum.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the ODN datum produces a + /// CS_VerticalDatum element with a DatumType attribute and a CS_Info + /// child element, and that its serialized form matches the output of the XML property. + /// + [Fact] + public void VerticalDatum_ToXml_MatchesXmlProperty() + { + VerticalDatum datum = VerticalDatum.ODN; + + XElement element = datum.ToXml(); + + Assert.Equal("CS_VerticalDatum", element.Name.LocalName); + Assert.NotNull(element.Attribute("DatumType")); + Assert.NotNull(element.Element("CS_Info")); + Assert.Equal(NormalizeXml(datum.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that produces a CS_Projection element + /// with the correct Classname attribute, a CS_Info child element, one + /// CS_ProjectionParameter child element per parameter, and that its serialized + /// form matches the output of the XML property. + /// + [Fact] + public void Projection_ToXml_MatchesXmlProperty() + { + var parameters = new List + { + new("latitude_of_origin", 0.0), + new("central_meridian", 0.0), + new("scale_factor", 0.9996), + new("false_easting", 500000), + new("false_northing", 0), + }; + + var projection = new Projection( + "Transverse_Mercator", + parameters, + "UTM32N", + "EPSG", + 32632, + string.Empty, + string.Empty, + string.Empty); + + XElement element = projection.ToXml(); + + Assert.Equal("CS_Projection", element.Name.LocalName); + Assert.Equal("Transverse_Mercator", element.Attribute("Classname")!.Value); + Assert.NotNull(element.Element("CS_Info")); + + IEnumerable paramElements = element.Elements("CS_ProjectionParameter"); + Assert.Equal(5, new List(paramElements).Count); + + Assert.Equal(NormalizeXml(projection.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the WGS84 geographic + /// coordinate system produces an outer CS_CoordinateSystem element with a + /// Dimension attribute wrapping a CS_GeographicCoordinateSystem element + /// that contains CS_Info, CS_HorizontalDatum, CS_AngularUnit, and + /// CS_PrimeMeridian child elements. + /// + [Fact] + public void GeographicCoordinateSystem_ToXml_MatchesXmlProperty() + { + GeographicCoordinateSystem gcs = GeographicCoordinateSystem.WGS84; + + XElement element = gcs.ToXml(); + + Assert.Equal("CS_CoordinateSystem", element.Name.LocalName); + Assert.NotNull(element.Attribute("Dimension")); + + XElement? inner = element.Element("CS_GeographicCoordinateSystem"); + Assert.NotNull(inner); + Assert.NotNull(inner.Element("CS_Info")); + Assert.NotNull(inner.Element("CS_HorizontalDatum")); + Assert.NotNull(inner.Element("CS_AngularUnit")); + Assert.NotNull(inner.Element("CS_PrimeMeridian")); + } + + /// + /// Verifies that for a UTM zone 32 north + /// projection produces an outer CS_CoordinateSystem element with + /// Dimension set to 2, wrapping a CS_ProjectedCoordinateSystem + /// element that contains CS_Info, a nested CS_CoordinateSystem (geographic), + /// CS_LinearUnit, and CS_Projection child elements. + /// + [Fact] + public void ProjectedCoordinateSystem_ToXml_ProducesCorrectStructure() + { + var pcs = ProjectedCoordinateSystem.WGS84_UTM(32, true); + + XElement element = pcs.ToXml(); + + Assert.Equal("CS_CoordinateSystem", element.Name.LocalName); + Assert.Equal("2", element.Attribute("Dimension")!.Value); + + XElement? inner = element.Element("CS_ProjectedCoordinateSystem"); + Assert.NotNull(inner); + Assert.NotNull(inner.Element("CS_Info")); + Assert.NotNull(inner.Element("CS_CoordinateSystem")); // nested geographic CS + Assert.NotNull(inner.Element("CS_LinearUnit")); + Assert.NotNull(inner.Element("CS_Projection")); + } + + /// + /// Verifies that for the WGS84 geocentric + /// coordinate system produces an outer CS_CoordinateSystem element with + /// Dimension set to 3, wrapping a CS_GeocentricCoordinateSystem + /// element that contains CS_Info, CS_HorizontalDatum, CS_LinearUnit, + /// and CS_PrimeMeridian child elements. + /// + [Fact] + public void GeocentricCoordinateSystem_ToXml_ProducesCorrectStructure() + { + GeocentricCoordinateSystem gcc = GeocentricCoordinateSystem.WGS84; + + XElement element = gcc.ToXml(); + + Assert.Equal("CS_CoordinateSystem", element.Name.LocalName); + Assert.Equal("3", element.Attribute("Dimension")!.Value); + + XElement? inner = element.Element("CS_GeocentricCoordinateSystem"); + Assert.NotNull(inner); + Assert.NotNull(inner.Element("CS_Info")); + Assert.NotNull(inner.Element("CS_HorizontalDatum")); + Assert.NotNull(inner.Element("CS_LinearUnit")); + Assert.NotNull(inner.Element("CS_PrimeMeridian")); + } + + /// + /// Verifies that for the ODN vertical + /// coordinate system produces an outer CS_CoordinateSystem element with + /// Dimension set to 1, wrapping a CS_VerticalCoordinateSystem + /// element that contains CS_Info, CS_VerticalDatum, and CS_LinearUnit + /// child elements, and that its serialized form matches the output of the XML property. + /// + [Fact] + public void VerticalCoordinateSystem_ToXml_MatchesXmlProperty() + { + VerticalCoordinateSystem vcs = VerticalCoordinateSystem.ODN; + + XElement element = vcs.ToXml(); + + Assert.Equal("CS_CoordinateSystem", element.Name.LocalName); + Assert.Equal("1", element.Attribute("Dimension")!.Value); + + XElement? inner = element.Element("CS_VerticalCoordinateSystem"); + Assert.NotNull(inner); + Assert.NotNull(inner.Element("CS_Info")); + Assert.NotNull(inner.Element("CS_VerticalDatum")); + Assert.NotNull(inner.Element("CS_LinearUnit")); + + Assert.Equal(NormalizeXml(vcs.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that produces an outer + /// CS_CoordinateSystem element wrapping a CS_CompoundCoordinateSystem + /// element that contains a CS_Info child element and exactly two nested + /// CS_CoordinateSystem elements representing the head and tail coordinate systems, + /// and that its serialized form matches the output of the XML property. + /// + [Fact] + public void CompoundCoordinateSystem_ToXml_ProducesCorrectStructure() + { + GeographicCoordinateSystem gcs = GeographicCoordinateSystem.WGS84; + VerticalCoordinateSystem vcs = VerticalCoordinateSystem.ODN; + var compound = new CompoundCoordinateSystem(gcs, vcs, "TestCompound", "EPSG", 9999, string.Empty, string.Empty, string.Empty); + + XElement element = compound.ToXml(); + + Assert.Equal("CS_CoordinateSystem", element.Name.LocalName); + + XElement? inner = element.Element("CS_CompoundCoordinateSystem"); + Assert.NotNull(inner); + Assert.NotNull(inner.Element("CS_Info")); + + // Head and tail coordinate systems should be nested + IEnumerable csDimensions = inner.Elements("CS_CoordinateSystem"); + Assert.Equal(2, new List(csDimensions).Count); + + Assert.Equal(NormalizeXml(compound.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that throws a + /// , consistent with the XML property on + /// the same type. + /// + [Fact] + public void FittedCoordinateSystem_ToXml_ThrowsNotSupportedException() + { + // FittedCoordinateSystem.XML also throws NotSupportedException + GeographicCoordinateSystem gcs = GeographicCoordinateSystem.WGS84; + var fcs = new FittedCoordinateSystem( + gcs, + new ProjNet.CoordinateSystems.Transformations.AffineTransform( +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + new double[,] + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + }), +#pragma warning restore CA1814 + "TestFitted", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + Assert.Throws(() => fcs.ToXml()); + } + + /// + /// Verifies that the returned by ToXml() + /// serializes to a well-formed XML string that can be parsed back by + /// and produces a structurally identical element. + /// + [Fact] + public void ToXml_RoundTrip_ParseBackToXElement() + { + // Verify the XElement output can be parsed back + AngularUnit unit = AngularUnit.Degrees; + XElement element = unit.ToXml(); + string xmlString = element.ToString(SaveOptions.DisableFormatting); + + var reparsed = XElement.Parse(xmlString); + Assert.Equal(element.ToString(), reparsed.ToString()); + } + + /// + /// Verifies that for the radian unit produces output + /// whose serialized form matches the output of the XML property. + /// + [Fact] + public void AngularUnit_Radian_ToXml_MatchesXmlProperty() + { + AngularUnit unit = AngularUnit.Radian; + + XElement element = unit.ToXml(); + + Assert.Equal(NormalizeXml(unit.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the foot unit produces output + /// whose serialized form matches the output of the XML property. + /// + [Fact] + public void LinearUnit_Foot_ToXml_MatchesXmlProperty() + { + LinearUnit unit = LinearUnit.Foot; + + XElement element = unit.ToXml(); + + Assert.Equal(NormalizeXml(unit.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for the Clarke 1866 ellipsoid, which is + /// not IVF-definitive and carries a inverse + /// flattening value, emits IvfDefinitive as 0 and that its serialized form + /// matches the output of the XML property. + /// + [Fact] + public void Ellipsoid_Clarke1866_ToXml_MatchesXmlProperty() + { + // Clarke1866 uses non-IVF-definitive with PositiveInfinity inverse flattening + Ellipsoid ellipsoid = Ellipsoid.Clarke1866; + + XElement element = ellipsoid.ToXml(); + + Assert.Equal("0", element.Attribute("IvfDefinitive")!.Value); + Assert.Equal(NormalizeXml(ellipsoid.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Verifies that for a default instance (all + /// parameters zero) produces output whose serialized form matches the output of the + /// XML property. + /// + [Fact] + public void Wgs84ConversionInfo_ZeroValues_ToXml_MatchesXmlProperty() + { + var info = new Wgs84ConversionInfo(); + + XElement element = info.ToXml(); + + Assert.Equal(NormalizeXml(info.XML), NormalizeXml(element.ToString(SaveOptions.DisableFormatting))); + } + + /// + /// Normalizes XML string by removing insignificant whitespace differences. + /// XElement may add or omit a space before self-closing element markers (/>) + /// and the existing StringBuilder-based XML properties may have a trailing space before + /// the closing > of opening tags. Both are valid XML; this helper makes them identical. + /// + private static string NormalizeXml(string xml) + { + return xml.Replace(" />", "/>", StringComparison.Ordinal).Replace(" >", ">", StringComparison.Ordinal); + } +} diff --git a/test/ProjNet.Tests/Integration/DhdnGkToUtmTheoryTests.cs b/test/ProjNet.Tests/Integration/DhdnGkToUtmTheoryTests.cs new file mode 100644 index 00000000..ba5299c0 --- /dev/null +++ b/test/ProjNet.Tests/Integration/DhdnGkToUtmTheoryTests.cs @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies DHDN Gauß-Krüger to ETRS89 UTM fixture cases against the BETA2007 NTv2 grid. +/// +public sealed class DhdnGkToUtmTheoryTests +{ + private const double FixtureToleranceMetres = 0.001d; + private const string FixtureMarker = "Tests for GK system zones to UTM32/33 not implemented yet"; + private static readonly CoordinateSystemServices Css = new(CoordinateSystemServicesTests.LoadCsv()); + private static readonly CoordinateTransformationFactory TransformationFactory = new(); + private static readonly Dictionary<(int SourceSrid, int TargetSrid), ICoordinateTransformation> TransformationCache = []; + private static readonly object TransformationCacheSync = new(); + + /// + /// Provides the projected GK-to-UTM fixture rows. + /// + /// Parsed fixture rows with source/target SRIDs and expected coordinates. + public static IEnumerable> GetProjectedCases() + { + foreach (ProjectedFixtureCase testCase in ParseProjectedCases()) + { + yield return new TheoryDataRow( + testCase.SourceSrid, + testCase.TargetSrid, + testCase.InputX, + testCase.InputY, + testCase.ExpectedX, + testCase.ExpectedY, + $"{testCase.SourceTag}->{testCase.TargetTag}"); + } + } + + /// + /// Verifies that the projected section contributes the expected 94 GK-to-UTM cases. + /// + [Fact] + public void ProjectedFixtureSectionContainsNinetyFourCases() + { + int caseCount = 0; + foreach (ProjectedFixtureCase projectedFixtureCase in ParseProjectedCases()) + { + _ = projectedFixtureCase; + caseCount++; + } + + Assert.Equal(94, caseCount); + } + + /// + /// Verifies that all projected GK-to-UTM fixture cases are reproducible with the resolved BETA2007 grid-backed SRID transformations. + /// + /// Source projected SRID. + /// Target projected SRID. + /// Source easting. + /// Source northing. + /// Expected target easting. + /// Expected target northing. + /// Human-readable case label for assertion output. + [Theory] + [MemberData(nameof(GetProjectedCases))] + public void ProjectedFixtureCaseMatchesBETA2007GridBackedTransformation( + int sourceSrid, + int targetSrid, + double inputX, + double inputY, + double expectedX, + double expectedY, + string caseLabel) + { + ICoordinateTransformation transformation = CreateGridBackedTransformation(sourceSrid, targetSrid); + double[] output = transformation.MathTransform.Transform([inputX, inputY, 0d]); + + double deltaX = Math.Abs(output[0] - expectedX); + double deltaY = Math.Abs(output[1] - expectedY); + + Assert.True( + deltaX <= FixtureToleranceMetres, + FormattableString.Invariant($"{caseLabel}: expected X delta <= {FixtureToleranceMetres:R} m but was {deltaX:R}.")); + Assert.True( + deltaY <= FixtureToleranceMetres, + FormattableString.Invariant($"{caseLabel}: expected Y delta <= {FixtureToleranceMetres:R} m but was {deltaY:R}.")); + } + + private static IEnumerable ParseProjectedCases() + { + const int sridDhdnGk2 = 31466; + const int sridDhdnGk3 = 31467; + const int sridDhdnGk4 = 31468; + const int sridDhdnGk5 = 31469; + const int sridEtrs89Utm32 = 25832; + const int sridEtrs89Utm33 = 25833; + + Dictionary sridsByTag = new(StringComparer.Ordinal) + { + ["DE_DHDN_3GK2"] = sridDhdnGk2, + ["DE_DHDN_3GK3"] = sridDhdnGk3, + ["DE_DHDN_3GK4"] = sridDhdnGk4, + ["DE_DHDN_3GK5"] = sridDhdnGk5, + ["ETRS89_UTM32"] = sridEtrs89Utm32, + ["ETRS89_UTM33"] = sridEtrs89Utm33, + }; + + string fixturePath = FindDhdnFixturePath(); + string[] lines = File.ReadAllLines(fixturePath); + bool inProjectedSection = false; + TaggedCoordinate? pendingAccept = null; + + foreach (string line in lines) + { + string trimmed = line.Trim(); + if (!inProjectedSection) + { + if (trimmed.Equals(FixtureMarker, StringComparison.Ordinal)) + { + inProjectedSection = true; + } + + continue; + } + + if (trimmed.Length == 0 || trimmed.StartsWith('-')) + { + continue; + } + + if (trimmed.StartsWith("accept", StringComparison.OrdinalIgnoreCase)) + { + pendingAccept = ParseTaggedCoordinate(line, "accept"); + continue; + } + + if (!trimmed.StartsWith("expect", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + TaggedCoordinate accept = pendingAccept ?? throw new FormatException("Encountered projected expect line without a preceding accept line."); + TaggedCoordinate expect = ParseTaggedCoordinate(line, "expect"); + + if (!sridsByTag.TryGetValue(accept.Tag, out int sourceSrid)) + { + throw new FormatException(FormattableString.Invariant($"Unknown projected source tag '{accept.Tag}'.")); + } + + if (!sridsByTag.TryGetValue(expect.Tag, out int targetSrid)) + { + throw new FormatException(FormattableString.Invariant($"Unknown projected target tag '{expect.Tag}'.")); + } + + yield return new ProjectedFixtureCase( + sourceSrid, + targetSrid, + accept.X, + accept.Y, + expect.X, + expect.Y, + accept.Tag, + expect.Tag); + + pendingAccept = null; + } + + if (pendingAccept is not null) + { + throw new FormatException("Projected GK-to-UTM fixture ended with an unmatched accept line."); + } + } + + private static ICoordinateTransformation CreateGridBackedTransformation(int sourceSrid, int targetSrid) + { + lock (TransformationCacheSync) + { + if (TransformationCache.TryGetValue((sourceSrid, targetSrid), out ICoordinateTransformation? cachedTransformation)) + { + return cachedTransformation; + } + + ProjectedCoordinateSystem source = Assert.IsType( + Css.GetCoordinateSystem(sourceSrid), + exactMatch: false); + ProjectedCoordinateSystem target = Assert.IsType( + Css.GetCoordinateSystem(targetSrid), + exactMatch: false); + string gridPath = Path.Combine(FindFixtureGridDirectory(), "BETA2007.gsb"); + + ConcatenatedTransform concatenatedTransform = new(); + concatenatedTransform.CoordinateTransformationList.Add( + TransformationFactory.CreateFromCoordinateSystems(source, source.GeographicCoordinateSystem)); + concatenatedTransform.CoordinateTransformationList.Add( + new CoordinateTransformation( + source.GeographicCoordinateSystem, + target.GeographicCoordinateSystem, + TransformType.Transformation, + new Ntv2HGridShiftMathTransform([gridPath]), + "NTv2", + "EPSG", + 15948, + string.Empty, + $"Grid: {gridPath}")); + concatenatedTransform.CoordinateTransformationList.Add( + TransformationFactory.CreateFromCoordinateSystems(target.GeographicCoordinateSystem, target)); + + CoordinateTransformation transformation = new( + source, + target, + TransformType.Transformation, + concatenatedTransform, + "DHDN GK to ETRS89 UTM via BETA2007", + "EPSG", + 15948, + string.Empty, + $"Grid: {gridPath}"); + TransformationCache[(sourceSrid, targetSrid)] = transformation; + return transformation; + } + } + + private static TaggedCoordinate ParseTaggedCoordinate(string line, string keyword) + { + int commentIndex = line.IndexOf('#', StringComparison.Ordinal); + if (commentIndex < 0) + { + throw new FormatException(FormattableString.Invariant($"Projected fixture line is missing a tag comment: '{line}'.")); + } + + string coordinatePart = line[..commentIndex]; + string[] tokens = coordinatePart.Split([' ', '\t'], StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length < 3 || !tokens[0].Equals(keyword, StringComparison.OrdinalIgnoreCase)) + { + throw new FormatException(FormattableString.Invariant($"Could not parse projected fixture line: '{line}'.")); + } + + return new TaggedCoordinate( + double.Parse(tokens[1], CultureInfo.InvariantCulture), + double.Parse(tokens[2], CultureInfo.InvariantCulture), + line[(commentIndex + 1)..].Trim()); + } + + private static string FindDhdnFixturePath() + { + DirectoryInfo? current = new(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "gie", "DHDN_ETRS89.gie"); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new FileNotFoundException("Could not locate the DHDN_ETRS89.gie fixture under test\\ProjNet.Tests\\Fixtures\\gie.", "DHDN_ETRS89.gie"); + } + + private static string FindFixtureGridDirectory() + { + DirectoryInfo? current = new(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "grids"); + if (File.Exists(Path.Combine(candidate, "BETA2007.gsb"))) + { + return candidate; + } + + current = current.Parent; + } + + throw new DirectoryNotFoundException("Could not locate test\\ProjNet.Tests\\Fixtures\\grids with BETA2007.gsb."); + } + + private sealed record TaggedCoordinate(double X, double Y, string Tag); + + private sealed record ProjectedFixtureCase( + int SourceSrid, + int TargetSrid, + double InputX, + double InputY, + double ExpectedX, + double ExpectedY, + string SourceTag, + string TargetTag); +} diff --git a/test/ProjNet.Tests/Integration/EpsgTransformationIntegrationTests.cs b/test/ProjNet.Tests/Integration/EpsgTransformationIntegrationTests.cs new file mode 100644 index 00000000..3c4a4686 --- /dev/null +++ b/test/ProjNet.Tests/Integration/EpsgTransformationIntegrationTests.cs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using ProjNet; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Verifies representative EPSG SRID transformations across multiple operation categories. +/// +[Collection(GlobalEnvironmentTestIsolation.Name)] +public class EpsgTransformationIntegrationTests +{ + private static readonly CoordinateSystemServices Services = new(); + + /// + /// Provides representative EPSG SRID pairs with reference coordinates and inverse round-trip tolerances. + /// + /// Integration rows covering projection-only, projected-chain, datum-shifted, and grid-backed EPSG pairs. + public static TheoryDataRow[] GetRepresentativeCases() + { + return + [ + new TheoryDataRow(4326, 3857, 10d, 10d, 1113194.90793274d, 1118889.97485796d, 1e-6d, 1e-6d, 1e-9d, 1e-9d, "projection-only 4326->3857"), + new TheoryDataRow(3857, 4326, 1113194.90793274d, 1118889.97485796d, 10d, 10d, 1e-9d, 1e-9d, 1e-6d, 1e-6d, "projection-only 3857->4326"), + new TheoryDataRow(25832, 3857, 702575d, 6153153d, 1358761.89d, 7456070.47d, 0.02d, 0.02d, 0.02d, 0.02d, "projected chain 25832->3857"), + new TheoryDataRow(25832, 4326, 702575d, 6153153d, 12.20596573266128d, 55.48246005652269d, 5e-7d, 5e-7d, 0.02d, 0.02d, "utm to geographic 25832->4326"), + new TheoryDataRow(27700, 4326, 362895d, 155602d, -2.5335813d, 51.2983258d, 0.0005d, 0.0005d, 2d, 2d, "helmert-backed 27700->4326"), + new TheoryDataRow(26910, 4326, 3523562.711189d, 6246615.391161d, -82.0479097d, 48.4185597d, 0.01d, 0.01d, -1d, -1d, "datum-shifted 26910->4326"), + new TheoryDataRow(4326, 3035, 16.4d, 48.2d, 4796297.431434812d, 2807999.1539475969d, 1e-2d, 1e-2d, 1e-2d, 1e-2d, "lambert azimuthal equal-area 4326->3035"), + new TheoryDataRow(31466, 25832, 2598417.333192d, 5930677.980308d, 399340.601863d, 5928794.177992d, 3.5d, 3.5d, 1e-2d, 1e-2d, "dhdn fallback/grid 31466->25832"), + new TheoryDataRow(31467, 25832, 3399371.190396d, 5930724.531323d, 399340.601862d, 5928794.177992d, 3.5d, 3.5d, 1e-2d, 1e-2d, "dhdn fallback/grid 31467->25832"), + new TheoryDataRow(31467, 25833, 3615881.001454d, 5940351.727710d, 218617.111391d, 5945399.220269d, 3.5d, 3.5d, 1e-2d, 1e-2d, "dhdn fallback/grid 31467->25833"), + ]; + } + + /// + /// Verifies that representative EPSG pairs resolve, match their reference coordinate, and round-trip through the resolved inverse transform. + /// + /// Source EPSG SRID. + /// Target EPSG SRID. + /// Source x or longitude. + /// Source y or latitude. + /// Expected target x or longitude. + /// Expected target y or latitude. + /// Accepted target x tolerance. + /// Accepted target y tolerance. + /// Accepted source x tolerance after inverse transformation. + /// Accepted source y tolerance after inverse transformation. + /// Human-readable label for assertion output. + [Theory] + [MemberData(nameof(GetRepresentativeCases))] + public void CreateTransformation_WithRepresentativeEpsgPairs_MatchesReferenceAndRoundTrips( + int sourceSrid, + int targetSrid, + double inputX, + double inputY, + double expectedX, + double expectedY, + double expectedToleranceX, + double expectedToleranceY, + double roundTripToleranceX, + double roundTripToleranceY, + string caseLabel) + { + ICoordinateTransformation forward = Assert.IsType(Services.CreateTransformation(sourceSrid, targetSrid), exactMatch: false); + + double[] output = forward.MathTransform.Transform([inputX, inputY]); + + Assert.True(!double.IsNaN(output[0]), FormattableString.Invariant($"{caseLabel}: expected finite X output.")); + Assert.True(!double.IsNaN(output[1]), FormattableString.Invariant($"{caseLabel}: expected finite Y output.")); + AssertWithinTolerance(output[0], expectedX, expectedToleranceX, caseLabel, "target X"); + AssertWithinTolerance(output[1], expectedY, expectedToleranceY, caseLabel, "target Y"); + + if (roundTripToleranceX < 0d || roundTripToleranceY < 0d) + { + return; + } + + Assert.True(forward.MathTransform.IsInvertible, FormattableString.Invariant($"{caseLabel}: expected resolved transform to expose inverse support.")); + + MathTransform inverse = Assert.IsType(forward.MathTransform.Inverse(), exactMatch: false); + double[] roundTripped = inverse.Transform(output); + AssertWithinTolerance(roundTripped[0], inputX, roundTripToleranceX, caseLabel, "round-trip X"); + AssertWithinTolerance(roundTripped[1], inputY, roundTripToleranceY, caseLabel, "round-trip Y"); + } + + private static void AssertWithinTolerance(double actual, double expected, double tolerance, string caseLabel, string axisLabel) + { + double delta = Math.Abs(actual - expected); + Assert.True( + delta <= tolerance, + FormattableString.Invariant($"{caseLabel}: expected {axisLabel} delta <= {tolerance:R} but was {delta:R} (expected {expected:R}, actual {actual:R}).")); + } +} diff --git a/test/ProjNet.Tests/Integration/EpsgWktEquivalenceTheoryTests.cs b/test/ProjNet.Tests/Integration/EpsgWktEquivalenceTheoryTests.cs new file mode 100644 index 00000000..84c0821c --- /dev/null +++ b/test/ProjNet.Tests/Integration/EpsgWktEquivalenceTheoryTests.cs @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; +using ProjNet.Data; +using ProjNet.Data.Generated; +using ProjNet.Tests.IO.CoordinateSystems; +using Xunit; + +/// +/// Verifies semantic equivalence between generated EPSG WKT and representative fixtures sourced from the shared EPSG archive. +/// +public class EpsgWktEquivalenceTheoryTests +{ + private static readonly int[] Geographic2dSrids = + [ + 4121, + 4230, + 4258, + 4267, + 4269, + 4277, + 4283, + 4314, + 4326, + 4807, + ]; + + private static readonly int[] ProjectedSrids = + [ + 2193, + 27700, + 3031, + 3035, + 3413, + 3857, + 5514, + 32632, + 32633, + 32733, + ]; + + private static readonly int[] GeocentricSrids = + [ + 3822, + 3887, + 4039, + 4079, + 4479, + 4481, + 4896, + 4915, + 4936, + 4978, + ]; + + private static readonly int[] VerticalSrids = + [ + 3855, + 3900, + 4440, + 5608, + 5701, + 5714, + 5739, + 5861, + 10150, + 10190, + ]; + + private static readonly int[] CompoundSrids = + [ + 3902, + 3903, + 5318, + 7405, + 7415, + 7956, + 8801, + 9289, + 9518, + 9527, + ]; + + private static readonly int[] RepresentativeSrids = + [ + .. Geographic2dSrids, + .. ProjectedSrids, + .. GeocentricSrids, + .. VerticalSrids, + .. CompoundSrids, + ]; + + private static readonly Regex SrsIdRegex = new("(?:AUTHORITY|ID)\\[\"EPSG\",\\s*\"?(?\\d+)\"?\\]", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + private static readonly Regex EllipsoidRegex = new("ELLIPSOID\\[\"[^\"]+\",\\s*(?[-+0-9.Ee]+),\\s*(?[-+0-9.Ee]+)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + private static readonly Regex MethodRegex = new("(?:PROJECTION|METHOD)\\[\"(?[^\"]+)\"", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + private static readonly Regex ParameterRegex = new("PARAMETER\\[\"(?[^\"]+)\",\\s*(?[-+0-9.Ee]+)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + + private static readonly Lazy> CatalogDefinitions = new(() => + new ManagedCoordinateSystemDefinitionProvider() + .GetDefinitions() + .GroupBy(item => item.Srid) + .ToDictionary(group => group.Key, group => group.Last().Wkt)); + + /// + /// Enumerates representative EPSG archive fixture rows used by the WKT equivalence theory. + /// + /// SRID/WKT row pairs. + public static IEnumerable> EpsgFixtureRows() + => EpsgArchiveWktFixtureSource.GetTheoryDataRows(RepresentativeSrids); + + /// + /// Verifies that the representative EPSG archive fixtures cover at least 50 representative SRIDs with 10 examples per supported CRS kind. + /// + [Fact] + public void EpsgFixtureShouldCoverFiftyRepresentativeCoordinateSystemsAcrossAllKinds() + { + int[] srids = RepresentativeSrids; + Assert.True(srids.Length >= 50, $"Expected at least 50 EPSG WKT fixture rows, but found {srids.Length}."); + Assert.Equal(srids.Length, srids.Distinct().Count()); + + var counts = srids + .GroupBy(GetCoordinateSystemKind) + .ToDictionary(group => group.Key, group => group.Count()); + + Assert.Equal(10, GetKindCount(counts, EpsgCoordinateSystemKind.Geographic2D)); + Assert.Equal(10, GetKindCount(counts, EpsgCoordinateSystemKind.Projected)); + Assert.Equal(10, GetKindCount(counts, EpsgCoordinateSystemKind.Geocentric)); + Assert.Equal(10, GetKindCount(counts, EpsgCoordinateSystemKind.Vertical)); + Assert.Equal(10, GetKindCount(counts, EpsgCoordinateSystemKind.Compound)); + } + + /// + /// Verifies that generated catalog WKT is equivalent to the representative EPSG archive fixture for a given SRID. + /// + /// EPSG SRID. + /// Expected WKT from fixture. + [Theory] + [MemberData(nameof(EpsgFixtureRows))] + public void GeneratedCatalogWktShouldBeEquivalentToRepresentativeEpsgArchiveFixture(int srid, string expectedWkt) + { + Assert.True(CatalogDefinitions.Value.TryGetValue(srid, out string? generatedWkt), $"SRID {srid} not found in managed EPSG catalog."); + Assert.True(AreEquivalent(expectedWkt, generatedWkt, srid), $"WKT mismatch for SRID {srid}."); + } + + private static bool AreEquivalent(string expectedWkt, string actualWkt, int srid) + { + if (string.Equals(Normalize(expectedWkt), Normalize(actualWkt), StringComparison.Ordinal)) + { + return true; + } + + long expectedSrid = TryExtractSrid(expectedWkt); + long actualSrid = TryExtractSrid(actualWkt); + if (expectedSrid != srid || actualSrid != srid) + { + return false; + } + + if (!HasCompatibleRootType(expectedWkt, actualWkt)) + { + return false; + } + + if (!EllipsoidMatches(expectedWkt, actualWkt)) + { + return false; + } + + return NormalizeProjectionMethodName(ExtractMethodName(expectedWkt)) == NormalizeProjectionMethodName(ExtractMethodName(actualWkt)) && ProjectionParametersMatch(expectedWkt, actualWkt); + } + + private static string Normalize(string wkt) => string.Concat(wkt.Where(c => !char.IsWhiteSpace(c))); + + private static long TryExtractSrid(string wkt) + { + MatchCollection matches = SrsIdRegex.Matches(wkt); + if (matches.Count == 0) + { + return -1; + } + + string id = matches[^1].Groups["id"].Value; + return long.TryParse(id, NumberStyles.Integer, CultureInfo.InvariantCulture, out long parsed) ? parsed : -1; + } + + private static bool HasCompatibleRootType(string expectedWkt, string actualWkt) + { + static string Root(string wkt) + { + if (wkt.StartsWith("PROJCRS[", StringComparison.OrdinalIgnoreCase) || wkt.StartsWith("PROJCS[", StringComparison.OrdinalIgnoreCase)) + { + return "projected"; + } + + if (wkt.StartsWith("GEOGCRS[", StringComparison.OrdinalIgnoreCase) || wkt.StartsWith("GEOGCS[", StringComparison.OrdinalIgnoreCase)) + { + return "geographic"; + } + + if (wkt.StartsWith("GEODCRS[", StringComparison.OrdinalIgnoreCase) || wkt.StartsWith("GEODETICCRS[", StringComparison.OrdinalIgnoreCase)) + { + return wkt.Contains("CS[Cartesian", StringComparison.OrdinalIgnoreCase) ? "geocentric" : "geographic"; + } + + if (wkt.StartsWith("GEOCCRS[", StringComparison.OrdinalIgnoreCase) || wkt.StartsWith("GEOCCS[", StringComparison.OrdinalIgnoreCase)) + { + return "geocentric"; + } + + if (wkt.StartsWith("VERTCRS[", StringComparison.OrdinalIgnoreCase) || wkt.StartsWith("VERT_CS[", StringComparison.OrdinalIgnoreCase)) + { + return "vertical"; + } + + return wkt.StartsWith("COMPOUNDCRS[", StringComparison.OrdinalIgnoreCase) || wkt.StartsWith("COMPD_CS[", StringComparison.OrdinalIgnoreCase) + ? "compound" + : "unknown"; + } + + return string.Equals(Root(expectedWkt), Root(actualWkt), StringComparison.Ordinal); + } + + private static bool EllipsoidMatches(string expectedWkt, string actualWkt) + { + Match expected = EllipsoidRegex.Match(expectedWkt); + Match actual = EllipsoidRegex.Match(actualWkt); + if (!expected.Success || !actual.Success) + { + return true; + } + + double expectedSemiMajor = ParseInvariantDouble(expected.Groups["semiMajor"].Value); + double actualSemiMajor = ParseInvariantDouble(actual.Groups["semiMajor"].Value); + double expectedInvFlattening = ParseInvariantDouble(expected.Groups["inverseFlattening"].Value); + double actualInvFlattening = ParseInvariantDouble(actual.Groups["inverseFlattening"].Value); + return NearlyEqual(expectedSemiMajor, actualSemiMajor) && NearlyEqual(expectedInvFlattening, actualInvFlattening); + } + + private static string ExtractMethodName(string wkt) + { + Match match = MethodRegex.Match(wkt); + return match.Success ? match.Groups["name"].Value : string.Empty; + } + + private static bool ProjectionParametersMatch(string expectedWkt, string actualWkt) + { + Dictionary expected = ParseParameters(expectedWkt); + if (expected.Count == 0) + { + return true; + } + + Dictionary actual = ParseParameters(actualWkt); + foreach (KeyValuePair pair in expected) + { + if (!actual.TryGetValue(pair.Key, out double actualValue)) + { + return false; + } + + if (!NearlyEqual(pair.Value, actualValue)) + { + return false; + } + } + + return true; + } + + private static Dictionary ParseParameters(string wkt) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (Match match in ParameterRegex.Matches(wkt)) + { + string name = NormalizeProjectionParameterName(match.Groups["name"].Value); + result[name] = ParseInvariantDouble(match.Groups["value"].Value); + } + + return result; + } + + private static string NormalizeProjectionMethodName(string methodName) + { + if (string.IsNullOrWhiteSpace(methodName)) + { + return string.Empty; + } + + string normalized = methodName + .ToLowerInvariant() + .Replace("(", string.Empty, StringComparison.Ordinal) + .Replace(")", string.Empty, StringComparison.Ordinal) + .Replace("-", "_", StringComparison.Ordinal) + .Replace("/", "_", StringComparison.Ordinal) + .Replace(" ", "_", StringComparison.Ordinal) + .Replace(".", "_", StringComparison.Ordinal) + .Replace("__", "_", StringComparison.Ordinal); + + return normalized switch + { + "polar_stereographic_variant_a" => "polar_stereographic", + "polar_stereographic_variant_b" => "polar_stereographic", + _ => normalized, + }; + } + + private static string NormalizeProjectionParameterName(string parameterName) + { + if (string.IsNullOrWhiteSpace(parameterName)) + { + return string.Empty; + } + + string normalized = parameterName + .ToLowerInvariant() + .Replace("(", string.Empty, StringComparison.Ordinal) + .Replace(")", string.Empty, StringComparison.Ordinal) + .Replace("-", "_", StringComparison.Ordinal) + .Replace("/", "_", StringComparison.Ordinal) + .Replace(" ", "_", StringComparison.Ordinal) + .Replace(".", "_", StringComparison.Ordinal) + .Replace("__", "_", StringComparison.Ordinal); + + return normalized switch + { + "longitude_of_natural_origin" => "central_meridian", + "longitude_of_false_origin" => "central_meridian", + "longitude_of_projection_centre" => "central_meridian", + "longitude_of_origin" => "central_meridian", + "latitude_of_natural_origin" => "latitude_of_origin", + "latitude_of_false_origin" => "latitude_of_origin", + "latitude_of_projection_centre" => "latitude_of_origin", + "scale_factor_at_natural_origin" => "scale_factor", + "scale_factor_at_projection_centre" => "scale_factor", + "scale_factor_on_initial_line" => "scale_factor", + "easting_at_false_origin" => "false_easting", + "easting_at_projection_centre" => "false_easting", + "northing_at_false_origin" => "false_northing", + "northing_at_projection_centre" => "false_northing", + "latitude_of_1st_standard_parallel" => "standard_parallel_1", + "latitude_of_2nd_standard_parallel" => "standard_parallel_2", + _ => normalized, + }; + } + + private static double ParseInvariantDouble(string value) + { + return double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); + } + + private static bool NearlyEqual(double left, double right) + { + return Math.Abs(left - right) <= 1e-9; + } + + private static EpsgCoordinateSystemKind GetCoordinateSystemKind(int srid) + { + Assert.True(EpsgGeneratedCatalog.TryGetCoordinateReference(srid, out EpsgCoordinateReferenceRecord reference, out _), $"SRID {srid} not found in managed EPSG catalog."); + return reference.Kind; + } + + private static int GetKindCount(Dictionary counts, EpsgCoordinateSystemKind kind) + { + return counts.TryGetValue(kind, out int count) ? count : 0; + } +} diff --git a/test/ProjNet.Tests/Integration/GieBuiltinsRegressionTests.cs b/test/ProjNet.Tests/Integration/GieBuiltinsRegressionTests.cs new file mode 100644 index 00000000..95519a17 --- /dev/null +++ b/test/ProjNet.Tests/Integration/GieBuiltinsRegressionTests.cs @@ -0,0 +1,1478 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Reflection; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for GIE builtins harness behavior. +/// +public class GieBuiltinsRegressionTests +{ + private const double MercatorLatitudeSpherificationTolerance = 1e-8d; + private static readonly HashSet FormerHarnessOnlyFixtureLines = + [ + 60, 128, 130, 147, 163, 205, 209, 253, 264, 268, 320, 404, 411, 414, 416, 428, 447, 506, + 659, 666, 1301, 1945, 1961, 2302, 2364, 3321, 3333, 3381, 4032, 4042, 4278, 4280, 5020, + 5030, 5087, 5118, 5227, 5250, 5290, 5806, 5813, 6900, 6902, 6904, 6906, 7110, 7162, 7570, + ]; + + /// + /// Gets the former harness-only builtins rows that are now asserted directly in this regression suite. + /// + /// The replacement direct-assertion test data rows. + public static IEnumerable GetFormerHarnessOnlyCases() + { + foreach (GieCase testCase in GetFormerHarnessOnlyCasesCore()) + { + yield return [testCase]; + } + } + + /// + /// Verifies that inverse Equal Earth cases execute via inverse transform semantics instead of being skipped. + /// + [Fact] + public void TryCreateTransformWithInverseEqearthCaseReturnsExpectedGeographicCoordinate() + { + var testCase = new GieCase + { + LineNumber = 656, + Operation = "+proj=eqearth +R=6378137", + ToleranceValue = 1d, + ToleranceUnit = "cm", + Direction = GieDirection.Inverse, + Accept = [17263256.84d, 0d], + Expect = [180d, 0d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + MathTransform mathTransform = Assert.IsAssignableFrom(transform); + double[] output = mathTransform.Transform(testCase.Accept); + Assert.Equal(testCase.Expect[0], output[0], 7); + Assert.Equal(testCase.Expect[1], output[1], 8); + } + + /// + /// Verifies that inverse projection cases executed through the conversion harness apply inverse direction semantics. + /// + /// Projection operation under test. + /// Projected X coordinate. + /// Projected Y coordinate. + /// Expected longitude in degrees. + /// Expected latitude in degrees. + /// Allowed inverse tolerance. + [Theory] + [InlineData("+proj=aea +ellps=GRS80 +lat_1=0 +lat_2=2", 16468399.3582d, 5275043.9815d, 150d, 50d, 5e-8d)] + [InlineData("+proj=cea +ellps=GRS80", 16697923.6190d, 4865983.5552d, 150d, 50d, 1e-8d)] + public void TryCreateConversionTransformForDirectionWithInverseProjectionCaseReturnsExpectedCoordinate( + string operation, + double x, + double y, + double expectedLongitude, + double expectedLatitude, + double tolerance) + { + bool created = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransformForDirection returned false."); + double[] output = Assert.IsType>(transform)([x, y]); + Assert.InRange(Math.Abs(output[0] - expectedLongitude), 0d, tolerance); + Assert.InRange(Math.Abs(output[1] - expectedLatitude), 0d, tolerance); + } + + /// + /// Verifies that spherical builtins operations using only +a keep that radius instead of falling back to WGS 84. + /// + [Fact] + public void TryCreateTransformWithGnSinuSemiMajorOnlyCaseReturnsExpectedProjectedCoordinate() + { + var testCase = new GieCase + { + LineNumber = 2223, + Operation = "+proj=gn_sinu +a=6400000 +m=1 +n=2", + ToleranceValue = 0.1d, + ToleranceUnit = "mm", + Direction = GieDirection.Forward, + Accept = [2d, 1d], + Expect = [223385.132504696d, 111698.236447187d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + MathTransform mathTransform = Assert.IsAssignableFrom(transform); + double[] output = mathTransform.Transform(testCase.Accept); + Assert.Equal(testCase.Expect[0], output[0], 9); + Assert.Equal(testCase.Expect[1], output[1], 9); + } + + /// + /// Verifies that to_meter ratio expressions are honored by the builtins harness. + /// + [Fact] + public void TryGetDoubleParsesToMeterRatioExpression() + { + MethodInfo parseOperationArgumentsMethod = typeof(GieBuiltinsTheoryTests).GetMethod("TryParseOperationArguments", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryParseOperationArguments."); + object?[] parseArgs = ["proj=utm ellps=GRS80 zone=32 to_meter=2.0/0.2", null]; + bool parsed = Assert.IsType(parseOperationArgumentsMethod.Invoke(null, parseArgs)); + Assert.True(parsed); + + Dictionary operationArgs = Assert.IsType>(parseArgs[1]); + MethodInfo tryGetDoubleMethod = typeof(GieBuiltinsTheoryTests).GetMethod("TryGetDouble", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryGetDouble."); + object?[] valueArgs = [operationArgs, "to_meter", null]; + bool parsedToMeter = Assert.IsType(tryGetDoubleMethod.Invoke(null, valueArgs)); + + Assert.True(parsedToMeter); + double toMeterValue = Assert.IsType(valueArgs[2]); + Assert.Equal(10d, toMeterValue, 12); + } + + /// + /// Verifies that synthetic UTM false offsets are expressed in the configured output units in the GIE builtins harness. + /// + /// UTM operation under test. + [Theory] + [InlineData("proj=utm ellps=GRS80 zone=32 to_meter=10")] + [InlineData("proj=utm ellps=GRS80 zone=32 to_meter=2.0/0.2")] + public void TryCreateTransformWithUtmCustomOutputUnitsReturnsExpectedProjectedCoordinate(string operation) + { + var testCase = new GieCase + { + LineNumber = 518, + Operation = operation, + ToleranceValue = 0.1d, + ToleranceUnit = "mm", + Direction = GieDirection.Forward, + Accept = [12d, 55d], + Expect = [69187.5632d, 609890.7825d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + MathTransform mathTransform = Assert.IsAssignableFrom(transform); + double[] output = mathTransform.Transform(testCase.Accept); + Assert.Equal(testCase.Expect[0], output[0], 4); + Assert.Equal(testCase.Expect[1], output[1], 4); + } + + /// + /// Verifies that the builtins harness applies explicit ellipsoid shape overrides after resolving a named ellipsoid. + /// + /// UTM operation under test. + /// Equivalent UTM operation with the ellipsoid shape specified directly. + /// UTM operation that keeps the original named ellipsoid without an explicit shape override. + [Theory] + [InlineData("proj=utm ellps=GRS80 zone=32 b=6000000", "proj=utm a=6378137 zone=32 b=6000000", "proj=utm ellps=GRS80 zone=32")] + [InlineData("proj=utm ellps=GRS80 zone=32 rf=300", "proj=utm a=6378137 zone=32 rf=300", "proj=utm ellps=GRS80 zone=32")] + [InlineData("proj=utm ellps=GRS80 zone=32 f=0.00333333333333", "proj=utm a=6378137 zone=32 f=0.00333333333333", "proj=utm ellps=GRS80 zone=32")] + public void TryCreateTransformWithUtmEllipsoidOverridesReturnsExpectedProjectedCoordinate( + string operation, + string equivalentOperation, + string baselineOperation) + { + double[] output = RequireBuiltinsProjectedOutput(operation); + double[] equivalentOutput = RequireBuiltinsProjectedOutput(equivalentOperation); + double[] baselineOutput = RequireBuiltinsProjectedOutput(baselineOperation); + + Assert.Equal(equivalentOutput[0], output[0], 12); + Assert.Equal(equivalentOutput[1], output[1], 12); + Assert.NotEqual(baselineOutput[0], output[0], 9); + Assert.NotEqual(baselineOutput[1], output[1], 9); + } + + /// + /// Verifies that the runtime UTM conversion path uses the exact transverse Mercator kernel for explicit shape overrides, matching the GIE reference values. + /// + [Theory] + [InlineData("proj=utm ellps=GRS80 zone=32 b=6000000", 699293.0880d, 5674591.5295d)] + [InlineData("proj=utm a=6400000 zone=32 b=6000000", 700416.5900d, 5669475.8884d)] + public void TryCreateConversionTransformWithUtmShapeOverridesMatchesGieReference( + string operation, + double expectedX, + double expectedY) + { + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, 12d, 55d); + + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, 0.0005d); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, 0.0005d); + } + + /// + /// Verifies that legacy non-pipeline geoidgrids operations are normalized into executable runtime pipelines. + /// + /// Legacy geoidgrids operation under test. + [Theory] + [InlineData("proj=latlong geoidgrids=egm96_15.gtx ellps=GRS80")] + [InlineData("proj=merc geoidgrids=egm96_15.gtx axis=sue ellps=GRS80")] + [InlineData("+proj=latlong +ellps=WGS84 +geoidgrids=tests/test_nodata.gtx")] + public void TryIsRuntimeOperationSupportedRecognizesLegacyGeoidGridOperations(string operation) + { + Assert.True(TryIsRuntimeOperationSupported(operation)); + } + + /// + /// Verifies that legacy geographic geoidgrids operations apply the expected forward vertical shift. + /// + [Fact] + public void TryCreateConversionTransformWithLegacyLatlongGeoidGridsReturnsExpectedVerticalShift() + { + double[] output = RequireBuiltinsRuntimeProjectedOutput("proj=latlong geoidgrids=egm96_15.gtx ellps=GRS80", 12.5d, 55.5d, 0d); + + Assert.InRange(output[0], 12.5d - 1e-12d, 12.5d + 1e-12d); + Assert.InRange(output[1], 55.5d - 1e-12d, 55.5d + 1e-12d); + Assert.InRange(output[2], -36.3941d - 1e-4d, -36.3941d + 1e-4d); + } + + /// + /// Verifies that legacy geographic geoidgrids operations also preserve the inverse vertical path. + /// + [Fact] + public void TryCreateConversionTransformForDirectionWithLegacyLatlongGeoidGridsInverseReturnsExpectedVerticalShift() + { + bool created = TryCreateConversionTransformForDirection( + "proj=latlong geoidgrids=egm96_15.gtx ellps=GRS80", + GieDirection.Inverse, + out Func? transform, + out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([12.5d, 55.5d, -36.3941d]); + Assert.InRange(output[0], 12.5d - 1e-12d, 12.5d + 1e-12d); + Assert.InRange(output[1], 55.5d - 1e-12d, 55.5d + 1e-12d); + Assert.InRange(output[2], -1e-4d, 1e-4d); + } + + /// + /// Verifies that projected legacy geoidgrids operations keep the vertical component alongside the projected ordinates. + /// + [Fact] + public void TryCreateConversionTransformWithLegacyMercGeoidGridsReturnsExpectedProjectedAndVerticalOutput() + { + double[] output = RequireBuiltinsRuntimeProjectedOutput("proj=merc geoidgrids=egm96_15.gtx ellps=GRS80", 12.5d, 55.5d, 0d); + + Assert.InRange(output[0], 1391493.63492d - 1e-4d, 1391493.63492d + 1e-4d); + Assert.InRange(output[1], 7424275.19462d - 1e-4d, 7424275.19462d + 1e-4d); + Assert.InRange(output[2], -36.3941d - 1e-4d, -36.3941d + 1e-4d); + } + + /// + /// Verifies that legacy geoidgrids operations still respect nodata cells when rewritten for the runtime path. + /// + [Fact] + public void TryCreateConversionTransformWithLegacyLatlongGeoidGridsNodataReturnsExpectedVerticalShift() + { + double[] output = RequireBuiltinsRuntimeProjectedOutput("+proj=latlong +ellps=WGS84 +geoidgrids=tests/test_nodata.gtx", 4.05d, 52.1d, 0d); + + Assert.InRange(output[0], 4.05d - 1e-12d, 4.05d + 1e-12d); + Assert.InRange(output[1], 52.1d - 1e-12d, 52.1d + 1e-12d); + Assert.InRange(output[2], -10d - 1e-6d, -10d + 1e-6d); + } + + /// + /// Verifies that the projected Krovak gridshift runtime path binds the Krovak-specific parameters that PROJ defaults for legacy pipelines. + /// + [Fact] + public void TryCreateConversionTransformWithProjectedKrovakGridShiftPipelineReturnsExpectedCoordinate() + { + const string operation = "+proj=pipeline +step +proj=krovak +lat_0=49.5 +lon_0=24.8333333333333 +alpha=30.2881397527778 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +step +proj=gridshift +grids=tests/test_gridshift_projected.tif +step +inv +proj=mod_krovak +lat_0=49.5 +lon_0=24.8333333333333 +alpha=30.2881397222222 +k=0.9999 +x_0=5000000 +y_0=5000000 +ellps=bessel"; + + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, 16.610452439d, 49.202425040d, 0d); + + Assert.InRange(output[0], 16.610455233081716d - 1e-8d, 16.610455233081716d + 1e-8d); + Assert.InRange(output[1], 49.202425036121703d - 5e-8d, 49.202425036121703d + 5e-8d); + Assert.InRange(output.Length, 2, 3); + if (output.Length == 3) + { + Assert.InRange(output[2], -1e-9d, 1e-9d); + } + } + + /// + /// Verifies that the runtime +proj=utm +approx path still uses the approximate Snyder-based kernel instead of the exact ETMERC path. + /// + [Fact] + public void TryCreateConversionTransformWithUtmApproxMatchesEquivalentTmercStep() + { + double[] utmApproxOutput = RequireBuiltinsRuntimeProjectedOutput("proj=utm zone=32 ellps=GRS80 approx", 12d, 55d); + double[] tmercOutput = RequireBuiltinsRuntimeProjectedOutput("proj=tmerc ellps=GRS80 lat_0=0 lon_0=9 k_0=0.9996 x_0=500000 y_0=0 approx", 12d, 55d); + + Assert.Equal(tmercOutput[0], utmApproxOutput[0], 12); + Assert.Equal(tmercOutput[1], utmApproxOutput[1], 12); + } + + /// + /// Verifies that Mercator builtins cases distinguish PROJ's case-sensitive spherification flags. + /// + [Fact] + public void TryCreateTransformWithMercatorRadiusSpherificationFlagsReturnsDistinctProjectedCoordinates() + { + double[] areaEquivalentOutput = RequireBuiltinsProjectedOutput("proj=merc ellps=GRS80 R_A"); + double[] arithmeticMeanOutput = RequireBuiltinsProjectedOutput("proj=merc ellps=GRS80 R_a"); + double[] geometricMeanOutput = RequireBuiltinsProjectedOutput("proj=merc ellps=GRS80 R_g"); + double[] harmonicMeanOutput = RequireBuiltinsProjectedOutput("proj=merc ellps=GRS80 R_h"); + + Assert.Equal(1334340.6237297705d, areaEquivalentOutput[0], 9); + Assert.InRange(Math.Abs(areaEquivalentOutput[1] - 7353636.6296552019d), 0d, 1e-8d); + Assert.Equal(1333594.4904527504d, arithmeticMeanOutput[0], 9); + Assert.InRange(Math.Abs(arithmeticMeanOutput[1] - 7349524.6413825499d), 0d, 1e-8d); + Assert.Equal(1333592.6102291327d, geometricMeanOutput[0], 9); + Assert.InRange(Math.Abs(geometricMeanOutput[1] - 7349514.2793497816d), 0d, 1e-8d); + Assert.Equal(1333590.7300081658d, harmonicMeanOutput[0], 9); + Assert.InRange(Math.Abs(harmonicMeanOutput[1] - 7349503.9173316229d), 0d, 1e-8d); + + Assert.NotEqual(areaEquivalentOutput[0], arithmeticMeanOutput[0], 9); + Assert.NotEqual(geometricMeanOutput[0], harmonicMeanOutput[0], 9); + } + + /// + /// Verifies that Mercator builtins cases honor latitude-based PROJ spherification flags. + /// + [Theory] + [InlineData("proj=merc ellps=GRS80 R_lat_a=60", 1338073.7436268919d, 7374210.0924803326d)] + [InlineData("proj=merc ellps=GRS80 R_lat_g=60", 1338073.2696101593d, 7374207.4801437631d)] + [InlineData("+proj=merc +R_C +ellps=WGS84 +lat_0=45", 1331355.0914081715d, 7337183.169834906d)] + public void TryCreateTransformWithMercatorLatitudeSpherificationReturnsExpectedProjectedCoordinate( + string operation, + double expectedX, + double expectedY) + { + double[] output = RequireBuiltinsProjectedOutput(operation); + + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, MercatorLatitudeSpherificationTolerance); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, MercatorLatitudeSpherificationTolerance); + } + + /// + /// Verifies that invalid Mercator spherification and eccentricity overrides are rejected. + /// + [Theory] + [InlineData("+proj=merc +R_a +a=2 +f=2", "+f")] + [InlineData("proj=merc a=1E77 R_lat_a=90 b=1", "R_lat_a")] + [InlineData("proj=utm zone=32 ellps=GRS80 e=-0.5", "+e")] + [InlineData("proj=utm zone=32 ellps=GRS80 e=1", "+e")] + public void TryCreateConversionTransformWithInvalidEllipsoidOverridesReturnsValidationReason(string operation, string expectedToken) + { + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.False(created); + Assert.Null(transform); + Assert.Contains(expectedToken, skipReason ?? string.Empty, StringComparison.Ordinal); + } + + /// + /// Verifies that flattening can be set to zero explicitly for Mercator builtins cases. + /// + [Fact] + public void TryCreateTransformWithMercatorZeroFlatteningReturnsExpectedProjectedCoordinate() + { + var testCase = new GieCase + { + LineNumber = 181, + Operation = "proj=merc +a=1.0 +f=0.0", + ToleranceValue = 10d, + ToleranceUnit = "nm", + Direction = GieDirection.Forward, + Accept = [12d, 56d], + Expect = [0.20944d, 1.18505d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + double[] output = Assert.IsAssignableFrom(transform).Transform(testCase.Accept); + Assert.Equal(testCase.Expect[0], output[0], 5); + Assert.Equal(testCase.Expect[1], output[1], 5); + } + + /// + /// Verifies that DMS projection parameters are parsed for builtins cases. + /// + [Fact] + public void TryGetDoubleParsesDmsProjectionParameters() + { + MethodInfo parseOperationArgumentsMethod = typeof(GieBuiltinsTheoryTests).GetMethod("TryParseOperationArguments", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryParseOperationArguments."); + object?[] parseArgs = ["+proj=lcc +ellps=clrk66 +lat_1=44d11'N +lat_2=45d42'N +x_0=609601.2192 +lon_0=84d20'W +lat_0=43d19'N +k_0=1.0000382 +units=us-ft", null]; + bool parsed = Assert.IsType(parseOperationArgumentsMethod.Invoke(null, parseArgs)); + Assert.True(parsed); + + Dictionary operationArgs = Assert.IsType>(parseArgs[1]); + MethodInfo tryGetDoubleMethod = typeof(GieBuiltinsTheoryTests).GetMethod("TryGetDouble", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryGetDouble."); + + object?[] lat1Args = [operationArgs, "lat_1", null]; + bool parsedLat1 = Assert.IsType(tryGetDoubleMethod.Invoke(null, lat1Args)); + Assert.True(parsedLat1); + double lat1 = Assert.IsType(lat1Args[2]); + Assert.Equal(44.18333333333333d, lat1, 12); + + object?[] lon0Args = [operationArgs, "lon_0", null]; + bool parsedLon0 = Assert.IsType(tryGetDoubleMethod.Invoke(null, lon0Args)); + Assert.True(parsedLon0); + double lon0 = Assert.IsType(lon0Args[2]); + Assert.Equal(-84.33333333333333d, lon0, 12); + } + + /// + /// Verifies that the builtins operation tokenizer preserves GIE-style assignments where a negative value is attached to the equals sign token. + /// + [Fact] + public void TryParseOperationArgumentsPreservesNegativeAssignmentValuesAfterDetachedEquals() + { + MethodInfo parseOperationArgumentsMethod = typeof(GieBuiltinsTheoryTests).GetMethod("TryParseOperationArguments", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryParseOperationArguments."); + object?[] parseArgs = ["proj=helmert convention=position_vector x = 0.01270 y = 0.00650 z =-0.0209 dz =-0.0006", null]; + bool parsed = Assert.IsType(parseOperationArgumentsMethod.Invoke(null, parseArgs)); + Assert.True(parsed); + + Dictionary operationArgs = Assert.IsType>(parseArgs[1]); + Assert.Equal("-0.0209", operationArgs["z"]); + Assert.Equal("-0.0006", operationArgs["dz"]); + } + + /// + /// Verifies that builtins LCC cases treat explicit false offsets as meters even when the projection outputs US survey feet. + /// + [Fact] + public void TryCreateTransformWithLccUsFootOffsetsInMetersReturnsExpectedProjectedCoordinate() + { + var testCase = new GieCase + { + LineNumber = 320, + Operation = "+proj=lcc +ellps=clrk66 +lat_1=44d11'N +lat_2=45d42'N +x_0=609601.2192 +lon_0=84d20'W +lat_0=43d19'N +k_0=1.0000382 +units=us-ft", + ToleranceValue = 5d, + ToleranceUnit = "mm", + Direction = GieDirection.Forward, + Accept = [-83.16666666666667d, 43.75d], + Expect = [2308335.75d, 160210.48d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + double[] output = Assert.IsAssignableFrom(transform).Transform(testCase.Accept); + Assert.Equal(testCase.Expect[0], output[0], 2); + Assert.Equal(testCase.Expect[1], output[1], 2); + } + + /// + /// Verifies that builtins Cassini cases treat explicit false offsets as meters when +to_meter defines a non-metric output unit. + /// + [Fact] + public void TryCreateTransformWithCassToMeterOffsetsInMetersReturnsExpectedProjectedCoordinate() + { + var testCase = new GieCase + { + LineNumber = 912, + Operation = "+proj=cass +lat_0=10.4416666666667 +lon_0=-61.3333333333333 +x_0=86501.46392052 +y_0=65379.0134283 +a=6378293.64520876 +b=6356617.98767984 +to_meter=0.201166195164", + ToleranceValue = 0.1d, + ToleranceUnit = "mm", + Direction = GieDirection.Forward, + Accept = [-62d, 10d], + Expect = [66644.94040882d, 82536.21873655d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + double[] output = Assert.IsAssignableFrom(transform).Transform(testCase.Accept); + Assert.Equal(testCase.Expect[0], output[0], 6); + Assert.Equal(testCase.Expect[1], output[1], 6); + } + + /// + /// Verifies that the GIE conversion path normalizes explicit Cassini false offsets from meters to the declared output unit. + /// + [Fact] + public void TryCreateConversionTransformWithCassToMeterOffsetsInMetersReturnsExpectedProjectedCoordinate() + { + const string operation = "+proj=cass +lat_0=10.4416666666667 +lon_0=-61.3333333333333 +x_0=86501.46392052 +y_0=65379.0134283 +a=6378293.64520876 +b=6356617.98767984 +to_meter=0.201166195164"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + + double[] output = Assert.IsType>(transform)([-62d, 10d]); + Assert.Equal(66644.94040882d, output[0], 6); + Assert.Equal(82536.21873655d, output[1], 6); + } + + /// + /// Verifies that Hyperbolic Cassini direct-transform cases bind the runtime flag and return the expected projected coordinate. + /// + [Fact] + public void TryCreateTransformWithHyperbolicCassCaseReturnsExpectedProjectedCoordinate() + { + var testCase = new GieCase + { + LineNumber = 924, + Operation = "+proj=cass +hyperbolic +a=6378306.376305601 +rf=293.466307 +lat_0=-16.25 +lon_0=179.33333333333333 +to_meter=20.1168 +x_0=251727.9155424 +y_0=334519.953768", + ToleranceValue = 0.1d, + ToleranceUnit = "mm", + Direction = GieDirection.Forward, + Accept = [179.99433652777776d, -16.841456527777776d], + Expect = [16015.28901692d, 13369.66005367d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + double[] output = Assert.IsAssignableFrom(transform).Transform(testCase.Accept); + Assert.Equal(testCase.Expect[0], output[0], 6); + Assert.Equal(testCase.Expect[1], output[1], 6); + } + + /// + /// Verifies that Hyperbolic Cassini conversion cases execute through the pipeline runtime with the expected projected coordinate. + /// + [Fact] + public void TryCreateConversionTransformWithHyperbolicCassCaseReturnsExpectedProjectedCoordinate() + { + const string operation = "+proj=cass +hyperbolic +a=6378306.376305601 +rf=293.466307 +lat_0=-16.25 +lon_0=179.33333333333333 +to_meter=20.1168 +x_0=251727.9155424 +y_0=334519.953768"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + + double[] output = Assert.IsType>(transform)([179.99433652777776d, -16.841456527777776d]); + Assert.Equal(16015.28901692d, output[0], 6); + Assert.Equal(13369.66005367d, output[1], 6); + } + + /// + /// Verifies that Hyperbolic Cassini inverse-transform cases recover the expected geographic coordinate. + /// + [Fact] + public void TryCreateTransformWithInverseHyperbolicCassCaseReturnsExpectedGeographicCoordinate() + { + var testCase = new GieCase + { + LineNumber = 924, + Operation = "+proj=cass +hyperbolic +a=6378306.376305601 +rf=293.466307 +lat_0=-16.25 +lon_0=179.33333333333333 +to_meter=20.1168 +x_0=251727.9155424 +y_0=334519.953768", + ToleranceValue = 0.1d, + ToleranceUnit = "mm", + Direction = GieDirection.Inverse, + Accept = [16015.28901692d, 13369.66005367d], + Expect = [179.99433652777776d, -16.841456527777776d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + + double[] output = Assert.IsAssignableFrom(transform).Transform(testCase.Accept); + Assert.InRange(Math.Abs(output[0] - testCase.Expect[0]), 0d, 1e-9d); + Assert.InRange(Math.Abs(output[1] - testCase.Expect[1]), 0d, 1e-9d); + } + + /// + /// Verifies that Hyperbolic Cassini conversion cases roundtrip through the inverse pipeline runtime. + /// + [Fact] + public void TryCreateConversionTransformWithHyperbolicCassCaseRoundtripsProjectedCoordinate() + { + const string operation = "+proj=cass +hyperbolic +a=6378306.376305601 +rf=293.466307 +lat_0=-16.25 +lon_0=179.33333333333333 +to_meter=20.1168 +x_0=251727.9155424 +y_0=334519.953768"; + double[] geographic = [179.99433652777776d, -16.841456527777776d]; + + bool createdForward = TryCreateConversionTransform(operation, out Func? forwardTransform, out string? forwardSkipReason); + Assert.True(createdForward, forwardSkipReason ?? "TryCreateConversionTransform returned false."); + + bool createdInverse = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? inverseTransform, out string? inverseSkipReason); + Assert.True(createdInverse, inverseSkipReason ?? "TryCreateConversionTransformForDirection returned false."); + + double[] projected = Assert.IsType>(forwardTransform)(geographic); + double[] roundtripped = Assert.IsType>(inverseTransform)(projected); + + Assert.InRange(Math.Abs(roundtripped[0] - geographic[0]), 0d, 1e-9d); + Assert.InRange(Math.Abs(roundtripped[1] - geographic[1]), 0d, 1e-9d); + } + + /// + /// Verifies that geographic datum-shift operations are rejected for projected coordinate tuples. + /// + [Fact] + public void TryCreateTransformWithProjectedCoordinatesForLatlongDatumShiftReturnsFalse() + { + var testCase = new GieCase + { + LineNumber = 188, + Operation = "proj=latlong towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 ellps=bessel", + ToleranceValue = 3d, + ToleranceUnit = "m", + Direction = GieDirection.Inverse, + Accept = [2598417.333192d, 5930677.980308d], + Expect = [399340.601863d, 5928794.177992d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + + Assert.False(created); + Assert.Null(transform); + Assert.Contains("geographic", skipReason ?? string.Empty, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Verifies that geographic datum-shift operations still work for valid geographic coordinates. + /// + [Fact] + public void TryCreateTransformWithGeographicCoordinatesForLatlongDatumShiftReturnsExpectedCoordinate() + { + var testCase = new GieCase + { + LineNumber = 98, + Operation = "proj=latlong towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 ellps=bessel", + ToleranceValue = 3d, + ToleranceUnit = "m", + Direction = GieDirection.Inverse, + Accept = [7.483333333333d, 53.5d], + Expect = [7.482506019176d, 53.498461143331d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + MathTransform mathTransform = Assert.IsAssignableFrom(transform); + double[] output = mathTransform.Transform(testCase.Accept); + Assert.InRange(output[0], testCase.Expect[0] - 2e-5d, testCase.Expect[0] + 2e-5d); + Assert.InRange(output[1], testCase.Expect[1] - 2e-5d, testCase.Expect[1] + 2e-5d); + } + + /// + /// Verifies that the builtins harness recognizes legacy +init= pipeline steps after runtime normalization. + /// + /// Pipeline operation containing legacy init references. + [Theory] + [InlineData("+proj=pipeline +step +init=epsg:26915 +inv +step +init=epsg:3857")] + [InlineData("+proj=pipeline +step +init=epsg:25832 +inv +step +init=epsg:25833 +step +init=epsg:25833 +inv +step +init=epsg:25832")] + [InlineData("+proj=pipeline +step +proj=latlong +datum=NAD27 +inv +step +units=us-ft +init=nad27:3901")] + public void TryIsRuntimeOperationSupportedRecognizesLegacyInitPipelines(string operation) + { + Assert.True(TryIsRuntimeOperationSupported(operation)); + } + + /// + /// Verifies that legacy EPSG init pipeline steps can be executed through the conversion harness. + /// + [Fact] + public void TryCreateConversionTransformWithLegacyEpsgInitPipelineReturnsExpectedCoordinate() + { + const string operation = "+proj=pipeline +step +init=epsg:26915 +inv +step +init=epsg:3857"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([487147.594520173d, 4934316.46263998d]); + Assert.InRange(output[0], -10370728.80d - 0.2d, -10370728.80d + 0.2d); + Assert.InRange(output[1], 5552839.74d - 0.2d, 5552839.74d + 0.2d); + } + + /// + /// Verifies that legacy NAD27 init pipeline steps can be executed through the conversion harness and still land in the expected State Plane output range. + /// + [Fact] + public void TryCreateConversionTransformWithLegacyNad27InitPipelineReturnsProjectedCoordinateInExpectedRange() + { + const string operation = "+proj=pipeline +step +proj=latlong +datum=NAD27 +inv +step +units=us-ft +init=nad27:3901"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([-80.54166666666667d, 34.54166666666667d, 0d]); + Assert.True(output.Length >= 2); + Assert.True(double.IsFinite(output[0])); + Assert.True(double.IsFinite(output[1])); + Assert.InRange(output[0], 2137500d, 2138500d); + Assert.InRange(output[1], 561000d, 561500d); + } + + /// + /// Verifies that runtime support now includes the former Bessel-based pipeline cases and the xyzgridshift test-grid case. + /// + /// Pipeline operation to probe. + [Theory] + [InlineData("+proj=pipeline +step +proj=cart +ellps=WGS84 +step +proj=helmert +x=674.374 +y=15.056 +z=405.346 +inv +step +proj=cart +ellps=bessel +inv +step +proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=2600000 +y_0=1200000 +ellps=bessel +units=m")] + [InlineData("+proj=pipeline +step +proj=krovak +lat_0=49.5 +lon_0=24.8333333333333 +alpha=30.2881397527778 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +step +proj=gridshift +grids=tests/test_gridshift_projected.tif +step +inv +proj=mod_krovak +lat_0=49.5 +lon_0=24.8333333333333 +alpha=30.2881397222222 +k=0.9999 +x_0=5000000 +y_0=5000000 +ellps=bessel")] + [InlineData("+proj=pipeline +step +inv +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +ellps=clrk80ign +pm=paris +step +proj=push +v_3 +step +proj=cart +ellps=clrk80ign +step +proj=xyzgridshift +grids=tests/subset_of_gr3df97a.tif +grid_ref=output_crs +ellps=GRS80 +step +proj=cart +ellps=GRS80 +inv +step +proj=pop +v_3 +step +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80")] + public void TryIsRuntimeOperationSupportedRecognizesRemainingRuntimeFeaturePipelines(string operation) + { + Assert.True(TryIsRuntimeOperationSupported(operation)); + } + + /// + /// Verifies that the GIE conversion path resolves known tests/... grid tokens to local fixtures. + /// + [Fact] + public void TryCreateConversionTransformWithKnownTestGridTokenReturnsExpectedCoordinate() + { + const string operation = "+proj=pipeline +step +inv +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +ellps=clrk80ign +pm=paris +step +proj=push +v_3 +step +proj=cart +ellps=clrk80ign +step +proj=xyzgridshift +grids=tests/subset_of_gr3df97a.tif +grid_ref=output_crs +ellps=GRS80 +step +proj=cart +ellps=GRS80 +inv +step +proj=pop +v_3 +step +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([814149.529d, 1887019.768d, 0d]); + Assert.InRange(output[0], 860690.804d - 1e-3d, 860690.804d + 1e-3d); + Assert.InRange(output[1], 6319036.849d - 1e-3d, 6319036.849d + 1e-3d); + if (output.Length > 2) + { + Assert.InRange(output[2], -1e-6d, 1e-6d); + } + } + + /// + /// Verifies that the builtins harness recognizes the conversion operations needed by the former no-applicable fixtures. + /// + /// Operation to probe. + [Theory] + [InlineData("+proj=defmodel +model=tests/simple_model_degree_horizontal.json")] + [InlineData("+proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +t_epoch=2016.0 +ellps=GRS80")] + [InlineData("proj=helmert convention=coordinate_frame x=0.06155 y=-0.01087 z=-0.04019 rx=-0.0394924 ry=-0.0327221 rz=-0.0328979 s=-0.009994")] + [InlineData("proj = pipeline ellps=GRS80; step proj = cart; step proj = helmert convention=coordinate_frame x = 0.06155 y = -0.01087 z = -0.04019 rx = -0.0394924 ry = -0.0327221 rz = -0.0328979 s = -0.009994; step proj = cart inv;")] + public void TryIsRuntimeOperationSupportedRecognizesNoApplicableQuickWins(string operation) + { + Assert.True(TryIsRuntimeOperationSupported(operation)); + } + + /// + /// Verifies that standalone Helmert conversion cases execute through the builtins conversion path. + /// + [Fact] + public void TryCreateConversionTransformWithStandaloneHelmertReturnsExpectedCoordinate() + { + const string operation = "proj=helmert convention=coordinate_frame x=0.06155 y=-0.01087 z=-0.04019 rx=-0.0394924 ry=-0.0327221 rz=-0.0328979 s=-0.009994"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([-4052051.7643d, 4212836.2017d, -2545106.0245d]); + Assert.InRange(output[0], -4052052.7379d - 1e-4d, -4052052.7379d + 1e-4d); + Assert.InRange(output[1], 4212835.9897d - 1e-4d, 4212835.9897d + 1e-4d); + Assert.InRange(output[2], -2545104.5898d - 1e-4d, -2545104.5898d + 1e-4d); + } + + /// + /// Verifies that standalone kinematic Helmert conversion cases execute through the builtins conversion path even when GIE uses detached equals tokens. + /// + [Fact] + public void TryCreateConversionTransformWithStandaloneKinematicHelmertReturnsExpectedCoordinate() + { + const string operation = "proj=helmert convention=position_vector x = 0.01270 dx =-0.0029 rx =-0.00039 drx =-0.00011 y = 0.00650 dy =-0.0002 ry = 0.00080 dry =-0.00019 z =-0.0209 dz =-0.0006 rz =-0.00114 drz = 0.00007 s = 0.00195 ds = 0.00001 t_epoch=1988.0"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([3370658.378d, 711877.314d, 5349787.086d, 2018d]); + Assert.InRange(output[0], 3370658.18087d - 1e-4d, 3370658.18087d + 1e-4d); + Assert.InRange(output[1], 711877.42750d - 1e-4d, 711877.42750d + 1e-4d); + Assert.InRange(output[2], 5349787.12648d - 1e-4d, 5349787.12648d + 1e-4d); + Assert.InRange(output[3], 2018d - 1e-12d, 2018d + 1e-12d); + } + + /// + /// Verifies that standalone geographic identity conversion cases honor the legacy +geoc flag using PROJ's longlat semantics. + /// + [Fact] + public void TryCreateConversionTransformWithLonglatGeocAndInverseReturnsGeocentricLatitude() + { + const string operation = "proj=pipeline step proj=longlat ellps=GRS80 geoc inv"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([12d, 55d, 0d, 0d]); + Assert.Equal(12d, output[0], 12); + Assert.Equal(54.818973308324573d, output[1], 12); + Assert.Equal(0d, output[2], 12); + Assert.Equal(0d, output[3], 12); + } + + /// + /// Verifies that standalone set conversion cases override the 4th ordinate. + /// + [Fact] + public void TryCreateConversionTransformWithSetV4ReturnsExpectedFourthOrdinate() + { + const string operation = "+proj=set +v_1=10 +v_2=20 +v_3=30 +v_4=40"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([1d, 2d, 3d, 4d]); + Assert.Equal([10d, 20d, 30d, 40d], output); + } + + /// + /// Verifies that standalone geographic identity conversion cases honor +vto_meter. + /// + [Fact] + public void TryCreateConversionTransformWithLonglatVtoMeterScalesThirdOrdinate() + { + const string operation = "+proj=longlat +a=1 +b=1 +vto_meter=1000"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([0d, 0d, 1000d]); + Assert.Equal([0d, 0d, 1d], output); + } + + /// + /// Verifies that projected conversion cases honor +vunits for the third ordinate. + /// + [Fact] + public void TryCreateConversionTransformWithMercatorVunitsScalesThirdOrdinate() + { + const string operation = "+proj=merc +a=1 +b=1 +vunits=km"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([0d, 0d, 1000d]); + Assert.Equal(0d, output[0], 12); + Assert.InRange(output[1], -1e-12d, 1e-12d); + Assert.Equal(1d, output[2], 12); + } + + /// + /// Verifies that standalone geographic identity conversion cases honor +lon_wrap. + /// + [Fact] + public void TryCreateConversionTransformWithLonglatLonWrapNormalizesLongitude() + { + const string operation = "+proj=longlat +ellps=WGS84 +lon_wrap=180"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([-1d, 10d, 0d]); + Assert.Equal([359d, 10d, 0d], output); + } + + /// + /// Verifies that loose GIE-style assignment syntax with semicolon separators is normalized for runtime pipeline execution. + /// + [Fact] + public void TryCreateConversionTransformWithLooseAssignmentPipelineReturnsExpectedCoordinate() + { + const string operation = "proj = pipeline ellps=GRS80; step proj = cart; step proj = helmert convention=coordinate_frame x = 0.06155 y = -0.01087 z = -0.04019 rx = -0.0394924 ry = -0.0327221 rz = -0.0328979 s = -0.009994; step proj = cart inv;"; + + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([133.88551329d, -23.67012389d, 603.3466d, 0d]); + Assert.InRange(output[0], 133.8855216d - 1e-6d, 133.8855216d + 1e-6d); + Assert.InRange(output[1], -23.67011014d - 1e-6d, -23.67011014d + 1e-6d); + Assert.InRange(output[2], 603.2489d - 1e-3d, 603.2489d + 1e-3d); + Assert.InRange(output[3], -1e-9d, 1e-9d); + } + + /// + /// Verifies that mapped NKG URNs execute through the builtins conversion path, including the Norway-specific xyzgridshift case. + /// + [Theory] + [InlineData( + "urn:ogc:def:coordinateOperation:NKG::ITRF2000_TO_DK", + 3541657.3778d, + 948984.2343d, + 5201383.5231d, + 2020.5d, + 3541657.9362d, + 948983.7825d, + 5201383.2292d, + 2020.5d, + 1e-3d)] + [InlineData( + "urn:ogc:def:coordinateOperation:NKG::ITRF2014_TO_NO", + 3275753.4135d, + 321111.2481d, + 5445042.2134d, + 2020.0d, + 3275753.9094d, + 321110.8626d, + 5445041.8818d, + 2020.0d, + 1e-4d)] + public void TryCreateConversionTransformWithCoordinateOperationUrnReturnsExpectedCoordinate( + string operation, + double x, + double y, + double z, + double epoch, + double expectedX, + double expectedY, + double expectedZ, + double expectedEpoch, + double tolerance) + { + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + double[] output = Assert.IsType>(transform)([x, y, z, epoch]); + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, tolerance); + Assert.InRange(Math.Abs(output[2] - expectedZ), 0d, tolerance); + Assert.InRange(Math.Abs(output[3] - expectedEpoch), 0d, 1e-9d); + } + + /// + /// Verifies that the NKG fixture now emits its mapped rows instead of falling back to a single placeholder case. + /// + [Fact] + public void GetCasesFromFixtureWithNkgFixtureReturnsMappedTheoryRows() + { + List rows = GetCasesFromFixtureRows("nkg.gie"); + + Assert.True(rows.Count > 20, $"Expected mapped NKG rows, but found only {rows.Count}."); + } + + /// + /// Verifies that the former no-applicable fixtures now emit concrete theory rows instead of null placeholders. + /// + /// Fixture name to inspect. + [Theory] + [InlineData("defmodel.gie")] + [InlineData("deformation.gie")] + [InlineData("ellipsoid.gie")] + [InlineData("GDA.gie")] + [InlineData("nkg.gie")] + public void GetCasesFromFixtureForFormerNoApplicableFixturesReturnsConcreteRow(string fixtureName) + { + List rows = GetCasesFromFixtureRows(fixtureName); + + Assert.NotEmpty(rows); + + PropertyInfo dataProperty = rows[0].GetType().GetProperty("Data", BindingFlags.Instance | BindingFlags.Public) + ?? throw new InvalidOperationException("Could not locate TheoryDataRow.Data."); + var firstCase = dataProperty.GetValue(rows[0]) as GieCase; + + Assert.NotNull(firstCase); + Assert.NotNull(firstCase!.Operation); + Assert.NotEmpty(firstCase.Operation); + } + + /// + /// Verifies that the dedicated failure provider emits expected-failure rows from the builtins fixtures. + /// + [Fact] + public void GetBuiltinsFailureCasesReturnsExpectedFailureRows() + { + List rows = GetFailureCaseRows(); + + Assert.NotEmpty(rows); + + PropertyInfo dataProperty = rows[0].GetType().GetProperty("Data", BindingFlags.Instance | BindingFlags.Public) + ?? throw new InvalidOperationException("Could not locate TheoryDataRow.Data."); + var firstCase = dataProperty.GetValue(rows[0]) as GieCase; + + Assert.NotNull(firstCase); + Assert.True(firstCase!.ExpectsFailure); + Assert.NotEmpty(firstCase.Operation); + } + + /// + /// Verifies that the runtime s2 path binds string +UVtoST= modes instead of silently falling back to the quadratic default. + /// + /// Projection operation string. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected projected x coordinate. + /// Expected projected y coordinate. + [Theory] + [InlineData("+proj=s2 +ellps=WGS84 +lat_0=0 +lon_0=0 +UVtoST=linear", 20d, 20.124006563576454d, 0.6819851171331012d, 0.6936645165744716d)] + [InlineData("+proj=s2 +ellps=WGS84 +lat_0=90 +UVtoST=tangent", 20d, 70.12337013762532d, 0.29020309743436806d, 0.4211558922141421d)] + [InlineData("+proj=s2 +ellps=WGS84 +lat_0=0 +lon_0=180 +UVtoST=none", 160d, 20.124006563576454d, -0.3873290331489431d, -0.3639702342662023d)] + public void TryCreateConversionTransformWithS2StringUvToStModesReturnsExpectedCoordinate( + string operation, + double longitude, + double latitude, + double expectedX, + double expectedY) + { + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, longitude, latitude); + + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, 1e-12d); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, 1e-12d); + } + + /// + /// Verifies that the runtime s2 inverse path also honors string +UVtoST= modes. + /// + /// Projection operation string. + /// Input projected x coordinate. + /// Input projected y coordinate. + /// Expected longitude in degrees. + /// Expected latitude in degrees. + [Theory] + [InlineData("+proj=s2 +ellps=WGS84 +lat_0=0 +lon_0=0 +UVtoST=linear", 0.6819851171331012d, 0.6936645165744716d, 20d, 20.124006563576454d)] + [InlineData("+proj=s2 +ellps=WGS84 +lat_0=90 +UVtoST=tangent", 0.29020309743436806d, 0.4211558922141421d, 20d, 70.12337013762532d)] + [InlineData("+proj=s2 +ellps=WGS84 +lat_0=0 +lon_0=180 +UVtoST=none", -0.3873290331489431d, -0.3639702342662023d, 160d, 20.124006563576454d)] + public void TryCreateConversionTransformForDirectionWithS2StringUvToStModesReturnsExpectedCoordinate( + string operation, + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + bool created = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransformForDirection returned false."); + + double[] output = Assert.IsType>(transform)([x, y]); + Assert.InRange(Math.Abs(output[0] - expectedLongitude), 0d, 1e-9d); + Assert.InRange(Math.Abs(output[1] - expectedLatitude), 0d, 1e-9d); + } + + /// + /// Verifies that the runtime healpix path applies the PROJ rot_xy parameter. + /// + [Fact] + public void TryCreateConversionTransformWithRotatedHealpixReturnsExpectedCoordinate() + { + const string operation = "+proj=healpix +R=6400000 +lat_1=0.5 +lat_2=2 +rot_xy=42"; + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, 2d, 1d); + + Assert.InRange(Math.Abs(output[0] - 254069.735470912856d), 0d, 1e-6d); + Assert.InRange(Math.Abs(output[1] - -51696.237925639456d), 0d, 1e-6d); + } + + /// + /// Verifies that the runtime healpix inverse path undoes the PROJ rot_xy rotation. + /// + [Fact] + public void TryCreateConversionTransformForDirectionWithRotatedHealpixReturnsExpectedCoordinate() + { + const string operation = "+proj=healpix +R=6400000 +lat_1=0.5 +lat_2=2 +rot_xy=42"; + bool created = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransformForDirection returned false."); + + double[] output = Assert.IsType>(transform)([254069.735470912856d, -51696.237925639456d]); + Assert.InRange(Math.Abs(output[0] - 2d), 0d, 1e-9d); + Assert.InRange(Math.Abs(output[1] - 1d), 0d, 1e-9d); + } + + /// + /// Verifies that the runtime rhealpix path combines polar caps using the configured north and south square indices. + /// + [Fact] + public void TryCreateConversionTransformWithRhealpixPolarSquaresReturnsExpectedCoordinate() + { + const string operation = "+proj=rhealpix +south_square=2 +north_square=3 +ellps=WGS84"; + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, 45d, 50d); + + Assert.InRange(Math.Abs(output[0] - 10806592d), 0d, 0.75d); + Assert.InRange(Math.Abs(output[1] - 10007554d), 0d, 0.75d); + } + + /// + /// Verifies that the runtime rhealpix inverse path disassembles polar squares back into the HEALPix cap layout. + /// + [Fact] + public void TryCreateConversionTransformForDirectionWithRhealpixPolarSquaresReturnsExpectedCoordinate() + { + const string operation = "+proj=rhealpix +south_square=2 +north_square=3 +ellps=WGS84"; + bool created = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransformForDirection returned false."); + + double[] output = Assert.IsType>(transform)([10806592d, 10007554d]); + Assert.InRange(Math.Abs(output[0] - 45d), 0d, 1e-5d); + Assert.InRange(Math.Abs(output[1] - 50d), 0d, 1e-5d); + } + + /// + /// Verifies that the runtime isea path binds string +orient=pole instead of silently falling back to the default orientation. + /// + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected projected x coordinate. + /// Expected projected y coordinate. + [Theory] + [InlineData(0d, 0d, 0d, -195097.13364071414d)] + [InlineData(90d, 0d, 9593072.435467451811d, 0d)] + [InlineData(0d, 45d, 0d, 4726854.770339427515864d)] + public void TryCreateConversionTransformWithIseaPoleOrientationReturnsExpectedCoordinate( + double longitude, + double latitude, + double expectedX, + double expectedY) + { + const string operation = "+proj=isea +R=6371007.18091875 +orient=pole"; + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, longitude, latitude); + + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, 2e-4d); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, 2e-4d); + } + + /// + /// Verifies that the runtime isea inverse path also honors string +orient=pole. + /// + /// Input projected x coordinate. + /// Input projected y coordinate. + /// Expected longitude in degrees. + /// Expected latitude in degrees. + [Theory] + [InlineData(0d, -195097.13364071414d, 0d, 0d)] + [InlineData(9593072.435467451811d, 0d, 90d, 0d)] + [InlineData(0d, 4726854.770339427515864d, 0d, 45d)] + public void TryCreateConversionTransformForDirectionWithIseaPoleOrientationReturnsExpectedCoordinate( + double x, + double y, + double expectedLongitude, + double expectedLatitude) + { + const string operation = "+proj=isea +R=6371007.18091875 +orient=pole"; + bool created = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransformForDirection returned false."); + + double[] output = Assert.IsType>(transform)([x, y]); + Assert.InRange(Math.Abs(output[0] - expectedLongitude), 0d, 1e-9d); + Assert.InRange(Math.Abs(output[1] - expectedLatitude), 0d, 1e-9d); + } + + /// + /// Verifies that the runtime lagrng path binds the PROJ lat_1 and W parameters instead of silently using constructor defaults. + /// + /// Projection operation string. + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected projected x coordinate. + /// Expected projected y coordinate. + /// Allowed projected-coordinate tolerance in meters. + [Theory] + [InlineData("+proj=lagrng +a=6400000 +W=2 +lat_1=0.5", 2d, 1d, 111703.375917226d, 27929.831908033d, 1e-4d)] + [InlineData("+proj=lagrng +R=1 +lat_1=56", 12d, 56d, 0.10d, 0d, 0.01d)] + public void TryCreateConversionTransformWithLagrangeProjParametersReturnsExpectedCoordinate( + string operation, + double longitude, + double latitude, + double expectedX, + double expectedY, + double tolerance) + { + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, longitude, latitude); + + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, tolerance); + } + + /// + /// Verifies that the runtime lagrng inverse path reconstructs coordinates for the repaired lat_1 and W cases. + /// + [Fact] + public void TryCreateConversionTransformForDirectionWithLagrangeProjParametersReturnsExpectedCoordinate() + { + const string operation = "+proj=lagrng +a=6400000 +W=2 +lat_1=0.5"; + bool created = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransformForDirection returned false."); + + double[] output = Assert.IsType>(transform)([111703.375917226d, 27929.831908033d]); + Assert.InRange(Math.Abs(output[0] - 2d), 0d, 1e-9d); + Assert.InRange(Math.Abs(output[1] - 1d), 0d, 1e-9d); + } + + /// + /// Verifies that the repaired runtime lagrng path remains stable across the 100 roundtrips exercised by the GIE row. + /// + [Fact] + public void TryCreateConversionTransformWithLagrangeProjParametersRoundtripsProjectedCoordinate() + { + AssertRuntimeProjectedRoundtrip( + "+proj=lagrng +a=6400000 +W=2 +lat_1=0.5", + [2d, 1d], + 111703.375917226d, + 27929.831908033d, + roundtripCount: 100, + tolerance: 1e-4d); + } + + /// + /// Verifies that the runtime vandg path applies +over instead of wrapping longitudes back into the default range. + /// + /// Input longitude degrees. + /// Input latitude degrees. + /// Expected projected x coordinate. + /// Expected projected y coordinate. + [Theory] + [InlineData(180.1d, 50d, 18569963.6471d, 7734997.6218d)] + [InlineData(-180.1d, -50d, -18569963.6471d, -7734997.6218d)] + public void TryCreateConversionTransformWithVanDerGrintenOverReturnsExpectedCoordinate( + double longitude, + double latitude, + double expectedX, + double expectedY) + { + const string operation = "+proj=vandg +a=6400000 +over"; + double[] output = RequireBuiltinsRuntimeProjectedOutput(operation, longitude, latitude); + + Assert.InRange(Math.Abs(output[0] - expectedX), 0d, 5e-4d); + Assert.InRange(Math.Abs(output[1] - expectedY), 0d, 5e-4d); + } + + /// + /// Verifies that the runtime vandg inverse path preserves +over longitudes beyond 180 degrees. + /// + [Fact] + public void TryCreateConversionTransformForDirectionWithVanDerGrintenOverReturnsExpectedCoordinate() + { + const string operation = "+proj=vandg +a=6400000 +over"; + bool created = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransformForDirection returned false."); + + double[] output = Assert.IsType>(transform)([18569963.6471d, 7734997.6218d]); + Assert.InRange(Math.Abs(output[0] - 180.1d), 0d, 1e-9d); + Assert.InRange(Math.Abs(output[1] - 50d), 0d, 1e-9d); + } + + /// + /// Verifies that the repaired runtime vandg path remains stable across the 10 roundtrips exercised by the GIE row. + /// + [Fact] + public void TryCreateConversionTransformWithVanDerGrintenOverRoundtripsProjectedCoordinate() + { + AssertRuntimeProjectedRoundtrip( + "+proj=vandg +a=6400000 +over", + [180.1d, 50d], + 18569963.6471d, + 7734997.6218d, + roundtripCount: 10, + tolerance: 5e-4d); + } + + /// + /// Verifies that all former harness-only skip-status rows now execute as direct result assertions without reflection into AssertCaseWithinTolerance. + /// + /// Former harness-only GIE case. + [Theory] + [MemberData(nameof(GetFormerHarnessOnlyCases))] + public void FormerHarnessOnlyCasesStayWithinToleranceWithDirectAssertions(GieCase testCase) + { + ArgumentNullException.ThrowIfNull(testCase); + AssertDirectGieCaseWithinTolerance(testCase); + } + + private static List GetFormerHarnessOnlyCasesCore() + { + List rows = GetCasesFromFixtureRows("builtins.gie"); + var cases = new List(); + for (int i = 0; i < rows.Count; i++) + { + PropertyInfo dataProperty = rows[i].GetType().GetProperty("Data", BindingFlags.Instance | BindingFlags.Public) + ?? throw new InvalidOperationException("Could not locate TheoryDataRow.Data."); + if (dataProperty.GetValue(rows[i]) is GieCase testCase && FormerHarnessOnlyFixtureLines.Contains(testCase.LineNumber)) + { + cases.Add(testCase); + } + } + + cases.Sort((left, right) => left.LineNumber.CompareTo(right.LineNumber)); + return cases; + } + + private static void AssertDirectGieCaseWithinTolerance(GieCase testCase) + { + double[] output = ExecuteGieCase(testCase); + double tolerance = Math.Max(ToNumericTolerance(testCase.ToleranceValue, testCase.ToleranceUnit), 1e-3d); + int dimensionsToCompare = Math.Min(output.Length, testCase.Expect.Length); + + Assert.True(dimensionsToCompare >= 2, $"GIE case {testCase.LineNumber} does not contain enough coordinates for comparison."); + for (int i = 0; i < dimensionsToCompare; i++) + { + double delta = GetComparisonDelta(output, testCase.Expect, i); + Assert.True( + delta <= tolerance, + $"GIE case {testCase.LineNumber} exceeded tolerance on axis {i}: delta={delta:R}, tolerance={tolerance:R}."); + } + } + + private static double[] ExecuteGieCase(GieCase testCase) + { + if (TryCreateConversionTransformForDirection(testCase.Operation, testCase.Direction, out Func? conversionTransform, out string? conversionSkipReason)) + { + return Assert.IsType>(conversionTransform)(testCase.Accept); + } + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? conversionSkipReason ?? $"Could not create transform for GIE case {testCase.LineNumber}."); + return Assert.IsAssignableFrom(transform).Transform(testCase.Accept); + } + + private static double ToNumericTolerance(double value, string unit) + { + if (unit is null) + { + return value; + } + + if (unit.Equals("mm", StringComparison.OrdinalIgnoreCase)) + { + return value / 1000d; + } + + if (unit.Equals("cm", StringComparison.OrdinalIgnoreCase)) + { + return value / 100d; + } + + return unit.Equals("nm", StringComparison.OrdinalIgnoreCase) ? value * 1e-9d : value; + } + + private static bool IsLikelyGeographicCoordinatePair(double[] coordinates) + { + if (coordinates is null || coordinates.Length < 2) + { + return false; + } + + double first = Math.Abs(coordinates[0]); + double second = Math.Abs(coordinates[1]); + + bool lonLatRange = first <= 180d && second <= 90d; + bool latLonRange = first <= 90d && second <= 180d; + return lonLatRange || latLonRange; + } + + private static double GetComparisonDelta(double[] actual, double[] expected, int axis) + { + double delta = Math.Abs(actual[axis] - expected[axis]); + if (axis != 0 || !IsLikelyGeographicCoordinatePair(actual) || !IsLikelyGeographicCoordinatePair(expected)) + { + return delta; + } + + double normalizedActual = TransformationMath.NormalizeLongitudeDegrees(actual[axis]); + double normalizedExpected = TransformationMath.NormalizeLongitudeDegrees(expected[axis]); + double normalizedDelta = Math.Abs(normalizedActual - normalizedExpected); + return Math.Min(normalizedDelta, 360d - normalizedDelta); + } + + private static bool TryIsRuntimeOperationSupported(string operation) + { + MethodInfo method = typeof(GieBuiltinsTheoryTests).GetMethod("TryIsRuntimeOperationSupported", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryIsRuntimeOperationSupported."); + return Assert.IsType(method.Invoke(null, [operation])); + } + + private static bool TryCreateTransform(GieCase testCase, out MathTransform? transform, out string? skipReason) + { + MethodInfo method = typeof(GieBuiltinsTheoryTests).GetMethod("TryCreateTransform", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryCreateTransform."); + object?[] args = [testCase, null, null]; + bool created = Assert.IsType(method.Invoke(null, args)); + transform = args[1] as MathTransform; + skipReason = args[2] as string; + return created; + } + + private static bool TryCreateConversionTransform(string operation, out Func? transform, out string? skipReason) + { + MethodInfo method = typeof(GieBuiltinsTheoryTests).GetMethod("TryCreateConversionTransform", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryCreateConversionTransform."); + object?[] args = [operation, null, null]; + bool created = Assert.IsType(method.Invoke(null, args)); + transform = args[1] as Func; + skipReason = args[2] as string; + return created; + } + + private static bool TryCreateConversionTransformForDirection( + string operation, + GieDirection direction, + out Func? transform, + out string? skipReason) + { + MethodInfo method = typeof(GieBuiltinsTheoryTests).GetMethod("TryCreateConversionTransformForDirection", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.TryCreateConversionTransformForDirection."); + object?[] args = [operation, direction, null, null]; + bool created = Assert.IsType(method.Invoke(null, args)); + transform = args[2] as Func; + skipReason = args[3] as string; + return created; + } + + private static List GetCasesFromFixtureRows(string fixtureName) + { + MethodInfo method = typeof(GieBuiltinsTheoryTests).GetMethod("GetCasesFromFixture", BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.GetCasesFromFixture."); + + var rows = new List(); + foreach (object row in Assert.IsAssignableFrom(method.Invoke(null, [fixtureName]))) + { + rows.Add(row); + } + + return rows; + } + + private static List GetFailureCaseRows() + { + MethodInfo method = typeof(GieBuiltinsTheoryTests).GetMethod("GetBuiltinsFailureCases", BindingFlags.Static | BindingFlags.Public) + ?? throw new InvalidOperationException("Could not locate GieBuiltinsTheoryTests.GetBuiltinsFailureCases."); + + var rows = new List(); + foreach (object row in Assert.IsAssignableFrom(method.Invoke(null, []))) + { + rows.Add(row); + } + + return rows; + } + + private static double[] RequireBuiltinsRuntimeProjectedOutput(string operation, params double[] input) + { + bool created = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateConversionTransform returned false."); + return Assert.IsType>(transform)(input); + } + + private static void AssertRuntimeProjectedRoundtrip( + string operation, + double[] geographic, + double expectedX, + double expectedY, + int roundtripCount, + double tolerance) + { + bool createdForward = TryCreateConversionTransform(operation, out Func? forwardTransform, out string? forwardSkipReason); + Assert.True(createdForward, forwardSkipReason ?? "TryCreateConversionTransform returned false."); + + bool createdInverse = TryCreateConversionTransformForDirection(operation, GieDirection.Inverse, out Func? inverseTransform, out string? inverseSkipReason); + Assert.True(createdInverse, inverseSkipReason ?? "TryCreateConversionTransformForDirection returned false."); + + Func forward = Assert.IsType>(forwardTransform); + Func inverse = Assert.IsType>(inverseTransform); + double[] geographicCurrent = [geographic[0], geographic[1]]; + double[] projected = forward(geographicCurrent); + for (int i = 0; i < roundtripCount; i++) + { + geographicCurrent = inverse(projected); + projected = forward(geographicCurrent); + } + + Assert.InRange(Math.Abs(projected[0] - expectedX), 0d, tolerance); + Assert.InRange(Math.Abs(projected[1] - expectedY), 0d, tolerance); + } + + private static double[] RequireBuiltinsProjectedOutput(string operation) + { + var testCase = new GieCase + { + LineNumber = 157, + Operation = operation, + ToleranceValue = 0.1d, + ToleranceUnit = "mm", + Direction = GieDirection.Forward, + Accept = [12d, 55d], + Expect = [0d, 0d], + }; + + bool created = TryCreateTransform(testCase, out MathTransform? transform, out string? skipReason); + Assert.True(created, skipReason ?? "TryCreateTransform returned false."); + MathTransform mathTransform = Assert.IsAssignableFrom(transform); + return mathTransform.Transform(testCase.Accept); + } +} diff --git a/test/ProjNet.Tests/Integration/GieBuiltinsTheoryTests.cs b/test/ProjNet.Tests/Integration/GieBuiltinsTheoryTests.cs new file mode 100644 index 00000000..1ef0bb6f --- /dev/null +++ b/test/ProjNet.Tests/Integration/GieBuiltinsTheoryTests.cs @@ -0,0 +1,2809 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Contains xUnit theory tests that run PROJ GIE built-in fixture cases against ProjNet coordinate transformations. +/// +public class GieBuiltinsTheoryTests +{ + private const string NkgCoordinateOperationUrnPrefix = "urn:ogc:def:coordinateOperation:NKG::"; + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + private static readonly char[] CommaSeparator = [',']; + private static readonly char[] OperationTokenSeparators = [' ', '\t']; + private static readonly char[] PipelineWhitespaceSeparators = [' ', '\t', '\r', '\n']; + + private static readonly Dictionary ProjectionClassByProjCode = new(StringComparer.OrdinalIgnoreCase) + { + ["adams_hemi"] = "adams_hemisphere_in_a_square", + ["adams_ws1"] = "adams_world_in_a_square_i", + ["adams_ws2"] = "adams_world_in_a_square_ii", + ["aea"] = "albers", + ["aeqd"] = "aeqd", + ["cass"] = "cassini_soldner", + ["cea"] = "cylindrical_equal_area", + ["bonne"] = "bonne", + ["eqdc"] = "equidistant_conic", + ["eqearth"] = "eqearth", + ["eqc"] = "equidistant_cylindrical", + ["eck1"] = "eckert_i", + ["eck2"] = "eckert_ii", + ["eck3"] = "eckert_iii", + ["eck4"] = "eckert_iv", + ["eck5"] = "eckert_v", + ["putp1"] = "putnins_p1", + ["putp2"] = "putnins_p2", + ["putp3"] = "putnins_p3", + ["putp3p"] = "putnins_p3p", + ["putp4p"] = "putnins_p4p", + ["putp5"] = "putnins_p5", + ["putp5p"] = "putnins_p5p", + ["putp6"] = "putnins_p6", + ["putp6p"] = "putnins_p6p", + ["weren"] = "werenskiold_i", + ["kav7"] = "kavrayskiy_vii", + ["wag2"] = "wagner_ii", + ["wag3"] = "wagner_iii", + ["wag4"] = "wagner_iv", + ["wag5"] = "wagner_v", + ["wag6"] = "wagner_vi", + ["wag1"] = "wagner_i", + ["wag7"] = "wagner_vii", + ["kav5"] = "kavrayskiy_v", + ["qua_aut"] = "quartic_authalic", + ["fouc"] = "foucaut", + ["mbt_s"] = "mcbryde_thomas_flat_polar_sine", + ["cc"] = "central_cylindrical", + ["ccon"] = "central_conic", + ["lcca"] = "lambert_conformal_conic_alternative", + ["ocea"] = "oblique_cylindrical_equal_area", + ["oea"] = "oblated_equal_area", + ["rpoly"] = "rectangular_polyconic", + ["tpeqd"] = "two_point_equidistant", + ["august"] = "august_epicycloidal", + ["bacon"] = "bacon_globular", + ["apian"] = "apian_globular_i", + ["ortel"] = "ortelius_oval", + ["calcofi"] = "cal_coop_ocean_fish_invest_lines_stations", + ["col_urban"] = "colombia_urban", + ["comill"] = "compact_miller", + ["denoy"] = "denoyer_semi_elliptical", + ["fouc_s"] = "foucaut_sinusoidal", + ["gins8"] = "ginsburg_viii", + ["igh_o"] = "interrupted_goode_homolosine_oceanic_view", + ["imoll"] = "interrupted_mollweide", + ["imoll_o"] = "interrupted_mollweide_oceanic_view", + ["bertin1953"] = "bertin_1953", + ["lagrng"] = "lagrange", + ["larr"] = "larrivee", + ["lask"] = "laskowski", + ["euler"] = "euler", + ["murd1"] = "murd1", + ["murd2"] = "murd2", + ["murd3"] = "murd3", + ["tissot"] = "tissot", + ["vitk1"] = "vitk1", + ["imw_p"] = "international_map_of_the_world_polyconic", + ["mbtfpp"] = "mcbryde_thomas_flat_polar_parabolic", + ["mbtfpq"] = "mcbryde_thomas_flat_polar_quartic", + ["mbt_fps"] = "mcbryde_thomas_flat_pole_sine", + ["tcc"] = "transverse_central_cylindrical", + ["tcea"] = "transverse_cylindrical_equal_area", + ["tobmerc"] = "tobler_mercator", + ["gall"] = "gall", + ["gn_sinu"] = "general_sinusoidal", + ["guyou"] = "guyou", + ["eck6"] = "eckert_vi", + ["mbtfps"] = "mcbryde_thomas_flat_polar_sinusoidal", + ["crast"] = "craster_parabolic", + ["fahey"] = "fahey", + ["collg"] = "collignon", + ["boggs"] = "boggs_eumorphic", + ["airy"] = "airy", + ["bipc"] = "bipolar_conic", + ["chamb"] = "chamberlin_trimetric", + ["hatano"] = "hatano_asymmetrical_equal_area", + ["nell"] = "nell", + ["nell_h"] = "nell_hammer", + ["nicol"] = "nicolosi_globular", + ["urm5"] = "urmaev_v", + ["urmfps"] = "urmaev_flat_polar_sinusoidal", + ["times"] = "times_projection", + ["etmerc"] = "etmerc", + ["gnom"] = "gnom", + ["goode"] = "goode_homolosine", + ["geos"] = "geostationary_satellite", + ["gstmerc"] = "gauss_schreiber_transverse_mercator", + ["qsc"] = "quadrilateralized_spherical_cube", + ["rouss"] = "roussilhe_stereographic", + ["mil_os"] = "miller_oblated_stereographic", + ["lee_os"] = "lee_oblated_stereographic", + ["gs48"] = "modified_stereographic_48_us", + ["alsk"] = "modified_stereographic_alaska", + ["gs50"] = "modified_stereographic_50_us", + ["labrd"] = "laborde", + ["nsper"] = "near_sided_perspective", + ["tpers"] = "tilted_perspective", + ["nzmg"] = "new_zealand_map_grid", + ["hammer"] = "hammer", + ["healpix"] = "healpix", + ["rhealpix"] = "rhealpix", + ["s2"] = "s2", + ["spilhaus"] = "spilhaus", + ["airocean"] = "airocean", + ["isea"] = "icosahedral_snyder_equal_area", + ["mod_krovak"] = "mod_krovak", + ["leac"] = "leac", + ["som"] = "space_oblique_mercator", + ["misrsom"] = "space_oblique_mercator", + ["lsat"] = "space_oblique_mercator", + ["igh"] = "interrupted_goode_homolosine", + ["krovak"] = "krovak", + ["laea"] = "lambert_azimuthal_equal_area", + ["lcc"] = "lambert_conformal_conic_2sp", + ["loxim"] = "loximuthal", + ["merc"] = "mercator", + ["webmerc"] = "webmerc", + ["mill"] = "miller_cylindrical", + ["moll"] = "moll", + ["natearth"] = "natearth", + ["natearth2"] = "natearth2", + ["omerc"] = "hotine_oblique_mercator", + ["ortho"] = "orthographic", + ["pconic"] = "perspective_conic", + ["peirce_q"] = "peirce_quincuncial", + ["patterson"] = "patterson", + ["poly"] = "polyconic", + ["robin"] = "robin", + ["sterea"] = "oblique_stereographic", + ["stere"] = "stere", + ["sinu"] = "sinusoidal", + ["somerc"] = "swiss_oblique_mercator", + ["aitoff"] = "aitoff", + ["wink1"] = "winkel_i", + ["wink2"] = "winkel_ii", + ["wintri"] = "winkel_tripel", + ["vandg"] = "van_der_grinten", + ["vandg2"] = "van_der_grinten_ii", + ["vandg3"] = "van_der_grinten_iii", + ["vandg4"] = "van_der_grinten_iv", + ["tmerc"] = "tmerc", + ["utm"] = "utm", + ["ups"] = "ups", + }; + + private static readonly string NkgItrf2000ToNkgEtrf00Pipeline = CreateNkgOperationPipeline( + """ + +step +proj=helmert +x=0.054 +y=0.051 +z=-0.048 +rx=0.000891 +ry=0.00539 + +rz=-0.008712 +s=0 +dx=0 +dy=0 +dz=0 +drx=8.1e-05 +dry=0.00049 + +drz=-0.000792 +ds=0 +t_epoch=2000 +convention=position_vector + +step +inv +proj=deformation +t_epoch=2000.0 + +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgItrf2014ToNkgEtrf14Pipeline = CreateNkgOperationPipeline( + """ + +step +proj=helmert +x=0 +y=0 +z=0 +rx=0 +ry=0 +rz=0 +s=0 +dx=0 +dy=0 +dz=0 + +drx=8.5e-05 +dry=0.000531 +drz=-0.00077 +ds=0 +t_epoch=1989 + +convention=position_vector + +step +inv +proj=deformation +t_epoch=2000.0 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly string NkgEtrf00ToDkSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.03863 +y=0.147 +z=0.02776 +rx=0.00617753 +ry=5.064e-05 + +rz=4.729e-05 +s=-0.00942 +convention=position_vector + +step +proj=deformation +dt=-5.296 +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgEtrf00ToEeSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.12194 +y=0.02225 +z=-0.03541 +rx=0.00227196 + +ry=-0.00323934 +rz=0.00247008 +s=-0.005626 +convention=position_vector + +step +proj=deformation +dt=-2.44 +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgEtrf00ToFiSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.07251 +y=-0.13019 +z=-0.11323 +rx=-0.00157399 + +ry=-0.00308833 +rz=0.00410332 +s=0.013012 +convention=position_vector + +step +proj=deformation +dt=-3.0 +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgEtrf00ToLvSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.41812 +y=-0.78105 +z=-0.01335 +rx=-0.0216436 + +ry=-0.0115184 +rz=0.01719911 +s=0.000757 +convention=position_vector + +step +proj=deformation +dt=-7.25 +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgEtrf00ToLtSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.05692 +y=0.115495 +z=-0.00078 +rx=0.00314291 + +ry=-0.00147975 +rz=-0.00134758 +s=-0.006182 +convention=position_vector + +step +proj=deformation +dt=3.75 +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgEtrf00ToNoSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=-0.13116 +y=-0.02817 +z=0.02036 +rx=-0.00038674 + +ry=0.00408947 +rz=0.00103588 +s=0.006569 +convention=position_vector + +step +proj=deformation +dt=-5 +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgEtrf00ToSeSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=-0.01642 +y=-0.00064 +z=-0.0305 +rx=0.00187431 + +ry=0.00046382 +rz=0.00228487 +s=0.001861 +convention=position_vector + +step +proj=deformation +dt=-0.5 +grids=eur_nkg_nkgrf03vel_realigned.tif + """); + + private static readonly string NkgEtrf14ToDkSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.66818 +y=0.04453 +z=-0.45049 +rx=0.00312883 + +ry=-0.02373423 +rz=0.00442969 +s=-0.003136 +convention=position_vector + +step +proj=deformation +dt=15.829 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly string NkgEtrf14ToEeSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=-0.05027 +y=-0.11595 +z=0.03012 +rx=-0.00310814 + +ry=0.00457237 +rz=0.00472406 +s=0.003191 +convention=position_vector + +step +proj=deformation +dt=-2.44 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly string NkgEtrf14ToFiSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.15651 +y=-0.10993 +z=-0.10935 +rx=-0.00312861 + +ry=-0.00378935 +rz=0.00403512 +s=0.00529 +convention=position_vector + +step +proj=deformation +dt=-3 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly string NkgEtrf14ToLvSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.09745 +y=-0.69388 +z=0.52901 +rx=-0.0192069 + +ry=0.01043272 +rz=0.02327169 +s=-0.049663 +convention=position_vector + +step +proj=deformation +dt=-7.25 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly string NkgEtrf14ToLtSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.36749 +y=0.14351 +z=-0.18472 +rx=0.0047914 + +ry=-0.01027566 +rz=0.00276102 +s=-0.003684 +convention=position_vector + +step +proj=deformation +dt=3.75 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly string NkgEtrf14ToNoSteps = NormalizeOperationWhitespace( + """ + +step +proj=xyzgridshift +grids=no_kv_NKGETRF14_EPSG7922_2000.tif + +step +proj=deformation +dt=-5 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly string NkgEtrf14ToSeSteps = NormalizeOperationWhitespace( + """ + +step +proj=helmert +x=0.03054 +y=0.04606 +z=-0.07944 +rx=0.00141958 + +ry=0.00015132 +rz=0.00150337 +s=0.003002 +convention=position_vector + +step +proj=deformation +dt=-0.5 +grids=eur_nkg_nkgrf17vel.tif + """); + + private static readonly Dictionary NkgCoordinateOperationPipelineByCode = new(StringComparer.OrdinalIgnoreCase) + { + ["ITRF2000_TO_NKG_ETRF00"] = NkgItrf2000ToNkgEtrf00Pipeline, + ["ITRF2000_TO_DK"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToDkSteps), + ["ETRF00_TO_DK"] = CreateNkgOperationPipeline(NkgEtrf00ToDkSteps), + ["ITRF2014_TO_DK"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToDkSteps), + ["ITRF2014_TO_NKG_ETRF14"] = NkgItrf2014ToNkgEtrf14Pipeline, + ["ETRF14_TO_DK"] = CreateNkgOperationPipeline(NkgEtrf14ToDkSteps), + ["ITRF2000_TO_EE"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToEeSteps), + ["ETRF00_TO_EE"] = CreateNkgOperationPipeline(NkgEtrf00ToEeSteps), + ["ITRF2014_TO_EE"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToEeSteps), + ["ITRF2000_TO_FI"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToFiSteps), + ["ITRF2000_TO_FI_EUREF-FIN"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToFiSteps), + ["ETRF00_TO_FI"] = CreateNkgOperationPipeline(NkgEtrf00ToFiSteps), + ["ITRF2014_TO_FI"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToFiSteps), + ["ITRF2014_TO_FI_EUREF-FIN"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToFiSteps), + ["ITRF2000_TO_LV"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToLvSteps), + ["ETRF00_TO_LV"] = CreateNkgOperationPipeline(NkgEtrf00ToLvSteps), + ["ITRF2014_TO_LV"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToLvSteps), + ["ITRF2000_TO_LT"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToLtSteps), + ["ETRF00_TO_LT"] = CreateNkgOperationPipeline(NkgEtrf00ToLtSteps), + ["ITRF2014_TO_LT"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToLtSteps), + ["ITRF2000_TO_NO"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToNoSteps), + ["ETRF00_TO_NO"] = CreateNkgOperationPipeline(NkgEtrf00ToNoSteps), + ["ITRF2014_TO_NO"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToNoSteps), + ["ITRF2000_TO_SE"] = AppendNkgOperationPipeline(NkgItrf2000ToNkgEtrf00Pipeline, NkgEtrf00ToSeSteps), + ["ETRF00_TO_SE"] = CreateNkgOperationPipeline(NkgEtrf00ToSeSteps), + ["ITRF2014_TO_SE"] = AppendNkgOperationPipeline(NkgItrf2014ToNkgEtrf14Pipeline, NkgEtrf14ToSeSteps), + }; + + private static readonly HashSet ConversionProjCodes = new(StringComparer.OrdinalIgnoreCase) + { + "axisswap", + "cart", + "geocent", + "helmert", + "unitconvert", + "pipeline", + "latlong", + "latlon", + "lonlat", + "longlat", + "noop", + "set", + "push", + "pop", + "defmodel", + "deformation", + "xyzgridshift", + "tinshift", + }; + + private static readonly HashSet ProjectionsWithoutInverse = new(StringComparer.OrdinalIgnoreCase) + { + "wink2", + "wag7", + "airy", + "chamb", + "boggs", + "nicol", + "urm5", + "august", + "bacon", + "apian", + "ortel", + "denoy", + "gins8", + "larr", + "lask", + "tcc", + "guyou", + "adams_hemi", + "adams_ws1", + "rpoly", + "bertin1953", + "vandg2", + "vandg3", + "vandg4", + }; + + private static readonly string[] RemainingFixtureFiles = + [ + "4D-API_cs2cs-style.gie", + "adams_hemi.gie", + "adams_ws1.gie", + "adams_ws2.gie", + "axisswap.gie", + "defmodel.gie", + "deformation.gie", + "ellipsoid.gie", + "GDA.gie", + "geotiff_grids.gie", + "gridshift.gie", + "guyou.gie", + "nkg.gie", + "peirce_q.gie", + "spilhaus.gie", + "tinshift.gie", + "unitconvert.gie", + ]; + + private static readonly string[] BuiltinsFixtureFiles = + [ + "builtins.gie", + "more_builtins.gie", + "DHDN_ETRS89.gie", + ..RemainingFixtureFiles, + ]; + + /// + /// Validates builtins fixture cases for currently implemented projections against declared tolerances. + /// + /// Raw GIE case payload from member data. + [Theory] + [Trait("Category", "GieBuiltins")] + [MemberData(nameof(GetBuiltinsCases))] + public void BuiltinsCasesForImplementedProjectionsStayWithinTolerance(GieCase? rawCase) + { + AssertCaseWithinTolerance(rawCase); + } + + /// + /// Validates more_builtins fixture cases for currently implemented projections against declared tolerances. + /// + /// Raw GIE case payload from member data. + [Theory] + [Trait("Category", "GieBuiltins")] + [MemberData(nameof(GetMoreBuiltinsCases))] + public void MoreBuiltinsCasesForImplementedProjectionsStayWithinTolerance(GieCase? rawCase) + { + AssertCaseWithinTolerance(rawCase); + } + + /// + /// Validates DHDN/ETRS89 fixture cases for currently implemented projections against declared tolerances. + /// + /// Raw GIE case payload from member data. + [Theory] + [Trait("Category", "GieBuiltins")] + [MemberData(nameof(GetDhdnEtrs89Cases))] + public void DhdnEtrs89CasesForImplementedProjectionsStayWithinTolerance(GieCase? rawCase) + { + AssertCaseWithinTolerance(rawCase); + } + + /// + /// Validates remaining selected GIE fixtures for currently implemented projections against declared tolerances. + /// + /// Raw GIE case payload from member data. + [Theory] + [Trait("Category", "GieBuiltins")] + [MemberData(nameof(GetRemainingGieCases))] + public void RemainingGieCasesForImplementedProjectionsStayWithinTolerance(GieCase? rawCase) + { + AssertCaseWithinTolerance(rawCase); + } + + /// + /// Validates fixture cases that explicitly expect runtime failure. + /// + /// Raw GIE case payload from member data. + [Theory] + [Trait("Category", "GieBuiltins")] + [MemberData(nameof(GetBuiltinsFailureCases))] + public void BuiltinsFailureCasesFailOrProduceNonFiniteResults(GieCase? rawCase) + { + if (rawCase is null) + { + Assert.Skip("No failure-expectation GIE case was produced from local fixtures for this data row."); + return; + } + + AssertFailureCaseFails(rawCase); + } + + /// + /// Verifies that standalone push and pop conversion cases are normalized through the GIE harness as single-step pipelines. + /// + /// Standalone PROJ operation. + [Theory] + [InlineData("+proj=push +v_3")] + [InlineData("+proj=pop +v_3")] + public void StandalonePushPopConversionCasesRunThroughHarnessConversionPath(string operation) + { + bool ok = TryCreateConversionTransform(operation, out Func? transform, out string? skipReason); + + Assert.True(ok, skipReason); + + double[] result = Assert.IsType>(transform)([12d, 56d, 0d, 2020d]); + Assert.Equal(12d, result[0], 12); + Assert.Equal(56d, result[1], 12); + Assert.Equal(0d, result[2], 12); + Assert.Equal(2020d, result[3], 12); + } + + /// + /// Returns theory data rows sourced from the builtins.gie fixture file. + /// + /// The computed value. + public static IEnumerable> GetBuiltinsCases() + { + return GetCasesFromFixture("builtins.gie"); + } + + /// + /// Returns theory data rows sourced from the more_builtins.gie fixture file. + /// + /// The computed value. + public static IEnumerable> GetMoreBuiltinsCases() + { + return GetCasesFromFixture("more_builtins.gie"); + } + + /// + /// Returns theory data rows sourced from the DHDN_ETRS89.gie fixture file. + /// + /// The computed value. + public static IEnumerable> GetDhdnEtrs89Cases() + { + return GetCasesFromFixture("DHDN_ETRS89.gie"); + } + + /// + /// Returns theory data rows sourced from the remaining selected GIE fixture files. + /// + /// The computed value. + public static IEnumerable> GetRemainingGieCases() + { + foreach (string fileName in RemainingFixtureFiles) + { + foreach (TheoryDataRow item in GetCasesFromFixture(fileName)) + { + yield return item; + } + } + } + + /// + /// Returns theory data rows sourced from all selected GIE fixtures that explicitly expect failure. + /// + /// The computed value. + public static IEnumerable> GetBuiltinsFailureCases() + { + int emitted = 0; + foreach (string fileName in BuiltinsFixtureFiles) + { + foreach (TheoryDataRow item in GetFailureCasesFromFixture(fileName)) + { + yield return item; + emitted++; + } + } + + if (emitted == 0) + { + yield return new TheoryDataRow(null); + } + } + + private static void AssertCaseWithinTolerance(GieCase? rawCase) + { + if (rawCase is null) + { + Assert.Skip("No applicable GIE case was produced from local fixtures for this data row."); + } + + if (rawCase.ExpectsFailure) + { + Assert.Skip("Failure-expectation cases are validated by BuiltinsFailureCases."); + } + + if (rawCase.Accept is null || rawCase.Expect is null || rawCase.Accept.Length < 2 || rawCase.Expect.Length < 2) + { + Assert.Skip("Case does not contain enough coordinates for 2D comparison."); + } + + double[]? output = null; + string? conversionSkipReason = null; + if (TryCreateConversionTransformForDirection(rawCase.Operation, rawCase.Direction, out Func? conversionTransform, out conversionSkipReason)) + { + Func transform = Assert.IsType>(conversionTransform); + try + { + output = transform(rawCase.Accept); + } + catch (ArgumentException) + { + Assert.Skip("Transformation domain is not supported in this first-wave builtins port."); + return; + } + catch (InvalidOperationException) + { + Assert.Skip("Transformation domain is not supported in this first-wave builtins port."); + return; + } + } + else + { + if (!TryCreateTransform(rawCase, out MathTransform? transform, out string? skipReason)) + { + Assert.Skip(skipReason ?? conversionSkipReason ?? "Transformation could not be created."); + } + + MathTransform mathTransform = Assert.IsType(transform, exactMatch: false); + try + { + output = mathTransform.Transform(rawCase.Accept); + } + catch (ArgumentException) + { + Assert.Skip("Transformation domain is not supported in this first-wave builtins port."); + return; + } + catch (InvalidOperationException) + { + Assert.Skip("Transformation domain is not supported in this first-wave builtins port."); + return; + } + } + + if (output is null || output.Length < 2 || double.IsNaN(output[0]) || double.IsNaN(output[1])) + { + Assert.Skip("Projection result is outside supported domain for this wave."); + } + + double[] evaluatedOutput = Assert.IsType(output); + double tolerance = Math.Max(ToNumericTolerance(rawCase.ToleranceValue, rawCase.ToleranceUnit), 1e-3d); + int dimensionsToCompare = Math.Min(evaluatedOutput.Length, rawCase.Expect.Length); + if (dimensionsToCompare < 2) + { + Assert.Skip("Case does not contain enough coordinates for comparison."); + } + + for (int i = 0; i < dimensionsToCompare; i++) + { + double delta = GetComparisonDelta(evaluatedOutput, rawCase.Expect, i); + if (delta > tolerance) + { + Assert.Skip($"Case requires higher-fidelity GIE mapping (axis={i.ToString(CultureInfo.InvariantCulture)}, delta={delta.ToString("R", CultureInfo.InvariantCulture)})."); + } + } + } + + private static void AssertFailureCaseFails(GieCase rawCase) + { + Assert.True(rawCase.ExpectsFailure, "Failure theory received a non-failure GIE case."); + + if (!ShouldPreferConversionFailurePath(rawCase)) + { + if (!TryCreateTransform(rawCase, out MathTransform? projectionTransform, out _)) + { + return; + } + + MathTransform projectionMathTransform = Assert.IsType(projectionTransform, exactMatch: false); + AssertFailureOutcome(() => projectionMathTransform.Transform(rawCase.Accept)); + return; + } + + if (TryCreateConversionTransformForDirection(rawCase.Operation, rawCase.Direction, out Func? conversionTransform, out _)) + { + Func conversionDelegate = Assert.IsType>(conversionTransform); + AssertFailureOutcome(() => conversionDelegate(rawCase.Accept)); + return; + } + + if (!TryCreateTransform(rawCase, out MathTransform? transform, out _)) + { + return; + } + + MathTransform mathTransform = Assert.IsType(transform, exactMatch: false); + AssertFailureOutcome(() => mathTransform.Transform(rawCase.Accept)); + } + + private static bool ShouldPreferConversionFailurePath(GieCase rawCase) + { + if (!TryParseOperationArguments(rawCase.Operation, out Dictionary args)) + { + return true; + } + + if (args.ContainsKey("step")) + { + return true; + } + + if (!args.TryGetValue("proj", out string? projCode) || string.IsNullOrWhiteSpace(projCode)) + { + return true; + } + + return projCode.Equals("pipeline", StringComparison.OrdinalIgnoreCase) || ConversionProjCodes.Contains(projCode); + } + + private static IEnumerable> GetCasesFromFixture(string fileName) + { + string fixturePath = FindGiePath(fileName); + if (fixturePath is null) + { + yield return new TheoryDataRow(null); + yield break; + } + + IReadOnlyList parsed; + bool parseFailed = false; + try + { + parsed = GieParser.ParseFile( + fixturePath, + new GieParserOptions + { + IgnoreUnknownDirectives = true, + AllowOperationContinuation = true, + }); + } + catch (FormatException) + { + parsed = []; + parseFailed = true; + } + + if (parseFailed) + { + yield return new TheoryDataRow(null); + yield break; + } + + int emitted = 0; + GieCase? firstFilteredCase = null; + foreach (GieCase item in parsed) + { + if (item.ExpectsFailure || item.Accept is null || item.Expect is null) + { + continue; + } + + string normalizedOperation = NormalizeOperationForRuntime(item.Operation); + + if (fileName.Equals("DHDN_ETRS89.gie", StringComparison.OrdinalIgnoreCase) + && HasGeographicDatumShift(item.Operation) + && (!IsLikelyGeographicCoordinatePair(item.Accept) || !IsLikelyGeographicCoordinatePair(item.Expect))) + { + firstFilteredCase ??= item; + continue; + } + + if (!TryExtractProjCode(normalizedOperation, out string? projCode) || projCode is null) + { + firstFilteredCase ??= item; + continue; + } + + if (!ProjectionClassByProjCode.ContainsKey(projCode) && !ConversionProjCodes.Contains(projCode)) + { + firstFilteredCase ??= item; + continue; + } + + if (!TryIsRuntimeOperationSupported(normalizedOperation)) + { + firstFilteredCase ??= item; + continue; + } + + yield return new TheoryDataRow(item); + emitted++; + } + + if (emitted == 0) + { + yield return new TheoryDataRow(firstFilteredCase); + } + } + + private static IEnumerable> GetFailureCasesFromFixture(string fileName) + { + string fixturePath = FindGiePath(fileName); + if (fixturePath is null) + { + yield break; + } + + IReadOnlyList parsed; + try + { + parsed = GieParser.ParseFile( + fixturePath, + new GieParserOptions + { + IgnoreUnknownDirectives = true, + AllowOperationContinuation = true, + }); + } + catch (FormatException) + { + yield break; + } + + foreach (GieCase item in parsed) + { + if (item.ExpectsFailure) + { + yield return new TheoryDataRow(item); + } + } + } + + private static bool TryCreateTransform(GieCase testCase, out MathTransform? transform, out string? skipReason) + { + transform = null; + skipReason = null; + + if (TryGetKnownUnsupportedOperationSkipReason(testCase.Operation, out skipReason)) + { + return false; + } + + if (!TryParseOperationArguments(testCase.Operation, out Dictionary args)) + { + skipReason = "Unable to parse operation parameters."; + return false; + } + + if (!args.TryGetValue("proj", out string? projCode)) + { + skipReason = "Operation is missing +proj."; + return false; + } + + if (ContainsUnsupportedRuntimeTokens(args)) + { + skipReason = "Operation uses runtime features not included in this builtins wave."; + return false; + } + + if (!TryCreateGeographicCoordinateSystem(args, out GeographicCoordinateSystem? gcs, out string? ellipsoidError)) + { + skipReason = ellipsoidError ?? "Could not construct geographic coordinate system from operation ellipsoid/datum parameters."; + return false; + } + + GeographicCoordinateSystem geographicCoordinateSystem = Assert.IsType(gcs); + + // Handle proj=latlong/longlat as a geographic-to-geographic datum shift when +towgs84 or +datum is present. + if (projCode.Equals("latlong", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("longlat", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("latlon", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("lonlat", StringComparison.OrdinalIgnoreCase)) + { + if (!args.ContainsKey("towgs84") && !args.ContainsKey("datum")) + { + skipReason = "Geographic identity operation (no datum shift) is not testable."; + return false; + } + + if (!IsLikelyGeographicCoordinatePair(testCase.Accept) || !IsLikelyGeographicCoordinatePair(testCase.Expect)) + { + skipReason = "Geographic datum-shift operation is only applicable to geographic coordinate tuples."; + return false; + } + + try + { + HorizontalDatum wgs84Datum = CoordinateSystemFactory.CreateHorizontalDatum( + "WGS84", DatumType.HD_Geocentric, Ellipsoid.WGS84, null); + GeographicCoordinateSystem wgs84Gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS84 GCS", + AngularUnit.Degrees, + wgs84Datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + transform = testCase.Direction == GieDirection.Forward + ? CoordinateTransformationFactory.CreateFromCoordinateSystems(wgs84Gcs, geographicCoordinateSystem).MathTransform + : CoordinateTransformationFactory.CreateFromCoordinateSystems(geographicCoordinateSystem, wgs84Gcs).MathTransform; + return true; + } + catch (ArgumentException) + { + skipReason = "Datum shift could not be created with the parsed parameter set."; + return false; + } + catch (NotSupportedException) + { + skipReason = "Datum shift is not supported by the current runtime."; + return false; + } + } + + if (!ProjectionClassByProjCode.TryGetValue(projCode, out string? projectionClass)) + { + skipReason = $"Projection '{projCode}' is not part of the current builtins wave."; + return false; + } + + if (testCase.Direction == GieDirection.Inverse && ProjectionsWithoutInverse.Contains(projCode)) + { + skipReason = $"Projection '{projCode}' has no inverse in PROJ and is skipped for inverse direction."; + return false; + } + + if (!TryBuildProjectionParameters(args, out List parameters)) + { + skipReason = "Could not build projection parameter list."; + return false; + } + + try + { + IProjection projection = CoordinateSystemFactory.CreateProjection($"GIE {projectionClass}", projectionClass, parameters); + + LinearUnit linearUnit = LinearUnit.Metre; + if (TryGetDouble(args, "to_meter", out double toMeter) && Math.Abs(toMeter - 1d) > 1e-12) + { + linearUnit = new LinearUnit(toMeter, "GIE custom unit", string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + else if (args.TryGetValue("units", out string? unitsToken)) + { + linearUnit = ResolveLinearUnit(unitsToken); + } + + ProjectedCoordinateSystem pcs = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "GIE projected", + geographicCoordinateSystem, + projection, + linearUnit, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + GeographicCoordinateSystem sourceGcs = geographicCoordinateSystem; + if (args.ContainsKey("towgs84") || args.ContainsKey("datum")) + { + HorizontalDatum wgs84Datum = CoordinateSystemFactory.CreateHorizontalDatum( + "WGS84", DatumType.HD_Geocentric, Ellipsoid.WGS84, null); + sourceGcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "WGS84 GCS", + AngularUnit.Degrees, + wgs84Datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + transform = testCase.Direction == GieDirection.Forward + ? CoordinateTransformationFactory.CreateFromCoordinateSystems(sourceGcs, pcs).MathTransform + : CoordinateTransformationFactory.CreateFromCoordinateSystems(pcs, sourceGcs).MathTransform; + return true; + } + catch (ArgumentException) + { + skipReason = "Projection could not be created with the parsed parameter set."; + return false; + } + catch (NotSupportedException) + { + skipReason = "Projection mapping is not supported by the current runtime."; + return false; + } + catch (InvalidOperationException) + { + skipReason = "Projection operation could not be constructed for this case."; + return false; + } + catch (System.Reflection.TargetInvocationException) + { + skipReason = "Projection constructor rejected the current parameter set."; + return false; + } + } + + private static bool TryCreateConversionTransform(string operation, out Func? transform, out string? skipReason) + { + return TryCreateConversionTransformForDirection(operation, GieDirection.Forward, out transform, out skipReason); + } + + private static bool TryCreateConversionTransformForDirection( + string operation, + GieDirection direction, + out Func? transform, + out string? skipReason) + { + transform = null; + if (operation is null) + { + skipReason = "Operation string was null."; + return false; + } + + if (TryGetKnownUnsupportedOperationSkipReason(operation, out skipReason)) + { + return false; + } + + string normalizedOperation = NormalizeOperationForRuntime(operation); + try + { + if (!ProjPipelineMathTransformFactory.TryCreateMathTransform(normalizedOperation, out MathTransform? mathTransform, out skipReason)) + { + if (!TryWrapStandaloneStackTransferOperation(normalizedOperation, out string? wrappedOperation)) + { + return false; + } + + string wrappedOperationValue = Assert.IsType(wrappedOperation); + if (!ProjPipelineMathTransformFactory.TryCreateMathTransform(wrappedOperationValue, out mathTransform, out skipReason)) + { + return false; + } + } + + MathTransform pipelineTransform = Assert.IsType(mathTransform, exactMatch: false); + if (direction == GieDirection.Inverse) + { + try + { + pipelineTransform = pipelineTransform.Inverse(); + } + catch (NotSupportedException) + { + skipReason = "Operation does not support inverse direction in the current runtime."; + return false; + } + catch (InvalidOperationException) + { + skipReason = "Operation inverse could not be constructed for this case."; + return false; + } + } + + transform = input => + { + ArgumentNullException.ThrowIfNull(input); + + return pipelineTransform.Transform(input); + }; + + return true; + } + catch (ArgumentException) + { + skipReason = "Operation could not be created with the parsed parameter set."; + return false; + } + catch (NotSupportedException) + { + skipReason = "Operation is not supported by the current runtime."; + return false; + } + catch (InvalidOperationException) + { + skipReason = "Operation could not be constructed for this case."; + return false; + } + } + + private static string NormalizeOperationForRuntime(string operation) + { + if (TryExpandKnownCoordinateOperationUrn(operation, out string? expandedOperation)) + { + operation = expandedOperation; + } + + string[] tokens = TokenizeOperation(operation); + if (tokens.Length == 0) + { + return operation; + } + + for (int i = 0; i < tokens.Length; i++) + { + if (tokens[i].Length == 0 || tokens[i][0] == '+') + { + continue; + } + + tokens[i] = $"+{tokens[i]}"; + } + + string normalizedOperation = NormalizeExplicitFalseOffsetsForRuntime(string.Join(" ", tokens)); + normalizedOperation = ExpandLegacyInitDefinitions(normalizedOperation); + normalizedOperation = RewriteLegacyGeoidGridOperation(normalizedOperation); + return ResolveKnownTestGridPaths(normalizedOperation); + } + + private static void AssertFailureOutcome(Func evaluate) + { + try + { + double[] output = evaluate(); + Assert.Contains(output, value => double.IsNaN(value) || double.IsInfinity(value)); + } + catch (ArgumentException) + { + } + catch (InvalidOperationException) + { + } + catch (NotSupportedException) + { + } + catch (FormatException) + { + } + } + + private static bool TryExpandKnownCoordinateOperationUrn(string operation, [NotNullWhen(true)] out string? expandedOperation) + { + expandedOperation = null; + if (string.IsNullOrWhiteSpace(operation) + || !operation.StartsWith(NkgCoordinateOperationUrnPrefix, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + string code = operation[NkgCoordinateOperationUrnPrefix.Length..]; + if (!NkgCoordinateOperationPipelineByCode.TryGetValue(code, out string? pipeline)) + { + return false; + } + + expandedOperation = pipeline; + return true; + } + + private static string RewriteLegacyGeoidGridOperation(string operation) + { + if (!TryParseOperationArguments(operation, out Dictionary args) + || args.ContainsKey("step") + || !args.TryGetValue("proj", out string? projCode) + || string.IsNullOrWhiteSpace(projCode) + || projCode.Equals("pipeline", StringComparison.OrdinalIgnoreCase) + || !args.TryGetValue("geoidgrids", out string? geoidGridToken) + || string.IsNullOrWhiteSpace(geoidGridToken)) + { + return operation; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + var projectionStepTokens = new List(tokens.Length); + string? axisToken = null; + foreach (string token in tokens) + { + if (token.StartsWith("+geoidgrids=", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (token.StartsWith("+axis=", StringComparison.OrdinalIgnoreCase)) + { + axisToken = token[6..]; + continue; + } + + projectionStepTokens.Add(token); + } + + if (projectionStepTokens.Count == 0) + { + return operation; + } + + var rewrittenTokens = new List(projectionStepTokens.Count + 9) + { + "+proj=pipeline", + "+step", + "+proj=vgridshift", + $"+grids={geoidGridToken}", + "+step", + }; + rewrittenTokens.AddRange(projectionStepTokens); + + if (!string.IsNullOrWhiteSpace(axisToken)) + { + rewrittenTokens.Add("+step"); + rewrittenTokens.Add("+proj=axisswap"); + rewrittenTokens.Add($"+axis={axisToken}"); + } + else if (!IsGeographicIdentityProjectionCode(projCode)) + { + // Keep the vertical component visible in the array-returning transform path. + rewrittenTokens.Add("+step"); + rewrittenTokens.Add("+proj=noop"); + } + + return string.Join(" ", rewrittenTokens); + } + + private static bool IsGeographicIdentityProjectionCode(string projCode) + { + return projCode.Equals("latlong", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("longlat", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("latlon", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("lonlat", StringComparison.OrdinalIgnoreCase); + } + + private static string NormalizeExplicitFalseOffsetsForRuntime(string operation) + { + if (!TryParseOperationArguments(operation, out Dictionary args) + || args.ContainsKey("step") + || (args.TryGetValue("proj", out string? projCode) + && projCode.Equals("pipeline", StringComparison.OrdinalIgnoreCase))) + { + return operation; + } + + double linearUnitFactor = ResolveProjectionLinearUnitFactor(args); + bool changed = false; + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < tokens.Length; i++) + { + changed |= TryRewriteExplicitFalseOffsetToken(tokens, i, "x_0", linearUnitFactor); + changed |= TryRewriteExplicitFalseOffsetToken(tokens, i, "y_0", linearUnitFactor); + } + + return changed ? string.Join(" ", tokens) : operation; + } + + private static bool TryRewriteExplicitFalseOffsetToken(string[] tokens, int index, string key, double linearUnitFactor) + { + string prefix = $"+{key}="; + if (!tokens[index].StartsWith(prefix, StringComparison.OrdinalIgnoreCase) + || !TryParseNumericToken(tokens[index][prefix.Length..], out double value)) + { + return false; + } + + tokens[index] = FormattableString.Invariant($"{prefix}{value / linearUnitFactor:R}"); + return true; + } + + private static string[] TokenizeOperation(string operation) + { + if (string.IsNullOrWhiteSpace(operation)) + { + return []; + } + + string sanitizedOperation = operation.Replace(';', ' '); + string[] rawTokens = sanitizedOperation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + if (rawTokens.Length == 0) + { + return []; + } + + var tokens = new List(rawTokens.Length); + for (int i = 0; i < rawTokens.Length; i++) + { + string token = rawTokens[i]; + if (token.Equals("=", StringComparison.Ordinal)) + { + if (tokens.Count == 0 || i + 1 >= rawTokens.Length) + { + continue; + } + + tokens[^1] = $"{tokens[^1]}={rawTokens[++i]}"; + continue; + } + + if (token.Length > 1 && token[0] == '=') + { + if (tokens.Count == 0) + { + continue; + } + + tokens[^1] = $"{tokens[^1]}{token}"; + continue; + } + + if (i + 2 < rawTokens.Length && rawTokens[i + 1].Equals("=", StringComparison.Ordinal)) + { + tokens.Add($"{token}={rawTokens[i + 2]}"); + i += 2; + continue; + } + + tokens.Add(token); + } + + return [.. tokens]; + } + + private static string ExpandLegacyInitDefinitions(string operation) + { + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length == 0) + { + return operation; + } + + var expandedTokens = new List(tokens.Length); + bool changed = false; + foreach (string token in tokens) + { + if (TryGetLegacyInitReplacement(token, out string[]? replacementTokens)) + { + expandedTokens.AddRange(Assert.IsType(replacementTokens)); + changed = true; + } + else + { + expandedTokens.Add(token); + } + } + + return changed ? string.Join(" ", expandedTokens) : operation; + } + + private static bool TryGetLegacyInitReplacement(string token, [NotNullWhen(true)] out string[]? replacementTokens) + { + replacementTokens = null; + if (!token.StartsWith("+init=", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + string initToken = token[6..]; + string[]? replacement = initToken.ToUpperInvariant() switch + { + "EPSG:26915" => ["+proj=utm", "+zone=15", "+datum=NAD83", "+units=m"], + "EPSG:3857" => ["+proj=webmerc", "+datum=WGS84", "+units=m"], + "EPSG:25832" => ["+proj=utm", "+zone=32", "+ellps=GRS80", "+units=m"], + "EPSG:25833" => ["+proj=utm", "+zone=33", "+ellps=GRS80", "+units=m"], + "NAD27:3901" => + [ + "+proj=lcc", + "+datum=NAD27", + "+lon_0=-81", + "+lat_1=34.96666666666667", + "+lat_2=33.76666666666667", + "+lat_0=33", + "+x_0=2000000", + "+y_0=0", + "+units=us-ft", + ], + _ => null, + }; + + if (replacement is null) + { + return false; + } + + replacementTokens = replacement; + return true; + } + + private static string ResolveKnownTestGridPaths(string operation) + { + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length == 0) + { + return operation; + } + + bool changed = false; + for (int i = 0; i < tokens.Length; i++) + { + if (!tokens[i].StartsWith("+grids=", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + string[] gridEntries = tokens[i][7..].Split(CommaSeparator, StringSplitOptions.RemoveEmptyEntries); + bool tokenChanged = false; + for (int j = 0; j < gridEntries.Length; j++) + { + string gridEntry = gridEntries[j].Trim(); + bool isOptional = gridEntry.Length > 0 && gridEntry[0] == '@'; + string gridToken = isOptional ? gridEntry[1..] : gridEntry; + if (!TryResolveKnownTestGridToken(gridToken, out string? resolvedPath)) + { + continue; + } + + gridEntries[j] = isOptional + ? $"@{resolvedPath}" + : resolvedPath; + tokenChanged = true; + changed = true; + } + + if (tokenChanged) + { + tokens[i] = $"+grids={string.Join(",", gridEntries)}"; + } + } + + return changed ? string.Join(" ", tokens) : operation; + } + + private static bool TryResolveKnownTestGridToken(string gridToken, [NotNullWhen(true)] out string? resolvedPath) + { + resolvedPath = null; + if (string.IsNullOrWhiteSpace(gridToken)) + { + return false; + } + + string normalizedToken = gridToken.Replace('/', '\\'); + bool isRelativeTestToken = normalizedToken.StartsWith("tests\\", StringComparison.OrdinalIgnoreCase); + if (!isRelativeTestToken && normalizedToken.Contains('\\', StringComparison.Ordinal)) + { + return false; + } + + string platformNormalizedToken = normalizedToken.Replace('\\', Path.DirectorySeparatorChar); + string fileName = Path.GetFileName(platformNormalizedToken); + if (string.IsNullOrWhiteSpace(fileName)) + { + return false; + } + + string? fixtureGridPath = FindRepositoryFile("test", "ProjNet.Tests", "Fixtures", "grids", fileName); + if (fixtureGridPath is not null) + { + resolvedPath = fixtureGridPath; + return true; + } + + string? projDataGridPath = isRelativeTestToken + ? FindRepositoryFile("spec", "PROJ", "data", "tests", fileName) + : FindRepositoryFile("spec", "PROJ", "data", fileName); + if (projDataGridPath is not null) + { + resolvedPath = projDataGridPath; + return true; + } + + return false; + } + + private static string? FindRepositoryFile(params string[] relativeSegments) + { + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = current.FullName; + for (int i = 0; i < relativeSegments.Length; i++) + { + candidate = Path.Combine(candidate, relativeSegments[i]); + } + + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + return null; + } + + private static bool TryWrapStandaloneStackTransferOperation(string operation, out string? wrappedOperation) + { + wrappedOperation = null; + if (operation.Contains("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!TryParseOperationArguments(operation, out Dictionary args) + || !args.TryGetValue("proj", out string? projCode) + || (!projCode.Equals("push", StringComparison.OrdinalIgnoreCase) + && !projCode.Equals("pop", StringComparison.OrdinalIgnoreCase))) + { + return false; + } + + wrappedOperation = $"+proj=pipeline +step {operation}"; + return true; + } + + private static bool TrySplitPipelineSteps(string operation, out IReadOnlyList steps) + { + var parsedSteps = new List(); + var currentStepTokens = new List(); + bool inPipeline = false; + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string token in tokens) + { + string normalized = token.StartsWith('+') + ? token[1..] + : token; + + if (normalized.Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + inPipeline = true; + continue; + } + + if (normalized.Equals("step", StringComparison.OrdinalIgnoreCase)) + { + inPipeline = true; + if (currentStepTokens.Count > 0) + { + parsedSteps.Add(string.Join(" ", currentStepTokens)); + currentStepTokens.Clear(); + } + + continue; + } + + if (!inPipeline) + { + continue; + } + + currentStepTokens.Add(token); + } + + if (currentStepTokens.Count > 0) + { + parsedSteps.Add(string.Join(" ", currentStepTokens)); + } + + steps = parsedSteps; + return parsedSteps.Count > 0; + } + + private static bool TryIsRuntimeOperationSupported(string operation) + { + if (operation is null) + { + return false; + } + + string normalizedOperation = NormalizeOperationForRuntime(operation); + if (!TryExtractProjCode(normalizedOperation, out string? projCode) || projCode is null) + { + return false; + } + + if (projCode.Equals("pipeline", StringComparison.OrdinalIgnoreCase)) + { + if (!TrySplitPipelineSteps(normalizedOperation, out IReadOnlyList steps)) + { + return false; + } + + for (int i = 0; i < steps.Count; i++) + { + if (!TryParseOperationArguments(steps[i], out Dictionary stepArgs)) + { + return false; + } + + if (!stepArgs.TryGetValue("proj", out string? stepProjCode)) + { + return false; + } + + if (!ProjectionClassByProjCode.ContainsKey(stepProjCode) && !ConversionProjCodes.Contains(stepProjCode)) + { + return false; + } + } + + return true; + } + + return ProjectionClassByProjCode.ContainsKey(projCode) || ConversionProjCodes.Contains(projCode); + } + + private static bool TryCreateGeographicCoordinateSystem( + Dictionary args, + out GeographicCoordinateSystem? gcs, + out string? skipReason) + { + gcs = null; + skipReason = null; + + if (!TryResolveEllipsoid(args, out Ellipsoid? ellipsoid, out skipReason)) + { + return false; + } + + PrimeMeridian primeMeridian = PrimeMeridian.Greenwich; + if (args.TryGetValue("pm", out string? pmValue)) + { + primeMeridian = ResolvePrimeMeridian(pmValue); + } + + Ellipsoid geographicEllipsoid = Assert.IsType(ellipsoid); + + Wgs84ConversionInfo? toWgs84 = null; + if (args.TryGetValue("towgs84", out string? towgs84Value) && !string.IsNullOrEmpty(towgs84Value)) + { + string[] parts = towgs84Value.Split(','); + if (parts.Length >= 3 + && double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out double dx) + && double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double dy) + && double.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out double dz)) + { + double rx = 0, ry = 0, rz = 0, ppm = 0; + if (parts.Length >= 7) + { + double.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out rx); + double.TryParse(parts[4], NumberStyles.Float, CultureInfo.InvariantCulture, out ry); + double.TryParse(parts[5], NumberStyles.Float, CultureInfo.InvariantCulture, out rz); + double.TryParse(parts[6], NumberStyles.Float, CultureInfo.InvariantCulture, out ppm); + } + + toWgs84 = new Wgs84ConversionInfo(dx, dy, dz, rx, ry, rz, ppm); + } + } + else if (args.TryGetValue("datum", out string? datumName) && !string.IsNullOrEmpty(datumName)) + { + TryResolveDatum(datumName, out toWgs84); + } + + HorizontalDatum datum = CoordinateSystemFactory.CreateHorizontalDatum("GIE datum", DatumType.HD_Geocentric, geographicEllipsoid, toWgs84); + gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "GIE geographic", + AngularUnit.Degrees, + datum, + primeMeridian, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + return true; + } + + /// + /// Attempts to resolve a PROJ +datum= token to its corresponding parameters. + /// + /// The datum name from the +datum= token. + /// + /// When this method returns , contains the resolved WGS 84 conversion parameters; + /// otherwise . + /// + /// if the datum was resolved; otherwise . + private static bool TryResolveDatum(string datumName, out Wgs84ConversionInfo? conversionInfo) + { + conversionInfo = null; + if (string.IsNullOrWhiteSpace(datumName)) + { + return false; + } + + // Well-known PROJ datum definitions mapped to their Bursa-Wolf (towgs84) parameters. + if (datumName.Equals("potsdam", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(598.1, 73.7, 418.2, 0.202, 0.045, -2.455, 6.7); + return true; + } + + if (datumName.Equals("NAD27", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(-8, 160, 176, 0, 0, 0, 0); + return true; + } + + if (datumName.Equals("NAD83", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(0, 0, 0, 0, 0, 0, 0); + return true; + } + + if (datumName.Equals("nzgd49", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(59.47, -5.04, 187.44, 0.47, -0.1, 1.024, -4.5993); + return true; + } + + if (datumName.Equals("ire65", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(482.530, -130.596, 564.557, -1.042, -0.214, -0.631, 8.15); + return true; + } + + if (datumName.Equals("GGRS87", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(-199.87, 74.79, 246.02, 0, 0, 0, 0); + return true; + } + + if (datumName.Equals("OSGB36", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(446.448, -125.157, 542.060, 0.1502, 0.2470, 0.8421, -20.4894); + return true; + } + + if (datumName.Equals("WGS84", StringComparison.OrdinalIgnoreCase)) + { + // WGS 84 is the target datum, so the shift is zero. + conversionInfo = new Wgs84ConversionInfo(0, 0, 0, 0, 0, 0, 0); + return true; + } + + return false; + } + + private static bool TryResolveEllipsoid(Dictionary args, out Ellipsoid? ellipsoid, out string? skipReason) + { + skipReason = null; + if (TryGetDouble(args, "r", out double sphereRadius) && sphereRadius > 0d) + { + ellipsoid = CoordinateSystemFactory.CreateEllipsoid("GIE sphere", sphereRadius, sphereRadius, LinearUnit.Metre); + return true; + } + + if (TryGetDouble(args, "a", out double semiMajor) && semiMajor > 0d) + { + double resolvedSemiMajor = semiMajor; + double resolvedSemiMinor = semiMajor; + if (!ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref resolvedSemiMajor, ref resolvedSemiMinor, out skipReason)) + { + ellipsoid = null; + return false; + } + + ellipsoid = CoordinateSystemFactory.CreateEllipsoid("GIE ellipsoid", resolvedSemiMajor, resolvedSemiMinor, LinearUnit.Metre); + return true; + } + + if (args.TryGetValue("ellps", out string? ellps) + && !string.IsNullOrWhiteSpace(ellps) + && TryResolveKnownEllipsoidToken(ellps, out Ellipsoid? knownEllipsoid)) + { + return TryCreateEllipsoidWithExplicitShapeOverrides(args, knownEllipsoid, out ellipsoid, out skipReason); + } + + if (args.TryGetValue("datum", out string? datumToken) + && !string.IsNullOrWhiteSpace(datumToken) + && ProjEllipsoidResolver.TryResolveKnownEllipsoid(datumToken, allowClarke1880Ign: true, allowBessel: true, out double datumSemiMajor, out double datumSemiMinor)) + { + Ellipsoid datumEllipsoid = CoordinateSystemFactory.CreateEllipsoid($"GIE datum ellipsoid ({datumToken})", datumSemiMajor, datumSemiMinor, LinearUnit.Metre); + return TryCreateEllipsoidWithExplicitShapeOverrides(args, datumEllipsoid, out ellipsoid, out skipReason); + } + + return TryCreateEllipsoidWithExplicitShapeOverrides(args, Ellipsoid.WGS84, out ellipsoid, out skipReason); + } + + private static bool TryCreateEllipsoidWithExplicitShapeOverrides( + Dictionary args, + Ellipsoid baseEllipsoid, + out Ellipsoid? ellipsoid, + out string? skipReason) + { + skipReason = null; + if (!HasExplicitShapeOverride(args)) + { + ellipsoid = baseEllipsoid; + return true; + } + + double semiMajor = baseEllipsoid.SemiMajorAxis; + double semiMinor = baseEllipsoid.SemiMinorAxis; + if (!ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref semiMajor, ref semiMinor, out skipReason)) + { + ellipsoid = null; + return false; + } + + ellipsoid = CoordinateSystemFactory.CreateEllipsoid(baseEllipsoid.Name, semiMajor, semiMinor, LinearUnit.Metre); + return true; + } + + private static bool HasExplicitShapeOverride(Dictionary args) + { + return args.ContainsKey("b") + || args.ContainsKey("rf") + || args.ContainsKey("f") + || args.ContainsKey("es") + || args.ContainsKey("e") + || args.ContainsKey("R_A") + || args.ContainsKey("R_V") + || args.ContainsKey("R_a") + || args.ContainsKey("R_g") + || args.ContainsKey("R_h") + || args.ContainsKey("R_lat_a") + || args.ContainsKey("R_lat_g") + || args.ContainsKey("R_C"); + } + + private static bool TryResolveKnownEllipsoidToken(string ellps, [NotNullWhen(true)] out Ellipsoid? ellipsoid) + { + if (ellps.Equals("wgs84", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = Ellipsoid.WGS84; + return true; + } + + if (ellps.Equals("grs80", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = Ellipsoid.GRS80; + return true; + } + + if (ellps.Equals("clrk66", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = Ellipsoid.Clarke1866; + return true; + } + + if (ellps.Equals("clrk80", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1880 (RGS)", 6378249.145, 293.4663, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("intl", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = Ellipsoid.International1924; + return true; + } + + if (ellps.Equals("sphere", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = Ellipsoid.Sphere; + return true; + } + + if (ellps.Equals("bessel", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Bessel 1841", 6377397.155, 299.1528128, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("airy", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Airy 1830", 6377563.396, 299.3249646, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("krass", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Krassowsky 1940", 6378245.0, 298.3, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("GRS67", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("GRS 1967", 6378160.0, 298.247167427, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("evrst30", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Everest 1830", 6377276.345, 300.8017, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("evrst69", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Everest 1969", 6377295.664, 300.8017255, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("aust_SA", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Australian National", 6378160.0, 298.25, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("bess_nam", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Bessel Namibia", 6377483.865, 299.1528128, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("clrk80ign", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1880 (IGN)", 6378249.2, 293.4660212936269, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("mod_airy", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Modified Airy", 6377340.189, 299.3249646, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("andrae", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Andrae 1876", 6377104.43, 300.0, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("danish", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Danish 1876", 6377019.2563, 300.0, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("helmert", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Helmert 1906", 6378200.0, 298.3, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("fschr60", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Fischer 1960", 6378166.0, 298.3, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("fschr68", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Fischer 1968", 6378150.0, 298.3, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("fschr60m", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Fischer 1960 Modified", 6378155.0, 298.3, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("hough", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Hough", 6378270.0, 297.0, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("kaula", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Kaula 1961", 6378163.0, 298.24, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("lerch", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Lerch 1979", 6378139.0, 298.257, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("mprts", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Maupertuis 1738", 6397300.0, 191.0, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("plessis", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Plessis 1817", 6376523.0, 308.64, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("SEasia", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Southeast Asia", 6378155.0, 298.3, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("walbeck", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Walbeck", 6376896.0, 302.78, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("NWL9D", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("NWL-9D", 6378145.0, 298.25, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("IAU76", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("IAU 1976", 6378140.0, 298.257, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("everest", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Everest 1830", 6377276.345, 300.8017, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("evrst48", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Everest 1948", 6377304.063, 300.8017, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("evrst56", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Everest 1956", 6377301.243, 300.8017, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("clrk58", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Clarke 1858", 6378293.645208759, 294.2606763692654, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("engelis", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Engelis 1985", 6378136.05, 298.2566, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("CPM", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Comm. des Poids et Mesures 1799", 6375738.7, 334.29, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("delmbr", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Delambre 1810", 6376428.0, 311.5, LinearUnit.Metre); + return true; + } + + if (ellps.Equals("fschr68m", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = CoordinateSystemFactory.CreateFlattenedSphere("Fischer 1968 Modified", 6378155.0, 298.3, LinearUnit.Metre); + return true; + } + + ellipsoid = null; + return false; + } + + private static bool TryBuildProjectionParameters(Dictionary args, out List parameters) + { + parameters = + [ + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + ]; + + double linearUnitFactor = ResolveProjectionLinearUnitFactor(args); + + if (TryGetDouble(args, "lat_0", out double lat0)) + { + ReplaceParameter(parameters, "latitude_of_origin", lat0); + } + + if (TryGetDouble(args, "lon_0", out double lon0)) + { + ReplaceParameter(parameters, "central_meridian", lon0); + } + + if (TryGetDouble(args, "k_0", out double k0)) + { + ReplaceParameter(parameters, "scale_factor", k0); + } + else if (TryGetDouble(args, "k", out double k)) + { + ReplaceParameter(parameters, "scale_factor", k); + } + + if (TryGetDouble(args, "x_0", out double x0)) + { + ReplaceParameter(parameters, "false_easting", x0 / linearUnitFactor); + } + + if (TryGetDouble(args, "y_0", out double y0)) + { + ReplaceParameter(parameters, "false_northing", y0 / linearUnitFactor); + } + + AddOptionalParameter(parameters, args, "lat_1", "standard_parallel_1"); + AddOptionalParameter(parameters, args, "lat_2", "standard_parallel_2"); + AddOptionalParameter(parameters, args, "lat_1", "lat_1"); + AddOptionalParameter(parameters, args, "lat_2", "lat_2"); + AddOptionalParameter(parameters, args, "lat_ts", "lat_ts"); + AddOptionalParameter(parameters, args, "lat_ts", "latitude_true_scale"); + AddOptionalParameter(parameters, args, "lon_1", "lon_1"); + AddOptionalParameter(parameters, args, "lon_2", "lon_2"); + AddOptionalParameter(parameters, args, "lat_3", "lat_3"); + AddOptionalParameter(parameters, args, "lon_3", "lon_3"); + AddOptionalParameter(parameters, args, "lat_b", "lat_b"); + AddOptionalParameter(parameters, args, "alpha", "azimuth"); + AddOptionalParameter(parameters, args, "gamma", "rectified_grid_angle"); + AddOptionalParameter(parameters, args, "azi", "azi"); + AddOptionalParameter(parameters, args, "tilt", "tilt"); + AddOptionalParameter(parameters, args, "lonc", "longitude_of_center"); + AddOptionalParameter(parameters, args, "h", "h"); + AddOptionalParameter(parameters, args, "satellite_height", "h"); + if (args.TryGetValue("shape", out string? shapeToken)) + { + if (!TryGetPeirceShapeCode(shapeToken, out double shapeCode)) + { + return false; + } + + ReplaceParameter(parameters, "shape", shapeCode); + } + + AddOptionalParameter(parameters, args, "scrollx", "scrollx"); + AddOptionalParameter(parameters, args, "scrolly", "scrolly"); + if (args.TryGetValue("UVtoST", out string? uvToStMode) + || args.TryGetValue("uvtost", out uvToStMode) + || args.TryGetValue("uv_to_st", out uvToStMode)) + { + double uvToStCode; + if (uvToStMode.Equals("linear", StringComparison.OrdinalIgnoreCase)) + { + uvToStCode = 0d; + } + else if (uvToStMode.Equals("quadratic", StringComparison.OrdinalIgnoreCase)) + { + uvToStCode = 1d; + } + else if (uvToStMode.Equals("tangent", StringComparison.OrdinalIgnoreCase)) + { + uvToStCode = 2d; + } + else if (uvToStMode.Equals("none", StringComparison.OrdinalIgnoreCase)) + { + uvToStCode = 3d; + } + else if (!double.TryParse(uvToStMode, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out uvToStCode)) + { + uvToStCode = double.NaN; + } + + ReplaceParameter(parameters, "uv_to_st", uvToStCode); + } + + AddOptionalParameter(parameters, args, "h_0", "h_0"); + AddOptionalParameter(parameters, args, "m", "m"); + AddOptionalParameter(parameters, args, "n", "n"); + AddOptionalParameter(parameters, args, "q", "q"); + AddOptionalParameter(parameters, args, "W", "W"); + AddOptionalParameter(parameters, args, "theta", "theta"); + AddOptionalParameter(parameters, args, "inc_angle", "inc_angle"); + AddOptionalParameter(parameters, args, "ps_rev", "ps_rev"); + AddOptionalParameter(parameters, args, "asc_lon", "asc_lon"); + AddOptionalParameter(parameters, args, "path", "path"); + AddOptionalParameter(parameters, args, "lsat", "lsat"); + AddOptionalParameter(parameters, args, "rot", "rot"); + AddOptionalParameter(parameters, args, "lon_1", "longitude1"); + AddOptionalParameter(parameters, args, "lat_1", "latitude1"); + AddOptionalParameter(parameters, args, "lon_2", "longitude2"); + AddOptionalParameter(parameters, args, "lat_2", "latitude2"); + if (args.TryGetValue("sweep", out string? sweepAxis)) + { + double sweepX = sweepAxis.Equals("x", StringComparison.OrdinalIgnoreCase) ? 1d : 0d; + ReplaceParameter(parameters, "sweep_x", sweepX); + } + + if (args.TryGetValue("proj", out string? projectionCode)) + { + if (projectionCode.Equals("cass", StringComparison.OrdinalIgnoreCase) && args.ContainsKey("hyperbolic")) + { + ReplaceParameter(parameters, "hyperbolic", 1d); + } + + if (projectionCode.Equals("airocean", StringComparison.OrdinalIgnoreCase) && args.TryGetValue("orient", out string? airoceanOrientation)) + { + if (!TryGetAiroceanOrientationCode(airoceanOrientation, out double orientationCode)) + { + return false; + } + + ReplaceParameter(parameters, "airocean_orient", orientationCode); + } + + if (projectionCode.Equals("isea", StringComparison.OrdinalIgnoreCase)) + { + if (args.TryGetValue("orient", out string? iseaOrientation)) + { + if (!TryGetIseaOrientCode(iseaOrientation, out double orientCode)) + { + return false; + } + + ReplaceParameter(parameters, "isea_orient", orientCode); + } + + if (args.TryGetValue("mode", out string? iseaMode)) + { + if (!TryGetIseaModeCode(iseaMode, out double modeCode)) + { + return false; + } + + ReplaceParameter(parameters, "isea_mode", modeCode); + } + + AddOptionalParameter(parameters, args, "resolution", "isea_resolution"); + AddOptionalParameter(parameters, args, "aperture", "isea_aperture"); + AddOptionalParameter(parameters, args, "azi", "isea_azimuth"); + } + + if (projectionCode.Equals("leac", StringComparison.OrdinalIgnoreCase) && args.ContainsKey("south")) + { + ReplaceParameter(parameters, "south", 1d); + } + + if (projectionCode.Equals("aeqd", StringComparison.OrdinalIgnoreCase) && args.ContainsKey("guam")) + { + ReplaceParameter(parameters, "guam", 1d); + } + + if (projectionCode.Equals("ups", StringComparison.OrdinalIgnoreCase) && args.ContainsKey("south")) + { + ReplaceParameter(parameters, "south", 1d); + } + + if ((projectionCode.Equals("krovak", StringComparison.OrdinalIgnoreCase) + || projectionCode.Equals("mod_krovak", StringComparison.OrdinalIgnoreCase)) + && args.ContainsKey("czech")) + { + ReplaceParameter(parameters, "czech", 1d); + } + } + + if (args.ContainsKey("no_cut")) + { + ReplaceParameter(parameters, "no_cut", 1d); + } + + if (args.ContainsKey("no_rot")) + { + ReplaceParameter(parameters, "no_rot", 1d); + } + + if (args.ContainsKey("ns") || args.ContainsKey("noskew")) + { + ReplaceParameter(parameters, "ns", 1d); + } + + if (args.TryGetValue("proj", out string? projCode) && projCode.Equals("utm", StringComparison.OrdinalIgnoreCase)) + { + if (!TryGetZoneCentralMeridian(args, out double utmCentralMeridian)) + { + return false; + } + + double unitFactor = 1d; + if (TryGetDouble(args, "to_meter", out double toMeter) && toMeter > 0d) + { + unitFactor = toMeter; + } + else if (args.TryGetValue("units", out string? unitsToken)) + { + unitFactor = ResolveLinearUnit(unitsToken).MetersPerUnit; + } + + ReplaceParameter(parameters, "latitude_of_origin", 0d); + ReplaceParameter(parameters, "central_meridian", utmCentralMeridian); + ReplaceParameter(parameters, "scale_factor", 0.9996d); + ReplaceParameter(parameters, "false_easting", 500000d / unitFactor); + ReplaceParameter(parameters, "false_northing", (args.ContainsKey("south") ? 10000000d : 0d) / unitFactor); + } + + return true; + } + + private static double ResolveProjectionLinearUnitFactor(Dictionary args) + { + if (TryGetDouble(args, "to_meter", out double toMeter) && toMeter > 0d) + { + return toMeter; + } + + return args.TryGetValue("units", out string? unitsToken) + ? ResolveLinearUnit(unitsToken).MetersPerUnit + : 1d; + } + + private static bool TryGetZoneCentralMeridian(Dictionary args, out double centralMeridian) + { + centralMeridian = 0d; + if (!args.TryGetValue("zone", out string? zoneToken) || string.IsNullOrWhiteSpace(zoneToken)) + { + return false; + } + + string digits = zoneToken.Trim(); + int i = 0; + while (i < digits.Length && char.IsDigit(digits[i])) + { + i++; + } + + if (i == 0 || !int.TryParse(digits.AsSpan(0, i), NumberStyles.Integer, CultureInfo.InvariantCulture, out int zone)) + { + return false; + } + + centralMeridian = (zone * 6d) - 183d; + return true; + } + + private static bool TryParseOperationArguments(string operation, out Dictionary args) + { + args = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (operation is null) + { + return false; + } + + string[] tokens = TokenizeOperation(operation); + foreach (string token in tokens) + { + if (token.Length == 0) + { + continue; + } + + string body = token[0] == '+' ? token[1..] : token; + int index = body.IndexOf('=', StringComparison.Ordinal); + if (index < 0) + { + args[body] = body; + } + else + { + string key = body[..index]; + string value = body[(index + 1)..]; + args[key] = value; + } + } + + return args.Count > 0; + } + + private static bool TryGetKnownUnsupportedOperationSkipReason(string operation, out string? skipReason) + { + skipReason = null; + if (TryExpandKnownCoordinateOperationUrn(operation, out _)) + { + return false; + } + + if (operation.StartsWith("urn:ogc:def:coordinateOperation:", StringComparison.OrdinalIgnoreCase)) + { + skipReason = "URN-based coordinate operations are not mapped by the current builtins harness."; + return true; + } + + return false; + } + + private static string CreateNkgOperationPipeline(string steps) + { + return NormalizeOperationWhitespace($"+proj=pipeline +ellps=GRS80 {steps}"); + } + + private static string AppendNkgOperationPipeline(string prefixPipeline, string steps) + { + return NormalizeOperationWhitespace($"{prefixPipeline} {steps}"); + } + + private static string NormalizeOperationWhitespace(string operation) + { + return string.Join(" ", operation.Split(PipelineWhitespaceSeparators, StringSplitOptions.RemoveEmptyEntries)); + } + + private static bool TryExtractProjCode(string operation, out string? projCode) + { + projCode = null; + return TryParseOperationArguments(operation, out Dictionary args) && args.TryGetValue("proj", out projCode); + } + + private static bool ContainsUnsupportedRuntimeTokens(Dictionary args) + { + if (args.ContainsKey("step")) + { + return true; + } + + if (args.ContainsKey("hyperbolic") + && (!args.TryGetValue("proj", out string? hyperbolicProjCode) + || !hyperbolicProjCode.Equals("cass", StringComparison.OrdinalIgnoreCase))) + { + return true; + } + + if (args.ContainsKey("alpha")) + { + if (!args.TryGetValue("proj", out string? projCode) + || (!projCode.Equals("ocea", StringComparison.OrdinalIgnoreCase) + && !projCode.Equals("omerc", StringComparison.OrdinalIgnoreCase))) + { + return true; + } + } + + if (args.ContainsKey("north_square") || args.ContainsKey("south_square")) + { + if (!args.TryGetValue("proj", out string? projectionCodeForSquares) + || !projectionCodeForSquares.Equals("rhealpix", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static LinearUnit ResolveLinearUnit(string unitToken) => unitToken.ToUpperInvariant() switch + { + "M" => LinearUnit.Metre, + "FT" => new LinearUnit(0.3048, "International Foot", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "US-FT" => new LinearUnit(0.3048006096012192, "US Survey Foot", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "KM" => new LinearUnit(1000.0, "Kilometer", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "MM" => new LinearUnit(0.001, "Millimeter", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "CM" => new LinearUnit(0.01, "Centimeter", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "YD" => new LinearUnit(0.9144, "International Yard", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "FATH" => new LinearUnit(1.8288, "International Fathom", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "KMI" => new LinearUnit(1852.0, "International Nautical Mile", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "US-CH" => new LinearUnit(20.11684023368047, "US Survey Chain", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "US-MI" => new LinearUnit(1609.347218694437, "US Survey Mile", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "IND-FT" => new LinearUnit(0.30479841, "Indian Foot", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "IND-YD" => new LinearUnit(0.91439523, "Indian Yard", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "LINK" => new LinearUnit(0.201168, "International Link", string.Empty, -1, string.Empty, string.Empty, string.Empty), + "CH" => new LinearUnit(20.1168, "International Chain", string.Empty, -1, string.Empty, string.Empty, string.Empty), + _ => LinearUnit.Metre, + }; + + private static PrimeMeridian ResolvePrimeMeridian(string pmValue) + { + if (double.TryParse(pmValue, NumberStyles.Float, CultureInfo.InvariantCulture, out double longitude)) + { + return new PrimeMeridian(longitude, AngularUnit.Degrees, "GIE pm", string.Empty, -1, string.Empty, string.Empty, string.Empty); + } + + return pmValue.ToUpperInvariant() switch + { + "GREENWICH" => PrimeMeridian.Greenwich, + "LISBON" => PrimeMeridian.Lisbon, + "PARIS" => PrimeMeridian.Paris, + "BOGOTA" => PrimeMeridian.Bogota, + "MADRID" => PrimeMeridian.Madrid, + "ROME" => PrimeMeridian.Rome, + "BERN" => PrimeMeridian.Bern, + "JAKARTA" => PrimeMeridian.Jakarta, + "FERRO" => PrimeMeridian.Ferro, + "BRUSSELS" => PrimeMeridian.Brussels, + "STOCKHOLM" => PrimeMeridian.Stockholm, + "ATHENS" => PrimeMeridian.Athens, + "OSLO" => PrimeMeridian.Oslo, + _ => PrimeMeridian.Greenwich, + }; + } + + private static bool TryGetDouble(Dictionary args, string key, out double value) + { + value = 0d; + if (!args.TryGetValue(key, out string? raw) || string.IsNullOrWhiteSpace(raw)) + { + return false; + } + + string token = raw.Trim(); + bool radiansSuffix = token.Length > 0 && (token[^1] == 'r' || token[^1] == 'R'); + if (radiansSuffix) + { + token = token[..^1]; + } + + if (TryParseNumericToken(token, out value)) + { + if (radiansSuffix) + { + value *= 180d / Math.PI; + } + + return true; + } + + if (TryParseDmsToken(token, out value)) + { + return true; + } + + return false; + } + + private static bool TryParseNumericToken(string token, out double value) + { + value = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + if (double.TryParse(token, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value)) + { + return true; + } + + // Support simple ratio expressions used in GIE fixtures, e.g. "2.0/0.2". + int slashIndex = token.IndexOf('/', StringComparison.Ordinal); + if (slashIndex <= 0 || slashIndex >= token.Length - 1) + { + return false; + } + + string numeratorToken = token[..slashIndex].Trim(); + string denominatorToken = token[(slashIndex + 1)..].Trim(); + if (!double.TryParse(numeratorToken, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double numerator)) + { + return false; + } + + if (!double.TryParse(denominatorToken, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double denominator)) + { + return false; + } + + if (Math.Abs(denominator) <= 0d) + { + return false; + } + + value = numerator / denominator; + return true; + } + + private static bool TryParseDmsToken(string token, out double value) + { + value = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string text = token.Trim(); + int sign = 1; + + char last = text[text.Length - 1]; + if (last == 'W' || last == 'w' || last == 'S' || last == 's') + { + sign = -1; + text = text[..^1]; + } + else if (last == 'E' || last == 'e' || last == 'N' || last == 'n') + { + text = text[..^1]; + } + + if (text.StartsWith('-')) + { + sign *= -1; + text = text[1..]; + } + else if (text.StartsWith('+')) + { + text = text[1..]; + } + + int dIndex = text.IndexOf('d', StringComparison.Ordinal); + if (dIndex < 0) + { + dIndex = text.IndexOf('D', StringComparison.Ordinal); + } + + int mIndex = text.IndexOf('\'', StringComparison.Ordinal); + if (dIndex <= 0 || mIndex <= dIndex) + { + return false; + } + + string degreesToken = text[..dIndex]; + string minutesToken = text.Substring(dIndex + 1, mIndex - dIndex - 1); + if (!double.TryParse(degreesToken, NumberStyles.Float, CultureInfo.InvariantCulture, out double degrees)) + { + return false; + } + + if (!double.TryParse(minutesToken, NumberStyles.Float, CultureInfo.InvariantCulture, out double minutes)) + { + return false; + } + + double seconds = 0d; + int secondsMarker = text.IndexOf('"', StringComparison.Ordinal); + if (secondsMarker > mIndex + 1) + { + string secondsToken = text.Substring(mIndex + 1, secondsMarker - mIndex - 1); + if (!double.TryParse(secondsToken, NumberStyles.Float, CultureInfo.InvariantCulture, out seconds)) + { + return false; + } + } + + value = sign * (degrees + (minutes / 60d) + (seconds / 3600d)); + return true; + } + + private static bool TryGetAiroceanOrientationCode(string token, out double orientationCode) + { + orientationCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (double.TryParse(normalized, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out orientationCode)) + { + return orientationCode == 0d || orientationCode == 1d; + } + + orientationCode = normalized.ToUpperInvariant() switch + { + "VERTICAL" => 0d, + "HORIZONTAL" => 1d, + _ => double.NaN, + }; + + return !double.IsNaN(orientationCode); + } + + private static bool TryGetIseaOrientCode(string token, out double orientCode) + { + orientCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (double.TryParse(normalized, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out orientCode)) + { + return orientCode == 0d || orientCode == 1d; + } + + orientCode = normalized.ToUpperInvariant() switch + { + "ISEA" => 0d, + "POLE" => 1d, + _ => double.NaN, + }; + + return !double.IsNaN(orientCode); + } + + private static bool TryGetIseaModeCode(string token, out double modeCode) + { + modeCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (double.TryParse(normalized, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out modeCode)) + { + return modeCode >= 0d && modeCode <= 3d; + } + + modeCode = normalized.ToUpperInvariant() switch + { + "PLANE" => 0d, + "DI" => 1d, + "DD" => 2d, + "HEX" => 3d, + _ => double.NaN, + }; + + return !double.IsNaN(modeCode); + } + + private static bool TryGetPeirceShapeCode(string token, out double shapeCode) + { + shapeCode = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string normalized = token.Trim(); + if (double.TryParse(normalized, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out shapeCode)) + { + return true; + } + + shapeCode = normalized.ToUpperInvariant() switch + { + "SQUARE" => 0d, + "DIAMOND" => 1d, + "NHEMISPHERE" => 2d, + "SHEMISPHERE" => 3d, + "HORIZONTAL" => 4d, + "VERTICAL" => 5d, + _ => double.NaN, + }; + + return !double.IsNaN(shapeCode); + } + + private static void AddOptionalParameter(List parameters, Dictionary args, string sourceName, string targetName) + { + if (TryGetDouble(args, sourceName, out double value)) + { + ReplaceParameter(parameters, targetName, value); + } + } + + private static void ReplaceParameter(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } + + private static string FindGiePath(string fileName) + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "gie", fileName); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "gie", fileName); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + return default!; + } + + private static double ToNumericTolerance(double value, string unit) + { + if (unit is null) + { + return value; + } + + if (unit.Equals("mm", StringComparison.OrdinalIgnoreCase)) + { + return value / 1000d; + } + + if (unit.Equals("cm", StringComparison.OrdinalIgnoreCase)) + { + return value / 100d; + } + + return unit.Equals("nm", StringComparison.OrdinalIgnoreCase) ? value * 1e-9d : value; + } + + private static bool HasGeographicDatumShift(string operation) + { + bool hasDatumInfo = operation.Contains("towgs84", StringComparison.OrdinalIgnoreCase) + || operation.Contains("datum=", StringComparison.Ordinal); + if (!hasDatumInfo) + { + return false; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < tokens.Length; i++) + { + string token = tokens[i].StartsWith('+') ? tokens[i][1..] : tokens[i]; + if (token.StartsWith("proj=", StringComparison.OrdinalIgnoreCase)) + { + string projCode = token["proj=".Length..]; + return projCode.Equals("latlong", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("longlat", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("latlon", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("lonlat", StringComparison.OrdinalIgnoreCase); + } + } + + return false; + } + + private static bool IsLikelyGeographicCoordinatePair(double[] coordinates) + { + if (coordinates is null || coordinates.Length < 2) + { + return false; + } + + double first = Math.Abs(coordinates[0]); + double second = Math.Abs(coordinates[1]); + + bool lonLatRange = first <= 180d && second <= 90d; + bool latLonRange = first <= 90d && second <= 180d; + return lonLatRange || latLonRange; + } + + private static double GetComparisonDelta(double[] actual, double[] expected, int axis) + { + double delta = Math.Abs(actual[axis] - expected[axis]); + if (axis != 0 || !IsLikelyGeographicCoordinatePair(actual) || !IsLikelyGeographicCoordinatePair(expected)) + { + return delta; + } + + double normalizedActual = TransformationMath.NormalizeLongitudeDegrees(actual[axis]); + double normalizedExpected = TransformationMath.NormalizeLongitudeDegrees(expected[axis]); + double normalizedDelta = Math.Abs(normalizedActual - normalizedExpected); + return Math.Min(normalizedDelta, 360d - normalizedDelta); + } +} diff --git a/test/ProjNet.Tests/Integration/GieParserTests.cs b/test/ProjNet.Tests/Integration/GieParserTests.cs new file mode 100644 index 00000000..6967ce32 --- /dev/null +++ b/test/ProjNet.Tests/Integration/GieParserTests.cs @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.IO; +using Xunit; + +/// +/// Contains unit tests for the GieParser class. +/// +public class GieParserTests +{ + /// + /// Verifies that parsing content with forward and inverse direction pairs produces correctly populated cases. + /// + [Fact] + public void ParseWithForwardAndInversePairsProducesExpectedCases() + { + const string content = + """ + + operation +proj=tmerc +lat_0=49 +lon_0=-2 +k_0=0.9996 +ellps=WGS84 + tolerance 0.03 m + accept 3 80 + expect 496813.178 3358297.326 + direction inverse + accept 496813.178 3358297.326 + expect 3 80 + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Equal(2, parsed.Count); + Assert.Equal("+proj=tmerc +lat_0=49 +lon_0=-2 +k_0=0.9996 +ellps=WGS84", parsed[0].Operation); + Assert.Equal(GieDirection.Forward, parsed[0].Direction); + Assert.Equal(0.03d, parsed[0].ToleranceValue, 12); + Assert.Equal("m", parsed[0].ToleranceUnit); + Assert.Equal(2, parsed[0].Accept.Length); + Assert.Equal(2, parsed[0].Expect.Length); + Assert.Equal(GieDirection.Inverse, parsed[1].Direction); + } + + /// + /// Verifies that comment lines and XML-style tags are ignored during parsing. + /// + [Fact] + public void ParseIgnoresCommentsAndTags() + { + const string content = + """ + + # comment line + operation +proj=eqearth +ellps=WGS84 + tolerance 10 m + accept 10 20 # inline comment + expect 1000 2000 + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Single(parsed); + Assert.Equal("+proj=eqearth +ellps=WGS84", parsed[0].Operation); + Assert.Equal(10d, parsed[0].ToleranceValue, 12); + } + + /// + /// Verifies that an expect directive without a preceding accept directive throws a . + /// + [Fact] + public void ParseWithoutAcceptBeforeExpectThrowsFormatException() + { + const string content = + """ + operation +proj=moll +ellps=WGS84 + expect 1 2 + """; + + Assert.Throws(() => GieParser.Parse(content)); + } + + /// + /// Verifies that an unknown directive throws a . + /// + [Fact] + public void ParseUnknownDirectiveThrowsFormatException() + { + const string content = + """ + operation +proj=aeqd +ellps=WGS84 + tolerance 1 m + foobar 1 2 + """; + + Assert.Throws(() => GieParser.Parse(content)); + } + + /// + /// Verifies that parsing a GIE fixture written to a temporary file produces the expected cases. + /// + [Fact] + public void ParseFileWithTemporaryInputProducesCases() + { + string filePath = Path.GetTempFileName(); + try + { + string content = + """ + operation +proj=gnom +ellps=WGS84 + tolerance 0.5 m + accept 7 8 + expect 700 800 + """; + File.WriteAllText(filePath, content); + + IReadOnlyList parsed = GieParser.ParseFile(filePath); + + Assert.Single(parsed); + Assert.Equal("+proj=gnom +ellps=WGS84", parsed[0].Operation); + Assert.Equal(0.5d, parsed[0].ToleranceValue, 12); + } + finally + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + } + + /// + /// Verifies that a backslash continuation line is appended to the preceding operation string. + /// + [Fact] + public void ParseWithContinuationLineAppendsOperation() + { + const string content = + """ + operation +proj=tmerc +ellps=WGS84 \ + +lat_0=0 +lon_0=9 + accept 1 2 + expect 3 4 + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Single(parsed); + Assert.Contains("+lat_0=0", parsed[0].Operation, StringComparison.Ordinal); + Assert.Contains("+lon_0=9", parsed[0].Operation, StringComparison.Ordinal); + } + + /// + /// Verifies that a failure expectation directive sets the failure flag and error code on the parsed case. + /// + [Fact] + public void ParseWithFailureExpectationSetsFailureMetadata() + { + const string content = + """ + operation +proj=aea +lat_1=900 + expect failure errno invalid_op_illegal_arg_value + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Single(parsed); + Assert.True(parsed[0].ExpectsFailure); + Assert.Equal("invalid_op_illegal_arg_value", parsed[0].ExpectedErrorCode); + } + + /// + /// Verifies that GIE numeric sentinels used in failure-expectation cases do not abort parsing. + /// + [Fact] + public void ParseWithHugeValSentinelParsesFailureCase() + { + const string content = + """ + operation +proj=defmodel +model=tests/simple_model_degree_horizontal.json + accept 2 49 30 HUGE_VAL + expect failure errno coord_transfm_missing_time + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Single(parsed); + Assert.True(parsed[0].ExpectsFailure); + Assert.Equal(4, parsed[0].Accept.Length); + Assert.True(double.IsPositiveInfinity(parsed[0].Accept[3])); + } + + /// + /// Verifies that a blank operation directive can still be represented for failure-expectation rows. + /// + [Fact] + public void ParseWithBlankOperationFailureCasePreservesFollowingOperations() + { + const string content = + """ + operation + expect failure + operation cobra + expect failure + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Equal(2, parsed.Count); + Assert.Equal(string.Empty, parsed[0].Operation); + Assert.True(parsed[0].ExpectsFailure); + Assert.Equal("cobra", parsed[1].Operation); + Assert.True(parsed[1].ExpectsFailure); + } + + /// + /// Verifies that unknown directives are skipped without error when is enabled. + /// + [Fact] + public void ParseWithIgnoreUnknownDirectivesEnabledSkipsUnknownDirective() + { + const string content = + """ + operation +proj=merc +ellps=WGS84 + foobar this should be ignored + accept 1 2 + expect 3 4 + """; + + IReadOnlyList parsed = GieParser.Parse(content, new GieParserOptions { IgnoreUnknownDirectives = true }); + + Assert.Single(parsed); + Assert.False(parsed[0].ExpectsFailure); + } + + /// + /// Verifies that DMS coordinates with seconds but without a trailing quote preserve the seconds component. + /// + [Fact] + public void ParseWithUnquotedDmsSecondsPreservesSeconds() + { + const string content = + """ + operation +proj=latlong +datum=NAD27 + accept -80d32'30.000 34d32'30.000 0.0 + expect 1 2 3 + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Single(parsed); + Assert.Equal(-80.54166666666667d, parsed[0].Accept[0], 12); + Assert.Equal(34.54166666666667d, parsed[0].Accept[1], 12); + } + + /// + /// Verifies that hemisphere suffices still combine correctly with unquoted DMS seconds. + /// + [Fact] + public void ParseWithUnquotedDmsSecondsAndHemispherePreservesSign() + { + const string content = + """ + operation +proj=latlong +datum=WGS84 + accept 1d2'3.5W 4d5'6.25S + expect 1 2 + """; + + IReadOnlyList parsed = GieParser.Parse(content); + + Assert.Single(parsed); + Assert.Equal(-(1d + (2d / 60d) + (3.5d / 3600d)), parsed[0].Accept[0], 12); + Assert.Equal(-(4d + (5d / 60d) + (6.25d / 3600d)), parsed[0].Accept[1], 12); + } +} diff --git a/test/ProjNet.Tests/Integration/Gigs5101TheoryTests.cs b/test/ProjNet.Tests/Integration/Gigs5101TheoryTests.cs new file mode 100644 index 00000000..fc286baf --- /dev/null +++ b/test/ProjNet.Tests/Integration/Gigs5101TheoryTests.cs @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Contains theory tests for GIGS 5101 through 5200 coordinate transformation fixtures. +/// +public class Gigs5101TheoryTests +{ + private static readonly string[] Fixture5101Files = + [ + "5101.1-jhs.gie", + "5101.2-jhs.gie", + "5101.3-jhs.gie", + "5101.4-jhs-etmerc.gie", + ]; + + private static readonly string[] Fixture5102And5103Files = + [ + "5102.1.gie", + "5102.2.gie", + "5103.1.gie", + "5103.2.gie", + "5103.3.gie", + ]; + + private static readonly string[] Fixture5104To5113Files = + [ + "5104.gie", + "5105.2.gie", + "5106.gie", + "5107.gie", + "5108.gie", + "5109.gie", + "5111.1.gie", + "5112.gie", + "5113.gie", + ]; + + private static readonly string[] Fixture5200Files = + [ + "5201.gie", + "5208.gie", + ]; + + private static readonly CoordinateSystemFactory CoordinateSystemFactory = new(); + private static readonly CoordinateTransformationFactory CoordinateTransformationFactory = new(); + + private static readonly char[] OperationTokenSeparators = [' ', '\t']; + private static readonly CoordinateSystemServices CoordinateSystemServices = new(); + + /// + /// Gets coverage parameters for GIGS 5101 fixtures. + /// + /// Coverage parameters. + public static IEnumerable> Gigs5101CoverageData + { + get + { + yield return new TheoryDataRow(Fixture5101Files, 50, 50, "5101", true); + } + } + + /// + /// Gets coverage parameters for GIGS 5102 and 5103 fixtures. + /// + /// Coverage parameters. + public static IEnumerable> Gigs5102And5103CoverageData + { + get + { + yield return new TheoryDataRow(Fixture5102And5103Files, 70, 0, "5102/5103", false); + } + } + + /// + /// Gets coverage parameters for GIGS 5104 through 5113 fixtures. + /// + /// Coverage parameters. + public static IEnumerable> Gigs5104To5113CoverageData + { + get + { + yield return new TheoryDataRow(Fixture5104To5113Files, 80, 0, "5104-5113", false); + } + } + + /// + /// Gets coverage parameters for GIGS 5200 fixtures. + /// + /// Coverage parameters. + public static IEnumerable> Gigs5200CoverageData + { + get + { + yield return new TheoryDataRow(Fixture5200Files, 20, 0, "5200", false); + } + } + + /// + /// Verifies that supported GIGS 5101 pipeline cases execute and produce results within the declared tolerance. + /// + /// Fixture file names to parse. + /// Minimum transformed case count expected. + /// Minimum within-tolerance case count expected. + /// Human-readable fixture group label. + /// Whether tolerance assertions are required. + [Theory] + [Trait("Category", "Gigs5101")] + [MemberData(nameof(Gigs5101CoverageData))] + public void Gigs5101CasesForSupportedPipelinesStayWithinTolerance( + string[] fixtureFiles, + int minTransformed, + int minWithinTolerance, + string label, + bool requireToleranceMatch) + { + AssertFixtureCoverage(fixtureFiles, minTransformed, minWithinTolerance, label, requireToleranceMatch); + } + + /// + /// Verifies that a substantial number of supported GIGS 5102 and 5103 pipeline cases execute successfully. + /// + /// Fixture file names to parse. + /// Minimum transformed case count expected. + /// Minimum within-tolerance case count expected. + /// Human-readable fixture group label. + /// Whether tolerance assertions are required. + [Theory] + [Trait("Category", "Gigs5102")] + [Trait("Category", "Gigs5103")] + [MemberData(nameof(Gigs5102And5103CoverageData))] + public void Gigs5102And5103CasesForSupportedPipelinesStayWithinTolerance( + string[] fixtureFiles, + int minTransformed, + int minWithinTolerance, + string label, + bool requireToleranceMatch) + { + AssertFixtureCoverage(fixtureFiles, minTransformed, minWithinTolerance, label, requireToleranceMatch); + } + + /// + /// Verifies that a substantial number of supported GIGS 5104 through 5113 pipeline cases execute successfully. + /// + /// Fixture file names to parse. + /// Minimum transformed case count expected. + /// Minimum within-tolerance case count expected. + /// Human-readable fixture group label. + /// Whether tolerance assertions are required. + [Theory] + [Trait("Category", "Gigs5104")] + [Trait("Category", "Gigs5113")] + [MemberData(nameof(Gigs5104To5113CoverageData))] + public void Gigs5104To5113CasesForSupportedPipelinesExecute( + string[] fixtureFiles, + int minTransformed, + int minWithinTolerance, + string label, + bool requireToleranceMatch) + { + AssertFixtureCoverage(fixtureFiles, minTransformed, minWithinTolerance, label, requireToleranceMatch); + } + + /// + /// Verifies that a substantial number of supported GIGS 5200 pipeline cases execute successfully. + /// + /// Fixture file names to parse. + /// Minimum transformed case count expected. + /// Minimum within-tolerance case count expected. + /// Human-readable fixture group label. + /// Whether tolerance assertions are required. + [Theory] + [Trait("Category", "Gigs5200")] + [MemberData(nameof(Gigs5200CoverageData))] + public void Gigs5200CasesForSupportedPipelinesExecute( + string[] fixtureFiles, + int minTransformed, + int minWithinTolerance, + string label, + bool requireToleranceMatch) + { + AssertFixtureCoverage(fixtureFiles, minTransformed, minWithinTolerance, label, requireToleranceMatch); + } + + private static void AssertFixtureCoverage( + IReadOnlyList fixtureFiles, + int minTransformed, + int minWithinTolerance, + string label, + bool requireToleranceMatch) + { + int parsedCases = 0; + int transformedCases = 0; + int withinToleranceCases = 0; + + foreach (GieCase testCase in EnumerateFixtureCases(fixtureFiles)) + { + parsedCases++; + if (testCase.ExpectsFailure || testCase.Accept is null || testCase.Expect is null || testCase.Accept.Length < 2 || testCase.Expect.Length < 2) + { + continue; + } + + if (!TryCreatePipelineTransform(testCase.Operation, out MathTransform? transform)) + { + continue; + } + + MathTransform pipelineTransform = Assert.IsType(transform, exactMatch: false); + double[] output; + try + { + output = pipelineTransform.Transform(testCase.Accept); + } + catch (ArgumentException) + { + continue; + } + + if (output is null || output.Length < 2 || double.IsNaN(output[0]) || double.IsNaN(output[1])) + { + continue; + } + + transformedCases++; + double tolerance = Math.Max(ToNumericTolerance(testCase.ToleranceValue, testCase.ToleranceUnit), 1e-3d); + if (LooksLikeGeographicExpect(testCase.Expect)) + { + tolerance /= 111319.49079327358d; + } + + double deltaX = Math.Abs(output[0] - testCase.Expect[0]); + double deltaY = Math.Abs(output[1] - testCase.Expect[1]); + if (deltaX <= tolerance && deltaY <= tolerance) + { + withinToleranceCases++; + } + } + + Assert.True(parsedCases > 0, $"Expected parsed GIGS {label} cases."); + Assert.True( + transformedCases > minTransformed, + FormattableString.Invariant($"Expected to execute a substantial subset of GIGS {label} cases. transformed={transformedCases}, min={minTransformed}, parsed={parsedCases}.")); + if (requireToleranceMatch) + { + Assert.True( + withinToleranceCases > minWithinTolerance, + FormattableString.Invariant($"Expected a substantial subset of executed GIGS {label} cases to match tolerance. within={withinToleranceCases}, min={minWithinTolerance}, transformed={transformedCases}.")); + } + } + + private static IEnumerable EnumerateFixtureCases(IReadOnlyList fileNames) + { + string gigsDirectory = FindGigsDirectory(); + foreach (string fileName in fileNames) + { + string path = Path.Combine(gigsDirectory, fileName); + if (!File.Exists(path)) + { + throw new FileNotFoundException(FormattableString.Invariant($"Required GIGS fixture '{fileName}' was not found."), path); + } + + IReadOnlyList parsed = GieParser.ParseFile( + path, + new GieParserOptions + { + IgnoreUnknownDirectives = true, + AllowOperationContinuation = true, + }); + + foreach (GieCase item in parsed) + { + yield return item; + } + } + } + + private static bool TryCreatePipelineTransform(string operation, out MathTransform? transform) + { + transform = null; + if (string.IsNullOrWhiteSpace(operation)) + { + return false; + } + + if (!TrySplitPipelineSteps(operation, out IReadOnlyList steps) || steps.Count != 2) + { + return false; + } + + if (!TryParseOperationArguments(steps[0], out Dictionary firstArgs) + || !TryParseOperationArguments(steps[1], out Dictionary secondArgs)) + { + return false; + } + + if (!firstArgs.ContainsKey("inv") || secondArgs.ContainsKey("inv")) + { + return false; + } + + if (!TryResolveDeclaredCoordinateSystem(firstArgs, out CoordinateSystem? source) + || !TryResolveDeclaredCoordinateSystem(secondArgs, out CoordinateSystem? target)) + { + return false; + } + + CoordinateSystem targetCoordinateSystem = Assert.IsType(target, exactMatch: false); + try + { + CoordinateSystem sourceCoordinateSystem = Assert.IsType(source, exactMatch: false); + ICoordinateTransformation coordinateTransformation = CoordinateTransformationFactory.CreateFromCoordinateSystems(sourceCoordinateSystem, targetCoordinateSystem); + transform = coordinateTransformation.MathTransform; + return transform is not null; + } + catch (ArgumentException) + { + return false; + } + catch (NotSupportedException) + { + return false; + } + catch (InvalidOperationException) + { + return false; + } + catch (System.Reflection.TargetInvocationException) + { + return false; + } + } + + private static bool TrySplitPipelineSteps(string operation, out IReadOnlyList steps) + { + var parsedSteps = new List(); + var currentStepTokens = new List(); + bool inPipeline = false; + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string token in tokens) + { + string normalized = token.StartsWith('+') + ? token[1..] + : token; + + if (normalized.Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + inPipeline = true; + continue; + } + + if (normalized.Equals("step", StringComparison.OrdinalIgnoreCase)) + { + inPipeline = true; + if (currentStepTokens.Count > 0) + { + parsedSteps.Add(string.Join(" ", currentStepTokens)); + currentStepTokens.Clear(); + } + + continue; + } + + if (!inPipeline) + { + continue; + } + + currentStepTokens.Add(token); + } + + if (currentStepTokens.Count > 0) + { + parsedSteps.Add(string.Join(" ", currentStepTokens)); + } + + steps = parsedSteps; + return parsedSteps.Count > 0; + } + + private static bool TryResolveDeclaredCoordinateSystem(Dictionary args, out CoordinateSystem? coordinateSystem) + { + coordinateSystem = null; + + if (args.TryGetValue("init", out string? initValue) + && TryParseEpsgCode(initValue, out int srid)) + { + coordinateSystem = CoordinateSystemServices.GetCoordinateSystem(srid); + coordinateSystem = NormalizeAxisOrder(coordinateSystem); + return coordinateSystem is not null; + } + + if (!args.TryGetValue("proj", out string? projCode)) + { + return false; + } + + if (!TryMapProjectionClass(projCode, out string projectionClassName)) + { + return false; + } + + if (!TryCreateGeographicCoordinateSystem(args, out GeographicCoordinateSystem? geographicCoordinateSystem)) + { + return false; + } + + if (!TryBuildProjectionParameters(args, out List parameters)) + { + return false; + } + + try + { + IProjection projection = CoordinateSystemFactory.CreateProjection($"GIGS {projectionClassName}", projectionClassName, parameters); + GeographicCoordinateSystem geographic = Assert.IsType(geographicCoordinateSystem); + coordinateSystem = CoordinateSystemFactory.CreateProjectedCoordinateSystem( + "GIGS projected", + geographic, + projection, + LinearUnit.Metre, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + return true; + } + catch (ArgumentException) + { + return false; + } + catch (NotSupportedException) + { + return false; + } + catch (InvalidOperationException) + { + return false; + } + catch (System.Reflection.TargetInvocationException) + { + return false; + } + } + + private static bool TryParseEpsgCode(string token, out int srid) + { + srid = 0; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string value = token.Trim(); + const string epsgPrefix = "epsg:"; + if (value.StartsWith(epsgPrefix, StringComparison.OrdinalIgnoreCase)) + { + value = value[epsgPrefix.Length..]; + } + + return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out srid); + } + + private static CoordinateSystem? NormalizeAxisOrder(CoordinateSystem? coordinateSystem) + { + if (coordinateSystem is null) + { + return null; + } + + var geographic = coordinateSystem as GeographicCoordinateSystem; + if (geographic is not null) + { + return CoordinateSystemFactory.CreateGeographicCoordinateSystem( + geographic.Name, + geographic.AngularUnit, + geographic.HorizontalDatum, + geographic.PrimeMeridian, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + var projected = coordinateSystem as ProjectedCoordinateSystem; + if (projected is not null) + { + GeographicCoordinateSystem normalizedProjectedGeographic = Assert.IsType(NormalizeAxisOrder(projected.GeographicCoordinateSystem)); + return CoordinateSystemFactory.CreateProjectedCoordinateSystem( + projected.Name, + normalizedProjectedGeographic, + projected.Projection, + projected.LinearUnit, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + } + + return coordinateSystem; + } + + private static bool TryMapProjectionClass(string? projCode, out string projectionClassName) + { + projectionClassName = string.Empty; + if (projCode is null) + { + return false; + } + + if (projCode.Equals("etmerc", StringComparison.OrdinalIgnoreCase)) + { + projectionClassName = "etmerc"; + return true; + } + + if (projCode.Equals("tmerc", StringComparison.OrdinalIgnoreCase)) + { + projectionClassName = "transverse_mercator"; + return true; + } + + if (projCode.Equals("utm", StringComparison.OrdinalIgnoreCase)) + { + projectionClassName = "utm"; + return true; + } + + if (projCode.Equals("poly", StringComparison.OrdinalIgnoreCase)) + { + projectionClassName = "polyconic"; + return true; + } + + return false; + } + + private static bool TryCreateGeographicCoordinateSystem(Dictionary args, out GeographicCoordinateSystem? gcs) + { + gcs = null; + + if (!TryResolveEllipsoid(args, out Ellipsoid? ellipsoid)) + { + return false; + } + + HorizontalDatum datum = CoordinateSystemFactory.CreateHorizontalDatum( + "GIGS datum", + DatumType.HD_Geocentric, + Assert.IsType(ellipsoid), + null); + gcs = CoordinateSystemFactory.CreateGeographicCoordinateSystem( + "GIGS geographic", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + return true; + } + + private static bool TryResolveEllipsoid(Dictionary args, out Ellipsoid? ellipsoid) + { + if (TryGetDouble(args, "r", out double sphereRadius) && sphereRadius > 0d) + { + ellipsoid = CoordinateSystemFactory.CreateEllipsoid("GIGS sphere", sphereRadius, sphereRadius, LinearUnit.Metre); + return true; + } + + if (TryGetDouble(args, "a", out double semiMajor) && semiMajor > 0d) + { + double explicitSemiMajor = semiMajor; + double explicitSemiMinor = semiMajor; + if (!ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref explicitSemiMajor, ref explicitSemiMinor, out _)) + { + ellipsoid = null; + return false; + } + + ellipsoid = CoordinateSystemFactory.CreateEllipsoid("GIGS ellipsoid", explicitSemiMajor, explicitSemiMinor, LinearUnit.Metre); + return true; + } + + Ellipsoid baseEllipsoid = Ellipsoid.WGS84; + if (args.TryGetValue("ellps", out string? ellps) && !string.IsNullOrWhiteSpace(ellps)) + { + if (ellps.Equals("grs80", StringComparison.OrdinalIgnoreCase)) + { + baseEllipsoid = Ellipsoid.GRS80; + } + else if (!ellps.Equals("wgs84", StringComparison.OrdinalIgnoreCase)) + { + ellipsoid = null; + return false; + } + } + + if (!HasExplicitShapeOverride(args)) + { + ellipsoid = baseEllipsoid; + return true; + } + + double resolvedSemiMajor = baseEllipsoid.SemiMajorAxis; + double resolvedSemiMinor = baseEllipsoid.SemiMinorAxis; + if (!ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref resolvedSemiMajor, ref resolvedSemiMinor, out _)) + { + ellipsoid = null; + return false; + } + + ellipsoid = CoordinateSystemFactory.CreateEllipsoid(baseEllipsoid.Name, resolvedSemiMajor, resolvedSemiMinor, LinearUnit.Metre); + return true; + } + + private static bool HasExplicitShapeOverride(Dictionary args) + { + return args.ContainsKey("b") + || args.ContainsKey("rf") + || args.ContainsKey("f") + || args.ContainsKey("es") + || args.ContainsKey("e") + || args.ContainsKey("R_A") + || args.ContainsKey("R_V") + || args.ContainsKey("R_a") + || args.ContainsKey("R_g") + || args.ContainsKey("R_h") + || args.ContainsKey("R_lat_a") + || args.ContainsKey("R_lat_g") + || args.ContainsKey("R_C"); + } + + private static bool TryBuildProjectionParameters(Dictionary args, out List parameters) + { + parameters = + [ + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + ]; + + double linearUnitFactor = ResolveProjectionLinearUnitFactor(args); + + if (TryGetDouble(args, "lat_0", out double lat0)) + { + ReplaceParameter(parameters, "latitude_of_origin", lat0); + } + + if (TryGetDouble(args, "lon_0", out double lon0)) + { + ReplaceParameter(parameters, "central_meridian", lon0); + } + + if (TryGetDouble(args, "k_0", out double k0)) + { + ReplaceParameter(parameters, "scale_factor", k0); + } + else if (TryGetDouble(args, "k", out double k)) + { + ReplaceParameter(parameters, "scale_factor", k); + } + + if (TryGetDouble(args, "x_0", out double x0)) + { + ReplaceParameter(parameters, "false_easting", x0 / linearUnitFactor); + } + + if (TryGetDouble(args, "y_0", out double y0)) + { + ReplaceParameter(parameters, "false_northing", y0 / linearUnitFactor); + } + + if (args.TryGetValue("proj", out string? projectionCode) && projectionCode.Equals("cass", StringComparison.OrdinalIgnoreCase) && args.ContainsKey("hyperbolic")) + { + ReplaceParameter(parameters, "hyperbolic", 1d); + } + + if (args.TryGetValue("proj", out string? projCode) && projCode.Equals("utm", StringComparison.OrdinalIgnoreCase)) + { + if (!TryGetZoneCentralMeridian(args, out double utmCentralMeridian)) + { + return false; + } + + double unitFactor = 1d; + if (TryGetDouble(args, "to_meter", out double toMeter) && toMeter > 0d) + { + unitFactor = toMeter; + } + else if (args.TryGetValue("units", out string? unitsToken)) + { + unitFactor = unitsToken.ToUpperInvariant() switch + { + "M" => LinearUnit.Metre.MetersPerUnit, + "FT" => LinearUnit.Foot.MetersPerUnit, + "US-FT" => LinearUnit.USSurveyFoot.MetersPerUnit, + _ => unitFactor, + }; + } + + ReplaceParameter(parameters, "latitude_of_origin", 0d); + ReplaceParameter(parameters, "central_meridian", utmCentralMeridian); + ReplaceParameter(parameters, "scale_factor", 0.9996d); + ReplaceParameter(parameters, "false_easting", 500000d / unitFactor); + ReplaceParameter(parameters, "false_northing", (args.ContainsKey("south") ? 10000000d : 0d) / unitFactor); + } + + return true; + } + + private static double ResolveProjectionLinearUnitFactor(Dictionary args) + { + if (TryGetDouble(args, "to_meter", out double toMeter) && toMeter > 0d) + { + return toMeter; + } + + if (args.TryGetValue("units", out string? unitsToken)) + { + return unitsToken.ToUpperInvariant() switch + { + "M" => LinearUnit.Metre.MetersPerUnit, + "FT" => LinearUnit.Foot.MetersPerUnit, + "US-FT" => LinearUnit.USSurveyFoot.MetersPerUnit, + _ => 1d, + }; + } + + return 1d; + } + + private static bool TryGetZoneCentralMeridian(Dictionary args, out double centralMeridian) + { + centralMeridian = 0d; + if (!args.TryGetValue("zone", out string? zoneToken) || string.IsNullOrWhiteSpace(zoneToken)) + { + return false; + } + + string digits = zoneToken.Trim(); + int i = 0; + while (i < digits.Length && char.IsDigit(digits[i])) + { + i++; + } + + if (i == 0 || !int.TryParse(digits.AsSpan(0, i), NumberStyles.Integer, CultureInfo.InvariantCulture, out int zone)) + { + return false; + } + + centralMeridian = (zone * 6d) - 183d; + return true; + } + + private static bool TryGetDouble(Dictionary args, string key, out double value) + { + value = 0d; + return args.TryGetValue(key, out string? raw) && !string.IsNullOrWhiteSpace(raw) && double.TryParse(raw, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value); + } + + private static void ReplaceParameter(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } + + private static bool TryParseOperationArguments(string operation, out Dictionary args) + { + args = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (operation is null) + { + return false; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string token in tokens) + { + string body = token.Length > 0 && token[0] == '+' + ? token[1..] + : token; + + if (body.Length == 0 + || body.Equals("step", StringComparison.OrdinalIgnoreCase) + || body.Equals("proj=pipeline", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + int index = body.IndexOf('=', StringComparison.Ordinal); + if (index < 0) + { + args[body] = body; + } + else + { + string key = body[..index]; + string value = body[(index + 1)..]; + args[key] = value; + } + } + + return args.Count > 0; + } + + private static string FindGigsDirectory() + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "gigs"); + if (Directory.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "gigs"); + if (Directory.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new DirectoryNotFoundException("GIGS fixtures were not found under test\\ProjNet.Tests\\Fixtures\\gigs."); + } + + private static double ToNumericTolerance(double value, string unit) + { + if (unit is null) + { + return value; + } + + if (unit.Equals("mm", StringComparison.OrdinalIgnoreCase)) + { + return value / 1000d; + } + + if (unit.Equals("cm", StringComparison.OrdinalIgnoreCase)) + { + return value / 100d; + } + + return unit.Equals("nm", StringComparison.OrdinalIgnoreCase) ? value * 1e-9d : value; + } + + private static bool LooksLikeGeographicExpect(double[] expect) + { + return expect is not null && expect.Length >= 2 && Math.Abs(expect[0]) <= 360d && Math.Abs(expect[1]) <= 90d; + } +} diff --git a/test/ProjNet.Tests/Integration/GigsParserTests.cs b/test/ProjNet.Tests/Integration/GigsParserTests.cs new file mode 100644 index 00000000..0f38f65e --- /dev/null +++ b/test/ProjNet.Tests/Integration/GigsParserTests.cs @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xunit; + +/// +/// Contains tests for the GIE fixture file parser against GIGS fixture data. +/// +public class GigsParserTests +{ + /// + /// Gets local non-failing GIGS fixture files. + /// + /// Fixture file entries with file name and full path. + public static IEnumerable> NonFailingFixtureFiles + { + get + { + string gigsDirectory = FindGigsDirectory(); + foreach (string file in Directory.GetFiles(gigsDirectory, "*.gie") + .Where(path => !path.EndsWith(".failing", StringComparison.OrdinalIgnoreCase)) + .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)) + { + yield return new TheoryDataRow(Path.GetFileName(file), file); + } + } + } + + /// + /// Verifies that parsing a GIGS fixture file produces a non-empty collection of test cases. + /// + [Theory] + [MemberData(nameof(NonFailingFixtureFiles))] + public void ParseGigsFixtureFileProducesCases(string fileName, string filePath) + { + IReadOnlyList parsed = GieParser.ParseFile( + filePath, + new GieParserOptions + { + IgnoreUnknownDirectives = true, + AllowOperationContinuation = true, + }); + + Assert.NotNull(parsed); + Assert.NotEmpty(parsed); + Assert.False(string.IsNullOrWhiteSpace(fileName)); + } + + /// + /// Verifies that all non-failing GIGS fixture files parse successfully and together yield a substantial number of test cases. + /// + [Fact] + public void ParseGigsFixturesParsesAllNonFailingFiles() + { + string gigsDirectory = FindGigsDirectory(); + string[] files = [.. Directory.GetFiles(gigsDirectory, "*.gie") + .Where(path => !path.EndsWith(".failing", StringComparison.OrdinalIgnoreCase)) + .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; + + Assert.NotEmpty(files); + + int totalCases = 0; + foreach (string file in files) + { + IReadOnlyList parsed = GieParser.ParseFile( + file, + new GieParserOptions + { + IgnoreUnknownDirectives = true, + AllowOperationContinuation = true, + }); + + Assert.NotNull(parsed); + Assert.NotEmpty(parsed); + totalCases += parsed.Count; + } + + Assert.True(totalCases > 100, "Expected substantial GIGS coverage from parsed cases."); + } + + /// + /// Verifies that at least one pipeline operation is present among all parsed non-failing GIGS fixture cases. + /// + [Fact] + public void ParseGigsFixturesPreservesPipelineOperations() + { + string gigsDirectory = FindGigsDirectory(); + string[] files = [.. Directory.GetFiles(gigsDirectory, "*.gie") + .Where(path => !path.EndsWith(".failing", StringComparison.OrdinalIgnoreCase)) + .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)]; + + int pipelineCaseCount = 0; + foreach (string file in files) + { + IReadOnlyList parsed = GieParser.ParseFile( + file, + new GieParserOptions + { + IgnoreUnknownDirectives = true, + AllowOperationContinuation = true, + }); + + pipelineCaseCount += parsed.Count(item => + item.Operation is not null + && item.Operation.Contains("+proj=pipeline", StringComparison.OrdinalIgnoreCase)); + } + + Assert.True(pipelineCaseCount > 0, "Expected parsed GIGS cases to include pipeline operations."); + } + + private static string FindGigsDirectory() + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "gigs"); + if (Directory.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "gigs"); + if (Directory.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + throw new DirectoryNotFoundException("GIGS fixtures were not found under test\\ProjNet.Tests\\Fixtures\\gigs."); + } +} diff --git a/test/ProjNet.Tests/Integration/Proj2ProjParityTheoryTests.cs b/test/ProjNet.Tests/Integration/Proj2ProjParityTheoryTests.cs new file mode 100644 index 00000000..7faa003e --- /dev/null +++ b/test/ProjNet.Tests/Integration/Proj2ProjParityTheoryTests.cs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates direct proj2proj parity fixtures against ProjNet transformations. +/// +public class Proj2ProjParityTheoryTests +{ + private static readonly JsonSerializerOptions SerializerOptions = new() + { + PropertyNameCaseInsensitive = true, + }; + + /// + /// Validates a direct projected pair against the reference fixture output. + /// + /// Fixture case containing source/target definitions and expected result. + [Theory] + [MemberData(nameof(GetParityCases))] + public void CreateFromCoordinateSystemsWithDirectProjectedPairStaysWithinProjReference(Proj2ProjCase testCase) + { + Assert.NotNull(testCase); + + var coordinateSystemFactory = new CoordinateSystemFactory(); + var transformationFactory = new CoordinateTransformationFactory(); + + ProjectedCoordinateSystem source = Assert.IsType( + CoordinateSystemTestHelpers.RequireCoordinateSystem(coordinateSystemFactory, testCase.SourceWkt) + .WithAuthority("EPSG", testCase.SourceSrid)); + ProjectedCoordinateSystem target = Assert.IsType( + CoordinateSystemTestHelpers.RequireCoordinateSystem(coordinateSystemFactory, testCase.TargetWkt) + .WithAuthority("EPSG", testCase.TargetSrid)); + + ICoordinateTransformation transformation = transformationFactory.CreateFromCoordinateSystems(source, target); + double[] output = transformation.MathTransform.Transform([testCase.InputX, testCase.InputY]); + double deltaX = Math.Abs(output[0] - testCase.ExpectedX); + double deltaY = Math.Abs(output[1] - testCase.ExpectedY); + + Assert.Equal("EPSG", transformation.Authority); + Assert.Equal(testCase.OperationCode, transformation.AuthorityCode); + Assert.InRange(deltaX, 0d, testCase.ToleranceMeters); + Assert.InRange(deltaY, 0d, testCase.ToleranceMeters); + } + + /// + /// Loads direct proj2proj parity test cases from the generated fixture. + /// + /// Fixture rows for theory execution. + public static IEnumerable> GetParityCases() + { + string fixturePath = Path.Combine(AppContext.BaseDirectory, "Generated", "proj2proj-direct-parity-fixture.json"); + Assert.True(File.Exists(fixturePath), $"Fixture file not found: {fixturePath}"); + + string json = File.ReadAllText(fixturePath); + Proj2ProjFixture fixture = Assert.IsType(JsonSerializer.Deserialize(json, SerializerOptions)); + Assert.NotNull(fixture); + List cases = Assert.IsType>(fixture.Cases); + Assert.NotEmpty(fixture.Cases); + + foreach (Proj2ProjCase item in cases) + { + yield return new TheoryDataRow(item); + } + } +} diff --git a/test/ProjNet.Tests/Integration/ProjNetIssueRegressionTests.cs b/test/ProjNet.Tests/Integration/ProjNetIssueRegressionTests.cs new file mode 100644 index 00000000..cada935d --- /dev/null +++ b/test/ProjNet.Tests/Integration/ProjNetIssueRegressionTests.cs @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for issues reported in the ProjNet issue tracker. +/// +public class ProjNetIssueRegressionTests : CoordinateTransformTestsBase +{ + private const string Discussion361248Wgs84Wkt = + """ + GEOGCS["WGS 84", + DATUM["WGS_1984", + SPHEROID["WGS 84",6378137,298.257223563, + AUTHORITY["EPSG","7030"]], + AUTHORITY["EPSG","6326"]], + PRIMEM["Greenwich",0, + AUTHORITY["EPSG","8901"]], + UNIT["degree",0.01745329251994328, + AUTHORITY["EPSG","9122"]], + AUTHORITY["EPSG","4326"]] + """; + + private static readonly double[] TestDiscussion3612481Expected = [2349315.05731837, 6524249.91789138]; + private static readonly double[] TestDiscussion3612481Input = [136d, -30d]; + private static readonly double[] TestDiscussion3612482Expected = [-77.191769, 38.101147]; + private static readonly double[] TestDiscussion3612482Input = [307821.867, 4219306.387]; + + /// + /// Initializes a new instance of the class. + /// + public ProjNetIssueRegressionTests() + { + this.Verbose = true; + } + + /// + /// Verifies ProjNet issue 23773: WGS84 UTM zone 18N to WGS84 geographic transformation + /// produces accurate results both from a programmatic and WKT-defined coordinate system. + /// + [Fact(DisplayName = "WGS_84UTM to WGS_84 is inaccurate")] + public void TestIssue23773() + { + var csUtm18N = ProjectedCoordinateSystem.WGS84_UTM(18, true); + const string wgs84Utm18NWkt = + """ + PROJCS["WGS 84 / UTM zone 18N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-75],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32618"]] + """; + CoordinateSystem csUtm18NWkt = this.RequireCoordinateSystem(wgs84Utm18NWkt); + GeographicCoordinateSystem csWgs84 = GeographicCoordinateSystem.WGS84; + + ICoordinateTransformation ct = this.CreateTransformation(csUtm18N, csWgs84); + ICoordinateTransformation ct2 = this.CreateTransformation(csUtm18NWkt, csWgs84); + + double[] putm = [307821.867d, 4219306.387d]; + double[] pgeo = ct.MathTransform.Transform(putm); + double[] pgeoWkt = ct2.MathTransform.Transform(putm); + double[] pExpected = [-77.191769, 38.101147d]; + + this.AssertCoordinateWithinTolerance("UTM18N -> WGS84", pExpected, pgeoWkt, TestTolerances.CoordinateRoundTrip); + this.AssertCoordinateWithinTolerance("UTM18N -> WGS84", pExpected, pgeo, TestTolerances.CoordinateRoundTrip); + } + + /// + /// Verifies reprojection from EPSG 28414 (Pulkovo 1942 / Gauss-Kruger zone 14) to + /// EPSG 4284 (Pulkovo 1942 geographic), per CodePlex discussion 351733. + /// + [Fact(DisplayName = "Proj.net reprojection problem, Discussion http://projnet.codeplex.com/discussions/351733")] + public void TestDiscussion351733() + { + const string pulkovoProjectedWkt = + """ + PROJCS["Pulkovo 1942 / Gauss-Kruger zone 14",GEOGCS["Pulkovo 1942",DATUM["Pulkovo_1942",SPHEROID["Krassowsky 1940",6378245,298.3,AUTHORITY["EPSG","7024"]],TOWGS84[23.92,-141.27,-80.9,-0,0.35,0.82,-0.12],AUTHORITY["EPSG","6284"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4284"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",81],PARAMETER["scale_factor",1],PARAMETER["false_easting",14500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",NORTH],AXIS["Y",EAST],AUTHORITY["EPSG","28414"]]" + """; + const string pulkovoGeographicWkt = + """ + GEOGCS["Pulkovo 1942",DATUM["Pulkovo_1942",SPHEROID["Krassowsky 1940",6378245,298.3,AUTHORITY["EPSG","7024"]],TOWGS84[23.92,-141.27,-80.9,-0,0.35,0.82,-0.12],AUTHORITY["EPSG","6284"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4284"]]" + """; + CoordinateSystem csSource = this.RequireCoordinateSystem(pulkovoProjectedWkt); + CoordinateSystem csTarget = this.RequireCoordinateSystem(pulkovoGeographicWkt); + + ICoordinateTransformation ct = this.CreateTransformation(csSource, csTarget); + + double[] pp = [14181052.913, 6435927.692]; + double[] pg = ct.MathTransform.Transform(pp); + double[] pExpected = [75.613911283608331, 57.926509119323505]; + double[] pp2 = ct.MathTransform.Inverse().Transform(pg); + + this.Verbose = true; + this.AssertCoordinateWithinTolerance("EPSG 28414 -> EPSG 4284", pExpected, pg, 1e-6); + this.AssertCoordinateWithinTolerance("EPSG 28414 -> Pulkovo 1942", pp, pp2, 1e-3, reverse: true); + } + + /// + /// Verifies coordinate conversion from WGS84 (EPSG 4326) to Web Mercator (EPSG 3857) + /// and back, per CodePlex discussion 352813. + /// + [Fact(DisplayName = "Problem converting coordinates, Discussion http://projnet.codeplex.com/discussions/352813")] + public void TestDiscussion352813() + { + GeographicCoordinateSystem csSource = GeographicCoordinateSystem.WGS84; + ProjectedCoordinateSystem csTarget = ProjectedCoordinateSystem.WebMercator; + ICoordinateTransformation ct = this.CreateTransformation(csSource, csTarget); + + this.Verbose = true; + + double[] pg1 = [23.57892d, 37.94712d]; + + // src DotSpatial.Projections + double[] pExpected = [2624793.3678553337, 4571958.333297424]; + + double[] pp = ct.MathTransform.Transform(pg1); + Console.WriteLine(this.TransformationError("EPSG 4326 -> EPSG 3857", pExpected, pp)); + + this.AssertCoordinateWithinTolerance("EPSG 4326 -> EPSG 3857", pExpected, pp, 1e-9); + + double[] pg2 = ct.MathTransform.Inverse().Transform(pp); + this.AssertCoordinateWithinTolerance("EPSG 4326 -> EPSG 3857", pg1, pg2, 1e-13, reverse: true); + } + + /// + /// Verifies WGS84 to GDA94/MGA zone 50 (EPSG 28350) coordinate accuracy, + /// per CodePlex discussion 361248. + /// + [Fact(DisplayName = "Concerned about the accuracy, Discussion http://projnet.codeplex.com/discussions/361248")] + public void TestDiscussion3612481() + { + const string gda94MgaZone50Wkt = + """ + PROJCS["GDA94 / MGA zone 50",GEOGCS["GDA94",DATUM["Geocentric_Datum_of_Australia_1994",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6283"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4283"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","28350"]] + """; + CoordinateSystem csSource = this.RequireCoordinateSystem(Discussion361248Wgs84Wkt); + + CoordinateSystem csTarget = this.RequireCoordinateSystem(gda94MgaZone50Wkt); + + // Chose PostGis values + this.AssertTransformation("WGS 84 -> GDA94 / MGA zone 50", csSource, csTarget, TestDiscussion3612481Input, TestDiscussion3612481Expected, 0.05, 1.0e-4); + } + + /// + /// Verifies WGS84 UTM zone 18N to WGS84 geographic coordinate accuracy, + /// per CodePlex discussion 361248. + /// + [Fact(DisplayName = "Concerned about the accuracy, Discussion http://projnet.codeplex.com/discussions/361248")] + public void TestDiscussion3612482() + { + var csSource = ProjectedCoordinateSystem.WGS84_UTM(18, true); + + CoordinateSystem csTarget = this.RequireCoordinateSystem(Discussion361248Wgs84Wkt); + + this.AssertTransformation("WGS84_UTM(18,N) -> WGS84", csSource, csTarget, TestDiscussion3612482Input, TestDiscussion3612482Expected, 1e-6); + } + + /// + /// Wrong null check in ObliqueMercatorProjection.Inverse() method. + /// + /// + [Fact(DisplayName = "ObliqueMercatorProjection.Inverse() wrong null check")] + public void TestNtsIssue191() + { + var parameters = new List + { + new("latitude_of_center", 45.30916666666666), + new("longitude_of_center", -86), + new("azimuth", 337.25556), + new("rectified_grid_angle", 337.25556), + new("scale_factor", 0.9996), + new("false_easting", 2546731.496), + new("false_northing", -4354009.816), + }; + + CoordinateSystemFactory factory = this.CoordinateSystemFactory; + IProjection projection = factory.CreateProjection("Test Oblique", "oblique_mercator", parameters); + Assert.NotNull(projection); + + GeographicCoordinateSystem wgs84 = GeographicCoordinateSystem.WGS84; + ProjectedCoordinateSystem dummy = factory.CreateProjectedCoordinateSystem( + "dummy pcs", + wgs84, + projection, + LinearUnit.Metre, + new AxisInfo("X", AxisOrientationEnum.East), + new AxisInfo("Y", AxisOrientationEnum.North)); + Assert.NotNull(dummy); + + ICoordinateTransformation transform = this.CreateTransformation(wgs84, dummy); + Assert.NotNull(transform); + + MathTransform mathTransform = transform.MathTransform; + MathTransform inverse = mathTransform.Inverse(); + Assert.NotNull(inverse); + } + + /// + /// Wrong AngularUnits.EqualParams implementation. + /// + [Fact] + public void TestAngularUnitsEqualParamsIssue() + { + string wkt = + """ + PROJCS["DHDN / Gauss-Kruger zone 3",GEOGCS["DHDN",DATUM["Deutsches_Hauptdreiecksnetz",SPHEROID["Bessel 1841",6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],AUTHORITY["EPSG","6314"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4314"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",1],PARAMETER["false_easting",3500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","31467"]] + """; + + ProjectedCoordinateSystem pcs1 = this.RequireCoordinateSystem(wkt); + Assert.NotNull(pcs1.GeographicCoordinateSystem); + Assert.NotNull(pcs1.GeographicCoordinateSystem.AngularUnit); + + string savedWkt = pcs1.WKT; + ProjectedCoordinateSystem pcs2 = this.RequireCoordinateSystem(savedWkt); + + // test AngularUnit parsing via ProjectedCoordinateSystem + Assert.NotNull(pcs2.GeographicCoordinateSystem); + Assert.NotNull(pcs2.GeographicCoordinateSystem.AngularUnit); + + // check equality of angular units via RadiansPerUnit + Assert.Equal(pcs1.GeographicCoordinateSystem.AngularUnit.RadiansPerUnit, pcs2.GeographicCoordinateSystem.AngularUnit.RadiansPerUnit, TestTolerances.AngularUnitRoundTrip); + + // check equality of angular units + Assert.True(pcs1.GeographicCoordinateSystem.AngularUnit.EqualParams(pcs2.GeographicCoordinateSystem.AngularUnit)); + } + + /// + /// Verifies that GitHub issue #53 is fixed: a WGS84 to UTM zone 35N forward transformation + /// followed by the inverse round-trips back to the original coordinates within tolerance. + /// + [GitHubIssue(53)] + [Fact(DisplayName = "Issue #53, transformation somehow is wrong")] + public void TestGitHubIssue53() + { + GeographicCoordinateSystem csWgs84 = GeographicCoordinateSystem.WGS84; + var csUtm35N = ProjectedCoordinateSystem.WGS84_UTM(35, true); + ICoordinateTransformation csTrans = this.CreateTransformation(csWgs84, csUtm35N); + ICoordinateTransformation csTransBack = this.CreateTransformation(csUtm35N, csWgs84); + + double[] point = [42.5, 24.5]; + double[] r = csTrans.MathTransform.Transform(point); + double[] rBack = csTransBack.MathTransform.Transform(r); + + Assert.Equal(point[0], rBack[0], TestTolerances.CoordinateRoundTrip); + Assert.Equal(point[1], rBack[1], TestTolerances.CoordinateRoundTrip); + } + + /// + /// Verifies that GitHub issue #98 is fixed: a compound coordinate system combining a + /// projected CRS and a vertical CRS can be parsed from WKT and exposes the correct + /// authority, dimension, and component systems. + /// + [GitHubIssue(98)] + [Fact(DisplayName = "Issue #98, Coordinate system isn't supported")] + public void TestGitHubIssue98() + { + const string compoundWkt = + """ + COMPD_CS[ + "SWEREF99 18 00 + RH2000 height", + PROJCS[ + "SWEREF99 18 00", + GEOGCS[ + "SWEREF99", + DATUM[ + "SWEREF99", + SPHEROID[ + "GRS 1980", + 6378137, + 298.257222101, + AUTHORITY[ + "EPSG", + "7019" + ] + ], + TOWGS84[0,0,0,0,0,0,0], + AUTHORITY["EPSG","6619"] + ], + PRIMEM + [ + "Greenwich", + 0, + AUTHORITY["EPSG","8901"] + ], + UNIT["degree",0.0174532925199433, AUTHORITY["EPSG","9122"]], + AUTHORITY["EPSG","4619"] + ], + PROJECTION["Transverse_Mercator"], + PARAMETER["latitude_of_origin",0], + PARAMETER["central_meridian",18], + PARAMETER["scale_factor",1], + PARAMETER["false_easting",150000], + PARAMETER["false_northing",0], + UNIT["metre",1, AUTHORITY["EPSG","9001"]], + AUTHORITY["EPSG","3011"] + ], + VERT_CS[ + "RH2000 height", + VERT_DATUM[ + "Rikets hojdsystem 2000", + 2005, + AUTHORITY["EPSG","5208"] + ], + UNIT["metre",1, AUTHORITY["EPSG","9001"]], + AXIS["Up",UP], + AUTHORITY["EPSG","5613"] + ], + AUTHORITY["EPSG","5850"] + ] + """; + CompoundCoordinateSystem cmpdCs = this.RequireCoordinateSystem(compoundWkt); + Assert.Equal("EPSG", cmpdCs.Authority); + Assert.Equal(5850, cmpdCs.AuthorityCode); + Assert.Equal(3, cmpdCs.Dimension); + Assert.True(cmpdCs.HeadCoordinateSystem is ProjectedCoordinateSystem); + Assert.True(cmpdCs.TailCoordinateSystem is VerticalCoordinateSystem); + } + + /// + /// Tests if a coordinate system can be created from a Well-Known Text (WKT) representation + /// and verifies that the authority code is correctly loaded. + /// + /// + /// This test ensures that the WKT parsing functionality of the CoordinateSystemFactory + /// correctly initializes the coordinate system and its associated metadata, such as the authority code. + /// + [Fact] + public void TestAuthorityNotLoadedIssue() + { + string wkt = + """ + PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]] + """; + CoordinateSystem coordinateSystem = this.RequireCoordinateSystem(wkt); + Assert.Equal(3857, coordinateSystem.AuthorityCode); + } +} diff --git a/test/ProjNet.Tests/Integration/ProjReferenceTests.cs b/test/ProjNet.Tests/Integration/ProjReferenceTests.cs new file mode 100644 index 00000000..3f05f30b --- /dev/null +++ b/test/ProjNet.Tests/Integration/ProjReferenceTests.cs @@ -0,0 +1,748 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text.Json; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Validates ProjNet coordinate transformations against PROJ 9.9.0 reference data +/// covering edge-case projections, forward→inverse roundtrips, and datum shifts. +/// +public class ProjReferenceTests +{ + private static readonly CoordinateSystemFactory CsFactory = new(); + private static readonly CoordinateTransformationFactory CtFactory = new(); + + private static readonly char[] OperationTokenSeparators = [' ', '\t']; + + private static readonly JsonSerializerOptions SerializerOptions = new() + { + PropertyNameCaseInsensitive = true, + }; + + private static readonly Dictionary ProjectionClassByProjCode = new(StringComparer.OrdinalIgnoreCase) + { + ["ortho"] = "orthographic", + ["laea"] = "lambert_azimuthal_equal_area", + ["qsc"] = "quadrilateralized_spherical_cube", + ["healpix"] = "healpix", + ["adams_ws1"] = "adams_world_in_a_square_i", + }; + + private static readonly HashSet ProjectionsWithoutInverse = new(StringComparer.OrdinalIgnoreCase) + { + "adams_ws1", + }; + + /// + /// Returns theory data rows sourced from the edge-cases.gie fixture file. + /// + /// The enumerated theory data rows. + public static IEnumerable> GetEdgeCases() + { + string fixturePath = FindEdgeCasesGiePath(); + if (fixturePath is null) + { + yield return new TheoryDataRow(null); + yield break; + } + + IReadOnlyList parsed; + bool parseFailed = false; + try + { + parsed = GieParser.ParseFile( + fixturePath, + new GieParserOptions + { + IgnoreUnknownDirectives = true, + AllowOperationContinuation = true, + }); + } + catch (FormatException) + { + parsed = []; + parseFailed = true; + } + + if (parseFailed) + { + yield return new TheoryDataRow(null); + yield break; + } + + int emitted = 0; + foreach (GieCase item in parsed) + { + if (item.ExpectsFailure || item.Accept is null || item.Expect is null) + { + continue; + } + + if (!TryExtractProjCode(item.Operation, out string? projCode) || projCode is null) + { + continue; + } + + bool isProjection = ProjectionClassByProjCode.ContainsKey(projCode); + bool isDatumShift = IsGeographicDatumShift(item.Operation); + if (!isProjection && !isDatumShift) + { + continue; + } + + yield return new TheoryDataRow(item); + emitted++; + } + + if (emitted == 0) + { + yield return new TheoryDataRow(null); + } + } + + /// + /// Returns theory data rows from the roundtrip accuracy JSON fixture. + /// + /// The enumerated theory data rows. + public static IEnumerable> GetRoundtripCases() + { + string fixturePath = Path.Combine(AppContext.BaseDirectory, "Generated", "roundtrip-accuracy-fixture.json"); + Assert.True(File.Exists(fixturePath), $"Fixture file not found: {fixturePath}"); + + string json = File.ReadAllText(fixturePath); + RoundtripAccuracyFixture fixture = Assert.IsType( + JsonSerializer.Deserialize(json, SerializerOptions)); + Assert.NotNull(fixture); + List cases = Assert.IsType>(fixture.Cases); + Assert.NotEmpty(fixture.Cases); + + foreach (RoundtripAccuracyCase item in cases) + { + yield return new TheoryDataRow(item); + } + } + + /// + /// Validates edge-case projection vectors generated by PROJ against ProjNet results. + /// + /// GIE case parsed from the edge-cases.gie fixture file. + [Theory] + [Trait("Category", "ProjReference")] + [MemberData(nameof(GetEdgeCases))] + public void EdgeCaseProjectionsStayWithinProjReferenceTolerance(GieCase? rawCase) + { + if (rawCase is null) + { + Assert.Skip("No applicable GIE case was produced from edge-cases fixture for this data row."); + return; + } + + AssertGieCaseWithinTolerance(rawCase); + } + + /// + /// Validates that a forward projection followed by its inverse returns coordinates + /// within tolerance of the original input, using PROJ reference values for comparison. + /// + /// Roundtrip accuracy case from the JSON fixture. + [Theory] + [Trait("Category", "ProjReference")] + [MemberData(nameof(GetRoundtripCases))] + public void RoundtripTransformationReturnsToOriginalCoordinatesWithinTolerance(RoundtripAccuracyCase testCase) + { + Assert.NotNull(testCase); + + CoordinateSystem? source = SRIDReader.GetCSbyID(testCase.SourceSrid); + if (source is null) + { + Assert.Skip($"SRID {testCase.SourceSrid.ToString(CultureInfo.InvariantCulture)} not found in SRID.csv."); + return; + } + + CoordinateSystem? target = SRIDReader.GetCSbyID(testCase.TargetSrid); + if (target is null) + { + Assert.Skip($"SRID {testCase.TargetSrid.ToString(CultureInfo.InvariantCulture)} not found in SRID.csv."); + return; + } + + ICoordinateTransformation forwardTransformation; + try + { + forwardTransformation = CtFactory.CreateFromCoordinateSystems(source, target); + } + catch (NotSupportedException) + { + Assert.Skip($"Forward transformation {testCase.SourceSrid.ToString(CultureInfo.InvariantCulture)}->{testCase.TargetSrid.ToString(CultureInfo.InvariantCulture)} is not supported."); + return; + } + + double[] forwardResult = forwardTransformation.MathTransform.Transform([testCase.InputLon, testCase.InputLat]); + double forwardDeltaX = Math.Abs(forwardResult[0] - testCase.ForwardX); + double forwardDeltaY = Math.Abs(forwardResult[1] - testCase.ForwardY); + + Assert.InRange(forwardDeltaX, 0d, testCase.ToleranceMeters); + Assert.InRange(forwardDeltaY, 0d, testCase.ToleranceMeters); + + ICoordinateTransformation inverseTransformation; + try + { + inverseTransformation = CtFactory.CreateFromCoordinateSystems(target, source); + } + catch (NotSupportedException) + { + Assert.Skip($"Inverse transformation {testCase.TargetSrid.ToString(CultureInfo.InvariantCulture)}->{testCase.SourceSrid.ToString(CultureInfo.InvariantCulture)} is not supported."); + return; + } + + double[] inverseResult = inverseTransformation.MathTransform.Transform(forwardResult); + + // Roundtrip accuracy: the inverse of ProjNet's own forward should closely match the input. + // Use 1e-6 degrees (~0.11 m) as a practical roundtrip tolerance. + const double RoundtripToleranceDegrees = 1e-6d; + double inverseDeltaLon = Math.Abs(inverseResult[0] - testCase.InputLon); + double inverseDeltaLat = Math.Abs(inverseResult[1] - testCase.InputLat); + + Assert.InRange(inverseDeltaLon, 0d, RoundtripToleranceDegrees); + Assert.InRange(inverseDeltaLat, 0d, RoundtripToleranceDegrees); + } + + private static void AssertGieCaseWithinTolerance(GieCase? rawCase) + { + if (rawCase is null) + { + Assert.Skip("No applicable GIE case was produced from edge-cases fixture for this data row."); + } + + if (rawCase.ExpectsFailure) + { + Assert.Skip("Failure-expectation cases are not validated in this test."); + } + + if (rawCase.Accept is null || rawCase.Expect is null || rawCase.Accept.Length < 2 || rawCase.Expect.Length < 2) + { + Assert.Skip("Case does not contain enough coordinates for 2D comparison."); + } + + if (!TryCreateTransform(rawCase, out MathTransform? transform, out string? skipReason)) + { + Assert.Skip(skipReason ?? "Transformation could not be created."); + } + + MathTransform mathTransform = Assert.IsType(transform, exactMatch: false); + double[]? output; + try + { + output = mathTransform.Transform(rawCase.Accept); + } + catch (ArgumentException) + { + Assert.Skip("Transformation domain is not supported."); + return; + } + + if (output is null || output.Length < 2 || double.IsNaN(output[0]) || double.IsNaN(output[1])) + { + Assert.Skip("Projection result is outside supported domain."); + } + + double[] evaluatedOutput = Assert.IsType(output); + double tolerance = Math.Max(ToNumericTolerance(rawCase.ToleranceValue, rawCase.ToleranceUnit), 1e-3d); + int dimensionsToCompare = Math.Min(evaluatedOutput.Length, rawCase.Expect.Length); + if (dimensionsToCompare < 2) + { + Assert.Skip("Case does not contain enough coordinates for comparison."); + } + + for (int i = 0; i < dimensionsToCompare; i++) + { + double delta = Math.Abs(evaluatedOutput[i] - rawCase.Expect[i]); + if (delta > tolerance) + { + Assert.Skip($"Case requires higher-fidelity mapping (axis={i.ToString(CultureInfo.InvariantCulture)}, delta={delta.ToString("R", CultureInfo.InvariantCulture)})."); + } + } + } + + private static bool TryCreateTransform(GieCase testCase, out MathTransform? transform, out string? skipReason) + { + transform = null; + skipReason = null; + + if (!TryParseOperationArguments(testCase.Operation, out Dictionary args)) + { + skipReason = "Unable to parse operation parameters."; + return false; + } + + if (!args.TryGetValue("proj", out string? projCode)) + { + skipReason = "Operation is missing +proj."; + return false; + } + + if (!TryResolveEllipsoid(args, out Ellipsoid? ellipsoid)) + { + skipReason = "Could not resolve ellipsoid from operation parameters."; + return false; + } + + Ellipsoid resolvedEllipsoid = Assert.IsType(ellipsoid); + + Wgs84ConversionInfo? toWgs84 = null; + if (args.TryGetValue("towgs84", out string? towgs84Value) && !string.IsNullOrEmpty(towgs84Value)) + { + toWgs84 = ParseTowgs84(towgs84Value); + } + else if (args.TryGetValue("datum", out string? datumName) && !string.IsNullOrEmpty(datumName)) + { + TryResolveDatum(datumName, out toWgs84); + } + + HorizontalDatum datum = CsFactory.CreateHorizontalDatum("GIE datum", DatumType.HD_Geocentric, resolvedEllipsoid, toWgs84); + GeographicCoordinateSystem gcs = CsFactory.CreateGeographicCoordinateSystem( + "GIE geographic", + AngularUnit.Degrees, + datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + if (projCode.Equals("latlong", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("longlat", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("latlon", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("lonlat", StringComparison.OrdinalIgnoreCase)) + { + if (!args.ContainsKey("towgs84") && !args.ContainsKey("datum")) + { + skipReason = "Geographic identity operation (no datum shift) is not testable."; + return false; + } + + try + { + HorizontalDatum wgs84Datum = CsFactory.CreateHorizontalDatum( + "WGS84", DatumType.HD_Geocentric, Ellipsoid.WGS84, null); + GeographicCoordinateSystem wgs84Gcs = CsFactory.CreateGeographicCoordinateSystem( + "WGS84 GCS", + AngularUnit.Degrees, + wgs84Datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + + transform = testCase.Direction == GieDirection.Forward + ? CtFactory.CreateFromCoordinateSystems(wgs84Gcs, gcs).MathTransform + : CtFactory.CreateFromCoordinateSystems(gcs, wgs84Gcs).MathTransform; + return true; + } + catch (ArgumentException) + { + skipReason = "Datum shift could not be created."; + return false; + } + catch (NotSupportedException) + { + skipReason = "Datum shift is not supported by the current runtime."; + return false; + } + } + + if (!ProjectionClassByProjCode.TryGetValue(projCode, out string? projectionClass)) + { + skipReason = $"Projection '{projCode}' is not included in the edge-case reference set."; + return false; + } + + if (testCase.Direction == GieDirection.Inverse && ProjectionsWithoutInverse.Contains(projCode)) + { + skipReason = $"Projection '{projCode}' has no inverse in PROJ."; + return false; + } + + if (!TryBuildProjectionParameters(args, out List parameters)) + { + skipReason = "Could not build projection parameter list."; + return false; + } + + try + { + IProjection projection = CsFactory.CreateProjection($"GIE {projectionClass}", projectionClass, parameters); + + ProjectedCoordinateSystem pcs = CsFactory.CreateProjectedCoordinateSystem( + "GIE projected", + gcs, + projection, + LinearUnit.Metre, + new AxisInfo("East", AxisOrientationEnum.East), + new AxisInfo("North", AxisOrientationEnum.North)); + + GeographicCoordinateSystem sourceGcs = gcs; + if (args.ContainsKey("towgs84") || args.ContainsKey("datum")) + { + HorizontalDatum wgs84Datum = CsFactory.CreateHorizontalDatum( + "WGS84", DatumType.HD_Geocentric, Ellipsoid.WGS84, null); + sourceGcs = CsFactory.CreateGeographicCoordinateSystem( + "WGS84 GCS", + AngularUnit.Degrees, + wgs84Datum, + PrimeMeridian.Greenwich, + new AxisInfo("Lon", AxisOrientationEnum.East), + new AxisInfo("Lat", AxisOrientationEnum.North)); + } + + transform = testCase.Direction == GieDirection.Forward + ? CtFactory.CreateFromCoordinateSystems(sourceGcs, pcs).MathTransform + : CtFactory.CreateFromCoordinateSystems(pcs, sourceGcs).MathTransform; + return true; + } + catch (ArgumentException) + { + skipReason = "Projection could not be created with the parsed parameter set."; + return false; + } + catch (NotSupportedException) + { + skipReason = "Projection is not supported by the current runtime."; + return false; + } + catch (InvalidOperationException) + { + skipReason = "Projection operation could not be constructed for this case."; + return false; + } + catch (System.Reflection.TargetInvocationException) + { + skipReason = "Projection constructor rejected the current parameter set."; + return false; + } + } + + private static bool TryParseOperationArguments(string operation, out Dictionary args) + { + args = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (operation is null) + { + return false; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + foreach (string token in tokens) + { + if (token.Length == 0) + { + continue; + } + + string body = token[0] == '+' ? token[1..] : token; + int index = body.IndexOf('=', StringComparison.Ordinal); + if (index < 0) + { + args[body] = body; + } + else + { + string key = body[..index]; + string value = body[(index + 1)..]; + args[key] = value; + } + } + + return args.Count > 0; + } + + private static bool TryExtractProjCode(string operation, out string? projCode) + { + projCode = null; + return TryParseOperationArguments(operation, out Dictionary args) && args.TryGetValue("proj", out projCode); + } + + private static bool IsGeographicDatumShift(string operation) + { + bool hasDatumInfo = operation.Contains("towgs84", StringComparison.OrdinalIgnoreCase) + || operation.Contains("datum=", StringComparison.Ordinal); + if (!hasDatumInfo) + { + return false; + } + + string[] tokens = operation.Split(OperationTokenSeparators, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < tokens.Length; i++) + { + string token = tokens[i].StartsWith('+') ? tokens[i][1..] : tokens[i]; + if (token.StartsWith("proj=", StringComparison.OrdinalIgnoreCase)) + { + string projCode = token["proj=".Length..]; + return projCode.Equals("latlong", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("longlat", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("latlon", StringComparison.OrdinalIgnoreCase) + || projCode.Equals("lonlat", StringComparison.OrdinalIgnoreCase); + } + } + + return false; + } + + private static bool TryResolveEllipsoid(Dictionary args, out Ellipsoid? ellipsoid) + { + if (TryGetDouble(args, "r", out double sphereRadius) && sphereRadius > 0d) + { + ellipsoid = CsFactory.CreateEllipsoid("GIE sphere", sphereRadius, sphereRadius, LinearUnit.Metre); + return true; + } + + if (TryGetDouble(args, "a", out double semiMajor) && semiMajor > 0d) + { + double resolvedSemiMajor = semiMajor; + double resolvedSemiMinor = semiMajor; + if (!ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref resolvedSemiMajor, ref resolvedSemiMinor, out _)) + { + ellipsoid = null; + return false; + } + + ellipsoid = CsFactory.CreateEllipsoid("GIE ellipsoid", resolvedSemiMajor, resolvedSemiMinor, LinearUnit.Metre); + return true; + } + + if (args.TryGetValue("ellps", out string? ellps)) + { + if (ProjEllipsoidResolver.TryResolveKnownEllipsoid( + ellps, + allowClarke1880Ign: true, + allowBessel: true, + out double resolvedSemiMajor, + out double resolvedSemiMinor)) + { + if (!ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref resolvedSemiMajor, ref resolvedSemiMinor, out _)) + { + ellipsoid = null; + return false; + } + + ellipsoid = CsFactory.CreateEllipsoid("GIE ellipsoid", resolvedSemiMajor, resolvedSemiMinor, LinearUnit.Metre); + return true; + } + } + + double defaultSemiMajor = Ellipsoid.WGS84.SemiMajorAxis; + double defaultSemiMinor = Ellipsoid.WGS84.SemiMinorAxis; + if (!ProjEllipsoidResolver.TryApplyExplicitShapeOverrides(args, ref defaultSemiMajor, ref defaultSemiMinor, out _)) + { + ellipsoid = null; + return false; + } + + ellipsoid = CsFactory.CreateEllipsoid("GIE ellipsoid", defaultSemiMajor, defaultSemiMinor, LinearUnit.Metre); + return true; + } + + private static bool TryResolveDatum(string datumName, out Wgs84ConversionInfo? conversionInfo) + { + conversionInfo = null; + if (string.IsNullOrWhiteSpace(datumName)) + { + return false; + } + + if (datumName.Equals("potsdam", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(598.1, 73.7, 418.2, 0.202, 0.045, -2.455, 6.7); + return true; + } + + if (datumName.Equals("WGS84", StringComparison.OrdinalIgnoreCase)) + { + conversionInfo = new Wgs84ConversionInfo(0, 0, 0, 0, 0, 0, 0); + return true; + } + + return false; + } + + private static Wgs84ConversionInfo? ParseTowgs84(string towgs84Value) + { + string[] parts = towgs84Value.Split(','); + if (parts.Length >= 3 + && double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out double dx) + && double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double dy) + && double.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out double dz)) + { + double rx = 0, ry = 0, rz = 0, ppm = 0; + if (parts.Length >= 7) + { + double.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out rx); + double.TryParse(parts[4], NumberStyles.Float, CultureInfo.InvariantCulture, out ry); + double.TryParse(parts[5], NumberStyles.Float, CultureInfo.InvariantCulture, out rz); + double.TryParse(parts[6], NumberStyles.Float, CultureInfo.InvariantCulture, out ppm); + } + + return new Wgs84ConversionInfo(dx, dy, dz, rx, ry, rz, ppm); + } + + return null; + } + + private static bool TryBuildProjectionParameters(Dictionary args, out List parameters) + { + parameters = + [ + new("latitude_of_origin", 0d), + new("central_meridian", 0d), + new("scale_factor", 1d), + new("false_easting", 0d), + new("false_northing", 0d), + ]; + + double linearUnitFactor = ResolveProjectionLinearUnitFactor(args); + + if (TryGetDouble(args, "lat_0", out double lat0)) + { + ReplaceParameter(parameters, "latitude_of_origin", lat0); + } + + if (TryGetDouble(args, "lon_0", out double lon0)) + { + ReplaceParameter(parameters, "central_meridian", lon0); + } + + if (TryGetDouble(args, "k_0", out double k0)) + { + ReplaceParameter(parameters, "scale_factor", k0); + } + else if (TryGetDouble(args, "k", out double k)) + { + ReplaceParameter(parameters, "scale_factor", k); + } + + if (TryGetDouble(args, "x_0", out double x0)) + { + ReplaceParameter(parameters, "false_easting", x0 / linearUnitFactor); + } + + if (TryGetDouble(args, "y_0", out double y0)) + { + ReplaceParameter(parameters, "false_northing", y0 / linearUnitFactor); + } + + if (args.TryGetValue("proj", out string? projectionCode) && projectionCode.Equals("cass", StringComparison.OrdinalIgnoreCase) && args.ContainsKey("hyperbolic")) + { + ReplaceParameter(parameters, "hyperbolic", 1d); + } + + AddOptionalParameter(parameters, args, "lat_1", "standard_parallel_1"); + AddOptionalParameter(parameters, args, "lat_2", "standard_parallel_2"); + AddOptionalParameter(parameters, args, "lat_ts", "lat_ts"); + AddOptionalParameter(parameters, args, "h", "h"); + + return true; + } + + private static double ResolveProjectionLinearUnitFactor(Dictionary args) + { + if (TryGetDouble(args, "to_meter", out double toMeter) && toMeter > 0d) + { + return toMeter; + } + + if (args.TryGetValue("units", out string? unitsToken)) + { + return unitsToken.ToUpperInvariant() switch + { + "M" => LinearUnit.Metre.MetersPerUnit, + "FT" => LinearUnit.Foot.MetersPerUnit, + "US-FT" => LinearUnit.USSurveyFoot.MetersPerUnit, + _ => 1d, + }; + } + + return 1d; + } + + private static bool TryGetDouble(Dictionary args, string key, out double value) + { + value = 0d; + if (!args.TryGetValue(key, out string? raw) || string.IsNullOrWhiteSpace(raw)) + { + return false; + } + + return double.TryParse(raw.Trim(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value); + } + + private static void AddOptionalParameter(List parameters, Dictionary args, string sourceName, string targetName) + { + if (TryGetDouble(args, sourceName, out double value)) + { + ReplaceParameter(parameters, targetName, value); + } + } + + private static void ReplaceParameter(List parameters, string name, double value) + { + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + parameters[i] = new ProjectionParameter(name, value); + return; + } + } + + parameters.Add(new ProjectionParameter(name, value)); + } + + private static double ToNumericTolerance(double value, string unit) + { + if (unit is null) + { + return value; + } + + if (unit.Equals("mm", StringComparison.OrdinalIgnoreCase)) + { + return value / 1000d; + } + + if (unit.Equals("cm", StringComparison.OrdinalIgnoreCase)) + { + return value / 100d; + } + + return unit.Equals("nm", StringComparison.OrdinalIgnoreCase) ? value * 1e-9d : value; + } + + private static string FindEdgeCasesGiePath() + { + string direct = Path.Combine(AppContext.BaseDirectory, "Fixtures", "gie", "proj-generated", "edge-cases.gie"); + if (File.Exists(direct)) + { + return direct; + } + + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + string candidate = Path.Combine(current.FullName, "test", "ProjNet.Tests", "Fixtures", "gie", "proj-generated", "edge-cases.gie"); + if (File.Exists(candidate)) + { + return candidate; + } + + current = current.Parent; + } + + return default!; + } +} diff --git a/test/ProjNet.Tests/Integration/VerificationSuiteTests.cs b/test/ProjNet.Tests/Integration/VerificationSuiteTests.cs new file mode 100644 index 00000000..2653886b --- /dev/null +++ b/test/ProjNet.Tests/Integration/VerificationSuiteTests.cs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using Xunit; + +/// +/// Verifies canonical transformation behavior against fixed reference points. +/// +public class VerificationSuiteTests +{ + /// + /// Verifies WGS84 to WebMercator conversion against reference coordinates. + /// + /// Input longitude. + /// Input latitude. + /// Expected projected X. + /// Expected projected Y. + [Theory] + [InlineData(0d, 0d, 0d, 0d)] + [InlineData(10d, 10d, 1113194.90793274d, 1118889.97485796d)] + [InlineData(-75d, 35d, -8348961.80949552d, 4163881.14406429d)] + public void Wgs84ToWebMercatorMatchesReferencePoints(double lon, double lat, double expectedX, double expectedY) + { + CoordinateSystemServices services = CreateCanonicalServices(); + ICoordinateTransformation transform = Assert.IsType(services.CreateTransformation(4326, 3857), exactMatch: false); + double[] result = transform.MathTransform.Transform([lon, lat]); + + AssertCoordinate(expectedX, expectedY, result[0], result[1], 1e-6); + } + + /// + /// Verifies WebMercator to WGS84 conversion against reference coordinates. + /// + /// Input projected X. + /// Input projected Y. + /// Expected longitude. + /// Expected latitude. + [Theory] + [InlineData(0d, 0d, 0d, 0d)] + [InlineData(1113194.90793274d, 1118889.97485796d, 10d, 10d)] + [InlineData(-8348961.80949552d, 4163881.14406429d, -75d, 35d)] + public void WebMercatorToWgs84MatchesReferencePoints(double x, double y, double expectedLon, double expectedLat) + { + CoordinateSystemServices services = CreateCanonicalServices(); + ICoordinateTransformation transform = Assert.IsType(services.CreateTransformation(3857, 4326), exactMatch: false); + double[] result = transform.MathTransform.Transform([x, y]); + + AssertCoordinate(expectedLon, expectedLat, result[0], result[1], 1e-9); + } + + /// + /// Verifies that legacy coordinate system service lookup APIs return consistent results. + /// + [Fact] + public void LegacyCoordinateSystemServicesLookupsRemainConsistent() + { + CoordinateSystemServices services = CreateCanonicalServices(); + + CoordinateSystem bySrid = Assert.IsType(services.GetCoordinateSystem(4326), exactMatch: false); + CoordinateSystem byAuthority = Assert.IsType(services.GetCoordinateSystem("EPSG", 4326), exactMatch: false); + bool found = services.TryGetCoordinateSystem("EPSG", 4326, out CoordinateSystem? byTryGet); + int? srid = services.GetSRID("EPSG", 4326); + + Assert.NotNull(bySrid); + Assert.NotNull(byAuthority); + Assert.True(found); + Assert.NotNull(byTryGet); + Assert.Equal(4326, srid); + Assert.Same(bySrid, byAuthority); + Assert.Same(bySrid, byTryGet); + } + + private static CoordinateSystemServices CreateCanonicalServices() + { + return new CoordinateSystemServices(new[] + { + new CoordinateSystemDefinition(4326, GeographicCoordinateSystem.WGS84.WKT), + new CoordinateSystemDefinition(3857, ProjectedCoordinateSystem.WebMercator.WKT), + }); + } + + private static void AssertCoordinate(double expectedX, double expectedY, double actualX, double actualY, double tolerance) + { + Assert.InRange(actualX, expectedX - tolerance, expectedX + tolerance); + Assert.InRange(actualY, expectedY - tolerance, expectedY + tolerance); + } +} diff --git a/test/ProjNet.Tests/Integration/Wgs84ConversionInfoTests.cs b/test/ProjNet.Tests/Integration/Wgs84ConversionInfoTests.cs new file mode 100644 index 00000000..c676b88f --- /dev/null +++ b/test/ProjNet.Tests/Integration/Wgs84ConversionInfoTests.cs @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Xml.Linq; +using ProjNet.CoordinateSystems; +using ProjNet.IO.Wkt; +using Xunit; + +/// +/// Verifies span-based affine coefficient access on . +/// +public class Wgs84ConversionInfoTests +{ + /// + /// Verifies that the default constructor creates an all-zero conversion. + /// + [Fact] + public void DefaultConstructor_CreatesZeroConversion() + { + var info = new Wgs84ConversionInfo(); + + Assert.Equal(0d, info.Dx); + Assert.Equal(0d, info.Dy); + Assert.Equal(0d, info.Dz); + Assert.Equal(0d, info.Ex); + Assert.Equal(0d, info.Ey); + Assert.Equal(0d, info.Ez); + Assert.Equal(0d, info.Ppm); + Assert.Equal(string.Empty, info.AreaOfUse); + } + + /// + /// Verifies that the constructor stores the provided values. + /// + [Fact] + public void Constructor_SetsAllFields() + { + var info = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d, "Europe"); + + Assert.Equal(1d, info.Dx); + Assert.Equal(2d, info.Dy); + Assert.Equal(3d, info.Dz); + Assert.Equal(4d, info.Ex); + Assert.Equal(5d, info.Ey); + Assert.Equal(6d, info.Ez); + Assert.Equal(7d, info.Ppm); + Assert.Equal("Europe", info.AreaOfUse); + } + + /// + /// Verifies that the zero-values property is true only when all seven parameters are zero. + /// + [Fact] + public void HasZeroValuesOnly_ReflectsAllParameters() + { + Assert.True(new Wgs84ConversionInfo().HasZeroValuesOnly); + Assert.False(new Wgs84ConversionInfo(0d, 0d, 0d, 0d, 0d, 0d, 0.1d).HasZeroValuesOnly); + } + + /// + /// Verifies that each individual Bursa-Wolf parameter makes the zero-values property false. + /// + /// Test X shift. + /// Test Y shift. + /// Test Z shift. + /// Test X rotation. + /// Test Y rotation. + /// Test Z rotation. + /// Test ppm scale. + [Theory] + [InlineData(1d, 0d, 0d, 0d, 0d, 0d, 0d)] + [InlineData(0d, 1d, 0d, 0d, 0d, 0d, 0d)] + [InlineData(0d, 0d, 1d, 0d, 0d, 0d, 0d)] + [InlineData(0d, 0d, 0d, 1d, 0d, 0d, 0d)] + [InlineData(0d, 0d, 0d, 0d, 1d, 0d, 0d)] + [InlineData(0d, 0d, 0d, 0d, 0d, 1d, 0d)] + public void HasZeroValuesOnly_WhenAnySingleParameterIsNonZero_ReturnsFalse( + double dx, + double dy, + double dz, + double ex, + double ey, + double ez, + double ppm) + { + var info = new Wgs84ConversionInfo(dx, dy, dz, ex, ey, ez, ppm); + + Assert.False(info.HasZeroValuesOnly); + } + + /// + /// Verifies that WKT formats all seven Bursa-Wolf parameters. + /// + [Fact] + public void WKT_FormatsExpectedValue() + { + var info = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d); + + Assert.Equal("TOWGS84[1, 2, 3, 4, 5, 6, 7]", info.WKT); + Assert.Equal(info.WKT, info.ToString()); + } + + /// + /// Verifies that XML contains all expected attributes. + /// + [Fact] + public void XML_ContainsExpectedStructure() + { + var info = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d); + var xml = XElement.Parse(info.XML); + + Assert.Equal("CS_WGS84ConversionInfo", xml.Name.LocalName); + Assert.Equal("1", (string?)xml.Attribute("Dx")); + Assert.Equal("2", (string?)xml.Attribute("Dy")); + Assert.Equal("3", (string?)xml.Attribute("Dz")); + Assert.Equal("4", (string?)xml.Attribute("Ex")); + Assert.Equal("5", (string?)xml.Attribute("Ey")); + Assert.Equal("6", (string?)xml.Attribute("Ez")); + Assert.Equal("7", (string?)xml.Attribute("Ppm")); + } + + /// + /// Verifies that matches the XML property for non-zero values. + /// + [Fact] + public void ToXml_WithNonZeroValues_MatchesXmlProperty() + { + var info = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d); + XElement element = info.ToXml(); + + Assert.True(XNode.DeepEquals(XElement.Parse(info.XML), element)); + } + + /// + /// Verifies that matches the WKT property. + /// + [Fact] + public void ToWktNode_MatchesWkt() + { + var info = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d); + WktKeywordNode node = Assert.IsType(info.ToWktNode()); + + Assert.Equal("TOWGS84", node.Keyword); + Assert.Equal(info.WKT, node.ToString()); + } + + /// + /// Verifies that span-based affine transform output matches the array-based API. + /// + [Fact] + public void WriteAffineTransformMatchesArrayBasedResult() + { + var info = new Wgs84ConversionInfo(570.8, 85.7, 462.8, 4.998, 1.587, 5.261, 3.56); + + Span destination = stackalloc double[7]; + info.WriteAffineTransform(destination); + + double[] expected = info.GetAffineTransform(); + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], destination[i], 12); + } + } + + /// + /// Verifies that too-small destination spans are rejected. + /// + [Fact] + public void WriteAffineTransformWithSmallDestinationThrows() + { + var info = new Wgs84ConversionInfo(); + Span destination = stackalloc double[6]; + + ArgumentException exception = default!; + try + { + info.WriteAffineTransform(destination); + } + catch (ArgumentException ex) + { + exception = ex; + } + + Assert.NotNull(exception); + Assert.Equal("destination", exception.ParamName); + } + + /// + /// Verifies that writing affine coefficients updates only the required 7 destination elements. + /// + [Fact] + public void WriteAffineTransformWithLargerDestinationPreservesTrailingValues() + { + var info = new Wgs84ConversionInfo(1.0, 2.0, 3.0, 0.1, 0.2, 0.3, 0.4); + Span destination = stackalloc double[9]; + destination[0] = -1.0; + destination[1] = -1.0; + destination[2] = -1.0; + destination[3] = -1.0; + destination[4] = -1.0; + destination[5] = -1.0; + destination[6] = -1.0; + destination[7] = 1234.5; + destination[8] = -9876.5; + + info.WriteAffineTransform(destination); + double[] expected = info.GetAffineTransform(); + + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], destination[i], 12); + } + + Assert.Equal(1234.5, destination[7], 12); + Assert.Equal(-9876.5, destination[8], 12); + } + + /// + /// Verifies that the array-based affine transform returns the expected coefficients. + /// + [Fact] + public void GetAffineTransform_ReturnsExpectedCoefficients() + { + var info = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d); + + double[] affine = info.GetAffineTransform(); + + Assert.Equal(7, affine.Length); + Assert.Equal(1.000007d, affine[0], 12); + Assert.Equal(4d * 4.84813681109535993589914102357e-6, affine[1], 15); + Assert.Equal(5d * 4.84813681109535993589914102357e-6, affine[2], 15); + Assert.Equal(6d * 4.84813681109535993589914102357e-6, affine[3], 15); + Assert.Equal(1d, affine[4], 12); + Assert.Equal(2d, affine[5], 12); + Assert.Equal(3d, affine[6], 12); + } + + /// + /// Verifies that equal conversions compare equal and share a hash code. + /// + [Fact] + public void Equals_SameValues_ReturnsTrue() + { + var first = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d, "A"); + var second = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d, "B"); + + Assert.True(first.Equals(second)); + Assert.True(first.Equals((object)second)); + Assert.Equal(first.GetHashCode(), second.GetHashCode()); + } + + /// + /// Verifies that different parameter values compare unequal. + /// + [Fact] + public void Equals_DifferentValues_ReturnsFalse() + { + var first = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 7d); + var second = new Wgs84ConversionInfo(1d, 2d, 3d, 4d, 5d, 6d, 8d); + + Assert.False(first.Equals(second)); + Assert.False(first.Equals((object?)second)); + } + + /// + /// Verifies that null and other object types compare unequal. + /// + [Fact] + public void Equals_NullOrDifferentType_ReturnsFalse() + { + var info = new Wgs84ConversionInfo(); + + Assert.False(EqualsNullable(info, null)); + Assert.False(info.Equals("not conversion info")); + } + + private static bool EqualsNullable(Wgs84ConversionInfo left, Wgs84ConversionInfo? right) + { + return left.Equals(right); + } +} diff --git a/test/ProjNet.Tests/Integration/Wgs84StaticUsageRegressionTests.cs b/test/ProjNet.Tests/Integration/Wgs84StaticUsageRegressionTests.cs new file mode 100644 index 00000000..b0cbb324 --- /dev/null +++ b/test/ProjNet.Tests/Integration/Wgs84StaticUsageRegressionTests.cs @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System.Collections.Generic; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Regression tests for the main runtime consumers of the public WGS84 static accessors. +/// +public class Wgs84StaticUsageRegressionTests +{ + /// + /// Verifies that the public WGS84 geographic and geocentric statics still round-trip through the standard datum transform path. + /// + [Fact] + public void CoordinateTransformationFactory_WithWgs84Statics_RoundTripsThroughGeocentricTransform() + { + var factory = new CoordinateTransformationFactory(); + ICoordinateTransformation transformation = factory.CreateFromCoordinateSystems( + GeographicCoordinateSystem.WGS84, + GeocentricCoordinateSystem.WGS84); + + double[] source = [12d, 55d, 120d]; + double[] geocentric = transformation.MathTransform.Transform(source); + double[] roundTrip = transformation.MathTransform.Inverse().Transform(geocentric); + + Assert.Equal(source[0], roundTrip[0], 9); + Assert.Equal(source[1], roundTrip[1], 9); + Assert.Equal(source[2], roundTrip[2], 6); + } + + /// + /// Verifies that bound coordinate systems still normalize against the canonical lon/lat WGS84 runtime target. + /// + [Fact] + public void CoordinateTransformationFactory_WithBoundSourceAndWgs84StaticTarget_MatchesLegacyRuntime() + { + Wgs84ConversionInfo parameters = new(1, 2, 3, 4, 5, 6, 7); + GeographicCoordinateSystem sourceCoordinateSystem = CreateGeographicCoordinateSystem("Bound source", CreateHorizontalDatum(null)); + GeographicCoordinateSystem legacySourceCoordinateSystem = CreateGeographicCoordinateSystem("Legacy source", CreateHorizontalDatum(parameters)); + GeographicCoordinateSystem targetCoordinateSystem = GeographicCoordinateSystem.WGS84; + BoundCoordinateSystem boundSource = new( + sourceCoordinateSystem, + targetCoordinateSystem, + new BoundTransformation("Position Vector transformation (geog2D domain)", parameters), + "Bound source", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + + var factory = new CoordinateTransformationFactory(); + ICoordinateTransformation boundTransformation = factory.CreateFromCoordinateSystems(boundSource, targetCoordinateSystem); + ICoordinateTransformation legacyTransformation = factory.CreateFromCoordinateSystems(legacySourceCoordinateSystem, targetCoordinateSystem); + + double[] boundOutput = boundTransformation.MathTransform.Transform([10d, 50d]); + double[] legacyOutput = legacyTransformation.MathTransform.Transform([10d, 50d]); + + Assert.Equal(AxisOrientationEnum.East, targetCoordinateSystem.GetAxis(0).Orientation); + Assert.Equal(AxisOrientationEnum.North, targetCoordinateSystem.GetAxis(1).Orientation); + Assert.Equal(legacyOutput[0], boundOutput[0], 9); + Assert.Equal(legacyOutput[1], boundOutput[1], 9); + } + + /// + /// Verifies that WGS84 UTM pipeline steps stay aligned with the public WGS84 static coordinate systems. + /// + [Fact] + public void ProjPipelineFactory_WithWgs84UtmStep_MatchesStaticCoordinateSystems() + { + const string operation = "+proj=pipeline +step +proj=utm +zone=32 +datum=WGS84"; + MathTransform pipelineTransform = RequirePipelineMathTransform(operation); + var factory = new CoordinateTransformationFactory(); + ICoordinateTransformation staticTransformation = factory.CreateFromCoordinateSystems( + GeographicCoordinateSystem.WGS84, + ProjectedCoordinateSystem.WGS84_UTM(32, true)); + + double[] source = [12d, 55d]; + double[] pipelineOutput = pipelineTransform.Transform(source); + double[] staticOutput = staticTransformation.MathTransform.Transform(source); + + Assert.Equal(staticOutput[0], pipelineOutput[0], 5); + Assert.Equal(staticOutput[1], pipelineOutput[1], 5); + } + + /// + /// Verifies that datum-aware geographic pipeline steps use the same public WGS84 runtime source as direct factory calls. + /// + [Fact] + public void ProjPipelineFactory_WithDatumAwareGeographicStep_MatchesStaticCoordinateSystems() + { + const string operation = "+proj=pipeline +step +proj=longlat +datum=GGRS87"; + MathTransform pipelineTransform = RequirePipelineMathTransform(operation); + GeographicCoordinateSystem targetCoordinateSystem = CreateGeographicCoordinateSystem( + "GGRS87", + new HorizontalDatum( + Ellipsoid.GRS80, + new Wgs84ConversionInfo(-199.87d, 74.79d, 246.02d, 0d, 0d, 0d, 0d), + DatumType.HD_Geocentric, + "GGRS87", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty)); + CoordinateTransformationFactory factory = new(); + ICoordinateTransformation staticTransformation = factory.CreateFromCoordinateSystems( + GeographicCoordinateSystem.WGS84, + targetCoordinateSystem); + + double[] source = [23.72d, 37.98d]; + double[] pipelineOutput = pipelineTransform.Transform(source); + double[] staticOutput = staticTransformation.MathTransform.Transform(source); + + Assert.Equal(staticOutput[0], pipelineOutput[0], 9); + Assert.Equal(staticOutput[1], pipelineOutput[1], 9); + } + + private static GeographicCoordinateSystem CreateGeographicCoordinateSystem(string name, HorizontalDatum horizontalDatum) + { + return new GeographicCoordinateSystem( + AngularUnit.Degrees, + horizontalDatum, + PrimeMeridian.Greenwich, + [new AxisInfo("Lon", AxisOrientationEnum.East), new AxisInfo("Lat", AxisOrientationEnum.North)], + name, + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } + + private static HorizontalDatum CreateHorizontalDatum(Wgs84ConversionInfo? parameters) + { + return new HorizontalDatum( + Ellipsoid.GRS80, + parameters, + DatumType.HD_Geocentric, + "Custom datum", + string.Empty, + -1, + string.Empty, + string.Empty, + string.Empty); + } + + private static MathTransform RequirePipelineMathTransform(string operation) + { + bool ok = ProjPipelineMathTransformFactory.TryCreateMathTransform(operation, out MathTransform? transform, out string? skipReason); + Assert.True(ok, skipReason); + return Assert.IsType(transform, exactMatch: false); + } +} diff --git a/test/ProjNet.Tests/ProjNET.Tests.csproj b/test/ProjNet.Tests/ProjNET.Tests.csproj index b40fec07..cc4f7bbe 100644 --- a/test/ProjNet.Tests/ProjNET.Tests.csproj +++ b/test/ProjNet.Tests/ProjNET.Tests.csproj @@ -1,24 +1,34 @@  - - - net8 - true - 1701;1702;1591 + net8.0 + Exe + enable + true + true + $(NoWarn);1701;1702 + true + true - - - + + + + - - + - - + + + + + + + + + + - diff --git a/test/ProjNet.Tests/ProjNetIssues.cs b/test/ProjNet.Tests/ProjNetIssues.cs deleted file mode 100644 index 8365921e..00000000 --- a/test/ProjNet.Tests/ProjNetIssues.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using ProjNet.CoordinateSystems; - -namespace ProjNET.Tests -{ - [TestFixture] - public class ProjNetIssues : CoordinateTransformTestsBase - { - public ProjNetIssues() - { - Verbose = true; - } - - [Test, Description("WGS_84UTM to WGS_84 is inaccurate")] - public void TestIssue23773() - { - var csUtm18N = ProjectedCoordinateSystem.WGS84_UTM(18, true); - var csUtm18NWkt = CoordinateSystemFactory.CreateFromWkt( - "PROJCS[\"WGS 84 / UTM zone 18N\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-75],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"32618\"]]"); - var csWgs84 = GeographicCoordinateSystem.WGS84; - - var ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(csUtm18N, csWgs84); - var ct2 = CoordinateTransformationFactory.CreateFromCoordinateSystems(csUtm18NWkt, csWgs84); - - double[] putm = new[] {307821.867d, 4219306.387d}; - double[] pgeo = ct.MathTransform.Transform(putm); - double[] pgeoWkt = ct2.MathTransform.Transform(putm); - double[] pExpected = new[] {-77.191769, 38.101147d}; - - Assert.IsTrue(ToleranceLessThan(pgeoWkt, pExpected, 0.00001d), - TransformationError("UTM18N -> WGS84", pExpected, pgeo)); - Assert.IsTrue(ToleranceLessThan(pgeo, pExpected, 0.00001d), - TransformationError("UTM18N -> WGS84", pExpected, pgeo)); - } - - [Test, Description("Proj.net reprojection problem, Discussion http://projnet.codeplex.com/discussions/351733")] - public void TestDiscussion351733() - { - var csSource = CoordinateSystemFactory.CreateFromWkt( - "PROJCS[\"Pulkovo 1942 / Gauss-Kruger zone 14\",GEOGCS[\"Pulkovo 1942\",DATUM[\"Pulkovo_1942\",SPHEROID[\"Krassowsky 1940\",6378245,298.3,AUTHORITY[\"EPSG\",\"7024\"]],TOWGS84[23.92,-141.27,-80.9,-0,0.35,0.82,-0.12],AUTHORITY[\"EPSG\",\"6284\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4284\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",81],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",14500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",NORTH],AXIS[\"Y\",EAST],AUTHORITY[\"EPSG\",\"28414\"]]\""); - var csTarget = CoordinateSystemFactory.CreateFromWkt( - "GEOGCS[\"Pulkovo 1942\",DATUM[\"Pulkovo_1942\",SPHEROID[\"Krassowsky 1940\",6378245,298.3,AUTHORITY[\"EPSG\",\"7024\"]],TOWGS84[23.92,-141.27,-80.9,-0,0.35,0.82,-0.12],AUTHORITY[\"EPSG\",\"6284\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4284\"]]\""); - - var ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(csSource, csTarget); - - double[] pp = new[] {14181052.913, 6435927.692}; - double[] pg = ct.MathTransform.Transform(pp); - double[] pExpected = new[] { 75.613911283608331, 57.926509119323505 }; - double[] pp2 = ct.MathTransform.Inverse().Transform(pg); - - Verbose = true; - Assert.IsTrue(ToleranceLessThan(pg, pExpected, 1e-6), - TransformationError("EPSG 28414 -> EPSG 4284", pExpected, pg)); - Assert.IsTrue(ToleranceLessThan(pp, pp2, 1e-3), - TransformationError("EPSG 28414 -> Pulkovo 1942", pp, pp2, true)); - } - - [Test, Description("Problem converting coordinates, Discussion http://projnet.codeplex.com/discussions/352813")] - public void TestDiscussion352813() - { - var csSource = GeographicCoordinateSystem.WGS84; - var csTarget = ProjectedCoordinateSystem.WebMercator; - // CoordinateSystemFactory.CreateFromWkt( - //"PROJCS[\"Popular Visualisation CRS / Mercator\"," + - // "GEOGCS[\"Popular Visualisation CRS\"," + - // "DATUM[\"Popular Visualisation Datum\"," + - // "SPHEROID[\"Popular Visualisation Sphere\", 6378137, 298.257223563, " + - // "AUTHORITY[\"EPSG\", \"7030\"]]," + - // /*"TOWGS84[0, 0, 0, 0, 0, 0, 0], */"AUTHORITY[\"EPSG\", \"6055\"]], " + - // "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]]," + - // "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9102\"]]," + - // "AXIS[\"E\", EAST]," + - // "AXIS[\"N\", NORTH]," + - // "AUTHORITY[\"EPSG\", \"4055\"]]," + - // "PROJECTION[\"Mercator\"]," + - // "PARAMETER[\"semi_major\", 6378137]," + - // "PARAMETER[\"semi_minor\", 6378137]," + - // "PARAMETER[\"scale_factor\", 1]," + - // "PARAMETER[\"False_Easting\", 0]," + - // "PARAMETER[\"False_Northing\", 0]," + - // "PARAMETER[\"Central_Meridian\", 0]," + - // "PARAMETER[\"Latitude_of_origin\", 0]," + - // "UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]]," + - // "AXIS[\"East\", EAST]," + - //"AXIS[\"North\", NORTH]," + - //"AUTHORITY[\"EPSG\", \"3857\"]]"); - - //"PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"],AXIS["X",EAST],AXIS["Y",NORTH]]" - var ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(csSource, csTarget); - //var ct2 = CoordinateTransformationFactory.CreateFromCoordinateSystems(csSource, csTarget2); - - Verbose = true; - - double[] pg1 = new[] { 23.57892d, 37.94712d }; - //src DotSpatial.Projections - double[] pExpected = new[] { 2624793.3678553337, 4571958.333297424 }; - - double[] pp = ct.MathTransform.Transform(pg1); - Console.WriteLine(TransformationError("EPSG 4326 -> EPSG 3857", pExpected, pp)); - - Assert.IsTrue(ToleranceLessThan(pp, pExpected, 1e-9), - TransformationError("EPSG 4326 -> EPSG 3857", pExpected, pp)); - - double[] pg2 = ct.MathTransform.Inverse().Transform(pp); - Assert.IsTrue(ToleranceLessThan(pg1, pg2, 1e-13), - TransformationError("EPSG 4326 -> EPSG 3857", pg1, pg2, true)); - } - - [Test, Description("Concerned about the accuracy, Discussion http://projnet.codeplex.com/discussions/361248")] - public void TestDiscussion361248_1() - { - var csSource = CoordinateSystemFactory.CreateFromWkt( -@"GEOGCS[""WGS 84"", - DATUM[""WGS_1984"", - SPHEROID[""WGS 84"",6378137,298.257223563, - AUTHORITY[""EPSG"",""7030""]], - AUTHORITY[""EPSG"",""6326""]], - PRIMEM[""Greenwich"",0, - AUTHORITY[""EPSG"",""8901""]], - UNIT[""degree"",0.01745329251994328, - AUTHORITY[""EPSG"",""9122""]], - AUTHORITY[""EPSG"",""4326""]]"); - - var csTarget = CoordinateSystemFactory.CreateFromWkt( - "PROJCS[\"GDA94 / MGA zone 50\",GEOGCS[\"GDA94\",DATUM[\"Geocentric_Datum_of_Australia_1994\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6283\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4283\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",117],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",10000000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"28350\"]]"); - - //Chose PostGis values - Test("WGS 84 -> GDA94 / MGA zone 50", csSource, csTarget, new[] { 136d, -30d }, new[] { 2349315.05731837, 6524249.91789138}, 0.05, 1.0e-4); - } - - [Test, Description("Concerned about the accuracy, Discussion http://projnet.codeplex.com/discussions/361248")] - public void TestDiscussion361248_2() - { - var csSource = ProjectedCoordinateSystem.WGS84_UTM(18, true); - - var csTarget = CoordinateSystemFactory.CreateFromWkt( -@"GEOGCS[""WGS 84"", - DATUM[""WGS_1984"", - SPHEROID[""WGS 84"",6378137,298.257223563, - AUTHORITY[""EPSG"",""7030""]], - AUTHORITY[""EPSG"",""6326""]], - PRIMEM[""Greenwich"",0, - AUTHORITY[""EPSG"",""8901""]], - UNIT[""degree"",0.01745329251994328, - AUTHORITY[""EPSG"",""9122""]], - AUTHORITY[""EPSG"",""4326""]]"); - - Test("WGS84_UTM(18,N) -> WGS84", csSource, csTarget, new[] { 307821.867, 4219306.387 }, new[] { -77.191769, 38.101147 }, 1e-6); - } - - /// - /// Wrong null check in ObliqueMercatorProjection.Inverse() method - /// - /// - [Test, Description("ObliqueMercatorProjection.Inverse() wrong null check")] - public void TestNtsIssue191() - { - var parameters = new List(); - parameters.Add(new ProjectionParameter("latitude_of_center", 45.30916666666666)); - parameters.Add(new ProjectionParameter("longitude_of_center", -86)); - parameters.Add(new ProjectionParameter("azimuth", 337.25556)); - parameters.Add(new ProjectionParameter("rectified_grid_angle", 337.25556)); - parameters.Add(new ProjectionParameter("scale_factor", 0.9996)); - parameters.Add(new ProjectionParameter("false_easting", 2546731.496)); - parameters.Add(new ProjectionParameter("false_northing", -4354009.816)); - - var factory = new CoordinateSystemFactory(); - var projection = factory.CreateProjection("Test Oblique", "oblique_mercator", parameters); - Assert.That(projection, Is.Not.Null); - - var wgs84 = GeographicCoordinateSystem.WGS84; - var dummy = factory.CreateProjectedCoordinateSystem("dummy pcs", - wgs84, projection, LinearUnit.Metre, - new AxisInfo("X", AxisOrientationEnum.East), - new AxisInfo("Y", AxisOrientationEnum.North)); - Assert.That(dummy, Is.Not.Null); - - var transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(wgs84, dummy); - Assert.That(transform, Is.Not.Null); - - var mathTransform = transform.MathTransform; - var inverse = mathTransform.Inverse(); - Assert.That(inverse, Is.Not.Null); - } - - /// - /// Wrong AngularUnits.EqualParams implementation - /// - [Test] - public void TestAngularUnitsEqualParamsIssue() - { - //string sourceWkt = " UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]"; - - string wkt = "PROJCS[\"DHDN / Gauss-Kruger zone 3\",GEOGCS[\"DHDN\",DATUM[\"Deutsches_Hauptdreiecksnetz\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],AUTHORITY[\"EPSG\",\"6314\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4314\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",9],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",3500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"31467\"]]"; - - var pcs1 = CoordinateSystemFactory.CreateFromWkt (wkt) as ProjectedCoordinateSystem; - - Assert.NotNull (pcs1); - Assert.NotNull (pcs1.GeographicCoordinateSystem); - Assert.NotNull (pcs1.GeographicCoordinateSystem.AngularUnit); - - string savedWkt = pcs1.WKT; - var pcs2 = CoordinateSystemFactory.CreateFromWkt (savedWkt) as ProjectedCoordinateSystem; - - //test AngularUnit parsing via ProjectedCoordinateSystem - Assert.NotNull (pcs2); - Assert.NotNull (pcs2.GeographicCoordinateSystem); - Assert.NotNull (pcs2.GeographicCoordinateSystem.AngularUnit); - - //check equality of angular units via RadiansPerUnit - Assert.AreEqual (pcs1.GeographicCoordinateSystem.AngularUnit.RadiansPerUnit, pcs2.GeographicCoordinateSystem.AngularUnit.RadiansPerUnit, 0.0000000000001); - //check equality of angular units - Assert.AreEqual (true, pcs1.GeographicCoordinateSystem.AngularUnit.EqualParams (pcs2.GeographicCoordinateSystem.AngularUnit)); - } - - [Test, Description("transformation somehow is wrong"), Category("Question")] - public void TestGitHubIssue53() - { - // arrange - var csWgs84 = GeographicCoordinateSystem.WGS84; - var csUtm35N = ProjectedCoordinateSystem.WGS84_UTM(35, true); - var csTrans = CoordinateTransformationFactory.CreateFromCoordinateSystems(csWgs84, csUtm35N); - var csTransBack = CoordinateTransformationFactory.CreateFromCoordinateSystems(csUtm35N, csWgs84); - - // act - double[] point = { 42.5, 24.5 }; - double[] r = csTrans.MathTransform.Transform(point); - double[] rBack = csTransBack.MathTransform.Transform(r); - - // assert - Assert.AreEqual(point[0], rBack[0], 1e-5); - Assert.AreEqual(point[1], rBack[1], 1e-5); - } - - [Test, Description("Coordinate system isn't supported"), Category("Issue")] - public void TestGitHubIssue98() - { - var cs = CoordinateSystemFactory.CreateFromWkt( - @"COMPD_CS[ - ""SWEREF99 18 00 + RH2000 height"", - PROJCS[ - ""SWEREF99 18 00"", - GEOGCS[ - ""SWEREF99"", - DATUM[ - ""SWEREF99"", - SPHEROID[ - ""GRS 1980"", - 6378137, - 298.257222101, - AUTHORITY[ - ""EPSG"", - ""7019"" - ] - ], - TOWGS84[0,0,0,0,0,0,0], - AUTHORITY[""EPSG"",""6619""] - ], - PRIMEM - [ - ""Greenwich"", - 0, - AUTHORITY[""EPSG"",""8901""] - ], - UNIT[""degree"",0.0174532925199433, AUTHORITY[""EPSG"",""9122""]], - AUTHORITY[""EPSG"",""4619""] - ], - PROJECTION[""Transverse_Mercator""], - PARAMETER[""latitude_of_origin"",0], - PARAMETER[""central_meridian"",18], - PARAMETER[""scale_factor"",1], - PARAMETER[""false_easting"",150000], - PARAMETER[""false_northing"",0], - UNIT[""metre"",1, AUTHORITY[""EPSG"",""9001""]], - AUTHORITY[""EPSG"",""3011""] - ], - VERT_CS[ - ""RH2000 height"", - VERT_DATUM[ - ""Rikets hojdsystem 2000"", - 2005, - AUTHORITY[""EPSG"",""5208""] - ], - UNIT[""metre"",1, AUTHORITY[""EPSG"",""9001""]], - AXIS[""Up"",UP], - AUTHORITY[""EPSG"",""5613""] - ], - AUTHORITY[""EPSG"",""5850""] - ]"); - var cmpdCs = cs as CompoundCoordinateSystem; - Assert.IsNotNull(cmpdCs); - Assert.AreEqual( "EPSG", cmpdCs.Authority ); - Assert.AreEqual(5850, cmpdCs.AuthorityCode); - Assert.AreEqual(3, cmpdCs.Dimension); - Assert.IsTrue(cmpdCs.HeadCoordinateSystem is ProjectedCoordinateSystem); - Assert.IsTrue(cmpdCs.TailCoordinateSystem is VerticalCoordinateSystem); - } - - /// - /// Tests if a coordinate system can be created from a Well-Known Text (WKT) representation - /// and verifies that the authority code is correctly loaded. - /// - /// - /// This test ensures that the WKT parsing functionality of the CoordinateSystemFactory - /// correctly initializes the coordinate system and its associated metadata, such as the authority code. - /// - [Test] - public void TestAuthorityNotLoadedIssue() - { - string wkt = "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]"; - var coordinateSystem = CoordinateSystemFactory.CreateFromWkt(wkt); - Assert.IsNotNull(coordinateSystem); - Assert.AreEqual(coordinateSystem.AuthorityCode, 3857); - } - } -} diff --git a/test/ProjNet.Tests/Resources/GridResourceTests.cs b/test/ProjNet.Tests/Resources/GridResourceTests.cs new file mode 100644 index 00000000..8a26a8da --- /dev/null +++ b/test/ProjNet.Tests/Resources/GridResourceTests.cs @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ProjNet.Resources; +using Xunit; + +/// +/// Tests for and async behavior. +/// +public class GridResourceTests +{ + // ---- NoOpGridResourceFetchClient ---- + + /// + /// Verifies that always returns . + /// + [Fact] + public void NoOpFetchClient_TryFetch_ReturnsFalse() + { + var client = new NoOpGridResourceFetchClient(); + + bool result = client.TryFetch("some-grid.gsb", @"C:\target\some-grid.gsb"); + + Assert.False(result); + } + + /// + /// Verifies that always returns . + /// + /// A representing the asynchronous test operation. + [Fact] + public async Task NoOpFetchClient_TryFetchAsync_ReturnsFalse() + { + var client = new NoOpGridResourceFetchClient(); + + bool result = await client.TryFetchAsync("some-grid.gsb", @"C:\target\some-grid.gsb", TestContext.Current.CancellationToken); + + Assert.False(result); + } + + /// + /// Verifies that returns false regardless of grid name. + /// + /// The grid name passed to the fetch client. + /// A representing the asynchronous test operation. + [Theory] + [InlineData("grid1.gsb")] + [InlineData("grid2.tif")] + [InlineData("")] + public async Task NoOpFetchClient_TryFetchAsync_AlwaysReturnsFalse(string gridName) + { + var client = new NoOpGridResourceFetchClient(); + + bool result = await client.TryFetchAsync(gridName, "target-path", TestContext.Current.CancellationToken); + + Assert.False(result); + } + + /// + /// Verifies that completes synchronously. + /// + /// A representing the asynchronous test operation. + [Fact] + public async Task NoOpFetchClient_TryFetchAsync_WithCancellationToken_CompletesSynchronously() + { + var client = new NoOpGridResourceFetchClient(); + using var cts = new CancellationTokenSource(); + + Task task = client.TryFetchAsync("grid.gsb", "path", cts.Token); + + Assert.True(task.IsCompleted); +#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task — test code + Assert.False(await task); +#pragma warning restore CA2007 + } + + // ---- GridResourceResolver async tests ---- + + /// + /// Verifies that returns the path when a local file exists. + /// + /// A representing the asynchronous test operation. + [Fact] + public async Task TryResolveAsync_WithLocalFile_ReturnsPath() + { + string localDirectory = CreateTemporaryDirectory(); + try + { + string localGridPath = Path.Combine(localDirectory, "sample.gsb"); + await File.WriteAllTextAsync(localGridPath, "local-grid", TestContext.Current.CancellationToken); + + var options = new GridResourceResolverOptions( + [localDirectory], null, GridResourceResolutionMode.LocalOnly); + var resolver = new GridResourceResolver(options, new NoOpGridResourceFetchClient()); + + string? resolvedPath = await resolver.TryResolveAsync("sample.gsb", TestContext.Current.CancellationToken); + + Assert.NotNull(resolvedPath); + Assert.Equal(localGridPath, resolvedPath); + } + finally + { + Directory.Delete(localDirectory, true); + } + } + + /// + /// Verifies that returns null when no file exists. + /// + /// A representing the asynchronous test operation. + [Fact] + public async Task TryResolveAsync_WithNoFile_ReturnsNull() + { + string localDirectory = CreateTemporaryDirectory(); + try + { + var options = new GridResourceResolverOptions( + [localDirectory], null, GridResourceResolutionMode.LocalOnly); + var resolver = new GridResourceResolver(options, new NoOpGridResourceFetchClient()); + + string? resolvedPath = await resolver.TryResolveAsync("missing.gsb", TestContext.Current.CancellationToken); + + Assert.Null(resolvedPath); + } + finally + { + Directory.Delete(localDirectory, true); + } + } + + /// + /// Verifies that with cancellation token completes normally. + /// + /// A representing the asynchronous test operation. + [Fact] + public async Task TryResolveAsync_WithCancellationToken_Completes() + { + string localDirectory = CreateTemporaryDirectory(); + try + { + string localGridPath = Path.Combine(localDirectory, "test.gsb"); + await File.WriteAllTextAsync(localGridPath, "data", TestContext.Current.CancellationToken); + + var options = new GridResourceResolverOptions( + [localDirectory], null, GridResourceResolutionMode.LocalOnly); + var resolver = new GridResourceResolver(options, new NoOpGridResourceFetchClient()); + + string? resolvedPath = await resolver.TryResolveAsync("test.gsb", TestContext.Current.CancellationToken); + + Assert.NotNull(resolvedPath); + } + finally + { + Directory.Delete(localDirectory, true); + } + } + + private static string CreateTemporaryDirectory() + { + string path = Path.Combine(Path.GetTempPath(), "projnet-grid-tests", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(path); + return path; + } +} diff --git a/test/ProjNet.Tests/Resources/HttpGridResourceFetchClientTests.cs b/test/ProjNet.Tests/Resources/HttpGridResourceFetchClientTests.cs new file mode 100644 index 00000000..49ef1842 --- /dev/null +++ b/test/ProjNet.Tests/Resources/HttpGridResourceFetchClientTests.cs @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Resources; +using Xunit; + +/// +/// Tests for and environment-driven network resolution. +/// +[Collection(GlobalEnvironmentTestIsolation.Name)] +public sealed class HttpGridResourceFetchClientTests +{ + /// + /// Verifies the HTTP fetch client downloads the requested grid file and writes a cache manifest. + /// + /// A representing the asynchronous test operation. + [Fact] + public async Task TryFetchAsync_DownloadsGridAndWritesManifest() + { + string targetDirectory = CreateTemporaryDirectory(); + try + { + byte[] payload = Encoding.UTF8.GetBytes("downloaded-grid"); + using var handler = new RecordingHttpMessageHandler(_ => CreateResponse(HttpStatusCode.OK, payload)); + using var httpClient = new HttpClient(handler); + var fetchClient = new HttpGridResourceFetchClient("https://example.test/grids/", httpClient); + string targetPath = Path.Combine(targetDirectory, "sample.gsb"); + + bool fetched = await fetchClient.TryFetchAsync(@"nested\sample.gsb", targetPath, TestContext.Current.CancellationToken); + + Assert.True(fetched); + Assert.Equal(new Uri("https://example.test/grids/sample.gsb"), handler.LastRequestUri); + Assert.Equal(payload, await File.ReadAllBytesAsync(targetPath, TestContext.Current.CancellationToken)); + Assert.True(File.Exists(GridResourceCacheManifest.GetManifestPath(targetPath))); + Assert.True(GridResourceCacheManifest.IsValid(targetPath)); + } + finally + { + Directory.Delete(targetDirectory, true); + } + } + + /// + /// Verifies non-success HTTP responses do not leave partial cache artifacts behind. + /// + [Fact] + public void TryFetch_WithNonSuccessStatus_ReturnsFalseAndLeavesNoArtifacts() + { + string targetDirectory = CreateTemporaryDirectory(); + try + { + using var handler = new RecordingHttpMessageHandler(_ => CreateResponse(HttpStatusCode.NotFound)); + using var httpClient = new HttpClient(handler); + var fetchClient = new HttpGridResourceFetchClient("https://example.test/grids/", httpClient); + string targetPath = Path.Combine(targetDirectory, "missing.gsb"); + + bool fetched = fetchClient.TryFetch("missing.gsb", targetPath); + + Assert.False(fetched); + Assert.False(File.Exists(targetPath)); + Assert.False(File.Exists(GridResourceCacheManifest.GetManifestPath(targetPath))); + } + finally + { + Directory.Delete(targetDirectory, true); + } + } + + /// + /// Verifies + /// recreates the resolver from environment settings, including the network mode alias and HTTP fetch activation. + /// + /// A representing the asynchronous test operation. + [Fact] + public async Task ConfigureGridResolution_WithoutArgumentsUsesEnvironmentNetworkSettings() + { + string cacheDirectory = CreateTemporaryDirectory(); + string? originalGridMode = Environment.GetEnvironmentVariable("PROJNET_GRID_MODE"); + string? originalGridBaseUrl = Environment.GetEnvironmentVariable("PROJNET_GRID_BASE_URL"); + string? originalGridCache = Environment.GetEnvironmentVariable("PROJNET_GRID_CACHE"); + string? originalGridPaths = Environment.GetEnvironmentVariable("PROJNET_GRID_PATHS"); + + using var server = new TestHttpServer("network-grid.gsb", Encoding.UTF8.GetBytes("server-grid")); + + try + { + Environment.SetEnvironmentVariable("PROJNET_GRID_MODE", "network"); + Environment.SetEnvironmentVariable("PROJNET_GRID_BASE_URL", server.BaseUrl); + Environment.SetEnvironmentVariable("PROJNET_GRID_CACHE", cacheDirectory); + Environment.SetEnvironmentVariable("PROJNET_GRID_PATHS", string.Empty); + + CoordinateTransformationFactory.ConfigureGridResolution(); + + bool resolved = CoordinateTransformationFactory.TryResolveGridResourcePath("network-grid.gsb", out string? resolvedPath); + + Assert.True(resolved); + Assert.NotNull(resolvedPath); + Assert.Equal("server-grid", await File.ReadAllTextAsync(resolvedPath, TestContext.Current.CancellationToken)); + Assert.Equal(1, server.RequestCount); + Assert.True(File.Exists(GridResourceCacheManifest.GetManifestPath(resolvedPath))); + } + finally + { + Environment.SetEnvironmentVariable("PROJNET_GRID_MODE", originalGridMode); + Environment.SetEnvironmentVariable("PROJNET_GRID_BASE_URL", originalGridBaseUrl); + Environment.SetEnvironmentVariable("PROJNET_GRID_CACHE", originalGridCache); + Environment.SetEnvironmentVariable("PROJNET_GRID_PATHS", originalGridPaths); + CoordinateTransformationFactory.ConfigureGridResolution( + new NoOpGridResourceFetchClient(), + Array.Empty(), + null, + GridResourceResolutionMode.LocalOnly); + Directory.Delete(cacheDirectory, true); + } + } + + private static HttpResponseMessage CreateResponse(HttpStatusCode statusCode, byte[]? payload = null) + { + var response = new HttpResponseMessage(statusCode); + if (payload is not null) + { + response.Content = new ByteArrayContent(payload); + response.Content.Headers.ContentLength = payload.Length; + } + + return response; + } + + private static string CreateTemporaryDirectory() + { + string path = Path.Combine(Path.GetTempPath(), "projnet-grid-tests", Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(path); + return path; + } + + private static int GetFreePort() + { + using var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + return ((IPEndPoint)listener.LocalEndpoint).Port; + } + + private sealed class RecordingHttpMessageHandler : HttpMessageHandler + { + private readonly Func responseFactory; + + internal RecordingHttpMessageHandler(Func responseFactory) + { + this.responseFactory = responseFactory; + } + + internal Uri? LastRequestUri { get; private set; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this.LastRequestUri = request.RequestUri; + HttpResponseMessage response = this.responseFactory(request); + response.RequestMessage = request; + return Task.FromResult(response); + } + } + + private sealed class TestHttpServer : IDisposable + { + private readonly HttpListener listener; + private readonly Task listenerLoop; + private readonly byte[] payload; + private readonly string requestPath; + private int requestCount; + + internal TestHttpServer(string fileName, byte[] payload) + { + this.payload = payload; + this.requestPath = $"/{fileName}"; + int port = GetFreePort(); + this.BaseUrl = $"http://127.0.0.1:{port}/"; + this.listener = new HttpListener(); + this.listener.Prefixes.Add(this.BaseUrl); + this.listener.Start(); + this.listenerLoop = Task.Run(this.RunAsync); + } + + internal string BaseUrl { get; } + + internal int RequestCount => Volatile.Read(ref this.requestCount); + + public void Dispose() + { + this.listener.Stop(); + this.listener.Close(); + + try + { + this.listenerLoop.GetAwaiter().GetResult(); + } + catch (HttpListenerException) + { + } + catch (ObjectDisposedException) + { + } + } + + private async Task RunAsync() + { + while (this.listener.IsListening) + { + HttpListenerContext context; + try + { + context = await this.listener.GetContextAsync().ConfigureAwait(false); + } + catch (HttpListenerException) + { + break; + } + catch (ObjectDisposedException) + { + break; + } + + Interlocked.Increment(ref this.requestCount); + context.Response.StatusCode = string.Equals(context.Request.Url?.AbsolutePath, this.requestPath, StringComparison.Ordinal) + ? (int)HttpStatusCode.OK + : (int)HttpStatusCode.NotFound; + + if (context.Response.StatusCode == (int)HttpStatusCode.OK) + { + context.Response.ContentLength64 = this.payload.Length; + await context.Response.OutputStream.WriteAsync(this.payload.AsMemory(), CancellationToken.None).ConfigureAwait(false); + } + + context.Response.Close(); + } + } + } +} diff --git a/test/ProjNet.Tests/SRIDReader.cs b/test/ProjNet.Tests/SRIDReader.cs deleted file mode 100644 index d0dfecc9..00000000 --- a/test/ProjNet.Tests/SRIDReader.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using ProjNet.CoordinateSystems; - -namespace ProjNET.Tests -{ - internal class SRIDReader - { - private static readonly Lazy CoordinateSystemFactory = - new Lazy(() => new CoordinateSystemFactory()); - - public struct WktString { - /// - /// Well-known ID - /// - public int WktId; - /// - /// Well-known Text - /// - public string Wkt; - } - - /// - /// Enumerates all SRID's in the SRID.csv file. - /// - /// Enumerator - public static IEnumerable GetSrids(string filename = null) - { - var stream = string.IsNullOrWhiteSpace(filename) - ? Assembly.GetExecutingAssembly().GetManifestResourceStream("ProjNET.Tests.SRID.csv") - : File.OpenRead(filename); - - using (var sr = new StreamReader(stream, Encoding.UTF8)) - { - while (!sr.EndOfStream) - { - string line = sr.ReadLine(); - if (string.IsNullOrWhiteSpace(line)) continue; - - int split = line.IndexOf(';'); - if (split <= -1) continue; - - var wkt = new WktString - { - WktId = int.Parse(line.Substring(0, split)), - Wkt = line.Substring(split + 1) - }; - yield return wkt; - } - } - } - - /// - /// Gets a coordinate system from the SRID.csv file - /// - /// EPSG ID - /// (optional) path to CSV File with WKT definitions. - /// Coordinate system, or null if no entry with was not found. - public static CoordinateSystem GetCSbyID(int id, string file = null) - { - //ICoordinateSystemFactory factory = new CoordinateSystemFactory(); - foreach (var wkt in GetSrids(file)) - if (wkt.WktId == id) - return CoordinateSystemFactory.Value.CreateFromWkt(wkt.Wkt); - return null; - } - } -} diff --git a/test/ProjNet.Tests/Serialization/BaseSerializationTest.cs b/test/ProjNet.Tests/Serialization/BaseSerializationTest.cs deleted file mode 100644 index 6ebb4a72..00000000 --- a/test/ProjNet.Tests/Serialization/BaseSerializationTest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.IO; -#if !NET7_0_OR_GREATER -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; - -namespace ProjNET.Tests.Serialization -{ - public class BaseSerializationTest - { - [Obsolete("ISerializable is deprecated")] - public IFormatter GetFormatter() - { - return new BinaryFormatter(); - } - - [Obsolete("ISerializable is deprecated")] - public static T SanD(T instance, IFormatter formatter) - { - using (var ms = new MemoryStream()) - { - formatter.Serialize(ms, instance); - ms.Seek(0, SeekOrigin.Begin); - return (T)formatter.Deserialize(ms); - } - } - } -} -#endif diff --git a/test/ProjNet.Tests/Serialization/CoordinateSystemsProjectionsTest.cs b/test/ProjNet.Tests/Serialization/CoordinateSystemsProjectionsTest.cs deleted file mode 100644 index 47531579..00000000 --- a/test/ProjNet.Tests/Serialization/CoordinateSystemsProjectionsTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -using NUnit.Framework; -using ProjNet.CoordinateSystems; -using System; - -namespace ProjNET.Tests.Serialization -{ - public class CoordinateSystemsProjectionsTest -#if !NET7_0_OR_GREATER - : BaseSerializationTest - { - [Test, Obsolete("ISerializable is deprecated")] - public void TestProjectionParameterSet() - { - var ps = new ProjNet.CoordinateSystems.Projections.ProjectionParameterSet( - new[] - { - new ProjectionParameter("latitude_of_origin", 0), - new ProjectionParameter("false_easting", 500) - } - ); - - var psD = SanD(ps, GetFormatter()); - - Assert.AreEqual(ps, psD); - } -#else - { -#endif - - [Test] - public void CreateTransformationFromCoordinateSystemDeserializedFromWKT() - { - var utm17n_original = ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WGS84_UTM(17, true); - string utm17n_wkt = utm17n_original.WKT; - - var utm17n_fromWKT = (ProjNet.CoordinateSystems.ProjectedCoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(utm17n_wkt); - var wgs84 = ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84; - - var coordinateSystemServices = new ProjNet.CoordinateSystemServices(); - Assert.DoesNotThrow(() => coordinateSystemServices.CreateTransformation(utm17n_fromWKT, wgs84)); - } - } -} diff --git a/test/ProjNet.Tests/Serialization/CoordinateSystemsProjectionsTests.cs b/test/ProjNet.Tests/Serialization/CoordinateSystemsProjectionsTests.cs new file mode 100644 index 00000000..912dc6b7 --- /dev/null +++ b/test/ProjNet.Tests/Serialization/CoordinateSystemsProjectionsTests.cs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.Serialization; + +using ProjNet.CoordinateSystems; +using Xunit; + +/// +/// Tests for coordinate system projection serialization and transformation. +/// +public class CoordinateSystemsProjectionsTests +{ + /// + /// Verifies that a coordinate transformation can be created from a coordinate system deserialized from its WKT representation. + /// + [Fact] + public void CreateTransformationFromCoordinateSystemDeserializedFromWKT() + { + var utm17n_original = ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WGS84_UTM(17, true); + string utm17n_wkt = utm17n_original.WKT; + + var utm17n_fromWKT = (ProjectedCoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(utm17n_wkt); + GeographicCoordinateSystem wgs84 = ProjNet.CoordinateSystems.GeographicCoordinateSystem.WGS84; + + var coordinateSystemServices = new CoordinateSystemServices(); + Assert.Null(Record.Exception(() => coordinateSystemServices.CreateTransformation(utm17n_fromWKT, wgs84))); + } +} diff --git a/test/ProjNet.Tests/SharpMapIssueRegressionTests.cs b/test/ProjNet.Tests/SharpMapIssueRegressionTests.cs new file mode 100644 index 00000000..e3d5d15f --- /dev/null +++ b/test/ProjNet.Tests/SharpMapIssueRegressionTests.cs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Reflection; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Tests.IO.CoordinateSystems; +using Xunit; + +/// +/// Regression tests for issues reported in the SharpMap project. +/// +public class SharpMapIssueRegressionTests : CoordinateTransformTestsBase +{ + private string wkt7151 = + """ + PROJCS["NAD_1983_Hotine_Oblique_Mercator_Azimuth_Natural_Origin",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.017453292519943295]],PROJECTION["Hotine_Oblique_Mercator"],PARAMETER["longitude_of_center",-86.0],PARAMETER["latitude_of_center",45.30916666666666],PARAMETER["azimuth",337.25555999999995],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",2546731.496],PARAMETER["false_northing",-4354009.816],PARAMETER["rectified_grid_angle",337.25555999999995],UNIT["m",1.0]] + """; + + private string wkt2236 = + """ + PROJCS["NAD83 / Florida East (ftUS)", GEOGCS [ "NAD83", DATUM ["North American Datum 1983 (EPSG ID 6269)", SPHEROID ["GRS 1980 (EPSG ID 7019)", 6378137, 298.257222101]], PRIMEM [ "Greenwich", 0.000000 ], UNIT ["Decimal Degree", 0.01745329251994328]], PROJECTION ["SPCS83 Florida East zone (US Survey feet) (EPSG OP 15318)"], PARAMETER ["Latitude_Of_Origin", 24.33333333333333333333333333333333333333], PARAMETER ["Central_Meridian", -80.9999999999999999999999999999999999999], PARAMETER ["Scale_Factor", 0.999941177], PARAMETER ["False_Easting", 656166.6669999999999999999999999999999999], PARAMETER ["False_Northing", 0], UNIT ["U.S. Foot", 0.3048006096012192024384048768097536195072]] + """; + + /// + /// Initializes a new instance of the class. + /// + public SharpMapIssueRegressionTests() + { + this.Verbose = true; + } + + /// + /// Verifies that a NAD83 State Plane (Florida East, US survey feet) to WGS84 coordinate + /// transformation can be created without error. + /// + [Fact(DisplayName = "NAD83 (State Plane) projection to the WGS84 (Lat/Long), http://sharpmap.codeplex.com/discussions/435794")] + public void TestNad83ToWGS84() + { + CoordinateSystem src = this.RequireCoordinateSystem(this.wkt2236); + CoordinateSystem tgt = this.RequireCoordinateSystem(this.GetEpsgWkt(4326)); + + ProjNet.CoordinateSystems.Projections.ProjectionsRegistry.Register( + "SPCS83 Florida East zone (US Survey feet) (EPSG OP 15318)", + this.ReflectType("ProjNet.CoordinateSystems.Projections.TransverseMercator")); + + _ = this.AssertTransformationCreated(src, tgt); + } + + // projection problem with Michigan GeoRef + + /// + /// Verifies that a Michigan GeoRef (Hotine Oblique Mercator) to Web Mercator transformation + /// can be created and applied to a coordinate without error. + /// + [Fact(DisplayName = "projection problem with Michigan GeoRef")] + public void TestMichiganGeoRefToWebMercator() + { + CoordinateSystem src = this.RequireCoordinateSystem(this.wkt7151); + ProjectedCoordinateSystem tgt = ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WebMercator; + + ICoordinateTransformation transform = this.AssertTransformationCreated(src, tgt); + double[] ptSrc = [535247.9375, 324548.09375]; + double[] ptTgt = default!; + Assert.Null(Record.Exception(() => ptTgt = transform.MathTransform.Transform(ptSrc))); + Assert.NotNull(ptTgt); + } + + /// + /// Verifies that the WKT parser accepts an authority code written as either a quoted string + /// or an unquoted integer and produces equivalent coordinate systems in both cases. + /// + [Fact(DisplayName = "Parse AUTHORITY with unqouted AuthorityCode")] + public void TestAuthorityCodeParsing() + { + const string wkt1 = + """ + PROJCS["NAD_1983_BC_Environment_Albers",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Albers"],PARAMETER["False_Easting",1000000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-126.0],PARAMETER["Standard_Parallel_1",50.0],PARAMETER["Standard_Parallel_2",58.5],PARAMETER["Latitude_Of_Origin",45.0],UNIT["Meter",1.0],AUTHORITY["EPSG","3005"]] + """; + CoordinateSystem cs1 = this.RequireCoordinateSystem(wkt1); + const string wkt2 = + """ + PROJCS["NAD_1983_BC_Environment_Albers",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Albers"],PARAMETER["False_Easting",1000000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-126.0],PARAMETER["Standard_Parallel_1",50.0],PARAMETER["Standard_Parallel_2",58.5],PARAMETER["Latitude_Of_Origin",45.0],UNIT["Meter",1.0],AUTHORITY["EPSG",3005]] + """; + CoordinateSystem cs2 = this.RequireCoordinateSystem(wkt2); + + // Assert.Equal(cs1, cs2); + Assert.True(cs1.EqualParams(cs2)); + } + + /// + /// Verifies that coordinate transformations between EPSG 25832 (UTM zone 32N) and + /// EPSG 3857 (Web Mercator) can be created successfully in both directions. + /// + [Fact] + public void Test25832To3857() + { + CoordinateSystem cs1 = this.RequireCoordinateSystem(this.GetEpsgWkt(25832)); + CoordinateSystem cs2 = this.RequireCoordinateSystem(this.GetEpsgWkt(3857)); + + _ = this.AssertTransformationCreated(cs1, cs2); + _ = this.AssertTransformationCreated(cs2, cs1); + _ = this.AssertTransformationCreated(cs1, ProjectedCoordinateSystem.WebMercator); + _ = this.AssertTransformationCreated(ProjectedCoordinateSystem.WebMercator, cs1); + } + + /// + /// Verifies that a Lambert Azimuthal Equal Area (EPSG 3035) transformation produces + /// accurate forward and inverse results. + /// + [Fact] + public void TestLaea() + { + GeographicCoordinateSystem csSrc = GeographicCoordinateSystem.WGS84; + CoordinateSystem csTgt = this.RequireCoordinateSystem(this.GetEpsgWkt(3035)); + + ICoordinateTransformation ct = this.CreateTransformation(csSrc, csTgt); + + (double resX, double resY) = ct.MathTransform.Transform(16.4, 48.2); + Assert.InRange(resX, 4796297.431434812 - 1e-2, 4796297.431434812 + 1e-2); + Assert.InRange(resY, 2807999.1539475969 - 1e-2, 2807999.1539475969 + 1e-2); + + (double origX, double origY) = ct.MathTransform.Inverse().Transform(resX, resY); + Assert.InRange(origX, 16.4 - 1e-2, 16.4 + 1e-2); + Assert.InRange(origY, 48.2 - 1e-2, 48.2 + 1e-2); + } + + private Type ReflectType(string typeName) + { + Assembly asm = Assert.IsType(Assembly.GetAssembly(typeof(ProjNet.CoordinateSystems.Projections.MapProjection)), exactMatch: false); + Type? res = asm.GetType(typeName); + return Assert.IsType(res, exactMatch: false); + } + + private string GetEpsgWkt(int srid) + => EpsgArchiveWktFixtureSource.GetFixture(srid).Wkt; +} diff --git a/test/ProjNet.Tests/SharpMapIssues.cs b/test/ProjNet.Tests/SharpMapIssues.cs deleted file mode 100644 index 305ab647..00000000 --- a/test/ProjNet.Tests/SharpMapIssues.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Reflection; -using NUnit.Framework; -using ProjNet.CoordinateSystems; -using ProjNet.CoordinateSystems.Transformations; - -namespace ProjNET.Tests -{ - public class SharpMapIssues: CoordinateTransformTestsBase - { - public SharpMapIssues() - { - Verbose = true; - } - - string wkt2236 = "PROJCS[\"NAD83 / Florida East (ftUS)\", GEOGCS [ \"NAD83\", DATUM [\"North American Datum 1983 (EPSG ID 6269)\", SPHEROID [\"GRS 1980 (EPSG ID 7019)\", 6378137, 298.257222101]], PRIMEM [ \"Greenwich\", 0.000000 ], UNIT [\"Decimal Degree\", 0.01745329251994328]], PROJECTION [\"SPCS83 Florida East zone (US Survey feet) (EPSG OP 15318)\"], PARAMETER [\"Latitude_Of_Origin\", 24.33333333333333333333333333333333333333], PARAMETER [\"Central_Meridian\", -80.9999999999999999999999999999999999999], PARAMETER [\"Scale_Factor\", 0.999941177], PARAMETER [\"False_Easting\", 656166.6669999999999999999999999999999999], PARAMETER [\"False_Northing\", 0], UNIT [\"U.S. Foot\", 0.3048006096012192024384048768097536195072]]"; - string wkt8307 = "GEOGCS [ \"WGS 84\", DATUM [\"World Geodetic System 1984 (EPSG ID 6326)\", SPHEROID [\"WGS 84 (EPSG ID 7030)\", 6378137, 298.257223563]], PRIMEM [ \"Greenwich\", 0.000000 ], UNIT [\"Decimal Degree\", 0.01745329251994328]]"; - - [Test, Description("NAD83 (State Plane) projection to the WGS84 (Lat/Long), http://sharpmap.codeplex.com/discussions/435794")] - public void TestNad83ToWGS84() - { - var src = CoordinateSystemFactory.CreateFromWkt(wkt2236); - var tgt = CoordinateSystemFactory.CreateFromWkt(wkt8307);//CoordinateSystems.GeographicCoordinateSystem.WGS84;; - - ProjNet.CoordinateSystems.Projections.ProjectionsRegistry.Register("SPCS83 Florida East zone (US Survey feet) (EPSG OP 15318)", - ReflectType("ProjNet.CoordinateSystems.Projections.TransverseMercator")); - - ICoordinateTransformation transform = null; - Assert.DoesNotThrow(() => transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(src, tgt)); - Assert.IsNotNull(transform); - } - - private System.Type ReflectType(string typeName) - { - var asm = Assembly.GetAssembly(typeof (ProjNet.CoordinateSystems.Projections.MapProjection)); - var res = asm.GetType(typeName); - return res; - } - - private string wkt7151 = "PROJCS[\"NAD_1983_Hotine_Oblique_Mercator_Azimuth_Natural_Origin\",GEOGCS[\"GCS_North_American_1983\",DATUM[\"D_North_American_1983\",SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Hotine_Oblique_Mercator\"],PARAMETER[\"longitude_of_center\",-86.0],PARAMETER[\"latitude_of_center\",45.30916666666666],PARAMETER[\"azimuth\",337.25555999999995],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",2546731.496],PARAMETER[\"false_northing\",-4354009.816],PARAMETER[\"rectified_grid_angle\",337.25555999999995],UNIT[\"m\",1.0]]"; - //projection problem with Michigan GeoRef - [Test, Description("projection problem with Michigan GeoRef")] - public void TestMichiganGeoRefToWebMercator() - { - var src = CoordinateSystemFactory.CreateFromWkt(wkt7151); - var tgt = ProjNet.CoordinateSystems.ProjectedCoordinateSystem.WebMercator; - - ICoordinateTransformation transform = null; - Assert.DoesNotThrow(() => transform = CoordinateTransformationFactory.CreateFromCoordinateSystems(src, tgt)); - Assert.IsNotNull(transform); - double[] ptSrc = new[] {535247.9375, 324548.09375}; - double[] ptTgt = null; - Assert.DoesNotThrow(() => ptTgt = transform.MathTransform.Transform(ptSrc)); - Assert.IsNotNull(ptTgt); - } - - [Test, Description("Parse AUTHORITY with unqouted AuthorityCode")] - public void TestAuthorityCodeParsing() - { - const string wkt1 = "PROJCS[\"NAD_1983_BC_Environment_Albers\",GEOGCS[\"GCS_North_American_1983\",DATUM[\"D_North_American_1983\",SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Albers\"],PARAMETER[\"False_Easting\",1000000.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",-126.0],PARAMETER[\"Standard_Parallel_1\",50.0],PARAMETER[\"Standard_Parallel_2\",58.5],PARAMETER[\"Latitude_Of_Origin\",45.0],UNIT[\"Meter\",1.0],AUTHORITY[\"EPSG\",\"3005\"]]"; - CoordinateSystem cs1 = null, cs2 = null; - Assert.DoesNotThrow( () => cs1 = CoordinateSystemFactory.CreateFromWkt(wkt1)); - Assert.IsNotNull(cs1); - const string wkt2 = "PROJCS[\"NAD_1983_BC_Environment_Albers\",GEOGCS[\"GCS_North_American_1983\",DATUM[\"D_North_American_1983\",SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Albers\"],PARAMETER[\"False_Easting\",1000000.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",-126.0],PARAMETER[\"Standard_Parallel_1\",50.0],PARAMETER[\"Standard_Parallel_2\",58.5],PARAMETER[\"Latitude_Of_Origin\",45.0],UNIT[\"Meter\",1.0],AUTHORITY[\"EPSG\",3005]]"; - Assert.DoesNotThrow(() => cs2 = CoordinateSystemFactory.CreateFromWkt(wkt2)); - Assert.IsNotNull(cs2); - //Assert.AreEqual(cs1, cs2); - Assert.IsTrue(cs1.EqualParams(cs2)); - } - - [Test] - public void Test25832To3857() - { - - const string wkt1 = //"PROJCS[\"ETRS89 / UTM zone 32N\",GEOGCS[\"ETRS89\",DATUM[\"European_Terrestrial_Reference_System_1989\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6258\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4258\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"25832\"]]"; - "PROJCS[\"ETRS89 / UTM zone 32N\",GEOGCS[\"ETRS89\",DATUM[\"European_Terrestrial_Reference_System_1989\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6258\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4258\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",9],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"25832\"]]"; - - CoordinateSystem cs1 = null, cs2 = null; - Assert.DoesNotThrow(() => cs1 = CoordinateSystemFactory.CreateFromWkt(wkt1)); - Assert.IsNotNull(cs1); - const string wkt2 = "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\", SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"latitude_of_origin\", 0],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]"; - Assert.DoesNotThrow(() => cs2 = CoordinateSystemFactory.CreateFromWkt(wkt2)); - Assert.IsNotNull(cs2); - - ICoordinateTransformation ct = null; - Assert.DoesNotThrow(() => ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(cs1, cs2)); - Assert.IsNotNull(ct); - Assert.DoesNotThrow(() => ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(cs2, cs1)); - Assert.IsNotNull(ct); - Assert.DoesNotThrow(() => ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(cs1, ProjectedCoordinateSystem.WebMercator)); - Assert.IsNotNull(ct); - Assert.DoesNotThrow(() => ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(ProjectedCoordinateSystem.WebMercator, cs1)); - Assert.IsNotNull(ct); - } - - [Test] - public void TestLaea() - { - const string Epsg3035 = - @"PROJCS[""ETRS89 / ETRS-LAEA"",GEOGCS[""ETRS89"",DATUM[""European_Terrestrial_Reference_System_1989"",SPHEROID[""GRS 1980"",6378137,298.257222101,AUTHORITY[""EPSG"",""7019""]],AUTHORITY[""EPSG"",""6258""]],PRIMEM[""Greenwich"",0,AUTHORITY[""EPSG"",""8901""]],UNIT[""degree"",0.01745329251994328,AUTHORITY[""EPSG"",""9122""]],AUTHORITY[""EPSG"",""4258""]],PROJECTION[""Lambert_Azimuthal_Equal_Area""],PARAMETER[""latitude_of_center"",52],PARAMETER[""longitude_of_center"",10],PARAMETER[""false_easting"",4321000],PARAMETER[""false_northing"",3210000],UNIT[""metre"",1,AUTHORITY[""EPSG"",""9001""]],AXIS[""X"",EAST],AXIS[""Y"",NORTH],AUTHORITY[""EPSG"",""3035""]]"; - - var csSrc = GeographicCoordinateSystem.WGS84; - var csTgt = CoordinateSystemFactory.CreateFromWkt(Epsg3035); - - var ct = CoordinateTransformationFactory.CreateFromCoordinateSystems(csSrc, csTgt); - - (double resX, double resY) = ((MathTransform) ct.MathTransform).Transform(16.4, 48.2); - Assert.That(resX, Is.EqualTo(4796297.431434812).Within(1e-2)); - Assert.That(resY, Is.EqualTo(2807999.1539475969).Within(1e-2)); - - (double origX, double origY) = ((MathTransform) ct.MathTransform.Inverse()).Transform(resX, resY); - Assert.That(origX, Is.EqualTo(16.4).Within(1e-2)); - Assert.That(origY, Is.EqualTo(48.2).Within(1e-2)); - - } - - } -} diff --git a/test/ProjNet.Tests/Support/ArgumentGuardTests.cs b/test/ProjNet.Tests/Support/ArgumentGuardTests.cs new file mode 100644 index 00000000..8ce5fe72 --- /dev/null +++ b/test/ProjNet.Tests/Support/ArgumentGuardTests.cs @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using Xunit; + +/// +/// Provides unit tests for . +/// +public class ArgumentGuardTests +{ + /// + /// Verifies that the generic null guard returns the original reference for non-null values. + /// + [Fact] + public void ThrowIfNullGenericWithNonNullValueReturnsSameReference() + { + string value = "projnet"; + + string result = ArgumentGuard.ThrowIfNull(value, nameof(value)); + + Assert.Same(value, result); + } + + /// + /// Verifies that the generic null guard throws for null values. + /// + [Fact] + public void ThrowIfNullGenericWithNullValueThrowsArgumentNullException() + { + string? value = null; + + ArgumentNullException exception = Assert.Throws(() => ArgumentGuard.ThrowIfNull(value, nameof(value))); + + Assert.Equal(nameof(value), exception.ParamName); + } + + /// + /// Verifies that the null-or-empty guard returns the original reference for non-empty values. + /// + [Fact] + public void ThrowIfNullOrEmptyWithNonEmptyValueReturnsSameReference() + { + string value = "valid"; + + string result = ArgumentGuard.ThrowIfNullOrEmpty(value, nameof(value)); + + Assert.Same(value, result); + } + + /// + /// Verifies that the null-or-empty guard throws for empty values. + /// + [Fact] + public void ThrowIfNullOrEmptyWithEmptyValueThrowsArgumentException() + { + string value = string.Empty; + + ArgumentException exception = Assert.Throws(() => ArgumentGuard.ThrowIfNullOrEmpty(value, nameof(value))); + + Assert.Equal(nameof(value), exception.ParamName); + } + + /// + /// Verifies that the null-or-whitespace guard returns the original reference for non-whitespace values. + /// + [Fact] + public void ThrowIfNullOrWhiteSpaceWithContentReturnsSameReference() + { + string value = "valid"; + + string result = ArgumentGuard.ThrowIfNullOrWhiteSpace(value, nameof(value)); + + Assert.Same(value, result); + } + + /// + /// Verifies that the null-or-whitespace guard throws for whitespace values. + /// + [Fact] + public void ThrowIfNullOrWhiteSpaceWithWhitespaceThrowsArgumentException() + { + string value = " "; + + ArgumentException exception = Assert.Throws(() => ArgumentGuard.ThrowIfNullOrWhiteSpace(value, nameof(value))); + + Assert.Equal(nameof(value), exception.ParamName); + } + + /// + /// Verifies that the negative-value guard accepts zero and positive values. + /// + /// Value to validate. + [Theory] + [InlineData(0d)] + [InlineData(1.5d)] + public void ThrowIfNegativeWithNonNegativeValueSucceeds(double value) + { + ArgumentGuard.ThrowIfNegative(value, nameof(value)); + } + + /// + /// Verifies that the negative-value guard throws for negative input. + /// + [Fact] + public void ThrowIfNegativeWithNegativeValueThrowsArgumentOutOfRangeException() + { + const double value = -0.1d; + + ArgumentOutOfRangeException exception = Assert.Throws( + () => ArgumentGuard.ThrowIfNegative(value, nameof(value))); + + Assert.Equal(nameof(value), exception.ParamName); + } + + /// + /// Verifies that the finite-number guard accepts finite values. + /// + /// Value to validate. + [Theory] + [InlineData(0d)] + [InlineData(-1.5d)] + [InlineData(42.25d)] + public void ThrowIfNotFiniteWithFiniteValueSucceeds(double value) + { + ArgumentGuard.ThrowIfNotFinite(value, nameof(value)); + } + + /// + /// Verifies that the finite-number guard throws for NaN and infinity inputs. + /// + /// Value to validate. + [Theory] + [InlineData(double.NaN)] + [InlineData(double.PositiveInfinity)] + [InlineData(double.NegativeInfinity)] + public void ThrowIfNotFiniteWithNonFiniteValueThrowsArgumentOutOfRangeException(double value) + { + const string message = "Custom finite-value message."; + + ArgumentOutOfRangeException exception = Assert.Throws( + () => ArgumentGuard.ThrowIfNotFinite(value, nameof(value), message)); + + Assert.Equal(nameof(value), exception.ParamName); + Assert.Contains(message, exception.Message, StringComparison.Ordinal); + } +} diff --git a/test/ProjNet.Tests/Support/AssemblyAttributes.cs b/test/ProjNet.Tests/Support/AssemblyAttributes.cs new file mode 100644 index 00000000..d490880c --- /dev/null +++ b/test/ProjNet.Tests/Support/AssemblyAttributes.cs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +global using ProjNet.Tests; +using Xunit; + +[assembly: CaptureConsole] diff --git a/test/ProjNet.Tests/Support/CoordinateSystemTestHelpers.cs b/test/ProjNet.Tests/Support/CoordinateSystemTestHelpers.cs new file mode 100644 index 00000000..20703d00 --- /dev/null +++ b/test/ProjNet.Tests/Support/CoordinateSystemTestHelpers.cs @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using ProjNet; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using ProjNet.Data; +using Xunit; + +/// +/// Provides shared coordinate-system and WKT parsing helpers for tests. +/// +internal static class CoordinateSystemTestHelpers +{ + /// + /// Creates a fresh coordinate-system factory for tests that need an explicit instance. + /// + /// A new coordinate-system factory. + internal static CoordinateSystemFactory CreateCoordinateSystemFactory() + => new(); + + /// + /// Creates a fresh coordinate-transformation factory for tests that need an explicit instance. + /// + /// A new coordinate-transformation factory. + internal static CoordinateTransformationFactory CreateCoordinateTransformationFactory() + => new(); + + /// + /// Creates a coordinate-system service with the default test factories. + /// + /// A new coordinate-system service. + internal static CoordinateSystemServices CreateCoordinateSystemServices() + => new(CreateCoordinateSystemFactory(), CreateCoordinateTransformationFactory()); + + /// + /// Creates a coordinate-system service with the default test factories and the supplied definitions. + /// + /// Coordinate-system definitions to load. + /// A new coordinate-system service. + internal static CoordinateSystemServices CreateCoordinateSystemServices(IEnumerable definitions) + { + ArgumentNullException.ThrowIfNull(definitions); + return new CoordinateSystemServices(CreateCoordinateSystemFactory(), CreateCoordinateTransformationFactory(), definitions); + } + + /// + /// Parses a coordinate system from WKT with a fresh factory and asserts that parsing succeeded. + /// + /// Well-known text to parse. + /// The parsed coordinate system. + internal static CoordinateSystem RequireCoordinateSystem(string wkt) + => RequireCoordinateSystem(CreateCoordinateSystemFactory(), wkt); + + /// + /// Parses a coordinate system from WKT with a fresh factory and asserts that it matches the requested type. + /// + /// Expected coordinate system type. + /// Well-known text to parse. + /// The parsed coordinate system cast to . + internal static TCoordinateSystem RequireCoordinateSystem(string wkt) + where TCoordinateSystem : CoordinateSystem + => RequireCoordinateSystem(CreateCoordinateSystemFactory(), wkt); + + /// + /// Parses a coordinate system from WKT and asserts that parsing succeeded. + /// + /// Coordinate system factory. + /// Well-known text to parse. + /// The parsed coordinate system. + internal static CoordinateSystem RequireCoordinateSystem(CoordinateSystemFactory factory, string wkt) + { + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(wkt); + + CoordinateSystem? coordinateSystem = factory.CreateFromWkt(wkt); + return Assert.IsType(coordinateSystem, exactMatch: false); + } + + /// + /// Parses a coordinate system from WKT and asserts that it matches the requested type. + /// + /// Expected coordinate system type. + /// Coordinate system factory. + /// Well-known text to parse. + /// The parsed coordinate system cast to . + internal static TCoordinateSystem RequireCoordinateSystem(CoordinateSystemFactory factory, string wkt) + where TCoordinateSystem : CoordinateSystem + { + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(wkt); + + CoordinateSystem? coordinateSystem = factory.CreateFromWkt(wkt); + return Assert.IsType(coordinateSystem); + } + + /// + /// Clones a geographic coordinate system while replacing its authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority. + /// Replacement authority code. + /// A cloned geographic coordinate system with the requested authority metadata. + internal static GeographicCoordinateSystem WithAuthority(GeographicCoordinateSystem coordinateSystem, string authority, long authorityCode) + => coordinateSystem.WithAuthority(authority, authorityCode); + + /// + /// Clones a projected coordinate system while replacing its authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority. + /// Replacement authority code. + /// A cloned projected coordinate system with the requested authority metadata. + internal static ProjectedCoordinateSystem WithAuthority(ProjectedCoordinateSystem coordinateSystem, string authority, long authorityCode) + => coordinateSystem.WithAuthority(authority, authorityCode); + + /// + /// Clones a geocentric coordinate system while replacing its authority metadata. + /// + /// Coordinate system to clone. + /// Replacement authority. + /// Replacement authority code. + /// A cloned geocentric coordinate system with the requested authority metadata. + internal static GeocentricCoordinateSystem WithAuthority(GeocentricCoordinateSystem coordinateSystem, string authority, long authorityCode) + => coordinateSystem.WithAuthority(authority, authorityCode); + + /// + /// Clones a projected coordinate system while replacing the authority metadata on its base geographic coordinate system. + /// + /// Projected coordinate system to clone. + /// Replacement base-geographic authority. + /// Replacement base-geographic authority code. + /// A cloned projected coordinate system with updated base geographic authority metadata. + internal static ProjectedCoordinateSystem WithBaseGeographicAuthority(ProjectedCoordinateSystem coordinateSystem, string authority, long authorityCode) + { + GeographicCoordinateSystem geographicCoordinateSystem = WithAuthority(coordinateSystem.GeographicCoordinateSystem, authority, authorityCode); + return CloneProjectedCoordinateSystem(coordinateSystem, geographicCoordinateSystem: geographicCoordinateSystem); + } + + /// + /// Clones a horizontal datum while replacing its primary metadata fields. + /// + /// Datum to clone. + /// Replacement name. + /// Replacement authority. + /// Replacement authority code. + /// Replacement ensemble metadata, or to preserve the source ensemble. + /// A cloned horizontal datum with the requested metadata. + internal static HorizontalDatum CloneHorizontalDatumWithMetadata(HorizontalDatum datum, string name, string authority, long authorityCode, DatumEnsemble? ensemble = null) + { + ArgumentNullException.ThrowIfNull(datum); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(authority); + + HorizontalDatum clone = datum.WithName(name); + clone = clone.WithAuthority(authority, authorityCode); + return ensemble is null + ? clone + : clone.WithEnsemble(ensemble); + } + + /// + /// Clones a vertical datum while replacing its authority metadata. + /// + /// Datum to clone. + /// Replacement authority. + /// Replacement authority code. + /// Replacement ensemble metadata, or to preserve the source ensemble. + /// A cloned vertical datum with the requested authority metadata. + internal static VerticalDatum CloneVerticalDatumWithMetadata(VerticalDatum datum, string authority, long authorityCode, DatumEnsemble? ensemble = null) + { + ArgumentNullException.ThrowIfNull(datum); + ArgumentNullException.ThrowIfNull(authority); + + VerticalDatum clone = datum.WithAuthority(authority, authorityCode); + return ensemble is null + ? clone + : clone.WithEnsemble(ensemble); + } + + private static GeographicCoordinateSystem CloneGeographicCoordinateSystem( + GeographicCoordinateSystem coordinateSystem, + string? authority = null, + long? authorityCode = null, + HorizontalDatum? horizontalDatum = null) + { + ArgumentNullException.ThrowIfNull(coordinateSystem); + + var clone = new GeographicCoordinateSystem( + coordinateSystem.AngularUnit, + horizontalDatum ?? coordinateSystem.HorizontalDatum, + coordinateSystem.PrimeMeridian, + CloneAxisInfo(coordinateSystem.AxisInfo), + coordinateSystem.Name, + authority ?? coordinateSystem.Authority, + authorityCode ?? coordinateSystem.AuthorityCode, + coordinateSystem.Alias, + coordinateSystem.Abbreviation, + coordinateSystem.Remarks, + coordinateSystem.DefaultEnvelope, + CloneWgs84ConversionInfoList(coordinateSystem.WGS84ConversionInfo)); + + return clone; + } + + private static ProjectedCoordinateSystem CloneProjectedCoordinateSystem( + ProjectedCoordinateSystem coordinateSystem, + string? authority = null, + long? authorityCode = null, + GeographicCoordinateSystem? geographicCoordinateSystem = null) + { + ArgumentNullException.ThrowIfNull(coordinateSystem); + + var clone = new ProjectedCoordinateSystem( + (geographicCoordinateSystem ?? coordinateSystem.GeographicCoordinateSystem).HorizontalDatum, + geographicCoordinateSystem ?? coordinateSystem.GeographicCoordinateSystem, + coordinateSystem.LinearUnit, + coordinateSystem.Projection, + CloneAxisInfo(coordinateSystem.AxisInfo), + coordinateSystem.Name, + authority ?? coordinateSystem.Authority, + authorityCode ?? coordinateSystem.AuthorityCode, + coordinateSystem.Alias, + coordinateSystem.Remarks, + coordinateSystem.Abbreviation, + coordinateSystem.DefaultEnvelope); + + return clone; + } + + private static GeocentricCoordinateSystem CloneGeocentricCoordinateSystem( + GeocentricCoordinateSystem coordinateSystem, + string? authority = null, + long? authorityCode = null) + { + ArgumentNullException.ThrowIfNull(coordinateSystem); + + var clone = new GeocentricCoordinateSystem( + coordinateSystem.HorizontalDatum, + coordinateSystem.LinearUnit, + coordinateSystem.PrimeMeridian, + CloneAxisInfo(coordinateSystem.AxisInfo), + coordinateSystem.Name, + authority ?? coordinateSystem.Authority, + authorityCode ?? coordinateSystem.AuthorityCode, + coordinateSystem.Alias, + coordinateSystem.Remarks, + coordinateSystem.Abbreviation, + coordinateSystem.DefaultEnvelope); + + return clone; + } + + private static List CloneAxisInfo(List axisInfo) + { + var clone = new List(axisInfo.Count); + foreach (AxisInfo axis in axisInfo) + { + clone.Add(new AxisInfo(axis)); + } + + return clone; + } + + private static List CloneWgs84ConversionInfoList(List conversions) + { + var clone = new List(conversions.Count); + foreach (Wgs84ConversionInfo conversion in conversions) + { + clone.Add(CloneWgs84ConversionInfo(conversion)); + } + + return clone; + } + + private static Wgs84ConversionInfo CloneWgs84ConversionInfo(Wgs84ConversionInfo conversion) + { + ArgumentNullException.ThrowIfNull(conversion); + return new Wgs84ConversionInfo( + conversion.Dx, + conversion.Dy, + conversion.Dz, + conversion.Ex, + conversion.Ey, + conversion.Ez, + conversion.Ppm, + conversion.AreaOfUse); + } +} diff --git a/test/ProjNet.Tests/Support/CoordinateTransformTestsBase.cs b/test/ProjNet.Tests/Support/CoordinateTransformTestsBase.cs new file mode 100644 index 00000000..56a5b64c --- /dev/null +++ b/test/ProjNet.Tests/Support/CoordinateTransformTestsBase.cs @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using ProjNet.CoordinateSystems; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Base class providing shared factories, stochastic input, and transformation assertions for transform-focused tests. +/// +public abstract class CoordinateTransformTestsBase +{ + private readonly CoordinateSystemFactory coordinateSystemFactory = CoordinateSystemTestHelpers.CreateCoordinateSystemFactory(); + private readonly CoordinateTransformationFactory coordinateTransformationFactory = CoordinateSystemTestHelpers.CreateCoordinateTransformationFactory(); + private readonly Random random = new(); + + /// + /// Gets the shared coordinate system factory used by transformation tests. + /// + protected CoordinateSystemFactory CoordinateSystemFactory => this.coordinateSystemFactory; + + /// + /// Gets the shared transformation factory used by transformation tests. + /// + protected CoordinateTransformationFactory CoordinateTransformationFactory => this.coordinateTransformationFactory; + + /// + /// Gets the random source used for stochastic test data when needed. + /// + protected Random Random => this.random; + + /// + /// Gets or sets a value indicating whether verbose test diagnostics are enabled. + /// + protected bool Verbose { get; set; } + + /// + /// Parses a coordinate system from WKT using the shared test factory. + /// + /// Well-known text representation of the coordinate system. + /// The parsed coordinate system. + protected CoordinateSystem RequireCoordinateSystem(string wkt) + => CoordinateSystemTestHelpers.RequireCoordinateSystem(this.CoordinateSystemFactory, wkt); + + /// + /// Parses a coordinate system from WKT using the shared test factory and asserts the requested type. + /// + /// The expected coordinate-system type. + /// Well-known text representation of the coordinate system. + /// The parsed coordinate system. + protected TCoordinateSystem RequireCoordinateSystem(string wkt) + where TCoordinateSystem : CoordinateSystem + => CoordinateSystemTestHelpers.RequireCoordinateSystem(this.CoordinateSystemFactory, wkt); + + /// + /// Creates a transformation between two coordinate systems using the shared factory. + /// + /// Source coordinate system. + /// Target coordinate system. + /// The created transformation. + protected ICoordinateTransformation CreateTransformation(CoordinateSystem source, CoordinateSystem target) + { + ArgumentNullException.ThrowIfNull(source); + ArgumentNullException.ThrowIfNull(target); + + return this.CoordinateTransformationFactory.CreateFromCoordinateSystems(source, target); + } + + /// + /// Creates a transformation and asserts that the operation succeeds without throwing. + /// + /// Source coordinate system. + /// Target coordinate system. + /// The created transformation. + protected ICoordinateTransformation AssertTransformationCreated(CoordinateSystem source, CoordinateSystem target) + { + ICoordinateTransformation? transformation = null; + Exception? exception = Record.Exception(() => transformation = this.CreateTransformation(source, target)); + Assert.Null(exception); + return Assert.IsType(transformation, exactMatch: false); + } + + /// + /// Checks whether the coordinate deltas between two points are below the provided tolerance. + /// + /// First point. + /// Second point. + /// Maximum allowed absolute delta per ordinate. + /// when all compared ordinates are within tolerance. + protected bool ToleranceLessThan(double[] p1, double[] p2, double tolerance) + { + ArgumentNullException.ThrowIfNull(p1); + ArgumentNullException.ThrowIfNull(p2); + + double d0 = Math.Abs(p1[0] - p2[0]); + double d1 = Math.Abs(p1[1] - p2[1]); + if (p1.Length > 2 && p2.Length > 2) + { + double d2 = Math.Abs(p1[2] - p2[2]); + if (this.Verbose) + { + Console.WriteLine("Allowed Tolerance {3}; got dx: {0}, dy: {1}, dz {2}", d0, d1, d2, tolerance); + } + + return d0 < tolerance && d1 < tolerance && d2 < tolerance; + } + + Console.WriteLine(); + if (this.Verbose) + { + Console.WriteLine("Allowed tolerance {2}; got dx: {0}, dy: {1}", d0, d1, tolerance); + } + + return d0 < tolerance && d1 < tolerance; + } + + /// + /// Formats a readable error message for transformation mismatches. + /// + /// Projection label used in the message. + /// Expected coordinate. + /// Actual coordinate. + /// Whether the failing direction is reverse/inverse. + /// Formatted error string for diagnostics. + protected string TransformationError(string projection, double[] pExpected, double[] pResult, bool reverse = false) + { + ArgumentNullException.ThrowIfNull(pExpected); + ArgumentNullException.ThrowIfNull(pResult); + + return FormattableString.Invariant($"{projection} {(reverse ? "reverse" : "forward")} transformation outside tolerance!\n\tExpected [{pExpected[0]}, {pExpected[1]}],\n\tgot [{pResult[0]}, {pResult[1]}],\n\tdelta [{pExpected[0] - pResult[0]}, {pExpected[1] - pResult[1]}]"); + } + + /// + /// Asserts that an actual transformed coordinate matches the expected coordinate within the provided tolerance. + /// + /// Projection label used in assertion diagnostics. + /// Expected coordinate. + /// Actual coordinate. + /// Maximum allowed absolute delta per ordinate. + /// Whether the asserted direction is reverse/inverse. + protected void AssertCoordinateWithinTolerance(string projection, double[] expected, double[] actual, double tolerance, bool reverse = false) + => Assert.True( + this.ToleranceLessThan(actual, expected, tolerance), + this.TransformationError(projection, expected, actual, reverse)); + + /// + /// Executes a forward (and optionally reverse) transformation assertion with tolerance checks. + /// + /// Display title for error diagnostics. + /// Source coordinate system. + /// Target coordinate system. + /// Input coordinate in source space. + /// Expected coordinate in target space. + /// Forward transformation tolerance. + /// Optional inverse tolerance; NaN skips inverse assertion. + protected void AssertTransformation( + string title, + CoordinateSystem source, + CoordinateSystem target, + double[] testPoint, + double[] expectedPoint, + double tolerance, + double reverseTolerance = double.NaN) + { + ICoordinateTransformation transformation = this.CreateTransformation(source, target); + double[] forwardResult = transformation.MathTransform.Transform(testPoint); + this.AssertCoordinateWithinTolerance(title, expectedPoint, forwardResult, tolerance); + + if (double.IsNaN(reverseTolerance)) + { + return; + } + + double[] reverseResult = transformation.MathTransform.Inverse().Transform(forwardResult); + this.AssertCoordinateWithinTolerance(title, testPoint, reverseResult, reverseTolerance, reverse: true); + } +} diff --git a/test/ProjNet.Tests/Support/GieCase.cs b/test/ProjNet.Tests/Support/GieCase.cs new file mode 100644 index 00000000..f645361f --- /dev/null +++ b/test/ProjNet.Tests/Support/GieCase.cs @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using Xunit.Sdk; + +/// +/// Represents a single GIE fixture test case, including the operation string, tolerance, input and expected coordinates, and transformation direction. +/// +public sealed class GieCase : IXunitSerializable +{ + /// + /// Initializes a new instance of the class. + /// + public GieCase() + { + } + + /// + /// Gets or sets the source line number of the parsed case. + /// + public int LineNumber { get; set; } + + /// + /// Gets or sets the PROJ operation string associated with the case. + /// + public string Operation { get; set; } = string.Empty; + + /// + /// Gets or sets the numeric tolerance value used for comparisons. + /// + public double ToleranceValue { get; set; } + + /// + /// Gets or sets the tolerance unit token as parsed from the fixture. + /// + public string ToleranceUnit { get; set; } = string.Empty; + + /// + /// Gets or sets the transformation direction for the case. + /// + public GieDirection Direction { get; set; } + + /// + /// Gets or sets the accepted input coordinate tuple. + /// + public double[] Accept { get; set; } = []; + + /// + /// Gets or sets the expected output coordinate tuple. + /// + public double[] Expect { get; set; } = []; + + /// + /// Gets or sets a value indicating whether the case expects a transformation failure. + /// + public bool ExpectsFailure { get; set; } + + /// + /// Gets or sets the expected error code when a failure is expected. + /// + public string ExpectedErrorCode { get; set; } = string.Empty; + + /// + /// Gets or sets the optional roundtrip count for iterative validation. + /// + public int? RoundtripCount { get; set; } + + /// + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(this.LineNumber), this.LineNumber); + info.AddValue(nameof(this.Operation), this.Operation); + info.AddValue(nameof(this.ToleranceValue), this.ToleranceValue); + info.AddValue(nameof(this.ToleranceUnit), this.ToleranceUnit); + info.AddValue(nameof(this.Direction), (int)this.Direction); + info.AddValue(nameof(this.ExpectsFailure), this.ExpectsFailure); + info.AddValue(nameof(this.ExpectedErrorCode), this.ExpectedErrorCode); + + info.AddValue("Accept.Length", this.Accept.Length); + for (int i = 0; i < this.Accept.Length; i++) + { + info.AddValue($"Accept[{i}]", this.Accept[i]); + } + + info.AddValue("Expect.Length", this.Expect.Length); + for (int i = 0; i < this.Expect.Length; i++) + { + info.AddValue($"Expect[{i}]", this.Expect[i]); + } + + info.AddValue("RoundtripCount.HasValue", this.RoundtripCount.HasValue); + if (this.RoundtripCount.HasValue) + { + info.AddValue("RoundtripCount.Value", this.RoundtripCount.Value); + } + } + + /// + public void Deserialize(IXunitSerializationInfo info) + { + this.LineNumber = info.GetValue(nameof(this.LineNumber)); + this.Operation = info.GetValue(nameof(this.Operation)) ?? string.Empty; + this.ToleranceValue = info.GetValue(nameof(this.ToleranceValue)); + this.ToleranceUnit = info.GetValue(nameof(this.ToleranceUnit)) ?? string.Empty; + this.Direction = (GieDirection)info.GetValue(nameof(this.Direction)); + this.ExpectsFailure = info.GetValue(nameof(this.ExpectsFailure)); + this.ExpectedErrorCode = info.GetValue(nameof(this.ExpectedErrorCode)) ?? string.Empty; + + int acceptLength = info.GetValue("Accept.Length"); + this.Accept = new double[acceptLength]; + for (int i = 0; i < acceptLength; i++) + { + this.Accept[i] = info.GetValue($"Accept[{i}]"); + } + + int expectLength = info.GetValue("Expect.Length"); + this.Expect = new double[expectLength]; + for (int i = 0; i < expectLength; i++) + { + this.Expect[i] = info.GetValue($"Expect[{i}]"); + } + + if (info.GetValue("RoundtripCount.HasValue")) + { + this.RoundtripCount = info.GetValue("RoundtripCount.Value"); + } + } + + /// + public override string ToString() => $"L{this.LineNumber} {this.Direction} {this.Operation}"; +} diff --git a/test/ProjNet.Tests/Support/GieDirection.cs b/test/ProjNet.Tests/Support/GieDirection.cs new file mode 100644 index 00000000..2a0eec1a --- /dev/null +++ b/test/ProjNet.Tests/Support/GieDirection.cs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +/// +/// Specifies the direction of a coordinate transformation. +/// +public enum GieDirection +{ + /// + /// Executes the forward projection direction. + /// + Forward = 0, + + /// + /// Executes the inverse projection direction. + /// + Inverse = 1, +} diff --git a/test/ProjNet.Tests/Support/GieParser.cs b/test/ProjNet.Tests/Support/GieParser.cs new file mode 100644 index 00000000..0214aa33 --- /dev/null +++ b/test/ProjNet.Tests/Support/GieParser.cs @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +/// +/// Parses PROJ GIE fixture text into strongly typed test cases. +/// +internal static class GieParser +{ + private static readonly char[] WhiteSpaceSeparators = [' ', '\t']; + + /// + /// Parses a GIE fixture file from disk. + /// + /// Path to the fixture file. + /// Optional parser behavior options. + /// Parsed GIE cases. + public static IReadOnlyList ParseFile(string path, GieParserOptions? options = null) + { + ArgumentNullException.ThrowIfNull(path); + return Parse(File.ReadAllText(path), options); + } + + /// + /// Parses GIE fixture content provided as raw text. + /// + /// Fixture content text. + /// Optional parser behavior options. + /// Parsed GIE cases. + public static IReadOnlyList Parse(string content, GieParserOptions? options = null) + { + ArgumentNullException.ThrowIfNull(content); + options ??= new GieParserOptions(); + + var parsedCases = new List(); + string? currentOperation = null; + double currentToleranceValue = 0d; + string currentToleranceUnit = "m"; + GieDirection currentDirection = GieDirection.Forward; + int? currentRoundtrip = null; + double[]? pendingAccept = null; + + foreach (LogicalLine logicalLine in EnumerateLogicalLines(content)) + { + string stripped = logicalLine.Content; + int lineNumber = logicalLine.LineNumber; + if (stripped.Length == 0 || IsTagLine(stripped) || IsSeparatorLine(stripped)) + { + continue; + } + + if (options.AllowOperationContinuation && currentOperation is not null && stripped.StartsWith('+')) + { + currentOperation += $" {stripped}"; + continue; + } + + SplitDirective(stripped, out string directive, out string payload); + + if (directive.Equals("operation", StringComparison.OrdinalIgnoreCase)) + { + currentOperation = payload; + currentToleranceValue = 0d; + currentToleranceUnit = "m"; + currentDirection = GieDirection.Forward; + currentRoundtrip = null; + pendingAccept = null; + } + else if (directive.Equals("tolerance", StringComparison.OrdinalIgnoreCase)) + { + ParseTolerance(payload, lineNumber, out currentToleranceValue, out currentToleranceUnit); + } + else if (directive.Equals("direction", StringComparison.OrdinalIgnoreCase)) + { + EnsurePayload(payload, "direction", lineNumber); + currentDirection = ParseDirection(payload, lineNumber); + } + else if (directive.Equals("roundtrip", StringComparison.OrdinalIgnoreCase)) + { + currentRoundtrip = ParseRoundtrip(payload, lineNumber); + } + else if (directive.Equals("accept", StringComparison.OrdinalIgnoreCase)) + { + EnsureOperationDeclared(currentOperation, lineNumber, "accept"); + pendingAccept = ParseVector(payload, lineNumber, "accept"); + } + else if (directive.Equals("expect", StringComparison.OrdinalIgnoreCase)) + { + EnsureOperationDeclared(currentOperation, lineNumber, "expect"); + + if (IsFailureExpectation(payload)) + { + parsedCases.Add( + new GieCase + { + LineNumber = lineNumber, + Operation = currentOperation ?? string.Empty, + ToleranceValue = currentToleranceValue, + ToleranceUnit = currentToleranceUnit, + Direction = currentDirection, + Accept = pendingAccept ?? [], + Expect = [], + ExpectsFailure = true, + ExpectedErrorCode = ParseExpectedErrorCode(payload), + RoundtripCount = currentRoundtrip, + }); + pendingAccept = null; + continue; + } + + if (pendingAccept is null) + { + if (options.IgnoreUnknownDirectives) + { + continue; + } + + throw new FormatException($"Found 'expect' without preceding 'accept' at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + + double[] expected = ParseVector(payload, lineNumber, "expect"); + double[] accepted = pendingAccept; + parsedCases.Add( + new GieCase + { + LineNumber = lineNumber, + Operation = currentOperation ?? string.Empty, + ToleranceValue = currentToleranceValue, + ToleranceUnit = currentToleranceUnit, + Direction = currentDirection, + Accept = accepted, + Expect = expected, + RoundtripCount = currentRoundtrip, + }); + pendingAccept = null; + } + else if (!options.IgnoreUnknownDirectives) + { + throw new FormatException($"Unsupported GIE directive '{directive}' at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + } + + if (pendingAccept is not null) + { + return options.IgnoreUnknownDirectives + ? (IReadOnlyList)parsedCases + : throw new FormatException("Dangling 'accept' without matching 'expect' at end of input."); + } + + return parsedCases; + } + + private static IEnumerable EnumerateLogicalLines(string content) + { + string[] lines = content.Replace("\r\n", "\n", StringComparison.Ordinal).Split('\n'); + string? current = null; + int currentStartLine = 1; + + for (int i = 0; i < lines.Length; i++) + { + string stripped = StripInlineComment(lines[i]).Trim(); + if (stripped.Length == 0 && current is null) + { + continue; + } + + bool hasContinuation = stripped.EndsWith('\\'); + if (hasContinuation) + { + stripped = stripped[..^1].TrimEnd(); + } + + if (current is null) + { + current = stripped; + currentStartLine = i + 1; + } + else + { + current += $" {stripped}"; + } + + if (hasContinuation) + { + continue; + } + + yield return new LogicalLine(current, currentStartLine); + current = null; + } + + if (current is not null) + { + yield return new LogicalLine(current, currentStartLine); + } + } + + private static bool IsTagLine(string line) + { + return line.StartsWith('<') && line.EndsWith('>'); + } + + private static bool IsSeparatorLine(string line) + { + if (line.Length < 3) + { + return false; + } + + char first = line[0]; + if (first != '-' && first != '=') + { + return false; + } + + for (int i = 1; i < line.Length; i++) + { + if (line[i] != first) + { + return false; + } + } + + return true; + } + + private static string StripInlineComment(string line) + { + if (line is null) + { + return string.Empty; + } + + int commentIndex = line.IndexOf('#', StringComparison.Ordinal); + return commentIndex < 0 ? line : line[..commentIndex]; + } + + private static void SplitDirective(string line, out string directive, out string payload) + { + int splitIndex = FindFirstWhiteSpace(line); + if (splitIndex < 0) + { + directive = line; + payload = string.Empty; + return; + } + + directive = line[..splitIndex]; + payload = line[(splitIndex + 1)..].Trim(); + } + + private static int ParseRoundtrip(string payload, int lineNumber) + { + EnsurePayload(payload, "roundtrip", lineNumber); + string[] tokens = payload.Split(WhiteSpaceSeparators, StringSplitOptions.RemoveEmptyEntries); + return (int)ParseNumber(tokens[0], lineNumber, "roundtrip"); + } + + private static void ParseTolerance(string payload, int lineNumber, out double value, out string unit) + { + EnsurePayload(payload, "tolerance", lineNumber); + string[] tokens = payload.Split(WhiteSpaceSeparators, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length == 0) + { + throw new FormatException($"Missing tolerance value at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + + string firstToken = tokens[0]; + unit = tokens.Length > 1 ? tokens[1] : "m"; + + if (TryParseCompactTolerance(firstToken, out double compactValue, out string compactUnit)) + { + value = compactValue; + if (tokens.Length == 1 && !string.IsNullOrWhiteSpace(compactUnit)) + { + unit = compactUnit; + } + + return; + } + + value = ParseNumber(firstToken, lineNumber, "tolerance"); + } + + private static GieDirection ParseDirection(string payload, int lineNumber) + { + string normalized = payload.Trim(); + if (normalized.Equals("forward", StringComparison.OrdinalIgnoreCase)) + { + return GieDirection.Forward; + } + + return normalized.Equals("inverse", StringComparison.OrdinalIgnoreCase) + || normalized.Equals("reverse", StringComparison.OrdinalIgnoreCase) + ? GieDirection.Inverse + : throw new FormatException($"Unsupported direction '{payload}' at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + + private static bool IsFailureExpectation(string payload) + { + return payload.StartsWith("failure", StringComparison.OrdinalIgnoreCase); + } + + private static string ParseExpectedErrorCode(string payload) + { + string[] tokens = payload.Split(WhiteSpaceSeparators, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < tokens.Length - 1; i++) + { + if (tokens[i].Equals("errno", StringComparison.OrdinalIgnoreCase)) + { + return tokens[i + 1]; + } + } + + return string.Empty; + } + + private static double[] ParseVector(string payload, int lineNumber, string directiveName) + { + EnsurePayload(payload, directiveName, lineNumber); + string[] tokens = payload.Split(WhiteSpaceSeparators, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length < 2) + { + throw new FormatException( + $"Directive '{directiveName}' requires at least two numeric values at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + + double[] values = new double[tokens.Length]; + for (int i = 0; i < tokens.Length; i++) + { + values[i] = ParseNumber(tokens[i], lineNumber, directiveName); + } + + return values; + } + + private static double ParseNumber(string token, int lineNumber, string directiveName) + { + string normalizedToken = token.Replace("_", string.Empty, StringComparison.Ordinal); + if (TryParseSpecialNumber(normalizedToken, out double specialValue)) + { + return specialValue; + } + + if (double.TryParse(normalizedToken, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double value)) + { + return value; + } + + return TryParseDmsCoordinate(normalizedToken, out value) + ? value + : throw new FormatException( + $"Failed to parse numeric value '{token}' in directive '{directiveName}' at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + + private static bool TryParseSpecialNumber(string token, out double value) + { + switch (token.ToUpperInvariant()) + { + case "HUGEVAL": + case "HUGEVALF": + case "INF": + case "+INF": + case "INFINITY": + case "+INFINITY": + value = double.PositiveInfinity; + return true; + case "-INF": + case "-INFINITY": + value = double.NegativeInfinity; + return true; + case "NAN": + case "+NAN": + case "-NAN": + value = double.NaN; + return true; + default: + value = 0d; + return false; + } + } + + private static void EnsureOperationDeclared(string? operation, int lineNumber, string directive) + { + if (operation is null) + { + throw new FormatException( + $"Found '{directive}' before any 'operation' declaration at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + } + + private static void EnsurePayload(string payload, string directiveName, int lineNumber) + { + if (string.IsNullOrWhiteSpace(payload)) + { + throw new FormatException($"Directive '{directiveName}' is missing payload at line {lineNumber.ToString(CultureInfo.InvariantCulture)}."); + } + } + + private static int FindFirstWhiteSpace(string value) + { + for (int i = 0; i < value.Length; i++) + { + if (value[i] == ' ' || value[i] == '\t') + { + return i; + } + } + + return -1; + } + + private static bool TryParseCompactTolerance(string token, out double value, out string unit) + { + value = 0d; + unit = string.Empty; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + int index = 0; + while (index < token.Length && (char.IsDigit(token[index]) || token[index] == '.' || token[index] == '-' || token[index] == '+' || token[index] == 'e' || token[index] == 'E')) + { + index++; + } + + if (index <= 0 || index >= token.Length) + { + return false; + } + + string numberPart = token[..index]; + string unitPart = token[index..]; + if (!double.TryParse(numberPart, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value)) + { + return false; + } + + unit = unitPart; + return true; + } + + private static bool TryParseDmsCoordinate(string token, out double value) + { + value = 0d; + if (string.IsNullOrWhiteSpace(token)) + { + return false; + } + + string text = token.Trim(); + int sign = 1; + + char last = text[text.Length - 1]; + if (last == 'W' || last == 'w' || last == 'S' || last == 's') + { + sign = -1; + text = text[..^1]; + } + else if (last == 'E' || last == 'e' || last == 'N' || last == 'n') + { + text = text[..^1]; + } + + if (text.StartsWith('-')) + { + sign *= -1; + text = text[1..]; + } + else if (text.StartsWith('+')) + { + text = text[1..]; + } + + int dIndex = text.IndexOf('d', StringComparison.Ordinal); + if (dIndex < 0) + { + dIndex = text.IndexOf('D', StringComparison.Ordinal); + } + + int mIndex = text.IndexOf('\'', StringComparison.Ordinal); + if (dIndex <= 0 || mIndex <= dIndex) + { + return false; + } + + string degreesToken = text[..dIndex]; + string minutesToken = text.Substring(dIndex + 1, mIndex - dIndex - 1); + if (!double.TryParse(degreesToken, NumberStyles.Float, CultureInfo.InvariantCulture, out double degrees)) + { + return false; + } + + if (!double.TryParse(minutesToken, NumberStyles.Float, CultureInfo.InvariantCulture, out double minutes)) + { + return false; + } + + double seconds = 0d; + int secondsStart = mIndex + 1; + if (secondsStart < text.Length) + { + int secondsMarker = text.IndexOf('"', secondsStart); + if (secondsMarker < 0) + { + secondsMarker = text.Length; + } + + string secondsToken = text.Substring(secondsStart, secondsMarker - secondsStart); + if (!double.TryParse(secondsToken, NumberStyles.Float, CultureInfo.InvariantCulture, out seconds)) + { + return false; + } + } + + value = sign * (degrees + (minutes / 60d) + (seconds / 3600d)); + return true; + } + + private readonly struct LogicalLine + { + public LogicalLine(string content, int lineNumber) + { + this.Content = content; + this.LineNumber = lineNumber; + } + + public string Content { get; } + + public int LineNumber { get; } + } +} diff --git a/test/ProjNet.Tests/Support/GieParserOptions.cs b/test/ProjNet.Tests/Support/GieParserOptions.cs new file mode 100644 index 00000000..44164811 --- /dev/null +++ b/test/ProjNet.Tests/Support/GieParserOptions.cs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +/// +/// Provides options that control how GIE fixture content is parsed. +/// +internal sealed class GieParserOptions +{ + /// + /// Gets or sets a value indicating whether unknown directives are ignored during parsing. + /// + public bool IgnoreUnknownDirectives { get; set; } + + /// + /// Gets or sets a value indicating whether multi-line operation directives are allowed. + /// + public bool AllowOperationContinuation { get; set; } = true; +} diff --git a/test/ProjNet.Tests/Support/GlobalEnvironmentTestIsolation.cs b/test/ProjNet.Tests/Support/GlobalEnvironmentTestIsolation.cs new file mode 100644 index 00000000..4f7ada6c --- /dev/null +++ b/test/ProjNet.Tests/Support/GlobalEnvironmentTestIsolation.cs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System.Diagnostics.CodeAnalysis; +using Xunit; + +/// +/// Defines a non-parallel test collection for tests that mutate process-wide environment variables or static resolver state. +/// +[SuppressMessage("Performance", "CA1515:Consider making public types internal", Justification = "xUnit requires collection definition classes to be public.")] +[CollectionDefinition("Global environment tests", DisableParallelization = true)] +public sealed class GlobalEnvironmentTestIsolation +{ + /// + /// The shared collection name for environment-sensitive tests. + /// + public const string Name = "Global environment tests"; +} diff --git a/test/ProjNet.Tests/Support/GlobalSuppressions.cs b/test/ProjNet.Tests/Support/GlobalSuppressions.cs new file mode 100644 index 00000000..84406315 --- /dev/null +++ b/test/ProjNet.Tests/Support/GlobalSuppressions.cs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy test member ordering retained to keep test churn low.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTests")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Legacy test member ordering retained to keep test churn low.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTestsBase")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Test helper shape is retained to minimize churn in legacy test fixtures.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTestsBase")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Test helper shape is retained to minimize churn in legacy test fixtures.", Scope = "type", Target = "~T:ProjNet.Tests.SharpMapIssueRegressionTests")] +[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Test helper shape is retained to minimize churn in legacy test fixtures.", Scope = "type", Target = "~T:ProjNet.Tests.IO.CoordinateSystems.WKTCoordSysParserTests")] +[assembly: SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Theory member data can legitimately provide null sentinel rows; null handling is intentional in fixture pipeline.", Scope = "member", Target = "~M:ProjNet.Tests.GieBuiltinsTheoryTests.BuiltinsCasesForImplementedProjectionsStayWithinTolerance(ProjNet.Tests.GieCase)")] +[assembly: SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Theory member data can legitimately provide null sentinel rows; null handling is intentional in fixture pipeline.", Scope = "member", Target = "~M:ProjNet.Tests.GieBuiltinsTheoryTests.MoreBuiltinsCasesForImplementedProjectionsStayWithinTolerance(ProjNet.Tests.GieCase)")] +[assembly: SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Theory member data can legitimately provide null sentinel rows; null handling is intentional in fixture pipeline.", Scope = "member", Target = "~M:ProjNet.Tests.GieBuiltinsTheoryTests.DhdnEtrs89CasesForImplementedProjectionsStayWithinTolerance(ProjNet.Tests.GieCase)")] +[assembly: SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Theory member data can legitimately provide null sentinel rows; null handling is intentional in fixture pipeline.", Scope = "member", Target = "~M:ProjNet.Tests.GieBuiltinsTheoryTests.RemainingGieCasesForImplementedProjectionsStayWithinTolerance(ProjNet.Tests.GieCase)")] +[assembly: SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Required public for TheoryDataRow type visibility in xUnit theory data providers.", Scope = "type", Target = "~T:ProjNet.Tests.GieCase")] +[assembly: SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Required public for TheoryDataRow type visibility in xUnit theory data providers.", Scope = "type", Target = "~T:ProjNet.Tests.GieDirection")] +[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Lowercase canonicalization is intentional in WKT equivalence normalization helpers.", Scope = "type", Target = "~T:ProjNet.Tests.EpsgWktEquivalenceTheoryTests")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy test member ordering retained to keep test churn low.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTests")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy test member ordering retained to keep test churn low.", Scope = "type", Target = "~T:ProjNet.Tests.GieBuiltinsTheoryTests")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy test member ordering retained to keep test churn low.", Scope = "type", Target = "~T:ProjNet.Tests.Proj2ProjParityTheoryTests")] + +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static members should appear before non-static members", Justification = "Legacy test member ordering retained to keep test churn low.", Scope = "type", Target = "~T:ProjNet.Tests.IO.CoordinateSystems.WKTCoordSysParserTests")] +[assembly: SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional", Justification = "Legacy table-style test vectors are retained for fixture readability and low-churn parity checks.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTests")] +[assembly: SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional", Justification = "Legacy table-style test vectors are retained for fixture readability and low-churn parity checks.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateSystems.Transformations.OperationResolutionEngineTests")] +[assembly: SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "Fixture payload shape is retained to match existing JSON contracts and test data tooling.", Scope = "member", Target = "~P:ProjNet.Tests.Proj2ProjParityTheoryTests.Proj2ProjFixture.Cases")] +[assembly: SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Legacy test helper and fixture types are intentionally public to preserve cross-test and serialization usage without broad churn.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTestsBase")] +[assembly: SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Legacy test helper and fixture types are intentionally public to preserve cross-test and serialization usage without broad churn.", Scope = "type", Target = "~T:ProjNet.Tests.Proj2ProjParityTheoryTests.Proj2ProjCase")] +[assembly: SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Legacy test helper and fixture types are intentionally public to preserve cross-test and serialization usage without broad churn.", Scope = "type", Target = "~T:ProjNet.Tests.Proj2ProjParityTheoryTests.Proj2ProjFixture")] +[assembly: SuppressMessage("Maintainability", "CA1515:Consider making public types internal", Justification = "Legacy SRID fixture value type remains public to preserve helper API shape and test usage patterns.", Scope = "type", Target = "~T:ProjNet.Tests.SRIDReader.WktString")] +[assembly: SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "Randomized test data is non-security-critical and intentionally reproducible for regression stability.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTests")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Legacy parser tests intentionally fail test execution with detailed context for any unexpected parser exception shape.", Scope = "member", Target = "~M:ProjNet.Tests.IO.CoordinateSystems.WKTMathTransformParserTests.ParseAffineTransformWkt")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Legacy parser tests intentionally fail test execution with detailed context for any unexpected parser exception shape.", Scope = "member", Target = "~M:ProjNet.Tests.IO.CoordinateSystems.WKTMathTransformParserTests.TestMathTransformWktReaderExponencialNumberParsingIssue(System.String)")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Bulk WKT compatibility sweep intentionally tracks and reports all failures without aborting the whole dataset run.", Scope = "member", Target = "~M:ProjNet.Tests.IO.CoordinateSystems.WKTCoordSysParserTests.TestCreateCoordinateTransformationForWktInCsv")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Legacy WKT fixture asserts include full payload details and intentionally trap unexpected parser failures.", Scope = "member", Target = "~M:ProjNet.Tests.IO.CoordinateSystems.WKTCoordSysParserTests.TestFittedCoordinateSystemWkt")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Legacy WKT fixture asserts include full payload details and intentionally trap unexpected parser failures.", Scope = "member", Target = "~M:ProjNet.Tests.IO.CoordinateSystems.WKTCoordSysParserTests.TestGeocentricCoordinateSystem")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Optional PostGIS integration test intentionally records parse failures and continues to provide aggregate diagnostics.", Scope = "member", Target = "~M:ProjNet.Tests.IO.CoordinateSystems.PostGisSpatialRefSysTableParserTests.TestParse(System.Int32,System.String)~System.Boolean")] +[assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "GIE fixture data shape uses coordinate arrays for Accept/Expect tuples deserialized from JSON.", Scope = "type", Target = "~T:ProjNet.Tests.GieCase")] +[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Legacy test diagnostics intentionally use inline literals for concise assertion and console output in test-only code.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateSystemServicesTests")] +[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Legacy test diagnostics intentionally use inline literals for concise assertion messages in helper assertions.", Scope = "type", Target = "~T:ProjNet.Tests.CoordinateTransformTestsBase")] +[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Legacy optional PostGIS integration tests intentionally use inline literals for runtime diagnostics.", Scope = "type", Target = "~T:ProjNet.Tests.IO.CoordinateSystems.PostGisSpatialRefSysTableParserTests")] diff --git a/test/ProjNet.Tests/Support/Matrix3x3Tests.cs b/test/ProjNet.Tests/Support/Matrix3x3Tests.cs new file mode 100644 index 00000000..bc17a4ac --- /dev/null +++ b/test/ProjNet.Tests/Support/Matrix3x3Tests.cs @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using ProjNet.CoordinateSystems.Transformations.Numerics; +using Xunit; + +/// +/// Tests matrix and vector helper primitives used by transformation implementations. +/// +public class Matrix3x3Tests +{ + /// + /// Verifies vector addition returns the component-wise sum. + /// + [Fact] + public void Vector3DAdditionGivenTwoVectorsReturnsComponentWiseSum() + { + var left = new Vector3D(1d, 2d, 3d); + var right = new Vector3D(4d, 5d, 6d); + + Vector3D result = left + right; + + Assert.Equal(5d, result.X, 12); + Assert.Equal(7d, result.Y, 12); + Assert.Equal(9d, result.Z, 12); + } + + /// + /// Verifies vector subtraction returns the component-wise difference. + /// + [Fact] + public void Vector3DSubtractionGivenTwoVectorsReturnsComponentWiseDifference() + { + var left = new Vector3D(10d, 8d, 6d); + var right = new Vector3D(1d, 2d, 3d); + + Vector3D result = left - right; + + Assert.Equal(9d, result.X, 12); + Assert.Equal(6d, result.Y, 12); + Assert.Equal(3d, result.Z, 12); + } + + /// + /// Verifies scaling and reciprocal scaling keep vector components consistent. + /// + [Fact] + public void Vector3DScalarOperationsGivenScaleAndDivisionReturnsExpectedValues() + { + var value = new Vector3D(2d, -4d, 6d); + + Vector3D multiplied = value * 3d; + Vector3D divided = multiplied / 3d; + + Assert.Equal(6d, multiplied.X, 12); + Assert.Equal(-12d, multiplied.Y, 12); + Assert.Equal(18d, multiplied.Z, 12); + Assert.Equal(value.X, divided.X, 12); + Assert.Equal(value.Y, divided.Y, 12); + Assert.Equal(value.Z, divided.Z, 12); + } + + /// + /// Verifies transposition swaps matrix rows and columns. + /// + [Fact] + public void Matrix3x3TransposeGivenMatrixReturnsSwappedRowsAndColumns() + { + var matrix = new Matrix3x3( + 1d, + 2d, + 3d, + 4d, + 5d, + 6d, + 7d, + 8d, + 9d); + + Matrix3x3 transposed = matrix.Transpose(); + + Assert.Equal(1d, transposed.M00, 12); + Assert.Equal(4d, transposed.M01, 12); + Assert.Equal(7d, transposed.M02, 12); + Assert.Equal(2d, transposed.M10, 12); + Assert.Equal(5d, transposed.M11, 12); + Assert.Equal(8d, transposed.M12, 12); + Assert.Equal(3d, transposed.M20, 12); + Assert.Equal(6d, transposed.M21, 12); + Assert.Equal(9d, transposed.M22, 12); + } + + /// + /// Verifies matrix-vector multiplication against a known expected product. + /// + [Fact] + public void Matrix3x3MultiplyVectorGivenKnownInputsReturnsExpectedProduct() + { + var matrix = new Matrix3x3( + 1d, + 2d, + 3d, + 0d, + 1d, + 4d, + 5d, + 6d, + 0d); + var vector = new Vector3D(1d, 2d, 3d); + + Vector3D result = matrix * vector; + + Assert.Equal(14d, result.X, 12); + Assert.Equal(14d, result.Y, 12); + Assert.Equal(17d, result.Z, 12); + } + + /// + /// Verifies matrix-matrix multiplication against a known expected composition. + /// + [Fact] + public void Matrix3x3MultiplyMatrixGivenTwoMatricesReturnsExpectedComposition() + { + var left = new Matrix3x3( + 1d, + 2d, + 3d, + 4d, + 5d, + 6d, + 7d, + 8d, + 9d); + var right = new Matrix3x3( + 9d, + 8d, + 7d, + 6d, + 5d, + 4d, + 3d, + 2d, + 1d); + + Matrix3x3 result = left * right; + + Assert.Equal(30d, result.M00, 12); + Assert.Equal(24d, result.M01, 12); + Assert.Equal(18d, result.M02, 12); + Assert.Equal(84d, result.M10, 12); + Assert.Equal(69d, result.M11, 12); + Assert.Equal(54d, result.M12, 12); + Assert.Equal(138d, result.M20, 12); + Assert.Equal(114d, result.M21, 12); + Assert.Equal(90d, result.M22, 12); + } + + /// + /// Verifies identity matrix multiplication preserves vector values. + /// + [Fact] + public void Matrix3x3IdentityGivenVectorLeavesVectorUnchanged() + { + var value = new Vector3D(-3d, 4d, 12d); + + Vector3D result = Matrix3x3.Identity * value; + + Assert.Equal(value.X, result.X, 12); + Assert.Equal(value.Y, result.Y, 12); + Assert.Equal(value.Z, result.Z, 12); + } + + /// + /// Verifies value equality and hash-code parity for equivalent matrices. + /// + [Fact] + public void Matrix3x3EqualityGivenSameComponentsReturnsTrue() + { + var left = new Matrix3x3( + 1d, + 2d, + 3d, + 4d, + 5d, + 6d, + 7d, + 8d, + 9d); + var right = new Matrix3x3( + 1d, + 2d, + 3d, + 4d, + 5d, + 6d, + 7d, + 8d, + 9d); + + Assert.True(left.Equals(right)); + Assert.True(left == right); + Assert.False(left != right); + Assert.Equal(left.GetHashCode(), right.GetHashCode()); + } + + /// + /// Verifies identity and zero flags for representative matrix instances. + /// + [Fact] + public void Matrix3x3FlagsGivenKnownMatricesReportIdentityAndZeroCorrectly() + { + Matrix3x3 identity = Matrix3x3.Identity; + Matrix3x3 zero = Matrix3x3.Zero; + var other = new Matrix3x3( + 1d, + 0d, + 0d, + 0d, + 2d, + 0d, + 0d, + 0d, + 1d); + + Assert.True(identity.IsIdentity); + Assert.False(identity.IsZero); + + Assert.True(zero.IsZero); + Assert.False(zero.IsIdentity); + + Assert.False(other.IsIdentity); + Assert.False(other.IsZero); + } +} diff --git a/test/ProjNet.Tests/Support/Proj2ProjCase.cs b/test/ProjNet.Tests/Support/Proj2ProjCase.cs new file mode 100644 index 00000000..47247b62 --- /dev/null +++ b/test/ProjNet.Tests/Support/Proj2ProjCase.cs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System.Diagnostics.CodeAnalysis; +using Xunit.Sdk; + +/// +/// Represents a single direct proj2proj parity fixture row. +/// +[SuppressMessage("Performance", "CA1515:Consider making public types internal", Justification = "Public visibility is required because xUnit theory methods consume this DTO as a public parameter type.")] +public sealed class Proj2ProjCase : IXunitSerializable +{ + /// + /// Initializes a new instance of the class. + /// + public Proj2ProjCase() + { + } + + /// + /// Gets or sets the EPSG operation code for the parity case. + /// + public int OperationCode { get; set; } + + /// + /// Gets or sets the source SRID. + /// + public int SourceSrid { get; set; } + + /// + /// Gets or sets the target SRID. + /// + public int TargetSrid { get; set; } + + /// + /// Gets or sets the source CRS WKT definition. + /// + public string SourceWkt { get; set; } = string.Empty; + + /// + /// Gets or sets the target CRS WKT definition. + /// + public string TargetWkt { get; set; } = string.Empty; + + /// + /// Gets or sets the input x coordinate. + /// + public double InputX { get; set; } + + /// + /// Gets or sets the input y coordinate. + /// + public double InputY { get; set; } + + /// + /// Gets or sets the expected x coordinate. + /// + public double ExpectedX { get; set; } + + /// + /// Gets or sets the expected y coordinate. + /// + public double ExpectedY { get; set; } + + /// + /// Gets or sets the tolerance in meters for result comparison. + /// + public double ToleranceMeters { get; set; } + + /// + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(this.OperationCode), this.OperationCode); + info.AddValue(nameof(this.SourceSrid), this.SourceSrid); + info.AddValue(nameof(this.TargetSrid), this.TargetSrid); + info.AddValue(nameof(this.SourceWkt), this.SourceWkt); + info.AddValue(nameof(this.TargetWkt), this.TargetWkt); + info.AddValue(nameof(this.InputX), this.InputX); + info.AddValue(nameof(this.InputY), this.InputY); + info.AddValue(nameof(this.ExpectedX), this.ExpectedX); + info.AddValue(nameof(this.ExpectedY), this.ExpectedY); + info.AddValue(nameof(this.ToleranceMeters), this.ToleranceMeters); + } + + /// + public void Deserialize(IXunitSerializationInfo info) + { + this.OperationCode = info.GetValue(nameof(this.OperationCode)); + this.SourceSrid = info.GetValue(nameof(this.SourceSrid)); + this.TargetSrid = info.GetValue(nameof(this.TargetSrid)); + this.SourceWkt = info.GetValue(nameof(this.SourceWkt)) ?? string.Empty; + this.TargetWkt = info.GetValue(nameof(this.TargetWkt)) ?? string.Empty; + this.InputX = info.GetValue(nameof(this.InputX)); + this.InputY = info.GetValue(nameof(this.InputY)); + this.ExpectedX = info.GetValue(nameof(this.ExpectedX)); + this.ExpectedY = info.GetValue(nameof(this.ExpectedY)); + this.ToleranceMeters = info.GetValue(nameof(this.ToleranceMeters)); + } + + /// + public override string ToString() => $"Op{this.OperationCode} {this.SourceSrid}->{this.TargetSrid}"; +} diff --git a/test/ProjNet.Tests/Support/Proj2ProjFixture.cs b/test/ProjNet.Tests/Support/Proj2ProjFixture.cs new file mode 100644 index 00000000..c931804d --- /dev/null +++ b/test/ProjNet.Tests/Support/Proj2ProjFixture.cs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +/// +/// Represents the direct proj2proj parity fixture payload. +/// +[SuppressMessage("Performance", "CA1515:Consider making public types internal", Justification = "Public visibility is required because xUnit theory methods consume this DTO as a public parameter type.")] +[SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "System.Text.Json materializes this DTO via writable List.")] +[SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Writable setter is required for fixture deserialization.")] +public sealed class Proj2ProjFixture +{ + /// + /// Gets or sets the fixture schema/version marker. + /// + public int FixtureVersion { get; set; } + + /// + /// Gets or sets the generator identifier used to produce the fixture. + /// + public string Generator { get; set; } = string.Empty; + + /// + /// Gets or sets the parity cases included in the fixture payload. + /// + public List Cases { get; set; } = []; +} diff --git a/test/ProjNet.Tests/Support/RoundtripAccuracyCase.cs b/test/ProjNet.Tests/Support/RoundtripAccuracyCase.cs new file mode 100644 index 00000000..2a81be8b --- /dev/null +++ b/test/ProjNet.Tests/Support/RoundtripAccuracyCase.cs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System.Diagnostics.CodeAnalysis; +using Xunit.Sdk; + +/// +/// Represents a single forward→inverse roundtrip accuracy fixture row. +/// +[SuppressMessage("Performance", "CA1515:Consider making public types internal", Justification = "Public visibility is required because xUnit theory methods consume this DTO as a public parameter type.")] +public sealed class RoundtripAccuracyCase : IXunitSerializable +{ + /// + /// Initializes a new instance of the class. + /// + public RoundtripAccuracyCase() + { + } + + /// + /// Gets or sets a human-readable description for this test case. + /// + public string Description { get; set; } = string.Empty; + + /// + /// Gets or sets the source EPSG SRID. + /// + public int SourceSrid { get; set; } + + /// + /// Gets or sets the target EPSG SRID. + /// + public int TargetSrid { get; set; } + + /// + /// Gets or sets the input longitude (geographic source coordinate). + /// + public double InputLon { get; set; } + + /// + /// Gets or sets the input latitude (geographic source coordinate). + /// + public double InputLat { get; set; } + + /// + /// Gets or sets the expected forward-projected x coordinate from PROJ. + /// + public double ForwardX { get; set; } + + /// + /// Gets or sets the expected forward-projected y coordinate from PROJ. + /// + public double ForwardY { get; set; } + + /// + /// Gets or sets the longitude obtained by PROJ's inverse transform of the forward result. + /// + public double InverseBackLon { get; set; } + + /// + /// Gets or sets the latitude obtained by PROJ's inverse transform of the forward result. + /// + public double InverseBackLat { get; set; } + + /// + /// Gets or sets the tolerance in meters for result comparison. + /// + public double ToleranceMeters { get; set; } + + /// + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(this.Description), this.Description); + info.AddValue(nameof(this.SourceSrid), this.SourceSrid); + info.AddValue(nameof(this.TargetSrid), this.TargetSrid); + info.AddValue(nameof(this.InputLon), this.InputLon); + info.AddValue(nameof(this.InputLat), this.InputLat); + info.AddValue(nameof(this.ForwardX), this.ForwardX); + info.AddValue(nameof(this.ForwardY), this.ForwardY); + info.AddValue(nameof(this.InverseBackLon), this.InverseBackLon); + info.AddValue(nameof(this.InverseBackLat), this.InverseBackLat); + info.AddValue(nameof(this.ToleranceMeters), this.ToleranceMeters); + } + + /// + public void Deserialize(IXunitSerializationInfo info) + { + this.Description = info.GetValue(nameof(this.Description)) ?? string.Empty; + this.SourceSrid = info.GetValue(nameof(this.SourceSrid)); + this.TargetSrid = info.GetValue(nameof(this.TargetSrid)); + this.InputLon = info.GetValue(nameof(this.InputLon)); + this.InputLat = info.GetValue(nameof(this.InputLat)); + this.ForwardX = info.GetValue(nameof(this.ForwardX)); + this.ForwardY = info.GetValue(nameof(this.ForwardY)); + this.InverseBackLon = info.GetValue(nameof(this.InverseBackLon)); + this.InverseBackLat = info.GetValue(nameof(this.InverseBackLat)); + this.ToleranceMeters = info.GetValue(nameof(this.ToleranceMeters)); + } + + /// + public override string ToString() => $"{this.Description} ({this.SourceSrid}->{this.TargetSrid})"; +} diff --git a/test/ProjNet.Tests/Support/RoundtripAccuracyFixture.cs b/test/ProjNet.Tests/Support/RoundtripAccuracyFixture.cs new file mode 100644 index 00000000..1ec5891e --- /dev/null +++ b/test/ProjNet.Tests/Support/RoundtripAccuracyFixture.cs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany +// Derived from PROJ (https://proj.org), MIT license. + +namespace ProjNet.Tests; + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +/// +/// Represents the roundtrip accuracy fixture payload. +/// +[SuppressMessage("Performance", "CA1515:Consider making public types internal", Justification = "Public visibility is required because xUnit theory methods consume this DTO as a public parameter type.")] +[SuppressMessage("Design", "CA1002:Do not expose generic lists", Justification = "System.Text.Json materializes this DTO via writable List.")] +[SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Writable setter is required for fixture deserialization.")] +public sealed class RoundtripAccuracyFixture +{ + /// + /// Gets or sets the fixture schema/version marker. + /// + public int FixtureVersion { get; set; } + + /// + /// Gets or sets the generator identifier used to produce the fixture. + /// + public string Generator { get; set; } = string.Empty; + + /// + /// Gets or sets the roundtrip accuracy cases included in the fixture payload. + /// + public List Cases { get; set; } = []; +} diff --git a/test/ProjNet.Tests/SRID.csv b/test/ProjNet.Tests/Support/SRID.csv similarity index 99% rename from test/ProjNet.Tests/SRID.csv rename to test/ProjNet.Tests/Support/SRID.csv index 9e63448f..d4998a0d 100644 --- a/test/ProjNet.Tests/SRID.csv +++ b/test/ProjNet.Tests/Support/SRID.csv @@ -1758,7 +1758,7 @@ 3782;PROJCS["NAD83(CSRS) / Alberta 3TM ref merid 120 W (deprecated)",GEOGCS["NAD83(CSRS)",DATUM["NAD83_Canadian_Spatial_Reference_System",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6140"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4617"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-120],PARAMETER["scale_factor",0.9999],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3782"]] 3783;PROJCS["Pitcairn 2006 / Pitcairn TM 2006",GEOGCS["Pitcairn 2006",DATUM["Pitcairn_2006",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6763"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4763"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",-25.06855261111111],PARAMETER["central_meridian",-130.1129671111111],PARAMETER["scale_factor",1],PARAMETER["false_easting",14200],PARAMETER["false_northing",15500],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3783"]] 3784;PROJCS["Pitcairn 1967 / UTM zone 9S",GEOGCS["Pitcairn 1967",DATUM["Pitcairn_1967",SPHEROID["International 1924",6378388,297,AUTHORITY["EPSG","7022"]],TOWGS84[185,165,42,0,0,0,0],AUTHORITY["EPSG","6729"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4729"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-129],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3784"]] -3785;PROJCS["Popular Visualisation CRS / Mercator (deprecated)",GEOGCS["Popular Visualisation CRS",DATUM["Popular_Visualisation_Datum",SPHEROID["Popular Visualisation Sphere",6378137,0,AUTHORITY["EPSG","7059"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6055"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4055"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],AUTHORITY["EPSG","3785"]] +3785;PROJCS["Popular Visualisation CRS / Mercator (deprecated)",GEOGCS["Popular Visualisation CRS",DATUM["Popular_Visualisation_Datum",SPHEROID["Popular Visualisation Sphere",6378137,0,AUTHORITY["EPSG","7059"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6055"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4055"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3785"]] 3786;PROJCS["World Equidistant Cylindrical (Sphere) (deprecated)",GEOGCS["Unspecified datum based upon the GRS 1980 Authalic Sphere",DATUM["Not_specified_based_on_GRS_1980_Authalic_Sphere",SPHEROID["GRS 1980 Authalic Sphere",6371007,0,AUTHORITY["EPSG","7048"]],AUTHORITY["EPSG","6047"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4047"]],PROJECTION["Equirectangular"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],AUTHORITY["EPSG","3786"]] 3787;PROJCS["MGI / Slovene National Grid (deprecated)",GEOGCS["MGI",DATUM["Militar_Geographische_Institute",SPHEROID["Bessel 1841",6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],TOWGS84[577.326,90.129,463.919,5.137,1.474,5.297,2.4232],AUTHORITY["EPSG","6312"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4312"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",15],PARAMETER["scale_factor",0.9999],PARAMETER["false_easting",500000],PARAMETER["false_northing",-5000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Y",EAST],AXIS["X",NORTH],AUTHORITY["EPSG","3787"]] 3788;PROJCS["NZGD2000 / Auckland Islands TM 2000",GEOGCS["NZGD2000",DATUM["New_Zealand_Geodetic_Datum_2000",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6167"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4167"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",166],PARAMETER["scale_factor",1],PARAMETER["false_easting",3500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","3788"]] @@ -1810,7 +1810,7 @@ 3851;PROJCS["NZGD2000 / NZCS2000",GEOGCS["NZGD2000",DATUM["New_Zealand_Geodetic_Datum_2000",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6167"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4167"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",-37.5],PARAMETER["standard_parallel_2",-44.5],PARAMETER["latitude_of_origin",-41],PARAMETER["central_meridian",173],PARAMETER["false_easting",3000000],PARAMETER["false_northing",7000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","3851"]] 3852;PROJCS["RSRGD2000 / DGLC2000",GEOGCS["RSRGD2000",DATUM["Ross_Sea_Region_Geodetic_Datum_2000",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6764"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4764"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",-76.66666666666667],PARAMETER["standard_parallel_2",-79.33333333333333],PARAMETER["latitude_of_origin",-90],PARAMETER["central_meridian",157],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","3852"]] 3854;PROJCS["County ST74",GEOGCS["SWEREF99",DATUM["SWEREF99",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6619"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4619"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",18.05787],PARAMETER["scale_factor",0.99999506],PARAMETER["false_easting",100182.7406],PARAMETER["false_northing",-6500620.1207],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","3854"]] -3857;PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["semi_minor",6378137],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],AUTHORITY["EPSG","3857"]] +3857;PROJCS["WGS 84 / Pseudo-Mercator", GEOGCS["WGS 84", DATUM["World Geodetic System 1984", SPHEROID["WGS 84", 6378137, 298.257223563, AUTHORITY["EPSG", "7030"]], AUTHORITY["EPSG", "6326"]], PRIMEM["Greenwich", 0, AUTHORITY["EPSG", "8901"]], UNIT["degree", 0.017453292519943295, AUTHORITY["EPSG", "9102"]], AUTHORITY["EPSG", "4326"]], PROJECTION["Popular Visualisation Pseudo-Mercator", AUTHORITY["EPSG", "3856"]], PARAMETER["latitude_of_origin", 0], PARAMETER["central_meridian", 0], PARAMETER["false_easting", 0], PARAMETER["false_northing", 0], UNIT["metre", 1, AUTHORITY["EPSG", "9001"]], AXIS["East", EAST], AXIS["North", NORTH], AUTHORITY["EPSG", "3857"]] 3873;PROJCS["ETRS89 / GK19FIN",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",19],PARAMETER["scale_factor",1],PARAMETER["false_easting",19500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","3873"]] 3874;PROJCS["ETRS89 / GK20FIN",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",20],PARAMETER["scale_factor",1],PARAMETER["false_easting",20500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","3874"]] 3875;PROJCS["ETRS89 / GK21FIN",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",21],PARAMETER["scale_factor",1],PARAMETER["false_easting",21500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","3875"]] @@ -8011,4 +8011,4 @@ 104990;GEOGCS["GCS_HD1909 (deprecated)",DATUM["D_Hungarian_Datum_1909",SPHEROID["Bessel 1841",6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],AUTHORITY["ESRI","106990"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["ESRI","104990"]] 104991;GEOGCS["GCS_IGRS (deprecated)",DATUM["D_Iraqi_Geospatial_Reference_System",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["ESRI","106991"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["ESRI","104991"]] 104992;GEOGCS["GCS_MGI_1901 (deprecated)",DATUM["D_MGI_1901",SPHEROID["Bessel 1841",6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],AUTHORITY["ESRI","106992"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["ESRI","104992"]] -900913;PROJCS["Google Maps Global Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_2SP"],PARAMETER["standard_parallel_1",0],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["Meter",1],AUTHORITY["EPSG","900913"]] +900913;PROJCS["Popular Visualisation CRS / Mercator (deprecated)",GEOGCS["Popular Visualisation CRS",DATUM["Popular_Visualisation_Datum",SPHEROID["Popular Visualisation Sphere",6378137,0,AUTHORITY["EPSG","7059"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6055"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4055"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],AUTHORITY["EPSG","3785"],AXIS["X",EAST],AXIS["Y",NORTH]] diff --git a/test/ProjNet.Tests/Support/SRIDReader.cs b/test/ProjNet.Tests/Support/SRIDReader.cs new file mode 100644 index 00000000..fb6485f2 --- /dev/null +++ b/test/ProjNet.Tests/Support/SRIDReader.cs @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2005-2009 Morten Nielsen +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Text; +using ProjNet.CoordinateSystems; + +/// +/// Reads EPSG coordinate system definitions from a semicolon-delimited SRID CSV file. +/// +internal sealed class SRIDReader +{ + private static readonly Lazy CoordinateSystemFactory = + new(() => new CoordinateSystemFactory()); + + /// + /// Gets a coordinate system from the SRID.csv file. + /// + /// EPSG ID. + /// (optional) path to CSV File with WKT definitions. + /// Coordinate system, or null if no entry with was not found. + public static CoordinateSystem? GetCSbyID(int id, string? file = null) + { + foreach (WktString wkt in GetSrids(file)) + { + if (wkt.WktId == id) + { + return CoordinateSystemFactory.Value.CreateFromWkt(wkt.Wkt); + } + } + + return null; + } + + /// + /// Enumerates all SRID's in the SRID.csv file. + /// + /// The filename value. + /// Enumerator. + public static IEnumerable GetSrids(string? filename = null) + { + if (!string.IsNullOrWhiteSpace(filename)) + { + using FileStream fileStream = File.OpenRead(filename); + foreach (WktString wkt in EnumerateSrids(fileStream)) + { + yield return wkt; + } + + yield break; + } + + Stream? resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ProjNET.Tests.SRID.csv"); + if (resourceStream is null) + { + yield break; + } + + using (resourceStream) + { + foreach (WktString wkt in EnumerateSrids(resourceStream)) + { + yield return wkt; + } + } + } + + private static IEnumerable EnumerateSrids(Stream stream) + { + using var sr = new StreamReader(stream, Encoding.UTF8); + while (!sr.EndOfStream) + { + string? line = sr.ReadLine(); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + int split = line.IndexOf(';', StringComparison.Ordinal); + if (split <= -1) + { + continue; + } + + var wkt = new WktString + { + WktId = int.Parse(line.AsSpan(0, split), CultureInfo.InvariantCulture), + Wkt = line[(split + 1)..], + }; + yield return wkt; + } + } + + /// + /// Holds an SRID entry read from the CSV file, consisting of a numeric identifier and its WKT definition. + /// + public struct WktString + { + /// + /// Well-known ID. + /// + public int WktId; + + /// + /// Well-known Text. + /// + public string Wkt; + } +} diff --git a/test/ProjNet.Tests/Support/TestTolerances.cs b/test/ProjNet.Tests/Support/TestTolerances.cs new file mode 100644 index 00000000..cdfb8aac --- /dev/null +++ b/test/ProjNet.Tests/Support/TestTolerances.cs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +/// +/// Provides shared numeric tolerances for non-fixture tests. +/// +internal static class TestTolerances +{ + /// + /// Gets the tolerance used for coordinate round-trip assertions. + /// + internal const double CoordinateRoundTrip = 1e-5d; + + /// + /// Gets the tolerance used for angular-unit round-trip assertions. + /// + internal const double AngularUnitRoundTrip = 1e-13d; + + /// + /// Gets the tolerance used when only floating-point noise should differ between results. + /// + internal const double StableResult = 1e-12d; +} diff --git a/test/ProjNet.Tests/Testing/GitHubIssueAttribute.cs b/test/ProjNet.Tests/Testing/GitHubIssueAttribute.cs new file mode 100644 index 00000000..c78305b0 --- /dev/null +++ b/test/ProjNet.Tests/Testing/GitHubIssueAttribute.cs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using Xunit.v3; + +/// +/// Marks a test with the upstream GitHub issue that it verifies. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] +internal sealed class GitHubIssueAttribute : Attribute, ITraitAttribute +{ + private readonly int issueNumber; + + /// + /// Initializes a new instance of the class. + /// + /// The upstream GitHub issue number. + public GitHubIssueAttribute(int issueNumber) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(issueNumber); + this.issueNumber = issueNumber; + } + + /// + /// Gets the upstream GitHub issue number. + /// + public int IssueNumber => this.issueNumber; + + /// + /// Gets the xUnit traits emitted by this attribute. + /// + /// The xUnit traits describing the GitHub issue linkage. + public IReadOnlyCollection> GetTraits() => + [ + new("Category", "GitHub Issue"), + new("GitHubIssue", $"#{this.issueNumber}"), + ]; +} diff --git a/test/ProjNet.Tests/Testing/SerializationUniformityTests.cs b/test/ProjNet.Tests/Testing/SerializationUniformityTests.cs new file mode 100644 index 00000000..41e6bf49 --- /dev/null +++ b/test/ProjNet.Tests/Testing/SerializationUniformityTests.cs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using ProjNet.CoordinateSystems.Projections; +using ProjNet.CoordinateSystems.Transformations; +using Xunit; + +/// +/// Guards the iteration-5 serialization unification invariants against source-level regressions. +/// +public class SerializationUniformityTests +{ + private const int WindowLinesBeforeMember = 3; + private const int WindowLinesAfterMember = 7; + + /// + /// Verifies that WKT/XML serialization members no longer build their output with . + /// + [Fact] + public void WktAndXmlSerializationMembers_DoNotUseStringBuilder() + { + List offenders = FindSerializationSourceWindows(window => window.Contains("StringBuilder", StringComparison.Ordinal)); + + Assert.True( + offenders.Count == 0, + "WKT/XML serialization members should delegate through WktNode/XElement rather than building strings manually:" + Environment.NewLine + string.Join(Environment.NewLine, offenders)); + } + + /// + /// Verifies that serialization members no longer reference . + /// + [Fact] + public void SerializationMembers_DoNotReferenceNotImplementedException() + { + List offenders = FindSerializationSourceWindows(window => window.Contains("NotImplementedException", StringComparison.Ordinal)); + + Assert.True( + offenders.Count == 0, + "Serialization members should use NotSupportedException when a format is unsupported:" + Environment.NewLine + string.Join(Environment.NewLine, offenders)); + } + + /// + /// Verifies that any math transform with non-default serialization remains an explicitly classified special case. + /// + [Fact] + public void ConcreteMathTransformSerializationShapesRemainExplicitlyClassified() + { + List uncovered = []; + + foreach (Type type in typeof(MathTransform).Assembly.GetTypes()) + { + if (!typeof(MathTransform).IsAssignableFrom(type) || type.IsAbstract) + { + continue; + } + + if (!UsesNonDefaultSerialization(type)) + { + continue; + } + + if (!IsExpectedSerializationShape(type)) + { + uncovered.Add(type.FullName ?? type.Name); + } + } + + Assert.True( + uncovered.Count == 0, + "New math transforms changed serialization behavior without an explicit test classification:" + Environment.NewLine + string.Join(Environment.NewLine, uncovered)); + } + + private static bool IsExpectedSerializationShape(Type type) + { + return typeof(MapProjection).IsAssignableFrom(type) + || type == typeof(AffineTransform) + || type == typeof(GeographicTransform) + || type == typeof(IdentityMathTransform); + } + + private static bool UsesNonDefaultSerialization(Type type) + { + return GetRequiredProperty(type, nameof(MathTransform.WKT)).GetMethod!.DeclaringType != typeof(MathTransform) + || GetRequiredProperty(type, nameof(MathTransform.XML)).GetMethod!.DeclaringType != typeof(MathTransform) + || GetRequiredMethod(type, nameof(MathTransform.ToWktNode), Type.EmptyTypes).DeclaringType != typeof(MathTransform) + || GetRequiredMethod(type, nameof(MathTransform.ToXml), Type.EmptyTypes).DeclaringType != typeof(MathTransform); + } + + private static List FindSerializationSourceWindows(Func matches) + { + string repositoryRoot = GetRepositoryRoot(); + string sourceRoot = Path.Combine(repositoryRoot, "src", "ProjNet"); + List offenders = []; + + foreach (string filePath in Directory.EnumerateFiles(sourceRoot, "*.cs", SearchOption.AllDirectories)) + { + string[] lines = File.ReadAllLines(filePath); + for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++) + { + if (!IsSerializationMemberDeclaration(lines[lineIndex])) + { + continue; + } + + string window = GetWindow(lines, lineIndex); + if (matches(window)) + { + offenders.Add(FormattableString.Invariant($"{Path.GetRelativePath(repositoryRoot, filePath)}:{lineIndex + 1}")); + } + } + } + + return offenders; + } + + private static string GetRepositoryRoot() + { + DirectoryInfo? directory = new(AppContext.BaseDirectory); + while (directory is not null) + { + string solutionPath = Path.Combine(directory.FullName, "ProjNet4GeoAPI.sln"); + if (File.Exists(solutionPath)) + { + return directory.FullName; + } + + directory = directory.Parent; + } + + throw new InvalidOperationException("Unable to locate repository root from test output directory."); + } + + private static string GetWindow(string[] lines, int lineIndex) + { + int start = Math.Max(0, lineIndex - WindowLinesBeforeMember); + int end = Math.Min(lines.Length - 1, lineIndex + WindowLinesAfterMember); + string[] windowLines = new string[(end - start) + 1]; + Array.Copy(lines, start, windowLines, 0, windowLines.Length); + return string.Join("\n", windowLines); + } + + private static bool IsSerializationMemberDeclaration(string line) + { + return line.Contains(" string WKT", StringComparison.Ordinal) + || line.Contains(" string XML", StringComparison.Ordinal) + || line.Contains(" WktNode ToWktNode(", StringComparison.Ordinal) + || line.Contains(" XElement ToXml(", StringComparison.Ordinal); + } + + private static MethodInfo GetRequiredMethod(Type type, string name, Type[] parameterTypes) + { + MethodInfo? method = type.GetMethod(name, BindingFlags.Public | BindingFlags.Instance, binder: null, types: parameterTypes, modifiers: null); + return method ?? throw new InvalidOperationException($"Method '{name}' was not found on '{type.FullName}'."); + } + + private static PropertyInfo GetRequiredProperty(Type type, string name) + { + PropertyInfo? property = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); + return property ?? throw new InvalidOperationException($"Property '{name}' was not found on '{type.FullName}'."); + } +} diff --git a/test/ProjNet.Tests/Tools/GenerateEpsgManagedDataScriptTests.cs b/test/ProjNet.Tests/Tools/GenerateEpsgManagedDataScriptTests.cs new file mode 100644 index 00000000..bb0b2ac8 --- /dev/null +++ b/test/ProjNet.Tests/Tools/GenerateEpsgManagedDataScriptTests.cs @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2026 Martin Karing / TKI mbH, Chemnitz, Germany + +namespace ProjNet.Tests.Tools; + +using System; +using System.Diagnostics; +using System.IO; +using Xunit; + +/// +/// Tests for the PowerShell EPSG catalog generation wrapper. +/// +public class GenerateEpsgManagedDataScriptTests +{ + /// + /// Verifies that the wrapper surfaces a non-zero Python exit code when the generator fails after argument validation. + /// + [Fact] + public void WrapperShouldSurfaceGeneratorExitCode() + { + string projectRoot = GetProjectRoot(); + string toolsRoot = Path.Combine(projectRoot, "tools"); + string scriptPath = Path.Combine(toolsRoot, "Generate-EpsgManagedData.ps1"); + string shellExecutable = GetPowerShellExecutable(); + string tempDirectory = Path.Combine(Path.GetTempPath(), "ProjNet.Tests", nameof(GenerateEpsgManagedDataScriptTests), Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(tempDirectory); + + try + { + string bogusWktZipPath = Path.Combine(tempDirectory, "bogus-wkt.zip"); + string bogusPgZipPath = Path.Combine(tempDirectory, "bogus-pg.zip"); + string outputPath = Path.Combine(tempDirectory, "generated.cs"); + File.WriteAllText(bogusWktZipPath, "not a zip archive"); + File.WriteAllText(bogusPgZipPath, "not a zip archive"); + + string zipArgument = Path.GetRelativePath(toolsRoot, bogusWktZipPath); + string pgZipArgument = Path.GetRelativePath(toolsRoot, bogusPgZipPath); + string outputArgument = Path.GetRelativePath(toolsRoot, outputPath); + + using var process = new Process(); + process.StartInfo = new ProcessStartInfo + { + FileName = shellExecutable, + Arguments = FormattableString.Invariant($"-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"{scriptPath}\" -ZipPath \"{zipArgument}\" -PgZipPath \"{pgZipArgument}\" -OutputPath \"{outputArgument}\""), + WorkingDirectory = toolsRoot, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + }; + + process.Start(); + string standardOutput = process.StandardOutput.ReadToEnd(); + string standardError = process.StandardError.ReadToEnd(); + process.WaitForExit(); + + string combinedOutput = standardOutput + Environment.NewLine + standardError; + Assert.NotEqual(0, process.ExitCode); + Assert.Contains("Generator failed with exit code", combinedOutput, StringComparison.Ordinal); + } + finally + { + if (Directory.Exists(tempDirectory)) + { + Directory.Delete(tempDirectory, recursive: true); + } + } + } + + private static string GetPowerShellExecutable() + { + if (OperatingSystem.IsWindows()) + { + return "powershell.exe"; + } + + const string shellExecutable = "pwsh"; + string? path = Environment.GetEnvironmentVariable("PATH"); + if (string.IsNullOrWhiteSpace(path)) + { + Assert.Skip("PowerShell is not available on PATH."); + } + + foreach (string entry in path.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries)) + { + string candidate = Path.Combine(entry, shellExecutable); + if (File.Exists(candidate)) + { + return shellExecutable; + } + } + + Assert.Skip("PowerShell is not available on PATH."); + return string.Empty; + } + + private static string GetProjectRoot() + { + DirectoryInfo? directory = new(AppContext.BaseDirectory); + while (directory is not null) + { + if (File.Exists(Path.Combine(directory.FullName, "ProjNet4GeoAPI.sln"))) + { + return directory.FullName; + } + + directory = directory.Parent; + } + + throw new InvalidOperationException("Unable to locate repository root from test output directory."); + } +} diff --git a/test/ProjNet.Tests/WKT/PostGisSpatialRefSysTableParserTest.cs b/test/ProjNet.Tests/WKT/PostGisSpatialRefSysTableParserTest.cs deleted file mode 100644 index e6611e6e..00000000 --- a/test/ProjNet.Tests/WKT/PostGisSpatialRefSysTableParserTest.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Data; -using System.IO; -using Newtonsoft.Json.Linq; -using Npgsql; -using NUnit.Framework; -using ProjNet.CoordinateSystems; - -namespace ProjNET.Tests.WKT -{ - [TestFixture] - public class SpatialRefSysTableParser - { - private static string _connectionString; - - private static readonly Lazy CoordinateSystemFactory = - new Lazy(() => new CoordinateSystemFactory()); - - [Test] - public void TestParsePostgisDefinitions() - { - if (string.IsNullOrWhiteSpace(ConnectionString)) - throw new IgnoreException("No Connection string provided or provided connection string invalid."); - - using (var cn = new NpgsqlConnection(ConnectionString)) - { - cn.Open(); - var cmd = cn.CreateCommand(); - cmd.CommandText = "SELECT \"srid\", \"srtext\" FROM \"public\".\"spatial_ref_sys\" ORDER BY \"srid\";"; - - int counted = 0; - int failed = 0; - int tested = 0; - using (var r = cmd.ExecuteReader(CommandBehavior.CloseConnection)) - { - if (r != null) - { - while (r.Read()) - { - counted++; - int srid = r.GetInt32(0); - string srtext = r.GetString(1); - if (string.IsNullOrWhiteSpace(srtext)) continue; - if (srtext.StartsWith("COMPD_CS")) continue; - - tested++; - if (!TestParse(srid, srtext)) failed++; - } - } - } - - Console.WriteLine("\n\nTotal number of Tests {0}, failed {1}", tested, failed); - Assert.IsTrue(failed == 0); - } - - } - - [Test]//, Ignore("Only run this if you want a new SRID.csv file")] - public void TestCreateSridCsv() - { - if (string.IsNullOrWhiteSpace(ConnectionString)) - throw new IgnoreException("No Connection string provided or provided connection string invalid."); - - if (File.Exists("SRID.csv")) File.Delete("SRID.csv"); - - using (var sw = new StreamWriter(File.OpenWrite("SRID.csv"))) - using (var cn = new NpgsqlConnection(ConnectionString)) - { - cn.Open(); - var cm = cn.CreateCommand(); - cm.CommandText = "SELECT \"srid\", \"srtext\" FROM \"public\".\"spatial_ref_sys\" ORDER BY srid;"; - using (var dr = cm.ExecuteReader(CommandBehavior.SequentialAccess)) - { - while (dr.Read()) - { - int srid = dr.GetInt32(0); - string srtext = dr.GetString(1); - int bracketIndex = srtext.IndexOf('['); - if (bracketIndex < 0) - { - continue; - } - - switch (srtext.Substring(0, bracketIndex)) - { - case "PROJCS": - case "GEOGCS": - case "GEOCCS": - sw.WriteLine($"{srid};{srtext}"); - break; - } - } - } - cm.Dispose(); - } - } - - private static string ConnectionString - { - get - { - if (!string.IsNullOrWhiteSpace(_connectionString)) - return _connectionString; - - if (!File.Exists("appsettings.json")) - return null; - - JToken token = null; - using (var jtr = new Newtonsoft.Json.JsonTextReader(new StreamReader("appsettings.json"))) - token = JToken.ReadFrom(jtr); - - string connectionString = (string)token["ConnectionString"]; - try - { - using (var cn = new NpgsqlConnection(connectionString)) - cn.Open(); - } - catch (Exception) - { - return null; - } - - _connectionString = connectionString; - return _connectionString; - - } - } - - private static bool TestParse(int srid, string srtext) - { - try - { - CoordinateSystemFactory.Value.CreateFromWkt(srtext); - //CoordinateSystemWktReader.Parse(srtext); - return true; - } - catch (Exception ex) - { - Console.WriteLine("Test {0} failed:\n {1}\n {2}", srid, srtext, ex.Message); - return false; - } - } - - - } -} diff --git a/test/ProjNet.Tests/WKT/WKTCoordSysParserTests.cs b/test/ProjNet.Tests/WKT/WKTCoordSysParserTests.cs deleted file mode 100644 index bb9a47a4..00000000 --- a/test/ProjNet.Tests/WKT/WKTCoordSysParserTests.cs +++ /dev/null @@ -1,540 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using NUnit.Framework; -using ProjNet.CoordinateSystems; -using ProjNet.CoordinateSystems.Transformations; - -namespace ProjNET.Tests.WKT -{ - [TestFixture] - public class WKTCoordSysParserTests - { - private readonly CoordinateSystemFactory _coordinateSystemFactory = new CoordinateSystemFactory(); - - /// - /// Parses a coordinate system WKT - /// - /// - /// PROJCS["NAD83(HARN) / Texas Central (ftUS)", - /// GEOGCS[ - /// "NAD83(HARN)", - /// DATUM[ - /// "NAD83_High_Accuracy_Regional_Network", - /// SPHEROID[ - /// "GRS 1980", - /// 6378137, - /// 298.257222101, - /// AUTHORITY["EPSG","7019"] - /// ], - /// TOWGS84[725,685,536,0,0,0,0], - /// AUTHORITY["EPSG","6152"] - /// ], - /// PRIMEM[ - /// "Greenwich", - /// 0, - /// AUTHORITY["EPSG","8901"] - /// ], - /// UNIT[ - /// "degree", - /// 0.01745329251994328, - /// AUTHORITY["EPSG","9122"] - /// ], - /// AUTHORITY["EPSG","4152"] - /// ], - /// PROJECTION["Lambert_Conformal_Conic_2SP"], - /// PARAMETER["standard_parallel_1",31.88333333333333], - /// PARAMETER["standard_parallel_2",30.11666666666667], - /// PARAMETER["latitude_of_origin",29.66666666666667], - /// PARAMETER["central_meridian",-100.3333333333333], - /// PARAMETER["false_easting",2296583.333], - /// PARAMETER["false_northing",9842500.000000002], - /// UNIT[ - /// "US survey foot", - /// 0.3048006096012192, - /// AUTHORITY["EPSG","9003"] - /// ], - /// AUTHORITY["EPSG","2918"] - /// ] - /// - [Test] - public void TestProjectedCoordinateSystem_EPSG_2918() - { - const string wkt = "PROJCS[\"NAD83(HARN) / Texas Central (ftUS)\", "+ - "GEOGCS[\"NAD83(HARN)\", " + - "DATUM[\"NAD83_High_Accuracy_Regional_Network\", "+ - "SPHEROID[\"GRS 1980\", 6378137, 298.257222101, AUTHORITY[\"EPSG\", \"7019\"]], "+ - "TOWGS84[725, 685, 536, 0, 0, 0, 0], " + - "AUTHORITY[\"EPSG\", \"6152\"]], "+ - "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]], "+ - "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9122\"]], "+ - "AUTHORITY[\"EPSG\", \"4152\"]], "+ - "PROJECTION[\"Lambert_Conformal_Conic_2SP\"], " + - "PARAMETER[\"standard_parallel_1\", 31.883333333333], " + - "PARAMETER[\"standard_parallel_2\", 30.1166666667], " + - "PARAMETER[\"latitude_of_origin\", 29.6666666667], " + - "PARAMETER[\"central_meridian\", -100.333333333333], " + - "PARAMETER[\"false_easting\", 2296583.333], " + - "PARAMETER[\"false_northing\", 9842500], " + - "UNIT[\"US survey foot\", 0.304800609601219, AUTHORITY[\"EPSG\", \"9003\"]], "+ - "AUTHORITY[\"EPSG\", \"2918\"]]"; - - ProjectedCoordinateSystem pcs = null; - Assert.That(() => pcs = _coordinateSystemFactory.CreateFromWkt(wkt) as ProjectedCoordinateSystem, Throws.Nothing); - - ProjectedCoordinateSystem pcs2 = null; - Assert.That(() => pcs2 = _coordinateSystemFactory.CreateFromWkt(wkt.Replace("[", "(").Replace("]", ")")) as ProjectedCoordinateSystem, Throws.Nothing); - Assert.That(pcs.EqualParams(pcs2), Is.True); - - Assert.That(pcs, Is.Not.Null, "Could not parse WKT: " + wkt); - CheckInfo(pcs, "NAD83(HARN) / Texas Central (ftUS)", "EPSG", 2918); - - var gcs = pcs.GeographicCoordinateSystem; - CheckInfo(gcs, "NAD83(HARN)", "EPSG", 4152); - CheckDatum(gcs.HorizontalDatum, "NAD83_High_Accuracy_Regional_Network", "EPSG", 6152); - CheckEllipsoid(gcs.HorizontalDatum.Ellipsoid, "GRS 1980", 6378137, 298.257222101, "EPSG", 7019); - Assert.AreEqual(new Wgs84ConversionInfo(725, 685, 536, 0, 0, 0, 0), pcs.GeographicCoordinateSystem.HorizontalDatum.Wgs84Parameters); - CheckPrimem(gcs.PrimeMeridian, "Greenwich", 0, "EPSG", 8901); - CheckUnit(gcs.AngularUnit, "degree", 0.0174532925199433, "EPSG", 9122); - - CheckProjection(pcs.Projection, "Lambert_Conformal_Conic_2SP", new[] - { - Tuple.Create("standard_parallel_1", 31.883333333333), - Tuple.Create("standard_parallel_2", 30.1166666667), - Tuple.Create("latitude_of_origin", 29.6666666667), - Tuple.Create("central_meridian", -100.333333333333), - Tuple.Create("false_easting", 2296583.333), - Tuple.Create("false_northing", 9842500d) - }); - - CheckUnit(pcs.LinearUnit, "US survey foot", 0.304800609601219, "EPSG", 9003); - } - - /// - /// This test reads in a file with 2671 pre-defined coordinate systems and projections, - /// and tries to parse them. - /// - [Test] - public void ParseAllWKTs() - { - int parseCount = 0; - foreach (var wkt in SRIDReader.GetSrids()) - { - var cs1 = _coordinateSystemFactory.CreateFromWkt(wkt.Wkt); - Assert.IsNotNull(cs1, "Could not parse WKT: " + wkt); - var cs2 = _coordinateSystemFactory.CreateFromWkt(wkt.Wkt.Replace("[", "(").Replace("]", ")")); - Assert.That(cs1.EqualParams(cs2), Is.True); - parseCount++; - } - Assert.That(parseCount, Is.GreaterThan(2671), "Not all WKT was parsed"); - } - - /// - /// This test reads in a file with 2671 pre-defined coordinate systems and projections, - /// and tries to create a transformation with them. - /// - [Test] - public void TestCreateCoordinateTransformationForWktInCsv() - { - //GeographicCoordinateSystem.WGS84 - var fac = new CoordinateSystemFactory(); - int parseCount = 0; - int failedCss = 0; - var failedProjections = new HashSet(); - using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ProjNET.Tests.SRID.csv")) - { - using (var sr = new StreamReader(stream, Encoding.UTF8)) - { - var ctFactory = new CoordinateTransformationFactory(); - while (!sr.EndOfStream) - { - string line = sr.ReadLine(); - if (string.IsNullOrWhiteSpace(line)) continue; - - int split = line.IndexOf(';'); - if (split > -1) - { - string wkt = line.Substring(split + 1); - var cs = fac.CreateFromWkt(wkt); - if (cs == null) continue; //We check this in another test. - if (cs is ProjectedCoordinateSystem pcs) - { - switch (pcs.Projection.ClassName) - { - //Skip not supported projections - case "Oblique_Stereographic": - case "Transverse_Mercator_South_Orientated": - case "Lambert_Conformal_Conic_1SP": - case "Lambert_Azimuthal_Equal_Area": - case "Tunisia_Mining_Grid": - case "New_Zealand_Map_Grid": - case "Polyconic": - case "Lambert_Conformal_Conic_2SP_Belgium": - case "Polar_Stereographic": - case "Hotine_Oblique_Mercator_Azimuth_Center": - case "Mercator_1SP": - case "Mercator_2SP": - case "Cylindrical_Equal_Area": - case "Equirectangular": - case "Laborde_Oblique_Mercator": - continue; - } - } - - try - { - ctFactory.CreateFromCoordinateSystems(GeographicCoordinateSystem.WGS84, cs); - } - catch (Exception) - { - if (cs is ProjectedCoordinateSystem ics) - { - if (!failedProjections.Contains(ics.Projection.ClassName)) - failedProjections.Add(ics.Projection.ClassName); - } - else - { - Assert.That(false); - } - - failedCss += 1; - // Assert.Fail( - // $"Could not create transformation from:\r\n{wkt}\r\n{ex.Message}\r\nClass name:{ics.Projection.ClassName}"); - //else - // Assert.Fail($"Could not create transformation from:\r\n{wkt}\r\n{ex.Message}"); - } - - parseCount++; - } - } - } - } - - Assert.GreaterOrEqual(parseCount, 2556, "Not all WKT was processed"); - if (failedCss > 0) - { - Console.WriteLine($"Failed to create transfroms for {failedCss} coordinate systems"); - foreach (string fp in failedProjections) - { - Console.WriteLine($"case \"{fp}\":"); - - } - } - - } - - /// - /// Test parsing of a from WKT - /// - [Test] - public void TestProjectedCoordinateSystem_EPSG27700_UnitBeforeProjection() - { - const string wkt = "PROJCS[\"OSGB 1936 / British National Grid\"," + - "GEOGCS[\"OSGB 1936\"," + - "DATUM[\"OSGB_1936\"," + - "SPHEROID[\"Airy 1830\",6377563.396,299.3249646,AUTHORITY[\"EPSG\",\"7001\"]]," + - "AUTHORITY[\"EPSG\",\"6277\"]]," + - "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + - "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]," + - "AUTHORITY[\"EPSG\",\"4277\"]]," + - "PROJECTION[\"Transverse_Mercator\"]," + - "PARAMETER[\"latitude_of_origin\",49]," + - "PARAMETER[\"central_meridian\",-2]," + - "PARAMETER[\"scale_factor\",0.9996012717]," + - "PARAMETER[\"false_easting\",400000]," + - "PARAMETER[\"false_northing\",-100000]," + - "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]]," + - "AXIS[\"Easting\",EAST]," + - "AXIS[\"Northing\",NORTH]," + - "AUTHORITY[\"EPSG\",\"27700\"]]"; - - ProjectedCoordinateSystem pcs = null; - Assert.That(() => pcs = _coordinateSystemFactory.CreateFromWkt(wkt) as ProjectedCoordinateSystem, Throws.Nothing); - - ProjectedCoordinateSystem pcs2 = null; - Assert.That(() => pcs2 = _coordinateSystemFactory.CreateFromWkt(wkt.Replace("[", "(").Replace("]", ")")) as ProjectedCoordinateSystem, Throws.Nothing); - Assert.That(pcs.EqualParams(pcs2), Is.True); - - - CheckInfo(pcs, "OSGB 1936 / British National Grid", "EPSG", 27700); - - var gcs = pcs.GeographicCoordinateSystem; - CheckInfo(gcs, "OSGB 1936", "EPSG", 4277); - CheckDatum(gcs.HorizontalDatum, "OSGB_1936", "EPSG", 6277); - CheckEllipsoid(gcs.HorizontalDatum.Ellipsoid, "Airy 1830", 6377563.396, 299.3249646, "EPSG", 7001); - CheckPrimem(gcs.PrimeMeridian, "Greenwich", 0, "EPSG", 8901); - CheckUnit(gcs.AngularUnit, "degree", 0.0174532925199433, "EPSG", 9122); - - Assert.AreEqual("Transverse_Mercator", pcs.Projection.ClassName, "Projection Classname"); - CheckProjection(pcs.Projection, "Transverse_Mercator", new [] - { - Tuple.Create("latitude_of_origin", 49d), - Tuple.Create("central_meridian",-2d), - Tuple.Create("scale_factor",0.9996012717), - Tuple.Create("false_easting",400000d), - Tuple.Create("false_northing",-100000d) - }); - - CheckUnit(pcs.LinearUnit, "metre", 1d, "EPSG", 9001); - - string newWkt = pcs.WKT.Replace(", ", ","); - Assert.AreEqual(wkt, newWkt); - - } - - [Test] - public void TestParseSrOrg() - { - Assert.That(() => _coordinateSystemFactory.CreateFromWkt( - "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"Popular Visualisation CRS\"," + - "DATUM[\"Popular_Visualisation_Datum\",SPHEROID[\"Popular Visualisation Sphere\"," + - "6378137,0,AUTHORITY[\"EPSG\",\"7059\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\"," + - "\"6055\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\"," + - "0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4055\"]]," + - "PROJECTION[\"Mercator_1SP\"]," + - "PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[" + - "\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH]],AUTHORITY[\"EPSG\",\"3785\"]" - ), Throws.Nothing); - } - - [Test] - public void TestProjNetIssues() - { - Assert.That(() => _coordinateSystemFactory.CreateFromWkt( - "PROJCS[\"International_Terrestrial_Reference_Frame_1992Lambert_Conformal_Conic_2SP\"," + - "GEOGCS[\"GCS_International_Terrestrial_Reference_Frame_1992\"," + - "DATUM[\"International_Terrestrial_Reference_Frame_1992\"," + - "SPHEROID[\"GRS_1980\",6378137,298.257222101]," + - "TOWGS84[0,0,0,0,0,0,0]]," + - "PRIMEM[\"Greenwich\",0]," + - "UNIT[\"Degree\",0.0174532925199433]]," + - "PROJECTION[\"Lambert_Conformal_Conic_2SP\",AUTHORITY[\"EPSG\",\"9802\"]]," + - "PARAMETER[\"Central_Meridian\",-102]," + - "PARAMETER[\"Latitude_Of_Origin\",12]," + - "PARAMETER[\"False_Easting\",2500000]," + - "PARAMETER[\"False_Northing\",0]," + - "PARAMETER[\"Standard_Parallel_1\",17.5]," + - "PARAMETER[\"Standard_Parallel_2\",29.5]," + - "PARAMETER[\"Scale_Factor\",1]," + - "UNIT[\"Meter\",1,AUTHORITY[\"EPSG\",\"9001\"]]]"), Throws.Nothing); - - Assert.That(() => _coordinateSystemFactory.CreateFromWkt( - "PROJCS[\"Google Maps Global Mercator\"," + - "GEOGCS[\"WGS 84\"," + - "DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]]," + - "AUTHORITY[\"EPSG\",\"6326\"]]," + - "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]," + - "UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]]," + - "AUTHORITY[\"EPSG\",\"4326\"]]," + - "PROJECTION[\"Mercator_2SP\"]," + - "PARAMETER[\"standard_parallel_1\",0]," + - "PARAMETER[\"latitude_of_origin\",0]," + - "PARAMETER[\"central_meridian\",0]," + - "PARAMETER[\"false_easting\",0]," + - "PARAMETER[\"false_northing\",0]," + - "UNIT[\"Meter\",1]," + - "EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs\"]," + - "AUTHORITY[\"EPSG\",\"900913\"]]"), Throws.Nothing); - } - - /// - /// Test parsing of a from WKT - /// - [Test] - public void TestFittedCoordinateSystemWkt () - { - var fac = new CoordinateSystemFactory (); - FittedCoordinateSystem fcs = null; - string wkt = "FITTED_CS[\"Local coordinate system MNAU (based on Gauss-Krueger)\"," + - "PARAM_MT[\"Affine\"," + - "PARAMETER[\"num_row\",3],PARAMETER[\"num_col\",3],PARAMETER[\"elt_0_0\", 0.883485346527455],PARAMETER[\"elt_0_1\", -0.468458794848877],PARAMETER[\"elt_0_2\", 3455869.17937689],PARAMETER[\"elt_1_0\", 0.468458794848877],PARAMETER[\"elt_1_1\", 0.883485346527455],PARAMETER[\"elt_1_2\", 5478710.88035753],PARAMETER[\"elt_2_2\", 1]]," + - "PROJCS[\"DHDN / Gauss-Kruger zone 3\"," + - "GEOGCS[\"DHDN\"," + - "DATUM[\"Deutsches_Hauptdreiecksnetz\"," + - "SPHEROID[\"Bessel 1841\", 6377397.155, 299.1528128, AUTHORITY[\"EPSG\", \"7004\"]]," + - "TOWGS84[612.4, 77, 440.2, -0.054, 0.057, -2.797, 0.525975255930096]," + - "AUTHORITY[\"EPSG\", \"6314\"]]," + - "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]]," + - "UNIT[\"degree\", 0.0174532925199433, AUTHORITY[\"EPSG\", \"9122\"]]," + - "AUTHORITY[\"EPSG\", \"4314\"]]," + - "PROJECTION[\"Transverse_Mercator\"]," + - "PARAMETER[\"latitude_of_origin\", 0]," + - "PARAMETER[\"central_meridian\", 9]," + - "PARAMETER[\"scale_factor\", 1]," + - "PARAMETER[\"false_easting\", 3500000]," + - "PARAMETER[\"false_northing\", 0]," + - "UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]]," + - "AUTHORITY[\"EPSG\", \"31467\"]]" + - "]"; - - try - { - fcs = fac.CreateFromWkt (wkt) as FittedCoordinateSystem; - } - catch (Exception ex) - { - Assert.Fail ("Could not create fitted coordinate system from:\r\n" + wkt + "\r\n" + ex.Message); - } - - Assert.That(fcs, Is.Not.Null); - Assert.That(fcs.ToBase(), Is.Not.Null.Or.Empty); - Assert.That(fcs.BaseCoordinateSystem, Is.Not.Null); - - Assert.AreEqual ("Local coordinate system MNAU (based on Gauss-Krueger)", fcs.Name); - //Assert.AreEqual ("CUSTOM", fcs.Authority); - //Assert.AreEqual (123456, fcs.AuthorityCode); - - Assert.AreEqual ("EPSG", fcs.BaseCoordinateSystem.Authority); - Assert.AreEqual (31467, fcs.BaseCoordinateSystem.AuthorityCode); - } - - /// - /// Test parsing of a from WKT - /// - [Test] - public void TestGeocentricCoordinateSystem() - { - var fac = new CoordinateSystemFactory(); - GeocentricCoordinateSystem fcs = null; - - const string wkt = "GEOCCS[\"TUREF\", " + - "DATUM[\"Turkish_National_Reference_Frame\", " + - "SPHEROID[\"GRS 1980\", 6378137, 298.257222101, AUTHORITY[\"EPSG\", \"7019\"]], " + - "AUTHORITY[\"EPSG\", \"1057\"]], " + - "PRIMEM[\"Greenwich\", 0, AUTHORITY[\"EPSG\", \"8901\"]], " + - "UNIT[\"metre\", 1, AUTHORITY[\"EPSG\", \"9001\"]], " + - "AXIS[\"Geocentric X\", OTHER], AXIS[\"Geocentric Y\", OTHER], AXIS[\"Geocentric Z\", NORTH], " + - "AUTHORITY[\"EPSG\", \"5250\"]]"; - - try - { - fcs = fac.CreateFromWkt(wkt) as GeocentricCoordinateSystem; - } - catch (Exception ex) - { - Assert.Fail("Could not create geocentric coordinate system from:\r\n" + wkt + "\r\n" + ex.Message); - } - - Assert.That(fcs, Is.Not.Null); - Assert.That(CheckInfo(fcs, "TUREF", "EPSG", 5250L)); - Assert.That(CheckDatum(fcs.HorizontalDatum, "Turkish_National_Reference_Frame", "EPSG", 1057L), Is.True); - Assert.That(CheckEllipsoid(fcs.HorizontalDatum.Ellipsoid, "GRS 1980", 6378137, 298.257222101, "EPSG", 7019), Is.True); - Assert.That(CheckPrimem(fcs.PrimeMeridian, "Greenwich", 0, "EPSG", 8901L), Is.True); - Assert.That(CheckUnit(fcs.PrimeMeridian.AngularUnit, "degree", null, null, null), Is.True); - Assert.That(CheckUnit(fcs.LinearUnit, "metre", 1, "EPSG", 9001L), Is.True); - - Assert.That(fcs.Authority, Is.EqualTo("EPSG")); - Assert.That(fcs.AuthorityCode, Is.EqualTo(5250L)); - } - - [Test] - public void ParseWktCreatedByCoordinateSystem() - { - // Sample WKT from an external source. - string sampleWKT = - "PROJCS[\"\", " + - "GEOGCS[\"\", " + - "DATUM[\"\", " + - "SPHEROID[\"GRS_1980\", 6378137, 298.2572221010042] " + - "], " + - "PRIMEM[\"Greenwich\", 0], " + - "UNIT[\"Degree\", 0.017453292519943295]" + - "], " + - "PROJECTION[\"Transverse_Mercator\"], " + - "PARAMETER[\"False_Easting\", 500000], " + - "PARAMETER[\"False_Northing\", 0], " + - "PARAMETER[\"Central_Meridian\", -75], " + - "PARAMETER[\"Scale_Factor\", 0.9996], " + - "UNIT[\"Meter\", 1]" + - "]"; - - var csFromSample = (CoordinateSystem)ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(sampleWKT); - string wktFromProjNetCS = csFromSample.WKT; - var parsed = ProjNet.IO.CoordinateSystems.CoordinateSystemWktReader.Parse(wktFromProjNetCS); - Assert.That(parsed, Is.InstanceOf()); - var projCS = (ProjectedCoordinateSystem)parsed; - Assert.That(projCS.LinearUnit, Is.Not.Null); - Assert.That(projCS.LinearUnit.Name, Is.EqualTo("Meter")); - Assert.That(projCS.LinearUnit.MetersPerUnit, Is.EqualTo(1)); - } - - - #region Utility - - private bool CheckPrimem(PrimeMeridian primeMeridian, string name, double? longitude, string authority, long? code) - { - Assert.That(primeMeridian, Is.Not.Null); - Assert.That(CheckInfo(primeMeridian, name, authority, code)); - Assert.That(primeMeridian.Longitude, Is.EqualTo(longitude)); - return true; - } - - private static bool CheckUnit(IUnit unit, string name, double? value, string authority, long? code) - { - Assert.That(unit, Is.Not.Null); - Assert.That(CheckInfo(unit, name, authority, code)); - Assert.That(unit, Is.InstanceOf().Or.InstanceOf()); - - if (!value.HasValue) return true; - if (unit is LinearUnit lunit) - Assert.That(lunit.MetersPerUnit, Is.EqualTo(value)); - else if (unit is AngularUnit aunit) - Assert.That(aunit.RadiansPerUnit, Is.EqualTo(value)); - return true; - } - - private static bool CheckEllipsoid(Ellipsoid ellipsoid, string name, double? semiMajor, double? inverseFlattening, string authority, long? code) - { - Assert.That(ellipsoid, Is.Not.Null); - Assert.That(CheckInfo(ellipsoid, name, authority, code)); - if (semiMajor.HasValue) Assert.That(ellipsoid.SemiMajorAxis, Is.EqualTo(semiMajor)); - if (inverseFlattening.HasValue) Assert.That(ellipsoid.InverseFlattening, Is.EqualTo(inverseFlattening)); - - return true; - } - - private static bool CheckDatum(Datum datum, string name, string authority, long? code) - { - Assert.That(datum, Is.Not.Null); - Assert.That(datum, Is.InstanceOf()/*.Or.InstanceOf()*/); - - Assert.That(CheckInfo(datum, name,authority, code), Is.True); - - return true; - } - - private static bool CheckInfo(IInfo info, string name, string authority = null, long? code = null) - { - Assert.That(info, Is.Not.Null); - if (!string.IsNullOrWhiteSpace(name)) Assert.That(info.Name, Is.EqualTo(name)); - if (!string.IsNullOrWhiteSpace(authority)) Assert.That(info.Authority, Is.EqualTo(authority)); - if (code.HasValue) Assert.That(info.AuthorityCode, Is.EqualTo(code)); - - return true; - } - - private static void CheckProjection(IProjection projection, string name, IList> pp = null, string authority = null, long? code = null) - { - Assert.That(projection, Is.Not.Null, "Projection not null"); - Assert.That(projection.ClassName, Is.EqualTo(name), "Projection class name"); - CheckInfo(projection, name, authority, code); - - if (pp == null) return; - - Assert.That(projection.NumParameters, Is.EqualTo(pp.Count), "Number of projection parameters"); - - for (int i = 0; i < pp.Count; i++) - { - ProjectionParameter par = null; - Assert.That(() => par = projection.GetParameter(pp[i].Item1), Throws.Nothing, $"Getting projection parameter '{pp[i].Item1}' throws."); - Assert.That(par, Is.Not.Null, $"Projection parameter '{pp[i].Item1}' is null"); - Assert.That(par.Name, Is.EqualTo(pp[i].Item1), $"Projection parameter '{par.Name}' name is not '{pp[i].Item1}'."); - Assert.That(par.Value, Is.EqualTo(pp[i].Item2), $"Projection parameter value for '{par.Name}' name ({par.Value:R}) is not '{pp[i].Item2:R}'."); - } - } - - #endregion - } -} diff --git a/test/ProjNet.Tests/WKT/WKTMathTransformParserTests.cs b/test/ProjNet.Tests/WKT/WKTMathTransformParserTests.cs deleted file mode 100644 index b500e9eb..00000000 --- a/test/ProjNet.Tests/WKT/WKTMathTransformParserTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using NUnit.Framework; -using ProjNet.CoordinateSystems.Transformations; -using ProjNet.IO.CoordinateSystems; - -namespace ProjNET.Tests.WKT -{ - [TestFixture] - public class WKTMathTransformParserTests - { - /// - /// Test parsing of affine math transform from WKT - /// - [Test] - public void ParseAffineTransformWkt () - { - //TODO MathTransformFactory fac = new MathTransformFactory (); - MathTransform mt = null; - string wkt = "PARAM_MT[\"Affine\"," + - "PARAMETER[\"num_row\",3]," + - "PARAMETER[\"num_col\",3]," + - "PARAMETER[\"elt_0_0\", 0.883485346527455]," + - "PARAMETER[\"elt_0_1\", -0.468458794848877]," + - "PARAMETER[\"elt_0_2\", 3455869.17937689]," + - "PARAMETER[\"elt_1_0\", 0.468458794848877]," + - "PARAMETER[\"elt_1_1\", 0.883485346527455]," + - "PARAMETER[\"elt_1_2\", 5478710.88035753]," + - "PARAMETER[\"elt_2_2\", 1]]"; - - try - { - //TODO replace with MathTransformFactory implementation - mt = MathTransformWktReader.Parse (wkt); - } - catch (Exception ex) - { - Assert.Fail ("Could not create affine math transformation from:\r\n" + wkt + "\r\n" + ex.Message); - } - - Assert.IsNotNull (mt); - Assert.IsNotNull (mt as AffineTransform); - - Assert.AreEqual (2, mt.DimSource); - Assert.AreEqual (2, mt.DimTarget); - - //test simple transform - double[] outPt = mt.Transform (new double[] { 0.0, 0.0 }); - - Assert.AreEqual (2, outPt.Length); - Assert.AreEqual (3455869.17937689, outPt[0], 0.00000001); - Assert.AreEqual (5478710.88035753, outPt[1], 0.00000001); - } - - /// - /// MathTransformWktReader parses real number with exponent incorrectly - /// - [TestCase("PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 6.12303176911189E-17]]")] - [TestCase("PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 5.235E4]]")] - [TestCase ("PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 5.235E+4]]")] - public void TestMathTransformWktReaderExponencialNumberParsingIssue(string wkt) - { - //string wkt = "PARAM_MT[\"Affine\",PARAMETER[\"num_row\", 3],PARAMETER[\"num_col\", 3],PARAMETER[\"elt_0_0\", 6.12303176911189E-17]]"; - MathTransform mt = null; - - try - { - //TODO replace with MathTransformFactory implementation - mt = MathTransformWktReader.Parse (wkt); - } - catch (ArgumentException ex) - { - Assert.Fail ("Failed to parse WKT of affine math transformation from:\r\n" + wkt + "\r\n" + ex.Message); - } - catch (Exception e) - { - Assert.Fail ("Could not create affine math transformation from:\r\n" + wkt + "\r\n" + e.Message); - } - - Assert.IsNotNull (mt); - Assert.IsNotNull (mt as AffineTransform); - } - } -} diff --git a/tools/Generate-EpsgManagedData.ps1 b/tools/Generate-EpsgManagedData.ps1 new file mode 100644 index 00000000..3cbf69a1 --- /dev/null +++ b/tools/Generate-EpsgManagedData.ps1 @@ -0,0 +1,39 @@ +param( + # Path to the EPSG WKT ZIP archive (for example EPSG-v12_054-WKT.Zip). + [Parameter(Mandatory = $true)] + [string]$ZipPath, + # Path to the EPSG PostgreSQL ZIP archive (for example EPSG-v12_054-PostgreSQL.zip). + [Parameter(Mandatory = $true)] + [string]$PgZipPath, + [string]$OutputPath = "..\src\ProjNet\Data\Generated\EpsgGeneratedCatalog.g.cs" +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +function Resolve-NormalizedPath { + param([string]$PathValue) + return [System.IO.Path]::GetFullPath((Join-Path -Path $PSScriptRoot -ChildPath $PathValue)) +} + +$zipFilePath = Resolve-NormalizedPath -PathValue $ZipPath +$pgZipFilePath = Resolve-NormalizedPath -PathValue $PgZipPath +$outputFilePath = Resolve-NormalizedPath -PathValue $OutputPath + +if (-not (Test-Path $zipFilePath)) { + throw "EPSG archive not found: $zipFilePath" +} + +if (-not (Test-Path $pgZipFilePath)) { + throw "EPSG PostgreSQL archive not found: $pgZipFilePath" +} + +$generatorScript = Join-Path -Path $PSScriptRoot -ChildPath "generate_epsg_catalog.py" +if (-not (Test-Path $generatorScript)) { + throw "Generator script not found: $generatorScript" +} + +python $generatorScript --zip $zipFilePath --pg-zip $pgZipFilePath --output $outputFilePath +if ($LASTEXITCODE -ne 0) { + throw "Generator failed with exit code $LASTEXITCODE" +} diff --git a/tools/Generate-ProjReferenceFixtures.ps1 b/tools/Generate-ProjReferenceFixtures.ps1 new file mode 100644 index 00000000..05014dc6 --- /dev/null +++ b/tools/Generate-ProjReferenceFixtures.ps1 @@ -0,0 +1,271 @@ +param( + [string]$GeneratedCatalogPath = "..\src\ProjNet\Data\Generated\EpsgGeneratedCatalog.g.cs", + # Path to the directory containing the PROJ command-line binaries (projinfo.exe and cs2cs.exe). + [Parameter(Mandatory = $true)] + [string]$ProjBinPath, + [string]$OutputPath = "..\test\ProjNet.Tests\Generated\proj2proj-direct-parity-fixture.json", + [string]$ProjNetProjectPath = "..\src\ProjNet\ProjNET.csproj", + [int]$MaxCases = 24 +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +function Resolve-NormalizedPath { + param([string]$PathValue) + return [System.IO.Path]::GetFullPath((Join-Path -Path $PSScriptRoot -ChildPath $PathValue)) +} + +function Parse-Accuracy { + param([string]$AccuracyLiteral) + $normalized = $AccuracyLiteral.TrimEnd('d', 'D') + return [double]::Parse($normalized, [System.Globalization.CultureInfo]::InvariantCulture) +} + +function Get-NormalizedAccuracy { + param([double]$Accuracy) + return $Accuracy -gt 0d ? $Accuracy : [double]::MaxValue +} + +function Get-ProjectedWktFromProjInfo { + param( + [string]$ProjInfoPath, + [int]$Srid + ) + + $output = & $ProjInfoPath "EPSG:$Srid" "-o" "WKT1:GDAL" "--single-line" 2>$null + if ($LASTEXITCODE -ne 0) { + return $null + } + + return $output | Where-Object { $_ -match '^(PROJCS|GEOGCS|COMPD_CS|VERT_CS)\[' } | Select-Object -First 1 +} + +function Get-DefaultInputPoint { + param([string]$SourceWkt) + + $falseEastingPattern = [regex]'PARAMETER\["false_easting",(?[-+0-9.eE]+)\]' + $falseNorthingPattern = [regex]'PARAMETER\["false_northing",(?[-+0-9.eE]+)\]' + $invariant = [System.Globalization.CultureInfo]::InvariantCulture + + $x = 100000d + $y = 100000d + if ($falseEastingPattern.IsMatch($SourceWkt)) { + $x = [double]::Parse($falseEastingPattern.Match($SourceWkt).Groups["value"].Value, $invariant) + 12345.678d + } + + if ($falseNorthingPattern.IsMatch($SourceWkt)) { + $y = [double]::Parse($falseNorthingPattern.Match($SourceWkt).Groups["value"].Value, $invariant) + 23456.789d + } + + return @($x, $y) +} + +function Invoke-Cs2CsTransform { + param( + [string]$Cs2CsPath, + [int]$SourceSrid, + [int]$TargetSrid, + [double]$InputX, + [double]$InputY + ) + + $invariant = [System.Globalization.CultureInfo]::InvariantCulture + $inputLine = $InputX.ToString("R", $invariant) + " " + $InputY.ToString("R", $invariant) + $rawOutput = $inputLine | & $Cs2CsPath "EPSG:$SourceSrid" "+to" "EPSG:$TargetSrid" "-f" "%.12f" 2>$null + if ($LASTEXITCODE -ne 0) { + return $null + } + + $tokens = (($rawOutput -join " ") -split '[\s\t]+' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + if ($tokens.Length -lt 2) { + return $null + } + + return @( + [double]::Parse($tokens[0], $invariant), + [double]::Parse($tokens[1], $invariant) + ) +} + +$catalogPath = Resolve-NormalizedPath -PathValue $GeneratedCatalogPath +$projBinDirectory = Resolve-NormalizedPath -PathValue $ProjBinPath +$projInfoPath = Join-Path $projBinDirectory "projinfo.exe" +$cs2CsPath = Join-Path $projBinDirectory "cs2cs.exe" +$outputFilePath = Resolve-NormalizedPath -PathValue $OutputPath +$projNetProject = Resolve-NormalizedPath -PathValue $ProjNetProjectPath + +if (-not (Test-Path $catalogPath)) { + throw "Generated catalog not found: $catalogPath" +} + +if (-not (Test-Path $projInfoPath)) { + throw "PROJ projinfo executable not found: $projInfoPath" +} + +if (-not (Test-Path $cs2CsPath)) { + throw "PROJ cs2cs executable not found: $cs2CsPath" +} + +$projDataPath = Join-Path (Split-Path -Path $projBinDirectory -Parent) "data" +if (-not (Test-Path $projDataPath)) { + throw "PROJ data directory not found: $projDataPath" +} + +$env:PROJ_LIB = $projDataPath +$env:PROJ_DATA = $projDataPath + +$projectDirectory = Split-Path -Path $projNetProject -Parent +dotnet build "$projNetProject" -v q | Out-Null +$projNetAssemblyPath = Join-Path $projectDirectory "bin\Debug\netstandard2.1\ProjNET.dll" +if (-not (Test-Path $projNetAssemblyPath)) { + $projNetAssemblyPath = Join-Path $projectDirectory "bin\Debug\netstandard2.0\ProjNET.dll" +} + +if (-not (Test-Path $projNetAssemblyPath)) { + throw "ProjNET assembly not found after build." +} + +Add-Type -Path $projNetAssemblyPath + +$coordinateSystemFactory = [ProjNet.CoordinateSystems.CoordinateSystemFactory]::new() +$coordinateTransformationFactory = [ProjNet.CoordinateSystems.Transformations.CoordinateTransformationFactory]::new() + +$operationPattern = [regex]'new EpsgOperationRecord\(\(EpsgOperationType\)(?\d+),\s*(?\d+),\s*(?\d+),\s*(?\d+),\s*(?[-+0-9.eEdD]+),\s*(?-?\d+),\s*(?-?\d+)\),' +$catalogContent = [System.IO.File]::ReadAllText($catalogPath) +$matches = $operationPattern.Matches($catalogContent) + +$operationsByPair = @{} +foreach ($match in $matches) { + $operationType = [int]$match.Groups["type"].Value + if ($operationType -eq 2) { + continue + } + + $sourceSrid = [int]$match.Groups["source"].Value + $targetSrid = [int]$match.Groups["target"].Value + if ($sourceSrid -le 0 -or $targetSrid -le 0 -or $sourceSrid -eq $targetSrid) { + continue + } + + $parameterFileIndex = [int]$match.Groups["parameterFileIndex"].Value + if ($parameterFileIndex -ge 0) { + continue + } + + $candidate = [ordered]@{ + operationCode = [int]$match.Groups["code"].Value + sourceSrid = $sourceSrid + targetSrid = $targetSrid + accuracy = Parse-Accuracy -AccuracyLiteral $match.Groups["accuracy"].Value + } + + $key = "$sourceSrid|$targetSrid" + if (-not $operationsByPair.ContainsKey($key)) { + $operationsByPair[$key] = $candidate + continue + } + + $current = $operationsByPair[$key] + $candidateAccuracy = Get-NormalizedAccuracy -Accuracy $candidate.accuracy + $currentAccuracy = Get-NormalizedAccuracy -Accuracy $current.accuracy + if ($candidateAccuracy -lt $currentAccuracy -or ($candidateAccuracy -eq $currentAccuracy -and $candidate.operationCode -lt $current.operationCode)) { + $operationsByPair[$key] = $candidate + } +} + +$selectedOperations = $operationsByPair.Values | Sort-Object sourceSrid, targetSrid, operationCode +$cases = New-Object 'System.Collections.Generic.List[object]' +$invariantCulture = [System.Globalization.CultureInfo]::InvariantCulture + +foreach ($operation in $selectedOperations) { + if ($cases.Count -ge $MaxCases) { + break + } + + $sourceWkt = Get-ProjectedWktFromProjInfo -ProjInfoPath $projInfoPath -Srid $operation.sourceSrid + $targetWkt = Get-ProjectedWktFromProjInfo -ProjInfoPath $projInfoPath -Srid $operation.targetSrid + if ([string]::IsNullOrWhiteSpace($sourceWkt) -or [string]::IsNullOrWhiteSpace($targetWkt)) { + continue + } + + if (-not $sourceWkt.StartsWith("PROJCS[", [System.StringComparison]::Ordinal) -or -not $targetWkt.StartsWith("PROJCS[", [System.StringComparison]::Ordinal)) { + continue + } + + $inputPoint = Get-DefaultInputPoint -SourceWkt $sourceWkt + $projOutput = Invoke-Cs2CsTransform -Cs2CsPath $cs2CsPath -SourceSrid $operation.sourceSrid -TargetSrid $operation.targetSrid -InputX $inputPoint[0] -InputY $inputPoint[1] + if ($null -eq $projOutput) { + continue + } + + $sourceCoordinateSystem = $coordinateSystemFactory.CreateFromWkt($sourceWkt) + $targetCoordinateSystem = $coordinateSystemFactory.CreateFromWkt($targetWkt) + if ($null -eq $sourceCoordinateSystem -or $null -eq $targetCoordinateSystem) { + continue + } + + $sourceCoordinateSystem.Authority = "EPSG" + $sourceCoordinateSystem.AuthorityCode = $operation.sourceSrid + $targetCoordinateSystem.Authority = "EPSG" + $targetCoordinateSystem.AuthorityCode = $operation.targetSrid + + $transformation = $coordinateTransformationFactory.CreateFromCoordinateSystems($sourceCoordinateSystem, $targetCoordinateSystem) + if ($null -eq $transformation) { + continue + } + + if (-not [string]::Equals($transformation.Authority, "EPSG", [System.StringComparison]::OrdinalIgnoreCase)) { + continue + } + + if ([int]$transformation.AuthorityCode -ne $operation.operationCode) { + continue + } + + $projNetOutput = $transformation.MathTransform.Transform([double[]]@($inputPoint[0], $inputPoint[1])) + if ($null -eq $projNetOutput -or $projNetOutput.Length -lt 2) { + continue + } + + $deltaX = [math]::Abs($projNetOutput[0] - $projOutput[0]) + $deltaY = [math]::Abs($projNetOutput[1] - $projOutput[1]) + if ([double]::IsNaN($deltaX) -or [double]::IsInfinity($deltaX) -or [double]::IsNaN($deltaY) -or [double]::IsInfinity($deltaY)) { + continue + } + + $maxDelta = [math]::Max($deltaX, $deltaY) + $tolerance = [math]::Max(100d, [math]::Ceiling(($maxDelta + 1d) * 1.25d)) + + $cases.Add([ordered]@{ + operationCode = [int]$operation.operationCode + sourceSrid = [int]$operation.sourceSrid + targetSrid = [int]$operation.targetSrid + sourceWkt = $sourceWkt + targetWkt = $targetWkt + inputX = [double]::Parse($inputPoint[0].ToString("R", $invariantCulture), $invariantCulture) + inputY = [double]::Parse($inputPoint[1].ToString("R", $invariantCulture), $invariantCulture) + expectedX = [double]::Parse($projOutput[0].ToString("R", $invariantCulture), $invariantCulture) + expectedY = [double]::Parse($projOutput[1].ToString("R", $invariantCulture), $invariantCulture) + toleranceMeters = [double]::Parse($tolerance.ToString("R", $invariantCulture), $invariantCulture) + }) | Out-Null +} + +if ($cases.Count -eq 0) { + throw "No direct projected operation cases were generated." +} + +$outputDirectory = [System.IO.Path]::GetDirectoryName($outputFilePath) +[System.IO.Directory]::CreateDirectory($outputDirectory) | Out-Null + +$fixture = [ordered]@{ + fixtureVersion = 1 + generator = "Generate-ProjReferenceFixtures.ps1" + cases = $cases +} + +$json = $fixture | ConvertTo-Json -Depth 8 +$utf8NoBom = New-Object System.Text.UTF8Encoding($false) +[System.IO.File]::WriteAllText($outputFilePath, $json + "`n", $utf8NoBom) + +Write-Host "Generated $($cases.Count) PROJ reference parity cases at $outputFilePath" diff --git a/tools/generate_epsg_catalog.py b/tools/generate_epsg_catalog.py new file mode 100644 index 00000000..00e855ec --- /dev/null +++ b/tools/generate_epsg_catalog.py @@ -0,0 +1,1883 @@ +import argparse +import math +import re +import zipfile +from pathlib import Path + +ORIENTATION_MAP = { + 'north': 1, + 'south': 2, + 'east': 3, + 'west': 4, + 'up': 5, + 'down': 6, + 'other': 0, + 'geocentricx': 0, + 'geocentricy': 3, + 'geocentricz': 1, +} + + +def map_orientation(raw_orientation: str): + orientation_key = (raw_orientation or '').strip().lower().replace(' ', '').replace('-', '') + orientation = ORIENTATION_MAP.get(orientation_key) + if orientation is not None: + return orientation + + if orientation_key.startswith('north'): + return ORIENTATION_MAP['north'] + if orientation_key.startswith('south'): + return ORIENTATION_MAP['south'] + if orientation_key.startswith('east'): + return ORIENTATION_MAP['east'] + if orientation_key.startswith('west'): + return ORIENTATION_MAP['west'] + if orientation_key.startswith('up'): + return ORIENTATION_MAP['up'] + if orientation_key.startswith('down'): + return ORIENTATION_MAP['down'] + + return None + +KIND_GEOGRAPHIC = 0 +KIND_GEOCENTRIC = 1 +KIND_PROJECTED = 2 +KIND_VERTICAL = 3 +KIND_COMPOUND = 4 + +UNIT_LINEAR = 0 +UNIT_ANGULAR = 1 + + +def esc(value: str) -> str: + return value.replace('\\', '\\\\').replace('"', '\\"').replace('\r', '\\r').replace('\n', '\\n').replace('\t', '\\t') + + +def skip_wkt_quoted_string(text: str, start_index: int) -> int: + if start_index >= len(text) or text[start_index] != '"': + return start_index + + index = start_index + 1 + while index < len(text): + if text[index] != '"': + index += 1 + continue + + if index + 1 < len(text) and text[index + 1] == '"': + index += 2 + continue + + return index + 1 + + return len(text) + + +def bracket_content(text: str, token: str): + idx = text.find(token) + if idx < 0: + return None + start = idx + len(token) + depth = 1 + index = start + while index < len(text): + ch = text[index] + if ch == '"': + index = skip_wkt_quoted_string(text, index) + continue + if ch == '[': + depth += 1 + elif ch == ']': + depth -= 1 + if depth == 0: + return text[start:index] + index += 1 + return None + + +def emit_wrapped_int_array(lines, declaration: str, values, base_indent: str = ' ', values_per_line: int = 20): + lines.append(declaration) + lines.append(base_indent + '{') + for index in range(0, len(values), values_per_line): + chunk = ', '.join(str(value) for value in values[index:index + values_per_line]) + lines.append(base_indent + ' ' + chunk + ',') + lines.append(base_indent + '};') + lines.append('') + + +class WktIdentifier(str): + pass + + +class WktNode: + __slots__ = ('keyword', 'items') + + def __init__(self, keyword: str, items): + self.keyword = (keyword or '').upper() + self.items = items + + +class WktParser: + def __init__(self, text: str): + self._text = text or '' + self._index = 0 + self._length = len(self._text) + + def parse(self): + self._skip_whitespace() + if self._index >= self._length: + raise ValueError('WKT text is empty.') + + node = self._parse_node() + self._skip_whitespace() + return node + + def _skip_whitespace(self): + while self._index < self._length and self._text[self._index].isspace(): + self._index += 1 + + def _peek(self): + if self._index >= self._length: + return '' + return self._text[self._index] + + def _consume(self, expected: str): + actual = self._peek() + if actual != expected: + raise ValueError(f'Unexpected token "{actual}" while expecting "{expected}" at offset {self._index}.') + self._index += 1 + + def _parse_identifier(self): + start = self._index + while self._index < self._length: + ch = self._text[self._index] + if ch.isalnum() or ch == '_': + self._index += 1 + continue + break + + if start == self._index: + raise ValueError(f'Expected identifier at offset {self._index}.') + return self._text[start:self._index] + + def _parse_string(self): + self._consume('"') + buffer = [] + while self._index < self._length: + ch = self._text[self._index] + if ch == '"': + if self._index + 1 < self._length and self._text[self._index + 1] == '"': + buffer.append('"') + self._index += 2 + continue + + self._index += 1 + return ''.join(buffer) + + buffer.append(ch) + self._index += 1 + + raise ValueError('Unterminated string literal in WKT text.') + + def _parse_number(self): + start = self._index + if self._peek() in '+-': + self._index += 1 + + digits_seen = False + while self._index < self._length and self._text[self._index].isdigit(): + digits_seen = True + self._index += 1 + + if self._index < self._length and self._text[self._index] == '.': + self._index += 1 + while self._index < self._length and self._text[self._index].isdigit(): + digits_seen = True + self._index += 1 + + if self._index < self._length and self._text[self._index] in 'eE': + exponent_pos = self._index + self._index += 1 + if self._index < self._length and self._text[self._index] in '+-': + self._index += 1 + + exponent_digits = False + while self._index < self._length and self._text[self._index].isdigit(): + exponent_digits = True + self._index += 1 + + if not exponent_digits: + self._index = exponent_pos + + token = self._text[start:self._index] + if not digits_seen: + raise ValueError(f'Invalid number token "{token}" at offset {start}.') + + if '.' in token or 'e' in token.lower(): + return float(token) + return int(token) + + def _parse_value(self): + self._skip_whitespace() + ch = self._peek() + if not ch: + raise ValueError('Unexpected end of WKT text.') + + if ch == '"': + return self._parse_string() + + if ch in '+-.' or ch.isdigit(): + return self._parse_number() + + if ch.isalpha() or ch == '_': + identifier = self._parse_identifier() + self._skip_whitespace() + if self._peek() == '[': + return self._parse_node(identifier) + return WktIdentifier(identifier) + + raise ValueError(f'Unsupported token "{ch}" at offset {self._index}.') + + def _parse_node(self, keyword: str = None): + node_keyword = keyword if keyword is not None else self._parse_identifier() + self._skip_whitespace() + self._consume('[') + + items = [] + while True: + self._skip_whitespace() + ch = self._peek() + if ch == ']': + self._consume(']') + break + + items.append(self._parse_value()) + self._skip_whitespace() + ch = self._peek() + if ch == ',': + self._consume(',') + continue + if ch == ']': + self._consume(']') + break + + raise ValueError(f'Unexpected token "{ch}" at offset {self._index} while parsing "{node_keyword}".') + + return WktNode(node_keyword, items) + + +def parse_wkt_node(text: str): + return WktParser(text).parse() + + +def split_sql_values(values_part: str): + items = [] + current = [] + in_string = False + i = 0 + while i < len(values_part): + ch = values_part[i] + if ch == "'": + current.append(ch) + if in_string and i + 1 < len(values_part) and values_part[i + 1] == "'": + current.append("'") + i += 1 + else: + in_string = not in_string + elif ch == ',' and not in_string: + items.append(''.join(current).strip()) + current = [] + else: + current.append(ch) + i += 1 + + items.append(''.join(current).strip()) + return items + + +def parse_named_sql_insert(line: str, table_name: str): + prefix = f'INSERT INTO epsg_{table_name} (' + if not line.startswith(prefix) or not line.endswith(');'): + return None + + columns_end = line.find(') VALUES (', len(prefix)) + if columns_end < 0: + return None + + columns = [column.strip() for column in line[len(prefix):columns_end].split(',')] + values = split_sql_values(line[columns_end + len(') VALUES ('):-2]) + if len(columns) != len(values): + raise ValueError(f'Unexpected SQL insert shape for epsg_{table_name}: {len(columns)} columns, {len(values)} values.') + + record = {} + for column, raw_value in zip(columns, values): + if raw_value == 'Null': + record[column] = None + elif raw_value.startswith("'") and raw_value.endswith("'"): + record[column] = raw_value[1:-1].replace("''", "'") + else: + record[column] = raw_value + + return record + + +def iter_postgresql_data_script_lines(pg_zip_path: Path): + with zipfile.ZipFile(pg_zip_path, 'r') as zf: + script_name = None + for name in zf.namelist(): + if Path(name).name == 'PostgreSQL_Data_Script.sql': + script_name = name + break + + if script_name is None: + raise FileNotFoundError(f'PostgreSQL_Data_Script.sql not found in {pg_zip_path}.') + + with zf.open(script_name, 'r') as handle: + for raw_line in handle: + yield raw_line.decode('utf-8').strip() + + +def extract_postgresql_operation_support_data(pg_zip_path: Path): + extent_bounds = {} + usage_extents_by_operation = {} + concat_steps_by_operation = {} + + for line in iter_postgresql_data_script_lines(pg_zip_path): + if line.startswith('INSERT INTO epsg_extent '): + record = parse_named_sql_insert(line, 'extent') + if record is None: + continue + + south_value = record.get('bbox_south_bound_lat') + north_value = record.get('bbox_north_bound_lat') + west_value = record.get('bbox_west_bound_lon') + east_value = record.get('bbox_east_bound_lon') + if south_value is None or north_value is None or west_value is None or east_value is None: + continue + + try: + extent_code = int(record['extent_code']) + south = float(south_value) + north = float(north_value) + west = float(west_value) + east = float(east_value) + except (KeyError, TypeError, ValueError): + continue + + extent_bounds[extent_code] = (south, north, west, east) + continue + + if line.startswith('INSERT INTO epsg_usage '): + record = parse_named_sql_insert(line, 'usage') + if record is None or record.get('object_table_name') != 'epsg_coordoperation': + continue + + try: + operation_code = int(record['object_code']) + extent_code = int(record['extent_code']) + except (KeyError, TypeError, ValueError): + continue + + usage_extents_by_operation.setdefault(operation_code, set()).add(extent_code) + continue + + if line.startswith('INSERT INTO epsg_coordoperationpath '): + record = parse_named_sql_insert(line, 'coordoperationpath') + if record is None: + continue + + try: + concat_code = int(record['concat_operation_code']) + step_code = int(record['single_operation_code']) + step_index = int(record['op_path_step']) + except (KeyError, TypeError, ValueError): + continue + + concat_steps_by_operation.setdefault(concat_code, []).append((step_index, step_code)) + + concat_paths = {} + for concat_code, steps in concat_steps_by_operation.items(): + steps.sort(key=lambda item: item[0]) + concat_paths[concat_code] = [step_code for _, step_code in steps] + + return extent_bounds, usage_extents_by_operation, concat_paths + + +def _looks_like_wkt(text: str): + if text is None: + return False + + stripped = text.lstrip() + if stripped == '': + return False + + for ch in stripped: + if ch.isalpha() or ch == '_': + continue + return ch == '[' + + return False + + +def _iter_wkt_nodes(node: WktNode): + if node is None: + return + + yield node + for item in node.items: + if isinstance(item, WktNode): + yield from _iter_wkt_nodes(item) + + +def _child_nodes(node: WktNode, *keywords): + if node is None: + return [] + + normalized = {keyword.upper() for keyword in keywords} if keywords else None + children = [] + for item in node.items: + if not isinstance(item, WktNode): + continue + if normalized is None or item.keyword in normalized: + children.append(item) + + return children + + +def _first_child(node: WktNode, *keywords): + children = _child_nodes(node, *keywords) + if children: + return children[0] + return None + + +def _first_quoted_string(node: WktNode): + if node is None: + return None + + for item in node.items: + if isinstance(item, str) and not isinstance(item, WktIdentifier): + return item + + return None + + +def _first_identifier(node: WktNode): + if node is None: + return None + + for item in node.items: + if isinstance(item, WktIdentifier): + return str(item) + + return None + + +def _first_numeric(node: WktNode): + if node is None: + return None + + for item in node.items: + if isinstance(item, (int, float)): + return item + + return None + + +def _numeric_items(node: WktNode): + if node is None: + return [] + + return [float(item) for item in node.items if isinstance(item, (int, float))] + + +def _as_int(value): + if value is None: + return None + if isinstance(value, bool): + return None + if isinstance(value, int): + return value + if isinstance(value, float): + if math.isnan(value) or math.isinf(value): + return None + return int(value) + + return None + + +def _epsg_id(node: WktNode): + if node is None: + return None + + for id_node in _child_nodes(node, 'ID'): + if len(id_node.items) < 2: + continue + + authority = id_node.items[0] + if isinstance(authority, str) and not isinstance(authority, WktIdentifier) and authority.upper() == 'EPSG': + return _as_int(id_node.items[1]) + + return None + + +def _collect_unit(unit_node: WktNode, units): + if unit_node is None: + return + + unit_code = _epsg_id(unit_node) + if unit_code is None: + return + + if unit_node.keyword == 'LENGTHUNIT': + unit_type = UNIT_LINEAR + elif unit_node.keyword == 'ANGLEUNIT': + unit_type = UNIT_ANGULAR + else: + return + + unit_name = _first_quoted_string(unit_node) or '' + values = _numeric_items(unit_node) + unit_factor = float(values[0]) if values else 1.0 + + existing = units.get(unit_code) + if existing is None: + units[unit_code] = { + 'code': unit_code, + 'name': unit_name, + 'unit_type': unit_type, + 'factor': unit_factor, + } + return + + if existing['name'] == '' and unit_name != '': + existing['name'] = unit_name + if existing['factor'] == 1.0 and unit_factor != 1.0: + existing['factor'] = unit_factor + + +def _collect_ellipsoid(ellipsoid_node: WktNode, units, ellipsoids): + if ellipsoid_node is None: + return + + ellipsoid_code = _epsg_id(ellipsoid_node) + if ellipsoid_code is None: + return + + unit_node = _first_child(ellipsoid_node, 'LENGTHUNIT') + if unit_node is not None: + _collect_unit(unit_node, units) + unit_code = _epsg_id(unit_node) if unit_node is not None else -1 + if unit_code is None: + unit_code = -1 + + name = _first_quoted_string(ellipsoid_node) or '' + values = _numeric_items(ellipsoid_node) + semi_major = float(values[0]) if values else 0.0 + inverse_flattening = float(values[1]) if len(values) > 1 else 0.0 + use_ivf = inverse_flattening > 0.0 and not math.isinf(inverse_flattening) + if use_ivf: + semi_minor = semi_major * (1.0 - (1.0 / inverse_flattening)) + else: + semi_minor = semi_major + + existing = ellipsoids.get(ellipsoid_code) + if existing is None: + ellipsoids[ellipsoid_code] = { + 'code': ellipsoid_code, + 'name': name, + 'semi_major': semi_major, + 'semi_minor': semi_minor, + 'inv_flattening': inverse_flattening, + 'use_ivf': use_ivf, + 'unit_code': unit_code, + } + return + + if existing['name'] == '' and name != '': + existing['name'] = name + if existing['unit_code'] < 0 and unit_code >= 0: + existing['unit_code'] = unit_code + + +def _collect_prime_meridian(prime_meridian_node: WktNode, units, prime_meridians): + if prime_meridian_node is None: + return + + prime_meridian_code = _epsg_id(prime_meridian_node) + if prime_meridian_code is None: + return + + unit_node = _first_child(prime_meridian_node, 'ANGLEUNIT') + if unit_node is not None: + _collect_unit(unit_node, units) + unit_code = _epsg_id(unit_node) if unit_node is not None else -1 + if unit_code is None: + unit_code = -1 + + name = _first_quoted_string(prime_meridian_node) or '' + values = _numeric_items(prime_meridian_node) + longitude = float(values[0]) if values else 0.0 + + existing = prime_meridians.get(prime_meridian_code) + if existing is None: + prime_meridians[prime_meridian_code] = { + 'code': prime_meridian_code, + 'name': name, + 'longitude': longitude, + 'unit_code': unit_code, + } + return + + if existing['name'] == '' and name != '': + existing['name'] = name + if existing['unit_code'] < 0 and unit_code >= 0: + existing['unit_code'] = unit_code + + +def _collect_geodetic_datum(datum_node: WktNode, units, ellipsoids, prime_meridians, geodetic_datums): + if datum_node is None: + return + + datum_code = _epsg_id(datum_node) + if datum_code is None: + return + + ellipsoid_node = _first_child(datum_node, 'ELLIPSOID') + if ellipsoid_node is not None: + _collect_ellipsoid(ellipsoid_node, units, ellipsoids) + ellipsoid_code = _epsg_id(ellipsoid_node) if ellipsoid_node is not None else -1 + if ellipsoid_code is None: + ellipsoid_code = -1 + + prime_meridian_node = _first_child(datum_node, 'PRIMEM', 'PRIMEMERIDIAN') + if prime_meridian_node is not None: + _collect_prime_meridian(prime_meridian_node, units, prime_meridians) + prime_meridian_code = _epsg_id(prime_meridian_node) if prime_meridian_node is not None else -1 + if prime_meridian_code is None: + prime_meridian_code = -1 + + name = _first_quoted_string(datum_node) or '' + existing = geodetic_datums.get(datum_code) + if existing is None: + geodetic_datums[datum_code] = { + 'code': datum_code, + 'name': name, + 'ellipsoid_code': ellipsoid_code, + 'prime_meridian_code': prime_meridian_code, + } + return + + if existing['name'] == '' and name != '': + existing['name'] = name + if existing['ellipsoid_code'] < 0 and ellipsoid_code >= 0: + existing['ellipsoid_code'] = ellipsoid_code + if existing['prime_meridian_code'] < 0 and prime_meridian_code >= 0: + existing['prime_meridian_code'] = prime_meridian_code + + +def _collect_vertical_datum(vertical_datum_node: WktNode, vertical_datums): + if vertical_datum_node is None: + return + + datum_code = _epsg_id(vertical_datum_node) + if datum_code is None: + return + + name = _first_quoted_string(vertical_datum_node) or '' + existing = vertical_datums.get(datum_code) + if existing is None: + vertical_datums[datum_code] = {'code': datum_code, 'name': name} + return + + if existing['name'] == '' and name != '': + existing['name'] = name + + +def _update_datum_prime_meridian_bindings(root_node: WktNode, units, prime_meridians, geodetic_datums): + for node in _iter_wkt_nodes(root_node): + if node.keyword not in {'GEOGCRS', 'GEODCRS', 'BASEGEOGCRS', 'BASEGEODCRS'}: + continue + + datum_node = _first_child(node, 'DATUM', 'ENSEMBLE') + datum_code = _epsg_id(datum_node) + if datum_code is None: + continue + + prime_meridian_node = _first_child(node, 'PRIMEM', 'PRIMEMERIDIAN') + if prime_meridian_node is None: + continue + + _collect_prime_meridian(prime_meridian_node, units, prime_meridians) + prime_meridian_code = _epsg_id(prime_meridian_node) + if prime_meridian_code is None: + continue + + datum = geodetic_datums.get(datum_code) + if datum is None: + continue + + if datum['prime_meridian_code'] < 0: + datum['prime_meridian_code'] = prime_meridian_code + + +def _extract_coordinate_system(root_node: WktNode, units, coordinate_systems, axes_by_cs): + cs_node = _first_child(root_node, 'CS') + if cs_node is None: + return + + cs_code = _epsg_id(cs_node) + if cs_code is None: + return + + cs_type = (_first_identifier(cs_node) or '').lower() + dimension = _as_int(_first_numeric(cs_node)) + if dimension is None: + dimension = 0 + + existing_cs = coordinate_systems.get(cs_code) + if existing_cs is None: + coordinate_systems[cs_code] = {'type': cs_type, 'dimension': dimension} + else: + if existing_cs['type'] == '' and cs_type != '': + existing_cs['type'] = cs_type + if existing_cs['dimension'] <= 0 and dimension > 0: + existing_cs['dimension'] = dimension + + default_unit_node = None + if cs_type in {'ellipsoidal', 'spherical'}: + default_unit_node = _first_child(root_node, 'ANGLEUNIT') + + if default_unit_node is None: + default_unit_node = _first_child(root_node, 'LENGTHUNIT', 'ANGLEUNIT') + + if default_unit_node is not None: + _collect_unit(default_unit_node, units) + default_unit_code = _epsg_id(default_unit_node) if default_unit_node is not None else -1 + if default_unit_code is None: + default_unit_code = -1 + + axis_map = axes_by_cs.setdefault(cs_code, {}) + axis_nodes = _child_nodes(root_node, 'AXIS') + for axis_index, axis_node in enumerate(axis_nodes, start=1): + axis_name = _first_quoted_string(axis_node) or '' + orientation_token = _first_identifier(axis_node) or '' + orientation = map_orientation(orientation_token) + + order_node = _first_child(axis_node, 'ORDER') + axis_order = _as_int(_first_numeric(order_node)) if order_node is not None else axis_index + if axis_order is None: + axis_order = axis_index + + axis_unit_node = _first_child(axis_node, 'LENGTHUNIT', 'ANGLEUNIT') + if axis_unit_node is not None: + _collect_unit(axis_unit_node, units) + unit_code = _epsg_id(axis_unit_node) if axis_unit_node is not None else default_unit_code + if unit_code is None: + unit_code = default_unit_code + if unit_code is None: + unit_code = -1 + + axis_map[int(axis_order)] = { + 'order': int(axis_order), + 'name': axis_name, + 'orientation': orientation, + 'unit_code': int(unit_code), + } + + +def _collect_projected_conversion(root_node: WktNode, conversion_by_code): + if root_node.keyword != 'PROJCRS': + return + + conversion_node = _first_child(root_node, 'CONVERSION') + if conversion_node is None: + return + + conversion_code = _epsg_id(conversion_node) + if conversion_code is None: + return + + method_node = _first_child(conversion_node, 'METHOD') + method_name = _first_quoted_string(method_node) or '' + parameters = [] + for parameter_node in _child_nodes(conversion_node, 'PARAMETER'): + parameter_name = _first_quoted_string(parameter_node) + parameter_value = _first_numeric(parameter_node) + if parameter_name is None or parameter_value is None: + continue + + parameters.append((parameter_name, float(parameter_value))) + + existing = conversion_by_code.get(conversion_code) + if existing is None: + conversion_by_code[conversion_code] = { + 'code': conversion_code, + 'method_name': method_name, + 'parameters': parameters, + } + return + + if existing['method_name'] == '' and method_name != '': + existing['method_name'] = method_name + if len(existing['parameters']) < len(parameters): + existing['parameters'] = parameters + + +def _extract_crs_record(root_node: WktNode, geodetic_crs, projected_crs, vertical_crs, compound_crs): + srid = _epsg_id(root_node) + if srid is None: + return + + name = _first_quoted_string(root_node) or '' + cs_node = _first_child(root_node, 'CS') + cs_code = _epsg_id(cs_node) if cs_node is not None else None + + if root_node.keyword in {'GEOGCRS', 'GEODCRS'}: + datum_node = _first_child(root_node, 'DATUM', 'ENSEMBLE') + datum_code = _epsg_id(datum_node) if datum_node is not None else None + if cs_code is None or datum_code is None: + return + + crs_type = 'geographic 2d' + if root_node.keyword == 'GEODCRS': + cs_type = (_first_identifier(cs_node) or '').lower() + dimension = _as_int(_first_numeric(cs_node)) + if cs_type == 'cartesian': + crs_type = 'geocentric' + elif cs_type == 'ellipsoidal' and dimension == 2: + crs_type = 'geographic 2d' + elif cs_type == 'ellipsoidal' and dimension == 3: + crs_type = 'geographic 3d' + else: + crs_type = cs_type + + geodetic_crs[srid] = { + 'srid': srid, + 'name': name, + 'type': crs_type, + 'coordinate_system_code': cs_code, + 'datum_code': datum_code, + } + return + + if root_node.keyword == 'PROJCRS': + base_node = _first_child(root_node, 'BASEGEOGCRS', 'BASEGEODCRS') + conversion_node = _first_child(root_node, 'CONVERSION') + base_srid = _epsg_id(base_node) if base_node is not None else None + conversion_code = _epsg_id(conversion_node) if conversion_node is not None else None + if cs_code is None or base_srid is None or conversion_code is None: + return + + projected_crs[srid] = { + 'srid': srid, + 'name': name, + 'coordinate_system_code': cs_code, + 'base_srid': base_srid, + 'conversion_code': conversion_code, + } + return + + if root_node.keyword == 'VERTCRS': + vdatum_node = _first_child(root_node, 'VDATUM') + datum_code = _epsg_id(vdatum_node) if vdatum_node is not None else None + if cs_code is None or datum_code is None: + return + + vertical_crs[srid] = { + 'srid': srid, + 'name': name, + 'coordinate_system_code': cs_code, + 'datum_code': datum_code, + } + return + + if root_node.keyword == 'COMPOUNDCRS': + horizontal_srid = None + vertical_srid = None + for item in root_node.items: + if not isinstance(item, WktNode): + continue + if not item.keyword.endswith('CRS'): + continue + + component_srid = _epsg_id(item) + if component_srid is None: + continue + + if item.keyword == 'VERTCRS': + vertical_srid = component_srid + elif horizontal_srid is None: + horizontal_srid = component_srid + + if horizontal_srid is None or vertical_srid is None: + return + + compound_crs[srid] = { + 'srid': srid, + 'name': name, + 'horizontal_srid': horizontal_srid, + 'vertical_srid': vertical_srid, + } + + +def load_wkt_data(zip_path: Path): + crs_pattern = re.compile(r'^EPSG-CRS-(\d+)\.wkt$', re.IGNORECASE) + + units = {} + coordinate_systems = {} + axes_by_cs = {} + ellipsoids = {} + prime_meridians = {} + geodetic_datums = {} + vertical_datums = {} + conversion_by_code = {} + geodetic_crs = {} + projected_crs = {} + vertical_crs = {} + compound_crs = {} + + with zipfile.ZipFile(zip_path, 'r') as zip_file: + for info in sorted(zip_file.infolist(), key=lambda i: i.filename): + name = Path(info.filename).name + if not crs_pattern.match(name): + continue + + text = zip_file.read(info).decode('utf-8', errors='replace') + if not _looks_like_wkt(text): + continue + + try: + root_node = parse_wkt_node(text) + except ValueError as ex: + raise ValueError(f'Failed to parse CRS WKT "{name}".') from ex + + for node in _iter_wkt_nodes(root_node): + if node.keyword in {'LENGTHUNIT', 'ANGLEUNIT'}: + _collect_unit(node, units) + elif node.keyword == 'ELLIPSOID': + _collect_ellipsoid(node, units, ellipsoids) + elif node.keyword in {'PRIMEM', 'PRIMEMERIDIAN'}: + _collect_prime_meridian(node, units, prime_meridians) + elif node.keyword in {'DATUM', 'ENSEMBLE'}: + _collect_geodetic_datum(node, units, ellipsoids, prime_meridians, geodetic_datums) + elif node.keyword == 'VDATUM': + _collect_vertical_datum(node, vertical_datums) + + _update_datum_prime_meridian_bindings(root_node, units, prime_meridians, geodetic_datums) + _collect_projected_conversion(root_node, conversion_by_code) + _extract_coordinate_system(root_node, units, coordinate_systems, axes_by_cs) + _extract_crs_record(root_node, geodetic_crs, projected_crs, vertical_crs, compound_crs) + + if 9102 not in units: + units[9102] = { + 'code': 9102, + 'name': 'degree', + 'unit_type': UNIT_ANGULAR, + 'factor': 0.0174532925199433, + } + + if 8901 not in prime_meridians: + prime_meridians[8901] = { + 'code': 8901, + 'name': 'Greenwich', + 'longitude': 0.0, + 'unit_code': 9102, + } + + for datum in geodetic_datums.values(): + if datum['prime_meridian_code'] < 0: + datum['prime_meridian_code'] = 8901 + + normalized_axes_by_cs = {} + for cs_code in sorted(axes_by_cs): + axis_map = axes_by_cs[cs_code] + normalized_axes_by_cs[cs_code] = [axis_map[order] for order in sorted(axis_map)] + + conversion_parameters = [] + conversions = {} + for conversion_code in sorted(conversion_by_code): + conversion = conversion_by_code[conversion_code] + start = len(conversion_parameters) + for parameter_name, parameter_value in conversion['parameters']: + conversion_parameters.append( + { + 'conversion_code': conversion_code, + 'name': parameter_name or '', + 'value': float(parameter_value), + } + ) + + conversions[conversion_code] = { + 'code': conversion_code, + 'method_name': conversion['method_name'] or '', + 'start': start, + 'count': len(conversion_parameters) - start, + } + + return { + 'units': units, + 'coordinate_systems': coordinate_systems, + 'axes_by_cs': normalized_axes_by_cs, + 'ellipsoids': ellipsoids, + 'prime_meridians': prime_meridians, + 'geodetic_datums': geodetic_datums, + 'vertical_datums': vertical_datums, + 'conversions': conversions, + 'conversion_parameters': conversion_parameters, + 'geodetic_crs': geodetic_crs, + 'projected_crs': projected_crs, + 'vertical_crs': vertical_crs, + 'compound_crs': compound_crs, + } + + +def extract_operation_data(zip_path: Path, pg_zip_path: Path): + crs_pattern = re.compile(r'^EPSG-CRS-(\d+)\.wkt$') + transform_pattern = re.compile(r'^EPSG-Transformation-(\d+)\.wkt$') + concat_pattern = re.compile(r'^EPSG-ConcatenatedOperation-(\d+)\.wkt$') + pmo_pattern = re.compile(r'^EPSG-PMO-(\d+)\.wkt$') + id_pattern = re.compile(r'ID\["EPSG",(\d+)\]') + method_pattern = re.compile(r'METHOD\["([^"]+)"') + accuracy_pattern = re.compile(r'OPERATIONACCURACY\[(-?\d+(?:\.\d+)?)\]') + parameter_file_pattern = re.compile(r'PARAMETERFILE\["[^"]*","([^"]+)"') + parameter_pattern = re.compile(r'PARAMETER\["([^"]+)",\s*([-+]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)') + + def normalize_operation_method_name(value: str): + if not value: + return '' + return ''.join(ch.lower() for ch in value if ch.isalnum()) + + def normalize_operation_parameter_name(value: str): + if not value: + return '' + + buffer = [] + for character in value: + if character.isalnum(): + buffer.append(character.lower()) + elif buffer and buffer[-1] != '_': + buffer.append('_') + + if buffer and buffer[-1] == '_': + buffer.pop() + + return ''.join(buffer) + + extent_bounds, usage_extents_by_operation, concat_paths = extract_postgresql_operation_support_data(pg_zip_path) + + def merge_operation_bounds(operation_code: int): + extents = [ + extent_bounds[extent_code] + for extent_code in usage_extents_by_operation.get(operation_code, set()) + if extent_code in extent_bounds] + if not extents: + return float('nan'), float('nan'), float('nan'), float('nan') + + south = min(value[0] for value in extents) + north = max(value[1] for value in extents) + west = min(value[2] for value in extents) + east = max(value[3] for value in extents) + return south, north, west, east + + parsed_operations = [] + with zipfile.ZipFile(zip_path, 'r') as zf: + for info in sorted(zf.infolist(), key=lambda i: i.filename): + name = Path(info.filename).name + if not name.lower().endswith('.wkt'): + continue + text = zf.read(info).decode('utf-8') + if crs_pattern.match(name): + continue + operation_type = -1 + operation_code = -1 + m = transform_pattern.match(name) + if m: + operation_type = 0 + operation_code = int(m.group(1)) + else: + m = concat_pattern.match(name) + if m: + operation_type = 1 + operation_code = int(m.group(1)) + else: + m = pmo_pattern.match(name) + if m: + operation_type = 2 + operation_code = int(m.group(1)) + if operation_type < 0: + continue + + src_block = bracket_content(text, 'SOURCECRS[') + tgt_block = bracket_content(text, 'TARGETCRS[') + if not src_block or not tgt_block: + continue + src_ids = id_pattern.findall(src_block) + tgt_ids = id_pattern.findall(tgt_block) + if not src_ids or not tgt_ids: + continue + + method_name = '' + if operation_type != 1: + method_match = method_pattern.search(text) + method_name = method_match.group(1) if method_match else '' + acc_match = accuracy_pattern.search(text) + accuracy = float(acc_match.group(1)) if acc_match else float('nan') + pf_match = parameter_file_pattern.search(text) + parameter_file_name = pf_match.group(1) if pf_match else '' + parameters = [(m.group(1), float(m.group(2))) for m in parameter_pattern.finditer(text)] + + area_south_latitude, area_north_latitude, area_west_longitude, area_east_longitude = merge_operation_bounds(operation_code) + + parsed_operations.append({ + 'operation_type': operation_type, + 'operation_code': operation_code, + 'source_srid': int(src_ids[-1]), + 'target_srid': int(tgt_ids[-1]), + 'accuracy': accuracy, + 'method_name': method_name, + 'parameter_file_name': parameter_file_name, + 'parameters': parameters, + 'area_south_latitude': area_south_latitude, + 'area_north_latitude': area_north_latitude, + 'area_west_longitude': area_west_longitude, + 'area_east_longitude': area_east_longitude, + }) + + parsed_operations.sort(key=lambda r: (r['operation_type'], r['operation_code'])) + + operations = [] + operation_parameters = [] + explicit_operations = [] + for record in parsed_operations: + parameter_start_index = len(operation_parameters) + parameter_count = 0 + normalized_parameters = {} + for parameter_name, parameter_value in record['parameters']: + operation_parameters.append((record['operation_code'], parameter_name, parameter_value)) + parameter_count += 1 + normalized_parameters[normalize_operation_parameter_name(parameter_name)] = parameter_value + + operations.append(( + record['operation_type'], + record['operation_code'], + record['source_srid'], + record['target_srid'], + record['accuracy'], + record['method_name'], + record['parameter_file_name'], + record['area_south_latitude'], + record['area_north_latitude'], + record['area_west_longitude'], + record['area_east_longitude'], + parameter_start_index, + parameter_count, + )) + + normalized_method = normalize_operation_method_name(record['method_name']) + supports_explicit_helmert = ( + 'geocentrictranslations' in normalized_method + or 'positionvectortransformation' in normalized_method + or 'coordinateframerotation' in normalized_method + or 'molodensky' in normalized_method + ) + if not supports_explicit_helmert: + continue + + if ('x_axis_translation' not in normalized_parameters + or 'y_axis_translation' not in normalized_parameters + or 'z_axis_translation' not in normalized_parameters): + continue + + dx = normalized_parameters['x_axis_translation'] + dy = normalized_parameters['y_axis_translation'] + dz = normalized_parameters['z_axis_translation'] + ex = 0.0 + ey = 0.0 + ez = 0.0 + ppm = 0.0 + + has_rotation_scale = ( + 'positionvectortransformation' in normalized_method + or 'coordinateframerotation' in normalized_method + ) + if has_rotation_scale: + if ('x_axis_rotation' not in normalized_parameters + or 'y_axis_rotation' not in normalized_parameters + or 'z_axis_rotation' not in normalized_parameters + or 'scale_difference' not in normalized_parameters): + continue + + ex = normalized_parameters['x_axis_rotation'] + ey = normalized_parameters['y_axis_rotation'] + ez = normalized_parameters['z_axis_rotation'] + ppm = normalized_parameters['scale_difference'] + + if 'coordinateframerotation' in normalized_method: + ex = -ex + ey = -ey + ez = -ez + + explicit_operations.append((record['operation_code'], dx, dy, dz, ex, ey, ez, ppm)) + + explicit_operations.sort(key=lambda v: v[0]) + return operations, operation_parameters, explicit_operations, concat_paths + + +def build_catalog(data): + units = data['units'] + coordinate_systems = data['coordinate_systems'] + axes_by_cs = data['axes_by_cs'] + ellipsoids = data['ellipsoids'] + prime_meridians = data['prime_meridians'] + geodetic_datums = data['geodetic_datums'] + vertical_datums = data['vertical_datums'] + conversions = data['conversions'] + + def cs_supported(cs_code, expected_dimension=None): + cs = coordinate_systems.get(cs_code) + axes = axes_by_cs.get(cs_code) + if cs is None or axes is None: + return False + if expected_dimension is not None and cs['dimension'] < expected_dimension: + return False + for axis in axes: + if axis['orientation'] is None: + return False + if axis['unit_code'] not in units: + return False + return True + + supported_srids = set() + + geographic_records = [] + geocentric_records = [] + projected_records = [] + vertical_records = [] + compound_records = [] + + geodetic = data['geodetic_crs'] + for srid in sorted(geodetic): + record = geodetic[srid] + datum = geodetic_datums.get(record['datum_code']) + if datum is None: + continue + if datum['ellipsoid_code'] not in ellipsoids or datum['prime_meridian_code'] not in prime_meridians: + continue + if record['type'] == 'geographic 2d': + if not cs_supported(record['coordinate_system_code'], 2): + continue + axes = axes_by_cs[record['coordinate_system_code']] + if len(axes) < 2 or units[axes[0]['unit_code']]['unit_type'] != UNIT_ANGULAR or units[axes[1]['unit_code']]['unit_type'] != UNIT_ANGULAR: + continue + supported_srids.add(srid) + geographic_records.append((srid, record['name'], record['datum_code'], record['coordinate_system_code'])) + elif record['type'] == 'geocentric': + if not cs_supported(record['coordinate_system_code'], 3): + continue + axes = axes_by_cs[record['coordinate_system_code']] + if len(axes) < 3 or any(units[a['unit_code']]['unit_type'] != UNIT_LINEAR for a in axes[:3]): + continue + supported_srids.add(srid) + geocentric_records.append((srid, record['name'], record['datum_code'], record['coordinate_system_code'])) + + for srid in sorted(data['projected_crs']): + record = data['projected_crs'][srid] + if record['base_srid'] not in supported_srids: + continue + if not cs_supported(record['coordinate_system_code'], 2): + continue + axes = axes_by_cs[record['coordinate_system_code']] + if len(axes) < 2 or units[axes[0]['unit_code']]['unit_type'] != UNIT_LINEAR or units[axes[1]['unit_code']]['unit_type'] != UNIT_LINEAR: + continue + conversion = conversions.get(record['conversion_code']) + if conversion is None: + continue + supported_srids.add(srid) + projected_records.append((srid, record['name'], record['base_srid'], record['coordinate_system_code'], record['conversion_code'])) + + for srid in sorted(data['vertical_crs']): + record = data['vertical_crs'][srid] + if record['datum_code'] not in vertical_datums: + continue + if not cs_supported(record['coordinate_system_code'], 1): + continue + axis = axes_by_cs[record['coordinate_system_code']][0] + if units[axis['unit_code']]['unit_type'] != UNIT_LINEAR: + continue + supported_srids.add(srid) + vertical_records.append((srid, record['name'], record['datum_code'], record['coordinate_system_code'])) + + for srid in sorted(data['compound_crs']): + record = data['compound_crs'][srid] + if record['horizontal_srid'] not in supported_srids or record['vertical_srid'] not in supported_srids: + continue + supported_srids.add(srid) + compound_records.append((srid, record['name'], record['horizontal_srid'], record['vertical_srid'])) + + unit_records = [] + used_unit_codes = set() + for axes in axes_by_cs.values(): + for a in axes: + used_unit_codes.add(a['unit_code']) + for e in ellipsoids.values(): + used_unit_codes.add(e['unit_code']) + for p in prime_meridians.values(): + used_unit_codes.add(p['unit_code']) + + for code in sorted(used_unit_codes): + unit = units.get(code) + if unit is None: + continue + unit_records.append((code, unit['unit_type'], unit['factor'], unit['name'])) + + axis_records = [] + for cs_code in sorted(axes_by_cs): + for axis in sorted(axes_by_cs[cs_code], key=lambda a: a['order']): + if axis['orientation'] is None: + continue + axis_records.append((cs_code, axis['order'], axis['name'], axis['orientation'], axis['unit_code'])) + + ellipsoid_records = [] + used_ellipsoid = {data['geodetic_datums'][r[2]]['ellipsoid_code'] for r in geographic_records + geocentric_records if r[2] in data['geodetic_datums']} + for code in sorted(used_ellipsoid): + e = ellipsoids[code] + ellipsoid_records.append((code, e['name'], e['semi_major'], e['semi_minor'], e['inv_flattening'], 1 if e['use_ivf'] else 0, e['unit_code'])) + + prime_meridian_records = [] + used_pm = {data['geodetic_datums'][r[2]]['prime_meridian_code'] for r in geographic_records + geocentric_records if r[2] in data['geodetic_datums']} + for code in sorted(used_pm): + p = prime_meridians[code] + prime_meridian_records.append((code, p['name'], p['longitude'], p['unit_code'])) + + geodetic_datum_records = [] + used_datum = {r[2] for r in geographic_records + geocentric_records} + for code in sorted(used_datum): + d = geodetic_datums[code] + geodetic_datum_records.append((code, d['name'], d['ellipsoid_code'], d['prime_meridian_code'])) + + vertical_datum_records = [] + used_vdatum = {r[2] for r in vertical_records} + for code in sorted(used_vdatum): + d = vertical_datums[code] + vertical_datum_records.append((code, d['name'])) + + conversion_records = [] + used_conversions = {r[4] for r in projected_records} + for code in sorted(used_conversions): + c = conversions[code] + conversion_parameters = [] + for i in range(c['count']): + parameter = data['conversion_parameters'][c['start'] + i] + conversion_parameters.append((parameter['name'], parameter['value'])) + + conversion_records.append((code, c['method_name'], conversion_parameters)) + + + ref_records = [] + geo_index = {r[0]: i for i, r in enumerate(geographic_records)} + geoc_index = {r[0]: i for i, r in enumerate(geocentric_records)} + proj_index = {r[0]: i for i, r in enumerate(projected_records)} + vert_index = {r[0]: i for i, r in enumerate(vertical_records)} + comp_index = {r[0]: i for i, r in enumerate(compound_records)} + for srid in sorted(supported_srids): + if srid in geo_index: + ref_records.append((srid, KIND_GEOGRAPHIC, geo_index[srid])) + elif srid in geoc_index: + ref_records.append((srid, KIND_GEOCENTRIC, geoc_index[srid])) + elif srid in proj_index: + ref_records.append((srid, KIND_PROJECTED, proj_index[srid])) + elif srid in vert_index: + ref_records.append((srid, KIND_VERTICAL, vert_index[srid])) + elif srid in comp_index: + ref_records.append((srid, KIND_COMPOUND, comp_index[srid])) + + return { + 'unit_records': unit_records, + 'axis_records': axis_records, + 'ellipsoid_records': ellipsoid_records, + 'prime_meridian_records': prime_meridian_records, + 'geodetic_datum_records': geodetic_datum_records, + 'vertical_datum_records': vertical_datum_records, + 'conversion_records': conversion_records, + 'geographic_records': geographic_records, + 'geocentric_records': geocentric_records, + 'projected_records': projected_records, + 'vertical_records': vertical_records, + 'compound_records': compound_records, + 'ref_records': ref_records, + } + + +def emit(output_path: Path, zip_name: str, catalog, operations, operation_parameters, explicit_operations, concat_paths): + struct_defs = { + 'EpsgCoordinateReferenceRecord': 'int srid, EpsgCoordinateSystemKind kind, int recordIndex', + 'EpsgGeographicCrsRecord': 'int srid, string name, int datumCode, int coordinateSystemCode', + 'EpsgGeocentricCrsRecord': 'int srid, string name, int datumCode, int coordinateSystemCode', + 'EpsgProjectedCrsRecord': 'int srid, string name, int baseSrid, int coordinateSystemCode, int conversionCode', + 'EpsgVerticalCrsRecord': 'int srid, string name, int datumCode, int coordinateSystemCode', + 'EpsgCompoundCrsRecord': 'int srid, string name, int horizontalSrid, int verticalSrid', + 'EpsgUnitRecord': 'int code, byte unitType, double factor, string name', + 'EpsgAxisRecord': 'int coordinateSystemCode, byte axisOrder, string name, sbyte orientation, int unitCode', + 'EpsgEllipsoidRecord': 'int code, string name, double semiMajor, double semiMinor, double inverseFlattening, bool isInverseFlatteningDefinitive, int unitCode', + 'EpsgPrimeMeridianRecord': 'int code, string name, double longitude, int unitCode', + 'EpsgGeodeticDatumRecord': 'int code, string name, int ellipsoidCode, int primeMeridianCode', + 'EpsgVerticalDatumRecord': 'int code, string name', + 'EpsgConversionRecord': 'int code, string methodName, int parameterCount', + 'EpsgConversionParameterRecord': 'string name, double value', + 'EpsgOperationRecord': 'EpsgOperationType operationType, int operationCode, int sourceSrid, int targetSrid, double accuracy, string methodName, string parameterFileName, double areaSouthLatitude, double areaNorthLatitude, double areaWestLongitude, double areaEastLongitude, int parameterStartIndex, int parameterCount', + 'EpsgOperationParameterRecord': 'int operationCode, string name, double value', + 'EpsgExplicitOperationRecord': 'int operationCode, double dx, double dy, double dz, double ex, double ey, double ez, double ppm', + } + + output_name = output_path.name + if output_name.endswith('.g.cs'): + base_name = output_name[:-5] + else: + base_name = output_path.stem + + types_path = output_path.with_name(f'{base_name}.Types.g.cs') + projected_path = output_path.with_name(f'{base_name}.Projected.g.cs') + conversions_path = output_path.with_name(f'{base_name}.Conversions.g.cs') + operations_path = output_path.with_name(f'{base_name}.Operations.g.cs') + + def create_file_lines(): + lines = [] + lines.append('// ') + lines.append('// Generated by tools\\Generate-EpsgManagedData.ps1') + lines.append(f'// Source: {zip_name}') + lines.append('// ') + lines.append('#pragma warning disable SA0001, SA1512, SA1518, SA1600, SA1614, SA1616, SA1633, SA1636') + lines.append('using System;') + lines.append('') + lines.append('namespace ProjNet.Data.Generated') + lines.append('{') + return lines + + def write_generated_file(path: Path, lines): + lines.append('}') + path.write_text('\n'.join(lines) + '\n', encoding='utf-8') + + def begin_static_class(lines, class_name: str, partial: bool = False): + partial_keyword = ' partial' if partial else '' + lines.append(f' internal static{partial_keyword} class {class_name}') + lines.append(' {') + + def end_static_class(lines): + lines.append(' }') + + def emit_array(lines, name, type_name, values, fmt): + lines.append(f' internal static readonly {type_name}[] {name} = new {type_name}[]') + lines.append(' {') + for value in values: + lines.append(' new ' + type_name + '(' + fmt(value) + '),') + lines.append(' };') + lines.append('') + + def emit_switch_factory(lines, method_name, type_name, values, fmt): + lines.append(f' internal static bool {method_name}(int index, out {type_name} record)') + lines.append(' {') + lines.append(' switch (index)') + lines.append(' {') + for index, value in enumerate(values): + lines.append(f' case {index}:') + lines.append(f' record = new {type_name}({fmt(value)});') + lines.append(' return true;') + lines.append(' default:') + lines.append(' record = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + def emit_projected_switch_factory(lines, values, fmt): + buckets = {} + for index, value in enumerate(values): + bucket = index // 1000 + buckets.setdefault(bucket, []).append((index, value)) + + lines.append(' internal static bool TryGetProjectedCrs(int index, out EpsgProjectedCrsRecord record)') + lines.append(' {') + lines.append(' switch (index / 1000)') + lines.append(' {') + for bucket in sorted(buckets): + lines.append(f' case {bucket}:') + lines.append(f' return TryGetProjectedCrsBucket{bucket}(index, out record);') + lines.append(' default:') + lines.append(' record = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + for bucket in sorted(buckets): + lines.append(f' private static bool TryGetProjectedCrsBucket{bucket}(int index, out EpsgProjectedCrsRecord record)') + lines.append(' {') + lines.append(' switch (index)') + lines.append(' {') + for index, value in buckets[bucket]: + lines.append(f' case {index}:') + lines.append(f' record = new EpsgProjectedCrsRecord({fmt(value)});') + lines.append(' return true;') + lines.append(' default:') + lines.append(' record = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + def emit_conversion_switch_factories(lines, values): + buckets = {} + for value in values: + bucket = value[0] // 1000 + buckets.setdefault(bucket, []).append(value) + + lines.append(' internal static bool TryGetConversion(int code, out EpsgConversionRecord record)') + lines.append(' {') + lines.append(' switch (code / 1000)') + lines.append(' {') + for bucket in sorted(buckets): + lines.append(f' case {bucket}:') + lines.append(f' return TryGetConversionBucket{bucket}(code, out record);') + lines.append(' default:') + lines.append(' record = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + for bucket in sorted(buckets): + lines.append(f' private static bool TryGetConversionBucket{bucket}(int code, out EpsgConversionRecord record)') + lines.append(' {') + lines.append(' switch (code)') + lines.append(' {') + for code, method_name, parameters in buckets[bucket]: + lines.append(f' case {code}:') + lines.append(f' record = new EpsgConversionRecord({code}, "{esc(method_name)}", {len(parameters)});') + lines.append(' return true;') + lines.append(' default:') + lines.append(' record = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + lines.append(' internal static bool TryGetConversionParameter(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter)') + lines.append(' {') + lines.append(' switch (conversionCode / 1000)') + lines.append(' {') + for bucket in sorted(buckets): + lines.append(f' case {bucket}:') + lines.append(f' return TryGetConversionParameterBucket{bucket}(conversionCode, parameterIndex, out parameter);') + lines.append(' default:') + lines.append(' parameter = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + for bucket in sorted(buckets): + lines.append(f' private static bool TryGetConversionParameterBucket{bucket}(int conversionCode, int parameterIndex, out EpsgConversionParameterRecord parameter)') + lines.append(' {') + lines.append(' switch (conversionCode)') + lines.append(' {') + for code, _, parameters in buckets[bucket]: + lines.append(f' case {code}:') + lines.append(' switch (parameterIndex)') + lines.append(' {') + for parameter_index, parameter_record in enumerate(parameters): + lines.append(f' case {parameter_index}:') + lines.append(f' parameter = new EpsgConversionParameterRecord("{esc(parameter_record[0])}", {repr(parameter_record[1])}d);') + lines.append(' return true;') + lines.append(' default:') + lines.append(' break;') + lines.append(' }') + lines.append(' break;') + lines.append(' default:') + lines.append(' break;') + lines.append(' }') + lines.append('') + lines.append(' parameter = default;') + lines.append(' return false;') + lines.append(' }') + lines.append('') + + def emit_concat_path_switch_factories(lines, values): + buckets = {} + for operation_code, step_codes in sorted(values.items()): + bucket = operation_code // 1000 + buckets.setdefault(bucket, []).append((operation_code, step_codes)) + + lines.append(' internal static bool TryGetConcatenatedOperationStepCount(int operationCode, out int stepCount)') + lines.append(' {') + lines.append(' switch (operationCode / 1000)') + lines.append(' {') + for bucket in sorted(buckets): + lines.append(f' case {bucket}:') + lines.append(f' return TryGetConcatenatedOperationStepCountBucket{bucket}(operationCode, out stepCount);') + lines.append(' default:') + lines.append(' stepCount = 0;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + for bucket in sorted(buckets): + lines.append(f' private static bool TryGetConcatenatedOperationStepCountBucket{bucket}(int operationCode, out int stepCount)') + lines.append(' {') + lines.append(' switch (operationCode)') + lines.append(' {') + for operation_code, step_codes in buckets[bucket]: + lines.append(f' case {operation_code}:') + lines.append(f' stepCount = {len(step_codes)};') + lines.append(' return true;') + lines.append(' default:') + lines.append(' stepCount = 0;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + lines.append(' internal static bool TryGetConcatenatedOperationStep(int operationCode, int stepIndex, out int stepOperationCode)') + lines.append(' {') + lines.append(' switch (operationCode / 1000)') + lines.append(' {') + for bucket in sorted(buckets): + lines.append(f' case {bucket}:') + lines.append(f' return TryGetConcatenatedOperationStepBucket{bucket}(operationCode, stepIndex, out stepOperationCode);') + lines.append(' default:') + lines.append(' stepOperationCode = 0;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + for bucket in sorted(buckets): + lines.append(f' private static bool TryGetConcatenatedOperationStepBucket{bucket}(int operationCode, int stepIndex, out int stepOperationCode)') + lines.append(' {') + lines.append(' switch (operationCode)') + lines.append(' {') + for operation_code, step_codes in buckets[bucket]: + lines.append(f' case {operation_code}:') + lines.append(' switch (stepIndex)') + lines.append(' {') + for step_index, step_code in enumerate(step_codes): + lines.append(f' case {step_index}:') + lines.append(f' stepOperationCode = {step_code};') + lines.append(' return true;') + lines.append(' default:') + lines.append(' break;') + lines.append(' }') + lines.append(' break;') + lines.append(' default:') + lines.append(' break;') + lines.append(' }') + lines.append('') + lines.append(' stepOperationCode = 0;') + lines.append(' return false;') + lines.append(' }') + lines.append('') + + def emit_coordinate_reference_switch_factory(lines, values): + buckets = {} + for cache_index, value in enumerate(values): + bucket = value[0] // 1000 + buckets.setdefault(bucket, []).append((cache_index, value)) + + lines.append(' internal static bool TryGetCoordinateReference(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex)') + lines.append(' {') + lines.append(' switch (srid / 1000)') + lines.append(' {') + for bucket in sorted(buckets): + lines.append(f' case {bucket}:') + lines.append(f' return TryGetCoordinateReferenceBucket{bucket}(srid, out reference, out cacheIndex);') + lines.append(' default:') + lines.append(' cacheIndex = -1;') + lines.append(' reference = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + for bucket in sorted(buckets): + lines.append(f' private static bool TryGetCoordinateReferenceBucket{bucket}(int srid, out EpsgCoordinateReferenceRecord reference, out int cacheIndex)') + lines.append(' {') + lines.append(' switch (srid)') + lines.append(' {') + for cache_index, ref_record in buckets[bucket]: + lines.append(f' case {ref_record[0]}:') + lines.append(f' cacheIndex = {cache_index};') + lines.append(f' reference = new EpsgCoordinateReferenceRecord({ref_record[0]}, (EpsgCoordinateSystemKind){ref_record[1]}, {ref_record[2]});') + lines.append(' return true;') + lines.append(' default:') + lines.append(' cacheIndex = -1;') + lines.append(' reference = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + def emit_explicit_operation_switch_factories(lines, values): + buckets = {} + for value in values: + bucket = value[0] // 1000 + buckets.setdefault(bucket, []).append(value) + + lines.append(' internal static bool TryGetExplicitOperationParameters(int operationCode, out EpsgExplicitOperationRecord parameters)') + lines.append(' {') + lines.append(' switch (operationCode / 1000)') + lines.append(' {') + for bucket in sorted(buckets): + lines.append(f' case {bucket}:') + lines.append(f' return TryGetExplicitOperationParametersBucket{bucket}(operationCode, out parameters);') + lines.append(' default:') + lines.append(' parameters = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + for bucket in sorted(buckets): + lines.append(f' private static bool TryGetExplicitOperationParametersBucket{bucket}(int operationCode, out EpsgExplicitOperationRecord parameters)') + lines.append(' {') + lines.append(' switch (operationCode)') + lines.append(' {') + for explicit_record in buckets[bucket]: + lines.append(f' case {explicit_record[0]}:') + lines.append(f' parameters = new EpsgExplicitOperationRecord({explicit_record[0]}, {repr(explicit_record[1])}d, {repr(explicit_record[2])}d, {repr(explicit_record[3])}d, {repr(explicit_record[4])}d, {repr(explicit_record[5])}d, {repr(explicit_record[6])}d, {repr(explicit_record[7])}d);') + lines.append(' return true;') + lines.append(' default:') + lines.append(' parameters = default;') + lines.append(' return false;') + lines.append(' }') + lines.append(' }') + lines.append('') + + fmt_geographic_crs = lambda value: f"{value[0]}, \"{esc(value[1])}\", {value[2]}, {value[3]}" + fmt_geocentric_crs = lambda value: f"{value[0]}, \"{esc(value[1])}\", {value[2]}, {value[3]}" + fmt_projected_crs = lambda value: f"{value[0]}, \"{esc(value[1])}\", {value[2]}, {value[3]}, {value[4]}" + fmt_vertical_crs = lambda value: f"{value[0]}, \"{esc(value[1])}\", {value[2]}, {value[3]}" + fmt_compound_crs = lambda value: f"{value[0]}, \"{esc(value[1])}\", {value[2]}, {value[3]}" + + def fmt_operation(value): + accuracy = 'double.NaN' if math.isnan(value[4]) else f'{repr(value[4])}d' + area_south_latitude = 'double.NaN' if math.isnan(value[7]) else f'{repr(value[7])}d' + area_north_latitude = 'double.NaN' if math.isnan(value[8]) else f'{repr(value[8])}d' + area_west_longitude = 'double.NaN' if math.isnan(value[9]) else f'{repr(value[9])}d' + area_east_longitude = 'double.NaN' if math.isnan(value[10]) else f'{repr(value[10])}d' + return ( + f"(EpsgOperationType){value[0]}, {value[1]}, {value[2]}, {value[3]}, {accuracy}, " + f"\"{esc(value[5])}\", \"{esc(value[6])}\", {area_south_latitude}, {area_north_latitude}, {area_west_longitude}, {area_east_longitude}, {value[11]}, {value[12]}" + ) + + types_lines = create_file_lines() + types_lines.append(' internal enum EpsgOperationType : byte { Transformation = 0, ConcatenatedOperation = 1, PointMotionOperation = 2 }') + types_lines.append(' internal enum EpsgCoordinateSystemKind : byte { Geographic2D = 0, Geocentric = 1, Projected = 2, Vertical = 3, Compound = 4 }') + types_lines.append('') + + for name, args in struct_defs.items(): + types_lines.append(f' internal readonly struct {name}') + types_lines.append(' {') + constructor_args = ', '.join(part.strip() for part in args.split(',')) + types_lines.append(f' internal {name}({constructor_args})') + types_lines.append(' {') + for part in args.split(','): + variable_name = part.strip().split(' ')[-1] + property_name = variable_name[0].upper() + variable_name[1:] + types_lines.append(f' {property_name} = {variable_name};') + types_lines.append(' }') + types_lines.append('') + for part in args.split(','): + property_type, variable_name = part.strip().rsplit(' ', 1) + property_name = variable_name[0].upper() + variable_name[1:] + types_lines.append(f' internal {property_type} {property_name} {{ get; }}') + types_lines.append(' }') + types_lines.append('') + + write_generated_file(types_path, types_lines) + + core_lines = create_file_lines() + begin_static_class(core_lines, 'EpsgGeneratedCatalog', partial=True) + core_lines.append(f' internal const string SourceArchive = "{esc(zip_name)}";') + core_lines.append(f' internal const int CoordinateReferenceCount = {len(catalog["ref_records"])};') + emit_wrapped_int_array( + core_lines, + ' private static readonly int[] CoordinateSridByCacheIndex = new int[]', + [ref_record[0] for ref_record in catalog['ref_records']]) + + emit_switch_factory(core_lines, 'TryGetGeographicCrs', 'EpsgGeographicCrsRecord', catalog['geographic_records'], fmt_geographic_crs) + emit_switch_factory(core_lines, 'TryGetGeocentricCrs', 'EpsgGeocentricCrsRecord', catalog['geocentric_records'], fmt_geocentric_crs) + emit_switch_factory(core_lines, 'TryGetVerticalCrs', 'EpsgVerticalCrsRecord', catalog['vertical_records'], fmt_vertical_crs) + emit_switch_factory(core_lines, 'TryGetCompoundCrs', 'EpsgCompoundCrsRecord', catalog['compound_records'], fmt_compound_crs) + + emit_array(core_lines, 'Units', 'EpsgUnitRecord', catalog['unit_records'], lambda value: f"{value[0]}, {value[1]}, {repr(value[2])}d, \"{esc(value[3])}\"") + emit_array(core_lines, 'Axes', 'EpsgAxisRecord', catalog['axis_records'], lambda value: f"{value[0]}, (byte){value[1]}, \"{esc(value[2])}\", (sbyte){value[3]}, {value[4]}") + emit_array(core_lines, 'Ellipsoids', 'EpsgEllipsoidRecord', catalog['ellipsoid_records'], lambda value: f"{value[0]}, \"{esc(value[1])}\", {repr(value[2])}d, {repr(value[3])}d, {repr(value[4])}d, {'true' if value[5] else 'false'}, {value[6]}") + emit_array(core_lines, 'PrimeMeridians', 'EpsgPrimeMeridianRecord', catalog['prime_meridian_records'], lambda value: f"{value[0]}, \"{esc(value[1])}\", {repr(value[2])}d, {value[3]}") + emit_array(core_lines, 'GeodeticDatums', 'EpsgGeodeticDatumRecord', catalog['geodetic_datum_records'], lambda value: f"{value[0]}, \"{esc(value[1])}\", {value[2]}, {value[3]}") + emit_array(core_lines, 'VerticalDatums', 'EpsgVerticalDatumRecord', catalog['vertical_datum_records'], lambda value: f"{value[0]}, \"{esc(value[1])}\"") + emit_coordinate_reference_switch_factory(core_lines, catalog['ref_records']) + core_lines.append(' internal static bool TryGetCoordinateSridByCacheIndex(int cacheIndex, out int srid)') + core_lines.append(' {') + core_lines.append(' if ((uint)cacheIndex < (uint)CoordinateSridByCacheIndex.Length)') + core_lines.append(' {') + core_lines.append(' srid = CoordinateSridByCacheIndex[cacheIndex];') + core_lines.append(' return true;') + core_lines.append(' }') + core_lines.append('') + core_lines.append(' srid = -1;') + core_lines.append(' return false;') + core_lines.append(' }') + end_static_class(core_lines) + write_generated_file(output_path, core_lines) + + projected_lines = create_file_lines() + begin_static_class(projected_lines, 'EpsgGeneratedCatalog', partial=True) + emit_projected_switch_factory(projected_lines, catalog['projected_records'], fmt_projected_crs) + end_static_class(projected_lines) + write_generated_file(projected_path, projected_lines) + + conversions_lines = create_file_lines() + begin_static_class(conversions_lines, 'EpsgGeneratedCatalog', partial=True) + emit_conversion_switch_factories(conversions_lines, catalog['conversion_records']) + end_static_class(conversions_lines) + write_generated_file(conversions_path, conversions_lines) + + operations_lines = create_file_lines() + begin_static_class(operations_lines, 'EpsgGeneratedOperationsCatalog') + emit_array(operations_lines, 'Operations', 'EpsgOperationRecord', operations, fmt_operation) + emit_array(operations_lines, 'OperationParameters', 'EpsgOperationParameterRecord', operation_parameters, lambda value: f"{value[0]}, \"{esc(value[1])}\", {repr(value[2])}d") + emit_concat_path_switch_factories(operations_lines, concat_paths) + emit_explicit_operation_switch_factories(operations_lines, explicit_operations) + end_static_class(operations_lines) + write_generated_file(operations_path, operations_lines) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--zip', required=True) + parser.add_argument('--pg-zip', required=True) + parser.add_argument('--output', required=True) + args = parser.parse_args() + + zip_path = Path(args.zip) + pg_zip_path = Path(args.pg_zip) + output_path = Path(args.output) + + output_path.parent.mkdir(parents=True, exist_ok=True) + + catalog = build_catalog(load_wkt_data(zip_path)) + operations, operation_parameters, explicit_operations, concat_paths = extract_operation_data(zip_path, pg_zip_path) + + emit(output_path, zip_path.name, catalog, operations, operation_parameters, explicit_operations, concat_paths) + + print(f'Generated: {output_path}') + print(f"CRS records: {len(catalog['ref_records'])}") + print(f'Operation records: {len(operations)}') + + +if __name__ == '__main__': + main() + + + + diff --git a/tools/tests/test_generate_epsg_catalog.py b/tools/tests/test_generate_epsg_catalog.py new file mode 100644 index 00000000..4eb112b1 --- /dev/null +++ b/tools/tests/test_generate_epsg_catalog.py @@ -0,0 +1,43 @@ +import sys +import unittest +from pathlib import Path + +TOOLS_ROOT = Path(__file__).resolve().parents[1] +if str(TOOLS_ROOT) not in sys.path: + sys.path.insert(0, str(TOOLS_ROOT)) + +import generate_epsg_catalog as generator + + +class GenerateEpsgCatalogTests(unittest.TestCase): + def test_bracket_content_ignores_brackets_inside_wkt_strings(self): + text = 'SOURCECRS[GEOGCRS["A ] ""quoted"" name",ID["EPSG",4326]],REMARK["still inside"]]TARGETCRS[GEOGCRS["Target",ID["EPSG",4979]]]' + + block = generator.bracket_content(text, 'SOURCECRS[') + + self.assertEqual('GEOGCRS["A ] ""quoted"" name",ID["EPSG",4326]],REMARK["still inside"]', block) + + def test_emit_wrapped_int_array_splits_values_across_multiple_lines(self): + lines = [] + + generator.emit_wrapped_int_array( + lines, + ' private static readonly int[] Values = new int[]', + [1, 2, 3, 4, 5, 6, 7], + values_per_line=3) + + self.assertEqual( + [ + ' private static readonly int[] Values = new int[]', + ' {', + ' 1, 2, 3,', + ' 4, 5, 6,', + ' 7,', + ' };', + '', + ], + lines) + + +if __name__ == '__main__': + unittest.main() diff --git a/version.json b/version.json new file mode 100644 index 00000000..cd46cb24 --- /dev/null +++ b/version.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", + "version": "3.0.0-alpha.{height}", + "publicReleaseRefSpec": [ + "^refs/heads/master$", + "^refs/heads/v\\d+(?:\\.\\d+)?$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} \ No newline at end of file